diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 607470d6f..6977bfe8c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -126,7 +126,7 @@ jobs: permissions: contents: write name: Generate build data - runs-on: blacksmith-2vcpu-ubuntu-2404 + runs-on: ubuntu-latest needs: buildid outputs: build_date: ${{ steps.data.outputs.builddate }} @@ -359,7 +359,7 @@ jobs: name: AppImage build - Linux ${{ matrix.arch }} permissions: contents: write - runs-on: blacksmith-2vcpu-ubuntu-2404 + runs-on: ubuntu-latest strategy: matrix: arch: [x86_64, aarch64] @@ -439,7 +439,7 @@ jobs: path: ./dist/zen-${{ matrix.arch }}.AppImage.zsync stop-self-hosted: - runs-on: blacksmith-2vcpu-ubuntu-2404 + runs-on: ubuntu-latest needs: [windows-step-3, linux] if: always() steps: @@ -505,10 +505,29 @@ jobs: run: | git clone https://github.com/zen-browser/windows-binaries.git .github/workflows/object --depth 1 + - name: Sign MAR files + env: + SIGNMAR: ${{ github.workspace }}/linux-bin-x86_64/signmar + ZEN_MAR_SIGNING_PASSWORD: ${{ secrets.ZEN_MAR_SIGNING_PASSWORD }} + ZEN_SIGNING_CERT_PEM_BASE64: ${{ secrets.ZEN_SIGNING_CERT_PEM_BASE64 }} + ZEN_SIGNING_PRIVATE_KEY_PEM_BASE64: ${{ secrets.ZEN_SIGNING_PRIVATE_KEY_PEM_BASE64 }} + run: | + bash scripts/mar_sign.sh -s + + - name: Verify MARs and update manifests + env: + SIGNMAR: ${{ github.workspace }}/linux-bin-x86_64/signmar + RELEASE_BRANCH: ${{ inputs.update_branch }} + run: | + bash scripts/mar_verify.sh + - name: Copy update manifests env: RELEASE_BRANCH: ${{ inputs.update_branch }} run: | + # IMPORTANT: If changing any of these names, + # make sure to also update the paths in the mar_sign.sh script + cd updates-server mkdir -p updates cp -a ../linux_update_manifest_x86_64/. updates/ @@ -551,8 +570,6 @@ jobs: ./zen-x86_64.AppImage.zsync/* ./zen-aarch64.AppImage/* ./zen-aarch64.AppImage.zsync/* - ./zen.win-x86_64.zip/* - ./zen.win-arm64.zip/* ./linux.mar/* ./linux-aarch64.mar/* ./windows.mar/* @@ -590,8 +607,6 @@ jobs: ./zen-x86_64.AppImage.zsync/* ./zen-aarch64.AppImage/* ./zen-aarch64.AppImage.zsync/* - ./.github/workflows/object/windows-x64-signed-x86_64/zen.win-x86_64.zip - ./.github/workflows/object/windows-x64-signed-arm64/zen.win-arm64.zip ./linux.mar/* ./linux-aarch64.mar/* ./.github/workflows/object/windows-x64-signed-x86_64/windows.mar @@ -606,7 +621,7 @@ jobs: permissions: write-all name: Prepare Flatpak needs: [release, linux, build-data] - runs-on: blacksmith-2vcpu-ubuntu-2404 + runs-on: ubuntu-latest steps: - name: Checkout Flatpak repository @@ -665,7 +680,7 @@ jobs: permissions: write-all name: Release Flatpak needs: [prepare-flatpak, build-data] - runs-on: blacksmith-2vcpu-ubuntu-2404 + runs-on: ubuntu-latest steps: - name: Checkout Flatpak repository diff --git a/.github/workflows/issue-metrics.yml b/.github/workflows/issue-metrics.yml index 56ac6763e..7a623410f 100644 --- a/.github/workflows/issue-metrics.yml +++ b/.github/workflows/issue-metrics.yml @@ -42,7 +42,7 @@ jobs: echo "last_month_year=$previous_year" >> "$GITHUB_ENV" - name: Run issue-metrics tool - uses: github/issue-metrics@v2 + uses: github-community-projects/issue-metrics@v2 env: GH_TOKEN: ${{ secrets.DEPLOY_KEY }} HIDE_AUTHOR: true diff --git a/.github/workflows/linux-release-build.yml b/.github/workflows/linux-release-build.yml index 93c6a70da..5e5a32bff 100644 --- a/.github/workflows/linux-release-build.yml +++ b/.github/workflows/linux-release-build.yml @@ -34,7 +34,6 @@ jobs: env: SCCACHE_GHA_ENABLED: ${{ inputs.use-sccache && 'true' || 'false' }} CARGO_TERM_COLOR: always - RUSTC_WRAPPER: ${{ inputs.use-sccache && 'sccache' || '' }} CARGO_INCREMENTAL: 0 name: Build Linux - ${{ matrix.arch }} @@ -104,6 +103,8 @@ jobs: - name: Import env: SURFER_COMPAT: ${{ matrix.arch }} + SURFER_CERT_PATCH_ISSUER: ${{ secrets.SURFER_CERT_PATCH_ISSUER }} + SURFER_CERT_PATCH_NAME: ${{ secrets.SURFER_CERT_PATCH_NAME }} run: | . "$HOME/.cargo/env" npm run import @@ -118,15 +119,45 @@ jobs: ./mach --no-interactive bootstrap --application-choice browser cd .. - - name: Insert API Keys + - name: Build (PGO stage 1 - generate) + if: ${{ matrix.arch == 'x86_64' }} + env: + SURFER_COMPAT: ${{ matrix.arch }} + ZEN_RELEASE_BRANCH: ${{ inputs.release-branch }} + ZEN_GA_GENERATE_PROFILE: 1 + ZEN_SAFEBROWSING_API_KEY: ${{ secrets.ZEN_SAFEBROWSING_API_KEY }} + ZEN_MOZILLA_API_KEY: ${{ secrets.ZEN_MOZILLA_API_KEY }} + ZEN_GOOGLE_LOCATION_SERVICE_API_KEY: ${{ secrets.ZEN_GOOGLE_LOCATION_SERVICE_API_KEY }} + continue-on-error: true run: | - mkdir -p ~/.zen-keys - echo "${{ secrets.ZEN_SAFEBROWSING_API_KEY }}" > ~/.zen-keys/safebrowsing.dat + export SURFER_PLATFORM="linux" + if [[ -n ${{ inputs.MOZ_BUILD_DATE }} ]];then + export MOZ_BUILD_DATE=${{ inputs.MOZ_BUILD_DATE }} + fi + bash .github/workflows/src/release-build.sh + + - name: Generate PGO profile data + if: ${{ matrix.arch == 'x86_64' }} + run: | + set -x + export DISPLAY=:2 + export LLVM_PROFDATA="$HOME/.mozbuild/clang/bin/llvm-profdata" + export JARLOG_FILE=en-US.log + mkdir -p "$HOME/artifact" + cd engine + ./mach python ../scripts/download_pgo_extended_corpus.py + ./mach package + ./mach python build/pgo/profileserver.py --extended-corpus ./pgo-extended-corpus + mv merged.profdata "$HOME/artifact/merged.profdata" + mv en-US.log "$HOME/artifact/en-US.log" - name: Build env: SURFER_COMPAT: ${{ matrix.arch }} ZEN_RELEASE_BRANCH: ${{ inputs.release-branch }} + ZEN_SAFEBROWSING_API_KEY: ${{ secrets.ZEN_SAFEBROWSING_API_KEY }} + ZEN_MOZILLA_API_KEY: ${{ secrets.ZEN_MOZILLA_API_KEY }} + ZEN_GOOGLE_LOCATION_SERVICE_API_KEY: ${{ secrets.ZEN_GOOGLE_LOCATION_SERVICE_API_KEY }} continue-on-error: true run: | export SURFER_PLATFORM="linux" @@ -144,10 +175,6 @@ jobs: export ZEN_RELEASE=1 npm run package - - name: Remove API Keys - run: | - rm -rf ~/.zen-keys - - name: Rename artifacts run: | mv dist/zen-*.tar.xz "zen.linux-${{ matrix.arch }}.tar.xz" @@ -173,3 +200,11 @@ jobs: retention-days: 5 name: linux_update_manifest_${{ matrix.arch }} path: ./dist/update + + - name: Upload linux bin + if: ${{ matrix.arch == 'x86_64' }} + uses: actions/upload-artifact@v4 + with: + retention-days: 2 + name: linux-bin-x86_64 + path: engine/obj-x86_64-pc-linux-gnu/dist/bin/ diff --git a/.github/workflows/macos-release-build.yml b/.github/workflows/macos-release-build.yml index ccfeacf39..66f2789f0 100644 --- a/.github/workflows/macos-release-build.yml +++ b/.github/workflows/macos-release-build.yml @@ -26,7 +26,7 @@ on: jobs: mac-build: name: Build macOS - ${{ matrix.arch }} - runs-on: macos-26 + runs-on: ${{ (inputs.release-branch == 'release') && 'blacksmith-6vcpu-macos-latest' || 'macos-26' }} strategy: fail-fast: false @@ -35,7 +35,6 @@ jobs: env: SCCACHE_GHA_ENABLED: ${{ inputs.use-sccache && 'true' || 'false' }} CARGO_TERM_COLOR: always - RUSTC_WRAPPER: ${{ inputs.use-sccache && 'sccache' || '' }} CARGO_INCREMENTAL: 0 steps: - name: Checkout repository @@ -118,7 +117,9 @@ jobs: - name: Import env: SURFER_COMPAT: ${{ matrix.arch }} - run: npm run import -- --verbose + SURFER_CERT_PATCH_ISSUER: ${{ secrets.SURFER_CERT_PATCH_ISSUER }} + SURFER_CERT_PATCH_NAME: ${{ secrets.SURFER_CERT_PATCH_NAME }} + run: npm run import - name: Bootstrap run: | @@ -132,15 +133,43 @@ jobs: - name: Build language packs run: sh scripts/download-language-packs.sh - - name: Insert API Keys + - name: Build Zen (PGO stage 1 - generate) + env: + SURFER_COMPAT: ${{ matrix.arch }} + ZEN_RELEASE_BRANCH: ${{ inputs.release-branch }} + ZEN_GA_GENERATE_PROFILE: 1 + ZEN_SAFEBROWSING_API_KEY: ${{ secrets.ZEN_SAFEBROWSING_API_KEY }} + ZEN_MOZILLA_API_KEY: ${{ secrets.ZEN_MOZILLA_API_KEY }} + ZEN_GOOGLE_LOCATION_SERVICE_API_KEY: ${{ secrets.ZEN_GOOGLE_LOCATION_SERVICE_API_KEY }} run: | - mkdir -p ~/.zen-keys - echo "${{ secrets.ZEN_SAFEBROWSING_API_KEY }}" > ~/.zen-keys/safebrowsing.dat + export SURFER_PLATFORM="darwin" + if [[ -n ${{ inputs.MOZ_BUILD_DATE }} ]];then + export MOZ_BUILD_DATE=${{ inputs.MOZ_BUILD_DATE }} + fi + bash .github/workflows/src/release-build.sh + + - name: Generate PGO profile data + env: + SURFER_COMPAT: ${{ matrix.arch }} + run: | + set -x + export LLVM_PROFDATA="$HOME/.mozbuild/clang/bin/llvm-profdata" + export JARLOG_FILE=en-US.log + mkdir -p "$HOME/artifact" + cd engine + ./mach python ../scripts/download_pgo_extended_corpus.py + ./mach package + ./mach python build/pgo/profileserver.py --extended-corpus ./pgo-extended-corpus + mv merged.profdata "$HOME/artifact/merged.profdata" + mv en-US.log "$HOME/artifact/en-US.log" - name: Build Zen env: SURFER_COMPAT: ${{ matrix.arch }} ZEN_RELEASE_BRANCH: ${{ inputs.release-branch }} + ZEN_SAFEBROWSING_API_KEY: ${{ secrets.ZEN_SAFEBROWSING_API_KEY }} + ZEN_MOZILLA_API_KEY: ${{ secrets.ZEN_MOZILLA_API_KEY }} + ZEN_GOOGLE_LOCATION_SERVICE_API_KEY: ${{ secrets.ZEN_GOOGLE_LOCATION_SERVICE_API_KEY }} run: | export SURFER_PLATFORM="darwin" if [[ -n ${{ inputs.MOZ_BUILD_DATE }} ]];then @@ -157,10 +186,6 @@ jobs: export ZEN_RELEASE=1 npm run package - - name: Remove API Keys - run: | - rm -rf ~/.zen-keys - - name: Rename artifacts run: | echo "Tarballing DMG" diff --git a/.github/workflows/macos-universal-release-build.yml b/.github/workflows/macos-universal-release-build.yml index dbc2130f0..b4fea39a0 100644 --- a/.github/workflows/macos-universal-release-build.yml +++ b/.github/workflows/macos-universal-release-build.yml @@ -93,6 +93,7 @@ jobs: env: SURFER_MOZCONFIG_ONLY: true ZEN_RELEASE: 1 + ZEN_GA_DISABLE_PGO: true run: | npm run build cd engine @@ -232,6 +233,7 @@ jobs: - name: Package for mar env: JUST_MAR: true + ZEN_GA_DISABLE_PGO: true run: | # we don't need it anymore set -ex diff --git a/.github/workflows/src/release-build.sh b/.github/workflows/src/release-build.sh index e553f3942..fbccedce2 100644 --- a/.github/workflows/src/release-build.sh +++ b/.github/workflows/src/release-build.sh @@ -8,8 +8,15 @@ if command -v apt-get &> /dev/null; then sudo apt-get install -y xvfb libnvidia-egl-wayland1 mesa-utils libgl1-mesa-dri fi +mkdir -p ~/.zen-keys +echo "$ZEN_SAFEBROWSING_API_KEY" > ~/.zen-keys/safebrowsing.dat +echo "$ZEN_MOZILLA_API_KEY" > ~/.zen-keys/mozilla.dat +echo "$ZEN_GOOGLE_LOCATION_SERVICE_API_KEY" > ~/.zen-keys/google_location_service.dat + . $HOME/.cargo/env +bash ./scripts/mar_sign.sh -i + ulimit -n 4096 if command -v Xvfb &> /dev/null; then @@ -28,3 +35,6 @@ else export ZEN_RELEASE=1 npm run build fi + +echo "Build complete, removing API keys" +rm -rf ~/.zen-keys diff --git a/.github/workflows/sync-upstream.yml b/.github/workflows/sync-upstream.yml index 3913e4099..a8f81b400 100644 --- a/.github/workflows/sync-upstream.yml +++ b/.github/workflows/sync-upstream.yml @@ -103,6 +103,10 @@ jobs: fi echo "version=$VERSION" >> $GITHUB_OUTPUT + - name: Import external patches + if: steps.git-check.outputs.files_changed == 'true' + run: python3 scripts/update_external_patches.py || true + - name: Check if patches got applied if: steps.git-check.outputs.files_changed == 'true' id: check-patches @@ -115,10 +119,6 @@ jobs: if: steps.git-check.outputs.files_changed == 'true' run: python3 scripts/import_external_tests.py || true - - name: Import external patches - if: steps.git-check.outputs.files_changed == 'true' - run: python3 scripts/update_external_patches.py || true - - name: Create pull request uses: peter-evans/create-pull-request@v7 if: steps.git-check.outputs.files_changed == 'true' diff --git a/.github/workflows/twilight-release-schedule.yml b/.github/workflows/twilight-release-schedule.yml index de9e3ff1e..5b50f55c3 100644 --- a/.github/workflows/twilight-release-schedule.yml +++ b/.github/workflows/twilight-release-schedule.yml @@ -20,7 +20,7 @@ jobs: with: create_release: ${{ github.event_name != 'workflow_dispatch' && true || inputs.create_release }} update_branch: twilight - use-sccache: false + use-sccache: true update_version: false post-build: diff --git a/.github/workflows/windows-profile-build.yml b/.github/workflows/windows-profile-build.yml index 740c5fd3f..0984f2d37 100644 --- a/.github/workflows/windows-profile-build.yml +++ b/.github/workflows/windows-profile-build.yml @@ -93,7 +93,8 @@ jobs: echo "cd $workspace_dir" '' >> mozilla-build-run.sh echo 'export PATH=/c/mozilla-build/msys2/usr/bin:$PATH' '' >> mozilla-build-run.sh echo './mach --no-interactive bootstrap --application-choice browser' '' >> mozilla-build-run.sh - echo 'LLVM_PROFDATA=/c/Users/runneradmin/.mozbuild/clang/bin/llvm-profdata.exe JARLOG_FILE=en-US.log ./mach python build/pgo/profileserver.py --binary /c/artifact/zen/zen.exe' '' >> mozilla-build-run.sh + echo './mach python ../scripts/download_pgo_extended_corpus.py' '' >> mozilla-build-run.sh + echo 'LLVM_PROFDATA=/c/Users/runneradmin/.mozbuild/clang/bin/llvm-profdata.exe JARLOG_FILE=en-US.log ./mach python build/pgo/profileserver.py --binary /c/artifact/zen/zen.exe --extended-corpus ./pgo-extended-corpus --' '' >> mozilla-build-run.sh C:\mozilla-build\start-shell.bat $workspace_dir_current\mozilla-build-run.sh - name: Move profile data diff --git a/.github/workflows/windows-release-build.yml b/.github/workflows/windows-release-build.yml index a7260a733..d2570dbfd 100644 --- a/.github/workflows/windows-release-build.yml +++ b/.github/workflows/windows-release-build.yml @@ -39,7 +39,6 @@ jobs: env: SCCACHE_GHA_ENABLED: ${{ inputs.use-sccache && 'true' || 'false' }} CARGO_TERM_COLOR: always - RUSTC_WRAPPER: ${{ inputs.use-sccache && 'sccache' || '' }} CARGO_INCREMENTAL: 0 strategy: fail-fast: false @@ -165,6 +164,8 @@ jobs: if: ${{ !(inputs.generate-gpo && matrix.arch == 'aarch64') }} env: SURFER_COMPAT: ${{ matrix.arch }} + SURFER_CERT_PATCH_ISSUER: ${{ secrets.SURFER_CERT_PATCH_ISSUER }} + SURFER_CERT_PATCH_NAME: ${{ secrets.SURFER_CERT_PATCH_NAME }} run: | . "$HOME/.cargo/env" npm run import -- --verbose @@ -226,16 +227,14 @@ jobs: chmod +x ~/artifact/en-US.log chmod +x ~/artifact/merged.profdata - - name: Insert API Keys - run: | - mkdir -p ~/.zen-keys - echo "${{ secrets.ZEN_SAFEBROWSING_API_KEY }}" > ~/.zen-keys/safebrowsing.dat - - name: Build if: ${{ !(inputs.generate-gpo && matrix.arch == 'aarch64') }} env: SURFER_COMPAT: ${{ matrix.arch }} ZEN_RELEASE_BRANCH: ${{ inputs.release-branch }} + ZEN_SAFEBROWSING_API_KEY: ${{ secrets.ZEN_SAFEBROWSING_API_KEY }} + ZEN_MOZILLA_API_KEY: ${{ secrets.ZEN_MOZILLA_API_KEY }} + ZEN_GOOGLE_LOCATION_SERVICE_API_KEY: ${{ secrets.ZEN_GOOGLE_LOCATION_SERVICE_API_KEY }} run: | set -x dos2unix configs/windows/mozconfig @@ -264,10 +263,6 @@ jobs: ls ./dist ls . - - name: Remove API Keys - run: | - rm -rf ~/.zen-keys - - name: Move package for PGO upload if: ${{ inputs.generate-gpo && matrix.arch == 'x86_64' }} run: | @@ -285,7 +280,7 @@ jobs: uses: actions/upload-artifact@v4 if: ${{ inputs.generate-gpo && matrix.arch == 'x86_64' }} with: - retention-days: 5 + retention-days: 2 name: ${{ matrix.arch == 'aarch64' && 'arm64' || matrix.arch }}-${{ inputs.profile-data-path-archive }} path: ./zen.win64-pgo-stage-1.zip @@ -312,14 +307,6 @@ jobs: name: windows-x64-obj-${{ matrix.arch == 'aarch64' && 'arm64' || matrix.arch }} path: obj-${{ matrix.arch }}-pc-windows-msvc - - name: Upload artifact (if Twilight branch, binary) - if: ${{ inputs.release-branch == 'twilight' && !inputs.generate-gpo }} - uses: actions/upload-artifact@v4 - with: - retention-days: 5 - name: zen.win-${{ matrix.arch == 'aarch64' && 'arm64' || matrix.arch }}.zip - path: ./zen.win-${{ matrix.arch == 'aarch64' && 'arm64' || matrix.arch }}.zip - - name: Upload artifact (if Twilight branch, installer) if: ${{ inputs.release-branch == 'twilight' && !inputs.generate-gpo }} uses: actions/upload-artifact@v4 diff --git a/.gitignore b/.gitignore index 716fd810f..67b3b7ef6 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,10 @@ locales/firefox-l10n/ .DS_Store mozconfig + +build/signing/env/ +build/signing/nss_config/ +build/signing/cert.pem +build/signing/private_key.pem +build/signing/private_key.p12 + diff --git a/README.md b/README.md index b6a817c47..9df57ae26 100644 --- a/README.md +++ b/README.md @@ -34,12 +34,12 @@ Zen is a firefox-based browser with the aim of pushing your productivity to a ne ### Firefox Versions -- [`Release`](https://zen-browser.app/download) - Is currently built using Firefox version `149.0`! 🚀 -- [`Twilight`](https://zen-browser.app/download?twilight) - Is currently built using Firefox version `RC 149.0`! +- [`Release`](https://zen-browser.app/download) - Is currently built using Firefox version `150.0.2`! 🚀 +- [`Twilight`](https://zen-browser.app/download?twilight) - Is currently built using Firefox version `RC 150.0.2`! ### Contributing -If you'd like to report a bug, please do so on our [GitHub Issues page](https://github.com/zen-browser/desktop/issues/) and for feature requests, you can use [Github Discussions](https://github.com/zen-browser/desktop/discussions). +If you'd like to report a bug, please do so on our [GitHub Issues page](https://github.com/zen-browser/desktop/issues/) and for feature requests, you can use [GitHub Discussions](https://github.com/zen-browser/desktop/discussions). Zen is an open-source project, and we welcome contributions from the community! Please take a look at the [contribution guidelines](./docs/contribute.md) before getting started! diff --git a/build/AppDir/zen.desktop b/build/AppDir/zen.desktop index a889e53b4..7dede46fe 100644 --- a/build/AppDir/zen.desktop +++ b/build/AppDir/zen.desktop @@ -1,31 +1,698 @@ [Desktop Entry] Name=Zen Browser -Comment=Experience tranquillity while browsing the web without people tracking you! +Comment=A fast, private and secure web browser built to improve your day-to-day experience. Exec=zen %u Icon=zen Type=Application -MimeType=text/html;text/xml;application/xhtml+xml;x-scheme-handler/http;x-scheme-handler/https;application/x-xpinstall;application/pdf;application/json; +MimeType=application/json;application/pdf;application/rdf+xml;application/rss+xml;application/x-xpinstall;application/xhtml+xml;application/xml;audio/flac;audio/ogg;audio/webm;image/avif;image/gif;image/jpeg;image/png;image/svg+xml;image/webp;text/html;text/xml;video/ogg;video/webm;x-scheme-handler/chrome;x-scheme-handler/http;x-scheme-handler/https;x-scheme-handler/mailto; StartupWMClass=zen Categories=Network;WebBrowser; StartupNotify=true Terminal=false X-MultipleArgs=false +GenericName=Web Browser +GenericName[ach]=Web Browser +GenericName[af]=Web Browser +GenericName[an]=Web Browser +GenericName[ar]=متصفح الوِب +GenericName[ast]=Web Browser +GenericName[az]=Web Browser +GenericName[be]=Вэб-браўзер +GenericName[bg]=Уеб браузър +GenericName[bn]=ওয়েব ব্রাউজার +GenericName[bqi]=گشت گر وب +GenericName[br]=Merdeer Web +GenericName[brx]=Web Browser +GenericName[bs]=Web pretraživač +GenericName[ca]=Navegador web +GenericName[ca_valencia]=Web Browser +GenericName[cak]=Web Okik'amaya'l +GenericName[ckb]=Web Browser +GenericName[cs]=Webový prohlížeč +GenericName[cy]=Porwr Gwe +GenericName[da]=Webbrowser +GenericName[de]=Internet-Browser +GenericName[dsb]=Webwobglědowak +GenericName[el]=Πρόγραμμα περιήγησης +GenericName[en_CA]=Web Browser +GenericName[en_GB]=Web Browser +GenericName[eo]=Retumilo +GenericName[es_AR]=Navegador web +GenericName[es_CL]=Navegador Web +GenericName[es_ES]=Navegador web +GenericName[es_MX]=Navegador Web +GenericName[et]=Web Browser +GenericName[eu]=Web nabigatzailea +GenericName[fa]=مرورگر وب +GenericName[ff]=Web Browser +GenericName[fi]=Verkkoselain +GenericName[fr]=Navigateur web +GenericName[fur]=Navigadôr Web +GenericName[fy_NL]=Webbrowser +GenericName[ga_IE]=Web Browser +GenericName[gd]=Brabhsair-lìn +GenericName[gl]=Navegador web +GenericName[gn]=Ñanduti Kundahára +GenericName[gu_IN]=Web Browser +GenericName[he]=דפדפן אינטרנט +GenericName[hi_IN]=Web Browser +GenericName[hr]=Web preglednik +GenericName[hsb]=Webwobhladowak +GenericName[hu]=Webböngésző +GenericName[hy_AM]=Վեբ դիտարկիչ +GenericName[hye]=Web Browser +GenericName[ia]=Navigator web +GenericName[id]=Peramban Web +GenericName[is]=Vafri +GenericName[it]=Browser web +GenericName[ja]=ウェブブラウザー +GenericName[ka]=ბრაუზერი +GenericName[kab]=Iminig web +GenericName[kk]=Веб-браузері +GenericName[km]=Web Browser +GenericName[kn]=Web Browser +GenericName[ko]=웹 브라우저 +GenericName[lij]=Navegatô Web +GenericName[lo]=ຕົວ​ທ່ອງ​ເວັບ​ເວັບ​ໄຊ​ຕ​໌ +GenericName[lt]=Web Browser +GenericName[ltg]=Web Browser +GenericName[lv]=Tīmekļa pārlūks +GenericName[meh]=Web Browser +GenericName[mk]=Web Browser +GenericName[ml]=ഗോളാന്തരവല അന്വേഷിയന്ത്രം +GenericName[mr]=Web Browser +GenericName[ms]=Web Browser +GenericName[my]=Web Browser +GenericName[nb_NO]=Nettleser +GenericName[ne_NP]=वेब ब्राउजर +GenericName[nl]=Webbrowser +GenericName[nn_NO]=Nettlesar +GenericName[oc]=Navegador Web +GenericName[pa_IN]=ਵੈੱਬ ਬਰਾਊਜ਼ਰ +GenericName[pl]=Przeglądarka internetowa +GenericName[pt_BR]=Navegador web +GenericName[pt_PT]=Navegador Web +GenericName[rm]=Navigatur web +GenericName[ro]=Browser web +GenericName[ru]=Веб-браузер +GenericName[sat]=ᱣᱮᱵᱽ ᱵᱽᱨᱟᱣᱡᱚᱨ +GenericName[sc]=Navigadore web +GenericName[sco]=Web Browser +GenericName[si]=වියමන අතිරික්සුව +GenericName[sk]=Webový prehliadač +GenericName[skr]=ویب براؤزر +GenericName[sl]=Spletni brskalnik +GenericName[son]=Web Browser +GenericName[sq]=Shfletues +GenericName[sr]=Веб прегледач +GenericName[sv_SE]=Webbläsare +GenericName[szl]=Web Browser +GenericName[ta]=Web Browser +GenericName[te]=జాల విహారిణి +GenericName[tg]=Браузери веб +GenericName[th]=เว็บเบราว์เซอร์ +GenericName[tl]=Web Browser +GenericName[tr]=Web Tarayıcısı +GenericName[trs]=Web riña gāchē nu’ +GenericName[uk]=Браузер +GenericName[ur]=Web Browser +GenericName[uz]=Web Browser +GenericName[vi]=Trình duyệt web +GenericName[wo]=Web Browser +GenericName[xh]=Web Browser +GenericName[zh_CN]=Web 浏览器 +GenericName[zh_TW]=網頁瀏覽器 Keywords=Internet;WWW;Browser;Web;Explorer; +Keywords[ach]=Internet;WWW;Browser;Web;Explorer; +Keywords[af]=Internet;WWW;Browser;Web;Explorer; +Keywords[an]=Internet;WWW;Browser;Web;Explorer; +Keywords[ar]=إنترنت;WWW;متصفح;ويب;مستكشف; +Keywords[ast]=Internet;WWW;Browser;Web;Explorer; +Keywords[az]=Internet;WWW;Browser;Web;Explorer; +Keywords[be]=Internet;WWW;Browser;Web;Explorer; +Keywords[bg]=Internet;WWW;Browser;Web;Explorer; +Keywords[bn]=ইন্টারনেট;WWW;ব্রাউজার;ওয়েব;এক্সপ্লোরার; +Keywords[bqi]=Internet;WWW;Browser;Web;Explorer; +Keywords[br]=Internet;WWW;Merdeer;Web;Ergerzhout; +Keywords[brx]=Internet;WWW;Browser;Web;Explorer; +Keywords[bs]=Internet;WWW;Pretraživač;Web;Istraživač; +Keywords[ca]=Internet;WWW;Browser;Web;Explorador;Navegador; +Keywords[ca_valencia]=Internet;WWW;Browser;Web;Explorer; +Keywords[cak]=K'amaya'l;WWW;Okik'amaya'l;Kanob'äl; +Keywords[ckb]=Internet;WWW;Browser;Web;Explorer; +Keywords[cs]=internet;WWW;prohlížeč;web; +Keywords[cy]=Rhyngrwyd;WWW;Porwr;Gwe;Archwiliwr; +Keywords[da]=Internet;WWW;Browser;Nettet;Explorer; +Keywords[de]=Internet;WWW;Browser;Web;Explorer; +Keywords[dsb]=Internet;WWW;wobglědowak;Web;Explorer; +Keywords[el]=Internet;WWW;Browser;Web;Explorer;Διαδίκτυο;Ιστός;Ίντερνετ; +Keywords[en_CA]=Internet;WWW;Browser;Web;Explorer; +Keywords[en_GB]=Internet;WWW;Browser;Web;Explorer; +Keywords[eo]=Interreto;Retumilo;TTT;Teksaĵo;Reto;Internet;WWW;Browser;Web;Explorer; +Keywords[es_AR]=Internet;WWW;Navegador;Web;Explorador; +Keywords[es_CL]=Internet;WWW;Navegador;Web;Explorador; +Keywords[es_ES]=Internet;WWW;Navegador;Web;Explorador; +Keywords[es_MX]=Internet;WWW;Navegador;Web;Explorador; +Keywords[et]=Internet;WWW;Browser;Web;Explorer; +Keywords[eu]=Internet;WWW;Nabigatzailea;Web;Arakatzailea; +Keywords[fa]=Internet;WWW;Browser;Web;Explorer; +Keywords[ff]=Internet;WWW;Browser;Web;Explorer; +Keywords[fi]=Internet;WWW;Browser;Web;Explorer;netti;webbi;selain; +Keywords[fr]=Internet;WWW;Navigateur;Web;Explorer; +Keywords[fur]=Internet;WWW;Browser;Navigadôr;Web;Esploradôr;Explorer; +Keywords[fy_NL]=Ynternet;WWW;Browser;Web;Ferkenner; +Keywords[ga_IE]=Internet;WWW;Browser;Web;Explorer; +Keywords[gd]=Internet;WWW;Browser;Web;Explorer;eadar-lìon;brabhsair;brobhsair;lìon;taisgealaiche; +Keywords[gl]=Internet;WWW;Navegador;Web;Explorador; +Keywords[gn]=Internet;WWW;Browser;Web;Explorer; +Keywords[gu_IN]=Internet;WWW;Browser;Web;Explorer; +Keywords[he]=אינטרנט;WWW;דפדפן;רשת;סייר;מרשתת; +Keywords[hi_IN]=Internet;WWW;Browser;Web;Explorer; +Keywords[hr]=Internet;WWW;Preglednik;Web;Istraživač; +Keywords[hsb]=Internet;WWW;wobhladowak;Web;Explorer; +Keywords[hu]=Internet;WWW;Böngésző;Web;Világháló; +Keywords[hy_AM]=Համացանց,WWW,Զննիչ,Վեբ,Ցանցախույզ: +Keywords[hye]=Internet;WWW;Browser;Web;Explorer; +Keywords[ia]=Internet;WWW;Navigator;Web;Explorator; +Keywords[id]=Internet;WWW;Browser;Web;Explorer; +Keywords[is]=Internet;WWW; Vafri; Vefur; Explorer; +Keywords[it]=Internet;WWW;Browser;Web;Explorer;Navigatore; +Keywords[ja]=Internet;WWW;Browser;Web;Explorer;インターネット;ブラウザー;ウェブ; +Keywords[ka]=ინტერნეტი;WWW;ბრაუზერი;ქსელი;ქსელთან წვდომა; +Keywords[kab]=Internet;WWW;Browser;Web;Explorer; +Keywords[kk]=Internet;WWW;Browser;Web;Explorer;Интернет;Ғаламтор;Браузер;Желі;Шолғыш; +Keywords[km]=Internet;WWW;Browser;Web;Explorer; +Keywords[kn]=Internet;WWW;Browser;Web;Explorer; +Keywords[ko]=인터넷;브라우저;웹;탐색기;Internet;WWW;Browser;Web;Explorer; +Keywords[lij]=Internet;WWW;Browser;Web;Explorer;Navegatô; +Keywords[lo]=Internet;WWW;Browser;Web;Explorer; +Keywords[lt]=Internet;WWW;Browser;Web;Explorer; +Keywords[ltg]=Internet;WWW;Browser;Web;Explorer; +Keywords[lv]=Internets;WWW;Pārlūkprogramma;Tīmeklis; +Keywords[meh]=Internet;WWW;Browser;Web;Explorer; +Keywords[mk]=Internet;WWW;Browser;Web;Explorer; +Keywords[ml]=ഗോളാന്തരവല;WWW;അന്വേഷിയന്ത്രം;ഗോളാന്തരവല;ആരായൻ; +Keywords[mr]=Internet;WWW;Browser;Web;Explorer; +Keywords[ms]=Internet;WWW;Browser;Web;Explorer; +Keywords[my]=Internet;WWW;Browser;Web;Explorer; +Keywords[nb_NO]=Internett;WWW;Nettleser;Web;Utforsker; +Keywords[ne_NP]=Internet;WWW;Browser;Web;Explorer; +Keywords[nl]=Internet;WWW;Browser;Web;Verkenner; +Keywords[nn_NO]=Internett;WWW;Nettlesar;Web;Utforskar; +Keywords[oc]=Internet;WWW;Navegador;Navigador;Navegator;Navigator;Web;Explorer; +Keywords[pa_IN]=ਇੰਟਰਨੈੱਟ;WWW;ਬਰਾਊਜ਼ਰ;ਵੈੱਬ;ਐਕਸਪਲਰੋਰ;ਵੈਬ;ਇੰਟਰਨੈਟ; +Keywords[pl]=Internet;WWW;Przeglądarka;Browser;Wyszukiwarka;Web;Sieć;Explorer;Eksplorer;Strony;Witryny;internetowe; +Keywords[pt_BR]=Internet;WWW;Browser;Web;Explorer;Navegador; +Keywords[pt_PT]=Internet;WWW;Navegador;Web;Explorador; +Keywords[rm]=Internet;WWW;Browser;Web;Explorer;navigatur; +Keywords[ro]=Internet; WWW; Browser; Web; Explorer; +Keywords[ru]=Сеть;Интернет;Браузер;Доступ в Интернет; +Keywords[sat]=Internet;WWW;Browser;Web;Explorer; +Keywords[sc]=Internet;WWW;Navigadore;Web;Explorer; +Keywords[sco]=Internet;WWW;Browser;Web;Explorer; +Keywords[si]=අන්තර්ජාලය;අතිරික්සුව;පිරික්සන්න;ගවේශකය;Internet;WWW;Browser;Web;Explorer; +Keywords[sk]=Internet;WWW;Prehliadač;Web;Prieskumník; +Keywords[skr]=Internet;WWW;Browser;Web;Explorer; +Keywords[sl]=internet;www;brskalnik;splet; +Keywords[son]=Internet;WWW;Browser;Web;Explorer; +Keywords[sq]=Internet;WWW;Shfletues;Web;Eksplorues; +Keywords[sr]=Internet;WWW;Browser;Web;Explorer;интернет;њњњ;веб;мрежа;прегледач;експлорер;internet;pregledač;veb;mreža;pregledač;eksplorer; +Keywords[sv_SE]=Internet;WWW;Webbläsare;Webb;Utforskare; +Keywords[szl]=Internet;WWW;Browser;Web;Explorer; +Keywords[ta]=Internet;WWW;Browser;Web;Explorer; +Keywords[te]=Internet;WWW;Browser;Web;Explorer; +Keywords[tg]=Интернет;WWW;Браузер;Сомона;Ҷустуҷӯгар; +Keywords[th]=อินเทอร์เน็ต;เบราว์เซอร์;เว็บ;Internet;WWW;Browser;Web;Explorer; +Keywords[tl]=Internet;WWW;Browser;Web;Explorer; +Keywords[tr]=Internet;WWW;Browser;Web;Explorer;İnternet;Tarayıcı; +Keywords[trs]=Internet;WWW;Browser;Web;Explorer; +Keywords[uk]=Інтернет;WWW;Браузер;Веб;Переглядач; +Keywords[ur]=Internet;WWW;Browser;Web;Explorer; +Keywords[uz]=Internet;WWW;Browser;Web;Explorer; +Keywords[vi]=Internet;WWW;Trình duyệt;Web;Duyệt web; +Keywords[wo]=Internet;WWW;Browser;Web;Explorer; +Keywords[xh]=Internet;WWW;Browser;Web;Explorer; +Keywords[zh_CN]=Internet;WWW;Browser;Web;Explorer; +Keywords[zh_TW]=網際網路;網路;瀏覽器;網頁;上網;Internet;WWW;Browser;Web;Explorer; Actions=new-window;new-blank-window;new-private-window;profilemanager; X-AppImage-Version=$VERSION [Desktop Action new-window] -Name=Open a New Window Exec=zen %u +Name=New Window +Name[ach]=New Window +Name[af]=New Window +Name[an]=New Window +Name[ar]=نافذة جديدة +Name[ast]=New Window +Name[az]=New Window +Name[be]=Новае акно +Name[bg]=Нов прозорец +Name[bn]=নতুন উইন্ডো +Name[bqi]=نیمدری نۊ +Name[br]=Prenestr nevez +Name[brx]=New Window +Name[bs]=Novi prozor +Name[ca]=Finestra nova +Name[ca_valencia]=New Window +Name[cak]=K'ak'a' Tzuwäch +Name[ckb]=New Window +Name[cs]=Nové okno +Name[cy]=Ffenestr Newydd +Name[da]=Nyt vindue +Name[de]=Neues Fenster +Name[dsb]=Nowe wokno +Name[el]=Νέο παράθυρο +Name[en_CA]=New Window +Name[en_GB]=New Window +Name[eo]=Nova fenestro +Name[es_AR]=Nueva ventana +Name[es_CL]=Nueva ventana +Name[es_ES]=Nueva ventana +Name[es_MX]=Nueva ventana +Name[et]=New Window +Name[eu]=Leiho berria +Name[fa]=پنجره جدید‌ +Name[ff]=New Window +Name[fi]=Uusi ikkuna +Name[fr]=Nouvelle fenêtre +Name[fur]=Gnûf barcon +Name[fy_NL]=Nij finster +Name[ga_IE]=New Window +Name[gd]=Uinneag ùr +Name[gl]=Nova xanela +Name[gn]=Ovetã pyahu +Name[gu_IN]=New Window +Name[he]=חלון חדש +Name[hi_IN]=New Window +Name[hr]=Novi prozor +Name[hsb]=Nowe wokno +Name[hu]=Új ablak +Name[hy_AM]=Նոր պատուհան +Name[hye]=New Window +Name[ia]=Nove fenestra +Name[id]=Jendela Baru +Name[is]=Nýr gluggi +Name[it]=Nuova finestra +Name[ja]=新しいウィンドウ +Name[ka]=ახალი ფანჯარა +Name[kab]=Asfaylu amaynut +Name[kk]=Жаңа терезе +Name[km]=បង្អួច​​​ថ្មី +Name[kn]=New Window +Name[ko]=새 창 +Name[lij]=Neuvo Barcon +Name[lo]=ວິນໂດໃໝ່ +Name[lt]=New Window +Name[ltg]=New Window +Name[lv]=Jauns logs +Name[meh]=New Window +Name[mk]=New Window +Name[ml]=പുതിയ ജാലകം +Name[mr]=New Window +Name[ms]=New Window +Name[my]=New Window +Name[nb_NO]=Nytt vindu +Name[ne_NP]=नयाँ सञ्झ्याल +Name[nl]=Nieuw venster +Name[nn_NO]=Nytt vindauge +Name[oc]=Fenèstra novèla +Name[pa_IN]=ਨਵੀਂ ਵਿੰਡੋ +Name[pl]=Nowe okno +Name[pt_BR]=Nova janela +Name[pt_PT]=Nova janela +Name[rm]=Nova fanestra +Name[ro]=Fereastră nouă +Name[ru]=Новое окно +Name[sat]=ᱱᱟᱶᱟ ᱣᱤᱱᱰᱳ +Name[sc]=Ventana noa +Name[sco]=New Window +Name[si]=නව කවුළුව +Name[sk]=Nové okno +Name[skr]=نویں ونڈو +Name[sl]=Novo okno +Name[son]=New Window +Name[sq]=Dritare e Re +Name[sr]=Нови прозор +Name[sv_SE]=Nytt fönster +Name[szl]=New Window +Name[ta]=New Window +Name[te]=కొత్త కిటికీ +Name[tg]=Равзанаи нав +Name[th]=หน้าต่างใหม่ +Name[tl]=New Window +Name[tr]=Yeni pencere +Name[trs]=Bēntanâ nākàa +Name[uk]=Нове вікно +Name[ur]=New Window +Name[uz]=New Window +Name[vi]=Cửa sổ mới +Name[wo]=New Window +Name[xh]=New Window +Name[zh_CN]=新建窗口 +Name[zh_TW]=開新視窗 [Desktop Action new-blank-window] -Name=Open a New Blank Window Exec=zen --blank-window %u +Name=New Blank Window +Name[ach]=New Blank Window +Name[af]=New Blank Window +Name[an]=New Blank Window +Name[ar]=نافذة فارغة جديدة +Name[ast]=New Blank Window +Name[az]=New Blank Window +Name[be]=Новае пустое акно +Name[bg]=Нов празен прозорец +Name[bn]=নতুন ফাঁকা উইন্ডো +Name[bqi]=نیمدری نۊ خالی +Name[br]=Prenestr goulloù nevez +Name[brx]=New Blank Window +Name[bs]=Novi prazni prozor +Name[ca]=Finestra en blanc nova +Name[ca_valencia]=New Blank Window +Name[cak]=K'ak'a' Tzuwäch K'axk'ol +Name[ckb]=New Blank Window +Name[cs]=Nové prázdné okno +Name[cy]=Ffenestr Wag Newydd +Name[da]=Nyt tomt vindue +Name[de]=Neues leeres Fenster +Name[dsb]=Nowe prázdne wokno +Name[el]=Νέο κενό παράθυρο +Name[en_CA]=New Blank Window +Name[en_GB]=New Blank Window +Name[eo]=Nova malplena fenestro +Name[es_AR]=Nueva ventana en blanco +Name[es_CL]=Nueva ventana en blanco +Name[es_ES]=Nueva ventana en blanco +Name[es_MX]=Nueva ventana en blanco +Name[et]=New Blank Window +Name[eu]=Leiho huts berri +Name[fa]=پنجره خالی جدید +Name[ff]=New Blank Window +Name[fi]=Uusi tyhjä ikkuna +Name[fr]=Nouvelle fenêtre vierge +Name[fur]=Gnûf barcon vuot +Name[fy_NL]=Nij leeg finster +Name[ga_IE]=New Blank Window +Name[gd]=Uinneag bhàn ùr +Name[gl]=Nova xanela en branco +Name[gn]=Ovetã ñemi pyahu +Name[gu_IN]=New Blank Window +Name[he]=חלון ריק חדש +Name[hi_IN]=New Blank Window +Name[hr]=Novi prazni prozor +Name[hsb]=Nowe prázdne wokno +Name[hu]=Új üres ablak +Name[hy_AM]=Նոր դատարկ պատուհան +Name[hye]=New Blank Window +Name[ia]=Nove fenestra vacue +Name[id]=Jendela Kosong Baru +Name[is]=Nýr tómur gluggi +Name[it]=Nuova finestra vuota +Name[ja]=新しい空白のウィンドウ +Name[ka]=ახალი ცარიელი ფანჯარა +Name[kab]=Asfaylu amaynut n tunigin tusligt +Name[kk]=Жаңа бос терезе +Name[km]=បង្អួច​ថ្មី​ឯកជន +Name[kn]=New Blank Window +Name[ko]=새 빈 창 +Name[lij]=Neuvo Barcon Vuot +Name[lo]=ວິນໂດແອ່ງໃໝ່ +Name[lt]=New Blank Window +Name[ltg]=New Blank Window +Name[lv]=Jauns tukšais logs +Name[meh]=New Blank Window +Name[mk]=New Blank Window +Name[ml]=പുതിയ ശൂന്യ ജാലകം +Name[mr]=New Blank Window +Name[ms]=New Blank Window +Name[my]=New Blank Window +Name[nb_NO]=Nytt tomt vindu +Name[ne_NP]=नयाँ खाली सञ्झ्याल +Name[nl]=Nieuw leeg venster +Name[nn_NO]=Nytt tomt vindauge +Name[oc]=Fenèstra en blanc novèla +Name[pa_IN]=ਨਵੀਂ ਖਾਲੀ ਵਿੰਡੋ +Name[pl]=Nowe puste okno +Name[pt_BR]=Nova janela em branco +Name[pt_PT]=Nova janela em branco +Name[rm]=Nova fanestra vacue +Name[ro]=Fereastră nouă și goală +Name[ru]=Новое пустое окно +Name[sat]=ᱱᱟᱶᱟ ᱣᱤᱱᱰᱳ ᱵᱽᱨᱟᱣᱡᱚᱨ +Name[sc]=Ventana en blanc noa +Name[sco]=New Blank Window +Name[si]=නව හිස් කවුළුව +Name[sk]=Nové prázdné okno +Name[skr]=نویں خالی ونڈو +Name[sl]=Novo prazno okno +Name[son]=New Blank Window +Name[sq]=Dritare e Re e Pafajshme +Name[sr]=Нови празни прозор +Name[sv_SE]=Nytt tomt fönster +Name[szl]=New Blank Window +Name[ta]=New Blank Window +Name[te]=కొత్త ఖాళీ కిటికీ +Name[tg]=Равзанаи холӣ нав +Name[th]=หน้าต่างว่างเปล่าใหม่ +Name[tl]=New Blank Window +Name[tr]=Yeni boş pencere +Name[trs]=Bēntanâ huì nākàa +Name[uk]=Нове порожнє вікно +Name[ur]=New Blank Window +Name[uz]=New Blank Window +Name[vi]=Cửa sổ trống mới +Name[wo]=New Blank Window +Name[xh]=New Blank Window +Name[zh_CN]=新建空白窗口 +Name[zh_TW]=開新空白視窗 [Desktop Action new-private-window] -Name=Open a New Private Window Exec=zen --private-window %u +Name=New Private Window +Name[ach]=New Private Window +Name[af]=New Private Window +Name[an]=New Private Window +Name[ar]=نافذة خاصة جديدة +Name[ast]=New Private Window +Name[az]=New Private Window +Name[be]=Новае прыватнае акно +Name[bg]=Нов личен прозорец +Name[bn]=নতুন ব্যক্তিগত উইন্ডো +Name[bqi]=نیمدری سیخومی نۊ +Name[br]=Prenestr prevez nevez +Name[brx]=New Private Window +Name[bs]=Novi privatni prozor +Name[ca]=Finestra privada nova +Name[ca_valencia]=New Private Window +Name[cak]=K'ak'a' Ichinan Tzuwäch +Name[ckb]=New Private Window +Name[cs]=Nové anonymní okno +Name[cy]=Ffenestr Breifat Newydd +Name[da]=Nyt privat vindue +Name[de]=Neues privates Fenster +Name[dsb]=Nowe priwatne wokno +Name[el]=Νέο ιδιωτικό παράθυρο +Name[en_CA]=New Private Window +Name[en_GB]=New Private Window +Name[eo]=Nova privata fenestro +Name[es_AR]=Nueva ventana privada +Name[es_CL]=Nueva ventana privada +Name[es_ES]=Nueva ventana privada +Name[es_MX]=Nueva ventana privada +Name[et]=New Private Window +Name[eu]=Leiho pribatu berria +Name[fa]=پنجره ناشناس جدید +Name[ff]=New Private Window +Name[fi]=Uusi yksityinen ikkuna +Name[fr]=Nouvelle fenêtre privée +Name[fur]=Gnûf barcon privât +Name[fy_NL]=Nij priveefinster +Name[ga_IE]=New Private Window +Name[gd]=Uinneag phrìobhaideach ùr +Name[gl]=Nova xanela privada +Name[gn]=Ovetã ñemi pyahu +Name[gu_IN]=New Private Window +Name[he]=חלון פרטי חדש +Name[hi_IN]=New Private Window +Name[hr]=Novi privatni prozor +Name[hsb]=Nowe priwatne wokno +Name[hu]=Új privát ablak +Name[hy_AM]=Նոր գաղտնի պատուհան +Name[hye]=New Private Window +Name[ia]=Nove fenestra private +Name[id]=Jendela Mode Pribadi Baru +Name[is]=Nýr huliðsgluggi +Name[it]=Nuova finestra anonima +Name[ja]=新しいプライベートウィンドウ +Name[ka]=ახალი პირადი ფანჯარა +Name[kab]=Asfaylu amaynut n tunigin tusligt +Name[kk]=Жаңа жекелік терезе +Name[km]=បង្អួច​ឯកជន​ថ្មី +Name[kn]=New Private Window +Name[ko]=새 사생활 보호 창 +Name[lij]=Neuvo Barcon Privòu +Name[lo]=ວິນໂດສ່ວນຕົວໃໝ່ +Name[lt]=New Private Window +Name[ltg]=New Private Window +Name[lv]=Jauns privātais logs +Name[meh]=New Private Window +Name[mk]=New Private Window +Name[ml]=പുതിയ സ്വകാര്യ ജാലകം +Name[mr]=New Private Window +Name[ms]=New Private Window +Name[my]=New Private Window +Name[nb_NO]=Nytt privat vindu +Name[ne_NP]=नयाँ निजी सञ्झ्याल +Name[nl]=Nieuw privévenster +Name[nn_NO]=Nytt privat vindauge +Name[oc]=Fenèstra privada novèla +Name[pa_IN]=ਨਵੀਂ ਪ੍ਰਾਈਵੇਟ ਵਿੰਡੋ +Name[pl]=Nowe okno prywatne +Name[pt_BR]=Nova janela privativa +Name[pt_PT]=Nova janela privada +Name[rm]=Nova fanestra privata +Name[ro]=Fereastră privată nouă +Name[ru]=Новое приватное окно +Name[sat]=ᱱᱟᱶᱟ ᱱᱤᱡᱮᱨᱟᱜ ᱣᱤᱱᱰᱳ +Name[sc]=Ventana privada noa +Name[sco]=New Private Window +Name[si]=නව පෞද්. කවුළුව +Name[sk]=Nové súkromné okno +Name[skr]=نویں نجی ونڈو +Name[sl]=Novo zasebno okno +Name[son]=New Private Window +Name[sq]=Dritare e Re Private +Name[sr]=Нови приватни прозор +Name[sv_SE]=Nytt privat fönster +Name[szl]=New Private Window +Name[ta]=New Private Window +Name[te]=కొత్త ఆంతరంగిక కిటికీ +Name[tg]=Равзанаи хусусии нав +Name[th]=หน้าต่างส่วนตัวใหม่ +Name[tl]=New Private Window +Name[tr]=Yeni gizli pencere +Name[trs]=Bēntanâ huì nākàa +Name[uk]=Приватне вікно +Name[ur]=New Private Window +Name[uz]=New Private Window +Name[vi]=Cửa sổ riêng tư mới +Name[wo]=New Private Window +Name[xh]=New Private Window +Name[zh_CN]=新建隐私窗口 +Name[zh_TW]=開新隱私視窗 [Desktop Action profilemanager] -Name=Open the Profile Manager Exec=zen --ProfileManager %u +Name=Open Profile Manager +Name[ach]=Open Profile Manager +Name[af]=Open Profile Manager +Name[an]=Open Profile Manager +Name[ar]=افتح مدير الملف الشخصي +Name[ast]=Open Profile Manager +Name[az]=Open Profile Manager +Name[be]=Адкрыць менеджар профіляў +Name[bg]=Отваряне на мениджъра на профили +Name[bn]=Open Profile Manager +Name[bqi]=گۊشیڌن دؽوۉداری پوروفایل +Name[br]=Digeriñ an ardoer aeladoù +Name[brx]=Open Profile Manager +Name[bs]=Otvori Menadžera profila +Name[ca]=Obre el gestor de perfils +Name[ca_valencia]=Open Profile Manager +Name[cak]=Open Profile Manager +Name[ckb]=Open Profile Manager +Name[cs]=Otevřít Správce profilů +Name[cy]=Agorwch y Rheolwr Proffil +Name[da]=Åbn profilhåndtering +Name[de]=Profilverwaltung öffnen +Name[dsb]=Profilowy zastojnik wócyniś +Name[el]=Άνοιγμα Διαχείρισης προφίλ +Name[en_CA]=Open Profile Manager +Name[en_GB]=Open Profile Manager +Name[eo]=Malfermi administranton de profiloj +Name[es_AR]=Abrir administrador de perfiles +Name[es_CL]=Abrir administrador de perfiles +Name[es_ES]=Abrir administrador de perfiles +Name[es_MX]=Abrir administrador de perfiles +Name[et]=Open Profile Manager +Name[eu]=Ireki profilen kudeatzailea +Name[fa]=گشودن مدیر نمایه +Name[ff]=Open Profile Manager +Name[fi]=Avaa profiilien hallinta +Name[fr]=Ouvrir le gestionnaire de profils +Name[fur]=Vierç gjestôr profîi +Name[fy_NL]=Profylbehearder iepenje +Name[ga_IE]=Open Profile Manager +Name[gd]=Fosgail manaidsear nam pròifilean +Name[gl]=Abrir o xestor de perfís +Name[gn]=Embojuruja mba’ete ñangarekoha +Name[gu_IN]=Open Profile Manager +Name[he]=פתיחת מנהל הפרופילים +Name[hi_IN]=Open Profile Manager +Name[hr]=Otvori upravljač profila +Name[hsb]=Zrjadowak profilow wočinić +Name[hu]=Profilkezelő megnyitása +Name[hy_AM]=Բացեք պրոֆիլի կառավարիչը +Name[hye]=Open Profile Manager +Name[ia]=Aperir le gestor de profilo +Name[id]=Buka Pengelola Profil +Name[is]=Opna umsýslu notandasniða +Name[it]=Apri gestore profili +Name[ja]=プロファイルマネージャーを開く +Name[ka]=პროფილის მმართველის გახსნა +Name[kab]=Ldi amsefrak n umaɣnu +Name[kk]=Профильдер бақарушысын ашу +Name[km]=Open Profile Manager +Name[kn]=Open Profile Manager +Name[ko]=프로필 관리자 열기 +Name[lij]=Open Profile Manager +Name[lo]=ເປີດຕົວຈັດການໂປຣໄຟລ໌ +Name[lt]=Open Profile Manager +Name[ltg]=Open Profile Manager +Name[lv]=Atvērt profilu pārvaldnieku +Name[meh]=Open Profile Manager +Name[mk]=Open Profile Manager +Name[ml]=രൂപരേഖ മാനേചർ +Name[mr]=Open Profile Manager +Name[ms]=Open Profile Manager +Name[my]=Open Profile Manager +Name[nb_NO]=Åpne profilbehandler +Name[ne_NP]=Open Profile Manager +Name[nl]=Profielbeheerder openen +Name[nn_NO]=Opne profilhandsaming +Name[oc]=Dobrir lo gestionari de perfils +Name[pa_IN]=ਪਰੋਫ਼ਾਈਲ ਮੈਨੇਜਰ ਖੋਲ੍ਹੋ +Name[pl]=Menedżer profili +Name[pt_BR]=Abrir gerenciador de perfis +Name[pt_PT]=Abrir o Gestor de Perfis +Name[rm]=Avrir l'administraziun da profils +Name[ro]=Deschide managerul de profiluri +Name[ru]=Открыть менеджер профилей +Name[sat]=ᱢᱮᱫᱦᱟᱸ ᱢᱮᱱᱮᱡᱚᱨ ᱠᱷᱩᱞᱟᱹᱭ ᱢᱮ +Name[sc]=Aberi su gestore de profilos +Name[sco]=Open Profile Manager +Name[si]=පැතිකඩ කළමනාකරු අරින්න +Name[sk]=Otvoriť Správcu profilov +Name[skr]=پروفائل منیجر کھولو +Name[sl]=Odpri upravitelja profilov +Name[son]=Open Profile Manager +Name[sq]=Hapni Përgjegjës Profilesh +Name[sr]=Отворите управљач профила +Name[sv_SE]=Öppna Profilhanteraren +Name[szl]=Open Profile Manager +Name[ta]=Open Profile Manager +Name[te]=Open Profile Manager +Name[tg]=Кушодани мудири профилҳо +Name[th]=เปิดตัวจัดการโปรไฟล์ +Name[tl]=Open Profile Manager +Name[tr]=Profil yöneticisini aç +Name[trs]=Sa nīkāj ñu’ūnj nej perfî huā nì’nï̀nj ïn +Name[uk]=Відкрити менеджер профілів +Name[ur]=Open Profile Manager +Name[uz]=Open Profile Manager +Name[vi]=Mở trình quản lý hồ sơ +Name[wo]=Open Profile Manager +Name[xh]=Open Profile Manager +Name[zh_CN]=打开配置文件管理器 +Name[zh_TW]=開啟設定檔管理員 diff --git a/build/firefox-cache/l10n-last-commit-hash b/build/firefox-cache/l10n-last-commit-hash index 4024374c4..9b5d1a99d 100644 --- a/build/firefox-cache/l10n-last-commit-hash +++ b/build/firefox-cache/l10n-last-commit-hash @@ -1 +1 @@ -0b65b47ceee455b324e13114b5bc3a7033a8b2a5 \ No newline at end of file +73901ca17f4a2159dd4488cea8684e9abbfdcc89 \ No newline at end of file diff --git a/build/signing/public_key.der b/build/signing/public_key.der new file mode 100644 index 000000000..733d57508 Binary files /dev/null and b/build/signing/public_key.der differ diff --git a/build/windows/sign.ps1 b/build/windows/sign.ps1 index 2c935d776..b9301141c 100644 --- a/build/windows/sign.ps1 +++ b/build/windows/sign.ps1 @@ -4,6 +4,7 @@ param( [string][Parameter(Mandatory=$true)]$SignIdentity, + [string][Parameter(Mandatory=$true)]$SignIdentityIssuer, [string][Parameter(Mandatory=$true)]$GithubRunId ) @@ -26,6 +27,9 @@ mkdir windsign-temp -ErrorAction SilentlyContinue $env:SURFER_MOZCONFIG_ONLY="1" $env:SURFER_SIGNING_MODE="" +$env:SURFER_CERT_PATCH_ISSUER=$SignIdentityIssuer +$env:SURFER_CERT_PATCH_NAME=$SignIdentity + Start-Job -Name "DownloadGitl10n" -ScriptBlock { param($PWD) cd $PWD diff --git a/configs/common/mozconfig b/configs/common/mozconfig index 3d41bc83a..22b8e95c5 100644 --- a/configs/common/mozconfig +++ b/configs/common/mozconfig @@ -26,24 +26,33 @@ export MOZ_INCLUDE_SOURCE_INFO=1 ac_add_options --enable-application=browser -# Anything except 'SCCACHE_GHA_ENABLED == false' -if ! test "$SCCACHE_GHA_ENABLED" = "false"; then +if test "$SCCACHE_GHA_ENABLED" = "true"; then if test -x "$(command -v sccache)"; then ac_add_options --with-ccache=sccache elif test -f "$HOME/.mozbuild/sccache/sccache"; then ac_add_options --with-ccache="$HOME"/.mozbuild/sccache/sccache fi + + mk_add_options 'export RUSTC_WRAPPER=sccache' + mk_add_options 'export CCACHE_CPP2=yes' fi -# add safe browsing key if it exists on a file +# add API keys if it exists on a file if test -f "$HOME/.zen-keys/safebrowsing.dat"; then ac_add_options --with-google-safebrowsing-api-keyfile="$HOME/.zen-keys/safebrowsing.dat" fi +if test -f "$HOME/.zen-keys/mozilla.dat"; then + ac_add_options --with-mozilla-api-keyfile="$HOME/.zen-keys/mozilla.dat" +fi + +if test -f "$HOME/.zen-keys/google_location_service.dat"; then + ac_add_options --with-google-location-service-api-keyfile="$HOME/.zen-keys/google_location_service.dat" +fi + if test "$ZEN_RELEASE"; then - # TODO: Make this successful in builds - # ac_add_options --enable-clang-plugin + ac_add_options --enable-clang-plugin ac_add_options --enable-bootstrap=-sccache ac_add_options --enable-optimize @@ -89,8 +98,6 @@ if test "$ZEN_RELEASE"; then ac_add_options --enable-replace-malloc fi -ac_add_options --enable-unverified-updates - ac_add_options --enable-jxl ac_add_options --with-unsigned-addon-scopes=app,system diff --git a/configs/linux/mozconfig b/configs/linux/mozconfig index b15dcd399..9816e7580 100644 --- a/configs/linux/mozconfig +++ b/configs/linux/mozconfig @@ -18,8 +18,14 @@ if test "$ZEN_RELEASE"; then # Enable Profile Guided Optimization if ! test "$ZEN_GA_DISABLE_PGO"; then - export MOZ_PGO=1 - ac_add_options MOZ_PGO=1 + if test "$ZEN_GA_GENERATE_PROFILE"; then + mk_add_options "export MOZ_AUTOMATION_PACKAGE_GENERATED_SOURCES=0" + ac_add_options --enable-profile-generate=cross + else + ac_add_options --enable-profile-use=cross + ac_add_options --with-pgo-profile-path="$(echo ~)/artifact/merged.profdata" + ac_add_options --with-pgo-jarlog="$(echo ~)/artifact/en-US.log" + fi fi elif test "$SURFER_COMPAT" = "aarch64"; then ac_add_options --target=aarch64-linux-gnu diff --git a/configs/macos/mozconfig b/configs/macos/mozconfig index 317f6b4e8..a4c359bc0 100644 --- a/configs/macos/mozconfig +++ b/configs/macos/mozconfig @@ -8,18 +8,15 @@ ac_add_options --disable-dmd ac_add_options --enable-eme=widevine if test "$ZEN_RELEASE"; then - # override LTO settings - # TODO: Dont use LTO for now, it's causing a lot of issues - export MOZ_LTO=cross,thin - ac_add_options --enable-lto=cross,thin -fi - -if test "$ZEN_RELEASE"; then - if test "$ZEN_GA_DISABLE_PGO"; then - export ZEN_DUMMY=1 - else - export MOZ_PGO=1 - ac_add_options MOZ_PGO=1 + if ! test "$ZEN_GA_DISABLE_PGO"; then + if test "$ZEN_GA_GENERATE_PROFILE"; then + mk_add_options "export MOZ_AUTOMATION_PACKAGE_GENERATED_SOURCES=0" + ac_add_options --enable-profile-generate=cross + else + ac_add_options --enable-profile-use=cross + ac_add_options --with-pgo-profile-path="$(echo ~)/artifact/merged.profdata" + ac_add_options --with-pgo-jarlog="$(echo ~)/artifact/en-US.log" + fi fi fi @@ -28,9 +25,12 @@ if test "$SURFER_COMPAT" = "x86_64"; then if test "$ZEN_RELEASE"; then ac_add_options --enable-wasm-avx + + # override LTO settings + export MOZ_LTO=cross,thin + ac_add_options --enable-lto=cross,thin fi else - ac_add_options --enable-clang-plugin ac_add_options --target=aarch64-apple-darwin if test "$ZEN_RELEASE"; then diff --git a/configs/windows/mozconfig b/configs/windows/mozconfig index ffe7892bf..065ed1291 100644 --- a/configs/windows/mozconfig +++ b/configs/windows/mozconfig @@ -22,13 +22,6 @@ if test "$ZEN_CROSS_COMPILING"; then fi fi -# We wrongly detect ccache on windows, which leads to build failures. -# This line should be removed once the detection is fixed. -ac_add_options --without-ccache - -ac_add_options --disable-maintenance-service -ac_add_options --disable-bits-download - if test "$SURFER_COMPAT" = "x86_64"; then ac_add_options --target=x86_64-pc-windows-msvc ac_add_options --enable-eme=widevine,wmfcdm diff --git a/crowdin.yml b/crowdin.yml index fe20b9a6f..ad29d0395 100644 --- a/crowdin.yml +++ b/crowdin.yml @@ -22,3 +22,5 @@ files: translation: browser/browser/zen-folders.ftl - source: en-US/browser/browser/zen-library.ftl translation: browser/browser/zen-library.ftl + - source: en-US/browser/browser/zen-boosts.ftl + translation: browser/browser/zen-boosts.ftl diff --git a/locales/ar/browser/browser/preferences/zen-preferences.ftl b/locales/ar/browser/browser/preferences/zen-preferences.ftl index 14e96263f..c8d4332a5 100644 --- a/locales/ar/browser/browser/preferences/zen-preferences.ftl +++ b/locales/ar/browser/browser/preferences/zen-preferences.ftl @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = قلب DOM zen-devtools-toggle-accessibility-shortcut = قلب إمكانية الوصول zen-close-all-unpinned-tabs-shortcut = إغلاق جميع علامات التبويب الغير مثبتة zen-new-unsynced-window-shortcut = New Unsynced Window +zen-duplicate-tab-shortcut = Duplicate Tab diff --git a/locales/ar/browser/browser/zen-general.ftl b/locales/ar/browser/browser/zen-general.ftl index 0ceabb83e..688288f1b 100644 --- a/locales/ar/browser/browser/zen-general.ftl +++ b/locales/ar/browser/browser/zen-general.ftl @@ -6,9 +6,9 @@ zen-panel-ui-current-profile-text = الملف الشخصي الحالي unified-extensions-description = تستخدم الإضافات لجلب المزيد من الوظائف الإضافية إلى { -brand-short-name }. tab-context-zen-reset-pinned-tab = .label = - {$isEssential -> - [true] إعادة تعيين علامة التبويب الأساسية - *[false] إعادة تعيين التبويب المثبت + { $isEssential -> + [true] إعادة تعيين علامة التبويب الأساسية + *[false] إعادة تعيين التبويب المثبت } .accesskey = ر tab-context-zen-add-essential = @@ -20,10 +20,11 @@ tab-context-zen-remove-essential = .accesskey = R tab-context-zen-replace-pinned-url-with-current = .label = - {$isEssential -> + { $isEssential -> [true] استبدل الرابط الأساسي بـ - *[false] استبدل الرابط المثبت بـ - الحالي + *[false] + استبدل الرابط المثبت بـ + الحالي } .accesskey = C tab-context-zen-edit-title = @@ -51,6 +52,7 @@ zen-pinned-tab-replaced = Pinned tab URL has been replaced with the current URL. zen-tabs-renamed = تم تغيير اسم التبويب بنجاح! zen-background-tab-opened-toast = تم فتح علامة تبويب خلفية جديدة! zen-workspace-renamed-toast = تم تغيير اسم مساحة العمل بنجاح! +zen-split-view-limit-toast = Can't add more panels to the split view! zen-toggle-compact-mode-button = .label = الوضع المدمج .tooltiptext = تبديل الوضع المدمج diff --git a/locales/ar/browser/browser/zen-menubar.ftl b/locales/ar/browser/browser/zen-menubar.ftl index ccfb9f5ba..b1f8ed00d 100644 --- a/locales/ar/browser/browser/zen-menubar.ftl +++ b/locales/ar/browser/browser/zen-menubar.ftl @@ -4,7 +4,7 @@ zen-menubar-toggle-pinned-tabs = .label = - {$pinnedAreCollapsed -> + { $pinnedAreCollapsed -> [true] توسيع علامات التبويب المثبتة *[false] طي علامات التبويب المثبتة } diff --git a/locales/ar/browser/browser/zen-split-view.ftl b/locales/ar/browser/browser/zen-split-view.ftl index 6539c2f0b..281cc5cfd 100644 --- a/locales/ar/browser/browser/zen-split-view.ftl +++ b/locales/ar/browser/browser/zen-split-view.ftl @@ -5,8 +5,9 @@ tab-zen-split-tabs = .label = { $tabCount -> - [1] تبويب تقسيم (عدة علامات تبويب محددة مطلوبة) - *[other] تقسيم { $tabCount } علامات التبويب + [-1] Split out tab + [1] Add split view... + *[other] Join { $tabCount } Tabs } .accesskey = س zen-split-link = diff --git a/locales/ar/browser/browser/zen-vertical-tabs.ftl b/locales/ar/browser/browser/zen-vertical-tabs.ftl index 151c53648..ab6a38a17 100644 --- a/locales/ar/browser/browser/zen-vertical-tabs.ftl +++ b/locales/ar/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = إخفاء كليهما .accesskey = خ +zen-toolbar-context-move-to-folder = + .label = Move to Folder... + .accesskey = M zen-toolbar-context-new-folder = .label = مجلّد جديد .accesskey = ن @@ -26,19 +29,19 @@ sidebar-zen-create-new = .label = إنشاء جديد... tabbrowser-unload-tab-button = .tooltiptext = - {$tabCount -> + { $tabCount -> [one] تفريغ والتبديل إلى علامة التبويب *[other] تفريغ { $tabCount } علامات التبويب والتبديل إلى الأولى } tabbrowser-reset-pin-button = .tooltiptext = - {$tabCount -> + { $tabCount -> [one] إعادة تعيين علامة التبويب وتثبيتها - *[other] إعادة تعيين وتثبيت { $tabCount} + *[other] إعادة تعيين وتثبيت { $tabCount } } zen-tab-sublabel = - {$tabSubtitle -> + { $tabSubtitle -> [zen-default-pinned] العودة إلى الرابط المثبت [zen-default-pinned-cmd] فصل عن علامة التبويب المثبتة - *[other] { $tabSubtitle} + *[other] { $tabSubtitle } } diff --git a/locales/bg/browser/browser/preferences/zen-preferences.ftl b/locales/bg/browser/browser/preferences/zen-preferences.ftl index da9d313b4..ed26f72b8 100644 --- a/locales/bg/browser/browser/preferences/zen-preferences.ftl +++ b/locales/bg/browser/browser/preferences/zen-preferences.ftl @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = Toggle DOM zen-devtools-toggle-accessibility-shortcut = Toggle Accessibility zen-close-all-unpinned-tabs-shortcut = Close All Unpinned Tabs zen-new-unsynced-window-shortcut = New Unsynced Window +zen-duplicate-tab-shortcut = Duplicate Tab diff --git a/locales/bg/browser/browser/zen-general.ftl b/locales/bg/browser/browser/zen-general.ftl index ead213776..3e7defe97 100644 --- a/locales/bg/browser/browser/zen-general.ftl +++ b/locales/bg/browser/browser/zen-general.ftl @@ -50,6 +50,7 @@ zen-pinned-tab-replaced = Адресът на закачения раздел б zen-tabs-renamed = Разделът беше успешно преименуван! zen-background-tab-opened-toast = Отворен е нов раздел на заден план! zen-workspace-renamed-toast = Работното пространство беше преименувано успешно! +zen-split-view-limit-toast = Can't add more panels to the split view! zen-toggle-compact-mode-button = .label = Компактен изглед .tooltiptext = Превключи компактен режим diff --git a/locales/bg/browser/browser/zen-split-view.ftl b/locales/bg/browser/browser/zen-split-view.ftl index e7cb9c67b..e09ec8107 100644 --- a/locales/bg/browser/browser/zen-split-view.ftl +++ b/locales/bg/browser/browser/zen-split-view.ftl @@ -5,8 +5,9 @@ tab-zen-split-tabs = .label = { $tabCount -> - [1] Split Tab (multiple selected tabs needed) - *[other] Split { $tabCount } Tabs + [-1] Split out tab + [1] Add split view... + *[other] Join { $tabCount } Tabs } .accesskey = S zen-split-link = diff --git a/locales/bg/browser/browser/zen-vertical-tabs.ftl b/locales/bg/browser/browser/zen-vertical-tabs.ftl index ff8a3775c..9989c7666 100644 --- a/locales/bg/browser/browser/zen-vertical-tabs.ftl +++ b/locales/bg/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = Hide both .accesskey = H +zen-toolbar-context-move-to-folder = + .label = Move to Folder... + .accesskey = M zen-toolbar-context-new-folder = .label = New Folder .accesskey = N diff --git a/locales/bs/browser/browser/preferences/zen-preferences.ftl b/locales/bs/browser/browser/preferences/zen-preferences.ftl index da9d313b4..ed26f72b8 100644 --- a/locales/bs/browser/browser/preferences/zen-preferences.ftl +++ b/locales/bs/browser/browser/preferences/zen-preferences.ftl @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = Toggle DOM zen-devtools-toggle-accessibility-shortcut = Toggle Accessibility zen-close-all-unpinned-tabs-shortcut = Close All Unpinned Tabs zen-new-unsynced-window-shortcut = New Unsynced Window +zen-duplicate-tab-shortcut = Duplicate Tab diff --git a/locales/bs/browser/browser/zen-general.ftl b/locales/bs/browser/browser/zen-general.ftl index 21d9a7475..709b4575f 100644 --- a/locales/bs/browser/browser/zen-general.ftl +++ b/locales/bs/browser/browser/zen-general.ftl @@ -50,6 +50,7 @@ zen-pinned-tab-replaced = Pinned tab URL has been replaced with the current URL! zen-tabs-renamed = Tab has been successfully renamed! zen-background-tab-opened-toast = New background tab opened! zen-workspace-renamed-toast = Workspace has been successfully renamed! +zen-split-view-limit-toast = Can't add more panels to the split view! zen-toggle-compact-mode-button = .label = Compact Mode .tooltiptext = Toggle Compact Mode diff --git a/locales/bs/browser/browser/zen-split-view.ftl b/locales/bs/browser/browser/zen-split-view.ftl index 10593eed9..4430fab34 100644 --- a/locales/bs/browser/browser/zen-split-view.ftl +++ b/locales/bs/browser/browser/zen-split-view.ftl @@ -5,8 +5,9 @@ tab-zen-split-tabs = .label = { $tabCount -> - [1] Split Tab (multiple selected tabs needed) - *[other] Split { $tabCount } Tabs + [-1] Split out tab + [1] Add split view... + *[other] Join { $tabCount } Tabs } .accesskey = S zen-split-link = diff --git a/locales/bs/browser/browser/zen-vertical-tabs.ftl b/locales/bs/browser/browser/zen-vertical-tabs.ftl index ff8a3775c..9989c7666 100644 --- a/locales/bs/browser/browser/zen-vertical-tabs.ftl +++ b/locales/bs/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = Hide both .accesskey = H +zen-toolbar-context-move-to-folder = + .label = Move to Folder... + .accesskey = M zen-toolbar-context-new-folder = .label = New Folder .accesskey = N diff --git a/locales/ca/browser/browser/preferences/zen-preferences.ftl b/locales/ca/browser/browser/preferences/zen-preferences.ftl index 0c7d866c6..4b9b2c758 100644 --- a/locales/ca/browser/browser/preferences/zen-preferences.ftl +++ b/locales/ca/browser/browser/preferences/zen-preferences.ftl @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = Commuta el DOM zen-devtools-toggle-accessibility-shortcut = Commuta l'accessibilitat zen-close-all-unpinned-tabs-shortcut = Tanca totes les pestanyes no fixades zen-new-unsynced-window-shortcut = Nova finestra en blanc +zen-duplicate-tab-shortcut = Duplica la pestanya diff --git a/locales/ca/browser/browser/zen-general.ftl b/locales/ca/browser/browser/zen-general.ftl index ea3ccbf70..d50b46e7c 100644 --- a/locales/ca/browser/browser/zen-general.ftl +++ b/locales/ca/browser/browser/zen-general.ftl @@ -14,7 +14,7 @@ tab-context-zen-reset-pinned-tab = tab-context-zen-add-essential = .label = Afegeix als essencials .accesskey = E -tab-context-zen-add-essential-badge = { $num } / { $max } espais ocupats +tab-context-zen-add-essential-badge = { $num } / { $max } tab-context-zen-remove-essential = .label = Elimina dels essencials .accesskey = R @@ -50,6 +50,7 @@ zen-pinned-tab-replaced = L'URL de la pestanya fixada s'ha substituït per l'URL zen-tabs-renamed = S'ha canviat el nom de la pestanya correctament zen-background-tab-opened-toast = S'ha obert una nova pestanya de fons zen-workspace-renamed-toast = S'ha canviat el nom de l'espai de treball correctament +zen-split-view-limit-toast = No es poden afegir més panells a la vista dividida! zen-toggle-compact-mode-button = .label = Mode compacte .tooltiptext = Commuta el mode compacte diff --git a/locales/ca/browser/browser/zen-split-view.ftl b/locales/ca/browser/browser/zen-split-view.ftl index 3da537f6b..5a2953f5a 100644 --- a/locales/ca/browser/browser/zen-split-view.ftl +++ b/locales/ca/browser/browser/zen-split-view.ftl @@ -5,8 +5,9 @@ tab-zen-split-tabs = .label = { $tabCount -> - [1] Pestanya dividida (calen diverses pestanyes seleccionades) - *[other] Divideix { $tabCount } pestanyes + [-1] Pestanya dividida + [1] Afegeix una vista dividida... + *[other] Uneix { $tabCount } pestanyes } .accesskey = S zen-split-link = diff --git a/locales/ca/browser/browser/zen-vertical-tabs.ftl b/locales/ca/browser/browser/zen-vertical-tabs.ftl index 868efd1b5..d8932f8a9 100644 --- a/locales/ca/browser/browser/zen-vertical-tabs.ftl +++ b/locales/ca/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = Amaga les dues .accesskey = H +zen-toolbar-context-move-to-folder = + .label = Mou a la carpeta... + .accesskey = M zen-toolbar-context-new-folder = .label = Carpeta nova .accesskey = N diff --git a/locales/cs/browser/browser/preferences/zen-preferences.ftl b/locales/cs/browser/browser/preferences/zen-preferences.ftl index 1b46b53ff..fdacf7348 100644 --- a/locales/cs/browser/browser/preferences/zen-preferences.ftl +++ b/locales/cs/browser/browser/preferences/zen-preferences.ftl @@ -43,11 +43,11 @@ category-zen-workspaces = .tooltiptext = { pane-zen-tabs-title } pane-settings-workspaces-title = Pracoviště zen-tabs-select-recently-used-on-close = - .label = When closing a tab, switch to the most recently used tab instead of the next tab + .label = Při zavření panelu přepnout na naposledy použitý panel namísto dalšího panelu zen-tabs-close-on-back-with-no-history = .label = Zavřít panel a přepnout na jeho nadřazený panel (nebo naposledy použitý panel), když se vracíte zpět bez historie zen-settings-workspaces-sync-unpinned-tabs = - .label = Sync only pinned tabs in workspaces + .label = Synchronizovat pouze připnuté panely v pracovních prostorách zen-tabs-cycle-by-attribute = .label = Ctrl+Tab zahrnuje Essentials v pracovních panelech zen-tabs-cycle-ignore-pending-tabs = @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = Přepnout DOM zen-devtools-toggle-accessibility-shortcut = Přepnout přístupnost zen-close-all-unpinned-tabs-shortcut = Zavřít všechny nepřipnuté panely zen-new-unsynced-window-shortcut = Nové nesynchronizované okno +zen-duplicate-tab-shortcut = Duplikovat panel diff --git a/locales/cs/browser/browser/zen-general.ftl b/locales/cs/browser/browser/zen-general.ftl index a7afe69a1..ca6132599 100644 --- a/locales/cs/browser/browser/zen-general.ftl +++ b/locales/cs/browser/browser/zen-general.ftl @@ -7,8 +7,8 @@ unified-extensions-description = Rozšíření slouží k přidání dalších f tab-context-zen-reset-pinned-tab = .label = { $isEssential -> - [true] Reset Essential Tab - *[false] Reset Pinned Tab + [true] Resetovat Essential panel + *[false] Resetovat připnutý panel } .accesskey = R tab-context-zen-add-essential = @@ -21,8 +21,8 @@ tab-context-zen-remove-essential = tab-context-zen-replace-pinned-url-with-current = .label = { $isEssential -> - [true] Replace Essential URL with Current - *[false] Replace Pinned URL with Current + [true] Nahradit Essential URL aktuální + *[false] Nahradit připnutou URL aktuální } .accesskey = C tab-context-zen-edit-title = @@ -41,7 +41,7 @@ pictureinpicture-minimize-btn = .tooltip = Minimalizovat zen-panel-ui-gradient-generator-custom-color = Vlastní barva zen-copy-current-url-confirmation = URL adresa byla zkopírována! -zen-copy-current-url-as-markdown-confirmation = Copied current URL as Markdown! +zen-copy-current-url-as-markdown-confirmation = Kopírovat aktuální URL jako Markdown! zen-general-cancel-label = .label = Zrušit zen-general-confirm = @@ -50,6 +50,7 @@ zen-pinned-tab-replaced = Připnutá URL adresa panelu byla nahrazena aktuální zen-tabs-renamed = Panel byl úspěšně přejmenován! zen-background-tab-opened-toast = Nový panel na pozadí byl otevřen! zen-workspace-renamed-toast = Pracovní prostor byl úspěšně přejmenován! +zen-split-view-limit-toast = Do rozděleného zobrazení nelze přidat více panelů! zen-toggle-compact-mode-button = .label = Kompaktní režim .tooltiptext = Přepnout kompaktní režim @@ -123,4 +124,4 @@ zen-window-sync-migration-dialog-message = Zen nyní synchronizuje okna na stejn zen-window-sync-migration-dialog-learn-more = Zjistit více zen-window-sync-migration-dialog-accept = Rozumím zen-appmenu-new-blank-window = - .label = New blank window + .label = Nové prázdné okno diff --git a/locales/cs/browser/browser/zen-menubar.ftl b/locales/cs/browser/browser/zen-menubar.ftl index 4f09f176f..738649024 100644 --- a/locales/cs/browser/browser/zen-menubar.ftl +++ b/locales/cs/browser/browser/zen-menubar.ftl @@ -19,4 +19,4 @@ zen-menubar-appearance-light = zen-menubar-appearance-dark = .label = Tmavý zen-menubar-new-blank-window = - .label = New Blank Window + .label = Nové prázdné okno diff --git a/locales/cs/browser/browser/zen-split-view.ftl b/locales/cs/browser/browser/zen-split-view.ftl index 8b807444f..c6f4f5e07 100644 --- a/locales/cs/browser/browser/zen-split-view.ftl +++ b/locales/cs/browser/browser/zen-split-view.ftl @@ -5,11 +5,9 @@ tab-zen-split-tabs = .label = { $tabCount -> - [1] Rozdělit kartu (je třeba více vybraných karet) - [2] Rozdělit { $tabCount } karty - [3] Rozdělit { $tabCount } karty - [4] Rozdělit { $tabCount } karty - *[other] Rozdělit { $tabCount } karet + [-1] Split out tab + [1] Add split view... + *[other] Join { $tabCount } Tabs } .accesskey = S zen-split-link = diff --git a/locales/cs/browser/browser/zen-vertical-tabs.ftl b/locales/cs/browser/browser/zen-vertical-tabs.ftl index a76c866a5..bfeae9159 100644 --- a/locales/cs/browser/browser/zen-vertical-tabs.ftl +++ b/locales/cs/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = Skrýt obojí .accesskey = H +zen-toolbar-context-move-to-folder = + .label = Přesunout do složky... + .accesskey = M zen-toolbar-context-new-folder = .label = Nová složka .accesskey = N diff --git a/locales/cs/browser/browser/zen-workspaces.ftl b/locales/cs/browser/browser/zen-workspaces.ftl index 140984833..529d3514c 100644 --- a/locales/cs/browser/browser/zen-workspaces.ftl +++ b/locales/cs/browser/browser/zen-workspaces.ftl @@ -10,7 +10,7 @@ zen-panel-ui-workspaces-create = zen-panel-ui-folder-create = .label = Vytvořit složku zen-panel-ui-live-folder-create = - .label = Live Folder + .label = Živá složka zen-panel-ui-new-empty-split = .label = Nové rozdělení zen-workspaces-panel-context-delete = @@ -25,7 +25,7 @@ zen-workspaces-panel-context-default-profile = zen-workspaces-panel-unload = .label = Uspat prostor zen-workspaces-panel-unload-others = - .label = Unload All Other Spaces + .label = Uvolnit všechny ostatní prostory zen-workspaces-how-to-reorder-title = Jak změnit pořadí prostorů zen-workspaces-how-to-reorder-desc = Pořadí prostorů změníte přetažením jejich ikon dole v postranním panelu zen-workspaces-change-theme = diff --git a/locales/cy/browser/browser/preferences/zen-preferences.ftl b/locales/cy/browser/browser/preferences/zen-preferences.ftl index 541c1735c..080a9aa69 100644 --- a/locales/cy/browser/browser/preferences/zen-preferences.ftl +++ b/locales/cy/browser/browser/preferences/zen-preferences.ftl @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = Toglo DOM zen-devtools-toggle-accessibility-shortcut = Toglo Hygyrchedd zen-close-all-unpinned-tabs-shortcut = Cau Pob Tab Heb ei Binio zen-new-unsynced-window-shortcut = Ffenestr Wag Newydd +zen-duplicate-tab-shortcut = Duplicate Tab diff --git a/locales/cy/browser/browser/zen-general.ftl b/locales/cy/browser/browser/zen-general.ftl index a4f31b20b..aa8a8074e 100644 --- a/locales/cy/browser/browser/zen-general.ftl +++ b/locales/cy/browser/browser/zen-general.ftl @@ -22,7 +22,7 @@ tab-context-zen-replace-pinned-url-with-current = .label = { $isEssential -> [true] Amnewid URL Hanfodol gyda'r Cyfredol - *[false] Amnewid URL wedi'i binio gyda'r Cyfredol + *[false] Amnewid URL wedi'i binio gyda'r Cyfredol } .accesskey = P tab-context-zen-edit-title = @@ -50,6 +50,7 @@ zen-pinned-tab-replaced = Mae URL y tab wedi'i binio wedi'i newid i'r URL gyfred zen-tabs-renamed = Mae'r tab wedi cael ei ailenwi'n llwyddiannus! zen-background-tab-opened-toast = Tab cefndir newydd wedi'i agor! zen-workspace-renamed-toast = Mae'r Man Gwaith wedi cael ei ailenwi'n llwyddiannus! +zen-split-view-limit-toast = Can't add more panels to the split view! zen-toggle-compact-mode-button = .label = Modd Cryno .tooltiptext = Togglo Modd Cryno diff --git a/locales/cy/browser/browser/zen-split-view.ftl b/locales/cy/browser/browser/zen-split-view.ftl index 2c1ac6ee6..e0aa8889e 100644 --- a/locales/cy/browser/browser/zen-split-view.ftl +++ b/locales/cy/browser/browser/zen-split-view.ftl @@ -5,8 +5,9 @@ tab-zen-split-tabs = .label = { $tabCount -> - [1] Tab Hollt (angen sawl tab wedi'u dewis) - *[other] Hollti { $tabCount } Tab + [-1] Split out tab + [1] Add split view... + *[other] Join { $tabCount } Tabs } .accesskey = H zen-split-link = diff --git a/locales/cy/browser/browser/zen-vertical-tabs.ftl b/locales/cy/browser/browser/zen-vertical-tabs.ftl index 27fb40f5c..688e6aa86 100644 --- a/locales/cy/browser/browser/zen-vertical-tabs.ftl +++ b/locales/cy/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = Cuddio'r ddau .accesskey = C +zen-toolbar-context-move-to-folder = + .label = Move to Folder... + .accesskey = M zen-toolbar-context-new-folder = .label = Ffolder Newydd .accesskey = N diff --git a/locales/cy/browser/browser/zen-workspaces.ftl b/locales/cy/browser/browser/zen-workspaces.ftl index 30bc974eb..28f2adf99 100644 --- a/locales/cy/browser/browser/zen-workspaces.ftl +++ b/locales/cy/browser/browser/zen-workspaces.ftl @@ -2,7 +2,7 @@ # 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/. -zen-panel-ui-workspaces-text = Gofodau Gwaith +zen-panel-ui-workspaces-text = Gofodau zen-panel-ui-spaces-label = .label = Gofodau zen-panel-ui-workspaces-create = @@ -10,7 +10,7 @@ zen-panel-ui-workspaces-create = zen-panel-ui-folder-create = .label = Creu Ffolder zen-panel-ui-live-folder-create = - .label = Live Folder + .label = Ffolder Byw zen-panel-ui-new-empty-split = .label = Hollt Newydd zen-workspaces-panel-context-delete = @@ -25,7 +25,7 @@ zen-workspaces-panel-context-default-profile = zen-workspaces-panel-unload = .label = Dadlwytho Gofod zen-workspaces-panel-unload-others = - .label = Unload All Other Spaces + .label = Dadlwytho Pob Gofod Arall zen-workspaces-how-to-reorder-title = Sut i aildrefnu gofodau zen-workspaces-how-to-reorder-desc = Llusgwch yr eiconau gofod ar waelod y bar ochr i'w haildrefnu zen-workspaces-change-theme = diff --git a/locales/da/browser/browser/preferences/zen-preferences.ftl b/locales/da/browser/browser/preferences/zen-preferences.ftl index 8d0f4e290..c710dc69c 100644 --- a/locales/da/browser/browser/preferences/zen-preferences.ftl +++ b/locales/da/browser/browser/preferences/zen-preferences.ftl @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = Vis/skjul DOM zen-devtools-toggle-accessibility-shortcut = Vis/skjul Tilgængelighed zen-close-all-unpinned-tabs-shortcut = Close All Unpinned Tabs zen-new-unsynced-window-shortcut = New Unsynced Window +zen-duplicate-tab-shortcut = Duplicate Tab diff --git a/locales/da/browser/browser/zen-general.ftl b/locales/da/browser/browser/zen-general.ftl index 6f835c8c7..b8181d220 100644 --- a/locales/da/browser/browser/zen-general.ftl +++ b/locales/da/browser/browser/zen-general.ftl @@ -50,6 +50,7 @@ zen-pinned-tab-replaced = Den fastgjorte fane-URL blev erstattet med den aktuell zen-tabs-renamed = Fanen blev omdøbt! zen-background-tab-opened-toast = Ny baggrundsfane åbnet! zen-workspace-renamed-toast = Arbejdsområde blev omdøbt! +zen-split-view-limit-toast = Can't add more panels to the split view! zen-toggle-compact-mode-button = .label = Kompakt tilstand .tooltiptext = Kompakt tilstand til/fra diff --git a/locales/da/browser/browser/zen-split-view.ftl b/locales/da/browser/browser/zen-split-view.ftl index 80323f2b9..5dd6024ab 100644 --- a/locales/da/browser/browser/zen-split-view.ftl +++ b/locales/da/browser/browser/zen-split-view.ftl @@ -5,8 +5,9 @@ tab-zen-split-tabs = .label = { $tabCount -> - [1] Opdel fane (flere faner skal vælges) - *[other] Opdel { $tabCount } faner + [-1] Split out tab + [1] Add split view... + *[other] Join { $tabCount } Tabs } .accesskey = S zen-split-link = diff --git a/locales/da/browser/browser/zen-vertical-tabs.ftl b/locales/da/browser/browser/zen-vertical-tabs.ftl index 80af30f7f..77325e82f 100644 --- a/locales/da/browser/browser/zen-vertical-tabs.ftl +++ b/locales/da/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = Skjul begge .accesskey = H +zen-toolbar-context-move-to-folder = + .label = Move to Folder... + .accesskey = M zen-toolbar-context-new-folder = .label = Ny mappe .accesskey = N diff --git a/locales/de/browser/browser/preferences/zen-preferences.ftl b/locales/de/browser/browser/preferences/zen-preferences.ftl index e17936377..86aff9c81 100644 --- a/locales/de/browser/browser/preferences/zen-preferences.ftl +++ b/locales/de/browser/browser/preferences/zen-preferences.ftl @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = DOM umschalten zen-devtools-toggle-accessibility-shortcut = Barrierefreiheit umschalten zen-close-all-unpinned-tabs-shortcut = Alle nicht angehefteten Tabs schließen zen-new-unsynced-window-shortcut = Neues leeres Fenster +zen-duplicate-tab-shortcut = Tab duplizieren diff --git a/locales/de/browser/browser/zen-general.ftl b/locales/de/browser/browser/zen-general.ftl index ad0ad3fce..66b8d4849 100644 --- a/locales/de/browser/browser/zen-general.ftl +++ b/locales/de/browser/browser/zen-general.ftl @@ -50,6 +50,7 @@ zen-pinned-tab-replaced = Die URL des angehefteten Tabs wurde aktualisiert! zen-tabs-renamed = Tab umbenannt! zen-background-tab-opened-toast = Neuer Tab im Hintergrund geöffnet! zen-workspace-renamed-toast = Arbeitsbereich umbenannt! +zen-split-view-limit-toast = Diese Split View kann keine weiteren Panels aufnehmen! zen-toggle-compact-mode-button = .label = Kompakter Modus .tooltiptext = Kompakten Modus umschalten diff --git a/locales/de/browser/browser/zen-split-view.ftl b/locales/de/browser/browser/zen-split-view.ftl index d466fbbd5..d230a24cb 100644 --- a/locales/de/browser/browser/zen-split-view.ftl +++ b/locales/de/browser/browser/zen-split-view.ftl @@ -5,9 +5,9 @@ tab-zen-split-tabs = .label = { $tabCount -> - [-1] Aufteilung aufheben - [1] Tab aufteilen (wähle mehrere Tabs aus) - *[other] { $tabCount } Tabs aufteilen + [-1] Tab abtrennen + [1] Split View hinzufügen... + *[other] { $tabCount } Tabs zusammenführen } .accesskey = S zen-split-link = diff --git a/locales/de/browser/browser/zen-vertical-tabs.ftl b/locales/de/browser/browser/zen-vertical-tabs.ftl index 4b38283b1..633973af8 100644 --- a/locales/de/browser/browser/zen-vertical-tabs.ftl +++ b/locales/de/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = Beides ausblenden .accesskey = H +zen-toolbar-context-move-to-folder = + .label = In Ordner verschieben... + .accesskey = M zen-toolbar-context-new-folder = .label = Neuer Ordner .accesskey = N diff --git a/locales/el/browser/browser/preferences/zen-preferences.ftl b/locales/el/browser/browser/preferences/zen-preferences.ftl index 968896da6..d46b987d2 100644 --- a/locales/el/browser/browser/preferences/zen-preferences.ftl +++ b/locales/el/browser/browser/preferences/zen-preferences.ftl @@ -43,11 +43,11 @@ category-zen-workspaces = .tooltiptext = { pane-zen-tabs-title } pane-settings-workspaces-title = Χώροι Εργασίας zen-tabs-select-recently-used-on-close = - .label = When closing a tab, switch to the most recently used tab instead of the next tab + .label = Στο κλείσιμο καρτέλας, αλλαγή στην πιο πρόσφατα χρησιμοποιημένη καρτέλα αντί για την επόμενη καρτέλα zen-tabs-close-on-back-with-no-history = .label = Κλείσιμο καρτέλας και αλλαγή στην καρτέλα ιδιοκτήτη (ή στην πιο πρόσφατα χρησιμοποιούμενη καρτέλα) όταν επιστρέφεις χωρίς ιστορικό zen-settings-workspaces-sync-unpinned-tabs = - .label = Sync only pinned tabs in workspaces + .label = Συγχρονισμός μόνο καρφιτσωμένων καρτελών στους χώρους εργασίας zen-tabs-cycle-by-attribute = .label = Ctrl+Tab επιλέγει κυκλικά μόνο από τις καρτέλες στα Απαραίτητα η στο Χώρο Εργασίας zen-tabs-cycle-ignore-pending-tabs = @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = Εναλλαγή DOM zen-devtools-toggle-accessibility-shortcut = Εναλλαγή Προσβασιμότητας zen-close-all-unpinned-tabs-shortcut = Κλείσιμο όλων των μη καρφιτσωμένων καρτελών zen-new-unsynced-window-shortcut = New Unsynced Window +zen-duplicate-tab-shortcut = Διπλασιασμός Καρτέλας diff --git a/locales/el/browser/browser/zen-general.ftl b/locales/el/browser/browser/zen-general.ftl index 0ef357e09..2966c4ca8 100644 --- a/locales/el/browser/browser/zen-general.ftl +++ b/locales/el/browser/browser/zen-general.ftl @@ -7,8 +7,8 @@ unified-extensions-description = Οι επεκτάσεις χρησιμοποι tab-context-zen-reset-pinned-tab = .label = { $isEssential -> - [true] Reset Essential Tab - *[false] Reset Pinned Tab + [true] Επαναφορά Απαραίτητης Καρτέλας + *[false] Επαναφορά Καρφιτσωμένης Καρτέλας } .accesskey = R tab-context-zen-add-essential = @@ -21,8 +21,8 @@ tab-context-zen-remove-essential = tab-context-zen-replace-pinned-url-with-current = .label = { $isEssential -> - [true] Replace Essential URL with Current - *[false] Replace Pinned URL with Current + [true] Αντικατάσταση Απαραίτητης διεύθυνσής με την τωρινή + *[false] Αντικατάσταση Καρφιτσωμένης διεύθυνσής με την τωρινή } .accesskey = C tab-context-zen-edit-title = @@ -41,7 +41,7 @@ pictureinpicture-minimize-btn = .tooltip = Ελαχιστοποίηση zen-panel-ui-gradient-generator-custom-color = Προσαρμοσμένο Χρώμα zen-copy-current-url-confirmation = Αντιγράφηκε το τρέχον URL! -zen-copy-current-url-as-markdown-confirmation = Copied current URL as Markdown! +zen-copy-current-url-as-markdown-confirmation = Η τωρινή διεύθυνσή αντιγράφηκε ως Markdown! zen-general-cancel-label = .label = Ακύρωση zen-general-confirm = @@ -50,6 +50,7 @@ zen-pinned-tab-replaced = Το URL της καρφιτσωμένης καρτέ zen-tabs-renamed = Η καρτέλα μετονομάστηκε επιτυχώς! zen-background-tab-opened-toast = Άνοιξε νέα καρτέλα στο παρασκήνιο! zen-workspace-renamed-toast = Ο χώρος εργασίας μετονομάστηκε επιτυχώς! +zen-split-view-limit-toast = Can't add more panels to the split view! zen-toggle-compact-mode-button = .label = Συμπαγής Λειτουργία .tooltiptext = Εναλλαγή Συμπαγούς Λειτουργίας @@ -123,4 +124,4 @@ zen-window-sync-migration-dialog-message = Το Zen άρα στην ίδια σ zen-window-sync-migration-dialog-learn-more = Περισσότερα zen-window-sync-migration-dialog-accept = Κατάλαβα zen-appmenu-new-blank-window = - .label = New blank window + .label = Νέο κενό παράθυρο diff --git a/locales/el/browser/browser/zen-menubar.ftl b/locales/el/browser/browser/zen-menubar.ftl index 32e7f3032..5e0256174 100644 --- a/locales/el/browser/browser/zen-menubar.ftl +++ b/locales/el/browser/browser/zen-menubar.ftl @@ -19,4 +19,4 @@ zen-menubar-appearance-light = zen-menubar-appearance-dark = .label = Σκοτεινό zen-menubar-new-blank-window = - .label = New Blank Window + .label = Νέο Κενό Παράθυρο diff --git a/locales/el/browser/browser/zen-split-view.ftl b/locales/el/browser/browser/zen-split-view.ftl index 11ba4ae59..70e1a3735 100644 --- a/locales/el/browser/browser/zen-split-view.ftl +++ b/locales/el/browser/browser/zen-split-view.ftl @@ -5,8 +5,9 @@ tab-zen-split-tabs = .label = { $tabCount -> - [1] Διαίρεση Καρτέλας (απαιτούνται πολλαπλές επιλεγμένες καρτέλες) - *[other] Διαίρεση { $tabCount } Καρτελών + [-1] Split out tab + [1] Add split view... + *[other] Join { $tabCount } Tabs } .accesskey = S zen-split-link = diff --git a/locales/el/browser/browser/zen-vertical-tabs.ftl b/locales/el/browser/browser/zen-vertical-tabs.ftl index 4ee3103d6..3d4c81e91 100644 --- a/locales/el/browser/browser/zen-vertical-tabs.ftl +++ b/locales/el/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = Απόκρυψη όλων .accesskey = H +zen-toolbar-context-move-to-folder = + .label = Move to Folder... + .accesskey = M zen-toolbar-context-new-folder = .label = Δημιουργία Φακέλου .accesskey = N @@ -38,7 +41,7 @@ tabbrowser-reset-pin-button = } zen-tab-sublabel = { $tabSubtitle -> - [zen-default-pinned] Back to pinned url - [zen-default-pinned-cmd] Separate from pinned tab + [zen-default-pinned] Πίσω στην καρφιτσωμένη διεύθυνσή + [zen-default-pinned-cmd] Διαχωρισμός από την καρφιτσωμένη καρτέλα *[other] { $tabSubtitle } } diff --git a/locales/el/browser/browser/zen-workspaces.ftl b/locales/el/browser/browser/zen-workspaces.ftl index 7c0263a5c..320be140b 100644 --- a/locales/el/browser/browser/zen-workspaces.ftl +++ b/locales/el/browser/browser/zen-workspaces.ftl @@ -10,7 +10,7 @@ zen-panel-ui-workspaces-create = zen-panel-ui-folder-create = .label = Δημιουργία Φακέλου zen-panel-ui-live-folder-create = - .label = Live Folder + .label = Ζωντανός Φάκελος zen-panel-ui-new-empty-split = .label = Νέος Διαχωρισμός zen-workspaces-panel-context-delete = @@ -25,7 +25,7 @@ zen-workspaces-panel-context-default-profile = zen-workspaces-panel-unload = .label = Εκφόρτωση Χώρου Εργασίας zen-workspaces-panel-unload-others = - .label = Unload All Other Spaces + .label = Εκφόρτωση Όλων Των Άλλων Χώρων zen-workspaces-how-to-reorder-title = Πώς να αναδιατάξετε τους χώρους zen-workspaces-how-to-reorder-desc = Σύρετε τα εικονίδια χώρου στο κάτω μέρος της πλαϊνής μπάρας για να τα αναδιατάξετε zen-workspaces-change-theme = diff --git a/locales/en-GB/browser/browser/preferences/zen-preferences.ftl b/locales/en-GB/browser/browser/preferences/zen-preferences.ftl index 6c2471ead..a7e3f3c3f 100644 --- a/locales/en-GB/browser/browser/preferences/zen-preferences.ftl +++ b/locales/en-GB/browser/browser/preferences/zen-preferences.ftl @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = Toggle DOM zen-devtools-toggle-accessibility-shortcut = Toggle Accessibility zen-close-all-unpinned-tabs-shortcut = Close All Unpinned Tabs zen-new-unsynced-window-shortcut = New Unsynced Window +zen-duplicate-tab-shortcut = Duplicate Tab diff --git a/locales/en-GB/browser/browser/zen-general.ftl b/locales/en-GB/browser/browser/zen-general.ftl index 5319fcadd..479798b15 100644 --- a/locales/en-GB/browser/browser/zen-general.ftl +++ b/locales/en-GB/browser/browser/zen-general.ftl @@ -48,6 +48,7 @@ zen-pinned-tab-replaced = Pinned tab URL has been replaced with the current URL. zen-tabs-renamed = Tab has been successfully renamed! zen-background-tab-opened-toast = New background tab opened! zen-workspace-renamed-toast = Workspace has been successfully renamed! +zen-split-view-limit-toast = Can't add more panels to the split view! zen-toggle-compact-mode-button = .label = Compact Mode .tooltiptext = Toggle Compact Mode diff --git a/locales/en-GB/browser/browser/zen-split-view.ftl b/locales/en-GB/browser/browser/zen-split-view.ftl index 3593d390b..844d6dcbd 100644 --- a/locales/en-GB/browser/browser/zen-split-view.ftl +++ b/locales/en-GB/browser/browser/zen-split-view.ftl @@ -5,8 +5,9 @@ tab-zen-split-tabs = .label = { $tabCount -> - [1] Split Tab (multiple selected tabs needed) - *[other] Split { $tabCount } Tabs + [-1] Split out tab + [1] Add split view... + *[other] Join { $tabCount } Tabs } .accesskey = S zen-split-link = diff --git a/locales/en-GB/browser/browser/zen-vertical-tabs.ftl b/locales/en-GB/browser/browser/zen-vertical-tabs.ftl index 41494167b..cddebc1ee 100644 --- a/locales/en-GB/browser/browser/zen-vertical-tabs.ftl +++ b/locales/en-GB/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = Hide both .accesskey = H +zen-toolbar-context-move-to-folder = + .label = Move to Folder... + .accesskey = M zen-toolbar-context-new-folder = .label = New Folder .accesskey = N diff --git a/locales/en-US/browser/browser/preferences/zen-preferences.ftl b/locales/en-US/browser/browser/preferences/zen-preferences.ftl index 7b304613c..6ec2bcf30 100644 --- a/locales/en-US/browser/browser/preferences/zen-preferences.ftl +++ b/locales/en-US/browser/browser/preferences/zen-preferences.ftl @@ -321,6 +321,7 @@ zen-workspace-shortcut-switch-9 = Switch to Workspace 9 zen-workspace-shortcut-switch-10 = Switch to Workspace 10 zen-workspace-shortcut-forward = Forward Workspace zen-workspace-shortcut-backward = Backward Workspace +zen-workspace-shortcut-create = Create New Workspace zen-sidebar-shortcut-toggle = Toggle Sidebar's Width zen-pinned-tab-shortcut-reset = Reset Pinned Tab to Pinned URL zen-split-view-shortcut-grid = Toggle Split View Grid @@ -357,3 +358,4 @@ zen-devtools-toggle-dom-shortcut = Toggle DOM zen-devtools-toggle-accessibility-shortcut = Toggle Accessibility zen-close-all-unpinned-tabs-shortcut = Close All Unpinned Tabs zen-new-unsynced-window-shortcut = New Blank Window +zen-duplicate-tab-shortcut = Duplicate Tab \ No newline at end of file diff --git a/locales/en-US/browser/browser/zen-boosts.ftl b/locales/en-US/browser/browser/zen-boosts.ftl new file mode 100644 index 000000000..65f67d689 --- /dev/null +++ b/locales/en-US/browser/browser/zen-boosts.ftl @@ -0,0 +1,58 @@ +# 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/. + +zen-boost-edit-rename = + .label = Rename Boost +zen-boost-edit-shuffle = + .label = Shuffle Vibes +zen-boost-edit-reset = + .label = Reset All Edits +zen-boost-edit-delete = + .label = Delete Boost +zen-boost-size = Size +zen-boost-zap = Zap +zen-boost-code = Code +zen-boost-back = Back +zen-boost-shuffle = + .tooltiptext = Shuffle Boost Settings +zen-boost-invert = + .tooltiptext = Smart Invert Colors +zen-boost-controls = + .tooltiptext = Advanced Color Controls +zen-boost-disable = + .tooltiptext = Disable Color Adjustments +zen-boost-text-case-toggle = + .tooltiptext = Toggle Text Case +zen-boost-css-picker = + .tooltiptext = Pick Selector +zen-boost-css-inspector = + .tooltiptext = Open Inspector +zen-bootst-color-contrast = Contrast +zen-bootst-color-brightness = Brightness +zen-bootst-color-original-saturation = Original Saturation +zen-add-zap-helper = Click elements on the page to Zap them +zen-remove-zap-helper = ← Click to Unzap +zen-select-this = Insert selector for this +zen-select-related = Insert selector for related +zen-select-cancel = Cancel +zen-zap-this = Zap this +zen-zap-related = Zap all related elements +zen-zap-cancel = Cancel +zen-zap-done = Done +zen-unzap-tooltip = + { + $elementCount -> + [0] No elements zapped + [1] { $elementCount } element zapped + *[other] { $elementCount } elements zapped + } +zen-boost-save = + .tooltiptext = Export Boost +zen-boost-load = + .tooltiptext = Import Boost +zen-panel-ui-boosts-exported-message = Boost exported! +zen-site-data-boosts = Boosts +zen-site-data-create-boost = + .tooltiptext = Create new boost +zen-boost-rename-boost-prompt = Rename Boost? \ No newline at end of file diff --git a/locales/en-US/browser/browser/zen-general.ftl b/locales/en-US/browser/browser/zen-general.ftl index ef3bbe3c5..d1229b04f 100644 --- a/locales/en-US/browser/browser/zen-general.ftl +++ b/locales/en-US/browser/browser/zen-general.ftl @@ -15,7 +15,7 @@ tab-context-zen-reset-pinned-tab = tab-context-zen-add-essential = .label = Add to Essentials .accesskey = E -tab-context-zen-add-essential-badge = { $num } / { $max } slots filled +tab-context-zen-add-essential-badge = { $num } / { $max } tab-context-zen-remove-essential = .label = Remove from Essentials .accesskey = R @@ -58,6 +58,7 @@ zen-pinned-tab-replaced = Pinned tab URL has been replaced with the current URL! zen-tabs-renamed = Tab has been successfully renamed! zen-background-tab-opened-toast = New background tab opened! zen-workspace-renamed-toast = Workspace has been successfully renamed! +zen-split-view-limit-toast = Can't add more panels to the split view! zen-toggle-compact-mode-button = .label = Compact Mode diff --git a/locales/en-US/browser/browser/zen-split-view.ftl b/locales/en-US/browser/browser/zen-split-view.ftl index 86090453a..ee1e7081f 100644 --- a/locales/en-US/browser/browser/zen-split-view.ftl +++ b/locales/en-US/browser/browser/zen-split-view.ftl @@ -5,9 +5,9 @@ tab-zen-split-tabs = .label = { $tabCount -> - [-1] Unsplit Tabs - [1] Split Tab (multiple selected tabs needed) - *[other] Split { $tabCount } Tabs + [-1] Split out tab + [1] Add split view... + *[other] Join { $tabCount } Tabs } .accesskey = S diff --git a/locales/en-US/browser/browser/zen-vertical-tabs.ftl b/locales/en-US/browser/browser/zen-vertical-tabs.ftl index f54a0d3b0..73e4a2a03 100644 --- a/locales/en-US/browser/browser/zen-vertical-tabs.ftl +++ b/locales/en-US/browser/browser/zen-vertical-tabs.ftl @@ -19,6 +19,10 @@ zen-toolbar-context-compact-mode-hide-both = .label = Hide both .accesskey = H +zen-toolbar-context-move-to-folder = + .label = Move to Folder... + .accesskey = M + zen-toolbar-context-new-folder = .label = New Folder .accesskey = N diff --git a/locales/es-ES/browser/browser/preferences/zen-preferences.ftl b/locales/es-ES/browser/browser/preferences/zen-preferences.ftl index fe1f7c7f7..7e81a58a4 100644 --- a/locales/es-ES/browser/browser/preferences/zen-preferences.ftl +++ b/locales/es-ES/browser/browser/preferences/zen-preferences.ftl @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = Alternar DOM zen-devtools-toggle-accessibility-shortcut = Alternar accesibilidad zen-close-all-unpinned-tabs-shortcut = Cerrar todas las pestañas sin fijar zen-new-unsynced-window-shortcut = Nueva ventana no sincronizada +zen-duplicate-tab-shortcut = Duplicar pestaña diff --git a/locales/es-ES/browser/browser/zen-general.ftl b/locales/es-ES/browser/browser/zen-general.ftl index 7410c1077..b9fb5237f 100644 --- a/locales/es-ES/browser/browser/zen-general.ftl +++ b/locales/es-ES/browser/browser/zen-general.ftl @@ -50,6 +50,7 @@ zen-pinned-tab-replaced = La URL de la pestaña fijada se ha reemplazado por la zen-tabs-renamed = ¡La pestaña se ha renombrado con éxito! zen-background-tab-opened-toast = ¡Nueva pestaña abierta en segundo plano! zen-workspace-renamed-toast = ¡El espacio de trabajo ha sido renombrado con éxito! +zen-split-view-limit-toast = ¡No se pueden añadir más paneles a la vista dividida! zen-toggle-compact-mode-button = .label = Modo compacto .tooltiptext = Alternar modo compacto diff --git a/locales/es-ES/browser/browser/zen-split-view.ftl b/locales/es-ES/browser/browser/zen-split-view.ftl index 915fdba54..2d3f297c0 100644 --- a/locales/es-ES/browser/browser/zen-split-view.ftl +++ b/locales/es-ES/browser/browser/zen-split-view.ftl @@ -5,9 +5,9 @@ tab-zen-split-tabs = .label = { $tabCount -> - [-1] Deshacer división - [1] Vista dividida (seleccione varias pestañas) - *[other] Añadir { $tabCount } pestañas a la vista dividida + [-1] Separar pestaña + [1] Añadir vista dividida... + *[other] Unir { $tabCount } pestañas } .accesskey = S zen-split-link = diff --git a/locales/es-ES/browser/browser/zen-vertical-tabs.ftl b/locales/es-ES/browser/browser/zen-vertical-tabs.ftl index ddefab297..5d892fcde 100644 --- a/locales/es-ES/browser/browser/zen-vertical-tabs.ftl +++ b/locales/es-ES/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = Ocultar ambas .accesskey = H +zen-toolbar-context-move-to-folder = + .label = Mover a la carpeta... + .accesskey = M zen-toolbar-context-new-folder = .label = Nueva carpeta .accesskey = N diff --git a/locales/et/browser/browser/preferences/zen-preferences.ftl b/locales/et/browser/browser/preferences/zen-preferences.ftl index 7918c3927..c15f1b56d 100644 --- a/locales/et/browser/browser/preferences/zen-preferences.ftl +++ b/locales/et/browser/browser/preferences/zen-preferences.ftl @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = Lülita DOM sisse/välja zen-devtools-toggle-accessibility-shortcut = Lülita ligipääsetavuse sektsioon sisse/välja zen-close-all-unpinned-tabs-shortcut = Close All Unpinned Tabs zen-new-unsynced-window-shortcut = New Unsynced Window +zen-duplicate-tab-shortcut = Duplicate Tab diff --git a/locales/et/browser/browser/zen-general.ftl b/locales/et/browser/browser/zen-general.ftl index 1c55e454d..7ba7cbd2d 100644 --- a/locales/et/browser/browser/zen-general.ftl +++ b/locales/et/browser/browser/zen-general.ftl @@ -50,6 +50,7 @@ zen-pinned-tab-replaced = Pinned tab URL has been replaced with the current URL. zen-tabs-renamed = Kaart on edukalt ümber nimetatud! zen-background-tab-opened-toast = Taustal avati uus kaart! zen-workspace-renamed-toast = Tööruum on edukalt ümber nimetatud! +zen-split-view-limit-toast = Can't add more panels to the split view! zen-toggle-compact-mode-button = .label = Compact Mode .tooltiptext = Lülita kompaktne režiim sisse/välja diff --git a/locales/et/browser/browser/zen-split-view.ftl b/locales/et/browser/browser/zen-split-view.ftl index 626e4a33a..01814d861 100644 --- a/locales/et/browser/browser/zen-split-view.ftl +++ b/locales/et/browser/browser/zen-split-view.ftl @@ -5,8 +5,9 @@ tab-zen-split-tabs = .label = { $tabCount -> - [1] Jaota kaart (mitu kaarti peab olema valitud) - *[other] Jaota { $tabCount } kaarti + [-1] Split out tab + [1] Add split view... + *[other] Join { $tabCount } Tabs } .accesskey = J zen-split-link = diff --git a/locales/et/browser/browser/zen-vertical-tabs.ftl b/locales/et/browser/browser/zen-vertical-tabs.ftl index 79e19799d..c4ecdab54 100644 --- a/locales/et/browser/browser/zen-vertical-tabs.ftl +++ b/locales/et/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = Peida mõlemad .accesskey = P +zen-toolbar-context-move-to-folder = + .label = Move to Folder... + .accesskey = M zen-toolbar-context-new-folder = .label = Uus kaust .accesskey = U diff --git a/locales/eu/browser/browser/preferences/zen-preferences.ftl b/locales/eu/browser/browser/preferences/zen-preferences.ftl index 48e927caa..728fc5f2e 100644 --- a/locales/eu/browser/browser/preferences/zen-preferences.ftl +++ b/locales/eu/browser/browser/preferences/zen-preferences.ftl @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = Toggle DOM zen-devtools-toggle-accessibility-shortcut = Toggle Accessibility zen-close-all-unpinned-tabs-shortcut = Close All Unpinned Tabs zen-new-unsynced-window-shortcut = New Blank Window +zen-duplicate-tab-shortcut = Duplicate Tab diff --git a/locales/eu/browser/browser/zen-general.ftl b/locales/eu/browser/browser/zen-general.ftl index 06d5c5810..c2754b4f4 100644 --- a/locales/eu/browser/browser/zen-general.ftl +++ b/locales/eu/browser/browser/zen-general.ftl @@ -50,6 +50,7 @@ zen-pinned-tab-replaced = Oraingo estekak ordezkatu du ainguratutako fitxarena! zen-tabs-renamed = Fitxaren izena ongi aldatu da! zen-background-tab-opened-toast = Fitxa berri bat zabaldu da bigarren planoan! zen-workspace-renamed-toast = Lan-eremuaren izena ongi aldatu da! +zen-split-view-limit-toast = Can't add more panels to the split view! zen-toggle-compact-mode-button = .label = Modu Trinkoa .tooltiptext = Modu Trinkoa Aktibatu/Desaktibatu diff --git a/locales/eu/browser/browser/zen-split-view.ftl b/locales/eu/browser/browser/zen-split-view.ftl index 0cf9037a1..4430fab34 100644 --- a/locales/eu/browser/browser/zen-split-view.ftl +++ b/locales/eu/browser/browser/zen-split-view.ftl @@ -5,9 +5,9 @@ tab-zen-split-tabs = .label = { $tabCount -> - [-1] Unsplit Tabs - [1] Split Tab (multiple selected tabs needed) - *[other] Split { $tabCount } Tabs + [-1] Split out tab + [1] Add split view... + *[other] Join { $tabCount } Tabs } .accesskey = S zen-split-link = diff --git a/locales/eu/browser/browser/zen-vertical-tabs.ftl b/locales/eu/browser/browser/zen-vertical-tabs.ftl index ff8a3775c..9989c7666 100644 --- a/locales/eu/browser/browser/zen-vertical-tabs.ftl +++ b/locales/eu/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = Hide both .accesskey = H +zen-toolbar-context-move-to-folder = + .label = Move to Folder... + .accesskey = M zen-toolbar-context-new-folder = .label = New Folder .accesskey = N diff --git a/locales/fa/browser/browser/preferences/zen-preferences.ftl b/locales/fa/browser/browser/preferences/zen-preferences.ftl index f28962a53..8f63791ca 100644 --- a/locales/fa/browser/browser/preferences/zen-preferences.ftl +++ b/locales/fa/browser/browser/preferences/zen-preferences.ftl @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = Toggle DOM zen-devtools-toggle-accessibility-shortcut = Toggle Accessibility zen-close-all-unpinned-tabs-shortcut = Close All Unpinned Tabs zen-new-unsynced-window-shortcut = New Unsynced Window +zen-duplicate-tab-shortcut = Duplicate Tab diff --git a/locales/fa/browser/browser/zen-general.ftl b/locales/fa/browser/browser/zen-general.ftl index 281a24d0b..f4b3f238d 100644 --- a/locales/fa/browser/browser/zen-general.ftl +++ b/locales/fa/browser/browser/zen-general.ftl @@ -50,6 +50,7 @@ zen-pinned-tab-replaced = Pinned tab URL has been replaced with the current URL. zen-tabs-renamed = Tab has been successfully renamed! zen-background-tab-opened-toast = New background tab opened! zen-workspace-renamed-toast = Workspace has been successfully renamed! +zen-split-view-limit-toast = Can't add more panels to the split view! zen-toggle-compact-mode-button = .label = Compact Mode .tooltiptext = Toggle Compact Mode diff --git a/locales/fa/browser/browser/zen-split-view.ftl b/locales/fa/browser/browser/zen-split-view.ftl index c9d226f89..bfc0e2ca8 100644 --- a/locales/fa/browser/browser/zen-split-view.ftl +++ b/locales/fa/browser/browser/zen-split-view.ftl @@ -5,8 +5,9 @@ tab-zen-split-tabs = .label = { $tabCount -> - [1] Split Tab (multiple selected tabs needed) - *[other] Split { $tabCount } Tabs + [-1] Split out tab + [1] Add split view... + *[other] Join { $tabCount } Tabs } .accesskey = S zen-split-link = diff --git a/locales/fa/browser/browser/zen-vertical-tabs.ftl b/locales/fa/browser/browser/zen-vertical-tabs.ftl index 41494167b..cddebc1ee 100644 --- a/locales/fa/browser/browser/zen-vertical-tabs.ftl +++ b/locales/fa/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = Hide both .accesskey = H +zen-toolbar-context-move-to-folder = + .label = Move to Folder... + .accesskey = M zen-toolbar-context-new-folder = .label = New Folder .accesskey = N diff --git a/locales/fi/browser/browser/preferences/zen-preferences.ftl b/locales/fi/browser/browser/preferences/zen-preferences.ftl index be58fe2cd..7f518ecc6 100644 --- a/locales/fi/browser/browser/preferences/zen-preferences.ftl +++ b/locales/fi/browser/browser/preferences/zen-preferences.ftl @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = Vaihda DOM:ia zen-devtools-toggle-accessibility-shortcut = Vaihda Esteettömyystilaan zen-close-all-unpinned-tabs-shortcut = Sulje kaikki kiinnittämättömät välilehdet zen-new-unsynced-window-shortcut = New Unsynced Window +zen-duplicate-tab-shortcut = Duplicate Tab diff --git a/locales/fi/browser/browser/zen-general.ftl b/locales/fi/browser/browser/zen-general.ftl index c3afa728a..7ddce52f2 100644 --- a/locales/fi/browser/browser/zen-general.ftl +++ b/locales/fi/browser/browser/zen-general.ftl @@ -50,6 +50,7 @@ zen-pinned-tab-replaced = Pinned tab URL has been replaced with the current URL. zen-tabs-renamed = Välilehti on nimetty uudelleen! zen-background-tab-opened-toast = Uusi taustavälilehti avattu! zen-workspace-renamed-toast = Työtila on nimetty uudelleen! +zen-split-view-limit-toast = Can't add more panels to the split view! zen-toggle-compact-mode-button = .label = Kompakti Tila .tooltiptext = Ota käyttöön Kompakti tila diff --git a/locales/fi/browser/browser/zen-split-view.ftl b/locales/fi/browser/browser/zen-split-view.ftl index bbc3dbeda..a1e905819 100644 --- a/locales/fi/browser/browser/zen-split-view.ftl +++ b/locales/fi/browser/browser/zen-split-view.ftl @@ -5,8 +5,9 @@ tab-zen-split-tabs = .label = { $tabCount -> - [1] Split Välilehti (tarvitaan useita valittuja välilehtiä) - *[other] Split { $tabCount } Välilehdet + [-1] Split out tab + [1] Add split view... + *[other] Join { $tabCount } Tabs } .accesskey = S zen-split-link = diff --git a/locales/fi/browser/browser/zen-vertical-tabs.ftl b/locales/fi/browser/browser/zen-vertical-tabs.ftl index ed852a2fa..c12141fde 100644 --- a/locales/fi/browser/browser/zen-vertical-tabs.ftl +++ b/locales/fi/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = Piilota molemmat .accesskey = H +zen-toolbar-context-move-to-folder = + .label = Move to Folder... + .accesskey = M zen-toolbar-context-new-folder = .label = Uusi kansio .accesskey = N diff --git a/locales/fr/browser/browser/preferences/zen-preferences.ftl b/locales/fr/browser/browser/preferences/zen-preferences.ftl index 89d3fddc8..f2deb00b8 100644 --- a/locales/fr/browser/browser/preferences/zen-preferences.ftl +++ b/locales/fr/browser/browser/preferences/zen-preferences.ftl @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = Afficher l’onglet DOM zen-devtools-toggle-accessibility-shortcut = Afficher l’onglet Accessibilité zen-close-all-unpinned-tabs-shortcut = Fermer tous les onglets non épinglés zen-new-unsynced-window-shortcut = Nouvelle fenêtre vierge +zen-duplicate-tab-shortcut = Dupliquer l'onglet diff --git a/locales/fr/browser/browser/zen-general.ftl b/locales/fr/browser/browser/zen-general.ftl index ccb6cbd0b..41d53b68f 100644 --- a/locales/fr/browser/browser/zen-general.ftl +++ b/locales/fr/browser/browser/zen-general.ftl @@ -14,7 +14,7 @@ tab-context-zen-reset-pinned-tab = tab-context-zen-add-essential = .label = Ajouter aux Essentials .accesskey = E -tab-context-zen-add-essential-badge = { $num } / { $max } emplacements occupés +tab-context-zen-add-essential-badge = { $num } / { $max } tab-context-zen-remove-essential = .label = Retirer des Essentials .accesskey = R @@ -50,6 +50,7 @@ zen-pinned-tab-replaced = L’adresse de l'onglet épinglé a été remplacée p zen-tabs-renamed = L’onglet a été renommé avec succès ! zen-background-tab-opened-toast = Nouvel onglet ouvert en arrière-plan ! zen-workspace-renamed-toast = L'espace de travail a été renommé avec succès ! +zen-split-view-limit-toast = Impossible d'ajouter d'autres panneaux à la vue fractionnée ! zen-toggle-compact-mode-button = .label = Mode compact .tooltiptext = Activer/Désactiver le mode compact diff --git a/locales/fr/browser/browser/zen-split-view.ftl b/locales/fr/browser/browser/zen-split-view.ftl index 4b2db603f..a0041cedd 100644 --- a/locales/fr/browser/browser/zen-split-view.ftl +++ b/locales/fr/browser/browser/zen-split-view.ftl @@ -5,8 +5,9 @@ tab-zen-split-tabs = .label = { $tabCount -> - [1] Fractionner (sélectionner plusieurs onglets) - *[other] Fractionner { $tabCount } onglets + [-1] Séparer l'onglet + [1] Ajouter une vue fractionnée... + *[other] Fusionner { $tabCount } onglets } .accesskey = S zen-split-link = diff --git a/locales/fr/browser/browser/zen-vertical-tabs.ftl b/locales/fr/browser/browser/zen-vertical-tabs.ftl index 2355634e1..ee6ac4683 100644 --- a/locales/fr/browser/browser/zen-vertical-tabs.ftl +++ b/locales/fr/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = Masquer les deux .accesskey = H +zen-toolbar-context-move-to-folder = + .label = Déplacer vers le dossier... + .accesskey = M zen-toolbar-context-new-folder = .label = Nouveau dossier .accesskey = N diff --git a/locales/ga-IE/browser/browser/preferences/zen-preferences.ftl b/locales/ga-IE/browser/browser/preferences/zen-preferences.ftl index 0b07ca8df..10f96fedd 100644 --- a/locales/ga-IE/browser/browser/preferences/zen-preferences.ftl +++ b/locales/ga-IE/browser/browser/preferences/zen-preferences.ftl @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = Scoránaigh DOM zen-devtools-toggle-accessibility-shortcut = Scoránaigh Inrochtaineacht zen-close-all-unpinned-tabs-shortcut = Dún Gach Cluaisín Gan Phionáil zen-new-unsynced-window-shortcut = Fuinneog Nua Neamhshioncrónaithe +zen-duplicate-tab-shortcut = Cluaisín Dúblach diff --git a/locales/ga-IE/browser/browser/zen-general.ftl b/locales/ga-IE/browser/browser/zen-general.ftl index 4bd0dd106..7b73f797e 100644 --- a/locales/ga-IE/browser/browser/zen-general.ftl +++ b/locales/ga-IE/browser/browser/zen-general.ftl @@ -7,8 +7,8 @@ unified-extensions-description = Úsáidtear síntí chun níos mó feidhmiúlac tab-context-zen-reset-pinned-tab = .label = { $isEssential -> - [true] Athshocraigh an Cluaisín Riachtanach - *[false] Athshocraigh an Cluaisín Priontáilte + [true] Athshocraigh an Cluaisín Riachtanach + *[false] Athshocraigh an Cluaisín Priontáilte } .accesskey = R tab-context-zen-add-essential = @@ -21,8 +21,8 @@ tab-context-zen-remove-essential = tab-context-zen-replace-pinned-url-with-current = .label = { $isEssential -> - [true] Cuir an URL Riachtanach in ionad an URL Reatha - *[false] Cuir an URL Priontáilte in ionad an URL Reatha + [true] Cuir an URL Riachtanach in ionad an URL Reatha + *[false] Cuir an URL Priontáilte in ionad an URL Reatha } .accesskey = C tab-context-zen-edit-title = @@ -50,6 +50,7 @@ zen-pinned-tab-replaced = Tá URL an chluaisín phinnáilte curtha in ionad an U zen-tabs-renamed = Athainmníodh an cluaisín go rathúil! zen-background-tab-opened-toast = Tá cluaisín cúlra nua oscailte! zen-workspace-renamed-toast = Athainmníodh an spás oibre go rathúil! +zen-split-view-limit-toast = Ní féidir níos mó painéil a chur leis an radharc scoilte! zen-toggle-compact-mode-button = .label = Mód Dlúth .tooltiptext = Mód Dlúth a Athrú diff --git a/locales/ga-IE/browser/browser/zen-split-view.ftl b/locales/ga-IE/browser/browser/zen-split-view.ftl index 8fc871cdb..f99a79e01 100644 --- a/locales/ga-IE/browser/browser/zen-split-view.ftl +++ b/locales/ga-IE/browser/browser/zen-split-view.ftl @@ -5,8 +5,9 @@ tab-zen-split-tabs = .label = { $tabCount -> - [1] Scoilt Cluaisín (tá gá le roinnt cluaisíní roghnaithe) - *[other] Scoilt { $tabCount } Cluaisíní + [-1] Cluaisín scoilte + [1] Amharc scoilte a chur leis... + *[other] Ceangail le { $tabCount } Cluaisíní } .accesskey = S zen-split-link = diff --git a/locales/ga-IE/browser/browser/zen-vertical-tabs.ftl b/locales/ga-IE/browser/browser/zen-vertical-tabs.ftl index 7d73f6055..d107deb12 100644 --- a/locales/ga-IE/browser/browser/zen-vertical-tabs.ftl +++ b/locales/ga-IE/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = Folaigh an dá cheann .accesskey = H +zen-toolbar-context-move-to-folder = + .label = Bog go Fillteán... + .accesskey = M zen-toolbar-context-new-folder = .label = Fillteán Nua .accesskey = N @@ -38,7 +41,7 @@ tabbrowser-reset-pin-button = } zen-tab-sublabel = { $tabSubtitle -> - [zen-default-pinned] Ar ais go dtí an url bioráilte - [zen-default-pinned-cmd] Ar leithligh ón gcluaisín bioráilte - *[other] { $tabSubtitle } + [zen-default-pinned] Ar ais go dtí an url bioráilte + [zen-default-pinned-cmd] Ar leithligh ón gcluaisín bioráilte + *[other] { $tabSubtitle } } diff --git a/locales/he/browser/browser/preferences/zen-preferences.ftl b/locales/he/browser/browser/preferences/zen-preferences.ftl index 69eca12ed..fc21b7eb8 100644 --- a/locales/he/browser/browser/preferences/zen-preferences.ftl +++ b/locales/he/browser/browser/preferences/zen-preferences.ftl @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = הפעל/כבה DOM zen-devtools-toggle-accessibility-shortcut = הפעל/כבה נגישות zen-close-all-unpinned-tabs-shortcut = סגירת כל הלשוניות שאינן מוצמדות zen-new-unsynced-window-shortcut = חלון לא מסונכרן חדש +zen-duplicate-tab-shortcut = Duplicate Tab diff --git a/locales/he/browser/browser/zen-general.ftl b/locales/he/browser/browser/zen-general.ftl index a5a01774a..0cf72f91c 100644 --- a/locales/he/browser/browser/zen-general.ftl +++ b/locales/he/browser/browser/zen-general.ftl @@ -48,6 +48,7 @@ zen-pinned-tab-replaced = כתובת הלשונית המוצמדת הוחלפה zen-tabs-renamed = שם הלשונית השתנה בהצלחה! zen-background-tab-opened-toast = לשונית נפתחה ברקע! zen-workspace-renamed-toast = שם מרחב העבודה השתנה בהצלחה! +zen-split-view-limit-toast = Can't add more panels to the split view! zen-toggle-compact-mode-button = .label = מצב חסכוני .tooltiptext = הפעלת/כיבוי מצב חסכוני diff --git a/locales/he/browser/browser/zen-split-view.ftl b/locales/he/browser/browser/zen-split-view.ftl index 4a0f06957..96042d9b5 100644 --- a/locales/he/browser/browser/zen-split-view.ftl +++ b/locales/he/browser/browser/zen-split-view.ftl @@ -5,8 +5,9 @@ tab-zen-split-tabs = .label = { $tabCount -> - [1] פיצול לשונית (נדרש מספר לשוניות מסומנות) - *[other] פיצול { $tabCount } לשוניות + [-1] Split out tab + [1] Add split view... + *[other] Join { $tabCount } Tabs } .accesskey = ד zen-split-link = diff --git a/locales/he/browser/browser/zen-vertical-tabs.ftl b/locales/he/browser/browser/zen-vertical-tabs.ftl index 8365a562d..28121137b 100644 --- a/locales/he/browser/browser/zen-vertical-tabs.ftl +++ b/locales/he/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = הסתרת שני הסרגלים .accesskey = י +zen-toolbar-context-move-to-folder = + .label = Move to Folder... + .accesskey = M zen-toolbar-context-new-folder = .label = תיקייה חדשה .accesskey = מ diff --git a/locales/hu/browser/browser/preferences/zen-preferences.ftl b/locales/hu/browser/browser/preferences/zen-preferences.ftl index 4637f208e..02da4b7a0 100644 --- a/locales/hu/browser/browser/preferences/zen-preferences.ftl +++ b/locales/hu/browser/browser/preferences/zen-preferences.ftl @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = DOM kapcsolása zen-devtools-toggle-accessibility-shortcut = Hozzáférhetőség kapcsolása zen-close-all-unpinned-tabs-shortcut = Összes rögzítetlen lap bezárása zen-new-unsynced-window-shortcut = Új szinkronizálatlan ablak +zen-duplicate-tab-shortcut = Lap duplikálása diff --git a/locales/hu/browser/browser/zen-general.ftl b/locales/hu/browser/browser/zen-general.ftl index dd9279f42..93f887358 100644 --- a/locales/hu/browser/browser/zen-general.ftl +++ b/locales/hu/browser/browser/zen-general.ftl @@ -50,6 +50,7 @@ zen-pinned-tab-replaced = A rögzített lap URL címe helyébe az aktuális URL zen-tabs-renamed = A lap sikeresen át lett nevezve! zen-background-tab-opened-toast = Új lap megnyitva! zen-workspace-renamed-toast = A munkakörnyezet sikeresen át lett nevezve! +zen-split-view-limit-toast = Nem lehet további paneleket hozzáadni az osztott nézethez! zen-toggle-compact-mode-button = .label = Kompakt mód .tooltiptext = Kompakt mód ki-/bekapcsolása diff --git a/locales/hu/browser/browser/zen-split-view.ftl b/locales/hu/browser/browser/zen-split-view.ftl index 350e3e4e3..9f4b81858 100644 --- a/locales/hu/browser/browser/zen-split-view.ftl +++ b/locales/hu/browser/browser/zen-split-view.ftl @@ -5,8 +5,9 @@ tab-zen-split-tabs = .label = { $tabCount -> - [1] Felosztott lapok (válassz ki több lapot) - *[other] { $tabCount } lap felosztása + [-1] Lap kivétele + [1] Osztott nézethez adás... + *[other] { $tabCount } lap összevonása } .accesskey = S zen-split-link = diff --git a/locales/hu/browser/browser/zen-vertical-tabs.ftl b/locales/hu/browser/browser/zen-vertical-tabs.ftl index dd14881b7..63c85ee0c 100644 --- a/locales/hu/browser/browser/zen-vertical-tabs.ftl +++ b/locales/hu/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = Mindkettő elrejtése .accesskey = H +zen-toolbar-context-move-to-folder = + .label = Áthelyezés mappába... + .accesskey = M zen-toolbar-context-new-folder = .label = Új mappa .accesskey = N diff --git a/locales/id/browser/browser/preferences/zen-preferences.ftl b/locales/id/browser/browser/preferences/zen-preferences.ftl index 4939f5492..7f5e29879 100644 --- a/locales/id/browser/browser/preferences/zen-preferences.ftl +++ b/locales/id/browser/browser/preferences/zen-preferences.ftl @@ -43,13 +43,13 @@ category-zen-workspaces = .tooltiptext = { pane-zen-tabs-title } pane-settings-workspaces-title = Ruang Kerja zen-tabs-select-recently-used-on-close = - .label = When closing a tab, switch to the most recently used tab instead of the next tab + .label = Saat menutup tab, beralih ke tab yang terakhir digunakan alih-alih tab berikutnya zen-tabs-close-on-back-with-no-history = - .label = Tutup tab dan beralih ke tab pemiliknya (atau tab yang terakhir digunakan) saat kembali tanpa riwayat + .label = Tutup tab dan beralih ke tab asal (atau terakhir digunakan) saat kembali tanpa riwayat zen-settings-workspaces-sync-unpinned-tabs = - .label = Sync only pinned tabs in workspaces + .label = (Window Sync) Hanya sinkronkan tab tersemat dalam ruang kerja zen-tabs-cycle-by-attribute = - .label = Ctrl+Tab berputar hanya dalam tab Esensial atau Ruang Kerja + .label = Ctrl+Tab hanya beralih di antara tab Esensial atau Ruang Kerja zen-tabs-cycle-ignore-pending-tabs = .label = Lewati tab tak termuat saat beralih dengan Ctrl+Tab zen-tabs-cycle-by-attribute-warning = Ctrl+Tab akan beralih berdasarkan urutan terakhir digunakan @@ -63,18 +63,18 @@ zen-pinned-tab-manager-description = Kelola perilaku tambahan dari tab yang dise zen-pinned-tab-manager-restore-pinned-tabs-to-pinned-url = .label = Pulihkan tab yang disematkan ke URL awal saat mulai ulang zen-pinned-tab-manager-container-specific-essentials-enabled = - .label = Aktifkan kontainer-spesifik essentials + .label = Aktifkan pemisahan esensial per kontainer zen-pinned-tab-manager-close-shortcut-behavior-label = Perilaku Pintasan Tutup Tab zen-pinned-tab-manager-reset-unload-switch-close-shortcut-option = - .label = Setel Ulang URL, lepaskan, dan beralih ke tab berikutnya + .label = Setel ulang URL, lepaskan, dan beralih ke tab berikutnya zen-pinned-tab-manager-unload-switch-close-shortcut-option = .label = Lepaskan dan beralih ke tab berikutnya zen-pinned-tab-manager-reset-switch-close-shortcut-option = - .label = Setel Ulang URL dan beralih ke tab berikutnya + .label = Setel ulang URL dan beralih ke tab berikutnya zen-pinned-tab-manager-switch-close-shortcut-option = .label = Beralih ke tab berikutnya zen-pinned-tab-manager-reset-close-shortcut-option = - .label = Setel Ulang URL + .label = Setel ulang URL zen-pinned-tab-manager-close-close-shortcut-option = .label = Tutup tab pane-zen-workspaces-header = Ruang Kerja @@ -137,7 +137,7 @@ pane-zen-marketplace-title = Zen Mods zen-themes-auto-update = .label = Otomatis perbarui mod yang terinstal saat startup zen-settings-workspaces-force-container-tabs-to-workspace = - .label = Saat membuka tab kontainer, otomatis pindahkan ke ruang kerja default kontainer tab itu + .label = Otomatis pindahkan tab kontainer ke ruang kerja defaultnya saat dibuka zen-theme-marketplace-link = Kunjungi Toko zen-dark-theme-styles-header = Gaya Tema Gelap zen-dark-theme-styles-description = Mengkustomisasi mode gelap sesuai kemauanmu @@ -189,7 +189,7 @@ zen-tab-new-shortcut = Tab Baru zen-key-redo = Ulangi zen-restore-last-closed-tab-shortcut = Pulihkan Tab yang Terakhir Ditutup zen-location-open-shortcut = Buka Lokasi -zen-location-open-shortcut-alt = Buka Lokasi +zen-location-open-shortcut-alt = Buka Lokasi (Alt) zen-key-undo-close-window = Batalkan Tutup Jendela zen-text-action-undo-shortcut = Batalkan zen-text-action-redo-shortcut = Ulangi @@ -246,7 +246,7 @@ zen-search-find-again-shortcut = Temukan Lagi zen-search-find-again-shortcut-prev = Cari Sebelumnya zen-search-find-again-shortcut-2 = Cari Lagi (Alt) zen-bookmark-this-page-shortcut = Markahi Laman Ini -zen-bookmark-show-library-shortcut = Tampilkan Pustaka Bookmark +zen-bookmark-show-library-shortcut = Tampilkan Pustaka Markah zen-key-stop = Berhenti Memuat zen-full-zoom-reduce-shortcut = Perkecil zen-full-zoom-enlarge-shortcut = Perbesar @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = Ubah DOM zen-devtools-toggle-accessibility-shortcut = Ubah Aksesibilitas zen-close-all-unpinned-tabs-shortcut = Tutup Semua Tab yang Tidak Disematkan zen-new-unsynced-window-shortcut = Jendela Kosong Baru +zen-duplicate-tab-shortcut = Duplicate Tab diff --git a/locales/id/browser/browser/zen-general.ftl b/locales/id/browser/browser/zen-general.ftl index 0e5f6bfdc..860c3deed 100644 --- a/locales/id/browser/browser/zen-general.ftl +++ b/locales/id/browser/browser/zen-general.ftl @@ -3,26 +3,26 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. zen-panel-ui-current-profile-text = profil saat ini -unified-extensions-description = Ekstensi digunakan untuk menambahkan lebih banyak fungsi ekstra ke { -brand-short-name }. +unified-extensions-description = Ekstensi digunakan untuk menambahkan fungsi ekstra ke { -brand-short-name }. tab-context-zen-reset-pinned-tab = .label = { $isEssential -> - [true] Reset Essential Tab - *[false] Reset Pinned Tab + [true] Reset Tab Esensial ke URL awal + *[false] Reset Tab Sematan ke URL awal } .accesskey = R tab-context-zen-add-essential = - .label = Tambahkan ke Essentials + .label = Tambahkan ke Esensial .accesskey = E tab-context-zen-add-essential-badge = { $num } / { $max } slot terisi tab-context-zen-remove-essential = - .label = Hapus dari Essentials + .label = Hapus dari Esensial .accesskey = R tab-context-zen-replace-pinned-url-with-current = .label = { $isEssential -> - [true] Replace Essential URL with Current - *[false] Replace Pinned URL with Current + [true] Perbarui URL awal Tab Esensial + *[false] Perbarui URL awal Tab Sematan } .accesskey = C tab-context-zen-edit-title = @@ -41,7 +41,7 @@ pictureinpicture-minimize-btn = .tooltip = Minimalkan zen-panel-ui-gradient-generator-custom-color = Warna Kustom zen-copy-current-url-confirmation = URL Disalin! -zen-copy-current-url-as-markdown-confirmation = Copied current URL as Markdown! +zen-copy-current-url-as-markdown-confirmation = URL disalin sebagai Markdown! zen-general-cancel-label = .label = Batalkan zen-general-confirm = @@ -50,6 +50,7 @@ zen-pinned-tab-replaced = URL awal dari tab yang disematkan telah diganti dengan zen-tabs-renamed = Tab telah berhasil diubah namanya! zen-background-tab-opened-toast = Tab baru telah terbuka di latar belakang! zen-workspace-renamed-toast = Ruang Kerja telah berhasil diubah namanya! +zen-split-view-limit-toast = Can't add more panels to the split view! zen-toggle-compact-mode-button = .label = Mode Ringkas .tooltiptext = Aktifkan/Sembunyikan Mode Ringkas @@ -69,7 +70,7 @@ zen-site-data-settings = Pengaturan zen-generic-manage = Kelola zen-generic-more = Selengkapnya zen-generic-next = Lanjut -zen-essentials-promo-label = Tambahkan ke Essentials +zen-essentials-promo-label = Tambahkan ke Esensial zen-essentials-promo-sublabel = Akses tab favorit Anda hanya dengan sekali klik # These labels will be used for the site data panel settings zen-site-data-setting-allow = Diizinkan diff --git a/locales/id/browser/browser/zen-menubar.ftl b/locales/id/browser/browser/zen-menubar.ftl index 1b91dd1ab..3045694cf 100644 --- a/locales/id/browser/browser/zen-menubar.ftl +++ b/locales/id/browser/browser/zen-menubar.ftl @@ -19,4 +19,4 @@ zen-menubar-appearance-light = zen-menubar-appearance-dark = .label = Gelap zen-menubar-new-blank-window = - .label = New Blank Window + .label = Jendela Kosong Baru diff --git a/locales/id/browser/browser/zen-split-view.ftl b/locales/id/browser/browser/zen-split-view.ftl index 9184dc5cd..c1785a709 100644 --- a/locales/id/browser/browser/zen-split-view.ftl +++ b/locales/id/browser/browser/zen-split-view.ftl @@ -5,8 +5,9 @@ tab-zen-split-tabs = .label = { $tabCount -> - [1] Belah Tab (pilih beberapa tab dulu) - *[other] Belah { $tabCount } Tab + [-1] Split out tab + [1] Add split view... + *[other] Join { $tabCount } Tabs } .accesskey = S zen-split-link = diff --git a/locales/id/browser/browser/zen-vertical-tabs.ftl b/locales/id/browser/browser/zen-vertical-tabs.ftl index edb8c6381..b8a94f2a5 100644 --- a/locales/id/browser/browser/zen-vertical-tabs.ftl +++ b/locales/id/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = Sembunyikan keduanya .accesskey = H +zen-toolbar-context-move-to-folder = + .label = Move to Folder... + .accesskey = M zen-toolbar-context-new-folder = .label = Folder Baru .accesskey = N @@ -38,7 +41,7 @@ tabbrowser-reset-pin-button = } zen-tab-sublabel = { $tabSubtitle -> - [zen-default-pinned] Back to pinned url - [zen-default-pinned-cmd] Separate from pinned tab + [zen-default-pinned] Kembali ke URL Awal + [zen-default-pinned-cmd] Pisahkan dari tab tersemat *[other] { $tabSubtitle } } diff --git a/locales/id/browser/browser/zen-welcome.ftl b/locales/id/browser/browser/zen-welcome.ftl index 8cebcb240..4bfb2798e 100644 --- a/locales/id/browser/browser/zen-welcome.ftl +++ b/locales/id/browser/browser/zen-welcome.ftl @@ -3,23 +3,23 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. zen-welcome-title-line1 = Selamat datang di -zen-welcome-title-line2 = internet yang tenang +zen-welcome-title-line2 = internet yang lebih tenang zen-welcome-import-title = Permulaan baru, Bookmark tetap sama zen-welcome-import-description-1 = Bookmark, histori dan password anda adalah Jejak remah roti yang anda tinggalkan di internet--jangan tinggalkan begitu saja! zen-welcome-import-description-2 = Semudah mengambil dari peramban lain dan melanjutkan dari yang anda tinggalkan. zen-welcome-import-button = Impor sekarang -zen-welcome-set-default-browser = Atur { -brand-short-name } sebagai peramban bawaan -zen-welcome-dont-set-default-browser = Jangan membuat { -brand-short-name } sebagai peramban bawaan -zen-welcome-initial-essentials-title = Tab penting anda, akan selalu dalam genggaman -zen-welcome-initial-essentials-description-1 = Simpan tab penting anda mudah diakses dan berada dalam jangkauan anda, seberapapun anda membukanya. -zen-welcome-initial-essentials-description-2 = Tab penting selalu terlihat, dimanapun tempat kerja anda. -zen-welcome-workspace-colors-title = Ruang Kerja anda, Warna anda +zen-welcome-set-default-browser = Jadikan { -brand-short-name } sebagai peramban baku +zen-welcome-dont-set-default-browser = Jangan jadikan { -brand-short-name } sebagai peramban baku +zen-welcome-initial-essentials-title = Tab Penting Anda, Selalu dalam Jangkauan +zen-welcome-initial-essentials-description-1 = Pastikan tab terpenting Anda mudah diakses dan selalu tersedia, berapa pun jumlah tab yang Anda buka. +zen-welcome-initial-essentials-description-2 = Tab esensial selalu terlihat, di ruang kerja mana pun Anda berada. +zen-welcome-workspace-colors-title = Ruang Kerja Anda, Warna Anda zen-welcome-workspace-colors-description = Atur peramban anda dengan memberikan warna tersendiri tiap ruang kerja. zen-welcome-start-browsing-title = Semua Pengaturan selesai?
Mari mulai menjelajah! zen-welcome-start-browsing-description-1 = Anda selesai melakukan pengaturan dan siap untuk mulai. Klik tombol di bawah ini untuk mulai menjelajah dengan { -brand-short-name }. -zen-welcome-start-browsing = Penyelaman dimulai! +zen-welcome-start-browsing = Ayo, mulai! zen-welcome-default-search-title = Mesin Pencari Default Anda zen-welcome-default-search-description = Pilih mesin pencari default Anda. Anda selalu dapat mengubahnya nanti! zen-welcome-skip-button = Lewati diff --git a/locales/id/browser/browser/zen-workspaces.ftl b/locales/id/browser/browser/zen-workspaces.ftl index 138955863..abc634e71 100644 --- a/locales/id/browser/browser/zen-workspaces.ftl +++ b/locales/id/browser/browser/zen-workspaces.ftl @@ -25,7 +25,7 @@ zen-workspaces-panel-context-default-profile = zen-workspaces-panel-unload = .label = Lepaskan Ruang zen-workspaces-panel-unload-others = - .label = Unload All Other Spaces + .label = Lepaskan Ruang Lainnya zen-workspaces-how-to-reorder-title = Cara Mengurutkan Ulang Ruang zen-workspaces-how-to-reorder-desc = Seret ikon ruang di bagian bawah bilah sisi untuk menyusun ulang urutannya zen-workspaces-change-theme = @@ -61,7 +61,7 @@ zen-workspace-creation-profile = Profil .tooltiptext = Profil (Kontainer) digunakan untuk memisahkan cookie dan data situs antar Ruang. zen-workspace-creation-header = Buat sebuah Ruang zen-workspace-creation-label = Ruang digunakan untuk mengorganisasikan tab dan sesi Anda. -zen-workspaces-delete-workspace-title = Delete Workspace? +zen-workspaces-delete-workspace-title = Hapus Ruang? zen-workspaces-delete-workspace-body = Apakah Anda yakin ingin menghapus { $name }? Tindakan ini tidak bisa dibatalkan. # Note that the html tag MUST not be changed or removed, as it is used to better # display the shortcut in the toast notification. diff --git a/locales/is/browser/browser/preferences/zen-preferences.ftl b/locales/is/browser/browser/preferences/zen-preferences.ftl index 8e3de3969..db2e11183 100644 --- a/locales/is/browser/browser/preferences/zen-preferences.ftl +++ b/locales/is/browser/browser/preferences/zen-preferences.ftl @@ -18,7 +18,7 @@ sync-engine-workspaces = .accesskey = V zen-glance-title = Skyndisýn zen-glance-header = Almennar stillingar fyrir skyndisýn -zen-glance-description = Fáðu fljótlegt heildarsýn yfir tenglana þína án þess að þurfa að opna þá í nýjum flipum +zen-glance-description = Fáðu fljótlegt heildaryfirlit á tenglana þína án þess að þurfa að opna þá í nýjum flipum zen-glance-trigger-label = Virkjunaraðferð zen-glance-enabled = .label = Virkja Skyndisýn @@ -30,7 +30,7 @@ zen-glance-trigger-shift-click = .label = Shift + smella zen-glance-trigger-meta-click = .label = Meta (Slaufa) + smella -zen-look-and-feel-compact-view-header = Sýna í þjappaðri sýn +zen-look-and-feel-compact-view-header = Sýna í þjöppuðu yfirliti zen-look-and-feel-compact-view-description = Aðeins birta verkfærastikurnar sem þú notar! zen-look-and-feel-compact-view-enabled = .label = Virkja þjappað viðmót í { -brand-short-name } @@ -181,7 +181,7 @@ zenCKSOption-group-mediaAndDisplay = Myndefni og birting zenCKSOption-group-zen-compact-mode = Þjappað viðmót zenCKSOption-group-zen-workspace = Vinnusvæði zenCKSOption-group-zen-other = Aðrir eiginleikar Zen -zenCKSOption-group-zen-split-view = Klofningarsýn +zenCKSOption-group-zen-split-view = Klofið yfirlit zenCKSOption-group-devTools = Forritunarverkfæri zen-key-quick-restart = Fljótleg endurræsing zen-window-new-shortcut = Nýr gluggi @@ -283,11 +283,11 @@ zen-workspace-shortcut-forward = Vinnusvæði áfram zen-workspace-shortcut-backward = Vinnusvæði afturábak zen-sidebar-shortcut-toggle = Víxla breidd hliðarspjalds zen-pinned-tab-shortcut-reset = Endursetja festan flipa á festa slóð -zen-split-view-shortcut-grid = Víxla reitaskiptri klofningarsýn -zen-split-view-shortcut-vertical = Víxla lóðréttri klofningarsýn -zen-split-view-shortcut-horizontal = Víxla láréttri klofningarsýn -zen-split-view-shortcut-unsplit = Hætta klofningarsýn -zen-new-empty-split-view-shortcut = Ný auð klofningarsýn +zen-split-view-shortcut-grid = Víxla reitaskiptu klofnu yfirliti +zen-split-view-shortcut-vertical = Víxla lóðréttu klofnu yfirliti +zen-split-view-shortcut-horizontal = Víxla láréttu klofnu yfirliti +zen-split-view-shortcut-unsplit = Loka klofnu yfirliti +zen-new-empty-split-view-shortcut = Nýtt autt klofið yfirlit zen-key-select-tab-1 = Velja flipa #1 zen-key-select-tab-2 = Velja flipa #2 zen-key-select-tab-3 = Velja flipa #3 @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = Víxla DOM af/á zen-devtools-toggle-accessibility-shortcut = Víxla auðvelduðu aðgengi af/á zen-close-all-unpinned-tabs-shortcut = Loka öllum ófestum flipum zen-new-unsynced-window-shortcut = Nýr auður gluggi +zen-duplicate-tab-shortcut = Tvítaka flipa diff --git a/locales/is/browser/browser/zen-general.ftl b/locales/is/browser/browser/zen-general.ftl index 5b5bc95f4..39f8aa763 100644 --- a/locales/is/browser/browser/zen-general.ftl +++ b/locales/is/browser/browser/zen-general.ftl @@ -14,7 +14,7 @@ tab-context-zen-reset-pinned-tab = tab-context-zen-add-essential = .label = Bæta við þarfaflipa .accesskey = F -tab-context-zen-add-essential-badge = { $num } / { $max } hólf fyllt +tab-context-zen-add-essential-badge = { $num } / { $max } tab-context-zen-remove-essential = .label = Fjarlægja úr þarfaflipum .accesskey = R @@ -48,6 +48,7 @@ zen-pinned-tab-replaced = URL-slóð festa flipans hefur verið skipt út með f zen-tabs-renamed = Tókst að endurnefna flipann! zen-background-tab-opened-toast = Nýr bakgrunnsflipi opnaður! zen-workspace-renamed-toast = Tókst að endurnefna vinnusvæðið! +zen-split-view-limit-toast = Get ekki bætt fleiri spjöldum á klofið yfirlit! zen-toggle-compact-mode-button = .label = Þjappað viðmót .tooltiptext = Víxla þjöppuðu viðmóti diff --git a/locales/is/browser/browser/zen-split-view.ftl b/locales/is/browser/browser/zen-split-view.ftl index 47e317d79..04158c866 100644 --- a/locales/is/browser/browser/zen-split-view.ftl +++ b/locales/is/browser/browser/zen-split-view.ftl @@ -5,13 +5,14 @@ tab-zen-split-tabs = .label = { $tabCount -> - [1] Skipta tengli (margir valdir flipar nauðsynlegir) - *[other] Skipta { $tabCount } flipum + [-1] Kljúfa út flipa + [1] Bæta við klofnu yfirliti... + *[other] Sameina { $tabCount } flipa } .accesskey = S zen-split-link = .label = Kljúfa tengil í nýjan flipa .accesskey = S -zen-split-view-modifier-header = Klofningarsýn +zen-split-view-modifier-header = Klofið yfirlit zen-split-view-modifier-activate-reallocation = .label = Virkja umröðun diff --git a/locales/is/browser/browser/zen-vertical-tabs.ftl b/locales/is/browser/browser/zen-vertical-tabs.ftl index 422618c21..15fa498b3 100644 --- a/locales/is/browser/browser/zen-vertical-tabs.ftl +++ b/locales/is/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = Fela bæði .accesskey = H +zen-toolbar-context-move-to-folder = + .label = Færa í möppu... + .accesskey = M zen-toolbar-context-new-folder = .label = Ný mappa .accesskey = N diff --git a/locales/it/browser/browser/preferences/zen-preferences.ftl b/locales/it/browser/browser/preferences/zen-preferences.ftl index a1ab688e8..91ddbba1b 100644 --- a/locales/it/browser/browser/preferences/zen-preferences.ftl +++ b/locales/it/browser/browser/preferences/zen-preferences.ftl @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = Attiva/Disattiva DOM zen-devtools-toggle-accessibility-shortcut = Attiva/Disattiva Accessibilità zen-close-all-unpinned-tabs-shortcut = Chiudi Tutte Le Schede Non Bloccate zen-new-unsynced-window-shortcut = New Unsynced Window +zen-duplicate-tab-shortcut = Duplicate Tab diff --git a/locales/it/browser/browser/zen-general.ftl b/locales/it/browser/browser/zen-general.ftl index 9e06ec892..b4b052db6 100644 --- a/locales/it/browser/browser/zen-general.ftl +++ b/locales/it/browser/browser/zen-general.ftl @@ -48,6 +48,7 @@ zen-pinned-tab-replaced = L'URL della scheda bloccata è stato sostituito con l' zen-tabs-renamed = La scheda è stata rinominata con successo! zen-background-tab-opened-toast = Nuova scheda aperta in background! zen-workspace-renamed-toast = Il Workspace è stato rinominato con successo! +zen-split-view-limit-toast = Can't add more panels to the split view! zen-toggle-compact-mode-button = .label = Modalità compatta .tooltiptext = Attiva/disattiva Modalità compatta diff --git a/locales/it/browser/browser/zen-split-view.ftl b/locales/it/browser/browser/zen-split-view.ftl index e0958b3ce..27af93a9b 100644 --- a/locales/it/browser/browser/zen-split-view.ftl +++ b/locales/it/browser/browser/zen-split-view.ftl @@ -5,8 +5,9 @@ tab-zen-split-tabs = .label = { $tabCount -> - [1] Split Tab (sono necessarie più schede selezionate) - *[other] Split { $tabCount } Tabs + [-1] Split out tab + [1] Add split view... + *[other] Join { $tabCount } Tabs } .accesskey = S zen-split-link = diff --git a/locales/it/browser/browser/zen-vertical-tabs.ftl b/locales/it/browser/browser/zen-vertical-tabs.ftl index d5c8357e4..ca8bf28a1 100644 --- a/locales/it/browser/browser/zen-vertical-tabs.ftl +++ b/locales/it/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = Nascondi entrambi .accesskey = H +zen-toolbar-context-move-to-folder = + .label = Move to Folder... + .accesskey = M zen-toolbar-context-new-folder = .label = Nuova cartella .accesskey = N diff --git a/locales/ja/browser/browser/preferences/zen-preferences.ftl b/locales/ja/browser/browser/preferences/zen-preferences.ftl index a3cba696b..feab7607a 100644 --- a/locales/ja/browser/browser/preferences/zen-preferences.ftl +++ b/locales/ja/browser/browser/preferences/zen-preferences.ftl @@ -5,7 +5,7 @@ pane-zen-looks-title = 外観 category-zen-looks = .tooltiptext = { pane-zen-looks-title }{ pane-zen-looks-title } -zen-warning-language = デフォルト言語を変更すると、ウェブサイトがあなたにトラッキングしやすいです。 +zen-warning-language = デフォルト言語を変更すると、ウェブサイトにトラッキングされやすくなります。 zen-vertical-tabs-layout-header = ブラウザーのレイアウト zen-vertical-tabs-layout-description = 自分に合ったレイアウトを選んでください zen-layout-single-toolbar = サイドバーのみ @@ -43,11 +43,11 @@ category-zen-workspaces = .tooltiptext = { pane-zen-tabs-title } pane-settings-workspaces-title = ワークスペース zen-tabs-select-recently-used-on-close = - .label = When closing a tab, switch to the most recently used tab instead of the next tab + .label = タブを閉じるときに、次のタブではなく最後に使用したタブに切り替える zen-tabs-close-on-back-with-no-history = .label = タブを閉じ、履歴がない状態で戻るときに所有者のタブ(または最近使用したタブ)に切り替えます zen-settings-workspaces-sync-unpinned-tabs = - .label = Sync only pinned tabs in workspaces + .label = ワークスペースのタブのうちピン留めされているタブのみを同期する zen-tabs-cycle-by-attribute = .label = Ctrl+Tabキーを押してワークスペースタブ内または重要なタブ内のサイクルを切り替えます zen-tabs-cycle-ignore-pending-tabs = @@ -57,11 +57,11 @@ zen-look-and-feel-compact-toolbar-themed = .label = コンパクトツールバーにテーマの背景を使用する zen-workspace-continue-where-left-off = .label = 中断したところから再開する -pane-zen-pinned-tab-manager-title = 固定したタブ -zen-pinned-tab-manager-header = 固定したタブの一般的な設定 -zen-pinned-tab-manager-description = 固定したタブの追加的な動作を管理する +pane-zen-pinned-tab-manager-title = ピン留めされたタブ +zen-pinned-tab-manager-header = ピン留めされたタブの設定 +zen-pinned-tab-manager-description = ピン留めされたタブの追加的な動作を管理する zen-pinned-tab-manager-restore-pinned-tabs-to-pinned-url = - .label = 固定したタブを起動時に元の固定したURLに復元します + .label = ピン留めされたタブを起動時に元のピン留めされた URL に復元します zen-pinned-tab-manager-container-specific-essentials-enabled = .label = コンテナ固有のEssentialsを有効にする zen-pinned-tab-manager-close-shortcut-behavior-label = タブを閉じるショートカットの動作 @@ -79,7 +79,7 @@ zen-pinned-tab-manager-close-close-shortcut-option = .label = タブを閉じる pane-zen-workspaces-header = ワークスペース zen-settings-workspaces-header = ワークスペースの一般的な設定 -zen-settings-workspaces-description = ワークスペースを使用すると、一度に複数のブラウジングセッションがあるといいです! +zen-settings-workspaces-description = ワークスペースを使用すると、同時に複数のブラウジングセッションを行うことができます! zen-settings-workspaces-enabled = .label = ワークスペースを有効にする zen-settings-workspaces-hide-default-container-indicator = @@ -279,8 +279,8 @@ zen-workspace-shortcut-switch-7 = ワークスペース7に切り替える zen-workspace-shortcut-switch-8 = ワークスペース8に切り替える zen-workspace-shortcut-switch-9 = ワークスペース9に切り替える zen-workspace-shortcut-switch-10 = ワークスペース10に切り替える -zen-workspace-shortcut-forward = ワークスペースを転送 -zen-workspace-shortcut-backward = 後方ワークスペース +zen-workspace-shortcut-forward = 次のワークスペースに移動 +zen-workspace-shortcut-backward = 前のワークスペースへ移動 zen-sidebar-shortcut-toggle = サイドバーの幅を切り替える zen-pinned-tab-shortcut-reset = ピン留めされたタブをピン留めしたURLにリセット zen-split-view-shortcut-grid = 分割表示グリッドの切り替え @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = DOMの切り替え zen-devtools-toggle-accessibility-shortcut = アクセシビリティの切り替え zen-close-all-unpinned-tabs-shortcut = ピン留めされていないすべてのタブを閉じる zen-new-unsynced-window-shortcut = New Unsynced Window +zen-duplicate-tab-shortcut = タブを複製 diff --git a/locales/ja/browser/browser/zen-general.ftl b/locales/ja/browser/browser/zen-general.ftl index 85b35e249..81f90c814 100644 --- a/locales/ja/browser/browser/zen-general.ftl +++ b/locales/ja/browser/browser/zen-general.ftl @@ -7,8 +7,8 @@ unified-extensions-description = 拡張機能は{ -brand-short-name }に多く tab-context-zen-reset-pinned-tab = .label = { $isEssential -> - [true] Reset Essential Tab - *[false] Reset Pinned Tab + [true] Essentialタブの遷移をリセット + *[false] ピン留めされたタブの遷移をリセット } .accesskey = R tab-context-zen-add-essential = @@ -21,8 +21,8 @@ tab-context-zen-remove-essential = tab-context-zen-replace-pinned-url-with-current = .label = { $isEssential -> - [true] Replace Essential URL with Current - *[false] Replace Pinned URL with Current + [true] EssentialタブのURLを今開いているURLで置き換える + *[false] ピン留めされたタブのURLを今開いているURLで置き換える } .accesskey = C tab-context-zen-edit-title = @@ -41,15 +41,16 @@ pictureinpicture-minimize-btn = .tooltip = 最小化 zen-panel-ui-gradient-generator-custom-color = カスタムカラー zen-copy-current-url-confirmation = URLをクリップボードにコピーしました! -zen-copy-current-url-as-markdown-confirmation = Copied current URL as Markdown! +zen-copy-current-url-as-markdown-confirmation = URLをMarkdownとしてコピーしました! zen-general-cancel-label = .label = キャンセル zen-general-confirm = .label = 確定 zen-pinned-tab-replaced = 固定したタブのURLが現在のURLに置き換えられました! -zen-tabs-renamed = タグの名前は無事に変更されました! +zen-tabs-renamed = タブの名前は無事に変更されました! zen-background-tab-opened-toast = 新しい背景タブが開きました! zen-workspace-renamed-toast = ワークスペースの名前が変更されました! +zen-split-view-limit-toast = 分割ビューにこれ以上パネルを追加できません! zen-toggle-compact-mode-button = .label = コンパクトモード .tooltiptext = コンパクトモードの切り替え diff --git a/locales/ja/browser/browser/zen-split-view.ftl b/locales/ja/browser/browser/zen-split-view.ftl index d5c224f25..85df2f982 100644 --- a/locales/ja/browser/browser/zen-split-view.ftl +++ b/locales/ja/browser/browser/zen-split-view.ftl @@ -5,8 +5,9 @@ tab-zen-split-tabs = .label = { $tabCount -> - [1] タブを分割する(2つ以上のタブを選択してください) - *[other] 選択したタブを{ $tabCount }つに分割する + [-1] 分割タブ + [1] 分割ビューを追加... + *[other] { $tabCount } 個のタブを結合するs } .accesskey = S zen-split-link = diff --git a/locales/ja/browser/browser/zen-vertical-tabs.ftl b/locales/ja/browser/browser/zen-vertical-tabs.ftl index d6fcad712..dc726b4bb 100644 --- a/locales/ja/browser/browser/zen-vertical-tabs.ftl +++ b/locales/ja/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = サイドバーとツールバーを隠す .accesskey = H +zen-toolbar-context-move-to-folder = + .label = フォルダに移動する… + .accesskey = M zen-toolbar-context-new-folder = .label = 新しいフォルダ .accesskey = N @@ -34,11 +37,11 @@ tabbrowser-reset-pin-button = .tooltiptext = { $tabCount -> [one] タブをリセットして固定する - *[other] タブをリセットして{ $tabCount }つタブを固定する + *[other] タブをリセットして{ $tabCount }つのタブを固定する } zen-tab-sublabel = { $tabSubtitle -> - [zen-default-pinned] Back to pinned url - [zen-default-pinned-cmd] Separate from pinned tab + [zen-default-pinned] 固定された URL に戻る + [zen-default-pinned-cmd] 固定されたタブから切り離す *[other] { $tabSubtitle } } diff --git a/locales/ja/browser/browser/zen-workspaces.ftl b/locales/ja/browser/browser/zen-workspaces.ftl index 3310fe1e0..5af1417c3 100644 --- a/locales/ja/browser/browser/zen-workspaces.ftl +++ b/locales/ja/browser/browser/zen-workspaces.ftl @@ -2,7 +2,7 @@ # 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/. -zen-panel-ui-workspaces-text = スペース +zen-panel-ui-workspaces-text = ワークスペース zen-panel-ui-spaces-label = .label = スペース zen-panel-ui-workspaces-create = @@ -25,7 +25,7 @@ zen-workspaces-panel-context-default-profile = zen-workspaces-panel-unload = .label = スペースをアンロードする zen-workspaces-panel-unload-others = - .label = Unload All Other Spaces + .label = 他のスペースをアンロードする zen-workspaces-how-to-reorder-title = 並べ替える方法 zen-workspaces-how-to-reorder-desc = サイドバーの下部にあるスペースアイコンをドラッグして並べ替えます zen-workspaces-change-theme = @@ -68,7 +68,7 @@ zen-workspaces-delete-workspace-body = { $name }を削除してもよろしい zen-workspaces-close-all-unpinned-tabs-toast = タブを閉じました!元に戻すには、 { $shortcut }を使用してください。 zen-workspaces-close-all-unpinned-tabs-title = .label = 削除する - .tooltiptext = すべての固定しなかったタブを閉じる + .tooltiptext = すべてのピン留めされていないタブを閉じる zen-panel-ui-workspaces-change-forward = .label = 次のスペースに移動 zen-panel-ui-workspaces-change-back = diff --git a/locales/ko/browser/browser/preferences/zen-preferences.ftl b/locales/ko/browser/browser/preferences/zen-preferences.ftl index 143922cf1..a1f34396e 100644 --- a/locales/ko/browser/browser/preferences/zen-preferences.ftl +++ b/locales/ko/browser/browser/preferences/zen-preferences.ftl @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = DOM 탭 전환 zen-devtools-toggle-accessibility-shortcut = 접근성 탭 전환 zen-close-all-unpinned-tabs-shortcut = 모든 고정되지 않은 탭 닫기 zen-new-unsynced-window-shortcut = 새 비동기화 창 +zen-duplicate-tab-shortcut = 탭 복제 diff --git a/locales/ko/browser/browser/zen-general.ftl b/locales/ko/browser/browser/zen-general.ftl index 3c99a323a..dd2018713 100644 --- a/locales/ko/browser/browser/zen-general.ftl +++ b/locales/ko/browser/browser/zen-general.ftl @@ -50,6 +50,7 @@ zen-pinned-tab-replaced = 고정 URL이 현재 URL로 변경되었습니다! zen-tabs-renamed = 탭의 이름이 성공적으로 변경되었습니다! zen-background-tab-opened-toast = 새 백그라운드 탭이 열렸습니다! zen-workspace-renamed-toast = 워크스페이스 이름이 변경되었습니다! +zen-split-view-limit-toast = 패널을 더 추가할 수 없습니다! zen-toggle-compact-mode-button = .label = 사이드바 축소 모드 .tooltiptext = 사이드바 축소 토글 diff --git a/locales/ko/browser/browser/zen-split-view.ftl b/locales/ko/browser/browser/zen-split-view.ftl index e742efdf2..ac933dac1 100644 --- a/locales/ko/browser/browser/zen-split-view.ftl +++ b/locales/ko/browser/browser/zen-split-view.ftl @@ -5,8 +5,9 @@ tab-zen-split-tabs = .label = { $tabCount -> - [1] 탭 나누기 (여러 탭이 선택되어 있어야 함) - *[other] 탭 { $tabCount }개 나누기 + [-1] 탭 나누기 + [1] 나눠진 탭 추가... + *[other] { $tabCount }개 탭 합치기 } .accesskey = S zen-split-link = diff --git a/locales/ko/browser/browser/zen-vertical-tabs.ftl b/locales/ko/browser/browser/zen-vertical-tabs.ftl index 269f6a93b..90b619006 100644 --- a/locales/ko/browser/browser/zen-vertical-tabs.ftl +++ b/locales/ko/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = 둘 다 숨기기 .accesskey = H +zen-toolbar-context-move-to-folder = + .label = 폴더로 옮기기... + .accesskey = M zen-toolbar-context-new-folder = .label = 새 폴더 .accesskey = N diff --git a/locales/lt/browser/browser/preferences/zen-preferences.ftl b/locales/lt/browser/browser/preferences/zen-preferences.ftl index 7a9822f54..47df9d551 100644 --- a/locales/lt/browser/browser/preferences/zen-preferences.ftl +++ b/locales/lt/browser/browser/preferences/zen-preferences.ftl @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = Perjungti DOM zen-devtools-toggle-accessibility-shortcut = Perjungti prieinamumą zen-close-all-unpinned-tabs-shortcut = Užverti visas neprisegtas korteles zen-new-unsynced-window-shortcut = Naujas tuščias langas +zen-duplicate-tab-shortcut = Dubliuoti kortelę diff --git a/locales/lt/browser/browser/zen-general.ftl b/locales/lt/browser/browser/zen-general.ftl index 376ff37ff..17f8c20b2 100644 --- a/locales/lt/browser/browser/zen-general.ftl +++ b/locales/lt/browser/browser/zen-general.ftl @@ -50,6 +50,7 @@ zen-pinned-tab-replaced = Prisegtos kortelės URL pakeistas dabartiniu URL. zen-tabs-renamed = Kortelė sėkmingai pervadinta. zen-background-tab-opened-toast = Nauja fonos kortelė atverta. zen-workspace-renamed-toast = Darbo sritis sėkmingai pervadintas. +zen-split-view-limit-toast = Negalima įtraukti daugiau skydelių į suskaidytą rodinį. zen-toggle-compact-mode-button = .label = Kompaktinis režimas .tooltiptext = Perjungti kompaktinį režimą diff --git a/locales/lt/browser/browser/zen-split-view.ftl b/locales/lt/browser/browser/zen-split-view.ftl index 1e309f3dd..dd0d361bd 100644 --- a/locales/lt/browser/browser/zen-split-view.ftl +++ b/locales/lt/browser/browser/zen-split-view.ftl @@ -5,11 +5,12 @@ tab-zen-split-tabs = .label = { $tabCount -> - [1] Skaidyti kortelę (reikia kelių pasirinktų kortelių) - [one] Skaidyti { $tabCount } kortelę - [few] Skaidyti { $tabCount } korteles - [many] Skaidyti { $tabCount } kortelės - *[other] Skaidyti { $tabCount } kortelių + [-1] Išskaidyti kortelę + [1] Įtraukti skaidymo rodinį + [one] Sujungti { $tabCount } kortelę + [few] Sujungti { $tabCount } korteles + [many] Sujungti { $tabCount } kortelės + *[other] Sujungti { $tabCount } kortelių } .accesskey = S zen-split-link = diff --git a/locales/lt/browser/browser/zen-vertical-tabs.ftl b/locales/lt/browser/browser/zen-vertical-tabs.ftl index 52542643a..4c8929735 100644 --- a/locales/lt/browser/browser/zen-vertical-tabs.ftl +++ b/locales/lt/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = Slėpti abi .accesskey = H +zen-toolbar-context-move-to-folder = + .label = Perkelti į aplanką... + .accesskey = M zen-toolbar-context-new-folder = .label = Naujas aplankas .accesskey = N diff --git a/locales/nb/browser/browser/preferences/zen-preferences.ftl b/locales/nb/browser/browser/preferences/zen-preferences.ftl index d34e4a570..0feb70cca 100644 --- a/locales/nb/browser/browser/preferences/zen-preferences.ftl +++ b/locales/nb/browser/browser/preferences/zen-preferences.ftl @@ -316,4 +316,5 @@ zen-devtools-toggle-storage-shortcut = Veksle Lagring zen-devtools-toggle-dom-shortcut = Veksle DOM zen-devtools-toggle-accessibility-shortcut = Veksle Tilgjengelighet zen-close-all-unpinned-tabs-shortcut = Lukk alle ufestede faner -zen-new-unsynced-window-shortcut = Nytt usynkronisert vindu +zen-new-unsynced-window-shortcut = Nytt tomt vindu +zen-duplicate-tab-shortcut = Dupliser fane diff --git a/locales/nb/browser/browser/zen-general.ftl b/locales/nb/browser/browser/zen-general.ftl index 16e27e1d2..97afeee60 100644 --- a/locales/nb/browser/browser/zen-general.ftl +++ b/locales/nb/browser/browser/zen-general.ftl @@ -2,26 +2,26 @@ # 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/. -zen-panel-ui-current-profile-text = nåværende profil -unified-extensions-description = Utvidelser er brukt for å bringe mer ekstra funksjonalitet til { -brand-short-name }. +zen-panel-ui-current-profile-text = gjeldende profil +unified-extensions-description = Utvidelser brukes for å bringe ekstra funksjonalitet til { -brand-short-name }. tab-context-zen-reset-pinned-tab = .label = { $isEssential -> - [true] Nullstill essensiell fane + [true] Nullstill Essential fane *[false] Nullstill festet fane } .accesskey = R tab-context-zen-add-essential = - .label = Legg til i essensielle + .label = Legg til i Essentials .accesskey = E -tab-context-zen-add-essential-badge = { $num } / { $max } felt utfylt +tab-context-zen-add-essential-badge = { $num } / { $max } tab-context-zen-remove-essential = - .label = Fjern fra essensielle + .label = Fjern fra Essentials .accesskey = R tab-context-zen-replace-pinned-url-with-current = .label = { $isEssential -> - [true] Erstatt essensiell nettadresse med gjeldende + [true] Erstatt Essential nettadresse med gjeldende *[false] Erstatt festet nettadresse med gjeldende } .accesskey = C @@ -30,7 +30,7 @@ tab-context-zen-edit-title = tab-context-zen-edit-icon = .label = Endre ikon... zen-themes-corrupted = Din { -brand-short-name }-mods fil er skadet. De har blitt tilbakestilt til standardtemaet. -zen-shortcuts-corrupted = { -brand-short-name } snarvei-filen din er skadet. De har blitt tilbakestilt til standard snarveier. +zen-shortcuts-corrupted = { -brand-short-name } snarvei-filen din er skadet. De har blitt tilbakestilt til standard-snarveiene. # note: Do not translate the "
" tags in the following string zen-new-urlbar-notification = Det nye nettadressefeltet har blitt aktivert, som tar vekk behovet for nye fanesider.

@@ -46,13 +46,14 @@ zen-general-cancel-label = .label = Avbryt zen-general-confirm = .label = Bekreft -zen-pinned-tab-replaced = Nettadressen til den festede fanen har blitt erstattet med gjeldende nettadresse! +zen-pinned-tab-replaced = Festet fanes nettadresse har blit erstattet med gjeldende nettadresse! zen-tabs-renamed = Fanen har fått nytt navn! zen-background-tab-opened-toast = Ny bakgrunnsfane åpnet! zen-workspace-renamed-toast = Arbeidsområdet har fått nytt navn! +zen-split-view-limit-toast = Kan ikke legge til flere paneler i delt visning! zen-toggle-compact-mode-button = - .label = Kompaktmodus - .tooltiptext = Veksle kompaktmodus + .label = Kompakt modus + .tooltiptext = Veksle kompakt modus # note: Do not translate the "
" tags in the following string @@ -69,7 +70,7 @@ zen-site-data-settings = Innstillinger zen-generic-manage = Behandle zen-generic-more = Mer zen-generic-next = Neste -zen-essentials-promo-label = Legg til i essensielle +zen-essentials-promo-label = Legg til i Essentials zen-essentials-promo-sublabel = Hold favorittfanene dine bare et klikk unna # These labels will be used for the site data panel settings zen-site-data-setting-allow = Tillat diff --git a/locales/nb/browser/browser/zen-split-view.ftl b/locales/nb/browser/browser/zen-split-view.ftl index 8010baa96..93bf95ad6 100644 --- a/locales/nb/browser/browser/zen-split-view.ftl +++ b/locales/nb/browser/browser/zen-split-view.ftl @@ -5,8 +5,9 @@ tab-zen-split-tabs = .label = { $tabCount -> - [1] Del fane (flere valgte faner trengs) - *[other] Del { $tabCount } faner + [-1] Fordelt fane + [1] Legg til delt visning + *[other] Spleis { $tabCount } faner } .accesskey = S zen-split-link = diff --git a/locales/nb/browser/browser/zen-vertical-tabs.ftl b/locales/nb/browser/browser/zen-vertical-tabs.ftl index 434f68633..c5e89d3e1 100644 --- a/locales/nb/browser/browser/zen-vertical-tabs.ftl +++ b/locales/nb/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = Skjul begge .accesskey = H +zen-toolbar-context-move-to-folder = + .label = Flytt til mappe... + .accesskey = Flytt zen-toolbar-context-new-folder = .label = Ny mappe .accesskey = N diff --git a/locales/nb/browser/browser/zen-welcome.ftl b/locales/nb/browser/browser/zen-welcome.ftl index 43f4ea7f9..e29b123af 100644 --- a/locales/nb/browser/browser/zen-welcome.ftl +++ b/locales/nb/browser/browser/zen-welcome.ftl @@ -6,7 +6,7 @@ zen-welcome-title-line1 = Velkommen til zen-welcome-title-line2 = et roligere internett zen-welcome-import-title = En ny start, samme bokmerker zen-welcome-import-description-1 = Bokmerker, historikken og passordene dine er som en sti av brødsmuler gjennom internettet — ikke la dem ligge igjen! -zen-welcome-import-description-2 = Overfør dem enkelt fra en annen nettleser og fortsett akkuratt der du slapp. +zen-welcome-import-description-2 = Overfør dem enkelt fra en annen nettleser og fortsett akkurat der du slapp. zen-welcome-import-button = Importer nå zen-welcome-set-default-browser = Sett { -brand-short-name } som din standard nettleser zen-welcome-dont-set-default-browser = IKKE sett { -brand-short-name } som din standard nettleser diff --git a/locales/nl/browser/browser/preferences/zen-preferences.ftl b/locales/nl/browser/browser/preferences/zen-preferences.ftl index 281e9755e..1db60e73f 100644 --- a/locales/nl/browser/browser/preferences/zen-preferences.ftl +++ b/locales/nl/browser/browser/preferences/zen-preferences.ftl @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = DOM togglen zen-devtools-toggle-accessibility-shortcut = Toegankelijkheid togglen zen-close-all-unpinned-tabs-shortcut = Alle niet-vastgezette tabbladen sluiten zen-new-unsynced-window-shortcut = Nieuw leeg venster +zen-duplicate-tab-shortcut = Tabblad dupliceren diff --git a/locales/nl/browser/browser/zen-general.ftl b/locales/nl/browser/browser/zen-general.ftl index 0f6bb7a16..045faced3 100644 --- a/locales/nl/browser/browser/zen-general.ftl +++ b/locales/nl/browser/browser/zen-general.ftl @@ -14,7 +14,7 @@ tab-context-zen-reset-pinned-tab = tab-context-zen-add-essential = .label = Toevoegen aan Essentials .accesskey = E -tab-context-zen-add-essential-badge = { $num } / { $max } plekken gevuld +tab-context-zen-add-essential-badge = { $num } / { $max } plekken bezet tab-context-zen-remove-essential = .label = Verwijderen uit Essentials .accesskey = R @@ -50,6 +50,7 @@ zen-pinned-tab-replaced = Vastgemaakte tabblad URL is vervangen met de huidige U zen-tabs-renamed = Tabblad is succesvol hernoemd! zen-background-tab-opened-toast = Nieuw achtergrondtabblad geopend! zen-workspace-renamed-toast = Werkruimte succesvol is hernoemd! +zen-split-view-limit-toast = Kan geen panelen meer toevoegen aan de gesplitste weergave! zen-toggle-compact-mode-button = .label = Compacte modus .tooltiptext = Compacte modus togglen diff --git a/locales/nl/browser/browser/zen-menubar.ftl b/locales/nl/browser/browser/zen-menubar.ftl index f42fbea90..7757e34f2 100644 --- a/locales/nl/browser/browser/zen-menubar.ftl +++ b/locales/nl/browser/browser/zen-menubar.ftl @@ -19,4 +19,4 @@ zen-menubar-appearance-light = zen-menubar-appearance-dark = .label = Donker zen-menubar-new-blank-window = - .label = New Blank Window + .label = Nieuw blanco venster diff --git a/locales/nl/browser/browser/zen-split-view.ftl b/locales/nl/browser/browser/zen-split-view.ftl index 9f0121fba..e700a9f78 100644 --- a/locales/nl/browser/browser/zen-split-view.ftl +++ b/locales/nl/browser/browser/zen-split-view.ftl @@ -5,8 +5,9 @@ tab-zen-split-tabs = .label = { $tabCount -> - [1] Splits tabblad (meerdere geselecteerde tabbladen vereist) - *[other] Splits { $tabCount } tabbladen + [-1] Splits tabbladen op + [1] Voeg gesplitste weergave toe... + *[other] Voeg { $tabCount } tabbladen samen } .accesskey = S zen-split-link = diff --git a/locales/nl/browser/browser/zen-vertical-tabs.ftl b/locales/nl/browser/browser/zen-vertical-tabs.ftl index b46aaaca8..bd2250a67 100644 --- a/locales/nl/browser/browser/zen-vertical-tabs.ftl +++ b/locales/nl/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = Beide verbergen .accesskey = U +zen-toolbar-context-move-to-folder = + .label = Verplaats naar map... + .accesskey = M zen-toolbar-context-new-folder = .label = Nieuwe map .accesskey = N @@ -38,7 +41,7 @@ tabbrowser-reset-pin-button = } zen-tab-sublabel = { $tabSubtitle -> - [zen-default-pinned] Back to pinned url - [zen-default-pinned-cmd] Separate from pinned tab + [zen-default-pinned] Terug naar vastgezette URL + [zen-default-pinned-cmd] Afsplitsen van vastgezette tabblad *[other] { $tabSubtitle } } diff --git a/locales/nl/browser/browser/zen-workspaces.ftl b/locales/nl/browser/browser/zen-workspaces.ftl index 2d2529937..11ba1aad7 100644 --- a/locales/nl/browser/browser/zen-workspaces.ftl +++ b/locales/nl/browser/browser/zen-workspaces.ftl @@ -10,7 +10,7 @@ zen-panel-ui-workspaces-create = zen-panel-ui-folder-create = .label = Map aanmaken zen-panel-ui-live-folder-create = - .label = Live Folder + .label = Live map zen-panel-ui-new-empty-split = .label = Nieuwe splitsing zen-workspaces-panel-context-delete = @@ -25,7 +25,7 @@ zen-workspaces-panel-context-default-profile = zen-workspaces-panel-unload = .label = Ruimte ontladen zen-workspaces-panel-unload-others = - .label = Unload All Other Spaces + .label = Alle andere ruimtes ontladen zen-workspaces-how-to-reorder-title = Hoe ruimtes te herschikken zen-workspaces-how-to-reorder-desc = Sleep het ruimte icoon onderaan de zijbalk om ze te herschikken zen-workspaces-change-theme = diff --git a/locales/nn-NO/browser/browser/preferences/zen-preferences.ftl b/locales/nn-NO/browser/browser/preferences/zen-preferences.ftl index a9765ae24..61552982c 100644 --- a/locales/nn-NO/browser/browser/preferences/zen-preferences.ftl +++ b/locales/nn-NO/browser/browser/preferences/zen-preferences.ftl @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = Toggle DOM zen-devtools-toggle-accessibility-shortcut = Toggle Accessibility zen-close-all-unpinned-tabs-shortcut = Close All Unpinned Tabs zen-new-unsynced-window-shortcut = New Unsynced Window +zen-duplicate-tab-shortcut = Duplicate Tab diff --git a/locales/nn-NO/browser/browser/zen-general.ftl b/locales/nn-NO/browser/browser/zen-general.ftl index fed8bc947..f4cd42a80 100644 --- a/locales/nn-NO/browser/browser/zen-general.ftl +++ b/locales/nn-NO/browser/browser/zen-general.ftl @@ -50,6 +50,7 @@ zen-pinned-tab-replaced = Pinned tab URL has been replaced with the current URL. zen-tabs-renamed = Tab has been successfully renamed! zen-background-tab-opened-toast = New background tab opened! zen-workspace-renamed-toast = Workspace has been successfully renamed! +zen-split-view-limit-toast = Can't add more panels to the split view! zen-toggle-compact-mode-button = .label = Compact Mode .tooltiptext = Toggle Compact Mode diff --git a/locales/nn-NO/browser/browser/zen-split-view.ftl b/locales/nn-NO/browser/browser/zen-split-view.ftl index 10593eed9..4430fab34 100644 --- a/locales/nn-NO/browser/browser/zen-split-view.ftl +++ b/locales/nn-NO/browser/browser/zen-split-view.ftl @@ -5,8 +5,9 @@ tab-zen-split-tabs = .label = { $tabCount -> - [1] Split Tab (multiple selected tabs needed) - *[other] Split { $tabCount } Tabs + [-1] Split out tab + [1] Add split view... + *[other] Join { $tabCount } Tabs } .accesskey = S zen-split-link = diff --git a/locales/nn-NO/browser/browser/zen-vertical-tabs.ftl b/locales/nn-NO/browser/browser/zen-vertical-tabs.ftl index 41494167b..cddebc1ee 100644 --- a/locales/nn-NO/browser/browser/zen-vertical-tabs.ftl +++ b/locales/nn-NO/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = Hide both .accesskey = H +zen-toolbar-context-move-to-folder = + .label = Move to Folder... + .accesskey = M zen-toolbar-context-new-folder = .label = New Folder .accesskey = N diff --git a/locales/pl/browser/browser/preferences/zen-preferences.ftl b/locales/pl/browser/browser/preferences/zen-preferences.ftl index 1fd35037c..32fb99b1b 100644 --- a/locales/pl/browser/browser/preferences/zen-preferences.ftl +++ b/locales/pl/browser/browser/preferences/zen-preferences.ftl @@ -5,9 +5,9 @@ pane-zen-looks-title = Wygląd i działanie category-zen-looks = .tooltiptext = { pane-zen-looks-title } -zen-warning-language = Zmiana domyślnego języka może ułatwić śledzenie stron internetowych. -zen-vertical-tabs-layout-header = Wygląd przeglądarki -zen-vertical-tabs-layout-description = Wybierz wygląd, który pasuje Ci najbardziej +zen-warning-language = Zmiana domyślnego języka może ułatwić stronom internetowym śledzenie Twojej aktywności. +zen-vertical-tabs-layout-header = Układ przeglądarki +zen-vertical-tabs-layout-description = Wybierz układ, który najbardziej Ci odpowiada zen-layout-single-toolbar = Tylko pasek boczny zen-layout-multiple-toolbar = Pasek boczny i górny pasek narzędzi zen-layout-collapsed-toolbar = Zwinięty pasek boczny @@ -43,15 +43,15 @@ category-zen-workspaces = .tooltiptext = { pane-zen-tabs-title } pane-settings-workspaces-title = Obszary robocze zen-tabs-select-recently-used-on-close = - .label = Podczas zamykania karty, przejdź do ostatnio używanej karty zamiast do następnej karty + .label = Podczas zamykania karty, przełącz na ostatnio używaną kartę zamiast na następną kartę zen-tabs-close-on-back-with-no-history = - .label = Zamknij kartę i przejdź do karty głównej (lub ostatnio używanej karty) podczas powrotu bez historii + .label = Zamknij kartę i przełącz na kartę nadrzędną (lub ostatnio używaną kartę) podczas cofania bez historii zen-settings-workspaces-sync-unpinned-tabs = .label = Zsynchronizuj tylko przypięte karty w obszarach roboczych zen-tabs-cycle-by-attribute = .label = Ctrl+Tab przełącza tylko między kartami niezbędnymi lub kartami obszaru roboczego zen-tabs-cycle-ignore-pending-tabs = - .label = Ignoruj oczekujące karty podczas przełączania między kartami z Ctrl+Tab + .label = Ignoruj oczekujące karty podczas przełączania się za pomocą skrótu Ctrl+Tab zen-tabs-cycle-by-attribute-warning = Ctrl+Tab będzie przełączał karty według ostatnio używanej kolejności, ponieważ jest włączona zen-look-and-feel-compact-toolbar-themed = .label = Użyj tła motywu dla kompaktowego paska narzędzi @@ -61,18 +61,18 @@ pane-zen-pinned-tab-manager-title = Przypięte karty zen-pinned-tab-manager-header = Ogólne ustawienia przypiętych kart zen-pinned-tab-manager-description = Zarządzaj dodatkowym zachowaniem przypiętych kart zen-pinned-tab-manager-restore-pinned-tabs-to-pinned-url = - .label = Przywróć przypięte karty do pierwotnie przypiętego adresu URL przy starcie + .label = Przywróć przypięte karty do ich pierwotnych adresów URL podczas uruchamiania zen-pinned-tab-manager-container-specific-essentials-enabled = - .label = Włącz podstawowe funkcje specyficzne dla kontenera + .label = Włącz niezbędne karty specyficzne dla kontenerów zen-pinned-tab-manager-close-shortcut-behavior-label = Zachowanie skrótu zamykania karty zen-pinned-tab-manager-reset-unload-switch-close-shortcut-option = - .label = Zresetuj adres URL, wyładuj i przejdź do następnej karty + .label = Zresetuj adres URL, wyładuj i przełącz na następną kartę zen-pinned-tab-manager-unload-switch-close-shortcut-option = - .label = Wyładuj i przejdź do następnej karty + .label = Wyładuj i przełącz na następną kartę zen-pinned-tab-manager-reset-switch-close-shortcut-option = - .label = Zresetuj adres URL i przejdź do następnej karty + .label = Zresetuj adres URL i przełącz na następną kartę zen-pinned-tab-manager-switch-close-shortcut-option = - .label = Przejdź do następnej karty + .label = Przełącz na następną kartę zen-pinned-tab-manager-reset-close-shortcut-option = .label = Zresetuj adres URL zen-pinned-tab-manager-close-close-shortcut-option = @@ -98,9 +98,9 @@ zen-vertical-tabs-newtab-top-button-up = .label = Przenieś przycisk nowej karty na górę zen-vertical-tabs-expand-tabs-by-default = Rozwiń karty domyślnie zen-vertical-tabs-dont-expand-tabs-by-default = Nie rozwijaj domyślnie kart -zen-vertical-tabs-expand-tabs-on-hover = Rozwiń zakładki po najechaniu kursorem (nie działa w trybie kompaktowym) +zen-vertical-tabs-expand-tabs-on-hover = Rozwiń karty po najechaniu kursorem (nie działa w trybie kompaktowym) zen-vertical-tabs-expand-tabs-header = Jak rozwinąć karty -zen-vertical-tabs-expand-tabs-description = Wybierz jak rozwinąć karty na pasku bocznym +zen-vertical-tabs-expand-tabs-description = Wybierz sposób rozwijania kart na pasku bocznym zen-theme-marketplace-header = Modyfikacje Zen zen-theme-disable-all-enabled = .title = Wyłącz wszystkie modyfikacje @@ -137,7 +137,7 @@ pane-zen-marketplace-title = Modyfikacje Zen zen-themes-auto-update = .label = Automatycznie aktualizuj zainstalowane modyfikacje podczas startu przeglądarki zen-settings-workspaces-force-container-tabs-to-workspace = - .label = Przełącz się do obszaru roboczego, w którym kontener jest ustawiony jako domyślny podczas otwierania kart kontenera + .label = Przełącz na obszar roboczy, w którym kontener jest ustawiony jako domyślny podczas otwierania kart kontenera zen-theme-marketplace-link = Odwiedź sklep zen-dark-theme-styles-header = Style ciemnego motywu zen-dark-theme-styles-description = Dostosuj ciemny motyw do swoich upodobań @@ -154,9 +154,9 @@ zen-urlbar-behavior-label = Zachowanie zen-urlbar-behavior-normal = .label = Normalne zen-urlbar-behavior-floating-on-type = - .label = Widoczny podczas pisania + .label = Pływający tylko podczas pisania zen-urlbar-behavior-float = - .label = Zawsze widoczny + .label = Zawsze pływający pane-zen-CKS-title = Skróty klawiaturowe category-zen-CKS = .tooltiptext = { pane-zen-CKS-title } @@ -166,7 +166,7 @@ category-zen-marketplace = zen-settings-CKS-header = Dostosuj skróty klawiaturowe zen-settings-CKS-description = Zmień domyślne skróty klawiaturowe zgodnie z własnymi preferencjami i popraw komfort przeglądania stron internetowych zen-settings-CKS-disable-firefox = - .label = Wyłącz domyślne skróty klawiszowe { -brand-short-name } + .label = Wyłącz domyślne skróty klawiaturowe { -brand-short-name } zen-settings-CKS-duplicate-shortcut = .label = Duplikuj skrót zen-settings-CKS-reset-shortcuts = @@ -182,7 +182,7 @@ zenCKSOption-group-zen-compact-mode = Tryb kompaktowy zenCKSOption-group-zen-workspace = Obszary robocze zenCKSOption-group-zen-other = Inne funkcje Zen zenCKSOption-group-zen-split-view = Podziel widok -zenCKSOption-group-devTools = Narzędzia developerskie +zenCKSOption-group-devTools = Narzędzia deweloperskie zen-key-quick-restart = Szybkie ponowne uruchomienie zen-window-new-shortcut = Nowe okno zen-tab-new-shortcut = Nowa karta @@ -214,7 +214,7 @@ zen-help-shortcut = Otwórz pomoc zen-preferences-shortcut = Otwórz ustawienia zen-hide-app-shortcut = Ukryj aplikację zen-hide-other-apps-shortcut = Ukryj inne aplikacje -zen-search-focus-shortcut = Szukaj w centrum uwagi +zen-search-focus-shortcut = Przejdź do wyszukiwania zen-search-focus-shortcut-alt = Przejdź do wyszukiwania (Alt) zen-downloads-shortcut = Otwórz pobieranie zen-addons-shortcut = Otwórz dodatki @@ -223,7 +223,7 @@ zen-save-page-shortcut = Zapisz stronę zen-print-shortcut = Wydrukuj stronę zen-close-shortcut-2 = Zamknij kartę zen-mute-toggle-shortcut = Przełącz wyciszenie -zen-key-delete = Usuń klucz +zen-key-delete = Klawisz Delete zen-key-go-back = Powrót zen-key-go-forward = Przejdź do przodu zen-nav-back-shortcut-alt = Nawiguj wstecz (Alt) @@ -281,13 +281,13 @@ zen-workspace-shortcut-switch-9 = Przełącz na obszar roboczy 9 zen-workspace-shortcut-switch-10 = Przełącz na obszar roboczy 10 zen-workspace-shortcut-forward = Następny obszar roboczy zen-workspace-shortcut-backward = Poprzedni obszar roboczy -zen-sidebar-shortcut-toggle = Przełącz szerokość paska bocznego -zen-pinned-tab-shortcut-reset = Zresetuj przypiętą kartę do przypiętego adresu URL -zen-split-view-shortcut-grid = Przełącz siatkę widoku dzielonego +zen-sidebar-shortcut-toggle = Przełącz szerokość panelu bocznego +zen-pinned-tab-shortcut-reset = Przywróć przypiętą kartę do przypiętego adresu URL +zen-split-view-shortcut-grid = Przełącz widok podziału na siatkę zen-split-view-shortcut-vertical = Przełącz podział ekranu w pionie zen-split-view-shortcut-horizontal = Przełącz podział ekranu w poziomie zen-split-view-shortcut-unsplit = Zamknij podział widoku -zen-new-empty-split-view-shortcut = Nowy pusty widok podziału +zen-new-empty-split-view-shortcut = Nowy pusty widok podzielony zen-key-select-tab-1 = Wybierz kartę #1 zen-key-select-tab-2 = Wybierz kartę #2 zen-key-select-tab-3 = Wybierz kartę #3 @@ -302,18 +302,19 @@ zen-key-goto-history = Przejdź do historii zen-key-go-home = Przejdź do strony głównej zen-bookmark-show-sidebar-shortcut = Pokaż pasek boczny zakładek zen-bookmark-show-toolbar-shortcut = Pokaż pasek zakładek -zen-devtools-toggle-shortcut = Włącz narzędzia developerskie +zen-devtools-toggle-shortcut = Przełącz narzędzia deweloperskie zen-devtools-toggle-browser-toolbox-shortcut = Włącz narzędzia przeglądarki zen-devtools-toggle-browser-console-shortcut = Włącz konsolę przeglądarki zen-devtools-toggle-responsive-design-mode-shortcut = Włącz tryb responsywny zen-devtools-toggle-inspector-shortcut = Włącz Inspektor -zen-devtools-toggle-web-console-shortcut = Włącz konsolę sieciową +zen-devtools-toggle-web-console-shortcut = Włącz konsolę zen-devtools-toggle-js-debugger-shortcut = Włącz debugger JavaScript zen-devtools-toggle-net-monitor-shortcut = Włącz monitor sieci zen-devtools-toggle-style-editor-shortcut = Włącz edytor stylów zen-devtools-toggle-performance-shortcut = Włącz Wydajność -zen-devtools-toggle-storage-shortcut = Włącz Pamięć +zen-devtools-toggle-storage-shortcut = Włącz Dane zen-devtools-toggle-dom-shortcut = Włącz DOM zen-devtools-toggle-accessibility-shortcut = Włącz Dostępność zen-close-all-unpinned-tabs-shortcut = Zamknij wszystkie nieprzypięte karty zen-new-unsynced-window-shortcut = Nowe niesynchronizowane okno +zen-duplicate-tab-shortcut = Duplikuj kartę diff --git a/locales/pl/browser/browser/zen-folders.ftl b/locales/pl/browser/browser/zen-folders.ftl index d684d1c58..214a529c3 100644 --- a/locales/pl/browser/browser/zen-folders.ftl +++ b/locales/pl/browser/browser/zen-folders.ftl @@ -9,13 +9,13 @@ zen-folders-panel-rename-folder = zen-folders-panel-unpack-folder = .label = Rozpakuj folder zen-folders-new-subfolder = - .label = Nowy folder + .label = Nowy podfolder zen-folders-panel-delete-folder = .label = Usuń folder zen-folders-panel-convert-folder-to-space = .label = Konwertuj folder na przestrzeń zen-folders-panel-change-folder-space = - .label = Zmień nazwę przestrzeni... + .label = Zmień przestrzeń... zen-folders-unload-all-tooltip = .tooltiptext = Wyładuj aktywne w tym folderze zen-folders-unload-folder = diff --git a/locales/pl/browser/browser/zen-general.ftl b/locales/pl/browser/browser/zen-general.ftl index 9dec64db3..3d041a34f 100644 --- a/locales/pl/browser/browser/zen-general.ftl +++ b/locales/pl/browser/browser/zen-general.ftl @@ -14,7 +14,7 @@ tab-context-zen-reset-pinned-tab = tab-context-zen-add-essential = .label = Dodaj do niezbędnych .accesskey = E -tab-context-zen-add-essential-badge = { $num } / { $max } zajętych miejsc +tab-context-zen-add-essential-badge = { $num } / { $max } tab-context-zen-remove-essential = .label = Usuń z niezbędnych .accesskey = R @@ -47,9 +47,10 @@ zen-general-cancel-label = zen-general-confirm = .label = Potwierdź zen-pinned-tab-replaced = URL przypiętej karty został zastąpiony bieżącym adresem! -zen-tabs-renamed = Nazwa karty została z powodzeniem zmieniona! +zen-tabs-renamed = Nazwa karty została pomyślnie zmieniona! zen-background-tab-opened-toast = Nowa karta została otworzona w tle! zen-workspace-renamed-toast = Zmieniono nazwę przestrzeni roboczej! +zen-split-view-limit-toast = Nie można dodać kolejnych paneli do widoku podzielonego! zen-toggle-compact-mode-button = .label = Tryb kompaktowy .tooltiptext = Przełącz tryb kompaktowy @@ -92,7 +93,7 @@ zen-site-data-site-settings = zen-site-data-header-share = .tooltiptext = Udostępnij tę stronę zen-site-data-header-reader-mode = - .tooltiptext = Wejdź do trybu czytnika + .tooltiptext = Przejdź do trybu czytania zen-site-data-header-screenshot = .tooltiptext = Zrzut ekranu zen-site-data-header-bookmark = @@ -119,7 +120,7 @@ zen-sidebar-notification-restart-safe-mode-label = Coś się zepsuło? zen-sidebar-notification-restart-safe-mode-tooltip = .title = Zrestartuj w trybie bezpiecznym zen-window-sync-migration-dialog-title = Utrzymuj synchronizację okien -zen-window-sync-migration-dialog-message = Zen synchronizuje okna na jednym urządzeniu, żeby zmiany w jednym oknie były natychmiast odzwierciedlone w pozostałych. +zen-window-sync-migration-dialog-message = Zen synchronizuje teraz okna na tym samym urządzeniu, dzięki czemu zmiany wprowadzone w jednym oknie są natychmiast odzwierciedlane w pozostałych. zen-window-sync-migration-dialog-learn-more = Dowiedz się więcej zen-window-sync-migration-dialog-accept = Rozumiem zen-appmenu-new-blank-window = diff --git a/locales/pl/browser/browser/zen-split-view.ftl b/locales/pl/browser/browser/zen-split-view.ftl index 2b1fbc60d..a706f90eb 100644 --- a/locales/pl/browser/browser/zen-split-view.ftl +++ b/locales/pl/browser/browser/zen-split-view.ftl @@ -5,14 +5,15 @@ tab-zen-split-tabs = .label = { $tabCount -> - [-1] Rozdziel karty - [1] Podziel kartę (wymagane zaznaczenie wielu kart) - *[other] Podziel karty w liczbie: { $tabCount } + [-1] Oddziel kartę + [1] Utwórz widok podzielony + [few] Złącz { $tabCount } karty + *[other] Złącz { $tabCount } kart } .accesskey = S zen-split-link = - .label = Podziel link na nową kartę + .label = Otwórz link w widoku podzielonym .accesskey = S -zen-split-view-modifier-header = Podziel widok +zen-split-view-modifier-header = Widok podzielony zen-split-view-modifier-activate-reallocation = .label = Aktywuj realokację diff --git a/locales/pl/browser/browser/zen-vertical-tabs.ftl b/locales/pl/browser/browser/zen-vertical-tabs.ftl index b4eb2a704..5b5cb6824 100644 --- a/locales/pl/browser/browser/zen-vertical-tabs.ftl +++ b/locales/pl/browser/browser/zen-vertical-tabs.ftl @@ -3,7 +3,7 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. zen-toolbar-context-tabs-right = - .label = Zakładki po prawej + .label = Karty po prawej .accesskey = R zen-toolbar-context-compact-mode = .label = Tryb kompaktowy @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = Ukryj oba .accesskey = H +zen-toolbar-context-move-to-folder = + .label = Przenieś do folderu... + .accesskey = M zen-toolbar-context-new-folder = .label = Nowy folder .accesskey = N @@ -27,14 +30,16 @@ sidebar-zen-create-new = tabbrowser-unload-tab-button = .tooltiptext = { $tabCount -> - [one] Dezaktywuj i przełącz na kartę - *[other] Dezaktywuj { $tabCount } karty i przełącz na pierwszą + [one] Wyładuj i przełącz na kartę + [few] Wyładuj { $tabCount } karty i przełącz na pierwszą + *[other] Wyładuj { $tabCount } kart i przełącz na pierwszą } tabbrowser-reset-pin-button = .tooltiptext = { $tabCount -> [one] Zresetuj i przypnij kartę - *[other] Zresetuj i przypnij karty w liczbie: { $tabCount } + [few] Zresetuj i przypnij { $tabCount } karty + *[other] Zresetuj i przypnij { $tabCount } kart } zen-tab-sublabel = { $tabSubtitle -> diff --git a/locales/pl/browser/browser/zen-welcome.ftl b/locales/pl/browser/browser/zen-welcome.ftl index 41665e0c9..b3ec9ae53 100644 --- a/locales/pl/browser/browser/zen-welcome.ftl +++ b/locales/pl/browser/browser/zen-welcome.ftl @@ -10,7 +10,7 @@ zen-welcome-import-description-2 = Łatwo przenieś je z innej przeglądarki i k zen-welcome-import-button = Zaimportuj teraz zen-welcome-set-default-browser = Ustaw { -brand-short-name } jako swoją domyślną przeglądarkę zen-welcome-dont-set-default-browser = NIE ustawiaj { -brand-short-name } jako swoja domyślna przeglądarka -zen-welcome-initial-essentials-title = Twoje kluczowe karty, zawsze w zasięgu +zen-welcome-initial-essentials-title = Twoje kluczowe karty, zawsze pod ręką zen-welcome-initial-essentials-description-1 = Utrzymuj swoje najważniejsze karty zawsze pod ręką, niezależnie od tego, jak wiele masz ich otwartych. zen-welcome-initial-essentials-description-2 = Niezbędne karty są zawsze widoczne, niezależnie od bieżącego obszaru roboczego. zen-welcome-workspace-colors-title = Twój obszar roboczy, Twoje kolory diff --git a/locales/pl/browser/browser/zen-workspaces.ftl b/locales/pl/browser/browser/zen-workspaces.ftl index 9b2cfc70d..143622b49 100644 --- a/locales/pl/browser/browser/zen-workspaces.ftl +++ b/locales/pl/browser/browser/zen-workspaces.ftl @@ -58,7 +58,7 @@ zen-move-tab-to-workspace-button = zen-workspaces-panel-context-reorder = .label = Reorganizuj przestrzenie zen-workspace-creation-profile = Profil - .tooltiptext = Profile są wykorzystywane do oddzielenia ciasteczek i danych stron pomiędzy przestrzeniami. + .tooltiptext = Profile służą do rozdzielania plików cookie i danych witryny między przestrzeniami. zen-workspace-creation-header = Utwórz przestrzeń zen-workspace-creation-label = Przestrzenie są wykorzystywane do zorganizowania kart i sesji. zen-workspaces-delete-workspace-title = Usunąć przestrzeń? diff --git a/locales/pt-BR/browser/browser/preferences/zen-preferences.ftl b/locales/pt-BR/browser/browser/preferences/zen-preferences.ftl index 14bc467d5..8bc0fee98 100644 --- a/locales/pt-BR/browser/browser/preferences/zen-preferences.ftl +++ b/locales/pt-BR/browser/browser/preferences/zen-preferences.ftl @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = Ativar/Desativar DOM zen-devtools-toggle-accessibility-shortcut = Ativar/Desativar Acessibilidade zen-close-all-unpinned-tabs-shortcut = Fechar Todas as Abas Não Fixadas zen-new-unsynced-window-shortcut = Nova Janela Dessincronizada +zen-duplicate-tab-shortcut = Duplicar Aba diff --git a/locales/pt-BR/browser/browser/zen-general.ftl b/locales/pt-BR/browser/browser/zen-general.ftl index f8ba80093..d1a05e002 100644 --- a/locales/pt-BR/browser/browser/zen-general.ftl +++ b/locales/pt-BR/browser/browser/zen-general.ftl @@ -48,6 +48,7 @@ zen-pinned-tab-replaced = A URL da guia fixada foi substituída pela URL atual! zen-tabs-renamed = A guia foi renomeada com sucesso! zen-background-tab-opened-toast = Nova guia em segundo plano aberta! zen-workspace-renamed-toast = A área de trabalho foi renomeada com sucesso! +zen-split-view-limit-toast = Não é possível adicionar mais painéis à visualização dividida! zen-toggle-compact-mode-button = .label = Modo Compacto .tooltiptext = Alternar Modo Compacto diff --git a/locales/pt-BR/browser/browser/zen-split-view.ftl b/locales/pt-BR/browser/browser/zen-split-view.ftl index 4d80e0a41..9006d5896 100644 --- a/locales/pt-BR/browser/browser/zen-split-view.ftl +++ b/locales/pt-BR/browser/browser/zen-split-view.ftl @@ -5,8 +5,9 @@ tab-zen-split-tabs = .label = { $tabCount -> - [1] Dividir aba (é necessário várias abas selecionadas) - *[other] Dividir { $tabCount } Abas + [-1] Dividir Aba + [1] Adicionar divisão... + *[other] Juntar { $tabCount } Abas } .accesskey = S.O. zen-split-link = diff --git a/locales/pt-BR/browser/browser/zen-vertical-tabs.ftl b/locales/pt-BR/browser/browser/zen-vertical-tabs.ftl index 19aeefae9..152f25918 100644 --- a/locales/pt-BR/browser/browser/zen-vertical-tabs.ftl +++ b/locales/pt-BR/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = Esconder os dois .accesskey = A +zen-toolbar-context-move-to-folder = + .label = Mover para Pasta... + .accesskey = M zen-toolbar-context-new-folder = .label = Nova Pasta .accesskey = N diff --git a/locales/pt-PT/browser/browser/preferences/zen-preferences.ftl b/locales/pt-PT/browser/browser/preferences/zen-preferences.ftl index 4bb041172..8ed17f431 100644 --- a/locales/pt-PT/browser/browser/preferences/zen-preferences.ftl +++ b/locales/pt-PT/browser/browser/preferences/zen-preferences.ftl @@ -43,11 +43,11 @@ category-zen-workspaces = .tooltiptext = { pane-zen-tabs-title } pane-settings-workspaces-title = Espaços de Trabalho zen-tabs-select-recently-used-on-close = - .label = When closing a tab, switch to the most recently used tab instead of the next tab + .label = Ao fechar um separador, mudar para o separador usado mais recentemente, em vez do próximo separador zen-tabs-close-on-back-with-no-history = .label = Fechar separador e mudar para o seu separador proprietário (ou para o mais recente) quando retroceder zen-settings-workspaces-sync-unpinned-tabs = - .label = Sync only pinned tabs in workspaces + .label = Sincronizar apenas separadores fixados em espaços de trabalho zen-tabs-cycle-by-attribute = .label = Ctrl+Tab percorre apenas nos separadores Essenciais e de Espaço de trabalho zen-tabs-cycle-ignore-pending-tabs = @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = Alternar DOM zen-devtools-toggle-accessibility-shortcut = Alternar Acessibilidade zen-close-all-unpinned-tabs-shortcut = Fechar Todos os Separadores Não Fixados zen-new-unsynced-window-shortcut = Nova janela sem sincronização +zen-duplicate-tab-shortcut = Duplicar Separador diff --git a/locales/pt-PT/browser/browser/zen-general.ftl b/locales/pt-PT/browser/browser/zen-general.ftl index 609ad22f4..c92bfecfe 100644 --- a/locales/pt-PT/browser/browser/zen-general.ftl +++ b/locales/pt-PT/browser/browser/zen-general.ftl @@ -7,8 +7,8 @@ unified-extensions-description = As extensões são usadas para trazer funcional tab-context-zen-reset-pinned-tab = .label = { $isEssential -> - [true] Reset Essential Tab - *[false] Reset Pinned Tab + [true] Repor Separador Essencial + *[false] Repor Separador Fixado } .accesskey = R tab-context-zen-add-essential = @@ -21,8 +21,8 @@ tab-context-zen-remove-essential = tab-context-zen-replace-pinned-url-with-current = .label = { $isEssential -> - [true] Replace Essential URL with Current - *[false] Replace Pinned URL with Current + [true] Substituir URL Essencial com o Atual + *[false] Substituir URL Fixado com o Atual } .accesskey = C tab-context-zen-edit-title = @@ -41,7 +41,7 @@ pictureinpicture-minimize-btn = .tooltip = Minimizar zen-panel-ui-gradient-generator-custom-color = Cor personalizada zen-copy-current-url-confirmation = URL atual copiado! -zen-copy-current-url-as-markdown-confirmation = Copied current URL as Markdown! +zen-copy-current-url-as-markdown-confirmation = URL atual copiado em Markdown! zen-general-cancel-label = .label = Cancelar zen-general-confirm = @@ -50,6 +50,7 @@ zen-pinned-tab-replaced = O URL do separador fixado foi substituído pelo URL at zen-tabs-renamed = Nome do separador alterado com sucesso! zen-background-tab-opened-toast = Novo separador aberto em segundo plano! zen-workspace-renamed-toast = Nome do espaço de trabalho alterado com sucesso! +zen-split-view-limit-toast = Não é possível adicionar mais painéis à vista dividida! zen-toggle-compact-mode-button = .label = Modo Compacto .tooltiptext = Alternar Modo Compacto @@ -123,4 +124,4 @@ zen-window-sync-migration-dialog-message = Agora, o Zen sincroniza as janelas no zen-window-sync-migration-dialog-learn-more = Saber Mais zen-window-sync-migration-dialog-accept = Entendido zen-appmenu-new-blank-window = - .label = New blank window + .label = Nova janela sem sincronização diff --git a/locales/pt-PT/browser/browser/zen-menubar.ftl b/locales/pt-PT/browser/browser/zen-menubar.ftl index 0c6919501..c0ac59017 100644 --- a/locales/pt-PT/browser/browser/zen-menubar.ftl +++ b/locales/pt-PT/browser/browser/zen-menubar.ftl @@ -19,4 +19,4 @@ zen-menubar-appearance-light = zen-menubar-appearance-dark = .label = Escuro zen-menubar-new-blank-window = - .label = New Blank Window + .label = Nova Janela sem Sincronização diff --git a/locales/pt-PT/browser/browser/zen-split-view.ftl b/locales/pt-PT/browser/browser/zen-split-view.ftl index 5983f03f8..e6e9e73f6 100644 --- a/locales/pt-PT/browser/browser/zen-split-view.ftl +++ b/locales/pt-PT/browser/browser/zen-split-view.ftl @@ -5,8 +5,9 @@ tab-zen-split-tabs = .label = { $tabCount -> - [1] Dividir Separadores (selecione vários separadores) - *[other] Dividir { $tabCount } Separadores + [-1] Dividir separador + [1] Adicionar vista dividida... + *[other] Juntar { $tabCount } Separadores } .accesskey = S zen-split-link = diff --git a/locales/pt-PT/browser/browser/zen-vertical-tabs.ftl b/locales/pt-PT/browser/browser/zen-vertical-tabs.ftl index bc873641d..190b4e630 100644 --- a/locales/pt-PT/browser/browser/zen-vertical-tabs.ftl +++ b/locales/pt-PT/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = Ocultar ambas .accesskey = H +zen-toolbar-context-move-to-folder = + .label = Mover para Pasta... + .accesskey = M zen-toolbar-context-new-folder = .label = Nova Pasta .accesskey = N @@ -28,7 +31,7 @@ tabbrowser-unload-tab-button = .tooltiptext = { $tabCount -> [one] Hibernar e mudar para o separador - *[other] Hibernar { $tabCount } separadores mudar para o primeiro + *[other] Hibernar { $tabCount } separadores e mudar para o primeiro } tabbrowser-reset-pin-button = .tooltiptext = @@ -37,8 +40,8 @@ tabbrowser-reset-pin-button = *[other] Restaurar e fixar { $tabCount } separadores } zen-tab-sublabel = - { $tabSubtitle -> - [zen-default-pinned] Back to pinned url - [zen-default-pinned-cmd] Separate from pinned tab + rl{ $tabSubtitle -> + [zen-default-pinned] Voltar para URL fixado + [zen-default-pinned-cmd] Separar do separador fixado *[other] { $tabSubtitle } } diff --git a/locales/pt-PT/browser/browser/zen-workspaces.ftl b/locales/pt-PT/browser/browser/zen-workspaces.ftl index c6e36e7f8..985a837c1 100644 --- a/locales/pt-PT/browser/browser/zen-workspaces.ftl +++ b/locales/pt-PT/browser/browser/zen-workspaces.ftl @@ -10,7 +10,7 @@ zen-panel-ui-workspaces-create = zen-panel-ui-folder-create = .label = Criar Pasta zen-panel-ui-live-folder-create = - .label = Live Folder + .label = Pasta Viva zen-panel-ui-new-empty-split = .label = Nova Divisão zen-workspaces-panel-context-delete = @@ -25,7 +25,7 @@ zen-workspaces-panel-context-default-profile = zen-workspaces-panel-unload = .label = Descarregar Espaço zen-workspaces-panel-unload-others = - .label = Unload All Other Spaces + .label = Descarregar Todos os Outros Espaços zen-workspaces-how-to-reorder-title = Como reordenar espaços zen-workspaces-how-to-reorder-desc = Arraste os ícones de espaço na parte inferior da barra lateral para os reordenar zen-workspaces-change-theme = diff --git a/locales/ro/browser/browser/preferences/zen-preferences.ftl b/locales/ro/browser/browser/preferences/zen-preferences.ftl index da9d313b4..ed26f72b8 100644 --- a/locales/ro/browser/browser/preferences/zen-preferences.ftl +++ b/locales/ro/browser/browser/preferences/zen-preferences.ftl @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = Toggle DOM zen-devtools-toggle-accessibility-shortcut = Toggle Accessibility zen-close-all-unpinned-tabs-shortcut = Close All Unpinned Tabs zen-new-unsynced-window-shortcut = New Unsynced Window +zen-duplicate-tab-shortcut = Duplicate Tab diff --git a/locales/ro/browser/browser/zen-general.ftl b/locales/ro/browser/browser/zen-general.ftl index 21d9a7475..709b4575f 100644 --- a/locales/ro/browser/browser/zen-general.ftl +++ b/locales/ro/browser/browser/zen-general.ftl @@ -50,6 +50,7 @@ zen-pinned-tab-replaced = Pinned tab URL has been replaced with the current URL! zen-tabs-renamed = Tab has been successfully renamed! zen-background-tab-opened-toast = New background tab opened! zen-workspace-renamed-toast = Workspace has been successfully renamed! +zen-split-view-limit-toast = Can't add more panels to the split view! zen-toggle-compact-mode-button = .label = Compact Mode .tooltiptext = Toggle Compact Mode diff --git a/locales/ro/browser/browser/zen-split-view.ftl b/locales/ro/browser/browser/zen-split-view.ftl index 10593eed9..4430fab34 100644 --- a/locales/ro/browser/browser/zen-split-view.ftl +++ b/locales/ro/browser/browser/zen-split-view.ftl @@ -5,8 +5,9 @@ tab-zen-split-tabs = .label = { $tabCount -> - [1] Split Tab (multiple selected tabs needed) - *[other] Split { $tabCount } Tabs + [-1] Split out tab + [1] Add split view... + *[other] Join { $tabCount } Tabs } .accesskey = S zen-split-link = diff --git a/locales/ro/browser/browser/zen-vertical-tabs.ftl b/locales/ro/browser/browser/zen-vertical-tabs.ftl index ff8a3775c..9989c7666 100644 --- a/locales/ro/browser/browser/zen-vertical-tabs.ftl +++ b/locales/ro/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = Hide both .accesskey = H +zen-toolbar-context-move-to-folder = + .label = Move to Folder... + .accesskey = M zen-toolbar-context-new-folder = .label = New Folder .accesskey = N diff --git a/locales/ru/browser/browser/preferences/zen-preferences.ftl b/locales/ru/browser/browser/preferences/zen-preferences.ftl index 78388440c..4942900e9 100644 --- a/locales/ru/browser/browser/preferences/zen-preferences.ftl +++ b/locales/ru/browser/browser/preferences/zen-preferences.ftl @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = Переключить DOM zen-devtools-toggle-accessibility-shortcut = Включить/выключить специальные возможности zen-close-all-unpinned-tabs-shortcut = Закрыть все не закреплённые вкладки zen-new-unsynced-window-shortcut = Новое пустое окно +zen-duplicate-tab-shortcut = Дублировать вкладку diff --git a/locales/ru/browser/browser/zen-general.ftl b/locales/ru/browser/browser/zen-general.ftl index f23c9f228..af51df15e 100644 --- a/locales/ru/browser/browser/zen-general.ftl +++ b/locales/ru/browser/browser/zen-general.ftl @@ -50,6 +50,7 @@ zen-pinned-tab-replaced = Адрес закреплённой вкладки з zen-tabs-renamed = Вкладка успешно переименована! zen-background-tab-opened-toast = Открыта новая фоновая вкладка! zen-workspace-renamed-toast = Пространство успешно переименовано! +zen-split-view-limit-toast = Can't add more panels to the split view! zen-toggle-compact-mode-button = .label = Компактный режим .tooltiptext = Переключить компактный режим diff --git a/locales/ru/browser/browser/zen-split-view.ftl b/locales/ru/browser/browser/zen-split-view.ftl index 580394f91..b89b20b46 100644 --- a/locales/ru/browser/browser/zen-split-view.ftl +++ b/locales/ru/browser/browser/zen-split-view.ftl @@ -5,10 +5,9 @@ tab-zen-split-tabs = .label = { $tabCount -> - [1] Разделить вкладку (выберите несколько вкладок) - [one] Разделить { $tabCount } вкладку - [few] Разделить { $tabCount } вкладки - *[other] Разделить { $tabCount } вкладок + [-1] Split out tab + [1] Add split view... + *[other] Join { $tabCount } Tabs } .accesskey = Ы zen-split-link = diff --git a/locales/ru/browser/browser/zen-vertical-tabs.ftl b/locales/ru/browser/browser/zen-vertical-tabs.ftl index 8f5b9decf..6a9227289 100644 --- a/locales/ru/browser/browser/zen-vertical-tabs.ftl +++ b/locales/ru/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = Скрыть оба .accesskey = Н +zen-toolbar-context-move-to-folder = + .label = Переместить в папку... + .accesskey = M zen-toolbar-context-new-folder = .label = Новая папка .accesskey = Т diff --git a/locales/sk/browser/browser/preferences/zen-preferences.ftl b/locales/sk/browser/browser/preferences/zen-preferences.ftl index a88a6104d..91d5cd501 100644 --- a/locales/sk/browser/browser/preferences/zen-preferences.ftl +++ b/locales/sk/browser/browser/preferences/zen-preferences.ftl @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = Prepnúť DOM zen-devtools-toggle-accessibility-shortcut = Prepnúť prístupnosť zen-close-all-unpinned-tabs-shortcut = Zatvoriť všetky nepripnuté karty zen-new-unsynced-window-shortcut = New Unsynced Window +zen-duplicate-tab-shortcut = Duplicate Tab diff --git a/locales/sk/browser/browser/zen-general.ftl b/locales/sk/browser/browser/zen-general.ftl index 8b99903bc..2768b6852 100644 --- a/locales/sk/browser/browser/zen-general.ftl +++ b/locales/sk/browser/browser/zen-general.ftl @@ -48,6 +48,7 @@ zen-pinned-tab-replaced = URL pripnutej karty bola nahradená aktuálnou URL! zen-tabs-renamed = Karta bola úspešne premenovaná! zen-background-tab-opened-toast = Nová karta otvorená na pozadí! zen-workspace-renamed-toast = Pracovný priestor bol úspešne premenovaný! +zen-split-view-limit-toast = Can't add more panels to the split view! zen-toggle-compact-mode-button = .label = Kompaktný Režim .tooltiptext = Prepnúť Kompaktný Režim diff --git a/locales/sk/browser/browser/zen-split-view.ftl b/locales/sk/browser/browser/zen-split-view.ftl index fb145b79e..802a8db0f 100644 --- a/locales/sk/browser/browser/zen-split-view.ftl +++ b/locales/sk/browser/browser/zen-split-view.ftl @@ -5,8 +5,9 @@ tab-zen-split-tabs = .label = { $tabCount -> - [1] Split Tab (multiple selected tabs needed) - *[other] Split { $tabCount } Tabs + [-1] Split out tab + [1] Add split view... + *[other] Join { $tabCount } Tabs } .accesskey = S zen-split-link = diff --git a/locales/sk/browser/browser/zen-vertical-tabs.ftl b/locales/sk/browser/browser/zen-vertical-tabs.ftl index 42ea7be3a..03fab80b1 100644 --- a/locales/sk/browser/browser/zen-vertical-tabs.ftl +++ b/locales/sk/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = Skryť oboje .accesskey = H +zen-toolbar-context-move-to-folder = + .label = Move to Folder... + .accesskey = M zen-toolbar-context-new-folder = .label = Nový Priečinok .accesskey = N diff --git a/locales/sv-SE/browser/browser/preferences/zen-preferences.ftl b/locales/sv-SE/browser/browser/preferences/zen-preferences.ftl index fd3b8b586..5734cbcc6 100644 --- a/locales/sv-SE/browser/browser/preferences/zen-preferences.ftl +++ b/locales/sv-SE/browser/browser/preferences/zen-preferences.ftl @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = Växla DOM zen-devtools-toggle-accessibility-shortcut = Växla tillgänglighetsinställningar zen-close-all-unpinned-tabs-shortcut = Stäng alla flikar som inte är fästa zen-new-unsynced-window-shortcut = Nytt tomt fönster +zen-duplicate-tab-shortcut = Duplicera flik diff --git a/locales/sv-SE/browser/browser/zen-general.ftl b/locales/sv-SE/browser/browser/zen-general.ftl index 61acc0b1c..db279ce9e 100644 --- a/locales/sv-SE/browser/browser/zen-general.ftl +++ b/locales/sv-SE/browser/browser/zen-general.ftl @@ -50,6 +50,7 @@ zen-pinned-tab-replaced = Den fästa flikens URL har ersatts med den aktuella UR zen-tabs-renamed = Fliken har fått nytt namn! zen-background-tab-opened-toast = Ny bakgrundsflik öppnad! zen-workspace-renamed-toast = Arbetsytan har fått ett nytt namn! +zen-split-view-limit-toast = Kan inte lägga till fler paneler till delad vy! zen-toggle-compact-mode-button = .label = Kompakt läge .tooltiptext = Växla kompakt läge diff --git a/locales/sv-SE/browser/browser/zen-split-view.ftl b/locales/sv-SE/browser/browser/zen-split-view.ftl index 9a2208714..6aacf61f4 100644 --- a/locales/sv-SE/browser/browser/zen-split-view.ftl +++ b/locales/sv-SE/browser/browser/zen-split-view.ftl @@ -5,9 +5,9 @@ tab-zen-split-tabs = .label = { $tabCount -> - [-1] Avdela flikar - [1] Delad flik (flera valda flikar behövs) - *[other] Delad { $tabCount } flikar + [-1] Dela upp flik + [1] Lägg till delad vy... + *[other] Gå med i { $tabCount } flikar } .accesskey = S zen-split-link = diff --git a/locales/sv-SE/browser/browser/zen-vertical-tabs.ftl b/locales/sv-SE/browser/browser/zen-vertical-tabs.ftl index c474bd227..5d8e94fc6 100644 --- a/locales/sv-SE/browser/browser/zen-vertical-tabs.ftl +++ b/locales/sv-SE/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = Dölj båda .accesskey = H +zen-toolbar-context-move-to-folder = + .label = Flytta till mapp... + .accesskey = M zen-toolbar-context-new-folder = .label = Ny mapp .accesskey = N diff --git a/locales/th/browser/browser/preferences/zen-preferences.ftl b/locales/th/browser/browser/preferences/zen-preferences.ftl index e960be53a..809b5601a 100644 --- a/locales/th/browser/browser/preferences/zen-preferences.ftl +++ b/locales/th/browser/browser/preferences/zen-preferences.ftl @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = Toggle DOM zen-devtools-toggle-accessibility-shortcut = Toggle Accessibility zen-close-all-unpinned-tabs-shortcut = Close All Unpinned Tabs zen-new-unsynced-window-shortcut = New Unsynced Window +zen-duplicate-tab-shortcut = Duplicate Tab diff --git a/locales/th/browser/browser/zen-general.ftl b/locales/th/browser/browser/zen-general.ftl index 4fc7e513a..c8f27e979 100644 --- a/locales/th/browser/browser/zen-general.ftl +++ b/locales/th/browser/browser/zen-general.ftl @@ -50,6 +50,7 @@ zen-pinned-tab-replaced = Pinned tab URL has been replaced with the current URL. zen-tabs-renamed = Tab has been successfully renamed! zen-background-tab-opened-toast = New background tab opened! zen-workspace-renamed-toast = Workspace has been successfully renamed! +zen-split-view-limit-toast = Can't add more panels to the split view! zen-toggle-compact-mode-button = .label = โหมดกะทัดรัด .tooltiptext = Toggle Compact Mode diff --git a/locales/th/browser/browser/zen-split-view.ftl b/locales/th/browser/browser/zen-split-view.ftl index 10593eed9..4430fab34 100644 --- a/locales/th/browser/browser/zen-split-view.ftl +++ b/locales/th/browser/browser/zen-split-view.ftl @@ -5,8 +5,9 @@ tab-zen-split-tabs = .label = { $tabCount -> - [1] Split Tab (multiple selected tabs needed) - *[other] Split { $tabCount } Tabs + [-1] Split out tab + [1] Add split view... + *[other] Join { $tabCount } Tabs } .accesskey = S zen-split-link = diff --git a/locales/th/browser/browser/zen-vertical-tabs.ftl b/locales/th/browser/browser/zen-vertical-tabs.ftl index cc392cbea..f808dd82d 100644 --- a/locales/th/browser/browser/zen-vertical-tabs.ftl +++ b/locales/th/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = ซ่อนทั้งคู่ .accesskey = H +zen-toolbar-context-move-to-folder = + .label = Move to Folder... + .accesskey = M zen-toolbar-context-new-folder = .label = สร้างโฟลเดอร์ใหม่ .accesskey = N diff --git a/locales/tr/browser/browser/preferences/zen-preferences.ftl b/locales/tr/browser/browser/preferences/zen-preferences.ftl index 591473b78..9fe0debf3 100644 --- a/locales/tr/browser/browser/preferences/zen-preferences.ftl +++ b/locales/tr/browser/browser/preferences/zen-preferences.ftl @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = DOM'u aç/kapat zen-devtools-toggle-accessibility-shortcut = Erişilebilirliği aç/kapat zen-close-all-unpinned-tabs-shortcut = Sabitlenmemiş tüm sekmeleri kapat zen-new-unsynced-window-shortcut = Yeni boş pencere +zen-duplicate-tab-shortcut = Sekmeyi çoğalt diff --git a/locales/tr/browser/browser/zen-general.ftl b/locales/tr/browser/browser/zen-general.ftl index 31df52d33..111178b6b 100644 --- a/locales/tr/browser/browser/zen-general.ftl +++ b/locales/tr/browser/browser/zen-general.ftl @@ -14,7 +14,7 @@ tab-context-zen-reset-pinned-tab = tab-context-zen-add-essential = .label = Temel sekmelere ekle .accesskey = E -tab-context-zen-add-essential-badge = { $num } / { $max } yuva dolu +tab-context-zen-add-essential-badge = { $num } / { $max } tab-context-zen-remove-essential = .label = Temel sekmelerden kaldır .accesskey = R @@ -50,6 +50,7 @@ zen-pinned-tab-replaced = Sabitlenmiş sekmenin URL’si, mevcut URL ile değiş zen-tabs-renamed = Sekme başarıyla yeniden adlandırıldı! zen-background-tab-opened-toast = Yeni arka plan sekmesi açıldı! zen-workspace-renamed-toast = Çalışma alanı başarıyla yeniden adlandırıldı! +zen-split-view-limit-toast = Bölünmüş görünüme daha fazla panel eklenemiyor! zen-toggle-compact-mode-button = .label = Kompakt mod .tooltiptext = Kompakt modu aç/kapat diff --git a/locales/tr/browser/browser/zen-split-view.ftl b/locales/tr/browser/browser/zen-split-view.ftl index ac0cd0fef..c70955488 100644 --- a/locales/tr/browser/browser/zen-split-view.ftl +++ b/locales/tr/browser/browser/zen-split-view.ftl @@ -5,9 +5,9 @@ tab-zen-split-tabs = .label = { $tabCount -> - [-1] Sekmeleri birleştir - [1] Sekmeyi böl (birden fazla seçilmiş sekme gerekir) - *[other] { $tabCount } Sekmeyi Böl + [-1] Sekmeyi ayır + [1] Bölünmüş görünüme ekle... + *[other] { $tabCount } sekmeyi birleştir } .accesskey = S zen-split-link = diff --git a/locales/tr/browser/browser/zen-vertical-tabs.ftl b/locales/tr/browser/browser/zen-vertical-tabs.ftl index f80af01e3..47f635615 100644 --- a/locales/tr/browser/browser/zen-vertical-tabs.ftl +++ b/locales/tr/browser/browser/zen-vertical-tabs.ftl @@ -6,7 +6,7 @@ zen-toolbar-context-tabs-right = .label = Sekmeler sağda olsun .accesskey = R zen-toolbar-context-compact-mode = - .label = Kompakt Mod + .label = Kompakt mod zen-toolbar-context-compact-mode-enable = .label = Kompakt modu aktifleştir .accesskey = D @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = İkisini de gizle .accesskey = H +zen-toolbar-context-move-to-folder = + .label = Klasöre taşı... + .accesskey = M zen-toolbar-context-new-folder = .label = Yeni klasör .accesskey = N diff --git a/locales/uk/browser/browser/preferences/zen-preferences.ftl b/locales/uk/browser/browser/preferences/zen-preferences.ftl index 4a93aa684..3dcce31ba 100644 --- a/locales/uk/browser/browser/preferences/zen-preferences.ftl +++ b/locales/uk/browser/browser/preferences/zen-preferences.ftl @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = Увімк. / Вимк. DOM zen-devtools-toggle-accessibility-shortcut = Увімк. / Вимк. доступ zen-close-all-unpinned-tabs-shortcut = Закрити всі відкріплені вкладки zen-new-unsynced-window-shortcut = Нове несинхронізоване вікно +zen-duplicate-tab-shortcut = Дублювати вкладку diff --git a/locales/uk/browser/browser/zen-general.ftl b/locales/uk/browser/browser/zen-general.ftl index 89c949c3f..a727b433b 100644 --- a/locales/uk/browser/browser/zen-general.ftl +++ b/locales/uk/browser/browser/zen-general.ftl @@ -50,6 +50,7 @@ zen-pinned-tab-replaced = URL-адресу закріпленої вкладки zen-tabs-renamed = Вкладку успішно перейменовано! zen-background-tab-opened-toast = Відкрито нову фонову вкладку! zen-workspace-renamed-toast = Робочий простір успішно перейменовано! +zen-split-view-limit-toast = Неможливо додати більше панелей до розділеного перегляду! zen-toggle-compact-mode-button = .label = Компактний режим .tooltiptext = Увімк. / Вимк. компактний режим diff --git a/locales/uk/browser/browser/zen-split-view.ftl b/locales/uk/browser/browser/zen-split-view.ftl index 5c970da09..77ea3247f 100644 --- a/locales/uk/browser/browser/zen-split-view.ftl +++ b/locales/uk/browser/browser/zen-split-view.ftl @@ -5,8 +5,10 @@ tab-zen-split-tabs = .label = { $tabCount -> - [1] Розділити вкладку (потрібно кілька виділених вкладок) - *[other] Розділити { $tabCount } вкладки + [-1] Відокремити вкладку + [1] Додати розділений перегляд... + [few] Об’єднати { $tabCount } вкладки + *[other] Об’єднати { $tabCount } вкладок } .accesskey = S zen-split-link = diff --git a/locales/uk/browser/browser/zen-vertical-tabs.ftl b/locales/uk/browser/browser/zen-vertical-tabs.ftl index 97bb9312b..bc42a7fed 100644 --- a/locales/uk/browser/browser/zen-vertical-tabs.ftl +++ b/locales/uk/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = Приховувати обидві .accesskey = Н +zen-toolbar-context-move-to-folder = + .label = Перемістити до теки... + .accesskey = M zen-toolbar-context-new-folder = .label = Нова тека .accesskey = N diff --git a/locales/vi/browser/browser/preferences/zen-preferences.ftl b/locales/vi/browser/browser/preferences/zen-preferences.ftl index 3539717bd..cc89ed45d 100644 --- a/locales/vi/browser/browser/preferences/zen-preferences.ftl +++ b/locales/vi/browser/browser/preferences/zen-preferences.ftl @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = Bật/Tắt DOM zen-devtools-toggle-accessibility-shortcut = Bật/Tắt trợ năng zen-close-all-unpinned-tabs-shortcut = Đóng tất cả thẻ chưa ghim zen-new-unsynced-window-shortcut = Cửa sổ trống mới +zen-duplicate-tab-shortcut = Duplicate Tab diff --git a/locales/vi/browser/browser/zen-general.ftl b/locales/vi/browser/browser/zen-general.ftl index e3c36371c..71e6413c8 100644 --- a/locales/vi/browser/browser/zen-general.ftl +++ b/locales/vi/browser/browser/zen-general.ftl @@ -7,8 +7,8 @@ unified-extensions-description = Các tiện ích mở rộng được sử dụ tab-context-zen-reset-pinned-tab = .label = { $isEssential -> - [true] Đặt lại thẻ chính - *[false] Đặt lại thẻ đã ghim + [true] Đặt lại thẻ chính + *[false] Đặt lại thẻ đã ghim } .accesskey = R tab-context-zen-add-essential = @@ -48,6 +48,7 @@ zen-pinned-tab-replaced = URL của thẻ đã ghim đã được thay thế b zen-tabs-renamed = Thẻ đã được đổi tên! zen-background-tab-opened-toast = Một thẻ mới đã được mở dưới nền! zen-workspace-renamed-toast = Không gian làm việc đã được đổi tên! +zen-split-view-limit-toast = Can't add more panels to the split view! zen-toggle-compact-mode-button = .label = Chế độ thu gọn .tooltiptext = Bật Chế độ thu gọn diff --git a/locales/vi/browser/browser/zen-split-view.ftl b/locales/vi/browser/browser/zen-split-view.ftl index 7a6fbb796..ec563698d 100644 --- a/locales/vi/browser/browser/zen-split-view.ftl +++ b/locales/vi/browser/browser/zen-split-view.ftl @@ -5,8 +5,9 @@ tab-zen-split-tabs = .label = { $tabCount -> - [1] Chia tab (cần nhiều tab được chọn) - *[other] Chia { $tabCount } tab + [-1] Split out tab + [1] Add split view... + *[other] Join { $tabCount } Tabs } .accesskey = S zen-split-link = diff --git a/locales/vi/browser/browser/zen-vertical-tabs.ftl b/locales/vi/browser/browser/zen-vertical-tabs.ftl index 4f3287af3..ae63b8b3b 100644 --- a/locales/vi/browser/browser/zen-vertical-tabs.ftl +++ b/locales/vi/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = Ẩn cả hai .accesskey = H +zen-toolbar-context-move-to-folder = + .label = Move to Folder... + .accesskey = M zen-toolbar-context-new-folder = .label = Thư mục mới .accesskey = N diff --git a/locales/zh-CN/browser/browser/preferences/zen-preferences.ftl b/locales/zh-CN/browser/browser/preferences/zen-preferences.ftl index 48730f6e2..da82d2771 100644 --- a/locales/zh-CN/browser/browser/preferences/zen-preferences.ftl +++ b/locales/zh-CN/browser/browser/preferences/zen-preferences.ftl @@ -181,7 +181,7 @@ zenCKSOption-group-mediaAndDisplay = 媒体与显示 zenCKSOption-group-zen-compact-mode = 简洁模式 zenCKSOption-group-zen-workspace = 工作区 zenCKSOption-group-zen-other = Zen 的其它功能 -zenCKSOption-group-zen-split-view = 拆分视图 +zenCKSOption-group-zen-split-view = 分屏视图 zenCKSOption-group-devTools = 开发者工具 zen-key-quick-restart = 快速重启 zen-window-new-shortcut = 新建窗口 @@ -283,10 +283,10 @@ zen-workspace-shortcut-forward = 下一个工作区 zen-workspace-shortcut-backward = 上一个工作区 zen-sidebar-shortcut-toggle = 折叠/展开侧边栏 zen-pinned-tab-shortcut-reset = 重置固定标签页至其固定的 URL -zen-split-view-shortcut-grid = 切换网格拆分视图 -zen-split-view-shortcut-vertical = 切换垂直拆分视图 -zen-split-view-shortcut-horizontal = 切换水平拆分视图 -zen-split-view-shortcut-unsplit = 关闭拆分视图 +zen-split-view-shortcut-grid = 切换网格分屏视图 +zen-split-view-shortcut-vertical = 切换垂直分屏视图 +zen-split-view-shortcut-horizontal = 切换水平分屏视图 +zen-split-view-shortcut-unsplit = 关闭分屏视图 zen-new-empty-split-view-shortcut = 新建空白分屏视图 zen-key-select-tab-1 = 选择标签页 #1 zen-key-select-tab-2 = 选择标签页 #2 @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = 切换 DOM zen-devtools-toggle-accessibility-shortcut = 切换无障碍环境 zen-close-all-unpinned-tabs-shortcut = 关闭所有未固定的标签页 zen-new-unsynced-window-shortcut = 新建空白窗口 +zen-duplicate-tab-shortcut = 克隆标签页 diff --git a/locales/zh-CN/browser/browser/zen-general.ftl b/locales/zh-CN/browser/browser/zen-general.ftl index 06202624d..0f2a108fc 100644 --- a/locales/zh-CN/browser/browser/zen-general.ftl +++ b/locales/zh-CN/browser/browser/zen-general.ftl @@ -48,6 +48,7 @@ zen-pinned-tab-replaced = 固定标签页的网址已更新为当前页面网址 zen-tabs-renamed = 标签页重命名成功! zen-background-tab-opened-toast = 新的后台标签页已打开 ! zen-workspace-renamed-toast = 工作区重命名成功! +zen-split-view-limit-toast = 无法在分屏视图中添加更多面板! zen-toggle-compact-mode-button = .label = 简洁模式 .tooltiptext = 切换简洁模式 @@ -121,4 +122,4 @@ zen-window-sync-migration-dialog-message = Zen 现已支持同一设备上的窗 zen-window-sync-migration-dialog-learn-more = 了解更多 zen-window-sync-migration-dialog-accept = 知道了 zen-appmenu-new-blank-window = - .label = 新空白窗口 + .label = 新建空白窗口 diff --git a/locales/zh-CN/browser/browser/zen-split-view.ftl b/locales/zh-CN/browser/browser/zen-split-view.ftl index 92e65dd2f..eb2e38c01 100644 --- a/locales/zh-CN/browser/browser/zen-split-view.ftl +++ b/locales/zh-CN/browser/browser/zen-split-view.ftl @@ -6,13 +6,13 @@ tab-zen-split-tabs = .label = { $tabCount -> [-1] 取消分屏标签页 - [1] 分屏标签页(需要选择多个标签页) + [1] 添加分屏视图 *[other] 分屏 { $tabCount } 个标签页 } .accesskey = S zen-split-link = .label = 拆分链接到新标签页 .accesskey = S -zen-split-view-modifier-header = 拆分视图 +zen-split-view-modifier-header = 分屏视图 zen-split-view-modifier-activate-reallocation = .label = 激活重新分配 diff --git a/locales/zh-CN/browser/browser/zen-vertical-tabs.ftl b/locales/zh-CN/browser/browser/zen-vertical-tabs.ftl index b8268f043..1ef740980 100644 --- a/locales/zh-CN/browser/browser/zen-vertical-tabs.ftl +++ b/locales/zh-CN/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = 两者都隐藏 .accesskey = H +zen-toolbar-context-move-to-folder = + .label = 移动到文件夹… + .accesskey = M zen-toolbar-context-new-folder = .label = 新建文件夹 .accesskey = N diff --git a/locales/zh-TW/browser/browser/preferences/zen-preferences.ftl b/locales/zh-TW/browser/browser/preferences/zen-preferences.ftl index ed55f391d..0b7a4eb24 100644 --- a/locales/zh-TW/browser/browser/preferences/zen-preferences.ftl +++ b/locales/zh-TW/browser/browser/preferences/zen-preferences.ftl @@ -8,9 +8,9 @@ category-zen-looks = zen-warning-language = 更改預設的語言可能會讓網站更容易的追蹤你。 zen-vertical-tabs-layout-header = 瀏覽器版面配置 zen-vertical-tabs-layout-description = 選擇最適合您的版面 -zen-layout-single-toolbar = 單一工具列 -zen-layout-multiple-toolbar = 多個工具列 -zen-layout-collapsed-toolbar = 摺疊工具列 +zen-layout-single-toolbar = 單一側邊欄 +zen-layout-multiple-toolbar = 側邊欄及頂部工具列 +zen-layout-collapsed-toolbar = 摺疊側邊欄 sync-currently-syncing-workspaces = 工作區 sync-engine-workspaces = .label = 工作區 @@ -18,7 +18,7 @@ sync-engine-workspaces = .accesskey = W zen-glance-title = Glance zen-glance-header = Glance 一般設定 -zen-glance-description = 無需打開新分頁,即可快速預覽 +zen-glance-description = 無需打開新分頁,即可快速預覽連結 zen-glance-trigger-label = 觸發方式 zen-glance-enabled = .label = 啟用 Glance @@ -43,9 +43,9 @@ category-zen-workspaces = .tooltiptext = { pane-zen-tabs-title } pane-settings-workspaces-title = 工作區 zen-tabs-select-recently-used-on-close = - .label = 關閉分頁後,切換至最近造訪過的分頁而非下一個相鄰分頁 + .label = 關閉分頁後,切換至最近造訪的分頁而非下一個相鄰分頁 zen-tabs-close-on-back-with-no-history = - .label = 在沒有返回紀錄時,關閉分頁並切換回其來源分頁(或最近使用的分頁) + .label = 若沒有上一頁紀錄,返回時關閉分頁並切換為其來源分頁(或最近造訪的分頁) zen-settings-workspaces-sync-unpinned-tabs = .label = 僅對工作區中的釘選分頁進行同步 zen-tabs-cycle-by-attribute = @@ -54,9 +54,9 @@ zen-tabs-cycle-ignore-pending-tabs = .label = 使用 Ctrl+Tab 切換分頁時,忽略卸載的分頁 zen-tabs-cycle-by-attribute-warning = Ctrl+Tab 將按最近使用順序循環,因為該設定已啟用 zen-look-and-feel-compact-toolbar-themed = - .label = 將主題顏色套用至折疊工具欄 + .label = 將主題顏色套用至緊湊模式時的工具欄 zen-workspace-continue-where-left-off = - .label = 繼續上次的工作階段 + .label = 從上次最後造訪的分頁繼續開始 pane-zen-pinned-tab-manager-title = 釘選分頁 zen-pinned-tab-manager-header = 釘選分頁的一般設定 zen-pinned-tab-manager-description = 釘選分頁的額外行為 @@ -64,7 +64,7 @@ zen-pinned-tab-manager-restore-pinned-tabs-to-pinned-url = .label = 啟動時恢復釘選分頁至其初始的網址 zen-pinned-tab-manager-container-specific-essentials-enabled = .label = 啟用容器專屬 Essentials 功能 -zen-pinned-tab-manager-close-shortcut-behavior-label = 關閉分頁快捷鍵的行為 +zen-pinned-tab-manager-close-shortcut-behavior-label = 使用快捷鍵關閉分頁時, zen-pinned-tab-manager-reset-unload-switch-close-shortcut-option = .label = 重置網址,卸載並切換到下一個分頁 zen-pinned-tab-manager-unload-switch-close-shortcut-option = @@ -83,7 +83,7 @@ zen-settings-workspaces-description = 透過工作區,您能同時擁有多個 zen-settings-workspaces-enabled = .label = 啟用工作區 zen-settings-workspaces-hide-default-container-indicator = - .label = 隱藏分頁欄中的容器指標 + .label = 在分頁欄中隱藏工作區預設的容器指標 zen-key-unsaved = 快捷鍵未儲存!請重新輸入並按下 "Escape" 鍵來儲存快捷鍵。 zen-key-conflict = 與 { $group } -> { $shortcut } 衝突 pane-zen-theme-title = 主題設定 @@ -164,7 +164,7 @@ pane-settings-CKS-title = { -brand-short-name } 快捷鍵 category-zen-marketplace = .tooltiptext = Zen 模組 zen-settings-CKS-header = 自定義您的快捷鍵 -zen-settings-CKS-description = 依據您的愛好來更改預設的快捷鍵以近一步改善你的瀏覽體驗 +zen-settings-CKS-description = 依據您的愛好來更改預設的快捷鍵以進一步改善瀏覽體驗 zen-settings-CKS-disable-firefox = .label = 停用 { -brand-short-name } 的預設快捷鍵 zen-settings-CKS-duplicate-shortcut = @@ -317,3 +317,4 @@ zen-devtools-toggle-dom-shortcut = 開啟 DOM zen-devtools-toggle-accessibility-shortcut = 開啟輔助功能 zen-close-all-unpinned-tabs-shortcut = 關閉所有未釘選的分頁 zen-new-unsynced-window-shortcut = 開新簡白視窗 +zen-duplicate-tab-shortcut = 複製分頁 diff --git a/locales/zh-TW/browser/browser/zen-general.ftl b/locales/zh-TW/browser/browser/zen-general.ftl index a4ad834c4..2f32fe5eb 100644 --- a/locales/zh-TW/browser/browser/zen-general.ftl +++ b/locales/zh-TW/browser/browser/zen-general.ftl @@ -14,15 +14,15 @@ tab-context-zen-reset-pinned-tab = tab-context-zen-add-essential = .label = 新增至 Essentials .accesskey = E -tab-context-zen-add-essential-badge = 已使用 { $num } / { $max } 個位置 +tab-context-zen-add-essential-badge = { $num } / { $max } tab-context-zen-remove-essential = .label = 從 Essentials 中移除 .accesskey = R tab-context-zen-replace-pinned-url-with-current = .label = { $isEssential -> - [true] 以當前URL替換Essentials - *[false] 以當前URL替換釘選分頁 + [true] 以當前網址替換Essentials + *[false] 以當前網址替換釘選分頁 } .accesskey = C tab-context-zen-edit-title = @@ -39,7 +39,7 @@ pictureinpicture-minimize-btn = .tooltip = 最小化 zen-panel-ui-gradient-generator-custom-color = 自訂顏色 zen-copy-current-url-confirmation = 網址已複製到剪貼簿! -zen-copy-current-url-as-markdown-confirmation = 已以Markdown格式複製當前URL! +zen-copy-current-url-as-markdown-confirmation = 已以Markdown格式複製當前網址! zen-general-cancel-label = .label = 取消 zen-general-confirm = @@ -48,9 +48,10 @@ zen-pinned-tab-replaced = 釘選分頁網址已替換為當前網址! zen-tabs-renamed = 已成功重新命名分頁! zen-background-tab-opened-toast = 已在背景開啟新分頁! zen-workspace-renamed-toast = 已成功重新命名工作區! +zen-split-view-limit-toast = 無法加入更多分頁至分割畫面! zen-toggle-compact-mode-button = - .label = 簡潔模式 - .tooltiptext = 切換簡潔模式 + .label = 緊湊模式 + .tooltiptext = 切換緊湊模式 # note: Do not translate the "
" tags in the following string diff --git a/locales/zh-TW/browser/browser/zen-split-view.ftl b/locales/zh-TW/browser/browser/zen-split-view.ftl index f4d7e4837..7072401a8 100644 --- a/locales/zh-TW/browser/browser/zen-split-view.ftl +++ b/locales/zh-TW/browser/browser/zen-split-view.ftl @@ -5,8 +5,9 @@ tab-zen-split-tabs = .label = { $tabCount -> - [1] 分割畫面頁籤 (需要選擇多個分頁) - *[other] 分割 { $tabCount } 個分頁 + [-1] 將分頁移出分割畫面 + [1] 新增分割畫面... + *[other] 將 { $tabCount } 個分頁組成分割畫面 } .accesskey = S zen-split-link = diff --git a/locales/zh-TW/browser/browser/zen-vertical-tabs.ftl b/locales/zh-TW/browser/browser/zen-vertical-tabs.ftl index dd94e1d15..9cc2509bd 100644 --- a/locales/zh-TW/browser/browser/zen-vertical-tabs.ftl +++ b/locales/zh-TW/browser/browser/zen-vertical-tabs.ftl @@ -17,6 +17,9 @@ zen-toolbar-context-compact-mode-just-toolbar = zen-toolbar-context-compact-mode-hide-both = .label = 兩者皆隱藏 .accesskey = H +zen-toolbar-context-move-to-folder = + .label = 移至分頁夾... + .accesskey = M zen-toolbar-context-new-folder = .label = 新增分頁夾 .accesskey = N diff --git a/locales/zh-TW/browser/browser/zen-workspaces.ftl b/locales/zh-TW/browser/browser/zen-workspaces.ftl index 33fd84550..c0a20939b 100644 --- a/locales/zh-TW/browser/browser/zen-workspaces.ftl +++ b/locales/zh-TW/browser/browser/zen-workspaces.ftl @@ -6,13 +6,13 @@ zen-panel-ui-workspaces-text = 工作區 zen-panel-ui-spaces-label = .label = 工作區 zen-panel-ui-workspaces-create = - .label = 建立工作區 + .label = 新增工作區 zen-panel-ui-folder-create = - .label = 建立分頁夾 + .label = 新增分頁夾 zen-panel-ui-live-folder-create = - .label = 建立動態分頁夾 + .label = 新增動態分頁夾 zen-panel-ui-new-empty-split = - .label = 新分頁分割 + .label = 新增分割畫面 zen-workspaces-panel-context-delete = .label = 刪除工作區 .accesskey = D @@ -54,7 +54,7 @@ zen-workspace-creation-name = .placeholder = 工作區名稱 zen-move-tab-to-workspace-button = .label = 移動至… - .tooltiptext = 將此視窗所有頁面移至單一工作區 + .tooltiptext = 將此視窗所有分頁移至單一工作區 zen-workspaces-panel-context-reorder = .label = 排序工作區 zen-workspace-creation-profile = 設定檔 diff --git a/package-lock.json b/package-lock.json index 65a139677..c0703a8f6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "devDependencies": { "@babel/preset-typescript": "^7.27.0", "@zen-browser/prettier": "^3.9.3", - "@zen-browser/surfer": "^1.13.4", + "@zen-browser/surfer": "^1.14.6", "formal-git": "^1.2.9", "globals": "^16.3.0", "husky": "^9.1.7", @@ -868,9 +868,9 @@ } }, "node_modules/@zen-browser/surfer": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/@zen-browser/surfer/-/surfer-1.13.4.tgz", - "integrity": "sha512-rpwceHYN3k1kaZP+IhAiZ5ksLgedqGv8uo8kqACFR4dDX/DO5P7XoCQtooOPrEyrxcI7uu/gI/xtuldfOBKUkQ==", + "version": "1.14.6", + "resolved": "https://registry.npmjs.org/@zen-browser/surfer/-/surfer-1.14.6.tgz", + "integrity": "sha512-hgTKadIJ/9/9dizHn4229ZZHQckd7D4OR6e7HVkcmtuq1Jke3OAn6SH7u7NjvzvYy35Mb2sXjVUvZEG1ITIfgg==", "dev": true, "license": "MPL-2.0", "dependencies": { @@ -1005,15 +1005,15 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.13.6", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz", - "integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.16.0.tgz", + "integrity": "sha512-6hp5CwvTPlN2A31g5dxnwAX0orzM7pmCRDLnZSX772mv8WDqICwFjowHuPs04Mc8deIld1+ejhtaMn5vp6b+1w==", "dev": true, "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.11", + "follow-redirects": "^1.16.0", "form-data": "^4.0.5", - "proxy-from-env": "^1.1.0" + "proxy-from-env": "^2.1.0" } }, "node_modules/b4a": { @@ -1902,9 +1902,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", "dev": true, "funding": [ { @@ -3254,11 +3254,14 @@ } }, "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=10" + } }, "node_modules/pump": { "version": "3.0.2", diff --git a/package.json b/package.json index a9d156ad1..705e3e515 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,6 @@ "sync:l10n": "python3 scripts/update_ff.py --just-l10n", "lint": "cd engine && ./mach lint zen", "lint:fix": "npm run lint -- --fix", - "prepare": "husky", "reset-ff": "surfer reset", "surfer": "surfer", "test": "python3 scripts/run_tests.py", @@ -38,9 +37,6 @@ "type": "git", "url": "git+https://github.com/zen-browser/desktop.git" }, - "hooks": { - "pre-commit": "npm run prepare" - }, "keywords": [], "author": "", "license": "MPL-2.0", @@ -51,7 +47,7 @@ "devDependencies": { "@babel/preset-typescript": "^7.27.0", "@zen-browser/prettier": "^3.9.3", - "@zen-browser/surfer": "^1.13.4", + "@zen-browser/surfer": "^1.14.6", "formal-git": "^1.2.9", "globals": "^16.3.0", "husky": "^9.1.7", diff --git a/prefs/firefox/browser.yaml b/prefs/firefox/browser.yaml index 41575963d..23db6a4e4 100644 --- a/prefs/firefox/browser.yaml +++ b/prefs/firefox/browser.yaml @@ -86,3 +86,7 @@ - name: browser.tabs.splitView.enabled value: false locked: true + +# See gh-12985 for details on the following preferences +- name: browser.search.widget.new + value: true diff --git a/prefs/zen/boosts.yaml b/prefs/zen/boosts.yaml new file mode 100644 index 000000000..b0de3d3fa --- /dev/null +++ b/prefs/zen/boosts.yaml @@ -0,0 +1,22 @@ +# 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/. + +- name: zen.boosts.enabled + value: "@IS_TWILIGHT@" + +- name: zen.boosts.dissolve-on-zap + value: true + +- name: zen.boosts.invert-channel-floor + value: 15 + cpptype: uint32_t + mirror: once + type: static + +- name: zen.boosts.disable-on-anonymous-content + value: true + cpptype: bool + mirror: once + type: static + diff --git a/prefs/zen/glance.yaml b/prefs/zen/glance.yaml index ef01648c1..5788dbca1 100644 --- a/prefs/zen/glance.yaml +++ b/prefs/zen/glance.yaml @@ -18,4 +18,4 @@ value: 350 # in milliseconds - name: zen.glance.deactivate-docshell-during-animation - value: true + value: false diff --git a/prefs/zen/gtk.yaml b/prefs/zen/gtk.yaml index 5fb3a41ab..c0f39dd26 100644 --- a/prefs/zen/gtk.yaml +++ b/prefs/zen/gtk.yaml @@ -4,9 +4,9 @@ # GTK-specific preferences - name: widget.gtk.rounded-bottom-corners.enabled - # Disabled for https://github.com/zen-browser/desktop/issues/6302, - # also see https://bugzilla.mozilla.org/show_bug.cgi?id=1979083 - value: false + # See https://bugzilla.mozilla.org/show_bug.cgi?id=1979083 + # and https://bugzilla.mozilla.org/show_bug.cgi?id=1994267 + value: true condition: "defined(MOZ_WIDGET_GTK)" - name: zen.widget.linux.transparency diff --git a/prefs/zen/live-folders.yaml b/prefs/zen/live-folders.yaml new file mode 100644 index 000000000..f6573e637 --- /dev/null +++ b/prefs/zen/live-folders.yaml @@ -0,0 +1,6 @@ +# 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/. + +- name: zen.live-folders.github.skip-new-pr-ui-check + value: false diff --git a/prefs/zen/macos.yaml b/prefs/zen/macos.yaml index bf28c7266..abca5c987 100644 --- a/prefs/zen/macos.yaml +++ b/prefs/zen/macos.yaml @@ -27,3 +27,7 @@ - name: widget.macos.native-popovers value: true condition: "defined(XP_MACOSX)" + +- name: zen.widget.macos.override-system-swipe-gestures + value: true + condition: "defined(XP_MACOSX)" diff --git a/prefs/zen/split-view.yaml b/prefs/zen/split-view.yaml index 869b4c459..094d3516f 100644 --- a/prefs/zen/split-view.yaml +++ b/prefs/zen/split-view.yaml @@ -5,6 +5,9 @@ - name: zen.splitView.enable-tab-drop value: true +- name: zen.splitView.enable-tab-click-split + value: true + - name: zen.splitView.min-resize-width value: 7 @@ -15,7 +18,7 @@ value: true - name: zen.splitView.drag-over-split-delayMC - value: 1000 + value: 500 - name: zen.splitView.drag-over-split-threshold value: 40 diff --git a/prefs/zen/theme.yaml b/prefs/zen/theme.yaml index a77cff584..fe0a8d485 100644 --- a/prefs/zen/theme.yaml +++ b/prefs/zen/theme.yaml @@ -18,7 +18,7 @@ value: true - name: zen.theme.acrylic-elements - value: false + value: "@IS_TWILIGHT@" - name: zen.theme.disable-lightweight value: true diff --git a/prefs/zen/view.yaml b/prefs/zen/view.yaml index d990c13fa..4e84831c8 100644 --- a/prefs/zen/view.yaml +++ b/prefs/zen/view.yaml @@ -32,7 +32,10 @@ value: false - name: zen.view.grey-out-inactive-windows + cpptype: bool value: true + mirror: always + type: static - name: zen.view.show-newtab-button-top value: true @@ -57,3 +60,9 @@ - name: zen.view.overflow-webext-toolbar value: "@IS_TWILIGHT@" + +- name: zen.view.overflow-webext-toolbar-threshold + value: 60 + +- name: zen.view.enable-loading-indicator + value: true diff --git a/prefs/zen/workspaces.yaml b/prefs/zen/workspaces.yaml index 400a4bc9e..24aaf887c 100644 --- a/prefs/zen/workspaces.yaml +++ b/prefs/zen/workspaces.yaml @@ -17,6 +17,12 @@ - name: zen.workspaces.swipe-actions value: true +- name: zen.workspaces.swipe-actions.delta-multiplier + value: 100 + +- name: zen.workspaces.switch-animation-duration + value: 200 + - name: zen.workspaces.wrap-around-navigation value: true @@ -33,7 +39,7 @@ value: true - name: zen.workspaces.dnd-switch-padding - value: 5 + value: 20 - name: zen.workspaces.debug value: "@cond" diff --git a/prefs/zen/zen-urlbar.yaml b/prefs/zen/zen-urlbar.yaml index a39a77e24..fefa24e40 100644 --- a/prefs/zen/zen-urlbar.yaml +++ b/prefs/zen/zen-urlbar.yaml @@ -39,3 +39,6 @@ - name: zen.urlbar.suggestions.quick-actions value: true + +- name: browser.urlbar.shortcuts.workspaces + value: true \ No newline at end of file diff --git a/prefs/zen/zen.yaml b/prefs/zen/zen.yaml index fd0dbd67f..685bb3866 100644 --- a/prefs/zen/zen.yaml +++ b/prefs/zen/zen.yaml @@ -27,7 +27,7 @@ value: 20 # Percentage of folder height to trigger dragover - name: zen.tabs.dnd-switch-space-delay - value: 800 # milliseconds + value: 500 # milliseconds - name: zen.ctrlTab.show-pending-tabs value: false diff --git a/scripts/download_pgo_extended_corpus.py b/scripts/download_pgo_extended_corpus.py new file mode 100644 index 000000000..f8a06190a --- /dev/null +++ b/scripts/download_pgo_extended_corpus.py @@ -0,0 +1,59 @@ +# 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/. + +import yaml +import os +import requests +import tarfile +import hashlib + +EXTENDED_CORPUS_KEY="pgo-extended-corpus" +TASKCLUSTER_PATH=os.path.join("taskcluster", "kinds", "fetch", "benchmarks.yml") + +def download_corpus(corpus_url, expected_sha256, output_path): + response = requests.get(corpus_url, stream=True) + response.raise_for_status() + + os.makedirs(output_path, exist_ok=True) + archive_path = os.path.join(output_path, "corpus.tar.gz") + + with open(archive_path, "wb") as f: + for chunk in response.iter_content(chunk_size=8192): + f.write(chunk) + + # Verify the SHA256 checksum + sha256 = hashlib.sha256() + with open(archive_path, "rb") as f: + for chunk in iter(lambda: f.read(8192), b""): + sha256.update(chunk) + if sha256.hexdigest() != expected_sha256: + os.remove(archive_path) + raise ValueError("SHA256 checksum does not match expected value.") + + print("Checksum verified ({}). Extracting corpus...".format(expected_sha256)) + with tarfile.open(archive_path, "r:gz") as tar: + tar.extractall(path=output_path) + + # rename "JetStream-[id]" to just "JetStream" + for item in os.listdir(output_path): + if item.startswith("JetStream-"): + os.rename(os.path.join(output_path, item), os.path.join(output_path, "JetStream")) + break + + # Clean up the downloaded archive + os.remove(archive_path) + print(f"Corpus downloaded and extracted to: {output_path}") + +def main(): + print("\n------------------------------------\n") + print("Fetching PGO extended corpus information from Taskcluster...") + with open(TASKCLUSTER_PATH, "r", encoding="utf-8") as f: + benchmarks = yaml.safe_load(f) + corpus_url = benchmarks[EXTENDED_CORPUS_KEY] + fetch_info = corpus_url["fetch"] + download_corpus(fetch_info["url"], fetch_info["sha256"], "pgo-extended-corpus") + print("\n------------------------------------\n") + +if __name__ == "__main__": + main() diff --git a/scripts/mar_sign.sh b/scripts/mar_sign.sh new file mode 100644 index 000000000..48cde3858 --- /dev/null +++ b/scripts/mar_sign.sh @@ -0,0 +1,244 @@ +#!/usr/bin/env bash +# 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/. + +set -e + +CERT_PATH_DIR=build/signing +UPDATER_CERT_DIR="engine/toolkit/mozapps/update/updater" +NSS_CONFIG_DIR="$CERT_PATH_DIR/nss_config" + +generate_certs() { + mkdir temp + cd temp + + # 1. Generate private key + openssl genrsa -out private_key.pem 4096 + + # 2. Generate self-signed certificate (required for PKCS#12 bundling) + # RFC 5280 "no well-defined expiration" sentinel: 99991231235959Z + openssl req -new -x509 \ + -key private_key.pem \ + -out cert.pem \ + -subj "/CN=MAR Signing" \ + -not_before 20000101000000Z \ + -not_after 99991231235959Z + + # 3. Export certificate as DER (for embedding in updater) + openssl x509 -in cert.pem -outform DER -out public_key.der + + cd .. + mkdir -p "$CERT_PATH_DIR" + mv temp/private_key.pem "$CERT_PATH_DIR"/private_key.pem + mv temp/cert.pem "$CERT_PATH_DIR"/cert.pem + mv temp/public_key.der "$CERT_PATH_DIR"/public_key.der + + mkdir -p "$CERT_PATH_DIR/env" + base64 -w 0 "$CERT_PATH_DIR"/cert.pem > "$CERT_PATH_DIR"/env/ZEN_SIGNING_CERT_PEM_BASE64 + base64 -w 0 "$CERT_PATH_DIR"/private_key.pem > "$CERT_PATH_DIR"/env/ZEN_SIGNING_PRIVATE_KEY_PEM_BASE64 + + # Make sure no private keys or certs are left + # in the public_key.der file, which is the only one that + # should be distributed and embedded in the updater + openssl x509 -in "$CERT_PATH_DIR"/public_key.der -inform DER -noout -text > /dev/null + + rm -rf temp +} + +import_cert() { + if [ ! -f "$CERT_PATH_DIR/public_key.der" ]; then + echo "Error: public_key.der not found. Run with -g first." >&2 + exit 1 + fi + files=( + "$UPDATER_CERT_DIR/release_primary.der" + "$UPDATER_CERT_DIR/release_secondary.der" + "$UPDATER_CERT_DIR/dep1.der" + "$UPDATER_CERT_DIR/dep2.der" + "$UPDATER_CERT_DIR/xpcshellCertificate.der" + ) + for file in "${files[@]}"; do + if [ ! -f "$file" ]; then + echo "Error: $file not found. Make sure the updater certificates exist." >&2 + exit 1 + fi + rm -f "$file" + echo "Copying $CERT_PATH_DIR/public_key.der to $file" + cp "$CERT_PATH_DIR/public_key.der" "$file" + done + echo "Done. Rebuild the updater to embed the new certificate." +} + +create_nss_config_dir() { + rm -rf "$NSS_CONFIG_DIR" + mkdir "$NSS_CONFIG_DIR" + + if [ -z "$ZEN_MAR_SIGNING_PASSWORD" ]; then + echo "Warning: ZEN_MAR_SIGNING_PASSWORD environment variable not set. Using empty password." >&2 + ZEN_MAR_SIGNING_PASSWORD="" + fi + + password_file="$NSS_CONFIG_DIR/password.txt" + echo "$ZEN_MAR_SIGNING_PASSWORD" > "$password_file" + + if [ "$ZEN_SIGNING_CERT_PEM_BASE64" ]; then + echo "Decoding signing certificate from ZEN_SIGNING_CERT_PEM_BASE64 environment variable..." + echo "$ZEN_SIGNING_CERT_PEM_BASE64" | base64 -d > "$CERT_PATH_DIR/cert.pem" + fi + + if [ "$ZEN_SIGNING_PRIVATE_KEY_PEM_BASE64" ]; then + echo "Decoding signing private key from ZEN_SIGNING_PRIVATE_KEY_PEM_BASE64 environment variable..." + echo "$ZEN_SIGNING_PRIVATE_KEY_PEM_BASE64" | base64 -d > "$CERT_PATH_DIR/private_key.pem" + fi + + echo "Generating NSS config directory at $NSS_CONFIG_DIR" + certutil -N -d "$NSS_CONFIG_DIR" -f "$password_file" + + echo "Wrapping private key into PKCS#12..." + echo "Wrapping key + cert into PKCS#12..." + openssl pkcs12 -export \ + -inkey "$CERT_PATH_DIR/private_key.pem" \ + -in "$CERT_PATH_DIR/cert.pem" \ + -name "mar_sig" \ + -passout pass:"$ZEN_MAR_SIGNING_PASSWORD" \ + -out "$CERT_PATH_DIR/private_key.p12" + + echo "Importing PKCS#12 into NSS database..." + pk12util \ + -i "$CERT_PATH_DIR/private_key.p12" \ + -d "$NSS_CONFIG_DIR" \ + -W "$ZEN_MAR_SIGNING_PASSWORD" \ + -K "$ZEN_MAR_SIGNING_PASSWORD" +} + +cleanup_certs() { + rm -rf "$NSS_CONFIG_DIR" + rm -rf "$CERT_PATH_DIR/env" + + rm -f "$CERT_PATH_DIR/private_key.p12" + rm -f "$CERT_PATH_DIR/private_key.pem" + rm -f "$CERT_PATH_DIR/cert.pem" +} + +update_manifests() { + mar_file=$(basename "$1") + if [[ "$mar_file" == "linux.mar" ]]; then + manifest="linux_update_manifest_x86_64" + elif [[ "$mar_file" == "linux-aarch64.mar" ]]; then + manifest="linux_update_manifest_aarch64" + elif [[ "$mar_file" == "windows.mar" ]]; then + manifest=".github/workflows/object/windows-x64-signed-x86_64/update_manifest" + if [ ! -d "$manifest" ]; then + manifest="windows_update_manifest_x86_64" + fi + elif [[ "$mar_file" == "windows-arm64.mar" ]]; then + manifest=".github/workflows/object/windows-x64-signed-arm64/update_manifest" + if [ ! -d "$manifest" ]; then + manifest="windows_update_manifest_arm64" + fi + elif [[ "$mar_file" == "macos.mar" ]]; then + manifest="macos_update_manifest" + else + echo "Unknown MAR file name format: $mar_file. Skipping manifest update." >&2 + exit 1 + fi + # There can be any update.xml file, lets just recursively search for the one + manifest_files=$(find "$manifest" -type f -name "update.xml") + for manifest_file in $manifest_files; do + # Example manifest: + # + # + # + # + # When signing the mar, hashValue and size will change, so we need to update the manifest with + # the new values. We can get the new values by running "mar -i signed_mar_file.mar" + echo "Updating manifest $manifest_file with new hash and size for $mar_file" + size=$(wc -c < "$1" | tr -d ' ') + hashValue=$(sha512sum "$1" | awk '{print $1}') + # Update the manifest with the new values. We can use sed to do this. + # We need to find the line that contains the URL of the mar file, and update the hashValue and size attributes in the same element. + old_hashValue=$(grep -oP 'hashValue="\K[^"]+' "$manifest_file") + old_size=$(grep -oP 'size="\K[^"]+' "$manifest_file") + if [ -z "$old_hashValue" ] || [ -z "$old_size" ]; then + echo "Could not find old hashValue or size in manifest. Skipping manifest update." >&2 + exit 1 + fi + echo "Old hashValue: $old_hashValue, Old size: $old_size" + echo "New hashValue: $hashValue, New size: $size" + sed -i.bak "s/hashValue=\"$old_hashValue\"/hashValue=\"$hashValue\"/g; s/size=\"$old_size\"/size=\"$size\"/g" "$manifest_file" + rm "$manifest_file.bak" + echo "Manifest updated with new hashValue and size for $mar_file" + done +} + +sign_mars() { + if [ ! -f "$SIGNMAR" ]; then + echo "Error: signmar not found at $SIGNMAR. Build the engine first." >&2 + exit 1 + fi + + chmod +x "$SIGNMAR" + + create_nss_config_dir + + folders=( + linux.mar + linux-aarch64.mar + macos.mar + ) + + if [ -d ".github/workflows/object/windows-x64-signed-x86_64" ]; then + folders+=(".github/workflows/object/windows-x64-signed-x86_64") + folders+=(".github/workflows/object/windows-x64-signed-arm64") + else + folders+=("windows.mar") + folders+=("windows-arm64.mar") + fi + + # each folder will contain the .mar files for that platform, and the signature will be written in-place + for folder in "${folders[@]}"; do + if [ -d "$folder" ]; then + for mar_file in "$folder"/*.mar; do + if [ -f "$mar_file" ]; then + echo "" + echo "Signing $mar_file..." + # mar [-C workingDir] -d NSSConfigDir -n certname -s archive.mar out_signed_archive.mar + "$SIGNMAR" -d "$NSS_CONFIG_DIR" -n "mar_sig" -s "$mar_file" "$mar_file".signed + echo "Signed $mar_file. Verifying signature..." + "$SIGNMAR" -d "$NSS_CONFIG_DIR" -n "mar_sig" -v "$mar_file".signed + mv "$mar_file".signed "$mar_file" + echo "Successfully signed $mar_file" + update_manifests "$mar_file" + else + echo "No .mar files found in $folder, skipping." + exit 1 + fi + done + else + echo "Directory $folder not found, skipping." + exit 1 + fi + done + + cleanup_certs +} + +case "$1" in + -g) + generate_certs + ;; + -i) + import_cert + ;; + -s) + sign_mars + ;; + *) + echo "Usage: $0 [-g] [-i] [-s]" >&2 + echo " -g Generate MAR signing certificates" >&2 + echo " -i Import the certificate into the updater (release_primary.der)" >&2 + echo " -s Sign *.mar files in the current directory in-place" >&2 + exit 1 + ;; +esac diff --git a/scripts/mar_verify.sh b/scripts/mar_verify.sh new file mode 100755 index 000000000..f9b5d670b --- /dev/null +++ b/scripts/mar_verify.sh @@ -0,0 +1,280 @@ +#!/usr/bin/env bash +# 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/. +# +# Pre-release verification for MAR artifacts. +# +# Run this AFTER `scripts/mar_sign.sh -s` and BEFORE publishing a release. +# It confirms that every MAR we are about to ship is present, non-empty, +# signed with the cert whose public half lives at build/signing/public_key.der, +# and that the accompanying update.xml manifest reflects the signed file's +# current sha512 / size. Exits non-zero on the first verification failure so +# CI halts before we overwrite a good release with a broken one. + +set -euo pipefail + +CERT_PATH_DIR="build/signing" +PUBLIC_KEY_DER="$CERT_PATH_DIR/public_key.der" +VERIFY_NSS_DIR="$CERT_PATH_DIR/nss_verify" +LINUX_EXTRACT_DIR="$CERT_PATH_DIR/extracted_linux" +LINUX_ARCHIVE="zen.linux-x86_64.tar.xz/zen.linux-x86_64.tar.xz" + +FAILURES=0 + +fail() { + echo " [FAIL] $*" >&2 + FAILURES=$((FAILURES + 1)) +} + +ok() { + echo " [ OK ] $*" +} + +cleanup_verify_db() { + rm -rf "$VERIFY_NSS_DIR" + rm -rf "$LINUX_EXTRACT_DIR" +} +trap cleanup_verify_db EXIT + +if [ -z "${SIGNMAR:-}" ]; then + echo "Error: SIGNMAR environment variable is not set." >&2 + exit 1 +fi +if [ ! -f "$SIGNMAR" ]; then + echo "Error: signmar not found at $SIGNMAR." >&2 + exit 1 +fi +chmod +x "$SIGNMAR" + +if [ ! -f "$PUBLIC_KEY_DER" ]; then + echo "Error: $PUBLIC_KEY_DER not found. Run 'mar_sign.sh -g' first." >&2 + exit 1 +fi + +EXPECTED_MAR_CHANNEL="${RELEASE_BRANCH:-}" +if [ -z "$EXPECTED_MAR_CHANNEL" ]; then + echo "Error: RELEASE_BRANCH environment variable is not set (expected 'release' or 'twilight')." >&2 + exit 1 +fi + +# Build a throwaway NSS database that trusts only the signing cert, so +# signmar -v can verify signatures without the private key being present. +setup_verify_db() { + rm -rf "$VERIFY_NSS_DIR" + mkdir -p "$VERIFY_NSS_DIR" + local pass="$VERIFY_NSS_DIR/password.txt" + : > "$pass" + certutil -N -d "$VERIFY_NSS_DIR" -f "$pass" + certutil -A -d "$VERIFY_NSS_DIR" -n "mar_sig" -t "CT,C,C" -i "$PUBLIC_KEY_DER" +} + +# Each entry: "||" +declare -a pairs=( + "linux.mar/linux.mar|linux_update_manifest_x86_64|linux-x86_64" + "linux-aarch64.mar/linux-aarch64.mar|linux_update_manifest_aarch64|linux-aarch64" + "macos.mar/macos.mar|macos_update_manifest|macos" +) + +if [ -d ".github/workflows/object/windows-x64-signed-x86_64" ]; then + pairs+=( + ".github/workflows/object/windows-x64-signed-x86_64/windows.mar|.github/workflows/object/windows-x64-signed-x86_64/update_manifest|windows-x86_64" + ".github/workflows/object/windows-x64-signed-arm64/windows-arm64.mar|.github/workflows/object/windows-x64-signed-arm64/update_manifest|windows-arm64" + ) +else + pairs+=( + "windows.mar/windows.mar|windows_update_manifest_x86_64|windows-x86_64" + "windows-arm64.mar/windows-arm64.mar|windows_update_manifest_arm64|windows-arm64" + ) +fi + +hash_file() { + if command -v sha512sum >/dev/null 2>&1; then + sha512sum "$1" | awk '{print $1}' + else + shasum -a 512 "$1" | awk '{print $1}' + fi +} + +size_file() { + wc -c < "$1" | tr -d ' ' +} + +verify_signature() { + local mar="$1" + if "$SIGNMAR" -d "$VERIFY_NSS_DIR" -n "mar_sig" -v "$mar" >/dev/null 2>&1; then + ok "Signature valid: $mar" + else + fail "Signature INVALID (or missing) for $mar" + fi +} + +# Cache signmar -T output per MAR so we only pay for it once per file. +mar_info() { + local mar="$1" + "$SIGNMAR" -T "$mar" 2>&1 +} + +verify_signature_count() { + local mar="$1" + local info count + info=$(mar_info "$mar") + # signmar -T prints one "Signature block found with 1 signature" line per signature block. + count=$(echo "$info" | grep -cE '^[[:space:]]*Signature block found with [0-9]+ signature' || true) + if [ "$count" != "1" ]; then + fail "$mar has $count signatures, expected exactly 1" + else + ok "$mar has exactly 1 signature" + fi +} + +verify_mar_channel() { + local mar="$1" + local info channel + info=$(mar_info "$mar") + # Accept either "MAR channel name:" or "MAR channel ID:" — the label + # has drifted between Mozilla releases. + channel=$(echo "$info" \ + | grep -iE 'MAR channel (name|id)[[:space:]]*:' \ + | head -1 \ + | sed -E 's/.*MAR channel (name|id)[[:space:]]*:[[:space:]]*//I' \ + | tr -d '[:space:]') + if [ -z "$channel" ]; then + fail "$mar: could not read MAR channel from product info block" + return + fi + if [ "$channel" != "$EXPECTED_MAR_CHANNEL" ]; then + fail "$mar: MAR channel is '$channel', expected '$EXPECTED_MAR_CHANNEL' (RELEASE_BRANCH)" + else + ok "$mar: MAR channel = $channel" + fi +} + +verify_update_settings() { + echo "" + echo "Checking update-settings.ini in $LINUX_ARCHIVE..." + if [ ! -f "$LINUX_ARCHIVE" ]; then + fail "Linux build archive not found at $LINUX_ARCHIVE" + return + fi + + rm -rf "$LINUX_EXTRACT_DIR" + mkdir -p "$LINUX_EXTRACT_DIR" + if ! tar -xf "$LINUX_ARCHIVE" -C "$LINUX_EXTRACT_DIR"; then + fail "Failed to extract $LINUX_ARCHIVE" + return + fi + + local ini + ini=$(find "$LINUX_EXTRACT_DIR" -type f -name "update-settings.ini" | head -1) + if [ -z "$ini" ]; then + fail "update-settings.ini not found inside $LINUX_ARCHIVE" + return + fi + + local accepted + accepted=$(grep -oP '^ACCEPTED_MAR_CHANNEL_IDS[[:space:]]*=[[:space:]]*\K.*' "$ini" \ + | head -1 | tr -d '\r') + if [ -z "$accepted" ]; then + fail "ACCEPTED_MAR_CHANNEL_IDS not set in $ini" + return + fi + + # ACCEPTED_MAR_CHANNEL_IDS is a comma-separated list; membership is what + # the updater enforces, so we check membership rather than strict equality. + local found=0 entry + IFS=',' read -ra entries <<< "$accepted" + for entry in "${entries[@]}"; do + entry=$(echo "$entry" | tr -d '[:space:]') + if [ "$entry" = "$EXPECTED_MAR_CHANNEL" ]; then + found=1 + break + fi + done + + if [ "$found" = "1" ]; then + ok "update-settings.ini accepts MAR channel '$EXPECTED_MAR_CHANNEL' (ACCEPTED_MAR_CHANNEL_IDS=$accepted)" + else + fail "update-settings.ini ACCEPTED_MAR_CHANNEL_IDS='$accepted' does not include '$EXPECTED_MAR_CHANNEL'" + fi +} + +verify_manifest() { + local mar="$1" manifest_dir="$2" label="$3" + + if [ ! -d "$manifest_dir" ]; then + fail "$label: manifest directory $manifest_dir not found" + return + fi + + local xmls + xmls=$(find "$manifest_dir" -type f -name "update.xml") + if [ -z "$xmls" ]; then + fail "$label: no update.xml files found under $manifest_dir" + return + fi + + local actual_hash actual_size + actual_hash=$(hash_file "$mar") + actual_size=$(size_file "$mar") + + while IFS= read -r xml; do + local xhash xsize xurl + xhash=$(grep -oP 'hashValue="\K[^"]+' "$xml" | head -1 || true) + xsize=$(grep -oP 'size="\K[^"]+' "$xml" | head -1 || true) + xurl=$(grep -oP 'URL="\K[^"]+' "$xml" | head -1 || true) + if [ -z "$xhash" ] || [ -z "$xsize" ]; then + fail "$label: $xml is missing hashValue or size" + continue + fi + if [ -z "$xurl" ]; then + fail "$label: $xml is missing URL attribute" + continue + fi + if ! grep -q 'hashFunction="sha512"' "$xml"; then + fail "$label: $xml hashFunction is not sha512" + continue + fi + if [ "$xhash" != "$actual_hash" ]; then + fail "$label: hashValue mismatch in $xml" + echo " manifest: $xhash" >&2 + echo " actual: $actual_hash" >&2 + continue + fi + if [ "$xsize" != "$actual_size" ]; then + fail "$label: size mismatch in $xml (manifest=$xsize, actual=$actual_size)" + continue + fi + ok "$label: $(basename "$xml") matches $mar (size=$actual_size)" + done <<< "$xmls" +} + +setup_verify_db +verify_update_settings + +for entry in "${pairs[@]}"; do + IFS='|' read -r mar manifest label <<< "$entry" + echo "" + echo "Verifying $label: $mar" + + if [ ! -f "$mar" ]; then + fail "$label: MAR file $mar not found" + continue + fi + if [ ! -s "$mar" ]; then + fail "$label: MAR file $mar is empty" + continue + fi + + verify_signature "$mar" + verify_signature_count "$mar" + verify_mar_channel "$mar" + verify_manifest "$mar" "$manifest" "$label" +done + +echo "" +if [ "$FAILURES" -gt 0 ]; then + echo "Pre-release verification FAILED with $FAILURES issue(s)." >&2 + exit 1 +fi +echo "Pre-release verification passed." diff --git a/scripts/update_external_patches.py b/scripts/update_external_patches.py index 1ad279ab6..341cb77f7 100644 --- a/scripts/update_external_patches.py +++ b/scripts/update_external_patches.py @@ -24,6 +24,9 @@ def download_phab_patch(phab_id, output_file): print(f"Downloading patch from {patch_url}") response = requests.get(patch_url) response.raise_for_status() # Raise an error for bad responses + folder = os.path.dirname(output_file) + if not os.path.exists(folder): + os.makedirs(folder) with open(output_file, 'wb') as f: f.write(response.content) print(f"Patch saved to {output_file}") @@ -51,24 +54,27 @@ def main(): expected_files = set() for patch in manifest: if patch.get("type") == "phabricator": - phab_id = patch.get("id") + phab_ids = [patch.get("id")] if patch.get("id") else patch.get("ids", []) name = patch.get("name") - if not phab_id or not name: + if not phab_ids or not name: die(f"Patch entry missing 'id' or 'name': {patch}") - name = name.replace(" ", "_").lower() - output_file = os.path.join(OUTPUT_DIR, "firefox", f"{name}.patch") - print(f"Processing Phabricator patch: {phab_id} -> {output_file}") - download_phab_patch(phab_id, output_file) - replaces = patch.get("replaces", {}) - for replace in replaces.keys(): - value = replaces[replace] - with open(output_file, 'r') as f: - content = f.read() - if replace not in content: - die(f"Replace string '{replace}' not found in {output_file}") - with open(output_file, 'w') as f: - f.write(content.replace(replace, value)) - expected_files.add(output_file) + name = name.replace(" ", "_").replace(".", "_").lower() + for phab_id in phab_ids: + output_file = ( + os.path.join(OUTPUT_DIR, "firefox", f"{name}.patch") + if len(phab_ids) == 1 else + os.path.join(OUTPUT_DIR, "firefox", name, f"{phab_id}.patch") + ) + print(f"Processing Phabricator patch: {phab_id} -> {output_file}") + download_phab_patch(phab_id, output_file) + replaces = patch.get("replaces", {}) + for replace in replaces.keys(): + value = replaces[replace] + with open(output_file, 'r') as f: + content = f.read() + with open(output_file, 'w') as f: + f.write(content.replace(replace, value)) + expected_files.add(output_file) elif patch.get("type") == "local": print(f"Local patch: {patch.get('path')}") expected_files.add(os.path.join(OUTPUT_DIR, patch.get("path"))) diff --git a/src/-prettierignore.patch b/src/-prettierignore.patch index a1963c31b..5f64ab634 100644 --- a/src/-prettierignore.patch +++ b/src/-prettierignore.patch @@ -1,8 +1,8 @@ diff --git a/.prettierignore b/.prettierignore -index 3e562d74d744bb703c9ebb8ea2df496feb40f48a..3285ee51c32029ce80e8b22616389bc776345da4 100644 +index 4c6d186c501f7e1b029db186d387f73d97dfed1e..81d0f7dc7f412151256c13ed93bd34d7bd872681 100644 --- a/.prettierignore +++ b/.prettierignore -@@ -1804,3 +1804,12 @@ tools/ts/test/baselines/ +@@ -1806,3 +1806,12 @@ tools/ts/test/baselines/ try_task_config.json xpcom/idl-parser/xpidl/fixtures/xpctest.d.json **/package-lock.json diff --git a/src/browser/base/content/browser-addons-js.patch b/src/browser/base/content/browser-addons-js.patch index ba8db225e..eb78c7461 100644 --- a/src/browser/base/content/browser-addons-js.patch +++ b/src/browser/base/content/browser-addons-js.patch @@ -1,5 +1,5 @@ diff --git a/browser/base/content/browser-addons.js b/browser/base/content/browser-addons.js -index 737ccf0feff4910426f7f75a20ac20ec480461dc..177ab12f9478e1ce664b7b2c66b0e2c8bca63573 100644 +index 098cf2408bc462f61fb547716754a3af832ffa1e..d062d03f7d258b66740d785cc30abe7932eaea41 100644 --- a/browser/base/content/browser-addons.js +++ b/browser/base/content/browser-addons.js @@ -1061,7 +1061,7 @@ var gXPInstallObserver = { diff --git a/src/browser/base/content/browser-box-inc-xhtml.patch b/src/browser/base/content/browser-box-inc-xhtml.patch index 7e8420c22..6f5f7ee91 100644 --- a/src/browser/base/content/browser-box-inc-xhtml.patch +++ b/src/browser/base/content/browser-box-inc-xhtml.patch @@ -1,32 +1,40 @@ diff --git a/browser/base/content/browser-box.inc.xhtml b/browser/base/content/browser-box.inc.xhtml -index 2faed30e09511c381051bc40910a883d1d7bc10d..959fa83f647a8919641c5b852a4cb8814fca9ab5 100644 +index d58fcdf99843d110b708f3fbf9fb317787fadfcf..cfc2aad902641609c3804e615c4cb66ce65299b7 100644 --- a/browser/base/content/browser-box.inc.xhtml +++ b/browser/base/content/browser-box.inc.xhtml -@@ -3,6 +3,9 @@ +@@ -3,12 +3,22 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. ++#include navigator-toolbox.inc.xhtml + + + + diff --git a/src/browser/base/content/browser-commands-js.patch b/src/browser/base/content/browser-commands-js.patch index fa9a0af70..47fc89a6b 100644 --- a/src/browser/base/content/browser-commands-js.patch +++ b/src/browser/base/content/browser-commands-js.patch @@ -1,5 +1,5 @@ diff --git a/browser/base/content/browser-commands.js b/browser/base/content/browser-commands.js -index 75ddd82c18979571f377dec94fe1883f1349cc16..58ae4d88f7f0b37187cb61fcaf4cf84a9af7991f 100644 +index e7f88f69fcc22efca4c7fc0363b9fe2729e1be6f..d7c4063d38576a34545b9af06c37937dc9090628 100644 --- a/browser/base/content/browser-commands.js +++ b/browser/base/content/browser-commands.js @@ -14,6 +14,10 @@ var BrowserCommands = { @@ -13,7 +13,7 @@ index 75ddd82c18979571f377dec94fe1883f1349cc16..58ae4d88f7f0b37187cb61fcaf4cf84a try { gBrowser.goBack(); } catch (ex) {} -@@ -315,6 +319,10 @@ var BrowserCommands = { +@@ -229,6 +233,10 @@ var BrowserCommands = { } } @@ -24,11 +24,14 @@ index 75ddd82c18979571f377dec94fe1883f1349cc16..58ae4d88f7f0b37187cb61fcaf4cf84a // A notification intended to be useful for modular peformance tracking // starting as close as is reasonably possible to the time when the user // expressed the intent to open a new tab. Since there are a lot of -@@ -399,6 +407,11 @@ var BrowserCommands = { +@@ -313,6 +321,14 @@ var BrowserCommands = { return; } + if (gBrowser.selectedTab.hasAttribute("zen-empty-tab")) { ++ if (gBrowser.selectedTab.hasAttribute("split-view")) { ++ return; ++ } + gZenWorkspaces.handleTabCloseWindow(); + return; + } @@ -36,7 +39,7 @@ index 75ddd82c18979571f377dec94fe1883f1349cc16..58ae4d88f7f0b37187cb61fcaf4cf84a // Keyboard shortcuts that would close a tab that is pinned select the first // unpinned tab instead. if ( -@@ -406,8 +419,8 @@ var BrowserCommands = { +@@ -320,8 +336,8 @@ var BrowserCommands = { (event.ctrlKey || event.metaKey || event.altKey) && gBrowser.selectedTab.pinned ) { diff --git a/src/browser/base/content/browser-fullScreenAndPointerLock-js.patch b/src/browser/base/content/browser-fullScreenAndPointerLock-js.patch index 60692a527..3a25805c0 100644 --- a/src/browser/base/content/browser-fullScreenAndPointerLock-js.patch +++ b/src/browser/base/content/browser-fullScreenAndPointerLock-js.patch @@ -1,13 +1,13 @@ diff --git a/browser/base/content/browser-fullScreenAndPointerLock.js b/browser/base/content/browser-fullScreenAndPointerLock.js -index 38b4267c737ba247068e1001c12f1cb93cd248f7..f0d7002246645de48dd3ed24a84122d0bcc95b5a 100644 +index cd46701e9f4b672f392533c9ecc1c8570c3b2c5a..beb992702ea90e0b81f7bda4a6c0522932e84dfe 100644 --- a/browser/base/content/browser-fullScreenAndPointerLock.js +++ b/browser/base/content/browser-fullScreenAndPointerLock.js -@@ -461,8 +461,6 @@ var FullScreen = { - gNavToolbox.classList.toggle("fullscreen-with-menubar", shiftSize > 0); - - let transform = shiftSize > 0 ? `translateY(${shiftSize}px)` : ""; -- gNavToolbox.style.transform = transform; -- gURLBar.style.transform = gURLBar.hasAttribute("breakout") ? transform : ""; +@@ -464,8 +464,6 @@ var FullScreen = { + // don't need that kind of precision in our CSS. + shiftSize = shiftSize.toFixed(2); + let translate = shiftSize > 0 ? `0 ${shiftSize}px` : ""; +- document.body.style.translate = translate; +- gURLBar.style.translate = gURLBar.hasAttribute("breakout") ? translate : ""; let searchbar = document.getElementById("searchbar-new"); if (searchbar) { - searchbar.style.transform = searchbar.hasAttribute("breakout") + searchbar.style.translate = searchbar.hasAttribute("breakout") diff --git a/src/browser/base/content/browser-gestureSupport-js.patch b/src/browser/base/content/browser-gestureSupport-js.patch index ef205d5df..7132d6fdd 100644 --- a/src/browser/base/content/browser-gestureSupport-js.patch +++ b/src/browser/base/content/browser-gestureSupport-js.patch @@ -1,8 +1,19 @@ diff --git a/browser/base/content/browser-gestureSupport.js b/browser/base/content/browser-gestureSupport.js -index a28d54bf72c0e6495b9586f220d1859aac794936..66154668b9f85ffbaacea1e8351370659260227b 100644 +index a28d54bf72c0e6495b9586f220d1859aac794936..411d7255a68c48643617d77cc279a0a831fdf5c9 100644 --- a/browser/base/content/browser-gestureSupport.js +++ b/browser/base/content/browser-gestureSupport.js -@@ -832,7 +832,7 @@ var gHistorySwipeAnimation = { +@@ -247,6 +247,10 @@ var gGestureSupport = { + : aEvent.DIRECTION_LEFT; + } + ++ if (!gHistorySwipeAnimation._isSupported()) { ++ return; ++ } ++ + return canGoBack || canGoForward; + }, + +@@ -832,7 +836,7 @@ var gHistorySwipeAnimation = { * @return true if there is a previous page in history, false otherwise. */ canGoBack: function HSA_canGoBack() { diff --git a/src/browser/base/content/browser-init-js.patch b/src/browser/base/content/browser-init-js.patch index e5b1bb945..bb9a738a2 100644 --- a/src/browser/base/content/browser-init-js.patch +++ b/src/browser/base/content/browser-init-js.patch @@ -1,8 +1,8 @@ diff --git a/browser/base/content/browser-init.js b/browser/base/content/browser-init.js -index dba83914d1131616ead67f48949bda74105b7d46..86294fe79cd30b2a24b20738c9cd1f5114e8e669 100644 +index b64d2f52ed1eb3d0b8aa86700bc082d3fc95152b..dd8636148d0646f6949d6279d70e20c09d8c7c17 100644 --- a/browser/base/content/browser-init.js +++ b/browser/base/content/browser-init.js -@@ -247,6 +247,7 @@ var gBrowserInit = { +@@ -252,6 +252,7 @@ var gBrowserInit = { }, onLoad() { @@ -10,7 +10,7 @@ index dba83914d1131616ead67f48949bda74105b7d46..86294fe79cd30b2a24b20738c9cd1f51 gBrowser.addEventListener("DOMUpdateBlockedPopups", e => PopupAndRedirectBlockerObserver.handleEvent(e) ); -@@ -444,6 +445,7 @@ var gBrowserInit = { +@@ -446,6 +447,7 @@ var gBrowserInit = { this._handleURIToLoad(); diff --git a/src/browser/base/content/browser-js.patch b/src/browser/base/content/browser-js.patch index 15dfe2198..9cddb9c5c 100644 --- a/src/browser/base/content/browser-js.patch +++ b/src/browser/base/content/browser-js.patch @@ -1,5 +1,5 @@ diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js -index 0ea3d82b88819c41ffd866ae9533ebb5a7bff957..37db181d7e71fb6250df5bae363e9cf984b44f79 100644 +index 942bb5688e98f66ad71560fb32e1ca26a8081edf..25ba741c35c49afbf4e6053df3a0a7296d3040d8 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -33,6 +33,7 @@ ChromeUtils.defineESModuleGetters(this, { @@ -10,7 +10,7 @@ index 0ea3d82b88819c41ffd866ae9533ebb5a7bff957..37db181d7e71fb6250df5bae363e9cf9 DevToolsSocketStatus: "resource://devtools/shared/security/DevToolsSocketStatus.sys.mjs", DownloadUtils: "resource://gre/modules/DownloadUtils.sys.mjs", -@@ -814,7 +815,12 @@ function UpdateBackForwardCommands(aWebNavigation) { +@@ -816,7 +817,12 @@ function UpdateBackForwardCommands(aWebNavigation) { var backDisabled = backCommand.hasAttribute("disabled"); var forwardDisabled = forwardCommand.hasAttribute("disabled"); @@ -24,16 +24,7 @@ index 0ea3d82b88819c41ffd866ae9533ebb5a7bff957..37db181d7e71fb6250df5bae363e9cf9 if (backDisabled) { backCommand.removeAttribute("disabled"); } else { -@@ -2305,6 +2311,8 @@ var XULBrowserWindow = { - AboutReaderParent.updateReaderButton(gBrowser.selectedBrowser); - TranslationsParent.onLocationChange(gBrowser.selectedBrowser); - -+ gZenPinnedTabManager.onLocationChange(gBrowser.selectedBrowser, location); -+ - PictureInPicture.updateUrlbarToggle(gBrowser.selectedBrowser); - - if (!gMultiProcessBrowser) { -@@ -3820,7 +3828,7 @@ function warnAboutClosingWindow() { +@@ -3830,7 +3836,7 @@ function warnAboutClosingWindow() { if (!isPBWindow && !toolbar.visible) { return gBrowser.warnAboutClosingTabs( @@ -42,7 +33,7 @@ index 0ea3d82b88819c41ffd866ae9533ebb5a7bff957..37db181d7e71fb6250df5bae363e9cf9 gBrowser.closingTabsEnum.ALL ); } -@@ -3860,7 +3868,7 @@ function warnAboutClosingWindow() { +@@ -3870,7 +3876,7 @@ function warnAboutClosingWindow() { return ( isPBWindow || gBrowser.warnAboutClosingTabs( @@ -51,7 +42,7 @@ index 0ea3d82b88819c41ffd866ae9533ebb5a7bff957..37db181d7e71fb6250df5bae363e9cf9 gBrowser.closingTabsEnum.ALL ) ); -@@ -3885,7 +3893,7 @@ function warnAboutClosingWindow() { +@@ -3895,7 +3901,7 @@ function warnAboutClosingWindow() { AppConstants.platform != "macosx" || isPBWindow || gBrowser.warnAboutClosingTabs( @@ -60,7 +51,7 @@ index 0ea3d82b88819c41ffd866ae9533ebb5a7bff957..37db181d7e71fb6250df5bae363e9cf9 gBrowser.closingTabsEnum.ALL ) ); -@@ -4825,6 +4833,9 @@ var ConfirmationHint = { +@@ -4835,6 +4841,9 @@ var ConfirmationHint = { } document.l10n.setAttributes(this._message, messageId, options.l10nArgs); diff --git a/src/browser/base/content/browser-sets-js.patch b/src/browser/base/content/browser-sets-js.patch index 14f373418..b8e1e993c 100644 --- a/src/browser/base/content/browser-sets-js.patch +++ b/src/browser/base/content/browser-sets-js.patch @@ -1,5 +1,5 @@ diff --git a/browser/base/content/browser-sets.js b/browser/base/content/browser-sets.js -index 2918f0c46f404cba1658d4da823c6ca41cba8ded..2cb8e8c1c9dd1059bee45911159bf392f3a86ee9 100644 +index d9ab2ad00b862d56ea7aa8e2469d050d8df7f5dc..2efb25ff9cfd3a224724fffadfa0aee693ddf47d 100644 --- a/browser/base/content/browser-sets.js +++ b/browser/base/content/browser-sets.js @@ -272,7 +272,7 @@ document.addEventListener( diff --git a/src/browser/base/content/browser-xhtml.patch b/src/browser/base/content/browser-xhtml.patch index 364f2198f..0dfd8f916 100644 --- a/src/browser/base/content/browser-xhtml.patch +++ b/src/browser/base/content/browser-xhtml.patch @@ -1,5 +1,5 @@ diff --git a/browser/base/content/browser.xhtml b/browser/base/content/browser.xhtml -index a95ce8f0b28d98632c06f605b510b5ae3e98c4ed..275b3f5f30046a5e08d9ebceff0d2b91c865eb1b 100644 +index b42287485c2baeabe831b0d54c6f09330b0caabd..e404c2e028e24eb4fa9b97f143ba476c1b1461fd 100644 --- a/browser/base/content/browser.xhtml +++ b/browser/base/content/browser.xhtml @@ -19,6 +19,7 @@ @@ -10,7 +10,7 @@ index a95ce8f0b28d98632c06f605b510b5ae3e98c4ed..275b3f5f30046a5e08d9ebceff0d2b91 persist="screenX screenY width height sizemode" data-l10n-sync="true"> -@@ -100,8 +101,10 @@ +@@ -101,8 +102,10 @@ @@ -21,12 +21,12 @@ index a95ce8f0b28d98632c06f605b510b5ae3e98c4ed..275b3f5f30046a5e08d9ebceff0d2b91 # All sets except for popupsets (commands, keys, and stringbundles) -@@ -134,9 +137,11 @@ +@@ -135,9 +138,10 @@ +-#include navigator-toolbox.inc.xhtml + - #include navigator-toolbox.inc.xhtml #include browser-box.inc.xhtml + diff --git a/src/browser/base/content/main-popupset-inc-xhtml.patch b/src/browser/base/content/main-popupset-inc-xhtml.patch index 7068d13b2..219c94196 100644 --- a/src/browser/base/content/main-popupset-inc-xhtml.patch +++ b/src/browser/base/content/main-popupset-inc-xhtml.patch @@ -1,8 +1,8 @@ diff --git a/browser/base/content/main-popupset.inc.xhtml b/browser/base/content/main-popupset.inc.xhtml -index 0d73eb17b340001312a885ea10f5d6ad871f14d2..475824a0f9a4bbc15dc9bc7f1d2a09a51e58bc65 100644 +index 81b3d0a744b9f40358219696e0e52a8114c53e83..71eb93e50f19af1d8f44fb425970cc87416c84a3 100644 --- a/browser/base/content/main-popupset.inc.xhtml +++ b/browser/base/content/main-popupset.inc.xhtml -@@ -234,6 +234,10 @@ +@@ -235,6 +235,10 @@ hidden="true" tabspecific="true" aria-labelledby="editBookmarkPanelTitle"> @@ -13,7 +13,7 @@ index 0d73eb17b340001312a885ea10f5d6ad871f14d2..475824a0f9a4bbc15dc9bc7f1d2a09a5 -@@ -259,6 +263,7 @@ +@@ -260,6 +264,7 @@ class="footer-button"/> @@ -21,7 +21,7 @@ index 0d73eb17b340001312a885ea10f5d6ad871f14d2..475824a0f9a4bbc15dc9bc7f1d2a09a5 -@@ -678,6 +683,8 @@ +@@ -699,6 +704,8 @@ #include popup-notifications.inc.xhtml diff --git a/src/browser/base/content/navigator-toolbox-inc-xhtml.patch b/src/browser/base/content/navigator-toolbox-inc-xhtml.patch index d75f5a2c9..604d54485 100644 --- a/src/browser/base/content/navigator-toolbox-inc-xhtml.patch +++ b/src/browser/base/content/navigator-toolbox-inc-xhtml.patch @@ -1,17 +1,17 @@ diff --git a/browser/base/content/navigator-toolbox.inc.xhtml b/browser/base/content/navigator-toolbox.inc.xhtml -index 3f7b85086de11923f9453c8f02afb517c49afa8b..5be31a0cb3c5a52ba90cd2cd7f333a0354d9f2af 100644 +index edeb473e46b3aa4b12eb4b59ce62e5ae48edd2a1..99210f8bb5633d50d2cba24f1e13ca866c5b6959 100644 --- a/browser/base/content/navigator-toolbox.inc.xhtml +++ b/browser/base/content/navigator-toolbox.inc.xhtml @@ -2,7 +2,7 @@ # 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/. -- -+ +- ++ - -# Scripts used all over the browser - - - - - - -s - - - diff --git a/src/browser/base/content/zen-assets.jar.inc.mn b/src/browser/base/content/zen-assets.jar.inc.mn index a43972d9b..000f7d81e 100644 --- a/src/browser/base/content/zen-assets.jar.inc.mn +++ b/src/browser/base/content/zen-assets.jar.inc.mn @@ -18,5 +18,6 @@ #include ../../../zen/images/jar.inc.mn #include ../../../zen/vendor/jar.inc.mn #include ../../../zen/fonts/jar.inc.mn +#include ../../../zen/boosts/jar.inc.mn #include ../../../zen/live-folders/jar.inc.mn #include ../../../zen/library/jar.inc.mn diff --git a/src/browser/base/content/zen-commands.inc.xhtml b/src/browser/base/content/zen-commands.inc.xhtml index 1d3dac0df..7eb8c13d6 100644 --- a/src/browser/base/content/zen-commands.inc.xhtml +++ b/src/browser/base/content/zen-commands.inc.xhtml @@ -68,4 +68,6 @@ + + diff --git a/src/browser/base/content/zen-locales.inc.xhtml b/src/browser/base/content/zen-locales.inc.xhtml index 2040154fa..ed52e6fca 100644 --- a/src/browser/base/content/zen-locales.inc.xhtml +++ b/src/browser/base/content/zen-locales.inc.xhtml @@ -9,5 +9,6 @@ + diff --git a/src/browser/base/content/zen-panels/popups.inc b/src/browser/base/content/zen-panels/popups.inc index c79bf726b..c888a26c2 100644 --- a/src/browser/base/content/zen-panels/popups.inc +++ b/src/browser/base/content/zen-panels/popups.inc @@ -3,23 +3,6 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. - - - - - - - - diff --git a/src/browser/base/content/zen-panels/site-data.inc b/src/browser/base/content/zen-panels/site-data.inc index 4929a1645..aa95fb9f8 100644 --- a/src/browser/base/content/zen-panels/site-data.inc +++ b/src/browser/base/content/zen-panels/site-data.inc @@ -75,6 +75,14 @@ data-l10n-id="unified-extensions-manage-extensions" hidden="true" /> + + + + + + + @@ -74,18 +62,6 @@ index b006d5e5e64c418fbf930c010401e4ea3a3647a3..27886c04258e40f7c6ec444320a9a2a1 - - -- - - - @@ -266,6 +312,10 @@ #include sync.inc.xhtml #include experimental.inc.xhtml diff --git a/src/browser/components/preferences/sync-inc-xhtml.patch b/src/browser/components/preferences/sync-inc-xhtml.patch index 98a7f91e4..4dd2da0e0 100644 --- a/src/browser/components/preferences/sync-inc-xhtml.patch +++ b/src/browser/components/preferences/sync-inc-xhtml.patch @@ -1,5 +1,5 @@ diff --git a/browser/components/preferences/sync.inc.xhtml b/browser/components/preferences/sync.inc.xhtml -index d473643ccf12b1f1116acaed2d5541622982ee6e..2e3496c24533f4c663c9b07bf689d18c1ed11b95 100644 +index b889d42301de3c4f845105bf09d19b052dca9eae..015b751bd46f2499a93e328c903475ce2d60d079 100644 --- a/browser/components/preferences/sync.inc.xhtml +++ b/browser/components/preferences/sync.inc.xhtml @@ -223,6 +223,10 @@ diff --git a/src/browser/components/preferences/zen-settings.js b/src/browser/components/preferences/zen-settings.js index 4541bf90a..16ddcbc63 100644 --- a/src/browser/components/preferences/zen-settings.js +++ b/src/browser/components/preferences/zen-settings.js @@ -1067,7 +1067,7 @@ var gZenCKSSettings = { zenMissingKeyboardShortcutL10n[conflictShortcut.getID()] ?? conflictShortcut.getL10NID(); - const [group] = await document.l10n.formatValues([ + const [group, conflictName] = await document.l10n.formatValues([ { id: `${ZEN_CKS_GROUP_PREFIX}-${conflictShortcut.getGroup()}` }, { id: shortcutL10nKey }, ]); @@ -1082,7 +1082,7 @@ var gZenCKSSettings = { document.l10n.setAttributes(input.nextElementSibling, "zen-key-conflict", { group: group ?? "", - shortcut: shortcut ?? "", + shortcut: conflictName ?? shortcut ?? "", }); } } else { diff --git a/src/browser/components/screenshots/overlay/overlay-css.patch b/src/browser/components/screenshots/overlay/overlay-css.patch index 318dc4d53..cb366e076 100644 --- a/src/browser/components/screenshots/overlay/overlay-css.patch +++ b/src/browser/components/screenshots/overlay/overlay-css.patch @@ -1,5 +1,5 @@ diff --git a/browser/components/screenshots/overlay/overlay.css b/browser/components/screenshots/overlay/overlay.css -index 94d87522bfaaf59b9138ec5ca960be0a0ea31339..ab3fa41cefdfc1d93edeb8a2d626bd9b1e07344b 100644 +index a2cd691cfcfa7f54f275fcafbb3c6d1a5ef78f0d..bee7703d32404ce3b882e02aa4e60bdad6d43c7f 100644 --- a/browser/components/screenshots/overlay/overlay.css +++ b/browser/components/screenshots/overlay/overlay.css @@ -200,6 +200,9 @@ diff --git a/src/browser/components/sessionstore/SessionStore-sys-mjs.patch b/src/browser/components/sessionstore/SessionStore-sys-mjs.patch index a59ddb865..84ecce245 100644 --- a/src/browser/components/sessionstore/SessionStore-sys-mjs.patch +++ b/src/browser/components/sessionstore/SessionStore-sys-mjs.patch @@ -1,5 +1,5 @@ diff --git a/browser/components/sessionstore/SessionStore.sys.mjs b/browser/components/sessionstore/SessionStore.sys.mjs -index 3bebeb70e1a88aefb3ef4bc2114bd85b0d8d1e51..5414adeca242f00bff98c5e41de74f33dd2846bb 100644 +index 71921cec2b6aa0f103856c31254fd6c4affefccb..ed13d7e2aa5ff1199872fedbc26494aae463ace4 100644 --- a/browser/components/sessionstore/SessionStore.sys.mjs +++ b/browser/components/sessionstore/SessionStore.sys.mjs @@ -129,6 +129,9 @@ const TAB_EVENTS = [ @@ -12,7 +12,7 @@ index 3bebeb70e1a88aefb3ef4bc2114bd85b0d8d1e51..5414adeca242f00bff98c5e41de74f33 ]; const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; -@@ -200,6 +203,8 @@ ChromeUtils.defineESModuleGetters(lazy, { +@@ -199,6 +202,8 @@ ChromeUtils.defineESModuleGetters(lazy, { TabStateCache: "resource:///modules/sessionstore/TabStateCache.sys.mjs", TabStateFlusher: "resource:///modules/sessionstore/TabStateFlusher.sys.mjs", setTimeout: "resource://gre/modules/Timer.sys.mjs", @@ -21,7 +21,7 @@ index 3bebeb70e1a88aefb3ef4bc2114bd85b0d8d1e51..5414adeca242f00bff98c5e41de74f33 }); ChromeUtils.defineLazyGetter(lazy, "blankURI", () => { -@@ -1292,10 +1297,7 @@ var SessionStoreInternal = { +@@ -1291,10 +1296,7 @@ var SessionStoreInternal = { */ get willAutoRestore() { return ( @@ -33,7 +33,7 @@ index 3bebeb70e1a88aefb3ef4bc2114bd85b0d8d1e51..5414adeca242f00bff98c5e41de74f33 ); }, -@@ -1972,6 +1974,9 @@ var SessionStoreInternal = { +@@ -1970,6 +1972,9 @@ var SessionStoreInternal = { case "TabPinned": case "TabUnpinned": case "SwapDocShells": @@ -43,7 +43,7 @@ index 3bebeb70e1a88aefb3ef4bc2114bd85b0d8d1e51..5414adeca242f00bff98c5e41de74f33 this.saveStateDelayed(win); break; case "TabGroupCreate": -@@ -2085,6 +2090,10 @@ var SessionStoreInternal = { +@@ -2083,6 +2088,10 @@ var SessionStoreInternal = { this._windows[aWindow.__SSi].isTaskbarTab = true; } @@ -54,7 +54,7 @@ index 3bebeb70e1a88aefb3ef4bc2114bd85b0d8d1e51..5414adeca242f00bff98c5e41de74f33 if (lazy.AIWindow.isAIWindowActiveAndEnabled(aWindow)) { this._windows[aWindow.__SSi].isAIWindow = true; } -@@ -2121,7 +2130,7 @@ var SessionStoreInternal = { +@@ -2119,7 +2128,7 @@ var SessionStoreInternal = { let isTaskbarTab = this._windows[aWindow.__SSi].isTaskbarTab; // A regular window is not a private window, taskbar tab window, or popup window let isRegularWindow = @@ -63,7 +63,7 @@ index 3bebeb70e1a88aefb3ef4bc2114bd85b0d8d1e51..5414adeca242f00bff98c5e41de74f33 // perform additional initialization when the first window is loading if (lazy.RunState.isStopped) { -@@ -2133,7 +2142,7 @@ var SessionStoreInternal = { +@@ -2131,7 +2140,7 @@ var SessionStoreInternal = { // to disk to NOW() to enforce a full interval before the next write. lazy.SessionSaver.updateLastSaveTime(); @@ -72,7 +72,7 @@ index 3bebeb70e1a88aefb3ef4bc2114bd85b0d8d1e51..5414adeca242f00bff98c5e41de74f33 this._log.debug( "initializeWindow, the window is private or a web app. Saving SessionStartup.state for possibly restoring later" ); -@@ -2176,6 +2185,7 @@ var SessionStoreInternal = { +@@ -2173,6 +2182,7 @@ var SessionStoreInternal = { null, "sessionstore-one-or-no-tab-restored" ); @@ -80,7 +80,7 @@ index 3bebeb70e1a88aefb3ef4bc2114bd85b0d8d1e51..5414adeca242f00bff98c5e41de74f33 this._deferredAllWindowsRestored.resolve(); } // this window was opened by _openWindowWithState -@@ -2225,7 +2235,6 @@ var SessionStoreInternal = { +@@ -2222,7 +2232,6 @@ var SessionStoreInternal = { if (closedWindowState) { let newWindowState; if ( @@ -88,7 +88,7 @@ index 3bebeb70e1a88aefb3ef4bc2114bd85b0d8d1e51..5414adeca242f00bff98c5e41de74f33 !lazy.SessionStartup.willRestore() ) { // We want to split the window up into pinned tabs and unpinned tabs. -@@ -2261,6 +2270,7 @@ var SessionStoreInternal = { +@@ -2258,6 +2267,7 @@ var SessionStoreInternal = { } if (newWindowState) { @@ -96,7 +96,7 @@ index 3bebeb70e1a88aefb3ef4bc2114bd85b0d8d1e51..5414adeca242f00bff98c5e41de74f33 // Ensure that the window state isn't hidden this._restoreCount = 1; let state = { windows: [newWindowState] }; -@@ -2289,6 +2299,15 @@ var SessionStoreInternal = { +@@ -2286,6 +2296,15 @@ var SessionStoreInternal = { }); this._shouldRestoreLastSession = false; } @@ -112,7 +112,7 @@ index 3bebeb70e1a88aefb3ef4bc2114bd85b0d8d1e51..5414adeca242f00bff98c5e41de74f33 if (this._restoreLastWindow && aWindow.toolbar.visible) { // always reset (if not a popup window) -@@ -2439,7 +2458,7 @@ var SessionStoreInternal = { +@@ -2436,7 +2455,7 @@ var SessionStoreInternal = { var tabbrowser = aWindow.gBrowser; @@ -121,7 +121,7 @@ index 3bebeb70e1a88aefb3ef4bc2114bd85b0d8d1e51..5414adeca242f00bff98c5e41de74f33 TAB_EVENTS.forEach(function (aEvent) { tabbrowser.tabContainer.removeEventListener(aEvent, this, true); -@@ -2490,7 +2509,7 @@ var SessionStoreInternal = { +@@ -2487,7 +2506,7 @@ var SessionStoreInternal = { let isLastRegularWindow = Object.values(this._windows).filter( @@ -130,7 +130,7 @@ index 3bebeb70e1a88aefb3ef4bc2114bd85b0d8d1e51..5414adeca242f00bff98c5e41de74f33 ).length == 1; this._log.debug( `onClose, closing window isLastRegularWindow? ${isLastRegularWindow}` -@@ -2547,8 +2566,8 @@ var SessionStoreInternal = { +@@ -2544,8 +2563,8 @@ var SessionStoreInternal = { // 2) Flush the window. // 3) When the flush is complete, revisit our decision to store the window // in _closedWindows, and add/remove as necessary. @@ -141,7 +141,7 @@ index 3bebeb70e1a88aefb3ef4bc2114bd85b0d8d1e51..5414adeca242f00bff98c5e41de74f33 } completionPromise = lazy.TabStateFlusher.flushWindow(aWindow).then(() => { -@@ -2568,8 +2587,9 @@ var SessionStoreInternal = { +@@ -2565,8 +2584,9 @@ var SessionStoreInternal = { // Save non-private windows if they have at // least one saveable tab or are the last window. @@ -153,7 +153,7 @@ index 3bebeb70e1a88aefb3ef4bc2114bd85b0d8d1e51..5414adeca242f00bff98c5e41de74f33 if (!isLastWindow && winData.closedId > -1) { this._addClosedAction( -@@ -2645,7 +2665,7 @@ var SessionStoreInternal = { +@@ -2642,7 +2662,7 @@ var SessionStoreInternal = { * to call this method again asynchronously (for example, after * a window flush). */ @@ -162,7 +162,7 @@ index 3bebeb70e1a88aefb3ef4bc2114bd85b0d8d1e51..5414adeca242f00bff98c5e41de74f33 // Make sure SessionStore is still running, and make sure that we // haven't chosen to forget this window. if ( -@@ -2662,6 +2682,7 @@ var SessionStoreInternal = { +@@ -2659,6 +2679,7 @@ var SessionStoreInternal = { // _closedWindows from a previous call to this function. let winIndex = this._closedWindows.indexOf(winData); let alreadyStored = winIndex != -1; @@ -170,7 +170,7 @@ index 3bebeb70e1a88aefb3ef4bc2114bd85b0d8d1e51..5414adeca242f00bff98c5e41de74f33 // If sidebar command is truthy, i.e. sidebar is open, store sidebar settings let shouldStore = hasSaveableTabs || isLastWindow; -@@ -3483,7 +3504,7 @@ var SessionStoreInternal = { +@@ -3480,7 +3501,7 @@ var SessionStoreInternal = { if (!isPrivateWindow && tabState.isPrivate) { return; } @@ -179,7 +179,7 @@ index 3bebeb70e1a88aefb3ef4bc2114bd85b0d8d1e51..5414adeca242f00bff98c5e41de74f33 return; } -@@ -4223,6 +4244,12 @@ var SessionStoreInternal = { +@@ -4220,6 +4241,12 @@ var SessionStoreInternal = { Math.min(tabState.index, tabState.entries.length) ); tabState.pinned = false; @@ -192,7 +192,7 @@ index 3bebeb70e1a88aefb3ef4bc2114bd85b0d8d1e51..5414adeca242f00bff98c5e41de74f33 if (inBackground === false) { aWindow.gBrowser.selectedTab = newTab; -@@ -4659,6 +4686,8 @@ var SessionStoreInternal = { +@@ -4656,6 +4683,8 @@ var SessionStoreInternal = { // Append the tab if we're opening into a different window, tabIndex: aSource == aTargetWindow ? pos : Infinity, pinned: state.pinned, @@ -296,7 +296,7 @@ index 3bebeb70e1a88aefb3ef4bc2114bd85b0d8d1e51..5414adeca242f00bff98c5e41de74f33 // Move the originally open tabs to the end. if (initialTabs) { -@@ -6638,6 +6679,25 @@ var SessionStoreInternal = { +@@ -6640,6 +6681,25 @@ var SessionStoreInternal = { // Most of tabData has been restored, now continue with restoring // attributes that may trigger external events. @@ -322,7 +322,7 @@ index 3bebeb70e1a88aefb3ef4bc2114bd85b0d8d1e51..5414adeca242f00bff98c5e41de74f33 if (tabData.pinned) { tabbrowser.pinTab(tab); -@@ -6797,6 +6857,9 @@ var SessionStoreInternal = { +@@ -6807,6 +6867,9 @@ var SessionStoreInternal = { aWindow.gURLBar.readOnly = false; } } @@ -332,7 +332,7 @@ index 3bebeb70e1a88aefb3ef4bc2114bd85b0d8d1e51..5414adeca242f00bff98c5e41de74f33 let promiseParts = Promise.withResolvers(); aWindow.setTimeout(() => { -@@ -7592,7 +7655,7 @@ var SessionStoreInternal = { +@@ -7602,7 +7665,7 @@ var SessionStoreInternal = { let groupsToSave = new Map(); for (let tIndex = 0; tIndex < window.tabs.length; ) { @@ -341,7 +341,7 @@ index 3bebeb70e1a88aefb3ef4bc2114bd85b0d8d1e51..5414adeca242f00bff98c5e41de74f33 // Adjust window.selected if (tIndex + 1 < window.selected) { window.selected -= 1; -@@ -7607,7 +7670,7 @@ var SessionStoreInternal = { +@@ -7617,7 +7680,7 @@ var SessionStoreInternal = { ); // We don't want to increment tIndex here. continue; @@ -350,3 +350,19 @@ index 3bebeb70e1a88aefb3ef4bc2114bd85b0d8d1e51..5414adeca242f00bff98c5e41de74f33 // Convert any open groups into saved groups. let groupStateToSave = window.groups.find( groupState => groupState.id == window.tabs[tIndex].groupId +@@ -8076,7 +8139,6 @@ var SessionStoreInternal = { + timer.initWithCallback( + function () { + if (beats <= 0) { +- this._log.debug(`looseTimer of ${delay} timed out`); + Glean.sessionRestore.shutdownFlushAllOutcomes.timed_out.add(1); + deferred.resolve(); + } +@@ -8552,6 +8614,7 @@ var SessionStoreInternal = { + if ( + !savedTabGroupState.tabs.length || + this.getSavedTabGroup(savedTabGroupState.id) ++ && false + ) { + return; + } diff --git a/src/browser/components/tabbrowser/content/drag-and-drop-js.patch b/src/browser/components/tabbrowser/content/drag-and-drop-js.patch index 2a9d213f2..8104bc45d 100644 --- a/src/browser/components/tabbrowser/content/drag-and-drop-js.patch +++ b/src/browser/components/tabbrowser/content/drag-and-drop-js.patch @@ -1,5 +1,5 @@ diff --git a/browser/components/tabbrowser/content/drag-and-drop.js b/browser/components/tabbrowser/content/drag-and-drop.js -index ced2bfd88de2d16e2c028ca3f4d9d27516363575..69b45c7dad9d294e4290de4ce878d184925e5610 100644 +index b93a8fcc8b76ed888524959cbe7d6865fe830f2f..9f39316e33daa8a272e48c84d9e07ad9df197586 100644 --- a/browser/components/tabbrowser/content/drag-and-drop.js +++ b/browser/components/tabbrowser/content/drag-and-drop.js @@ -35,6 +35,9 @@ @@ -47,7 +47,7 @@ index ced2bfd88de2d16e2c028ca3f4d9d27516363575..69b45c7dad9d294e4290de4ce878d184 event.stopPropagation(); + if (draggedTab && dropEffect == "move") { + this.handle_drop_transition?.(draggedTab._dragData.dropElement, draggedTab, movingTabs, draggedTab._dragData.dropBefore); -+ gZenPinnedTabManager.moveToAnotherTabContainerIfNecessary(event, draggedTab, movingTabs, this._getDropIndex(event)); ++ [draggedTab, movingTabs] = gZenPinnedTabManager.moveToAnotherTabContainerIfNecessary(event, draggedTab, movingTabs, this._getDropIndex(event)); + } if (draggedTab && dropEffect == "copy") { let duplicatedDraggedTab; @@ -185,7 +185,7 @@ index ced2bfd88de2d16e2c028ca3f4d9d27516363575..69b45c7dad9d294e4290de4ce878d184 return target; } -@@ -996,7 +1018,8 @@ +@@ -1002,7 +1024,8 @@ isTabGroupLabel(draggedTab) && draggedTab._dragData?.expandGroupOnDrop ) { @@ -195,7 +195,7 @@ index ced2bfd88de2d16e2c028ca3f4d9d27516363575..69b45c7dad9d294e4290de4ce878d184 } } -@@ -1180,7 +1203,6 @@ +@@ -1186,7 +1209,6 @@ // using updateDragImage. On Linux, we can use a panel. if (platform == "win" || platform == "macosx") { captureListener = function () { @@ -203,7 +203,7 @@ index ced2bfd88de2d16e2c028ca3f4d9d27516363575..69b45c7dad9d294e4290de4ce878d184 }; } else { // Create a panel to use it in setDragImage -@@ -1218,7 +1240,6 @@ +@@ -1224,7 +1246,6 @@ ); dragImageOffset = dragImageOffset * scale; } @@ -211,7 +211,7 @@ index ced2bfd88de2d16e2c028ca3f4d9d27516363575..69b45c7dad9d294e4290de4ce878d184 // _dragData.offsetX/Y give the coordinates that the mouse should be // positioned relative to the corner of the new window created upon -@@ -1237,7 +1258,7 @@ +@@ -1243,7 +1264,7 @@ let dropEffect = this.getDropEffectForTabDrag(event); let isMovingInTabStrip = !fromTabList && dropEffect == "move"; let collapseTabGroupDuringDrag = @@ -220,7 +220,7 @@ index ced2bfd88de2d16e2c028ca3f4d9d27516363575..69b45c7dad9d294e4290de4ce878d184 tab._dragData = { offsetX: this._tabbrowserTabs.verticalMode -@@ -1247,7 +1268,7 @@ +@@ -1253,7 +1274,7 @@ ? event.screenY - window.screenY - tabOffset : event.screenY - window.screenY, scrollPos: @@ -229,7 +229,7 @@ index ced2bfd88de2d16e2c028ca3f4d9d27516363575..69b45c7dad9d294e4290de4ce878d184 ? this._tabbrowserTabs.pinnedTabsContainer.scrollPosition : this._tabbrowserTabs.arrowScrollbox.scrollPosition, screenX: event.screenX, -@@ -1294,6 +1315,7 @@ +@@ -1300,6 +1321,7 @@ if (tabStripItemElement.hasAttribute("dragtarget")) { return; } @@ -237,7 +237,7 @@ index ced2bfd88de2d16e2c028ca3f4d9d27516363575..69b45c7dad9d294e4290de4ce878d184 let isPinned = tab.pinned; let dragAndDropElements = this._tabbrowserTabs.dragAndDropElements; let isGrid = this._tabbrowserTabs.isContainerVerticalPinnedGrid(tab); -@@ -1660,23 +1682,6 @@ +@@ -1666,23 +1688,6 @@ // Slide the relevant tabs to their new position. // non-moving tabs adjust for RTL @@ -261,7 +261,7 @@ index ced2bfd88de2d16e2c028ca3f4d9d27516363575..69b45c7dad9d294e4290de4ce878d184 // moving tabs don't adjust for RTL for (let item of selectedElements) { if ( -@@ -1725,7 +1730,6 @@ +@@ -1731,7 +1736,6 @@ for (let item of this._tabbrowserTabs.dragAndDropElements) { delete item._moveTogetherSelectedTabsData; item = elementToMove(item); @@ -269,7 +269,7 @@ index ced2bfd88de2d16e2c028ca3f4d9d27516363575..69b45c7dad9d294e4290de4ce878d184 item.removeAttribute("multiselected-move-together"); } } -@@ -2569,7 +2573,6 @@ +@@ -2578,7 +2582,6 @@ tab.style.top = ""; tab.style.maxWidth = ""; tab.style.pointerEvents = ""; @@ -277,7 +277,7 @@ index ced2bfd88de2d16e2c028ca3f4d9d27516363575..69b45c7dad9d294e4290de4ce878d184 tab.removeAttribute("small-stack"); tab.removeAttribute("big-stack"); } -@@ -2578,11 +2581,9 @@ +@@ -2587,11 +2590,9 @@ )) { label.style.width = ""; label.style.maxWidth = ""; diff --git a/src/browser/components/tabbrowser/content/tab-js.patch b/src/browser/components/tabbrowser/content/tab-js.patch index 8bd69d35d..262f69623 100644 --- a/src/browser/components/tabbrowser/content/tab-js.patch +++ b/src/browser/components/tabbrowser/content/tab-js.patch @@ -1,5 +1,5 @@ diff --git a/browser/components/tabbrowser/content/tab.js b/browser/components/tabbrowser/content/tab.js -index e4266a159a0d5c42cc294602d00b8f66131f35d5..88c321f05dabd948d06e155f6e0474669fc15c20 100644 +index 2e02bad1a7c89b4c3b5aee1e14c13bb953a64eb6..fb9ec4deb5871bc0ba57c323a413f07440e9aa42 100644 --- a/browser/components/tabbrowser/content/tab.js +++ b/browser/components/tabbrowser/content/tab.js @@ -21,6 +21,7 @@ @@ -52,7 +52,7 @@ index e4266a159a0d5c42cc294602d00b8f66131f35d5..88c321f05dabd948d06e155f6e047466 return; } -@@ -225,11 +228,23 @@ +@@ -225,11 +228,25 @@ } get visible() { @@ -74,14 +74,16 @@ index e4266a159a0d5c42cc294602d00b8f66131f35d5..88c321f05dabd948d06e155f6e047466 + } + currentParent = currentParent.group; + } -+ if (this.pinned && !this.hasAttribute("zen-essential") && gZenWorkspaces.activeWorkspaceElement?.hasCollapsedPinnedTabs) { ++ if (this.pinned && !this.hasAttribute("zen-essential") && ++ gZenWorkspaces.activeWorkspaceElement?.hasCollapsedPinnedTabs && ++ !gZenWorkspaces.activeWorkspaceElement.collapsiblePins.activeTabs?.includes(this)) { + return false; + } + return true; } get hidden() { -@@ -308,7 +323,7 @@ +@@ -308,7 +325,7 @@ return false; } @@ -90,7 +92,7 @@ index e4266a159a0d5c42cc294602d00b8f66131f35d5..88c321f05dabd948d06e155f6e047466 } get lastAccessed() { -@@ -393,7 +408,18 @@ +@@ -393,7 +410,18 @@ } get group() { @@ -110,7 +112,7 @@ index e4266a159a0d5c42cc294602d00b8f66131f35d5..88c321f05dabd948d06e155f6e047466 } get splitview() { -@@ -475,6 +501,10 @@ +@@ -475,6 +503,10 @@ } } @@ -121,7 +123,7 @@ index e4266a159a0d5c42cc294602d00b8f66131f35d5..88c321f05dabd948d06e155f6e047466 // If the previous target wasn't part of this tab then this is a mouseenter event. if (!this.contains(event.relatedTarget)) { this._mouseenter(); -@@ -504,6 +534,7 @@ +@@ -504,6 +536,7 @@ if (!this.contains(event.relatedTarget)) { this._mouseleave(); } @@ -129,7 +131,7 @@ index e4266a159a0d5c42cc294602d00b8f66131f35d5..88c321f05dabd948d06e155f6e047466 } on_dragstart(event) { -@@ -538,6 +569,8 @@ +@@ -538,6 +571,8 @@ this.style.MozUserFocus = "ignore"; } else if ( event.target.classList.contains("tab-close-button") || @@ -138,7 +140,7 @@ index e4266a159a0d5c42cc294602d00b8f66131f35d5..88c321f05dabd948d06e155f6e047466 event.target.classList.contains("tab-icon-overlay") || event.target.classList.contains("tab-audio-button") ) { -@@ -592,16 +625,21 @@ +@@ -594,6 +629,10 @@ this.style.MozUserFocus = ""; } @@ -149,6 +151,27 @@ index e4266a159a0d5c42cc294602d00b8f66131f35d5..88c321f05dabd948d06e155f6e047466 on_click(event) { if (event.button != 0) { return; +@@ -617,14 +656,31 @@ + trigger: "alt_click", + }); + } ++ if ( ++ !event.target.classList.contains("tab-close-button") && ++ !event.target.classList.contains("tab-icon-overlay") && ++ !event.target.classList.contains("tab-audio-button") && ++ Services.prefs.getBoolPref("zen.splitView.enable-tab-click-split", false) ++ ) { ++ if (!this.splitView) { ++ gZenViewSplitter.contextSplitTabs(this); ++ } else { ++ gBrowser.selectedTab = this; ++ gZenViewSplitter.removeTabFromGroup(this, undefined, { ++ forUnsplit: true, ++ changeTab: false ++ }); ++ } ++ } + return; } - if (event.getModifierState("Accel") || event.shiftKey) { @@ -161,7 +184,7 @@ index e4266a159a0d5c42cc294602d00b8f66131f35d5..88c321f05dabd948d06e155f6e047466 gBrowser.multiSelectedTabsCount > 0 && !event.target.classList.contains("tab-close-button") && !event.target.classList.contains("tab-icon-overlay") && -@@ -613,8 +651,9 @@ +@@ -636,8 +692,9 @@ } if ( @@ -173,7 +196,7 @@ index e4266a159a0d5c42cc294602d00b8f66131f35d5..88c321f05dabd948d06e155f6e047466 ) { if (this.activeMediaBlocked) { if (this.multiselected) { -@@ -632,7 +671,7 @@ +@@ -655,7 +712,7 @@ return; } @@ -182,7 +205,7 @@ index e4266a159a0d5c42cc294602d00b8f66131f35d5..88c321f05dabd948d06e155f6e047466 if (this.multiselected) { gBrowser.removeMultiSelectedTabs( lazy.TabMetrics.userTriggeredContext( -@@ -652,6 +691,14 @@ +@@ -675,6 +732,14 @@ // (see tabbrowser-tabs 'click' handler). gBrowser.tabContainer._blockDblClick = true; } @@ -197,7 +220,7 @@ index e4266a159a0d5c42cc294602d00b8f66131f35d5..88c321f05dabd948d06e155f6e047466 } on_dblclick(event) { -@@ -675,6 +722,8 @@ +@@ -698,6 +763,8 @@ animate: true, triggeringEvent: event, }); diff --git a/src/browser/components/tabbrowser/content/tabbrowser-js.patch b/src/browser/components/tabbrowser/content/tabbrowser-js.patch index f3d858c99..e03f24368 100644 --- a/src/browser/components/tabbrowser/content/tabbrowser-js.patch +++ b/src/browser/components/tabbrowser/content/tabbrowser-js.patch @@ -1,5 +1,5 @@ diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js -index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81a655e998 100644 +index 2643e1a2aa14ba5cb4a64a92e1c2dfa5f07d242f..3a21c40eb70621968b5fbfc4b9b6d8412310b7b7 100644 --- a/browser/components/tabbrowser/content/tabbrowser.js +++ b/browser/components/tabbrowser/content/tabbrowser.js @@ -413,6 +413,7 @@ @@ -99,7 +99,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 tab.linkedPanel = uniqueId; this._selectedTab = tab; this._selectedBrowser = browser; -@@ -949,13 +1007,18 @@ +@@ -1032,13 +1090,18 @@ } this.showTab(aTab); @@ -119,7 +119,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 aTab.setAttribute("pinned", "true"); this._updateTabBarForPinnedTabs(); -@@ -968,11 +1031,19 @@ +@@ -1051,11 +1114,19 @@ } this.#handleTabMove(aTab, () => { @@ -140,7 +140,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 }); aTab.style.marginInlineStart = ""; -@@ -1149,6 +1220,9 @@ +@@ -1232,6 +1303,9 @@ let LOCAL_PROTOCOLS = ["chrome:", "about:", "resource:", "data:"]; @@ -150,7 +150,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 if ( aIconURL && !LOCAL_PROTOCOLS.some(protocol => aIconURL.startsWith(protocol)) -@@ -1158,6 +1232,9 @@ +@@ -1241,6 +1315,9 @@ ); return; } @@ -160,7 +160,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 let browser = this.getBrowserForTab(aTab); browser.mIconURL = aIconURL; -@@ -1479,7 +1556,6 @@ +@@ -1563,7 +1640,6 @@ // Preview mode should not reset the owner if (!this._previewMode && !oldTab.selected) { @@ -168,7 +168,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 } let lastRelatedTab = this._lastRelatedTabMap.get(oldTab); -@@ -1570,6 +1646,7 @@ +@@ -1654,6 +1730,7 @@ if (!this._previewMode) { newTab.recordTimeFromUnloadToReload(); newTab.updateLastAccessed(); @@ -176,7 +176,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 oldTab.updateLastAccessed(); // if this is the foreground window, update the last-seen timestamps. if (this.ownerGlobal == BrowserWindowTracker.getTopWindow()) { -@@ -1784,6 +1861,9 @@ +@@ -1868,6 +1945,9 @@ } let activeEl = document.activeElement; @@ -186,7 +186,16 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 // If focus is on the old tab, move it to the new tab. if (activeEl == oldTab) { newTab.focus(); -@@ -2110,7 +2190,12 @@ +@@ -1906,7 +1986,7 @@ + // Focus the location bar if it was previously focused for that tab. + // In full screen mode, only bother making the location bar visible + // if the tab is a blank one. +- if (gURLBar.getBrowserState(newBrowser).urlbarFocused) { ++ if (gURLBar.getBrowserState(newBrowser).urlbarFocused && (!gZenVerticalTabsManager._hasSetSingleToolbar || isBlankPageURL(newBrowser.currentURI?.spec))) { + let selectURL = () => { + if (this._asyncTabSwitching) { + // Set _awaitingSetURI flag to suppress popup notification +@@ -2194,7 +2274,12 @@ return this._setTabLabel(aTab, aLabel); } @@ -200,7 +209,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 if (!aLabel || aLabel.includes("about:reader?")) { return false; } -@@ -2235,7 +2320,7 @@ +@@ -2319,7 +2404,7 @@ newIndex = this.selectedTab._tPos + 1; } @@ -209,7 +218,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 if (this.isTabGroupLabel(targetTab)) { throw new Error( "Replacing a tab group label with a tab is not supported" -@@ -2515,6 +2600,7 @@ +@@ -2599,6 +2684,7 @@ uriIsAboutBlank, userContextId, skipLoad, @@ -217,7 +226,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 } = {}) { let b = document.createXULElement("browser"); // Use the JSM global to create the permanentKey, so that if the -@@ -2588,8 +2674,7 @@ +@@ -2672,8 +2758,7 @@ // we use a different attribute name for this? b.setAttribute("name", name); } @@ -227,7 +236,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 b.setAttribute("transparent", "true"); } -@@ -2759,7 +2844,7 @@ +@@ -2843,7 +2928,7 @@ let panel = this.getPanel(browser); let uniqueId = this._generateUniquePanelID(); @@ -236,7 +245,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 aTab.linkedPanel = uniqueId; // Inject the into the DOM if necessary. -@@ -2819,8 +2904,8 @@ +@@ -2903,8 +2988,8 @@ // If we transitioned from one browser to two browsers, we need to set // hasSiblings=false on both the existing browser and the new browser. if (this.tabs.length == 2) { @@ -247,7 +256,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 } else { aTab.linkedBrowser.browsingContext.hasSiblings = this.tabs.length > 1; } -@@ -3005,7 +3090,6 @@ +@@ -3089,7 +3174,6 @@ this.selectedTab = this.addTrustedTab(BROWSER_NEW_TAB_URL, { tabIndex: tab._tPos + 1, userContextId: tab.userContextId, @@ -255,7 +264,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 focusUrlBar: true, }); resolve(this.selectedBrowser); -@@ -3115,6 +3199,9 @@ +@@ -3199,6 +3283,9 @@ schemelessInput, hasValidUserGestureActivation = false, textDirectiveUserActivation = false, @@ -265,7 +274,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 } = {} ) { // all callers of addTab that pass a params object need to pass -@@ -3125,10 +3212,17 @@ +@@ -3209,10 +3296,17 @@ ); } @@ -283,7 +292,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 // If we're opening a foreground tab, set the owner by default. ownerTab ??= inBackground ? null : this.selectedTab; -@@ -3136,6 +3230,7 @@ +@@ -3220,6 +3314,7 @@ if (this.selectedTab.owner) { this.selectedTab.owner = null; } @@ -291,7 +300,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 // Find the tab that opened this one, if any. This is used for // determining positioning, and inherited attributes such as the -@@ -3188,6 +3283,22 @@ +@@ -3272,6 +3367,22 @@ noInitialLabel, skipBackgroundNotify, }); @@ -314,7 +323,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 if (insertTab) { // Insert the tab into the tab container in the correct position. this.#insertTabAtIndex(t, { -@@ -3196,6 +3307,7 @@ +@@ -3280,6 +3391,7 @@ ownerTab, openerTab, pinned, @@ -322,7 +331,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 bulkOrderedOpen, tabGroup: tabGroup ?? openerTab?.group, }); -@@ -3214,6 +3326,7 @@ +@@ -3298,6 +3410,7 @@ openWindowInfo, skipLoad, triggeringRemoteType, @@ -330,7 +339,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 })); if (focusUrlBar) { -@@ -3338,6 +3451,12 @@ +@@ -3422,6 +3535,12 @@ } } @@ -343,7 +352,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 // Additionally send pinned tab events if (pinned) { this.#notifyPinnedStatus(t); -@@ -3656,6 +3775,7 @@ +@@ -3729,6 +3848,7 @@ isAdoptingGroup = false, isUserTriggered = false, telemetryUserCreateSource = "unknown", @@ -351,7 +360,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 } = {} ) { if ( -@@ -3666,9 +3786,6 @@ +@@ -3739,9 +3859,6 @@ !this.isSplitViewWrapper(tabOrSplitView) ) ) { @@ -361,7 +370,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 } if (!color) { -@@ -3689,9 +3806,14 @@ +@@ -3762,9 +3879,14 @@ label, isAdoptingGroup ); @@ -378,7 +387,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 ); group.addTabs(tabsAndSplitViews); -@@ -3812,7 +3934,7 @@ +@@ -3885,7 +4007,7 @@ } this.#handleTabMove(tab, () => @@ -387,7 +396,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 ); } -@@ -3896,6 +4018,7 @@ +@@ -3969,6 +4091,7 @@ color: group.color, insertBefore: newTabs[0], isAdoptingGroup: true, @@ -395,7 +404,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 }); } -@@ -4107,6 +4230,7 @@ +@@ -4179,6 +4302,7 @@ openWindowInfo, skipLoad, triggeringRemoteType, @@ -403,7 +412,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 } ) { // If we don't have a preferred remote type (or it is `NOT_REMOTE`), and -@@ -4176,6 +4300,7 @@ +@@ -4248,6 +4372,7 @@ openWindowInfo, name, skipLoad, @@ -411,7 +420,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 }); } -@@ -4389,9 +4514,9 @@ +@@ -4461,9 +4586,9 @@ } // Add a new tab if needed. @@ -423,7 +432,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 let url = "about:blank"; if (tabData.entries?.length) { -@@ -4428,8 +4553,10 @@ +@@ -4500,8 +4625,10 @@ insertTab: false, skipLoad: true, preferredRemoteType, @@ -435,7 +444,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 if (select) { tabToSelect = tab; } -@@ -4451,7 +4578,8 @@ +@@ -4523,7 +4650,8 @@ this.pinTab(tab); // Then ensure all the tab open/pinning information is sent. this._fireTabOpen(tab, {}); @@ -445,7 +454,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 let { groupId } = tabData; const tabGroup = tabGroupWorkingData.get(groupId); // if a tab refers to a tab group we don't know, skip any group -@@ -4471,7 +4599,10 @@ +@@ -4543,7 +4671,10 @@ tabGroup.stateData.id, tabGroup.stateData.color, tabGroup.stateData.collapsed, @@ -457,7 +466,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 ); tabsFragment.appendChild(tabGroup.node); } -@@ -4526,9 +4657,23 @@ +@@ -4598,9 +4729,21 @@ // to remove the old selected tab. if (tabToSelect) { let leftoverTab = this.selectedTab; @@ -474,14 +483,12 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 + } + } } -+ else { -+ gZenWorkspaces._tabToRemoveForEmpty = this.selectedTab; -+ } ++ delete window.__isNewZenWindow; + this._hasAlreadyInitializedZenSessionStore = true; if (tabs.length > 1 || !tabs[0].selected) { this._updateTabsAfterInsert(); -@@ -4719,11 +4864,14 @@ +@@ -4791,11 +4934,14 @@ if (ownerTab) { tab.owner = ownerTab; } @@ -497,7 +504,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 if ( !bulkOrderedOpen && ((openerTab && -@@ -4735,7 +4883,7 @@ +@@ -4807,7 +4953,7 @@ let lastRelatedTab = openerTab && this._lastRelatedTabMap.get(openerTab); let previousTab = lastRelatedTab || openerTab || this.selectedTab; @@ -506,7 +513,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 tabGroup = previousTab.group; } if ( -@@ -4751,7 +4899,7 @@ +@@ -4823,7 +4969,7 @@ previousTab.splitview ) + 1; } else if (previousTab.visible) { @@ -515,7 +522,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 } else if (previousTab == FirefoxViewHandler.tab) { elementIndex = 0; } -@@ -4779,14 +4927,14 @@ +@@ -4851,14 +4997,14 @@ } // Ensure index is within bounds. if (tab.pinned) { @@ -534,7 +541,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 if (pinned && !itemAfter?.pinned) { itemAfter = null; -@@ -4803,7 +4951,7 @@ +@@ -4875,7 +5021,7 @@ this.tabContainer._invalidateCachedTabs(); @@ -543,7 +550,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 if ( (this.isTab(itemAfter) && itemAfter.group == tabGroup) || this.isSplitViewWrapper(itemAfter) -@@ -4834,7 +4982,11 @@ +@@ -4906,7 +5052,11 @@ const tabContainer = pinned ? this.tabContainer.pinnedTabsContainer : this.tabContainer; @@ -555,7 +562,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 } if (tab.group?.collapsed) { -@@ -4849,6 +5001,7 @@ +@@ -4921,6 +5071,7 @@ if (pinned) { this._updateTabBarForPinnedTabs(); } @@ -563,7 +570,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 TabBarVisibility.update(); } -@@ -5397,6 +5550,7 @@ +@@ -5469,6 +5620,7 @@ telemetrySource, } = {} ) { @@ -571,7 +578,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 // When 'closeWindowWithLastTab' pref is enabled, closing all tabs // can be considered equivalent to closing the window. if ( -@@ -5486,6 +5640,7 @@ +@@ -5558,6 +5710,7 @@ if (lastToClose) { this.removeTab(lastToClose, aParams); } @@ -579,7 +586,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 } catch (e) { console.error(e); } -@@ -5531,6 +5686,13 @@ +@@ -5603,6 +5756,14 @@ return; } @@ -589,11 +596,12 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 + this.selectedTab = newTab; + } + } ++ animate &&= !aTab.group?.hasAttribute("split-view-group"); + let isVisibleTab = aTab.visible; // We have to sample the tab width now, since _beginRemoveTab might // end up modifying the DOM in such a way that aTab gets a new -@@ -5538,6 +5700,9 @@ +@@ -5610,6 +5771,9 @@ // state). let tabWidth = window.windowUtils.getBoundsWithoutFlushing(aTab).width; let isLastTab = this.#isLastTabInWindow(aTab); @@ -603,7 +611,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 if ( !this._beginRemoveTab(aTab, { closeWindowFastpath: true, -@@ -5549,13 +5714,14 @@ +@@ -5621,13 +5785,14 @@ telemetrySource, }) ) { @@ -619,7 +627,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 let lockTabSizing = !this.tabContainer.verticalMode && !aTab.pinned && -@@ -5586,7 +5752,13 @@ +@@ -5658,7 +5823,13 @@ // We're not animating, so we can cancel the animation stopwatch. Glean.browserTabclose.timeAnim.cancel(aTab._closeTimeAnimTimerId); aTab._closeTimeAnimTimerId = null; @@ -634,7 +642,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 return; } -@@ -5720,7 +5892,7 @@ +@@ -5792,7 +5963,7 @@ closeWindowWithLastTab != null ? closeWindowWithLastTab : !window.toolbar.visible || @@ -643,7 +651,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 if (closeWindow) { // We've already called beforeunload on all the relevant tabs if we get here, -@@ -5744,6 +5916,7 @@ +@@ -5816,6 +5987,7 @@ newTab = true; } @@ -651,7 +659,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 aTab._endRemoveArgs = [closeWindow, newTab]; // swapBrowsersAndCloseOther will take care of closing the window without animation. -@@ -5784,13 +5957,7 @@ +@@ -5856,13 +6028,7 @@ aTab._mouseleave(); if (newTab) { @@ -666,7 +674,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 } else { TabBarVisibility.update(); } -@@ -5923,6 +6090,7 @@ +@@ -5995,6 +6161,7 @@ this.tabs[i]._tPos = i; } @@ -674,7 +682,15 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 if (!this._windowIsClosing) { // update tab close buttons state this.tabContainer._updateCloseButtons(); -@@ -6153,6 +6321,7 @@ +@@ -6180,6 +6347,7 @@ + memory_after: await getTotalMemoryUsage(), + time_to_unload_in_ms: timeElapsed, + }); ++ return true; + } + + /** +@@ -6225,6 +6393,7 @@ } let excludeTabs = new Set(aExcludeTabs); @@ -682,7 +698,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 // If this tab has a successor, it should be selectable, since // hiding or closing a tab removes that tab as a successor. -@@ -6165,15 +6334,22 @@ +@@ -6237,15 +6406,22 @@ !excludeTabs.has(aTab.owner) && Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose") ) { @@ -707,7 +723,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 let tab = this.tabContainer.findNextTab(aTab, { direction: 1, filter: _tab => remainingTabs.includes(_tab), -@@ -6187,7 +6363,7 @@ +@@ -6259,7 +6435,7 @@ } if (tab) { @@ -716,7 +732,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 } // If no qualifying visible tab was found, see if there is a tab in -@@ -6208,7 +6384,7 @@ +@@ -6280,7 +6456,7 @@ }); } @@ -725,7 +741,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 } _blurTab(aTab) { -@@ -6219,7 +6395,7 @@ +@@ -6291,7 +6467,7 @@ * @returns {boolean} * False if swapping isn't permitted, true otherwise. */ @@ -734,7 +750,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 // Do not allow transfering a private tab to a non-private window // and vice versa. if ( -@@ -6273,6 +6449,7 @@ +@@ -6345,6 +6521,7 @@ // fire the beforeunload event in the process. Close the other // window if this was its last tab. if ( @@ -742,7 +758,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 !remoteBrowser._beginRemoveTab(aOtherTab, { adoptedByTab: aOurTab, closeWindowWithLastTab: true, -@@ -6284,7 +6461,7 @@ +@@ -6356,7 +6533,7 @@ // If this is the last tab of the window, hide the window // immediately without animation before the docshell swap, to avoid // about:blank being painted. @@ -751,7 +767,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 if (closeWindow) { let win = aOtherTab.ownerGlobal; win.windowUtils.suppressAnimation(true); -@@ -6412,11 +6589,13 @@ +@@ -6484,11 +6661,13 @@ } // Finish tearing down the tab that's going away. @@ -765,7 +781,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 this.setTabTitle(aOurTab); -@@ -6618,10 +6797,10 @@ +@@ -6690,10 +6869,10 @@ SessionStore.deleteCustomTabValue(aTab, "hiddenBy"); } @@ -778,7 +794,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 aTab.selected || aTab.closing || // Tabs that are sharing the screen, microphone or camera cannot be hidden. -@@ -6681,7 +6860,8 @@ +@@ -6753,7 +6932,8 @@ * @param {object} [aOptions={}] * Key-value pairs that will be serialized into the features string. */ @@ -788,7 +804,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 if (this.tabs.length == 1) { return null; } -@@ -6698,7 +6878,7 @@ +@@ -6770,7 +6950,7 @@ // tell a new window to take the "dropped" tab let args = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray); args.appendElement(aTab.splitview ?? aTab); @@ -797,7 +813,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 private: PrivateBrowsingUtils.isWindowPrivate(window), features: Object.entries(aOptions) .map(([key, value]) => `${key}=${value}`) -@@ -6706,6 +6886,8 @@ +@@ -6778,6 +6958,8 @@ openerWindow: window, args, }); @@ -806,7 +822,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 } /** -@@ -6818,7 +7000,7 @@ +@@ -6890,7 +7072,7 @@ * `true` if element is a `` */ isTabGroup(element) { @@ -815,7 +831,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 } /** -@@ -6903,8 +7085,8 @@ +@@ -6975,8 +7157,8 @@ } // Don't allow mixing pinned and unpinned tabs. @@ -826,7 +842,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 } else { tabIndex = Math.max(tabIndex, this.pinnedTabCount); } -@@ -6933,13 +7115,19 @@ +@@ -7005,13 +7187,19 @@ this.#handleTabMove( element, () => { @@ -848,7 +864,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 let useAfter = false; if (this.isTab(element)) { useAfter = neighbor && tabIndex > element._tPos; -@@ -7004,23 +7192,31 @@ +@@ -7076,23 +7264,31 @@ #moveTabNextTo(element, targetElement, moveBefore = false, metricsContext) { if (this.isTabGroupLabel(targetElement)) { targetElement = targetElement.group; @@ -886,7 +902,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 } else if (!element.pinned && targetElement && targetElement.pinned) { // If the caller asks to move an unpinned element next to a pinned // tab, move the unpinned element to be the first unpinned element -@@ -7033,12 +7229,35 @@ +@@ -7105,12 +7301,35 @@ // move the tab group right before the first unpinned tab. // 4. Moving a tab group and the first unpinned tab is grouped: // move the tab group right before the first unpinned tab's tab group. @@ -923,7 +939,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 // We want to include the splitview wrapper if it's the targetElement, but // not in the case where we want to reverse tabs within the same splitview. -@@ -7047,6 +7266,7 @@ +@@ -7119,6 +7338,7 @@ } let getContainer = () => @@ -931,7 +947,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 element.pinned ? this.tabContainer.pinnedTabsContainer : this.tabContainer; -@@ -7055,11 +7275,15 @@ +@@ -7127,11 +7347,15 @@ element, () => { if (moveBefore) { @@ -948,7 +964,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 } }, metricsContext -@@ -7133,10 +7357,10 @@ +@@ -7205,11 +7429,15 @@ * @param {TabMetricsContext} [metricsContext] */ moveTabToExistingGroup(aTab, aGroup, metricsContext) { @@ -957,11 +973,17 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 throw new Error("Can only move a tab into a tab group"); } - if (aTab.pinned) { +- return; + if (aTab.pinned != !!aGroup.pinned) { - return; ++ if (aGroup.pinned) { ++ this.pinTab(aTab); ++ } else { ++ this.unpinTab(aTab); ++ } } if (aTab.group && aTab.group.id === aGroup.id) { -@@ -7209,6 +7433,7 @@ + return; +@@ -7281,6 +7509,7 @@ let state = { tabIndex: tab._tPos, @@ -969,7 +991,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 }; if (tab.visible) { state.elementIndex = tab.elementIndex; -@@ -7240,7 +7465,7 @@ +@@ -7312,7 +7541,7 @@ let changedSplitView = previousTabState.splitViewId != currentTabState.splitViewId; @@ -978,7 +1000,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 tab.dispatchEvent( new CustomEvent("TabMove", { bubbles: true, -@@ -7281,6 +7506,10 @@ +@@ -7354,6 +7583,10 @@ moveActionCallback(); @@ -989,7 +1011,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 // Clear tabs cache after moving nodes because the order of tabs may have // changed. this.tabContainer._invalidateCachedTabs(); -@@ -7331,7 +7560,22 @@ +@@ -7404,7 +7637,22 @@ * @returns {object} * The new tab in the current window, null if the tab couldn't be adopted. */ @@ -1013,7 +1035,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 // Swap the dropped tab with a new one we create and then close // it in the other window (making it seem to have moved between // windows). We also ensure that the tab we create to swap into has -@@ -7374,6 +7618,8 @@ +@@ -7447,6 +7695,8 @@ } params.skipLoad = true; let newTab = this.addWebTab("about:blank", params); @@ -1022,7 +1044,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 aTab.container.tabDragAndDrop.finishAnimateTabMove(); -@@ -8076,7 +8322,7 @@ +@@ -8149,7 +8399,7 @@ // preventDefault(). It will still raise the window if appropriate. return; } @@ -1031,7 +1053,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 window.focus(); aEvent.preventDefault(); } -@@ -8093,7 +8339,6 @@ +@@ -8166,7 +8416,6 @@ on_TabGroupCollapse(aEvent) { aEvent.target.tabs.forEach(tab => { @@ -1039,7 +1061,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 }); } -@@ -8427,7 +8672,9 @@ +@@ -8500,7 +8749,9 @@ let filter = this._tabFilters.get(tab); if (filter) { @@ -1049,7 +1071,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 let listener = this._tabListeners.get(tab); if (listener) { -@@ -9233,6 +9480,7 @@ +@@ -9306,6 +9557,7 @@ aWebProgress.isTopLevel ) { this.mTab.setAttribute("busy", "true"); @@ -1057,7 +1079,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 gBrowser._tabAttrModified(this.mTab, ["busy"]); this.mTab._notselectedsinceload = !this.mTab.selected; } -@@ -9313,6 +9561,7 @@ +@@ -9386,6 +9638,7 @@ // known defaults. Note we use the original URL since about:newtab // redirects to a prerendered page. const shouldRemoveFavicon = @@ -1065,7 +1087,7 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 !this.mBrowser.mIconURL && !ignoreBlank && !(originalLocation.spec in FAVICON_DEFAULTS); -@@ -9487,13 +9736,6 @@ +@@ -9560,13 +9813,6 @@ this.mBrowser.originalURI = aRequest.originalURI; } @@ -1079,11 +1101,12 @@ index d88bc0e5570c8fd428a84fdf5af0f6bab1e2a636..d9491b680bf8839038dadc0c6ee52f81 } let userContextId = this.mBrowser.getAttribute("usercontextid") || 0; -@@ -10379,7 +10621,7 @@ var TabContextMenu = { +@@ -10450,7 +10696,8 @@ var TabContextMenu = { ); contextUnpinSelectedTabs.hidden = !this.contextTab.pinned || !this.multiselected; - ++ gZenViewSplitter.updateContextMenuItems(); + gZenPinnedTabManager.updatePinnedTabContextMenu(this.contextTab); // Build Ask Chat items TabContextMenu.GenAI.buildTabMenu( diff --git a/src/browser/components/tabbrowser/content/tabgroup-js.patch b/src/browser/components/tabbrowser/content/tabgroup-js.patch index 4517ac261..c34a94d09 100644 --- a/src/browser/components/tabbrowser/content/tabgroup-js.patch +++ b/src/browser/components/tabbrowser/content/tabgroup-js.patch @@ -1,5 +1,5 @@ diff --git a/browser/components/tabbrowser/content/tabgroup.js b/browser/components/tabbrowser/content/tabgroup.js -index 3ca119e8dc72fac652c98505211864483d98add2..026b524be51170882e788a701095b938d6923f3e 100644 +index 65c89aab0902cb33ad33fc523d3e0b954618778e..b42298457fdd2f8df6ff202c804122ffc999d409 100644 --- a/browser/components/tabbrowser/content/tabgroup.js +++ b/browser/components/tabbrowser/content/tabgroup.js @@ -14,11 +14,11 @@ @@ -101,7 +101,13 @@ index 3ca119e8dc72fac652c98505211864483d98add2..026b524be51170882e788a701095b938 resetDefaultGroupName = () => { this.#defaultGroupName = ""; -@@ -178,7 +201,9 @@ +@@ -175,10 +198,15 @@ + if (!this.#tabChangeObserver) { + this.#tabChangeObserver = new window.MutationObserver(mutations => { + if (!this.tabs.length) { ++ if (this.tagName === "zen-workspace-collapsible-pins") { ++ return; ++ } this.dispatchEvent( new CustomEvent("TabGroupRemoved", { bubbles: true }) ); @@ -111,7 +117,7 @@ index 3ca119e8dc72fac652c98505211864483d98add2..026b524be51170882e788a701095b938 Services.obs.notifyObservers( this, "browser-tabgroup-removed-from-dom" -@@ -223,7 +248,10 @@ +@@ -223,7 +251,10 @@ } }); } @@ -123,7 +129,7 @@ index 3ca119e8dc72fac652c98505211864483d98add2..026b524be51170882e788a701095b938 } get color() { -@@ -317,6 +345,9 @@ +@@ -317,6 +348,9 @@ } set collapsed(val) { @@ -133,7 +139,7 @@ index 3ca119e8dc72fac652c98505211864483d98add2..026b524be51170882e788a701095b938 if (!!val == this.collapsed) { return; } -@@ -403,7 +434,6 @@ +@@ -403,7 +437,6 @@ tabGroupName, }) .then(result => { @@ -141,7 +147,7 @@ index 3ca119e8dc72fac652c98505211864483d98add2..026b524be51170882e788a701095b938 }); } -@@ -478,13 +508,65 @@ +@@ -478,13 +511,65 @@ * @returns {MozTabbrowserTab[]} */ get tabs() { @@ -212,7 +218,7 @@ index 3ca119e8dc72fac652c98505211864483d98add2..026b524be51170882e788a701095b938 } /** -@@ -592,7 +674,6 @@ +@@ -592,7 +677,6 @@ ); } else { if (tabOrSplitView.pinned) { @@ -220,7 +226,7 @@ index 3ca119e8dc72fac652c98505211864483d98add2..026b524be51170882e788a701095b938 } let tabToMove = this.ownerGlobal === tabOrSplitView.ownerGlobal -@@ -661,7 +742,7 @@ +@@ -661,7 +745,7 @@ */ on_click(event) { let isToggleElement = @@ -229,7 +235,7 @@ index 3ca119e8dc72fac652c98505211864483d98add2..026b524be51170882e788a701095b938 event.target === this.#overflowCountLabel; if (isToggleElement && event.button === 0) { event.preventDefault(); -@@ -740,5 +821,6 @@ +@@ -740,5 +824,6 @@ } } diff --git a/src/browser/components/tabbrowser/content/tabs-js.patch b/src/browser/components/tabbrowser/content/tabs-js.patch index 4e985ecb0..c1222edd0 100644 --- a/src/browser/components/tabbrowser/content/tabs-js.patch +++ b/src/browser/components/tabbrowser/content/tabs-js.patch @@ -1,5 +1,5 @@ diff --git a/browser/components/tabbrowser/content/tabs.js b/browser/components/tabbrowser/content/tabs.js -index 17a56b0e5a7f1fcdf04904586572c7f719e57904..778a4e49fad39a160d1000733a9e27b0c258d78c 100644 +index a2db42fba8d1f1d9df1dc295adb64be3f5885d50..2dd43fe4898badeaf9bc669502ead2c56982d1aa 100644 --- a/browser/components/tabbrowser/content/tabs.js +++ b/browser/components/tabbrowser/content/tabs.js @@ -230,7 +230,7 @@ @@ -214,7 +214,7 @@ index 17a56b0e5a7f1fcdf04904586572c7f719e57904..778a4e49fad39a160d1000733a9e27b0 const newTab2 = this.newTabButton; const newTabVertical = document.getElementById( "vertical-tabs-newtab-button" -@@ -1382,8 +1413,10 @@ +@@ -1386,8 +1417,10 @@ */ _handleTabSelect(aInstant) { let selectedTab = this.selectedItem; @@ -225,7 +225,7 @@ index 17a56b0e5a7f1fcdf04904586572c7f719e57904..778a4e49fad39a160d1000733a9e27b0 selectedTab._notselectedsinceload = false; } -@@ -1392,7 +1425,7 @@ +@@ -1396,7 +1429,7 @@ * @param {boolean} [shouldScrollInstantly=false] */ #ensureTabIsVisible(tab, shouldScrollInstantly = false) { @@ -234,7 +234,7 @@ index 17a56b0e5a7f1fcdf04904586572c7f719e57904..778a4e49fad39a160d1000733a9e27b0 if (arrowScrollbox?.overflowing) { arrowScrollbox.ensureElementIsVisible(tab, shouldScrollInstantly); } -@@ -1519,7 +1552,7 @@ +@@ -1523,7 +1556,7 @@ } _notifyBackgroundTab(aTab) { diff --git a/src/browser/components/urlbar/UrlbarController-sys-mjs.patch b/src/browser/components/urlbar/UrlbarController-sys-mjs.patch index afcf34135..3dc552c02 100644 --- a/src/browser/components/urlbar/UrlbarController-sys-mjs.patch +++ b/src/browser/components/urlbar/UrlbarController-sys-mjs.patch @@ -1,8 +1,8 @@ diff --git a/browser/components/urlbar/UrlbarController.sys.mjs b/browser/components/urlbar/UrlbarController.sys.mjs -index 7751871815ac3aa4641a7752c21f625e03e34c82..418dbecd2739fb8a95ce44efb5d671ac97934f21 100644 +index da0a9c73118886d535d574810de8141cb96e54ba..104062e2c0142bd08a4498e7ca282fc46cda09be 100644 --- a/browser/components/urlbar/UrlbarController.sys.mjs +++ b/browser/components/urlbar/UrlbarController.sys.mjs -@@ -302,7 +302,6 @@ export class UrlbarController { +@@ -304,7 +304,6 @@ export class UrlbarController { const isMac = AppConstants.platform == "macosx"; // Handle readline/emacs-style navigation bindings on Mac. if ( @@ -10,7 +10,7 @@ index 7751871815ac3aa4641a7752c21f625e03e34c82..418dbecd2739fb8a95ce44efb5d671ac this.view.isOpen && event.ctrlKey && (event.key == "n" || event.key == "p") -@@ -451,6 +450,8 @@ export class UrlbarController { +@@ -455,6 +454,8 @@ export class UrlbarController { }); } event.preventDefault(); diff --git a/src/browser/components/urlbar/UrlbarPrefs-sys-mjs.patch b/src/browser/components/urlbar/UrlbarPrefs-sys-mjs.patch index 71d0e194c..9b41ab880 100644 --- a/src/browser/components/urlbar/UrlbarPrefs-sys-mjs.patch +++ b/src/browser/components/urlbar/UrlbarPrefs-sys-mjs.patch @@ -1,12 +1,21 @@ diff --git a/browser/components/urlbar/UrlbarPrefs.sys.mjs b/browser/components/urlbar/UrlbarPrefs.sys.mjs -index ec7b7eeee7999aba76286e84808ed09ffc6df463..12bfafdc7e34c5d6345579cd0aaf515a19d82b31 100644 +index 2d21248256c6c2bfb8dac958133c10e3251ef564..f788bd10ec2c08e4b27b77cd3bb0489fb04e8b7a 100644 --- a/browser/components/urlbar/UrlbarPrefs.sys.mjs +++ b/browser/components/urlbar/UrlbarPrefs.sys.mjs -@@ -760,6 +760,7 @@ function makeDefaultResultGroups({ showSearchSuggestionsFirst }) { +@@ -462,6 +462,7 @@ const PREF_URLBAR_DEFAULTS = /** @type {PreferenceDefinition[]} */ ([ + ["shortcuts.tabs", true], + ["shortcuts.history", true], + ["shortcuts.actions", true], ++ ["shortcuts.workspaces", true], + + // Boolean to determine if the providers defined in `exposureResults` + // should be displayed in search results. This can be set by a +@@ -799,6 +800,8 @@ function makeDefaultResultGroups({ showSearchSuggestionsFirst }) { */ let rootGroup = { children: [ + { children: [{ group: lazy.UrlbarUtils.RESULT_GROUP.ZEN_ACTION }] }, ++ { children: [{ group: lazy.UrlbarUtils.RESULT_GROUP.ZEN_WORKSPACE }] }, // heuristic { maxResultCount: 1, diff --git a/src/browser/components/urlbar/UrlbarProvidersManager-sys-mjs.patch b/src/browser/components/urlbar/UrlbarProvidersManager-sys-mjs.patch index dd171221a..92a550307 100644 --- a/src/browser/components/urlbar/UrlbarProvidersManager-sys-mjs.patch +++ b/src/browser/components/urlbar/UrlbarProvidersManager-sys-mjs.patch @@ -1,8 +1,8 @@ diff --git a/browser/components/urlbar/UrlbarProvidersManager.sys.mjs b/browser/components/urlbar/UrlbarProvidersManager.sys.mjs -index d9a0566c5ad2a9ae375a23769f856aecc6efd86c..f6e5004806d24e009f96de9482e24c88590939b1 100644 +index 08455d8d5da233639ccebc0e77c0810fb4f674c3..da8092b561c3dd8864e57f5a52a1a643db29ace1 100644 --- a/browser/components/urlbar/UrlbarProvidersManager.sys.mjs +++ b/browser/components/urlbar/UrlbarProvidersManager.sys.mjs -@@ -912,6 +912,7 @@ export class Query { +@@ -913,6 +913,7 @@ export class Query { if ( result.heuristic && this.context.searchMode && @@ -10,3 +10,26 @@ index d9a0566c5ad2a9ae375a23769f856aecc6efd86c..f6e5004806d24e009f96de9482e24c88 (!this.context.trimmedSearchString || (!this.context.searchMode.engineName && !result.autofill)) ) { +@@ -1043,6 +1044,7 @@ function updateSourcesIfEmpty(context) { + lazy.UrlbarTokenizer.TYPE.RESTRICT_TITLE, + lazy.UrlbarTokenizer.TYPE.RESTRICT_URL, + lazy.UrlbarTokenizer.TYPE.RESTRICT_ACTION, ++ lazy.UrlbarTokenizer.TYPE.RESTRICT_WORKSPACE, + ].includes(t.type) + ); + +@@ -1100,6 +1102,14 @@ function updateSourcesIfEmpty(context) { + acceptedSources.push(source); + } + break; ++ case lazy.UrlbarUtils.RESULT_SOURCE.WORKSPACES: ++ if ( ++ restrictTokenType === lazy.UrlbarTokenizer.TYPE.RESTRICT_WORKSPACE || ++ !restrictTokenType ++ ) { ++ acceptedSources.push(source); ++ } ++ break; + case lazy.UrlbarUtils.RESULT_SOURCE.OTHER_LOCAL: + case lazy.UrlbarUtils.RESULT_SOURCE.ADDON: + default: diff --git a/src/browser/components/urlbar/UrlbarTokenizer-sys-mjs.patch b/src/browser/components/urlbar/UrlbarTokenizer-sys-mjs.patch new file mode 100644 index 000000000..c195ba7d9 --- /dev/null +++ b/src/browser/components/urlbar/UrlbarTokenizer-sys-mjs.patch @@ -0,0 +1,28 @@ +diff --git a/browser/components/urlbar/UrlbarTokenizer.sys.mjs b/browser/components/urlbar/UrlbarTokenizer.sys.mjs +index d4af0ee5138a69139b94d898fb07e2345172f025..f750aae3f9f0a849ca009784510575b2b7119e6d 100644 +--- a/browser/components/urlbar/UrlbarTokenizer.sys.mjs ++++ b/browser/components/urlbar/UrlbarTokenizer.sys.mjs +@@ -66,6 +66,7 @@ export var UrlbarTokenizer = { + // `looksLikeOrigin()` returned `LOOKS_LIKE_ORIGIN.OTHER` for this token. It + // may or may not be an origin. + POSSIBLE_ORIGIN_BUT_SEARCH_ALLOWED: 12, ++ RESTRICT_WORKSPACE: 13, + }), + + // The special characters below can be typed into the urlbar to restrict +@@ -83,6 +84,7 @@ export var UrlbarTokenizer = { + TITLE: "#", + URL: "$", + ACTION: ">", ++ WORKSPACE: "`", + }), + + // The keys of characters in RESTRICT that will enter search mode. +@@ -97,6 +99,7 @@ export var UrlbarTokenizer = { + if (lazy.UrlbarPrefs.get("scotchBonnet.enableOverride")) { + keys.push(this.RESTRICT.ACTION); + } ++ keys.push(this.RESTRICT.WORKSPACE); + return new Set(keys); + }, + diff --git a/src/browser/components/urlbar/UrlbarUtils-sys-mjs.patch b/src/browser/components/urlbar/UrlbarUtils-sys-mjs.patch index 4a3ac4e4f..a4c5e9b40 100644 --- a/src/browser/components/urlbar/UrlbarUtils-sys-mjs.patch +++ b/src/browser/components/urlbar/UrlbarUtils-sys-mjs.patch @@ -1,29 +1,50 @@ diff --git a/browser/components/urlbar/UrlbarUtils.sys.mjs b/browser/components/urlbar/UrlbarUtils.sys.mjs -index 9e9f786b6fd3441000d9cdb0b582f817b73ad814..8f86652520a433c94e34fc0e35dc8aad4a86faa2 100644 +index 64afd613f454edd7786fcc1e2f307a582e4d5f51..92f91379f43785cf5417c96b178884c860db6bd3 100644 --- a/browser/components/urlbar/UrlbarUtils.sys.mjs +++ b/browser/components/urlbar/UrlbarUtils.sys.mjs -@@ -84,6 +84,7 @@ export var UrlbarUtils = { +@@ -85,6 +85,8 @@ export var UrlbarUtils = { RESTRICT_SEARCH_KEYWORD: "restrictSearchKeyword", SUGGESTED_INDEX: "suggestedIndex", TAIL_SUGGESTION: "tailSuggestion", + ZEN_ACTION: "zenAction", ++ ZEN_WORKSPACE: "zenWorkspace", }), // Defines provider types. -@@ -145,6 +146,7 @@ export var UrlbarUtils = { +@@ -146,6 +148,8 @@ export var UrlbarUtils = { OTHER_NETWORK: 6, ADDON: 7, ACTIONS: 8, + ZEN_ACTIONS: 9, ++ WORKSPACES: 10, }), // Per-result exposure telemetry. -@@ -585,6 +587,8 @@ export var UrlbarUtils = { +@@ -295,6 +299,14 @@ export var UrlbarUtils = { + telemetryLabel: "actions", + uiLabel: "urlbar-searchmode-actions", + }, ++ { ++ source: this.RESULT_SOURCE.WORKSPACES, ++ restrict: lazy.UrlbarTokenizer.RESTRICT.WORKSPACE, ++ icon: "chrome://browser/skin/zen-icons/selectable/layers.svg", ++ pref: "shortcuts.workspaces", ++ telemetryLabel: "workspaces", ++ uiLabel: "urlbar-search-mode-workspaces", ++ }, + ]); + }, + +@@ -587,6 +599,12 @@ export var UrlbarUtils = { return this.RESULT_GROUP.HEURISTIC_FALLBACK; case "UrlbarProviderHistoryUrlHeuristic": return this.RESULT_GROUP.HEURISTIC_HISTORY_URL; + case "ZenUrlbarProviderGlobalActions": -+ return this.RESULT_GROUP.ZEN_ACTION; ++ if (result.source == this.RESULT_SOURCE.WORKSPACES) { ++ return this.RESULT_GROUP.ZEN_WORKSPACE; ++ } else { ++ return this.RESULT_GROUP.ZEN_ACTION; ++ } case "UrlbarProviderOmnibox": return this.RESULT_GROUP.HEURISTIC_OMNIBOX; case "UrlbarProviderRestrictKeywordsAutofill": diff --git a/src/browser/components/urlbar/UrlbarView-sys-mjs.patch b/src/browser/components/urlbar/UrlbarView-sys-mjs.patch index d0f80b140..55f97628c 100644 --- a/src/browser/components/urlbar/UrlbarView-sys-mjs.patch +++ b/src/browser/components/urlbar/UrlbarView-sys-mjs.patch @@ -1,8 +1,8 @@ diff --git a/browser/components/urlbar/UrlbarView.sys.mjs b/browser/components/urlbar/UrlbarView.sys.mjs -index cba492223601a8aa4656d9dc60a09faeda9f2313..a40bd5d46912389ef7208c4c7b481027444400fd 100644 +index b665adb1a1ce8bbae8df4cbea6c3248c3e4fb431..1de7f9461b8ccbd4680b917e6dd5ba3c02f69a94 100644 --- a/browser/components/urlbar/UrlbarView.sys.mjs +++ b/browser/components/urlbar/UrlbarView.sys.mjs -@@ -618,7 +618,7 @@ export class UrlbarView { +@@ -640,7 +640,7 @@ export class UrlbarView { !this.input.value || this.input.getAttribute("pageproxystate") == "valid" ) { @@ -11,7 +11,18 @@ index cba492223601a8aa4656d9dc60a09faeda9f2313..a40bd5d46912389ef7208c4c7b481027 // Try to reuse the cached top-sites context. If it's not cached, then // there will be a gap of time between when the input is focused and // when the view opens that can be perceived as flicker. -@@ -2871,6 +2871,8 @@ export class UrlbarView { +@@ -777,10 +777,6 @@ export class UrlbarView { + } + + // If search mode isn't active, close the view. +- if (!this.input.searchMode) { +- this.close(); +- return; +- } + + // Search mode is active. If the one-offs should be shown, make sure they + // are enabled and show the view. +@@ -2988,6 +2984,8 @@ export class UrlbarView { if (row?.hasAttribute("row-selectable")) { row?.toggleAttribute("selected", true); } @@ -20,7 +31,7 @@ index cba492223601a8aa4656d9dc60a09faeda9f2313..a40bd5d46912389ef7208c4c7b481027 if (element != row) { row?.toggleAttribute("descendant-selected", true); } -@@ -3359,7 +3361,7 @@ export class UrlbarView { +@@ -3477,7 +3475,7 @@ export class UrlbarView { } #enableOrDisableRowWrap() { diff --git a/src/browser/components/urlbar/content/UrlbarInput-mjs.patch b/src/browser/components/urlbar/content/UrlbarInput-mjs.patch index 4bc827b9e..6498c19ec 100644 --- a/src/browser/components/urlbar/content/UrlbarInput-mjs.patch +++ b/src/browser/components/urlbar/content/UrlbarInput-mjs.patch @@ -1,8 +1,8 @@ diff --git a/browser/components/urlbar/content/UrlbarInput.mjs b/browser/components/urlbar/content/UrlbarInput.mjs -index 2e6e2be9d7e28c3f189131ec19a26d552d13af99..101b5a3a70c24f28a755f2ca6630a6bc6b01181b 100644 +index b23244f9d3278918b016bb3fcab19687bc2e292a..ade1f031bbb68202a37e6c9d3071a73f5c811a82 100644 --- a/browser/components/urlbar/content/UrlbarInput.mjs +++ b/browser/components/urlbar/content/UrlbarInput.mjs -@@ -68,6 +68,13 @@ const lazy = XPCOMUtils.declareLazy({ +@@ -90,6 +90,13 @@ const lazy = XPCOMUtils.declareLazy({ logger: () => lazy.UrlbarUtils.getLogger({ prefix: "Input" }), }); @@ -16,7 +16,7 @@ index 2e6e2be9d7e28c3f189131ec19a26d552d13af99..101b5a3a70c24f28a755f2ca6630a6bc const UNLIMITED_MAX_RESULTS = 99; let getBoundsWithoutFlushing = element => -@@ -649,7 +656,16 @@ export class UrlbarInput extends HTMLElement { +@@ -708,7 +715,16 @@ export class UrlbarInput extends HTMLElement { // See _on_select(). HTMLInputElement.select() dispatches a "select" // event but does not set the primary selection. this._suppressPrimaryAdjustment = true; @@ -33,7 +33,7 @@ index 2e6e2be9d7e28c3f189131ec19a26d552d13af99..101b5a3a70c24f28a755f2ca6630a6bc this._suppressPrimaryAdjustment = false; } -@@ -723,6 +739,10 @@ export class UrlbarInput extends HTMLElement { +@@ -782,6 +798,10 @@ export class UrlbarInput extends HTMLElement { hideSearchTerms = false, isSameDocument = false, } = {}) { @@ -44,7 +44,7 @@ index 2e6e2be9d7e28c3f189131ec19a26d552d13af99..101b5a3a70c24f28a755f2ca6630a6bc if (!this.#isAddressbar) { throw new Error( "Cannot set URI for UrlbarInput that is not an address bar" -@@ -1012,8 +1032,16 @@ export class UrlbarInput extends HTMLElement { +@@ -1071,8 +1091,16 @@ export class UrlbarInput extends HTMLElement { return; } } @@ -62,7 +62,7 @@ index 2e6e2be9d7e28c3f189131ec19a26d552d13af99..101b5a3a70c24f28a755f2ca6630a6bc } /** -@@ -1432,7 +1460,11 @@ export class UrlbarInput extends HTMLElement { +@@ -1496,7 +1524,11 @@ export class UrlbarInput extends HTMLElement { } if (!this.#providesSearchMode(result)) { @@ -75,7 +75,7 @@ index 2e6e2be9d7e28c3f189131ec19a26d552d13af99..101b5a3a70c24f28a755f2ca6630a6bc } if (isCanonized) { -@@ -2598,6 +2630,32 @@ export class UrlbarInput extends HTMLElement { +@@ -2696,6 +2728,42 @@ export class UrlbarInput extends HTMLElement { await this.#updateLayoutBreakoutDimensions(); } @@ -92,9 +92,19 @@ index 2e6e2be9d7e28c3f189131ec19a26d552d13af99..101b5a3a70c24f28a755f2ca6630a6bc + + get zenStrippedURI() { + let strippedURI = null; ++ let activeBrowser = this.window.gBrowser?.selectedBrowser; ++ let uriString = activeBrowser.userTypedValue || ++ (activeBrowser.currentURI ? activeBrowser.currentURI.spec : ""); ++ ++ let uri; ++ try { ++ uri = Services.io.newURI(uriString); ++ } catch (e) { ++ // Fallback if the provisional string isn't a valid URI yet ++ uri = activeBrowser.currentURI; ++ } + + // Error check occurs during isClipboardURIValid -+ let uri = this.window.gBrowser.currentURI; + try { + strippedURI = lazy.QueryStringStripper.stripForCopyOrShare(uri); + } catch (e) { @@ -108,7 +118,7 @@ index 2e6e2be9d7e28c3f189131ec19a26d552d13af99..101b5a3a70c24f28a755f2ca6630a6bc startLayoutExtend() { if (!this.#allowBreakout || this.hasAttribute("breakout-extend")) { // Do not expand if the Urlbar does not support being expanded or it is -@@ -2612,6 +2670,13 @@ export class UrlbarInput extends HTMLElement { +@@ -2710,6 +2778,13 @@ export class UrlbarInput extends HTMLElement { this.setAttribute("breakout-extend", "true"); @@ -122,7 +132,7 @@ index 2e6e2be9d7e28c3f189131ec19a26d552d13af99..101b5a3a70c24f28a755f2ca6630a6bc // Enable the animation only after the first extend call to ensure it // doesn't run when opening a new window. if (!this.hasAttribute("breakout-extend-animate")) { -@@ -2631,6 +2696,27 @@ export class UrlbarInput extends HTMLElement { +@@ -2729,6 +2804,27 @@ export class UrlbarInput extends HTMLElement { return; } @@ -150,7 +160,7 @@ index 2e6e2be9d7e28c3f189131ec19a26d552d13af99..101b5a3a70c24f28a755f2ca6630a6bc this.removeAttribute("breakout-extend"); this.#updateTextboxPosition(); } -@@ -2661,7 +2747,7 @@ export class UrlbarInput extends HTMLElement { +@@ -2759,7 +2855,7 @@ export class UrlbarInput extends HTMLElement { forceUnifiedSearchButtonAvailable = false ) { let prevState = this.getAttribute("pageproxystate"); @@ -159,7 +169,7 @@ index 2e6e2be9d7e28c3f189131ec19a26d552d13af99..101b5a3a70c24f28a755f2ca6630a6bc this.setAttribute("pageproxystate", state); this._inputContainer.setAttribute("pageproxystate", state); this._identityBox?.setAttribute("pageproxystate", state); -@@ -2915,10 +3001,12 @@ export class UrlbarInput extends HTMLElement { +@@ -3031,10 +3127,12 @@ export class UrlbarInput extends HTMLElement { return; } this.style.top = px( @@ -172,7 +182,7 @@ index 2e6e2be9d7e28c3f189131ec19a26d552d13af99..101b5a3a70c24f28a755f2ca6630a6bc ); } -@@ -2977,9 +3065,10 @@ export class UrlbarInput extends HTMLElement { +@@ -3093,9 +3191,10 @@ export class UrlbarInput extends HTMLElement { return; } @@ -184,7 +194,7 @@ index 2e6e2be9d7e28c3f189131ec19a26d552d13af99..101b5a3a70c24f28a755f2ca6630a6bc ); this.style.setProperty( "--urlbar-height", -@@ -3413,6 +3502,7 @@ export class UrlbarInput extends HTMLElement { +@@ -3597,6 +3696,7 @@ export class UrlbarInput extends HTMLElement { } _toggleActionOverride(event) { @@ -192,7 +202,7 @@ index 2e6e2be9d7e28c3f189131ec19a26d552d13af99..101b5a3a70c24f28a755f2ca6630a6bc if ( event.keyCode == KeyEvent.DOM_VK_SHIFT || event.keyCode == KeyEvent.DOM_VK_ALT || -@@ -3516,8 +3606,8 @@ export class UrlbarInput extends HTMLElement { +@@ -3709,8 +3809,8 @@ export class UrlbarInput extends HTMLElement { if (!this.#isAddressbar) { return val; } @@ -203,7 +213,7 @@ index 2e6e2be9d7e28c3f189131ec19a26d552d13af99..101b5a3a70c24f28a755f2ca6630a6bc : val; // Only trim value if the directionality doesn't change to RTL and we're not // showing a strikeout https protocol. -@@ -3824,6 +3914,7 @@ export class UrlbarInput extends HTMLElement { +@@ -4006,6 +4106,7 @@ export class UrlbarInput extends HTMLElement { resultDetails = null, browser = this.window.gBrowser.selectedBrowser ) { @@ -211,7 +221,7 @@ index 2e6e2be9d7e28c3f189131ec19a26d552d13af99..101b5a3a70c24f28a755f2ca6630a6bc if (this.#isAddressbar) { this.#prepareAddressbarLoad( url, -@@ -3935,6 +4026,10 @@ export class UrlbarInput extends HTMLElement { +@@ -4117,6 +4218,10 @@ export class UrlbarInput extends HTMLElement { } reuseEmpty = true; } @@ -222,7 +232,7 @@ index 2e6e2be9d7e28c3f189131ec19a26d552d13af99..101b5a3a70c24f28a755f2ca6630a6bc if ( where == "tab" && reuseEmpty && -@@ -3942,6 +4037,9 @@ export class UrlbarInput extends HTMLElement { +@@ -4124,6 +4229,9 @@ export class UrlbarInput extends HTMLElement { ) { where = "current"; } @@ -232,7 +242,7 @@ index 2e6e2be9d7e28c3f189131ec19a26d552d13af99..101b5a3a70c24f28a755f2ca6630a6bc return where; } -@@ -4196,6 +4294,7 @@ export class UrlbarInput extends HTMLElement { +@@ -4378,6 +4486,7 @@ export class UrlbarInput extends HTMLElement { this.setResultForCurrentValue(null); this.handleCommand(); this.controller.clearLastQueryContextCache(); @@ -240,7 +250,7 @@ index 2e6e2be9d7e28c3f189131ec19a26d552d13af99..101b5a3a70c24f28a755f2ca6630a6bc this._suppressStartQuery = false; }); -@@ -4203,7 +4302,6 @@ export class UrlbarInput extends HTMLElement { +@@ -4385,7 +4494,6 @@ export class UrlbarInput extends HTMLElement { contextMenu.addEventListener("popupshowing", () => { // Close the results pane when the input field contextual menu is open, // because paste and go doesn't want a result selection. @@ -248,7 +258,7 @@ index 2e6e2be9d7e28c3f189131ec19a26d552d13af99..101b5a3a70c24f28a755f2ca6630a6bc let controller = this.document.commandDispatcher.getControllerForCommand("cmd_paste"); -@@ -4319,7 +4417,11 @@ export class UrlbarInput extends HTMLElement { +@@ -4541,7 +4649,11 @@ export class UrlbarInput extends HTMLElement { if (!engineName && !source && !this.hasAttribute("searchmode")) { return; } @@ -261,7 +271,7 @@ index 2e6e2be9d7e28c3f189131ec19a26d552d13af99..101b5a3a70c24f28a755f2ca6630a6bc if (this._searchModeIndicatorTitle) { this._searchModeIndicatorTitle.textContent = ""; this._searchModeIndicatorTitle.removeAttribute("data-l10n-id"); -@@ -4629,6 +4731,7 @@ export class UrlbarInput extends HTMLElement { +@@ -4851,6 +4963,7 @@ export class UrlbarInput extends HTMLElement { this.document.l10n.setAttributes( this.inputField, @@ -269,7 +279,7 @@ index 2e6e2be9d7e28c3f189131ec19a26d552d13af99..101b5a3a70c24f28a755f2ca6630a6bc l10nId, l10nId == "urlbar-placeholder-with-name" ? { name: engineName } -@@ -4742,6 +4845,11 @@ export class UrlbarInput extends HTMLElement { +@@ -4964,6 +5077,11 @@ export class UrlbarInput extends HTMLElement { } _on_click(event) { @@ -281,7 +291,7 @@ index 2e6e2be9d7e28c3f189131ec19a26d552d13af99..101b5a3a70c24f28a755f2ca6630a6bc switch (event.target) { case this.inputField: case this._inputContainer: -@@ -4820,7 +4928,7 @@ export class UrlbarInput extends HTMLElement { +@@ -5042,7 +5160,7 @@ export class UrlbarInput extends HTMLElement { } } @@ -290,7 +300,7 @@ index 2e6e2be9d7e28c3f189131ec19a26d552d13af99..101b5a3a70c24f28a755f2ca6630a6bc this.view.autoOpen({ event }); } else { if (this._untrimOnFocusAfterKeydown) { -@@ -4860,9 +4968,16 @@ export class UrlbarInput extends HTMLElement { +@@ -5082,9 +5200,16 @@ export class UrlbarInput extends HTMLElement { } _on_mousedown(event) { @@ -308,7 +318,7 @@ index 2e6e2be9d7e28c3f189131ec19a26d552d13af99..101b5a3a70c24f28a755f2ca6630a6bc if ( event.composedTarget != this.inputField && event.composedTarget != this._inputContainer -@@ -4872,6 +4987,10 @@ export class UrlbarInput extends HTMLElement { +@@ -5094,6 +5219,10 @@ export class UrlbarInput extends HTMLElement { this.focusedViaMousedown = !this.focused; this._preventClickSelectsAll = this.focused; @@ -319,7 +329,7 @@ index 2e6e2be9d7e28c3f189131ec19a26d552d13af99..101b5a3a70c24f28a755f2ca6630a6bc // Keep the focus status, since the attribute may be changed // upon calling this.focus(). -@@ -4907,7 +5026,7 @@ export class UrlbarInput extends HTMLElement { +@@ -5129,7 +5258,7 @@ export class UrlbarInput extends HTMLElement { } // Don't close the view when clicking on a tab; we may want to keep the // view open on tab switch, and the TabSelect event arrived earlier. @@ -328,7 +338,7 @@ index 2e6e2be9d7e28c3f189131ec19a26d552d13af99..101b5a3a70c24f28a755f2ca6630a6bc break; } -@@ -5235,7 +5354,7 @@ export class UrlbarInput extends HTMLElement { +@@ -5411,7 +5540,7 @@ export class UrlbarInput extends HTMLElement { // When we are in actions search mode we can show more results so // increase the limit. let maxResults = diff --git a/src/browser/components/urlbar/content/enUS-searchFeatures-ftl.patch b/src/browser/components/urlbar/content/enUS-searchFeatures-ftl.patch new file mode 100644 index 000000000..d041e4155 --- /dev/null +++ b/src/browser/components/urlbar/content/enUS-searchFeatures-ftl.patch @@ -0,0 +1,14 @@ +diff --git a/browser/components/urlbar/content/enUS-searchFeatures.ftl b/browser/components/urlbar/content/enUS-searchFeatures.ftl +index a1fb86058d1f6f015160163f75999b4a429bf1fd..83098291e21a8239513a4105a4604394a1dfabc0 100644 +--- a/browser/components/urlbar/content/enUS-searchFeatures.ftl ++++ b/browser/components/urlbar/content/enUS-searchFeatures.ftl +@@ -2,6 +2,9 @@ + # 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/. + ++urlbar-search-mode-workspaces = Spaces ++urlbar-search-mode-workspaces-en = Spaces ++ + ### These strings are related to the Firefox Suggest feature. Firefox Suggest + ### shows recommended and sponsored third-party results in the address bar + ### panel. It also shows headings/labels above different groups of results. For diff --git a/src/browser/extensions/newtab/lib/ActivityStream-sys-mjs.patch b/src/browser/extensions/newtab/lib/ActivityStream-sys-mjs.patch index b6ddba914..aef8cc680 100644 --- a/src/browser/extensions/newtab/lib/ActivityStream-sys-mjs.patch +++ b/src/browser/extensions/newtab/lib/ActivityStream-sys-mjs.patch @@ -1,8 +1,8 @@ diff --git a/browser/extensions/newtab/lib/ActivityStream.sys.mjs b/browser/extensions/newtab/lib/ActivityStream.sys.mjs -index 8bb36697cca3761dc98d8be9d8ee954fdff3ce3d..bd40d251eeeff2b61fdd1d2170947d071b8f0872 100644 +index daf4e1dca839a6e882edf598bd82f13f1bf4eadd..ebb584e058c0a15f5a5fa0bd80bdce42636c9ba0 100644 --- a/browser/extensions/newtab/lib/ActivityStream.sys.mjs +++ b/browser/extensions/newtab/lib/ActivityStream.sys.mjs -@@ -289,7 +289,7 @@ export const PREFS_CONFIG = new Map([ +@@ -287,7 +287,7 @@ export const PREFS_CONFIG = new Map([ "showSponsoredTopSites", { title: "Show sponsored top sites", diff --git a/src/browser/installer/package-manifest-in.patch b/src/browser/installer/package-manifest-in.patch index 3aec7d9df..f07fcc3ac 100644 --- a/src/browser/installer/package-manifest-in.patch +++ b/src/browser/installer/package-manifest-in.patch @@ -1,18 +1,8 @@ diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in -index 36eafe35063f02fe3d4acaab24a08dc1b7b0ae24..951889b156498f66118d67d4245aca649e27a3a2 100644 +index 36eafe35063f02fe3d4acaab24a08dc1b7b0ae24..b6bf327bf286de5246e5c50d89519d16d5771caf 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in -@@ -378,17 +378,17 @@ bin/libfreebl_64int_3.so - ; [MaintenanceService] - ; - #ifdef MOZ_MAINTENANCE_SERVICE --@BINPATH@/maintenanceservice.exe --@BINPATH@/maintenanceservice_installer.exe -+;@BINPATH@/maintenanceservice.exe -+;@BINPATH@/maintenanceservice_installer.exe - #endif - - ; [Crash Reporter] +@@ -386,9 +386,9 @@ bin/libfreebl_64int_3.so ; #ifdef MOZ_CRASHREPORTER #ifdef XP_MACOSX diff --git a/src/browser/installer/windows/nsis/installer-nsi.patch b/src/browser/installer/windows/nsis/installer-nsi.patch index 8aebadb7c..48673127c 100644 --- a/src/browser/installer/windows/nsis/installer-nsi.patch +++ b/src/browser/installer/windows/nsis/installer-nsi.patch @@ -1,5 +1,5 @@ diff --git a/browser/installer/windows/nsis/installer.nsi b/browser/installer/windows/nsis/installer.nsi -index f752555d18b0c6c7325cc99e73a85819fc015f5c..2416d2a79ca358ab901103d9546bc3d0454333eb 100755 +index b3459f8233b2509744e27bfe33884c6a92164e36..76b71e0882381f8f6becaf88bc4e3cc56f8d8ad4 100755 --- a/browser/installer/windows/nsis/installer.nsi +++ b/browser/installer/windows/nsis/installer.nsi @@ -842,7 +842,7 @@ Section "-InstallEndCleanup" @@ -11,7 +11,7 @@ index f752555d18b0c6c7325cc99e73a85819fc015f5c..2416d2a79ca358ab901103d9546bc3d0 ${EndIf} SectionEnd -@@ -1984,5 +1984,5 @@ FunctionEnd +@@ -1978,5 +1978,5 @@ FunctionEnd Function .onGUIEnd ${OnEndCommon} diff --git a/src/browser/themes/linux/browser-css.patch b/src/browser/themes/linux/browser-css.patch index 014369b38..560034d03 100644 --- a/src/browser/themes/linux/browser-css.patch +++ b/src/browser/themes/linux/browser-css.patch @@ -1,5 +1,5 @@ diff --git a/browser/themes/linux/browser.css b/browser/themes/linux/browser.css -index dfb3e2c843d748e46c0f5878c9d06cebacffb603..4805a0bed7e60713218530c1b03f0f5dd65fe3af 100644 +index b82fa7229ad637a08563c06fa2c2abdf99998d7c..b5ee3aa9679244222a0e9eed4be54a41ec6a11f9 100644 --- a/browser/themes/linux/browser.css +++ b/browser/themes/linux/browser.css @@ -14,7 +14,6 @@ @@ -10,30 +10,12 @@ index dfb3e2c843d748e46c0f5878c9d06cebacffb603..4805a0bed7e60713218530c1b03f0f5d } } } -@@ -58,7 +57,8 @@ - -moz-default-appearance: -moz-window-decorations; +@@ -59,6 +58,8 @@ appearance: auto; -- #navigator-toolbox, + body, + #zen-main-app-wrapper, + #zen-browser-background, dialog::backdrop { border-top-left-radius: env(-moz-gtk-csd-titlebar-radius); border-top-right-radius: env(-moz-gtk-csd-titlebar-radius); -@@ -66,13 +66,11 @@ - - /* stylelint-disable-next-line media-query-no-invalid */ - @media -moz-pref("widget.gtk.rounded-bottom-corners.enabled") { -- #navigator-toolbox { -- /* The body clip below covers this. */ -- border-radius: 0; -- } - - body, -- dialog::backdrop { -+ dialog::backdrop, -+ #zen-main-app-wrapper, -+ #zen-browser-background { - /* Use an uniform clip to allow WebRender to optimize it better */ - border-radius: env(-moz-gtk-csd-titlebar-radius); - } diff --git a/src/browser/themes/shared/browser-shared-css.patch b/src/browser/themes/shared/browser-shared-css.patch index f0c494b61..c4790e30d 100644 --- a/src/browser/themes/shared/browser-shared-css.patch +++ b/src/browser/themes/shared/browser-shared-css.patch @@ -1,17 +1,8 @@ diff --git a/browser/themes/shared/browser-shared.css b/browser/themes/shared/browser-shared.css -index 4e7ca6259c92da2c45c6ea3157863b971fbd6201..15da124b3e10da664bc0ba3a63cd02b33e31337c 100644 +index d1b11a65e527cf7c13f42b3f980f2f09592c5e53..44fb86b570c69f7d7003d3949e330e08722fbbb8 100644 --- a/browser/themes/shared/browser-shared.css +++ b/browser/themes/shared/browser-shared.css -@@ -155,8 +155,6 @@ body { - */ - &.fullscreen-with-menubar { - z-index: var(--browser-area-z-index-toolbox-while-animating); -- box-shadow: var(--content-area-shadow); -- border-bottom-color: var(--chrome-content-separator-color); - } - - /* Themes define a set of toolbox foreground and background colors which we -@@ -265,13 +263,13 @@ body { +@@ -250,13 +250,13 @@ body { @media (-moz-platform: macos) and (not (-moz-mac-rtl)) { &:-moz-locale-dir(ltr) { diff --git a/src/browser/themes/shared/jar-inc-mn.patch b/src/browser/themes/shared/jar-inc-mn.patch index c04fd6d25..218750c59 100644 --- a/src/browser/themes/shared/jar-inc-mn.patch +++ b/src/browser/themes/shared/jar-inc-mn.patch @@ -1,8 +1,8 @@ diff --git a/browser/themes/shared/jar.inc.mn b/browser/themes/shared/jar.inc.mn -index 94de6f280e709161a54a65ffb621256b4e4e86fa..a882ce63c47e7757a9cd3b5bda7da792c5c3b616 100644 +index 3a12a7a33c33cec463b3d38647bdf647827784f8..4844da4b56b3172b333f5c5eb31fcd74f43fd0b0 100644 --- a/browser/themes/shared/jar.inc.mn +++ b/browser/themes/shared/jar.inc.mn -@@ -331,3 +331,5 @@ +@@ -334,3 +334,5 @@ skin/classic/browser/illustrations/market-opt-in.svg (../shared/illustrations/market-opt-in.svg) skin/classic/browser/illustrations/yelpRealtime-opt-in.svg (../shared/illustrations/yelpRealtime-opt-in.svg) diff --git a/src/browser/themes/shared/preferences/zen-preferences.css b/src/browser/themes/shared/preferences/zen-preferences.css index bae330acc..f67ae39f7 100644 --- a/src/browser/themes/shared/preferences/zen-preferences.css +++ b/src/browser/themes/shared/preferences/zen-preferences.css @@ -10,10 +10,6 @@ --in-content-box-background: var(--zen-colors-tertiary) !important; } -.main-content { - padding-top: 60px !important; -} - .pane-container { margin: auto; } @@ -38,10 +34,6 @@ html|dialog { animation: dialogPopin 0.2s ease-out; } -.sticky-inner-container { - display: none !important; -} - .subcategory { border-top: none !important; } @@ -504,8 +496,9 @@ groupbox h2 { #setting-control-sidebarChatbotFieldset, #aiControlsDescription, #category-ai-features, +#setting-control-supportFirefox, .mission-message, -html|setting-group[data-subcategory="layout"] { +html|setting-group:is([data-subcategory="layout"], [groupid="support"]) { display: none !important; } diff --git a/src/browser/themes/shared/tabbrowser/content-area-css.patch b/src/browser/themes/shared/tabbrowser/content-area-css.patch index 569299c80..972fe3d98 100644 --- a/src/browser/themes/shared/tabbrowser/content-area-css.patch +++ b/src/browser/themes/shared/tabbrowser/content-area-css.patch @@ -1,8 +1,8 @@ diff --git a/browser/themes/shared/tabbrowser/content-area.css b/browser/themes/shared/tabbrowser/content-area.css -index f01e484caadedbd7094a09c93dbd6f578261f24b..84e682e52f38c41fdaad1aea2f9468ce7040cc9d 100644 +index 13403bf438be18b5bcc9f3d497c94636b5fb5f6f..0505582a7efa212813cf39835c12d34d4d38eb4e 100644 --- a/browser/themes/shared/tabbrowser/content-area.css +++ b/browser/themes/shared/tabbrowser/content-area.css -@@ -89,7 +89,6 @@ +@@ -85,7 +85,6 @@ min-height: 0; /* We want to be able to show the frame color behind the clipped radiused corner */ @@ -10,7 +10,7 @@ index f01e484caadedbd7094a09c93dbd6f578261f24b..84e682e52f38c41fdaad1aea2f9468ce /* stylelint-disable-next-line media-query-no-invalid */ @media -moz-pref("sidebar.revamp") { -@@ -145,7 +144,6 @@ +@@ -141,7 +140,6 @@ } browser:is([blank], [pendingpaint]) { @@ -18,7 +18,7 @@ index f01e484caadedbd7094a09c93dbd6f578261f24b..84e682e52f38c41fdaad1aea2f9468ce } /* Exclude browsers with smartwindow-content attribute which inherit -@@ -431,7 +429,7 @@ split-view-footer { +@@ -427,7 +425,7 @@ split-view-footer { .dialogStack { z-index: var(--browser-stack-z-index-dialog-stack); @@ -27,7 +27,7 @@ index f01e484caadedbd7094a09c93dbd6f578261f24b..84e682e52f38c41fdaad1aea2f9468ce inset: 0; /* --browser-with-dialog set on browser[tabDialogShowing], we want to position the overlay only on the top of the element so it doesn't overlap the DevTools toolbox */ -@@ -598,7 +596,7 @@ split-view-footer { +@@ -594,7 +592,7 @@ split-view-footer { .dialogOverlay[topmost="true"], #window-modal-dialog::backdrop { diff --git a/src/browser/themes/shared/tabbrowser/ctrlTab-css.patch b/src/browser/themes/shared/tabbrowser/ctrlTab-css.patch index f0aff75c3..e990f547b 100644 --- a/src/browser/themes/shared/tabbrowser/ctrlTab-css.patch +++ b/src/browser/themes/shared/tabbrowser/ctrlTab-css.patch @@ -1,5 +1,5 @@ diff --git a/browser/themes/shared/tabbrowser/ctrlTab.css b/browser/themes/shared/tabbrowser/ctrlTab.css -index 6a7d295bfaed7f0429a8ff5ac9b8a2db4ee911e3..d1a3a8d44bc06102adaef294dc72f8f404be17dc 100644 +index d016bb363f306d7f6b08554f5029d425c01ca358..0f8e4d0a1aa2f9958124ea75423f83eb6ef35afe 100644 --- a/browser/themes/shared/tabbrowser/ctrlTab.css +++ b/browser/themes/shared/tabbrowser/ctrlTab.css @@ -11,6 +11,7 @@ @@ -7,6 +7,6 @@ index 6a7d295bfaed7f0429a8ff5ac9b8a2db4ee911e3..d1a3a8d44bc06102adaef294dc72f8f4 --panel-padding: 20px 10px 10px; -moz-window-shadow: none; + --panel-shadow-margin: 0; + background-color: transparent; @media not (-moz-platform: macos) { font-weight: var(--font-weight-bold); - } diff --git a/src/browser/themes/shared/tabbrowser/tabs-css.patch b/src/browser/themes/shared/tabbrowser/tabs-css.patch index e2e0bcd83..c639f757b 100644 --- a/src/browser/themes/shared/tabbrowser/tabs-css.patch +++ b/src/browser/themes/shared/tabbrowser/tabs-css.patch @@ -1,5 +1,5 @@ diff --git a/browser/themes/shared/tabbrowser/tabs.css b/browser/themes/shared/tabbrowser/tabs.css -index fc1e0870696fb5866663cdab0fc96faff0d5a3f8..2430d7afd4761c66cd5ad05a7f26200143299425 100644 +index 203b546933842f4b0134188fda020c4db4dcd0d2..0d67deabac2984574636248a16e58d1669f2e1a4 100644 --- a/browser/themes/shared/tabbrowser/tabs.css +++ b/browser/themes/shared/tabbrowser/tabs.css @@ -24,7 +24,7 @@ @@ -79,7 +79,7 @@ index fc1e0870696fb5866663cdab0fc96faff0d5a3f8..2430d7afd4761c66cd5ad05a7f262001 &[soundplaying] { list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-playing-small.svg"); } -@@ -668,7 +660,7 @@ tab-split-view-wrapper[dragtarget] { +@@ -666,7 +658,7 @@ tab-split-view-wrapper[dragtarget] { } } @@ -88,7 +88,7 @@ index fc1e0870696fb5866663cdab0fc96faff0d5a3f8..2430d7afd4761c66cd5ad05a7f262001 &[crashed] { display: revert; } -@@ -835,7 +827,7 @@ tab-split-view-wrapper[dragtarget] { +@@ -832,7 +824,7 @@ tab-split-view-wrapper[dragtarget] { has not been added to root. There are certain scenarios when that attribute is temporarily removed from root such as when toggling the sidebar to expand with the toolbar button. */ #tabbrowser-tabs[orient="horizontal"] &:not([pinned]):not([crashed]), @@ -97,16 +97,24 @@ index fc1e0870696fb5866663cdab0fc96faff0d5a3f8..2430d7afd4761c66cd5ad05a7f262001 &:is([soundplaying], [muted], [activemedia-blocked]) { display: flex; } -@@ -1562,7 +1554,7 @@ tab-group { +@@ -1048,7 +1040,6 @@ tab-split-view-wrapper[dragtarget] { + .tabbrowser-tab:is([image], [pinned]) > .tab-stack > .tab-content[attention]:not([selected]), + .tabbrowser-tab > .tab-stack > .tab-content[pinned][titlechanged]:not([selected]), + #tabbrowser-tabs[orient="vertical"] .tabbrowser-tab > .tab-stack > .tab-content[titlechanged]:not([selected]) { +- background-image: radial-gradient(circle, var(--tab-attention-dot-color), var(--tab-attention-dot-color) 2px, transparent 2px); + background-position: center bottom 6.5px; + background-size: 4px 4px; + background-repeat: no-repeat; +@@ -1614,7 +1605,7 @@ tab-group { } #tabbrowser-tabs[orient="vertical"][expanded] { - tab-group > :is(.tab-group-label-container, .tabbrowser-tab), + tab-group > :is(.tab-group-label-container, .tab-group-container, .tabbrowser-tab), - &[movingtab][movingtab-addToGroup]:not([movingtab-group], [movingtab-ungroup]) .tabbrowser-tab:is(:active, [multiselected]) { + &[movingtab][movingtab-addToGroup]:not([movingtab-group], [movingtab-ungroup]) tab-group > .tabbrowser-tab:is(:active, [multiselected]) { margin-inline-start: var(--space-medium); } -@@ -2057,7 +2049,7 @@ tab-group { +@@ -2089,7 +2080,7 @@ tab-group { } } @@ -115,7 +123,7 @@ index fc1e0870696fb5866663cdab0fc96faff0d5a3f8..2430d7afd4761c66cd5ad05a7f262001 #vertical-tabs-newtab-button { appearance: none; min-height: var(--tab-min-height); -@@ -2068,7 +2060,7 @@ tab-group { +@@ -2100,7 +2091,7 @@ tab-group { margin-inline: var(--tab-inner-inline-margin); #tabbrowser-tabs[orient="vertical"]:not([expanded]) & > .toolbarbutton-text { @@ -124,7 +132,7 @@ index fc1e0870696fb5866663cdab0fc96faff0d5a3f8..2430d7afd4761c66cd5ad05a7f262001 } &:hover { -@@ -2092,7 +2084,7 @@ tab-group { +@@ -2124,7 +2115,7 @@ tab-group { * flex container. #tabs-newtab-button is a child of the arrowscrollbox where * we don't want a gap (between tabs), so we have to add some margin. */ @@ -133,7 +141,7 @@ index fc1e0870696fb5866663cdab0fc96faff0d5a3f8..2430d7afd4761c66cd5ad05a7f262001 margin-block: var(--tab-block-margin); } -@@ -2280,7 +2272,6 @@ tab-group { +@@ -2312,7 +2303,6 @@ tab-group { &:not([expanded]) { .tabbrowser-tab[pinned] { @@ -141,7 +149,7 @@ index fc1e0870696fb5866663cdab0fc96faff0d5a3f8..2430d7afd4761c66cd5ad05a7f262001 } .tab-background { -@@ -2320,8 +2311,8 @@ tab-group { +@@ -2352,8 +2342,8 @@ tab-group { display: block; position: absolute; inset: auto; @@ -152,7 +160,7 @@ index fc1e0870696fb5866663cdab0fc96faff0d5a3f8..2430d7afd4761c66cd5ad05a7f262001 &:-moz-window-inactive { background-image: -@@ -2406,9 +2397,6 @@ tab-group { +@@ -2438,9 +2428,6 @@ tab-group { :root:not([privatebrowsingmode]) :is(toolbarbutton, toolbarpaletteitem) ~ #tabbrowser-tabs, :root[privatebrowsingmode] :is(toolbarbutton:not(#firefox-view-button), toolbarpaletteitem:not(#wrapper-firefox-view-button)) ~ #tabbrowser-tabs { @@ -162,7 +170,7 @@ index fc1e0870696fb5866663cdab0fc96faff0d5a3f8..2430d7afd4761c66cd5ad05a7f262001 } :root[privatebrowsingmode] :is(#firefox-view-button, #menu_openFirefoxView) { -@@ -2440,7 +2428,6 @@ toolbar:not(#TabsToolbar) #firefox-view-button { +@@ -2472,7 +2459,6 @@ toolbar:not(#TabsToolbar) #firefox-view-button { list-style-image: url(chrome://global/skin/icons/plus.svg); } diff --git a/src/browser/themes/shared/toolbarbuttons-css.patch b/src/browser/themes/shared/toolbarbuttons-css.patch index 4fb315a5f..cfd1bcefe 100644 --- a/src/browser/themes/shared/toolbarbuttons-css.patch +++ b/src/browser/themes/shared/toolbarbuttons-css.patch @@ -1,5 +1,5 @@ diff --git a/browser/themes/shared/toolbarbuttons.css b/browser/themes/shared/toolbarbuttons.css -index 08e7ff0d00f2f41b0ca253a6a895d5111d65668e..9b24e9f386edb881b95de75776e46a4d5e40c4cd 100644 +index 8c02df47ca003fa5094de0e7802cd8c10ef1bf2a..a16e8d7d3a00af8df648946950b7bb1c3f30efbb 100644 --- a/browser/themes/shared/toolbarbuttons.css +++ b/browser/themes/shared/toolbarbuttons.css @@ -13,20 +13,6 @@ diff --git a/src/browser/themes/shared/urlbar-searchbar-css.patch b/src/browser/themes/shared/urlbar-searchbar-css.patch index 416f79f84..8f644678a 100644 --- a/src/browser/themes/shared/urlbar-searchbar-css.patch +++ b/src/browser/themes/shared/urlbar-searchbar-css.patch @@ -1,5 +1,5 @@ diff --git a/browser/themes/shared/urlbar-searchbar.css b/browser/themes/shared/urlbar-searchbar.css -index cbbf55f31ae5e456401172f79ddbbe41256025a4..8cab0f2809a43c0aa4249453732eb0e006c2c676 100644 +index 0ee35cfe67c9bce37a844d4b7d9715d4fb50f709..5cdb8037e49a68ea4126af349690af8a16b5e59b 100644 --- a/browser/themes/shared/urlbar-searchbar.css +++ b/browser/themes/shared/urlbar-searchbar.css @@ -10,7 +10,7 @@ @@ -25,7 +25,7 @@ index cbbf55f31ae5e456401172f79ddbbe41256025a4..8cab0f2809a43c0aa4249453732eb0e0 .urlbar[breakout][breakout-extend] { height: auto; + align-items: center; -+ :root:not([zen-single-toolbar='true']) { ++ :root:not([zen-single-toolbar='true']) & { margin-left: calc(-1 * var(--urlbar-margin-inline)); + } width: calc(var(--urlbar-width) + 2 * var(--urlbar-margin-inline)); diff --git a/src/browser/themes/shared/urlbarView-css.patch b/src/browser/themes/shared/urlbarView-css.patch index 4a115a94c..a3551deb7 100644 --- a/src/browser/themes/shared/urlbarView-css.patch +++ b/src/browser/themes/shared/urlbarView-css.patch @@ -1,5 +1,5 @@ diff --git a/browser/themes/shared/urlbarView.css b/browser/themes/shared/urlbarView.css -index 212f3dc956df418b838e568d0b0fed5c1e848914..0dea37660e44ed5f000638eddf2770ebce049a43 100644 +index 2d6a63328642969e23da0a41ca6c7bb23eb96e8e..3060ba3f52ae7ab2f8fc36ff546d358724909b3e 100644 --- a/browser/themes/shared/urlbarView.css +++ b/browser/themes/shared/urlbarView.css @@ -21,7 +21,7 @@ diff --git a/src/browser/themes/shared/zen-icons/icons.css b/src/browser/themes/shared/zen-icons/icons.css index 5ae6db4cf..6b14799ea 100644 --- a/src/browser/themes/shared/zen-icons/icons.css +++ b/src/browser/themes/shared/zen-icons/icons.css @@ -8,7 +8,8 @@ #zen-welcome-start-button, .zen-toast button, .zen-current-workspace-indicator-chevron, -.pinned-tabs-container-separator toolbarbutton { +.pinned-tabs-container-separator toolbarbutton, +.zen-permission-popup-boost-editor-button { -moz-context-properties: fill, fill-opacity !important; fill: currentColor !important; } @@ -39,10 +40,21 @@ .close-icon, .zen-glance-sidebar-close, .zen-theme-picker-custom-list-item-remove, -#appMenu-quit-button2 { +#appMenu-quit-button2, +#zen-boost-delete { list-style-image: url("close.svg") !important; } +#PanelUI-zen-emojis-picker-none, +#zen-emojis-picker-none { + list-style-image: url("trash.svg"); +} + +#PanelUI-zen-gradient-generator-color-remove, +#zen-gradient-generator-color-remove { + list-style-image: url("unpin.svg") !important; +} + #PanelUI-zen-emojis-picker-none { list-style-image: url("trash.svg"); } @@ -117,7 +129,8 @@ } .zen-current-workspace-indicator-chevron, -#PanelUI-zen-gradient-generator-color-page-right { +#PanelUI-zen-gradient-generator-color-page-right, +.zen-permission-popup-boost-editor-button { list-style-image: url("arrow-right.svg"); } @@ -413,7 +426,8 @@ } .panel-header > .subviewbutton-back, -#PanelUI-zen-gradient-generator-color-page-left { +#PanelUI-zen-gradient-generator-color-page-left, +#zen-boost-back { list-style-image: url("arrow-left.svg") !important; } @@ -505,6 +519,30 @@ &:where([starred]) image { list-style-image: url("permissions-fill.svg"); } + &[boosting] image { + color: var(--color-accent-primary); + list-style-image: url("permissions-fill.svg"); + } + + position: relative; + + & .zen-site-data-boost-animation { + display: none; + } +} + +@media not (prefers-reduced-motion: reduce) { + #zen-site-data-icon-button[boosting] .zen-site-data-boost-animation { + position: absolute; + display: flex; + width: 100%; + height: 100%; + color: var(--color-accent-primary); + list-style-image: url("chrome://browser/content/zen-images/boost-indicator.svg"); + transform: translateX(-20%); + z-index: 0; + pointer-events: none; + } } .geo-icon { @@ -759,7 +797,8 @@ --fp-enabled: 1; } -#alltabs-button { +#alltabs-button, +#urlbar-engine-one-off-item-workspaces { list-style-image: url("chrome://browser/skin/tabs.svg") !important; } @@ -879,6 +918,34 @@ fill: currentColor; } +#zen-site-data-boost { + border-radius: 6px; + appearance: none; + padding: 6px 10px 6px 10px; + + position: relative; + + list-style-image: url("paintbrush.svg"); + + & .toolbarbutton-text { + display: none; + } + + @media -moz-pref("zen.boosts.enabled", false) { + display: none; + } + + & image { + -moz-context-properties: fill, fill-opacity; + fill: currentColor; + } +} + +.boost-brush { + list-style-image: url("boost.svg") !important; + padding: 10px; +} + #zen-site-data-security-info { -moz-context-properties: fill, fill-opacity; fill: currentColor; @@ -938,3 +1005,79 @@ list-style-image: url("link.svg"); fill-opacity: 0.65; } + +#zen-boost-text-case-toggle { + list-style-image: url("text-title-case.svg"); +} + +#zen-boost-text-case-toggle[case-mode="uppercase"] { + list-style-image: url("text-uppercase.svg"); +} + +#zen-boost-text-case-toggle[case-mode="lowercase"] { + list-style-image: url("text-lowercase.svg"); +} + +#zen-boost-code { + list-style-image: url("brackets-curly.svg"); +} + +#zen-boost-zap { + list-style-image: url("bolt.svg"); +} + +#zen-boost-controls { + list-style-image: url("sliders.svg"); +} + +#zen-boost-invert { + list-style-image: url("lightbulb.svg"); +} + +#zen-boost-disable { + list-style-image: url("block.svg"); +} + +#zen-boost-close { + list-style-image: url("close.svg"); + + @media (-moz-platform: macos) { + list-style-image: url("close-filled-round.svg"); + } + + @media (-moz-platform: windows) { + list-style-image: none; + list-style: none; + display: inline-flex; + + &::before { + font-family: "Segoe Fluent Icons", "Segoe MDL2 Assets"; + content: "\e8bb"; + font-size: 12px; + } + } +} + +#zen-boost-magic-theme { + list-style-image: url("sparkles.svg"); +} + +#zen-boost-shuffle { + list-style-image: url("arrow-rotate-anticlockwise.svg"); +} + +#zen-boost-css-picker { + list-style-image: url("eyedropper.svg"); +} + +#zen-boost-css-inspector { + list-style-image: url("hammer.svg"); +} + +#zen-boost-save { + list-style-image: url("downloads.svg"); +} + +#zen-boost-load { + list-style-image: url("open.svg"); +} diff --git a/src/browser/themes/shared/zen-icons/jar.inc.mn b/src/browser/themes/shared/zen-icons/jar.inc.mn index 7317c6ccb..be1479dcf 100644 --- a/src/browser/themes/shared/zen-icons/jar.inc.mn +++ b/src/browser/themes/shared/zen-icons/jar.inc.mn @@ -7,20 +7,27 @@ * skin/classic/browser/zen-icons/arrow-down.svg (../shared/zen-icons/nucleo/arrow-down.svg) * skin/classic/browser/zen-icons/arrow-left.svg (../shared/zen-icons/nucleo/arrow-left.svg) * skin/classic/browser/zen-icons/arrow-right.svg (../shared/zen-icons/nucleo/arrow-right.svg) +* skin/classic/browser/zen-icons/arrow-rotate-anticlockwise.svg (../shared/zen-icons/nucleo/arrow-rotate-anticlockwise.svg) * skin/classic/browser/zen-icons/arrow-up.svg (../shared/zen-icons/nucleo/arrow-up.svg) * skin/classic/browser/zen-icons/autoplay-media-blocked.svg (../shared/zen-icons/nucleo/autoplay-media-blocked.svg) * skin/classic/browser/zen-icons/autoplay-media-fill.svg (../shared/zen-icons/nucleo/autoplay-media-fill.svg) * skin/classic/browser/zen-icons/autoplay-media.svg (../shared/zen-icons/nucleo/autoplay-media.svg) * skin/classic/browser/zen-icons/back.svg (../shared/zen-icons/nucleo/back.svg) +* skin/classic/browser/zen-icons/block.svg (../shared/zen-icons/nucleo/block.svg) +* skin/classic/browser/zen-icons/bolt.svg (../shared/zen-icons/nucleo/bolt.svg) * skin/classic/browser/zen-icons/bookmark-hollow.svg (../shared/zen-icons/nucleo/bookmark-hollow.svg) * skin/classic/browser/zen-icons/bookmark-star-on-tray.svg (../shared/zen-icons/nucleo/bookmark-star-on-tray.svg) * skin/classic/browser/zen-icons/bookmark.svg (../shared/zen-icons/nucleo/bookmark.svg) +* skin/classic/browser/zen-icons/boost.svg (../shared/zen-icons/nucleo/boost.svg) +* skin/classic/browser/zen-icons/blocked-element.svg (../shared/zen-icons/nucleo/blocked-element.svg) +* skin/classic/browser/zen-icons/brackets-curly.svg (../shared/zen-icons/nucleo/brackets-curly.svg) * skin/classic/browser/zen-icons/camera-blocked.svg (../shared/zen-icons/nucleo/camera-blocked.svg) * skin/classic/browser/zen-icons/camera-fill.svg (../shared/zen-icons/nucleo/camera-fill.svg) * skin/classic/browser/zen-icons/camera.svg (../shared/zen-icons/nucleo/camera.svg) * skin/classic/browser/zen-icons/canvas-blocked.svg (../shared/zen-icons/nucleo/canvas-blocked.svg) * skin/classic/browser/zen-icons/canvas.svg (../shared/zen-icons/nucleo/canvas.svg) * skin/classic/browser/zen-icons/chevron.svg (../shared/zen-icons/nucleo/chevron.svg) +* skin/classic/browser/zen-icons/close-filled-round.svg (../shared/zen-icons/nucleo/close-filled-round.svg) * skin/classic/browser/zen-icons/close.svg (../shared/zen-icons/nucleo/close.svg) * skin/classic/browser/zen-icons/container-tab.svg (../shared/zen-icons/nucleo/container-tab.svg) * skin/classic/browser/zen-icons/cookies-fill.svg (../shared/zen-icons/nucleo/cookies-fill.svg) @@ -44,6 +51,7 @@ * skin/classic/browser/zen-icons/extension-blocked.svg (../shared/zen-icons/nucleo/extension-blocked.svg) * skin/classic/browser/zen-icons/extension-fill.svg (../shared/zen-icons/nucleo/extension-fill.svg) * skin/classic/browser/zen-icons/extension.svg (../shared/zen-icons/nucleo/extension.svg) +* skin/classic/browser/zen-icons/eyedropper.svg (../shared/zen-icons/nucleo/eyedropper.svg) * skin/classic/browser/zen-icons/face-sun.svg (../shared/zen-icons/nucleo/face-sun.svg) * skin/classic/browser/zen-icons/folder.svg (../shared/zen-icons/nucleo/folder.svg) * skin/classic/browser/zen-icons/forget.svg (../shared/zen-icons/nucleo/forget.svg) @@ -53,12 +61,14 @@ * skin/classic/browser/zen-icons/geo-blocked.svg (../shared/zen-icons/nucleo/geo-blocked.svg) * skin/classic/browser/zen-icons/geo-fill.svg (../shared/zen-icons/nucleo/geo-fill.svg) * skin/classic/browser/zen-icons/geo.svg (../shared/zen-icons/nucleo/geo.svg) +* skin/classic/browser/zen-icons/hammer.svg (../shared/zen-icons/nucleo/hammer.svg) * skin/classic/browser/zen-icons/heart-circle-fill.svg (../shared/zen-icons/nucleo/heart-circle-fill.svg) * skin/classic/browser/zen-icons/help.svg (../shared/zen-icons/nucleo/help.svg) * skin/classic/browser/zen-icons/history.svg (../shared/zen-icons/nucleo/history.svg) * skin/classic/browser/zen-icons/home.svg (../shared/zen-icons/nucleo/home.svg) * skin/classic/browser/zen-icons/info.svg (../shared/zen-icons/nucleo/info.svg) * skin/classic/browser/zen-icons/library.svg (../shared/zen-icons/nucleo/library.svg) +* skin/classic/browser/zen-icons/lightbulb.svg (../shared/zen-icons/nucleo/lightbulb.svg) * skin/classic/browser/zen-icons/link.svg (../shared/zen-icons/nucleo/link.svg) * skin/classic/browser/zen-icons/mail.svg (../shared/zen-icons/nucleo/mail.svg) * skin/classic/browser/zen-icons/manage.svg (../shared/zen-icons/nucleo/manage.svg) @@ -78,6 +88,8 @@ * skin/classic/browser/zen-icons/new-tab-image.svg (../shared/zen-icons/nucleo/new-tab-image.svg) * skin/classic/browser/zen-icons/open.svg (../shared/zen-icons/nucleo/open.svg) * skin/classic/browser/zen-icons/page-portrait.svg (../shared/zen-icons/nucleo/page-portrait.svg) +* skin/classic/browser/zen-icons/paintbrush-fill.svg (../shared/zen-icons/nucleo/paintbrush-fill.svg) +* skin/classic/browser/zen-icons/paintbrush.svg (../shared/zen-icons/nucleo/paintbrush.svg) * skin/classic/browser/zen-icons/palette.svg (../shared/zen-icons/nucleo/palette.svg) * skin/classic/browser/zen-icons/passwords.svg (../shared/zen-icons/nucleo/passwords.svg) * skin/classic/browser/zen-icons/permissions-fill.svg (../shared/zen-icons/nucleo/permissions-fill.svg) @@ -110,13 +122,19 @@ * skin/classic/browser/zen-icons/sidebar-right.svg (../shared/zen-icons/nucleo/sidebar-right.svg) * skin/classic/browser/zen-icons/sidebar.svg (../shared/zen-icons/nucleo/sidebar.svg) * skin/classic/browser/zen-icons/sidebars-right.svg (../shared/zen-icons/nucleo/sidebars-right.svg) +* skin/classic/browser/zen-icons/sliders.svg (../shared/zen-icons/nucleo/sliders.svg) * skin/classic/browser/zen-icons/sparkles.svg (../shared/zen-icons/nucleo/sparkles.svg) * skin/classic/browser/zen-icons/spell-check.svg (../shared/zen-icons/nucleo/spell-check.svg) * skin/classic/browser/zen-icons/split.svg (../shared/zen-icons/nucleo/split.svg) +* skin/classic/browser/zen-icons/square-wand-sparkle.svg (../shared/zen-icons/nucleo/square-wand-sparkle.svg) * skin/classic/browser/zen-icons/tab-audio-blocked-small.svg (../shared/zen-icons/nucleo/tab-audio-blocked-small.svg) * skin/classic/browser/zen-icons/tab-audio-muted-small.svg (../shared/zen-icons/nucleo/tab-audio-muted-small.svg) * skin/classic/browser/zen-icons/tab-audio-playing-small.svg (../shared/zen-icons/nucleo/tab-audio-playing-small.svg) * skin/classic/browser/zen-icons/tab.svg (../shared/zen-icons/nucleo/tab.svg) +* skin/classic/browser/zen-icons/text-lowercase.svg (../shared/zen-icons/nucleo/text-lowercase.svg) +* skin/classic/browser/zen-icons/text-size.svg (../shared/zen-icons/nucleo/text-size.svg) +* skin/classic/browser/zen-icons/text-title-case.svg (../shared/zen-icons/nucleo/text-title-case.svg) +* skin/classic/browser/zen-icons/text-uppercase.svg (../shared/zen-icons/nucleo/text-uppercase.svg) * skin/classic/browser/zen-icons/tool-profiler.svg (../shared/zen-icons/nucleo/tool-profiler.svg) * skin/classic/browser/zen-icons/tracking-protection-fill.svg (../shared/zen-icons/nucleo/tracking-protection-fill.svg) * skin/classic/browser/zen-icons/tracking-protection.svg (../shared/zen-icons/nucleo/tracking-protection.svg) @@ -125,6 +143,7 @@ * skin/classic/browser/zen-icons/unpin.svg (../shared/zen-icons/nucleo/unpin.svg) * skin/classic/browser/zen-icons/video-blocked-fill.svg (../shared/zen-icons/nucleo/video-blocked-fill.svg) * skin/classic/browser/zen-icons/video-fill.svg (../shared/zen-icons/nucleo/video-fill.svg) +* skin/classic/browser/zen-icons/wand-sparkle.svg (../shared/zen-icons/nucleo/wand-sparkle.svg) * skin/classic/browser/zen-icons/window.svg (../shared/zen-icons/nucleo/window.svg) * skin/classic/browser/zen-icons/xr-blocked.svg (../shared/zen-icons/nucleo/xr-blocked.svg) * skin/classic/browser/zen-icons/xr-fill.svg (../shared/zen-icons/nucleo/xr-fill.svg) @@ -136,20 +155,27 @@ * skin/classic/browser/zen-icons/arrow-down.svg (../shared/zen-icons/nucleo/arrow-down.svg) * skin/classic/browser/zen-icons/arrow-left.svg (../shared/zen-icons/nucleo/arrow-left.svg) * skin/classic/browser/zen-icons/arrow-right.svg (../shared/zen-icons/nucleo/arrow-right.svg) +* skin/classic/browser/zen-icons/arrow-rotate-anticlockwise.svg (../shared/zen-icons/nucleo/arrow-rotate-anticlockwise.svg) * skin/classic/browser/zen-icons/arrow-up.svg (../shared/zen-icons/nucleo/arrow-up.svg) * skin/classic/browser/zen-icons/autoplay-media-blocked.svg (../shared/zen-icons/nucleo/autoplay-media-blocked.svg) * skin/classic/browser/zen-icons/autoplay-media-fill.svg (../shared/zen-icons/nucleo/autoplay-media-fill.svg) * skin/classic/browser/zen-icons/autoplay-media.svg (../shared/zen-icons/nucleo/autoplay-media.svg) * skin/classic/browser/zen-icons/back.svg (../shared/zen-icons/nucleo/back.svg) +* skin/classic/browser/zen-icons/block.svg (../shared/zen-icons/nucleo/block.svg) +* skin/classic/browser/zen-icons/bolt.svg (../shared/zen-icons/nucleo/bolt.svg) * skin/classic/browser/zen-icons/bookmark-hollow.svg (../shared/zen-icons/nucleo/bookmark-hollow.svg) * skin/classic/browser/zen-icons/bookmark-star-on-tray.svg (../shared/zen-icons/nucleo/bookmark-star-on-tray.svg) * skin/classic/browser/zen-icons/bookmark.svg (../shared/zen-icons/nucleo/bookmark.svg) +* skin/classic/browser/zen-icons/boost.svg (../shared/zen-icons/nucleo/boost.svg) +* skin/classic/browser/zen-icons/blocked-element.svg (../shared/zen-icons/nucleo/blocked-element.svg) +* skin/classic/browser/zen-icons/brackets-curly.svg (../shared/zen-icons/nucleo/brackets-curly.svg) * skin/classic/browser/zen-icons/camera-blocked.svg (../shared/zen-icons/nucleo/camera-blocked.svg) * skin/classic/browser/zen-icons/camera-fill.svg (../shared/zen-icons/nucleo/camera-fill.svg) * skin/classic/browser/zen-icons/camera.svg (../shared/zen-icons/nucleo/camera.svg) * skin/classic/browser/zen-icons/canvas-blocked.svg (../shared/zen-icons/nucleo/canvas-blocked.svg) * skin/classic/browser/zen-icons/canvas.svg (../shared/zen-icons/nucleo/canvas.svg) * skin/classic/browser/zen-icons/chevron.svg (../shared/zen-icons/nucleo/chevron.svg) +* skin/classic/browser/zen-icons/close-filled-round.svg (../shared/zen-icons/nucleo/close-filled-round.svg) * skin/classic/browser/zen-icons/close.svg (../shared/zen-icons/nucleo/close.svg) * skin/classic/browser/zen-icons/container-tab.svg (../shared/zen-icons/nucleo/container-tab.svg) * skin/classic/browser/zen-icons/cookies-fill.svg (../shared/zen-icons/nucleo/cookies-fill.svg) @@ -173,6 +199,7 @@ * skin/classic/browser/zen-icons/extension-blocked.svg (../shared/zen-icons/nucleo/extension-blocked.svg) * skin/classic/browser/zen-icons/extension-fill.svg (../shared/zen-icons/nucleo/extension-fill.svg) * skin/classic/browser/zen-icons/extension.svg (../shared/zen-icons/nucleo/extension.svg) +* skin/classic/browser/zen-icons/eyedropper.svg (../shared/zen-icons/nucleo/eyedropper.svg) * skin/classic/browser/zen-icons/face-sun.svg (../shared/zen-icons/nucleo/face-sun.svg) * skin/classic/browser/zen-icons/folder.svg (../shared/zen-icons/nucleo/folder.svg) * skin/classic/browser/zen-icons/forget.svg (../shared/zen-icons/nucleo/forget.svg) @@ -182,12 +209,14 @@ * skin/classic/browser/zen-icons/geo-blocked.svg (../shared/zen-icons/nucleo/geo-blocked.svg) * skin/classic/browser/zen-icons/geo-fill.svg (../shared/zen-icons/nucleo/geo-fill.svg) * skin/classic/browser/zen-icons/geo.svg (../shared/zen-icons/nucleo/geo.svg) +* skin/classic/browser/zen-icons/hammer.svg (../shared/zen-icons/nucleo/hammer.svg) * skin/classic/browser/zen-icons/heart-circle-fill.svg (../shared/zen-icons/nucleo/heart-circle-fill.svg) * skin/classic/browser/zen-icons/help.svg (../shared/zen-icons/nucleo/help.svg) * skin/classic/browser/zen-icons/history.svg (../shared/zen-icons/nucleo/history.svg) * skin/classic/browser/zen-icons/home.svg (../shared/zen-icons/nucleo/home.svg) * skin/classic/browser/zen-icons/info.svg (../shared/zen-icons/nucleo/info.svg) * skin/classic/browser/zen-icons/library.svg (../shared/zen-icons/nucleo/library.svg) +* skin/classic/browser/zen-icons/lightbulb.svg (../shared/zen-icons/nucleo/lightbulb.svg) * skin/classic/browser/zen-icons/link.svg (../shared/zen-icons/nucleo/link.svg) * skin/classic/browser/zen-icons/mail.svg (../shared/zen-icons/nucleo/mail.svg) * skin/classic/browser/zen-icons/manage.svg (../shared/zen-icons/nucleo/manage.svg) @@ -207,6 +236,8 @@ * skin/classic/browser/zen-icons/new-tab-image.svg (../shared/zen-icons/nucleo/new-tab-image.svg) * skin/classic/browser/zen-icons/open.svg (../shared/zen-icons/nucleo/open.svg) * skin/classic/browser/zen-icons/page-portrait.svg (../shared/zen-icons/nucleo/page-portrait.svg) +* skin/classic/browser/zen-icons/paintbrush-fill.svg (../shared/zen-icons/nucleo/paintbrush-fill.svg) +* skin/classic/browser/zen-icons/paintbrush.svg (../shared/zen-icons/nucleo/paintbrush.svg) * skin/classic/browser/zen-icons/palette.svg (../shared/zen-icons/nucleo/palette.svg) * skin/classic/browser/zen-icons/passwords.svg (../shared/zen-icons/nucleo/passwords.svg) * skin/classic/browser/zen-icons/permissions-fill.svg (../shared/zen-icons/nucleo/permissions-fill.svg) @@ -239,13 +270,19 @@ * skin/classic/browser/zen-icons/sidebar-right.svg (../shared/zen-icons/nucleo/sidebar-right.svg) * skin/classic/browser/zen-icons/sidebar.svg (../shared/zen-icons/nucleo/sidebar.svg) * skin/classic/browser/zen-icons/sidebars-right.svg (../shared/zen-icons/nucleo/sidebars-right.svg) +* skin/classic/browser/zen-icons/sliders.svg (../shared/zen-icons/nucleo/sliders.svg) * skin/classic/browser/zen-icons/sparkles.svg (../shared/zen-icons/nucleo/sparkles.svg) * skin/classic/browser/zen-icons/spell-check.svg (../shared/zen-icons/nucleo/spell-check.svg) * skin/classic/browser/zen-icons/split.svg (../shared/zen-icons/nucleo/split.svg) +* skin/classic/browser/zen-icons/square-wand-sparkle.svg (../shared/zen-icons/nucleo/square-wand-sparkle.svg) * skin/classic/browser/zen-icons/tab-audio-blocked-small.svg (../shared/zen-icons/nucleo/tab-audio-blocked-small.svg) * skin/classic/browser/zen-icons/tab-audio-muted-small.svg (../shared/zen-icons/nucleo/tab-audio-muted-small.svg) * skin/classic/browser/zen-icons/tab-audio-playing-small.svg (../shared/zen-icons/nucleo/tab-audio-playing-small.svg) * skin/classic/browser/zen-icons/tab.svg (../shared/zen-icons/nucleo/tab.svg) +* skin/classic/browser/zen-icons/text-lowercase.svg (../shared/zen-icons/nucleo/text-lowercase.svg) +* skin/classic/browser/zen-icons/text-size.svg (../shared/zen-icons/nucleo/text-size.svg) +* skin/classic/browser/zen-icons/text-title-case.svg (../shared/zen-icons/nucleo/text-title-case.svg) +* skin/classic/browser/zen-icons/text-uppercase.svg (../shared/zen-icons/nucleo/text-uppercase.svg) * skin/classic/browser/zen-icons/tool-profiler.svg (../shared/zen-icons/nucleo/tool-profiler.svg) * skin/classic/browser/zen-icons/tracking-protection-fill.svg (../shared/zen-icons/nucleo/tracking-protection-fill.svg) * skin/classic/browser/zen-icons/tracking-protection.svg (../shared/zen-icons/nucleo/tracking-protection.svg) @@ -254,6 +291,7 @@ * skin/classic/browser/zen-icons/unpin.svg (../shared/zen-icons/nucleo/unpin.svg) * skin/classic/browser/zen-icons/video-blocked-fill.svg (../shared/zen-icons/nucleo/video-blocked-fill.svg) * skin/classic/browser/zen-icons/video-fill.svg (../shared/zen-icons/nucleo/video-fill.svg) +* skin/classic/browser/zen-icons/wand-sparkle.svg (../shared/zen-icons/nucleo/wand-sparkle.svg) * skin/classic/browser/zen-icons/window.svg (../shared/zen-icons/nucleo/window.svg) * skin/classic/browser/zen-icons/xr-blocked.svg (../shared/zen-icons/nucleo/xr-blocked.svg) * skin/classic/browser/zen-icons/xr-fill.svg (../shared/zen-icons/nucleo/xr-fill.svg) @@ -265,20 +303,27 @@ * skin/classic/browser/zen-icons/arrow-down.svg (../shared/zen-icons/nucleo/arrow-down.svg) * skin/classic/browser/zen-icons/arrow-left.svg (../shared/zen-icons/nucleo/arrow-left.svg) * skin/classic/browser/zen-icons/arrow-right.svg (../shared/zen-icons/nucleo/arrow-right.svg) +* skin/classic/browser/zen-icons/arrow-rotate-anticlockwise.svg (../shared/zen-icons/nucleo/arrow-rotate-anticlockwise.svg) * skin/classic/browser/zen-icons/arrow-up.svg (../shared/zen-icons/nucleo/arrow-up.svg) * skin/classic/browser/zen-icons/autoplay-media-blocked.svg (../shared/zen-icons/nucleo/autoplay-media-blocked.svg) * skin/classic/browser/zen-icons/autoplay-media-fill.svg (../shared/zen-icons/nucleo/autoplay-media-fill.svg) * skin/classic/browser/zen-icons/autoplay-media.svg (../shared/zen-icons/nucleo/autoplay-media.svg) * skin/classic/browser/zen-icons/back.svg (../shared/zen-icons/nucleo/back.svg) +* skin/classic/browser/zen-icons/block.svg (../shared/zen-icons/nucleo/block.svg) +* skin/classic/browser/zen-icons/bolt.svg (../shared/zen-icons/nucleo/bolt.svg) * skin/classic/browser/zen-icons/bookmark-hollow.svg (../shared/zen-icons/nucleo/bookmark-hollow.svg) * skin/classic/browser/zen-icons/bookmark-star-on-tray.svg (../shared/zen-icons/nucleo/bookmark-star-on-tray.svg) * skin/classic/browser/zen-icons/bookmark.svg (../shared/zen-icons/nucleo/bookmark.svg) +* skin/classic/browser/zen-icons/boost.svg (../shared/zen-icons/nucleo/boost.svg) +* skin/classic/browser/zen-icons/blocked-element.svg (../shared/zen-icons/nucleo/blocked-element.svg) +* skin/classic/browser/zen-icons/brackets-curly.svg (../shared/zen-icons/nucleo/brackets-curly.svg) * skin/classic/browser/zen-icons/camera-blocked.svg (../shared/zen-icons/nucleo/camera-blocked.svg) * skin/classic/browser/zen-icons/camera-fill.svg (../shared/zen-icons/nucleo/camera-fill.svg) * skin/classic/browser/zen-icons/camera.svg (../shared/zen-icons/nucleo/camera.svg) * skin/classic/browser/zen-icons/canvas-blocked.svg (../shared/zen-icons/nucleo/canvas-blocked.svg) * skin/classic/browser/zen-icons/canvas.svg (../shared/zen-icons/nucleo/canvas.svg) * skin/classic/browser/zen-icons/chevron.svg (../shared/zen-icons/nucleo/chevron.svg) +* skin/classic/browser/zen-icons/close-filled-round.svg (../shared/zen-icons/nucleo/close-filled-round.svg) * skin/classic/browser/zen-icons/close.svg (../shared/zen-icons/nucleo/close.svg) * skin/classic/browser/zen-icons/container-tab.svg (../shared/zen-icons/nucleo/container-tab.svg) * skin/classic/browser/zen-icons/cookies-fill.svg (../shared/zen-icons/nucleo/cookies-fill.svg) @@ -302,6 +347,7 @@ * skin/classic/browser/zen-icons/extension-blocked.svg (../shared/zen-icons/nucleo/extension-blocked.svg) * skin/classic/browser/zen-icons/extension-fill.svg (../shared/zen-icons/nucleo/extension-fill.svg) * skin/classic/browser/zen-icons/extension.svg (../shared/zen-icons/nucleo/extension.svg) +* skin/classic/browser/zen-icons/eyedropper.svg (../shared/zen-icons/nucleo/eyedropper.svg) * skin/classic/browser/zen-icons/face-sun.svg (../shared/zen-icons/nucleo/face-sun.svg) * skin/classic/browser/zen-icons/folder.svg (../shared/zen-icons/nucleo/folder.svg) * skin/classic/browser/zen-icons/forget.svg (../shared/zen-icons/nucleo/forget.svg) @@ -311,12 +357,14 @@ * skin/classic/browser/zen-icons/geo-blocked.svg (../shared/zen-icons/nucleo/geo-blocked.svg) * skin/classic/browser/zen-icons/geo-fill.svg (../shared/zen-icons/nucleo/geo-fill.svg) * skin/classic/browser/zen-icons/geo.svg (../shared/zen-icons/nucleo/geo.svg) +* skin/classic/browser/zen-icons/hammer.svg (../shared/zen-icons/nucleo/hammer.svg) * skin/classic/browser/zen-icons/heart-circle-fill.svg (../shared/zen-icons/nucleo/heart-circle-fill.svg) * skin/classic/browser/zen-icons/help.svg (../shared/zen-icons/nucleo/help.svg) * skin/classic/browser/zen-icons/history.svg (../shared/zen-icons/nucleo/history.svg) * skin/classic/browser/zen-icons/home.svg (../shared/zen-icons/nucleo/home.svg) * skin/classic/browser/zen-icons/info.svg (../shared/zen-icons/nucleo/info.svg) * skin/classic/browser/zen-icons/library.svg (../shared/zen-icons/nucleo/library.svg) +* skin/classic/browser/zen-icons/lightbulb.svg (../shared/zen-icons/nucleo/lightbulb.svg) * skin/classic/browser/zen-icons/link.svg (../shared/zen-icons/nucleo/link.svg) * skin/classic/browser/zen-icons/mail.svg (../shared/zen-icons/nucleo/mail.svg) * skin/classic/browser/zen-icons/manage.svg (../shared/zen-icons/nucleo/manage.svg) @@ -336,6 +384,8 @@ * skin/classic/browser/zen-icons/new-tab-image.svg (../shared/zen-icons/nucleo/new-tab-image.svg) * skin/classic/browser/zen-icons/open.svg (../shared/zen-icons/nucleo/open.svg) * skin/classic/browser/zen-icons/page-portrait.svg (../shared/zen-icons/nucleo/page-portrait.svg) +* skin/classic/browser/zen-icons/paintbrush-fill.svg (../shared/zen-icons/nucleo/paintbrush-fill.svg) +* skin/classic/browser/zen-icons/paintbrush.svg (../shared/zen-icons/nucleo/paintbrush.svg) * skin/classic/browser/zen-icons/palette.svg (../shared/zen-icons/nucleo/palette.svg) * skin/classic/browser/zen-icons/passwords.svg (../shared/zen-icons/nucleo/passwords.svg) * skin/classic/browser/zen-icons/permissions-fill.svg (../shared/zen-icons/nucleo/permissions-fill.svg) @@ -368,13 +418,19 @@ * skin/classic/browser/zen-icons/sidebar-right.svg (../shared/zen-icons/nucleo/sidebar-right.svg) * skin/classic/browser/zen-icons/sidebar.svg (../shared/zen-icons/nucleo/sidebar.svg) * skin/classic/browser/zen-icons/sidebars-right.svg (../shared/zen-icons/nucleo/sidebars-right.svg) +* skin/classic/browser/zen-icons/sliders.svg (../shared/zen-icons/nucleo/sliders.svg) * skin/classic/browser/zen-icons/sparkles.svg (../shared/zen-icons/nucleo/sparkles.svg) * skin/classic/browser/zen-icons/spell-check.svg (../shared/zen-icons/nucleo/spell-check.svg) * skin/classic/browser/zen-icons/split.svg (../shared/zen-icons/nucleo/split.svg) +* skin/classic/browser/zen-icons/square-wand-sparkle.svg (../shared/zen-icons/nucleo/square-wand-sparkle.svg) * skin/classic/browser/zen-icons/tab-audio-blocked-small.svg (../shared/zen-icons/nucleo/tab-audio-blocked-small.svg) * skin/classic/browser/zen-icons/tab-audio-muted-small.svg (../shared/zen-icons/nucleo/tab-audio-muted-small.svg) * skin/classic/browser/zen-icons/tab-audio-playing-small.svg (../shared/zen-icons/nucleo/tab-audio-playing-small.svg) * skin/classic/browser/zen-icons/tab.svg (../shared/zen-icons/nucleo/tab.svg) +* skin/classic/browser/zen-icons/text-lowercase.svg (../shared/zen-icons/nucleo/text-lowercase.svg) +* skin/classic/browser/zen-icons/text-size.svg (../shared/zen-icons/nucleo/text-size.svg) +* skin/classic/browser/zen-icons/text-title-case.svg (../shared/zen-icons/nucleo/text-title-case.svg) +* skin/classic/browser/zen-icons/text-uppercase.svg (../shared/zen-icons/nucleo/text-uppercase.svg) * skin/classic/browser/zen-icons/tool-profiler.svg (../shared/zen-icons/nucleo/tool-profiler.svg) * skin/classic/browser/zen-icons/tracking-protection-fill.svg (../shared/zen-icons/nucleo/tracking-protection-fill.svg) * skin/classic/browser/zen-icons/tracking-protection.svg (../shared/zen-icons/nucleo/tracking-protection.svg) @@ -383,6 +439,7 @@ * skin/classic/browser/zen-icons/unpin.svg (../shared/zen-icons/nucleo/unpin.svg) * skin/classic/browser/zen-icons/video-blocked-fill.svg (../shared/zen-icons/nucleo/video-blocked-fill.svg) * skin/classic/browser/zen-icons/video-fill.svg (../shared/zen-icons/nucleo/video-fill.svg) +* skin/classic/browser/zen-icons/wand-sparkle.svg (../shared/zen-icons/nucleo/wand-sparkle.svg) * skin/classic/browser/zen-icons/window.svg (../shared/zen-icons/nucleo/window.svg) * skin/classic/browser/zen-icons/xr-blocked.svg (../shared/zen-icons/nucleo/xr-blocked.svg) * skin/classic/browser/zen-icons/xr-fill.svg (../shared/zen-icons/nucleo/xr-fill.svg) diff --git a/src/browser/themes/shared/zen-icons/nucleo/algorithm.svg b/src/browser/themes/shared/zen-icons/nucleo/algorithm.svg index f35991ccc..4594ea39f 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/algorithm.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/algorithm.svg @@ -2,4 +2,4 @@ # 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/. - \ No newline at end of file + diff --git a/src/browser/themes/shared/zen-icons/nucleo/arrow-down.svg b/src/browser/themes/shared/zen-icons/nucleo/arrow-down.svg index 19b526e0b..af570ec31 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/arrow-down.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/arrow-down.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/arrow-left.svg b/src/browser/themes/shared/zen-icons/nucleo/arrow-left.svg index 885934f29..09a5dc028 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/arrow-left.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/arrow-left.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/arrow-right.svg b/src/browser/themes/shared/zen-icons/nucleo/arrow-right.svg index 88847cd96..9cefe9a0a 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/arrow-right.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/arrow-right.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/arrow-rotate-anticlockwise.svg b/src/browser/themes/shared/zen-icons/nucleo/arrow-rotate-anticlockwise.svg new file mode 100644 index 000000000..0952e887f --- /dev/null +++ b/src/browser/themes/shared/zen-icons/nucleo/arrow-rotate-anticlockwise.svg @@ -0,0 +1,31 @@ +#filter dumbComments emptyLines substitution +# 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/. + + + + + + + diff --git a/src/browser/themes/shared/zen-icons/nucleo/arrow-up.svg b/src/browser/themes/shared/zen-icons/nucleo/arrow-up.svg index b72ea070d..100fcab9d 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/arrow-up.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/arrow-up.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/autoplay-media-blocked.svg b/src/browser/themes/shared/zen-icons/nucleo/autoplay-media-blocked.svg index fda0785c6..65ba9fc5d 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/autoplay-media-blocked.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/autoplay-media-blocked.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/autoplay-media-fill.svg b/src/browser/themes/shared/zen-icons/nucleo/autoplay-media-fill.svg index d16ce98cf..8de8fafd8 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/autoplay-media-fill.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/autoplay-media-fill.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/autoplay-media.svg b/src/browser/themes/shared/zen-icons/nucleo/autoplay-media.svg index a7dc91f2c..2d970131d 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/autoplay-media.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/autoplay-media.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/block.svg b/src/browser/themes/shared/zen-icons/nucleo/block.svg new file mode 100644 index 000000000..e52975444 --- /dev/null +++ b/src/browser/themes/shared/zen-icons/nucleo/block.svg @@ -0,0 +1,31 @@ +#filter dumbComments emptyLines substitution +# 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/. + + + + + + + diff --git a/src/browser/themes/shared/zen-icons/nucleo/blocked-element.svg b/src/browser/themes/shared/zen-icons/nucleo/blocked-element.svg new file mode 100644 index 000000000..c04dac0a2 --- /dev/null +++ b/src/browser/themes/shared/zen-icons/nucleo/blocked-element.svg @@ -0,0 +1,5 @@ +#filter dumbComments emptyLines substitution +# 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/. + diff --git a/src/browser/themes/shared/zen-icons/nucleo/bolt.svg b/src/browser/themes/shared/zen-icons/nucleo/bolt.svg new file mode 100644 index 000000000..34ba1aaa6 --- /dev/null +++ b/src/browser/themes/shared/zen-icons/nucleo/bolt.svg @@ -0,0 +1,31 @@ +#filter dumbComments emptyLines substitution +# 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/. + + + + + + + diff --git a/src/browser/themes/shared/zen-icons/nucleo/bookmark-hollow.svg b/src/browser/themes/shared/zen-icons/nucleo/bookmark-hollow.svg index ab5f27a89..4c25b7b84 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/bookmark-hollow.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/bookmark-hollow.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/bookmark-star-on-tray.svg b/src/browser/themes/shared/zen-icons/nucleo/bookmark-star-on-tray.svg index a8fd00b0a..166796c0f 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/bookmark-star-on-tray.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/bookmark-star-on-tray.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/bookmark.svg b/src/browser/themes/shared/zen-icons/nucleo/bookmark.svg index 278b3fa90..2fad2d376 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/bookmark.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/bookmark.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/boost.svg b/src/browser/themes/shared/zen-icons/nucleo/boost.svg new file mode 100644 index 000000000..4f9261071 --- /dev/null +++ b/src/browser/themes/shared/zen-icons/nucleo/boost.svg @@ -0,0 +1,28 @@ +#filter dumbComments emptyLines substitution +# 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/. + + + + + + + diff --git a/src/browser/themes/shared/zen-icons/nucleo/brackets-curly.svg b/src/browser/themes/shared/zen-icons/nucleo/brackets-curly.svg new file mode 100644 index 000000000..919447d58 --- /dev/null +++ b/src/browser/themes/shared/zen-icons/nucleo/brackets-curly.svg @@ -0,0 +1,31 @@ +#filter dumbComments emptyLines substitution +# 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/. + + + + + + + diff --git a/src/browser/themes/shared/zen-icons/nucleo/camera-blocked.svg b/src/browser/themes/shared/zen-icons/nucleo/camera-blocked.svg index acb82e38f..d0a58ee48 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/camera-blocked.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/camera-blocked.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/camera-fill.svg b/src/browser/themes/shared/zen-icons/nucleo/camera-fill.svg index 60b32e501..ac6efa506 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/camera-fill.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/camera-fill.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/camera.svg b/src/browser/themes/shared/zen-icons/nucleo/camera.svg index 232a55ab9..5a49a2f80 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/camera.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/camera.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/canvas-blocked.svg b/src/browser/themes/shared/zen-icons/nucleo/canvas-blocked.svg index 05a21e86f..f012129e3 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/canvas-blocked.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/canvas-blocked.svg @@ -2,8 +2,4 @@ # 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/. - - - - - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/canvas.svg b/src/browser/themes/shared/zen-icons/nucleo/canvas.svg index 6d2221328..3f22037f0 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/canvas.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/canvas.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/chevron.svg b/src/browser/themes/shared/zen-icons/nucleo/chevron.svg index 8f6544c06..97611dfce 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/chevron.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/chevron.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/close-filled-round.svg b/src/browser/themes/shared/zen-icons/nucleo/close-filled-round.svg new file mode 100644 index 000000000..364302563 --- /dev/null +++ b/src/browser/themes/shared/zen-icons/nucleo/close-filled-round.svg @@ -0,0 +1,28 @@ +#filter dumbComments emptyLines substitution +# 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/. + + + + + + + diff --git a/src/browser/themes/shared/zen-icons/nucleo/close.svg b/src/browser/themes/shared/zen-icons/nucleo/close.svg index c4d0be066..dc4791118 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/close.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/close.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/container-tab.svg b/src/browser/themes/shared/zen-icons/nucleo/container-tab.svg index c9b62fbd4..97ea3578e 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/container-tab.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/container-tab.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/cookies-fill.svg b/src/browser/themes/shared/zen-icons/nucleo/cookies-fill.svg index 384076dc0..3fb917399 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/cookies-fill.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/cookies-fill.svg @@ -1,5 +1,5 @@ -#filter dumbComments emptyLines substitution -# 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/. - \ No newline at end of file +#filter dumbComments emptyLines substitution +# 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/. + diff --git a/src/browser/themes/shared/zen-icons/nucleo/customize.svg b/src/browser/themes/shared/zen-icons/nucleo/customize.svg index 502159c61..1f8568882 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/customize.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/customize.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/dart-down.svg b/src/browser/themes/shared/zen-icons/nucleo/dart-down.svg index 5ab92a59b..b8b5f8536 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/dart-down.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/dart-down.svg @@ -1,5 +1,5 @@ -#filter dumbComments emptyLines substitution -# 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/. - \ No newline at end of file +#filter dumbComments emptyLines substitution +# 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/. + diff --git a/src/browser/themes/shared/zen-icons/nucleo/desktop-notification-blocked.svg b/src/browser/themes/shared/zen-icons/nucleo/desktop-notification-blocked.svg index 2f34e73e5..e9c4bdd9d 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/desktop-notification-blocked.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/desktop-notification-blocked.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/desktop-notification-fill.svg b/src/browser/themes/shared/zen-icons/nucleo/desktop-notification-fill.svg index c94bb578c..d18a8cb00 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/desktop-notification-fill.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/desktop-notification-fill.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/desktop-notification.svg b/src/browser/themes/shared/zen-icons/nucleo/desktop-notification.svg index fe56121b8..4b47aa250 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/desktop-notification.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/desktop-notification.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/developer.svg b/src/browser/themes/shared/zen-icons/nucleo/developer.svg index 868c698b1..21cc6e2aa 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/developer.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/developer.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/downloads.svg b/src/browser/themes/shared/zen-icons/nucleo/downloads.svg index e2460d778..87aca2942 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/downloads.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/downloads.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/drag-indicator.svg b/src/browser/themes/shared/zen-icons/nucleo/drag-indicator.svg index fd2bb2afc..845c93494 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/drag-indicator.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/drag-indicator.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/duplicate-tab.svg b/src/browser/themes/shared/zen-icons/nucleo/duplicate-tab.svg index 5093208d1..806d411db 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/duplicate-tab.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/duplicate-tab.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/edit-copy.svg b/src/browser/themes/shared/zen-icons/nucleo/edit-copy.svg index 26ad8ee42..c88606b77 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/edit-copy.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/edit-copy.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/edit-cut.svg b/src/browser/themes/shared/zen-icons/nucleo/edit-cut.svg index e2c6dae14..1c30ad18e 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/edit-cut.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/edit-cut.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/edit-delete.svg b/src/browser/themes/shared/zen-icons/nucleo/edit-delete.svg index 187829213..c106d2db3 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/edit-delete.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/edit-delete.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/edit-paste.svg b/src/browser/themes/shared/zen-icons/nucleo/edit-paste.svg index ead4f5a3b..119b4f9b9 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/edit-paste.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/edit-paste.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/edit-theme.svg b/src/browser/themes/shared/zen-icons/nucleo/edit-theme.svg index a6c84ddf0..a949ad413 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/edit-theme.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/edit-theme.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/essential-add.svg b/src/browser/themes/shared/zen-icons/nucleo/essential-add.svg index 78ceabf53..815e8cc7d 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/essential-add.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/essential-add.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/essential-remove.svg b/src/browser/themes/shared/zen-icons/nucleo/essential-remove.svg index 5c8422cb3..d43e32d98 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/essential-remove.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/essential-remove.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/expand-sidebar.svg b/src/browser/themes/shared/zen-icons/nucleo/expand-sidebar.svg index 22d7bb3c0..fd2f53f0a 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/expand-sidebar.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/expand-sidebar.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/extension-blocked.svg b/src/browser/themes/shared/zen-icons/nucleo/extension-blocked.svg index 42df02498..5a24a6931 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/extension-blocked.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/extension-blocked.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/extension-fill.svg b/src/browser/themes/shared/zen-icons/nucleo/extension-fill.svg index bd54d8d8c..562d4f6b4 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/extension-fill.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/extension-fill.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/extension.svg b/src/browser/themes/shared/zen-icons/nucleo/extension.svg index 15f5e9b91..a706c639f 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/extension.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/extension.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/eyedropper.svg b/src/browser/themes/shared/zen-icons/nucleo/eyedropper.svg new file mode 100644 index 000000000..70e99d2e0 --- /dev/null +++ b/src/browser/themes/shared/zen-icons/nucleo/eyedropper.svg @@ -0,0 +1,5 @@ +#filter dumbComments emptyLines substitution +# 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/. + \ No newline at end of file diff --git a/src/browser/themes/shared/zen-icons/nucleo/face-sun.svg b/src/browser/themes/shared/zen-icons/nucleo/face-sun.svg index 543e131a0..9e989c0d9 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/face-sun.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/face-sun.svg @@ -2,4 +2,4 @@ # 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/. - \ No newline at end of file + diff --git a/src/browser/themes/shared/zen-icons/nucleo/folder.svg b/src/browser/themes/shared/zen-icons/nucleo/folder.svg index 0469c4dd3..4359e15ec 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/folder.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/folder.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/forget.svg b/src/browser/themes/shared/zen-icons/nucleo/forget.svg index db749e8a0..534c190fc 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/forget.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/forget.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/fullscreen-exit.svg b/src/browser/themes/shared/zen-icons/nucleo/fullscreen-exit.svg index 157cda0ca..6df790b96 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/fullscreen-exit.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/fullscreen-exit.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/fullscreen.svg b/src/browser/themes/shared/zen-icons/nucleo/fullscreen.svg index e426c3e8e..197955151 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/fullscreen.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/fullscreen.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/geo-blocked.svg b/src/browser/themes/shared/zen-icons/nucleo/geo-blocked.svg index d6238d47d..e3cbb283b 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/geo-blocked.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/geo-blocked.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/geo-fill.svg b/src/browser/themes/shared/zen-icons/nucleo/geo-fill.svg index c97eaf78d..4ff0589f3 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/geo-fill.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/geo-fill.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/geo.svg b/src/browser/themes/shared/zen-icons/nucleo/geo.svg index 28fb9ea6e..40b42baab 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/geo.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/geo.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/hammer.svg b/src/browser/themes/shared/zen-icons/nucleo/hammer.svg new file mode 100644 index 000000000..ee101e2c1 --- /dev/null +++ b/src/browser/themes/shared/zen-icons/nucleo/hammer.svg @@ -0,0 +1,5 @@ +#filter dumbComments emptyLines substitution +# 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/. + \ No newline at end of file diff --git a/src/browser/themes/shared/zen-icons/nucleo/heart-circle-fill.svg b/src/browser/themes/shared/zen-icons/nucleo/heart-circle-fill.svg index e349c7d4c..10a98896a 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/heart-circle-fill.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/heart-circle-fill.svg @@ -2,4 +2,4 @@ # 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/. - \ No newline at end of file + diff --git a/src/browser/themes/shared/zen-icons/nucleo/help.svg b/src/browser/themes/shared/zen-icons/nucleo/help.svg index 81e870c99..e5fb256db 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/help.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/help.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/history.svg b/src/browser/themes/shared/zen-icons/nucleo/history.svg index a010a1c29..267f03bb1 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/history.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/history.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/home.svg b/src/browser/themes/shared/zen-icons/nucleo/home.svg index fc558b3c4..45e7017b2 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/home.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/home.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/info.svg b/src/browser/themes/shared/zen-icons/nucleo/info.svg index 86bac90dc..3a49e573b 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/info.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/info.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/lightbulb.svg b/src/browser/themes/shared/zen-icons/nucleo/lightbulb.svg new file mode 100644 index 000000000..253a55272 --- /dev/null +++ b/src/browser/themes/shared/zen-icons/nucleo/lightbulb.svg @@ -0,0 +1,31 @@ +#filter dumbComments emptyLines substitution +# 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/. + + + + + + + diff --git a/src/browser/themes/shared/zen-icons/nucleo/link.svg b/src/browser/themes/shared/zen-icons/nucleo/link.svg index 278af3385..cb5530255 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/link.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/link.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/mail.svg b/src/browser/themes/shared/zen-icons/nucleo/mail.svg index ff8c736d7..5d35fbab6 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/mail.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/mail.svg @@ -2,6 +2,4 @@ # 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/. - - - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/manage.svg b/src/browser/themes/shared/zen-icons/nucleo/manage.svg index 6fe478571..b280bfb2c 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/manage.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/manage.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/media-mute.svg b/src/browser/themes/shared/zen-icons/nucleo/media-mute.svg index d1a959d39..2919778f9 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/media-mute.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/media-mute.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/media-next.svg b/src/browser/themes/shared/zen-icons/nucleo/media-next.svg index 324696a19..fe3fecb6b 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/media-next.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/media-next.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/media-pause.svg b/src/browser/themes/shared/zen-icons/nucleo/media-pause.svg index f443c908a..6b0e84500 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/media-pause.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/media-pause.svg @@ -2,7 +2,4 @@ # 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/. - - - - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/media-play.svg b/src/browser/themes/shared/zen-icons/nucleo/media-play.svg index e64a4522b..ece7b0c35 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/media-play.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/media-play.svg @@ -2,6 +2,4 @@ # 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/. - - - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/media-previous.svg b/src/browser/themes/shared/zen-icons/nucleo/media-previous.svg index b742cf229..54ac78515 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/media-previous.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/media-previous.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/media-unmute.svg b/src/browser/themes/shared/zen-icons/nucleo/media-unmute.svg index c984a067d..828cc1e51 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/media-unmute.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/media-unmute.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/menu.svg b/src/browser/themes/shared/zen-icons/nucleo/menu.svg index f40520328..60a5fe231 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/menu.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/menu.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/microphone-blocked-fill.svg b/src/browser/themes/shared/zen-icons/nucleo/microphone-blocked-fill.svg index 113cb70e6..53b52dad0 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/microphone-blocked-fill.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/microphone-blocked-fill.svg @@ -2,4 +2,4 @@ # 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/. - \ No newline at end of file + diff --git a/src/browser/themes/shared/zen-icons/nucleo/microphone-blocked.svg b/src/browser/themes/shared/zen-icons/nucleo/microphone-blocked.svg index ed26bd3d8..8a6d9a187 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/microphone-blocked.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/microphone-blocked.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/microphone-fill.svg b/src/browser/themes/shared/zen-icons/nucleo/microphone-fill.svg index 78ad217ad..67c2e4f02 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/microphone-fill.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/microphone-fill.svg @@ -2,4 +2,4 @@ # 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/. - \ No newline at end of file + diff --git a/src/browser/themes/shared/zen-icons/nucleo/microphone.svg b/src/browser/themes/shared/zen-icons/nucleo/microphone.svg index 5e4feacdd..69b35004f 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/microphone.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/microphone.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/midi.svg b/src/browser/themes/shared/zen-icons/nucleo/midi.svg index 8d0ca17c5..ca5660a70 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/midi.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/midi.svg @@ -2,6 +2,4 @@ # 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/. - - - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/moon-stars.svg b/src/browser/themes/shared/zen-icons/nucleo/moon-stars.svg index f4739fbae..523323b51 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/moon-stars.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/moon-stars.svg @@ -2,4 +2,4 @@ # 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/. - \ No newline at end of file + diff --git a/src/browser/themes/shared/zen-icons/nucleo/new-tab-image.svg b/src/browser/themes/shared/zen-icons/nucleo/new-tab-image.svg index dda94088c..76ad06602 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/new-tab-image.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/new-tab-image.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/open.svg b/src/browser/themes/shared/zen-icons/nucleo/open.svg index 96bfa0966..ecb782117 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/open.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/open.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/page-portrait.svg b/src/browser/themes/shared/zen-icons/nucleo/page-portrait.svg index 8d7b773ec..4305f13f1 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/page-portrait.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/page-portrait.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/paintbrush-fill.svg b/src/browser/themes/shared/zen-icons/nucleo/paintbrush-fill.svg new file mode 100644 index 000000000..38f1b4c7e --- /dev/null +++ b/src/browser/themes/shared/zen-icons/nucleo/paintbrush-fill.svg @@ -0,0 +1,27 @@ +#filter dumbComments emptyLines substitution +# 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/. + + + + + + + diff --git a/src/browser/themes/shared/zen-icons/nucleo/paintbrush.svg b/src/browser/themes/shared/zen-icons/nucleo/paintbrush.svg new file mode 100644 index 000000000..0182e3414 --- /dev/null +++ b/src/browser/themes/shared/zen-icons/nucleo/paintbrush.svg @@ -0,0 +1,36 @@ +#filter dumbComments emptyLines substitution +# 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/. + + + + + + + + diff --git a/src/browser/themes/shared/zen-icons/nucleo/palette.svg b/src/browser/themes/shared/zen-icons/nucleo/palette.svg index 97219e0a1..86311017c 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/palette.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/palette.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/passwords.svg b/src/browser/themes/shared/zen-icons/nucleo/passwords.svg index 21782beb0..c5134f8d8 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/passwords.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/passwords.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/permissions-fill.svg b/src/browser/themes/shared/zen-icons/nucleo/permissions-fill.svg index 3d38645aa..8fea9f6dd 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/permissions-fill.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/permissions-fill.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/permissions.svg b/src/browser/themes/shared/zen-icons/nucleo/permissions.svg index b9d1adceb..670958d43 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/permissions.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/permissions.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/persistent-storage-blocked.svg b/src/browser/themes/shared/zen-icons/nucleo/persistent-storage-blocked.svg index 42df02498..5a24a6931 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/persistent-storage-blocked.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/persistent-storage-blocked.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/persistent-storage-fill.svg b/src/browser/themes/shared/zen-icons/nucleo/persistent-storage-fill.svg index 1c5e3099d..46bf4c7b1 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/persistent-storage-fill.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/persistent-storage-fill.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/persistent-storage.svg b/src/browser/themes/shared/zen-icons/nucleo/persistent-storage.svg index 66ad08c43..f67adadbc 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/persistent-storage.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/persistent-storage.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/pin.svg b/src/browser/themes/shared/zen-icons/nucleo/pin.svg index a00a00b17..c9b7956e2 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/pin.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/pin.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/plus.svg b/src/browser/themes/shared/zen-icons/nucleo/plus.svg index 0423e9ab9..73b9e148f 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/plus.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/plus.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/popup-fill.svg b/src/browser/themes/shared/zen-icons/nucleo/popup-fill.svg index 958aed3f2..613e790e1 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/popup-fill.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/popup-fill.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/popup.svg b/src/browser/themes/shared/zen-icons/nucleo/popup.svg index 66f513bfd..985630a5c 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/popup.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/popup.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/print.svg b/src/browser/themes/shared/zen-icons/nucleo/print.svg index 8e16cfa3b..675345d5f 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/print.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/print.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/private-window.svg b/src/browser/themes/shared/zen-icons/nucleo/private-window.svg index 5e4a34322..9941ac6c8 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/private-window.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/private-window.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/privateBrowsing.svg b/src/browser/themes/shared/zen-icons/nucleo/privateBrowsing.svg index 5798464ba..9941ac6c8 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/privateBrowsing.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/privateBrowsing.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/reader-mode.svg b/src/browser/themes/shared/zen-icons/nucleo/reader-mode.svg index be854bc27..bbfbf0532 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/reader-mode.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/reader-mode.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/reload.svg b/src/browser/themes/shared/zen-icons/nucleo/reload.svg index 876728dc2..b48cd26d4 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/reload.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/reload.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/save.svg b/src/browser/themes/shared/zen-icons/nucleo/save.svg index aa51f6b3a..eeb0be9e3 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/save.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/save.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/screen-blocked.svg b/src/browser/themes/shared/zen-icons/nucleo/screen-blocked.svg index c1372dfb8..1a8f858ad 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/screen-blocked.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/screen-blocked.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/screen.svg b/src/browser/themes/shared/zen-icons/nucleo/screen.svg index 53453e919..3eaad0394 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/screen.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/screen.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/screenshot.svg b/src/browser/themes/shared/zen-icons/nucleo/screenshot.svg index 2d1224924..ab59fe5fb 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/screenshot.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/screenshot.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/search-glass.svg b/src/browser/themes/shared/zen-icons/nucleo/search-glass.svg index 947e7e4e5..05181f39a 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/search-glass.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/search-glass.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/search-page.svg b/src/browser/themes/shared/zen-icons/nucleo/search-page.svg index 473dcaf09..b5afcb418 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/search-page.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/search-page.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/security-broken.svg b/src/browser/themes/shared/zen-icons/nucleo/security-broken.svg index 86396585e..704c7440a 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/security-broken.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/security-broken.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/security-warning.svg b/src/browser/themes/shared/zen-icons/nucleo/security-warning.svg index 56671347e..fb1c7a4a6 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/security-warning.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/security-warning.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/security.svg b/src/browser/themes/shared/zen-icons/nucleo/security.svg index be0b9f9b2..d0d8a5831 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/security.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/security.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/send-to-device.svg b/src/browser/themes/shared/zen-icons/nucleo/send-to-device.svg index 47d89aa11..47f4831fc 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/send-to-device.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/send-to-device.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/settings-fill.svg b/src/browser/themes/shared/zen-icons/nucleo/settings-fill.svg index 749d47eba..88595365e 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/settings-fill.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/settings-fill.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/settings.svg b/src/browser/themes/shared/zen-icons/nucleo/settings.svg index 00cc61192..9eac58174 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/settings.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/settings.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/share.svg b/src/browser/themes/shared/zen-icons/nucleo/share.svg index a4eb9e169..124307efb 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/share.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/share.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/sidebar-right.svg b/src/browser/themes/shared/zen-icons/nucleo/sidebar-right.svg index d25f84075..1256a3997 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/sidebar-right.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/sidebar-right.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/sidebars-right.svg b/src/browser/themes/shared/zen-icons/nucleo/sidebars-right.svg index c9531e507..77be9acda 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/sidebars-right.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/sidebars-right.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/sliders.svg b/src/browser/themes/shared/zen-icons/nucleo/sliders.svg new file mode 100644 index 000000000..8b2635892 --- /dev/null +++ b/src/browser/themes/shared/zen-icons/nucleo/sliders.svg @@ -0,0 +1,31 @@ +#filter dumbComments emptyLines substitution +# 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/. + + + + + + + diff --git a/src/browser/themes/shared/zen-icons/nucleo/sparkles.svg b/src/browser/themes/shared/zen-icons/nucleo/sparkles.svg index 63f0bf063..3fa7f008c 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/sparkles.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/sparkles.svg @@ -2,4 +2,4 @@ # 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/. - \ No newline at end of file + diff --git a/src/browser/themes/shared/zen-icons/nucleo/spell-check.svg b/src/browser/themes/shared/zen-icons/nucleo/spell-check.svg index f800613a4..b2bc07dac 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/spell-check.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/spell-check.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/split.svg b/src/browser/themes/shared/zen-icons/nucleo/split.svg index 732e7f86f..6843842ae 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/split.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/split.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/square-wand-sparkle.svg b/src/browser/themes/shared/zen-icons/nucleo/square-wand-sparkle.svg new file mode 100644 index 000000000..b9c94857b --- /dev/null +++ b/src/browser/themes/shared/zen-icons/nucleo/square-wand-sparkle.svg @@ -0,0 +1,36 @@ +#filter dumbComments emptyLines substitution +# 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/. + + + + + + + + diff --git a/src/browser/themes/shared/zen-icons/nucleo/tab-audio-blocked-small.svg b/src/browser/themes/shared/zen-icons/nucleo/tab-audio-blocked-small.svg index 75e6d0a76..1a157f720 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/tab-audio-blocked-small.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/tab-audio-blocked-small.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/tab-audio-muted-small.svg b/src/browser/themes/shared/zen-icons/nucleo/tab-audio-muted-small.svg index 9e67740ff..503b7cae5 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/tab-audio-muted-small.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/tab-audio-muted-small.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/tab-audio-playing-small.svg b/src/browser/themes/shared/zen-icons/nucleo/tab-audio-playing-small.svg index 8aea8022d..371f0b12e 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/tab-audio-playing-small.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/tab-audio-playing-small.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/tab.svg b/src/browser/themes/shared/zen-icons/nucleo/tab.svg index 482873893..154b67429 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/tab.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/tab.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/text-lowercase.svg b/src/browser/themes/shared/zen-icons/nucleo/text-lowercase.svg new file mode 100644 index 000000000..71204cbed --- /dev/null +++ b/src/browser/themes/shared/zen-icons/nucleo/text-lowercase.svg @@ -0,0 +1,31 @@ +#filter dumbComments emptyLines substitution +# 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/. + + + + + + + diff --git a/src/browser/themes/shared/zen-icons/nucleo/text-size.svg b/src/browser/themes/shared/zen-icons/nucleo/text-size.svg new file mode 100644 index 000000000..ed5e170e8 --- /dev/null +++ b/src/browser/themes/shared/zen-icons/nucleo/text-size.svg @@ -0,0 +1,31 @@ +#filter dumbComments emptyLines substitution +# 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/. + + + + + + + diff --git a/src/browser/themes/shared/zen-icons/nucleo/text-title-case.svg b/src/browser/themes/shared/zen-icons/nucleo/text-title-case.svg new file mode 100644 index 000000000..48f1779b5 --- /dev/null +++ b/src/browser/themes/shared/zen-icons/nucleo/text-title-case.svg @@ -0,0 +1,31 @@ +#filter dumbComments emptyLines substitution +# 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/. + + + + + + + diff --git a/src/browser/themes/shared/zen-icons/nucleo/text-uppercase.svg b/src/browser/themes/shared/zen-icons/nucleo/text-uppercase.svg new file mode 100644 index 000000000..a257f56ac --- /dev/null +++ b/src/browser/themes/shared/zen-icons/nucleo/text-uppercase.svg @@ -0,0 +1,31 @@ +#filter dumbComments emptyLines substitution +# 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/. + + + + + + + diff --git a/src/browser/themes/shared/zen-icons/nucleo/tool-profiler.svg b/src/browser/themes/shared/zen-icons/nucleo/tool-profiler.svg index 721953849..741f98727 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/tool-profiler.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/tool-profiler.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/tracking-protection-fill.svg b/src/browser/themes/shared/zen-icons/nucleo/tracking-protection-fill.svg index 8a9ef1f1f..49f889a99 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/tracking-protection-fill.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/tracking-protection-fill.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/tracking-protection.svg b/src/browser/themes/shared/zen-icons/nucleo/tracking-protection.svg index 55aa2ee5d..7e9ee6b6a 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/tracking-protection.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/tracking-protection.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/translations.svg b/src/browser/themes/shared/zen-icons/nucleo/translations.svg index 5aecf2311..f32d848ca 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/translations.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/translations.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/trash.svg b/src/browser/themes/shared/zen-icons/nucleo/trash.svg index 83346f596..97fa8c232 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/trash.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/trash.svg @@ -2,4 +2,4 @@ # 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/. - \ No newline at end of file + diff --git a/src/browser/themes/shared/zen-icons/nucleo/unpin.svg b/src/browser/themes/shared/zen-icons/nucleo/unpin.svg index 6ef1ef8d2..aec72314c 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/unpin.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/unpin.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/video-blocked-fill.svg b/src/browser/themes/shared/zen-icons/nucleo/video-blocked-fill.svg index b1e8fa101..741e8b98a 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/video-blocked-fill.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/video-blocked-fill.svg @@ -2,4 +2,4 @@ # 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/. - \ No newline at end of file + diff --git a/src/browser/themes/shared/zen-icons/nucleo/video-fill.svg b/src/browser/themes/shared/zen-icons/nucleo/video-fill.svg index f168e7a1d..8262338e7 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/video-fill.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/video-fill.svg @@ -2,4 +2,4 @@ # 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/. - \ No newline at end of file + diff --git a/src/browser/themes/shared/zen-icons/nucleo/wand-sparkle.svg b/src/browser/themes/shared/zen-icons/nucleo/wand-sparkle.svg new file mode 100644 index 000000000..ba9ccadf3 --- /dev/null +++ b/src/browser/themes/shared/zen-icons/nucleo/wand-sparkle.svg @@ -0,0 +1,36 @@ +#filter dumbComments emptyLines substitution +# 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/. + + + + + + + + diff --git a/src/browser/themes/shared/zen-icons/nucleo/window.svg b/src/browser/themes/shared/zen-icons/nucleo/window.svg index dda94088c..7f00409e9 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/window.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/window.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/xr-blocked.svg b/src/browser/themes/shared/zen-icons/nucleo/xr-blocked.svg index bfd4c9f2e..2340b2eab 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/xr-blocked.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/xr-blocked.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/xr-fill.svg b/src/browser/themes/shared/zen-icons/nucleo/xr-fill.svg index bd387cd56..4719b3174 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/xr-fill.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/xr-fill.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/xr.svg b/src/browser/themes/shared/zen-icons/nucleo/xr.svg index e3e34c28c..5a38091ce 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/xr.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/xr.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/shared/zen-icons/nucleo/zoom-out.svg b/src/browser/themes/shared/zen-icons/nucleo/zoom-out.svg index 6ef1ef8d2..aec72314c 100644 --- a/src/browser/themes/shared/zen-icons/nucleo/zoom-out.svg +++ b/src/browser/themes/shared/zen-icons/nucleo/zoom-out.svg @@ -2,4 +2,4 @@ # 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/. - + diff --git a/src/browser/themes/windows/browser-css.patch b/src/browser/themes/windows/browser-css.patch index 30b5297d9..3388df380 100644 --- a/src/browser/themes/windows/browser-css.patch +++ b/src/browser/themes/windows/browser-css.patch @@ -1,5 +1,5 @@ diff --git a/browser/themes/windows/browser.css b/browser/themes/windows/browser.css -index a0ffa8cac5a4d7ec220e7ee5dd7d70dba81e9026..766d69d1a2c41f3de7de9b05ef096fcccaa5c2a0 100644 +index eca9fcfb453c6cf41c0a24f2775ad7bcf4678153..b7b609138a12695ff90663a707a7d985f9efb81a 100644 --- a/browser/themes/windows/browser.css +++ b/browser/themes/windows/browser.css @@ -41,7 +41,6 @@ diff --git a/src/build/moz-build.patch b/src/build/moz-build.patch index 7ccf4194c..79fe1cc03 100644 --- a/src/build/moz-build.patch +++ b/src/build/moz-build.patch @@ -1,8 +1,8 @@ diff --git a/build/moz.build b/build/moz.build -index 720fb1235f418fb063e88d8bbcd2eb444e84870b..ed831a4d35e006bc03aa514a6c00968a4dd19624 100644 +index 238caa7801292926db83b0ad4791b0400d30cba5..87bd7c541cdce8562ce6999e0b59fb5698348699 100644 --- a/build/moz.build +++ b/build/moz.build -@@ -93,7 +93,7 @@ if CONFIG["MOZ_APP_BASENAME"]: +@@ -91,7 +91,7 @@ if CONFIG["MOZ_APP_BASENAME"]: if CONFIG[var]: appini_defines[var] = True diff --git a/src/build/moz-configure/lto-pgo-configure.patch b/src/build/moz-configure/lto-pgo-configure.patch index e1c1f77ff..f91a32571 100644 --- a/src/build/moz-configure/lto-pgo-configure.patch +++ b/src/build/moz-configure/lto-pgo-configure.patch @@ -1,8 +1,16 @@ diff --git a/build/moz.configure/lto-pgo.configure b/build/moz.configure/lto-pgo.configure -index 165541b79842672d8bac48722220532f3913556d..6781b84b9c06ff26a41a0e7d8be5a958600fda62 100644 +index df2b0bf0081edfdc2c7bccaefc780d2b0c6555ff..53a3b55de7650799e3730372e72ad7b4bb2eea03 100644 --- a/build/moz.configure/lto-pgo.configure +++ b/build/moz.configure/lto-pgo.configure -@@ -258,7 +258,6 @@ option( +@@ -234,6 +234,7 @@ def moz_pgo_rust(pgo, profile_use, profile_generate, c_compiler): + if c_compiler.type == "gcc": + die("Cannot use cross-language PGO with GCC.") + ++ log.info("Enabling cross-language PGO for Rust") + return True + + +@@ -256,7 +257,6 @@ option( help="Indicate that ld64 is free of symbol aliasing bugs", ) diff --git a/src/build/moz-configure/update-programs-configure.patch b/src/build/moz-configure/update-programs-configure.patch new file mode 100644 index 000000000..3241275fd --- /dev/null +++ b/src/build/moz-configure/update-programs-configure.patch @@ -0,0 +1,13 @@ +diff --git a/build/moz.configure/update-programs.configure b/build/moz.configure/update-programs.configure +index 4edcccf4c49749e6d80ee75c1e43cfa32a555933..9c7c69c97740e5c02766f913d40bd4f059940d3a 100644 +--- a/build/moz.configure/update-programs.configure ++++ b/build/moz.configure/update-programs.configure +@@ -71,7 +71,7 @@ def mac_prod_requirements_string(identifier): + f'identifier "{identifier}" and anchor apple generic and ' + "certificate 1[field.1.2.840.113635.100.6.2.6] and " + "certificate leaf[field.1.2.840.113635.100.6.1.13] and " +- 'certificate leaf[subject.OU] = "43AQ936H96"' ++ 'certificate leaf[subject.OU] = "9V5K9TP787"' + ) + + diff --git a/src/build/pgo/profileserver-py.patch b/src/build/pgo/profileserver-py.patch index 637c8bfcc..876307200 100644 --- a/src/build/pgo/profileserver-py.patch +++ b/src/build/pgo/profileserver-py.patch @@ -1,5 +1,5 @@ diff --git a/build/pgo/profileserver.py b/build/pgo/profileserver.py -index faf15719a0537d7c5fc0a62afb93fa4a61605bf7..68d114fd6651b7c210aa1a58e6fcf4fa749f53b1 100755 +index b11171e3ef14b1f2c4a279b10d83eb4dadde45ca..fdf7c2d5ad6352bdd8f2ea20ee80c0927daa2ba2 100755 --- a/build/pgo/profileserver.py +++ b/build/pgo/profileserver.py @@ -17,7 +17,13 @@ from mozprofile import FirefoxProfile, Preferences diff --git a/src/docshell/base/BrowsingContext-h.patch b/src/docshell/base/BrowsingContext-h.patch new file mode 100644 index 000000000..afe06a4ea --- /dev/null +++ b/src/docshell/base/BrowsingContext-h.patch @@ -0,0 +1,31 @@ +diff --git a/docshell/base/BrowsingContext.h b/docshell/base/BrowsingContext.h +index dc64d5c13628cb7393705ebf96110705b045471f..497c2e646e2bde4d7732fa28efe7496264d58fa0 100644 +--- a/docshell/base/BrowsingContext.h ++++ b/docshell/base/BrowsingContext.h +@@ -263,6 +263,8 @@ struct EmbedderColorSchemes { + FIELD(HistoryEntryCount, uint32_t) \ + FIELD(HasRestoreData, bool) \ + FIELD(SessionStoreEpoch, uint32_t) \ ++ FIELD(ZenBoostsData, nscolor) \ ++ FIELD(IsZenBoostsInverted, bool) \ + /* Whether we can execute scripts in this BrowsingContext. Has no effect \ + * unless scripts are also allowed in the parent WindowContext. */ \ + FIELD(AllowJavascript, bool) \ +@@ -674,6 +676,8 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache { + + bool FullscreenAllowed() const; + ++ auto ZenBoostsData() const { return GetZenBoostsData(); } ++ auto IsZenBoostsInverted() const { return GetIsZenBoostsInverted(); } + float FullZoom() const { return GetFullZoom(); } + float TextZoom() const { return GetTextZoom(); } + +@@ -1274,6 +1278,8 @@ class BrowsingContext : public nsILoadContext, public nsWrapperCache { + } + + void DidSet(FieldIndex, uint32_t aOldValue); ++ void DidSet(FieldIndex, nscolor aOldValue); ++ void DidSet(FieldIndex, bool aOldValue); + + using CanSetResult = syncedcontext::CanSetResult; + diff --git a/src/docshell/base/nsAboutRedirector-cpp.patch b/src/docshell/base/nsAboutRedirector-cpp.patch index 4012d8890..592e9b0c6 100644 --- a/src/docshell/base/nsAboutRedirector-cpp.patch +++ b/src/docshell/base/nsAboutRedirector-cpp.patch @@ -1,8 +1,8 @@ diff --git a/docshell/base/nsAboutRedirector.cpp b/docshell/base/nsAboutRedirector.cpp -index 6789b3d1d8ba3477c30da8b8dff54a659206c37f..a13b07368c8ea1b7510d55ac9e3dcae50f226158 100644 +index 76f707e5f1e8e20a6acec9a445781377c052ba59..4f8f14deace21ff56157b77c15bcb93e1792e354 100644 --- a/docshell/base/nsAboutRedirector.cpp +++ b/docshell/base/nsAboutRedirector.cpp -@@ -117,7 +117,7 @@ static const RedirEntry kRedirMap[] = { +@@ -115,7 +115,7 @@ static const RedirEntry kRedirMap[] = { {"crashes", "chrome://global/content/crashes.html", nsIAboutModule::IS_SECURE_CHROME_UI}, #endif diff --git a/src/dom/base/Document-cpp.patch b/src/dom/base/Document-cpp.patch index 665d7a948..5e4514e95 100644 --- a/src/dom/base/Document-cpp.patch +++ b/src/dom/base/Document-cpp.patch @@ -1,8 +1,8 @@ diff --git a/dom/base/Document.cpp b/dom/base/Document.cpp -index b837e66d4fd5b6a96ad3d9c35f8e50e911cd168b..c35a395da59fc30d70b1e05b94db41b7136db0de 100644 +index 178078c084aad367f0e5d50e50f981ded24ae43e..e551dc7677e43c8ab179babdcbc6dd05aa962727 100644 --- a/dom/base/Document.cpp +++ b/dom/base/Document.cpp -@@ -464,6 +464,7 @@ +@@ -463,6 +463,7 @@ #include "prtime.h" #include "prtypes.h" #include "xpcpublic.h" @@ -10,7 +10,7 @@ index b837e66d4fd5b6a96ad3d9c35f8e50e911cd168b..c35a395da59fc30d70b1e05b94db41b7 // clang-format off #include "mozilla/Encoding.h" -@@ -3317,6 +3318,10 @@ void Document::FillStyleSetUserAndUASheets() { +@@ -3279,6 +3280,10 @@ void Document::FillStyleSetUserAndUASheets() { for (StyleSheet* sheet : *sheetService->UserStyleSheets()) { styleSet.AppendStyleSheet(*sheet); } diff --git a/src/dom/chrome-webidl/BrowsingContext-webidl.patch b/src/dom/chrome-webidl/BrowsingContext-webidl.patch new file mode 100644 index 000000000..91987c922 --- /dev/null +++ b/src/dom/chrome-webidl/BrowsingContext-webidl.patch @@ -0,0 +1,14 @@ +diff --git a/dom/chrome-webidl/BrowsingContext.webidl b/dom/chrome-webidl/BrowsingContext.webidl +index 3bb17157c1dba1ca75ad9cfee2c21fcafb2dd0ee..a43f8aa1e4c808418f1cc7ccc69ced12c9ebfef0 100644 +--- a/dom/chrome-webidl/BrowsingContext.webidl ++++ b/dom/chrome-webidl/BrowsingContext.webidl +@@ -169,6 +169,9 @@ interface BrowsingContext { + + [SetterThrows] attribute float textZoom; + ++ [SetterThrows] attribute long zenBoostsData; ++ [SetterThrows] attribute boolean isZenBoostsInverted; ++ + // Override the dots-per-CSS-pixel scaling factor in this BrowsingContext + // and all of its descendants. May only be set on the top BC, and should + // only be set from the parent process. diff --git a/src/dom/chrome-webidl/MediaController-webidl.patch b/src/dom/chrome-webidl/MediaController-webidl.patch index d7c868534..cd41a9cb5 100644 --- a/src/dom/chrome-webidl/MediaController-webidl.patch +++ b/src/dom/chrome-webidl/MediaController-webidl.patch @@ -1,9 +1,9 @@ diff --git a/dom/chrome-webidl/MediaController.webidl b/dom/chrome-webidl/MediaController.webidl -index 20f416d1c3b41798e0f90bbac5db40ed2a4ab000..1c5d893f9166a3aa7bc7802bb0d1207d169033ee 100644 +index eea39e9f70b1ba216dc5131bc3aa8b6ef46ca5ea..fd24ad430aeb64c4d622b3765af359579a903c40 100644 --- a/dom/chrome-webidl/MediaController.webidl +++ b/dom/chrome-webidl/MediaController.webidl -@@ -20,6 +20,12 @@ enum MediaControlKey { - "stop", +@@ -23,6 +23,12 @@ enum MediaControlKey { + "setvolume", }; +dictionary MediaControllerPositionState { @@ -15,9 +15,9 @@ index 20f416d1c3b41798e0f90bbac5db40ed2a4ab000..1c5d893f9166a3aa7bc7802bb0d1207d /** * MediaController is used to control media playback for a tab, and each tab * would only have one media controller, which can be accessed from the -@@ -32,10 +38,14 @@ interface MediaController : EventTarget { - readonly attribute boolean isAudible; +@@ -36,10 +42,14 @@ interface MediaController : EventTarget { readonly attribute boolean isPlaying; + readonly attribute boolean isAnyMediaBeingControlled; readonly attribute MediaSessionPlaybackState playbackState; + readonly attribute boolean isBeingUsedInPIPModeOrFullscreen; diff --git a/src/dom/media/mediacontrol/MediaController-cpp.patch b/src/dom/media/mediacontrol/MediaController-cpp.patch index 937897290..4f9cf1b75 100644 --- a/src/dom/media/mediacontrol/MediaController-cpp.patch +++ b/src/dom/media/mediacontrol/MediaController-cpp.patch @@ -1,8 +1,8 @@ diff --git a/dom/media/mediacontrol/MediaController.cpp b/dom/media/mediacontrol/MediaController.cpp -index 51369c404f10d6c83e83c4284efc6c19a08961a0..3d5c2e48187d59fba0dde348894da4dc03a66eee 100644 +index f28ec5e406bfeefd624c9538878d423e99b0df6a..afb60df8ddec86c0c83584b8325add8d1c373d0e 100644 --- a/dom/media/mediacontrol/MediaController.cpp +++ b/dom/media/mediacontrol/MediaController.cpp -@@ -51,6 +51,25 @@ void MediaController::GetSupportedKeys( +@@ -49,6 +49,25 @@ void MediaController::GetSupportedKeys( } } @@ -28,7 +28,7 @@ index 51369c404f10d6c83e83c4284efc6c19a08961a0..3d5c2e48187d59fba0dde348894da4dc void MediaController::GetMetadata(MediaMetadataInit& aMetadata, ErrorResult& aRv) { if (!IsActive() || mShutdown) { -@@ -412,6 +431,7 @@ void MediaController::SetIsInPictureInPictureMode( +@@ -435,6 +454,7 @@ void MediaController::SetIsInPictureInPictureMode( ForceToBecomeMainControllerIfNeeded(); UpdateDeactivationTimerIfNeeded(); mPictureInPictureModeChangedEvent.Notify(mIsInPictureInPictureMode); diff --git a/src/dom/media/mediacontrol/MediaController-h.patch b/src/dom/media/mediacontrol/MediaController-h.patch index d86b5d3f4..dffc7d160 100644 --- a/src/dom/media/mediacontrol/MediaController-h.patch +++ b/src/dom/media/mediacontrol/MediaController-h.patch @@ -1,8 +1,8 @@ diff --git a/dom/media/mediacontrol/MediaController.h b/dom/media/mediacontrol/MediaController.h -index 327ea6201304b3d26a2b4f0267b1c926c73ee9e4..ad14bb2572c29df4897672e14afc185b9b517da4 100644 +index ea05df396bcaa8f5e236105585c0a177496c8bd9..e064f279383004bdbdb030ecb23289a893c11a8c 100644 --- a/dom/media/mediacontrol/MediaController.h +++ b/dom/media/mediacontrol/MediaController.h -@@ -90,6 +90,7 @@ class MediaController final : public DOMEventTargetHelper, +@@ -91,6 +91,7 @@ class MediaController final : public DOMEventTargetHelper, JS::Handle aGivenProto) override; void GetSupportedKeys(nsTArray& aRetVal) const; void GetMetadata(MediaMetadataInit& aMetadata, ErrorResult& aRv); diff --git a/src/dom/media/mediaelement/HTMLMediaElement-cpp.patch b/src/dom/media/mediaelement/HTMLMediaElement-cpp.patch index 0df960077..5d50a707f 100644 --- a/src/dom/media/mediaelement/HTMLMediaElement-cpp.patch +++ b/src/dom/media/mediaelement/HTMLMediaElement-cpp.patch @@ -1,8 +1,8 @@ diff --git a/dom/media/mediaelement/HTMLMediaElement.cpp b/dom/media/mediaelement/HTMLMediaElement.cpp -index 624375b514cb0b101ae24bb5906d0097d4b335db..27c91b01db611a0cd75b20907310d2fa4c8a1b47 100644 +index df8c9441463e8c08e3f3d21a194df850bd418b67..3fd83aff3a7cc2ff6b9c889c5126befa99a91436 100644 --- a/dom/media/mediaelement/HTMLMediaElement.cpp +++ b/dom/media/mediaelement/HTMLMediaElement.cpp -@@ -451,6 +451,7 @@ class HTMLMediaElement::MediaControlKeyListener final +@@ -449,6 +449,7 @@ class HTMLMediaElement::MediaControlKeyListener final // audible state. Therefore, in that case we would noitfy the audible state // when media starts playing. if (mState == MediaPlaybackState::ePlayed) { @@ -10,7 +10,7 @@ index 624375b514cb0b101ae24bb5906d0097d4b335db..27c91b01db611a0cd75b20907310d2fa NotifyAudibleStateChanged(mIsOwnerAudible ? MediaAudibleState::eAudible : MediaAudibleState::eInaudible); -@@ -7392,6 +7393,9 @@ void HTMLMediaElement::FireTimeUpdate(TimeupdateType aType) { +@@ -7444,6 +7445,9 @@ void HTMLMediaElement::FireTimeUpdate(TimeupdateType aType) { QueueTask(std::move(runner)); mQueueTimeUpdateRunnerTime = TimeStamp::Now(); mLastCurrentTime = CurrentTime(); diff --git a/src/eslint-file-globals-config-mjs.patch b/src/eslint-file-globals-config-mjs.patch index 6764cf430..8754ddbe9 100644 --- a/src/eslint-file-globals-config-mjs.patch +++ b/src/eslint-file-globals-config-mjs.patch @@ -1,5 +1,5 @@ diff --git a/eslint-file-globals.config.mjs b/eslint-file-globals.config.mjs -index 4f07a1bf0e0845fc955601167d011cbac8d73801..d1bda7e60d4d807226153a98198150005b69abe0 100644 +index c17a96adb86eff46c8dafc5cbef83eb10b5fbeeb..4d00012889a846fabfd9172fb05459318e4b4746 100644 --- a/eslint-file-globals.config.mjs +++ b/eslint-file-globals.config.mjs @@ -22,6 +22,7 @@ @@ -10,7 +10,7 @@ index 4f07a1bf0e0845fc955601167d011cbac8d73801..d1bda7e60d4d807226153a9819815000 export default [ { -@@ -553,4 +554,9 @@ export default [ +@@ -549,4 +550,9 @@ export default [ ], languageOptions: { globals: globals.worker }, }, diff --git a/src/eslint-ignores-config-mjs.patch b/src/eslint-ignores-config-mjs.patch index cd4d8a22c..54ed7ffe3 100644 --- a/src/eslint-ignores-config-mjs.patch +++ b/src/eslint-ignores-config-mjs.patch @@ -1,11 +1,11 @@ diff --git a/eslint-ignores.config.mjs b/eslint-ignores.config.mjs -index 0cfd7e02ad58c331f48f1ba8e1588777e1ce2595..888674b5ed2b68dbe77eb177ba0947f94ed57c80 100644 +index bfb30a0a35af081a2496a05fd78d9efea15ee37f..04fee66309192b3e38986ae09e77ec7a4e1ad83b 100644 --- a/eslint-ignores.config.mjs +++ b/eslint-ignores.config.mjs -@@ -312,4 +312,8 @@ export default [ - // Test files for circular import in modules. - "dom/base/test/jsmodules/import_circular.mjs", +@@ -314,4 +314,8 @@ export default [ "dom/base/test/jsmodules/import_circular_1.mjs", + "dom/base/test/jsmodules/importmaps/multiple/import_circular.mjs", + "dom/base/test/jsmodules/importmaps/multiple/import_circular_1.mjs", + + "zen/common/emojis/ZenEmojisData.min.mjs", + "zen/tests/**", diff --git a/src/eslint-test-paths-config-mjs.patch b/src/eslint-test-paths-config-mjs.patch index 34fea2e39..9c26e3f16 100644 --- a/src/eslint-test-paths-config-mjs.patch +++ b/src/eslint-test-paths-config-mjs.patch @@ -1,5 +1,5 @@ diff --git a/eslint-test-paths.config.mjs b/eslint-test-paths.config.mjs -index 53d97521a676d04212abb0263cb166da06c889e0..fa8c261de7a8663a369fb41671476d314722c025 100644 +index 7a87fec812ea8ddea5c10dca53af560e7dc6385b..2b943b031ebd30e06e2aa1043aed13c11bd10db1 100644 --- a/eslint-test-paths.config.mjs +++ b/eslint-test-paths.config.mjs @@ -218,6 +218,7 @@ const extraBrowserTestPaths = [ diff --git a/src/external-patches/firefox/allow_backdrop_to_work_on_transparency.patch b/src/external-patches/firefox/allow_backdrop_to_work_on_transparency.patch new file mode 100644 index 000000000..befed26bc --- /dev/null +++ b/src/external-patches/firefox/allow_backdrop_to_work_on_transparency.patch @@ -0,0 +1,172 @@ +diff --git a/gfx/webrender_bindings/WebRenderAPI.cpp b/gfx/webrender_bindings/WebRenderAPI.cpp +--- a/gfx/webrender_bindings/WebRenderAPI.cpp ++++ b/gfx/webrender_bindings/WebRenderAPI.cpp +@@ -298,11 +298,13 @@ + panic_on_gl_error, picTileWidth, picTileHeight, + gfx::gfxVars::WebRenderRequiresHardwareDriver(), + StaticPrefs::gfx_webrender_low_quality_pinch_zoom_AtStartup(), + StaticPrefs::gfx_webrender_max_shared_surface_size_AtStartup(), + StaticPrefs::gfx_webrender_enable_subpixel_aa_AtStartup(), +- compositor->ShouldUseLayerCompositor())) { ++ compositor->ShouldUseLayerCompositor(), ++ StaticPrefs:: ++ gfx_webrender_opaque_backdrop_fallback_AtStartup())) { + // wr_window_new puts a message into gfxCriticalNote if it returns + // false + MOZ_ASSERT(errorMessage); + error.AssignASCII(errorMessage); + wr_api_free_error_msg(errorMessage); +diff --git a/gfx/webrender_bindings/src/bindings.rs b/gfx/webrender_bindings/src/bindings.rs +--- a/gfx/webrender_bindings/src/bindings.rs ++++ b/gfx/webrender_bindings/src/bindings.rs +@@ -1998,10 +1998,11 @@ + reject_software_rasterizer: bool, + low_quality_pinch_zoom: bool, + max_shared_surface_size: i32, + enable_subpixel_aa: bool, + use_layer_compositor: bool, ++ opaque_backdrop_fallback: bool, + ) -> bool { + assert!(unsafe { is_in_render_thread() }); + + // Ensure the WR profiler callbacks are hooked up to the Gecko profiler. + set_profiler_hooks(Some(&PROFILER_HOOKS)); +@@ -2164,10 +2165,11 @@ + texture_cache_config, + reject_software_rasterizer, + low_quality_pinch_zoom, + max_shared_surface_size, + enable_dithering, ++ opaque_backdrop_fallback, + precise_linear_gradients, + ..Default::default() + }; + + let window_size = DeviceIntSize::new(window_width, window_height); +diff --git a/gfx/wr/webrender/src/device/gl.rs b/gfx/wr/webrender/src/device/gl.rs +--- a/gfx/wr/webrender/src/device/gl.rs ++++ b/gfx/wr/webrender/src/device/gl.rs +@@ -3982,10 +3982,14 @@ + + pub fn disable_color_write(&self) { + self.gl.color_mask(false, false, false, false); + } + ++ pub fn set_color_mask(&self, r: bool, g: bool, b: bool, a: bool) { ++ self.gl.color_mask(r, g, b, a); ++ } ++ + pub fn set_blend(&mut self, enable: bool) { + if enable { + self.gl.enable(gl::BLEND); + } else { + self.gl.disable(gl::BLEND); +diff --git a/gfx/wr/webrender/src/renderer/init.rs b/gfx/wr/webrender/src/renderer/init.rs +--- a/gfx/wr/webrender/src/renderer/init.rs ++++ b/gfx/wr/webrender/src/renderer/init.rs +@@ -204,10 +204,12 @@ + pub low_quality_pinch_zoom: bool, + pub max_shared_surface_size: i32, + /// If true, open a debug socket to listen for remote debugger. + /// Relies on `debugger` cargo feature being enabled. + pub enable_debugger: bool, ++ /// See explanation of `gfx.webrender.opaque-backdrop-fallback`. ++ pub opaque_backdrop_fallback: bool, + + /// Use a more precise method for sampling gradients. + pub precise_linear_gradients: bool, + } + +@@ -277,10 +279,11 @@ + enable_instancing: true, + reject_software_rasterizer: false, + low_quality_pinch_zoom: false, + max_shared_surface_size: 2048, + enable_debugger: true, ++ opaque_backdrop_fallback: false, + precise_linear_gradients: false, + } + } + } + +@@ -802,10 +805,11 @@ + allocated_native_surfaces: FastHashSet::default(), + debug_overlay_state: DebugOverlayState::new(), + buffer_damage_tracker: BufferDamageTracker::default(), + max_primitive_instance_count, + enable_instancing: options.enable_instancing, ++ opaque_backdrop_fallback: options.opaque_backdrop_fallback, + consecutive_oom_frames: 0, + target_frame_publish_id: None, + pending_result_msg: None, + layer_compositor_frame_state_in_prev_frame: None, + external_composite_debug_items: Vec::new(), +diff --git a/gfx/wr/webrender/src/renderer/mod.rs b/gfx/wr/webrender/src/renderer/mod.rs +--- a/gfx/wr/webrender/src/renderer/mod.rs ++++ b/gfx/wr/webrender/src/renderer/mod.rs +@@ -867,10 +867,12 @@ + buffer_damage_tracker: BufferDamageTracker, + + max_primitive_instance_count: usize, + enable_instancing: bool, + ++ opaque_backdrop_fallback: bool, ++ + /// Count consecutive oom frames to detectif we are stuck unable to render + /// in a loop. + consecutive_oom_frames: u32, + + /// update() defers processing of ResultMsg, if frame_publish_id of +@@ -2787,18 +2789,29 @@ + let read_target = ReadTarget::from_texture(cache_texture); + + // Should always be drawing to picture cache tiles or off-screen surface! + debug_assert!(!draw_target.is_default()); + let device_to_framebuffer = Scale::new(1i32); ++ let dest_fb_rect = dest * device_to_framebuffer; + + self.device.blit_render_target( + read_target, + src * device_to_framebuffer, + draw_target, +- dest * device_to_framebuffer, ++ dest_fb_rect, + TextureFilter::Linear, + ); ++ ++ if self.opaque_backdrop_fallback { ++ self.device.set_color_mask(false, false, false, true); ++ self.device.clear_target( ++ Some([0.0, 0.0, 0.0, 1.0]), ++ None, ++ Some(dest_fb_rect), ++ ); ++ self.device.set_color_mask(true, true, true, true); ++ } + } + } + } + + fn draw_picture_cache_target( +diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml +--- a/modules/libpref/init/StaticPrefList.yaml ++++ b/modules/libpref/init/StaticPrefList.yaml +@@ -8439,10 +8439,17 @@ + #else + value: false + #endif + mirror: once + ++# Make backdrop-filter treat its captured backdrop as if it had been ++# composited over an opaque-black background. (See bug 2036640) ++- name: gfx.webrender.opaque-backdrop-fallback ++ type: bool ++ value: true ++ mirror: once ++ + # Disable wait of GPU execution completion + - name: gfx.webrender.wait-gpu-finished.disabled + type: bool + value: false + mirror: once + diff --git a/src/external-patches/firefox/bug_2013682_allow_stacking_contexts_to_be_promoted.patch b/src/external-patches/firefox/bug_2013682_allow_stacking_contexts_to_be_promoted.patch new file mode 100644 index 000000000..adc2d5903 --- /dev/null +++ b/src/external-patches/firefox/bug_2013682_allow_stacking_contexts_to_be_promoted.patch @@ -0,0 +1,104 @@ +diff --git a/gfx/wr/webrender/src/clip.rs b/gfx/wr/webrender/src/clip.rs +--- a/gfx/wr/webrender/src/clip.rs ++++ b/gfx/wr/webrender/src/clip.rs +@@ -97,11 +97,11 @@ + use api::units::*; + use crate::image_tiling::{self, Repetition}; + use crate::border::{ensure_no_corner_overlap, BorderRadiusAu}; + use crate::box_shadow::{BLUR_SAMPLE_SCALE, BoxShadowClipSource, BoxShadowCacheKey}; + use crate::renderer::GpuBufferBuilderF; +-use crate::spatial_tree::{SpatialTree, SpatialNodeIndex}; ++use crate::spatial_tree::{SceneSpatialTree, SpatialTree, SpatialNodeIndex}; + use crate::ellipse::Ellipse; + use crate::gpu_types::{BoxShadowStretchMode}; + use crate::intern; + use crate::internal_types::{FastHashMap, FastHashSet, LayoutPrimitiveInfo}; + use crate::prim_store::{VisibleMaskImageTile}; +@@ -745,10 +745,56 @@ + ) -> bool { + let clip_chain_index = self.clip_chain_map[&clip_chain_id]; + self.has_complex_clips_impl(clip_chain_index, interners) + } + ++ /// Check if all complex clips in a clip chain are fixed-position rounded ++ /// rectangles (in Clip mode). When true, the intermediate surface for a ++ /// root-level stacking context can be skipped because the clips will be ++ /// promoted to compositor clips on the tile cache slices. ++ pub fn clip_chain_complex_clips_are_promotable( ++ &self, ++ clip_chain_id: ClipChainId, ++ interners: &Interners, ++ spatial_tree: &SceneSpatialTree, ++ ) -> bool { ++ let clip_chain_index = self.clip_chain_map[&clip_chain_id]; ++ self.complex_clips_are_promotable_impl(clip_chain_index, interners, spatial_tree) ++ } ++ ++ fn complex_clips_are_promotable_impl( ++ &self, ++ clip_chain_index: usize, ++ interners: &Interners, ++ spatial_tree: &SceneSpatialTree, ++ ) -> bool { ++ let mut index = clip_chain_index; ++ ++ loop { ++ let clip_chain = &self.clip_chains[index]; ++ ++ for clip_entry in &clip_chain.clips { ++ let clip_info = &interners.clip[clip_entry.handle]; ++ ++ match clip_info.key.kind { ++ ClipItemKeyKind::Rectangle(_, ClipMode::Clip) => {} ++ ClipItemKeyKind::RoundedRectangle(_, _, ClipMode::Clip) => { ++ if !spatial_tree.is_root_coord_system(clip_entry.spatial_node_index) { ++ return false; ++ } ++ } ++ _ => return false, ++ } ++ } ++ ++ match clip_chain.parent { ++ Some(parent) => index = parent, ++ None => return true, ++ } ++ } ++ } ++ + /// Check if a clip-node has complex (non-rectangular) clips + pub fn clip_node_has_complex_clips( + &self, + clip_node_id: ClipNodeId, + interners: &Interners, +diff --git a/gfx/wr/webrender/src/scene_building.rs b/gfx/wr/webrender/src/scene_building.rs +--- a/gfx/wr/webrender/src/scene_building.rs ++++ b/gfx/wr/webrender/src/scene_building.rs +@@ -2369,11 +2369,26 @@ + + // If this stacking context has any complex clips, we need to draw it + // to an off-screen surface. + if let Some(clip_chain_id) = clip_chain_id { + if self.clip_tree_builder.clip_chain_has_complex_clips(clip_chain_id, &self.interners) { +- blit_reason |= BlitReason::CLIP; ++ // At the root level, if all complex clips are fixed-position ++ // rounded rectangles, we can skip the intermediate surface. ++ // The clips will be promoted to compositor clips on the tile ++ // cache slices, which applies them once to the composited ++ // surface — equivalent to the intermediate surface approach. ++ // This allows tile cache barriers to fire normally, enabling ++ // proper picture caching with multiple slices. ++ if !self.sc_stack.is_empty() || ++ !self.clip_tree_builder.clip_chain_complex_clips_are_promotable( ++ clip_chain_id, ++ &self.interners, ++ &self.spatial_tree, ++ ) ++ { ++ blit_reason |= BlitReason::CLIP; ++ } + } + } + + // Check if we know this stacking context is redundant (doesn't need a surface) + // The check for blend-container redundancy is more involved so it's handled below. diff --git a/src/external-patches/firefox/fix_macos_crash_on_shutdown_firefox_149.patch b/src/external-patches/firefox/fix_macos_crash_on_shutdown_firefox_149.patch deleted file mode 100644 index c76f06760..000000000 --- a/src/external-patches/firefox/fix_macos_crash_on_shutdown_firefox_149.patch +++ /dev/null @@ -1,177 +0,0 @@ -diff --git a/toolkit/components/asyncshutdown/AsyncShutdown.sys.mjs b/toolkit/components/asyncshutdown/AsyncShutdown.sys.mjs ---- a/toolkit/components/asyncshutdown/AsyncShutdown.sys.mjs -+++ b/toolkit/components/asyncshutdown/AsyncShutdown.sys.mjs -@@ -490,10 +490,27 @@ - if (accepted) { - return () => spinner.observe(); - } - return undefined; - }, -+ -+ /** -+ * Reset the phase after a call to _trigger(). -+ * For testing purposes only. -+ */ -+ get _reset() { -+ let accepted = Services.prefs.getBoolPref( -+ "toolkit.asyncshutdown.testing", -+ false -+ ); -+ if (accepted) { -+ return () => { -+ spinner = new Spinner(topic); -+ }; -+ } -+ return undefined; -+ }, - }); - gPhases.set(topic, phase); - return phase; - } - -diff --git a/toolkit/components/contentrelevancy/ContentRelevancyManager.sys.mjs b/toolkit/components/contentrelevancy/ContentRelevancyManager.sys.mjs ---- a/toolkit/components/contentrelevancy/ContentRelevancyManager.sys.mjs -+++ b/toolkit/components/contentrelevancy/ContentRelevancyManager.sys.mjs -@@ -104,18 +104,14 @@ - this._nimbusUpdateCallback = this.#onNimbusUpdate.bind(this); - // This will handle both Nimbus updates and pref changes. - lazy.NimbusFeatures.contentRelevancy.onUpdate(this._nimbusUpdateCallback); - this.#initialized = true; - -- if ( -- Services.startup.isInOrBeyondShutdownPhase( -- Ci.nsIAppStartup.SHUTDOWN_PHASE_APPSHUTDOWNCONFIRMED -- ) -- ) { -+ if (lazy.AsyncShutdown.profileChangeTeardown.isClosed) { - // Corner case, where we're already in the shutdown phase while being constructed. In this - // case, uninitialize immediately to deregister callback handlers -- // (#https://bugzilla.mozilla.org/show_bug.cgi?id=1990569#c11) -+ // (https://bugzilla.mozilla.org/show_bug.cgi?id=1990569#c11) - this.uninit(); - } else { - // If we're not in the above corner case, then register a shutdown blocker to uninitialize. - // Interrupt sooner prior to the `profile-before-change` phase to allow - // all the in-progress IOs to exit. -diff --git a/toolkit/components/contentrelevancy/tests/xpcshell/test_ContentRelevancyManager.js b/toolkit/components/contentrelevancy/tests/xpcshell/test_ContentRelevancyManager.js ---- a/toolkit/components/contentrelevancy/tests/xpcshell/test_ContentRelevancyManager.js -+++ b/toolkit/components/contentrelevancy/tests/xpcshell/test_ContentRelevancyManager.js -@@ -149,10 +149,39 @@ - await TestUtils.waitForCondition( - () => ContentRelevancyManager.interrupt.calledOnce, - "The interrupt shutdown blocker should be called" - ); - -+ AsyncShutdown.profileChangeTeardown._reset(); -+ Services.prefs.clearUserPref("toolkit.asyncshutdown.testing"); -+ Services.prefs.clearUserPref(PREF_CONTENT_RELEVANCY_ENABLED); -+ gSandbox.restore(); -+}); -+ -+add_task(async function test_dont_register_blocker_if_in_shutdown() { -+ // Test a corner case: the ContentRelevancyManager is initialized during shutdown. -+ // -+ // In this case it shouldn't register a shutdown blocker, because it's too late to do that. -+ // Instead, it should just immediately uninitialize itself. -+ // -+ // See https://bugzilla.mozilla.org/show_bug.cgi?id=1990569 -+ ContentRelevancyManager.uninit(); -+ Services.prefs.setBoolPref(PREF_CONTENT_RELEVANCY_ENABLED, true); -+ await TestUtils.waitForTick(); -+ -+ gSandbox.spy(ContentRelevancyManager, "interrupt"); -+ -+ // Simulate shutdown. -+ Services.prefs.setBoolPref("toolkit.asyncshutdown.testing", true); -+ AsyncShutdown.profileChangeTeardown._trigger(); -+ ContentRelevancyManager.init(); -+ Assert.ok( -+ !ContentRelevancyManager.initialized, -+ "ContentRelevancyManager should have uninitialized itself" -+ ); -+ -+ AsyncShutdown.profileChangeTeardown._reset(); - Services.prefs.clearUserPref("toolkit.asyncshutdown.testing"); - Services.prefs.clearUserPref(PREF_CONTENT_RELEVANCY_ENABLED); - gSandbox.restore(); - }); - -diff --git a/toolkit/modules/AppServicesTracing.sys.mjs b/toolkit/modules/AppServicesTracing.sys.mjs ---- a/toolkit/modules/AppServicesTracing.sys.mjs -+++ b/toolkit/modules/AppServicesTracing.sys.mjs -@@ -9,10 +9,11 @@ - */ - - const lazy = {}; - - ChromeUtils.defineESModuleGetters(lazy, { -+ AsyncShutdown: "resource://gre/modules/AsyncShutdown.sys.mjs", - // eslint-disable-next-line mozilla/use-console-createInstance - Log: "resource://gre/modules/Log.sys.mjs", - }); - - import { -@@ -118,20 +119,33 @@ - } - } - - /** A singleton uniffi callback interface. */ - class TracingEventHandler extends EventSink { -- static OBSERVER_NAME = "xpcom-will-shutdown"; -- - constructor() { - super(); - // Map targets to CallbackLists - this.targetCallbackLists = new Map(); - // CallbackList for callbacks registered with registerMinLevelEventSink - this.minLevelCallbackList = new CallbackList(); - -- Services.obs.addObserver(this, TracingEventHandler.OBSERVER_NAME); -+ // Choose `profileBeforeChange` to call `#close()` and deregister our callbacks. -+ // -+ // Most other components will shutdown during the `profileChangeTeardown` phase, since that's -+ // the last opportunity to write to the profile directory. By choosing the next one, we ensure -+ // we can forward any logging that happens when those components shutdown. -+ if (lazy.AsyncShutdown.profileBeforeChange.isClosed) { -+ // Corner case, where we're already in the shutdown phase while being constructed. In this -+ // case, uninitialize immediately. -+ this.#close(); -+ } else { -+ // If we're not in the above corner case, then register a shutdown blocker to uninitialize. -+ lazy.AsyncShutdown.profileBeforeChange.addBlocker( -+ "TracingEventHandler: deregister callbacks", -+ () => this.#close() -+ ); -+ } - } - - register(target, level, callback) { - if (this.targetCallbackLists === null) { - lazy.console.trace( -@@ -223,19 +237,17 @@ - targetList.processEvent(event); - } - this.minLevelCallbackList.processEvent(event); - } - -- observe(_aSubject, aTopic, _aData) { -- if (aTopic == TracingEventHandler.OBSERVER_NAME) { -- for (let target of this.targetCallbackLists.keys()) { -- unregisterEventSink(target); -- } -- unregisterMinLevelEventSink(); -- this.targetCallbackLists = null; -- this.minLevelCallbackList = null; -+ #close() { -+ for (let target of this.targetCallbackLists.keys()) { -+ unregisterEventSink(target); - } -+ unregisterMinLevelEventSink(); -+ this.targetCallbackLists = null; -+ this.minLevelCallbackList = null; - } - } - - // the singleton. - let tracingEventHandler = new TracingEventHandler(); - diff --git a/src/external-patches/firefox/gh-12979_clip_dirty_rect_to_device_size.patch b/src/external-patches/firefox/gh-12979_clip_dirty_rect_to_device_size.patch new file mode 100644 index 000000000..4cd381da1 --- /dev/null +++ b/src/external-patches/firefox/gh-12979_clip_dirty_rect_to_device_size.patch @@ -0,0 +1,21 @@ +diff --git a/gfx/wr/webrender/src/renderer/composite.rs b/gfx/wr/webrender/src/renderer/composite.rs +--- a/gfx/wr/webrender/src/renderer/composite.rs ++++ b/gfx/wr/webrender/src/renderer/composite.rs +@@ -974,12 +974,15 @@ + .iter() + .chain(self.layer_compositor_frame_state_in_prev_frame.as_ref().unwrap().rects_without_id.iter()) { + combined_dirty_rect = combined_dirty_rect.union(&rect); + } + ++ let device_rect = DeviceRect::from_size(device_size.to_f32()); ++ let clipped_dirty_rect = combined_dirty_rect.intersection_unchecked(&device_rect); ++ + partial_present_mode = Some(PartialPresentMode::Single { +- dirty_rect: combined_dirty_rect, ++ dirty_rect: clipped_dirty_rect, + }); + } else { + partial_present_mode = None; + } + + diff --git a/src/external-patches/firefox/native_macos_popovers.patch b/src/external-patches/firefox/native_macos_popovers.patch index c1cee907a..2b57225df 100644 --- a/src/external-patches/firefox/native_macos_popovers.patch +++ b/src/external-patches/firefox/native_macos_popovers.patch @@ -1,7 +1,7 @@ diff --git a/browser/base/content/main-popupset.inc.xhtml b/browser/base/content/main-popupset.inc.xhtml --- a/browser/base/content/main-popupset.inc.xhtml +++ b/browser/base/content/main-popupset.inc.xhtml -@@ -196,10 +196,11 @@ +@@ -192,10 +192,11 @@ -@@ -207,10 +208,11 @@ +@@ -203,10 +204,11 @@ -@@ -560,10 +562,11 @@ +@@ -610,10 +612,11 @@ type="arrow" orient="vertical" noautofocus="true" @@ -40,7 +40,7 @@ diff --git a/browser/base/content/main-popupset.inc.xhtml b/browser/base/content diff --git a/browser/components/asrouter/modules/FeatureCallout.sys.mjs b/browser/components/asrouter/modules/FeatureCallout.sys.mjs --- a/browser/components/asrouter/modules/FeatureCallout.sys.mjs +++ b/browser/components/asrouter/modules/FeatureCallout.sys.mjs -@@ -1046,10 +1046,11 @@ +@@ -1054,10 +1054,11 @@ noautofocus="true" flip="slide" type="arrow" @@ -70,22 +70,22 @@ diff --git a/browser/components/customizableui/content/panelUI.inc.xhtml b/brows diff --git a/dom/xul/XULPopupElement.cpp b/dom/xul/XULPopupElement.cpp --- a/dom/xul/XULPopupElement.cpp +++ b/dom/xul/XULPopupElement.cpp -@@ -82,10 +82,14 @@ +@@ -80,10 +80,14 @@ + } void XULPopupElement::OpenPopupAtScreen(int32_t aXPos, int32_t aYPos, bool aIsContextMenu, Event* aTriggerEvent) { - nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + // TODO(cheff): At nsCocoaWindow::Show but we check for ShouldShowAsNSPopover + // to determine whether to use a native popover or not. This should sort of + // "replicate" that logic here, but it's a bit of a hacky way. + SetAttr(kNameSpaceID_None, nsGkAtoms::nonnativepopover, u"true"_ns, true); + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); if (pm) { pm->ShowPopupAtScreen(this, aXPos, aYPos, aIsContextMenu, aTriggerEvent); } } - -@@ -94,10 +98,14 @@ +@@ -93,10 +97,14 @@ int32_t aWidth, int32_t aHeight, bool aIsContextMenu, bool aAttributesOverride, @@ -103,7 +103,7 @@ diff --git a/dom/xul/XULPopupElement.cpp b/dom/xul/XULPopupElement.cpp diff --git a/layout/xul/nsMenuPopupFrame.h b/layout/xul/nsMenuPopupFrame.h --- a/layout/xul/nsMenuPopupFrame.h +++ b/layout/xul/nsMenuPopupFrame.h -@@ -530,18 +530,10 @@ +@@ -528,18 +528,10 @@ // Move the popup to the position specified in its |left| and |top| // attributes. @@ -122,7 +122,7 @@ diff --git a/layout/xul/nsMenuPopupFrame.h b/layout/xul/nsMenuPopupFrame.h public: /** * Return whether the popup direction should be RTL. -@@ -550,10 +542,18 @@ +@@ -548,10 +540,18 @@ * * Return whether the popup direction should be RTL. */ @@ -144,7 +144,7 @@ diff --git a/layout/xul/nsMenuPopupFrame.h b/layout/xul/nsMenuPopupFrame.h diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml --- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml -@@ -19477,10 +19477,19 @@ +@@ -19672,10 +19672,19 @@ value: true mirror: always @@ -167,7 +167,7 @@ diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/Sta diff --git a/toolkit/themes/shared/global-shared.css b/toolkit/themes/shared/global-shared.css --- a/toolkit/themes/shared/global-shared.css +++ b/toolkit/themes/shared/global-shared.css -@@ -85,10 +85,22 @@ +@@ -72,10 +72,22 @@ --menuitem-border-radius: var(--arrowpanel-menuitem-border-radius); --menuitem-padding: var(--arrowpanel-menuitem-padding); --menuitem-margin: var(--arrowpanel-menuitem-margin); @@ -188,7 +188,7 @@ diff --git a/toolkit/themes/shared/global-shared.css b/toolkit/themes/shared/glo /* Lightweight theme roots */ :root[lwtheme] { - .browser-toolbox-background, + body, toolbar, diff --git a/widget/cocoa/nsCocoaWindow.h b/widget/cocoa/nsCocoaWindow.h --- a/widget/cocoa/nsCocoaWindow.h @@ -251,7 +251,7 @@ diff --git a/widget/cocoa/nsCocoaWindow.h b/widget/cocoa/nsCocoaWindow.h diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm --- a/widget/cocoa/nsCocoaWindow.mm +++ b/widget/cocoa/nsCocoaWindow.mm -@@ -3,10 +3,13 @@ +@@ -4,10 +4,13 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsCocoaWindow.h" @@ -265,7 +265,7 @@ diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm #include "nsIDOMWindowUtils.h" #include "nsILocalFileMac.h" #include "CocoaCompositorWidget.h" -@@ -4973,10 +4976,15 @@ +@@ -5031,10 +5034,15 @@ if (mWindowType == WindowType::Popup) { SetPopupWindowLevel(); mWindow.backgroundColor = NSColor.clearColor; @@ -281,7 +281,7 @@ diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm // the active space. Does not work with multiple displays. See // NeedsRecreateToReshow() for multi-display with multi-space workaround. mWindow.collectionBehavior = mWindow.collectionBehavior | -@@ -5178,10 +5186,57 @@ +@@ -5236,10 +5244,57 @@ NS_OBJC_END_TRY_IGNORE_BLOCK; } @@ -339,54 +339,58 @@ diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm NS_OBJC_BEGIN_TRY_IGNORE_BLOCK; if (!mWindow) { -@@ -5242,10 +5297,54 @@ +@@ -5300,10 +5355,58 @@ mWindow.contentView.needsDisplay = YES; if (!nativeParentWindow || mPopupLevel != PopupLevel::Parent) { [mWindow orderFront:nil]; } NS_OBJC_END_TRY_IGNORE_BLOCK; -+ if (ShouldShowAsNSPopover()) { ++ if (ShouldShowAsNSPopover() && nativeParentWindow) { + nsMenuPopupFrame* popupFrame = GetPopupFrame(); -+ if (nativeParentWindow) { -+ NSRectEdge preferredEdge = -+ AlignmentPositionToNSRectEdge(popupFrame->GetAlignmentPosition()); -+ nsRect anchorRectAppUnits = popupFrame->GetUntransformedAnchorRect(); -+ nsPresContext* pc = popupFrame->PresContext(); -+ int32_t appUnitsPerDevPixel = pc->AppUnitsPerDevPixel(); -+ mozilla::DesktopToLayoutDeviceScale desktopToLayoutScale = -+ pc->DeviceContext()->GetDesktopToDeviceScale(); -+ mozilla::DesktopIntRect popupAnchorRectScaled = -+ mozilla::DesktopIntRect::RoundOut( -+ mozilla::LayoutDeviceRect::FromAppUnits(anchorRectAppUnits, -+ appUnitsPerDevPixel) / -+ desktopToLayoutScale); -+ // Taking the now correctly scaled anchor rect and turning it into a -+ // gecko rect this accounts for the y-axis inversion that cocoa needs, -+ // as the origin is in the bottom left. This rect is in screen space -+ NSRect cocoaScreenRect = -+ nsCocoaUtils::GeckoRectToCocoaRect(popupAnchorRectScaled); -+ // We take the screen space rect and convert it to window space -+ // coordinates, as NSPopover requires the coordinates to be in view -+ // space and inside the view. If the coordinates are outside our view, -+ // the popover will fail silently -+ NSRect windowRect = -+ [nativeParentWindow convertRectFromScreen:cocoaScreenRect]; -+ NSView* parentView = [nativeParentWindow contentView]; -+ // We take the window space rect and convert it to view space for the -+ // specific parent view -+ NSRect positioningRect = [parentView convertRect:windowRect -+ fromView:nil]; -+ BOOL shouldHideAnchor = NO; -+ auto& element = popupFrame->PopupElement(); -+ if (element.GetBoolAttr(nsGkAtoms::hidepopovertail)) { -+ shouldHideAnchor = YES; -+ } -+ [(PopupWindow*)mWindow showPopoverRelativeToRect:positioningRect -+ ofView:parentView -+ preferredEdge:preferredEdge -+ hiddenAnchor:shouldHideAnchor]; -+ SyncPopoverBounds([(PopupWindow*)mWindow popover], popupFrame); ++ NSRectEdge preferredEdge = ++ AlignmentPositionToNSRectEdge(popupFrame->GetAlignmentPosition()); ++ nsRect anchorRectAppUnits = popupFrame->GetUntransformedAnchorRect(); ++ nsPresContext* pc = popupFrame->PresContext(); ++ int32_t appUnitsPerDevPixel = pc->AppUnitsPerDevPixel(); ++ mozilla::DesktopToLayoutDeviceScale desktopToLayoutScale = ++ pc->DeviceContext()->GetDesktopToDeviceScale(); ++ mozilla::DesktopIntRect popupAnchorRectScaled = ++ mozilla::DesktopIntRect::RoundOut( ++ mozilla::LayoutDeviceRect::FromAppUnits(anchorRectAppUnits, ++ appUnitsPerDevPixel) / ++ desktopToLayoutScale); ++ // Taking the now correctly scaled anchor rect and turning it into a ++ // gecko rect this accounts for the y-axis inversion that cocoa needs, ++ // as the origin is in the bottom left. This rect is in screen space ++ NSRect cocoaScreenRect = ++ nsCocoaUtils::GeckoRectToCocoaRect(popupAnchorRectScaled); ++ // We take the screen space rect and convert it to window space ++ // coordinates, as NSPopover requires the coordinates to be in view ++ // space and inside the view. If the coordinates are outside our view, ++ // the popover will fail silently ++ NSRect windowRect = ++ [nativeParentWindow convertRectFromScreen:cocoaScreenRect]; ++ NSView* parentView = [nativeParentWindow contentView]; ++ // We take the window space rect and convert it to view space for the ++ // specific parent view ++ NSRect positioningRect = [parentView convertRect:windowRect ++ fromView:nil]; ++ BOOL shouldHideAnchor = NO; ++ auto& element = popupFrame->PopupElement(); ++ if (element.GetBoolAttr(nsGkAtoms::hidepopovertail)) { ++ shouldHideAnchor = YES; + } ++ [(PopupWindow*)mWindow showPopoverRelativeToRect:positioningRect ++ ofView:parentView ++ preferredEdge:preferredEdge ++ hiddenAnchor:shouldHideAnchor]; ++ SyncPopoverBounds([(PopupWindow*)mWindow popover], popupFrame); ++ ++ ++ ++ ++ // Exit early here since the popover is now shown. ++ + return; + } // If our popup window is a non-native context menu, tell the OS (and @@ -394,12 +398,13 @@ diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm // close other programs' context menus when ours open. if ([mWindow isKindOfClass:[PopupWindow class]] && [(PopupWindow*)mWindow isContextMenu]) { -@@ -5316,10 +5415,15 @@ +@@ -5373,11 +5476,15 @@ + // unhook it here before ordering it out. When you order out the child // of a window it hides the parent window. if (mWindowType == WindowType::Popup && nativeParentWindow) { [nativeParentWindow removeChildWindow:mWindow]; } - +- + // Handle NSPopover hiding or traditional window hiding + if ([mWindow isKindOfClass:[PopupWindow class]] && + [(PopupWindow*)mWindow usePopover]) { @@ -410,7 +415,7 @@ diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm // other programs) that a menu has closed. if ([mWindow isKindOfClass:[PopupWindow class]] && [(PopupWindow*)mWindow isContextMenu]) { -@@ -5366,10 +5470,28 @@ +@@ -5424,10 +5531,28 @@ return false; } return nsIWidget::ShouldUseOffMainThreadCompositing(); @@ -439,12 +444,12 @@ diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm return mWindow.isOpaque ? TransparencyMode::Opaque : TransparencyMode::Transparent; -@@ -6328,10 +6450,20 @@ +@@ -6378,10 +6503,19 @@ + // We ignore aRepaint -- we have to call display:YES, otherwise the // title bar doesn't immediately get repainted and is displayed in // the wrong place, leading to a visual jump. [mWindow setFrame:newFrame display:YES]; - + if (ShouldUseNSPopover() && [(PopupWindow*)mWindow usePopover]) { + [(PopupWindow*)mWindow updatePopoverContent]; + // A popover won't resize by setting the frame @@ -454,18 +459,18 @@ diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm + [[(PopupWindow*)mWindow popover] setContentSize:contentSize]; + SyncPopoverBounds([(PopupWindow*)mWindow popover], GetPopupFrame()); + } -+ + NS_OBJC_END_TRY_IGNORE_BLOCK; } void nsCocoaWindow::Resize(const DesktopRect& aRect, bool aRepaint) { - DoResize(aRect.x, aRect.y, aRect.width, aRect.height, aRepaint, false); -@@ -8314,17 +8446,26 @@ +@@ -8393,18 +8527,31 @@ + backing:bufferingType defer:deferCreation]; if (!self) { return nil; } - +- + mPopover = nil; + mPopoverViewController = nil; + mUsePopover = NO; @@ -477,6 +482,11 @@ diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm } +- (void)dealloc { ++ if (mPopover) { ++ ChildViewMouseTracker::OnDestroyWindow( ++ mPopover.contentViewController.view.window); ++ } ++ + [mPopover release]; + [mPopoverViewController release]; + [super dealloc]; @@ -487,7 +497,7 @@ diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm // Return 0 in order to match what the system does for sheet windows and // _NSPopoverWindows. - (CGFloat)_backdropBleedAmount { -@@ -8378,10 +8519,122 @@ +@@ -8460,10 +8607,122 @@ - (void)setIsContextMenu:(BOOL)flag { mIsContextMenu = flag; @@ -613,7 +623,7 @@ diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm diff --git a/widget/nsIWidget.h b/widget/nsIWidget.h --- a/widget/nsIWidget.h +++ b/widget/nsIWidget.h -@@ -843,10 +843,15 @@ +@@ -829,10 +829,15 @@ virtual void SuppressAnimation(bool aSuppress) {} /** Sets windows-specific mica backdrop on this widget. */ @@ -644,7 +654,7 @@ diff --git a/xpcom/ds/StaticAtoms.py b/xpcom/ds/StaticAtoms.py Atom("highest", "highest"), Atom("horizontal", "horizontal"), Atom("hover", "hover"), -@@ -757,10 +758,11 @@ +@@ -759,10 +760,11 @@ Atom("nohref", "nohref"), Atom("noinitialselection", "noinitialselection"), Atom("nomodule", "nomodule"), diff --git a/src/external-patches/firefox/session_store_use_size_hint/D247145.patch b/src/external-patches/firefox/session_store_use_size_hint/D247145.patch new file mode 100644 index 000000000..c52eb4b92 --- /dev/null +++ b/src/external-patches/firefox/session_store_use_size_hint/D247145.patch @@ -0,0 +1,61 @@ +diff --git a/js/public/JSON.h b/js/public/JSON.h +--- a/js/public/JSON.h ++++ b/js/public/JSON.h +@@ -24,16 +24,23 @@ + * writing stringified data by exactly one call of |callback|, passing |data| as + * argument. + * + * In cases where JSON.stringify would return undefined, this function calls + * |callback| with the string "null". ++ * ++ * If a length hint is passed, space will be reserved for at least that many ++ * characters. + */ + extern JS_PUBLIC_API bool JS_Stringify(JSContext* cx, + JS::MutableHandle value, + JS::Handle replacer, + JS::Handle space, + JSONWriteCallback callback, void* data); ++extern JS_PUBLIC_API bool JS_StringifyWithLengthHint( ++ JSContext* cx, JS::MutableHandle value, ++ JS::Handle replacer, JS::Handle space, ++ JSONWriteCallback callback, void* data, size_t lengthHint); + + namespace JS { + + /** + * An API akin to JS_Stringify but with the goal of not having observable +diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp +--- a/js/src/jsapi.cpp ++++ b/js/src/jsapi.cpp +@@ -3663,17 +3663,29 @@ + } + + JS_PUBLIC_API bool JS_Stringify(JSContext* cx, MutableHandleValue vp, + HandleObject replacer, HandleValue space, + JSONWriteCallback callback, void* data) { ++ return JS_StringifyWithLengthHint(cx, vp, replacer, space, callback, data, 0); ++} ++ ++JS_PUBLIC_API bool JS_StringifyWithLengthHint(JSContext* cx, ++ MutableHandleValue vp, ++ HandleObject replacer, ++ HandleValue space, ++ JSONWriteCallback callback, ++ void* data, size_t lengthHint) { + AssertHeapIsIdle(); + CHECK_THREAD(cx); + cx->check(replacer, space); + StringBuilder sb(cx); + if (!sb.ensureTwoByteChars()) { + return false; + } ++ if (lengthHint && !sb.reserve(lengthHint)) { ++ return false; ++ } + if (!Stringify(cx, vp, replacer, space, sb, StringifyBehavior::Normal)) { + return false; + } + if (sb.empty() && !sb.append(cx->names().null)) { + return false; + diff --git a/src/external-patches/firefox/session_store_use_size_hint/D298708.patch b/src/external-patches/firefox/session_store_use_size_hint/D298708.patch new file mode 100644 index 000000000..965ab02c5 --- /dev/null +++ b/src/external-patches/firefox/session_store_use_size_hint/D298708.patch @@ -0,0 +1,280 @@ +diff --git a/browser/components/sessionstore/SessionWriter.sys.mjs b/browser/components/sessionstore/SessionWriter.sys.mjs +--- a/browser/components/sessionstore/SessionWriter.sys.mjs ++++ b/browser/components/sessionstore/SessionWriter.sys.mjs +@@ -80,10 +80,14 @@ + return await SessionWriterInternal.wipe(); + } finally { + unlock(); + } + }, ++ ++ get _jsonLengthHint() { ++ return SessionWriterInternal._lastJsonLength; ++ }, + }; + + const SessionWriterInternal = { + // Path to the files used by the SessionWriter + Paths: null, +@@ -104,10 +108,14 @@ + /** + * Number of old upgrade backups that are being kept + */ + maxUpgradeBackups: null, + ++ // Estimated JSON string length from the previous write, used to pre-size ++ // the serialization buffer and avoid incremental reallocations. ++ _lastJsonLength: 0, ++ + /** + * Initialize (or reinitialize) the writer. + * + * @param {string} origin Which of sessionstore.js or its backups + * was used. One of the `STATE_*` constants defined above. +@@ -201,48 +209,60 @@ + } + } + + let startWriteMs = Date.now(); + let fileStat; ++ // Add 5% headroom to the hint so small growth between saves doesn't ++ // cause reallocs. The compressed-size-based estimate already has ++ // sufficient margin from the 4x multiplier. ++ let jsonLengthHint = Math.ceil(this._lastJsonLength * 1.05); ++ ++ let uncompressedBytes; + + if (options.isFinalWrite) { + // We are shutting down. At this stage, we know that + // $Paths.clean is either absent or corrupted. If it was + // originally present and valid, it has been moved to + // $Paths.cleanBackup a long time ago. We can therefore write + // with the guarantees that we erase no important data. +- await IOUtils.writeJSON(this.Paths.clean, state, { ++ uncompressedBytes = await IOUtils.writeJSON(this.Paths.clean, state, { + tmpPath: this.Paths.clean + ".tmp", + compress: true, ++ jsonLengthHint, + }); + fileStat = await IOUtils.stat(this.Paths.clean); + } else if (this.state == STATE_RECOVERY) { + // At this stage, either $Paths.recovery was written >= 15 + // seconds ago during this session or we have just started + // from $Paths.recovery left from the previous session. Either + // way, $Paths.recovery is good. We can move $Path.backup to + // $Path.recoveryBackup without erasing a good file with a bad + // file. +- await IOUtils.writeJSON(this.Paths.recovery, state, { ++ uncompressedBytes = await IOUtils.writeJSON(this.Paths.recovery, state, { + tmpPath: this.Paths.recovery + ".tmp", + backupFile: this.Paths.recoveryBackup, + compress: true, ++ jsonLengthHint, + }); + fileStat = await IOUtils.stat(this.Paths.recovery); + } else { + // In other cases, either $Path.recovery is not necessary, or + // it doesn't exist or it has been corrupted. Regardless, + // don't backup $Path.recovery. +- await IOUtils.writeJSON(this.Paths.recovery, state, { ++ uncompressedBytes = await IOUtils.writeJSON(this.Paths.recovery, state, { + tmpPath: this.Paths.recovery + ".tmp", + compress: true, ++ jsonLengthHint, + }); + fileStat = await IOUtils.stat(this.Paths.recovery); + } + + telemetry.writeFileMs = Date.now() - startWriteMs; + telemetry.fileSizeBytes = fileStat.size; ++ // Use the actual pre-compression size from this write as the hint ++ // for the next write's buffer allocation. ++ this._lastJsonLength = uncompressedBytes; + lazy.sessionStoreLogger.debug( + `SessionWriter.write wrote ${telemetry.fileSizeBytes} bytes in ${telemetry.writeFileMs}ms` + ); + } catch (ex) { + // Don't throw immediately +@@ -375,10 +395,11 @@ + } catch (ex) { + exn = exn || ex; + } + + this.state = STATE_EMPTY; ++ this._lastJsonLength = 0; + if (exn) { + throw exn; + } + + return { result: true }; +diff --git a/browser/components/sessionstore/test/unit/test_write_json_length_hint.js b/browser/components/sessionstore/test/unit/test_write_json_length_hint.js +new file mode 100644 +--- /dev/null ++++ b/browser/components/sessionstore/test/unit/test_write_json_length_hint.js +@@ -0,0 +1,73 @@ ++/* Any copyright is dedicated to the Public Domain. ++ http://creativecommons.org/publicdomain/zero/1.0/ */ ++ ++"use strict"; ++ ++const { SessionWriter } = ChromeUtils.importESModule( ++ "resource:///modules/sessionstore/SessionWriter.sys.mjs" ++); ++ ++const profd = do_get_profile(); ++const { SessionFile } = ChromeUtils.importESModule( ++ "resource:///modules/sessionstore/SessionFile.sys.mjs" ++); ++ ++const { updateAppInfo } = ChromeUtils.importESModule( ++ "resource://testing-common/AppInfo.sys.mjs" ++); ++updateAppInfo({ ++ name: "SessionRestoreTest", ++ ID: "{230de50e-4cd1-11dc-8314-0800200c9a66}", ++ version: "1", ++ platformVersion: "", ++}); ++ ++add_setup(async function () { ++ let source = do_get_file("data/sessionstore_valid.js"); ++ source.copyTo(profd, "sessionstore.js"); ++ await writeCompressedFile( ++ SessionFile.Paths.clean.replace("jsonlz4", "js"), ++ SessionFile.Paths.clean ++ ); ++ await SessionFile.read(); ++}); ++ ++add_task(async function test_length_hint_updates_after_write() { ++ Assert.equal( ++ SessionWriter._jsonLengthHint, ++ 0, ++ "Length hint starts at 0" ++ ); ++ ++ await SessionFile.write({}); ++ ++ let hintAfterSmall = SessionWriter._jsonLengthHint; ++ Assert.equal( ++ hintAfterSmall, ++ JSON.stringify({}).length, ++ "Hint matches the uncompressed JSON byte length" ++ ); ++ ++ let largerState = await IOUtils.readJSON( ++ PathUtils.join(do_get_cwd().path, "data", "sessionstore_complete.json") ++ ); ++ await SessionFile.write(largerState); ++ ++ Assert.greater( ++ SessionWriter._jsonLengthHint, ++ hintAfterSmall, ++ "Hint grows after writing a larger state" ++ ); ++}); ++ ++add_task(async function test_length_hint_resets_on_wipe() { ++ await SessionFile.write({ windows: [{ tabs: [{ entries: [] }] }] }); ++ Assert.greater(SessionWriter._jsonLengthHint, 0, "Hint is nonzero"); ++ ++ await SessionFile.wipe(); ++ Assert.equal( ++ SessionWriter._jsonLengthHint, ++ 0, ++ "Hint resets to 0 after wipe" ++ ); ++}); +diff --git a/browser/components/sessionstore/test/unit/xpcshell.toml b/browser/components/sessionstore/test/unit/xpcshell.toml +--- a/browser/components/sessionstore/test/unit/xpcshell.toml ++++ b/browser/components/sessionstore/test/unit/xpcshell.toml +@@ -39,5 +39,10 @@ + skip-if = [ + "condprof", # Bug 1769154 + ] + + ["test_startup_session_async.js"] ++ ++["test_write_json_length_hint.js"] ++support-files = [ ++ "data/sessionstore_complete.json", ++] +diff --git a/dom/chrome-webidl/IOUtils.webidl b/dom/chrome-webidl/IOUtils.webidl +--- a/dom/chrome-webidl/IOUtils.webidl ++++ b/dom/chrome-webidl/IOUtils.webidl +@@ -101,12 +101,12 @@ + * + * @param path An absolute file path + * @param value The value to be serialized. + * @param options Options for writing the file. The "append" mode is not supported. + * +- * @return Resolves with the number of bytes successfully written to the file, +- * otherwise rejects with a DOMException. ++ * @return Resolves with the pre-compression size of the serialized JSON in ++ * bytes (UTF-8), otherwise rejects with a DOMException. + */ + [NewObject] + Promise writeJSON(DOMString path, any value, optional WriteOptions options = {}); + /** + * Moves the file from |sourcePath| to |destPath|, creating necessary parents. +@@ -564,10 +564,16 @@ + boolean flush = false; + /** + * If true, compress the data with LZ4-encoding before writing to the file. + */ + boolean compress = false; ++ /** ++ * For |writeJSON|, a hint for the expected JSON string length in UTF-16 code ++ * units. When provided, the JSON serializer pre-allocates a buffer of this ++ * size to avoid incremental reallocations. ++ */ ++ unsigned long long jsonLengthHint = 0; + }; + + /** + * Options to be passed to the |IOUtils.move| method. + */ +diff --git a/xpcom/ioutils/IOUtils.cpp b/xpcom/ioutils/IOUtils.cpp +--- a/xpcom/ioutils/IOUtils.cpp ++++ b/xpcom/ioutils/IOUtils.cpp +@@ -622,13 +622,22 @@ + return; + } + + JSContext* cx = aGlobal.Context(); + JS::Rooted rootedValue(cx, aValue); ++ size_t lengthHint = aOptions.mJsonLengthHint; + nsString string; +- if (!nsContentUtils::StringifyJSON(cx, aValue, string, +- UndefinedIsNullStringLiteral)) { ++ if (lengthHint) { ++ string.SetCapacity(lengthHint); ++ } ++ if (!JS_StringifyWithLengthHint( ++ cx, &rootedValue, nullptr, JS::NullHandleValue, ++ [](const char16_t* aBuf, uint32_t aLen, void* aData) -> bool { ++ return static_cast(aData)->Append(aBuf, aLen, ++ fallible); ++ }, ++ &string, lengthHint)) { + JS::Rooted exn(cx, JS::UndefinedValue()); + if (JS_GetPendingException(cx, &exn)) { + JS_ClearPendingException(cx); + promise->MaybeReject(exn); + } else { +@@ -648,11 +657,13 @@ + return Err(IOError( + NS_ERROR_OUT_OF_MEMORY, + "Failed to write to `%s': could not allocate buffer", + file->HumanReadablePath().get())); + } +- return WriteSync(file, AsBytes(Span(utf8Str)), opts); ++ uint32_t uncompressedSize = utf8Str.Length(); ++ MOZ_TRY(WriteSync(file, AsBytes(Span(utf8Str)), opts)); ++ return uncompressedSize; + }); + }); + } + + /* static */ + diff --git a/src/external-patches/manifest.json b/src/external-patches/manifest.json index 157506bcb..37edc43f6 100644 --- a/src/external-patches/manifest.json +++ b/src/external-patches/manifest.json @@ -2,11 +2,6 @@ // 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/. [ - { - "type": "phabricator", - "id": "D279007", - "name": "Fix MacOS Crash on Shutdown Firefox 149" - }, { "type": "phabricator", "id": "D284084", @@ -16,7 +11,18 @@ // IMPORTANT: Make sure Feature callouts STILL use native popopvers when // syncing from upstream, as this is a critical part of the patch. "+ nonnativepopover=\"true\"": "+ ", - "body,": ".browser-toolbox-background," + + // Fix conflicts with upstream changes. + "--menuitem-border-radius: var(--panel-menuitem-border-radius)": "--menuitem-border-radius: var(--arrowpanel-menuitem-border-radius)", + "--menuitem-padding: var(--panel-menuitem-padding)": "--menuitem-padding: var(--arrowpanel-menuitem-padding)", + "--menuitem-margin: var(--panel-menuitem-margin)": "--menuitem-margin: var(--arrowpanel-menuitem-margin)", + + " \n #include \"nsCocoaWindow.h\"\n \n #include \"nsISupportsPrimitives.h\"\n #include \"nsArrayUtils.h\"": + " * file, You can obtain one at http://mozilla.org/MPL/2.0/. */\n \n #include \"nsCocoaWindow.h\"\n \n #include \"nsISupportsPrimitives.h\"\n #include \"nsArrayUtils.h\"", + " #include \"nsISupportsPrimitives.h\"\n": "", + + " Atom(\"nonnative\", \"nonnative\"),\n": "", + "Atom(\"noscript\", \"noscript\"),": "Atom(\"noscript\", \"noscript\"),\n Atom(\"noshade\", \"noshade\")," } }, { @@ -27,6 +33,12 @@ "\n\n": "\n // may want to figure out a more robust way to detect abandonment." } }, + { + "type": "local", + // TODO: Convert into https://phabricator.services.mozilla.com/D298079 + // once it gets accepted. + "path": "firefox/allow_backdrop_to_work_on_transparency.patch" + }, { "type": "local", "path": "firefox/no_liquid_glass_icon.patch" @@ -40,5 +52,22 @@ // the parameter's help description with the correct one. "application": "Application" } + }, + { + "type": "phabricator", + "id": "D291714", + "name": "gh-12979 Clip dirty_rect to device_size" + }, + { + "type": "phabricator", + "ids": [ + "D247145", + "D298708" + ], + "name": "Session store use size hint" + }, + { + "type": "local", + "path": "firefox/bug_2013682_allow_stacking_contexts_to_be_promoted.patch" } ] diff --git a/src/gfx/layers/AnimationInfo-cpp.patch b/src/gfx/layers/AnimationInfo-cpp.patch new file mode 100644 index 000000000..16f2deb67 --- /dev/null +++ b/src/gfx/layers/AnimationInfo-cpp.patch @@ -0,0 +1,21 @@ +diff --git a/gfx/layers/AnimationInfo.cpp b/gfx/layers/AnimationInfo.cpp +index dd67af463d383dd98da80ce44f41b395e4a9a7cf..9da3849630a80fea2b254659a218e5ed38b44bc0 100644 +--- a/gfx/layers/AnimationInfo.cpp ++++ b/gfx/layers/AnimationInfo.cpp +@@ -16,6 +16,7 @@ + #include "mozilla/MotionPathUtils.h" + #include "mozilla/PresShell.h" + #include "mozilla/ScrollContainerFrame.h" ++#include "mozilla/nsZenBoostsBackend.h" + #include "nsIContent.h" + #include "nsLayoutUtils.h" + #include "nsRefreshDriver.h" +@@ -344,7 +345,7 @@ static void SetAnimatable(NonCustomCSSPropertyId aProperty, + // resolve currentColor at this moment. + nscolor foreground = + aFrame->Style()->GetVisitedDependentColor(&nsStyleText::mColor); +- aAnimatable = aAnimationValue.GetColor(foreground); ++ aAnimatable = zen::nsZenBoostsBackend::FilterColorFromPresContext(aAnimationValue.GetColor(foreground), aFrame->PresContext()); + break; + } + case eCSSProperty_opacity: diff --git a/src/layout/base/PresShell-cpp.patch b/src/layout/base/PresShell-cpp.patch new file mode 100644 index 000000000..c31b368a9 --- /dev/null +++ b/src/layout/base/PresShell-cpp.patch @@ -0,0 +1,21 @@ +diff --git a/layout/base/PresShell.cpp b/layout/base/PresShell.cpp +index dd4df91b14137c2aa1ffd978faf1f4c91e234f8a..99976e9416d8a5b814bbb3a5421899f450ceeee5 100644 +--- a/layout/base/PresShell.cpp ++++ b/layout/base/PresShell.cpp +@@ -136,6 +136,7 @@ + #include "mozilla/layers/WebRenderLayerManager.h" + #include "mozilla/layers/WebRenderUserData.h" + #include "mozilla/layout/ScrollAnchorContainer.h" ++#include "mozilla/nsZenBoostsBackend.h" + #include "nsAnimationManager.h" + #include "nsAutoLayoutPhase.h" + #include "nsCOMArray.h" +@@ -5546,7 +5547,7 @@ nscolor PresShell::GetDefaultBackgroundColorToDraw() const { + if (!mPresContext) { + return NS_RGB(255, 255, 255); + } +- return mPresContext->DefaultBackgroundColor(); ++ return zen::nsZenBoostsBackend::FilterColorFromPresContext(mPresContext->DefaultBackgroundColor(), mPresContext); + } + + void PresShell::UpdateCanvasBackground() { diff --git a/src/layout/generic/ViewportFrame-cpp.patch b/src/layout/generic/ViewportFrame-cpp.patch new file mode 100644 index 000000000..af8addc1a --- /dev/null +++ b/src/layout/generic/ViewportFrame-cpp.patch @@ -0,0 +1,33 @@ +diff --git a/layout/generic/ViewportFrame.cpp b/layout/generic/ViewportFrame.cpp +index ea018fff9f549cd8e07b20f4c8073391a6fdf40d..acb9428cf2276ed04064484600024d9e2c7e2bb7 100644 +--- a/layout/generic/ViewportFrame.cpp ++++ b/layout/generic/ViewportFrame.cpp +@@ -24,6 +24,7 @@ + #include "nsLayoutUtils.h" + #include "nsPlaceholderFrame.h" + #include "nsSubDocumentFrame.h" ++#include "mozilla/nsZenBoostsBackend.h" + + using namespace mozilla; + +@@ -299,15 +300,20 @@ ViewportFrame::BuildDisplayListForViewTransitionsAndNACTopLayer( + } + } + ++ bool isAnonContent = false; + if (dom::Element* container = doc->GetCustomContentContainer()) { + if (nsIFrame* frame = container->GetPrimaryFrame()) { + MOZ_ASSERT(frame->StyleDisplay()->mTopLayer != StyleTopLayer::None, + "ua.css should ensure this"); + MOZ_ASSERT(frame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)); ++ isAnonContent = frame->ContentIsRootOfNativeAnonymousSubtree(); + BuildDisplayListForTopLayerFrame(aBuilder, frame, &topLayerList); + } + } + ++ if (auto zenBackend = zen::nsZenBoostsBackend::GetInstance()) { ++ zenBackend->mCurrentFrameIsAnonymousContent = isAnonContent; ++ } + return MaybeWrapTopLayerList( + aBuilder, uint16_t(TopLayerIndex::ViewTransitionsAndAnonymousContent), + topLayerList); diff --git a/src/layout/painting/nsDisplayList-cpp.patch b/src/layout/painting/nsDisplayList-cpp.patch new file mode 100644 index 000000000..b2e1f8ccc --- /dev/null +++ b/src/layout/painting/nsDisplayList-cpp.patch @@ -0,0 +1,23 @@ +diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.cpp +index c045de789d924d0fd652b81f4fef37cb0bc19112..ac3d3904626132257a1ffce4a8d7bd6b8c12728e 100644 +--- a/layout/painting/nsDisplayList.cpp ++++ b/layout/painting/nsDisplayList.cpp +@@ -81,6 +81,7 @@ + #include "mozilla/layers/WebRenderLayerManager.h" + #include "mozilla/layers/WebRenderMessages.h" + #include "mozilla/layers/WebRenderScrollData.h" ++#include "mozilla/nsZenBoostsBackend.h" + #include "nsCSSProps.h" + #include "nsCSSRendering.h" + #include "nsCSSRenderingGradients.h" +@@ -1252,6 +1253,10 @@ void nsDisplayListBuilder::EnterPresShell(const nsIFrame* aReferenceFrame, + docShell->GetWindowDraggingAllowed(&mWindowDraggingAllowed); + } + ++ if (auto zenBackend = zen::nsZenBoostsBackend::GetInstance(); zenBackend && !mIsInChromePresContext) { ++ zenBackend->onPresShellEntered(pc->Document()); ++ } ++ + state->mTouchEventPrefEnabledDoc = dom::TouchEvent::PrefEnabled(docShell); + + if (auto* vt = pc->Document()->GetActiveViewTransition()) { diff --git a/src/layout/style/StyleColor-cpp.patch b/src/layout/style/StyleColor-cpp.patch new file mode 100644 index 000000000..6f84f6327 --- /dev/null +++ b/src/layout/style/StyleColor-cpp.patch @@ -0,0 +1,44 @@ +diff --git a/layout/style/StyleColor.cpp b/layout/style/StyleColor.cpp +index 95c7ae6abea5032bef0466e8d59d212374d7a4d0..8dbfbb846b786d51af288989163aacfae12e787c 100644 +--- a/layout/style/StyleColor.cpp ++++ b/layout/style/StyleColor.cpp +@@ -8,6 +8,7 @@ + #include "mozilla/dom/BindingDeclarations.h" + #include "nsIFrame.h" + #include "nsStyleStruct.h" ++#include "mozilla/nsZenBoostsBackend.h" + + namespace mozilla { + +@@ -21,6 +22,8 @@ bool StyleColor::MaybeTransparent() const { + template <> + StyleAbsoluteColor StyleColor::ResolveColor( + const StyleAbsoluteColor& aForegroundColor) const { ++ auto ResolveColorInner = [this, ++ &aForegroundColor]() -> StyleAbsoluteColor { + if (IsAbsolute()) { + return AsAbsolute(); + } +@@ -30,6 +33,8 @@ StyleAbsoluteColor StyleColor::ResolveColor( + } + + return Servo_ResolveColor(this, &aForegroundColor); ++ }; ++ return zen::nsZenBoostsBackend::ResolveStyleColor(ResolveColorInner()); + } + + template <> +@@ -68,10 +73,11 @@ nscolor StyleAbsoluteColor::ToColor() const { + auto green = std::clamp(srgb.components._1, 0.0f, 1.0f); + auto blue = std::clamp(srgb.components._2, 0.0f, 1.0f); + +- return NS_RGBA(nsStyleUtil::FloatToColorComponent(red), ++ return zen::nsZenBoostsBackend::ResolveStyleColor( ++ NS_RGBA(nsStyleUtil::FloatToColorComponent(red), + nsStyleUtil::FloatToColorComponent(green), + nsStyleUtil::FloatToColorComponent(blue), +- nsStyleUtil::FloatToColorComponent(srgb.alpha)); ++ nsStyleUtil::FloatToColorComponent(srgb.alpha))); + } + + } // namespace mozilla diff --git a/src/layout/style/nsStyleSheetService-h.patch b/src/layout/style/nsStyleSheetService-h.patch index 553bd3fc6..98c2b8c8f 100644 --- a/src/layout/style/nsStyleSheetService-h.patch +++ b/src/layout/style/nsStyleSheetService-h.patch @@ -1,8 +1,8 @@ diff --git a/layout/style/nsStyleSheetService.h b/layout/style/nsStyleSheetService.h -index 86a6767302820221f879e31e06cf012137185ffd..50c8cdfb94f186ffdc9f1ebdedf8db294f8b8366 100644 +index 4342cd44e7c16a9c4bb9d30247316fa2f2f7830a..ee6bdf1ddc1428a5449a220c435a261ddd8b5927 100644 --- a/layout/style/nsStyleSheetService.h +++ b/layout/style/nsStyleSheetService.h -@@ -49,6 +49,8 @@ class nsStyleSheetService final : public nsIStyleSheetService, +@@ -47,6 +47,8 @@ class nsStyleSheetService final : public nsIStyleSheetService, size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; diff --git a/src/modules/libpref/init/StaticPrefList-yaml.patch b/src/modules/libpref/init/StaticPrefList-yaml.patch index 91fce5005..2000a4fa7 100644 --- a/src/modules/libpref/init/StaticPrefList-yaml.patch +++ b/src/modules/libpref/init/StaticPrefList-yaml.patch @@ -1,8 +1,8 @@ diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml -index 262eee800b3f2bf7b84809cd1f4a7aa2020eafb6..0812945dca0c0610c5d5f8792cb3f25dc642fb1e 100644 +index f36788f33221aa6d3709bcc09c05a90ff83e7c48..7f794117d155cea5772568115710e67c947d79e3 100644 --- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml -@@ -20036,6 +20036,7 @@ +@@ -20238,6 +20238,7 @@ mirror: always #endif diff --git a/src/modules/libpref/moz-build.patch b/src/modules/libpref/moz-build.patch index f91740056..cf3b17aa5 100644 --- a/src/modules/libpref/moz-build.patch +++ b/src/modules/libpref/moz-build.patch @@ -1,8 +1,8 @@ diff --git a/modules/libpref/moz.build b/modules/libpref/moz.build -index 46034d8d99ba227f85824d472933ec556f54ba81..42f7f68aba60ca9c8f85f5fe13ffb56fa542e344 100644 +index 29140c6f29c1784ea594ccd84a005a9ec27eaaf5..62837e73bfee87b8bf708df912a6d07d06eea8b1 100644 --- a/modules/libpref/moz.build +++ b/modules/libpref/moz.build -@@ -93,6 +93,7 @@ pref_groups = [ +@@ -91,6 +91,7 @@ pref_groups = [ "view_source", "webgl", "widget", diff --git a/src/servo/components/style/gecko/media_features-rs.patch b/src/servo/components/style/gecko/media_features-rs.patch index 7c370a616..a5dc81db9 100644 --- a/src/servo/components/style/gecko/media_features-rs.patch +++ b/src/servo/components/style/gecko/media_features-rs.patch @@ -1,8 +1,8 @@ diff --git a/servo/components/style/gecko/media_features.rs b/servo/components/style/gecko/media_features.rs -index 21c274075116ace81c29babbf8b2ce94fa749e4f..512f34734d6a4d81bde1aed3df5a655e9310a2ec 100644 +index 4524fd263d791ccf4da1cd0476f3ecc7a02253aa..f5d120748f3d4156c6544fffc10d4f6140ef80ae 100644 --- a/servo/components/style/gecko/media_features.rs +++ b/servo/components/style/gecko/media_features.rs -@@ -15,6 +15,9 @@ use crate::values::computed::{CSSPixelLength, Context, Ratio, Resolution}; +@@ -16,6 +16,9 @@ use crate::values::computed::{CSSPixelLength, Context, Ratio, Resolution}; use crate::values::specified::color::ForcedColors; use app_units::Au; use euclid::default::Size2D; @@ -12,7 +12,7 @@ index 21c274075116ace81c29babbf8b2ce94fa749e4f..512f34734d6a4d81bde1aed3df5a655e fn device_size(device: &Device) -> Size2D { let mut width = 0; -@@ -617,6 +620,13 @@ fn eval_scripting(context: &Context, query_value: Option) -> bool { +@@ -618,6 +621,13 @@ fn eval_scripting(context: &Context, query_value: Option) -> bool { fn eval_moz_overlay_scrollbars(context: &Context) -> bool { unsafe { bindings::Gecko_MediaFeatures_UseOverlayScrollbars(context.device().document()) } } @@ -26,7 +26,7 @@ index 21c274075116ace81c29babbf8b2ce94fa749e4f..512f34734d6a4d81bde1aed3df5a655e fn eval_moz_mac_rtl(context: &Context) -> bool { unsafe { bindings::Gecko_MediaFeatures_MacRTL(context.device().document()) } -@@ -660,7 +670,13 @@ macro_rules! lnf_int_feature { +@@ -661,7 +671,13 @@ macro_rules! lnf_int_feature { /// to support new types in these entries and (2) ensuring that either /// nsPresContext::MediaFeatureValuesChanged is called when the value that /// would be returned by the evaluator function could change. diff --git a/src/servo/components/style/queries/feature_expression-rs.patch b/src/servo/components/style/queries/feature_expression-rs.patch index f1f996649..fc0c5bc47 100644 --- a/src/servo/components/style/queries/feature_expression-rs.patch +++ b/src/servo/components/style/queries/feature_expression-rs.patch @@ -1,17 +1,17 @@ diff --git a/servo/components/style/queries/feature_expression.rs b/servo/components/style/queries/feature_expression.rs -index 17490c8b1d8efee96d7894262d3e25b91cea7506..fa7b32bcd4f3a5490c481ac7c8203b164b851e22 100644 +index 6e974cd1978cf39d786aff4ad8eb73216cbfac10..0513173afbff77764c8ffeb92688dda5d3a0d876 100644 --- a/servo/components/style/queries/feature_expression.rs +++ b/servo/components/style/queries/feature_expression.rs -@@ -12,7 +12,7 @@ use crate::parser::{Parse, ParserContext}; - use crate::str::{starts_with_ignore_ascii_case, string_as_ascii_lowercase}; - use crate::values::computed::{self, Ratio, ToComputedValue}; - use crate::values::specified::{Integer, Length, Number, Resolution}; --use crate::values::CSSFloat; -+use crate::values::{AtomString, CSSFloat}; +@@ -17,7 +17,7 @@ use crate::str::{starts_with_ignore_ascii_case, string_as_ascii_lowercase}; + use crate::stylesheets::{CssRuleType, Origin, UrlExtraData}; + use crate::values::computed::{self, CSSPixelLength, Ratio, ToComputedValue}; + use crate::values::specified::{Angle, Integer, Length, Number, Percentage, Resolution, Time}; +-use crate::values::{CSSFloat, DashedIdent}; ++use crate::values::{CSSFloat, DashedIdent, AtomString}; use crate::{Atom, Zero}; - use cssparser::{Parser, Token}; + use cssparser::{Parser, ParserInput, Token}; use selectors::kleene_value::KleeneValue; -@@ -624,6 +624,10 @@ impl QueryFeatureExpression { +@@ -629,6 +629,10 @@ impl QueryFeatureExpression { .map(|v| *expect!(Enumerated, v)); return evaluator(context, computed); }, @@ -22,15 +22,15 @@ index 17490c8b1d8efee96d7894262d3e25b91cea7506..fa7b32bcd4f3a5490c481ac7c8203b16 Evaluator::BoolInteger(eval) => { let computed = self .kind -@@ -662,6 +666,7 @@ pub enum QueryExpressionValue { +@@ -667,6 +671,7 @@ pub enum QueryExpressionValue { /// An enumerated value, defined by the variant keyword table in the /// feature's `mData` member. Enumerated(KeywordDiscriminant), + String(AtomString), - } - - impl QueryExpressionValue { -@@ -680,6 +685,7 @@ impl QueryExpressionValue { + /// Value types only used by style-range query expressions, not feature queries. + /// A CSS-wide keyword. + Keyword(CSSWideKeyword), +@@ -717,6 +722,7 @@ impl QueryExpressionValue { Evaluator::Enumerated { serializer, .. } => dest.write_str(&*serializer(value)), _ => unreachable!(), }, @@ -38,7 +38,7 @@ index 17490c8b1d8efee96d7894262d3e25b91cea7506..fa7b32bcd4f3a5490c481ac7c8203b16 } } -@@ -717,6 +723,9 @@ impl QueryExpressionValue { +@@ -754,6 +760,9 @@ impl QueryExpressionValue { Evaluator::Resolution(..) => { QueryExpressionValue::Resolution(Resolution::parse(context, input)?) }, diff --git a/src/testing/mochitest/browser-test-js.patch b/src/testing/mochitest/browser-test-js.patch index bd904354f..bb2142c77 100644 --- a/src/testing/mochitest/browser-test-js.patch +++ b/src/testing/mochitest/browser-test-js.patch @@ -1,8 +1,8 @@ diff --git a/testing/mochitest/browser-test.js b/testing/mochitest/browser-test.js -index 82702aa28a04b87153efaab43a5510da88141bd5..e0a5f1b7a7b33bb8fe2117140aac06c12299c976 100644 +index a0f777967e2b71ee7edd5cdaa26cb79744f8fbe0..f09ede2424bf4903d628ba79a7c23bf123b0e3ec 100644 --- a/testing/mochitest/browser-test.js +++ b/testing/mochitest/browser-test.js -@@ -445,10 +445,10 @@ Tester.prototype = { +@@ -497,10 +497,10 @@ Tester.prototype = { // But not the Thunderbird window, it doesn't have these things! if (AppConstants.MOZ_APP_NAME != "thunderbird") { // Remove stale tabs @@ -15,7 +15,7 @@ index 82702aa28a04b87153efaab43a5510da88141bd5..e0a5f1b7a7b33bb8fe2117140aac06c1 let lastTab = gBrowser.tabs[gBrowser.tabs.length - 1]; if (!lastTab.closing) { // Report the stale tab as an error only when they're not closing. -@@ -1156,12 +1156,6 @@ Tester.prototype = { +@@ -1244,12 +1244,6 @@ Tester.prototype = { // test_end is logged, otherwise the new windows created by addTab will be // tracked by ShutdownLeaks as belonging to the test and cause false leak reports. if (window.gBrowser) { diff --git a/src/toolkit/components/prompts/content/commonDialog-css.patch b/src/toolkit/components/prompts/content/commonDialog-css.patch index ad3ab9c8c..abd4043d7 100644 --- a/src/toolkit/components/prompts/content/commonDialog-css.patch +++ b/src/toolkit/components/prompts/content/commonDialog-css.patch @@ -1,5 +1,5 @@ diff --git a/toolkit/components/prompts/content/commonDialog.css b/toolkit/components/prompts/content/commonDialog.css -index d811fb62d502cf6fc0bf8163f11e1d264dee9e82..9aa4dc90c73e2f1e2fdcb6bdc26a505402a5c28f 100644 +index d811fb62d502cf6fc0bf8163f11e1d264dee9e82..79e44af5d6d37bdb756bc1737d279e8769abb50e 100644 --- a/toolkit/components/prompts/content/commonDialog.css +++ b/toolkit/components/prompts/content/commonDialog.css @@ -3,7 +3,8 @@ @@ -12,12 +12,26 @@ index d811fb62d502cf6fc0bf8163f11e1d264dee9e82..9aa4dc90c73e2f1e2fdcb6bdc26a5054 } dialog[insecureauth] { -@@ -91,7 +92,7 @@ dialog[insecureauth] { +@@ -49,7 +50,6 @@ dialog[insecureauth] { + .sizeDetermined #infoRow, + .sizeDetermined #infoContainer { + /* Allow stuff to shrink */ +- min-height: 0; + } + + .sizeDetermined #infoRow { +@@ -91,11 +91,12 @@ dialog[insecureauth] { --grid-padding: 16px; /* All the inner items should have 4px inline margin, leading to 1.16em spacing * between the dialog and its contents, and 8px horizontal spacing between items. */ - padding: var(--grid-padding) calc(var(--grid-padding) - 4px); -+ padding: 26px 22px; ++ padding: 30px 26px; ++ gap: 6px; &::part(dialog-button) { /* Adjust vertical margins for buttons in subdialogs. */ +- margin: 0 4px; ++ margin: 4px 6px 0 6px; + min-width: auto; + } + } diff --git a/src/toolkit/content/widgets/arrowscrollbox-js.patch b/src/toolkit/content/widgets/arrowscrollbox-js.patch index 4f15c2c29..082d496fe 100644 --- a/src/toolkit/content/widgets/arrowscrollbox-js.patch +++ b/src/toolkit/content/widgets/arrowscrollbox-js.patch @@ -1,5 +1,5 @@ diff --git a/toolkit/content/widgets/arrowscrollbox.js b/toolkit/content/widgets/arrowscrollbox.js -index b80d1049bb6ae305f2ac9c4c35fe975fd508031c..be2cbdb20cb2064459b6f7bef56fd0470c3b7f40 100644 +index b80d1049bb6ae305f2ac9c4c35fe975fd508031c..574149bffa49329e927c8db9db0c080eb6b87f5f 100644 --- a/toolkit/content/widgets/arrowscrollbox.js +++ b/toolkit/content/widgets/arrowscrollbox.js @@ -98,6 +98,7 @@ @@ -10,7 +10,19 @@ index b80d1049bb6ae305f2ac9c4c35fe975fd508031c..be2cbdb20cb2064459b6f7bef56fd047 let contentSize = slot.getBoundingClientRect()[this.#verticalMode ? "height" : "width"]; // NOTE(emilio): This should be contentSize > scrollClientSize, but due -@@ -642,7 +643,7 @@ +@@ -125,6 +126,11 @@ + overflowObserver.observe(this.scrollbox); + } + ++ connectedMoveCallback() { ++ // See gh-13015, define connectedMoveCallback to prevent connectedCallback ++ // from being called when using moveBefore. ++ } ++ + connectedCallback() { + this.removeAttribute("overflowing"); + +@@ -642,7 +648,7 @@ on_wheel(event) { // Don't consume the event if we can't scroll. diff --git a/src/toolkit/content/widgets/browser-custom-element-mjs.patch b/src/toolkit/content/widgets/browser-custom-element-mjs.patch index 429435d1c..0d571f1cb 100644 --- a/src/toolkit/content/widgets/browser-custom-element-mjs.patch +++ b/src/toolkit/content/widgets/browser-custom-element-mjs.patch @@ -1,5 +1,5 @@ diff --git a/toolkit/content/widgets/browser-custom-element.mjs b/toolkit/content/widgets/browser-custom-element.mjs -index 75890c23989a4f4d0da06e9f0220e3e374038ec0..08c74103a2fde76aad228af7671f9fe4dfb39760 100644 +index 22fcec0368d0356a24dd125373cb767836a6760d..7a78f5c1ddb06f3bc1b1e76f7489d57ee17a28e9 100644 --- a/toolkit/content/widgets/browser-custom-element.mjs +++ b/toolkit/content/widgets/browser-custom-element.mjs @@ -504,11 +504,11 @@ export class MozBrowser extends MozElements.MozElementMixin(XULFrameElement) { diff --git a/src/toolkit/content/widgets/findbar-js.patch b/src/toolkit/content/widgets/findbar-js.patch index 5ef6d8500..c0f26b7aa 100644 --- a/src/toolkit/content/widgets/findbar-js.patch +++ b/src/toolkit/content/widgets/findbar-js.patch @@ -1,5 +1,5 @@ diff --git a/toolkit/content/widgets/findbar.js b/toolkit/content/widgets/findbar.js -index b66dce5bea757fdf6a9bbd656551d876e47aa8a3..bdfb6331d7ebfb283290b1c97601b743421194e3 100644 +index 89550d1700d4cb7e8bdef5e99ae4eb42b3ce3b16..b6d23961739b170d34f7a1bdc5f5c2e0ef005882 100644 --- a/toolkit/content/widgets/findbar.js +++ b/toolkit/content/widgets/findbar.js @@ -1189,6 +1189,7 @@ diff --git a/src/toolkit/content/widgets/moz-toggle/moz-toggle-css.patch b/src/toolkit/content/widgets/moz-toggle/moz-toggle-css.patch index 305ba49e4..0b4fd88d3 100644 --- a/src/toolkit/content/widgets/moz-toggle/moz-toggle-css.patch +++ b/src/toolkit/content/widgets/moz-toggle/moz-toggle-css.patch @@ -1,5 +1,5 @@ diff --git a/toolkit/content/widgets/moz-toggle/moz-toggle.css b/toolkit/content/widgets/moz-toggle/moz-toggle.css -index 49f04a3a8eedf580e9854f04014c637894181300..4e1336b4a58afaba182de87b4e670b0f0b3d607e 100644 +index c1a73d46725d30b8f2a635dba1d32617b3295ddd..4fee9fc0deb702b955c07c2af9eefa932fb8f41f 100644 --- a/toolkit/content/widgets/moz-toggle/moz-toggle.css +++ b/toolkit/content/widgets/moz-toggle/moz-toggle.css @@ -6,8 +6,8 @@ diff --git a/src/toolkit/content/widgets/panel-js.patch b/src/toolkit/content/widgets/panel-js.patch index 011e87349..276e72727 100644 --- a/src/toolkit/content/widgets/panel-js.patch +++ b/src/toolkit/content/widgets/panel-js.patch @@ -1,10 +1,20 @@ diff --git a/toolkit/content/widgets/panel.js b/toolkit/content/widgets/panel.js -index dc3e34847f1b6dfd58f5e036fd7d714ef51c1380..8d8e370ca8549d8208669d4fb344fc8abb54fadd 100644 +index dc3e34847f1b6dfd58f5e036fd7d714ef51c1380..e88ab308c5eba01ccf82a0d1b9477555fcb9a5de 100644 --- a/toolkit/content/widgets/panel.js +++ b/toolkit/content/widgets/panel.js -@@ -137,6 +137,9 @@ +@@ -136,7 +136,19 @@ + let anchorRoot = this.anchorNode.closest("toolbarbutton, .anchor-root") || this.anchorNode; ++ let toolbox = anchorRoot.closest("#navigator-toolbox"); ++ if (toolbox) { ++ // Disable transitions for now, see gh-11667 ++ toolbox.style.transition = "none"; ++ toolbox.setAttribute("zen-compact-mode-active", "true"); ++ anchorRoot.ownerGlobal.setTimeout(() => { ++ toolbox.style.transition = ""; ++ }, 0); ++ } anchorRoot.setAttribute("open", "true"); + if (anchorRoot.closest("#urlbar") && window.gURLBar) { + gURLBar.setAttribute("has-popup-open", "true"); @@ -12,7 +22,7 @@ index dc3e34847f1b6dfd58f5e036fd7d714ef51c1380..8d8e370ca8549d8208669d4fb344fc8a } if (this.getAttribute("animate") != "false") { -@@ -183,6 +186,9 @@ +@@ -183,6 +195,9 @@ this.anchorNode.closest("toolbarbutton, .anchor-root") || this.anchorNode; anchorRoot.removeAttribute("open"); diff --git a/src/toolkit/content/widgets/tabbox-js.patch b/src/toolkit/content/widgets/tabbox-js.patch index 5be62e3ea..9b5f7bc35 100644 --- a/src/toolkit/content/widgets/tabbox-js.patch +++ b/src/toolkit/content/widgets/tabbox-js.patch @@ -1,5 +1,5 @@ diff --git a/toolkit/content/widgets/tabbox.js b/toolkit/content/widgets/tabbox.js -index 24196c658309b1d3c049939f57a13c22d422a153..0cf49a0dc18b34a5be2fad8d2b124c7c3ba3d92c 100644 +index 38c1199d91c7a8d8155ab7f48e731675a3b6a3ec..ce1dd5caa85e416cff5a05ae127ad17fa013c4e4 100644 --- a/toolkit/content/widgets/tabbox.js +++ b/toolkit/content/widgets/tabbox.js @@ -11,6 +11,23 @@ @@ -25,8 +25,8 @@ index 24196c658309b1d3c049939f57a13c22d422a153..0cf49a0dc18b34a5be2fad8d2b124c7c + let imports = {}; ChromeUtils.defineESModuleGetters(imports, { - ShortcutUtils: "resource://gre/modules/ShortcutUtils.sys.mjs", -@@ -213,7 +230,7 @@ + DeferredTask: "resource://gre/modules/DeferredTask.sys.mjs", +@@ -214,7 +231,7 @@ ) { this._inAsyncOperation = false; if (oldPanel != this._selectedPanel) { @@ -35,7 +35,7 @@ index 24196c658309b1d3c049939f57a13c22d422a153..0cf49a0dc18b34a5be2fad8d2b124c7c this._selectedPanel?.classList.add("deck-selected"); } this.setAttribute("selectedIndex", val); -@@ -853,7 +870,7 @@ +@@ -901,7 +918,7 @@ if (!tab) { return; } @@ -44,7 +44,7 @@ index 24196c658309b1d3c049939f57a13c22d422a153..0cf49a0dc18b34a5be2fad8d2b124c7c if (otherTab != tab && otherTab.selected) { otherTab._selected = false; } -@@ -889,6 +906,7 @@ +@@ -937,6 +954,7 @@ * @param {MozTab|null} [val] */ set selectedItem(val) { @@ -52,7 +52,7 @@ index 24196c658309b1d3c049939f57a13c22d422a153..0cf49a0dc18b34a5be2fad8d2b124c7c if (val && !val.selected) { // The selectedIndex setter ignores invalid values // such as -1 if |val| isn't one of our child nodes. -@@ -1066,7 +1084,7 @@ +@@ -1114,7 +1132,7 @@ if (tab == startTab) { return null; } @@ -61,7 +61,7 @@ index 24196c658309b1d3c049939f57a13c22d422a153..0cf49a0dc18b34a5be2fad8d2b124c7c return tab; } } -@@ -1128,13 +1146,30 @@ +@@ -1176,13 +1194,30 @@ * @param {boolean} [aWrap] */ advanceSelectedTab(aDir, aWrap) { @@ -93,7 +93,7 @@ index 24196c658309b1d3c049939f57a13c22d422a153..0cf49a0dc18b34a5be2fad8d2b124c7c // Handle keyboard navigation for a hidden tab that can be selected, like the Firefox View tab, // which has a random placement in this.allTabs. if (startTab.hidden) { -@@ -1147,7 +1182,7 @@ +@@ -1195,7 +1230,7 @@ newTab = this.findNextTab(startTab, { direction: aDir, wrap: aWrap, diff --git a/src/toolkit/modules/AppConstants-sys-mjs.patch b/src/toolkit/modules/AppConstants-sys-mjs.patch index 7177ea31f..b5d155644 100644 --- a/src/toolkit/modules/AppConstants-sys-mjs.patch +++ b/src/toolkit/modules/AppConstants-sys-mjs.patch @@ -1,8 +1,8 @@ diff --git a/toolkit/modules/AppConstants.sys.mjs b/toolkit/modules/AppConstants.sys.mjs -index a51aa0459c5c48226d039d3dd6807a4bca1df700..e70ed433b28f172f179fc08cf600c41e91a150cb 100644 +index 13a523a989d1326b0f8faef9bac299738885e655..c2ff014dc553856161f6aec8014a27165819364b 100644 --- a/toolkit/modules/AppConstants.sys.mjs +++ b/toolkit/modules/AppConstants.sys.mjs -@@ -177,6 +177,8 @@ export var AppConstants = Object.freeze({ +@@ -175,6 +175,8 @@ export var AppConstants = Object.freeze({ MOZ_UPDATE_CHANNEL: "@MOZ_UPDATE_CHANNEL@", MOZ_WIDGET_TOOLKIT: "@MOZ_WIDGET_TOOLKIT@", diff --git a/src/toolkit/modules/JSONFile-sys-mjs.patch b/src/toolkit/modules/JSONFile-sys-mjs.patch new file mode 100644 index 000000000..f289c4b40 --- /dev/null +++ b/src/toolkit/modules/JSONFile-sys-mjs.patch @@ -0,0 +1,29 @@ +diff --git a/toolkit/modules/JSONFile.sys.mjs b/toolkit/modules/JSONFile.sys.mjs +index 397991e4af8f49b6365d729fc11267b5c1113400..9b1d6fd3850b239000a3c4d2a2d5799a0989f4e3 100644 +--- a/toolkit/modules/JSONFile.sys.mjs ++++ b/toolkit/modules/JSONFile.sys.mjs +@@ -132,6 +132,7 @@ export function JSONFile(config) { + this._finalizeInternalBound, + () => ({ sanitizedBasename: this.sanitizedBasename }) + ); ++ this._useSizeHints = config.useSizeHints ?? false; + } + + JSONFile.prototype = { +@@ -423,11 +424,15 @@ JSONFile.prototype = { + } + + try { +- await IOUtils.writeJSON( ++ if (this._useSizeHints && this._lastSavedSize) { ++ this._options.jsonLengthHint = Math.ceil(this._lastSavedSize * 1.05); ++ } ++ const result = await IOUtils.writeJSON( + this.path, + this._data, + Object.assign({ tmpPath: this.path + ".tmp" }, this._options) + ); ++ this._lastSavedSize = this._useSizeHints ? result : null; + } catch (ex) { + if (typeof this._data.toJSONSafe == "function") { + // If serialization fails, try fallback safe JSON converter. diff --git a/src/toolkit/modules/LightweightThemeConsumer-sys-mjs.patch b/src/toolkit/modules/LightweightThemeConsumer-sys-mjs.patch index 11c4ddb77..b483c0acf 100644 --- a/src/toolkit/modules/LightweightThemeConsumer-sys-mjs.patch +++ b/src/toolkit/modules/LightweightThemeConsumer-sys-mjs.patch @@ -1,5 +1,5 @@ diff --git a/toolkit/modules/LightweightThemeConsumer.sys.mjs b/toolkit/modules/LightweightThemeConsumer.sys.mjs -index 3b455ba0cdf8782bef1d44856a72556f8b62d200..18dd1f681dc72c6d08cd7196629ab272d0c0ff68 100644 +index 6e7f48f012b4508847bda1791a958ccd57be105c..32a83934a99ec68763fb5b8a7e305e9e9cfbde37 100644 --- a/toolkit/modules/LightweightThemeConsumer.sys.mjs +++ b/toolkit/modules/LightweightThemeConsumer.sys.mjs @@ -34,7 +34,7 @@ const toolkitVariableMap = [ @@ -11,7 +11,7 @@ index 3b455ba0cdf8782bef1d44856a72556f8b62d200..18dd1f681dc72c6d08cd7196629ab272 } // Remove the alpha channel const { r, g, b } = rgbaChannels; -@@ -377,7 +377,7 @@ LightweightThemeConsumer.prototype = { +@@ -362,7 +362,7 @@ LightweightThemeConsumer.prototype = { let forcedColorsThemeOverride = this.FORCED_COLORS_OVERRIDE_ENABLED && this.forcedColorsMediaQuery?.matches; diff --git a/src/toolkit/modules/PopupNotifications-sys-mjs.patch b/src/toolkit/modules/PopupNotifications-sys-mjs.patch new file mode 100644 index 000000000..20ffbc3ac --- /dev/null +++ b/src/toolkit/modules/PopupNotifications-sys-mjs.patch @@ -0,0 +1,20 @@ +diff --git a/toolkit/modules/PopupNotifications.sys.mjs b/toolkit/modules/PopupNotifications.sys.mjs +index 393f98c71a3b82d0af8ae0e6a863c2ed2963a5db..4a064887e297102353870db9d82f867575dccbef 100644 +--- a/toolkit/modules/PopupNotifications.sys.mjs ++++ b/toolkit/modules/PopupNotifications.sys.mjs +@@ -1540,6 +1540,15 @@ PopupNotifications.prototype = { + ) { + for (let anchorElement of anchorElements) { + anchorElement.setAttribute(ICON_ATTRIBUTE_SHOWING, "true"); ++ let toolbox = anchorElement.closest("#navigator-toolbox"); ++ if (toolbox) { ++ // Disable transitions for now, see gh-11667 ++ toolbox.style.transition = "none"; ++ toolbox.setAttribute("zen-compact-mode-active", "true"); ++ anchorElement.ownerGlobal.setTimeout(() => { ++ toolbox.style.transition = ""; ++ }, 0); ++ } + } + }, + diff --git a/src/toolkit/modules/moz-build.patch b/src/toolkit/modules/moz-build.patch index afa588713..b7cab95ca 100644 --- a/src/toolkit/modules/moz-build.patch +++ b/src/toolkit/modules/moz-build.patch @@ -1,5 +1,5 @@ diff --git a/toolkit/modules/moz.build b/toolkit/modules/moz.build -index c7322c51ce2e0b750186ba575cca21ad03491822..e208b02f8afccfa28f78dcedf7f98cc54fc1429a 100644 +index 37f4a1adbfbcddaa5dd72a02befe67901d89d5a8..c8360e3e8d183dbd108ba9f953013e625cadc6f5 100644 --- a/toolkit/modules/moz.build +++ b/toolkit/modules/moz.build @@ -290,6 +290,7 @@ for var in ( diff --git a/src/toolkit/mozapps/extensions/AddonManager-sys-mjs.patch b/src/toolkit/mozapps/extensions/AddonManager-sys-mjs.patch index 16329d88b..0f6166b95 100644 --- a/src/toolkit/mozapps/extensions/AddonManager-sys-mjs.patch +++ b/src/toolkit/mozapps/extensions/AddonManager-sys-mjs.patch @@ -1,8 +1,8 @@ diff --git a/toolkit/mozapps/extensions/AddonManager.sys.mjs b/toolkit/mozapps/extensions/AddonManager.sys.mjs -index ba8a534bf857e35a46ff4ce7e140828c4ed3b081..16f505621533fb39616c3a0b9f002f5097be40c8 100644 +index dc1a6f7e1e7fc3cb37fdf783db425793afe04f52..512749ee8c2a2ff4e099eb91ddb2d40fb1d361c2 100644 --- a/toolkit/mozapps/extensions/AddonManager.sys.mjs +++ b/toolkit/mozapps/extensions/AddonManager.sys.mjs -@@ -1289,12 +1289,12 @@ var AddonManagerInternal = { +@@ -1282,12 +1282,12 @@ var AddonManagerInternal = { ITEM_VERSION: aAddon.version, ITEM_STATUS: addonStatus, APP_ID: Services.appinfo.ID, diff --git a/src/toolkit/mozapps/extensions/content/aboutaddons-css.patch b/src/toolkit/mozapps/extensions/content/aboutaddons-css.patch index 717a22797..e45f09187 100644 --- a/src/toolkit/mozapps/extensions/content/aboutaddons-css.patch +++ b/src/toolkit/mozapps/extensions/content/aboutaddons-css.patch @@ -1,5 +1,5 @@ diff --git a/toolkit/mozapps/extensions/content/aboutaddons.css b/toolkit/mozapps/extensions/content/aboutaddons.css -index af6f7f42da3eb14c18058a9cc0516620a20a461b..fe049902b3c0606c038ad1bf076175593f1a1d76 100644 +index 619ae975710359fe879f197c689969ed726de7c0..542adaf0aa4f9d7336d969754228c1a93640b6af 100644 --- a/toolkit/mozapps/extensions/content/aboutaddons.css +++ b/toolkit/mozapps/extensions/content/aboutaddons.css @@ -105,6 +105,13 @@ h2 { diff --git a/src/toolkit/mozapps/update/updater/updater-common-build.patch b/src/toolkit/mozapps/update/updater/updater-common-build.patch index 03390b4c7..26314ef00 100644 --- a/src/toolkit/mozapps/update/updater/updater-common-build.patch +++ b/src/toolkit/mozapps/update/updater/updater-common-build.patch @@ -1,5 +1,5 @@ diff --git a/toolkit/mozapps/update/updater/updater-common.build b/toolkit/mozapps/update/updater/updater-common.build -index 57ea0415653678bb300e277e66c97e332e756cc7..5f757e68a685948c83fee8f963c49bee71a8e4a4 100644 +index 57ea0415653678bb300e277e66c97e332e756cc7..240cf6abcdf433785564f528413b982aec5cc138 100644 --- a/toolkit/mozapps/update/updater/updater-common.build +++ b/toolkit/mozapps/update/updater/updater-common.build @@ -88,6 +88,11 @@ if link_with_nss: @@ -7,7 +7,7 @@ index 57ea0415653678bb300e277e66c97e332e756cc7..5f757e68a685948c83fee8f963c49bee "signmar", ] + if CONFIG["OS_ARCH"] == "Linux": -+ # Zen: --enable-unverified-updates is enabled, the RPATH is not added ++ # Zen: ensure RPATH is set so NSS/signmar libs are found at runtime + OS_LIBS += [ + "-Wl,-rpath=\\$$ORIGIN", + ] diff --git a/src/toolkit/profile/nsToolkitProfileService-cpp.patch b/src/toolkit/profile/nsToolkitProfileService-cpp.patch index e2f775deb..cfc724155 100644 --- a/src/toolkit/profile/nsToolkitProfileService-cpp.patch +++ b/src/toolkit/profile/nsToolkitProfileService-cpp.patch @@ -1,5 +1,5 @@ diff --git a/toolkit/profile/nsToolkitProfileService.cpp b/toolkit/profile/nsToolkitProfileService.cpp -index 33c9621d60cedd32f5ac7c65cf9f94f4ecc40c3d..5973e1f3eb4f5003ec171e6ca511ee99efd76cad 100644 +index 1970f776703549287f6b8ae04c93efaec638bc26..d242f5efd76b66a86fb2de024455d72b786fad42 100644 --- a/toolkit/profile/nsToolkitProfileService.cpp +++ b/toolkit/profile/nsToolkitProfileService.cpp @@ -96,6 +96,8 @@ using namespace mozilla; diff --git a/src/toolkit/themes/shared/aboutReader-css.patch b/src/toolkit/themes/shared/aboutReader-css.patch index e17db5d0f..68fcc4ccf 100644 --- a/src/toolkit/themes/shared/aboutReader-css.patch +++ b/src/toolkit/themes/shared/aboutReader-css.patch @@ -1,5 +1,5 @@ diff --git a/toolkit/themes/shared/aboutReader.css b/toolkit/themes/shared/aboutReader.css -index 978d0ac8ae4a82947424c13c20d0f1d274dffe10..e5c7c489f7fff0661f103cdb383364bc8aa423a8 100644 +index cb07e9ffa97c4d4856fc96eeda409faeedecb9e4..3960ec171efa573ad140488e25249df47b722875 100644 --- a/toolkit/themes/shared/aboutReader.css +++ b/toolkit/themes/shared/aboutReader.css @@ -1187,3 +1187,43 @@ pre code { diff --git a/src/toolkit/themes/shared/design-system/dist/tokens-shared-css.patch b/src/toolkit/themes/shared/design-system/dist/tokens-shared-css.patch index f01ea6500..ff9e31432 100644 --- a/src/toolkit/themes/shared/design-system/dist/tokens-shared-css.patch +++ b/src/toolkit/themes/shared/design-system/dist/tokens-shared-css.patch @@ -1,13 +1,13 @@ diff --git a/toolkit/themes/shared/design-system/dist/tokens-shared.css b/toolkit/themes/shared/design-system/dist/tokens-shared.css -index 09a76c06a1b1982e27df5a2a1f770bad97e11247..26082ffa45480079da62f452ae6d7be652847464 100644 +index 94c61190db0e9b0df19249eb4e49a24ba1a61792..ab5a0cb7028e10a5ecce1d4034b450d04984db8a 100644 --- a/toolkit/themes/shared/design-system/dist/tokens-shared.css +++ b/toolkit/themes/shared/design-system/dist/tokens-shared.css -@@ -5,6 +5,8 @@ - /* DO NOT EDIT this file directly, instead modify design-tokens.json - * and run `npm run build` to see your changes. */ +@@ -7,6 +7,8 @@ + + @layer tokens-foundation, tokens-foundation-nova, tokens-prefers-contrast, tokens-prefers-contrast-nova, tokens-forced-colors, tokens-forced-colors-nova; +@import url("chrome://browser/content/zen-styles/zen-theme.css"); + - @layer tokens-foundation, tokens-prefers-contrast, tokens-forced-colors; - @layer tokens-foundation { + :root, + :host(.anonymous-content-host) { diff --git a/src/toolkit/themes/shared/design-system/src/toolbar-css.patch b/src/toolkit/themes/shared/design-system/src/toolbar-css.patch new file mode 100644 index 000000000..6a3c8765b --- /dev/null +++ b/src/toolkit/themes/shared/design-system/src/toolbar-css.patch @@ -0,0 +1,12 @@ +diff --git a/toolkit/themes/shared/design-system/src/toolbar.css b/toolkit/themes/shared/design-system/src/toolbar.css +index 61667b3f778a66b74aa7b169cd646a8437a5dd42..d1a9e5ad121a44f182e7061a21f4bbe9540cae8c 100644 +--- a/toolkit/themes/shared/design-system/src/toolbar.css ++++ b/toolkit/themes/shared/design-system/src/toolbar.css +@@ -61,7 +61,6 @@ + --toolbar-field-color: inherit; + + @media (prefers-color-scheme: light) { +- --toolbar-bgcolor: var(--color-white); + } + } + diff --git a/src/toolkit/themes/shared/in-content/common-shared-css.patch b/src/toolkit/themes/shared/in-content/common-shared-css.patch index 34e7e8bc9..605422e1c 100644 --- a/src/toolkit/themes/shared/in-content/common-shared-css.patch +++ b/src/toolkit/themes/shared/in-content/common-shared-css.patch @@ -1,5 +1,5 @@ diff --git a/toolkit/themes/shared/in-content/common-shared.css b/toolkit/themes/shared/in-content/common-shared.css -index 814075aef59969a7e8656974b4f46022f8e88783..53c5d857c15f0a9ec553a2bcfec66c2d2db121fa 100644 +index 31acab1e4b79d2ee54b64725480c3119b696c340..5f17d6222924ae9d2b4aba355f75f55c6d0fa7c5 100644 --- a/toolkit/themes/shared/in-content/common-shared.css +++ b/toolkit/themes/shared/in-content/common-shared.css @@ -54,7 +54,7 @@ diff --git a/src/toolkit/xre/nsXREDirProvider-cpp.patch b/src/toolkit/xre/nsXREDirProvider-cpp.patch index 17150fa97..2449bdc6c 100644 --- a/src/toolkit/xre/nsXREDirProvider-cpp.patch +++ b/src/toolkit/xre/nsXREDirProvider-cpp.patch @@ -1,5 +1,5 @@ diff --git a/toolkit/xre/nsXREDirProvider.cpp b/toolkit/xre/nsXREDirProvider.cpp -index 1b346ce2e62b6947e9f6b5f5f88ec16454847e09..301f896842a3f598b99eb3b9f617b361d423ca0e 100644 +index 7615c2c7cf75ce58b36fb5d37764125adcd06ba1..8b1b21dab0a62ab4621810f22076ec9cf6b6d51c 100644 --- a/toolkit/xre/nsXREDirProvider.cpp +++ b/toolkit/xre/nsXREDirProvider.cpp @@ -1342,9 +1342,11 @@ nsresult nsXREDirProvider::AppendFromAppData(nsIFile* aFile, bool aIsDotted) { diff --git a/src/tools/lint/eslint/__init__-py.patch b/src/tools/lint/eslint/__init__-py.patch index 880dae2c6..2020d5f77 100644 --- a/src/tools/lint/eslint/__init__-py.patch +++ b/src/tools/lint/eslint/__init__-py.patch @@ -1,8 +1,8 @@ diff --git a/tools/lint/eslint/__init__.py b/tools/lint/eslint/__init__.py -index cd45822500a8b5e1112efad81ed34e01c0dbcc19..9f47b4a46bf1c36db06b45e047a939ae08bcb703 100644 +index 4e7082179c632ac8fd1c4687329c2fe243cd6991..dadd614f8e7246ee1d390c0057af1536366653d6 100644 --- a/tools/lint/eslint/__init__.py +++ b/tools/lint/eslint/__init__.py -@@ -114,7 +114,7 @@ def lint(paths, config, binary=None, fix=None, rules=[], setup=None, **lintargs) +@@ -112,7 +112,7 @@ def lint(paths, config, binary=None, fix=None, rules=[], setup=None, **lintargs) [ binary, os.path.join( diff --git a/src/tools/update-packaging/common-sh.patch b/src/tools/update-packaging/common-sh.patch new file mode 100644 index 000000000..96aefa1cb --- /dev/null +++ b/src/tools/update-packaging/common-sh.patch @@ -0,0 +1,13 @@ +diff --git a/tools/update-packaging/common.sh b/tools/update-packaging/common.sh +index be2061b98cbba0d472e5f4d03cab2b058b249cb8..caa3379cd09efc2ded9686929fc658985a9dda51 100755 +--- a/tools/update-packaging/common.sh ++++ b/tools/update-packaging/common.sh +@@ -94,8 +94,6 @@ check_for_add_if_not_update() { + + if [[ "$(basename "$add_if_not_file_chk")" = "channel-prefs.js" || \ + "$add_if_not_file_chk" =~ (^|/)ChannelPrefs\.framework/ || \ +- "$(basename "$add_if_not_file_chk")" = "update-settings.ini" || \ +- "$add_if_not_file_chk" =~ (^|/)UpdateSettings\.framework/ || \ + "$(basename "$add_if_not_file_chk")" = "distribution.ini" ]]; then + ## "true" + return 0; diff --git a/src/widget/SwipeTracker-cpp.patch b/src/widget/SwipeTracker-cpp.patch index d90839d87..965a670ea 100644 --- a/src/widget/SwipeTracker-cpp.patch +++ b/src/widget/SwipeTracker-cpp.patch @@ -1,8 +1,8 @@ diff --git a/widget/SwipeTracker.cpp b/widget/SwipeTracker.cpp -index b09252fd60beb10d5865d226c39ee0c8a9c22d87..91f68161209c6ca3f3bac22997d4e2066f1fafec 100644 +index 887d06d3bd9cdaa934880e0ae7a11ec8b737fb61..979f93ddab0d661ac1a1d73e7a0ba27fa2c8f9b7 100644 --- a/widget/SwipeTracker.cpp +++ b/widget/SwipeTracker.cpp -@@ -5,6 +5,7 @@ +@@ -3,6 +3,7 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "SwipeTracker.h" @@ -10,7 +10,25 @@ index b09252fd60beb10d5865d226c39ee0c8a9c22d87..91f68161209c6ca3f3bac22997d4e206 #include "InputData.h" #include "mozilla/FlushType.h" -@@ -90,7 +91,7 @@ bool SwipeTracker::ComputeSwipeSuccess() const { +@@ -67,6 +68,9 @@ double SwipeTracker::SwipeSuccessTargetValue() const { + } + + double SwipeTracker::ClampToAllowedRange(double aGestureAmount) const { ++ if (StaticPrefs::zen_swipe_is_fast_swipe()) { ++ return aGestureAmount; ++ } + // gestureAmount needs to stay between -1 and 0 when swiping right and + // between 0 and 1 when swiping left. + double min = +@@ -84,13 +88,14 @@ bool SwipeTracker::ComputeSwipeSuccess() const { + // If the fingers were moving away from the target direction when they were + // lifted from the touchpad, abort the swipe. + if (mCurrentVelocity * targetValue < +- -StaticPrefs::widget_swipe_velocity_twitch_tolerance()) { ++ -StaticPrefs::widget_swipe_velocity_twitch_tolerance() ++ && !StaticPrefs::zen_swipe_is_fast_swipe()) { + return false; + } return (mGestureAmount * targetValue + mCurrentVelocity * targetValue * @@ -19,3 +37,13 @@ index b09252fd60beb10d5865d226c39ee0c8a9c22d87..91f68161209c6ca3f3bac22997d4e206 kSwipeSuccessThreshold; } +@@ -141,7 +146,8 @@ nsEventStatus SwipeTracker::ProcessEvent( + // display the UI as if we were at the success threshold as that would + // give a false indication that navigation would happen. + if (!computedSwipeSuccess && (eventAmount >= kSwipeSuccessThreshold || +- eventAmount <= -kSwipeSuccessThreshold)) { ++ eventAmount <= -kSwipeSuccessThreshold) ++ && !StaticPrefs::zen_swipe_is_fast_swipe()) { + eventAmount = 0.999 * kSwipeSuccessThreshold; + if (mGestureAmount < 0.f) { + eventAmount = -eventAmount; diff --git a/src/widget/cocoa/VibrancyManager-mm.patch b/src/widget/cocoa/VibrancyManager-mm.patch index e49e02f2b..00ea2b670 100644 --- a/src/widget/cocoa/VibrancyManager-mm.patch +++ b/src/widget/cocoa/VibrancyManager-mm.patch @@ -1,8 +1,8 @@ diff --git a/widget/cocoa/VibrancyManager.mm b/widget/cocoa/VibrancyManager.mm -index b4c14233c64dd3515518d3167f8ee2877fe41a93..2b91fa3044794765b8e15c6b66b348de4111f29f 100644 +index 5df70a63afb235d2db11712276bb63f756222a0f..a2865aa2748433cbfd956ae46d197200fbbcfadd 100644 --- a/widget/cocoa/VibrancyManager.mm +++ b/widget/cocoa/VibrancyManager.mm -@@ -13,6 +13,7 @@ +@@ -11,6 +11,7 @@ #include "nsCocoaWindow.h" #include "mozilla/StaticPrefs_widget.h" @@ -10,7 +10,17 @@ index b4c14233c64dd3515518d3167f8ee2877fe41a93..2b91fa3044794765b8e15c6b66b348de using namespace mozilla; -@@ -38,7 +39,23 @@ static NSVisualEffectMaterial VisualEffectMaterialForVibrancyType( +@@ -29,6 +30,9 @@ static NSVisualEffectState VisualEffectStateForVibrancyType( + case VibrancyType::Sidebar: + break; + } ++ if (!StaticPrefs::zen_view_grey_out_inactive_windows()) { ++ return NSVisualEffectStateActive; ++ } + return NSVisualEffectStateFollowsWindowActiveState; + } + +@@ -36,7 +40,23 @@ static NSVisualEffectMaterial VisualEffectMaterialForVibrancyType( VibrancyType aType) { switch (aType) { case VibrancyType::Sidebar: @@ -35,7 +45,7 @@ index b4c14233c64dd3515518d3167f8ee2877fe41a93..2b91fa3044794765b8e15c6b66b348de case VibrancyType::Titlebar: return NSVisualEffectMaterialTitlebar; } -@@ -78,6 +95,7 @@ - (NSView*)hitTest:(NSPoint)aPoint { +@@ -76,6 +96,7 @@ - (NSView*)hitTest:(NSPoint)aPoint { - (void)prefChanged { self.blendingMode = VisualEffectBlendingModeForVibrancyType(mType); @@ -43,7 +53,7 @@ index b4c14233c64dd3515518d3167f8ee2877fe41a93..2b91fa3044794765b8e15c6b66b348de } @end -@@ -88,6 +106,7 @@ static void PrefChanged(const char* aPref, void* aClosure) { +@@ -86,6 +107,7 @@ static void PrefChanged(const char* aPref, void* aClosure) { static constexpr nsLiteralCString kObservedPrefs[] = { "widget.macos.sidebar-blend-mode.behind-window"_ns, "widget.macos.titlebar-blend-mode.behind-window"_ns, diff --git a/src/widget/cocoa/nsCocoaUtils-mm.patch b/src/widget/cocoa/nsCocoaUtils-mm.patch new file mode 100644 index 000000000..f23ec88ef --- /dev/null +++ b/src/widget/cocoa/nsCocoaUtils-mm.patch @@ -0,0 +1,14 @@ +diff --git a/widget/cocoa/nsCocoaUtils.mm b/widget/cocoa/nsCocoaUtils.mm +index d83639ff2aab890d9c89c162ec5a3dfda52bc017..92ac99a3afb5931216486473490b004404110ce7 100644 +--- a/widget/cocoa/nsCocoaUtils.mm ++++ b/widget/cocoa/nsCocoaUtils.mm +@@ -1631,7 +1631,8 @@ bool static ShouldConsiderStartingSwipeFromEvent(NSEvent* anEvent) { + return [anEvent type] == NSEventTypeScrollWheel && + eventPhase == NSEventPhaseBegan && + [anEvent hasPreciseScrollingDeltas] && +- [NSEvent isSwipeTrackingFromScrollEventsEnabled]; ++ ([NSEvent isSwipeTrackingFromScrollEventsEnabled] || ++ Preferences::GetBool("zen.widget.macos.override-system-swipe-gestures")); + } + + PanGestureInput nsCocoaUtils::CreatePanGestureEvent( diff --git a/src/widget/cocoa/nsDragService-mm.patch b/src/widget/cocoa/nsDragService-mm.patch index 081b79b3e..67596f852 100644 --- a/src/widget/cocoa/nsDragService-mm.patch +++ b/src/widget/cocoa/nsDragService-mm.patch @@ -1,8 +1,8 @@ diff --git a/widget/cocoa/nsDragService.mm b/widget/cocoa/nsDragService.mm -index d5db98bff66a4daace6454af16406822d79fe912..522262b8d7ccba1f333755c48b0327cd098a5248 100644 +index 3b61c3a0d500e14e78d9009c821b47e70926cc97..b4cbc6e0dc0d43a55f074d4908dd414b859e16ed 100644 --- a/widget/cocoa/nsDragService.mm +++ b/widget/cocoa/nsDragService.mm -@@ -23,6 +23,7 @@ +@@ -22,6 +22,7 @@ #include "mozilla/PresShell.h" #include "mozilla/dom/Document.h" #include "mozilla/dom/DocumentInlines.h" @@ -10,7 +10,7 @@ index d5db98bff66a4daace6454af16406822d79fe912..522262b8d7ccba1f333755c48b0327cd #include "nsIContent.h" #include "nsCocoaUtils.h" #include "mozilla/gfx/2D.h" -@@ -147,6 +148,10 @@ +@@ -146,6 +147,10 @@ NSImage* nsDragSession::ConstructDragImage(nsINode* aDOMNode, bitsPerPixel:32]; uint8_t* dest = [imageRep bitmapData]; @@ -21,7 +21,7 @@ index d5db98bff66a4daace6454af16406822d79fe912..522262b8d7ccba1f333755c48b0327cd for (uint32_t i = 0; i < height; ++i) { uint8_t* src = map.mData + i * map.mStride; for (uint32_t j = 0; j < width; ++j) { -@@ -154,15 +159,15 @@ +@@ -153,15 +158,15 @@ NSImage* nsDragSession::ConstructDragImage(nsINode* aDOMNode, // is premultipled here. Also, Quartz likes RGBA, so do that translation // as well. #ifdef IS_BIG_ENDIAN diff --git a/src/xpfe/appshell/AppWindow-cpp.patch b/src/xpfe/appshell/AppWindow-cpp.patch index b7e51a153..a3b335610 100644 --- a/src/xpfe/appshell/AppWindow-cpp.patch +++ b/src/xpfe/appshell/AppWindow-cpp.patch @@ -1,8 +1,8 @@ diff --git a/xpfe/appshell/AppWindow.cpp b/xpfe/appshell/AppWindow.cpp -index f297266b3f7cb948d548f935a1feb1ea4b7be7d9..6bc589c1853bac48169b885581d4bdc89ad9e90a 100644 +index d980bca7b42bb8d81817756215067771d2793bfe..94992e9d96348e4c88e089e4cdc3234076efb656 100644 --- a/xpfe/appshell/AppWindow.cpp +++ b/xpfe/appshell/AppWindow.cpp -@@ -1849,7 +1849,7 @@ nsresult AppWindow::MaybeSaveEarlyWindowPersistentValues( +@@ -1847,7 +1847,7 @@ nsresult AppWindow::MaybeSaveEarlyWindowPersistentValues( } } diff --git a/src/zen/@types/lib.gecko.darwin.d.ts b/src/zen/@types/lib.gecko.darwin.d.ts index e83a0f0d2..f0ae454f1 100644 --- a/src/zen/@types/lib.gecko.darwin.d.ts +++ b/src/zen/@types/lib.gecko.darwin.d.ts @@ -233,7 +233,11 @@ declare global { } // global // Typedefs from xpidl. +type CSPDirective = nsIContentSecurityPolicy.CSPDirective; type PRTime = i64; +type RequireTrustedTypesForDirectiveState = + nsIContentSecurityPolicy.RequireTrustedTypesForDirectiveState; +type nsContentPolicyType = nsIContentPolicy.nsContentPolicyType; type nsTaskbarProgressState = i32; // XPCOM internal utility types. diff --git a/src/zen/@types/lib.gecko.dom.d.ts b/src/zen/@types/lib.gecko.dom.d.ts index 49cad66a9..1ac788e50 100644 --- a/src/zen/@types/lib.gecko.dom.d.ts +++ b/src/zen/@types/lib.gecko.dom.d.ts @@ -83,6 +83,10 @@ interface AnimationPropertyValueDetails { value?: string; } +interface AriaNotificationOptions { + priority?: AriaNotifyPriority; +} + interface AssignedNodesOptions { flatten?: boolean; } @@ -583,6 +587,7 @@ interface CookieChangeEventInit extends EventInit { interface CookieInit { domain?: string | null; expires?: DOMHighResTimeStamp | null; + maxAge?: number | null; name: string; partitioned?: boolean; path?: string; @@ -591,13 +596,7 @@ interface CookieInit { } interface CookieListItem { - domain?: string | null; - expires?: DOMHighResTimeStamp | null; name?: string; - partitioned?: boolean; - path?: string; - sameSite?: CookieSameSite; - secure?: boolean; value?: string; } @@ -619,6 +618,8 @@ interface CopyOptions { } interface CredentialCreationOptions { + digital?: DigitalCredentialCreationOptions; + mediation?: CredentialMediationRequirement; publicKey?: PublicKeyCredentialCreationOptions; signal?: AbortSignal; } @@ -628,6 +629,7 @@ interface CredentialPropertiesOutput { } interface CredentialRequestOptions { + digital?: DigitalCredentialRequestOptions; identity?: IdentityCredentialRequestOptions; mediation?: CredentialMediationRequirement; publicKey?: PublicKeyCredentialRequestOptions; @@ -761,6 +763,24 @@ interface DictWithAllowSharedBufferSource { arrayBufferView?: ArrayBufferView; } +interface DigitalCredentialCreateRequest { + data: any; + protocol: string; +} + +interface DigitalCredentialCreationOptions { + requests?: DigitalCredentialCreateRequest[]; +} + +interface DigitalCredentialGetRequest { + data: any; + protocol: string; +} + +interface DigitalCredentialRequestOptions { + requests: DigitalCredentialGetRequest[]; +} + interface DisplayMediaStreamConstraints { audio?: boolean | MediaTrackConstraints; video?: boolean | MediaTrackConstraints; @@ -781,6 +801,17 @@ interface DisplayNameResult { values?: string[]; } +interface DocumentPictureInPictureEventInit extends EventInit { + window: Window; +} + +interface DocumentPictureInPictureOptions { + disallowReturnToOpener?: boolean; + height?: number; + preferInitialWindowPlacement?: boolean; + width?: number; +} + interface DocumentTimelineOptions { originTime?: DOMHighResTimeStamp; } @@ -1157,7 +1188,7 @@ interface GPUExternalTextureDescriptor extends GPUObjectDescriptorBase { } interface GPUFragmentState extends GPUProgrammableStage { - targets: GPUColorTargetState[]; + targets: (GPUColorTargetState | null)[]; } interface GPUMultisampleState { @@ -1190,7 +1221,7 @@ interface GPUPipelineErrorInit { } interface GPUPipelineLayoutDescriptor extends GPUObjectDescriptorBase { - bindGroupLayouts: GPUBindGroupLayout[]; + bindGroupLayouts: (GPUBindGroupLayout | null)[]; } interface GPUPrimitiveState { @@ -1243,14 +1274,14 @@ interface GPURenderPassDepthStencilAttachment { } interface GPURenderPassDescriptor extends GPUObjectDescriptorBase { - colorAttachments: GPURenderPassColorAttachment[]; + colorAttachments: (GPURenderPassColorAttachment | null)[]; depthStencilAttachment?: GPURenderPassDepthStencilAttachment; occlusionQuerySet?: GPUQuerySet; timestampWrites?: GPURenderPassTimestampWrites; } interface GPURenderPassLayout extends GPUObjectDescriptorBase { - colorFormats: GPUTextureFormat[]; + colorFormats: (GPUTextureFormat | null)[]; depthStencilFormat?: GPUTextureFormat; sampleCount?: GPUSize32; } @@ -1357,6 +1388,7 @@ interface GPUTextureViewDescriptor extends GPUObjectDescriptorBase { dimension?: GPUTextureViewDimension; format?: GPUTextureFormat; mipLevelCount?: GPUIntegerCoordinate; + usage?: GPUTextureUsageFlags; } interface GPUUncapturedErrorEventInit extends EventInit { @@ -1402,6 +1434,11 @@ interface GamepadLightColor { red: number; } +interface GenerateTestReportParameters { + group?: string; + message: string; +} + interface GeometryUtilsOptions { createFramesForSuppressedWhitespace?: boolean; flush?: boolean; @@ -1471,6 +1508,15 @@ interface HeapSnapshotBoundaries { runtime?: boolean; } +interface HighlightHitResult { + highlight?: Highlight; + ranges?: AbstractRange[]; +} + +interface HighlightsFromPointOptions { + shadowRoots?: ShadowRoot[]; +} + interface IDBDatabaseInfo { name?: string; version?: number; @@ -1538,6 +1584,7 @@ interface ImageBitmapOptions { imageOrientation?: ImageOrientation; premultiplyAlpha?: PremultiplyAlpha; resizeHeight?: number; + resizeQuality?: ResizeQuality; resizeWidth?: number; } @@ -1570,6 +1617,11 @@ interface ImageEncodeOptions { type?: string; } +interface ImageSize { + height: number; + width: number; +} + interface ImageText { confidence: number; quad: DOMQuad; @@ -1616,6 +1668,18 @@ interface InputEventInit extends UIEventInit { targetRanges?: StaticRange[]; } +interface InputPickerColor { + alpha: number; + component1: number; + component2: number; + component3: number; +} + +interface InspectorAnchorElement { + element: Element; + type: InspectorAnchorType; +} + interface InspectorCSSPropertyDefinition { fromJS: boolean; inherits: boolean; @@ -1697,6 +1761,10 @@ interface IntersectionObserverInit { threshold?: number | number[]; } +interface InvokeToolOptions { + signal?: AbortSignal; +} + interface KeySystemTrackConfiguration { encryptionScheme?: string | null; robustness?: string; @@ -1760,8 +1828,10 @@ interface LibcConstants { FD_CLOEXEC?: number; F_SETFD?: number; F_SETFL?: number; + O_CLOEXEC?: number; O_CREAT?: number; O_NONBLOCK?: number; + O_PATH?: number; O_WRONLY?: number; POLLERR?: number; POLLHUP?: number; @@ -2002,6 +2072,12 @@ interface MediaConfiguration { video?: VideoConfiguration; } +interface MediaControllerPositionState { + duration: number; + playbackRate: number; + position: number; +} + interface MediaDecoderDebugInfo { PlayState?: string; channels?: number; @@ -2313,6 +2389,14 @@ interface MessageEventInit extends EventInit { source?: MessageEventSource | null; } +interface ModelContextTool { + annotations?: ToolAnnotations; + description: string; + execute: ToolExecuteCallback; + inputSchema?: any; + name: string; +} + interface MouseEventInit extends EventModifierInit { button?: number; buttons?: number; @@ -2420,7 +2504,7 @@ interface NavigateEventInit extends EventInit { } interface NavigationCurrentEntryChangeEventInit extends EventInit { - from: NavigationHistoryEntry; + from: NavigationHistoryEntry | null; navigationType?: NavigationType | null; } @@ -2472,14 +2556,19 @@ interface NotificationAction { interface NotificationOptions { actions?: NotificationAction[]; + badge?: string; body?: string; data?: any; dir?: NotificationDirection; icon?: string; + image?: string; lang?: string; + navigate?: string; + renotify?: boolean; requireInteraction?: boolean; silent?: boolean; tag?: string; + timestamp?: EpochTimeStamp; vibrate?: VibratePattern; } @@ -2556,8 +2645,19 @@ interface OscillatorOptions extends AudioNodeOptions { } interface PCErrorData { + errorDetail?: string; message: string; name: PCError; + sdpLineNumber?: number; +} + +interface PageRevealEventInit extends EventInit { + viewTransition?: ViewTransition | null; +} + +interface PageSwapEventInit extends EventInit { + activation?: NavigationActivation | null; + viewTransition?: ViewTransition | null; } interface PageTransitionEventInit extends EventInit { @@ -2905,6 +3005,7 @@ interface PointerEventInit extends MouseEventInit { } interface PopStateEventInit extends EventInit { + hasUAVisualTransition?: boolean; state?: any; } @@ -3023,7 +3124,6 @@ interface PublicKeyCredentialCreationOptions { interface PublicKeyCredentialCreationOptionsJSON { attestation?: string; - attestationFormats?: string[]; authenticatorSelection?: AuthenticatorSelectionCriteria; challenge: Base64URLString; excludeCredentials?: PublicKeyCredentialDescriptorJSON[]; @@ -3224,6 +3324,18 @@ interface RTCEncodedVideoFrameOptions { metadata?: RTCEncodedVideoFrameMetadata; } +interface RTCErrorEventInit extends EventInit { + error: RTCError; +} + +interface RTCErrorInit { + errorDetail: RTCErrorDetailType; + receivedAlert?: number; + sctpCauseCode?: number; + sdpLineNumber?: number; + sentAlert?: number; +} + interface RTCIceCandidateInit { candidate?: string; sdpMLineIndex?: number | null; @@ -3392,6 +3504,14 @@ interface RTCOutboundRtpStreamStats extends RTCSentRtpStreamStats { totalEncodedBytesTarget?: number; } +interface RTCPeerConnectionIceErrorEventInit extends EventInit { + address?: string | null; + errorCode: number; + errorText?: string; + port?: number | null; + url?: string; +} + interface RTCPeerConnectionIceEventInit extends EventInit { candidate?: RTCIceCandidate | null; } @@ -3639,10 +3759,10 @@ interface ReceiveMessageArgument { data?: any; json?: any; name: string; - ports?: MessagePort[]; + ports: MessagePort[]; sync: boolean; target: nsISupports; - targetFrameLoader?: FrameLoader; + targetFrameLoader?: FrameLoader | null; } interface RedirectBlockedEventInit extends EventInit { @@ -3676,6 +3796,7 @@ interface ReportingObserverOptions { interface RequestInit { body?: BodyInit | null; cache?: RequestCache; + cookieJarSettings?: nsICookieJarSettings; credentials?: RequestCredentials; headers?: HeadersInit; integrity?: string; @@ -3805,7 +3926,7 @@ interface ShadowRootInit { clonable?: boolean; delegatesFocus?: boolean; mode: ShadowRootMode; - referenceTarget?: string; + referenceTarget?: string | null; serializable?: boolean; slotAssignment?: SlotAssignmentMode; } @@ -3862,6 +3983,11 @@ interface SplitRelativeOptions { allowParentDir?: boolean; } +interface StartViewTransitionOptions { + types?: string[] | null; + update?: ViewTransitionUpdateCallback | null; +} + interface StaticRangeInit { endContainer: Node; endOffset: number; @@ -3924,24 +4050,47 @@ interface SvcOutputMetadata { temporalLayerId?: number; } -interface SynthesizeMouseEventData { - button?: number; - buttons?: number; - clickCount?: number; +interface SynthesizeEventData { identifier?: number; - inputSource?: number; - modifiers?: number; pressure?: number; } -interface SynthesizeMouseEventOptions { - ignoreRootScrollFrame?: boolean; +interface SynthesizeEventOptions { isAsyncEnabled?: boolean; isDOMEventSynthesized?: boolean; - isWidgetEventSynthesized?: boolean; toWindow?: boolean; } +interface SynthesizeMouseEventData extends SynthesizeEventData { + button?: number; + buttons?: number; + clickCount?: number; + inputSource?: number; + modifiers?: number; +} + +interface SynthesizeMouseEventOptions extends SynthesizeEventOptions { + ignoreRootScrollFrame?: boolean; + isWidgetEventSynthesized?: boolean; +} + +interface SynthesizeTouchEventData extends SynthesizeEventData { + altitudeAngle?: number; + azimuthAngle?: number; + offsetX: number; + offsetY: number; + radiiX?: number; + radiiY?: number; + rotationAngle?: number; + tiltX?: number; + tiltY?: number; + twist?: number; +} + +interface SynthesizeTouchEventOptions extends SynthesizeEventOptions { + isPen?: boolean; +} + interface TCPServerSocketEventInit extends EventInit { socket?: TCPSocket | null; } @@ -4029,6 +4178,10 @@ interface TogglePopoverOptions extends ShowPopoverOptions { force?: boolean; } +interface ToolAnnotations { + readOnlyHint?: boolean; +} + interface TouchEventInit extends EventModifierInit { changedTouches?: Touch[]; targetTouches?: Touch[]; @@ -4337,6 +4490,7 @@ interface WebExtensionInit { baseURL: string; contentScripts?: WebExtensionContentScriptInit[]; extensionPageCSP?: string | null; + hasRecommendedState?: boolean; id: string; ignoreQuarantine?: boolean; isPrivileged?: boolean; @@ -4348,6 +4502,7 @@ interface WebExtensionInit { readyPromise?: Promise; temporarilyInstalled?: boolean; type?: string; + version?: string; webAccessibleResources?: WebAccessibleResourceInit[]; } @@ -4513,6 +4668,15 @@ interface WorkletOptions { credentials?: RequestCredentials; } +interface WriteJSONOptions extends WriteOptions { + lengthHint?: number; +} + +interface WriteJSONResult { + jsonLength: number; + size: number; +} + interface WriteOptions { backupFile?: string; compress?: boolean; @@ -4708,6 +4872,10 @@ interface ARIAMixin { role: string | null; } +interface ARIANotifyMixin { + ariaNotify(announcement: string, options?: AriaNotificationOptions): void; +} + interface AbortController { readonly signal: AbortSignal; abort(reason?: any): void; @@ -4765,6 +4933,7 @@ interface AbstractRange { readonly endOffset: number; readonly startContainer: Node; readonly startOffset: number; + getShrunkenRangeToVisibleLeaves(): StaticRange | null; } declare var AbstractRange: { @@ -5851,6 +6020,7 @@ declare var BroadcastChannel: { interface BrowsingContext extends LoadContextMixin { allowJavascript: boolean; readonly ancestorsAreCurrent: boolean; + animationsPlayBackRateMultiplier: number; authorStyleDisabledDefault: boolean; browserId: number; readonly childOffset: number; @@ -5877,6 +6047,7 @@ interface BrowsingContext extends LoadContextMixin { readonly isActive: boolean; isAppTab: boolean; readonly isDiscarded: boolean; + readonly isDocumentPiP: boolean; readonly isInBFCache: boolean; languageOverride: string; mediumOverride: string; @@ -5901,9 +6072,11 @@ interface BrowsingContext extends LoadContextMixin { getAllBrowsingContextsInSubtree(): BrowsingContext[]; resetNavigationRateLimit(): void; resetOrientationOverride(): void; + resetScreenAreaOverride(): void; setGeolocationServiceOverride(position?: nsIDOMGeoPosition): void; setOrientationOverride(type: OrientationType, rotationAngle: number): void; setRDMPaneMaxTouchPoints(maxTouchPoints: number): void; + setScreenAreaOverride(screenWidth: number, screenHeight: number): void; } declare var BrowsingContext: { @@ -5998,6 +6171,14 @@ declare var CSSAnimation: { isInstance: IsInstance; }; +interface CSSAppearanceBaseRule extends CSSGroupingRule {} + +declare var CSSAppearanceBaseRule: { + prototype: CSSAppearanceBaseRule; + new (): CSSAppearanceBaseRule; + isInstance: IsInstance; +}; + interface CSSColor extends CSSColorValue { alpha: CSSNumberish; channels: CSSNumericValue[]; @@ -6089,8 +6270,31 @@ declare var CSSCustomPropertyRegisteredEvent: { isInstance: IsInstance; }; +interface CSSFontFaceDescriptors extends CSSStyleDeclaration { + ascentOverride: string; + descentOverride: string; + fontDisplay: string; + fontFamily: string; + fontFeatureSettings: string; + fontLanguageOverride: string; + fontStretch: string; + fontStyle: string; + fontVariationSettings: string; + fontWeight: string; + lineGapOverride: string; + sizeAdjust: string; + src: string; + unicodeRange: string; +} + +declare var CSSFontFaceDescriptors: { + prototype: CSSFontFaceDescriptors; + new (): CSSFontFaceDescriptors; + isInstance: IsInstance; +}; + interface CSSFontFaceRule extends CSSRule { - readonly style: CSSStyleDeclaration; + readonly style: CSSFontFaceDescriptors; } declare var CSSFontFaceRule: { @@ -6512,10 +6716,14 @@ declare var CSSOKLab: { interface CSSPageDescriptors extends CSSStyleDeclaration { margin: string; + marginBlock: string; marginBottom: string; + marginInline: string; marginLeft: string; marginRight: string; marginTop: string; + paddingBlock: string; + paddingInline: string; pageOrientation: string; size: string; } @@ -6542,6 +6750,8 @@ declare var CSSPerspective: { }; interface CSSPositionTryDescriptors extends CSSStyleDeclaration { + MozMarginEnd: string; + MozMarginStart: string; alignSelf: string; blockSize: string; bottom: string; @@ -6575,11 +6785,14 @@ interface CSSPositionTryDescriptors extends CSSStyleDeclaration { minHeight: string; minInlineSize: string; minWidth: string; + paddingBlock: string; + paddingInline: string; placeSelf: string; positionAnchor: string; positionArea: string; right: string; top: string; + webkitAlignSelf: string; width: string; } @@ -6776,6 +6989,7 @@ interface CSSStyleDeclaration { getCSSImageURLs(property: string): string[]; getPropertyPriority(property: string): string; getPropertyValue(property: string): string; + hasLonghandProperty(property: string): boolean; item(index: number): string; removeProperty(property: string): string; setProperty(property: string, value: string | null, priority?: string): void; @@ -6800,7 +7014,15 @@ interface CSSStyleProperties extends CSSStyleDeclaration { MozAnimationTimingFunction: string; MozAppearance: string; MozBackfaceVisibility: string; + MozBorderEnd: string; + MozBorderEndColor: string; + MozBorderEndStyle: string; + MozBorderEndWidth: string; MozBorderImage: string; + MozBorderStart: string; + MozBorderStartColor: string; + MozBorderStartStyle: string; + MozBorderStartWidth: string; MozBoxAlign: string; MozBoxCollapse: string; MozBoxDirection: string; @@ -6816,11 +7038,17 @@ interface CSSStyleProperties extends CSSStyleDeclaration { MozFontFeatureSettings: string; MozFontLanguageOverride: string; MozForceBrokenImageIcon: string; + MozHyphens: string; + MozMarginEnd: string; + MozMarginStart: string; MozOrient: string; MozOsxFontSmoothing: string; + MozPaddingEnd: string; + MozPaddingStart: string; MozPerspective: string; MozPerspectiveOrigin: string; MozSubtreeHiddenOnlyVisually: string; + MozTabSize: string; MozTextSizeAdjust: string; MozTheme: string; MozTransform: string; @@ -6832,6 +7060,7 @@ interface CSSStyleProperties extends CSSStyleDeclaration { MozTransitionProperty: string; MozTransitionTimingFunction: string; MozUserFocus: string; + MozUserSelect: string; MozWindowDragging: string; MozWindowInputRegionMargin: string; MozWindowOpacity: string; @@ -6841,6 +7070,7 @@ interface CSSStyleProperties extends CSSStyleDeclaration { alignContent: string; alignItems: string; alignSelf: string; + alignmentBaseline: string; all: string; anchorName: string; anchorScope: string; @@ -6853,6 +7083,9 @@ interface CSSStyleProperties extends CSSStyleDeclaration { animationIterationCount: string; animationName: string; animationPlayState: string; + animationRange: string; + animationRangeEnd: string; + animationRangeStart: string; animationTimeline: string; animationTimingFunction: string; appearance: string; @@ -6871,6 +7104,7 @@ interface CSSStyleProperties extends CSSStyleDeclaration { backgroundPositionY: string; backgroundRepeat: string; backgroundSize: string; + baselineShift: string; baselineSource: string; blockSize: string; border: string; @@ -6948,6 +7182,7 @@ interface CSSStyleProperties extends CSSStyleDeclaration { clipPath: string; clipRule: string; color: string; + colorAdjust: string; colorInterpolation: string; colorInterpolationFilters: string; colorScheme: string; @@ -7033,9 +7268,12 @@ interface CSSStyleProperties extends CSSStyleDeclaration { gridAutoRows: string; gridColumn: string; gridColumnEnd: string; + gridColumnGap: string; gridColumnStart: string; + gridGap: string; gridRow: string; gridRowEnd: string; + gridRowGap: string; gridRowStart: string; gridTemplate: string; gridTemplateAreas: string; @@ -7231,6 +7469,9 @@ interface CSSStyleProperties extends CSSStyleDeclaration { textAlignLast: string; textAnchor: string; textAutospace: string; + textBox: string; + textBoxEdge: string; + textBoxTrim: string; textCombineUpright: string; textDecoration: string; textDecorationColor: string; @@ -7255,6 +7496,7 @@ interface CSSStyleProperties extends CSSStyleDeclaration { textWrap: string; textWrapMode: string; textWrapStyle: string; + timelineScope: string; top: string; touchAction: string; transform: string; @@ -7279,18 +7521,84 @@ interface CSSStyleProperties extends CSSStyleDeclaration { viewTransitionClass: string; viewTransitionName: string; visibility: string; + webkitAlignContent: string; + webkitAlignItems: string; + webkitAlignSelf: string; + webkitAnimation: string; + webkitAnimationDelay: string; + webkitAnimationDirection: string; + webkitAnimationDuration: string; + webkitAnimationFillMode: string; + webkitAnimationIterationCount: string; + webkitAnimationName: string; + webkitAnimationPlayState: string; + webkitAnimationTimingFunction: string; + webkitAppearance: string; + webkitBackfaceVisibility: string; + webkitBackgroundClip: string; + webkitBackgroundOrigin: string; + webkitBackgroundSize: string; + webkitBorderBottomLeftRadius: string; + webkitBorderBottomRightRadius: string; + webkitBorderImage: string; + webkitBorderRadius: string; + webkitBorderTopLeftRadius: string; + webkitBorderTopRightRadius: string; + webkitBoxAlign: string; + webkitBoxDirection: string; + webkitBoxFlex: string; + webkitBoxOrdinalGroup: string; + webkitBoxOrient: string; + webkitBoxPack: string; + webkitBoxShadow: string; + webkitBoxSizing: string; + webkitClipPath: string; + webkitFilter: string; + webkitFlex: string; + webkitFlexBasis: string; + webkitFlexDirection: string; + webkitFlexFlow: string; + webkitFlexGrow: string; + webkitFlexShrink: string; + webkitFlexWrap: string; + webkitFontFeatureSettings: string; + webkitJustifyContent: string; webkitLineClamp: string; + webkitMask: string; + webkitMaskClip: string; + webkitMaskComposite: string; + webkitMaskImage: string; + webkitMaskOrigin: string; + webkitMaskPosition: string; + webkitMaskPositionX: string; + webkitMaskPositionY: string; + webkitMaskRepeat: string; + webkitMaskSize: string; + webkitOrder: string; + webkitPerspective: string; + webkitPerspectiveOrigin: string; webkitTextFillColor: string; webkitTextSecurity: string; + webkitTextSizeAdjust: string; webkitTextStroke: string; webkitTextStrokeColor: string; webkitTextStrokeWidth: string; + webkitTransform: string; + webkitTransformOrigin: string; + webkitTransformStyle: string; + webkitTransition: string; + webkitTransitionDelay: string; + webkitTransitionDuration: string; + webkitTransitionProperty: string; + webkitTransitionTimingFunction: string; + webkitUserSelect: string; whiteSpace: string; whiteSpaceCollapse: string; width: string; willChange: string; wordBreak: string; wordSpacing: string; + wordWrap: string; writingMode: string; x: string; y: string; @@ -7563,6 +7871,7 @@ interface CanonicalBrowsingContext extends BrowsingContext { readonly isUnderHiddenEmbedderElement: boolean; readonly mediaController: MediaController | null; readonly mostRecentLoadingSessionHistoryEntry: nsISHEntry | null; + readonly scopedPrefs: nsIScopedPrefs | null; readonly secureBrowserUI: nsISecureBrowserUI | null; readonly sessionHistory: nsISHistory | null; targetTopLevelLinkClicksToBlank: boolean; @@ -8126,7 +8435,6 @@ declare var ChildProcessMessageManager: { interface ChildSHistory { readonly count: number; readonly index: number; - readonly legacySHistory: nsISHistory; canGo(aOffset: number, aRequireUserInteraction?: boolean): boolean; go( aOffset: number, @@ -9215,12 +9523,6 @@ interface DeprecationReportBody extends ReportBody { toJSON(): any; } -declare var DeprecationReportBody: { - prototype: DeprecationReportBody; - new (): DeprecationReportBody; - isInstance: IsInstance; -}; - interface DeviceAcceleration { readonly x: number | null; readonly y: number | null; @@ -9290,6 +9592,20 @@ interface DeviceRotationRate { readonly gamma: number | null; } +/** Available only in secure contexts. */ +interface DigitalCredential extends Credential { + readonly data: any; + readonly protocol: string; + toJSON(): any; +} + +declare var DigitalCredential: { + prototype: DigitalCredential; + new (): DigitalCredential; + isInstance: IsInstance; + userAgentAllowsProtocol(protocol: string): boolean; +}; + interface Directory { readonly name: string; readonly path: string; @@ -9319,6 +9635,7 @@ interface DocumentEventMap interface Document extends Node, + ARIANotifyMixin, DocumentOrShadowRoot, FontFaceSource, GeometryUtils, @@ -9329,6 +9646,7 @@ interface Document TouchEventHandlers, XPathEvaluatorMixin { readonly URL: string; + readonly activeViewTransition: ViewTransition | null; alinkColor: string; readonly all: HTMLAllCollection; readonly anchors: HTMLCollection; @@ -9379,6 +9697,7 @@ interface Document readonly inputEncoding: string; readonly isInitialDocument: boolean; readonly isSrcdocDocument: boolean; + readonly isUncommittedInitialDocument: boolean; readonly l10n: DocumentL10n | null; readonly lastModified: string; readonly lastStyleSheetSet: string | null; @@ -9416,6 +9735,7 @@ interface Document readonly styleSheetSets: DOMStringList; readonly timeline: DocumentTimeline; title: string; + readonly tlsCertificateBindingURI: URI | null; readonly visibilityState: VisibilityState; vlinkColor: string; addCertException(isTemporary: boolean): Promise; @@ -9428,10 +9748,10 @@ interface Document y: number, options?: CaretPositionFromPointOptions ): CaretPosition | null; + caretRangeFromPoint(x?: number, y?: number): Range | null; clear(): void; clearUserGestureActivation(): void; close(): void; - completeStorageAccessRequestFromSite(serializedSite: string): Promise; consumeTransientUserGestureActivation(): boolean; createAttribute(name: string): Attr; createAttributeNS(namespace: string | null, name: string): Attr; @@ -9475,6 +9795,9 @@ interface Document createEvent(eventInterface: "DeviceLightEvent"): DeviceLightEvent; createEvent(eventInterface: "DeviceMotionEvent"): DeviceMotionEvent; createEvent(eventInterface: "DeviceOrientationEvent"): DeviceOrientationEvent; + createEvent( + eventInterface: "DocumentPictureInPictureEvent" + ): DocumentPictureInPictureEvent; createEvent(eventInterface: "DragEvent"): DragEvent; createEvent(eventInterface: "ErrorEvent"): ErrorEvent; createEvent(eventInterface: "FocusEvent"): FocusEvent; @@ -9520,6 +9843,8 @@ interface Document createEvent( eventInterface: "OfflineAudioCompletionEvent" ): OfflineAudioCompletionEvent; + createEvent(eventInterface: "PageRevealEvent"): PageRevealEvent; + createEvent(eventInterface: "PageSwapEvent"): PageSwapEvent; createEvent(eventInterface: "PageTransitionEvent"): PageTransitionEvent; createEvent( eventInterface: "PaymentMethodChangeEvent" @@ -9538,6 +9863,10 @@ interface Document createEvent(eventInterface: "PromiseRejectionEvent"): PromiseRejectionEvent; createEvent(eventInterface: "RTCDTMFToneChangeEvent"): RTCDTMFToneChangeEvent; createEvent(eventInterface: "RTCDataChannelEvent"): RTCDataChannelEvent; + createEvent(eventInterface: "RTCErrorEvent"): RTCErrorEvent; + createEvent( + eventInterface: "RTCPeerConnectionIceErrorEvent" + ): RTCPeerConnectionIceErrorEvent; createEvent( eventInterface: "RTCPeerConnectionIceEvent" ): RTCPeerConnectionIceEvent; @@ -9685,13 +10014,12 @@ interface Document thirdPartyOrigin: string, requireUserInteraction?: boolean ): Promise; - requestStorageAccessUnderSite(serializedSite: string): Promise; setKeyPressEventModel(aKeyPressEventModel: number): void; setNotifyFetchSuccess(aShouldNotify: boolean): void; setNotifyFormOrPasswordRemoved(aShouldNotify: boolean): void; setSuppressedEventListener(aListener: EventListener | null): void; startViewTransition( - updateCallback?: ViewTransitionUpdateCallback + callbackOptions?: ViewTransitionUpdateCallback | StartViewTransitionOptions ): ViewTransition; synchronouslyUpdateRemoteBrowserDimensions(aIncludeInactive?: boolean): void; userInteractionForTesting(): void; @@ -9752,6 +10080,7 @@ interface DocumentL10n extends DOMLocalization { interface DocumentOrShadowRoot { readonly activeElement: Element | null; adoptedStyleSheets: CSSStyleSheet[]; + readonly customElementRegistry: CustomElementRegistry | null; readonly fullscreenElement: Element | null; readonly mozFullScreenElement: Element | null; readonly pointerLockElement: Element | null; @@ -9763,6 +10092,63 @@ interface DocumentOrShadowRoot { nodesFromPoint(x: number, y: number): Node[]; } +interface DocumentPictureInPictureEventMap { + enter: Event; +} + +/** Available only in secure contexts. */ +interface DocumentPictureInPicture extends EventTarget { + onenter: ((this: DocumentPictureInPicture, ev: Event) => any) | null; + readonly window: Window | null; + requestWindow(options?: DocumentPictureInPictureOptions): Promise; + addEventListener( + type: K, + listener: ( + this: DocumentPictureInPicture, + ev: DocumentPictureInPictureEventMap[K] + ) => any, + options?: boolean | AddEventListenerOptions + ): void; + addEventListener( + type: string, + listener: EventListenerOrEventListenerObject, + options?: boolean | AddEventListenerOptions + ): void; + removeEventListener( + type: K, + listener: ( + this: DocumentPictureInPicture, + ev: DocumentPictureInPictureEventMap[K] + ) => any, + options?: boolean | EventListenerOptions + ): void; + removeEventListener( + type: string, + listener: EventListenerOrEventListenerObject, + options?: boolean | EventListenerOptions + ): void; +} + +declare var DocumentPictureInPicture: { + prototype: DocumentPictureInPicture; + new (): DocumentPictureInPicture; + isInstance: IsInstance; +}; + +/** Available only in secure contexts. */ +interface DocumentPictureInPictureEvent extends Event { + readonly window: Window; +} + +declare var DocumentPictureInPictureEvent: { + prototype: DocumentPictureInPictureEvent; + new ( + type: string, + eventInitDict: DocumentPictureInPictureEventInit + ): DocumentPictureInPictureEvent; + isInstance: IsInstance; +}; + interface DocumentTimeline extends AnimationTimeline {} declare var DocumentTimeline: { @@ -9930,6 +10316,7 @@ interface Element extends Node, ARIAMixin, + ARIANotifyMixin, Animatable, ChildNode, GeometryUtils, @@ -9946,6 +10333,7 @@ interface Element readonly clientWidth: number; readonly clientWidthDouble: number; readonly currentCSSZoom: number; + readonly customElementRegistry: CustomElementRegistry | null; readonly firstLineBoxBSize: number; readonly fontSizeInflation: number; readonly hasVisibleScrollbars: boolean; @@ -10099,7 +10487,7 @@ interface ElementCSSInlineStyle { } interface ElementInternals extends ARIAMixin { - readonly form: HTMLFormElement | null; + readonly form: Element | null; readonly labels: NodeList; readonly shadowRoot: ShadowRoot | null; readonly states: CustomStateSet; @@ -12025,7 +12413,11 @@ declare var GleanImpl: { isInstance: IsInstance; }; -interface GleanLabeled {} +interface GleanLabeled { + testGetValue( + aPingName?: string + ): Record | null; +} declare var GleanLabeled: { prototype: GleanLabeled; @@ -12761,7 +13153,7 @@ interface HTMLButtonElement extends HTMLElement, PopoverInvokerElement { command: string; commandForElement: Element | null; disabled: boolean; - readonly form: HTMLFormElement | null; + readonly form: Element | null; formAction: string; formEnctype: string; formMethod: string; @@ -13220,7 +13612,7 @@ declare var HTMLEmbedElement: { interface HTMLFieldSetElement extends HTMLElement { disabled: boolean; readonly elements: HTMLCollection; - readonly form: HTMLFormElement | null; + readonly form: Element | null; name: string; readonly type: string; readonly validationMessage: string; @@ -13681,11 +14073,13 @@ interface HTMLInputElement PopoverInvokerElement { accept: string; align: string; + alpha: boolean; alt: string; autocomplete: string; autofillState: string; capture: string; checked: boolean; + colorSpace: string; readonly controllers: XULControllers | null; readonly dateTimeBoxElement: Element | null; defaultChecked: boolean; @@ -13693,7 +14087,7 @@ interface HTMLInputElement dirName: string; disabled: boolean; files: FileList | null; - readonly form: HTMLFormElement | null; + readonly form: Element | null; formAction: string; formEnctype: string; formMethod: string; @@ -13704,7 +14098,7 @@ interface HTMLInputElement indeterminate: boolean; readonly labels: NodeList | null; readonly lastInteractiveValue: string; - readonly list: HTMLDataListElement | null; + readonly list: Element | null; max: string; maxLength: number; min: string; @@ -13738,6 +14132,7 @@ interface HTMLInputElement checkValidity(): boolean; closeDateTimePicker(): void; getAutocompleteInfo(): AutocompleteInfo | null; + getColor(): InputPickerColor; getDateTimeInputBoxValue(): DateTimeValue; getFilesAndDirectories(): Promise<(File | Directory)[]>; getMaximum(): number; @@ -13754,8 +14149,8 @@ interface HTMLInputElement reportValidity(): boolean; select(): void; setCustomValidity(error: string): void; - setDateTimePickerState(aIsOpen: boolean): void; setFocusState(aIsFocused: boolean): void; + setOpenState(aIsOpen: boolean): void; setRangeText(replacement: string): void; setRangeText( replacement: string, @@ -13764,10 +14159,10 @@ interface HTMLInputElement selectionMode?: SelectionMode ): void; setSelectionRange(start: number, end: number, direction?: string): void; + setUserInputColor(aColor: InputPickerColor): void; showPicker(): void; stepDown(n?: number): void; stepUp(n?: number): void; - updateDateTimePicker(value?: DateTimeValue): void; updateValidityState(): void; addEventListener( type: K, @@ -13833,7 +14228,7 @@ declare var HTMLLIElement: { interface HTMLLabelElement extends HTMLElement { readonly control: HTMLElement | null; - readonly form: HTMLFormElement | null; + readonly form: Element | null; htmlFor: string; addEventListener( type: K, @@ -13865,7 +14260,7 @@ declare var HTMLLabelElement: { interface HTMLLegendElement extends HTMLElement { align: string; - readonly form: HTMLFormElement | null; + readonly form: Element | null; addEventListener( type: K, listener: (this: HTMLLegendElement, ev: HTMLElementEventMap[K]) => any, @@ -14080,6 +14475,7 @@ interface HTMLMediaElement extends HTMLElement { language?: string ): TextTrack; canPlayType(type: string): string; + captureStream(): MediaStream; fastSeek(time: number): void; hasSuspendTaint(): boolean; load(): void; @@ -14324,7 +14720,7 @@ interface HTMLObjectElement readonly contentWindow: WindowProxy | null; data: string; declare: boolean; - readonly form: HTMLFormElement | null; + readonly form: Element | null; height: string; hspace: number; name: string; @@ -14405,7 +14801,7 @@ declare var HTMLOptGroupElement: { interface HTMLOptionElement extends HTMLElement { defaultSelected: boolean; disabled: boolean; - readonly form: HTMLFormElement | null; + readonly form: Element | null; readonly index: number; label: string; selected: boolean; @@ -14465,7 +14861,7 @@ interface HTMLOrForeignElement { interface HTMLOutputElement extends HTMLElement { defaultValue: string; - readonly form: HTMLFormElement | null; + readonly form: Element | null; readonly htmlFor: DOMTokenList; readonly labels: NodeList; name: string; @@ -14741,7 +15137,7 @@ interface HTMLSelectElement extends HTMLElement { autocomplete: string; autofillState: string; disabled: boolean; - readonly form: HTMLFormElement | null; + readonly form: Element | null; readonly isCombobox: boolean; readonly labels: NodeList; length: number; @@ -15187,7 +15583,7 @@ interface HTMLTemplateElement extends HTMLElement { shadowRootClonable: boolean; shadowRootDelegatesFocus: boolean; shadowRootMode: string; - shadowRootReferenceTarget: string; + shadowRootReferenceTarget: string | null; shadowRootSerializable: boolean; addEventListener( type: K, @@ -15225,7 +15621,7 @@ interface HTMLTextAreaElement extends HTMLElement, MozEditableElement { defaultValue: string; dirName: string; disabled: boolean; - readonly form: HTMLFormElement | null; + readonly form: Element | null; readonly labels: NodeList; maxLength: number; minLength: number; @@ -15566,6 +15962,11 @@ declare var Highlight: { }; interface HighlightRegistry { + highlightsFromPoint( + x: number, + y: number, + options?: HighlightsFromPointOptions + ): HighlightHitResult[]; forEach( callbackfn: ( value: Highlight, @@ -16129,6 +16530,7 @@ interface ImageTrack { readonly frameCount: number; readonly repetitionCount: number; selected: boolean; + getSizes(): ImageSize[]; } declare var ImageTrack: { @@ -16264,6 +16666,21 @@ declare var InspectorFontFace: { isInstance: IsInstance; }; +interface IntegrityViolationReportBody extends ReportBody { + readonly blockedURL: string; + readonly destination: string; + readonly documentURL: string; + readonly reason: IntegrityViolationReason | null; + readonly reportOnly: boolean; + toJSON(): any; +} + +declare var IntegrityViolationReportBody: { + prototype: IntegrityViolationReportBody; + new (): IntegrityViolationReportBody; + isInstance: IsInstance; +}; + interface IntersectionObserver { readonly root: Node | null; readonly rootMargin: string; @@ -16558,18 +16975,6 @@ declare var KeyEvent: { }; interface KeyEventMixin { - initKeyEvent( - type: string, - canBubble?: boolean, - cancelable?: boolean, - view?: Window | null, - ctrlKey?: boolean, - altKey?: boolean, - shiftKey?: boolean, - metaKey?: boolean, - keyCode?: number, - charCode?: number - ): void; readonly DOM_VK_CANCEL: 0x03; readonly DOM_VK_HELP: 0x06; readonly DOM_VK_BACK_SPACE: 0x08; @@ -17052,7 +17457,6 @@ interface L10nRegistry { aResourceIds: L10nResourceId[] ): FluentBundleIterator; getAvailableLocales(): string[]; - getSource(aName: string): L10nFileSource | null; getSourceNames(): string[]; hasSource(aName: string): boolean; registerSources(aSources: L10nFileSource[]): void; @@ -17144,6 +17548,7 @@ declare var Localization: { }; interface Location { + readonly ancestorOrigins: DOMStringList; hash: string; host: string; hostname: string; @@ -17591,7 +17996,9 @@ interface MediaControllerEventMap { interface MediaController extends EventTarget { readonly id: number; readonly isActive: boolean; + readonly isAnyMediaBeingControlled: boolean; readonly isAudible: boolean; + readonly isBeingUsedInPIPModeOrFullscreen: boolean; readonly isPlaying: boolean; onactivated: ((this: MediaController, ev: Event) => any) | null; ondeactivated: ((this: MediaController, ev: Event) => any) | null; @@ -17603,6 +18010,7 @@ interface MediaController extends EventTarget { readonly supportedKeys: MediaControlKey[]; focus(): void; getMetadata(): MediaMetadataInit; + getPositionState(): MediaControllerPositionState; nextTrack(): void; pause(): void; play(): void; @@ -18322,12 +18730,7 @@ interface MessageListenerManagerMixin { listener: MessageListener, listenWhenClosed?: boolean ): void; - addWeakMessageListener(messageName: string, listener: MessageListener): void; removeMessageListener(messageName: string, listener: MessageListener): void; - removeWeakMessageListener( - messageName: string, - listener: MessageListener - ): void; } interface MessageManagerGlobal { @@ -18420,6 +18823,35 @@ declare var MimeTypeArray: { isInstance: IsInstance; }; +/** Available only in secure contexts. */ +interface ModelContext { + getTools(): ModelContextTool[]; + invokeTool( + toolName: string, + input?: any, + options?: InvokeToolOptions + ): Promise; + registerTool(tool: ModelContextTool): void; + unregisterTool(toolName: string): void; +} + +declare var ModelContext: { + prototype: ModelContext; + new (): ModelContext; + isInstance: IsInstance; +}; + +/** Available only in secure contexts. */ +interface ModelContextClient { + requestUserInteraction(callback: UserInteractionCallback): Promise; +} + +declare var ModelContextClient: { + prototype: ModelContextClient; + new (): ModelContextClient; + isInstance: IsInstance; +}; + interface MouseEvent extends UIEvent { readonly altKey: boolean; readonly button: number; @@ -18849,7 +19281,7 @@ declare var NavigationActivation: { }; interface NavigationCurrentEntryChangeEvent extends Event { - readonly from: NavigationHistoryEntry; + readonly from: NavigationHistoryEntry | null; readonly navigationType: NavigationType | null; } @@ -18924,6 +19356,7 @@ declare var NavigationHistoryEntry: { }; interface NavigationPrecommitController { + addHandler(handler: NavigationInterceptHandler): void; redirect(url: string | URL, options?: NavigationNavigateOptions): void; } @@ -18991,6 +19424,7 @@ interface Navigator readonly mediaDevices: MediaDevices; readonly mediaSession: MediaSession; readonly mimeTypes: MimeTypeArray; + readonly modelContext: ModelContext; readonly mozAddonManager: AddonManager; readonly mozTCPSocket: LegacyMozTCPSocket; readonly oscpu: string; @@ -19286,18 +19720,18 @@ interface NotificationEventMap { interface Notification extends EventTarget { readonly actions: NotificationAction[]; - readonly body: string | null; + readonly body: string; readonly data: any; readonly dir: NotificationDirection; - readonly icon: string | null; - readonly lang: string | null; + readonly icon: string; + readonly lang: string; onclick: ((this: Notification, ev: Event) => any) | null; onclose: ((this: Notification, ev: Event) => any) | null; onerror: ((this: Notification, ev: Event) => any) | null; onshow: ((this: Notification, ev: Event) => any) | null; readonly requireInteraction: boolean; readonly silent: boolean; - readonly tag: string | null; + readonly tag: string; readonly title: string; readonly vibrate: number[]; close(): void; @@ -19655,6 +20089,27 @@ declare var OscillatorNode: { isInstance: IsInstance; }; +interface PageRevealEvent extends Event { + readonly viewTransition: ViewTransition | null; +} + +declare var PageRevealEvent: { + prototype: PageRevealEvent; + new (type: string, eventInitDict?: PageRevealEventInit): PageRevealEvent; + isInstance: IsInstance; +}; + +interface PageSwapEvent extends Event { + readonly activation: NavigationActivation | null; + readonly viewTransition: ViewTransition | null; +} + +declare var PageSwapEvent: { + prototype: PageSwapEvent; + new (type: string, eventInitDict?: PageSwapEventInit): PageSwapEvent; + isInstance: IsInstance; +}; + interface PageTransitionEvent extends Event { readonly inFrameSwap: boolean; readonly persisted: boolean; @@ -20037,6 +20492,13 @@ interface PeerConnectionObserver { candidate: string, ufrag: string ): void; + onIceCandidateError( + address: string, + port: number, + url: string, + errorCode: number, + errorText: string + ): void; onPacket( level: number, type: mozPacketDumpType, @@ -20712,6 +21174,7 @@ declare var PointerEvent: { }; interface PopStateEvent extends Event { + readonly hasUAVisualTransition: boolean; readonly state: any; } @@ -21125,6 +21588,30 @@ declare var RTCEncodedVideoFrame: { isInstance: IsInstance; }; +interface RTCError extends DOMException { + readonly errorDetail: RTCErrorDetailType; + readonly receivedAlert: number | null; + readonly sctpCauseCode: number | null; + readonly sdpLineNumber: number | null; + readonly sentAlert: number | null; +} + +declare var RTCError: { + prototype: RTCError; + new (init: RTCErrorInit, message?: string): RTCError; + isInstance: IsInstance; +}; + +interface RTCErrorEvent extends Event { + readonly error: RTCError; +} + +declare var RTCErrorEvent: { + prototype: RTCErrorEvent; + new (type: string, eventInitDict: RTCErrorEventInit): RTCErrorEvent; + isInstance: IsInstance; +}; + interface RTCIceCandidate { readonly address: string | null; readonly candidate: string; @@ -21158,6 +21645,7 @@ interface RTCIceTransport extends EventTarget { readonly gatheringState: RTCIceGathererState; ongatheringstatechange: ((this: RTCIceTransport, ev: Event) => any) | null; onstatechange: ((this: RTCIceTransport, ev: Event) => any) | null; + readonly role: RTCIceRole; readonly state: RTCIceTransportState; addEventListener( type: K, @@ -21207,6 +21695,7 @@ interface RTCPeerConnectionEventMap { connectionstatechange: Event; datachannel: Event; icecandidate: Event; + icecandidateerror: Event; iceconnectionstatechange: Event; icegatheringstatechange: Event; negotiationneeded: Event; @@ -21229,6 +21718,7 @@ interface RTCPeerConnection extends EventTarget { onconnectionstatechange: ((this: RTCPeerConnection, ev: Event) => any) | null; ondatachannel: ((this: RTCPeerConnection, ev: Event) => any) | null; onicecandidate: ((this: RTCPeerConnection, ev: Event) => any) | null; + onicecandidateerror: ((this: RTCPeerConnection, ev: Event) => any) | null; oniceconnectionstatechange: ((this: RTCPeerConnection, ev: Event) => any) | null; onicegatheringstatechange: @@ -21349,6 +21839,23 @@ declare var RTCPeerConnection: { ): Promise; }; +interface RTCPeerConnectionIceErrorEvent extends Event { + readonly address: string | null; + readonly errorCode: number; + readonly errorText: string; + readonly port: number | null; + readonly url: string; +} + +declare var RTCPeerConnectionIceErrorEvent: { + prototype: RTCPeerConnectionIceErrorEvent; + new ( + type: string, + eventInitDict: RTCPeerConnectionIceErrorEventInit + ): RTCPeerConnectionIceErrorEvent; + isInstance: IsInstance; +}; + interface RTCPeerConnectionIceEvent extends Event { readonly candidate: RTCIceCandidate | null; } @@ -21693,12 +22200,6 @@ interface Report { toJSON(): any; } -declare var Report: { - prototype: Report; - new (): Report; - isInstance: IsInstance; -}; - interface ReportBody { toJSON(): any; } @@ -24513,7 +25014,8 @@ declare var SVGSwitchElement: { isInstance: IsInstance; }; -interface SVGSymbolElement extends SVGElement, SVGFitToViewBox, SVGTests { +interface SVGSymbolElement + extends SVGGraphicsElement, SVGFitToViewBox, SVGTests { addEventListener( type: K, listener: (this: SVGSymbolElement, ev: SVGElementEventMap[K]) => any, @@ -25389,7 +25891,7 @@ interface ShadowRoot extends DocumentFragment, DocumentOrShadowRoot { innerHTML: TrustedHTML | string; readonly mode: ShadowRootMode; onslotchange: ((this: ShadowRoot, ev: Event) => any) | null; - referenceTarget: string; + referenceTarget: string | null; readonly serializable: boolean; readonly slotAssignment: SlotAssignmentMode; createElementAndAppendChildAt(parentNode: Node, localName: string): Node; @@ -26869,6 +27371,11 @@ declare var TestReflectedHTMLAttribute: { isInstance: IsInstance; }; +interface TestReportBody extends ReportBody { + readonly message: string; + toJSON(): any; +} + interface TestTrialInterface {} declare var TestTrialInterface: { @@ -28257,6 +28764,7 @@ declare var VideoTrackList: { interface ViewTransition { readonly finished: Promise; readonly ready: Promise; + readonly types: ViewTransitionTypeSet; readonly updateCallbackDone: Promise; skipTransition(): void; } @@ -28267,9 +28775,27 @@ declare var ViewTransition: { isInstance: IsInstance; }; +interface ViewTransitionTypeSet { + forEach( + callbackfn: ( + value: string, + key: string, + parent: ViewTransitionTypeSet + ) => void, + thisArg?: any + ): void; +} + +declare var ViewTransitionTypeSet: { + prototype: ViewTransitionTypeSet; + new (): ViewTransitionTypeSet; + isInstance: IsInstance; +}; + interface VisualViewportEventMap { resize: Event; scroll: Event; + scrollend: Event; } interface VisualViewport extends EventTarget { @@ -28278,6 +28804,7 @@ interface VisualViewport extends EventTarget { readonly offsetTop: number; onresize: ((this: VisualViewport, ev: Event) => any) | null; onscroll: ((this: VisualViewport, ev: Event) => any) | null; + onscrollend: ((this: VisualViewport, ev: Event) => any) | null; readonly pageLeft: number; readonly pageTop: number; readonly scale: number; @@ -28565,6 +29092,7 @@ interface WebExtensionPolicy { readonly browsingContextGroupId: number; readonly contentScripts: WebExtensionContentScript[]; readonly extensionPageCSP: string; + readonly hasRecommendedState: boolean; readonly id: string; ignoreQuarantine: boolean; readonly isPrivileged: boolean; @@ -28576,6 +29104,7 @@ interface WebExtensionPolicy { readonly readyPromise: any; readonly temporarilyInstalled: boolean; readonly type: string; + readonly version: string; canAccessURI( uri: URI, explicit?: boolean, @@ -31802,7 +32331,7 @@ interface WebTransport { ): Promise; createUnidirectionalStream( options?: WebTransportSendStreamOptions - ): Promise; + ): Promise; getStats(): Promise; } @@ -31946,6 +32475,8 @@ interface Window readonly devicePixelRatio: number; readonly docShell: nsIDocShell | null; readonly document: Document | null; + /** Available only in secure contexts. */ + readonly documentPictureInPicture: DocumentPictureInPicture; readonly event: Event | undefined; readonly external: External; readonly frameElement: Element | null; @@ -32047,7 +32578,6 @@ interface Window ): CSSStyleDeclaration | null; getGroupMessageManager(aGroup: string): ChromeMessageBroadcaster; getInterface(iid: any): any; - getRegionalPrefsLocales(): string[]; getSelection(): Selection | null; getWebExposedLocales(): string[]; getWorkspaceID(): string; @@ -32055,6 +32585,7 @@ interface Window maximize(): void; minimize(): void; moveBy(x: number, y: number): void; + moveResize(x: number, y: number, w: number, h: number): void; moveTo(x: number, y: number): void; moveToWorkspace(workspaceID: string): void; mozScrollSnap(): void; @@ -32112,6 +32643,13 @@ interface Window options?: SynthesizeMouseEventOptions, callback?: VoidFunction ): boolean; + synthesizeTouchEvent( + type: string, + touches: SynthesizeTouchEventData[], + modifiers?: number, + options?: SynthesizeTouchEventOptions, + callback?: VoidFunction + ): boolean; updateCommands(action: string): void; readonly STATE_MAXIMIZED: 1; readonly STATE_MINIMIZED: 2; @@ -32187,6 +32725,7 @@ interface WindowEventHandlersEventMap { offline: Event; online: Event; pagehide: Event; + pagereveal: Event; pageshow: Event; popstate: Event; rejectionhandled: Event; @@ -32208,6 +32747,7 @@ interface WindowEventHandlers { onoffline: ((this: WindowEventHandlers, ev: Event) => any) | null; ononline: ((this: WindowEventHandlers, ev: Event) => any) | null; onpagehide: ((this: WindowEventHandlers, ev: Event) => any) | null; + onpagereveal: ((this: WindowEventHandlers, ev: Event) => any) | null; onpageshow: ((this: WindowEventHandlers, ev: Event) => any) | null; onpopstate: ((this: WindowEventHandlers, ev: Event) => any) | null; onrejectionhandled: ((this: WindowEventHandlers, ev: Event) => any) | null; @@ -32285,6 +32825,7 @@ interface WindowGlobalParent extends WindowContext { readonly isCurrentGlobal: boolean; readonly isInitialDocument: boolean; readonly isProcessRoot: boolean; + readonly isUncommittedInitialDocument: boolean; readonly osPid: number; readonly outerWindowId: number; readonly rootFrameLoader: FrameLoader | null; @@ -33593,6 +34134,8 @@ declare namespace CSS { declare namespace ChromeUtils { var aliveUtilityProcesses: number; + var cpuTimeSinceProcessStart: number; + var currentProcessMemoryUsage: number; var domProcessChild: nsIDOMProcessChild | null; var recentJSDevError: any; function CreateOriginAttributesFromOriginSuffix( @@ -33601,7 +34144,7 @@ declare namespace ChromeUtils { function addProfilerMarker( name: string, options?: ProfilerMarkerOptions | number, - text?: string + data?: any ): void; function androidMoveTaskToBack(): void; function base64URLDecode( @@ -33654,6 +34197,10 @@ declare namespace ChromeUtils { aRemoteType: string ): Promise; function ensureJSOracleStarted(): void; + function fetchDecodedImage( + uri: URI, + channel: MozChannel + ): Promise; function fillNonDefaultOriginAttributes( originAttrs?: OriginAttributesDictionary ): OriginAttributesDictionary; @@ -33661,6 +34208,11 @@ declare namespace ChromeUtils { function getAllDOMProcesses(): nsIDOMProcessParent[]; function getAllPossibleUtilityActorNames(): string[]; function getBaseDomainFromPartitionKey(partitionKey: string): string; + function getCachedJavaScriptSource( + key: string, + uri: string, + hintCharset: string + ): any; function getCallerLocation(principal: Principal): any; function getClassName(obj: any, unwrap?: boolean): string; function getFormAutofillConfidences( @@ -33670,6 +34222,7 @@ declare namespace ChromeUtils { CDMInformation[] >; function getGlobalWindowCommandEnabled(name: string): boolean | null; + function getLastOOMStackTrace(): string; function getLibcConstants(): LibcConstants; function getObjectNodeId(obj: any): NodeId; function getPartitionKeyFromURL( @@ -33691,6 +34244,7 @@ declare namespace ChromeUtils { aResourceURI: string, aOptions?: ImportESModuleOptionsDictionary ): any; + function invalidateResourceCache(): void; function isClassifierBlockingErrorCode(aError: number): boolean; function isDOMObject(obj: any, unwrap?: boolean): boolean; function isDarkBackground(element: Element): boolean; @@ -33716,6 +34270,7 @@ declare namespace ChromeUtils { ): string; function privateNoteIntentionalCrash(): void; function readHeapSnapshot(filePath: string): HeapSnapshot; + function registerMarkerSchema(schema: any): void; function registerProcessActor( aName: string, aOptions?: ProcessActorOptions @@ -33848,8 +34403,8 @@ declare namespace IOUtils { function writeJSON( path: string, value: any, - options?: WriteOptions - ): Promise; + options?: WriteJSONOptions + ): Promise; function writeUTF8( path: string, string: string, @@ -33879,6 +34434,11 @@ declare namespace InspectorUtils { document: Document, documentOnly?: boolean ): StyleSheet[]; + function getAnchorFor( + element: Element, + anchorName?: string | null + ): InspectorAnchorElement | null; + function getAnchorNamesFor(element: Element): string[]; function getBlockLineCounts(element: Element): number[] | null; function getCSSPropertyNames(options?: PropertyNamesOptions): string[]; function getCSSPropertyPrefs(): PropertyPref[]; @@ -33891,6 +34451,7 @@ declare namespace InspectorUtils { name: string ): InspectorCSSPropertyDefinition | null; function getCSSValuesForProperty(property: string): string[]; + function getCSSWideKeywords(): string[]; function getChildrenForNode( node: Node, showingAnonymousContent: boolean, @@ -33978,6 +34539,10 @@ declare namespace InspectorUtils { ): boolean; } +declare namespace JSActorTypeUtils { + function serializeDeserialize(strict: boolean, val: any): any; +} + declare namespace L10nOverlays { function translateElement( element: Element, @@ -34101,6 +34666,10 @@ declare namespace SessionStoreUtils { ): void; } +declare namespace TestReportGenerator { + function generateReport(params: GenerateTestReportParameters): Promise; +} + declare namespace TestUtils { function gc(): Promise; } @@ -34118,6 +34687,9 @@ declare namespace UniFFIScaffolding { id: UniFFIFunctionId, ...args: UniFFIScaffoldingValue[] ): UniFFIScaffoldingCallResult; + function callbackHandleCreate(): number; + function callbackHandleFree(handle: number): void; + function callbackHandleRelease(handle: number): number; function deregisterCallbackHandler( interfaceId: UniFFICallbackInterfaceId ): void; @@ -34198,7 +34770,7 @@ interface Console { warn(...data: any[]): void; } -declare let console: Console; +declare var console: Console; interface AnyCallback { (value: any): any; @@ -34233,7 +34805,7 @@ interface CreateScriptURLCallback { } interface CustomElementConstructor { - (): any; + new (...params: any[]): HTMLElement; } interface CustomElementCreationCallback { @@ -34426,6 +34998,14 @@ interface TestThrowingCallback { (): void; } +interface ToolExecuteCallback { + (input: any, client: ModelContextClient): any; +} + +interface UserInteractionCallback { + (): any; +} + interface ValidateAssertionCallback { ( assertion: string, @@ -34639,13 +35219,13 @@ type ElementTagNameMap = HTMLElementTagNameMap & Exclude >; -declare let Audio: { +declare var Audio: { new (src?: string): HTMLAudioElement; }; -declare let Image: { +declare var Image: { new (width?: number, height?: number): HTMLImageElement; }; -declare let Option: { +declare var Option: { new ( text?: string, value?: string, @@ -34653,104 +35233,106 @@ declare let Option: { selected?: boolean ): HTMLOptionElement; }; -declare let webkitSpeechGrammar: { +declare var webkitSpeechGrammar: { new (): SpeechGrammar; }; -declare let webkitSpeechGrammarList: { +declare var webkitSpeechGrammarList: { new (): SpeechGrammarList; }; -declare let webkitSpeechRecognition: { +declare var webkitSpeechRecognition: { new (): SpeechRecognition; }; -declare let Glean: GleanImpl; -declare let GleanPings: GleanPingsImpl; -declare let InstallTrigger: any; -declare let browserDOMWindow: nsIBrowserDOMWindow | null; -declare let browsingContext: BrowsingContext; -declare let clientInformation: Navigator; -declare let clientPrincipal: Principal | null; -declare let closed: boolean; -declare let content: any; -declare let controllers: XULControllers; -declare let cookieStore: CookieStore; -declare let customElements: CustomElementRegistry; -declare let desktopToDeviceScale: number; -declare let devicePixelRatio: number; -declare let docShell: nsIDocShell | null; -declare let document: Document | null; -declare let event: Event | undefined; -declare let external: External; -declare let frameElement: Element | null; -declare let frames: WindowProxy; -declare let fullScreen: boolean; -declare let history: History; -declare let innerHeight: number; -declare let innerWidth: number; -declare let intlUtils: IntlUtils; -declare let isChromeWindow: boolean; -declare let isCloaked: boolean; -declare let isFullyOccluded: boolean; -declare let isInFullScreenTransition: boolean; -declare let length: number; -declare let location: Location; -declare let locationbar: BarProp; -declare let menubar: BarProp; -declare let messageManager: ChromeMessageBroadcaster; -declare let mozInnerScreenX: number; -declare let mozInnerScreenY: number; +declare var Glean: GleanImpl; +declare var GleanPings: GleanPingsImpl; +declare var InstallTrigger: any; +declare var browserDOMWindow: nsIBrowserDOMWindow | null; +declare var browsingContext: BrowsingContext; +declare var clientInformation: Navigator; +declare var clientPrincipal: Principal | null; +declare var closed: boolean; +declare var content: any; +declare var controllers: XULControllers; +declare var cookieStore: CookieStore; +declare var customElements: CustomElementRegistry; +declare var desktopToDeviceScale: number; +declare var devicePixelRatio: number; +declare var docShell: nsIDocShell | null; +declare var document: Document | null; +/** Available only in secure contexts. */ +declare var documentPictureInPicture: DocumentPictureInPicture; +declare var event: Event | undefined; +declare var external: External; +declare var frameElement: Element | null; +declare var frames: WindowProxy; +declare var fullScreen: boolean; +declare var history: History; +declare var innerHeight: number; +declare var innerWidth: number; +declare var intlUtils: IntlUtils; +declare var isChromeWindow: boolean; +declare var isCloaked: boolean; +declare var isFullyOccluded: boolean; +declare var isInFullScreenTransition: boolean; +declare var length: number; +declare var location: Location; +declare var locationbar: BarProp; +declare var menubar: BarProp; +declare var messageManager: ChromeMessageBroadcaster; +declare var mozInnerScreenX: number; +declare var mozInnerScreenY: number; /** @deprecated */ declare const name: void; -declare let navigation: Navigation; -declare let navigator: Navigator; -declare let ondevicelight: ((this: Window, ev: Event) => any) | null; -declare let ondevicemotion: ((this: Window, ev: Event) => any) | null; -declare let ondeviceorientation: ((this: Window, ev: Event) => any) | null; -declare let ondeviceorientationabsolute: +declare var navigation: Navigation; +declare var navigator: Navigator; +declare var ondevicelight: ((this: Window, ev: Event) => any) | null; +declare var ondevicemotion: ((this: Window, ev: Event) => any) | null; +declare var ondeviceorientation: ((this: Window, ev: Event) => any) | null; +declare var ondeviceorientationabsolute: ((this: Window, ev: Event) => any) | null; -declare let onorientationchange: ((this: Window, ev: Event) => any) | null; -declare let onuserproximity: ((this: Window, ev: Event) => any) | null; -declare let onvrdisplayactivate: ((this: Window, ev: Event) => any) | null; -declare let onvrdisplayconnect: ((this: Window, ev: Event) => any) | null; -declare let onvrdisplaydeactivate: ((this: Window, ev: Event) => any) | null; -declare let onvrdisplaydisconnect: ((this: Window, ev: Event) => any) | null; -declare let onvrdisplaypresentchange: ((this: Window, ev: Event) => any) | null; -declare let opener: any; -declare let orientation: number; -declare let originAgentCluster: boolean; -declare let outerHeight: number; -declare let outerWidth: number; -declare let pageXOffset: number; -declare let pageYOffset: number; -declare let paintWorklet: Worklet; -declare let parent: WindowProxy | null; -declare let performance: Performance | null; -declare let personalbar: BarProp; -declare let realFrameElement: Element | null; -declare let screen: Screen; -declare let screenEdgeSlopX: number; -declare let screenEdgeSlopY: number; -declare let screenLeft: number; -declare let screenTop: number; -declare let screenX: number; -declare let screenY: number; -declare let scrollMaxX: number; -declare let scrollMaxY: number; -declare let scrollMinX: number; -declare let scrollMinY: number; -declare let scrollX: number; -declare let scrollY: number; -declare let scrollbars: BarProp; -declare let self: WindowProxy; -declare let status: string; -declare let statusbar: BarProp; -declare let toolbar: BarProp; -declare let top: WindowProxy | null; -declare let visualViewport: VisualViewport; -declare let window: WindowProxy; -declare let windowGlobalChild: WindowGlobalChild | null; -declare let windowRoot: WindowRoot | null; -declare let windowState: number; -declare let windowUtils: nsIDOMWindowUtils; +declare var onorientationchange: ((this: Window, ev: Event) => any) | null; +declare var onuserproximity: ((this: Window, ev: Event) => any) | null; +declare var onvrdisplayactivate: ((this: Window, ev: Event) => any) | null; +declare var onvrdisplayconnect: ((this: Window, ev: Event) => any) | null; +declare var onvrdisplaydeactivate: ((this: Window, ev: Event) => any) | null; +declare var onvrdisplaydisconnect: ((this: Window, ev: Event) => any) | null; +declare var onvrdisplaypresentchange: ((this: Window, ev: Event) => any) | null; +declare var opener: any; +declare var orientation: number; +declare var originAgentCluster: boolean; +declare var outerHeight: number; +declare var outerWidth: number; +declare var pageXOffset: number; +declare var pageYOffset: number; +declare var paintWorklet: Worklet; +declare var parent: WindowProxy | null; +declare var performance: Performance | null; +declare var personalbar: BarProp; +declare var realFrameElement: Element | null; +declare var screen: Screen; +declare var screenEdgeSlopX: number; +declare var screenEdgeSlopY: number; +declare var screenLeft: number; +declare var screenTop: number; +declare var screenX: number; +declare var screenY: number; +declare var scrollMaxX: number; +declare var scrollMaxY: number; +declare var scrollMinX: number; +declare var scrollMinY: number; +declare var scrollX: number; +declare var scrollY: number; +declare var scrollbars: BarProp; +declare var self: WindowProxy; +declare var status: string; +declare var statusbar: BarProp; +declare var toolbar: BarProp; +declare var top: WindowProxy | null; +declare var visualViewport: VisualViewport; +declare var window: WindowProxy; +declare var windowGlobalChild: WindowGlobalChild | null; +declare var windowRoot: WindowRoot | null; +declare var windowState: number; +declare var windowUtils: nsIDOMWindowUtils; declare function alert(): void; declare function alert(message: string): void; declare function blur(): void; @@ -34783,7 +35365,6 @@ declare function getGroupMessageManager( aGroup: string ): ChromeMessageBroadcaster; declare function getInterface(iid: any): any; -declare function getRegionalPrefsLocales(): string[]; declare function getSelection(): Selection | null; declare function getWebExposedLocales(): string[]; declare function getWorkspaceID(): string; @@ -34791,6 +35372,7 @@ declare function matchMedia(query: string): MediaQueryList | null; declare function maximize(): void; declare function minimize(): void; declare function moveBy(x: number, y: number): void; +declare function moveResize(x: number, y: number, w: number, h: number): void; declare function moveTo(x: number, y: number): void; declare function moveToWorkspace(workspaceID: string): void; declare function mozScrollSnap(): void; @@ -34858,151 +35440,159 @@ declare function synthesizeMouseEvent( options?: SynthesizeMouseEventOptions, callback?: VoidFunction ): boolean; +declare function synthesizeTouchEvent( + type: string, + touches: SynthesizeTouchEventData[], + modifiers?: number, + options?: SynthesizeTouchEventOptions, + callback?: VoidFunction +): boolean; declare function updateCommands(action: string): void; declare function toString(): string; -declare let ownerGlobal: WindowProxy | null; +declare var ownerGlobal: WindowProxy | null; declare function dispatchEvent(event: Event): boolean; declare function getEventHandler(type: string): EventHandler; declare function setEventHandler(type: string, handler: EventHandler): void; declare function cancelAnimationFrame(handle: number): void; declare function requestAnimationFrame(callback: FrameRequestCallback): number; -declare let crypto: Crypto; -declare let onabort: ((this: Window, ev: Event) => any) | null; -declare let onanimationcancel: ((this: Window, ev: Event) => any) | null; -declare let onanimationend: ((this: Window, ev: Event) => any) | null; -declare let onanimationiteration: ((this: Window, ev: Event) => any) | null; -declare let onanimationstart: ((this: Window, ev: Event) => any) | null; -declare let onauxclick: ((this: Window, ev: Event) => any) | null; -declare let onbeforeinput: ((this: Window, ev: Event) => any) | null; -declare let onbeforematch: ((this: Window, ev: Event) => any) | null; -declare let onbeforetoggle: ((this: Window, ev: Event) => any) | null; -declare let onblur: ((this: Window, ev: Event) => any) | null; -declare let oncancel: ((this: Window, ev: Event) => any) | null; -declare let oncanplay: ((this: Window, ev: Event) => any) | null; -declare let oncanplaythrough: ((this: Window, ev: Event) => any) | null; -declare let onchange: ((this: Window, ev: Event) => any) | null; -declare let onclick: ((this: Window, ev: Event) => any) | null; -declare let onclose: ((this: Window, ev: Event) => any) | null; -declare let oncommand: ((this: Window, ev: Event) => any) | null; -declare let oncontentvisibilityautostatechange: +declare var crypto: Crypto; +declare var onabort: ((this: Window, ev: Event) => any) | null; +declare var onanimationcancel: ((this: Window, ev: Event) => any) | null; +declare var onanimationend: ((this: Window, ev: Event) => any) | null; +declare var onanimationiteration: ((this: Window, ev: Event) => any) | null; +declare var onanimationstart: ((this: Window, ev: Event) => any) | null; +declare var onauxclick: ((this: Window, ev: Event) => any) | null; +declare var onbeforeinput: ((this: Window, ev: Event) => any) | null; +declare var onbeforematch: ((this: Window, ev: Event) => any) | null; +declare var onbeforetoggle: ((this: Window, ev: Event) => any) | null; +declare var onblur: ((this: Window, ev: Event) => any) | null; +declare var oncancel: ((this: Window, ev: Event) => any) | null; +declare var oncanplay: ((this: Window, ev: Event) => any) | null; +declare var oncanplaythrough: ((this: Window, ev: Event) => any) | null; +declare var onchange: ((this: Window, ev: Event) => any) | null; +declare var onclick: ((this: Window, ev: Event) => any) | null; +declare var onclose: ((this: Window, ev: Event) => any) | null; +declare var oncommand: ((this: Window, ev: Event) => any) | null; +declare var oncontentvisibilityautostatechange: ((this: Window, ev: Event) => any) | null; -declare let oncontextlost: ((this: Window, ev: Event) => any) | null; -declare let oncontextmenu: ((this: Window, ev: Event) => any) | null; -declare let oncontextrestored: ((this: Window, ev: Event) => any) | null; -declare let oncopy: ((this: Window, ev: Event) => any) | null; -declare let oncuechange: ((this: Window, ev: Event) => any) | null; -declare let oncut: ((this: Window, ev: Event) => any) | null; -declare let ondblclick: ((this: Window, ev: Event) => any) | null; -declare let ondrag: ((this: Window, ev: Event) => any) | null; -declare let ondragend: ((this: Window, ev: Event) => any) | null; -declare let ondragenter: ((this: Window, ev: Event) => any) | null; -declare let ondragexit: ((this: Window, ev: Event) => any) | null; -declare let ondragleave: ((this: Window, ev: Event) => any) | null; -declare let ondragover: ((this: Window, ev: Event) => any) | null; -declare let ondragstart: ((this: Window, ev: Event) => any) | null; -declare let ondrop: ((this: Window, ev: Event) => any) | null; -declare let ondurationchange: ((this: Window, ev: Event) => any) | null; -declare let onemptied: ((this: Window, ev: Event) => any) | null; -declare let onended: ((this: Window, ev: Event) => any) | null; -declare let onfocus: ((this: Window, ev: Event) => any) | null; -declare let onformdata: ((this: Window, ev: Event) => any) | null; -declare let ongotpointercapture: ((this: Window, ev: Event) => any) | null; -declare let oninput: ((this: Window, ev: Event) => any) | null; -declare let oninvalid: ((this: Window, ev: Event) => any) | null; -declare let onkeydown: ((this: Window, ev: Event) => any) | null; -declare let onkeypress: ((this: Window, ev: Event) => any) | null; -declare let onkeyup: ((this: Window, ev: Event) => any) | null; -declare let onload: ((this: Window, ev: Event) => any) | null; -declare let onloadeddata: ((this: Window, ev: Event) => any) | null; -declare let onloadedmetadata: ((this: Window, ev: Event) => any) | null; -declare let onloadstart: ((this: Window, ev: Event) => any) | null; -declare let onlostpointercapture: ((this: Window, ev: Event) => any) | null; -declare let onmousedown: ((this: Window, ev: Event) => any) | null; -declare let onmouseenter: ((this: Window, ev: Event) => any) | null; -declare let onmouseleave: ((this: Window, ev: Event) => any) | null; -declare let onmousemove: ((this: Window, ev: Event) => any) | null; -declare let onmouseout: ((this: Window, ev: Event) => any) | null; -declare let onmouseover: ((this: Window, ev: Event) => any) | null; -declare let onmouseup: ((this: Window, ev: Event) => any) | null; -declare let onmozfullscreenchange: ((this: Window, ev: Event) => any) | null; -declare let onmozfullscreenerror: ((this: Window, ev: Event) => any) | null; -declare let onpaste: ((this: Window, ev: Event) => any) | null; -declare let onpause: ((this: Window, ev: Event) => any) | null; -declare let onplay: ((this: Window, ev: Event) => any) | null; -declare let onplaying: ((this: Window, ev: Event) => any) | null; -declare let onpointercancel: ((this: Window, ev: Event) => any) | null; -declare let onpointerdown: ((this: Window, ev: Event) => any) | null; -declare let onpointerenter: ((this: Window, ev: Event) => any) | null; -declare let onpointerleave: ((this: Window, ev: Event) => any) | null; -declare let onpointermove: ((this: Window, ev: Event) => any) | null; -declare let onpointerout: ((this: Window, ev: Event) => any) | null; -declare let onpointerover: ((this: Window, ev: Event) => any) | null; +declare var oncontextlost: ((this: Window, ev: Event) => any) | null; +declare var oncontextmenu: ((this: Window, ev: Event) => any) | null; +declare var oncontextrestored: ((this: Window, ev: Event) => any) | null; +declare var oncopy: ((this: Window, ev: Event) => any) | null; +declare var oncuechange: ((this: Window, ev: Event) => any) | null; +declare var oncut: ((this: Window, ev: Event) => any) | null; +declare var ondblclick: ((this: Window, ev: Event) => any) | null; +declare var ondrag: ((this: Window, ev: Event) => any) | null; +declare var ondragend: ((this: Window, ev: Event) => any) | null; +declare var ondragenter: ((this: Window, ev: Event) => any) | null; +declare var ondragexit: ((this: Window, ev: Event) => any) | null; +declare var ondragleave: ((this: Window, ev: Event) => any) | null; +declare var ondragover: ((this: Window, ev: Event) => any) | null; +declare var ondragstart: ((this: Window, ev: Event) => any) | null; +declare var ondrop: ((this: Window, ev: Event) => any) | null; +declare var ondurationchange: ((this: Window, ev: Event) => any) | null; +declare var onemptied: ((this: Window, ev: Event) => any) | null; +declare var onended: ((this: Window, ev: Event) => any) | null; +declare var onfocus: ((this: Window, ev: Event) => any) | null; +declare var onformdata: ((this: Window, ev: Event) => any) | null; +declare var ongotpointercapture: ((this: Window, ev: Event) => any) | null; +declare var oninput: ((this: Window, ev: Event) => any) | null; +declare var oninvalid: ((this: Window, ev: Event) => any) | null; +declare var onkeydown: ((this: Window, ev: Event) => any) | null; +declare var onkeypress: ((this: Window, ev: Event) => any) | null; +declare var onkeyup: ((this: Window, ev: Event) => any) | null; +declare var onload: ((this: Window, ev: Event) => any) | null; +declare var onloadeddata: ((this: Window, ev: Event) => any) | null; +declare var onloadedmetadata: ((this: Window, ev: Event) => any) | null; +declare var onloadstart: ((this: Window, ev: Event) => any) | null; +declare var onlostpointercapture: ((this: Window, ev: Event) => any) | null; +declare var onmousedown: ((this: Window, ev: Event) => any) | null; +declare var onmouseenter: ((this: Window, ev: Event) => any) | null; +declare var onmouseleave: ((this: Window, ev: Event) => any) | null; +declare var onmousemove: ((this: Window, ev: Event) => any) | null; +declare var onmouseout: ((this: Window, ev: Event) => any) | null; +declare var onmouseover: ((this: Window, ev: Event) => any) | null; +declare var onmouseup: ((this: Window, ev: Event) => any) | null; +declare var onmozfullscreenchange: ((this: Window, ev: Event) => any) | null; +declare var onmozfullscreenerror: ((this: Window, ev: Event) => any) | null; +declare var onpaste: ((this: Window, ev: Event) => any) | null; +declare var onpause: ((this: Window, ev: Event) => any) | null; +declare var onplay: ((this: Window, ev: Event) => any) | null; +declare var onplaying: ((this: Window, ev: Event) => any) | null; +declare var onpointercancel: ((this: Window, ev: Event) => any) | null; +declare var onpointerdown: ((this: Window, ev: Event) => any) | null; +declare var onpointerenter: ((this: Window, ev: Event) => any) | null; +declare var onpointerleave: ((this: Window, ev: Event) => any) | null; +declare var onpointermove: ((this: Window, ev: Event) => any) | null; +declare var onpointerout: ((this: Window, ev: Event) => any) | null; +declare var onpointerover: ((this: Window, ev: Event) => any) | null; /** Available only in secure contexts. */ -declare let onpointerrawupdate: ((this: Window, ev: Event) => any) | null; -declare let onpointerup: ((this: Window, ev: Event) => any) | null; -declare let onprogress: ((this: Window, ev: Event) => any) | null; -declare let onratechange: ((this: Window, ev: Event) => any) | null; -declare let onreset: ((this: Window, ev: Event) => any) | null; -declare let onresize: ((this: Window, ev: Event) => any) | null; -declare let onscroll: ((this: Window, ev: Event) => any) | null; -declare let onscrollend: ((this: Window, ev: Event) => any) | null; -declare let onsecuritypolicyviolation: +declare var onpointerrawupdate: ((this: Window, ev: Event) => any) | null; +declare var onpointerup: ((this: Window, ev: Event) => any) | null; +declare var onprogress: ((this: Window, ev: Event) => any) | null; +declare var onratechange: ((this: Window, ev: Event) => any) | null; +declare var onreset: ((this: Window, ev: Event) => any) | null; +declare var onresize: ((this: Window, ev: Event) => any) | null; +declare var onscroll: ((this: Window, ev: Event) => any) | null; +declare var onscrollend: ((this: Window, ev: Event) => any) | null; +declare var onsecuritypolicyviolation: ((this: Window, ev: Event) => any) | null; -declare let onseeked: ((this: Window, ev: Event) => any) | null; -declare let onseeking: ((this: Window, ev: Event) => any) | null; -declare let onselect: ((this: Window, ev: Event) => any) | null; -declare let onselectionchange: ((this: Window, ev: Event) => any) | null; -declare let onselectstart: ((this: Window, ev: Event) => any) | null; -declare let onslotchange: ((this: Window, ev: Event) => any) | null; -declare let onstalled: ((this: Window, ev: Event) => any) | null; -declare let onsubmit: ((this: Window, ev: Event) => any) | null; -declare let onsuspend: ((this: Window, ev: Event) => any) | null; -declare let ontimeupdate: ((this: Window, ev: Event) => any) | null; -declare let ontoggle: ((this: Window, ev: Event) => any) | null; -declare let ontransitioncancel: ((this: Window, ev: Event) => any) | null; -declare let ontransitionend: ((this: Window, ev: Event) => any) | null; -declare let ontransitionrun: ((this: Window, ev: Event) => any) | null; -declare let ontransitionstart: ((this: Window, ev: Event) => any) | null; -declare let onvolumechange: ((this: Window, ev: Event) => any) | null; -declare let onwaiting: ((this: Window, ev: Event) => any) | null; -declare let onwebkitanimationend: ((this: Window, ev: Event) => any) | null; -declare let onwebkitanimationiteration: +declare var onseeked: ((this: Window, ev: Event) => any) | null; +declare var onseeking: ((this: Window, ev: Event) => any) | null; +declare var onselect: ((this: Window, ev: Event) => any) | null; +declare var onselectionchange: ((this: Window, ev: Event) => any) | null; +declare var onselectstart: ((this: Window, ev: Event) => any) | null; +declare var onslotchange: ((this: Window, ev: Event) => any) | null; +declare var onstalled: ((this: Window, ev: Event) => any) | null; +declare var onsubmit: ((this: Window, ev: Event) => any) | null; +declare var onsuspend: ((this: Window, ev: Event) => any) | null; +declare var ontimeupdate: ((this: Window, ev: Event) => any) | null; +declare var ontoggle: ((this: Window, ev: Event) => any) | null; +declare var ontransitioncancel: ((this: Window, ev: Event) => any) | null; +declare var ontransitionend: ((this: Window, ev: Event) => any) | null; +declare var ontransitionrun: ((this: Window, ev: Event) => any) | null; +declare var ontransitionstart: ((this: Window, ev: Event) => any) | null; +declare var onvolumechange: ((this: Window, ev: Event) => any) | null; +declare var onwaiting: ((this: Window, ev: Event) => any) | null; +declare var onwebkitanimationend: ((this: Window, ev: Event) => any) | null; +declare var onwebkitanimationiteration: ((this: Window, ev: Event) => any) | null; -declare let onwebkitanimationstart: ((this: Window, ev: Event) => any) | null; -declare let onwebkittransitionend: ((this: Window, ev: Event) => any) | null; -declare let onwheel: ((this: Window, ev: Event) => any) | null; -declare let onerror: ((this: Window, ev: Event) => any) | null; -declare let speechSynthesis: SpeechSynthesis; -declare let ontouchcancel: ((this: Window, ev: Event) => any) | null; -declare let ontouchend: ((this: Window, ev: Event) => any) | null; -declare let ontouchmove: ((this: Window, ev: Event) => any) | null; -declare let ontouchstart: ((this: Window, ev: Event) => any) | null; -declare let onafterprint: ((this: Window, ev: Event) => any) | null; -declare let onbeforeprint: ((this: Window, ev: Event) => any) | null; -declare let onbeforeunload: ((this: Window, ev: Event) => any) | null; -declare let ongamepadconnected: ((this: Window, ev: Event) => any) | null; -declare let ongamepaddisconnected: ((this: Window, ev: Event) => any) | null; -declare let onhashchange: ((this: Window, ev: Event) => any) | null; -declare let onlanguagechange: ((this: Window, ev: Event) => any) | null; -declare let onmessage: ((this: Window, ev: Event) => any) | null; -declare let onmessageerror: ((this: Window, ev: Event) => any) | null; -declare let onoffline: ((this: Window, ev: Event) => any) | null; -declare let ononline: ((this: Window, ev: Event) => any) | null; -declare let onpagehide: ((this: Window, ev: Event) => any) | null; -declare let onpageshow: ((this: Window, ev: Event) => any) | null; -declare let onpopstate: ((this: Window, ev: Event) => any) | null; -declare let onrejectionhandled: ((this: Window, ev: Event) => any) | null; -declare let onstorage: ((this: Window, ev: Event) => any) | null; -declare let onunhandledrejection: ((this: Window, ev: Event) => any) | null; -declare let onunload: ((this: Window, ev: Event) => any) | null; -declare let localStorage: Storage | null; -declare let caches: CacheStorage; -declare let crossOriginIsolated: boolean; -declare let indexedDB: IDBFactory | null; -declare let isSecureContext: boolean; -declare let origin: string; -declare let scheduler: Scheduler; -declare let trustedTypes: TrustedTypePolicyFactory; +declare var onwebkitanimationstart: ((this: Window, ev: Event) => any) | null; +declare var onwebkittransitionend: ((this: Window, ev: Event) => any) | null; +declare var onwheel: ((this: Window, ev: Event) => any) | null; +declare var onerror: ((this: Window, ev: Event) => any) | null; +declare var speechSynthesis: SpeechSynthesis; +declare var ontouchcancel: ((this: Window, ev: Event) => any) | null; +declare var ontouchend: ((this: Window, ev: Event) => any) | null; +declare var ontouchmove: ((this: Window, ev: Event) => any) | null; +declare var ontouchstart: ((this: Window, ev: Event) => any) | null; +declare var onafterprint: ((this: Window, ev: Event) => any) | null; +declare var onbeforeprint: ((this: Window, ev: Event) => any) | null; +declare var onbeforeunload: ((this: Window, ev: Event) => any) | null; +declare var ongamepadconnected: ((this: Window, ev: Event) => any) | null; +declare var ongamepaddisconnected: ((this: Window, ev: Event) => any) | null; +declare var onhashchange: ((this: Window, ev: Event) => any) | null; +declare var onlanguagechange: ((this: Window, ev: Event) => any) | null; +declare var onmessage: ((this: Window, ev: Event) => any) | null; +declare var onmessageerror: ((this: Window, ev: Event) => any) | null; +declare var onoffline: ((this: Window, ev: Event) => any) | null; +declare var ononline: ((this: Window, ev: Event) => any) | null; +declare var onpagehide: ((this: Window, ev: Event) => any) | null; +declare var onpagereveal: ((this: Window, ev: Event) => any) | null; +declare var onpageshow: ((this: Window, ev: Event) => any) | null; +declare var onpopstate: ((this: Window, ev: Event) => any) | null; +declare var onrejectionhandled: ((this: Window, ev: Event) => any) | null; +declare var onstorage: ((this: Window, ev: Event) => any) | null; +declare var onunhandledrejection: ((this: Window, ev: Event) => any) | null; +declare var onunload: ((this: Window, ev: Event) => any) | null; +declare var localStorage: Storage | null; +declare var caches: CacheStorage; +declare var crossOriginIsolated: boolean; +declare var indexedDB: IDBFactory | null; +declare var isSecureContext: boolean; +declare var origin: string; +declare var scheduler: Scheduler; +declare var trustedTypes: TrustedTypePolicyFactory; declare function atob(atob: string): string; declare function btoa(btoa: string): string; declare function clearInterval(handle?: number): void; @@ -35040,7 +35630,7 @@ declare function structuredClone( value: any, options?: StructuredSerializeOptions ): any; -declare let sessionStorage: Storage | null; +declare var sessionStorage: Storage | null; declare function addEventListener( type: K, listener: (this: Window, ev: WindowEventMap[K]) => any, @@ -35149,6 +35739,7 @@ type GPUSize64Out = number; type GPUStencilValue = number; type GPUTextureUsageFlags = number; type GeometryNode = Text | Element | Document; +type GleanLabeledTestValue = boolean | number | string | GleanDistributionData; type HTMLOrSVGImageElement = HTMLImageElement | SVGImageElement; type HeadersInit = string[][] | Record; type ImageBitmapSource = @@ -35234,6 +35825,7 @@ type AlignSetting = "center" | "end" | "left" | "right" | "start"; type AlphaOption = "discard" | "keep"; type AnimationPlayState = "finished" | "idle" | "paused" | "running"; type AnimationReplaceState = "active" | "persisted" | "removed"; +type AriaNotifyPriority = "high" | "normal"; type AudioContextState = "closed" | "running" | "suspended"; type AudioSampleFormat = | "f32" @@ -35313,7 +35905,7 @@ type CodecState = "closed" | "configured" | "unconfigured"; type ColorGamut = "p3" | "rec2020" | "srgb"; type ColorSpaceConversion = "default" | "none"; type CompositeOperation = "accumulate" | "add" | "replace"; -type CompressionFormat = "deflate" | "deflate-raw" | "gzip" | "zstd"; +type CompressionFormat = "brotli" | "deflate" | "deflate-raw" | "gzip" | "zstd"; type ConnectionType = "bluetooth" | "cellular" | "ethernet" | "none" | "other" | "unknown" | "wifi"; type ConsoleLevel = "error" | "log" | "warning"; @@ -35377,7 +35969,8 @@ type DecoderDoctorReportType = | "mediawidevinenowmf" | "mediawmfneeded"; type DirectionSetting = "" | "lr" | "rl"; -type DisplayMode = "browser" | "fullscreen" | "minimal-ui" | "standalone"; +type DisplayMode = + "browser" | "fullscreen" | "minimal-ui" | "picture-in-picture" | "standalone"; type DistanceModelType = "exponential" | "inverse" | "linear"; type EncodedAudioChunkType = "delta" | "key"; type EncodedVideoChunkType = "delta" | "key"; @@ -35648,7 +36241,15 @@ type IdentityCredentialRequestOptionsMode = "active" | "passive"; type ImageOrientation = "flipY" | "from-image" | "none"; type ImportESModuleTargetGlobal = "contextual" | "current" | "devtools" | "shared"; +type InspectorAnchorType = "explicit" | "popover" | "pseudo-element"; type InspectorPropertyType = "color" | "gradient" | "timing-function"; +type IntegrityViolationReason = + | "invalid_manifest" + | "invalid_transparency_proof" + | "manifest_unavailable" + | "missing_from_manifest" + | "no_manifest_match" + | "untrusted_transparency_proof"; type IterationCompositeOperation = "accumulate" | "replace"; type JSRFPTarget = | "CSSPrefersColorScheme" @@ -35699,6 +36300,7 @@ type MLSObjectType = | "welcome"; type MediaControlKey = | "focus" + | "mute" | "nexttrack" | "pause" | "play" @@ -35707,8 +36309,10 @@ type MediaControlKey = | "seekbackward" | "seekforward" | "seekto" + | "setvolume" | "skipad" - | "stop"; + | "stop" + | "unmute"; type MediaDecodingType = "file" | "media-source"; type MediaDeviceKind = "audioinput" | "audiooutput" | "videoinput"; type MediaEncodingType = "record" | "transmission"; @@ -35842,6 +36446,8 @@ type PaymentShippingType = "delivery" | "pickup" | "shipping"; type PermissionName = | "camera" | "geolocation" + | "local-network" + | "loopback-network" | "microphone" | "midi" | "notifications" @@ -35894,6 +36500,14 @@ type RTCDegradationPreference = type RTCDtlsTransportState = "closed" | "connected" | "connecting" | "failed" | "new"; type RTCEncodedVideoFrameType = "delta" | "empty" | "key"; +type RTCErrorDetailType = + | "data-channel-failure" + | "dtls-failure" + | "fingerprint-failure" + | "hardware-encoder-error" + | "hardware-encoder-not-available" + | "sctp-failure" + | "sdp-syntax-error"; type RTCIceCandidateType = "host" | "prflx" | "relay" | "srflx"; type RTCIceComponent = "rtcp" | "rtp"; type RTCIceConnectionState = @@ -35908,6 +36522,7 @@ type RTCIceCredentialType = "password"; type RTCIceGathererState = "complete" | "gathering" | "new"; type RTCIceGatheringState = "complete" | "gathering" | "new"; type RTCIceProtocol = "tcp" | "udp"; +type RTCIceRole = "controlled" | "controlling" | "unknown"; type RTCIceTcpCandidateType = "active" | "passive" | "so"; type RTCIceTransportPolicy = "all" | "relay"; type RTCIceTransportState = @@ -36003,6 +36618,7 @@ type RequestPriority = "auto" | "high" | "low"; type RequestRedirect = "error" | "follow" | "manual"; type ResizeObserverBoxOptions = "border-box" | "content-box" | "device-pixel-content-box"; +type ResizeQuality = "high" | "low" | "medium" | "pixelated"; type ResourceCacheTarget = "chrome" | "content"; type ResourceCacheType = "image" | "script" | "stylesheet"; type ResponseType = @@ -36140,6 +36756,7 @@ type WebIDLUtilityActorName = | "audioDecoder_WMF" | "jSOracle" | "mfMediaEngineCDM" + | "pkcs11Module" | "unknown" | "windowsFileDialog" | "windowsUtils"; @@ -36728,6 +37345,8 @@ interface VideoTrackList { [Symbol.iterator](): IterableIterator; } +interface ViewTransitionTypeSet extends Set {} + interface WEBGL_draw_buffers { drawBuffersWEBGL(buffers: Iterable): void; } @@ -36983,6 +37602,13 @@ interface Window { marks: Iterable, onHorizontalScrollbar?: boolean ): void; + synthesizeTouchEvent( + type: string, + touches: Iterable, + modifiers?: number, + options?: SynthesizeTouchEventOptions, + callback?: VoidFunction + ): boolean; } interface Worker { diff --git a/src/zen/@types/lib.gecko.glean.d.ts b/src/zen/@types/lib.gecko.glean.d.ts index 1c76502aa..d5282ed4f 100644 --- a/src/zen/@types/lib.gecko.glean.d.ts +++ b/src/zen/@types/lib.gecko.glean.d.ts @@ -144,6 +144,15 @@ interface GleanImpl { http2UploadThroughput100: GleanCustomDistribution; http2UploadThroughput1050: GleanCustomDistribution; http2UploadThroughput50100: GleanCustomDistribution; + http3CongestionEventCount: GleanCustomDistribution; + http3CongestionEventReason: Record<"ecn-ce" | "loss", GleanCounter>; + http3CongestionWindowGrowth: Record< + | "had_growth" + | "no_growth" + | "no_growth_but_exit" + | "no_growth_then_exit_then_growth", + GleanCounter + >; http3ConnectionCloseReason: Record< | "AckedUnsentPacket" | "Application" @@ -208,7 +217,19 @@ interface GleanImpl { "black-hole" | "bleaching" | "capable" | "received-unsent-ect-1", GleanCounter >; + http3FinalCwnd: GleanMemoryDistribution; + http3FinalWMax: GleanMemoryDistribution; + http3HystartCssEntries: Record< + "exited_ce" | "exited_hystart" | "not_exited", + GleanCustomDistribution + >; + http3HystartCssRoundsFinished: Record< + "exited_ce" | "exited_hystart" | "not_exited", + GleanCustomDistribution + >; http3LossRatio: GleanCustomDistribution; + http3LossRatioFiltered: GleanCustomDistribution; + http3PeerMaxUdpPayload: GleanCustomDistribution; http3QuicFrameCount: Record< | "ack_frequency_rx" | "ack_frequency_tx" @@ -256,6 +277,27 @@ interface GleanImpl { | "streams_blocked_tx", GleanCounter >; + http3SlowStartExitAccuracy: Record< + "ce_exit" | "heuristic_exit", + GleanCustomDistribution + >; + http3SlowStartExitAccuracyWMax: Record< + "ce_exit" | "heuristic_exit", + GleanCustomDistribution + >; + http3SlowStartExitCwnd: GleanMemoryDistribution; + http3SlowStartExitDirectionHeuristic: Record< + "exact" | "overshoot" | "undershoot", + GleanCounter + >; + http3SlowStartExitDirectionLoss: Record< + "exact" | "overshoot" | "undershoot", + GleanCounter + >; + http3SlowStartExitReason: Record<"ce" | "heuristic", GleanCounter>; + http3SlowStartExited: Record<"exited" | "not_exited", GleanCounter>; + http3SlowStartExitedFiltered: Record<"exited" | "not_exited", GleanCounter>; + http3SpuriousCongestionEventRatio: GleanCustomDistribution; http3UdpDatagramSegmentSizeReceived: GleanMemoryDistribution; http3UdpDatagramSegmentSizeSent: GleanMemoryDistribution; http3UdpDatagramSegmentsReceived: GleanCustomDistribution; @@ -265,6 +307,7 @@ interface GleanImpl { http3UploadThroughput: GleanCustomDistribution; http3UploadThroughput100: GleanCustomDistribution; http3UploadThroughput1050: GleanCustomDistribution; + http3UploadThroughput110: GleanCustomDistribution; http3UploadThroughput50100: GleanCustomDistribution; httpChannelDisposition: Record< | "http_cancelled" @@ -378,38 +421,31 @@ interface GleanImpl { | "success", GleanCounter >; - localNetworkAccessPort: GleanCustomDistribution; + localNetworkAccessConnection: GleanEventWithExtras<{ + initiator?: string; + is_secure_context?: string; + load_success?: string; + prompt_action?: string; + protocol?: string; + target_host?: string; + target_ip?: string; + target_port?: string; + top_level_site?: string; + }>; localNetworkAccessPromptsShown: Record< - "local_network" | "localhost", + | "local_network" + | "local_network_cross_site" + | "localhost" + | "localhost_cross_site", GleanCounter >; localNetworkBlockedTracker: GleanCounter; osSocketLimitReached: GleanCounter; - prcloseTcpBlockingTimeConnectivityChange: GleanTimingDistribution; - prcloseTcpBlockingTimeLinkChange: GleanTimingDistribution; - prcloseTcpBlockingTimeNormal: GleanTimingDistribution; - prcloseTcpBlockingTimeOffline: GleanTimingDistribution; - prcloseTcpBlockingTimeShutdown: GleanTimingDistribution; prcloseUdpBlockingTimeConnectivityChange: GleanTimingDistribution; prcloseUdpBlockingTimeLinkChange: GleanTimingDistribution; prcloseUdpBlockingTimeNormal: GleanTimingDistribution; prcloseUdpBlockingTimeOffline: GleanTimingDistribution; prcloseUdpBlockingTimeShutdown: GleanTimingDistribution; - prconnectBlockingTimeConnectivityChange: GleanTimingDistribution; - prconnectBlockingTimeLinkChange: GleanTimingDistribution; - prconnectBlockingTimeNormal: GleanTimingDistribution; - prconnectBlockingTimeOffline: GleanTimingDistribution; - prconnectBlockingTimeShutdown: GleanTimingDistribution; - prconnectFailBlockingTimeConnectivityChange: GleanTimingDistribution; - prconnectFailBlockingTimeLinkChange: GleanTimingDistribution; - prconnectFailBlockingTimeNormal: GleanTimingDistribution; - prconnectFailBlockingTimeOffline: GleanTimingDistribution; - prconnectFailBlockingTimeShutdown: GleanTimingDistribution; - prconnectcontinueBlockingTimeConnectivityChange: GleanTimingDistribution; - prconnectcontinueBlockingTimeLinkChange: GleanTimingDistribution; - prconnectcontinueBlockingTimeNormal: GleanTimingDistribution; - prconnectcontinueBlockingTimeOffline: GleanTimingDistribution; - prconnectcontinueBlockingTimeShutdown: GleanTimingDistribution; proxyInfoType: Record< | "direct" | "http" @@ -423,17 +459,8 @@ interface GleanImpl { >; residualCacheFolderCount: GleanCounter; residualCacheFolderRemoval: Record<"failure" | "success", GleanCounter>; - speculativeConnectOutcome: Record< - | "aborted_https_not_enabled" - | "aborted_socket_fail" - | "aborted_socket_limit" - | "successful", - GleanCounter - >; sqliteCookiesBlockMainThread: GleanTimingDistribution; sqliteCookiesTimeToBlockMainThread: GleanTimingDistribution; - transactionWaitTime: GleanTimingDistribution; - transactionWaitTimeHttpsRr: GleanTimingDistribution; trrCompleteLoad: Record< | "dns.shaw.ca" | "dns.shaw.ca_2" @@ -497,15 +524,6 @@ interface GleanImpl { | "private.canadianshield.cira.ca_3", GleanTimingDistribution >; - trrFetchDuration: Record< - | "h1" - | "h1_network_only" - | "h2" - | "h2_network_only" - | "h3" - | "h3_network_only", - GleanTimingDistribution - >; trrFirstSentToLastReceived: Record< | "dns.shaw.ca" | "dns.shaw.ca_2" @@ -742,6 +760,222 @@ interface GleanImpl { smartblockembedsShown: GleanCounter; }; + smartWindow: { + addTabsClick: GleanEventWithExtras<{ + chat_id?: string; + location?: string; + message_seq?: string; + tabs_available?: string; + tabs_preselected?: string; + }>; + addTabsSelection: GleanEventWithExtras<{ + chat_id?: string; + location?: string; + message_seq?: string; + tabs_available?: string; + tabs_preselected?: string; + tabs_selected?: string; + }>; + chatPreviousSession: GleanEventWithExtras<{ tabs?: string }>; + chatRetrieved: GleanEventWithExtras<{ + chat_id?: string; + location?: string; + message_seq?: string; + time_delta?: string; + }>; + chatStorage: GleanQuantity; + chatSubmit: GleanEventWithExtras<{ + chat_id?: string; + detected_intent?: string; + location?: string; + memories?: string; + mentions?: string; + message_seq?: string; + model?: string; + submit_type?: string; + tabs?: string; + tokens?: string; + }>; + getPageContent: GleanEventWithExtras<{ + chat_id?: string; + length?: string; + location?: string; + message_seq?: string; + time?: string; + }>; + historyClick: GleanEventWithExtras<{ + chat_id?: string; + location?: string; + message_seq?: string; + position?: string; + total?: string; + }>; + historyDisplayed: GleanEventWithExtras<{ + chat_id?: string; + location?: string; + message_seq?: string; + reason?: string; + total?: string; + }>; + intentChangePreview: GleanEventWithExtras<{ + chat_id?: string; + current_intent?: string; + location?: string; + message_seq?: string; + }>; + linkClick: GleanEventWithExtras<{ + chat_id?: string; + location?: string; + message_seq?: string; + }>; + memoriesCount: Record<"conversation" | "history", GleanQuantity>; + memoriesLastUpdated: GleanDatetime; + memoriesNuke: GleanEventNoExtras; + memoriesOptin: Record< + "generate_from_conversation" | "generate_from_history", + GleanBoolean + >; + memoriesPanelDisplayed: GleanEventWithExtras<{ + memories?: string; + source?: string; + }>; + memoriesToggle: GleanEventWithExtras<{ + chat_id?: string; + location?: string; + memories?: string; + message_seq?: string; + toggle?: string; + }>; + memoryApplied: GleanEventWithExtras<{ + chat_id?: string; + location?: string; + message_seq?: string; + }>; + memoryAppliedClick: GleanEventWithExtras<{ + chat_id?: string; + location?: string; + message_seq?: string; + }>; + memoryRemovedPanel: GleanEventWithExtras<{ + memories?: string; + trigger?: string; + }>; + mentionRemove: GleanEventWithExtras<{ + chat_id?: string; + location?: string; + mentions?: string; + message_seq?: string; + }>; + mentionSelect: GleanEventWithExtras<{ + chat_id?: string; + length?: string; + location?: string; + mentions_available?: string; + message_seq?: string; + }>; + mentionStart: GleanEventWithExtras<{ + chat_id?: string; + location?: string; + mentions_available?: string; + message_seq?: string; + }>; + model: GleanString; + modelRequest: GleanEventWithExtras<{ + chat_id?: string; + detected_intent?: string; + intent?: string; + location?: string; + memories?: string; + message_seq?: string; + request_id?: string; + tokens?: string; + }>; + modelResponse: GleanEventWithExtras<{ + chat_id?: string; + duration?: string; + error?: string; + intent?: string; + latency?: string; + location?: string; + memories?: string; + message_seq?: string; + model?: string; + request_id?: string; + tokens?: string; + }>; + navigateSubmit: GleanEventWithExtras<{ + chat_id?: string; + detected_intent?: string; + location?: string; + message_seq?: string; + model?: string; + submit_type?: string; + }>; + onboardingComplete: GleanEventNoExtras; + onboardingModelNavigate: GleanEventWithExtras<{ model?: string }>; + onboardingModelSelected: GleanEventWithExtras<{ model?: string }>; + onboardingScreenImpression: GleanEventWithExtras<{ message_id?: string }>; + openWindow: GleanEventWithExtras<{ + fxa?: string; + onboarding?: string; + trigger?: string; + }>; + quickPromptClicked: GleanEventWithExtras<{ + chat_id?: string; + location?: string; + message_seq?: string; + starter?: string; + }>; + quickPromptDisplayed: GleanEventWithExtras<{ + chat_id?: string; + location?: string; + message_seq?: string; + prompts?: string; + }>; + removeTab: GleanEventWithExtras<{ + chat_id?: string; + location?: string; + message_seq?: string; + tabs_selected?: string; + }>; + retryNoMemories: GleanEventWithExtras<{ + chat_id?: string; + location?: string; + message_seq?: string; + }>; + searchHandoff: GleanEventWithExtras<{ + chat_id?: string; + location?: string; + message_seq?: string; + model?: string; + provider?: string; + }>; + searchSubmit: GleanEventWithExtras<{ + chat_id?: string; + detected_intent?: string; + length?: string; + location?: string; + message_seq?: string; + model?: string; + provider?: string; + }>; + settingsMemories: GleanEventWithExtras<{ enabled?: string; type?: string }>; + settingsModel: GleanEventWithExtras<{ + new_model?: string; + previous_model?: string; + }>; + sidebarClose: GleanEventWithExtras<{ + chat_id?: string; + message_seq?: string; + }>; + sidebarOpen: GleanEventWithExtras<{ + chat_id?: string; + message_seq?: string; + }>; + tabsOpened: GleanCounter; + uriLoad: GleanEventWithExtras<{ model?: string }>; + }; + messagingSystem: { addonVersion: GleanString; browserSessionId: GleanUuid; @@ -776,6 +1010,52 @@ interface GleanImpl { dltoken: GleanString; experiment: GleanString; medium: GleanString; + msclkid: GleanString; + msstoresignedin: GleanString; + source: GleanString; + ua: GleanString; + unknownKeys: Record; + variation: GleanString; + }; + + microsurvey: { + addonVersion: GleanString; + appChannel: GleanString; + appDisplayVersion: GleanString; + bucketId: GleanString; + event: GleanString; + eventContext: GleanText; + eventContextParseError: GleanCounter; + eventInputValue: GleanText; + eventPage: GleanString; + eventReason: GleanString; + eventScreenFamily: GleanText; + eventScreenId: GleanText; + eventScreenIndex: GleanQuantity; + eventScreenInitials: GleanText; + eventSource: GleanString; + gleanPingForPingFailures: GleanCounter; + impressionId: GleanUuid; + invalidNestedData: Record; + locale: GleanString; + messageId: GleanText; + os: GleanString; + osVersion: GleanString; + pingType: GleanString; + source: GleanString; + unknownKeyCount: GleanCounter; + unknownKeys: Record; + windowsBuildNumber: GleanQuantity; + }; + + microsurveyAttribution: { + campaign: GleanString; + content: GleanString; + dlsource: GleanString; + dltoken: GleanString; + experiment: GleanString; + medium: GleanString; + msclkid: GleanString; msstoresignedin: GleanString; source: GleanString; ua: GleanString; @@ -825,8 +1105,32 @@ interface GleanImpl { preferencesSize: GleanQuantity; profDDiskSpace: GleanQuantity; pswdEncrypted: GleanBoolean; + restoreComplete: GleanEventWithExtras<{ restore_id?: string }>; restoreDisabledReason: GleanString; restoreEnabled: GleanBoolean; + restoreFailed: GleanEventWithExtras<{ + error_type?: string; + restore_id?: string; + }>; + restoreFileChosen: GleanEventWithExtras<{ + app_name?: string; + backup_timestamp?: string; + build_id?: string; + encryption?: string; + location?: string; + os_name?: string; + os_version?: string; + restore_id?: string; + telemetry_enabled?: string; + valid?: string; + version?: string; + }>; + restoreStarted: GleanEventWithExtras<{ + replace?: string; + restore_id?: string; + }>; + restoredProfileData: GleanObject; + restoredProfileLaunched: GleanEventWithExtras<{ restore_id?: string }>; schedulerEnabled: GleanBoolean; securityDataSize: GleanQuantity; sessionStoreBackupsDirectorySize: GleanQuantity; @@ -852,6 +1156,11 @@ interface GleanImpl { }>; }; + browserCustomkeys: { + actions: Record<"change" | "clear" | "reset" | "reset_all", GleanCounter>; + opened: GleanCounter; + }; + downloads: { panelShown: GleanCounter; addedFileExtension: GleanEventWithExtras<{ value?: string }>; @@ -893,6 +1202,12 @@ interface GleanImpl { | "syncedtabs", GleanCustomDistribution >; + clickSendTab: GleanEventWithExtras<{ + action?: string; + device_count?: string; + }>; + sendTabExposed: GleanEventWithExtras<{ device_count?: string }>; + sendTabOpened: GleanEventWithExtras<{ device_count?: string }>; }; firefoxviewNext: { @@ -1085,15 +1400,24 @@ interface GleanImpl { }; ipprotection: { + alertButtonClicked: GleanEventWithExtras<{ + buttonType?: string; + reason?: string; + }>; + bandwidthUsedThreshold: GleanEventWithExtras<{ percentage?: string }>; clickUpgradeButton: GleanEventNoExtras; - enabled: GleanBoolean; - toggled: GleanEventWithExtras<{ - duration?: string; - enabled?: string; + enrollment: GleanEventWithExtras<{ enrolled?: string }>; + exclusionToggled: GleanEventWithExtras<{ excluded?: string }>; + getStarted: GleanEventNoExtras; + removedFromToolbar: GleanEventNoExtras; + error: GleanEventWithExtras<{ source?: string }>; + exclusionAdded: GleanCounter; + paused: GleanEventWithExtras<{ wasActive?: string }>; + started: GleanEventWithExtras<{ + inPrivateBrowsing?: string; userAction?: string; }>; - usageRx: GleanMemoryDistribution; - usageTx: GleanMemoryDistribution; + stopped: GleanEventWithExtras<{ duration?: string; userAction?: string }>; }; backgroundUpdate: { @@ -1120,6 +1444,11 @@ interface GleanImpl { }; browser: { + aiControlChanged: GleanEventWithExtras<{ + feature?: string; + selection?: string; + }>; + aiControlIsBlocking: Record; attributionErrors: Record< | "decode_error" | "empty_error" @@ -1130,11 +1459,21 @@ interface GleanImpl { GleanCounter >; defaultAtLaunch: GleanBoolean; + globalAiControlIsBlocking: GleanBoolean; + globalAiControlToggled: GleanEventWithExtras<{ blocked?: string }>; isUserDefault: Record<"false" | "true", GleanCounter>; isUserDefaultError: Record<"false" | "true", GleanCounter>; setDefaultAlwaysCheck: Record<"false" | "true", GleanCounter>; setDefaultDialogPromptRawcount: GleanCustomDistribution; setDefaultError: Record<"false" | "true", GleanCounter>; + setDefaultPdfHandlerModernSettingsResult: Record< + "Failure" | "Success", + GleanCounter + >; + setDefaultPdfHandlerOpenWithResult: Record< + "Failure" | "Success", + GleanCounter + >; setDefaultPdfHandlerUserChoiceResult: Record< | "ErrBuild" | "ErrExeHash" @@ -1213,6 +1552,10 @@ interface GleanImpl { enabled: GleanBoolean; }; + qrcode: { + opened: GleanCounter; + }; + security: { globalPrivacyControlEnabled: GleanQuantity; httpsOnlyModeEnabled: GleanQuantity; @@ -1223,9 +1566,6 @@ interface GleanImpl { value?: string; }>; shadowedHtmlDocumentPropertyAccess: GleanEventWithExtras<{ name?: string }>; - shadowedHtmlFormElementPropertyAccess: GleanEventWithExtras<{ - name?: string; - }>; cspViolationInternalPage: GleanEventWithExtras<{ blockeduridetails?: string; blockeduritype?: string; @@ -1876,18 +2216,6 @@ interface GleanImpl { previous_topics?: string; topics?: string; }>; - trendingSearchDismiss: GleanEventWithExtras<{ - newtab_visit_id?: string; - variant?: string; - }>; - trendingSearchImpression: GleanEventWithExtras<{ - newtab_visit_id?: string; - variant?: string; - }>; - trendingSearchSuggestionOpen: GleanEventWithExtras<{ - newtab_visit_id?: string; - variant?: string; - }>; wallpaperCategoryClick: GleanEventWithExtras<{ newtab_visit_id?: string; selected_category?: string; @@ -1918,6 +2246,30 @@ interface GleanImpl { newtab_visit_id?: string; user_selection?: string; }>; + widgetsContainerAction: GleanEventWithExtras<{ + action_type?: string; + action_value?: string; + newtab_visit_id?: string; + widget_size?: string; + }>; + widgetsEnabled: GleanEventWithExtras<{ + enabled?: string; + newtab_visit_id?: string; + widget_name?: string; + widget_size?: string; + widget_source?: string; + }>; + widgetsError: GleanEventWithExtras<{ + error_type?: string; + newtab_visit_id?: string; + widget_name?: string; + widget_size?: string; + }>; + widgetsImpression: GleanEventWithExtras<{ + newtab_visit_id?: string; + widget_name?: string; + widget_size?: string; + }>; widgetsListsChangeDisplay: GleanEventWithExtras<{ display_status?: string; newtab_visit_id?: string; @@ -1940,10 +2292,14 @@ interface GleanImpl { newtab_visit_id?: string; user_action?: string; }>; - }; - - newtabHandoffPreference: { - enabled: GleanBoolean; + widgetsUserEvent: GleanEventWithExtras<{ + action_value?: string; + newtab_visit_id?: string; + user_action?: string; + widget_name?: string; + widget_size?: string; + widget_source?: string; + }>; }; newtabSearch: { @@ -2052,21 +2408,34 @@ interface GleanImpl { section_position?: string; }>; surfaceId: GleanString; - thumbVotingInteraction: GleanEventWithExtras<{ - corpus_item_id?: string; - format?: string; - is_section_followed?: string; + topSitesClick: GleanEventWithExtras<{ + advertiser_name?: string; + frecency_boosted?: string; + frecency_boosted_has_exposure?: string; + is_sponsored?: string; + position?: string; + tile_id?: string; + visible_topsites?: string; + }>; + topSitesDismiss: GleanEventWithExtras<{ + advertiser_name?: string; + is_sponsored?: string; + position?: string; + tile_id?: string; + }>; + topSitesImpression: GleanEventWithExtras<{ + advertiser_name?: string; + frecency_boosted?: string; + frecency_boosted_has_exposure?: string; + is_sponsored?: string; + position?: string; + tile_id?: string; + visible_topsites?: string; + }>; + topSitesShowPrivacyClick: GleanEventWithExtras<{ + advertiser_name?: string; position?: string; - received_rank?: string; - recommendation_id?: string; - recommended_at?: string; - scheduled_corpus_item_id?: string; - section?: string; - section_position?: string; - thumbs_down?: string; - thumbs_up?: string; tile_id?: string; - topic?: string; }>; utcOffset: GleanQuantity; }; @@ -2151,25 +2520,8 @@ interface GleanImpl { topic?: string; }>; shim: GleanText; + spocPlaceholderDuration: GleanTimingDistribution; sponsoredStoriesEnabled: GleanBoolean; - thumbVotingInteraction: GleanEventWithExtras<{ - content_redacted?: string; - corpus_item_id?: string; - format?: string; - is_section_followed?: string; - newtab_visit_id?: string; - position?: string; - received_rank?: string; - recommendation_id?: string; - recommended_at?: string; - scheduled_corpus_item_id?: string; - section?: string; - section_position?: string; - thumbs_down?: string; - thumbs_up?: string; - tile_id?: string; - topic?: string; - }>; topicClick: GleanEventWithExtras<{ newtab_visit_id?: string; topic?: string; @@ -2292,9 +2644,15 @@ interface GleanImpl { }; aboutpreferences: { - showClick: GleanEventWithExtras<{ value?: string }>; - showHash: GleanEventWithExtras<{ value?: string }>; - showInitial: GleanEventWithExtras<{ value?: string }>; + change: GleanEventWithExtras<{ + pane?: string; + session?: string; + setting?: string; + }>; + close: GleanEventWithExtras<{ session?: string }>; + showClick: GleanEventWithExtras<{ session?: string; value?: string }>; + showHash: GleanEventWithExtras<{ session?: string; value?: string }>; + showInitial: GleanEventWithExtras<{ session?: string; value?: string }>; }; intlUiBrowserLanguage: { @@ -2325,6 +2683,12 @@ interface GleanImpl { providerChoiceValue: GleanEventWithExtras<{ value?: string }>; }; + securityPreferencesWarnings: { + warningDismissed: GleanEventNoExtras; + warningFixed: GleanEventNoExtras; + warningsShown: GleanEventWithExtras<{ count?: string }>; + }; + aboutprivatebrowsing: { clickDismissButton: GleanEventNoExtras; clickInfoLink: GleanEventNoExtras; @@ -2358,6 +2722,7 @@ interface GleanImpl { displayed: GleanEventNoExtras; learnMore: GleanEventNoExtras; name: GleanEventNoExtras; + shortcut: GleanEventWithExtras<{ value?: string }>; theme: GleanEventWithExtras<{ value?: string }>; }; @@ -2519,6 +2884,8 @@ interface GleanImpl { contextmenu: Record; contextmenuVisual: Record; searchbar: Record; + smartbar: Record; + smartwindowAssistant: Record; urlbar: Record; urlbarHandoff: Record; urlbarPersisted: Record; @@ -2533,6 +2900,8 @@ interface GleanImpl { contextmenuVisual: Record; reload: Record; searchbar: Record; + smartbar: Record; + smartwindowAssistant: Record; system: Record; tabhistory: Record; unknown: Record; @@ -2550,6 +2919,8 @@ interface GleanImpl { contextmenuVisual: Record; reload: Record; searchbar: Record; + smartbar: Record; + smartwindowAssistant: Record; system: Record; tabhistory: Record; unknown: Record; @@ -2567,6 +2938,8 @@ interface GleanImpl { contextmenuVisual: Record; reload: Record; searchbar: Record; + smartbar: Record; + smartwindowAssistant: Record; system: Record; tabhistory: Record; unknown: Record; @@ -2672,6 +3045,7 @@ interface GleanImpl { }>; experimentInfo: GleanObject; impression: GleanEventWithExtras<{ + has_ai_summary?: string; impression_id?: string; is_private?: string; is_shopping_page?: string; @@ -2690,6 +3064,7 @@ interface GleanImpl { handoff: Record; historymenu: Record; keywordoffer: Record; + messagingSystem: Record; oneoff: Record; other: Record; searchbutton: Record; @@ -2750,7 +3125,7 @@ interface GleanImpl { contextualManager: { passwordsEnabled: GleanEventWithExtras<{ checked?: string }>; - sidebarToggle: GleanEventWithExtras<{ opened?: string }>; + sidebarToggle: GleanEventWithExtras<{ opened?: string; version?: string }>; notificationInteraction: GleanEventWithExtras<{ action_type?: string; notification_detail?: string; @@ -2796,6 +3171,7 @@ interface GleanImpl { historyIconClick: GleanEventWithExtras<{ sidebar_open?: string }>; keyboardShortcut: GleanEventWithExtras<{ opened?: string; panel?: string }>; link: Record; + passwordsIconClick: GleanEventWithExtras<{ sidebar_open?: string }>; positionSettings: GleanString; resize: GleanEventWithExtras<{ current?: string; @@ -2869,6 +3245,10 @@ interface GleanImpl { browserUiInteraction: { allTabsPanelDragstartTabEventCount: GleanCounter; allTabsPanelEntrypoint: Record; + listAllTabsAction: Record< + "close_all_duplicates" | "search_tabs" | "tabs_from_devices", + GleanCounter + >; tabMovement: Record< | "from_external_app_next_to_active_tab" | "from_external_app_tab_strip_end" @@ -2885,6 +3265,7 @@ interface GleanImpl { pageactionPanel: Record; pageactionUrlbar: Record; pinnedOverflowMenu: Record; + preferencesPaneAi: Record; preferencesPaneContainers: Record; preferencesPaneExperimental: Record; preferencesPaneGeneral: Record; @@ -2911,6 +3292,18 @@ interface GleanImpl { openNextToActiveTabSettingsEnabled: GleanBoolean; }; + splitview: { + end: GleanEventWithExtras<{ tab_layout?: string; trigger?: string }>; + resize: GleanEventWithExtras<{ width?: string }>; + reverse: GleanEventWithExtras<{ trigger?: string }>; + start: GleanEventWithExtras<{ + tab_layout?: string; + tabgroup?: string; + trigger?: string; + }>; + uriCount: Record<"1" | "2", GleanCounter>; + }; + tabgroup: { activeGroups: Record<"collapsed" | "expanded", GleanQuantity>; addTab: GleanEventWithExtras<{ @@ -3002,6 +3395,13 @@ interface GleanImpl { ungroup: GleanEventWithExtras<{ source?: string }>; }; + tabNotes: { + added: GleanEventWithExtras<{ note_length?: string; source?: string }>; + deleted: GleanEventWithExtras<{ note_age_hours?: string; source?: string }>; + edited: GleanEventWithExtras<{ note_length?: string; source?: string }>; + expanded: GleanEventWithExtras<{ note_length?: string }>; + }; + webApp: { activate: GleanEventNoExtras; eject: GleanEventNoExtras; @@ -3077,8 +3477,12 @@ interface GleanImpl { abandonment_type?: string; actions?: string; available_semantic_sources?: string; + chat_id?: string; groups?: string; + intent?: string; interaction?: string; + location?: string; + model?: string; n_chars?: string; n_results?: string; n_words?: string; @@ -3091,8 +3495,12 @@ interface GleanImpl { autocompleteSixthResultTime: GleanTimingDistribution; autofillDeletion: GleanCounter; bounce: GleanEventWithExtras<{ + chat_id?: string; engagement_type?: string; + intent?: string; interaction?: string; + location?: string; + model?: string; n_chars?: string; n_results?: string; n_words?: string; @@ -3107,8 +3515,12 @@ interface GleanImpl { view_time?: string; }>; disable: GleanEventWithExtras<{ + chat_id?: string; feature?: string; + intent?: string; interaction?: string; + location?: string; + model?: string; n_chars?: string; n_results?: string; n_words?: string; @@ -3121,9 +3533,13 @@ interface GleanImpl { engagement: GleanEventWithExtras<{ actions?: string; available_semantic_sources?: string; + chat_id?: string; engagement_type?: string; groups?: string; + intent?: string; interaction?: string; + location?: string; + model?: string; n_chars?: string; n_results?: string; n_words?: string; @@ -3135,11 +3551,16 @@ interface GleanImpl { selected_position?: string; selected_result?: string; }>; - exposure: GleanEventWithExtras<{ results?: string; terminal?: string }>; + exposure: GleanEventWithExtras<{ + results?: string; + sap?: string; + terminal?: string; + }>; heuristicResultMissing: GleanRate; keywordExposure: GleanEventWithExtras<{ keyword?: string; result?: string; + sap?: string; terminal?: string; }>; prefMaxResults: GleanQuantity; @@ -3148,7 +3569,6 @@ interface GleanImpl { prefSuggestOnlineEnabled: GleanBoolean; prefSuggestSponsored: GleanBoolean; prefSuggestTopsites: GleanBoolean; - prefSwitchTabsSearchAllContainers: GleanBoolean; }; urlbarMerino: { @@ -3334,6 +3754,28 @@ interface GleanImpl { firstPaintTwo: GleanQuantity; }; + webNotificationPermission: { + iconClicked: GleanEventWithExtras<{ site_category?: string }>; + iconShown: GleanEventWithExtras<{ site_category?: string }>; + permissionRevokedPreferences: GleanEventWithExtras<{ + site_category?: string; + }>; + permissionRevokedToolbar: GleanEventWithExtras<{ site_category?: string }>; + promptBlocked: GleanEventWithExtras<{ + reason?: string; + site_category?: string; + }>; + promptInteraction: GleanEventWithExtras<{ + action?: string; + is_persistent?: string; + site_category?: string; + }>; + promptShown: GleanEventWithExtras<{ + site_category?: string; + trigger?: string; + }>; + }; + devtoolsAccessibility: { accessibleContextMenuItemActivated: Record; accessibleContextMenuOpened: GleanCounter; @@ -4152,6 +4594,19 @@ interface GleanImpl { time_open?: string; value?: string; }>; + toolboxComponentError: GleanEventWithExtras<{ + component_stack?: string; + error_name?: string; + stack?: string; + }>; + toolboxServerError: GleanEventWithExtras<{ + error_name?: string; + packet_error?: string; + packet_target?: string; + packet_type?: string; + server_stack?: string; + stack?: string; + }>; unregisterWorkerApplication: GleanEventWithExtras<{ session_id?: string; value?: string; @@ -4196,6 +4651,10 @@ interface GleanImpl { nonBlankPaint: GleanTimingDistribution; }; + urlfixup: { + suffix: GleanDualLabeledCounter; + }; + useCounter: { contentDocumentsDestroyed: GleanCounter; dedicatedWorkersDestroyed: GleanCounter; @@ -4205,16 +4664,15 @@ interface GleanImpl { }; useCounterCssDoc: { - alignmentBaseline: GleanCounter; backgroundRepeatX: GleanCounter; backgroundRepeatY: GleanCounter; - baselineShift: GleanCounter; bufferedRendering: GleanCounter; colorRendering: GleanCounter; cssAccentColor: GleanCounter; cssAlignContent: GleanCounter; cssAlignItems: GleanCounter; cssAlignSelf: GleanCounter; + cssAlignmentBaseline: GleanCounter; cssAll: GleanCounter; cssAnchorName: GleanCounter; cssAnchorScope: GleanCounter; @@ -4227,6 +4685,9 @@ interface GleanImpl { cssAnimationIterationCount: GleanCounter; cssAnimationName: GleanCounter; cssAnimationPlayState: GleanCounter; + cssAnimationRange: GleanCounter; + cssAnimationRangeEnd: GleanCounter; + cssAnimationRangeStart: GleanCounter; cssAnimationTimeline: GleanCounter; cssAnimationTimingFunction: GleanCounter; cssAppearance: GleanCounter; @@ -4245,6 +4706,7 @@ interface GleanImpl { cssBackgroundPositionY: GleanCounter; cssBackgroundRepeat: GleanCounter; cssBackgroundSize: GleanCounter; + cssBaselineShift: GleanCounter; cssBaselineSource: GleanCounter; cssBlockSize: GleanCounter; cssBorder: GleanCounter; @@ -4678,6 +5140,9 @@ interface GleanImpl { cssTextAlignLast: GleanCounter; cssTextAnchor: GleanCounter; cssTextAutospace: GleanCounter; + cssTextBox: GleanCounter; + cssTextBoxEdge: GleanCounter; + cssTextBoxTrim: GleanCounter; cssTextCombineUpright: GleanCounter; cssTextDecoration: GleanCounter; cssTextDecorationColor: GleanCounter; @@ -4702,6 +5167,7 @@ interface GleanImpl { cssTextWrap: GleanCounter; cssTextWrapMode: GleanCounter; cssTextWrapStyle: GleanCounter; + cssTimelineScope: GleanCounter; cssTop: GleanCounter; cssTouchAction: GleanCounter; cssTransform: GleanCounter; @@ -4912,16 +5378,15 @@ interface GleanImpl { }; useCounterCssPage: { - alignmentBaseline: GleanCounter; backgroundRepeatX: GleanCounter; backgroundRepeatY: GleanCounter; - baselineShift: GleanCounter; bufferedRendering: GleanCounter; colorRendering: GleanCounter; cssAccentColor: GleanCounter; cssAlignContent: GleanCounter; cssAlignItems: GleanCounter; cssAlignSelf: GleanCounter; + cssAlignmentBaseline: GleanCounter; cssAll: GleanCounter; cssAnchorName: GleanCounter; cssAnchorScope: GleanCounter; @@ -4934,6 +5399,9 @@ interface GleanImpl { cssAnimationIterationCount: GleanCounter; cssAnimationName: GleanCounter; cssAnimationPlayState: GleanCounter; + cssAnimationRange: GleanCounter; + cssAnimationRangeEnd: GleanCounter; + cssAnimationRangeStart: GleanCounter; cssAnimationTimeline: GleanCounter; cssAnimationTimingFunction: GleanCounter; cssAppearance: GleanCounter; @@ -4952,6 +5420,7 @@ interface GleanImpl { cssBackgroundPositionY: GleanCounter; cssBackgroundRepeat: GleanCounter; cssBackgroundSize: GleanCounter; + cssBaselineShift: GleanCounter; cssBaselineSource: GleanCounter; cssBlockSize: GleanCounter; cssBorder: GleanCounter; @@ -5385,6 +5854,9 @@ interface GleanImpl { cssTextAlignLast: GleanCounter; cssTextAnchor: GleanCounter; cssTextAutospace: GleanCounter; + cssTextBox: GleanCounter; + cssTextBoxEdge: GleanCounter; + cssTextBoxTrim: GleanCounter; cssTextCombineUpright: GleanCounter; cssTextDecoration: GleanCounter; cssTextDecorationColor: GleanCounter; @@ -5409,6 +5881,7 @@ interface GleanImpl { cssTextWrap: GleanCounter; cssTextWrapMode: GleanCounter; cssTextWrapStyle: GleanCounter; + cssTimelineScope: GleanCounter; cssTop: GleanCounter; cssTouchAction: GleanCounter; cssTransform: GleanCounter; @@ -5621,6 +6094,7 @@ interface GleanImpl { useCounterDeprecatedOpsDoc: { ambientLightEvent: GleanCounter; appCache: GleanCounter; + ckeditor4CompatHack: GleanCounter; components: GleanCounter; createImageBitmapCanvasRenderingContext2D: GleanCounter; deprecatedTestingAttribute: GleanCounter; @@ -5634,6 +6108,7 @@ interface GleanImpl { externalAddSearchProvider: GleanCounter; formSubmissionUntrustedEvent: GleanCounter; fullscreenAttribute: GleanCounter; + gwtrichTextAreaCompatHack: GleanCounter; idbobjectStoreCreateIndexLocale: GleanCounter; idbopenDboptionsStorageType: GleanCounter; imageBitmapRenderingContextTransferImageBitmap: GleanCounter; @@ -5645,15 +6120,23 @@ interface GleanImpl { lenientThis: GleanCounter; mathMlDeprecatedMathSpaceValue2: GleanCounter; mathMlDeprecatedMathVariant: GleanCounter; + mathMlDeprecatedMoExplicitAccent: GleanCounter; + mathMlDeprecatedMoverNonExplicitAccent: GleanCounter; + mathMlDeprecatedMunderNonExplicitAccentunder: GleanCounter; motionEvent: GleanCounter; mouseEventMozPressure: GleanCounter; mozInputSource: GleanCounter; mozRequestFullScreenDeprecatedPrefix: GleanCounter; mozfullscreenchangeDeprecatedPrefix: GleanCounter; mozfullscreenerrorDeprecatedPrefix: GleanCounter; + mozlockorientationDeprecated: GleanCounter; + mozorientationDeprecated: GleanCounter; + mozorientationchangeDeprecated: GleanCounter; + mozunlockorientationDeprecated: GleanCounter; navigatorGetUserMedia: GleanCounter; nodeIteratorDetach: GleanCounter; offscreenCanvasToBlob: GleanCounter; + oldZecompatHack: GleanCounter; orientationEvent: GleanCounter; proximityEvent: GleanCounter; rtcpeerConnectionGetStreams: GleanCounter; @@ -5664,11 +6147,13 @@ interface GleanImpl { webrtcDeprecatedPrefix: GleanCounter; windowCcOntrollers: GleanCounter; windowContentUntrusted: GleanCounter; + xsltdeprecated: GleanCounter; }; useCounterDeprecatedOpsPage: { ambientLightEvent: GleanCounter; appCache: GleanCounter; + ckeditor4CompatHack: GleanCounter; components: GleanCounter; createImageBitmapCanvasRenderingContext2D: GleanCounter; deprecatedTestingAttribute: GleanCounter; @@ -5682,6 +6167,7 @@ interface GleanImpl { externalAddSearchProvider: GleanCounter; formSubmissionUntrustedEvent: GleanCounter; fullscreenAttribute: GleanCounter; + gwtrichTextAreaCompatHack: GleanCounter; idbobjectStoreCreateIndexLocale: GleanCounter; idbopenDboptionsStorageType: GleanCounter; imageBitmapRenderingContextTransferImageBitmap: GleanCounter; @@ -5693,15 +6179,23 @@ interface GleanImpl { lenientThis: GleanCounter; mathMlDeprecatedMathSpaceValue2: GleanCounter; mathMlDeprecatedMathVariant: GleanCounter; + mathMlDeprecatedMoExplicitAccent: GleanCounter; + mathMlDeprecatedMoverNonExplicitAccent: GleanCounter; + mathMlDeprecatedMunderNonExplicitAccentunder: GleanCounter; motionEvent: GleanCounter; mouseEventMozPressure: GleanCounter; mozInputSource: GleanCounter; mozRequestFullScreenDeprecatedPrefix: GleanCounter; mozfullscreenchangeDeprecatedPrefix: GleanCounter; mozfullscreenerrorDeprecatedPrefix: GleanCounter; + mozlockorientationDeprecated: GleanCounter; + mozorientationDeprecated: GleanCounter; + mozorientationchangeDeprecated: GleanCounter; + mozunlockorientationDeprecated: GleanCounter; navigatorGetUserMedia: GleanCounter; nodeIteratorDetach: GleanCounter; offscreenCanvasToBlob: GleanCounter; + oldZecompatHack: GleanCounter; orientationEvent: GleanCounter; proximityEvent: GleanCounter; rtcpeerConnectionGetStreams: GleanCounter; @@ -5712,6 +6206,7 @@ interface GleanImpl { webrtcDeprecatedPrefix: GleanCounter; windowCcOntrollers: GleanCounter; windowContentUntrusted: GleanCounter; + xsltdeprecated: GleanCounter; }; useCounterDoc: { @@ -5793,6 +6288,7 @@ interface GleanImpl { filteredCrossOriginIframe: GleanCounter; getUserMediaInsec: GleanCounter; getUserMediaUnfocused: GleanCounter; + gridAutoFlowInitialValueChange: GleanCounter; htmldialogelementShow: GleanCounter; htmldocumentCaretrangefrompoint: GleanCounter; htmldocumentExitpictureinpicture: GleanCounter; @@ -5836,17 +6332,13 @@ interface GleanImpl { jsOptimizeArraySpeciesFuse: GleanCounter; jsOptimizeGetIteratorFuse: GleanCounter; jsOptimizePromiseLookupFuse: GleanCounter; - jsRegexpSymbolProtocolOnPrimitive: GleanCounter; jsSmallOomRecovered: GleanCounter; jsSmallOomReported: GleanCounter; - jsThenable: GleanCounter; - jsThenableObjectProto: GleanCounter; - jsThenableProto: GleanCounter; - jsThenableStandardProto: GleanCounter; jsUseAsm: GleanCounter; jsWasm: GleanCounter; jsWasmLegacyExceptions: GleanCounter; - locationAncestororigins: GleanCounter; + locationAncestororiginsGetter: GleanCounter; + locationAncestororiginsSetter: GleanCounter; mathMlused: GleanCounter; mediadevicesEnumeratedevices: GleanCounter; mediadevicesGetdisplaymedia: GleanCounter; @@ -5879,7 +6371,55 @@ interface GleanImpl { mlsSend: GleanCounter; mlsStatedelete: GleanCounter; mlsStatedeletegroup: GleanCounter; + navigateEventInterceptWithPrecommitHandler: GleanCounter; + navigateeventDownloadrequestGetter: GleanCounter; + navigateeventDownloadrequestSetter: GleanCounter; + navigateeventFormdataGetter: GleanCounter; + navigateeventFormdataSetter: GleanCounter; + navigateeventHasuavisualtransitionGetter: GleanCounter; + navigateeventHasuavisualtransitionSetter: GleanCounter; + navigateeventIntercept: GleanCounter; + navigateeventScroll: GleanCounter; + navigateeventSourceelementGetter: GleanCounter; + navigateeventSourceelementSetter: GleanCounter; + navigationBack: GleanCounter; + navigationEntries: GleanCounter; + navigationForward: GleanCounter; + navigationNavigate: GleanCounter; + navigationOncurrententrychange: GleanCounter; + navigationOnnavigate: GleanCounter; + navigationOnnavigateerror: GleanCounter; + navigationOnnavigatesuccess: GleanCounter; + navigationReload: GleanCounter; + navigationTraverseto: GleanCounter; + navigationUpdatecurrententry: GleanCounter; + navigationactivationEntryGetter: GleanCounter; + navigationactivationEntrySetter: GleanCounter; + navigationactivationFromGetter: GleanCounter; + navigationactivationFromSetter: GleanCounter; + navigationactivationNavigationtypeGetter: GleanCounter; + navigationactivationNavigationtypeSetter: GleanCounter; + navigationhistoryentryGetstate: GleanCounter; + navigationprecommitcontrollerAddhandler: GleanCounter; + navigationprecommitcontrollerRedirect: GleanCounter; + navigationtransitionCommittedGetter: GleanCounter; + navigationtransitionCommittedSetter: GleanCounter; + navigationtransitionFinishedGetter: GleanCounter; + navigationtransitionFinishedSetter: GleanCounter; + navigationtransitionFromGetter: GleanCounter; + navigationtransitionFromSetter: GleanCounter; + navigationtransitionNavigationtypeGetter: GleanCounter; + navigationtransitionNavigationtypeSetter: GleanCounter; navigatorMozgetusermedia: GleanCounter; + navigatorRequestmidiaccess: GleanCounter; + notificationoptionsActions: GleanCounter; + notificationoptionsBadge: GleanCounter; + notificationoptionsImage: GleanCounter; + notificationoptionsNavigate: GleanCounter; + notificationoptionsRenotify: GleanCounter; + notificationoptionsRequireinteraction: GleanCounter; + notificationoptionsTimestamp: GleanCounter; + notificationoptionsVibrate: GleanCounter; ondommousescroll: GleanCounter; onmozmousepixelscroll: GleanCounter; percentageStrokeWidthInSvg: GleanCounter; @@ -5895,6 +6435,7 @@ interface GleanImpl { pushmanagerSubscribe: GleanCounter; pushsubscriptionUnsubscribe: GleanCounter; rangeCreatecontextualfragment: GleanCounter; + reportingobserverConstructor: GleanCounter; sanitizerConstructor: GleanCounter; sanitizerSanitize: GleanCounter; schedulerPosttask: GleanCounter; @@ -5903,6 +6444,9 @@ interface GleanImpl { svgsvgelementGetelementbyid: GleanCounter; textDirectiveNotCreated: GleanCounter; textDirectivePages: GleanCounter; + videodecoderConstructor: GleanCounter; + videoencoderConstructor: GleanCounter; + wakelockRequest: GleanCounter; webgpuRenderOutput: GleanCounter; webgpuRequestAdapter: GleanCounter; windowAbsoluteorientationsensor: GleanCounter; @@ -5954,7 +6498,6 @@ interface GleanImpl { windowDevicemotioneventacceleration: GleanCounter; windowDevicemotioneventrotationrate: GleanCounter; windowDomerror: GleanCounter; - windowEncodedvideochunk: GleanCounter; windowEnterpictureinpictureevent: GleanCounter; windowExternal: GleanCounter; windowFederatedcredential: GleanCounter; @@ -5968,14 +6511,6 @@ interface GleanImpl { windowKeyboardlayoutmap: GleanCounter; windowLinearaccelerationsensor: GleanCounter; windowMediasettingsrange: GleanCounter; - windowMidiaccess: GleanCounter; - windowMidiconnectionevent: GleanCounter; - windowMidiinput: GleanCounter; - windowMidiinputmap: GleanCounter; - windowMidimessageevent: GleanCounter; - windowMidioutput: GleanCounter; - windowMidioutputmap: GleanCounter; - windowMidiport: GleanCounter; windowNetworkinformation: GleanCounter; windowOffscreenbuffering: GleanCounter; windowOnbeforeinstallprompt: GleanCounter; @@ -5986,7 +6521,8 @@ interface GleanImpl { windowOnselectionchange: GleanCounter; windowOpenEmptyUrl: GleanCounter; windowOpendatabase: GleanCounter; - windowOrientation: GleanCounter; + windowOrientationGetter: GleanCounter; + windowOrientationSetter: GleanCounter; windowOrientationsensor: GleanCounter; windowOverconstrainederror: GleanCounter; windowPasswordcredential: GleanCounter; @@ -6011,9 +6547,6 @@ interface GleanImpl { windowPresentationrequest: GleanCounter; windowRelativeorientationsensor: GleanCounter; windowRemoteplayback: GleanCounter; - windowReport: GleanCounter; - windowReportbody: GleanCounter; - windowReportingobserver: GleanCounter; windowRtcerror: GleanCounter; windowRtcerrorevent: GleanCounter; windowRtcicetransport: GleanCounter; @@ -6048,12 +6581,6 @@ interface GleanImpl { windowUsbisochronousouttransferpacket: GleanCounter; windowUsbisochronousouttransferresult: GleanCounter; windowUsbouttransferresult: GleanCounter; - windowUseractivation: GleanCounter; - windowVideocolorspace: GleanCounter; - windowVideodecoder: GleanCounter; - windowVideoencoder: GleanCounter; - windowVideoframe: GleanCounter; - windowWakelock: GleanCounter; windowWakelocksentinel: GleanCounter; windowWebkitcancelanimationframe: GleanCounter; windowWebkitmediastream: GleanCounter; @@ -6153,6 +6680,7 @@ interface GleanImpl { filteredCrossOriginIframe: GleanCounter; getUserMediaInsec: GleanCounter; getUserMediaUnfocused: GleanCounter; + gridAutoFlowInitialValueChange: GleanCounter; htmldialogelementShow: GleanCounter; htmldocumentCaretrangefrompoint: GleanCounter; htmldocumentExitpictureinpicture: GleanCounter; @@ -6196,17 +6724,13 @@ interface GleanImpl { jsOptimizeArraySpeciesFuse: GleanCounter; jsOptimizeGetIteratorFuse: GleanCounter; jsOptimizePromiseLookupFuse: GleanCounter; - jsRegexpSymbolProtocolOnPrimitive: GleanCounter; jsSmallOomRecovered: GleanCounter; jsSmallOomReported: GleanCounter; - jsThenable: GleanCounter; - jsThenableObjectProto: GleanCounter; - jsThenableProto: GleanCounter; - jsThenableStandardProto: GleanCounter; jsUseAsm: GleanCounter; jsWasm: GleanCounter; jsWasmLegacyExceptions: GleanCounter; - locationAncestororigins: GleanCounter; + locationAncestororiginsGetter: GleanCounter; + locationAncestororiginsSetter: GleanCounter; mathMlused: GleanCounter; mediadevicesEnumeratedevices: GleanCounter; mediadevicesGetdisplaymedia: GleanCounter; @@ -6239,7 +6763,55 @@ interface GleanImpl { mlsSend: GleanCounter; mlsStatedelete: GleanCounter; mlsStatedeletegroup: GleanCounter; + navigateEventInterceptWithPrecommitHandler: GleanCounter; + navigateeventDownloadrequestGetter: GleanCounter; + navigateeventDownloadrequestSetter: GleanCounter; + navigateeventFormdataGetter: GleanCounter; + navigateeventFormdataSetter: GleanCounter; + navigateeventHasuavisualtransitionGetter: GleanCounter; + navigateeventHasuavisualtransitionSetter: GleanCounter; + navigateeventIntercept: GleanCounter; + navigateeventScroll: GleanCounter; + navigateeventSourceelementGetter: GleanCounter; + navigateeventSourceelementSetter: GleanCounter; + navigationBack: GleanCounter; + navigationEntries: GleanCounter; + navigationForward: GleanCounter; + navigationNavigate: GleanCounter; + navigationOncurrententrychange: GleanCounter; + navigationOnnavigate: GleanCounter; + navigationOnnavigateerror: GleanCounter; + navigationOnnavigatesuccess: GleanCounter; + navigationReload: GleanCounter; + navigationTraverseto: GleanCounter; + navigationUpdatecurrententry: GleanCounter; + navigationactivationEntryGetter: GleanCounter; + navigationactivationEntrySetter: GleanCounter; + navigationactivationFromGetter: GleanCounter; + navigationactivationFromSetter: GleanCounter; + navigationactivationNavigationtypeGetter: GleanCounter; + navigationactivationNavigationtypeSetter: GleanCounter; + navigationhistoryentryGetstate: GleanCounter; + navigationprecommitcontrollerAddhandler: GleanCounter; + navigationprecommitcontrollerRedirect: GleanCounter; + navigationtransitionCommittedGetter: GleanCounter; + navigationtransitionCommittedSetter: GleanCounter; + navigationtransitionFinishedGetter: GleanCounter; + navigationtransitionFinishedSetter: GleanCounter; + navigationtransitionFromGetter: GleanCounter; + navigationtransitionFromSetter: GleanCounter; + navigationtransitionNavigationtypeGetter: GleanCounter; + navigationtransitionNavigationtypeSetter: GleanCounter; navigatorMozgetusermedia: GleanCounter; + navigatorRequestmidiaccess: GleanCounter; + notificationoptionsActions: GleanCounter; + notificationoptionsBadge: GleanCounter; + notificationoptionsImage: GleanCounter; + notificationoptionsNavigate: GleanCounter; + notificationoptionsRenotify: GleanCounter; + notificationoptionsRequireinteraction: GleanCounter; + notificationoptionsTimestamp: GleanCounter; + notificationoptionsVibrate: GleanCounter; ondommousescroll: GleanCounter; onmozmousepixelscroll: GleanCounter; percentageStrokeWidthInSvg: GleanCounter; @@ -6255,6 +6827,7 @@ interface GleanImpl { pushmanagerSubscribe: GleanCounter; pushsubscriptionUnsubscribe: GleanCounter; rangeCreatecontextualfragment: GleanCounter; + reportingobserverConstructor: GleanCounter; sanitizerConstructor: GleanCounter; sanitizerSanitize: GleanCounter; schedulerPosttask: GleanCounter; @@ -6263,6 +6836,9 @@ interface GleanImpl { svgsvgelementGetelementbyid: GleanCounter; textDirectiveNotCreated: GleanCounter; textDirectivePages: GleanCounter; + videodecoderConstructor: GleanCounter; + videoencoderConstructor: GleanCounter; + wakelockRequest: GleanCounter; webgpuRenderOutput: GleanCounter; webgpuRequestAdapter: GleanCounter; windowAbsoluteorientationsensor: GleanCounter; @@ -6314,7 +6890,6 @@ interface GleanImpl { windowDevicemotioneventacceleration: GleanCounter; windowDevicemotioneventrotationrate: GleanCounter; windowDomerror: GleanCounter; - windowEncodedvideochunk: GleanCounter; windowEnterpictureinpictureevent: GleanCounter; windowExternal: GleanCounter; windowFederatedcredential: GleanCounter; @@ -6328,14 +6903,6 @@ interface GleanImpl { windowKeyboardlayoutmap: GleanCounter; windowLinearaccelerationsensor: GleanCounter; windowMediasettingsrange: GleanCounter; - windowMidiaccess: GleanCounter; - windowMidiconnectionevent: GleanCounter; - windowMidiinput: GleanCounter; - windowMidiinputmap: GleanCounter; - windowMidimessageevent: GleanCounter; - windowMidioutput: GleanCounter; - windowMidioutputmap: GleanCounter; - windowMidiport: GleanCounter; windowNetworkinformation: GleanCounter; windowOffscreenbuffering: GleanCounter; windowOnbeforeinstallprompt: GleanCounter; @@ -6346,7 +6913,8 @@ interface GleanImpl { windowOnselectionchange: GleanCounter; windowOpenEmptyUrl: GleanCounter; windowOpendatabase: GleanCounter; - windowOrientation: GleanCounter; + windowOrientationGetter: GleanCounter; + windowOrientationSetter: GleanCounter; windowOrientationsensor: GleanCounter; windowOverconstrainederror: GleanCounter; windowPasswordcredential: GleanCounter; @@ -6371,9 +6939,6 @@ interface GleanImpl { windowPresentationrequest: GleanCounter; windowRelativeorientationsensor: GleanCounter; windowRemoteplayback: GleanCounter; - windowReport: GleanCounter; - windowReportbody: GleanCounter; - windowReportingobserver: GleanCounter; windowRtcerror: GleanCounter; windowRtcerrorevent: GleanCounter; windowRtcicetransport: GleanCounter; @@ -6408,12 +6973,6 @@ interface GleanImpl { windowUsbisochronousouttransferpacket: GleanCounter; windowUsbisochronousouttransferresult: GleanCounter; windowUsbouttransferresult: GleanCounter; - windowUseractivation: GleanCounter; - windowVideocolorspace: GleanCounter; - windowVideodecoder: GleanCounter; - windowVideoencoder: GleanCounter; - windowVideoframe: GleanCounter; - windowWakelock: GleanCounter; windowWakelocksentinel: GleanCounter; windowWebkitcancelanimationframe: GleanCounter; windowWebkitmediastream: GleanCounter; @@ -6478,6 +7037,14 @@ interface GleanImpl { mlsSend: GleanCounter; mlsStatedelete: GleanCounter; mlsStatedeletegroup: GleanCounter; + notificationoptionsActions: GleanCounter; + notificationoptionsBadge: GleanCounter; + notificationoptionsImage: GleanCounter; + notificationoptionsNavigate: GleanCounter; + notificationoptionsRenotify: GleanCounter; + notificationoptionsRequireinteraction: GleanCounter; + notificationoptionsTimestamp: GleanCounter; + notificationoptionsVibrate: GleanCounter; privateBrowsingCachesDelete: GleanCounter; privateBrowsingCachesHas: GleanCounter; privateBrowsingCachesKeys: GleanCounter; @@ -6487,7 +7054,10 @@ interface GleanImpl { privateBrowsingIdbfactoryOpen: GleanCounter; pushmanagerSubscribe: GleanCounter; pushsubscriptionUnsubscribe: GleanCounter; + reportingobserverConstructor: GleanCounter; schedulerPosttask: GleanCounter; + videodecoderConstructor: GleanCounter; + videoencoderConstructor: GleanCounter; webgpuRequestAdapter: GleanCounter; }; @@ -6535,6 +7105,14 @@ interface GleanImpl { mlsSend: GleanCounter; mlsStatedelete: GleanCounter; mlsStatedeletegroup: GleanCounter; + notificationoptionsActions: GleanCounter; + notificationoptionsBadge: GleanCounter; + notificationoptionsImage: GleanCounter; + notificationoptionsNavigate: GleanCounter; + notificationoptionsRenotify: GleanCounter; + notificationoptionsRequireinteraction: GleanCounter; + notificationoptionsTimestamp: GleanCounter; + notificationoptionsVibrate: GleanCounter; privateBrowsingCachesDelete: GleanCounter; privateBrowsingCachesHas: GleanCounter; privateBrowsingCachesKeys: GleanCounter; @@ -6544,7 +7122,10 @@ interface GleanImpl { privateBrowsingIdbfactoryOpen: GleanCounter; pushmanagerSubscribe: GleanCounter; pushsubscriptionUnsubscribe: GleanCounter; + reportingobserverConstructor: GleanCounter; schedulerPosttask: GleanCounter; + videodecoderConstructor: GleanCounter; + videoencoderConstructor: GleanCounter; webgpuRequestAdapter: GleanCounter; }; @@ -6592,6 +7173,14 @@ interface GleanImpl { mlsSend: GleanCounter; mlsStatedelete: GleanCounter; mlsStatedeletegroup: GleanCounter; + notificationoptionsActions: GleanCounter; + notificationoptionsBadge: GleanCounter; + notificationoptionsImage: GleanCounter; + notificationoptionsNavigate: GleanCounter; + notificationoptionsRenotify: GleanCounter; + notificationoptionsRequireinteraction: GleanCounter; + notificationoptionsTimestamp: GleanCounter; + notificationoptionsVibrate: GleanCounter; privateBrowsingCachesDelete: GleanCounter; privateBrowsingCachesHas: GleanCounter; privateBrowsingCachesKeys: GleanCounter; @@ -6601,7 +7190,10 @@ interface GleanImpl { privateBrowsingIdbfactoryOpen: GleanCounter; pushmanagerSubscribe: GleanCounter; pushsubscriptionUnsubscribe: GleanCounter; + reportingobserverConstructor: GleanCounter; schedulerPosttask: GleanCounter; + videodecoderConstructor: GleanCounter; + videoencoderConstructor: GleanCounter; webgpuRequestAdapter: GleanCounter; }; @@ -6627,11 +7219,26 @@ interface GleanImpl { geolocation: { accuracy: GleanCustomDistribution; fallback: Record<"none" | "on_error" | "on_timeout", GleanCounter>; + geoclueErrorCode: Record; + geolocationCacheHit: Record< + "NetworkGeolocationProvider" | "nsGeolocationRequest", + GleanCounter + >; + geolocationService: Record< + "geoclue" | "network_ip" | "network_wifi_and_ip" | "system", + GleanCounter + >; + linuxPortalError: Record< + "cant_read_response" | "empty_response", + GleanCounter + >; linuxProvider: Record<"geoclue" | "none" | "portal", GleanBoolean>; + macosErrorCode: Record; requestResult: Record< "permission_denied" | "position_unavailable" | "success" | "timeout", GleanCounter >; + windowsFailure: GleanDualLabeledCounter; }; idbMaintenance: { @@ -6675,15 +7282,6 @@ interface GleanImpl { gmp: { updateXmlFetchResult: Record< - | "cert_pin_abort" - | "cert_pin_failed" - | "cert_pin_invalid" - | "cert_pin_missing_data" - | "cert_pin_net_request_error" - | "cert_pin_net_timeout" - | "cert_pin_success" - | "cert_pin_unknown_error" - | "cert_pin_xml_parse_error" | "content_sig_abort" | "content_sig_failed" | "content_sig_invalid" @@ -6699,13 +7297,18 @@ interface GleanImpl { media: { audiblePlayTimePercent: Record; + captureStreamUsage: Record< + | "AudioSourceNodeCaptured" + | "CaptureStream" + | "MozAudioCaptured" + | "MozCaptureStream" + | "MozCaptureStreamUntilEnded" + | "WindowAudioCaptured", + GleanCounter + >; codecUsed: Record; elementInPageCount: GleanCounter; - error: GleanEventWithExtras<{ - error_name?: string; - error_type?: string; - key_system?: string; - }>; + error: GleanDualLabeledCounter; mediaPlayTime: Record; mkvCodecType: Record< | "AudioAac" @@ -6752,6 +7355,7 @@ interface GleanImpl { videoVisiblePlayTime: Record; videoWidevinePlayTime: GleanTimingDistribution; decoderBackendUsed: GleanCustomDistribution; + wmfCodecNoExtension: Record<"av1" | "hevc", GleanBoolean>; }; mediaAudio: { @@ -6776,17 +7380,11 @@ interface GleanImpl { }; mediaPlayback: { - decodeError: GleanEventWithExtras<{ - decoder_name?: string; - error_name?: string; - is_hardware_accelerated?: string; - key_system?: string; - mime_type?: string; - }>; deviceHardwareDecoderSupport: Record< "av1" | "h264" | "hevc" | "vp8" | "vp9", GleanBoolean >; + encryptedDecodeError: GleanDualLabeledCounter; firstFrameLoaded: GleanEventWithExtras<{ buffering_time?: string; decoder_name?: string; @@ -6802,6 +7400,8 @@ interface GleanImpl { video_codec?: string; }>; notSupportedVideoPerMimeType: Record; + unencryptedHwDecodeError: GleanDualLabeledCounter; + unencryptedSwDecodeError: GleanDualLabeledCounter; }; mediaRecorder: { @@ -7009,6 +7609,8 @@ interface GleanImpl { "AltData" | "Inline" | "Source" | "SourceFallback", GleanCounter >; + scriptMemoryCacheEverHit: GleanCustomDistribution; + scriptMemoryCacheRefCount: GleanCustomDistribution; slowScriptNoticeCount: GleanCounter; slowScriptPageCount: GleanCounter; storageAccessApiUi: Record< @@ -7045,17 +7647,12 @@ interface GleanImpl { coldApplinkMainToLoadUri: GleanTimingDistribution; coldApplinkProcessLaunchToLoadUri: GleanTimingDistribution; dnsFirstByte: Record; - dnsFirstContentfulPaint: Record; - h3pFirstContentfulPaint: Record; - h3pPageLoadTime: Record; - http3FirstContentfulPaint: Record; - http3PageLoadTime: Record; - jsExecAsmJs: GleanTimingDistribution; largestContentfulPaint: GleanTimingDistribution; largestContentfulPaintFromResponseStart: GleanTimingDistribution; pageLoad: GleanEventWithExtras<{ android_app_link_launch_type?: string; android_app_link_to_navigation_start?: string; + android_isolation_category?: string; cache_disposition?: string; delazify_time?: string; dns_lookup_time?: string; @@ -7072,6 +7669,11 @@ interface GleanImpl { redirect_time?: string; response_time?: string; same_origin_nav?: string; + script_from_necko_serialized?: string; + script_from_necko_text?: string; + script_memory_cache_evicted_dirty?: string; + script_memory_cache_revived?: string; + script_memory_cache_use?: string; time_to_request_start?: string; tls_handshake_time?: string; trr_domain?: string; @@ -7093,10 +7695,6 @@ interface GleanImpl { performancePageload: { fcp: GleanTimingDistribution; fcpResponsestart: GleanTimingDistribution; - h3pFcpWithPriority: GleanTimingDistribution; - http3FcpHttp3: GleanTimingDistribution; - http3FcpSupportsHttp3: GleanTimingDistribution; - http3FcpWithoutPriority: GleanTimingDistribution; loadTime: GleanTimingDistribution; loadTimeResponsestart: GleanTimingDistribution; }; @@ -7114,10 +7712,6 @@ interface GleanImpl { }; webNotification: { - iconUrlEncoding: Record< - "document_charset" | "either_way" | "neither_way" | "utf8", - GleanCounter - >; insecureContextPermissionRequest: GleanCounter; permissionOrigin: Record< "first_party" | "nested_first_party" | "third_party", @@ -7131,6 +7725,7 @@ interface GleanImpl { "first_party" | "nested_first_party" | "third_party", GleanCounter >; + showSafeBrowsingBlock: GleanRate; }; screenwakelock: { @@ -7141,6 +7736,8 @@ interface GleanImpl { webPush: { apiNotify: GleanCounter; contentEncoding: Record<"aes128gcm" | "aesgcm", GleanCounter>; + declarative: GleanCounter; + declarativeMutable: GleanCounter; detectedDuplicatedMessageIds: GleanCounter; errorCode: Record< | "decryption_error" @@ -7261,11 +7858,6 @@ interface GleanImpl { running: Record<"All" | "Fetch", GleanCustomDistribution>; }; - localdomstorage: { - preloadPendingOnFirstAccess: Record<"false" | "true", GleanCounter>; - shutdownDatabase: GleanTimingDistribution; - }; - webauthnCreate: { authenticatorAttachment: Record< "cross-platform" | "platform" | "unknown", @@ -7304,6 +7896,9 @@ interface GleanImpl { permissions: { defectiveSqlRemoved: GleanCounter; sqlCorrupted: GleanCounter; + unusedPermissionAgeAtExpiry: GleanCustomDistribution; + unusedPermissionModifiedAgeAtExpiry: GleanCustomDistribution; + unusedPermissionsExpiredByType: Record; }; apzZoom: { @@ -7471,6 +8066,10 @@ interface GleanImpl { srctype: GleanCustomDistribution; }; + webgpu: { + exposeAdapter: GleanDualLabeledCounter; + }; + wr: { framebuildTime: GleanTimingDistribution; gpuWaitTime: GleanTimingDistribution; @@ -7663,6 +8262,11 @@ interface GleanImpl { launchFailure: Record; }; + javascriptSelfHostedCache: { + hits: GleanNumerator; + total: GleanDenominator; + }; + javascriptGc: { animation: GleanTimingDistribution; budget: GleanTimingDistribution; @@ -8182,6 +8786,7 @@ interface GleanImpl { }; preferences: { + prefsFileFirstParseError: GleanString; prefsFileWasInvalid: GleanBoolean; userPrefs: GleanObject; }; @@ -8215,7 +8820,6 @@ interface GleanImpl { cacheV2InputStreamStatus: GleanCustomDistribution; cacheV2OutputStreamStatus: GleanCustomDistribution; diskCache2ShutdownClearPrivate: GleanTimingDistribution; - diskCacheShutdownV2: GleanTimingDistribution; httpCacheEntryAliveTime: GleanTimingDistribution; httpCacheEntryReloadTime: GleanTimingDistribution; httpCacheEntryReuseCount: GleanCustomDistribution; @@ -8236,11 +8840,15 @@ interface GleanImpl { | "TYPE_STYLE_USED", GleanCounter >; + sslTokenCacheExpired: GleanCounter; + sslTokenCacheHits: Record<"hit" | "miss", GleanCounter>; + urlclassifierHarmfulAddonBlock: GleanEventWithExtras<{ + addon_id?: string; + addon_version?: string; + etld?: string; + table?: string; + }>; alpnMismatchCount: Record; - asyncOpenChildToTransactionPendingExp: Record< - string, - GleanTimingDistribution - >; backPressureSuspensionCpType: GleanCustomDistribution; backPressureSuspensionDelayTime: GleanTimingDistribution; backPressureSuspensionRate: Record< @@ -8261,7 +8869,6 @@ interface GleanImpl { GleanCounter >; dnsEnd: GleanTimingDistribution; - dnsEndToConnectStartExp: Record; dnsStart: GleanTimingDistribution; firstFromCache: GleanTimingDistribution; firstSentToLastReceived: GleanTimingDistribution; @@ -8301,20 +8908,10 @@ interface GleanImpl { | "uses_http3_sub", GleanTimingDistribution >; - httpFetchDuration: Record< - | "h1_cloudflare" - | "h1_others" - | "h2_cloudflare" - | "h2_others" - | "h3_cloudflare" - | "h3_others", - GleanTimingDistribution - >; httpRevalidation: GleanTimingDistribution; openToFirstReceived: GleanTimingDistribution; openToFirstSent: GleanTimingDistribution; pageLoadSize: Record<"page" | "subresources", GleanMemoryDistribution>; - raceCacheBandwidthNotRace: GleanMemoryDistribution; raceCacheBandwidthRaceCacheWin: GleanMemoryDistribution; raceCacheBandwidthRaceNetworkWin: GleanMemoryDistribution; raceCacheValidation: Record< @@ -8323,8 +8920,6 @@ interface GleanImpl { >; raceCacheWithNetworkOcecOnStartDiff: GleanTimingDistribution; raceCacheWithNetworkSavedTime: GleanTimingDistribution; - responseEndParentToContent: Record; - responseStartParentToContentExp: Record; retriedSystemChannelAddonStatus: Record< | "cancel" | "connect_fail" @@ -8624,7 +9219,6 @@ interface GleanImpl { }; dns: { - blocklistCount: GleanCustomDistribution; byTypeCleanupAge: GleanTimingDistribution; byTypeFailedLookupTime: GleanTimingDistribution; byTypePrematureEviction: GleanTimingDistribution; @@ -8643,6 +9237,22 @@ interface GleanImpl { trrBlacklisted: GleanDualLabeledCounter; trrDisabled: GleanDualLabeledCounter; trrFirst: GleanDualLabeledCounter; + trrHttp30rttState: Record< + | "conn_closed_by_necko" + | "conn_error" + | "not_used" + | "rejected" + | "succeeded", + GleanCounter + >; + trrHttp30rttStateDuration: Record< + | "conn_closed_by_necko" + | "conn_error" + | "not_used" + | "rejected" + | "succeeded", + GleanTimingDistribution + >; trrHttpVersion: GleanDualLabeledCounter; trrLookupTime: Record; trrNsVerfified: GleanDualLabeledCounter; @@ -8696,38 +9306,6 @@ interface GleanImpl { totalEntraUses: GleanCounter; }; - predictor: { - baseConfidence: GleanCustomDistribution; - confidence: GleanCustomDistribution; - globalDegradation: GleanCustomDistribution; - learnAttempts: GleanCustomDistribution; - learnWorkTime: GleanTimingDistribution; - predictTimeToAction: GleanTimingDistribution; - predictTimeToInaction: GleanTimingDistribution; - predictWorkTime: GleanTimingDistribution; - predictionsCalculated: GleanCustomDistribution; - prefetchDecisionReason: GleanCustomDistribution; - prefetchIgnoreReason: GleanCustomDistribution; - prefetchTime: GleanTimingDistribution; - prefetchUseStatus: Record< - | "Auth" - | "Etag" - | "Expired" - | "Not200" - | "Redirect" - | "Used" - | "WaitedTooLong" - | "WouldVary", - GleanCounter - >; - subresourceDegradation: GleanCustomDistribution; - totalPreconnects: GleanCustomDistribution; - totalPredictions: GleanCustomDistribution; - totalPrefetches: GleanCustomDistribution; - totalPreresolves: GleanCustomDistribution; - waitTime: GleanTimingDistribution; - }; - http: { altsvcMappingChangedTarget: Record<"false" | "true", GleanCounter>; cacheDisposition: GleanDualLabeledCounter; @@ -8736,10 +9314,8 @@ interface GleanImpl { channelOnstartSuccess: Record<"false" | "true", GleanCounter>; channelPageOnstartSuccessTrr: Record; channelSubOnstartSuccessTrr: Record; - connectionCloseReason: Record; connectionEntryCacheHit: Record<"false" | "true", GleanCounter>; contentEncoding: GleanCustomDistribution; - dnsHttpssvcConnectionFailedReason: GleanCustomDistribution; dnsHttpssvcRecordReceivingStage: GleanCustomDistribution; dntUsage: GleanCustomDistribution; echconfigSuccessRate: Record< @@ -8791,13 +9367,10 @@ interface GleanImpl { transactionEchRetryWithEchCount: GleanCustomDistribution; transactionEchRetryWithoutEchCount: GleanCustomDistribution; transactionIsSsl: Record<"false" | "true", GleanCounter>; - transactionRestartReason: GleanCustomDistribution; transactionUseAltsvc: Record<"false" | "true", GleanCounter>; transactionWaitTimeHttp: GleanTimingDistribution; - transactionWaitTimeHttp2SupHttp3: GleanTimingDistribution; transactionWaitTimeHttp3: GleanTimingDistribution; transactionWaitTimeSpdy: GleanTimingDistribution; - uploadBandwidthMbps: Record; }; http3: { @@ -8914,10 +9487,6 @@ interface GleanImpl { handshakeType: GleanCustomDistribution; }; - parsing: { - svgUnusualPcdata: GleanRate; - }; - ysod: { shownYsod: GleanEventWithExtras<{ destroyed?: string; @@ -8950,6 +9519,7 @@ interface GleanImpl { | "StapledOCSP", GleanCounter >; + crliteNotCoveredCertAge: GleanTimingDistribution; crliteStatus: Record< | "no_filter" | "not_covered" @@ -8959,14 +9529,6 @@ interface GleanImpl { | "revoked_in_stash", GleanCounter >; - crliteVsOcspResult: Record< - | "CRLiteRevOCSPFail" - | "CRLiteRevOCSPOk" - | "CRLiteRevOCSPRev" - | "CRLiteRevOCSPSoft" - | "CRLiteRevOCSPUnk", - GleanCounter - >; }; sctSignatureCache: { @@ -8993,10 +9555,6 @@ interface GleanImpl { testResults: Record<"false" | "true", GleanCounter>; }; - certStorage: { - memory: GleanMemoryDistribution; - }; - certVerificationTime: { failure: GleanTimingDistribution; success: GleanTimingDistribution; @@ -9005,9 +9563,20 @@ interface GleanImpl { dataStorage: { alternateServices: GleanQuantity; clientAuthRememberList: GleanQuantity; + siteIntegrityServiceState: GleanQuantity; siteSecurityServiceState: GleanQuantity; }; + nss: { + initializationFallbacks: Record< + | "NO_DB_INIT" + | "READ_ONLY" + | "RENAME_MODULE_DB" + | "RENAME_MODULE_DB_READ_ONLY", + GleanCounter + >; + }; + ocspRequestTime: { cancel: GleanTimingDistribution; failure: GleanTimingDistribution; @@ -9122,15 +9691,6 @@ interface GleanImpl { }; uptakeRemotecontentResult: { - uptakeNormandy: GleanEventWithExtras<{ - age?: string; - duration?: string; - errorName?: string; - source?: string; - timestamp?: string; - trigger?: string; - value?: string; - }>; uptakeRemotesettings: GleanEventWithExtras<{ age?: string; duration?: string; @@ -9198,81 +9758,125 @@ interface GleanImpl { fxaAppMenu: { clickAccountSettings: GleanEventWithExtras<{ + action?: string; + device_count?: string; fxa_avatar?: string; fxa_status?: string; fxa_sync_on?: string; }>; clickCad: GleanEventWithExtras<{ + action?: string; + device_count?: string; fxa_avatar?: string; fxa_status?: string; fxa_sync_on?: string; }>; clickLogin: GleanEventWithExtras<{ + action?: string; + device_count?: string; fxa_avatar?: string; fxa_status?: string; fxa_sync_on?: string; }>; clickMonitorCta: GleanEventWithExtras<{ + action?: string; + device_count?: string; fxa_avatar?: string; fxa_status?: string; fxa_sync_on?: string; }>; clickOpenMonitor: GleanEventWithExtras<{ + action?: string; + device_count?: string; fxa_avatar?: string; fxa_status?: string; fxa_sync_on?: string; }>; clickOpenSend: GleanEventWithExtras<{ + action?: string; + device_count?: string; fxa_avatar?: string; fxa_status?: string; fxa_sync_on?: string; }>; clickRelayCta: GleanEventWithExtras<{ + action?: string; + device_count?: string; fxa_avatar?: string; fxa_status?: string; fxa_sync_on?: string; }>; clickSendTab: GleanEventWithExtras<{ + action?: string; + device_count?: string; fxa_avatar?: string; fxa_status?: string; fxa_sync_on?: string; }>; clickSyncCta: GleanEventWithExtras<{ + action?: string; + device_count?: string; fxa_avatar?: string; fxa_status?: string; fxa_sync_on?: string; }>; clickSyncNow: GleanEventWithExtras<{ + action?: string; + device_count?: string; fxa_avatar?: string; fxa_status?: string; fxa_sync_on?: string; }>; clickSyncSettings: GleanEventWithExtras<{ + action?: string; + device_count?: string; fxa_avatar?: string; fxa_status?: string; fxa_sync_on?: string; }>; clickSyncTabs: GleanEventWithExtras<{ + action?: string; + device_count?: string; fxa_avatar?: string; fxa_status?: string; fxa_sync_on?: string; }>; clickSyncTabsSidebar: GleanEventWithExtras<{ + action?: string; + device_count?: string; fxa_avatar?: string; fxa_status?: string; fxa_sync_on?: string; }>; clickToolbarIcon: GleanEventWithExtras<{ + action?: string; + device_count?: string; fxa_avatar?: string; fxa_status?: string; fxa_sync_on?: string; }>; clickUnverSyncSettings: GleanEventWithExtras<{ + action?: string; + device_count?: string; fxa_avatar?: string; fxa_status?: string; fxa_sync_on?: string; }>; clickVpnCta: GleanEventWithExtras<{ + action?: string; + device_count?: string; + fxa_avatar?: string; + fxa_status?: string; + fxa_sync_on?: string; + }>; + sendTabExposed: GleanEventWithExtras<{ + device_count?: string; + fxa_avatar?: string; + fxa_status?: string; + fxa_sync_on?: string; + }>; + sendTabOpened: GleanEventWithExtras<{ + device_count?: string; fxa_avatar?: string; fxa_status?: string; fxa_sync_on?: string; @@ -9281,85 +9885,145 @@ interface GleanImpl { fxaAvatarMenu: { clickAccountSettings: GleanEventWithExtras<{ + action?: string; + device_count?: string; fxa_avatar?: string; fxa_status?: string; fxa_sync_on?: string; }>; clickCad: GleanEventWithExtras<{ + action?: string; + device_count?: string; fxa_avatar?: string; fxa_status?: string; fxa_sync_on?: string; }>; clickLogin: GleanEventWithExtras<{ + action?: string; + device_count?: string; fxa_avatar?: string; fxa_status?: string; fxa_sync_on?: string; }>; clickMonitorCta: GleanEventWithExtras<{ + action?: string; + device_count?: string; fxa_avatar?: string; fxa_status?: string; fxa_sync_on?: string; }>; clickOpenMonitor: GleanEventWithExtras<{ + action?: string; + device_count?: string; fxa_avatar?: string; fxa_status?: string; fxa_sync_on?: string; }>; clickOpenSend: GleanEventWithExtras<{ + action?: string; + device_count?: string; fxa_avatar?: string; fxa_status?: string; fxa_sync_on?: string; }>; clickRelayCta: GleanEventWithExtras<{ + action?: string; + device_count?: string; fxa_avatar?: string; fxa_status?: string; fxa_sync_on?: string; }>; clickSendTab: GleanEventWithExtras<{ + action?: string; + device_count?: string; fxa_avatar?: string; fxa_status?: string; fxa_sync_on?: string; }>; clickSyncCta: GleanEventWithExtras<{ + action?: string; + device_count?: string; fxa_avatar?: string; fxa_status?: string; fxa_sync_on?: string; }>; clickSyncNow: GleanEventWithExtras<{ + action?: string; + device_count?: string; fxa_avatar?: string; fxa_status?: string; fxa_sync_on?: string; }>; clickSyncSettings: GleanEventWithExtras<{ + action?: string; + device_count?: string; fxa_avatar?: string; fxa_status?: string; fxa_sync_on?: string; }>; clickSyncTabs: GleanEventWithExtras<{ + action?: string; + device_count?: string; fxa_avatar?: string; fxa_status?: string; fxa_sync_on?: string; }>; clickSyncTabsSidebar: GleanEventWithExtras<{ + action?: string; + device_count?: string; fxa_avatar?: string; fxa_status?: string; fxa_sync_on?: string; }>; clickToolbarIcon: GleanEventWithExtras<{ + action?: string; + device_count?: string; fxa_avatar?: string; fxa_status?: string; fxa_sync_on?: string; }>; clickUnverSyncSettings: GleanEventWithExtras<{ + action?: string; + device_count?: string; fxa_avatar?: string; fxa_status?: string; fxa_sync_on?: string; }>; clickVpnCta: GleanEventWithExtras<{ + action?: string; + device_count?: string; fxa_avatar?: string; fxa_status?: string; fxa_sync_on?: string; }>; + sendTabExposed: GleanEventWithExtras<{ + device_count?: string; + fxa_avatar?: string; + fxa_status?: string; + fxa_sync_on?: string; + }>; + sendTabOpened: GleanEventWithExtras<{ + device_count?: string; + fxa_avatar?: string; + fxa_status?: string; + fxa_sync_on?: string; + }>; + }; + + pageContextMenu: { + clickSendTab: GleanEventWithExtras<{ + action?: string; + context_type?: string; + device_count?: string; + }>; + sendTabExposed: GleanEventWithExtras<{ + context_type?: string; + device_count?: string; + }>; + sendTabOpened: GleanEventWithExtras<{ + context_type?: string; + device_count?: string; + }>; }; sync: { @@ -9401,6 +10065,15 @@ interface GleanImpl { syncs: GleanObject; }; + tabContextMenu: { + clickSendTab: GleanEventWithExtras<{ + action?: string; + device_count?: string; + }>; + sendTabExposed: GleanEventWithExtras<{ device_count?: string }>; + sendTabOpened: GleanEventWithExtras<{ device_count?: string }>; + }; + startupCache: { requests: Record<"HitDisk" | "HitMemory" | "Miss", GleanCounter>; }; @@ -9430,6 +10103,9 @@ interface GleanImpl { "known_text" | "known_text_matched" | "unknown" | "unknown_matched", GleanCustomDistribution >; + canvasFingerprintingPerTab2: Record<"found" | "not_found", GleanCounter>; + canvasFingerprintingTypeAliasBySourcePerTab2: GleanDualLabeledCounter; + canvasFingerprintingTypeTextBySourcePerTab2: GleanDualLabeledCounter; category: GleanQuantity; cookieBehavior: GleanCustomDistribution; cryptominersBlockedCount: Record< @@ -9616,6 +10292,11 @@ interface GleanImpl { privacyTrackingprotectionPbmEnabled: GleanBoolean; }; + privateBrowsingCleanup: { + duration: GleanTimingDistribution; + errorRate: GleanRate; + }; + contentAnalysis: { agentName: GleanString; allowUrlRegexListSet: GleanBoolean; @@ -9652,13 +10333,6 @@ interface GleanImpl { }; cookieBanners: { - googleGdprChoiceCookie: Record; - googleGdprChoiceCookieEvent: GleanEventWithExtras<{ - choice?: string; - region?: string; - search_domain?: string; - }>; - googleGdprChoiceCookieEventPbm: GleanEventWithExtras<{ choice?: string }>; normalWindowServiceMode: Record< "disabled" | "invalid" | "reject" | "reject_or_accept", GleanBoolean @@ -9671,27 +10345,7 @@ interface GleanImpl { }; crash: { - appBuild: GleanString; - appChannel: GleanString; - appDisplayVersion: GleanString; - asyncShutdownTimeout: GleanObject; - backgroundTaskName: GleanString; compressedStoreSize: GleanMemoryDistribution; - eventLoopNestingLevel: GleanQuantity; - fontName: GleanString; - gpuProcessLaunch: GleanQuantity; - ipcChannelError: GleanString; - isGarbageCollecting: GleanBoolean; - mainThreadRunnableName: GleanString; - minidumpSha256Hash: GleanString; - mozCrashReason: GleanString; - processType: GleanString; - profilerChildShutdownPhase: GleanString; - quotaManagerShutdownTimeout: GleanObject; - remoteType: GleanString; - shutdownProgress: GleanString; - stackTraces: GleanObject; - startup: GleanBoolean; submitAttempt: Record< | "content-crash" | "content-hang" @@ -9718,13 +10372,6 @@ interface GleanImpl { GleanCounter >; submitSuccess: GleanDualLabeledCounter; - time: GleanDatetime; - utilityActorsName: GleanStringList; - }; - - crashWindows: { - errorReporting: GleanBoolean; - fileDialogErrorCode: GleanString; }; crashSubmission: { @@ -9736,61 +10383,6 @@ interface GleanImpl { success: GleanCounter; }; - dllBlocklist: { - initFailed: GleanBoolean; - list: GleanStringList; - user32LoadedBefore: GleanBoolean; - }; - - environment: { - headlessMode: GleanBoolean; - nimbusEnrollments: GleanStringList; - uptime: GleanTimespan; - }; - - memory: { - availableCommit: GleanQuantity; - availablePhysical: GleanQuantity; - availableSwap: GleanQuantity; - availableVirtual: GleanQuantity; - jsLargeAllocationFailure: GleanString; - jsOutOfMemory: GleanString; - lowPhysical: GleanQuantity; - oomAllocationSize: GleanQuantity; - purgeablePhysical: GleanQuantity; - systemUsePercentage: GleanQuantity; - texture: GleanQuantity; - totalPageFile: GleanQuantity; - totalPhysical: GleanQuantity; - totalVirtual: GleanQuantity; - collectionTime: GleanTimingDistribution; - distributionAmongContent: Record; - freePurgedPages: GleanTimingDistribution; - ghostWindows: GleanCustomDistribution; - heapAllocated: GleanMemoryDistribution; - heapOverheadFraction: GleanCustomDistribution; - imagesContentUsedUncompressed: GleanMemoryDistribution; - jsCompartmentsSystem: GleanCustomDistribution; - jsCompartmentsUser: GleanCustomDistribution; - jsGcHeap: Record; - jsRealmsSystem: GleanCustomDistribution; - jsRealmsUser: GleanCustomDistribution; - lowMemoryEventsPhysical: GleanCustomDistribution; - pageFaultsHard: GleanCustomDistribution; - residentFast: Record; - residentPeak: Record; - storageSqlite: GleanMemoryDistribution; - total: GleanMemoryDistribution; - unique: Record; - uniqueContentStartup: GleanMemoryDistribution; - vsize: GleanMemoryDistribution; - vsizeMaxContiguous: GleanMemoryDistribution; - }; - - windows: { - packageFamilyName: GleanString; - }; - doh: { evaluateV2Heuristics: GleanEventWithExtras<{ canaries?: string; @@ -9803,6 +10395,11 @@ interface GleanImpl { steeredProvider?: string; value?: string; }>; + regionChanged: GleanEventWithExtras<{ + new_region?: string; + old_region?: string; + trigger?: string; + }>; stateDisabled: GleanEventWithExtras<{ value?: string }>; stateEnabled: GleanEventWithExtras<{ value?: string }>; stateManuallyDisabled: GleanEventWithExtras<{ value?: string }>; @@ -9834,6 +10431,12 @@ interface GleanImpl { }; extensions: { + allowExecuteScriptInMozExtension: GleanBoolean; + matchMozExtensionDocument: GleanEventWithExtras<{ + addon_id?: string; + is_top_level_frame?: string; + restricted?: string; + }>; processEvent: Record< | "crashed_bg" | "crashed_fg" @@ -9868,6 +10471,7 @@ interface GleanImpl { >; browserActionPreloadResultByAddonid: GleanDualLabeledCounter; eventPageIdleResult: Record< + | "downloads_saveAs" | "launchWebAuthFlow" | "permissions_request" | "reset_event" @@ -9895,6 +10499,7 @@ interface GleanImpl { storageLocalCorruptedReset: GleanEventWithExtras<{ addon_id?: string; after_reset?: string; + is_addon_active?: string; reason?: string; reset_disabled?: string; reset_error_name?: string; @@ -10201,7 +10806,6 @@ interface GleanImpl { }; fog: { - dataDirectoryInfo: GleanObject; failedIdleRegistration: GleanBoolean; initializations: GleanTimingDistribution; initsDuringShutdown: GleanCounter; @@ -10218,6 +10822,7 @@ interface GleanImpl { bufferSizes: GleanMemoryDistribution; flushDurations: GleanTimingDistribution; flushFailures: GleanCounter; + flushRejections: GleanCounter; replayFailures: GleanCounter; shutdownRegistrationFailures: GleanCounter; }; @@ -10308,6 +10913,7 @@ interface GleanImpl { anExternalDenominator: GleanDenominator; anUnorderedBool: GleanBoolean; anUnorderedLabeledBoolean: Record; + anUnorderedQuantity: GleanQuantity; anotherDualLabeledCounter: GleanDualLabeledCounter; anotherLabeledCounter: Record; eventWithExtra: GleanEventWithExtras<{ @@ -10475,6 +11081,7 @@ interface GleanImpl { engineId?: string; error?: string; featureId?: string; + flow_id?: string; modelId?: string; taskName?: string; }>; @@ -10486,21 +11093,33 @@ interface GleanImpl { | "ml-suggest-ner" | "pdfjs" | "smart-intent" + | "smart-openai" + | "smart-openai-memories-generation" + | "smart-openai-memories-usage" | "smart-tab-embedding-engine" | "smart-tab-topic-engine" + | "title-generation-engine" | "webextension" | "wllamapreview", GleanTimingDistribution >; + engineCreationSuccessFlow: GleanEventWithExtras<{ + duration?: string; + engineId?: string; + flow_id?: string; + }>; engineRun: GleanEventWithExtras<{ backend?: string; + character_count?: string; cores?: string; cpu_milliseconds?: string; cpu_utilization?: string; engine_id?: string; feature_id?: string; + flow_id?: string; memory_bytes?: string; model_id?: string; + token_count?: string; wall_milliseconds?: string; }>; modelDeletion: GleanEventWithExtras<{ @@ -10522,7 +11141,9 @@ interface GleanImpl { }>; runInferenceFailure: GleanEventWithExtras<{ engineId?: string; + error?: string; featureId?: string; + flow_id?: string; modelId?: string; }>; runInferenceSuccess: Record< @@ -10533,12 +11154,38 @@ interface GleanImpl { | "ml-suggest-ner" | "pdfjs" | "smart-intent" + | "smart-openai" + | "smart-openai-memories-generation" + | "smart-openai-memories-usage" | "smart-tab-embedding-engine" | "smart-tab-topic-engine" + | "title-generation-engine" | "webextension" | "wllamapreview", GleanTimingDistribution >; + runInferenceSuccessFlow: GleanEventWithExtras<{ + decoding_time?: string; + flow_id?: string; + inference_time?: string; + input_tokens?: string; + output_tokens?: string; + time_per_output_token?: string; + time_to_first_token?: string; + tokenizing_time?: string; + tokens_per_second?: string; + }>; + sessionEnd: GleanEventWithExtras<{ + duration?: string; + feature_id?: string; + flow_id?: string; + status?: string; + }>; + sessionStart: GleanEventWithExtras<{ + feature_id?: string; + flow_id?: string; + interaction?: string; + }>; }; modelManagement: { @@ -10609,6 +11256,7 @@ interface GleanImpl { branch?: string; conflict_slug?: string; error_string?: string; + migration?: string; reason?: string; slug?: string; status?: string; @@ -10644,11 +11292,13 @@ interface GleanImpl { reason?: string; }>; unenrollment: GleanEventWithExtras<{ + about_config_change?: string; branch?: string; changed_pref?: string; conflicting_slug?: string; experiment?: string; locale?: string; + migration?: string; pref_name?: string; pref_type?: string; reason?: string; @@ -10740,6 +11390,7 @@ interface GleanImpl { GleanCounter >; prefTypeErrors: Record< + | "browser.ai.control.default" | "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.addons" | "browser.newtabpage.activity-stream.asrouter.userprefs.cfr.features" | "browser.newtabpage.activity-stream.feeds.section.highlights" @@ -10752,7 +11403,6 @@ interface GleanImpl { | "browser.startup.page" | "browser.toolbars.bookmarks.visibility" | "browser.urlbar.lastUrlbarSearchSeconds" - | "browser.urlbar.quicksuggest.dataCollection.enabled" | "browser.urlbar.showSearchSuggestionsFirst" | "browser.urlbar.suggest.quicksuggest.sponsored" | "media.videocontrols.picture-in-picture.enabled" @@ -10764,6 +11414,9 @@ interface GleanImpl { | "nimbus.qa.pref-2" | "security.sandbox.content.level" | "termsofuse.acceptedDate" + | "termsofuse.acceptedVersion" + | "termsofuse.bypassNotification" + | "termsofuse.firstAcceptedDate" | "trailhead.firstrun.didSeeAboutWelcome", GleanCounter >; @@ -10879,7 +11532,6 @@ interface GleanImpl { reason?: string; value?: string; }>; - recipeFreshness: Record; unenrollAddonRollback: GleanEventWithExtras<{ addonId?: string; addonVersion?: string; @@ -11051,6 +11703,7 @@ interface GleanImpl { pwmgr: { autocompleteFieldGeneratedpassword: GleanEventNoExtras; autocompleteShownGeneratedpassword: GleanEventNoExtras; + breachAlertLinkClicked: GleanEventWithExtras<{ breach_name?: string }>; cancelExistingLogin: GleanEventWithExtras<{ breached?: string; vulnerable?: string; @@ -11131,7 +11784,6 @@ interface GleanImpl { mgmtMenuItemUsedImportFromBrowser: GleanEventNoExtras; mgmtMenuItemUsedImportFromCsv: GleanEventNoExtras; mgmtMenuItemUsedPreferences: GleanEventNoExtras; - migration: GleanEventWithExtras<{ error?: string; value?: string }>; newNewLogin: GleanEventNoExtras; numImprovedGeneratedPasswords: Record<"false" | "true", GleanCounter>; numSavedPasswords: GleanQuantity; @@ -11166,29 +11818,40 @@ interface GleanImpl { value?: string; }>; requireOsReauthToggle: GleanEventWithExtras<{ toggle_state?: string }>; - rustIncompatibleLoginFormat: GleanEventWithExtras<{ - issue?: string; - operation?: string; - run_id?: string; - }>; - rustMigrationFailure: GleanEventWithExtras<{ - error_message?: string; - run_id?: string; - }>; rustMigrationStatus: GleanEventWithExtras<{ duration_ms?: string; had_errors?: string; + metric_version?: string; number_of_logins_migrated?: string; number_of_logins_to_migrate?: string; run_id?: string; }>; rustMirrorStatus: GleanEventWithExtras<{ - error_message?: string; + metric_version?: string; operation?: string; - poisoned?: string; run_id?: string; status?: string; }>; + rustWriteFailure: GleanEventWithExtras<{ + error_message?: string; + form_action_origin_error?: string; + form_action_origin_fixable?: string; + has_empty_password?: string; + has_ftp_origin?: string; + has_punycode_form_action_origin?: string; + has_punycode_origin?: string; + has_username_line_break?: string; + has_username_nul?: string; + is_deleted?: string; + metric_version?: string; + operation?: string; + origin_error?: string; + origin_fixable?: string; + poisoned?: string; + run_id?: string; + time_created?: string; + time_last_used?: string; + }>; saveExistingLogin: GleanEventWithExtras<{ breached?: string; vulnerable?: string; @@ -11295,6 +11958,7 @@ interface GleanImpl { | "inserted_image", GleanCounter >; + tagged: GleanCounter; timeToView: GleanCustomDistribution; used: GleanCounter; }; @@ -11362,6 +12026,13 @@ interface GleanImpl { }>; }; + pdfjsOrganize: { + action: Record< + "copy" | "cut" | "delete" | "export_selected" | "move" | "paste" | "save", + GleanCounter + >; + }; + pdfjsSignature: { added: GleanEventWithExtras<{ has_alt_text?: string; @@ -13061,6 +13732,7 @@ interface GleanImpl { webcompatreporting: { learnMore: GleanEventNoExtras; opened: GleanEventWithExtras<{ source?: string }>; + previewed: GleanEventNoExtras; reasonDropdown: GleanEventWithExtras<{ setting?: string }>; send: GleanEventWithExtras<{ sent_with_blocked_trackers?: string }>; sendMoreInfo: GleanEventNoExtras; @@ -13149,10 +13821,17 @@ interface GleanImpl { characteristics: { anyPointerType: GleanQuantity; audioChannels: GleanQuantity; + audioCompressorGainReduction: GleanString; audioFingerprint: GleanQuantity; + audioFingerprint2: GleanString; + audioFloatFrequencySum: GleanString; + audioFloatTimeDomainSum: GleanString; audioFrames: GleanQuantity; audioRate: GleanQuantity; + audioUniqueSamples: GleanQuantity; availHeight: GleanQuantity; + availLeft: GleanQuantity; + availTop: GleanQuantity; availWidth: GleanQuantity; buildDate: GleanQuantity; cameraCount: GleanQuantity; @@ -13160,33 +13839,117 @@ interface GleanImpl { canvasFeatureStatus: GleanString; canvasdata1: GleanString; canvasdata10: GleanString; - canvasdata10software: GleanString; + canvasdata10Raw: GleanText; + canvasdata10Software: GleanString; + canvasdata10SoftwareRaw: GleanText; canvasdata11Webgl: GleanString; - canvasdata11Webglsoftware: GleanString; + canvasdata11WebglRaw: GleanText; + canvasdata11WebglSoftware: GleanString; + canvasdata11WebglSoftwareRaw: GleanText; canvasdata12Fingerprintjs1: GleanString; - canvasdata12Fingerprintjs1software: GleanString; + canvasdata12Fingerprintjs1Raw: GleanText; + canvasdata12Fingerprintjs1Software: GleanString; + canvasdata12Fingerprintjs1SoftwareRaw: GleanText; canvasdata13Fingerprintjs2: GleanString; - canvasdata13Fingerprintjs2software: GleanString; - canvasdata1software: GleanString; + canvasdata13Fingerprintjs2Raw: GleanText; + canvasdata13Fingerprintjs2Software: GleanString; + canvasdata13Fingerprintjs2SoftwareRaw: GleanText; + canvasdata1Raw: GleanText; + canvasdata1Software: GleanString; + canvasdata1SoftwareRaw: GleanText; canvasdata2: GleanString; - canvasdata2software: GleanString; + canvasdata2Raw: GleanText; + canvasdata2Software: GleanString; + canvasdata2SoftwareRaw: GleanText; canvasdata3: GleanString; - canvasdata3software: GleanString; + canvasdata3Raw: GleanText; + canvasdata3Software: GleanString; + canvasdata3SoftwareRaw: GleanText; canvasdata4: GleanString; - canvasdata4software: GleanString; + canvasdata4Raw: GleanText; + canvasdata4Software: GleanString; + canvasdata4SoftwareRaw: GleanText; canvasdata5: GleanString; - canvasdata5software: GleanString; + canvasdata5Raw: GleanText; + canvasdata5Software: GleanString; + canvasdata5SoftwareRaw: GleanText; canvasdata6: GleanString; - canvasdata6software: GleanString; + canvasdata6Raw: GleanText; + canvasdata6Software: GleanString; + canvasdata6SoftwareRaw: GleanText; canvasdata7: GleanString; - canvasdata7software: GleanString; + canvasdata7Raw: GleanText; + canvasdata7Software: GleanString; + canvasdata7SoftwareRaw: GleanText; canvasdata8: GleanString; - canvasdata8software: GleanString; + canvasdata8Raw: GleanText; + canvasdata8Software: GleanString; + canvasdata8SoftwareRaw: GleanText; canvasdata9: GleanString; - canvasdata9software: GleanString; + canvasdata9Raw: GleanText; + canvasdata9Software: GleanString; + canvasdata9SoftwareRaw: GleanText; changedMediaPrefs: GleanString; channel: GleanString; clientIdentifier: GleanUuid; + clientrectsElementGbcr01: GleanString; + clientrectsElementGbcr02: GleanString; + clientrectsElementGbcr03: GleanString; + clientrectsElementGbcr04: GleanString; + clientrectsElementGbcr05: GleanString; + clientrectsElementGbcr06: GleanString; + clientrectsElementGbcr07: GleanString; + clientrectsElementGbcr08: GleanString; + clientrectsElementGbcr09: GleanString; + clientrectsElementGbcr10: GleanString; + clientrectsElementGbcr11: GleanString; + clientrectsElementGbcr12: GleanString; + clientrectsElementGcr01: GleanString; + clientrectsElementGcr02: GleanString; + clientrectsElementGcr03: GleanString; + clientrectsElementGcr04: GleanString; + clientrectsElementGcr05: GleanString; + clientrectsElementGcr06: GleanString; + clientrectsElementGcr07: GleanString; + clientrectsElementGcr08: GleanString; + clientrectsElementGcr09: GleanString; + clientrectsElementGcr10: GleanString; + clientrectsElementGcr11: GleanString; + clientrectsElementGcr12: GleanString; + clientrectsEmoji01: GleanString; + clientrectsEmoji02: GleanString; + clientrectsEmoji03: GleanString; + clientrectsEmoji04: GleanString; + clientrectsEmoji05: GleanString; + clientrectsEmoji06: GleanString; + clientrectsEmojiFontFamily: GleanString; + clientrectsGhostDimensions: GleanString; + clientrectsKnownDimensions: GleanString; + clientrectsRangeGbcr01: GleanString; + clientrectsRangeGbcr02: GleanString; + clientrectsRangeGbcr03: GleanString; + clientrectsRangeGbcr04: GleanString; + clientrectsRangeGbcr05: GleanString; + clientrectsRangeGbcr06: GleanString; + clientrectsRangeGbcr07: GleanString; + clientrectsRangeGbcr08: GleanString; + clientrectsRangeGbcr09: GleanString; + clientrectsRangeGbcr10: GleanString; + clientrectsRangeGbcr11: GleanString; + clientrectsRangeGbcr12: GleanString; + clientrectsRangeGcr01: GleanString; + clientrectsRangeGcr02: GleanString; + clientrectsRangeGcr03: GleanString; + clientrectsRangeGcr04: GleanString; + clientrectsRangeGcr05: GleanString; + clientrectsRangeGcr06: GleanString; + clientrectsRangeGcr07: GleanString; + clientrectsRangeGcr08: GleanString; + clientrectsRangeGcr09: GleanString; + clientrectsRangeGcr10: GleanString; + clientrectsRangeGcr11: GleanString; + clientrectsRangeGcr12: GleanString; + clientrectsTextFontFamily: GleanString; colorAccentcolor: GleanQuantity; colorAccentcolortext: GleanQuantity; colorCanvas: GleanQuantity; @@ -13198,7 +13961,10 @@ interface GleanImpl { colorSelecteditemtext: GleanQuantity; cpuArch: GleanString; cpuModel: GleanString; + cssSystemColors: GleanText; + cssSystemFonts: GleanText; errors: GleanText; + firefoxBinaryArch: GleanString; fontDefaultDefaultGroup: GleanString; fontDefaultModified: GleanQuantity; fontDefaultWestern: GleanString; @@ -13231,7 +13997,26 @@ interface GleanImpl { fontsVariantANonallowlisted: GleanString; fontsVariantBAllowlisted: GleanString; fontsVariantBNonallowlisted: GleanString; + fontsVariantCAllowlisted: GleanString; + fontsVariantCNonallowlisted: GleanString; + fontsVariantDAllowlisted: GleanString; + fontsVariantDNonallowlisted: GleanString; + fontsVariantEAllowlisted: GleanString; + fontsVariantENonallowlisted: GleanString; + fontsVariantFAllowlisted: GleanString; + fontsVariantFNonallowlisted: GleanString; + fontsVariantGAllowlisted: GleanString; + fontsVariantGNonallowlisted: GleanString; + fontsVariantHAllowlisted: GleanString; + fontsVariantHNonallowlisted: GleanString; + fontsVariantIAllowlisted: GleanString; + fontsVariantINonallowlisted: GleanString; + fpuControlState: GleanString; gamepads: GleanStringList; + gl2Alpha: GleanString; + gl2AlphaSoftware: GleanString; + gl2Antialias: GleanString; + gl2AntialiasSoftware: GleanString; gl2ContextType: GleanString; gl2ContextTypeSoftware: GleanString; gl2Extensions: GleanText; @@ -13262,6 +14047,10 @@ interface GleanImpl { gl2VersionRawSoftware: GleanString; gl2VertexShader: GleanString; gl2VertexShaderSoftware: GleanString; + glAlpha: GleanString; + glAlphaSoftware: GleanString; + glAntialias: GleanString; + glAntialiasSoftware: GleanString; glContextType: GleanString; glContextTypeSoftware: GleanString; glExtensions: GleanText; @@ -13306,6 +14095,7 @@ interface GleanImpl { machineModelName: GleanString; mathOps: GleanText; mathOpsFdlibm: GleanText; + mathOpsFdlibm2: GleanText; mathml1: GleanString; mathml10: GleanString; mathml2: GleanString; @@ -13316,6 +14106,9 @@ interface GleanImpl { mathml7: GleanString; mathml8: GleanString; mathml9: GleanString; + mathmlDiagFontFamily: GleanString; + mathmlDiagPrefsModified: GleanString; + mathmlDiagValues: GleanText; maxTouchPoints: GleanQuantity; mediaCapabilitiesH264: GleanText; mediaCapabilitiesNotEfficient: GleanText; @@ -13330,6 +14123,9 @@ interface GleanImpl { orientationFreq: GleanQuantity; orientationabsDecimals: GleanQuantity; orientationabsFreq: GleanQuantity; + osDistro: GleanString; + osDistroId: GleanString; + osDistroVersion: GleanString; osName: GleanString; osVersion: GleanString; oscpu: GleanString; @@ -13364,13 +14160,22 @@ interface GleanImpl { screenHeight: GleanQuantity; screenWidth: GleanQuantity; screens: GleanText; + sdpCodecList: GleanText; sizeMode: GleanQuantity; speakerCount: GleanQuantity; + storageQuota: GleanQuantity; submissionSchema: GleanQuantity; + svgBbox: GleanString; + svgComputedTextLength: GleanString; + svgEmojiSet: GleanString; + svgExtentOfChar: GleanString; + svgSubstringLength: GleanString; systemLocale: GleanString; targetFrameRate: GleanQuantity; textAntiAliasing: GleanString; timezone: GleanString; + timezoneOffsetWeb: GleanString; + timezoneWeb: GleanString; touchRotationAngle: GleanString; useDocumentColors: GleanBoolean; userAgent: GleanText; @@ -13384,6 +14189,8 @@ interface GleanImpl { voicesNonlocalSsdeep: GleanString; voicesSample: GleanText; voicesSha1: GleanText; + webauthnCapabilities: GleanText; + wgpuIsFallbackAdapter: GleanBoolean; wgpuMaxbindgroups: GleanQuantity; wgpuMaxbindgroupsplusvertexbuffers: GleanQuantity; wgpuMaxbindingsperbindgroup: GleanQuantity; @@ -13481,16 +14288,7 @@ interface GleanImpl { }; searchSuggestions: { - abortedRequests: Record; - failedRequests: Record; latency: Record; - successfulRequests: Record; - }; - - searchSuggestionsOhttp: { - enabled: GleanBoolean; - latency: Record; - requestCounter: GleanDualLabeledCounter; }; legacyTelemetry: { @@ -13647,7 +14445,10 @@ interface GleanImpl { lang_tags_match?: string; total_time?: string; }>; - requestCount: Record<"full_page" | "select", GleanCounter>; + requestCount: Record< + "about_translations" | "full_page" | "select", + GleanCounter + >; restorePage: GleanEventWithExtras<{ flow_id?: string }>; translationRequest: GleanEventWithExtras<{ auto_translate?: string; @@ -13658,12 +14459,32 @@ interface GleanImpl { source_text_code_units?: string; source_text_word_count?: string; to_language?: string; - top_preferred_language?: string; }>; }; translationsAboutTranslationsPage: { + clearSourceTextButton: GleanEventWithExtras<{ flow_id?: string }>; + copyButton: GleanEventWithExtras<{ flow_id?: string }>; + featureBlockedInfoMessage: GleanEventWithExtras<{ flow_id?: string }>; + languageLoadErrorMessage: GleanEventWithExtras<{ flow_id?: string }>; open: GleanEventWithExtras<{ flow_id?: string }>; + policyDisabledInfoMessage: GleanEventWithExtras<{ flow_id?: string }>; + swapButton: GleanEventWithExtras<{ flow_id?: string }>; + tryAgainButton: GleanEventWithExtras<{ flow_id?: string }>; + unblockFeature: GleanEventWithExtras<{ flow_id?: string }>; + unsupportedInfoMessage: GleanEventWithExtras<{ flow_id?: string }>; + unsupportedLanguageMessage: GleanEventWithExtras<{ + detected_language?: string; + flow_id?: string; + source_text_code_units?: string; + source_text_word_count?: string; + }>; + }; + + translationsFeature: { + disable: GleanEventNoExtras; + enable: GleanEventNoExtras; + reset: GleanEventNoExtras; }; translationsPanel: { @@ -13740,7 +14561,6 @@ interface GleanImpl { from_language?: string; text_source?: string; to_language?: string; - top_preferred_language?: string; }>; openSettingsMenu: GleanEventWithExtras<{ flow_id?: string }>; translateButton: GleanEventWithExtras<{ @@ -13773,6 +14593,28 @@ interface GleanImpl { completeTimeout: GleanDualLabeledCounter; completion: GleanEventWithExtras<{ hit?: string; table_name?: string }>; completionError: GleanCustomDistribution; + globalCacheHit: Record< + | "custom_normal" + | "custom_private" + | "other_normal" + | "other_private" + | "standard_normal" + | "standard_private" + | "strict_normal" + | "strict_private", + GleanCounter + >; + globalCacheMiss: Record< + | "custom_normal" + | "custom_private" + | "other_normal" + | "other_private" + | "standard_normal" + | "standard_private" + | "strict_normal" + | "strict_private", + GleanCounter + >; lookupHit: Record< | "ads-track-digest256" | "analytics-track-digest256" @@ -13838,6 +14680,39 @@ interface GleanImpl { GleanCounter >; lookupTime2: GleanTimingDistribution; + realtimeSimulationRequestCount: Record< + | "custom_normal" + | "custom_private" + | "other_normal" + | "other_private" + | "standard_normal" + | "standard_private" + | "strict_normal" + | "strict_private", + GleanCounter + >; + realtimeSimulationRequestSize: Record< + | "custom_normal" + | "custom_private" + | "other_normal" + | "other_private" + | "standard_normal" + | "standard_private" + | "strict_normal" + | "strict_private", + GleanCounter + >; + realtimeSimulationResponseSize: Record< + | "custom_normal" + | "custom_private" + | "other_normal" + | "other_private" + | "standard_normal" + | "standard_private" + | "strict_normal" + | "strict_private", + GleanCounter + >; shutdownTime: GleanTimingDistribution; threathitNetworkError: GleanCustomDistribution; threathitRemoteStatus: GleanCustomDistribution; @@ -13964,6 +14839,14 @@ interface GleanImpl { }>; }; + securityUiNeterror: { + loadAboutneterror: GleanEventWithExtras<{ + channel_status?: string; + is_frame?: string; + value?: string; + }>; + }; + securityUiTlserror: { loadAbouttlserror: GleanEventWithExtras<{ channel_status?: string; @@ -13989,6 +14872,7 @@ interface GleanImpl { }; firstStartup: { + categoryTasksTime: GleanQuantity; deleteTasksTime: GleanQuantity; elapsed: GleanQuantity; newProfile: GleanBoolean; @@ -14657,6 +15541,7 @@ interface GleanImpl { serviceEnabled: GleanBoolean; bitsResultComplete: GleanCustomDistribution; bitsResultPartial: GleanCustomDistribution; + blocked: GleanCounter; canUseBitsExternal: Record< | "CanUseBits" | "NoBits_FeatureOff" @@ -14793,6 +15678,10 @@ interface GleanImpl { profiles: { creationDate: GleanQuantity; + creationPlace: Record< + "legacy_existing" | "legacy_forced" | "xdg_config" | "xdg_default", + GleanCounter + >; firstUseDate: GleanQuantity; recoveredFromBackup: GleanQuantity; resetDate: GleanQuantity; @@ -14819,6 +15708,8 @@ interface GleanImpl { widget: { imeNameOnMac: Record; + macApplicationMenuOpened: GleanCounter; + desktopEnvironment: GleanString; gtkVersion: GleanString; imeNameOnLinux: Record; darkMode: GleanBoolean; @@ -14878,6 +15769,31 @@ interface GleanImpl { system: GleanObject; }; + memory: { + collectionTime: GleanTimingDistribution; + distributionAmongContent: Record; + freePurgedPages: GleanTimingDistribution; + ghostWindows: GleanCustomDistribution; + heapAllocated: GleanMemoryDistribution; + heapOverheadFraction: GleanCustomDistribution; + imagesContentUsedUncompressed: GleanMemoryDistribution; + jsCompartmentsSystem: GleanCustomDistribution; + jsCompartmentsUser: GleanCustomDistribution; + jsGcHeap: Record; + jsRealmsSystem: GleanCustomDistribution; + jsRealmsUser: GleanCustomDistribution; + lowMemoryEventsPhysical: GleanCustomDistribution; + pageFaultsHard: GleanCustomDistribution; + residentFast: Record; + residentPeak: Record; + storageSqlite: GleanMemoryDistribution; + total: GleanMemoryDistribution; + unique: Record; + uniqueContentStartup: GleanMemoryDistribution; + vsize: GleanMemoryDistribution; + vsizeMaxContiguous: GleanMemoryDistribution; + }; + memoryPhc: { slop: GleanMemoryDistribution; slotsAllocated: GleanCustomDistribution; @@ -14885,7 +15801,20 @@ interface GleanImpl { }; memoryWatcher: { + nonOomSample: GleanEventWithExtras<{ + psi_full_avg10?: string; + psi_full_avg60?: string; + psi_some_avg10?: string; + psi_some_avg60?: string; + }>; onHighMemoryStats: GleanEventWithExtras<{ value?: string }>; + processOomKilled: GleanEventWithExtras<{ + psi_available?: string; + psi_full_avg10?: string; + psi_full_avg60?: string; + psi_some_avg10?: string; + psi_some_avg60?: string; + }>; }; systemCpu: { @@ -14908,6 +15837,7 @@ interface GleanImpl { systemOs: { distro: GleanString; distroVersion: GleanString; + libstdcxxVersion: GleanString; locale: GleanString; name: GleanString; servicePackMajor: GleanQuantity; @@ -14928,6 +15858,9 @@ interface GleanImpl { interface GleanPingsImpl { messagingSystem: GleanPingNoReason; + microsurvey: GleanPingNoReason; + postProfileRestore: GleanPingNoReason; + profileRestore: GleanPingNoReason; newtab: GleanPingWithReason<"component_init" | "newtab_session_end">; newtabContent: GleanPingWithReason<"component_init" | "newtab_session_end">; spoc: GleanPingWithReason<"click" | "impression" | "save">; @@ -14947,16 +15880,16 @@ interface GleanPingsImpl { pageloadBaseDomain: GleanPingWithReason<"pageload">; useCounters: GleanPingWithReason<"app_shutdown_confirmed" | "idle_startup">; unexpectedScriptLoad: GleanPingNoReason; + localNetworkAccess: GleanPingNoReason; + urlClassifierHarmfulAddon: GleanPingNoReason; fxAccounts: GleanPingWithReason<"active" | "dirty_startup" | "inactive">; - sync: GleanPingWithReason<"idchanged" | "schedule" | "shutdown">; + sync: GleanPingWithReason<"idchange" | "schedule" | "shutdown">; bounceTrackingProtection: GleanPingNoReason; hangReport: GleanPingNoReason; backgroundTasks: GleanPingNoReason; captchaDetection: GleanPingNoReason; - crash: GleanPingWithReason<"crash" | "event_found">; geckoTrace: GleanPingWithReason<"buffer_full" | "idle" | "shutdown">; dauReporting: GleanPingWithReason<"active" | "dirty_startup" | "inactive">; - tempFogInitialState: GleanPingWithReason<"startup">; collectionDisabledPing: GleanPingNoReason; disabledPing: GleanPingNoReason; onePingOnly: GleanPingNoReason; @@ -14973,6 +15906,7 @@ interface GleanPingsImpl { usageReporting: GleanPingWithReason<"active" | "dirty_startup" | "inactive">; firstStartup: GleanPingNoReason; defaultAgent: GleanPingWithReason<"daily_ping">; + addons: GleanPingWithReason<"daily" | "startup" | "updated">; backgroundUpdate: GleanPingWithReason<"backgroundupdate_task">; update: GleanPingWithReason<"ready" | "success">; } diff --git a/src/zen/@types/lib.gecko.linux.d.ts b/src/zen/@types/lib.gecko.linux.d.ts index e83ac5907..df409e2c4 100644 --- a/src/zen/@types/lib.gecko.linux.d.ts +++ b/src/zen/@types/lib.gecko.linux.d.ts @@ -13,6 +13,8 @@ declare global { interface nsIGNOMEShellService extends nsIShellService { readonly canSetDesktopBackground: boolean; isDefaultForScheme(aScheme: string): boolean; + getGSettingsString(aScheme: string, aKey: string): string; + setGSettingsString(aScheme: string, aKey: string, aValue: string): void; } // https://searchfox.org/mozilla-central/source/browser/components/shell/nsIOpenTabsProvider.idl @@ -69,7 +71,11 @@ declare global { } // global // Typedefs from xpidl. +type CSPDirective = nsIContentSecurityPolicy.CSPDirective; type PRTime = i64; +type RequireTrustedTypesForDirectiveState = + nsIContentSecurityPolicy.RequireTrustedTypesForDirectiveState; +type nsContentPolicyType = nsIContentPolicy.nsContentPolicyType; type nsHandlerInfoAction = i32; type nsTaskbarProgressState = i32; diff --git a/src/zen/@types/lib.gecko.modules.d.ts b/src/zen/@types/lib.gecko.modules.d.ts index 181150aec..45173445b 100644 --- a/src/zen/@types/lib.gecko.modules.d.ts +++ b/src/zen/@types/lib.gecko.modules.d.ts @@ -8,7 +8,8 @@ export interface Modules { "chrome://browser/content/aboutlogins/aboutLoginsUtils.mjs": typeof import("chrome://browser/content/aboutlogins/aboutLoginsUtils.mjs"); - "chrome://browser/content/asrouter/components/fxa-menu-message.mjs": typeof import("chrome://browser/content/asrouter/components/fxa-menu-message.mjs"); + "chrome://browser/content/aiwindow/modules/TokenStreamParser.mjs": typeof import("chrome://browser/content/aiwindow/modules/TokenStreamParser.mjs"); + "chrome://browser/content/asrouter/components/menu-message.mjs": typeof import("chrome://browser/content/asrouter/components/menu-message.mjs"); "chrome://browser/content/backup/backup-constants.mjs": typeof import("chrome://browser/content/backup/backup-constants.mjs"); "chrome://browser/content/genai/content/link-preview-card-onboarding.mjs": typeof import("chrome://browser/content/genai/content/link-preview-card-onboarding.mjs"); "chrome://browser/content/genai/content/link-preview-card.mjs": typeof import("chrome://browser/content/genai/content/link-preview-card.mjs"); @@ -16,21 +17,31 @@ export interface Modules { "chrome://browser/content/ipprotection/ipprotection-constants.mjs": typeof import("chrome://browser/content/ipprotection/ipprotection-constants.mjs"); "chrome://browser/content/migration/migration-wizard-constants.mjs": typeof import("chrome://browser/content/migration/migration-wizard-constants.mjs"); "chrome://browser/content/nsContextMenu.sys.mjs": typeof import("chrome://browser/content/nsContextMenu.sys.mjs"); + "chrome://browser/content/preferences/config/SettingGroupManager.mjs": typeof import("chrome://browser/content/preferences/config/SettingGroupManager.mjs"); + "chrome://browser/content/preferences/config/SettingPaneManager.mjs": typeof import("chrome://browser/content/preferences/config/SettingPaneManager.mjs"); + "chrome://browser/content/preferences/config/home-startup.mjs": typeof import("chrome://browser/content/preferences/config/home-startup.mjs"); "chrome://browser/content/screenshots/fileHelpers.mjs": typeof import("chrome://browser/content/screenshots/fileHelpers.mjs"); "chrome://browser/content/sidebar/sidebar-main.mjs": typeof import("chrome://browser/content/sidebar/sidebar-main.mjs"); "chrome://browser/content/sidebar/sidebar-panel-header.mjs": typeof import("chrome://browser/content/sidebar/sidebar-panel-header.mjs"); + "chrome://browser/content/sidebar/sidebar-permissions-ui.mjs": typeof import("chrome://browser/content/sidebar/sidebar-permissions-ui.mjs"); + "chrome://browser/content/sidebar/sidebar-permissions.mjs": typeof import("chrome://browser/content/sidebar/sidebar-permissions.mjs"); "chrome://browser/content/tabbrowser/tab-hover-preview.mjs": typeof import("chrome://browser/content/tabbrowser/tab-hover-preview.mjs"); "chrome://browser/content/translations/TranslationsPanelShared.sys.mjs": typeof import("chrome://browser/content/translations/TranslationsPanelShared.sys.mjs"); + "chrome://browser/content/urlbar/SmartbarInput.mjs": typeof import("chrome://browser/content/urlbar/SmartbarInput.mjs"); + "chrome://browser/content/urlbar/SmartbarInputController.mjs": typeof import("chrome://browser/content/urlbar/SmartbarInputController.mjs"); + "chrome://browser/content/urlbar/UrlbarInput.mjs": typeof import("chrome://browser/content/urlbar/UrlbarInput.mjs"); "chrome://browser/content/webrtc/webrtc-preview.mjs": typeof import("chrome://browser/content/webrtc/webrtc-preview.mjs"); "chrome://devtools-startup/content/DevToolsShim.sys.mjs": typeof import("chrome://devtools-startup/content/DevToolsShim.sys.mjs"); "chrome://formautofill/content/manageDialog.mjs": typeof import("chrome://formautofill/content/manageDialog.mjs"); "chrome://global/content/aboutLogging/profileStorage.mjs": typeof import("chrome://global/content/aboutLogging/profileStorage.mjs"); + "chrome://global/content/bindings/colorpicker-common.mjs": typeof import("chrome://global/content/bindings/colorpicker-common.mjs"); "chrome://global/content/certviewer/certDecoder.mjs": typeof import("chrome://global/content/certviewer/certDecoder.mjs"); "chrome://global/content/elements/browser-custom-element.mjs": typeof import("chrome://global/content/elements/browser-custom-element.mjs"); "chrome://global/content/ml/BlockWords.sys.mjs": typeof import("chrome://global/content/ml/BlockWords.sys.mjs"); "chrome://global/content/ml/ClusterAlgos.sys.mjs": typeof import("chrome://global/content/ml/ClusterAlgos.sys.mjs"); "chrome://global/content/ml/EmbeddingsGenerator.sys.mjs": typeof import("chrome://global/content/ml/EmbeddingsGenerator.sys.mjs"); "chrome://global/content/ml/EngineProcess.sys.mjs": typeof import("chrome://global/content/ml/EngineProcess.sys.mjs"); + "chrome://global/content/ml/MLTelemetry.sys.mjs": typeof import("chrome://global/content/ml/MLTelemetry.sys.mjs"); "chrome://global/content/ml/ModelHub.sys.mjs": typeof import("chrome://global/content/ml/ModelHub.sys.mjs"); "chrome://global/content/ml/NLPUtils.sys.mjs": typeof import("chrome://global/content/ml/NLPUtils.sys.mjs"); "chrome://global/content/ml/OPFS.sys.mjs": typeof import("chrome://global/content/ml/OPFS.sys.mjs"); @@ -42,11 +53,19 @@ export interface Modules { "chrome://global/content/ml/backends/OpenAIPipeline.mjs": typeof import("chrome://global/content/ml/backends/OpenAIPipeline.mjs"); "chrome://global/content/ml/backends/Pipeline.mjs": typeof import("chrome://global/content/ml/backends/Pipeline.mjs"); "chrome://global/content/ml/backends/StaticEmbeddingsPipeline.mjs": typeof import("chrome://global/content/ml/backends/StaticEmbeddingsPipeline.mjs"); + "chrome://global/content/ml/security/ConditionEvaluator.sys.mjs": typeof import("chrome://global/content/ml/security/ConditionEvaluator.sys.mjs"); + "chrome://global/content/ml/security/DecisionTypes.sys.mjs": typeof import("chrome://global/content/ml/security/DecisionTypes.sys.mjs"); + "chrome://global/content/ml/security/PolicyEvaluator.sys.mjs": typeof import("chrome://global/content/ml/security/PolicyEvaluator.sys.mjs"); + "chrome://global/content/ml/security/SecurityLogger.sys.mjs": typeof import("chrome://global/content/ml/security/SecurityLogger.sys.mjs"); + "chrome://global/content/ml/security/SecurityOrchestrator.sys.mjs": typeof import("chrome://global/content/ml/security/SecurityOrchestrator.sys.mjs"); + "chrome://global/content/ml/security/SecurityUtils.sys.mjs": typeof import("chrome://global/content/ml/security/SecurityUtils.sys.mjs"); "chrome://global/content/preferences/Preferences.mjs": typeof import("chrome://global/content/preferences/Preferences.mjs"); + "chrome://global/content/translations/TranslationsFeature.sys.mjs": typeof import("chrome://global/content/translations/TranslationsFeature.sys.mjs"); "chrome://global/content/translations/TranslationsTelemetry.sys.mjs": typeof import("chrome://global/content/translations/TranslationsTelemetry.sys.mjs"); "chrome://global/content/translations/TranslationsUtils.mjs": typeof import("chrome://global/content/translations/TranslationsUtils.mjs"); "chrome://global/content/translations/translations-document.sys.mjs": typeof import("chrome://global/content/translations/translations-document.sys.mjs"); "chrome://global/content/translations/translations-engine.sys.mjs": typeof import("chrome://global/content/translations/translations-engine.sys.mjs"); + "chrome://mochikit/content/ShutdownLeakPathFinder.sys.mjs": typeof import("chrome://mochikit/content/ShutdownLeakPathFinder.sys.mjs"); "chrome://mochikit/content/ShutdownLeaksCollector.sys.mjs": typeof import("chrome://mochikit/content/ShutdownLeaksCollector.sys.mjs"); "chrome://mochikit/content/tests/SimpleTest/DragSourceChildContext.sys.mjs": typeof import("chrome://mochikit/content/tests/SimpleTest/DragSourceChildContext.sys.mjs"); "chrome://mochikit/content/tests/SimpleTest/DragSourceParentContext.sys.mjs": typeof import("chrome://mochikit/content/tests/SimpleTest/DragSourceParentContext.sys.mjs"); @@ -92,6 +111,7 @@ export interface Modules { "chrome://remote/content/shared/Addon.sys.mjs": typeof import("chrome://remote/content/shared/Addon.sys.mjs"); "chrome://remote/content/shared/AppInfo.sys.mjs": typeof import("chrome://remote/content/shared/AppInfo.sys.mjs"); "chrome://remote/content/shared/AsyncQueue.sys.mjs": typeof import("chrome://remote/content/shared/AsyncQueue.sys.mjs"); + "chrome://remote/content/shared/BiMap.sys.mjs": typeof import("chrome://remote/content/shared/BiMap.sys.mjs"); "chrome://remote/content/shared/Browser.sys.mjs": typeof import("chrome://remote/content/shared/Browser.sys.mjs"); "chrome://remote/content/shared/Capture.sys.mjs": typeof import("chrome://remote/content/shared/Capture.sys.mjs"); "chrome://remote/content/shared/ChallengeHeaderParser.sys.mjs": typeof import("chrome://remote/content/shared/ChallengeHeaderParser.sys.mjs"); @@ -103,6 +123,7 @@ export interface Modules { "chrome://remote/content/shared/Navigate.sys.mjs": typeof import("chrome://remote/content/shared/Navigate.sys.mjs"); "chrome://remote/content/shared/NavigationManager.sys.mjs": typeof import("chrome://remote/content/shared/NavigationManager.sys.mjs"); "chrome://remote/content/shared/NetworkCacheManager.sys.mjs": typeof import("chrome://remote/content/shared/NetworkCacheManager.sys.mjs"); + "chrome://remote/content/shared/NetworkDataBytes.sys.mjs": typeof import("chrome://remote/content/shared/NetworkDataBytes.sys.mjs"); "chrome://remote/content/shared/NetworkDecodedBodySizeMap.sys.mjs": typeof import("chrome://remote/content/shared/NetworkDecodedBodySizeMap.sys.mjs"); "chrome://remote/content/shared/NetworkRequest.sys.mjs": typeof import("chrome://remote/content/shared/NetworkRequest.sys.mjs"); "chrome://remote/content/shared/NetworkResponse.sys.mjs": typeof import("chrome://remote/content/shared/NetworkResponse.sys.mjs"); @@ -119,14 +140,17 @@ export interface Modules { "chrome://remote/content/shared/UserContextManager.sys.mjs": typeof import("chrome://remote/content/shared/UserContextManager.sys.mjs"); "chrome://remote/content/shared/WindowManager.sys.mjs": typeof import("chrome://remote/content/shared/WindowManager.sys.mjs"); "chrome://remote/content/shared/js-process-actors/WebDriverDocumentInsertedActor.sys.mjs": typeof import("chrome://remote/content/shared/js-process-actors/WebDriverDocumentInsertedActor.sys.mjs"); + "chrome://remote/content/shared/js-process-actors/WebDriverWorkerListenerActor.sys.mjs": typeof import("chrome://remote/content/shared/js-process-actors/WebDriverWorkerListenerActor.sys.mjs"); "chrome://remote/content/shared/listeners/BeforeStopRequestListener.sys.mjs": typeof import("chrome://remote/content/shared/listeners/BeforeStopRequestListener.sys.mjs"); "chrome://remote/content/shared/listeners/BrowsingContextListener.sys.mjs": typeof import("chrome://remote/content/shared/listeners/BrowsingContextListener.sys.mjs"); "chrome://remote/content/shared/listeners/CachedResourceListener.sys.mjs": typeof import("chrome://remote/content/shared/listeners/CachedResourceListener.sys.mjs"); + "chrome://remote/content/shared/listeners/ChromeWindowListener.sys.mjs": typeof import("chrome://remote/content/shared/listeners/ChromeWindowListener.sys.mjs"); "chrome://remote/content/shared/listeners/ConsoleAPIListener.sys.mjs": typeof import("chrome://remote/content/shared/listeners/ConsoleAPIListener.sys.mjs"); "chrome://remote/content/shared/listeners/ConsoleListener.sys.mjs": typeof import("chrome://remote/content/shared/listeners/ConsoleListener.sys.mjs"); "chrome://remote/content/shared/listeners/ContextualIdentityListener.sys.mjs": typeof import("chrome://remote/content/shared/listeners/ContextualIdentityListener.sys.mjs"); "chrome://remote/content/shared/listeners/DataChannelListener.sys.mjs": typeof import("chrome://remote/content/shared/listeners/DataChannelListener.sys.mjs"); "chrome://remote/content/shared/listeners/DownloadListener.sys.mjs": typeof import("chrome://remote/content/shared/listeners/DownloadListener.sys.mjs"); + "chrome://remote/content/shared/listeners/FilePickerListener.sys.mjs": typeof import("chrome://remote/content/shared/listeners/FilePickerListener.sys.mjs"); "chrome://remote/content/shared/listeners/LoadListener.sys.mjs": typeof import("chrome://remote/content/shared/listeners/LoadListener.sys.mjs"); "chrome://remote/content/shared/listeners/NavigationListener.sys.mjs": typeof import("chrome://remote/content/shared/listeners/NavigationListener.sys.mjs"); "chrome://remote/content/shared/listeners/NetworkEventRecord.sys.mjs": typeof import("chrome://remote/content/shared/listeners/NetworkEventRecord.sys.mjs"); @@ -154,19 +178,23 @@ export interface Modules { "chrome://remote/content/shared/webdriver/Certificates.sys.mjs": typeof import("chrome://remote/content/shared/webdriver/Certificates.sys.mjs"); "chrome://remote/content/shared/webdriver/Errors.sys.mjs": typeof import("chrome://remote/content/shared/webdriver/Errors.sys.mjs"); "chrome://remote/content/shared/webdriver/Event.sys.mjs": typeof import("chrome://remote/content/shared/webdriver/Event.sys.mjs"); + "chrome://remote/content/shared/webdriver/FilePickerHandler.sys.mjs": typeof import("chrome://remote/content/shared/webdriver/FilePickerHandler.sys.mjs"); "chrome://remote/content/shared/webdriver/KeyData.sys.mjs": typeof import("chrome://remote/content/shared/webdriver/KeyData.sys.mjs"); "chrome://remote/content/shared/webdriver/NodeCache.sys.mjs": typeof import("chrome://remote/content/shared/webdriver/NodeCache.sys.mjs"); "chrome://remote/content/shared/webdriver/Session.sys.mjs": typeof import("chrome://remote/content/shared/webdriver/Session.sys.mjs"); "chrome://remote/content/shared/webdriver/URLPattern.sys.mjs": typeof import("chrome://remote/content/shared/webdriver/URLPattern.sys.mjs"); "chrome://remote/content/shared/webdriver/UserPromptHandler.sys.mjs": typeof import("chrome://remote/content/shared/webdriver/UserPromptHandler.sys.mjs"); "chrome://remote/content/shared/webdriver/process-actors/WebDriverProcessDataParent.sys.mjs": typeof import("chrome://remote/content/shared/webdriver/process-actors/WebDriverProcessDataParent.sys.mjs"); + "chrome://remote/content/webdriver-bidi/DownloadBehaviorManager.sys.mjs": typeof import("chrome://remote/content/webdriver-bidi/DownloadBehaviorManager.sys.mjs"); "chrome://remote/content/webdriver-bidi/NewSessionHandler.sys.mjs": typeof import("chrome://remote/content/webdriver-bidi/NewSessionHandler.sys.mjs"); "chrome://remote/content/webdriver-bidi/ProxyPerUserContextManager.sys.mjs": typeof import("chrome://remote/content/webdriver-bidi/ProxyPerUserContextManager.sys.mjs"); "chrome://remote/content/webdriver-bidi/RemoteValue.sys.mjs": typeof import("chrome://remote/content/webdriver-bidi/RemoteValue.sys.mjs"); + "chrome://remote/content/webdriver-bidi/UserPromptHandlerManager.sys.mjs": typeof import("chrome://remote/content/webdriver-bidi/UserPromptHandlerManager.sys.mjs"); "chrome://remote/content/webdriver-bidi/WebDriverBiDi.sys.mjs": typeof import("chrome://remote/content/webdriver-bidi/WebDriverBiDi.sys.mjs"); "chrome://remote/content/webdriver-bidi/WebDriverBiDiConnection.sys.mjs": typeof import("chrome://remote/content/webdriver-bidi/WebDriverBiDiConnection.sys.mjs"); "chrome://remote/content/webdriver-bidi/modules/Intercept.sys.mjs": typeof import("chrome://remote/content/webdriver-bidi/modules/Intercept.sys.mjs"); "chrome://remote/content/webdriver-bidi/modules/ModuleRegistry.sys.mjs": typeof import("chrome://remote/content/webdriver-bidi/modules/ModuleRegistry.sys.mjs"); + "chrome://remote/content/webdriver-bidi/modules/root/_configuration.sys.mjs": typeof import("chrome://remote/content/webdriver-bidi/modules/root/_configuration.sys.mjs"); "chrome://remote/content/webdriver-bidi/modules/root/browser.sys.mjs": typeof import("chrome://remote/content/webdriver-bidi/modules/root/browser.sys.mjs"); "chrome://remote/content/webdriver-bidi/modules/root/browsingContext.sys.mjs": typeof import("chrome://remote/content/webdriver-bidi/modules/root/browsingContext.sys.mjs"); "chrome://remote/content/webdriver-bidi/modules/root/emulation.sys.mjs": typeof import("chrome://remote/content/webdriver-bidi/modules/root/emulation.sys.mjs"); @@ -179,6 +207,7 @@ export interface Modules { "chrome://remote/content/webdriver-bidi/modules/root/storage.sys.mjs": typeof import("chrome://remote/content/webdriver-bidi/modules/root/storage.sys.mjs"); "chrome://remote/content/webdriver-bidi/modules/root/webExtension.sys.mjs": typeof import("chrome://remote/content/webdriver-bidi/modules/root/webExtension.sys.mjs"); "chrome://remote/content/webdriver-bidi/modules/windowglobal-in-root/browsingContext.sys.mjs": typeof import("chrome://remote/content/webdriver-bidi/modules/windowglobal-in-root/browsingContext.sys.mjs"); + "chrome://remote/content/webdriver-bidi/modules/windowglobal-in-root/input.sys.mjs": typeof import("chrome://remote/content/webdriver-bidi/modules/windowglobal-in-root/input.sys.mjs"); "chrome://remote/content/webdriver-bidi/modules/windowglobal-in-root/log.sys.mjs": typeof import("chrome://remote/content/webdriver-bidi/modules/windowglobal-in-root/log.sys.mjs"); "chrome://remote/content/webdriver-bidi/modules/windowglobal-in-root/network.sys.mjs": typeof import("chrome://remote/content/webdriver-bidi/modules/windowglobal-in-root/network.sys.mjs"); "chrome://remote/content/webdriver-bidi/modules/windowglobal-in-root/script.sys.mjs": typeof import("chrome://remote/content/webdriver-bidi/modules/windowglobal-in-root/script.sys.mjs"); @@ -193,6 +222,38 @@ export interface Modules { "moz-src:///browser/components/DesktopActorRegistry.sys.mjs": typeof import("moz-src:///browser/components/DesktopActorRegistry.sys.mjs"); "moz-src:///browser/components/ProfileDataUpgrader.sys.mjs": typeof import("moz-src:///browser/components/ProfileDataUpgrader.sys.mjs"); "moz-src:///browser/components/StartupTelemetry.sys.mjs": typeof import("moz-src:///browser/components/StartupTelemetry.sys.mjs"); + "moz-src:///browser/components/aiwindow/models/Chat.sys.mjs": typeof import("moz-src:///browser/components/aiwindow/models/Chat.sys.mjs"); + "moz-src:///browser/components/aiwindow/models/ChatUtils.sys.mjs": typeof import("moz-src:///browser/components/aiwindow/models/ChatUtils.sys.mjs"); + "moz-src:///browser/components/aiwindow/models/CitationParser.sys.mjs": typeof import("moz-src:///browser/components/aiwindow/models/CitationParser.sys.mjs"); + "moz-src:///browser/components/aiwindow/models/ConversationSuggestions.sys.mjs": typeof import("moz-src:///browser/components/aiwindow/models/ConversationSuggestions.sys.mjs"); + "moz-src:///browser/components/aiwindow/models/IntentClassifier.sys.mjs": typeof import("moz-src:///browser/components/aiwindow/models/IntentClassifier.sys.mjs"); + "moz-src:///browser/components/aiwindow/models/SearchBrowsingHistory.sys.mjs": typeof import("moz-src:///browser/components/aiwindow/models/SearchBrowsingHistory.sys.mjs"); + "moz-src:///browser/components/aiwindow/models/SearchBrowsingHistoryDomainBoost.sys.mjs": typeof import("moz-src:///browser/components/aiwindow/models/SearchBrowsingHistoryDomainBoost.sys.mjs"); + "moz-src:///browser/components/aiwindow/models/TitleGeneration.sys.mjs": typeof import("moz-src:///browser/components/aiwindow/models/TitleGeneration.sys.mjs"); + "moz-src:///browser/components/aiwindow/models/Tools.sys.mjs": typeof import("moz-src:///browser/components/aiwindow/models/Tools.sys.mjs"); + "moz-src:///browser/components/aiwindow/models/Utils.sys.mjs": typeof import("moz-src:///browser/components/aiwindow/models/Utils.sys.mjs"); + "moz-src:///browser/components/aiwindow/models/memories/Memories.sys.mjs": typeof import("moz-src:///browser/components/aiwindow/models/memories/Memories.sys.mjs"); + "moz-src:///browser/components/aiwindow/models/memories/MemoriesChatSource.sys.mjs": typeof import("moz-src:///browser/components/aiwindow/models/memories/MemoriesChatSource.sys.mjs"); + "moz-src:///browser/components/aiwindow/models/memories/MemoriesConstants.sys.mjs": typeof import("moz-src:///browser/components/aiwindow/models/memories/MemoriesConstants.sys.mjs"); + "moz-src:///browser/components/aiwindow/models/memories/MemoriesConversationScheduler.sys.mjs": typeof import("moz-src:///browser/components/aiwindow/models/memories/MemoriesConversationScheduler.sys.mjs"); + "moz-src:///browser/components/aiwindow/models/memories/MemoriesDriftDetector.sys.mjs": typeof import("moz-src:///browser/components/aiwindow/models/memories/MemoriesDriftDetector.sys.mjs"); + "moz-src:///browser/components/aiwindow/models/memories/MemoriesHistoryScheduler.sys.mjs": typeof import("moz-src:///browser/components/aiwindow/models/memories/MemoriesHistoryScheduler.sys.mjs"); + "moz-src:///browser/components/aiwindow/models/memories/MemoriesHistorySource.sys.mjs": typeof import("moz-src:///browser/components/aiwindow/models/memories/MemoriesHistorySource.sys.mjs"); + "moz-src:///browser/components/aiwindow/models/memories/MemoriesManager.sys.mjs": typeof import("moz-src:///browser/components/aiwindow/models/memories/MemoriesManager.sys.mjs"); + "moz-src:///browser/components/aiwindow/models/memories/MemoriesSchedulers.sys.mjs": typeof import("moz-src:///browser/components/aiwindow/models/memories/MemoriesSchedulers.sys.mjs"); + "moz-src:///browser/components/aiwindow/models/memories/SensitiveInfoDetector.sys.mjs": typeof import("moz-src:///browser/components/aiwindow/models/memories/SensitiveInfoDetector.sys.mjs"); + "moz-src:///browser/components/aiwindow/services/MemoryStore.sys.mjs": typeof import("moz-src:///browser/components/aiwindow/services/MemoryStore.sys.mjs"); + "moz-src:///browser/components/aiwindow/ui/modules/AIWindow.sys.mjs": typeof import("moz-src:///browser/components/aiwindow/ui/modules/AIWindow.sys.mjs"); + "moz-src:///browser/components/aiwindow/ui/modules/AIWindowAccountAuth.sys.mjs": typeof import("moz-src:///browser/components/aiwindow/ui/modules/AIWindowAccountAuth.sys.mjs"); + "moz-src:///browser/components/aiwindow/ui/modules/AIWindowMenu.sys.mjs": typeof import("moz-src:///browser/components/aiwindow/ui/modules/AIWindowMenu.sys.mjs"); + "moz-src:///browser/components/aiwindow/ui/modules/AIWindowTabStatesManager.sys.mjs": typeof import("moz-src:///browser/components/aiwindow/ui/modules/AIWindowTabStatesManager.sys.mjs"); + "moz-src:///browser/components/aiwindow/ui/modules/AIWindowUI.sys.mjs": typeof import("moz-src:///browser/components/aiwindow/ui/modules/AIWindowUI.sys.mjs"); + "moz-src:///browser/components/aiwindow/ui/modules/ChatConstants.sys.mjs": typeof import("moz-src:///browser/components/aiwindow/ui/modules/ChatConstants.sys.mjs"); + "moz-src:///browser/components/aiwindow/ui/modules/ChatConversation.sys.mjs": typeof import("moz-src:///browser/components/aiwindow/ui/modules/ChatConversation.sys.mjs"); + "moz-src:///browser/components/aiwindow/ui/modules/ChatEnums.sys.mjs": typeof import("moz-src:///browser/components/aiwindow/ui/modules/ChatEnums.sys.mjs"); + "moz-src:///browser/components/aiwindow/ui/modules/ChatMessage.sys.mjs": typeof import("moz-src:///browser/components/aiwindow/ui/modules/ChatMessage.sys.mjs"); + "moz-src:///browser/components/aiwindow/ui/modules/ChatStore.sys.mjs": typeof import("moz-src:///browser/components/aiwindow/ui/modules/ChatStore.sys.mjs"); + "moz-src:///browser/components/aiwindow/ui/modules/ChatUtils.sys.mjs": typeof import("moz-src:///browser/components/aiwindow/ui/modules/ChatUtils.sys.mjs"); "moz-src:///browser/components/attribution/AttributionCode.sys.mjs": typeof import("moz-src:///browser/components/attribution/AttributionCode.sys.mjs"); "moz-src:///browser/components/attribution/MacAttribution.sys.mjs": typeof import("moz-src:///browser/components/attribution/MacAttribution.sys.mjs"); "moz-src:///browser/components/contentanalysis/content/ContentAnalysis.sys.mjs": typeof import("moz-src:///browser/components/contentanalysis/content/ContentAnalysis.sys.mjs"); @@ -213,8 +274,28 @@ export interface Modules { "moz-src:///browser/components/genai/LinkPreview.sys.mjs": typeof import("moz-src:///browser/components/genai/LinkPreview.sys.mjs"); "moz-src:///browser/components/genai/LinkPreviewModel.sys.mjs": typeof import("moz-src:///browser/components/genai/LinkPreviewModel.sys.mjs"); "moz-src:///browser/components/genai/PageAssist.sys.mjs": typeof import("moz-src:///browser/components/genai/PageAssist.sys.mjs"); - "moz-src:///browser/components/genai/SmartAssistEngine.sys.mjs": typeof import("moz-src:///browser/components/genai/SmartAssistEngine.sys.mjs"); + "moz-src:///browser/components/ipprotection/GuardianClient.sys.mjs": typeof import("moz-src:///browser/components/ipprotection/GuardianClient.sys.mjs"); + "moz-src:///browser/components/ipprotection/IPPAutoRestore.sys.mjs": typeof import("moz-src:///browser/components/ipprotection/IPPAutoRestore.sys.mjs"); + "moz-src:///browser/components/ipprotection/IPPChannelFilter.sys.mjs": typeof import("moz-src:///browser/components/ipprotection/IPPChannelFilter.sys.mjs"); + "moz-src:///browser/components/ipprotection/IPPEnrollAndEntitleManager.sys.mjs": typeof import("moz-src:///browser/components/ipprotection/IPPEnrollAndEntitleManager.sys.mjs"); + "moz-src:///browser/components/ipprotection/IPPExceptionsManager.sys.mjs": typeof import("moz-src:///browser/components/ipprotection/IPPExceptionsManager.sys.mjs"); + "moz-src:///browser/components/ipprotection/IPPNetworkErrorObserver.sys.mjs": typeof import("moz-src:///browser/components/ipprotection/IPPNetworkErrorObserver.sys.mjs"); + "moz-src:///browser/components/ipprotection/IPPNetworkUtils.sys.mjs": typeof import("moz-src:///browser/components/ipprotection/IPPNetworkUtils.sys.mjs"); + "moz-src:///browser/components/ipprotection/IPPNimbusHelper.sys.mjs": typeof import("moz-src:///browser/components/ipprotection/IPPNimbusHelper.sys.mjs"); + "moz-src:///browser/components/ipprotection/IPPOnboardingMessageHelper.sys.mjs": typeof import("moz-src:///browser/components/ipprotection/IPPOnboardingMessageHelper.sys.mjs"); + "moz-src:///browser/components/ipprotection/IPPProxyManager.sys.mjs": typeof import("moz-src:///browser/components/ipprotection/IPPProxyManager.sys.mjs"); + "moz-src:///browser/components/ipprotection/IPPSignInWatcher.sys.mjs": typeof import("moz-src:///browser/components/ipprotection/IPPSignInWatcher.sys.mjs"); + "moz-src:///browser/components/ipprotection/IPPStartupCache.sys.mjs": typeof import("moz-src:///browser/components/ipprotection/IPPStartupCache.sys.mjs"); + "moz-src:///browser/components/ipprotection/IPProtection.sys.mjs": typeof import("moz-src:///browser/components/ipprotection/IPProtection.sys.mjs"); + "moz-src:///browser/components/ipprotection/IPProtectionAlertManager.sys.mjs": typeof import("moz-src:///browser/components/ipprotection/IPProtectionAlertManager.sys.mjs"); + "moz-src:///browser/components/ipprotection/IPProtectionHelpers.sys.mjs": typeof import("moz-src:///browser/components/ipprotection/IPProtectionHelpers.sys.mjs"); + "moz-src:///browser/components/ipprotection/IPProtectionInfobarManager.sys.mjs": typeof import("moz-src:///browser/components/ipprotection/IPProtectionInfobarManager.sys.mjs"); + "moz-src:///browser/components/ipprotection/IPProtectionPanel.sys.mjs": typeof import("moz-src:///browser/components/ipprotection/IPProtectionPanel.sys.mjs"); + "moz-src:///browser/components/ipprotection/IPProtectionServerlist.sys.mjs": typeof import("moz-src:///browser/components/ipprotection/IPProtectionServerlist.sys.mjs"); + "moz-src:///browser/components/ipprotection/IPProtectionService.sys.mjs": typeof import("moz-src:///browser/components/ipprotection/IPProtectionService.sys.mjs"); + "moz-src:///browser/components/ipprotection/IPProtectionToolbarButton.sys.mjs": typeof import("moz-src:///browser/components/ipprotection/IPProtectionToolbarButton.sys.mjs"); "moz-src:///browser/components/mozcachedohttp/MozCachedOHTTPProtocolHandler.sys.mjs": typeof import("moz-src:///browser/components/mozcachedohttp/MozCachedOHTTPProtocolHandler.sys.mjs"); + "moz-src:///browser/components/newtab/AboutNewTabComponents.sys.mjs": typeof import("moz-src:///browser/components/newtab/AboutNewTabComponents.sys.mjs"); "moz-src:///browser/components/newtab/SponsorProtection.sys.mjs": typeof import("moz-src:///browser/components/newtab/SponsorProtection.sys.mjs"); "moz-src:///browser/components/pagedata/OpenGraphPageData.sys.mjs": typeof import("moz-src:///browser/components/pagedata/OpenGraphPageData.sys.mjs"); "moz-src:///browser/components/pagedata/PageDataSchema.sys.mjs": typeof import("moz-src:///browser/components/pagedata/PageDataSchema.sys.mjs"); @@ -227,6 +308,8 @@ export interface Modules { "moz-src:///browser/components/places/PlacesUIUtils.sys.mjs": typeof import("moz-src:///browser/components/places/PlacesUIUtils.sys.mjs"); "moz-src:///browser/components/privatebrowsing/ResetPBMPanel.sys.mjs": typeof import("moz-src:///browser/components/privatebrowsing/ResetPBMPanel.sys.mjs"); "moz-src:///browser/components/protections/ContentBlockingPrefs.sys.mjs": typeof import("moz-src:///browser/components/protections/ContentBlockingPrefs.sys.mjs"); + "moz-src:///browser/components/qrcode/QRCodeGenerator.sys.mjs": typeof import("moz-src:///browser/components/qrcode/QRCodeGenerator.sys.mjs"); + "moz-src:///browser/components/qrcode/QRCodeWorker.sys.mjs": typeof import("moz-src:///browser/components/qrcode/QRCodeWorker.sys.mjs"); "moz-src:///browser/components/reportbrokensite/ReportBrokenSite.sys.mjs": typeof import("moz-src:///browser/components/reportbrokensite/ReportBrokenSite.sys.mjs"); "moz-src:///browser/components/search/BrowserSearchTelemetry.sys.mjs": typeof import("moz-src:///browser/components/search/BrowserSearchTelemetry.sys.mjs"); "moz-src:///browser/components/search/OpenSearchManager.sys.mjs": typeof import("moz-src:///browser/components/search/OpenSearchManager.sys.mjs"); @@ -248,6 +331,8 @@ export interface Modules { "moz-src:///browser/components/tabbrowser/TabMetrics.sys.mjs": typeof import("moz-src:///browser/components/tabbrowser/TabMetrics.sys.mjs"); "moz-src:///browser/components/tabbrowser/TabUnloader.sys.mjs": typeof import("moz-src:///browser/components/tabbrowser/TabUnloader.sys.mjs"); "moz-src:///browser/components/tabbrowser/TabsList.sys.mjs": typeof import("moz-src:///browser/components/tabbrowser/TabsList.sys.mjs"); + "moz-src:///browser/components/tabnotes/CanonicalURL.sys.mjs": typeof import("moz-src:///browser/components/tabnotes/CanonicalURL.sys.mjs"); + "moz-src:///browser/components/tabnotes/TabNotes.sys.mjs": typeof import("moz-src:///browser/components/tabnotes/TabNotes.sys.mjs"); "moz-src:///browser/components/uitour/UITour.sys.mjs": typeof import("moz-src:///browser/components/uitour/UITour.sys.mjs"); "moz-src:///browser/components/uitour/UITourUtils.sys.mjs": typeof import("moz-src:///browser/components/uitour/UITourUtils.sys.mjs"); "moz-src:///browser/components/urlbar/ActionsProviderContextualSearch.sys.mjs": typeof import("moz-src:///browser/components/urlbar/ActionsProviderContextualSearch.sys.mjs"); @@ -255,14 +340,18 @@ export interface Modules { "moz-src:///browser/components/urlbar/MerinoClient.sys.mjs": typeof import("moz-src:///browser/components/urlbar/MerinoClient.sys.mjs"); "moz-src:///browser/components/urlbar/QuickActionsLoaderDefault.sys.mjs": typeof import("moz-src:///browser/components/urlbar/QuickActionsLoaderDefault.sys.mjs"); "moz-src:///browser/components/urlbar/QuickSuggest.sys.mjs": typeof import("moz-src:///browser/components/urlbar/QuickSuggest.sys.mjs"); + "moz-src:///browser/components/urlbar/SearchModeSwitcher.sys.mjs": typeof import("moz-src:///browser/components/urlbar/SearchModeSwitcher.sys.mjs"); + "moz-src:///browser/components/urlbar/SmartbarMentionsPanelSearch.sys.mjs": typeof import("moz-src:///browser/components/urlbar/SmartbarMentionsPanelSearch.sys.mjs"); "moz-src:///browser/components/urlbar/UrlbarController.sys.mjs": typeof import("moz-src:///browser/components/urlbar/UrlbarController.sys.mjs"); "moz-src:///browser/components/urlbar/UrlbarEventBufferer.sys.mjs": typeof import("moz-src:///browser/components/urlbar/UrlbarEventBufferer.sys.mjs"); - "moz-src:///browser/components/urlbar/UrlbarInput.sys.mjs": typeof import("moz-src:///browser/components/urlbar/UrlbarInput.sys.mjs"); "moz-src:///browser/components/urlbar/UrlbarPrefs.sys.mjs": typeof import("moz-src:///browser/components/urlbar/UrlbarPrefs.sys.mjs"); + "moz-src:///browser/components/urlbar/UrlbarProviderAiChat.sys.mjs": typeof import("moz-src:///browser/components/urlbar/UrlbarProviderAiChat.sys.mjs"); "moz-src:///browser/components/urlbar/UrlbarProviderAutofill.sys.mjs": typeof import("moz-src:///browser/components/urlbar/UrlbarProviderAutofill.sys.mjs"); "moz-src:///browser/components/urlbar/UrlbarProviderCalculator.sys.mjs": typeof import("moz-src:///browser/components/urlbar/UrlbarProviderCalculator.sys.mjs"); "moz-src:///browser/components/urlbar/UrlbarProviderClipboard.sys.mjs": typeof import("moz-src:///browser/components/urlbar/UrlbarProviderClipboard.sys.mjs"); "moz-src:///browser/components/urlbar/UrlbarProviderGlobalActions.sys.mjs": typeof import("moz-src:///browser/components/urlbar/UrlbarProviderGlobalActions.sys.mjs"); + "moz-src:///browser/components/urlbar/UrlbarProviderHeuristicFallback.sys.mjs": typeof import("moz-src:///browser/components/urlbar/UrlbarProviderHeuristicFallback.sys.mjs"); + "moz-src:///browser/components/urlbar/UrlbarProviderInputHistory.sys.mjs": typeof import("moz-src:///browser/components/urlbar/UrlbarProviderInputHistory.sys.mjs"); "moz-src:///browser/components/urlbar/UrlbarProviderInterventions.sys.mjs": typeof import("moz-src:///browser/components/urlbar/UrlbarProviderInterventions.sys.mjs"); "moz-src:///browser/components/urlbar/UrlbarProviderOpenTabs.sys.mjs": typeof import("moz-src:///browser/components/urlbar/UrlbarProviderOpenTabs.sys.mjs"); "moz-src:///browser/components/urlbar/UrlbarProviderPlaces.sys.mjs": typeof import("moz-src:///browser/components/urlbar/UrlbarProviderPlaces.sys.mjs"); @@ -280,10 +369,12 @@ export interface Modules { "moz-src:///browser/components/urlbar/UrlbarSearchUtils.sys.mjs": typeof import("moz-src:///browser/components/urlbar/UrlbarSearchUtils.sys.mjs"); "moz-src:///browser/components/urlbar/UrlbarTokenizer.sys.mjs": typeof import("moz-src:///browser/components/urlbar/UrlbarTokenizer.sys.mjs"); "moz-src:///browser/components/urlbar/UrlbarUtils.sys.mjs": typeof import("moz-src:///browser/components/urlbar/UrlbarUtils.sys.mjs"); + "moz-src:///browser/components/urlbar/UrlbarValueFormatter.sys.mjs": typeof import("moz-src:///browser/components/urlbar/UrlbarValueFormatter.sys.mjs"); "moz-src:///browser/components/urlbar/UrlbarView.sys.mjs": typeof import("moz-src:///browser/components/urlbar/UrlbarView.sys.mjs"); "moz-src:///browser/components/urlbar/private/AmpSuggestions.sys.mjs": typeof import("moz-src:///browser/components/urlbar/private/AmpSuggestions.sys.mjs"); "moz-src:///browser/components/urlbar/private/GeolocationUtils.sys.mjs": typeof import("moz-src:///browser/components/urlbar/private/GeolocationUtils.sys.mjs"); "moz-src:///browser/components/urlbar/private/MLSuggest.sys.mjs": typeof import("moz-src:///browser/components/urlbar/private/MLSuggest.sys.mjs"); + "moz-src:///browser/components/urlbar/private/SportsSuggestions.sys.mjs": typeof import("moz-src:///browser/components/urlbar/private/SportsSuggestions.sys.mjs"); "moz-src:///browser/components/urlbar/private/SuggestBackendRust.sys.mjs": typeof import("moz-src:///browser/components/urlbar/private/SuggestBackendRust.sys.mjs"); "moz-src:///browser/modules/CanvasPermissionPromptHelper.sys.mjs": typeof import("moz-src:///browser/modules/CanvasPermissionPromptHelper.sys.mjs"); "moz-src:///browser/modules/ContextId.sys.mjs": typeof import("moz-src:///browser/modules/ContextId.sys.mjs"); @@ -296,11 +387,13 @@ export interface Modules { "moz-src:///services/crypto/modules/WeaveCrypto.sys.mjs": typeof import("moz-src:///services/crypto/modules/WeaveCrypto.sys.mjs"); "moz-src:///services/crypto/modules/jwcrypto.sys.mjs": typeof import("moz-src:///services/crypto/modules/jwcrypto.sys.mjs"); "moz-src:///services/crypto/modules/utils.sys.mjs": typeof import("moz-src:///services/crypto/modules/utils.sys.mjs"); + "moz-src:///third_party/js/qrcode/qrcode.mjs": typeof import("moz-src:///third_party/js/qrcode/qrcode.mjs"); "moz-src:///toolkit/components/doh/DoHConfig.sys.mjs": typeof import("moz-src:///toolkit/components/doh/DoHConfig.sys.mjs"); "moz-src:///toolkit/components/doh/DoHController.sys.mjs": typeof import("moz-src:///toolkit/components/doh/DoHController.sys.mjs"); "moz-src:///toolkit/components/doh/DoHHeuristics.sys.mjs": typeof import("moz-src:///toolkit/components/doh/DoHHeuristics.sys.mjs"); "moz-src:///toolkit/components/doh/TRRPerformance.sys.mjs": typeof import("moz-src:///toolkit/components/doh/TRRPerformance.sys.mjs"); "moz-src:///toolkit/components/pageextractor/DOMExtractor.sys.mjs": typeof import("moz-src:///toolkit/components/pageextractor/DOMExtractor.sys.mjs"); + "moz-src:///toolkit/components/qrcode/encoder.mjs": typeof import("moz-src:///toolkit/components/qrcode/encoder.mjs"); "moz-src:///toolkit/components/reader/AboutReader.sys.mjs": typeof import("moz-src:///toolkit/components/reader/AboutReader.sys.mjs"); "moz-src:///toolkit/components/reader/ReaderMode.sys.mjs": typeof import("moz-src:///toolkit/components/reader/ReaderMode.sys.mjs"); "moz-src:///toolkit/components/reader/ReaderWorker.sys.mjs": typeof import("moz-src:///toolkit/components/reader/ReaderWorker.sys.mjs"); @@ -311,6 +404,7 @@ export interface Modules { "moz-src:///toolkit/components/search/PolicySearchEngine.sys.mjs": typeof import("moz-src:///toolkit/components/search/PolicySearchEngine.sys.mjs"); "moz-src:///toolkit/components/search/SearchEngine.sys.mjs": typeof import("moz-src:///toolkit/components/search/SearchEngine.sys.mjs"); "moz-src:///toolkit/components/search/SearchEngineSelector.sys.mjs": typeof import("moz-src:///toolkit/components/search/SearchEngineSelector.sys.mjs"); + "moz-src:///toolkit/components/search/SearchService.sys.mjs": typeof import("moz-src:///toolkit/components/search/SearchService.sys.mjs"); "moz-src:///toolkit/components/search/SearchSettings.sys.mjs": typeof import("moz-src:///toolkit/components/search/SearchSettings.sys.mjs"); "moz-src:///toolkit/components/search/SearchShortcuts.sys.mjs": typeof import("moz-src:///toolkit/components/search/SearchShortcuts.sys.mjs"); "moz-src:///toolkit/components/search/SearchStaticData.sys.mjs": typeof import("moz-src:///toolkit/components/search/SearchStaticData.sys.mjs"); @@ -325,6 +419,9 @@ export interface Modules { "moz-src:///toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustWebextstorage.sys.mjs": typeof import("moz-src:///toolkit/components/uniffi-bindgen-gecko-js/components/generated/RustWebextstorage.sys.mjs"); "moz-src:///toolkit/components/uniffi-bindgen-gecko-js/tests/generated/RustUniffiBindingsTests.sys.mjs": typeof import("moz-src:///toolkit/components/uniffi-bindgen-gecko-js/tests/generated/RustUniffiBindingsTests.sys.mjs"); "moz-src:///toolkit/components/uniffi-bindgen-gecko-js/tests/generated/RustUniffiBindingsTestsExternalTypes.sys.mjs": typeof import("moz-src:///toolkit/components/uniffi-bindgen-gecko-js/tests/generated/RustUniffiBindingsTestsExternalTypes.sys.mjs"); + "moz-src:///toolkit/modules/ColorPickerPanel.sys.mjs": typeof import("moz-src:///toolkit/modules/ColorPickerPanel.sys.mjs"); + "moz-src:///toolkit/modules/DateTimePickerPanel.sys.mjs": typeof import("moz-src:///toolkit/modules/DateTimePickerPanel.sys.mjs"); + "moz-src:///toolkit/modules/FaviconUtils.sys.mjs": typeof import("moz-src:///toolkit/modules/FaviconUtils.sys.mjs"); "moz-src:///toolkit/modules/PrefUtils.sys.mjs": typeof import("moz-src:///toolkit/modules/PrefUtils.sys.mjs"); "moz-src:///toolkit/profile/ProfilesDatastoreService.sys.mjs": typeof import("moz-src:///toolkit/profile/ProfilesDatastoreService.sys.mjs"); "resource:///actors/AboutLoginsParent.sys.mjs": typeof import("resource:///actors/AboutLoginsParent.sys.mjs"); @@ -334,7 +431,6 @@ export interface Modules { "resource:///actors/AboutReaderParent.sys.mjs": typeof import("resource:///actors/AboutReaderParent.sys.mjs"); "resource:///actors/AboutWelcomeParent.sys.mjs": typeof import("resource:///actors/AboutWelcomeParent.sys.mjs"); "resource:///actors/ClickHandlerParent.sys.mjs": typeof import("resource:///actors/ClickHandlerParent.sys.mjs"); - "resource:///actors/ContentSearchParent.sys.mjs": typeof import("resource:///actors/ContentSearchParent.sys.mjs"); "resource:///actors/ContextMenuChild.sys.mjs": typeof import("resource:///actors/ContextMenuChild.sys.mjs"); "resource:///actors/LinkHandlerParent.sys.mjs": typeof import("resource:///actors/LinkHandlerParent.sys.mjs"); "resource:///actors/LinkPreviewChild.sys.mjs": typeof import("resource:///actors/LinkPreviewChild.sys.mjs"); @@ -342,29 +438,30 @@ export interface Modules { "resource:///actors/ScreenshotsComponentChild.sys.mjs": typeof import("resource:///actors/ScreenshotsComponentChild.sys.mjs"); "resource:///actors/SearchSERPTelemetryChild.sys.mjs": typeof import("resource:///actors/SearchSERPTelemetryChild.sys.mjs"); "resource:///actors/WebRTCChild.sys.mjs": typeof import("resource:///actors/WebRTCChild.sys.mjs"); - "resource:///bar.sys.mjs": typeof import("resource:///bar.sys.mjs"); "resource:///modules/360seMigrationUtils.sys.mjs": typeof import("resource:///modules/360seMigrationUtils.sys.mjs"); "resource:///modules/AboutHomeStartupCache.sys.mjs": typeof import("resource:///modules/AboutHomeStartupCache.sys.mjs"); "resource:///modules/AboutNewTab.sys.mjs": typeof import("resource:///modules/AboutNewTab.sys.mjs"); "resource:///modules/AboutNewTabRedirector.sys.mjs": typeof import("resource:///modules/AboutNewTabRedirector.sys.mjs"); "resource:///modules/AboutNewTabResourceMapping.sys.mjs": typeof import("resource:///modules/AboutNewTabResourceMapping.sys.mjs"); "resource:///modules/AccountsGlue.sys.mjs": typeof import("resource:///modules/AccountsGlue.sys.mjs"); + "resource:///modules/BrowserContentHandler.sys.mjs": typeof import("resource:///modules/BrowserContentHandler.sys.mjs"); "resource:///modules/BrowserGlue.sys.mjs": typeof import("resource:///modules/BrowserGlue.sys.mjs"); "resource:///modules/BrowserUIUtils.sys.mjs": typeof import("resource:///modules/BrowserUIUtils.sys.mjs"); "resource:///modules/BrowserUsageTelemetry.sys.mjs": typeof import("resource:///modules/BrowserUsageTelemetry.sys.mjs"); "resource:///modules/BrowserWindowTracker.sys.mjs": typeof import("resource:///modules/BrowserWindowTracker.sys.mjs"); "resource:///modules/BuiltInThemeConfig.sys.mjs": typeof import("resource:///modules/BuiltInThemeConfig.sys.mjs"); "resource:///modules/BuiltInThemes.sys.mjs": typeof import("resource:///modules/BuiltInThemes.sys.mjs"); + "resource:///modules/ChangePasswordURLs.sys.mjs": typeof import("resource:///modules/ChangePasswordURLs.sys.mjs"); + "resource:///modules/ChatsController.sys.mjs": typeof import("resource:///modules/ChatsController.sys.mjs"); "resource:///modules/ChromeMacOSLoginCrypto.sys.mjs": typeof import("resource:///modules/ChromeMacOSLoginCrypto.sys.mjs"); "resource:///modules/ChromeMigrationUtils.sys.mjs": typeof import("resource:///modules/ChromeMigrationUtils.sys.mjs"); "resource:///modules/ChromeProfileMigrator.sys.mjs": typeof import("resource:///modules/ChromeProfileMigrator.sys.mjs"); "resource:///modules/ChromeWindowsLoginCrypto.sys.mjs": typeof import("resource:///modules/ChromeWindowsLoginCrypto.sys.mjs"); "resource:///modules/ContentCrashHandlers.sys.mjs": typeof import("resource:///modules/ContentCrashHandlers.sys.mjs"); + "resource:///modules/CustomKeys.sys.mjs": typeof import("resource:///modules/CustomKeys.sys.mjs"); "resource:///modules/Dedupe.sys.mjs": typeof import("resource:///modules/Dedupe.sys.mjs"); "resource:///modules/DevToolsStartup.sys.mjs": typeof import("resource:///modules/DevToolsStartup.sys.mjs"); "resource:///modules/Discovery.sys.mjs": typeof import("resource:///modules/Discovery.sys.mjs"); - "resource:///modules/DownloadsCommon.sys.mjs": typeof import("resource:///modules/DownloadsCommon.sys.mjs"); - "resource:///modules/DownloadsViewUI.sys.mjs": typeof import("resource:///modules/DownloadsViewUI.sys.mjs"); "resource:///modules/ESEDBReader.sys.mjs": typeof import("resource:///modules/ESEDBReader.sys.mjs"); "resource:///modules/EdgeProfileMigrator.sys.mjs": typeof import("resource:///modules/EdgeProfileMigrator.sys.mjs"); "resource:///modules/EveryWindow.sys.mjs": typeof import("resource:///modules/EveryWindow.sys.mjs"); @@ -391,6 +488,7 @@ export interface Modules { "resource:///modules/MigrationUtils.sys.mjs": typeof import("resource:///modules/MigrationUtils.sys.mjs"); "resource:///modules/MigratorBase.sys.mjs": typeof import("resource:///modules/MigratorBase.sys.mjs"); "resource:///modules/OpenTabs.sys.mjs": typeof import("resource:///modules/OpenTabs.sys.mjs"); + "resource:///modules/OpenTabsController.sys.mjs": typeof import("resource:///modules/OpenTabsController.sys.mjs"); "resource:///modules/PageActions.sys.mjs": typeof import("resource:///modules/PageActions.sys.mjs"); "resource:///modules/PartnerLinkAttribution.sys.mjs": typeof import("resource:///modules/PartnerLinkAttribution.sys.mjs"); "resource:///modules/PermissionUI.sys.mjs": typeof import("resource:///modules/PermissionUI.sys.mjs"); @@ -409,7 +507,6 @@ export interface Modules { "resource:///modules/ThemeVariableMap.sys.mjs": typeof import("resource:///modules/ThemeVariableMap.sys.mjs"); "resource:///modules/TransientPrefs.sys.mjs": typeof import("resource:///modules/TransientPrefs.sys.mjs"); "resource:///modules/URILoadingHelper.sys.mjs": typeof import("resource:///modules/URILoadingHelper.sys.mjs"); - "resource:///modules/UrlbarUtils.sys.mjs": typeof import("resource:///modules/UrlbarUtils.sys.mjs"); "resource:///modules/WebProtocolHandlerRegistrar.sys.mjs": typeof import("resource:///modules/WebProtocolHandlerRegistrar.sys.mjs"); "resource:///modules/WindowsJumpLists.sys.mjs": typeof import("resource:///modules/WindowsJumpLists.sys.mjs"); "resource:///modules/WindowsPreviewPerTab.sys.mjs": typeof import("resource:///modules/WindowsPreviewPerTab.sys.mjs"); @@ -418,6 +515,7 @@ export interface Modules { "resource:///modules/aboutwelcome/AWToolbarUtils.sys.mjs": typeof import("resource:///modules/aboutwelcome/AWToolbarUtils.sys.mjs"); "resource:///modules/aboutwelcome/AboutWelcomeDefaults.sys.mjs": typeof import("resource:///modules/aboutwelcome/AboutWelcomeDefaults.sys.mjs"); "resource:///modules/aboutwelcome/AboutWelcomeTelemetry.sys.mjs": typeof import("resource:///modules/aboutwelcome/AboutWelcomeTelemetry.sys.mjs"); + "resource:///modules/aiwindow/ui/modules/ChatStore.sys.mjs": typeof import("resource:///modules/aiwindow/ui/modules/ChatStore.sys.mjs"); "resource:///modules/asrouter/ASRouter.sys.mjs": typeof import("resource:///modules/asrouter/ASRouter.sys.mjs"); "resource:///modules/asrouter/ASRouterDefaultConfig.sys.mjs": typeof import("resource:///modules/asrouter/ASRouterDefaultConfig.sys.mjs"); "resource:///modules/asrouter/ASRouterNewTabHook.sys.mjs": typeof import("resource:///modules/asrouter/ASRouterNewTabHook.sys.mjs"); @@ -450,6 +548,7 @@ export interface Modules { "resource:///modules/backup/BackupError.mjs": typeof import("resource:///modules/backup/BackupError.mjs"); "resource:///modules/backup/BackupResource.sys.mjs": typeof import("resource:///modules/backup/BackupResource.sys.mjs"); "resource:///modules/backup/BackupService.sys.mjs": typeof import("resource:///modules/backup/BackupService.sys.mjs"); + "resource:///modules/backup/BookmarksBackupResource.sys.mjs": typeof import("resource:///modules/backup/BookmarksBackupResource.sys.mjs"); "resource:///modules/backup/CookiesBackupResource.sys.mjs": typeof import("resource:///modules/backup/CookiesBackupResource.sys.mjs"); "resource:///modules/backup/CredentialsAndSecurityBackupResource.sys.mjs": typeof import("resource:///modules/backup/CredentialsAndSecurityBackupResource.sys.mjs"); "resource:///modules/backup/FormHistoryBackupResource.sys.mjs": typeof import("resource:///modules/backup/FormHistoryBackupResource.sys.mjs"); @@ -457,24 +556,12 @@ export interface Modules { "resource:///modules/backup/MiscDataBackupResource.sys.mjs": typeof import("resource:///modules/backup/MiscDataBackupResource.sys.mjs"); "resource:///modules/backup/PlacesBackupResource.sys.mjs": typeof import("resource:///modules/backup/PlacesBackupResource.sys.mjs"); "resource:///modules/backup/PreferencesBackupResource.sys.mjs": typeof import("resource:///modules/backup/PreferencesBackupResource.sys.mjs"); + "resource:///modules/backup/SelectableProfileBackupResource.sys.mjs": typeof import("resource:///modules/backup/SelectableProfileBackupResource.sys.mjs"); "resource:///modules/backup/SessionStoreBackupResource.sys.mjs": typeof import("resource:///modules/backup/SessionStoreBackupResource.sys.mjs"); + "resource:///modules/backup/SiteSettingsBackupResource.sys.mjs": typeof import("resource:///modules/backup/SiteSettingsBackupResource.sys.mjs"); "resource:///modules/distribution.sys.mjs": typeof import("resource:///modules/distribution.sys.mjs"); "resource:///modules/firefox-view-synced-tabs-error-handler.sys.mjs": typeof import("resource:///modules/firefox-view-synced-tabs-error-handler.sys.mjs"); "resource:///modules/firefox-view-tabs-setup-manager.sys.mjs": typeof import("resource:///modules/firefox-view-tabs-setup-manager.sys.mjs"); - "resource:///modules/ipprotection/GuardianClient.sys.mjs": typeof import("resource:///modules/ipprotection/GuardianClient.sys.mjs"); - "resource:///modules/ipprotection/IPPChannelFilter.sys.mjs": typeof import("resource:///modules/ipprotection/IPPChannelFilter.sys.mjs"); - "resource:///modules/ipprotection/IPPExceptionsManager.sys.mjs": typeof import("resource:///modules/ipprotection/IPPExceptionsManager.sys.mjs"); - "resource:///modules/ipprotection/IPPNetworkErrorObserver.sys.mjs": typeof import("resource:///modules/ipprotection/IPPNetworkErrorObserver.sys.mjs"); - "resource:///modules/ipprotection/IPPNimbusHelper.sys.mjs": typeof import("resource:///modules/ipprotection/IPPNimbusHelper.sys.mjs"); - "resource:///modules/ipprotection/IPPProxyManager.sys.mjs": typeof import("resource:///modules/ipprotection/IPPProxyManager.sys.mjs"); - "resource:///modules/ipprotection/IPPSignInWatcher.sys.mjs": typeof import("resource:///modules/ipprotection/IPPSignInWatcher.sys.mjs"); - "resource:///modules/ipprotection/IPPStartupCache.sys.mjs": typeof import("resource:///modules/ipprotection/IPPStartupCache.sys.mjs"); - "resource:///modules/ipprotection/IPProtection.sys.mjs": typeof import("resource:///modules/ipprotection/IPProtection.sys.mjs"); - "resource:///modules/ipprotection/IPProtectionHelpers.sys.mjs": typeof import("resource:///modules/ipprotection/IPProtectionHelpers.sys.mjs"); - "resource:///modules/ipprotection/IPProtectionPanel.sys.mjs": typeof import("resource:///modules/ipprotection/IPProtectionPanel.sys.mjs"); - "resource:///modules/ipprotection/IPProtectionServerlist.sys.mjs": typeof import("resource:///modules/ipprotection/IPProtectionServerlist.sys.mjs"); - "resource:///modules/ipprotection/IPProtectionService.sys.mjs": typeof import("resource:///modules/ipprotection/IPProtectionService.sys.mjs"); - "resource:///modules/ipprotection/IPProtectionUsage.sys.mjs": typeof import("resource:///modules/ipprotection/IPProtectionUsage.sys.mjs"); "resource:///modules/policies/BookmarksPolicies.sys.mjs": typeof import("resource:///modules/policies/BookmarksPolicies.sys.mjs"); "resource:///modules/policies/Policies.sys.mjs": typeof import("resource:///modules/policies/Policies.sys.mjs"); "resource:///modules/policies/ProxyPolicies.sys.mjs": typeof import("resource:///modules/policies/ProxyPolicies.sys.mjs"); @@ -515,6 +602,7 @@ export interface Modules { "resource:///modules/topsites/TippyTopProvider.sys.mjs": typeof import("resource:///modules/topsites/TippyTopProvider.sys.mjs"); "resource:///modules/topsites/TopSites.sys.mjs": typeof import("resource:///modules/topsites/TopSites.sys.mjs"); "resource:///modules/topsites/constants.mjs": typeof import("resource:///modules/topsites/constants.mjs"); + "resource:///modules/urlbar/private/GeolocationUtils.sys.mjs": typeof import("resource:///modules/urlbar/private/GeolocationUtils.sys.mjs"); "resource:///modules/webrtcUI.sys.mjs": typeof import("resource:///modules/webrtcUI.sys.mjs"); "resource://autofill/FormAutofill.sys.mjs": typeof import("resource://autofill/FormAutofill.sys.mjs"); "resource://autofill/FormAutofillChild.ios.sys.mjs": typeof import("resource://autofill/FormAutofillChild.ios.sys.mjs"); @@ -528,6 +616,7 @@ export interface Modules { "resource://autofill/FormAutofillSync.sys.mjs": typeof import("resource://autofill/FormAutofillSync.sys.mjs"); "resource://autofill/ProfileAutoCompleteResult.sys.mjs": typeof import("resource://autofill/ProfileAutoCompleteResult.sys.mjs"); "resource://devtools/client/framework/browser-toolbox/Launcher.sys.mjs": typeof import("resource://devtools/client/framework/browser-toolbox/Launcher.sys.mjs"); + "resource://devtools/client/jsonview/json-size-profiler.mjs": typeof import("resource://devtools/client/jsonview/json-size-profiler.mjs"); "resource://devtools/client/performance-new/popup/logic.sys.mjs": typeof import("resource://devtools/client/performance-new/popup/logic.sys.mjs"); "resource://devtools/client/performance-new/popup/menu-button.sys.mjs": typeof import("resource://devtools/client/performance-new/popup/menu-button.sys.mjs"); "resource://devtools/client/performance-new/shared/background.sys.mjs": typeof import("resource://devtools/client/performance-new/shared/background.sys.mjs"); @@ -576,6 +665,7 @@ export interface Modules { "resource://devtools/shared/loader/Loader.sys.mjs": typeof import("resource://devtools/shared/loader/Loader.sys.mjs"); "resource://devtools/shared/loader/browser-loader.sys.mjs": typeof import("resource://devtools/shared/loader/browser-loader.sys.mjs"); "resource://devtools/shared/loader/worker-loader.sys.mjs": typeof import("resource://devtools/shared/loader/worker-loader.sys.mjs"); + "resource://devtools/shared/mdn.mjs": typeof import("resource://devtools/shared/mdn.mjs"); "resource://devtools/shared/network-observer/ChannelMap.sys.mjs": typeof import("resource://devtools/shared/network-observer/ChannelMap.sys.mjs"); "resource://devtools/shared/network-observer/NetworkAuthListener.sys.mjs": typeof import("resource://devtools/shared/network-observer/NetworkAuthListener.sys.mjs"); "resource://devtools/shared/network-observer/NetworkHelper.sys.mjs": typeof import("resource://devtools/shared/network-observer/NetworkHelper.sys.mjs"); @@ -599,6 +689,7 @@ export interface Modules { "resource://gre/actors/AutoCompleteParent.sys.mjs": typeof import("resource://gre/actors/AutoCompleteParent.sys.mjs"); "resource://gre/actors/FormHandlerChild.sys.mjs": typeof import("resource://gre/actors/FormHandlerChild.sys.mjs"); "resource://gre/actors/MLEngineParent.sys.mjs": typeof import("resource://gre/actors/MLEngineParent.sys.mjs"); + "resource://gre/actors/PageExtractorParent.sys.mjs": typeof import("resource://gre/actors/PageExtractorParent.sys.mjs"); "resource://gre/actors/PictureInPictureChild.sys.mjs": typeof import("resource://gre/actors/PictureInPictureChild.sys.mjs"); "resource://gre/actors/PopupAndRedirectBlockingParent.sys.mjs": typeof import("resource://gre/actors/PopupAndRedirectBlockingParent.sys.mjs"); "resource://gre/actors/SelectChild.sys.mjs": typeof import("resource://gre/actors/SelectChild.sys.mjs"); @@ -632,6 +723,7 @@ export interface Modules { "resource://gre/modules/BrowserUtils.sys.mjs": typeof import("resource://gre/modules/BrowserUtils.sys.mjs"); "resource://gre/modules/CSV.sys.mjs": typeof import("resource://gre/modules/CSV.sys.mjs"); "resource://gre/modules/CanonicalJSON.sys.mjs": typeof import("resource://gre/modules/CanonicalJSON.sys.mjs"); + "resource://gre/modules/CanvasHashData.sys.mjs": typeof import("resource://gre/modules/CanvasHashData.sys.mjs"); "resource://gre/modules/CaptchaDetectionPingUtils.sys.mjs": typeof import("resource://gre/modules/CaptchaDetectionPingUtils.sys.mjs"); "resource://gre/modules/CaptchaResponseObserver.sys.mjs": typeof import("resource://gre/modules/CaptchaResponseObserver.sys.mjs"); "resource://gre/modules/CertUtils.sys.mjs": typeof import("resource://gre/modules/CertUtils.sys.mjs"); @@ -663,9 +755,9 @@ export interface Modules { "resource://gre/modules/CustomElementsListener.sys.mjs": typeof import("resource://gre/modules/CustomElementsListener.sys.mjs"); "resource://gre/modules/DAPIncrementality.sys.mjs": typeof import("resource://gre/modules/DAPIncrementality.sys.mjs"); "resource://gre/modules/DAPReportController.sys.mjs": typeof import("resource://gre/modules/DAPReportController.sys.mjs"); + "resource://gre/modules/DAPSender.sys.mjs": typeof import("resource://gre/modules/DAPSender.sys.mjs"); "resource://gre/modules/DAPTelemetrySender.sys.mjs": typeof import("resource://gre/modules/DAPTelemetrySender.sys.mjs"); "resource://gre/modules/DAPVisitCounter.sys.mjs": typeof import("resource://gre/modules/DAPVisitCounter.sys.mjs"); - "resource://gre/modules/DateTimePickerPanel.sys.mjs": typeof import("resource://gre/modules/DateTimePickerPanel.sys.mjs"); "resource://gre/modules/DeferredTask.sys.mjs": typeof import("resource://gre/modules/DeferredTask.sys.mjs"); "resource://gre/modules/DelayedInit.sys.mjs": typeof import("resource://gre/modules/DelayedInit.sys.mjs"); "resource://gre/modules/DownloadCore.sys.mjs": typeof import("resource://gre/modules/DownloadCore.sys.mjs"); @@ -859,12 +951,12 @@ export interface Modules { "resource://gre/modules/SafeBrowsing.sys.mjs": typeof import("resource://gre/modules/SafeBrowsing.sys.mjs"); "resource://gre/modules/SandboxUtils.sys.mjs": typeof import("resource://gre/modules/SandboxUtils.sys.mjs"); "resource://gre/modules/Schemas.sys.mjs": typeof import("resource://gre/modules/Schemas.sys.mjs"); - "resource://gre/modules/SearchService.sys.mjs": typeof import("resource://gre/modules/SearchService.sys.mjs"); "resource://gre/modules/SecurityInfo.sys.mjs": typeof import("resource://gre/modules/SecurityInfo.sys.mjs"); "resource://gre/modules/SelectionUtils.sys.mjs": typeof import("resource://gre/modules/SelectionUtils.sys.mjs"); "resource://gre/modules/ServiceRequest.sys.mjs": typeof import("resource://gre/modules/ServiceRequest.sys.mjs"); "resource://gre/modules/ServiceWorkerCleanUp.sys.mjs": typeof import("resource://gre/modules/ServiceWorkerCleanUp.sys.mjs"); "resource://gre/modules/ShortcutUtils.sys.mjs": typeof import("resource://gre/modules/ShortcutUtils.sys.mjs"); + "resource://gre/modules/SitePolicyUtils.sys.mjs": typeof import("resource://gre/modules/SitePolicyUtils.sys.mjs"); "resource://gre/modules/Sqlite.sys.mjs": typeof import("resource://gre/modules/Sqlite.sys.mjs"); "resource://gre/modules/SubDialog.sys.mjs": typeof import("resource://gre/modules/SubDialog.sys.mjs"); "resource://gre/modules/Subprocess.sys.mjs": typeof import("resource://gre/modules/Subprocess.sys.mjs"); @@ -897,6 +989,7 @@ export interface Modules { "resource://gre/modules/UrlClassifierRemoteSettingsService.sys.mjs": typeof import("resource://gre/modules/UrlClassifierRemoteSettingsService.sys.mjs"); "resource://gre/modules/UrlUtils.sys.mjs": typeof import("resource://gre/modules/UrlUtils.sys.mjs"); "resource://gre/modules/UsageReporting.sys.mjs": typeof import("resource://gre/modules/UsageReporting.sys.mjs"); + "resource://gre/modules/UserCharacteristicsPageService.sys.mjs": typeof import("resource://gre/modules/UserCharacteristicsPageService.sys.mjs"); "resource://gre/modules/WPTEventsParent.sys.mjs": typeof import("resource://gre/modules/WPTEventsParent.sys.mjs"); "resource://gre/modules/WebAuthnFeature.sys.mjs": typeof import("resource://gre/modules/WebAuthnFeature.sys.mjs"); "resource://gre/modules/WebChannel.sys.mjs": typeof import("resource://gre/modules/WebChannel.sys.mjs"); @@ -943,6 +1036,7 @@ export interface Modules { "resource://gre/modules/narrate/NarrateControls.sys.mjs": typeof import("resource://gre/modules/narrate/NarrateControls.sys.mjs"); "resource://gre/modules/policies/WindowsGPOParser.sys.mjs": typeof import("resource://gre/modules/policies/WindowsGPOParser.sys.mjs"); "resource://gre/modules/policies/macOSPoliciesParser.sys.mjs": typeof import("resource://gre/modules/policies/macOSPoliciesParser.sys.mjs"); + "resource://gre/modules/psm/QWACs.sys.mjs": typeof import("resource://gre/modules/psm/QWACs.sys.mjs"); "resource://gre/modules/psm/RemoteSecuritySettings.sys.mjs": typeof import("resource://gre/modules/psm/RemoteSecuritySettings.sys.mjs"); "resource://gre/modules/psm/X509.sys.mjs": typeof import("resource://gre/modules/psm/X509.sys.mjs"); "resource://gre/modules/psm/pippki.sys.mjs": typeof import("resource://gre/modules/psm/pippki.sys.mjs"); @@ -999,11 +1093,13 @@ export interface Modules { "resource://newtab/lib/DefaultSites.sys.mjs": typeof import("resource://newtab/lib/DefaultSites.sys.mjs"); "resource://newtab/lib/DiscoveryStreamFeed.sys.mjs": typeof import("resource://newtab/lib/DiscoveryStreamFeed.sys.mjs"); "resource://newtab/lib/DownloadsManager.sys.mjs": typeof import("resource://newtab/lib/DownloadsManager.sys.mjs"); + "resource://newtab/lib/ExternalComponentsFeed.sys.mjs": typeof import("resource://newtab/lib/ExternalComponentsFeed.sys.mjs"); "resource://newtab/lib/FaviconFeed.sys.mjs": typeof import("resource://newtab/lib/FaviconFeed.sys.mjs"); "resource://newtab/lib/HighlightsFeed.sys.mjs": typeof import("resource://newtab/lib/HighlightsFeed.sys.mjs"); "resource://newtab/lib/InferredModel/FeatureModel.sys.mjs": typeof import("resource://newtab/lib/InferredModel/FeatureModel.sys.mjs"); "resource://newtab/lib/InferredModel/GreedyContentRanker.mjs": typeof import("resource://newtab/lib/InferredModel/GreedyContentRanker.mjs"); "resource://newtab/lib/InferredPersonalizationFeed.sys.mjs": typeof import("resource://newtab/lib/InferredPersonalizationFeed.sys.mjs"); + "resource://newtab/lib/NewTabActorRegistry.sys.mjs": typeof import("resource://newtab/lib/NewTabActorRegistry.sys.mjs"); "resource://newtab/lib/NewTabAttributionFeed.sys.mjs": typeof import("resource://newtab/lib/NewTabAttributionFeed.sys.mjs"); "resource://newtab/lib/NewTabAttributionService.sys.mjs": typeof import("resource://newtab/lib/NewTabAttributionService.sys.mjs"); "resource://newtab/lib/NewTabContentPing.sys.mjs": typeof import("resource://newtab/lib/NewTabContentPing.sys.mjs"); @@ -1011,11 +1107,10 @@ export interface Modules { "resource://newtab/lib/NewTabInit.sys.mjs": typeof import("resource://newtab/lib/NewTabInit.sys.mjs"); "resource://newtab/lib/NewTabMessaging.sys.mjs": typeof import("resource://newtab/lib/NewTabMessaging.sys.mjs"); "resource://newtab/lib/PersistentCache.sys.mjs": typeof import("resource://newtab/lib/PersistentCache.sys.mjs"); - "resource://newtab/lib/PersonalityProvider/PersonalityProvider.sys.mjs": typeof import("resource://newtab/lib/PersonalityProvider/PersonalityProvider.sys.mjs"); "resource://newtab/lib/PlacesFeed.sys.mjs": typeof import("resource://newtab/lib/PlacesFeed.sys.mjs"); "resource://newtab/lib/PrefsFeed.sys.mjs": typeof import("resource://newtab/lib/PrefsFeed.sys.mjs"); - "resource://newtab/lib/RecommendationProvider.sys.mjs": typeof import("resource://newtab/lib/RecommendationProvider.sys.mjs"); "resource://newtab/lib/Screenshots.sys.mjs": typeof import("resource://newtab/lib/Screenshots.sys.mjs"); + "resource://newtab/lib/SectionsLayoutFeed.sys.mjs": typeof import("resource://newtab/lib/SectionsLayoutFeed.sys.mjs"); "resource://newtab/lib/SectionsManager.sys.mjs": typeof import("resource://newtab/lib/SectionsManager.sys.mjs"); "resource://newtab/lib/SmartShortcutsFeed.sys.mjs": typeof import("resource://newtab/lib/SmartShortcutsFeed.sys.mjs"); "resource://newtab/lib/SmartShortcutsRanker/RankShortcuts.mjs": typeof import("resource://newtab/lib/SmartShortcutsRanker/RankShortcuts.mjs"); @@ -1025,14 +1120,15 @@ export interface Modules { "resource://newtab/lib/Store.sys.mjs": typeof import("resource://newtab/lib/Store.sys.mjs"); "resource://newtab/lib/SystemTickFeed.sys.mjs": typeof import("resource://newtab/lib/SystemTickFeed.sys.mjs"); "resource://newtab/lib/TelemetryFeed.sys.mjs": typeof import("resource://newtab/lib/TelemetryFeed.sys.mjs"); + "resource://newtab/lib/TemporaryMerinoClientShim.sys.mjs": typeof import("resource://newtab/lib/TemporaryMerinoClientShim.sys.mjs"); "resource://newtab/lib/TopSitesFeed.sys.mjs": typeof import("resource://newtab/lib/TopSitesFeed.sys.mjs"); "resource://newtab/lib/TopStoriesFeed.sys.mjs": typeof import("resource://newtab/lib/TopStoriesFeed.sys.mjs"); - "resource://newtab/lib/TrendingSearchFeed.sys.mjs": typeof import("resource://newtab/lib/TrendingSearchFeed.sys.mjs"); "resource://newtab/lib/UTEventReporting.sys.mjs": typeof import("resource://newtab/lib/UTEventReporting.sys.mjs"); "resource://newtab/lib/Wallpapers/WallpaperFeed.sys.mjs": typeof import("resource://newtab/lib/Wallpapers/WallpaperFeed.sys.mjs"); "resource://newtab/lib/WeatherFeed.sys.mjs": typeof import("resource://newtab/lib/WeatherFeed.sys.mjs"); "resource://newtab/lib/Widgets/ListsFeed.sys.mjs": typeof import("resource://newtab/lib/Widgets/ListsFeed.sys.mjs"); "resource://newtab/lib/Widgets/TimerFeed.sys.mjs": typeof import("resource://newtab/lib/Widgets/TimerFeed.sys.mjs"); + "resource://newtab/lib/actors/NewTabAttributionParent.sys.mjs": typeof import("resource://newtab/lib/actors/NewTabAttributionParent.sys.mjs"); "resource://nimbus/ExperimentAPI.sys.mjs": typeof import("resource://nimbus/ExperimentAPI.sys.mjs"); "resource://nimbus/FeatureManifest.sys.mjs": typeof import("resource://nimbus/FeatureManifest.sys.mjs"); "resource://nimbus/FirefoxLabs.sys.mjs": typeof import("resource://nimbus/FirefoxLabs.sys.mjs"); @@ -1076,8 +1172,8 @@ export interface Modules { "resource://normandy/lib/ShieldPreferences.sys.mjs": typeof import("resource://normandy/lib/ShieldPreferences.sys.mjs"); "resource://normandy/lib/Storage.sys.mjs": typeof import("resource://normandy/lib/Storage.sys.mjs"); "resource://normandy/lib/TelemetryEvents.sys.mjs": typeof import("resource://normandy/lib/TelemetryEvents.sys.mjs"); - "resource://normandy/lib/Uptake.sys.mjs": typeof import("resource://normandy/lib/Uptake.sys.mjs"); "resource://pdf.js/PdfJs.sys.mjs": typeof import("resource://pdf.js/PdfJs.sys.mjs"); + "resource://pdf.js/PdfJsAIFeature.sys.mjs": typeof import("resource://pdf.js/PdfJsAIFeature.sys.mjs"); "resource://pdf.js/PdfJsNetwork.sys.mjs": typeof import("resource://pdf.js/PdfJsNetwork.sys.mjs"); "resource://pdf.js/PdfJsTelemetry.sys.mjs": typeof import("resource://pdf.js/PdfJsTelemetry.sys.mjs"); "resource://pdf.js/PdfSandbox.sys.mjs": typeof import("resource://pdf.js/PdfSandbox.sys.mjs"); @@ -1146,6 +1242,7 @@ export interface Modules { "resource://test/TestRunner.sys.mjs": typeof import("resource://test/TestRunner.sys.mjs"); "resource://test/broadcast_handler.sys.mjs": typeof import("resource://test/broadcast_handler.sys.mjs"); "resource://test/contextual.sys.mjs": typeof import("resource://test/contextual.sys.mjs"); + "resource://test/error_twice.mjs": typeof import("resource://test/error_twice.mjs"); "resource://test/es6module.js": typeof import("resource://test/es6module.js"); "resource://test/es6module_absolute.js": typeof import("resource://test/es6module_absolute.js"); "resource://test/es6module_absolute2.js": typeof import("resource://test/es6module_absolute2.js"); @@ -1178,6 +1275,7 @@ export interface Modules { "resource://test/non_shared_nest_import_shared_target_1.sys.mjs": typeof import("resource://test/non_shared_nest_import_shared_target_1.sys.mjs"); "resource://test/non_shared_nest_import_shared_target_2.sys.mjs": typeof import("resource://test/non_shared_nest_import_shared_target_2.sys.mjs"); "resource://test/not_found.mjs": typeof import("resource://test/not_found.mjs"); + "resource://testing-common/AIWindowTestUtils.sys.mjs": typeof import("resource://testing-common/AIWindowTestUtils.sys.mjs"); "resource://testing-common/AddonTestUtils.sys.mjs": typeof import("resource://testing-common/AddonTestUtils.sys.mjs"); "resource://testing-common/AllJavascriptTypes.mjs": typeof import("resource://testing-common/AllJavascriptTypes.mjs"); "resource://testing-common/AppData.sys.mjs": typeof import("resource://testing-common/AppData.sys.mjs"); @@ -1190,7 +1288,6 @@ export interface Modules { "resource://testing-common/ContentTask.sys.mjs": typeof import("resource://testing-common/ContentTask.sys.mjs"); "resource://testing-common/ContentTaskUtils.sys.mjs": typeof import("resource://testing-common/ContentTaskUtils.sys.mjs"); "resource://testing-common/CookieXPCShellUtils.sys.mjs": typeof import("resource://testing-common/CookieXPCShellUtils.sys.mjs"); - "resource://testing-common/CoverageUtils.sys.mjs": typeof import("resource://testing-common/CoverageUtils.sys.mjs"); "resource://testing-common/CrashManagerTest.sys.mjs": typeof import("resource://testing-common/CrashManagerTest.sys.mjs"); "resource://testing-common/CustomizableUITestUtils.sys.mjs": typeof import("resource://testing-common/CustomizableUITestUtils.sys.mjs"); "resource://testing-common/DOMFullscreenTestUtils.sys.mjs": typeof import("resource://testing-common/DOMFullscreenTestUtils.sys.mjs"); @@ -1201,7 +1298,9 @@ export interface Modules { "resource://testing-common/FileTestUtils.sys.mjs": typeof import("resource://testing-common/FileTestUtils.sys.mjs"); "resource://testing-common/FirefoxViewTestUtils.sys.mjs": typeof import("resource://testing-common/FirefoxViewTestUtils.sys.mjs"); "resource://testing-common/FormHistoryTestUtils.sys.mjs": typeof import("resource://testing-common/FormHistoryTestUtils.sys.mjs"); + "resource://testing-common/GeolocationTestUtils.sys.mjs": typeof import("resource://testing-common/GeolocationTestUtils.sys.mjs"); "resource://testing-common/HandlerServiceTestUtils.sys.mjs": typeof import("resource://testing-common/HandlerServiceTestUtils.sys.mjs"); + "resource://testing-common/ImageTestUtils.sys.mjs": typeof import("resource://testing-common/ImageTestUtils.sys.mjs"); "resource://testing-common/InflightAssetsMessageProvider.sys.mjs": typeof import("resource://testing-common/InflightAssetsMessageProvider.sys.mjs"); "resource://testing-common/JSObjectsTestUtils.sys.mjs": typeof import("resource://testing-common/JSObjectsTestUtils.sys.mjs"); "resource://testing-common/LangPackMatcherTestUtils.sys.mjs": typeof import("resource://testing-common/LangPackMatcherTestUtils.sys.mjs"); diff --git a/src/zen/@types/lib.gecko.nsresult.d.ts b/src/zen/@types/lib.gecko.nsresult.d.ts index 09ce3bd26..1d79a757b 100644 --- a/src/zen/@types/lib.gecko.nsresult.d.ts +++ b/src/zen/@types/lib.gecko.nsresult.d.ts @@ -368,9 +368,6 @@ interface nsIXPCComponents_Results { /** The connection was refused */ NS_ERROR_CONNECTION_REFUSED: 0x804b000d; - /** User refused navigation to potentially unsafe URL with embedded credentials/superfluos authentication */ - NS_ERROR_SUPERFLUOS_AUTH: 0x804b005b; - /** User attempted basic HTTP authentication when it is disabled */ NS_ERROR_BASIC_HTTP_AUTH_DISABLED: 0x804b005c; @@ -674,6 +671,9 @@ interface nsIXPCComponents_Results { /** The URI is email tracking */ NS_ERROR_EMAILTRACKING_URI: 0x805d002c; + /** The URI is not available for add-ons */ + NS_ERROR_HARMFULADDON_URI: 0x805d002e; + // Profile manager error codes /** Flushing the profiles to disk would have overwritten changes made elsewhere. */ diff --git a/src/zen/@types/lib.gecko.services.d.ts b/src/zen/@types/lib.gecko.services.d.ts index 8fd3907d1..6331be7d3 100644 --- a/src/zen/@types/lib.gecko.services.d.ts +++ b/src/zen/@types/lib.gecko.services.d.ts @@ -35,6 +35,7 @@ interface JSServices { locale: mozILocaleService; logins: nsILoginManager; mm: ChromeMessageBroadcaster; + modelContext: nsIModelContextService; obs: nsIObserverService; perms: nsIPermissionManager; policies: nsIEnterprisePolicies; @@ -46,7 +47,6 @@ interface JSServices { rfp: nsIRFPService; scriptSecurityManager: nsIScriptSecurityManager; scriptloader: mozIJSSubScriptLoader; - search: nsISearchService; sessionStorage: nsISessionStorageService; startup: nsIAppStartup; storage: mozIStorageService; @@ -62,4 +62,5 @@ interface JSServices { wm: nsIWindowMediator; ww: nsIWindowWatcher; xulStore: nsIXULStore; + zen: nsIZenCommonUtils; } diff --git a/src/zen/@types/lib.gecko.tweaks.d.ts b/src/zen/@types/lib.gecko.tweaks.d.ts index a98c0bb27..7758bcc10 100644 --- a/src/zen/@types/lib.gecko.tweaks.d.ts +++ b/src/zen/@types/lib.gecko.tweaks.d.ts @@ -28,8 +28,24 @@ interface ChromeWindow extends Window { isChromeWindow: true; } +interface XULElementTagNameMap { + browser: MozBrowser; + iframe: XULFrameElement; + label: XULTextElement; + menu: XULMenuElement; + menupopup: XULPopupElement; + tree: XULTreeElement; +} + interface Document { - createXULElement(name: "browser"): MozBrowser; + createXULElement( + localName: K, + options?: string | ElementCreationOptions + ): XULElementTagNameMap[K]; + createXULElement( + localName: string, + options?: string | ElementCreationOptions + ): XULElement; } type nsIGleanPingNoReason = { diff --git a/src/zen/@types/lib.gecko.xpcom.d.ts b/src/zen/@types/lib.gecko.xpcom.d.ts index b1629155c..3c9c494a5 100644 --- a/src/zen/@types/lib.gecko.xpcom.d.ts +++ b/src/zen/@types/lib.gecko.xpcom.d.ts @@ -143,6 +143,7 @@ declare global { interface nsIBitsRequest extends nsIRequest { readonly bitsId: string; readonly transferError: nsBitsErrorType; + readonly transferErrorCode: i32; changeMonitorInterval( monitorIntervalMs: u32, callback: nsIBitsCallback @@ -944,11 +945,6 @@ declare global { // https://searchfox.org/mozilla-central/source/toolkit/components/alerts/nsIAlertsService.idl - interface nsIAlertNotificationImageListener extends nsISupports { - onImageReady(aUserData: nsISupports, aRequest: imgIRequest): void; - onImageMissing(aUserData: nsISupports): void; - } - interface nsIAlertAction extends nsISupports { readonly action: string; readonly title: string; @@ -974,9 +970,11 @@ declare global { aSilent?: boolean, aVibrate?: u32[] ): void; + initWithObject(aAlertNotification: nsIAlertNotification): void; readonly id: string; readonly name: string; readonly imageURL: string; + image: imgIContainer; readonly title: string; readonly text: string; readonly textClickable: boolean; @@ -995,31 +993,11 @@ declare global { readonly source: string; readonly origin: string; opaqueRelaunchData: string; - loadImage( - aTimeout: u32, - aListener: nsIAlertNotificationImageListener, - aUserData?: nsISupports - ): nsICancelable; getAction(aName: string): nsIAlertAction; } interface nsIAlertsService extends nsISupports { showAlert(aAlert: nsIAlertNotification, aAlertListener?: nsIObserver): void; - showAlertNotification( - aImageURL: string, - aTitle: string, - aText: string, - aTextClickable?: boolean, - aCookie?: string, - aAlertListener?: nsIObserver, - aName?: string, - aDir?: string, - aLang?: string, - aData?: string, - aPrincipal?: nsIPrincipal, - aInPrivateBrowsing?: boolean, - aRequireInteraction?: boolean - ): void; closeAlert(aName?: string, aContextClosed?: boolean): void; getHistory(): string[]; teardown(): void; @@ -1079,6 +1057,7 @@ declare global { readonly initialOpenWindowInfo: nsIOpenWindowInfo; needFastSnaphot(): void; rollupAllPopups(): void; + showInitialViewer(): void; } // https://searchfox.org/mozilla-central/source/xpfe/appshell/nsIWindowMediator.idl @@ -1162,7 +1141,10 @@ declare global { exitLastWindowClosingSurvivalArea(): void; readonly automaticSafeModeNecessary: boolean; restartInSafeMode(aQuitMode: u32): void; - createInstanceWithProfile(aProfile: nsIToolkitProfile): void; + createInstanceWithProfile( + aProfile: nsIToolkitProfile, + aArgs?: string[] + ): void; trackStartupCrashBegin(): boolean; trackStartupCrashEnd(): void; quit(aMode: u32, aExitCode?: i32): boolean; @@ -1209,7 +1191,6 @@ declare global { searchString: string; setInitiallySelectedIndex(index: i32): void; resetInternalState(): void; - resetSession(): void; } // https://searchfox.org/mozilla-central/source/toolkit/components/autocomplete/nsIAutoCompleteInput.idl @@ -1304,7 +1285,6 @@ declare global { listener: nsIAutoCompleteObserver ): void; stopSearch(): void; - resetSession(): void; } interface nsIAutoCompleteObserver extends nsISupports { @@ -1916,8 +1896,6 @@ declare global { readonly loadURIDelegate: nsILoadURIDelegate; suspendRefreshURIs(): void; resumeRefreshURIs(): void; - beginRestore(viewer: nsIDocumentViewer, top: boolean): void; - finishRestore(): void; clearCachedUserAgent(): void; clearCachedPlatform(): void; readonly restoringDocument: boolean; @@ -1954,7 +1932,6 @@ declare global { readonly editable: boolean; readonly hasEditingSession: boolean; makeEditable(inWaitForUriLoad: boolean): void; - getCurrentSHEntry(aEntry: OutParam): boolean; isCommandEnabled(command: string): boolean; doCommand(command: string): void; doCommandWithParams(command: string, aParams: nsICommandParams): void; @@ -2085,7 +2062,7 @@ declare global { readonly inPermitUnload: boolean; readonly beforeUnloadFiring: boolean; pageHide(isUnload: boolean): void; - close(historyEntry: nsISHEntry): void; + close(): void; destroy(): void; stop(): void; readonly DOMDocument: Document; @@ -2093,13 +2070,12 @@ declare global { show(): void; hide(): void; sticky: boolean; - open(aState: nsISupports, aSHEntry: nsISHEntry): void; + open(): void; clearHistoryEntry(): void; setPageModeForTesting( aPageMode: boolean, aPrintSettings: nsIPrintSettings ): void; - readonly historyEntry: nsISHEntry; readonly isTabModalPromptAllowed: boolean; isHidden: boolean; readonly deviceFullZoomForTest: float; @@ -2286,7 +2262,7 @@ declare global { readonly document: Document; readonly currentURI: nsIURI; readonly sessionHistory: nsISupports; - resumeRedirectedLoad(aLoadIdentifier: u64, aHistoryIndex: i32): void; + resumeRedirectedLoad(aLoadIdentifier: u64): void; } // https://searchfox.org/mozilla-central/source/docshell/base/nsIWebNavigationInfo.idl @@ -2304,7 +2280,6 @@ declare global { interface nsIWebPageDescriptor extends nsISupports { loadPageAsViewSource(otherDocShell: nsIDocShell, aURL: string): void; - readonly currentDescriptor: nsISupports; } // https://searchfox.org/mozilla-central/source/dom/base/mozIDOMWindow.idl @@ -2378,7 +2353,8 @@ declare enum nsIContentPolicy_nsContentPolicyType { TYPE_INTERNAL_EXTERNAL_RESOURCE = 61, TYPE_JSON = 62, TYPE_INTERNAL_JSON_PRELOAD = 63, - TYPE_END = 64, + TYPE_INTERNAL_IMAGE_NOTIFICATION = 64, + TYPE_END = 65, } declare global { @@ -2872,6 +2848,7 @@ declare global { readonly hasValidTransientUserGestureActivation: boolean; readonly isRequestDelegatedToUnsafeThirdParty: boolean; getDelegatePrincipal(aType: string): nsIPrincipal; + notifyShown(): void; cancel(): void; allow(choices?: any): void; } @@ -3227,50 +3204,6 @@ declare global { isFirstPaint: boolean; getPresShellId(): u32; isCORSSafelistedRequestHeader(name: string, value: string): boolean; - sendTouchEvent( - aType: string, - aIdentifiers: u32[], - aXs: i32[], - aYs: i32[], - aRxs: u32[], - aRys: u32[], - aRotationAngles: float[], - aForces: float[], - aTiltXs: i32[], - aTiltYs: i32[], - aTwists: i32[], - aModifiers: i32, - aAsyncEnabled?: nsIDOMWindowUtils.AsyncEnabledOption - ): boolean; - sendTouchEventAsPen( - aType: string, - aIdentifier: u32, - aX: i32, - aY: i32, - aRx: u32, - aRy: u32, - aRotationAngle: float, - aForce: float, - aTiltX: i32, - aTiltY: i32, - aTwist: i32, - aModifier: i32, - aAsyncEnabled?: nsIDOMWindowUtils.AsyncEnabledOption - ): boolean; - sendTouchEventToWindow( - aType: string, - aIdentifiers: u32[], - aXs: i32[], - aYs: i32[], - aRxs: u32[], - aRys: u32[], - aRotationAngles: float[], - aForces: float[], - aTiltXs: i32[], - aTiltYs: i32[], - aTwists: i32[], - aModifiers: i32 - ): boolean; sendWheelEvent( aX: float, aY: float, @@ -3399,7 +3332,6 @@ declare global { aOnlyVisible: boolean, aTransparencyThreshold?: float ): NodeList; - getTranslationNodes(aRoot: Node): nsITranslationNodeList; compareCanvases( aCanvas1: nsISupports, aCanvas2: nsISupports, @@ -3560,6 +3492,7 @@ declare global { getViewId(aElement: Element): nsViewID; checkAndClearPaintedState(aElement: Element): boolean; checkAndClearDisplayListState(aElement: Element): boolean; + checkAndClearWRDidRasterize(): boolean; getFileId(aFile: any): i64; getFilePath(aFile: any): string; getFileReferences( @@ -3625,7 +3558,7 @@ declare global { getDirectionFromText(aString: string): i32; ensureDirtyRootFrame(): void; wrCapture(): void; - wrStartCaptureSequence(aPath: string, aFlags: u32): void; + wrStartCaptureSequence(aFlags: u32): void; wrStopCaptureSequence(): void; setCompositionRecording(aValue: boolean): Promise; startCompositionRecording(): Promise; @@ -3649,12 +3582,6 @@ declare global { microTaskLevel: u32; } - interface nsITranslationNodeList extends nsISupports { - readonly length: u32; - item(index: u32): Node; - isTranslationRootAtIndex(index: u32): boolean; - } - interface nsIJSRAIIHelper extends nsISupports { destruct(): void; } @@ -3878,7 +3805,10 @@ declare global { ): nsIServiceWorkerRegistrationInfo; getScopeForUrl(aPrincipal: nsIPrincipal, aPath: string): string; getAllRegistrations(): nsIArray; - removeRegistrationsByOriginAttributes(aOriginAttributes: string): void; + removeRegistrationsByOriginAttributes( + aOriginAttributes: string, + aCallback?: nsIServiceWorkerUnregisterCallback + ): void; propagateUnregister( aPrincipal: nsIPrincipal, aCallback: nsIServiceWorkerUnregisterCallback, @@ -4278,6 +4208,13 @@ declare global { sanitizeDeviceIds(sinceWhen: i64): void; } + // https://searchfox.org/mozilla-central/source/dom/modelcontext/nsIModelContextService.idl + + interface nsIModelContextService extends nsISupports { + getToolsForWindow(innerWindowId: u64): Promise; + invokeTool(innerWindowId: u64, toolName: string, input?: any): Promise; + } + // https://searchfox.org/mozilla-central/source/dom/network/interfaces/nsITCPSocketCallback.idl interface nsITCPSocketCallback extends nsISupports { @@ -5034,7 +4971,7 @@ declare global { } type nsICSPEventListener = Callable<{ - onCSPViolationEvent(aJSON: string): void; + onCSPViolationEvent(aJSON: string, aReportGroupName: string): void; }>; // https://searchfox.org/mozilla-central/source/dom/interfaces/security/nsIIntegrityPolicy.idl @@ -5942,6 +5879,7 @@ declare global { readonly status: i16; readonly isEnterprise: boolean; isAllowed(feature: string): boolean; + isAllowedForURI(feature: string, uri: nsIURI): boolean; getActivePolicies(): any; getSupportMenu(): any; getExtensionPolicy(extensionID: string): any; @@ -7047,6 +6985,7 @@ declare global { readonly insertSeparatorBeforeAccesskeys: boolean; readonly acceptLanguages: string; readonly fontLanguageGroup: string; + readonly urlFixupSuffix: string; } // https://searchfox.org/mozilla-central/source/intl/locale/mozIOSPreferences.idl @@ -7109,18 +7048,32 @@ declare global { addLoginAsync(aLogin: nsILoginInfo): Promise; addLogins(aLogins: any): Promise; removeLogin(aLogin: nsILoginInfo): void; + removeLoginAsync(aLogin: nsILoginInfo): Promise; modifyLogin(oldLogin: nsILoginInfo, newLoginData: nsISupports): void; + modifyLoginAsync( + oldLogin: nsILoginInfo, + newLoginData: nsISupports + ): Promise; recordPasswordUse( aLogin: nsILoginInfo, aPrivateContextWithoutExplicitConsent: boolean, aLoginType: string, aFilled: boolean ): void; + recordPasswordUseAsync( + aLogin: nsILoginInfo, + aPrivateContextWithoutExplicitConsent: boolean, + aLoginType: string, + aFilled: boolean + ): Promise; removeAllUserFacingLogins(): void; + removeAllUserFacingLoginsAsync(): Promise; removeAllLogins(): void; + removeAllLoginsAsync(): Promise; getAllLogins(): Promise; getAllLoginsWithCallback(aCallback: nsILoginSearchCallback): void; reencryptAllLogins(): Promise; + listInvalidOrigins(): Promise; getAllDisabledHosts(): string[]; getLoginSavingEnabled(aHost: string): boolean; setLoginSavingEnabled(aHost: string, isEnabled: boolean): void; @@ -7134,6 +7087,11 @@ declare global { aActionOrigin: string, aHttpRealm: string ): u32; + countLoginsAsync( + aOrigin: string, + aActionOrigin: string, + aHttpRealm: string + ): Promise; searchLoginsAsync(matchData: any): Promise; searchLogins(matchData: nsIPropertyBag): nsILoginInfo[]; getSyncID(): Promise; @@ -7141,6 +7099,12 @@ declare global { getLastSync(): Promise; setLastSync(timestamp: double): Promise; ensureCurrentSyncID(newSyncID: string): Promise; + addPotentiallyVulnerablePassword(aLogin: nsILoginInfo): Promise; + isPotentiallyVulnerablePassword(aLogin: nsILoginInfo): Promise; + recordBreachAlertDismissal(aLoginGUID: string): Promise; + getBreachAlertDismissalsByLoginGUID(): Promise; + arePotentiallyVulnerablePasswords(aLogins: any): Promise; + clearAllPotentiallyVulnerablePasswords(): Promise; readonly uiBusy: boolean; readonly isLoggedIn: boolean; } @@ -7203,6 +7167,7 @@ declare global { timeLastUsed: u64; timePasswordChanged: u64; timesUsed: u32; + timeLastBreachAlertDismissed: u64; } // https://searchfox.org/mozilla-central/source/toolkit/components/passwordmgr/nsIPromptInstance.idl @@ -7644,6 +7609,13 @@ declare global { interface nsIInputStreamReceiver extends nsISupports { onInputStreamReady(aStream: nsIInputStream): void; } + + interface nsICacheEntryWriteHandle extends nsISupports { + openAlternativeOutputStream( + type: string, + predictedSize: i64 + ): nsIAsyncOutputStream; + } } // global declare enum nsICacheInfoChannel_PreferredAlternativeDataDeliveryType { @@ -7679,6 +7651,7 @@ declare global { readonly cacheTokenFetchCount: u32; readonly cacheTokenExpirationTime: u32; isFromCache(): boolean; + hasCacheEntry(): boolean; isRacing(): boolean; getCacheEntryId(): u64; cacheKey: u32; @@ -7693,6 +7666,7 @@ declare global { readonly alternativeDataType: string; readonly alternativeDataInputStream: nsIInputStream; getOriginalInputStream(aReceiver: nsIInputStreamReceiver): void; + getCacheEntryWriteHandle(): nsICacheEntryWriteHandle; openAlternativeOutputStream( type: string, predictedSize: i64 @@ -8345,6 +8319,7 @@ declare global { readonly BLOCKING_REASON_CLASSIFY_FINGERPRINTING_URI?: 2008; readonly BLOCKING_REASON_CLASSIFY_SOCIALTRACKING_URI?: 2009; readonly BLOCKING_REASON_CLASSIFY_EMAILTRACKING_URI?: 2010; + readonly BLOCKING_REASON_CLASSIFY_HARMFULADDON_URI?: 2011; readonly BLOCKING_REASON_MIXED_BLOCKED?: 3001; readonly BLOCKING_REASON_CONTENT_POLICY_GENERAL?: 4000; readonly BLOCKING_REASON_CONTENT_POLICY_NO_DATA_PROTOCOL?: 4001; @@ -8417,8 +8392,8 @@ declare global { readonly innerWindowID: u64; readonly browsingContextID: u64; readonly browsingContext: BrowsingContext; - workerAssociatedBrowsingContextID: u64; - readonly workerAssociatedBrowsingContext: BrowsingContext; + associatedBrowsingContextID: u64; + readonly associatedBrowsingContext: BrowsingContext; readonly frameBrowsingContextID: u64; readonly frameBrowsingContext: BrowsingContext; readonly targetBrowsingContextID: u64; @@ -8669,41 +8644,6 @@ declare global { readonly platformDNSIndications: u32; } - // https://searchfox.org/mozilla-central/source/netwerk/base/nsINetworkPredictor.idl - - interface nsINetworkPredictor extends nsISupports { - readonly PREDICT_LINK?: 0; - readonly PREDICT_LOAD?: 1; - readonly PREDICT_STARTUP?: 2; - readonly LEARN_LOAD_TOPLEVEL?: 0; - readonly LEARN_LOAD_SUBRESOURCE?: 1; - readonly LEARN_LOAD_REDIRECT?: 2; - readonly LEARN_STARTUP?: 3; - - predict( - targetURI: nsIURI, - sourceURI: nsIURI, - reason: PredictorPredictReason, - originAttributes: any, - verifier: nsINetworkPredictorVerifier - ): void; - learn( - targetURI: nsIURI, - sourceURI: nsIURI, - reason: PredictorLearnReason, - originAttributes: any - ): void; - reset(): void; - } - - // https://searchfox.org/mozilla-central/source/netwerk/base/nsINetworkPredictorVerifier.idl - - interface nsINetworkPredictorVerifier extends nsISupports { - onPredictPrefetch(uri: nsIURI, status: u32): void; - onPredictPreconnect(uri: nsIURI): void; - onPredictDNS(uri: nsIURI): void; - } - // https://searchfox.org/mozilla-central/source/netwerk/base/nsINullChannel.idl interface nsINullChannel extends nsISupports {} @@ -8798,6 +8738,9 @@ declare global { typeInclusions: string[], typeExceptions: string[] ): void; + updateLastInteractionForPrincipal(principal: nsIPrincipal): void; + removeOrphanedInteractionRecords(): Promise; + testFlushPendingWrites(): Promise; } // https://searchfox.org/mozilla-central/source/netwerk/base/nsIPrivateBrowsingChannel.idl @@ -9013,7 +8956,7 @@ declare global { newMASQUEProxyInfo( aHost: string, aPort: i32, - aPathTemplate: string, + aMasqueTemplate: string, aProxyAuthorizationHeader: string, aConnectionIsolationKey: string, aFlags: u32, @@ -9076,6 +9019,7 @@ declare global { readonly SOCKS_V4?: 4; readonly SOCKS_V5?: 5; readonly TRANSPARENT_PROXY_RESOLVES_HOST?: 1; + readonly ALWAYS_TUNNEL_VIA_PROXY?: 2; readonly host: string; readonly port: i32; @@ -9089,7 +9033,7 @@ declare global { sourceId: string; readonly proxyAuthorizationHeader: string; readonly connectionIsolationKey: string; - pathTemplate: string; + masqueTemplate: string; } // https://searchfox.org/mozilla-central/source/netwerk/base/nsIRandomGenerator.idl @@ -9322,6 +9266,7 @@ declare global { readonly effectiveTRRMode: nsIRequest.TRRMode; readonly trrSkipReason: nsITRRSkipReason.value; readonly retryDnsIfPossible: boolean; + readonly isTRRConnection: boolean; } // https://searchfox.org/mozilla-central/source/netwerk/base/nsISocketTransportService.idl @@ -9985,6 +9930,7 @@ declare global { readonly OPEN_SECRETLY?: 32; readonly OPEN_INTERCEPTED?: 64; readonly OPEN_COMPLETE_ONLY?: 128; + readonly OPEN_ALWAYS?: 256; asyncOpenURI( aURI: nsIURI, @@ -10078,6 +10024,9 @@ declare global { suspendCacheIOThread(aLevel: u32): void; resumeCacheIOThread(): void; flush(aObserver: nsIObserver): void; + clearDictionaryCacheMemory(): void; + corruptDictionaryHash(aURI: string): void; + clearDictionaryDataForTesting(aURI: string): void; } // https://searchfox.org/mozilla-central/source/netwerk/cookie/nsICookie.idl @@ -10166,20 +10115,6 @@ declare global { aSchemeMap: nsICookie.schemeType, aIsPartitioned?: boolean ): nsICookieValidation; - addForAddOn( - aHost: string, - aPath: string, - aName: string, - aValue: string, - aIsSecure: boolean, - aIsHttpOnly: boolean, - aIsSession: boolean, - aExpiry: i64, - aOriginAttributes: any, - aSameSite: i32, - aSchemeMap: nsICookie.schemeType, - aIsPartitioned?: boolean - ): nsICookieValidation; cookieExists( aHost: string, aPath: string, @@ -10208,6 +10143,8 @@ declare global { aExceptions: nsIThirdPartyCookieExceptionEntry[] ): void; testGet3PCBExceptions(): string[]; + testCloseCookieDB(): void; + testOpenCookieDB(): void; maybeCapExpiry(aExpiryInMSec: i64): i64; } @@ -10536,6 +10473,7 @@ declare global { readonly myHostName: string; readonly trrDomain: string; readonly TRRDomainKey: string; + setHttp3FirstForServer(aServer: string, aEnabled: boolean): void; } // https://searchfox.org/mozilla-central/source/netwerk/dns/nsIEffectiveTLDService.idl @@ -10775,6 +10713,31 @@ declare global { observeConnection: boolean; } + // https://searchfox.org/mozilla-central/source/netwerk/protocol/http/nsIHttpAuthCache.idl + + interface nsIHttpAuthCache extends nsISupports { + getEntries(): nsIHttpAuthEntry[]; + clearEntry(entry: nsIHttpAuthEntry): void; + } + + // https://searchfox.org/mozilla-central/source/netwerk/protocol/http/nsIHttpAuthEntry.idl + + interface nsIHttpAuthIdentity extends nsISupports { + readonly domain: string; + readonly user: string; + readonly password: string; + } + + interface nsIHttpAuthEntry extends nsISupports { + readonly realm: string; + readonly creds: string; + readonly challenge: string; + readonly domain: string; + readonly user: string; + readonly password: string; + readonly identity: nsIHttpAuthIdentity; + } + // https://searchfox.org/mozilla-central/source/netwerk/protocol/http/nsIHttpAuthManager.idl interface nsIHttpAuthManager extends nsISupports { @@ -10977,9 +10940,15 @@ declare global { // https://searchfox.org/mozilla-central/source/netwerk/protocol/http/nsINetworkErrorLogging.idl + interface nsINetworkErrorReport extends nsISupports { + readonly body: string; + readonly group: string; + readonly url: string; + } + interface nsINetworkErrorLogging extends nsISupports { registerPolicy(aChannel: nsIHttpChannel): void; - generateNELReport(aChannel: nsIHttpChannel): void; + generateNELReport(aChannel: nsIHttpChannel): nsINetworkErrorReport; } // https://searchfox.org/mozilla-central/source/netwerk/protocol/http/nsIObliviousHttp.idl @@ -11384,6 +11353,7 @@ declare global { ): void; sendDatagram(aData: u8[], aTrackingId: u64): void; getMaxDatagramSize(): void; + getHttpChannelID(): u64; } } // global @@ -11433,6 +11403,29 @@ declare global { readonly value: u8[]; } + // https://searchfox.org/mozilla-central/source/netwerk/protocol/webtransport/nsIWebTransportEventService.idl + + interface nsIWebTransportEventListener extends nsISupports { + webTransportSessionCreated(aHttpChannelId: u64): void; + webTransportSessionClosed( + aHttpChannelId: u64, + aCode: u16, + aReason: string + ): void; + } + + interface nsIWebTransportEventService extends nsISupports { + addListener( + aInnerWindowID: u64, + aListener: nsIWebTransportEventListener + ): void; + removeListener( + aInnerWindowID: u64, + aListener: nsIWebTransportEventListener + ): void; + hasListenerFor(aInnerWindowID: u64): boolean; + } + // https://searchfox.org/mozilla-central/source/netwerk/protocol/webtransport/nsIWebTransportStream.idl interface nsIWebTransportSendStreamStats extends nsISupports { @@ -11658,9 +11651,6 @@ declare global { interface nsICertStorage extends nsISupports { readonly DATA_TYPE_REVOCATION?: 1; readonly DATA_TYPE_CERTIFICATE?: 2; - readonly DATA_TYPE_CRLITE?: 3; - readonly DATA_TYPE_CRLITE_FILTER_FULL?: 4; - readonly DATA_TYPE_CRLITE_FILTER_INCREMENTAL?: 5; readonly STATE_UNSET?: 0; readonly STATE_ENFORCE?: 1; readonly STATE_NOT_ENROLLED?: 2; @@ -11680,6 +11670,7 @@ declare global { subject: u8[], pubkey: u8[] ): i16; + getCRLiteFilterHashes(callback: nsICertStorageCallback): void; setFullCRLiteFilter(filter: u8[], callback: nsICertStorageCallback): void; addCRLiteDelta( delta: u8[], @@ -11828,6 +11819,7 @@ declare enum nsIDataStorageManager_DataStorage { AlternateServices = 0, ClientAuthRememberList = 1, SiteSecurityServiceState = 2, + SiteIntegrityServiceState = 3, } declare global { @@ -11868,6 +11860,14 @@ declare global { readonly type: nsIDataStorage.DataType; } + // https://searchfox.org/mozilla-central/source/security/manager/ssl/nsIFIPSUtils.idl + + interface nsIFIPSUtils extends nsISupports { + readonly canToggleFIPS: boolean; + toggleFIPSMode(): void; + readonly isFIPSEnabled: boolean; + } + // https://searchfox.org/mozilla-central/source/security/manager/ssl/nsINSSComponent.idl interface nsINSSComponent extends nsISupports { @@ -11878,6 +11878,7 @@ declare global { addEnterpriseIntermediate(intermediateBytes: u8[]): void; clearSSLExternalAndInternalSessionCache(): void; asyncClearSSLExternalAndInternalSessionCache(): Promise; + readonly nssTaskQueue: nsISerialEventTarget; } // https://searchfox.org/mozilla-central/source/security/manager/ssl/nsINSSErrorsService.idl @@ -11970,23 +11971,20 @@ declare global { interface nsIPKCS11Module extends nsISupports { readonly name: string; readonly libName: string; - listSlots(): nsISimpleEnumerator; + readonly slots: nsIPKCS11Slot[]; } // https://searchfox.org/mozilla-central/source/security/manager/ssl/nsIPKCS11ModuleDB.idl interface nsIPKCS11ModuleDB extends nsISupports { - deleteModule(moduleName: string): void; + deleteModule(moduleName: string): Promise; addModule( moduleName: string, - libraryFullPath: string, - cryptoMechanismFlags: i32, - cipherFlags: i32 - ): void; - listModules(): nsISimpleEnumerator; - readonly canToggleFIPS: boolean; - toggleFIPSMode(): void; - readonly isFIPSEnabled: boolean; + libraryPath: string, + mechanismFlags: u32, + cipherFlags: u32 + ): Promise; + listModules(): Promise; } // https://searchfox.org/mozilla-central/source/security/manager/ssl/nsIPKCS11Slot.idl @@ -12038,6 +12036,10 @@ declare global { readonly WARNING_CONFIRM_POST_TO_INSECURE_FROM_SECURE_CLICK_THROUGH?: 10; } + // https://searchfox.org/mozilla-central/source/security/manager/ssl/nsISiteIntegrityService.idl + + interface nsISiteIntegrityService extends nsISupports {} + // https://searchfox.org/mozilla-central/source/security/manager/ssl/nsISiteSecurityService.idl } // global @@ -12222,6 +12224,7 @@ declare global { readonly certType: u32; getRawDER(): u8[]; getBase64DERString(): string; + readonly subjectPublicKeyInfo: u8[]; readonly sha256SubjectPublicKeyInfoDigest: string; } @@ -12867,7 +12870,7 @@ declare global { interface nsINavHistoryService extends nsISupports, Enums { - readonly DATABASE_SCHEMA_VERSION?: 82; + readonly DATABASE_SCHEMA_VERSION?: 86; readonly DATABASE_STATUS_OK?: 0; readonly DATABASE_STATUS_CREATE?: 1; readonly DATABASE_STATUS_CORRUPT?: 2; @@ -12906,7 +12909,6 @@ declare global { aBookmarked: boolean ): i64; hashURL(aSpec: string, aMode?: string): u64; - isFrecencyDecaying: boolean; readonly isAlternativeFrecencyEnabled: boolean; shouldStartFrecencyRecalculation: boolean; readonly DBConnection: mozIStorageConnection; @@ -12934,15 +12936,24 @@ declare global { } // https://searchfox.org/mozilla-central/source/modules/libpref/nsIPrefBranch.idl +} // global - interface nsIPrefBranch extends nsISupports { - readonly PREF_INVALID?: 0; - readonly PREF_STRING?: 32; - readonly PREF_INT?: 64; - readonly PREF_BOOL?: 128; +declare enum nsIPrefBranch_PreferenceType { + PREF_INVALID = 0, + PREF_STRING = 32, + PREF_INT = 64, + PREF_BOOL = 128, +} +declare global { + namespace nsIPrefBranch { + type PreferenceType = nsIPrefBranch_PreferenceType; + } + + interface nsIPrefBranch + extends nsISupports, Enums { readonly root: string; - getPrefType(aPrefName: string): i32; + getPrefType(aPrefName: string): nsIPrefBranch.PreferenceType; getBoolPref(aPrefName: string, aDefaultValue?: boolean): boolean; setBoolPref(aPrefName: string, aValue: boolean): void; getFloatPref(aPrefName: string, aDefaultValue?: float): float; @@ -13190,13 +13201,40 @@ declare global { readonly contentWin32kLockdownStateString: string; } + // https://searchfox.org/mozilla-central/source/security/sandbox/linux/interfaces/mozISandboxReporter.idl + + interface mozISandboxReport extends nsISupports { + readonly msecAgo: u64; + readonly pid: i32; + readonly tid: i32; + readonly procType: string; + readonly syscall: u32; + readonly numArgs: u32; + getArg(aIndex: u32): string; + } + + interface mozISandboxReportArray extends nsISupports { + readonly begin: u64; + readonly end: u64; + getElement(aIndex: u64): mozISandboxReport; + } + + interface mozISandboxReporter extends nsISupports { + snapshot(): mozISandboxReportArray; + } + // https://searchfox.org/mozilla-central/source/toolkit/components/satchel/nsIFormFillController.idl + interface nsIFormFillFocusListener extends nsISupports { + handleFocus(element: Element): Promise; + } + interface nsIFormFillController extends nsISupports { controlledElement: Element; readonly passwordPopupAutomaticallyOpened: boolean; markAsAutoCompletableField(aElement: Element): void; showPopup(): void; + addFocusListener(listener: nsIFormFillFocusListener): void; } type nsIFormFillCompleteObserver = Callable<{ @@ -13346,11 +13384,8 @@ declare global { hasUserInteraction: boolean; hasUserActivation: boolean; referrerInfo: nsIReferrerInfo; - documentViewer: nsIDocumentViewer; readonly isInBFCache: boolean; sticky: boolean; - windowState: nsISupports; - refreshURIList: nsIMutableArray; postData: nsIInputStream; readonly hasPostData: boolean; layoutHistoryState: nsILayoutHistoryState; @@ -13381,7 +13416,6 @@ declare global { clone(): nsISHEntry; hasDynamicallyAddedChild(): boolean; adoptBFCacheEntry(aEntry: nsISHEntry): void; - abandonBFCacheEntry(): void; sharesDocumentWith(aEntry: nsISHEntry): boolean; setLoadTypeAsHistory(): void; AddChild( @@ -13411,6 +13445,8 @@ declare global { updateIndex(): void; replaceEntry(aIndex: i32, aReplaceEntry: nsISHEntry): void; notifyOnHistoryReload(): boolean; + notifyOnHistoryCommit(): void; + notifyOnEntryTitleUpdated(aEntry: nsISHEntry): void; evictOutOfRangeDocumentViewers(aIndex: i32): void; evictAllDocumentViewers(): void; reload(aReloadFlags: u32): void; @@ -13428,6 +13464,8 @@ declare global { OnHistoryTruncate(aNumEntries: i32): void; OnHistoryReplaceEntry(): void; OnDocumentViewerEvicted(aNumEvicted: u32): void; + OnHistoryCommit(): void; + OnEntryTitleUpdated(aEntry: nsISHEntry): void; } // https://searchfox.org/mozilla-central/source/extensions/spellcheck/idl/mozIPersonalDictionary.idl @@ -13955,6 +13993,7 @@ declare global { interface nsIBounceTrackingPurgeEntry extends nsIBounceTrackingMapEntry { readonly purgeTime: PRTime; + readonly bounceTrackingRecord: nsIBounceTrackingRecord; } // https://searchfox.org/mozilla-central/source/toolkit/components/antitracking/bouncetrackingprotection/nsIBounceTrackingProtection.idl @@ -14014,6 +14053,15 @@ declare global { testMaybeMigrateUserInteractionPermissions(): void; } + // https://searchfox.org/mozilla-central/source/toolkit/components/antitracking/bouncetrackingprotection/nsIBounceTrackingRecord.idl + + interface nsIBounceTrackingRecord extends nsISupports { + readonly initialHost: string; + readonly finalHost: string; + readonly bounceHosts: string[]; + readonly storageAccessHosts: string[]; + } + // https://searchfox.org/mozilla-central/source/toolkit/components/antitracking/nsIContentBlockingAllowList.idl interface nsIContentBlockingAllowList extends nsISupports { @@ -14102,6 +14150,33 @@ declare global { testHasQPSObservers(): boolean; } + // https://searchfox.org/mozilla-central/source/toolkit/components/antitracking/scopedprefs/nsIScopedPrefs.idl +} // global + +declare enum nsIScopedPrefs_Pref { + PRIVACY_TRACKINGPROTECTION_ENABLED = 0, + NUM_SCOPED_BOOL_PREFS = 1, +} + +declare global { + namespace nsIScopedPrefs { + type Pref = nsIScopedPrefs_Pref; + } + + interface nsIScopedPrefs + extends nsISupports, Enums { + setBoolPrefScoped( + pref: nsIScopedPrefs.Pref, + bc: BrowsingContext, + value: boolean + ): void; + getBoolPrefScoped(pref: nsIScopedPrefs.Pref, bc: BrowsingContext): boolean; + clearScoped(): void; + clearScopedPref(pref: nsIScopedPrefs.Pref): void; + clearScopedByHost(aHost: string): void; + clearScopedPrefByHost(pref: nsIScopedPrefs.Pref, aHost: string): void; + } + // https://searchfox.org/mozilla-central/source/toolkit/components/asyncshutdown/nsIAsyncShutdown.idl interface nsIAsyncShutdownBlocker extends nsISupports { @@ -14191,7 +14266,6 @@ declare global { readonly CLEAR_DOWNLOADS?: 16; readonly CLEAR_MEDIA_DEVICES?: 64; readonly CLEAR_DOM_QUOTA?: 128; - readonly CLEAR_PREDICTOR_NETWORK_DATA?: 256; readonly CLEAR_DOM_PUSH_NOTIFICATIONS?: 512; readonly CLEAR_HISTORY?: 1024; readonly CLEAR_MESSAGING_LAYER_SECURITY_STATE?: 2048; @@ -14219,7 +14293,7 @@ declare global { readonly CLEAR_PERMISSIONS?: 2147500032; readonly CLEAR_ALL_CACHES?: 12648462; readonly CLEAR_DOM_STORAGES?: 262784; - readonly CLEAR_FORGET_ABOUT_SITE?: 3218591647; + readonly CLEAR_FORGET_ABOUT_SITE?: 3218591391; readonly CLEAR_COOKIES_AND_SITE_DATA?: 2013739649; readonly CLEAR_STATE_FOR_TRACKER_PURGING?: 2043624143; @@ -14275,6 +14349,7 @@ declare global { aFlags: u32, aCallback: nsIClearDataCallback ): void; + clearPrivateBrowsingData(aCallback: nsIClearDataCallback): void; hostMatchesSite( aHost: string, aOriginAttributes: any, @@ -14287,6 +14362,14 @@ declare global { onDataDeleted(aFailedFlags: u32): void; }>; + type nsIPBMCleanupCallback = Callable<{ + complete(aStatus: nsresult): void; + }>; + + interface nsIPBMCleanupCollector extends nsISupports { + addPendingCleanup(): nsIPBMCleanupCallback; + } + // https://searchfox.org/mozilla-central/source/toolkit/components/contentanalysis/nsIContentAnalysis.idl } // global @@ -14637,10 +14720,6 @@ declare global { removeAllExecutedRecords(aIsPrivate: boolean): void; } - // https://searchfox.org/mozilla-central/source/toolkit/components/cookiebanners/nsICookieBannerTelemetryService.idl - - interface nsICookieBannerTelemetryService extends nsISupports {} - // https://searchfox.org/mozilla-central/source/toolkit/components/cookiebanners/nsICookieRule.idl interface nsICookieRule extends nsISupports { @@ -14754,132 +14833,6 @@ declare global { pageLoaded(browsingContext: BrowsingContext, data: any): void; } - // https://searchfox.org/mozilla-central/source/toolkit/components/search/nsISearchService.idl - - interface nsISearchSubmission extends nsISupports { - readonly postData: nsIInputStream; - readonly uri: nsIURI; - } - - interface nsISearchEngine extends nsISupports { - getSubmission( - searchTerms: string, - responseType?: string - ): nsISearchSubmission; - readonly searchURLWithNoTerms: nsIURI; - searchTermFromResult(uri: nsIURI): string; - readonly searchUrlQueryParamName: string; - readonly searchUrlPublicSuffix: string; - supportsResponseType(responseType: string): boolean; - getIconURL(preferredWidth?: u16): Promise; - speculativeConnect(options: any): void; - alias: string; - readonly aliases: string[]; - hidden: boolean; - hideOneOffButton: boolean; - readonly name: string; - readonly id: string; - readonly telemetryId: string; - readonly loadPath: string; - readonly isAppProvided: boolean; - readonly isConfigEngine: boolean; - readonly inMemory: boolean; - readonly overriddenById: string; - readonly isGeneralPurposeEngine: boolean; - readonly searchUrlDomain: string; - readonly clickUrl: string; - readonly searchForm: string; - readonly partnerCode: string; - } - - interface nsISearchParseSubmissionResult extends nsISupports { - readonly engine: nsISearchEngine; - readonly terms: string; - readonly termsParameterName: string; - } -} // global - -declare enum nsISearchService_OpenSearchInstallErrors { - ERROR_DOWNLOAD_FAILURE = 1, - ERROR_DUPLICATE_ENGINE = 2, - ERROR_ENGINE_CORRUPTED = 3, -} - -declare enum nsISearchService_DefaultEngineChangeReason { - CHANGE_REASON_UNKNOWN = 0, - CHANGE_REASON_USER = 1, - CHANGE_REASON_USER_PRIVATE_SPLIT = 2, - CHANGE_REASON_USER_SEARCHBAR = 3, - CHANGE_REASON_USER_SEARCHBAR_CONTEXT = 4, - CHANGE_REASON_ADDON_INSTALL = 5, - CHANGE_REASON_ADDON_UNINSTALL = 6, - CHANGE_REASON_CONFIG = 7, - CHANGE_REASON_LOCALE = 8, - CHANGE_REASON_REGION = 9, - CHANGE_REASON_EXPERIMENT = 10, - CHANGE_REASON_ENTERPRISE = 11, - CHANGE_REASON_UITOUR = 12, - CHANGE_REASON_ENGINE_UPDATE = 13, - CHANGE_REASON_USER_PRIVATE_PREF_ENABLED = 14, - CHANGE_REASON_ENGINE_IGNORE_LIST_UPDATED = 15, - CHANGE_REASON_NO_EXISTING_DEFAULT_ENGINE = 16, -} - -declare global { - namespace nsISearchService { - type OpenSearchInstallErrors = nsISearchService_OpenSearchInstallErrors; - type DefaultEngineChangeReason = nsISearchService_DefaultEngineChangeReason; - } - - interface nsISearchService - extends - nsISupports, - Enums< - typeof nsISearchService_OpenSearchInstallErrors & - typeof nsISearchService_DefaultEngineChangeReason - > { - init(): Promise; - readonly promiseInitialized: Promise; - readonly isInitialized: boolean; - readonly hasSuccessfullyInitialized: boolean; - runBackgroundChecks(): Promise; - resetToAppDefaultEngine(): void; - addOpenSearchEngine( - engineURL: string, - iconURL: string, - originAttributes?: any - ): Promise; - addUserEngine(formInfo: any): Promise; - addEnginesFromExtension(extension: any): Promise; - restoreDefaultEngines(): void; - getEngineByAlias(alias: string): Promise; - getEngineByName(aEngineName: string): nsISearchEngine; - getEngineById(aEngineId: string): nsISearchEngine; - getEngines(): Promise; - getVisibleEngines(): Promise; - getAppProvidedEngines(): Promise; - getEnginesByExtensionID(extensionID: string): Promise; - findContextualSearchEngineByHost(host: string): Promise; - shouldShowInstallPrompt(engine: any): Promise; - addSearchEngine(engine: any): Promise; - moveEngine(engine: nsISearchEngine, newIndex: i32): Promise; - removeEngine(engine: nsISearchEngine): Promise; - removeWebExtensionEngine(id: string): Promise; - readonly appDefaultEngine: nsISearchEngine; - readonly appPrivateDefaultEngine: nsISearchEngine; - readonly defaultEngine: nsISearchEngine; - getDefault(): Promise; - setDefault(engine: nsISearchEngine, changeReason: u16): Promise; - readonly defaultPrivateEngine: nsISearchEngine; - getDefaultPrivate(): Promise; - setDefaultPrivate(engine: nsISearchEngine, changeReason: u16): Promise; - readonly separatePrivateDefaultUrlbarResultEnabled: boolean; - maybeSetAndOverrideDefault(extension: any): Promise; - getDefaultEngineInfo(): any; - parseSubmissionURL(url: string): nsISearchParseSubmissionResult; - getAlternateDomains(domain: string): string[]; - } - // https://searchfox.org/mozilla-central/source/toolkit/components/shell/nsIToolkitShellService.idl interface nsIToolkitShellService extends nsISupports { @@ -15003,6 +14956,11 @@ declare global { aRootDir: nsIFile, aNamePrefix: string ): nsIToolkitProfile; + getProfileDescriptor( + aRootDir: nsIFile, + aIsRelative: OutParam + ): string; + getLocalDirFromRootDir(aRootDir: nsIFile): nsIFile; readonly profileCount: u32; flush(): void; asyncFlush(): Promise; @@ -15779,6 +15737,8 @@ declare global { resetDatabase(): void; reloadDatabase(): void; clearCache(): void; + cleanRealTimeSimulatorCache(): void; + expireRealTimeSimulatorCache(): void; } // https://searchfox.org/mozilla-central/source/toolkit/components/url-classifier/nsIUrlClassifierHashCompleter.idl @@ -15875,9 +15835,11 @@ declare global { interface nsIUrlClassifierTestUtils extends nsISupports { makeUpdateResponseV5(aName: string, aSingleHash: u32): string; + makeUpdateResponseV5_32b(aName: string, aFullHash: string): string; makeFindFullHashResponseV5(aFullHash: string): string; generateLookupHash(aFragment: string): u32; generateFullHash(aFragment: string): string; + generateFullHashRaw(aFragment: string): string; } // https://searchfox.org/mozilla-central/source/toolkit/components/url-classifier/nsIUrlClassifierUtils.idl @@ -15900,6 +15862,7 @@ declare global { convertListNameToThreatType(listName: string): u32; convertServerListNameToLocalListNameV5(serverListName: string): string; convertLocalListNameToServerListNameV5(localListName: string): string; + convertThreatTypeToListNamesV5(threatType: u32): string; makeUpdateRequestV4(aListNames: string[], aStatesBase64: string[]): string; makeUpdateRequestV5(aListNames: string[], aStatesBase64: string[]): string; makeFindFullHashRequestV4( @@ -15994,6 +15957,7 @@ declare global { readonly CHROME_ALWAYS_ON_TOP?: 524288; readonly CHROME_REMOTE_WINDOW?: 1048576; readonly CHROME_FISSION_WINDOW?: 2097152; + readonly CHROME_DOCUMENT_PIP?: 4194304; readonly CHROME_SUPPRESS_ANIMATION?: 16777216; readonly CHROME_CENTER_SCREEN?: 134217728; readonly CHROME_DEPENDENT?: 268435456; @@ -16001,6 +15965,7 @@ declare global { readonly CHROME_OPENAS_DIALOG?: 1073741824; readonly CHROME_OPENAS_CHROME?: 2147483648; readonly CHROME_MINIMAL_POPUP?: 18126; + readonly CHROME_DOCUMENT_PICTURE_IN_PICTURE_FLAGS?: 4736718; setLinkStatus(status: string): void; chromeFlags: u32; @@ -16385,7 +16350,6 @@ declare global { cx: OutParam, cy: OutParam ): void; - repaint(force: boolean): void; readonly nativeHandle: string; visibility: boolean; enabled: boolean; @@ -17186,6 +17150,10 @@ declare global { readonly textDirectiveUserActivation: boolean; readonly originAttributes: any; cancel(): void; + cloneWithPrincipals( + aPrincipal: nsIPrincipal, + aPartitionedPrincipal: nsIPrincipal + ): nsIOpenWindowInfo; } // https://searchfox.org/mozilla-central/source/toolkit/components/windowwatcher/nsIPromptCollection.idl @@ -17502,6 +17470,12 @@ declare global { onUnloadAttemptCompleted(aResult: nsresult): void; } + // https://searchfox.org/mozilla-central/source/xpcom/base/nsIAvailableMemoryWatcherTestingLinux.idl + + interface nsIAvailableMemoryWatcherTestingLinux extends nsISupports { + setPSIPathForTesting(aPSIPath: string): void; + } + // https://searchfox.org/mozilla-central/source/xpcom/base/nsIConsoleListener.idl type nsIConsoleListener = Callable<{ @@ -17567,6 +17541,12 @@ declare global { aCompartmentAddress: string ): void; noteEdge(aFromAddress: string, aToAddress: string, aEdgeName: string): void; + noteWeakMapEntry( + aMap: string, + aKey: string, + aKeyDelegate: string, + aValue: string + ): void; describeRoot(aAddress: string, aKnownEdges: u32): void; describeGarbage(aAddress: string): void; } @@ -18543,15 +18523,6 @@ declare global { readonly outputStream: nsIAsyncOutputStream; } - interface nsISearchableInputStream extends nsISupports { - search( - forString: string, - ignoreCase: boolean, - found: OutParam, - offsetSearchedTo: OutParam - ): void; - } - // https://searchfox.org/mozilla-central/source/xpcom/io/nsIRandomAccessStream.idl interface nsIRandomAccessStream extends nsISeekableStream { @@ -18668,7 +18639,6 @@ declare global { annotateCrashReport(key: string, data: any): void; removeCrashReportAnnotation(key: string): void; isAnnotationValid(value: string): boolean; - isAnnotationAllowedForPing(value: string): boolean; isAnnotationAllowedForReport(value: string): boolean; appendAppNotesToCrashReport(data: string): void; registerAppMemory(ptr: u64, size: u64): void; @@ -18748,15 +18718,6 @@ declare global { setHighAccuracy(enable: boolean): void; } - // https://searchfox.org/mozilla-central/source/xpcom/system/nsIHapticFeedback.idl - - interface nsIHapticFeedback extends nsISupports { - readonly ShortPress?: 0; - readonly LongPress?: 1; - - performSimpleAction(isLongPress: i32): void; - } - // https://searchfox.org/mozilla-central/source/xpcom/system/nsIPlatformInfo.idl interface nsIPlatformInfo extends nsISupports { @@ -18863,7 +18824,6 @@ declare global { readonly fissionAutostart: boolean; readonly fissionDecisionStatus: nsIXULRuntime.FissionDecisionStatus; readonly fissionDecisionStatusString: string; - readonly sessionHistoryInParent: boolean; readonly sessionStorePlatformCollection: boolean; logConsoleErrors: boolean; readonly OS: string; @@ -18921,13 +18881,25 @@ declare enum nsIEventTarget_DispatchFlags { DISPATCH_FALLIBLE = 16, } +declare enum nsIEventTarget_FeatureFlags { + SUPPORTS_BASE = 0, + SUPPORTS_SHUTDOWN_TASKS = 1, + SUPPORTS_SHUTDOWN_TASK_DISPATCH = 2, + SUPPORTS_PRIORITIZATION = 4, +} + declare global { namespace nsIEventTarget { type DispatchFlags = nsIEventTarget_DispatchFlags; + type FeatureFlags = nsIEventTarget_FeatureFlags; } interface nsIEventTarget - extends nsISupports, Enums { + extends + nsISupports, + Enums< + typeof nsIEventTarget_DispatchFlags & typeof nsIEventTarget_FeatureFlags + > { isOnCurrentThread(): boolean; dispatch(event: nsIRunnable, flags: nsIEventTarget.DispatchFlags): void; } @@ -19533,6 +19505,35 @@ declare global { getInstallHash(): string; } + // https://searchfox.org/mozilla-central/source/zen/toolkit/common/nsIZenCommonUtils.idl + + interface nsIZenCommonUtils extends nsISupports { + share( + url: nsIURI, + title: string, + text: string, + x: u32, + y: u32, + width: u32, + height: u32 + ): void; + canShare(): boolean; + playHapticFeedback(): void; + } + + // https://searchfox.org/mozilla-central/source/zen/drag-and-drop/nsIZenDragAndDrop.idl + + interface nsIZenDragAndDrop extends nsISupports { + onDragStart(opacity: float): void; + onDragEnd(): void; + } + + // https://searchfox.org/mozilla-central/source/zen/mods/nsIZenModsBackend.idl + + interface nsIZenModsBackend extends nsISupports { + rebuildModsStyles(aContents: string): void; + } + // https://searchfox.org/mozilla-central/source/modules/libjar/zipwriter/nsIZipWriter.idl interface nsIZipWriter extends nsISupports { @@ -19615,7 +19616,6 @@ declare global { nsIAccessibleScrollType: nsJSIID; nsIAccessibleCoordinateType: nsJSIID; nsIAccessibleValue: nsJSIID; - nsIAlertNotificationImageListener: nsJSIID; nsIAlertAction: nsJSIID; nsIAlertNotification: nsJSIID; nsIAlertsService: nsJSIID; @@ -19749,7 +19749,6 @@ declare global { nsIDOMWindowUtils, typeof nsIDOMWindowUtils_AsyncEnabledOption >; - nsITranslationNodeList: nsJSIID; nsIJSRAIIHelper: nsJSIID; nsIFocusManager: nsJSIID; nsIGeckoViewServiceWorker: nsJSIID; @@ -19782,6 +19781,7 @@ declare global { nsIAudioDeviceInfo: nsJSIID; nsIMediaDevice: nsJSIID; nsIMediaManagerService: nsJSIID; + nsIModelContextService: nsJSIID; nsITCPSocketCallback: nsJSIID; nsIUDPSocketInternal: nsJSIID; nsINotificationActionStorageEntry: nsJSIID; @@ -20028,6 +20028,7 @@ declare global { nsIBufferedOutputStream: nsJSIID; nsIByteRangeRequest: nsJSIID; nsIInputStreamReceiver: nsJSIID; + nsICacheEntryWriteHandle: nsJSIID; nsICacheInfoChannel: nsJSIID< nsICacheInfoChannel, typeof nsICacheInfoChannel_PreferredAlternativeDataDeliveryType & @@ -20107,8 +20108,6 @@ declare global { nsIInterceptedChannel: nsJSIID; nsINetworkInterceptController: nsJSIID; nsINetworkLinkService: nsJSIID; - nsINetworkPredictor: nsJSIID; - nsINetworkPredictorVerifier: nsJSIID; nsINullChannel: nsJSIID; nsIParentChannel: nsJSIID; nsIPermission: nsJSIID; @@ -20254,12 +20253,16 @@ declare global { nsIEarlyHintObserver: nsJSIID; nsIHttpActivityObserver: nsJSIID; nsIHttpActivityDistributor: nsJSIID; + nsIHttpAuthCache: nsJSIID; + nsIHttpAuthIdentity: nsJSIID; + nsIHttpAuthEntry: nsJSIID; nsIHttpAuthManager: nsJSIID; nsIHttpChannel: nsJSIID; nsIHttpUpgradeListener: nsJSIID; nsIHttpChannelInternal: nsJSIID; nsIHttpHeaderVisitor: nsJSIID; nsIHttpProtocolHandler: nsJSIID; + nsINetworkErrorReport: nsJSIID; nsINetworkErrorLogging: nsJSIID; nsIObliviousHttpClientResponse: nsJSIID; nsIObliviousHttpClientRequest: nsJSIID; @@ -20301,6 +20304,8 @@ declare global { >; nsIWebTransportStreamCallback: nsJSIID; nsIWebTransportHash: nsJSIID; + nsIWebTransportEventListener: nsJSIID; + nsIWebTransportEventService: nsJSIID; nsIWebTransportSendStreamStats: nsJSIID; nsIWebTransportReceiveStreamStats: nsJSIID; nsIWebTransportStreamStatsCallback: nsJSIID; @@ -20340,6 +20345,7 @@ declare global { >; nsIDataStorage: nsJSIID; nsIDataStorageItem: nsJSIID; + nsIFIPSUtils: nsJSIID; nsINSSComponent: nsJSIID; nsINSSErrorsService: nsJSIID; nsINSSVersion: nsJSIID; @@ -20353,6 +20359,7 @@ declare global { nsIPublicKeyPinningService: nsJSIID; nsISecretDecoderRing: nsJSIID; nsISecurityUITelemetry: nsJSIID; + nsISiteIntegrityService: nsJSIID; nsISiteSecurityService: nsJSIID< nsISiteSecurityService, typeof nsISiteSecurityService_ResetStateBy @@ -20423,7 +20430,7 @@ declare global { >; nsIPlacesPreviewsHelperService: nsJSIID; nsITaggingService: nsJSIID; - nsIPrefBranch: nsJSIID; + nsIPrefBranch: nsJSIID; nsIPrefLocalizedString: nsJSIID; nsIPrefOverrideMap: nsJSIID; nsIPrefStatsCallback: nsJSIID; @@ -20441,6 +20448,10 @@ declare global { nsIApplicationReputationQuery: nsJSIID; nsIApplicationReputationCallback: nsJSIID; mozISandboxSettings: nsJSIID; + mozISandboxReport: nsJSIID; + mozISandboxReportArray: nsJSIID; + mozISandboxReporter: nsJSIID; + nsIFormFillFocusListener: nsJSIID; nsIFormFillController: nsJSIID; nsIFormFillCompleteObserver: nsJSIID; mozIBridgedSyncEngineCallback: nsJSIID; @@ -20491,6 +20502,7 @@ declare global { nsIBounceTrackingProtection, typeof nsIBounceTrackingProtection_Modes >; + nsIBounceTrackingRecord: nsJSIID; nsIContentBlockingAllowList: nsJSIID; nsIPartitioningExceptionListObserver: nsJSIID; nsIPartitioningExceptionListService: nsJSIID; @@ -20500,6 +20512,7 @@ declare global { nsIURLQueryStringStripper: nsJSIID; nsIURLQueryStrippingListObserver: nsJSIID; nsIURLQueryStrippingListService: nsJSIID; + nsIScopedPrefs: nsJSIID; nsIAsyncShutdownBlocker: nsJSIID; nsIAsyncShutdownClient: nsJSIID; nsIAsyncShutdownCompletionCallback: nsJSIID; @@ -20511,6 +20524,8 @@ declare global { nsIClearBySiteEntry: nsJSIID; nsIClearDataService: nsJSIID; nsIClearDataCallback: nsJSIID; + nsIPBMCleanupCallback: nsJSIID; + nsIPBMCleanupCollector: nsJSIID; nsIContentAnalysisAcknowledgement: nsJSIID< nsIContentAnalysisAcknowledgement, typeof nsIContentAnalysisAcknowledgement_Result & @@ -20539,7 +20554,6 @@ declare global { nsICookieBannerService, typeof nsICookieBannerService_Modes >; - nsICookieBannerTelemetryService: nsJSIID; nsICookieRule: nsJSIID; nsICrashService: nsJSIID; nsIFinalizationWitnessService: nsJSIID; @@ -20555,14 +20569,6 @@ declare global { nsIRFPService: nsJSIID; nsIRFPTargetSetIDL: nsJSIID; nsIUserCharacteristicsPageService: nsJSIID; - nsISearchSubmission: nsJSIID; - nsISearchEngine: nsJSIID; - nsISearchParseSubmissionResult: nsJSIID; - nsISearchService: nsJSIID< - nsISearchService, - typeof nsISearchService_OpenSearchInstallErrors & - typeof nsISearchService_DefaultEngineChangeReason - >; nsIToolkitShellService: nsJSIID; nsITerminatorTest: nsJSIID; nsIXULStore: nsJSIID; @@ -20739,6 +20745,7 @@ declare global { nsIWindowWatcher: nsJSIID; nsITabUnloader: nsJSIID; nsIAvailableMemoryWatcherBase: nsJSIID; + nsIAvailableMemoryWatcherTestingLinux: nsJSIID; nsIConsoleListener: nsJSIID; nsIConsoleMessage: nsJSIID; nsIConsoleService: nsJSIID< @@ -20845,7 +20852,6 @@ declare global { nsIObjectOutputStream: nsJSIID; nsIOutputStream: nsJSIID; nsIPipe: nsJSIID; - nsISearchableInputStream: nsJSIID; nsIRandomAccessStream: nsJSIID; nsISafeOutputStream: nsJSIID; nsIScriptableBase64Encoder: nsJSIID; @@ -20867,7 +20873,6 @@ declare global { nsIGIOService: nsJSIID; nsIGeolocationUpdate: nsJSIID; nsIGeolocationProvider: nsJSIID; - nsIHapticFeedback: nsJSIID; nsIPlatformInfo: nsJSIID; nsISystemInfo: nsJSIID; nsIXULAppInfo: nsJSIID; @@ -20880,7 +20885,7 @@ declare global { nsIEnvironment: nsJSIID; nsIEventTarget: nsJSIID< nsIEventTarget, - typeof nsIEventTarget_DispatchFlags + typeof nsIEventTarget_DispatchFlags & typeof nsIEventTarget_FeatureFlags >; nsINamed: nsJSIID; nsIProcess: nsJSIID; @@ -20931,6 +20936,9 @@ declare global { nsIControllers: nsJSIID; nsINativeAppSupport: nsJSIID; nsIXREDirProvider: nsJSIID; + nsIZenCommonUtils: nsJSIID; + nsIZenDragAndDrop: nsJSIID; + nsIZenModsBackend: nsJSIID; nsIZipWriter: nsJSIID; } } // global @@ -20945,8 +20953,6 @@ type DOMTimeStamp = u64; type EpochTimeStamp = u64; type PRTime = i64; type PivotMoveReason = i16; -type PredictorLearnReason = u32; -type PredictorPredictReason = u32; type RequireTrustedTypesForDirectiveState = nsIContentSecurityPolicy.RequireTrustedTypesForDirectiveState; type nsBitsErrorAction = i32; diff --git a/src/zen/boosts/ZenBoostStyles.sys.mjs b/src/zen/boosts/ZenBoostStyles.sys.mjs new file mode 100644 index 000000000..40c230f6f --- /dev/null +++ b/src/zen/boosts/ZenBoostStyles.sys.mjs @@ -0,0 +1,124 @@ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +/* 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/. */ + +import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; + +const lazy = XPCOMUtils.declareLazy({ + styleSheetService: { + service: "@mozilla.org/content/style-sheet-service;1", + iid: Ci.nsIStyleSheetService, + }, +}); + +const AGENT_SHEET = Ci.nsIStyleSheetService.AGENT_SHEET; + +export class nsZenBoostStyles { + #stylesCache = new Map(); + + /** + * Retrieves the CSS style string for a given boost configuration. + * Caches styles to optimize performance. + * + * @param {object} boostData - The boost configuration data. + * @param {string} domain - The domain associated with the boost. + * @returns {string} The generated CSS style string. + */ + getStyleForBoost(boostData, domain) { + if (this.#stylesCache.has(domain)) { + return this.#stylesCache.get(domain); + } + + const rawStyle = this.#generateStyleString(boostData); + if (!rawStyle || rawStyle.trim() === "") { + return null; + } + + const styleUri = this.#convertStyleToDataUri(rawStyle); + this.#cacheStyle(styleUri, domain); + return this.getStyleForBoost(boostData, domain); + } + + invalidateStyleForDomain(domain) { + if (this.#stylesCache.has(domain)) { + const { uri } = this.#stylesCache.get(domain); + lazy.styleSheetService.unregisterSheet(uri, AGENT_SHEET); + this.#stylesCache.delete(domain); + } + } + + /** + * Generates a CSS style string based on the boost configuration. + * + * @param {object} boostData - The boost configuration data. + * @returns {string} The generated CSS style string. + * @private + */ + #generateStyleString(boostData) { + let style = ``; + + const fontFamily = + boostData.fontFamily != "" + ? `font-family: ${boostData.fontFamily} !important;` + : ``; + const fontCase = + boostData.textCaseOverride != "none" + ? `text-transform: ${boostData.textCaseOverride} !important;` + : ``; + + let zapBlocks = ""; + if (boostData.zapSelectors) { + for (const selector of boostData.zapSelectors) { + zapBlocks += `${selector}:not([zen-zap-unhide]){ display: none !important; }\n`; + } + + if (zapBlocks != "") { + style += `/* Zen-Zaps */\n`; + style += `${zapBlocks}\n`; + } + } + + if (fontCase != "" || fontFamily != "") { + style += `/* Text Format */\n`; + style += `body * {\n`; + style += `${fontFamily}\n`; + style += `${fontCase}\n`; + style += `}\n`; + } + + if ((boostData.customCSS || "").trim() != "") { + style += `/* USER CSS */\n`; + style += `${boostData.customCSS || ""}\n`; + } + + return style; + } + + /** + * Converts a raw CSS style string into a data URI. + * + * @param {string} rawStyle - The raw CSS style string. + * @returns {string} The data URI representing the CSS style. + * @private + */ + #convertStyleToDataUri(rawStyle) { + const encodedStyle = encodeURIComponent(rawStyle); + return Services.io.newURI(`data:text/css;charset=utf-8,${encodedStyle}`); + } + + /** + * Prefetches the style from the data URI and caches it. + * + * @param {string} styleUri - The data URI of the CSS style. + * @param {string} domain - The domain associated with the boost. + * @private + */ + #cacheStyle(styleUri, domain) { + this.#stylesCache.set(domain, { + uuid: Services.uuid.generateUUID().toString(), + uri: styleUri, + }); + } +} diff --git a/src/zen/boosts/ZenBoostsEditor.mjs b/src/zen/boosts/ZenBoostsEditor.mjs new file mode 100644 index 000000000..d005125f0 --- /dev/null +++ b/src/zen/boosts/ZenBoostsEditor.mjs @@ -0,0 +1,1327 @@ +/* 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 { gZenBoostsManager } = ChromeUtils.importESModule( + "resource:///modules/zen/boosts/ZenBoostsManager.sys.mjs" +); + +export class nsZenBoostEditor { + doc = null; + editorWindow = null; + openerWindow = null; + codeEditorReady = false; + + static OBSERVERS = [ + "zen-boosts-kill-editor", + "zap-list-update", + "zap-state-update", + "selector-picker-state-update", + "zen-boosts-active-change", + ]; + + /** + * Creates a new boost editor instance for the specified domain. + * + * @param {Document} doc - The document object for the editor window. + * @param {string} domain - The domain for which to edit the boost. + * @param {Window} editorWindow - The window object for the editor. + * @param {Window} openerWindow - The window object which instanced this editor. + */ + constructor(doc, domain, editorWindow, openerWindow) { + this.doc = doc; + this.editorWindow = editorWindow; + this.openerWindow = openerWindow; + + this._codeEditorWidth = 450; + this._boostEditorWidth = 185; + this._pickerCallback = null; + + this.isMouseDown = false; + this.wasDragging = false; + this.mouseDownPosition = { x: 0, y: 0 }; + this.lastDotSetPos = { x: 0, y: 0 }; + this.currentBoostData = null; + this.boostInfo = null; + + this.killOtherEditorInstances(); + + nsZenBoostEditor.OBSERVERS.forEach(observe => { + Services.obs.addObserver(this, observe); + }); + + this.init(); + this.initColorPicker(); + this.initFonts(); + this.loadBoost(domain); + } + + /** + * Initializes the boost editor by setting up event listeners for all UI controls. + */ + init() { + this.editorWindow.addEventListener("unload", () => this.handleClose(), { + once: true, + }); + + this.doc.getElementById("zen-boost-editor-root").style.display = "flex"; + this.doc.getElementById("zen-boost-code-editor-root").style.display = + "none"; + + this.doc + .getElementById("zen-boost-color-contrast") + .addEventListener("input", this.onColorOptionChange.bind(this)); + this.doc + .getElementById("zen-boost-color-brightness") + .addEventListener("input", this.onColorOptionChange.bind(this)); + this.doc + .getElementById("zen-boost-color-saturation") + .addEventListener("input", this.onColorOptionChange.bind(this)); + + this.doc + .getElementById("zen-boost-text-case-toggle") + .addEventListener("click", this.onBoostCasePressed.bind(this)); + this.doc + .getElementById("zen-boost-zap") + .addEventListener("click", this.onZapButtonPressed.bind(this)); + this.doc + .getElementById("zen-boost-code") + .addEventListener("click", this.onCodeButtonPressed.bind(this)); + this.doc + .getElementById("zen-boost-back") + .addEventListener("click", this.onCodeBackButtonPressed.bind(this)); + this.doc + .getElementById("zen-boost-disable") + .addEventListener("click", this.onToggleDisable.bind(this)); + this.doc + .getElementById("zen-boost-invert") + .addEventListener("click", this.onToggleInvert.bind(this)); + this.doc + .getElementById("zen-boost-controls") + .addEventListener("click", event => this.openAdvancedColorOptions(event)); + this.doc + .getElementById("zen-boost-save") + .addEventListener("click", this.onSaveBoostClick.bind(this)); + this.doc + .getElementById("zen-boost-load") + .addEventListener("click", this.onLoadBoostClick.bind(this)); + this.doc + .getElementById("zen-boost-name-container") + .addEventListener("click", this.onNameTextClick.bind(this)); + this.doc + .getElementById("zen-boost-close") + .addEventListener("click", this.onClosePressed.bind(this)); + this.doc + .getElementById("zen-boost-shuffle") + .addEventListener("click", this.shuffleBoost.bind(this)); + this.doc + .getElementById("zen-boost-css-picker") + .addEventListener("click", this.onPickerButtonPressed.bind(this)); + this.doc + .getElementById("zen-boost-css-inspector") + .addEventListener("click", this.onInspectorButtonPressed.bind(this)); + + this.doc.addEventListener("keydown", event => { + if ( + event.key === "Escape" || + (event.key === "w" && (event.ctrlKey || event.metaKey)) + ) { + this.onClosePressed(); + } + }); + + this.initialized = true; + } + + /** + * Uninitializes the boost editor by cleaning up event listeners and observers. + */ + uninit() { + this.uninitColorPicker(); + + nsZenBoostEditor.OBSERVERS.forEach(observe => { + Services.obs.removeObserver(this, observe); + }); + } + + /** + * Kills other editor instances by sending a notification to close them. + * This ensures only one editor instance is open at a time. + */ + killOtherEditorInstances() { + Services.obs.notifyObservers(null, "zen-boosts-kill-editor"); + } + + /** + * Observer callback that handles notifications from the observer service. + * Closes the editor window when a 'zen-boosts-kill-editor' notification is received. + * + * @param {object} subject - The subject of the notification. + * @param {string} topic - The topic of the notification. + * @param {*} data - The message data. + */ + observe(subject, topic, data) { + switch (topic) { + case "zap-state-update": + this.onUpdateZapButtonVisual(); + break; + case "selector-picker-state-update": + this.onUpdatePickerButtonVisual(); + this.onUpdatePickerObserver(data); + break; + case "selector-picker-picked": + this.onPickerPickedCallback(data); + break; + case "zap-list-update": + this.onUpdateZapValue(); + this.currentBoostData.changeWasMade = true; + break; + case "zen-boosts-kill-editor": + this.editorWindow.close(); + break; + case "zen-boosts-active-change": + this.editorWindow.close(); + break; + } + } + + /** + * Initializes the code editor for the css editor + */ + async initCodeEditor() { + if (this.codeEditorReady) { + return; + } + + const { DevToolsLoader } = ChromeUtils.importESModule( + "resource://devtools/shared/loader/Loader.sys.mjs" + ); + const loader = new DevToolsLoader({ + invisibleToDebugger: true, + }); + const { require } = loader; + const Editor = require("resource://devtools/client/shared/sourceeditor/editor"); + const container = this.doc.getElementById("zen-boost-code-editor"); + + const editor = new Editor({ + mode: Editor.modes.css, + lineNumbers: true, + theme: "default", // default is light theme + readOnly: false, + gutters: ["CodeMirror-linenumbers"], + }); + + await editor.appendTo(container); + editor.refresh(); + editor.on("change", this.onCodeEditorChange.bind(this)); + + this.editorWindow._editor = editor; + this.codeEditorReady = true; + } + + /** + * Inserts a code snippet at the current cursor position + * + * @param {string} code The code to insert + */ + insertCode(code) { + if (!code) { + code = ""; + } + + const cm = this.editorWindow._editor.codeMirror; + const cursor = cm.getCursor(); // { line, ch } + cm.replaceRange(code, cursor); + cm.focus(); + } + + /** + * Inserts a code snippet at the end of the code + * + * @param {string} code The code to insert + */ + appendCode(code) { + if (!code) { + code = ""; + } + + const cm = this.editorWindow._editor.codeMirror; + const line = cm.lineCount(); + const content = this.editorWindow._editor.getText(); + const ch = 0; + + if (content == "") { + cm.replaceRange(code, { line, ch }); + } else { + cm.replaceRange(`\n${code}`, { line, ch }); + } + + cm.focus(); + } + + onCodeEditorChange() { + this.currentBoostData.customCSS = this.editorWindow._editor.getText(); + this.currentBoostData.changeWasMade = true; + this.updateCurrentBoost(); + } + + get commonFonts() { + const cFonts = [ + "Arial", + "Times New Roman", + "Courier New", + "Georgia", + "Comic Sans MS", + "Verdana", + "Trebuchet MS", + "Impact", + "Palatino Linotype", + "Tahoma", + ]; + return cFonts; + } + + /** + * Initializes the font selection UI by creating font buttons and dropdown options + * for the available font families. + */ + initFonts() { + const commonFonts = this.commonFonts; + const fonts = this.fetchFontList(); + + const fontButtonGroup = this.doc.getElementById("zen-boost-font-grid"); + const fontList = this.doc.getElementById("zen-boost-font-select"); + const buttonCount = 10; + + for (let i = 0; i < Math.min(commonFonts.length, buttonCount); i++) { + let font = fonts[i]; // Fallback + if (fonts.includes(commonFonts[i])) { + font = commonFonts[i]; + } + + const fontButton = this.doc.createElement("button"); + fontButton.setAttribute("font-data", `${font}`); + fontButton.classList.add("subviewbutton"); + fontButton.style.fontFamily = `'${font}'`; + fontButton.innerHTML = "Aa"; + fontButton.title = font; + fontButton.addEventListener("click", this.onFontButtonClick.bind(this)); + + fontButtonGroup.appendChild(fontButton); + } + + // Add default value + const defaultOption = this.doc.createElement("option"); + defaultOption.value = ""; // Use default font of site + defaultOption.label = "Default"; + fontList.appendChild(defaultOption); + fontList.appendChild(this.doc.createElement("hr")); + + for (let j = 0; j < fonts.length; j++) { + const font = fonts[j]; + const option = this.doc.createElement("option"); + option.style.fontFamily = `'${font}'`; + option.value = font; + option.label = font; + fontList.appendChild(option); + } + + fontList.addEventListener("change", this.onFontDropdownSelect.bind(this)); + } + + /** + * Fetches a list of all available system fonts. + * + * @returns {Array} An array with names of available fonts. + */ + fetchFontList() { + const enumerator = Cc["@mozilla.org/gfx/fontenumerator;1"].createInstance( + Ci.nsIFontEnumerator + ); + + return enumerator.EnumerateFonts(null, null); + } + + /** + * Handles the code editor button press, resizing and offsetting the window and enabling the code view + */ + onCodeButtonPressed() { + const offset = 265; + const openRightAligned = + this.openerWindow.screenX + this.openerWindow.outerWidth / 2 < + this.editorWindow.screenX; + const windowElem = this.doc.getElementById("zenBoostWindow"); + + if (windowElem.getAttribute("editor") == "code") { + return; + } + windowElem.setAttribute("editor", "code"); + + // Store the old boost editor width. + // The window needs the outer width which includes + // window chrome. This results in the window + // being smaller than it should be + this._boostEditorWidth = this.editorWindow.outerWidth; + + this.editorWindow.resizeTo( + this._codeEditorWidth, + this.editorWindow.outerHeight + ); + if (openRightAligned) { + this.editorWindow.moveTo( + this.editorWindow.screenX - offset, + this.editorWindow.screenY + ); + } + + this.doc.getElementById("zen-boost-editor-root").style.display = "none"; + this.doc.getElementById("zen-boost-code-editor-root").style.display = + "initial"; + } + + /** + * Handles the back button in the code view, resizing and offsetting the window and changing back to boost view + */ + onCodeBackButtonPressed() { + const offset = 265; + const openRightAligned = + this.openerWindow.screenX + this.openerWindow.outerWidth / 2 < + this.editorWindow.screenX; + const windowElem = this.doc.getElementById("zenBoostWindow"); + + if (windowElem.getAttribute("editor") == "boost") { + return; + } + windowElem.setAttribute("editor", "boost"); + + this.editorWindow.resizeTo( + this._boostEditorWidth, + this.editorWindow.outerHeight + ); + if (openRightAligned) { + this.editorWindow.moveTo( + this.editorWindow.screenX + offset, + this.editorWindow.screenY + ); + } + + this.doc.getElementById("zen-boost-editor-root").style.display = "flex"; + this.doc.getElementById("zen-boost-code-editor-root").style.display = + "none"; + + // Disable picker mode + this.disableAllPickers(); + } + + async onZapButtonPressed() { + const linkedBrowser = this.openerWindow.gBrowser.selectedTab.linkedBrowser; + const actor = + linkedBrowser.browsingContext.currentWindowGlobal.getActor("ZenBoosts"); + actor.sendQuery("ZenBoost:ToggleZapMode"); + + // Focus the parent browser window + this.openerWindow.focus(); + } + + async onPickerButtonPressed() { + const linkedBrowser = this.openerWindow.gBrowser.selectedTab.linkedBrowser; + const actor = + linkedBrowser.browsingContext.currentWindowGlobal.getActor("ZenBoosts"); + actor.sendQuery("ZenBoost:TogglePickerMode"); + this.openerWindow.focus(); + } + + onPickerPickedCallback(cssSelector) { + this.disableAllPickers(); + + // Insert the css selector at the cursor position in the css editor + this.appendCode(` +${cssSelector} { + +}`); + + Services.obs.removeObserver(this, "selector-picker-picked"); + } + + /** + * Disables zap mode and picker mode + */ + disableAllPickers() { + Services.obs.notifyObservers(null, "zen-boosts-disable-zap"); + Services.obs.notifyObservers(null, "zen-boosts-disable-picker"); + } + + onInspectorButtonPressed() { + const linkedBrowser = this.openerWindow.gBrowser.selectedTab.linkedBrowser; + const actor = + linkedBrowser.browsingContext.currentWindowGlobal.getActor("ZenBoosts"); + actor.sendQuery("ZenBoost:OpenInspector"); + } + + async onUpdateZapButtonVisual() { + const linkedBrowser = this.openerWindow.gBrowser.selectedTab.linkedBrowser; + const actor = + linkedBrowser.browsingContext.currentWindowGlobal.getActor("ZenBoosts"); + const zapButton = this.doc.getElementById("zen-boost-zap"); + + const zapEnabled = await actor.sendQuery("ZenBoost:ZapModeEnabled"); + // Checks if there are any zaps + const zapAny = await actor.sendQuery("ZenBoost:ZapModeAny"); + + zapButton.setAttribute("enabled", zapEnabled || zapAny ? "true" : "false"); + } + + async onUpdatePickerButtonVisual() { + const linkedBrowser = this.openerWindow.gBrowser.selectedTab.linkedBrowser; + const actor = + linkedBrowser.browsingContext.currentWindowGlobal.getActor("ZenBoosts"); + + const pickerButton = this.doc.getElementById("zen-boost-css-picker"); + const selectEnabled = await actor.sendQuery( + "ZenBoost:SelectorPickerModeEnabled" + ); + + pickerButton.setAttribute("enabled", selectEnabled ? "true" : "false"); + } + + onUpdatePickerObserver(data) { + if (!data) { + return; + } + + if (data == "onenable") { + Services.obs.addObserver(this, "selector-picker-picked"); + } else if (data == "ondisable") { + Services.obs.removeObserver(this, "selector-picker-picked"); + } + } + + onUpdateZapValue() { + const zapButton = this.doc.getElementById("zen-boost-zap"); + const zapValueBox = this.doc.getElementById("zen-boost-zap-value"); + const zapCount = this.currentBoostData.zapSelectors.length; + + if (zapCount == 0) { + zapValueBox.textContent = ""; + zapButton.setAttribute("hideicon", "false"); + } else { + zapValueBox.textContent = zapCount; + zapButton.setAttribute("hideicon", "true"); + } + } + + /** + * Initializes the color picker by setting up mouse event listeners for + * interactive color selection on the gradient picker. + */ + initColorPicker() { + const themePicker = this.doc.querySelector( + ".zen-boost-color-picker-gradient" + ); + this._onMouseMove = this.onMouseMove.bind(this); + this._onMouseUp = this.onMouseUp.bind(this); + this._onMouseDown = this.onMouseDown.bind(this); + this._onThemePickerClick = this.onThemePickerClick.bind(this); + this.doc.addEventListener("mousemove", this._onMouseMove); + this.doc.addEventListener("mouseup", this._onMouseUp); + themePicker.addEventListener("mousedown", this._onMouseDown); + themePicker.addEventListener("click", this._onThemePickerClick); + } + + /** + * Uninitializes the color picker by removing all mouse event listeners. + */ + uninitColorPicker() { + const themePicker = this.doc.querySelector( + ".zen-boost-color-picker-gradient" + ); + this.doc.removeEventListener("mousemove", this._onMouseMove); + this.doc.removeEventListener("mouseup", this._onMouseUp); + themePicker.removeEventListener("mousedown", this._onMouseDown); + themePicker.removeEventListener("click", this._onThemePickerClick); + this._onThemePickerClick = null; + this._onMouseMove = null; + this._onMouseUp = null; + this._onMouseDown = null; + } + + /** + * Handles mouse move events to update the color picker dot position while dragging. + * + * @param {MouseEvent} event - The mouse move event. + */ + onMouseMove(event) { + const minDragDistance = 4; + let nDistance = Math.sqrt( + (event.clientX - this.mouseDownPosition.x) ** 2 + + (event.clientY - this.mouseDownPosition.y) ** 2 + ); + + if (this.isMouseDown && nDistance > minDragDistance) { + this.wasDragging = true; + event.preventDefault(); + + if (event.target.id != "zen-boost-magic-theme") { + this.setDotPos(event.clientX, event.clientY, false); + } + } + } + + /** + * Handles mouse down events to initiate color picker dragging. + * + * @param {MouseEvent} event - The mouse down event. + */ + onMouseDown(event) { + if (event.button === 2) { + return; + } + + this.mouseDownPosition = { x: event.clientX, y: event.clientY }; + this.isMouseDown = true; + } + + /** + * Handles mouse up events to end color picker dragging. + * + * @param {MouseEvent} event - The mouse up event. + */ + onMouseUp(event) { + if (event.button === 2) { + return; + } + + this.isMouseDown = false; + this.wasDragging = false; + } + + /** + * Handles the text case toggle button press, cycling through case override options + * (none, lower, upper) and updating the UI accordingly. + */ + onBoostCasePressed() { + if (this.currentBoostData.textCaseOverride == "lowercase") { + this.currentBoostData.textCaseOverride = "uppercase"; + } else if (this.currentBoostData.textCaseOverride == "uppercase") { + this.currentBoostData.textCaseOverride = "none"; + } else { + this.currentBoostData.textCaseOverride = "lowercase"; + } + + this.updateCaseButtonVisuals(); + this.updateCurrentBoost(); + } + + /** + * Handles changes to color option sliders (contrast, brightness, saturation) + * and updates the current boost data accordingly. + */ + onColorOptionChange() { + this.currentBoostData.contrast = this.doc.getElementById( + "zen-boost-color-contrast" + ).value; + this.currentBoostData.brightness = this.doc.getElementById( + "zen-boost-color-brightness" + ).value; + this.currentBoostData.saturation = this.doc.getElementById( + "zen-boost-color-saturation" + ).value; + + this.updateCurrentBoost(); + } + + /** + * Opens the advanced color options popup panel. + * + * @param {Event} event - The click event that triggered this action. + */ + openAdvancedColorOptions(event) { + const panel = this.doc.getElementById( + "zen-boost-advanced-color-options-panel" + ); + panel.openPopup(event.target, "bottomcenter topcenter", 0, 2); + } + + /** + * Resets the color picker dot to the center position (default state). + */ + resetDotPosition() { + this.setDotPos(null, null); + } + + /** + * Handles clicks on the theme picker gradient or magic theme button. + * Updates the dot position or toggles auto-theme mode based on the click target. + * + * @param {MouseEvent} event - The click event. + */ + onThemePickerClick(event) { + event.preventDefault(); + + this.currentBoostData.changeWasMade = true; + + if (event.target.id == "zen-boost-magic-theme") { + this.currentBoostData.enableColorBoost = true; + this.currentBoostData.autoTheme = !this.currentBoostData.autoTheme; + this.updateButtonToggleVisuals(); + this.updateCurrentBoost(); + } else { + this.setDotPos(event.clientX, event.clientY, !this.wasDragging); + } + this.wasDragging = false; + } + + /** + * Sets the position of the color picker dot on the gradient and updates + * the boost data with the corresponding angle and distance values. + * + * @param {number|null} pixelX - The X coordinate in pixels, or null to center the dot. + * @param {number|null} pixelY - The Y coordinate in pixels, or null to center the dot. + * @param {boolean} animate - Whether to animate the dot movement (currently not implemented). + */ + setDotPos(pixelX, pixelY, animate = true) { + const gradient = this.doc.querySelector(".zen-boost-color-picker-gradient"); + const dot = this.doc.querySelector(".zen-boost-color-picker-dot"); + + const rect = gradient.getBoundingClientRect(); + const padding = 50; + + const centerX = rect.left + rect.width / 2; + const centerY = rect.top + rect.height / 2; + const radius = (rect.width - padding) / 2; + + if (!animate) { + let nDistance = Math.sqrt( + (pixelX - this.lastDotSetPos.x) ** 2 + + (pixelY - this.lastDotSetPos.y) ** 2 + ); + + if (nDistance > 15) { + // Optional haptic feedback + // Services.zen.playHapticFeedback(); + + this.lastDotSetPos = { + x: pixelX, + y: pixelY, + }; + } + } + + if (pixelX == null || pixelY == null) { + pixelX = centerX; + pixelY = centerY; + + this.currentBoostData.dotAngleDeg = 0; + this.currentBoostData.dotDistance = 0; + } else { + let distance = Math.sqrt( + (pixelX - centerX) ** 2 + (pixelY - centerY) ** 2 + ); + distance = Math.min(distance, radius); // Clamp distance + + const angle = Math.atan2(pixelY - centerY, pixelX - centerX); + + pixelX = centerX + Math.cos(angle) * distance; + pixelY = centerY + Math.sin(angle) * distance; + + // Rad to degree + this.currentBoostData.dotAngleDeg = + ((Math.atan2(pixelY - centerY, pixelX - centerX) * 180) / Math.PI + + 100) % + 360; + if (this.currentBoostData.dotAngleDeg < 0) { + this.currentBoostData.dotAngleDeg += 360; + } + + // Map to 0-1 range + this.currentBoostData.dotDistance = distance / radius; + + // Enable color boosting again + if (!this.currentBoostData.enableColorBoost) { + this.onToggleDisable(false); + } + this.currentBoostData.autoTheme = false; + } + + const relativeX = pixelX - rect.left; + const relativeY = pixelY - rect.top; + + // Capture normalized position of dot for restoring it correctly later + this.currentBoostData.dotPos.x = relativeX / rect.width; + this.currentBoostData.dotPos.y = relativeY / rect.height; + + dot.setAttribute("animated", animate ? "true" : "false"); + dot.style.left = `${relativeX}px`; + dot.style.top = `${relativeY}px`; + + this.updateButtonToggleVisuals(); + this.updateDot(); + this.updateCircleRadius(animate); + this.updateCurrentBoost(); + } + + /** + * Updates the visual appearance of the color picker dot + * based on the current boost data's angle and distance values. + */ + updateDot() { + const dot = this.doc.querySelector(".zen-boost-color-picker-dot"); + dot.style.setProperty( + "--zen-theme-picker-dot-color", + `hsl(${this.currentBoostData.dotAngleDeg}deg, ${this.currentBoostData.dotDistance * 100}%, 55%)` + ); + } + + /** + * Updates the radius of the circle based on the dot's position. + * + * @param {boolean} animate - Whether to animate the radius change (default: true). + */ + updateCircleRadius(animate = true) { + const gradient = this.doc.querySelector(".zen-boost-color-picker-gradient"); + const rect = gradient.getBoundingClientRect(); + const padding = 50; + const radius = (rect.width - padding) / 2; + + // Updating the circle size to match the distance of the point + const circle = this.doc.querySelector(".zen-boost-color-picker-circle"); + circle.setAttribute("animated", animate ? "true" : "false"); + circle.style.width = `${this.currentBoostData.dotDistance * radius * 2}px`; + circle.style.height = `${this.currentBoostData.dotDistance * radius * 2}px`; + } + + /** + * Toggles the color boost enable/disable state. + * + * @param {boolean} userAction - Whether this was triggered by a user action (default: true). + */ + onToggleDisable(userAction = true) { + this.currentBoostData.enableColorBoost = + !this.currentBoostData.enableColorBoost; + + if (userAction) { + this.currentBoostData.changeWasMade = true; + } + + this.updateButtonToggleVisuals(); + this.updateCurrentBoost(); + } + + /** + * Toggles the smart invert feature, which automatically inverts colors + * based on the window's color scheme. + * + * @param {boolean} userAction - Whether this was triggered by a user action (default: true). + */ + onToggleInvert(userAction = true) { + this.currentBoostData.smartInvert = !this.currentBoostData.smartInvert; + + if (userAction) { + this.currentBoostData.changeWasMade = true; + } + + this.updateButtonToggleVisuals(); + this.updateCurrentBoost(); + } + + /** + * Updates the visual state of the text case toggle button based on the current + * text case override value (none, upper, or lower). + */ + updateCaseButtonVisuals() { + const sizeValue = this.doc.getElementById("zen-boost-text-case-toggle"); + sizeValue.setAttribute("case-mode", this.currentBoostData.textCaseOverride); + } + + /** + * Updates the visual state of all toggle buttons (invert, disable, auto-theme) + * and applies grayscale effect to the gradient when color boosting is disabled. + */ + updateButtonToggleVisuals() { + const invertButton = this.doc.getElementById("zen-boost-invert"); + const disableButton = this.doc.getElementById("zen-boost-disable"); + const autoThemeButton = this.doc.getElementById("zen-boost-magic-theme"); + const gradient = this.doc.querySelector(".zen-boost-color-picker-gradient"); + + if (this.currentBoostData.autoTheme) { + autoThemeButton.classList.add("zen-boost-button-active"); + } else { + autoThemeButton.classList.remove("zen-boost-button-active"); + } + + if (this.currentBoostData.smartInvert) { + invertButton.classList.add("zen-boost-button-active"); + } else { + invertButton.classList.remove("zen-boost-button-active"); + } + + if (this.currentBoostData.smartInvert) { + invertButton.classList.add("zen-boost-button-active"); + } else { + invertButton.classList.remove("zen-boost-button-active"); + } + + if (!this.currentBoostData.enableColorBoost) { + disableButton.classList.add("zen-boost-button-active-transparent"); + } else { + disableButton.classList.remove("zen-boost-button-active-transparent"); + } + + // Give the gradient a grayscale effect + // when the color boosting is disabled + // or the theme is set automatically + if ( + !this.currentBoostData.enableColorBoost || + this.currentBoostData.autoTheme + ) { + gradient.classList.add("zen-boost-panel-disabled"); + } else { + gradient.classList.remove("zen-boost-panel-disabled"); + } + } + + /** + * Updates the value of the sliders with the current boost data + */ + updateColorControlSliderVisuals() { + const contrastSlider = this.doc.getElementById("zen-boost-color-contrast"); + const brightnessSlider = this.doc.getElementById( + "zen-boost-color-brightness" + ); + const saturationSlider = this.doc.getElementById( + "zen-boost-color-saturation" + ); + + contrastSlider.value = this.currentBoostData.contrast; + brightnessSlider.value = this.currentBoostData.brightness; + saturationSlider.value = this.currentBoostData.saturation; + } + + /** + * Handles font button clicks to change the selected font family. + * + * @param {Event} event - The click event from a font button. + */ + onFontButtonClick(event) { + const font = event?.target?.getAttribute("font-data") ?? ""; + this.onFontChange(font); + } + + /** + * Handles font dropdown selection changes to change the selected font family. + * + * @param {Event} event - The change event from the font dropdown. + */ + onFontDropdownSelect(event) { + const select = event.target; + this.onFontChange(select.value); + } + + /** + * Changes the font family for the boost. If the same font is selected again, + * it clears the font override (sets to empty string). + * + * @param {string} font - The font family string to apply. + */ + onFontChange(font) { + if (this.currentBoostData.fontFamily == font) { + this.currentBoostData.fontFamily = ""; + } else { + this.currentBoostData.fontFamily = font; + } + this.updateFontButtonVisuals(); + + this.currentBoostData.changeWasMade = true; + this.updateCurrentBoost(); + } + + /** + * Updates the visual state of font selection buttons and dropdown + * to reflect the currently selected font family. + */ + updateFontButtonVisuals() { + const fontButtonGroup = this.doc.getElementById("zen-boost-font-grid"); + let foundActive = false; + for (let i = 0; i < fontButtonGroup.children.length; i++) { + const fontButton = fontButtonGroup.children[i]; + if ( + fontButton.getAttribute("font-data") == this.currentBoostData.fontFamily + ) { + fontButton.classList.add("zen-boost-font-button-active"); + foundActive = true; + } else { + fontButton.classList.remove("zen-boost-font-button-active"); + } + } + + const fontSelect = this.doc.getElementById("zen-boost-font-select"); + for (let i = 0; i < fontSelect.options.length; i++) { + const option = fontSelect.options[i]; + if (option.value == this.currentBoostData.fontFamily) { + fontSelect.value = option.value; + break; + } + } + if (this.currentBoostData.fontFamily !== "" && !foundActive) { + fontSelect.setAttribute("has-selection", "true"); + } else { + fontSelect.removeAttribute("has-selection"); + } + } + + /** + * Updates the boost data in the boosts manager with the current boost data. + * This triggers notifications to observers but does not persist to disk. + */ + updateCurrentBoost() { + const boost = gZenBoostsManager.loadBoostFromStore( + this.boostInfo.domain, + this.boostInfo.id + ); + boost.boostEntry.boostData = this.currentBoostData; + gZenBoostsManager.updateBoost(boost); + } + + /** + * Deletes the current boost for the domain and closes the editor window. + */ + deleteBoost() { + const boost = gZenBoostsManager.loadBoostFromStore( + this.boostInfo.domain, + this.boostInfo.id + ); + gZenBoostsManager.deleteBoost(boost); + + this.currentBoostData = null; + this.editorWindow.close(); + } + + /** + * Handles showing the popup when clicking the name text + * + * @param {Event} event + */ + onNameTextClick(event) { + const renameBoost = this.doc.getElementById("zen-boost-edit-rename"); + const deleteBoost = this.doc.getElementById("zen-boost-edit-delete"); + const resetBoost = this.doc.getElementById("zen-boost-edit-reset"); + + const popup = this.doc.getElementById("zenBoostContextMenu"); + popup.openPopup( + event.target, + "bottomcenter topcenter", + 0, + 0, + true /* isContextMenu */, + false /* attributesOverride */, + event + ); + + // Don't give the user following options if the boost + // is not going to save / not currently saved (unchanged) + renameBoost.disabled = !this.currentBoostData.changeWasMade; + deleteBoost.disabled = !this.currentBoostData.changeWasMade; + resetBoost.disabled = !this.currentBoostData.changeWasMade; + } + + /** + * Handles showing a text field for renaming the boost + */ + async editBoostName() { + const nameText = this.doc.getElementById("zen-boost-name-text"); + + const [title] = await this.doc.l10n.formatMessages([ + "zen-boost-rename-boost-prompt", + ]); + + let input = { + value: this.currentBoostData.boostName, // Default value and also output + }; + const success = await Services.prompt.prompt( + this.openerWindow, + title.value, + null, + input, + null, + { value: false } + ); + + if (!success) { + return; + } + const newName = input.value; + const maxDisplayedNameChars = 10; + + if (newName.trim().length !== 0) { + var truncatedName = newName.substring(0, maxDisplayedNameChars); + this.currentBoostData.boostName = truncatedName; + nameText.textContent = this.currentBoostData.boostName; + this.updateCurrentBoost(); + } + } + + /** + * Handles the close button press by closing the editor window. + */ + onClosePressed() { + this.editorWindow.close(); + } + + /** + * Handles opening a save file dialog and exporting the boost data to a JSON file + */ + async onSaveBoostClick() { + const loadButton = this.doc.getElementById("zen-boost-save"); + loadButton.setAttribute("mode", "blue"); + + const success = await gZenBoostsManager.exportBoost( + this.editorWindow, + this.currentBoostData + ); + + loadButton.setAttribute("mode", ""); + if (success) { + this.openerWindow.gZenUIManager.showToast( + "zen-panel-ui-boosts-exported-message" + ); + } + } + + /** + * Handles opening a load file dialog and importing the boost data to a JSON file + */ + async onLoadBoostClick() { + const loadButton = this.doc.getElementById("zen-boost-load"); + loadButton.setAttribute("mode", "orange-red"); + + const data = await gZenBoostsManager.importBoost(this.editorWindow); + + loadButton.setAttribute("mode", ""); + if (data) { + this.currentBoostData = data; + this.updateAllVisuals(); + this.windowImportAnimation(); + } + } + + /** + * Handles animating the window with the import glint animation + */ + windowImportAnimation() { + const windowWrapper = this.doc.getElementById("zenBoostWindow"); + if (!windowWrapper) { + return; + } + + const element = this.doc.createElement("div"); + element.id = "import-animation"; + + const elementBorder = this.doc.createElement("div"); + elementBorder.id = "import-animation-border"; + + const elementShadow = this.doc.createElement("div"); + elementShadow.id = "import-animation-shadow"; + + this.editorWindow.requestAnimationFrame(() => { + if (this.openerWindow.gReduceMotion) { + element.remove(); + elementBorder.remove(); + elementShadow.remove(); + return; + } + + windowWrapper.appendChild(element); + windowWrapper.appendChild(elementBorder); + windowWrapper.appendChild(elementShadow); + + const anim1 = element.animate( + [ + { top: "100%", opacity: 0.5 }, + { top: "-50%", opacity: 1 }, + ], + { + duration: 350, + delay: 120, + fill: "forwards", + easing: "ease-out", + } + ); + + const anim2 = elementBorder.animate( + [{ "--background-top": "150%" }, { "--background-top": "-50%" }], + { + duration: 350, + delay: 200, + fill: "forwards", + easing: "ease-out", + } + ); + + const anim3 = elementShadow.animate( + [{ opacity: 0 }, { opacity: 1 }, { opacity: 0 }], + { + duration: 460, + fill: "forwards", + easing: "ease-out", + } + ); + + Promise.all([anim1.finished, anim2.finished, anim3.finished]).then(() => { + element.remove(); + elementBorder.remove(); + elementShadow.remove(); + }); + }); + } + + /** + * Shuffles the boost data and updates the presentation + */ + shuffleBoost() { + const availFonts = this.fetchFontList(); + const commonFonts = this.commonFonts; + let font = commonFonts[Math.round(Math.random() * commonFonts.length)]; + if (availFonts.includes(font)) { + this.currentBoostData.fontFamily = font; + } + + this.currentBoostData.smartInvert = Math.random() > 0.5; + this.currentBoostData.autoTheme = false; + + this.currentBoostData.brightness = Math.random(); + this.currentBoostData.contrast = Math.random(); + this.currentBoostData.saturation = Math.random(); + + const gradient = this.doc.querySelector(".zen-boost-color-picker-gradient"); + const rect = gradient.getBoundingClientRect(); + this.setDotPos( + Math.round(rect.left + Math.random() * rect.width), + Math.round(rect.top + Math.random() * rect.height), + true + ); + + this.currentBoostData.changeWasMade = true; + + this.updateCurrentBoost(); + this.updateAllVisuals(); + } + + /** + * Reverts boost data to defaults + */ + resetBoost() { + this.currentBoostData = gZenBoostsManager.getEmptyBoostEntry().boostData; + + this.updateCurrentBoost(); + this.updateAllVisuals(); + } + + /** + * Handles the editor window close event. Saves the boost if changes were made, + * or deletes it if no changes were made (temporary boost). + */ + handleClose() { + this.uninit(); + if (this.currentBoostData != null && this.currentBoostData.changeWasMade) { + this.saveBoost(); + } else if ( + this.currentBoostData != null && + !this.currentBoostData.changeWasMade + ) { + const boost = gZenBoostsManager.loadBoostFromStore( + this.boostInfo.domain, + this.boostInfo.id + ); + gZenBoostsManager.deleteBoost(boost); + } + + this.disableAllPickers(); + } + + /** + * Loads boost data for the specified domain and initializes the editor UI + * with the boost settings (dot position, sliders, buttons, etc.). + * + * @param {string} domain - The domain for which to load the boost. + */ + async loadBoost(domain) { + const boost = gZenBoostsManager.loadActiveBoostFromStore(domain); + this.currentBoostData = boost.boostEntry.boostData; + this.boostInfo = { domain, id: boost.id }; + + // Initial save to register the boost + gZenBoostsManager.saveBoostToStore(boost); + + // The code editor needs time to initialize + await this.initCodeEditor(); + this.updateAllVisuals(); + } + + updateAllVisuals() { + this.doc.getElementById("zen-boost-name-text").textContent = + this.currentBoostData.boostName; + const dot = this.doc.querySelector(".zen-boost-color-picker-dot"); + + if ( + this.currentBoostData.dotPos.x == null || + this.currentBoostData.dotPos.y == null + ) { + this.resetDotPosition(); + } else { + const gradient = this.doc.querySelector( + ".zen-boost-color-picker-gradient" + ); + const rect = gradient.getBoundingClientRect(); + + // Test if the stored position is a non-normalized dot position + if ( + this.currentBoostData.dotPos.x > 1 || + this.currentBoostData.dotPos.x < 0 || + this.currentBoostData.dotPos.y > 1 || + this.currentBoostData.dotPos.y < 0 + ) { + // Normalize position + this.currentBoostData.dotPos.x = + this.currentBoostData.dotPos.x / rect.width; + this.currentBoostData.dotPos.y = + this.currentBoostData.dotPos.y / rect.height; + } + + // Convert normalized position to relative position + const xPos = this.currentBoostData.dotPos.x * rect.width; + const yPos = this.currentBoostData.dotPos.y * rect.height; + + dot.setAttribute("animated", "true"); + dot.style.left = `${xPos}px`; + dot.style.top = `${yPos}px`; + } + + this.editorWindow._editor.setText(this.currentBoostData.customCSS || ""); + + this.updateFontButtonVisuals(); + this.updateCaseButtonVisuals(); + this.updateColorControlSliderVisuals(); + this.updateButtonToggleVisuals(); + this.updateDot(); + this.updateCircleRadius(); + this.onUpdateZapValue(); + this.onUpdateZapButtonVisual(); + } + + /** + * Saves the current boost data to persistent storage if changes were made. + */ + saveBoost() { + if (this.currentBoostData == null || !this.currentBoostData.changeWasMade) { + return; + } + + const boost = gZenBoostsManager.loadBoostFromStore( + this.boostInfo.domain, + this.boostInfo.id + ); + boost.boostEntry.boostData = this.currentBoostData; + + gZenBoostsManager.saveBoostToStore(boost); + } +} diff --git a/src/zen/boosts/ZenBoostsManager.sys.mjs b/src/zen/boosts/ZenBoostsManager.sys.mjs new file mode 100644 index 000000000..92cff3013 --- /dev/null +++ b/src/zen/boosts/ZenBoostsManager.sys.mjs @@ -0,0 +1,773 @@ +/* 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/. */ + +import { JSONFile } from "resource://gre/modules/JSONFile.sys.mjs"; +import { nsZenBoostStyles } from "resource:///modules/zen/boosts/ZenBoostStyles.sys.mjs"; + +class nsZenBoostsManager { + registeredDomains = new Map(); // , activeBoostID: null }> + #stylesManager = new nsZenBoostStyles(); + + #saveFilename = "zen-boosts.jsonlz4"; + + #file = null; + + constructor() { + this.#readBoostsFromStore(this.notify); + } + + /** + * @returns {object} New domain entry with empty boost map and active boost id + */ + #createDomainEntry() { + return { + boostEntries: new Map(), // + activeBoostId: null, + }; + } + + /** + * Will get or create a domain entry for the given domain + * + * @param {string} domain - The domain of which the data will be fetched + * @returns {object} The domain entry + */ + #getOrCreateDomainEntry(domain) { + if (!this.registeredDomains.has(domain)) { + this.registeredDomains.set(domain, this.#createDomainEntry()); + } + return this.registeredDomains.get(domain); + } + + /** + * Will get a domain entry for the given domain + * + * @param {string} domain - The domain of which the data will be fetched + * @returns {object|null} The domain entry + */ + #getDomainEntry(domain) { + if (!this.registeredDomains.has(domain)) { + return null; + } + return this.registeredDomains.get(domain); + } + + /** + * Will delete the domain entry for a domain + * + * @param {string} domain - The given domain + */ + #deleteDomainEntry(domain) { + if (this.registeredDomains.has(domain)) { + this.registeredDomains.delete(domain); + } + } + + /** + * Gets the active boost id for a given domain + * + * @param {string} domain - The target domain + * @returns {string | null} Will return the active boost id or null + */ + getActiveBoostId(domain) { + const domainEntry = this.#getDomainEntry(domain); + if (domainEntry) { + return domainEntry.activeBoostId; + } + return null; + } + + /** + * Deletes a boost for the specified domain and persists the change to disk. + * + * @param {object} boost - The targeted boost. + */ + deleteBoost(boost) { + const { domain, id } = boost; + + if (this.registeredDomains.has(domain)) { + let domainEntry = this.#getOrCreateDomainEntry(domain); + if (domainEntry.boostEntries.has(id)) { + domainEntry.boostEntries.delete(id); + } + if (domainEntry.activeBoostId == id) { + domainEntry.activeBoostId = null; + } + + if (domainEntry.boostEntries.size === 0) { + this.#deleteDomainEntry(domain); + } + } + + this.#stylesManager.invalidateStyleForDomain(domain); + this.notify(true); + + this.#writeToDisk(this.registeredDomains); + } + + /** + * @returns {object} Returns a new empty boost entry + */ + getEmptyBoostEntry() { + return { + boostData: { + boostName: "My Boost", + + dotAngleDeg: 0, + dotPos: { x: null, y: null }, + dotDistance: 0, + + brightness: 0.5, + saturation: 0.5, + contrast: 0.75, + + fontFamily: "", + + enableColorBoost: false, + smartInvert: false, + + // Choses theme based on Zen's workspace theme + autoTheme: false, + + textCaseOverride: "none", + + zapSelectors: [], + customCSS: "", + + changeWasMade: false, + }, + }; + } + + /** + * Will create a new boost + * + * @param {string} domain - The domain which will be affected by the boost + * @returns {object|null} The created boost with { id, domain, boostEntry: { boostData } } or null + */ + createNewBoost(domain) { + if (!domain) { + console.error("[ZenBoostsManager] Domain expected but got null."); + return null; + } + + const id = crypto.randomUUID(); + const boostEntry = this.getEmptyBoostEntry(domain); + + const domainEntry = this.#getOrCreateDomainEntry(domain); + domainEntry.boostEntries.set(id, boostEntry); + + const boost = { id, domain, boostEntry }; + return boost; + } + + /** + * Loads the boost configuration for the specified domain from storage. + * + * @param {string} domain - The domain for which to load the boost + * @returns {object[] | null} All boosts for the domain or null + */ + loadBoostsFromStore(domain) { + if (!domain) { + console.error("[ZenBoostsManager] Domain expected but got null."); + } + + const boosts = []; + const domainEntry = this.#getDomainEntry(domain); + + if (domainEntry) { + domainEntry.boostEntries.forEach((value, key) => { + const boost = { id: key, domain, boostEntry: value }; + boosts.push(boost); + }); + return boosts; + } + return null; + } + + /** + * Loads the boost for the specified domain and id from storage. + * If no boost is present, a new one will be created. + * + * @param {string} domain - The domain of the boost + * @param {string} id - The id of the boost + * @returns {object} Returns the boost with { id, domain, boostEntry: { boostData } } + */ + loadBoostFromStore(domain, id) { + if (!domain) { + console.error("[ZenBoostsManager] Domain expected but got null."); + } + if (!id) { + console.error("[ZenBoostsManager] ID expected but got null."); + } + + const domainEntry = this.#getOrCreateDomainEntry(domain); + + if (domainEntry.boostEntries.has(id)) { + const boostEntry = domainEntry.boostEntries.get(id); + return { id, domain, boostEntry }; + } + const boost = this.createNewBoost(domain); + return boost; + } + + /** + * Loads the active boost for the specified domain from storage. + * + * @param {string} domain - The domain of the boost + * @returns {object | null} Returns the boost with { id, domain, boostEntry: { boostData } } or null + */ + loadActiveBoostFromStore(domain) { + if (!domain) { + console.error("[ZenBoostsManager] Domain expected but got null."); + } + + const domainEntry = this.#getDomainEntry(domain); + + if (domainEntry) { + if (domainEntry.boostEntries.size === 0) { + return this.createNewBoost(domain); + } + + if (domainEntry.boostEntries.has(domainEntry.activeBoostId)) { + const boostEntry = domainEntry.boostEntries.get( + domainEntry.activeBoostId + ); + return { id: domainEntry.activeBoostId, domain, boostEntry }; + } + } + + return null; + } + + /** + * Adds the zap selector to the selectors list and updates the website. + * + * @param {string} selector - Selector which will hide the elements + * @param {string} domain - Domain of the target boost + */ + addZapSelectorToActive(selector, domain) { + const boost = this.loadActiveBoostFromStore(domain); + if (!boost) { + console.error("[ZenBoostsManager] Active boost is null"); + return; + } + + const { boostData } = boost.boostEntry; + + if (!boostData.zapSelectors) { + boostData.zapSelectors = []; + } + if (!boostData.zapSelectors.includes(selector)) { + boostData.zapSelectors.push(selector); + } + + this.updateBoost(boost); + } + + /** + * Removes the zap selector to the selectors list and updates the website. + * + * @param {string} selector - Selector which will no longer hide the elements + * @param {string} domain - Domain of the target boost + */ + removeZapSelectorToActive(selector, domain) { + const boost = this.loadActiveBoostFromStore(domain); + if (!boost) { + console.error("[ZenBoostsManager] Active boost is null"); + return; + } + + const { boostData } = boost.boostEntry; + + if (boostData.zapSelectors && boostData.zapSelectors.includes(selector)) { + const i = boostData.zapSelectors.indexOf(selector); + if (i !== -1) { + boostData.zapSelectors.splice(i, 1); + } + } + + this.updateBoost(boost); + } + + /** + * Makes the boost at the domain with the id active + * + * @param {string} domain The target domain + * @param {string} id The target boost id + */ + makeBoostActiveForDomain(domain, id) { + const domainEntry = this.#getDomainEntry(domain); + + if (domainEntry) { + if (domainEntry.boostEntries.has(id)) { + domainEntry.activeBoostId = id; + } + } + + Services.obs.notifyObservers(null, "zen-boosts-active-change", { id }); + + this.#stylesManager.invalidateStyleForDomain(domain); + this.notify(); + } + + /** + * Toggles the boost activeness at the domain with the id active + * + * @param {string} domain The target domain + * @param {string} id The target boost id + */ + toggleBoostActiveForDomain(domain, id) { + const domainEntry = this.#getDomainEntry(domain); + + if (domainEntry) { + if (domainEntry.boostEntries.has(id)) { + if (domainEntry.activeBoostId === id) { + domainEntry.activeBoostId = null; + Services.obs.notifyObservers(null, "zen-boosts-active-change", { + id: null, + }); + + this.#stylesManager.invalidateStyleForDomain(domain); + this.notify(true); + } else { + domainEntry.activeBoostId = id; + Services.obs.notifyObservers(null, "zen-boosts-active-change", { + id, + }); + + this.#stylesManager.invalidateStyleForDomain(domain); + this.notify(); + } + } + } + } + + /** + * Clears all zap selectors from a boost + * + * @param {string} domain - Target boost domain + */ + clearZapSelectorsForActive(domain) { + const boost = this.loadActiveBoostFromStore(domain); + if (!boost) { + console.error("[ZenBoostsManager] Active boost is null"); + return; + } + + const { boostData } = boost.boostEntry; + boostData.zapSelectors = []; + + this.updateBoost(boost); + } + + /** + * Updates the boost for a domain in memory and notifies observers of the change. + * + * @param {object} boost - The target boost + */ + updateBoost(boost) { + const { domain, id, boostEntry } = boost; + + const domainEntry = this.#getOrCreateDomainEntry(domain); + domainEntry.boostEntries.set(id, boostEntry); + + this.#stylesManager.invalidateStyleForDomain(domain); + this.notify(); + } + + /** + * Notifies all observers that boost data has been updated. + * This triggers a 'zen-boosts-update' notification event. + * + * @param {boolean} unloadStyles - Whether to unload styles during the update. + */ + notify(unloadStyles = false) { + Services.obs.notifyObservers(null, "zen-boosts-update", { unloadStyles }); + } + + /** + * Saves a boost configuration to persistent storage and notifies observers. + * + * @param {object | null} boost - The boost data object to save. If null, only saves existing boosts. + */ + saveBoostToStore(boost) { + if (boost) { + this.updateBoost(boost); + } + + this.#writeToDisk(this.registeredDomains); + this.notify(); + } + + /** + * Reads all boosts from persistent storage and updates the registered boosts map. + * + * @param {Function} done - Callback function to execute after reading is complete. + * @private + */ + #readBoostsFromStore(done) { + this.#readFromDisk().then(data => { + this.registeredDomains = data; + done(); + }); + } + + /** + * Gets the file path where boost data is stored in the user's profile directory. + * + * @returns {string} The full path to the boost storage file. + * @private + */ + get #storePath() { + const profilePath = PathUtils.profileDir; + return PathUtils.join(profilePath, this.#saveFilename); + } + + /** + * Gets the directory path where user css is stored in the user's profile directory. + * + * @returns {string} The full path to the boost userCSS directory. + * @private + */ + get #cssPath() { + const profilePath = PathUtils.profileDir; + return PathUtils.join(profilePath, "zen-boosts"); + } + + /** + * Reads boost data from disk, decompresses it, and converts it to a Map. + * + * @returns {Promise} A promise that resolves to a Map of domain to boost data. + * @private + */ + async #readFromDisk() { + this.#file = new JSONFile({ + path: this.#storePath, + compression: "lz4", + }); + + await this.#file.load(); + + const raw = this.#file.data ?? {}; + const map = new Map(); + + for (const [domain, entry] of Object.entries(raw)) { + const boostsMap = new Map(); + for (const [id, boostEntry] of Object.entries(entry.boostEntries ?? {})) { + // Reuinite the user css with the boost data if any exists + const userCSS = await this.#readBoostCSS(id); + if (userCSS) { + boostEntry.boostData.customCSS = userCSS; + } + + boostsMap.set(id, boostEntry); + } + + map.set(domain, { + activeBoostId: entry.activeBoostId ?? null, + boostEntries: boostsMap, + }); + } + + return map; + } + + /** + * Reads the user CSS of a boost with the given id from the dedicated folder. + * Returns null if the file doesn't exist. + * + * @param {string} id - The id of the boost + * @returns {string|null} Returns the user CSS or null + */ + async #readBoostCSS(id) { + const fileName = `${id}.css`; + const directoryPath = this.#cssPath; + const savePath = PathUtils.join(directoryPath, fileName); + + await IOUtils.makeDirectory(directoryPath, { createAncestors: true }); + + if (await IOUtils.exists(savePath)) { + const css = await IOUtils.readUTF8(savePath); + return css; + } + + return null; + } + + /** + * Writes boost data to disk by converting the Map to JSON and compressing it. + * + * @param {Map} map - The Map of domain to boost data to write to disk. + * @private + */ + #writeToDisk(map) { + const obj = {}; + + for (const [domain, entry] of map) { + const boostsObj = {}; + for (const [id, boostEntry] of entry.boostEntries) { + // Split the user css from the boost data + boostsObj[id] = structuredClone(boostEntry); + delete boostsObj[id].boostData.customCSS; + + this.#writeBoostCSS(id, boostEntry.boostData.customCSS); + } + obj[domain] = { + activeBoostId: entry.activeBoostId ?? null, + boostEntries: boostsObj, + }; + } + + this.#file.data = obj; + this.#file.saveSoon(); + } + + /** + * Writes the user CSS of a boost with the given id to a dedicated folder. + * + * @param {string} id - The id of the boost + * @param {string} css - The user CSS + */ + async #writeBoostCSS(id, css) { + const fileName = `${id}.css`; + const directoryPath = this.#cssPath; + const savePath = PathUtils.join(directoryPath, fileName); + + await IOUtils.makeDirectory(directoryPath, { createAncestors: true }); + await IOUtils.writeUTF8(savePath, css); + } + + /** + * Checks if any boost is registered and active for the specified domain. + * + * @param {string} domain - The domain to check for any registered and active boost. + * @returns {boolean} True if a boost exists for the domain and is active, false otherwise. + */ + registeredBoostForDomain(domain) { + const domainEntry = this.#getDomainEntry(domain); + + if (domainEntry) { + return domainEntry.boostEntries.has(domainEntry.activeBoostId); + } + + return false; + } + + /** + * Determines if a boost can be created for the given URI. + * Only HTTP and HTTPS schemes are supported for boosting. + * + * @param {nsIURI} uri - The URI to check for boost eligibility. + * @returns {boolean} True if the URI scheme is http or https, false otherwise. + */ + canBoostSite(uri) { + if (!uri || !uri.schemeIs) { + return false; + } + return uri.schemeIs("http") || uri.schemeIs("https"); + } + + /** + * Gets from cache or creates and caches a new style sheet for the given boost data. + * + * @param {string} domain - The domain of the boosts. + * @returns {nsIStyleSheet} The style sheet corresponding to the boost data. + */ + getStyleSheetForBoost(domain) { + const boost = this.loadActiveBoostFromStore(domain); + if (!boost) { + return null; + } + + const { boostData } = boost.boostEntry; + return this.#stylesManager.getStyleForBoost(boostData, domain); + } + + /** + * Opens the boost editor in a new popup window. + * + * @param {Window} parentWindow - The parent browser window + * @param {Boost} boost - The boost which will be edited + * @param {nsIURI} domainUri - The boost which will be edited + * @returns {Window|null} The instanced editor window + */ + openBoostWindow(parentWindow, boost, domainUri) { + if (!this.canBoostSite(domainUri)) { + console.error( + "[ZenBoostsManager] Cannot open editor for boost with invalid domain." + ); + return null; + } + + const domain = boost.domain; + const { availLeft, availWidth } = parentWindow.screen; + const screenX = parentWindow.screenX; + const screenY = parentWindow.screenY; + const width = parentWindow.outerWidth; + const height = parentWindow.outerHeight; + const editorWidth = 185; + const editorHeight = 565; + const pad = 20; + + let left = screenX + width + pad; + if (this.#areTabsOnRightSide()) { + left = screenX - (editorWidth + pad); + } + + let top = screenY + height / 2 - editorHeight / 2; + + if (left + editorWidth > availLeft + availWidth || left < availLeft) { + left = screenX + width - (editorWidth + pad); + if (this.#areTabsOnRightSide()) { + left = screenX + pad; + } + } + + const editor = Services.ww.openWindow( + parentWindow, + "chrome://browser/content/zen-components/windows/zen-boost-editor.xhtml", + null, + `left=${left},top=${top},chrome,alwaysontop,resizable=no,minimizable=no,dependent,dialog=yes`, + null + ); + + // Close the editor if the tab is switched + parentWindow.gBrowser.tabContainer.addEventListener( + "TabSelect", + editor.close.bind(editor), + { + once: true, + } + ); + + const progressListener = { + onLocationChange: webProgress => { + if (webProgress.isTopLevel) { + editor.close(); + parentWindow.gBrowser.removeTabsProgressListener(progressListener); + } + }, + }; + + parentWindow.gBrowser.addProgressListener(progressListener); + + // Give the domain + editor.domain = domain; + editor.openerWindow = parentWindow; + editor.focus(); + + // Make boost active + this.makeBoostActiveForDomain(domain, boost.id); + + return editor; + } + + /** + * Will spawn a file save dialog and export the selected boost + * + * @param {Window} parentWindow The window that will instance the file picker + * @param {object} boostData The data of the boost to be exported + * @returns {Promise} Returns a promise which will be resolved after the export action is complete + */ + exportBoost(parentWindow, boostData) { + // From: firefox-main/browser/base/content/browser-commands.js:354 + // https://searchfox.org/firefox-main/source/browser/base/content/browser-commands.js#355:~:text=try%20%7B-,const,fp%2Eopen%28fpCallback%29%3B + + const nsIFilePicker = Ci.nsIFilePicker; + const fp = Cc["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker); + + fp.init( + parentWindow.browsingContext, + `Exporting Boost ${boostData.boostName}...`, + nsIFilePicker.modeSave + ); + + // Sanitizing filename + // From: https://gist.github.com/barbietunnie/7bc6d48a424446c44ff4#:~:text=bytes%22%29%3B-,var,%7D + const illegalRe = /[\/\?<>\\:\*\|":]/g; + + // eslint-disable-next-line no-control-regex + const controlRe = /[\x00-\x1f\x80-\x9f]/g; + const reservedRe = /^\.+$/; + const windowsReservedRe = /^(con|prn|aux|nul|com[0-9]|lpt[0-9])(\..*)?$/i; + + let sanitized = boostData.boostName + .replace(illegalRe, "") + .replace(controlRe, "") + .replace(reservedRe, "") + .replace(windowsReservedRe, ""); + + // Replace if resulting filename is empty + if (!sanitized) { + sanitized = "New Boost"; + } + + fp.defaultString = sanitized; + fp.defaultExtension = "json"; + fp.appendFilters(nsIFilePicker.filterAll); + + return new Promise(resolve => { + fp.open(async result => { + if (result === nsIFilePicker.returnOK && fp.file) { + try { + const boostJSON = JSON.stringify(boostData); + await IOUtils.writeUTF8(fp.file.path, boostJSON); + resolve(true); + } catch (ex) { + console.error("Export failed:", ex); + resolve(false); + } + } else { + resolve(false); + } + }); + }); + } + + /** + * Will spawn a file open dialog and import the selected boost + * + * @param {Window} parentWindow The window that will instance the file picker + * @returns {Promise} Returns a promise with the boost data or null + */ + importBoost(parentWindow) { + const nsIFilePicker = Ci.nsIFilePicker; + const fp = Cc["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker); + + fp.init( + parentWindow.browsingContext, + "Importing Boost from JSON", + nsIFilePicker.modeOpen + ); + + fp.appendFilters(nsIFilePicker.filterAll); + + return new Promise(resolve => { + fp.open(async result => { + if (result === nsIFilePicker.returnOK && fp.file) { + try { + const fileContent = await IOUtils.readUTF8(fp.file.path); + resolve(JSON.parse(fileContent)); + } catch (e) { + console.error("Import failed:", e); + resolve(null); + } + } else { + resolve(null); + } + }); + }); + } + + /** + * Helper function to determine if tabs are on the right side. + * From: ZenDownloadAnimation.mjs + */ + #areTabsOnRightSide() { + return Services.prefs.getBoolPref("zen.tabs.vertical.right-side"); + } +} + +export const gZenBoostsManager = new nsZenBoostsManager(); diff --git a/src/zen/boosts/ZenSelectorComponent.sys.mjs b/src/zen/boosts/ZenSelectorComponent.sys.mjs new file mode 100644 index 000000000..5a39409a1 --- /dev/null +++ b/src/zen/boosts/ZenSelectorComponent.sys.mjs @@ -0,0 +1,787 @@ +/* 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.defineLazyGetter(lazy, "overlayLocalization", () => { + return new Localization(["browser/zen-boosts.ftl"], true); +}); + +export class SelectorComponent { + document = null; + window = null; + #initialized = false; + #content = null; + + #currentState = null; + #selectedElement = null; + #lastOverElement = null; + + #relatedValueIndex = 0; + + static STATES = { + SELECTING: "selecting", + SELECTED: "selected", + }; + + #zenContentIDs = []; + #onSelect = null; + #localizationArray = [ + { id: "zen-select-this" }, + { id: "zen-select-related" }, + { id: "zen-select-cancel" }, + ]; + + safeAreaPadding = { left: 0, right: 0, top: 0, bottom: 0 }; + + /** + * @param {Document} document Webpage document + * @param {ZenBoostsChild} zenBoostsChild Boost JSActor child + * @param {string[]} additionalZenContentIDs Additional IDs that will be considered as non website content + * @param {Function} onSelect Callback for when a selection was made. The parameter is the css selector + * @param {object[]} localizationArray An array of 3 { id: 'l10n-id' } fluent IDs for localization going from the inputs left to right + */ + constructor( + document, + zenBoostsChild, + additionalZenContentIDs, + onSelect, + localizationArray = null + ) { + this.document = document; + this.window = document.ownerGlobal; + this.zenBoostsChild = zenBoostsChild; + this.#onSelect = onSelect; + + if (localizationArray != null) { + this.#localizationArray = localizationArray; + } + + const baseSelectorIDs = ["select-controls", "select-controls-container"]; + this.#zenContentIDs = [...additionalZenContentIDs, ...baseSelectorIDs]; + } + + /** + * Initializes the zap mode and inserts anonymous content + */ + async initialize() { + if (this.#initialized) { + return; + } + + this.#content = this.document.insertAnonymousContent(); + this.#content.root.appendChild(this.fragment); + this.#initializeElements(); + this.setState(SelectorComponent.STATES.SELECTING); + + this.#initialized = true; + } + + /** + * Initializes all anonymous content and events + */ + #initializeElements() { + this.hoverDiv = this.getElementById("hover-div"); + this.selectorComponent = this.getElementById("select-component"); + + this.cancelButton = this.getElementById("select-cancel"); + this.cancelButton.addEventListener("click", this.#cancelSelect.bind(this)); + + this.selectThisButton = this.getElementById("select-this"); + this.selectThisButton.addEventListener( + "click", + this.#handleSelect.bind(this) + ); + + this.selectRelatedSlider = this.getElementById("select-related"); + this.selectRelatedSlider.addEventListener( + "click", + this.#handleSelect.bind(this) + ); + + // Initialize the related elements button + this.selectRelatedSlider.addEventListener("mousemove", e => { + const r = e.currentTarget.getBoundingClientRect(); + const mouseX = e.clientX; + + let value = (mouseX - r.left) / r.width; + value = Math.max(0, Math.min(1, value)); + + e.target.style = `--related-elements-value: ${value * 100}%;`; + + const lastIndex = this.#relatedValueIndex; + this.#relatedValueIndex = Math.round(value * 8); + + if (lastIndex != this.#relatedValueIndex) { + this.updateHighlight(); + this.#updatePathTextField(); + } + }); + + this.selectRelatedSlider.addEventListener("mouseout", e => { + e.currentTarget.style.removeProperty("--related-elements-value"); + + this.#relatedValueIndex = 0; + this.updateHighlight(); + this.#updatePathTextField(); + }); + } + + /** + * Sets the state of the zap mode + * + * @param {STATES} newState New state + * @param {*} data Optional additional data + */ + setState(newState, data = null) { + this.#currentState = newState; + + switch (newState) { + case SelectorComponent.STATES.SELECTED: + this.#selectedElement = data; + this.#relatedValueIndex = 0; // Reset index + + this.#hideHoverDiv(); + this.#showSelectorComponent(); + this.updateHighlight(); + this.#updatePathTextField(); + break; + case SelectorComponent.STATES.SELECTING: + this.#selectedElement = null; + this.#showHoverDiv(); + this.#hideSelectorComponent(); + this.removeHighlight(); + break; + } + } + + get content() { + if (!this.#content || Cu.isDeadWrapper(this.#content)) { + return null; + } + return this.#content; + } + + /** + * Helper for getting an anonymous element by id + * + * @param {string} id The id of the element + */ + getElementById(id) { + return this.content.root.getElementById(id); + } + + get markup() { + // Fetch localizations + let [thisElement, relatedElements, cancelAction] = + lazy.overlayLocalization.formatMessagesSync(this.#localizationArray); + + return ` + + `; + } + + get fragment() { + if (!this.template) { + let parser = new DOMParser(); + let doc = parser.parseFromString(this.markup, "text/html"); + this.template = this.document.importNode( + doc.querySelector("template"), + true + ); + } + let fragment = this.template.content.cloneNode(true); + return fragment; + } + + /** + * Handles the addition of the current zap selector + */ + #handleSelect() { + const cssPath = this.getSelectionPath( + this.document, + this.#relatedValueIndex, + this.#selectedElement + ); + + this.removeHighlight(); + this.#resetHoverDiv(); + + // The highlight should be gone before the onSelect is called + this.window.requestAnimationFrame(() => { + this.setState(SelectorComponent.STATES.SELECTING); + if (cssPath) { + this.#onSelect(cssPath); + } + }); + } + + /** + * Cancles the current selection operation + */ + #cancelSelect() { + this.setState(SelectorComponent.STATES.SELECTING); + } + + /** + * Rebuilds the selection highlight + */ + updateHighlight() { + this.removeHighlight(); + this.showHighlight(this.getSelection()); + } + + /** + * Highlights a selection of elements on the website + * + * @param {List} selection A list of the web elements that should be highlighted + */ + showHighlight(selection) { + const highlightContainerDiv = this.getElementById("highlight-container"); + highlightContainerDiv.style.display = "initial"; + + let counter = 0; + for (const element of selection) { + if (counter >= 100) { + break; + } // Avoid too many instanced objects + counter++; + + const padding = 5; + const elementMeasurement = element?.getBoundingClientRect() ?? undefined; + if (elementMeasurement == undefined) { + continue; + } + + const highlightDiv = this.document.createElement("div"); + highlightDiv.classList.add("highlight"); + + Object.assign(highlightDiv.style, { + left: `${elementMeasurement.left - padding}px`, + top: `${elementMeasurement.top - padding}px`, + width: `${elementMeasurement.width + padding * 2}px`, + height: `${elementMeasurement.height + padding * 2}px`, + }); + + highlightContainerDiv.appendChild(highlightDiv); + } + + this.getElementById("highlight-shadow").style.display = "initial"; + } + + /** + * Clears the highlight + */ + removeHighlight() { + const highlightContainerDiv = this.getElementById("highlight-container"); + highlightContainerDiv.style.display = "none"; + + // Clear all children elements + highlightContainerDiv.innerHTML = ""; + this.getElementById("highlight-shadow").style.display = "none"; + } + + /** + * Updates the path display text on the selector component + * based on the current selection + */ + #updatePathTextField() { + const maxPathLength = 64; + const selection = this.getSelection(); + const selectionPath = this.getSelectionPath( + this.document, + this.#relatedValueIndex, + this.#selectedElement + ); + + if (!selectionPath) { + return; + } + + const pathText = `[${selection.length}] ${selectionPath.substring(0, Math.min(maxPathLength, selectionPath.length))}`; + this.getElementById("selector-element-preview-text").setHTML(pathText); + } + + /** + * Removes all event listeners and removes the overlay from the Anonymous Content + */ + tearDown() { + if (this.#content) { + try { + this.document.removeAnonymousContent(this.#content); + } catch { + /* This might fail but that's not an issue */ + } + } + + this.window = null; + this.document = null; + this.#initialized = false; + } + + /** + * Hides the hover selection box + */ + #hideHoverDiv() { + this.hoverDiv.style.display = "none"; + } + + /** + * Shows the hover selection box + */ + #showHoverDiv() { + this.hoverDiv.style.display = "initial"; + } + + /** + * Resets the hover selection box bounds + */ + #resetHoverDiv() { + Object.assign(this.getElementById("hover-div").style, { + top: `0px`, + left: `0px`, + width: `0px`, + height: `0px`, + }); + } + + /** + * Hides the selector component + */ + #hideSelectorComponent() { + this.selectorComponent.style.visibility = "hidden"; + this.selectorComponent.setAttribute("is-appearing", "false"); + } + + /** + * Shows the selector component + */ + #showSelectorComponent() { + this.selectorComponent.style.visibility = "visible"; + this.#setSelectorComponentPosition(); + } + + /** + * Sets the aligned and clamped position for the zap component on the document + * relative to #selectedElement + */ + #setSelectorComponentPosition() { + const bounds = this.#selectedElement.getBoundingClientRect(); + const distance = 8; + + const rect = this.selectorComponent.getBoundingClientRect(); + const zapComponentWidth = rect.width; + const zapComponentHeight = rect.height; + + const windowWidth = this.window.innerWidth; + const windowHeight = this.window.innerHeight; + const windowPadding = 10; + + this.selectorComponent.setAttribute("is-appearing", "false"); + + // This clamps the position so the zap component never goes out of the client bounds and adds a small padding + const top = this.clamp( + bounds.top + bounds.height + distance, + windowPadding + this.safeAreaPadding.top, + windowHeight - + zapComponentHeight - + windowPadding - + this.safeAreaPadding.bottom + ); + const left = this.clamp( + bounds.left + bounds.width / 2 - zapComponentWidth / 2, + windowPadding + this.safeAreaPadding.left, + windowWidth - + zapComponentWidth - + windowPadding - + this.safeAreaPadding.right + ); + + Object.assign(this.selectorComponent.style, { + top: `${top}px`, + left: `${left}px`, + }); + + // Adjust transform origin for animation + const targetCenterX = bounds.left + bounds.width / 2; + const targetBottomY = bounds.top + bounds.height; + let originX = this.clamp(targetCenterX - left, 0, zapComponentWidth); + let originY = this.clamp(targetBottomY - top, 0, zapComponentHeight); + + this.selectorComponent.style.transformOrigin = `${originX}px ${originY}px`; + + this.window.requestAnimationFrame(() => { + this.selectorComponent.setAttribute("is-appearing", "true"); + }); + } + + /** + * This function handles page events while the overlay is active + * + * @param {Event} event The event which will be handled by the overlay + * @param {boolean} prevent True if the event should be prevented + */ + handleEvent(event, prevent) { + let isZenContent = false; + if (event?.originalTarget?.closest) { + const closestID = event.originalTarget.closest("div")?.id ?? ""; + isZenContent = this.#zenContentIDs.includes(closestID); + } + + switch (event.type) { + case "click": + this.#handleClick(event, isZenContent); + break; + case "mousemove": + this.#handleMouseMove(event, isZenContent); + break; + case "scroll": + this.#handlePageChange(event); + return; + case "resize": + this.#handlePageChange(event); + return; + } + + // Let the interactable ids pass through + if (isZenContent) { + return; + } + + if (prevent) { + // From ScreenshotsComponentChild.sys.mjs:103 + // Preventing a pointerdown event throws an error in debug builds. + // See https://searchfox.org/mozilla-central/rev/b41bb321fe4bd7d03926083698ac498ebec0accf/widget/WidgetEventImpl.cpp#566-572 + // Don't prevent the default context menu. + if (!["contextmenu", "pointerdown"].includes(event.type)) { + event.preventDefault(); + } + event.stopImmediatePropagation(); + } + } + + /** + * Called after a page change to update the highlight and selector component position + */ + #handlePageChange() { + if (this.#currentState !== SelectorComponent.STATES.SELECTED) { + return; + } + + this.updateHighlight(); + this.#setSelectorComponentPosition(); + } + + /** + * Handles the mouse move event + * + * @param {Event} event Mouse move event params + * @param {boolean} isZenContent Flag if the target element is a zen related element + */ + #handleMouseMove(event, isZenContent) { + if (this.#lastOverElement === event.target) { + return; + } + if (!isZenContent) { + this.#lastOverElement = event.target; + if (this.#currentState === SelectorComponent.STATES.SELECTING) { + this.#showHoverDiv(); + } + } else { + this.#hideHoverDiv(); + } + + if ( + this.#currentState !== SelectorComponent.STATES.SELECTING || + !event.target + ) { + return; + } + + const bounds = event.target.getBoundingClientRect(); + const padding = 5; + + Object.assign(this.getElementById("hover-div").style, { + top: `${bounds.top - padding}px`, + left: `${bounds.left - padding}px`, + width: `${bounds.width + padding * 2}px`, + height: `${bounds.height + padding * 2}px`, + }); + } + + /** + * Handles the mouse click event + * + * @param {Event} event Mouse move event params + * @param {boolean} isZenContent Flag if the target element is a zen related element + */ + #handleClick(event, isZenContent) { + // Safeguards for protecting anonymous content from being zapped + if ( + event.target === this.document.documentElement || + event.target === this.document.body || + !this.document.documentElement.contains(event.target) + ) { + return; + } + + if ( + this.#currentState === SelectorComponent.STATES.SELECTING && + !isZenContent + ) { + this.setState(SelectorComponent.STATES.SELECTED, event.target); + } + } + + /** + * @param {number} x Value + * @param {number} min Minimum limit + * @param {number} max Maximum limit + * @returns {number} A value which always lies between min and max + */ + clamp(x, min, max) { + return Math.min(Math.max(x, min), max); + } + + /** + * When selecting an area to zap there can be a set of zapped elements + * since related elements can be included. + * This method returns all targeted elements for the zapping process. + * + * @returns {Element[]} An array of selected elements + */ + getSelection() { + const selector = this.getSelectionPath( + this.document, + this.#relatedValueIndex, + this.#selectedElement + ); + if (!selector) { + return []; + } + + return this.document.querySelectorAll(selector); + } + + /** + * Used for retreiving the css path from the selected element and taking + * the related objects into account + * + * @param {Element} document + * @param {Element} relatedValueIndex + * @param {Element} selectedElement + */ + getSelectionPath(document, relatedValueIndex, selectedElement) { + let path = []; + + const cssescape = str => CSS.escape(str); + + // Body and Html nodes are not considered valid here + const isValidNode = element => { + if (!element) { + return false; + } else if (element.tagName.toLowerCase() === "body") { + return false; + } else if (element.tagName.toLowerCase() === "html") { + return false; + } + return true; + }; + + const nthChild = element => { + if (!element) { + return ""; + } + if (!element.parentNode) { + return ""; + } + const parent = element.parentNode; + const index = Array.prototype.indexOf.call(parent.children, element) + 1; + + if (index === 1) { + return ":first-child"; + } + if (index === parent.children.length) { + return ":last-child"; + } + return `:nth-child(${index})`; + }; + + const getIdentification = (element, specifity = 0) => { + if (!element) { + return ""; + } + const id = specifity < 2 && element.id ? `#${cssescape(element.id)}` : ""; + const cls = + specifity < 1 && element.classList.length + ? "." + [...element.classList].map(c => cssescape(c)).join(".") + : ""; + const tag = element.tagName ? element.tagName.toLowerCase() : ""; + return `${tag}${id}${cls}`; + }; + + const traverse = (element, specifity = 0, pathArray) => { + let currentElement = element; + if (!isValidNode(currentElement)) { + return; + } + + pathArray.push(nthChild(currentElement)); + pathArray.push(getIdentification(currentElement, specifity)); + + if (currentElement && currentElement.parentNode) { + pathArray.push(" > "); + pathArray.push(getIdentification(currentElement.parentNode, 0)); + const tempBuild = build(pathArray); + + while ( + tempBuild && + document.querySelectorAll(tempBuild).length > 1 && + isValidNode(currentElement.parentNode) + ) { + currentElement = currentElement.parentNode; + pathArray.push(" > "); + pathArray.push(nthChild(currentElement.parentNode)); + pathArray.push(getIdentification(currentElement.parentNode, 0)); + } + } + }; + + const build = pathArray => pathArray.toReversed().join(""); + + const findBestExactSelector = (element, doc) => { + let buildMap = new Map(); + + let pathExactElement = []; + traverse(element, 0, pathExactElement); + + const pathExactElementBuilt = build(pathExactElement); + const pathExactElementLength = ( + pathExactElementBuilt ? doc.querySelectorAll(pathExactElementBuilt) : [] + ).length; + + buildMap.set(pathExactElementLength, pathExactElementBuilt); + + let pathTypeElement = []; + pathTypeElement.push(getIdentification(element, 2)); + if (isValidNode(element.parentNode)) { + pathTypeElement.push(" > "); + traverse(element.parentNode, 0, pathTypeElement); + } + + const pathTypeElementBuilt = build(pathTypeElement); + const pathTypeElementLength = ( + pathTypeElementBuilt ? doc.querySelectorAll(pathTypeElementBuilt) : [] + ).length; + + if (!buildMap.has(pathTypeElementLength)) { + buildMap.set(pathTypeElementLength, pathTypeElementBuilt); + } + + let parentExactElement = []; + if (isValidNode(element.parentNode)) { + traverse(element.parentNode, 0, parentExactElement); + } + + const pathParentElementBuilt = build(parentExactElement); + const pathParentElementLength = ( + pathParentElementBuilt + ? doc.querySelectorAll(pathParentElementBuilt) + : [] + ).length; + + if (!buildMap.has(pathParentElementLength)) { + buildMap.set(pathParentElementLength, pathParentElementBuilt); + } + + let smallestLength = Number.MAX_VALUE; + let smallestLengthPath = null; + buildMap.forEach((buildPath, length) => { + if (length < smallestLength) { + smallestLength = length; + smallestLengthPath = buildPath; + } + }); + + return smallestLengthPath; + }; + + if (!isValidNode(selectedElement)) { + return null; + } + switch (relatedValueIndex) { + // Sometimes getting the exact element we want is not guaranteed by + // one specific path builder. It is best to build multiple possible paths + // and decide which one is best. + default: + case 0: + return findBestExactSelector(selectedElement, document); + // Getting the exact parent element of selected element + case 1: + if (!isValidNode(selectedElement.parentNode)) { + return null; + } + traverse(selectedElement.parentNode, 0, path); + break; + // Getting the type of selected element with same exact parent + case 2: + path.push(getIdentification(selectedElement, 2)); + if (isValidNode(selectedElement.parentNode)) { + path.push(" > "); + path.push(getIdentification(selectedElement.parentNode, 0)); + } + break; + // Getting the same type of selected element with same parent type + case 3: + path.push(getIdentification(selectedElement, 2)); + if (isValidNode(selectedElement.parentNode)) { + path.push(" > "); + path.push(getIdentification(selectedElement.parentNode, 2)); + } + break; + // Getting the same type of selected element + case 4: + path.push(getIdentification(selectedElement, 2)); + break; + // Getting the same type of the parent + case 5: + if (!isValidNode(selectedElement.parentNode)) { + return null; + } + path.push(getIdentification(selectedElement.parentNode, 2)); + break; + // Get any element with same parent + case 6: + if (!isValidNode(selectedElement.parentNode)) { + return null; + } + + path.push("*"); + path.push(" > "); + traverse(selectedElement.parentNode, 0, path); + break; + // Get elements with similar classes + case 7: + path.push(getIdentification(selectedElement, 1)); + break; + } + + return build(path); + } +} diff --git a/src/zen/boosts/ZenZapDissolve.sys.mjs b/src/zen/boosts/ZenZapDissolve.sys.mjs new file mode 100644 index 000000000..ab1276346 --- /dev/null +++ b/src/zen/boosts/ZenZapDissolve.sys.mjs @@ -0,0 +1,636 @@ +/* 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/. */ + +export class ZapDissolve { + FRAG = ` +precision mediump float; +uniform sampler2D u_DissolveTexture; +varying vec2 v_PCoord; +varying float v_PLifetime; +varying float v_POpacity; + +void main() { + if(v_PLifetime >= 1.0) discard; + + vec4 sampled = texture2D(u_DissolveTexture, v_PCoord); + if(sampled.a == 0.0) discard; + + float distToCenter = distance(vec2(0.5, 0.5), gl_PointCoord); + if(distToCenter > 0.5) discard; + + float alpha = sampled.a * v_POpacity; + float brightnessFactor = 1.5; + gl_FragColor = vec4(sampled.rgb * brightnessFactor, alpha); +} + `; + + VERT = ` +precision highp float; + +uniform float u_AnimationDuration; +uniform float u_ParticleSize; +uniform float u_ElapsedTime; +uniform float u_ViewportWidth; +uniform float u_ViewportHeight; +uniform float u_TextureWidth; +uniform float u_TextureHeight; +uniform float u_TextureLeft; +uniform float u_TextureTop; + +attribute float a_ParticleIndex; + +varying vec2 v_PCoord; +varying float v_PLifetime; +varying float v_POpacity; + +float hash1(float n) { return fract(sin(n) * 100000.0); } +float hash2(vec2 p) { return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453123); } +float hash3(vec2 p, float offset) { + return fract(sin(dot(p, vec2(12.9898 + offset, 78.233 - offset))) * 43758.5453123); +} + +/* coordinates */ +vec2 toNDC(vec2 pixel) { + vec2 ndc; + ndc.x = (pixel.x / u_ViewportWidth) * 2.0 - 1.0; + ndc.y = 1.0 - (pixel.y / u_ViewportHeight) * 2.0; + return ndc; +} + +float mix1(float a, float b, float t) { return a + (b - a) * t; } + +/* particle layout */ +vec2 particlePixelPos(float index) { + float cols = floor(u_TextureWidth / u_ParticleSize); + if(cols < 1.0) cols = 1.0; + float row = floor(index / cols); + float col = index - row * cols; + return vec2( + (col + 0.5) * u_ParticleSize + u_TextureLeft, + (row + 0.5) * u_ParticleSize + u_TextureTop + ); +} + +mat2 rotate2D(float angle) { + float s = sin(angle); + float c = cos(angle); + return mat2(c, -s, s, c); +} + +/* motion */ +vec4 animatedPosition(vec2 pixelPos, float seed, float seed2, float seed3, float t) { + vec2 base = toNDC(pixelPos); + + // Random rotation + float rotation1 = (seed - 0.5) * 0.52; // 15 * 2 * (π/180) + float rotation2 = (seed2 - 0.5) * 0.52; + + // Random translation + float randomRadian = 6.28318 * (seed3 - 0.5); + float translateX = 60.0 * cos(randomRadian) / u_ViewportWidth * 2.0; + float translateY = 30.0 * sin(randomRadian) / u_ViewportHeight * 2.0; + vec2 rotated1 = rotate2D(rotation1 * t) * base; + + vec2 translated = vec2( + mix1(rotated1.x, rotated1.x + translateX, t), + mix1(rotated1.y, rotated1.y + translateY, t) + ); + + vec2 finalPos = rotate2D(rotation2 * t) * (translated - base) + base; + + return vec4(finalPos, 0.0, 1.0); +} + +void main() { + vec2 pixelPos = particlePixelPos(a_ParticleIndex); + + // Multiple seeds for different random values + float seed = hash2(pixelPos); + float seed2 = hash3(pixelPos, 1.0); + float seed3 = hash3(pixelPos, 2.0); + + float normalizedX = (pixelPos.x - u_TextureLeft) / u_TextureWidth; + float frameAssignment = (seed + 2.0 * normalizedX) / 3.0; + float maxDelay = u_AnimationDuration * 0.42; + float delay = frameAssignment * maxDelay; + float particleDuration = u_AnimationDuration - delay; + float lifetime = clamp((u_ElapsedTime - delay) / particleDuration, 0.0, 1.0); + float easedT = 1.0 - pow(1.0 - lifetime, 3.0); + + gl_Position = animatedPosition(pixelPos, seed, seed2, seed3, easedT); + gl_PointSize = u_ParticleSize; + + v_PLifetime = lifetime; + v_POpacity = 1.0 - lifetime; + + // Texture coordinates + float cols = floor(u_TextureWidth / u_ParticleSize); + if(cols < 1.0) cols = 1.0; + + float row = floor(a_ParticleIndex / cols); + float col = a_ParticleIndex - row * cols; + + v_PCoord = vec2( + (col + 0.5) / cols, + (row + 0.5) / floor(u_TextureHeight / u_ParticleSize) + ); +} + `; + + window = null; + document = null; + #initialized = false; + #content = null; + + #webglContext = null; + #program; + #animationStartTime = -1; + #particlesCount = 0; + #duration = 1400; + #texture = null; + #buffer = null; + + #hasTriggered = false; + #rafId = null; + + #onComplete = null; + + /** + * @param {Document} document Webpage document + */ + constructor(document) { + if (!document) { + return; + } + this.document = document; + this.window = document.ownerGlobal; + } + + /** + * Initializes the zap mode and inserts anonymous content + */ + async initialize() { + if (this.#initialized) { + return; + } + + this.#content = this.document.insertAnonymousContent(); + this.#content.root.appendChild(this.fragment); + await this.#initializeElements(); + + this.#initialized = true; + } + + /** + * Initializes the elements and contexts required for the rendering + */ + async #initializeElements() { + const canvas = this.getElementById("zen-zap-dissolve-canvas"); + const gl = canvas.getContext("webgl", { + alpha: true, + antialias: true, + premultipliedAlpha: false, + }); + this.#webglContext = gl; + + if (!gl) { + console.error("WebGL is not supported."); + return; + } + + this.#resizeCanvasToClientSize(canvas); + + gl.enable(gl.BLEND); + gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA); + gl.clearColor(0, 0, 0, 0); + + // Ensure viewport is set correctly + gl.viewport(0, 0, canvas.width, canvas.height); + + this.#program = await this.#createProgram(gl, this.VERT, this.FRAG); + } + + /** + * Resizes the canvas to the correct size + * + * @param {Element} canvas + */ + #resizeCanvasToClientSize(canvas) { + const width = this.window.innerWidth; + const height = this.window.innerHeight; + + if (canvas.width !== width || canvas.height !== height) { + canvas.width = width; + canvas.height = height; + + // Set the WebGL viewport to match the canvas size + if (this.#webglContext) { + this.#webglContext.viewport(0, 0, width, height); + } + } + } + + /** + * Creates the canvas program + * + * @param {object} gl WebGL context + * @param {string} vertContent Content of the vertex shader + * @param {string} fragContent Content of the fragment shader + * @returns {*} The WebGL program + */ + async #createProgram(gl, vertContent, fragContent) { + const vertexShader = this.#compileShader(gl, vertContent, gl.VERTEX_SHADER); + const fragmentShader = this.#compileShader( + gl, + fragContent, + gl.FRAGMENT_SHADER + ); + + if (!vertexShader || !fragmentShader) { + console.error( + "Program creation aborted: One or more shaders failed to compile." + ); + return null; + } + + const program = gl.createProgram(); + gl.attachShader(program, vertexShader); + gl.attachShader(program, fragmentShader); + gl.linkProgram(program); + + gl.detachShader(program, vertexShader); + gl.detachShader(program, fragmentShader); + gl.deleteShader(vertexShader); + gl.deleteShader(fragmentShader); + + if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { + console.error( + "Shader program initializiation failure:", + gl.getProgramInfoLog(program) + ); + } + + return program; + } + + /** + * Compiles a shader + * + * @param {object} gl WebGL context + * @param {string} source Shader source + * @param {object} type Shader type + * @returns {*} The compiled shader + */ + #compileShader(gl, source, type) { + const shader = gl.createShader(type); + gl.shaderSource(shader, source); + gl.compileShader(shader); + + if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { + console.error("Shader compilation failure:", gl.getShaderInfoLog(shader)); + gl.deleteShader(shader); + return null; + } + + return shader; + } + + /** + * Retreives image data from a given image + * + * @param {Image} image The image + * @returns {ImageData} Image data + */ + #getImageData(image) { + const canvas = this.getElementById( + "zen-zap-dissolve-canvas" + ).ownerDocument.createElement("canvas"); + canvas.width = image.width; + canvas.height = image.height; + + const context = canvas.getContext("2d"); + context.drawImage(image, 0, 0); + return context.getImageData(0, 0, canvas.width, canvas.height); + } + + /** + * Creates and loads a WebGL texture from an Image + * + * @param {Image} image The target image + */ + #loadTexture(image) { + if (this.#texture) { + this.#webglContext.deleteTexture(this.#texture); + } + + const texture = this.#webglContext.createTexture(); + this.#webglContext.activeTexture(this.#webglContext.TEXTURE0); + this.#webglContext.bindTexture(this.#webglContext.TEXTURE_2D, texture); + + this.#webglContext.texParameteri( + this.#webglContext.TEXTURE_2D, + this.#webglContext.TEXTURE_WRAP_S, + this.#webglContext.CLAMP_TO_EDGE + ); + this.#webglContext.texParameteri( + this.#webglContext.TEXTURE_2D, + this.#webglContext.TEXTURE_WRAP_T, + this.#webglContext.CLAMP_TO_EDGE + ); + this.#webglContext.texParameteri( + this.#webglContext.TEXTURE_2D, + this.#webglContext.TEXTURE_MIN_FILTER, + this.#webglContext.NEAREST + ); + this.#webglContext.texParameteri( + this.#webglContext.TEXTURE_2D, + this.#webglContext.TEXTURE_MAG_FILTER, + this.#webglContext.NEAREST + ); + + if ( + image && + image instanceof Ci.nsIImageLoadingContent && + image.width && + image.height + ) { + this.#webglContext.texImage2D( + this.#webglContext.TEXTURE_2D, + 0, + this.#webglContext.RGBA, + this.#webglContext.RGBA, + this.#webglContext.UNSIGNED_BYTE, + image + ); + } else { + const imageData = this.#getImageData(image); + this.#webglContext.texImage2D( + this.#webglContext.TEXTURE_2D, + 0, + this.#webglContext.RGBA, + imageData.width, + imageData.height, + 0, + this.#webglContext.RGBA, + this.#webglContext.UNSIGNED_BYTE, + imageData.data + ); + } + + const textureLocation = this.#webglContext.getUniformLocation( + this.#program, + "u_DissolveTexture" + ); + this.#webglContext.uniform1i(textureLocation, 0); + this.#texture = texture; + } + + /** + * Binds the parameters to the program for a given Element + * + * @param {Element} element + */ + #bindParameters(element) { + const gl = this.#webglContext; + const rect = element.getBoundingClientRect(); + const particleSize = 1; + + const textureWidth = rect.width; + const textureHeight = rect.height; + + const cols = Math.floor(textureWidth / particleSize); + const rows = Math.floor(textureHeight / particleSize); + this.#particlesCount = cols * rows; + + gl.useProgram(this.#program); + + this.#setUniform1f("u_AnimationDuration", this.#duration); + this.#setUniform1f("u_ParticleSize", particleSize); + this.#setUniform1f("u_ViewportWidth", this.window.innerWidth); + this.#setUniform1f("u_ViewportHeight", this.window.innerHeight); + this.#setUniform1f("u_TextureWidth", textureWidth); + this.#setUniform1f("u_TextureHeight", textureHeight); + + this.#setUniform1f("u_TextureLeft", rect.left); + this.#setUniform1f("u_TextureTop", rect.top); + + const indices = new Float32Array(this.#particlesCount); + for (let i = 0; i < this.#particlesCount; i++) { + indices[i] = i; + } + + if (this.#buffer) { + gl.deleteBuffer(this.#buffer); + } + + const buffer = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, buffer); + gl.bufferData(gl.ARRAY_BUFFER, indices, gl.STATIC_DRAW); + + const aLoc = gl.getAttribLocation(this.#program, "a_ParticleIndex"); + gl.enableVertexAttribArray(aLoc); + gl.vertexAttribPointer(aLoc, 1, gl.FLOAT, false, 0, 0); + + this.#buffer = buffer; + } + + /** + * Requests an animation frame + */ + #requestDraw() { + this.#rafId = this.window.requestAnimationFrame(t => this.#draw(t)); + } + + /** + * Helper function for setting a uniform in the WebGL context + * + * @param {string} name The property name + * @param {*} value The property value + */ + #setUniform1f(name, value) { + const loc = this.#webglContext.getUniformLocation(this.#program, name); + this.#webglContext.uniform1f(loc, value); + } + + /** + * Renders the output to a canvas + * + * @param {number} time The frametime + */ + #draw(time) { + const gl = this.#webglContext; + gl.useProgram(this.#program); + + if (this.#animationStartTime === -1) { + this.#animationStartTime = time; + } + + const elapsed = time - this.#animationStartTime; + if (elapsed > this.#duration) { + this.#resetForNextRun(); + return; + } + + gl.clear(gl.COLOR_BUFFER_BIT); + this.#setUniform1f("u_ElapsedTime", elapsed); + + gl.drawArrays(gl.POINTS, 0, this.#particlesCount); + this.#requestDraw(); + } + + /** + * Displays a dissolve effect for the element + * + * @param {Element} element The element to dissolve + * @param {Function} onComplete Callback for when the animation is complete + */ + dissolve(element, onComplete) { + if (!this.#initialized || this.#hasTriggered || !element) { + return; + } + this.#hasTriggered = true; + + this.#onComplete = onComplete; + + const rect = element.getBoundingClientRect(); + if (rect.width === 0 || rect.height === 0) { + console.warn("[ZapDissolve]: element has zero size. Skipping dissolve"); + return; + } + + const captureCanvas = this.document.createElement("canvas"); + captureCanvas.width = rect.width; + captureCanvas.height = rect.height; + const ctx = captureCanvas.getContext("2d"); + + const canvas = this.getElementById("zen-zap-dissolve-canvas"); + this.#resizeCanvasToClientSize(canvas); + + ctx.drawWindow( + this.window, + rect.left + this.window.scrollX, + rect.top + this.window.scrollY, + rect.width, + rect.height, + "rgba(0,0,0,0)" + ); + + this.#loadTexture(captureCanvas); + this.#bindParameters(element); + + this.#animationStartTime = -1; + this.#requestDraw(); + } + + get content() { + if (!this.#content || Cu.isDeadWrapper(this.#content)) { + return null; + } + return this.#content; + } + + /** + * Helper for getting an anonymous element by id + * + * @param {string} id The id of the element + */ + getElementById(id) { + return this.content.root.getElementById(id); + } + + get markup() { + return ` + + `; + } + + get fragment() { + if (!this.template) { + let parser = new DOMParser(); + let doc = parser.parseFromString(this.markup, "text/html"); + this.template = this.document.importNode( + doc.querySelector("template"), + true + ); + } + let fragment = this.template.content.cloneNode(true); + return fragment; + } + + /** + * Makes the class ready for reusage + */ + #resetForNextRun() { + const gl = this.#webglContext; + if (gl) { + if (this.#texture) { + gl.deleteTexture(this.#texture); + this.#texture = null; + } + if (this.#buffer) { + gl.deleteBuffer(this.#buffer); + this.#buffer = null; + } + + gl.clear(gl.COLOR_BUFFER_BIT); + } + + this.#hasTriggered = false; + this.#animationStartTime = -1; + + if (this.#rafId) { + this.window.cancelAnimationFrame(this.#rafId); + this.#rafId = null; + } + + if (this.#onComplete) { + this.#onComplete(); + this.#onComplete = null; + } + } + + /** + * Removes all event listeners and removes the overlay from the Anonymous Content + */ + tearDown() { + const gl = this.#webglContext; + if (gl) { + if (this.#texture) { + gl.deleteTexture(this.#texture); + this.#texture = null; + } + if (this.#buffer) { + gl.deleteBuffer(this.#buffer); + this.#buffer = null; + } + + if (this.#program) { + gl.deleteProgram(this.#program); + this.#program = null; + } + + const loseCtx = gl.getExtension("WEBGL_lose_context"); + loseCtx?.loseContext(); + } + + if (this.window != null) { + this.window.cancelAnimationFrame(this.#rafId); + } + this.#rafId = null; + + if (this.#content) { + try { + this.document.removeAnonymousContent(this.#content); + } catch { + /* This might fail but that's not an issue */ + } + } + + this.#content = null; + this.#initialized = false; + } +} diff --git a/src/zen/boosts/ZenZapOverlayChild.sys.mjs b/src/zen/boosts/ZenZapOverlayChild.sys.mjs new file mode 100644 index 000000000..df7c10cc0 --- /dev/null +++ b/src/zen/boosts/ZenZapOverlayChild.sys.mjs @@ -0,0 +1,424 @@ +/* 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, { + ZapDissolve: "resource:///modules/zen/boosts/ZenZapDissolve.sys.mjs", + SelectorComponent: + "resource:///modules/zen/boosts/ZenSelectorComponent.sys.mjs", +}); + +ChromeUtils.defineLazyGetter(lazy, "overlayLocalization", () => { + return new Localization(["browser/zen-boosts.ftl"], true); +}); + +export class ZapOverlay { + document = null; + window = null; + #initialized = false; + #content = null; + + #zapContentIDs = ["zap-list", "zap-controls-container"]; + #selectorComponent = null; + + #dissolvePoolSize = 5; + #dissolveEffectPool = []; + #currentDissolveIndex = 0; + + /** + * @param {*} document Webpage document + * @param {*} zenBoostsChild Boost JSActor child + */ + constructor(document, zenBoostsChild) { + this.document = document; + this.window = document.ownerGlobal; + this.zenBoostsChild = zenBoostsChild; + + this.#selectorComponent = new lazy.SelectorComponent( + document, + zenBoostsChild, + this.#zapContentIDs, + this.handleSelectComponentSelect.bind(this), + [ + { id: "zen-zap-this" }, + { id: "zen-zap-related" }, + { id: "zen-zap-cancel" }, + ] + ); + + // Remove the bottom unzap bar to the safe area + this.#selectorComponent.safeAreaPadding.bottom = 65; + } + + /** + * Initializes the zap mode and inserts anonymous content + */ + async initialize() { + if (this.#initialized) { + console.warn( + "[ZenZapOverlayChild]: Skipping initialize because initialized." + ); + return; + } + + this.#selectorComponent.initialize(); + + this.#content = this.document.insertAnonymousContent(); + this.#content.root.appendChild(this.fragment); + this.#initializeElements(); + + this.#initialized = true; + } + + /** + * Initializes all anonymous content and events + */ + #initializeElements() { + this.zapDoneButton = this.getElementById("zap-done"); + this.zapDoneButton.addEventListener( + "click", + this.#disableZapMode.bind(this) + ); + + this.#updateZappedList(); + } + + /** + * Lazily loads the next available dissolve effect. + * The returned effect might not currently be ready to trigger again. + * + * @returns {Promise} Dissolve effect + */ + async #getNextDissolveEffect() { + // Effect does not exist yet, create and initialize + if (this.#currentDissolveIndex >= this.#dissolveEffectPool.length) { + const dissolveEffect = new lazy.ZapDissolve(this.document); + await dissolveEffect.initialize(); + this.#dissolveEffectPool.push(dissolveEffect); + } + + // Capture current index and increment for next call + const returnIndex = this.#currentDissolveIndex; + this.#currentDissolveIndex = + (this.#currentDissolveIndex + 1) % this.#dissolvePoolSize; + + return this.#dissolveEffectPool[returnIndex]; + } + + get content() { + if (!this.#content || Cu.isDeadWrapper(this.#content)) { + return null; + } + return this.#content; + } + + /** + * Helper for getting an anonymous element by id + * + * @param {string} id The id of the element + */ + getElementById(id) { + return this.content.root.getElementById(id); + } + + get markup() { + // Fetch localizations + let [done] = lazy.overlayLocalization.formatMessagesSync([ + { id: "zen-zap-done" }, + ]); + + return ` + + `; + } + + get fragment() { + if (!this.template) { + let parser = new DOMParser(); + let doc = parser.parseFromString(this.markup, "text/html"); + this.template = this.document.importNode( + doc.querySelector("template"), + true + ); + } + let fragment = this.template.content.cloneNode(true); + return fragment; + } + + /** + * Handles the onSelect callback from the SelectComponent + * + * @param {string} cssSelector The CSS selector of the selected element + */ + handleSelectComponentSelect(cssSelector) { + this.#handleZap(cssSelector); + } + + /** + * Notifies listeners for an update in the zap list + */ + onZapUpdate() { + this.#updateZappedList(); + this.zenBoostsChild.sendNotify("zap-list-update"); + } + + /** + * Handles the addition of the given zap selector + * + * @param {string} cssPath The css selector of the zap + */ + #handleZap(cssPath) { + const useDissolve = Services.prefs.getBoolPref( + "zen.boosts.dissolve-on-zap" + ); + if (!this.window.gReduceMotion && useDissolve) { + const elements = this.document.querySelectorAll(cssPath); + + let counter = 0; + elements.forEach(async element => { + if (counter >= this.#dissolvePoolSize) { + return; + } + counter++; + + this.#getNextDissolveEffect().then(dissolve => { + dissolve.dissolve(element, () => { + this.zenBoostsChild.addZapSelector(cssPath); + this.onZapUpdate(); + + this.window.requestAnimationFrame(() => { + element.style.removeProperty("opacity"); + }); + }); + element.style.setProperty("opacity", "0", "important"); + }); + }); + } else { + this.zenBoostsChild.addZapSelector(cssPath); + this.onZapUpdate(); + } + } + + /** + * Handles the removal of a zap selector + * + * @param {string} cssPath The css selector of the zap + */ + #handleUnzap(cssPath) { + this.zenBoostsChild.removeZapSelector(cssPath); + this.onZapUpdate(); + } + + /** + * Cancles the current zap operation + */ + #cancelZap() { + this.#selectorComponent.setState(lazy.SelectorComponent.STATES.SELECTING); + } + + /** + * Helper function for leaving the zap mode + */ + #disableZapMode() { + this.zenBoostsChild.disableZapMode(); + } + + /** + * Rebuilds the unzap button list at the bottom of the website + */ + async #updateZappedList() { + const zapList = this.getElementById("zap-list"); + zapList.innerHTML = ""; + + const boost = await this.zenBoostsChild.getWebsiteBoost(); + const { boostData } = boost.boostEntry; + + boostData.zapSelectors.forEach(selector => { + const unzapButton = zapList.ownerDocument.createElement("input"); + unzapButton.type = "button"; + unzapButton.id = "zen-zap-unzap"; + + const index = boostData.zapSelectors.indexOf(selector) + 1; + const zappedElementsCount = + selector == "" ? 0 : this.document.querySelectorAll(selector).length; + + const [tooltip] = lazy.overlayLocalization.formatMessagesSync([ + { + id: "zen-unzap-tooltip", + args: { elementCount: zappedElementsCount }, + }, + ]); + + unzapButton.value = index; + unzapButton.title = tooltip.value; + + unzapButton.setAttribute("index", index); + unzapButton.setAttribute("selector", selector); + zapList.appendChild(unzapButton); + }); + + // Fetch localizations + let [addZapHelper, removeZapHelper] = + lazy.overlayLocalization.formatMessagesSync([ + { id: "zen-add-zap-helper" }, + { id: "zen-remove-zap-helper" }, + ]); + + if (!boostData.zapSelectors.length) { + const addZapHelperText = zapList.ownerDocument.createElement("p"); + addZapHelperText.setHTML(addZapHelper.value); + addZapHelperText.classList.add("pcenter"); + zapList.appendChild(addZapHelperText); + } else { + const removeZapHelperText = zapList.ownerDocument.createElement("p"); + removeZapHelperText.setHTML(removeZapHelper.value); + zapList.appendChild(removeZapHelperText); + } + } + + /** + * Handles the mouse enter event for the unzap buttons + * + * @param {Event} event + */ + #unzapButtonHover(event) { + const button = event.originalTarget; + const selector = button.getAttribute("selector"); + this.zenBoostsChild.tempShowZappedElement(selector); + + button.value = "×"; + + // This has to run with later, as the elements we are trying to highlight do not exist yet. + // The css has to load first and calculate the bounding boxes for the elements before we can highlight. + this.window.requestAnimationFrame(() => { + const selection = this.document.querySelectorAll(selector); + if (selection.length) { + this.#selectorComponent.showHighlight(selection); + } + }); + + // Cancle an ongoing select action + this.#cancelZap(); + } + + /** + * Handles the mouse exit event for the unzap buttons + * + * @param {Event} event + */ + #unzapButtonUnhover(event) { + const button = event.originalTarget; + button.value = button.getAttribute("index"); + + this.zenBoostsChild.tempHideZappedElement(); + this.#selectorComponent.removeHighlight(); + } + + /** + * Handles button clicks from the unzap list + * + * @param {Event} event + */ + #unzapButtonClick(event) { + const button = event.originalTarget; + const selector = button.getAttribute("selector"); + + this.zenBoostsChild.tempHideZappedElement(); + this.#selectorComponent.removeHighlight(); + this.#cancelZap(); + + // In order to avoid the clicked element being null when the + // SelectorComponent receives it, push the list re-creation to the next frame + this.window.requestAnimationFrame(() => { + this.#handleUnzap(selector); + }); + } + + /** + * Removes all event listeners and removes the overlay from the Anonymous Content + */ + tearDown() { + this.#selectorComponent.tearDown(); + this.#selectorComponent = null; + + this.#dissolveEffectPool.forEach(dissolve => { + dissolve.tearDown(); + }); + + if (this.#content) { + try { + this.document.removeAnonymousContent(this.#content); + } catch { + /* This might fail but that's not an issue */ + } + } + + this.window = null; + this.document = null; + this.#initialized = false; + } + + /** + * This function handles page events while the overlay is active + * + * @param {Event} event The event which will be handled by the overlay + * @param {boolean} prevent True if the event should be prevented + */ + handleEvent(event, prevent) { + switch (event.type) { + case "click": + this.#handleClick(event); + break; + case "mouseover": + this.#handleHoverDelegation(event); + break; + case "mouseout": + this.#handleUnhoverDelegation(event); + } + + this.#selectorComponent.handleEvent(event, prevent); + } + + /** + * Handles the mouse click event + * + * @param {Event} event Mouse move event params + */ + #handleClick(event) { + if (event.originalTarget.id == "zen-zap-unzap") { + this.#unzapButtonClick(event); + } + } + + /** + * Handles the mouse enter event + * + * @param {Event} event Mouse enter event params + */ + #handleHoverDelegation(event) { + if (event.originalTarget.id == "zen-zap-unzap") { + this.#unzapButtonHover(event); + } + } + + /** + * Handles the mouse leave event + * + * @param {Event} event Mouse leave event params + */ + #handleUnhoverDelegation(event) { + if (event.originalTarget.id == "zen-zap-unzap") { + this.#unzapButtonUnhover(event); + } + } +} diff --git a/src/zen/boosts/actors/ZenBoostsChild.sys.mjs b/src/zen/boosts/actors/ZenBoostsChild.sys.mjs new file mode 100644 index 000000000..4217c3053 --- /dev/null +++ b/src/zen/boosts/actors/ZenBoostsChild.sys.mjs @@ -0,0 +1,539 @@ +// 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 AGENT_SHEET = Ci.nsIStyleSheetService.AGENT_SHEET; + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + ZapOverlay: "resource:///modules/zen/boosts/ZenZapOverlayChild.sys.mjs", + SelectorComponent: + "resource:///modules/zen/boosts/ZenSelectorComponent.sys.mjs", +}); + +export class ZenBoostsChild extends JSWindowActorChild { + #currentSheet = null; + #currentState = ZenBoostsChild.STATES.NONE; + #preventableEventsAdded = false; + #zappedElementsTempShown = []; + + #overlay = null; + + static STATES = { + NONE: "none", + ZAP: "zap", + PICKER: "picker", + }; + + static OVERLAY_EVENTS = [ + "click", + "pointerdown", + "pointermove", + "pointerup", + "scroll", + "resize", + ]; + + // A list of events that will be prevented from + // reaching the document + static PREVENTABLE_EVENTS = [ + "click", + "pointerdown", + "pointermove", + "pointerup", + "mousemove", + "mousedown", + "mouseup", + "mouseenter", + "mouseover", + "mouseout", + "mouseleave", + "touchstart", + "touchmove", + "touchend", + "dblclick", + "auxclick", + "keypress", + "contextmenu", + "pointerenter", + "pointerover", + "pointerout", + "pointerleave", + ]; + + // Caching the events in sets for performance + static ALL_EVENTS_SET = new Set([ + ...ZenBoostsChild.OVERLAY_EVENTS, + ...ZenBoostsChild.PREVENTABLE_EVENTS, + ]); + + static PREVENTABLE_SET = new Set(ZenBoostsChild.PREVENTABLE_EVENTS); + + /** + * Inverse of https://searchfox.org/firefox-main/rev/1a8c62b86277005f907151bc5389cf5c5091e76f/gfx/src/nsColor.h#23-27 + * + * > #define NS_RGBA(_r, _g, _b, _a) \ + * > ((nscolor)(((_a) << 24) | ((_b) << 16) | ((_g) << 8) | (_r))) + * + * Converts [r, g, b] array to (uint32_t) NSColor + * Make a color out of r,g,b,a values. This assumes that the r,g,b,a + * values are properly constrained to 0-255. + * + * @param {Array} rgb - Array of red, green, blue values [0, 255] + * @param {number} rgb.0 - Red color value [0, 255] + * @param {number} rgb.1 - Green color value [0, 255] + * @param {number} rgb.2 - Blue color value [0, 255] + * @param {number} contrast - Contrast value (default 255) + * @returns {number} NSColor integer representation + */ + #rgbToNSColor([r, g, b], contrast = 255) { + // Note will be using the alpha channel for contrast, since the colors will always + // be fully opaque and we need an extra byte to store the contrast value. This allows + // us to still use an nscolor as parameter instead of having to deal with WebIDL structs + // shenanigans. + // Take into account that its an unsigned int + return ((contrast << 24) | (b << 16) | (g << 8) | r) >>> 0; + } + + /** + * From ZenGradientGenerator.mjs + * Converts an HSL color value to RGB. Conversion formula + * adapted from https://en.wikipedia.org/wiki/HSL_color_space. + * Assumes h, s, and l are contained in the set [0, 1] and + * returns r, g, and b in the set [0, 255]. + * + * @param {number} h The hue + * @param {number} s The saturation + * @param {number} l The lightness + * @returns {Array} The RGB representation + */ + #hslToRgb(h, s, l) { + const { round } = Math; + let r, g, b; + + if (s === 0) { + r = g = b = l; // achromatic + } else { + const q = l < 0.5 ? l * (1 + s) : l + s - l * s; + const p = 2 * l - q; + r = this.#hueToRgb(p, q, h + 1 / 3); + g = this.#hueToRgb(p, q, h); + b = this.#hueToRgb(p, q, h - 1 / 3); + } + + return [round(r * 255), round(g * 255), round(b * 255)]; + } + + /** + * From ZenGradientGenerator.mjs + * Inverse of hslToRgb + * Converts an RGB color value to HSL. Conversion formula + * adapted from https://en.wikipedia.org/wiki/HSL_color_space. + * Assumes r, g, and b are contained in the set [0, 255] and + * returns h, s, and l in the set [0, 1]. + * + * @param {number} r The red value + * @param {number} g The green value + * @param {number} b The blue value + * @returns {Array} The HSL representation + */ + #rgbToHsl(r, g, b) { + r /= 255; + g /= 255; + b /= 255; + let max = Math.max(r, g, b); + let min = Math.min(r, g, b); + let d = max - min; + let h; + if (d === 0) { + h = 0; + } else if (max === r) { + h = ((g - b) / d) % 6; + } else if (max === g) { + h = (b - r) / d + 2; + } else if (max === b) { + h = (r - g) / d + 4; + } + let l = (min + max) / 2; + let s = d === 0 ? 0 : d / (1 - Math.abs(2 * l - 1)); + return [h * 60, s, l]; + } + + /** + * Handles DOM events for the actor. Applies boost settings when a document + * element is inserted. + * + * @param {Event} event - The DOM event to handle. + */ + handleEvent(event) { + switch (event.type) { + case "unload": + if (this.#currentState === ZenBoostsChild.STATES.ZAP) { + this.disableZapMode(); + } + this.#removeEventListeners(); + break; + case "DOMWindowCreated": + this.#applyBoostForPageIfAvailable(); + break; + default: + break; + } + } + + handleZapEvent(event) { + if (ZenBoostsChild.ALL_EVENTS_SET.has(event.type)) { + this.#overlay.handleEvent( + event, + ZenBoostsChild.PREVENTABLE_SET.has(event.type) + ); + } + } + + /** + * Adds necessary event listeners to the document + * to prevent content interactions + */ + #addEventListeners() { + this._handleZapEvent = this.handleZapEvent.bind(this); + this._disableZapMode = this.disableZapMode.bind(this); + + for (let event of ZenBoostsChild.OVERLAY_EVENTS) { + this.document.addEventListener(event, this._handleZapEvent, true); + } + + for (let event of ZenBoostsChild.PREVENTABLE_EVENTS) { + this.document.addEventListener(event, this._handleZapEvent, true); + } + this.#preventableEventsAdded = true; + } + + /** + * Removes the event listeners from the document + */ + #removeEventListeners() { + for (let event of ZenBoostsChild.OVERLAY_EVENTS) { + this.document.removeEventListener(event, this._handleZapEvent, true); + } + + if (this.#preventableEventsAdded) { + for (let event of ZenBoostsChild.PREVENTABLE_EVENTS) { + this.document.removeEventListener(event, this._handleZapEvent, true); + } + } + this.#preventableEventsAdded = false; + } + + /** + * Handles messages received from the parent actor. + * + * @param {object} message - The message object containing name and data. + */ + async receiveMessage(message) { + switch (message.name) { + case "ZenBoost:BoostDataUpdated": { + const { unloadStyles = false } = message.data || {}; + this.#applyBoostForPageIfAvailable(unloadStyles); + break; + } + case "ZenBoost:DisableZapMode": + if (this.#currentState === ZenBoostsChild.STATES.ZAP) { + this.disableZapMode(); + } + break; + case "ZenBoost:DisablePickerMode": + if (this.#currentState === ZenBoostsChild.STATES.PICKER) { + this.disablePickerMode(); + } + break; + case "ZenBoost:ToggleZapMode": + if (this.#currentState === ZenBoostsChild.STATES.NONE) { + this.#startZappingOverlay(); + } else if (this.#currentState === ZenBoostsChild.STATES.ZAP) { + this.disableZapMode(); + } + break; + case "ZenBoost:TogglePickerMode": + if (this.#currentState === ZenBoostsChild.STATES.NONE) { + this.#startPickingOverlay(); + } else if (this.#currentState === ZenBoostsChild.STATES.PICKER) { + this.disablePickerMode(); + } + break; + case "ZenBoost:ZapModeEnabled": + return this.#currentState === ZenBoostsChild.STATES.ZAP; + case "ZenBoost:ZapModeAny": { + const { boostData } = (await this.getWebsiteBoost())?.boostEntry || {}; + return !!boostData?.zapSelectors?.length; + } + case "ZenBoost:SelectorPickerModeEnabled": + return this.#currentState === ZenBoostsChild.STATES.PICKER; + case "ZenBoost:OpenInspector": + this.sendAsyncMessage("ZenBoost:OpenInspector"); + break; + } + return null; + } + + /** + * From ZenGradientGenerator.mjs + * Helper function for hslToRgb conversion + * + * @param {number} p + * @param {number} q + * @param {number} t + */ + #hueToRgb(p, q, t) { + if (t < 0) { + t += 1; + } + if (t > 1) { + t -= 1; + } + if (t < 1 / 6) { + return p + (q - p) * 6 * t; + } + if (t < 1 / 2) { + return q; + } + if (t < 2 / 3) { + return p + (q - p) * (2 / 3 - t) * 6; + } + return p; + } + + /** + * Aquires the boost data for this website + * + * @returns {object} Boost data for the current website + */ + getWebsiteBoost() { + const domain = this.browsingContext.topWindow?.location?.host; + if (!domain) { + return null; + } + + return this.sendQuery("ZenBoost:GetBoostForDomain", domain); + } + + /** + * Applies the boost settings for the current page if available. + * + * @param {boolean} unloadStyles - Indicates whether to unload styles. + */ + async #applyBoostForPageIfAvailable(unloadStyles = false) { + const browsingContext = this.browsingContext; + + // Prevent applying boosts to iframes or non-top-level browsing contexts. + // It makes the tab crash if we try to load stylesheets into an iframe's + if (!browsingContext || browsingContext.parent !== null) { + return; + } + + const boost = await this.getWebsiteBoost(); + + if (unloadStyles) { + this.#unloadCurrentStyleSheet(); + } + + if (boost) { + const { boostData } = boost.boostEntry; + if (boost.styleSheet) { + this.#loadStyleSheet(boost.styleSheet); + } + + browsingContext.isZenBoostsInverted = boostData.smartInvert; + if (boostData.enableColorBoost) { + if (boostData.autoTheme) { + // Workspace color is converted to the HSL color space + let primaryGradientColor = boost.workspaceGradient[0]?.c ?? [ + 0, 0, 0.6, + ]; + boost.workspaceGradient.forEach(color => { + if (color.isPrimary) { + primaryGradientColor = this.#rgbToHsl( + color.c[0], + color.c[1], + color.c[2] + ); + } + }); + + // Workspace color is converted back to rgb + // using the same modifiers as the color above + primaryGradientColor = this.#hslToRgb( + primaryGradientColor[0] / 360, + primaryGradientColor[1] * (1 - boostData.saturation), + 0.1 + primaryGradientColor[2] * 0.9 * boostData.brightness + ); + + const rgbColor = primaryGradientColor; + const nsColor = this.#rgbToNSColor( + rgbColor, + (1 - boostData.contrast) * 255 + ); + browsingContext.zenBoostsData = nsColor; + } else { + let colorWheelColor = this.#hslToRgb( + boostData.dotAngleDeg / 360, + /* already is [0, 1] */ + boostData.dotDistance * (1 - boostData.saturation), + /* lightness range from [0.1, 0.9] */ + 0.1 + boostData.dotDistance * 0.8 * boostData.brightness + ); + + const rgbColor = colorWheelColor; + const nsColor = this.#rgbToNSColor( + rgbColor, + (1 - boostData.contrast) * 255 + ); + browsingContext.zenBoostsData = nsColor; + } + return; + } + } else { + browsingContext.isZenBoostsInverted = false; + } + browsingContext.zenBoostsData = 0; + } + + /** + * Loads the given stylesheet into the website + * + * @param {object} styleSheet The stylesheet + */ + #loadStyleSheet(styleSheet) { + const browsingContext = this.browsingContext; + styleSheet.uri = Services.io.newURI(styleSheet.uri); + + if (this.#currentSheet?.uuid !== styleSheet.uuid) { + if (this.#currentSheet) { + this.#unloadCurrentStyleSheet(); + } + browsingContext.window.windowGlobalChild.browsingContext.window.windowUtils.loadSheet( + styleSheet.uri, + AGENT_SHEET + ); + this.#currentSheet = styleSheet; + } + } + + /** + * Unloads the currently loaded stylesheet + */ + #unloadCurrentStyleSheet() { + const browsingContext = this.browsingContext; + if (this.#currentSheet && browsingContext) { + browsingContext.window.windowGlobalChild.browsingContext.window.windowUtils.removeSheet( + this.#currentSheet.uri, + AGENT_SHEET + ); + this.#currentSheet = null; + } + } + + async #startZappingOverlay() { + if (this.#currentState === ZenBoostsChild.STATES.ZAP) { + return; + } + this.#currentState = ZenBoostsChild.STATES.ZAP; + + this.#overlay = new lazy.ZapOverlay(this.document, this); + this.#overlay.initialize(); + + this.#addEventListeners(); + this.sendNotify("zap-state-update"); + } + + async #startPickingOverlay() { + if (this.#currentState === ZenBoostsChild.STATES.PICKER) { + return; + } + this.#currentState = ZenBoostsChild.STATES.PICKER; + + this.#overlay = new lazy.SelectorComponent( + this.document, + this, + [], // No additional IDs needed + this.onPickerSelection.bind(this) + ); + + this.#overlay.initialize(); + + this.#addEventListeners(); + this.sendNotify("selector-picker-state-update", "onenable"); + } + + onPickerSelection(cssSelector) { + this.sendNotify("selector-picker-picked", cssSelector); + } + + addZapSelector(selector) { + const domain = this.browsingContext.topWindow?.location?.host; + this.sendQuery("ZenBoost:ZapSelector", { + action: "add", + selector, + domain, + }); + } + + removeZapSelector(selector) { + const domain = this.browsingContext.topWindow?.location?.host; + this.sendQuery("ZenBoost:ZapSelector", { + action: "remove", + selector, + domain, + }); + } + + async tempShowZappedElement(selector) { + this.document.querySelectorAll(selector).forEach(element => { + element.setAttribute("zen-zap-unhide", "true"); + }); + + if (!this.#zappedElementsTempShown.includes(selector)) { + this.#zappedElementsTempShown.push(selector); + } + } + + async tempHideZappedElement() { + this.#zappedElementsTempShown.forEach(selector => { + this.document.querySelectorAll(selector).forEach(element => { + element.removeAttribute("zen-zap-unhide"); + }); + }); + + this.#zappedElementsTempShown = []; + } + + disableZapMode() { + if (this.#currentState === ZenBoostsChild.STATES.NONE) { + return; + } + this.#currentState = ZenBoostsChild.STATES.NONE; + + this.#overlay?.tearDown(); + this.#overlay = null; + + this.#removeEventListeners(); + this.sendNotify("zap-state-update"); + } + + disablePickerMode() { + if (this.#currentState === ZenBoostsChild.STATES.NONE) { + return; + } + this.#currentState = ZenBoostsChild.STATES.NONE; + + this.#overlay?.tearDown(); + this.#overlay = null; + + this.#removeEventListeners(); + this.sendNotify("selector-picker-state-update", "ondisable"); + } + + sendNotify(topic, msg = null) { + this.sendAsyncMessage("ZenBoost:Notify", { topic, msg }); + } +} diff --git a/src/zen/boosts/actors/ZenBoostsParent.sys.mjs b/src/zen/boosts/actors/ZenBoostsParent.sys.mjs new file mode 100644 index 000000000..50e251955 --- /dev/null +++ b/src/zen/boosts/actors/ZenBoostsParent.sys.mjs @@ -0,0 +1,185 @@ +// 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, { + gZenBoostsManager: "resource:///modules/zen/boosts/ZenBoostsManager.sys.mjs", +}); + +export class ZenBoostsParent extends JSWindowActorParent { + static OBSERVERS = [ + "zen-boosts-update", + "zen-space-gradient-update", + "zen-boosts-disable-zap", + "zen-boosts-disable-picker", + ]; + + // Topics the content child is allowed to forward to the observer service. + // Anything outside this set is rejected to prevent content-triggered + // notifications on unrelated chrome observers. + static ALLOWED_NOTIFY_TOPICS = new Set([ + "zap-state-update", + "zap-list-update", + "selector-picker-state-update", + "selector-picker-picked", + ]); + + /** + * Creates a new ZenBoostsParent actor instance and sets up an observer + * for boost update notifications. + */ + constructor() { + super(); + + this._observe = this.observe.bind(this); + ZenBoostsParent.OBSERVERS.forEach(observe => { + Services.obs.addObserver(this._observe, observe); + }); + } + + /** + * Called when the actor is destroyed. Cleans up the observer. + */ + didDestroy() { + ZenBoostsParent.OBSERVERS.forEach(observe => { + Services.obs.removeObserver(this._observe, observe); + }); + } + + /** + * Observer callback that handles boost update notifications. + * Sends a message to child actors when boosts are updated. + * + * @param {object} subject - The subject of the notification. + * @param {string} topic - The topic of the notification. + */ + observe(subject, topic) { + switch (topic) { + case "zen-boosts-update": + case "zen-space-gradient-update": + this.sendAsyncMessage("ZenBoost:BoostDataUpdated", { + unloadStyles: true, + }); + break; + case "zen-boosts-disable-zap": + this.sendAsyncMessage("ZenBoost:DisableZapMode"); + break; + case "zen-boosts-disable-picker": + this.sendAsyncMessage("ZenBoost:DisablePickerMode"); + break; + } + } + + /** + * Handles messages received from child actors. + * Retrieves boost data for a domain when requested. + * + * @param {object} message - The message object containing name and data. + * @returns {Promise} A promise that resolves to the boost data or null. + */ + async receiveMessage(message) { + switch (message.name) { + case "ZenBoost:OpenInspector": { + const { require } = ChromeUtils.importESModule( + "resource://devtools/shared/loader/Loader.sys.mjs" + ); + + const { gDevTools } = require("devtools/client/framework/devtools"); + + let win = Services.wm.getMostRecentWindow("navigator:browser"); + let tab = win.gBrowser.selectedTab; + + let toolbox = gDevTools.getToolboxForTab(tab); + + if (toolbox) { + await gDevTools.closeToolboxForTab(tab); + } else { + await gDevTools.showToolboxForTab(tab, "inspector"); + } + break; + } + case "ZenBoost:Notify": { + const { topic, msg } = message.data ?? {}; + if (!ZenBoostsParent.ALLOWED_NOTIFY_TOPICS.has(topic)) { + console.warn( + `[ZenBoostsParent]: Rejected notify for disallowed topic: ${topic}` + ); + break; + } + Services.obs.notifyObservers(null, topic, msg); + break; + } + case "ZenBoost:ZapSelector": { + const data = message.data; + + if (!data.action) { + break; + } + if (!data.selector) { + break; + } + if (!data.domain) { + break; + } + + if (data.action == "add") { + lazy.gZenBoostsManager.addZapSelectorToActive( + data.selector, + data.domain + ); + } else if (data.action == "remove") { + lazy.gZenBoostsManager.removeZapSelectorToActive( + data.selector, + data.domain + ); + } else if (data.action == "clear") { + lazy.gZenBoostsManager.clearZapSelectorsForActive(data.domain); + } + break; + } + case "ZenBoost:GetBoostForDomain": { + const domain = message.data; + const embedder = this.browsingContext.top.embedderElement; + + if (!embedder || !domain) { + break; + } + + const exists = lazy.gZenBoostsManager.registeredBoostForDomain(domain); + if (!exists) { + break; + } + + const boost = lazy.gZenBoostsManager.loadActiveBoostFromStore(domain); + let workspaceGradient = []; + if (boost.boostEntry.boostData.autoTheme) { + const currentWorkspace = + await this.browsingContext.topChromeWindow.gZenWorkspaces.getActiveWorkspace(); + workspaceGradient = currentWorkspace.theme.gradientColors; + } + + const styleData = + await lazy.gZenBoostsManager.getStyleSheetForBoost(domain); + + return { + ...boost, + workspaceGradient, + styleSheet: styleData + ? { + uuid: styleData.uuid, + uri: styleData.uri.spec, + } + : null, + }; + } + default: { + console.warn(`[ZenBoostsParent]: Unknown message: ${message.name}`); + break; + } + } + + return null; + } +} diff --git a/src/zen/boosts/jar.inc.mn b/src/zen/boosts/jar.inc.mn new file mode 100644 index 000000000..b17e886b6 --- /dev/null +++ b/src/zen/boosts/jar.inc.mn @@ -0,0 +1,14 @@ +# 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/. + + content/browser/zen-styles/zen-boosts.css (../../zen/boosts/zen-boosts.css) + content/browser/zen-styles/content/zen-zap.css (../../zen/boosts/zen-zap.css) + content/browser/zen-styles/content/zen-selector.css (../../zen/boosts/zen-selector.css) + content/browser/zen-styles/zen-advanced-color-options.css (../../zen/boosts/zen-advanced-color-options.css) + + # Windows +* content/browser/zen-components/windows/zen-boost-editor.xhtml (../../zen/boosts/zen-boost-editor.inc.xhtml) + + # Images + content/browser/zen-images/boost-indicator.svg (../../zen/images/boost-indicator.svg) diff --git a/src/zen/boosts/moz.build b/src/zen/boosts/moz.build new file mode 100644 index 000000000..211d68fc2 --- /dev/null +++ b/src/zen/boosts/moz.build @@ -0,0 +1,28 @@ +# 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/. + +EXTRA_JS_MODULES.zen.boosts += [ + "ZenBoostsEditor.mjs", + "ZenBoostsManager.sys.mjs", + "ZenBoostStyles.sys.mjs", + "ZenSelectorComponent.sys.mjs", + "ZenZapDissolve.sys.mjs", + "ZenZapOverlayChild.sys.mjs", +] + +FINAL_TARGET_FILES.actors += [ + "actors/ZenBoostsChild.sys.mjs", + "actors/ZenBoostsParent.sys.mjs", +] + +EXPORTS.mozilla += [ + "nsZenBoostsBackend.h", +] + +SOURCES += [ + "nsZenBCOverrides.cpp", + "nsZenBoostsBackend.cpp", +] + +FINAL_LIBRARY = "xul" diff --git a/src/zen/boosts/nsZenBCOverrides.cpp b/src/zen/boosts/nsZenBCOverrides.cpp new file mode 100644 index 000000000..89c75b04e --- /dev/null +++ b/src/zen/boosts/nsZenBCOverrides.cpp @@ -0,0 +1,83 @@ +/* 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/. */ + +#include "nsZenBoostsBackend.h" + +#include "nsIXULRuntime.h" +#include "nsPresContext.h" + +#include "mozilla/StaticPtr.h" + +#include "mozilla/MediaFeatureChange.h" + +#include "mozilla/dom/Document.h" +#include "mozilla/dom/DocumentInlines.h" +#include "mozilla/dom/BrowsingContext.h" + +#define MARK_MEDIA_FEATURE_CHANGED(_pc) \ + (_pc)->MediaFeatureValuesChanged( \ + {mozilla::RestyleHint::RecascadeSubtree(), NS_STYLE_HINT_VISUAL, \ + mozilla::MediaFeatureChangeReason::PreferenceChange}, \ + mozilla::MediaFeatureChangePropagation::All); + +#define TRIGGER_PRES_CONTEXT_RESTYLE() \ + WalkPresContexts( \ + [&](nsPresContext* aPc) { MARK_MEDIA_FEATURE_CHANGED(aPc); }); + +using BrowsingContext = mozilla::dom::BrowsingContext; + +template +void BrowsingContext::WalkPresContexts(Callback&& aCallback) { + PreOrderWalk([&](BrowsingContext* aContext) { + if (nsIDocShell* shell = aContext->GetDocShell()) { + if (RefPtr pc = shell->GetPresContext()) { + aCallback(pc.get()); + } + } + }); +} + +static void RefreshBoostCacheIfMatchesCurrent(BrowsingContext* aChanged) { + auto* backend = zen::nsZenBoostsBackend::GetInstance(); + if (!backend) { + return; + } + auto current = backend->GetCurrentBrowsingContext(); + if (!current || current->Top() != aChanged) { + return; + } + backend->RefreshCachedBoostState(); +} + +/** + * @brief Called when the ZenBoostsData field is set on a browsing context. + * Triggers a restyle if the boost data has changed. + * @param aOldValue The previous value of the boost data. + */ +void BrowsingContext::DidSet(FieldIndex, + ZenBoostData aOldValue) { + MOZ_ASSERT(IsTop()); + if (ZenBoostsData() == aOldValue) { + return; + } + RefreshBoostCacheIfMatchesCurrent(this); + PresContextAffectingFieldChanged(); + TRIGGER_PRES_CONTEXT_RESTYLE(); +} + +/** + * @brief Called when the IsZenBoostsInverted field is set on a browsing + * context. Triggers a restyle if the value has changed. + * @param aOldValue The previous value of the IsZenBoostsInverted flag. + */ +void BrowsingContext::DidSet(FieldIndex, + bool aOldValue) { + MOZ_ASSERT(IsTop()); + if (IsZenBoostsInverted() == aOldValue) { + return; + } + RefreshBoostCacheIfMatchesCurrent(this); + PresContextAffectingFieldChanged(); + TRIGGER_PRES_CONTEXT_RESTYLE(); +} diff --git a/src/zen/boosts/nsZenBoostsBackend.cpp b/src/zen/boosts/nsZenBoostsBackend.cpp new file mode 100644 index 000000000..9c545ddf4 --- /dev/null +++ b/src/zen/boosts/nsZenBoostsBackend.cpp @@ -0,0 +1,382 @@ +/* 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/. */ + +#include +#include +#include + +#include "nsZenBoostsBackend.h" + +#include "nsIXULRuntime.h" +#include "nsPresContext.h" + +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/StaticPtr.h" + +#include "mozilla/ServoStyleConsts.h" +#include "mozilla/ServoStyleConstsInlines.h" +#include "mozilla/MediaFeatureChange.h" + +#include "mozilla/dom/Document.h" +#include "mozilla/dom/DocumentInlines.h" +#include "mozilla/dom/BrowsingContext.h" + +#include "mozilla/StaticPrefs_zen.h" + +// Lower bound applied to inverted channels so that pure white doesn't invert +// all the way to pure black, which makes inverted pages feel too dark. +#define INVERT_CHANNEL_FLOOR() \ + (mozilla::StaticPrefs::zen_boosts_invert_channel_floor_AtStartup()) + +#define SHOULD_APPLY_BOOSTS_TO_ANONYMOUS_CONTENT() \ + (!mozilla::StaticPrefs::zen_boosts_disable_on_anonymous_content_AtStartup()) + +#if defined(__clang__) || defined(__GNUC__) +# define ZEN_HOT_FUNCTION __attribute__((hot)) +#else +# define ZEN_HOT_FUNCTION +#endif + +// It's a bit of a hacky solution, but instead of using alpha as what it is +// (opacity), we use it to store contrast information for now. +// We do this primarily to avoid having to deal with WebIDL structs and +// serialization/deserialization between parent and content processes. +#define NS_GET_CONTRAST(_c) NS_GET_A(_c) + +namespace zen { + +nsZenAccentOklab nsZenBoostsBackend::mCachedAccent{0}; + +namespace { + +/** + * @brief Converts an sRGB color component to linear space. + * @param c The sRGB color component value (0.0 to 1.0). + * @return The linear color component value. + */ +static inline float srgbToLinear(float c) { + return c <= 0.04045f ? c * (1.0f / 12.92f) + : std::pow((c + 0.055f) * (1.0f / 1.055f), 2.4f); +} + +/** + * @brief Converts a linear color component to sRGB space. + * @param c The linear color component value. + * @return The sRGB color component value (0.0 to 1.0). + */ +static inline float linearToSrgb(float c) { + c = std::max(0.0f, c); + return c <= 0.0031308f ? 12.92f * c + : 1.055f * std::pow(c, 1.0f / 2.4f) - 0.055f; +} + +/* + * @brief Fast approximation of the cube root of a number. + * @param x The input value. + * @return The approximate cube root of the input value. + */ +static inline float fastCbrt(float x) { + if (x == 0.0f) return 0.0f; + float a = std::abs(x); + union { + float f; + uint32_t i; + } u = {a}; + u.i = u.i / 3 + 0x2a504a2e; + float y = u.f; + y = (2.0f * y + a / (y * y)) * (1.0f / 3.0f); + y = (2.0f * y + a / (y * y)) * (1.0f / 3.0f); + return x < 0.0f ? -y : y; +} + +/** + * @brief Precomputes the Oklab values for a given accent color. This allows us + * to efficiently apply the accent color as a filter to other colors without + * having to convert the accent color from sRGB to Oklab space on every filter + * operation. + * @param aAccentColor The accent color in nscolor format. + * @return A struct containing the precomputed Oklab values and contrast factor + * for the accent color. + */ +ZEN_HOT_FUNCTION +inline static auto zenPrecomputeAccent(nscolor aAccentColor) { + constexpr float inv255 = 1.0f / 255.0f; + + const float r = NS_GET_R(aAccentColor) * inv255; + const float g = NS_GET_G(aAccentColor) * inv255; + const float b = NS_GET_B(aAccentColor) * inv255; + + const float lr = srgbToLinear(r); + const float lg = srgbToLinear(g); + const float lb = srgbToLinear(b); + + const float l_ = + fastCbrt(0.4122214708f * lr + 0.5363325363f * lg + 0.0514459929f * lb); + const float m_ = + fastCbrt(0.2119034982f * lr + 0.6806995451f * lg + 0.1073969566f * lb); + const float s_ = + fastCbrt(0.0883024619f * lr + 0.2817188376f * lg + 0.6299787005f * lb); + + return nsZenAccentOklab{ + .accentNS = aAccentColor, + .accL = 0.2104542553f * l_ + 0.7936177850f * m_ - 0.0040720468f * s_, + .accA = 1.9779984951f * l_ - 2.4285922050f * m_ + 0.4505937099f * s_, + .accB = 0.0259040371f * l_ + 0.7827717662f * m_ - 0.8086757660f * s_, + .contrastFactor = NS_GET_CONTRAST(aAccentColor) * inv255, + }; +} + +/** + * @brief Applies a color filter to transform an original color toward an accent + * color. Preserves the original color's perceived luminance while shifting + * hue/chroma toward the accent. Uses the alpha channel of the accent color to + * store contrast information. + * @param aOriginalColor The original color to filter. + * @param aAccentColor The accent color to filter toward (alpha channel contains + * contrast value). + * @return The filtered color with transformations applied. + */ +[[nodiscard]] ZEN_HOT_FUNCTION static inline nscolor zenFilterColorChannel( + nscolor aOriginalColor, const nsZenAccentOklab& aAccent) { + const uint8_t oL = NS_GET_A(aOriginalColor); + const uint8_t contrast = NS_GET_CONTRAST(aAccent.accentNS); + if (oL == 0) { + return aOriginalColor; + } + + constexpr float inv255 = 1.0f / 255.0f; + const float blendFactor = contrast * inv255; + + // sRGB -> linear + const float lr = srgbToLinear(NS_GET_R(aOriginalColor) * inv255); + const float lg = srgbToLinear(NS_GET_G(aOriginalColor) * inv255); + const float lb = srgbToLinear(NS_GET_B(aOriginalColor) * inv255); + + // Linear RGB -> LMS -> cube root -> Oklab (fused) + const float l_ = + fastCbrt(0.4122214708f * lr + 0.5363325363f * lg + 0.0514459929f * lb); + const float m_ = + fastCbrt(0.2119034982f * lr + 0.6806995451f * lg + 0.1073969566f * lb); + const float s_ = + fastCbrt(0.0883024619f * lr + 0.2817188376f * lg + 0.6299787005f * lb); + + const float origL = + 0.2104542553f * l_ + 0.7936177850f * m_ - 0.0040720468f * s_; + const float origA = + 1.9779984951f * l_ - 2.4285922050f * m_ + 0.4505937099f * s_; + const float origB = + 0.0259040371f * l_ + 0.7827717662f * m_ - 0.8086757660f * s_; + + // Blend chroma toward accent + const float bA = origA + (aAccent.accA - origA) * blendFactor; + const float bB = origB + (aAccent.accB - origB) * blendFactor; + + // Luminance: at low contrast stay near the original, the higher the contrast, + // the more we shift toward the accent luminance, but we never go fully to + // the accent luminance to preserve some of the original color's character. + const float lumDelta = aAccent.accL - origL; + const float fL = + origL + lumDelta * (blendFactor * aAccent.contrastFactor * 0.5f); + + // Rotate hue in the Oklab a/b plane. Direction follows the luminance shift: + // pushing darker rotates clockwise ("right"), pushing lighter rotates the + // other way. Magnitude scales with blend strength so subtle accents stay + // subtle. + const float rotAngle = (lumDelta > 0.0f ? -1.0f : 1.0f) * blendFactor * + aAccent.contrastFactor * 0.25f; + const float cosR = std::cos(rotAngle); + const float sinR = std::sin(rotAngle); + const float fA = bA * cosR - bB * sinR; + const float fB = bA * sinR + bB * cosR; + + // Oklab -> LMS + const float fl_ = fL + 0.3963377774f * fA + 0.2158037573f * fB; + const float fm_ = fL - 0.1055613458f * fA - 0.0638541728f * fB; + const float fs_ = fL - 0.0894841775f * fA - 1.2914855480f * fB; + + // Cube + const float fl = fl_ * fl_ * fl_; + const float fm = fm_ * fm_ * fm_; + const float fs = fs_ * fs_ * fs_; + + // LMS -> linear RGB + const float rF = 4.0767416621f * fl - 3.3077115913f * fm + 0.2309699292f * fs; + const float gF = + -1.2684380046f * fl + 2.6097574011f * fm - 0.3413193965f * fs; + const float bF = + -0.0041960863f * fl - 0.7034186147f * fm + 1.7076147010f * fs; + + // Linear -> sRGB -> uint8 + return NS_RGBA(static_cast(std::clamp( + linearToSrgb(rF) * 255.0f + 0.5f, 0.0f, 255.0f)), + static_cast(std::clamp( + linearToSrgb(gF) * 255.0f + 0.5f, 0.0f, 255.0f)), + static_cast(std::clamp( + linearToSrgb(bF) * 255.0f + 0.5f, 0.0f, 255.0f)), + oL); +} + +/** + * @brief Inverts a color by inverting each RGB channel while preserving + * perceived luminance. This is done by inverting the color and then shifting it + * based on the sum of the inverted channels. + * @param aColor The color to invert. + * @return The inverted color with luminance preservation. + */ +ZEN_HOT_FUNCTION +inline static nscolor zenInvertColorChannel(nscolor aColor) { + const auto r = NS_GET_R(aColor); + const auto g = NS_GET_G(aColor); + const auto b = NS_GET_B(aColor); + const auto a = NS_GET_A(aColor); + if (a == 0) { + // Skip processing fully transparent colors since they won't be visible and + // we want to avoid unnecessary computations. + return aColor; + } + + const auto rInv = 255 - r; + const auto gInv = 255 - g; + const auto bInv = 255 - b; + + const auto max = std::max({rInv, gInv, bInv}); + const auto min = std::min({rInv, gInv, bInv}); + const auto sum = max + min; + + const auto rShifted = sum - rInv; + const auto gShifted = sum - gInv; + const auto bShifted = sum - bInv; + + // Compress the channel range into [FLOOR, 255] so dark inversions are + // lifted while light inversions are left untouched. This preserves hue + // since all three channels are scaled by the same factor. + const auto channelFloor = INVERT_CHANNEL_FLOOR(); + const uint32_t range = 255 - channelFloor; + const auto lift = [channelFloor, range](uint8_t c) -> uint8_t { + return static_cast(channelFloor + (c * range) / 255); + }; + + return NS_RGBA(lift(rShifted), lift(gShifted), lift(bShifted), a); +} + +/** + * @brief Retrieves the current boost data from the browsing context. When + * called without aPresContext, reads the precomputed cache populated on + * presshell entry; otherwise resolves from the supplied PresContext. + */ +ZEN_HOT_FUNCTION +inline static void GetZenBoostsDataFromBrowsingContext( + ZenBoostData* aData, bool* aIsInverted, + nsPresContext* aPresContext = nullptr) { + auto zenBoosts = nsZenBoostsBackend::GetInstance(); + if (!zenBoosts || (zenBoosts->mCurrentFrameIsAnonymousContent && + !SHOULD_APPLY_BOOSTS_TO_ANONYMOUS_CONTENT())) { + return; + } + if (!aPresContext) { + *aData = zenBoosts->mCachedCurrentAccent; + *aIsInverted = zenBoosts->mCachedCurrentInverted; + return; + } + mozilla::dom::BrowsingContext* browsingContext = nullptr; + if (auto document = aPresContext->Document()) { + browsingContext = document->GetBrowsingContext(); + } + if (!browsingContext) { + return; + } + browsingContext = browsingContext->Top(); + *aData = browsingContext->ZenBoostsData(); + *aIsInverted = browsingContext->IsZenBoostsInverted(); +} + +} // namespace + +static mozilla::StaticAutoPtr sZenBoostsBackend; + +auto nsZenBoostsBackend::GetInstance() -> nsZenBoostsBackend* { + if (!XRE_IsContentProcess()) { + // Zen boosts are only supported in content, so if we're in the parent + // process, just return null. + return nullptr; + } + if (!sZenBoostsBackend) { + sZenBoostsBackend = new nsZenBoostsBackend(); + mozilla::ClearOnShutdown(&sZenBoostsBackend); + } + return sZenBoostsBackend.get(); +} + +auto nsZenBoostsBackend::onPresShellEntered(mozilla::dom::Document* aDocument) + -> void { + if (auto displayDoc = aDocument->GetDisplayDocument()) { + onPresShellEntered(displayDoc); + return; + } + // Note that aDocument can be null when entering anonymous content frames. + // We explicitly do this to prevent applying boosts to anonymous content, such + // as devtools or screenshots. + mozilla::dom::BrowsingContext* browsingContext = + aDocument ? aDocument->GetBrowsingContext() : nullptr; + if (!browsingContext) { + return; + } + mCurrentBrowsingContext = browsingContext; + RefreshCachedBoostState(); +} + +auto nsZenBoostsBackend::RefreshCachedBoostState() -> void { + if (!mCurrentBrowsingContext) { + mCachedCurrentAccent = 0; + mCachedCurrentInverted = false; + return; + } + auto top = mCurrentBrowsingContext->Top(); + mCachedCurrentAccent = top->ZenBoostsData(); + mCachedCurrentInverted = top->IsZenBoostsInverted(); +} + +[[nodiscard]] ZEN_HOT_FUNCTION auto +nsZenBoostsBackend::FilterColorFromPresContext(nscolor aColor, + nsPresContext* aPresContext) + -> nscolor { + ZenBoostData accentNS = 0; + bool invertColors = false; + GetZenBoostsDataFromBrowsingContext(&accentNS, &invertColors, aPresContext); + if (accentNS) { + if (mCachedAccent.accentNS != accentNS) { + mCachedAccent = zenPrecomputeAccent(accentNS); + } + // Apply a filter-like tint: + // - Preserve the original color's perceived luminance + // - Map hue/chroma toward the accent by scaling the accent's RGB + // to match the original luminance + // - Keep the original alpha + // Convert both colors to nscolor to access channels + aColor = zenFilterColorChannel(aColor, mCachedAccent); + } + if (invertColors) { + aColor = zenInvertColorChannel(aColor); + } + return aColor; +} + +[[nodiscard]] ZEN_HOT_FUNCTION auto nsZenBoostsBackend::ResolveStyleColor( + mozilla::StyleAbsoluteColor aColor) -> mozilla::StyleAbsoluteColor { + const auto resultColor = FilterColorFromPresContext(aColor.ToColor()); + return mozilla::StyleAbsoluteColor::FromColor(resultColor); +} + +[[nodiscard]] ZEN_HOT_FUNCTION auto nsZenBoostsBackend::ResolveStyleColor( + nscolor aColor) -> nscolor { + if (NS_GET_A(aColor) == 0) { + // Skip processing fully transparent colors since they won't be visible and + // we want to avoid unnecessary computations. This also prevents issues with + // using the alpha channel for contrast information in the accent color. + return aColor; + } + return FilterColorFromPresContext(aColor); +} + +} // namespace zen diff --git a/src/zen/boosts/nsZenBoostsBackend.h b/src/zen/boosts/nsZenBoostsBackend.h new file mode 100644 index 000000000..b79d7ef4e --- /dev/null +++ b/src/zen/boosts/nsZenBoostsBackend.h @@ -0,0 +1,106 @@ +/* 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/. */ + +#ifndef mozilla_ZenBoostsBackend_h_ +#define mozilla_ZenBoostsBackend_h_ + +#include "nsColor.h" +#include "nsPresContext.h" + +#include "mozilla/RefPtr.h" + +#define ZEN_BOOSTS_BACKEND_CONTRACTID "@mozilla.org/zen/boosts-backend;1" + +using ZenBoostData = nscolor; // For now, Zen boosts data is just a color. + +namespace zen { + +struct nsZenAccentOklab { + nscolor accentNS; + float accL, accA, accB; + float contrastFactor; +}; + +class nsZenBoostsBackend final { + public: + explicit nsZenBoostsBackend() = default; + ~nsZenBoostsBackend() = default; + + /** + * Indicates whether the current frame being rendered is for anonymous + * content. + */ + bool mCurrentFrameIsAnonymousContent = false; + + /** + * @brief Resolve a StyleAbsoluteColor to take into account Zen boosts. + * @param aColor The color to resolve. + * @return The resolved color with Zen boost filters applied, or the original + * color if no boost is active. + * @see StyleColor::ResolveColor for reference. + */ + static auto ResolveStyleColor(mozilla::StyleAbsoluteColor aColor) + -> mozilla::StyleAbsoluteColor; + + /** + * @see ResolveStyleColor for reference. + */ + static auto ResolveStyleColor(nscolor aColor) -> nscolor; + + /** + * @brief Filter a color based on the current Zen boost settings. + * @param aColor The color to filter. + * @param aPresContext The presentation context to use for filtering. + * @return The filtered color. + */ + static auto FilterColorFromPresContext(nscolor aColor, + nsPresContext* aPresContext = nullptr) + -> nscolor; + + /** + * @brief Called when a presshell is entered during rendering. + * @param aDocument The document associated with the presshell being entered. + */ + auto onPresShellEntered(mozilla::dom::Document* aDocument) -> void; + + /** + * @brief Refresh the cached boost state from the current top BrowsingContext. + * Called from onPresShellEntered and from BrowsingContext::DidSet hooks when + * the underlying boost fields change. + */ + auto RefreshCachedBoostState() -> void; + + [[nodiscard]] + inline auto GetCurrentBrowsingContext() const { + return mCurrentBrowsingContext; + } + + /** + * Cached boost data for the current top BrowsingContext, refreshed on + * presshell entry and on DidSet hooks. Read by the per-color hot path so + * that boost-off pages don't pay for a BrowsingContext walk on every color + * resolve. + */ + ZenBoostData mCachedCurrentAccent = 0; + bool mCachedCurrentInverted = false; + + private: + /** + * The presshell of the current document being rendered. + */ + RefPtr mCurrentBrowsingContext; + + static nsZenAccentOklab mCachedAccent; + + public: + /** + * @brief Get the singleton instance of the ZenBoostsBackend. + * @return The singleton instance. + */ + static auto GetInstance() -> nsZenBoostsBackend*; +}; + +} // namespace zen + +#endif diff --git a/src/zen/boosts/zen-advanced-color-options.css b/src/zen/boosts/zen-advanced-color-options.css new file mode 100644 index 000000000..4cc651de8 --- /dev/null +++ b/src/zen/boosts/zen-advanced-color-options.css @@ -0,0 +1,43 @@ +/* + * 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/. + */ + +#zen-boost-advanced-color-options-panel { + color-scheme: light; + --panel-padding: 15px; + + & p { + color: #3a3a3b; + } + + & input { + width: 185px; + appearance: none !important; + height: 4px; + + background-color: #b5b5b9; + border-radius: 2px; + + margin-top: 10px; + } + + &::part(content) { + display: flex; + flex-direction: column; + gap: 24px; + } +} + +#zen-boost-advanced-color-options-panel input::-moz-range-thumb { + width: 18px; + height: 18px; + background-color: #e6e5ea; + border: solid 1px #d1d0d5; + border-radius: 100%; +} + +#zen-boost-advanced-color-options-panel input::-moz-range-thumb:active { + background-color: #cbcad0; +} diff --git a/src/zen/boosts/zen-boost-editor.inc.xhtml b/src/zen/boosts/zen-boost-editor.inc.xhtml new file mode 100644 index 000000000..c3a857092 --- /dev/null +++ b/src/zen/boosts/zen-boost-editor.inc.xhtml @@ -0,0 +1,153 @@ +#filter substitution + +# -*- Mode: HTML -*- +# +# 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/. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ +
+ +

+ +
+ +

+ +
+
+
+ + + + + + + + + + + + + + + + + + +
+ diff --git a/src/zen/boosts/zen-boosts.css b/src/zen/boosts/zen-boosts.css new file mode 100644 index 000000000..da851c036 --- /dev/null +++ b/src/zen/boosts/zen-boosts.css @@ -0,0 +1,805 @@ +/* + * 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/. + */ + +#zenBoostWindow { + /* For the mica effect we want a white tint */ + @media not (-moz-platform: linux) { + color-scheme: light; + } + appearance: none; + border: none; +} + +.source-editor-frame { + max-width: 1000px; + max-height: 1000px; + width: 100%; + height: 100%; + border: none; +} + +#zen-boost-code-top-bar { + top: 0; + z-index: 10; + + width: 100%; + height: 40px; + background-color: #f6f6f8; + @media (-moz-windows-mica) { + background-color: #f6f6f8c0; + } + + border: solid 0 #ededef; + border-bottom-width: 1px; +} + +#zen-boost-name-container { + background-image: url("chrome://global/skin/icons/arrow-down.svg"); + background-repeat: no-repeat; + background-position: calc(100% - 2px) calc(50% + 2px); + background-size: 12px 12px; + padding: 4px 18px 4px 2px; + margin-left: 4px; + border-radius: 8px; + + opacity: 0.75; + background-color: transparent; + + transition: + 0.25s opacity cubic-bezier(0.075, 0.82, 0.165, 1), + 0.25s background-color cubic-bezier(0.075, 0.82, 0.165, 1); + + -moz-window-dragging: no-drag; +} + +#zen-boost-name-container:hover { + opacity: 0.8; + background-color: #aaaaaa22; +} + +#zen-boost-code-bottom-bar { + bottom: 0; + z-index: 10; + + width: 100%; + height: 60px; + background-color: #f6f6f8; + @media (-moz-windows-mica) { + background-color: #f6f6f8c0; + } + + border: solid 0 #ededef; + border-top-width: 1px; + + display: flex; + align-items: center; + gap: 10px; + padding: 10px; +} + +#zen-boost-code-editor { + width: 100%; + height: calc(100% - 100px); + + margin-top: 0; + margin-bottom: 0; +} + +body { + overflow: clip; + display: flex; + margin: 0; + width: 100%; +} + +#zen-boost-editor-root, +#zen-boost-code-editor-root { + user-select: none; + + width: 100%; + height: 100%; + padding: 0; + margin: 0; + + & button { + border-radius: 6px; + @media (-moz-platform: macos) { + border-radius: 10px; + } + + padding: 0 !important; + min-width: fit-content !important; + + border: solid 6px transparent; + + transition: background 0.1s cubic-bezier(0.075, 0.82, 0.165, 1); + appearance: none; + + &:active { + transform: none !important; + } + + &:hover { + opacity: 0.8; + } + } + + & .button-text { + display: none; + } + + &[disabled] { + opacity: 0.5; + cursor: not-allowed; + } +} + +.subviewbutton { + color: #3a3a3b; +} + +#zen-boost-editor-view { + gap: 10px; +} + +#zen-boost-code-top-bar { + -moz-window-dragging: drag; /* Allow dragging the window with the custom titlebar for the code view */ +} + +#zen-boost-head-wrapper { + -moz-window-dragging: drag; /* Allow dragging the window with the custom titlebar */ + + height: 40px; + min-height: 40px; + max-height: 40px; + align-items: center; + background-color: #f6f6f8; + border: solid 1px #e7e7e7ab; + + @media (-moz-windows-mica) { + background-color: #f6f6f8c0; + border: none; + } + + display: flex; + + flex-direction: row-reverse; + @media (-moz-platform: macos) { + flex-direction: row; + } + + & button { + height: 24px !important; + width: 24px !important; + aspect-ratio: 1/1 !important; + background-color: transparent; + opacity: 0.45; + + transition: 0.25s opacity cubic-bezier(0.075, 0.82, 0.165, 1); + + &:hover { + opacity: 0.6; + } + } + + & #zen-boost-name { + transition: color 0.2s ease-in-out; + background-color: transparent; + border: none; + outline: none; + appearance: none; + + display: flex; + align-items: center; + } +} + +#zen-boost-name-text { + width: 100%; + justify-content: center; + font-size: 100%; + font-weight: 600; + + max-width: 100px; + overflow: hidden; +} + +#zen-boost-name-text { + text-align: center; + margin: auto !important; + height: min-content; +} + +#zen-boost-font-grid { + display: grid; + grid-template-columns: repeat(5, 1fr); + gap: 0; + width: 100%; + + padding: 2px; + + & button { + padding: auto; + margin: auto; + background-color: transparent; + } +} + +#zen-boost-filter-wrapper { + padding: 20px; + gap: 14px; + padding-top: 10px; + + background-color: #fcfcfe; + + -moz-window-dragging: drag; + & > * { + -moz-window-dragging: no-drag; + } +} + +.big-button { + width: 100% !important; + margin: auto; + text-indent: 6px; + vertical-align: middle; + font-size: 10pt; + + list-style-position: right; + + & p { + width: 75%; + margin: auto; + } +} + +#zen-boost-back { + width: 60px !important; + margin: 5px; + left: 5px; + + display: flex; + flex-direction: row-reverse; + + & hbox { + scale: 0.75; + margin-right: -7px; + margin-top: 2px; + } +} + +#zen-boost-code-bottom-bar button { + content: ""; + width: auto !important; + aspect-ratio: 1/1 !important; + + margin: 5px; + left: 5px; +} + +#zen-boost-zap { + & #zen-boost-zap-value { + text-align: right; + right: 8px; + margin: auto; + position: relative; + } + + & hbox { + margin-right: 8px; + } +} + +#zen-boost-zap[hideicon="true"] { + list-style: none; + list-style-type: none; + + & hbox { + margin-right: 0; + } +} + +#zen-boost-size { + list-style-type: none; + + & #zen-boost-size-value { + text-align: right; + right: 8px; + margin: auto; + position: relative; + } +} + +.zen-boost-panel-disabled { + filter: grayscale(1); +} + +.title-button { + scale: 0.9; +} + +.mod-button { + height: 38px !important; + + border-radius: 6px; + @media (-moz-platform: macos) { + border-radius: 12px; + } + + background-color: #ebebed; + + transition: + 0.4s background-color cubic-bezier(0.075, 0.82, 0.165, 1), + 0.4s opacity cubic-bezier(0.075, 0.82, 0.165, 1), + 0.4s filter cubic-bezier(0.075, 0.82, 0.165, 1) !important; + + &:hover { + opacity: 0.85; + } + + &:active { + transform: none !important; + + opacity: 0.9; + filter: brightness(0.9); + } +} + +#zen-boost-text-case-toggle[case-mode="none"] { + opacity: 0.5; + &:hover { + opacity: 0.6 !important; + } +} + +#zen-boost-text-case-toggle:not([case-mode="none"]) { + background-color: #ebebed; + font-weight: 600 !important; +} + +#zen-boost-code-top-bar .mod-button { + height: auto !important; +} + +#zen-boost-code-bottom-bar button { + margin: 0; +} + +#zen-boost-name { + height: 26px; + font-size: 8pt; + text-indent: 2px; + vertical-align: middle; + color: #3a3a3b; + + justify-content: center; +} + +#zen-boost-close { + margin-left: 0; + margin-right: 6px; + @media (-moz-platform: macos) { + margin-left: 6px; + margin-right: 0; + } +} + +#zen-boost-shuffle { + margin-right: 0; + margin-left: 4px; + @media (-moz-platform: macos) { + margin-right: 10px; + margin-left: 0; + } +} + +.zen-boost-button-active { + background-color: #3a3a3a; + color: #fcfcfe; +} + +.zen-boost-button-active:hover { + background-color: #5b5b5c; +} + +.zen-boost-button-active-transparent { + opacity: 0.35; +} + +.zen-boost-button-active-transparent:hover { + opacity: 0.5 !important; +} + +#zen-boost-magic-theme { + width: 18px; + height: 24px !important; + margin-top: 12px; + + z-index: 4; + box-shadow: 0 2px 4px #00000010; + + position: relative; + margin-left: auto; + margin-right: auto; + + & image { + width: 12px; + } + + &:not(.zen-boost-button-active) { + background: white; + } +} + +#zen-boost-toolbar-wrapper, +#zen-boost-toolbar-wrapper-colors { + width: 100%; + margin: auto; + justify-content: center; + align-items: center; + gap: 8px; + + & .small { + margin: 0; + flex: 1 1 30%; + } + + & .med { + margin: 0; + flex: 1 1 50%; + } +} + +#zen-boost-font-select { + width: 95px; + height: 20px !important; + + transition: 0.2s opacity ease-in-out; + + color: #727272; + background: none; + + font-size: 9pt; + + padding: 0; + text-indent: 2px; + + margin-left: 8px; + margin-right: 3px; + margin-top: 8px; + opacity: 0.75; + + &:hover { + opacity: 1; + } + + &[has-selection="true"] { + opacity: 1; + background-color: #ebebed; + } +} + +#zen-boost-font-toolbar { + display: flex; + flex-direction: row; + justify-content: space-between; + + & button { + border-radius: 6px; + min-width: fit-content; + height: 20px !important; + + margin-left: 8px; + margin-right: 8px; + margin-top: 8px; + background-color: transparent; + } +} + +#zen-boost-font-wrapper { + box-shadow: 0 2px 6px rgba(0,0,0,.15); + background-color: #ffffff; + + border-radius: 6px; + @media (-moz-platform: macos) { + border-radius: 8px; + } + + padding-top: 4px; + padding-bottom: 4px; + + display: flex; + flex-direction: column; + + & .visible-separator { + content: ""; + height: 1px; + width: auto; + + margin-right: 12px; + margin-left: 12px; + margin-top: 2px; + + opacity: 0.25; + + border-top: solid 1px #9a9a9a; + } + + & button { + filter: none; + color: rgba(0, 0, 0, 0.6); + border-radius: 50px; + transition: 0.1s transform ease; + font-size: 12px; + padding: 2px; + align-items: center; + justify-content: center; + display: flex; + height: 26px; + width: 26px; + transform-origin: center center; + + &:hover { + background-color: #9a9a9a30; + transform: scale(1.1); + } + } +} + +@property --mod-button-c1 { + syntax: ""; + inherits: false; + initial-value: #ebebed; +} + +@property --mod-button-c2 { + syntax: ""; + inherits: false; + initial-value: #ebebed; +} + +.mod-button[mode="orange"] { + color: #e3e9e4; + --mod-button-c1: #ffbb5d; + --mod-button-c2: #ffa01d; +} +.mod-button[mode="orange-red"] { + color: #e3e9e4; + --mod-button-c1: #ff8758; + --mod-button-c2: #ff5b1b; +} +.mod-button[mode="red"] { + color: #e3e9e4; + --mod-button-c1: #ff595f; + --mod-button-c2: #ff121b; +} +.mod-button[mode="blue"] { + color: #e3e9e4; + --mod-button-c1: #6650fc; + --mod-button-c2: #4125ff; +} +.mod-button[mode] { + background: linear-gradient(180deg, var(--mod-button-c1) 0%, var(--mod-button-c2) 100%) border-box; + transition: + 0.4s background-color cubic-bezier(0.075, 0.82, 0.165, 1), + 0.4s opacity cubic-bezier(0.075, 0.82, 0.165, 1), + 0.4s filter cubic-bezier(0.075, 0.82, 0.165, 1), + 0.75s --mod-button-c1 cubic-bezier(0.075, 0.82, 0.165, 1), + 0.55s --mod-button-c2 cubic-bezier(0.075, 0.82, 0.165, 1) !important; +} + +#zen-boost-back { + background-color: transparent; + + &:hover { + background-color: #e3e3e6; + } +} + +#zen-boost-code-editor-root button { + opacity: 0.6; +} + +.toggleable-button[enabled="true"] { + filter: invert(); + opacity: 0.9; + + &:active { + filter: invert(); + opacity: 0.8; + } +} + +.zen-boost-color-picker-gradient::after { + content: ""; + position: absolute; + inset: 0; + border-radius: 8px; + margin: 8px; + + z-index: 2; + + background: #fbfbfdea; + + background-position: -23px -23px; + backdrop-filter: saturate(2) blur(15px); + + background-size: 6px 6px; + background-image: radial-gradient(#e3e9e4, 1px, transparent 0); + + @media (-moz-platform: macos) { + background-size: 4px 4px; + background-image: radial-gradient(#e3e9e4 0.5px, transparent 0); + } +} + +.zen-boost-color-picker-gradient::before { + content: ""; + position: absolute; + inset: 0; + border-radius: inherit; + + z-index: 1; + + background: conic-gradient( + rgba(255, 0, 0, 1) 0%, + rgba(255, 162, 0, 1) 10%, + rgba(255, 242, 0, 1) 20%, + rgba(89, 255, 0, 1) 30%, + rgba(0, 255, 128, 1) 40%, + rgba(0, 255, 247, 1) 50%, + rgba(0, 89, 255, 1) 60%, + rgba(8, 0, 255, 1) 70%, + rgba(204, 0, 255, 1) 80%, + rgba(255, 0, 144, 1) 90%, + rgba(255, 0, 8, 1) 100% + ) + border-box; + + pointer-events: none; +} + +.zen-boost-color-picker-gradient { + position: relative; + overflow: hidden; + border-radius: 16px; + box-shadow: 0 4px 12px #00000021; + width: 100%; + aspect-ratio: 1 /1; + margin: 10px 0 4px 0; + + min-height: calc(var(--panel-width) - var(--panel-padding) * 2 - 2px); + + & [animated="true"] { + transition: all 0.4s cubic-bezier(0.14, 1.43, 0.56, 1.01) !important; + } + + & .zen-boost-color-picker-circle { + position: absolute; + z-index: 3; + transform-origin: center center; + transform: translate(-50%, -50%); + + opacity: 0; + transition: opacity 0.4s ease; + + left: 50%; + top: 50%; + + outline: solid 1px #9a9a9a88; + border-radius: 100%; + } + + & .zen-boost-color-picker-dot { + box-shadow: 0 2px 4px #00000022; + position: absolute; + z-index: 4; + width: 24px; + height: 24px; + border-radius: 50%; + background: var(--zen-theme-picker-dot-color); + @media (-prefers-color-scheme: dark) { + box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.1); + } + cursor: pointer; + border: 3px solid #ffffff; + transform: translate(-50%, -50%); + pointer-events: none; + transform-origin: top left; + + &:first-of-type { + width: 32px; + height: 32px; + border-width: 3px; + pointer-events: all; + transition: transform 0.2s; + z-index: 999; + &:hover { + transform: scale(1.05) translate(-50%, -50%); + } + } + + &[dragging="true"] { + transform: scale(1.2) translate(-50%, -50%); + } + } +} + +#zen-boost-editor-root:hover { + & .zen-boost-color-picker-circle { + opacity: 0.4; + } +} + + +.zen-boost-font-button-active { + background-color: #5454572f !important; +} + +.zen-boost-font-button-active:hover { + background-color: #5b5b5c28 !important; +} + +/** Import animation */ +/** From zen-single-components.css:211 */ + +@property --background-top { + syntax: ""; + inherits: false; + initial-value: -50%; +} + +#import-animation { + position: fixed; + width: 100%; + height: 100%; + background: linear-gradient(to bottom, transparent, rgba(255, 255, 255, 0.5) 40%, rgba(255, 255, 255, 0.6) 60%, transparent 100%); + left: 0; + z-index: 999; + opacity: 0; + transform: translateY(-50%); + pointer-events: none; +} + +#import-animation-shadow { + position: fixed; + mix-blend-mode: multiply; + background: #00000040; + left: 0; + right: 0; + top: 0; + bottom: 0; + z-index: 998; + opacity: 0; + pointer-events: none; +} + +#import-animation-border { + position: absolute; + border-radius: var(--zen-border-radius); + left: 0; + right: 0; + top: 0; + bottom: 0; + z-index: 1000; +} + +#import-animation-border::before { + content: ""; + position: absolute; + inset: 0; + border-radius: inherit; + padding: 4px; /* thickness of border */ + z-index: 1000; + + background: radial-gradient(ellipse 100% 50% at center var(--background-top), rgba(255, 255, 255, 0.9) 80%, transparent); + + /* Punch out the inside once (not animated) */ + mask: + linear-gradient(#fff 0 0) content-box, + linear-gradient(#fff 0 0); + mask-composite: exclude; +} diff --git a/src/zen/boosts/zen-selector.css b/src/zen/boosts/zen-selector.css new file mode 100644 index 000000000..7e7cde34e --- /dev/null +++ b/src/zen/boosts/zen-selector.css @@ -0,0 +1,200 @@ +/* + * 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/. + */ + +@keyframes select-component-in { + 0% { + scale: 0; + opacity: 0; + } + 100% { + scale: 1; + opacity: 1; + } +} + +#select-component { + width: min-content; + font-family: system-ui; + + height: 98px; + + pointer-events: auto; + + transform-origin: top; + overflow: hidden; + + position: fixed; + z-index: 9999; + + background-color: #f5f7fb; + margin: 4px; + + border-radius: 12px; + @media (-moz-platform: macos) { + border-radius: 24px; + } + + box-shadow: 0 0 20px#00000050; + + scale: 1; + opacity: 1; + + @media not (-moz-platform: windows) { + &[is-appearing="true"] { + scale: 0; + opacity: 0; + + animation: 0.4s select-component-in cubic-bezier(0.14, 1.43, 0.56, 1.01) forwards; + } + } + + & input { + width: fit-content; + min-width: 75px; + + height: 40px; + + border-radius: 6px; + @media (-moz-platform: macos) { + border-radius: 12px; + } + + padding-left: 10px; + padding-right: 10px; + + font-size: 11pt; + transition: + 0.15s filter ease, + 0.1s box-shadow ease-out, + scale 0.35s cubic-bezier(0.11, 1.6, 0.63, 1); + + &:hover { + filter: brightness(1.3); + } + + &:active { + scale: 0.95; + } + } +} + +#highlight-shadow { + z-index: 999; + + position: fixed; + width: 100%; + height: 100%; + + content: ""; + background-color: #00000044; +} + +#highlight-container { + z-index: 1000; + position: fixed; + width: 100%; + height: 100%; + content: ""; + + & .highlight { + position: fixed; + + border: 1px solid #e9dd38; + background-color: #c2b72046; + border-radius: 6px; + content: ""; + } +} + +#select-controls { + display: flex; + + gap: 10px; + padding: 10px; + @media not (-moz-platform: windows) { + gap: 12px; + padding: 12px; + } +} + +#select-related { + appearance: none; + text-align: left; + + width: 250px !important; + --related-elements-value: 100%; + background: linear-gradient(to top, rgb(247, 66, 0), rgb(245, 134, 86)); + + border: none; + color: #dadada; + box-shadow: 0 0 15px #00000052; + + &:hover { + box-shadow: 0 0 14px #00000066; + background: + linear-gradient(to right, transparent var(--related-elements-value), gray var(--related-elements-value)), + linear-gradient(to top, rgb(247, 66, 0), rgb(245, 134, 86)); + + @media not (-moz-platform: windows) { + box-shadow: 0 0 20px #00000077; + } + } +} + +#select-this { + appearance: none; + + background: linear-gradient(0deg, rgba(246, 27, 25, 1) 0%, rgba(254, 67, 59, 1) 100%); + border: none; + color: #dadada; + + box-shadow: 0 0 15px #00000052; +} + +#select-cancel { + appearance: none; + + background: rgb(90, 94, 100); + @media (-moz-platform: macos) { + background: linear-gradient(0deg, rgba(81, 83, 85, 1) 0%, rgba(108, 110, 112, 1) 100%); + } + + border: none; + color: #dadada; + + box-shadow: 0 0 15px #00000052; +} + +#selector-preview { + background-color: #e0e2e63d; + outline: 1px solid #e0e2e6ae; + width: 100%; + height: 34px; + text-indent: 18px; + + display: flex; + align-items: center; +} + +#selector-element-preview-text { + font-family: Arial, Helvetica, sans-serif; + font-size: 9pt; + color: rgb(76, 78, 80); +} + +#hover-div { + position: fixed; + z-index: 9999; + + content: ""; + outline: 1px solid #e9dd38; + border-radius: 6px; + + background-color: #c2b72046; + + transition: 0.1s all ease-out; + will-change: left, top, width, height; +} diff --git a/src/zen/boosts/zen-zap.css b/src/zen/boosts/zen-zap.css new file mode 100644 index 000000000..fd28f1fa3 --- /dev/null +++ b/src/zen/boosts/zen-zap.css @@ -0,0 +1,158 @@ +/* + * 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/. + */ + +@keyframes select-controls-container-in { + 0% { + bottom: -54px; + } + + 100% { + bottom: 8px; + } +} + +@keyframes zap-border-in { + 0% { + left: 0; + top: 0; + bottom: 0; + right: 0; + } + + 100% { + left: 2px; + top: 2px; + bottom: 2px; + right: 2px; + } +} + +#zap-border { + z-index: 9999; + content: ""; + position: fixed; + + left: 2px; + top: 2px; + bottom: 2px; + right: 2px; + + pointer-events: none; + background-color: transparent; + + outline: 4px solid #dfd43a; + animation: 0.15s zap-border-in ease-out; + + border-radius: 4px; + @media (-moz-platform: macos) { + border-radius: 8px; + } +} + +#zap-controls-container { + font-family: system-ui; + + display: flex; + pointer-events: auto; + z-index: 999999; + + bottom: 8px; + left: 8px; + right: 8px; + + margin: 0; + padding: 0 10px 0 10px; + height: 54px; + position: fixed; + + border-radius: 6px; + @media (-moz-platform: macos) { + border-radius: 12px; + } + + align-items: center; + gap: 8px; + + color: #d6d6d6; + background-color: #1d1b1b; + border: 1px solid #2f2b2b; + + animation: 0.15s select-controls-container-in cubic-bezier(0.07, 1.32, 0.41, 1); + + & input { + width: min-content; + height: 36px; + + border-radius: 4px; + @media (-moz-platform: macos) { + border-radius: 8px; + } + + border: none; + background: linear-gradient(0deg, #afafaf 0%, #e5e5e5 100%); + + color: #1d1b1b; + font-weight: bold; + + transition: + 0.1s filter ease-in-out, + scale 0.35s cubic-bezier(0.11, 1.6, 0.63, 1); + + &:hover { + filter: brightness(0.9); + } + &:active { + filter: brightness(0.8); + scale: 0.95; + } + } + + & #zap-list { + display: flex; + + flex: 1 1 auto; + min-width: 0; + + overflow-x: auto; + overflow-y: hidden; + white-space: nowrap; + + align-items: center; + vertical-align: middle; + gap: 8px; + + & input { + width: 36px; + flex: 0 0 auto; + } + + & p { + padding-left: 12px; + font-size: 10pt; + user-select: none; + } + + & .pcenter { + width: 100%; + text-align: center; + } + } + + & #zap-done { + flex: 0 0 auto; + padding: 0 12px 0 12px; + } +} + +#zen-zap-dissolve-canvas { + position: fixed; + width: 100%; + height: 100%; + left: 0; + top: 0; + pointer-events: none; + z-index: 2147483647; +} diff --git a/src/zen/common/ZenPreloadedScripts.js b/src/zen/common/ZenPreloadedScripts.js index 01fa01ddd..559d4c8e4 100644 --- a/src/zen/common/ZenPreloadedScripts.js +++ b/src/zen/common/ZenPreloadedScripts.js @@ -7,13 +7,52 @@ { Services.scriptloader.loadSubScript("chrome://browser/content/zen-components/ZenSpaceBookmarksStorage.js", this); - ChromeUtils.importESModule("chrome://browser/content/ZenStartup.mjs", { global: "current" }); - ChromeUtils.importESModule("resource:///modules/zen/ZenSpaceManager.mjs", { global: "current" }); - ChromeUtils.importESModule("chrome://browser/content/zen-components/ZenCompactMode.mjs", { global: "current" }); - ChromeUtils.importESModule("chrome://browser/content/ZenUIManager.mjs", { global: "current" }); - ChromeUtils.importESModule("chrome://browser/content/zen-components/ZenMods.mjs", { global: "current" }); - ChromeUtils.importESModule("chrome://browser/content/zen-components/ZenKeyboardShortcuts.mjs", { global: "current" }); - ChromeUtils.importESModule("chrome://browser/content/zen-components/ZenSessionStore.mjs", { global: "current" }); + let scripts = [ + "chrome://browser/content/ZenStartup.mjs", + "resource:///modules/zen/ZenSpaceManager.mjs", + "chrome://browser/content/zen-components/ZenCompactMode.mjs", + "chrome://browser/content/ZenUIManager.mjs", + "chrome://browser/content/zen-components/ZenMods.mjs", + "chrome://browser/content/zen-components/ZenKeyboardShortcuts.mjs", + "chrome://browser/content/zen-components/ZenSessionStore.mjs", + "chrome://browser/content/zen-components/ZenMediaController.mjs", + "chrome://browser/content/zen-components/ZenGlanceManager.mjs", + "chrome://browser/content/zen-components/ZenPinnedTabManager.mjs", + "chrome://browser/content/zen-components/ZenViewSplitter.mjs", + "chrome://browser/content/zen-components/ZenFolders.mjs", + "chrome://browser/content/zen-components/ZenEmojiPicker.mjs", + "chrome://browser/content/zen-components/ZenLiveFoldersUI.mjs", + "chrome://browser/content/zen-components/ZenDownloadAnimation.mjs", + ]; + + for (let script of scripts) { + ChromeUtils.importESModule(script, { global: "current" }); + } + + let customZenElements = [ + ["zen-folder", "chrome://browser/content/zen-components/ZenFolder.mjs"], + ["zen-workspace-creation", "resource:///modules/zen/ZenSpaceCreation.mjs"], + ["zen-workspace", "resource:///modules/zen/ZenSpace.mjs"], + ["zen-workspace-icons", "resource:///modules/zen/ZenSpaceIcons.mjs"] + ]; + + document.addEventListener( + "DOMContentLoaded", + () => { + // Only sync-import widgets once the document has loaded. If a widget is + // used before DOMContentLoaded it will be imported and upgraded when + // registering the customElements.setElementCreationCallback(). + for (let [tag, script] of customZenElements) { + customElements.setElementCreationCallback( + tag, + function customElementCreationCallback() { + ChromeUtils.importESModule(script, { global: "current" }); + } + ); + } + }, + { once: true } + ); Services.scriptloader.loadSubScript("chrome://browser/content/zen-components/ZenDragAndDrop.js", this); } diff --git a/src/zen/common/modules/ZenHasPolyfill.mjs b/src/zen/common/modules/ZenHasPolyfill.mjs index 986559d7f..44a7c685a 100644 --- a/src/zen/common/modules/ZenHasPolyfill.mjs +++ b/src/zen/common/modules/ZenHasPolyfill.mjs @@ -26,6 +26,12 @@ class nsHasPolyfill { if (selected?.tagName?.toLowerCase() === "menu") { return null; } + if (selected) { + gZenCompactModeManager.log( + `Selector "${selector}" exists for: `, + element + ); + } return selected; }); const { exists: shouldExist = true } = descendantSelectors; diff --git a/src/zen/common/modules/ZenMenubar.mjs b/src/zen/common/modules/ZenMenubar.mjs index 9616d0228..08ce51e81 100644 --- a/src/zen/common/modules/ZenMenubar.mjs +++ b/src/zen/common/modules/ZenMenubar.mjs @@ -92,7 +92,7 @@ export class nsZenMenuBar { key="zen-workspace-forward"/> `); diff --git a/src/zen/common/modules/ZenStartup.mjs b/src/zen/common/modules/ZenStartup.mjs index e9c7fa1c6..dd37719f8 100644 --- a/src/zen/common/modules/ZenStartup.mjs +++ b/src/zen/common/modules/ZenStartup.mjs @@ -17,7 +17,6 @@ class ZenStartup { init() { this.openWatermark(); - this.#changeSidebarLocation(); this.#zenInitBrowserLayout(); } @@ -46,18 +45,18 @@ class ZenStartup { } newContainer.appendChild(node); } - // Fix notification deck - const deckTemplate = document.getElementById( - "tab-notification-deck-template" - ); - if (deckTemplate) { - document.getElementById("zen-appcontent-wrapper").prepend(deckTemplate); - } + const deckTemplate = + document.getElementById("tab-notification-deck-template") || + document.getElementById("tab-notification-deck"); + + // overlap and interaction issues with vertical tabs + document.getElementById("browser").prepend(deckTemplate); gZenWorkspaces.init(); setTimeout(() => { gZenUIManager.init(); + this.#initUIComponents(); this.#checkForWelcomePage(); }, 0); } catch (e) { @@ -99,6 +98,10 @@ class ZenStartup { this.isReady = true; this.promiseInitializedResolve(); delete this.promiseInitializedResolve; + + setTimeout(() => { + gZenWorkspaces._invalidateBookmarkContainers(); + }); }); } @@ -143,18 +146,13 @@ class ZenStartup { }); } - #changeSidebarLocation() { - const kElementsToAppend = ["sidebar-splitter", "sidebar-box"]; - - const browser = document.getElementById("browser"); - browser.prepend(gNavToolbox); - - const sidebarPanelWrapper = document.getElementById("tabbrowser-tabbox"); - for (let id of kElementsToAppend) { - const elem = document.getElementById(id); - if (elem) { - sidebarPanelWrapper.prepend(elem); - } + #initUIComponents() { + const kUIComponents = ["ZenProgressBar"]; + for (let component of kUIComponents) { + const module = ChromeUtils.importESModule( + "resource:///modules/zen/ui/" + component + ".sys.mjs" + ); + new module[component](window); } } diff --git a/src/zen/common/modules/ZenUIManager.mjs b/src/zen/common/modules/ZenUIManager.mjs index c5b64643d..541574d77 100644 --- a/src/zen/common/modules/ZenUIManager.mjs +++ b/src/zen/common/modules/ZenUIManager.mjs @@ -101,10 +101,12 @@ window.gZenUIManager = { ) { const yValues = rawKeyframes.y || []; const xValues = rawKeyframes.x || []; - const scaleValues = rawKeyframes.scale || []; + const scaleYValues = rawKeyframes.scaleY || []; + const scaleXValues = rawKeyframes.scaleX || []; delete rawKeyframes.y; delete rawKeyframes.x; - delete rawKeyframes.scale; + delete rawKeyframes.scaleY; + delete rawKeyframes.scaleX; rawKeyframes.transform = []; if ( yValues.length !== 0 && @@ -116,14 +118,17 @@ window.gZenUIManager = { const keyframeLength = Math.max( yValues.length, xValues.length, - scaleValues.length + scaleYValues.length, + scaleXValues.length ); for (let i = 0; i < keyframeLength; i++) { const y = yValues[i] !== undefined ? `translateY(${yValues[i]}px)` : ""; const x = xValues[i] !== undefined ? `translateX(${xValues[i]}px)` : ""; - const scale = - scaleValues[i] !== undefined ? `scale(${scaleValues[i]})` : ""; - rawKeyframes.transform.push(`${x} ${y} ${scale}`.trim()); + const scaleY = + scaleYValues[i] !== undefined ? `scaleY(${scaleYValues[i]})` : ""; + const scaleX = + scaleXValues[i] !== undefined ? `scaleX(${scaleXValues[i]})` : ""; + rawKeyframes.transform.push(`${x} ${y} ${scaleX} ${scaleY}`.trim()); } } let keyframes = []; @@ -259,7 +264,7 @@ window.gZenUIManager = { ); gURLBar.style.setProperty( "--zen-urlbar-width", - `${Math.min(window.innerWidth / 2, 750)}px` + `${Math.min(window.innerWidth / 1.5, 750)}px` ); gZenVerticalTabsManager.actualWindowButtons.removeAttribute( "zen-has-hover" @@ -432,6 +437,9 @@ window.gZenUIManager = { }, onUrlbarSearchModeChanged(event) { + if (gReduceMotion) { + return; + } const { searchMode } = event.detail; const input = gURLBar; if (gURLBar.hasAttribute("breakout-extend") && !this._animatingSearchMode) { @@ -690,7 +698,22 @@ window.gZenUIManager = { this.urlbarShowDomainOnly ) { let url = BrowserUIUtils.removeSingleTrailingSlashFromURL(aURL); - return url.startsWith("https://") ? url.split("/")[2] : url; + requestAnimationFrame(() => { + const hoverAttr = "zen-has-implicit-hover"; + const hasHover = gNavToolbox.hasAttribute(hoverAttr); + gNavToolbox.removeAttribute(hoverAttr); + // Scroll the urlbar all the way to the right so that + // the domain is always visible when the url is too long. + gURLBar.inputField.scrollLeft = gURLBar.inputField.scrollWidth; + if (hasHover) { + gNavToolbox.setAttribute(hoverAttr, true); + } + }); + let stripped = url.startsWith("https://") ? url.split("/")[2] : url; + if (stripped.startsWith("www.")) { + stripped = stripped.substring(4); + } + return stripped; } return BrowserUIUtils.trimURL(aURL); }, @@ -999,7 +1022,7 @@ window.gZenVerticalTabsManager = { command="cmd_zenToggleTabsOnRight" /> `); - document.getElementById("viewToolbarsMenuSeparator").before(fragment); + document.getElementById("toolbar-context-customize").before(fragment); }, get _topButtonsSeparatorElement() { @@ -1014,6 +1037,7 @@ window.gZenVerticalTabsManager = { animateItemOpen(aItem) { if ( + gReduceMotion || !gZenUIManager.motion || !aItem || !gZenUIManager._hasLoadedDOM || @@ -1346,7 +1370,7 @@ window.gZenVerticalTabsManager = { buttonsTarget.append(this._topButtonsSeparatorElement); } for (const button of elements) { - this._topButtonsSeparatorElement.after(button); + this.appendCustomizableItem(this._topButtonsSeparatorElement, button); } buttonsTarget.prepend( document.getElementById("unified-extensions-button") @@ -1549,16 +1573,36 @@ window.gZenVerticalTabsManager = { Services.prefs.setBoolPref("zen.tabs.vertical.right-side", newVal); }, - appendCustomizableItem(target, child, placements) { + appendCustomizableItem(target, child, placements = []) { if ( - target.id === "zen-sidebar-top-buttons-customization-target" && this._hasSetSingleToolbar && - placements.includes(child.id) + (target.id === "zen-sidebar-top-buttons-customization-target" || + target === this._topButtonsSeparatorElement) ) { - this._topButtonsSeparatorElement.before(child); - return; + if (placements.includes(child.id)) { + this._topButtonsSeparatorElement.before(child); + return; + } else if ( + child.hasAttribute("data-extensionid") && + Services.prefs.getBoolPref("zen.view.overflow-webext-toolbar", true) + ) { + if (gURLBar._isOverflowingItems) { + const overflowElements = document.getElementById( + "zen-overflow-extensions-list" + ); + overflowElements.appendChild(child); + } else { + const element = document.getElementById("page-action-buttons"); + element.before(child); + } + return; + } + } + if (target === this._topButtonsSeparatorElement) { + this._topButtonsSeparatorElement.after(child); + } else { + target.appendChild(child); } - target.appendChild(child); }, async renameTabKeydown(event) { diff --git a/src/zen/common/moz.build b/src/zen/common/moz.build index 8dc973b44..115234dad 100644 --- a/src/zen/common/moz.build +++ b/src/zen/common/moz.build @@ -7,3 +7,8 @@ EXTRA_JS_MODULES += [ "sys/ZenCustomizableUI.sys.mjs", "sys/ZenUIMigration.sys.mjs", ] + +EXTRA_JS_MODULES.zen.ui += [ + "sys/ui/ZenProgressBar.sys.mjs", + "sys/ui/ZenUIComponent.sys.mjs", +] diff --git a/src/zen/common/styles/schemes/dark.inc.css b/src/zen/common/styles/schemes/dark.inc.css index 032dd757d..e66510082 100644 --- a/src/zen/common/styles/schemes/dark.inc.css +++ b/src/zen/common/styles/schemes/dark.inc.css @@ -3,5 +3,5 @@ % file, You can obtain one at http://mozilla.org/MPL/2.0/. color-scheme: dark; ---toolbar-color-scheme: dark; --zen-urlbar-outline-offset: -2px; +--zen-urlbar-filter: blur(40px) saturate(110%) brightness(25%) contrast(100%); diff --git a/src/zen/common/styles/schemes/light.inc.css b/src/zen/common/styles/schemes/light.inc.css index a3c18d0fd..81daec9b5 100644 --- a/src/zen/common/styles/schemes/light.inc.css +++ b/src/zen/common/styles/schemes/light.inc.css @@ -3,5 +3,5 @@ % file, You can obtain one at http://mozilla.org/MPL/2.0/. color-scheme: light; ---toolbar-color-scheme: light; --zen-urlbar-outline-offset: 0px; +--zen-urlbar-filter: blur(40px) saturate(110%) brightness(75%) contrast(100%); diff --git a/src/zen/common/styles/zen-animations.css b/src/zen/common/styles/zen-animations.css index 7f08377be..7784ef08f 100644 --- a/src/zen/common/styles/zen-animations.css +++ b/src/zen/common/styles/zen-animations.css @@ -48,6 +48,18 @@ } } +@keyframes zen-dialog-fade-in-shifted { + from { + opacity: 0; + transform: translateY(calc(-10% - 10px)); + } + + to { + opacity: 1; + transform: translateY(-10%); + } +} + @keyframes zen-text-gradient { 0% { background-position: 0% 50%; @@ -56,3 +68,35 @@ background-position: -400% 50%; } } + +@keyframes zen-progress-bar-pulse { + from { + transform: scale(0.85) translate(-50%, -50%); + opacity: 0.6; + } + + to { + transform: scale(0.95) translate(-50%, -50%); + opacity: 1; + } +} + +@keyframes zen-progress-bar-long-load { + 0% { + left: -100%; + opacity: 1; + } + + 100% { + left: 100%; + opacity: 1; + } +} + +@keyframes zen-progress-bar-settle { + to { + transform: translate(-50%, -50%) scale(1); + opacity: 1; + width: 10rem; + } +} diff --git a/src/zen/common/styles/zen-browser-container.css b/src/zen/common/styles/zen-browser-container.css index a8de2b24c..395a97c2b 100644 --- a/src/zen/common/styles/zen-browser-container.css +++ b/src/zen/common/styles/zen-browser-container.css @@ -18,7 +18,7 @@ } &[transparent="true"] { - background: light-dark(rgba(255, 255, 255, 0.4), rgba(255, 255, 255, 0.1)); + background: light-dark(rgba(255, 255, 255, 0.6), rgba(255, 255, 255, 0.1)); } } @@ -36,13 +36,14 @@ * is a hacky solution, but it works for now. Note that this makes * zen REALLY slow, so lets not enable it by default until we have * a better solution. */ - will-change: transform; + translate: 0; + backface-visibility: hidden; } } } /* stylelint-disable-next-line media-query-no-invalid */ - @media (not -moz-pref("zen.view.shift-down-site-on-hover")) and (not ((-moz-pref("zen.view.experimental-no-window-controls") or (not -moz-pref("zen.view.hide-window-controls"))) and -moz-pref("zen.view.use-single-toolbar"))) { + @media (not -moz-pref("zen.view.shift-down-site-on-hover")) and -moz-pref("zen.view.hide-window-controls") { .browserSidebarContainer:is(.deck-selected, [zen-split="true"]) .browserContainer { transition: margin var(--zen-hidden-toolbar-transition); @@ -58,6 +59,13 @@ --margin-top-fix: calc(-1 * var(--zen-toolbar-height-with-bookmarks) + var(--zen-element-separation)); } + /* stylelint-disable-next-line media-query-no-invalid */ + @media -moz-pref("zen.view.experimental-no-window-controls") or (not -moz-pref("browser.tabs.inTitlebar")) { + :root:not([zen-has-bookmarks="true"])[zen-single-toolbar="true"] & { + --margin-top-fix: 0px; + } + } + margin-top: var(--margin-top-fix); } } diff --git a/src/zen/common/styles/zen-browser-ui.css b/src/zen/common/styles/zen-browser-ui.css index c45e8a90d..f6bec02be 100644 --- a/src/zen/common/styles/zen-browser-ui.css +++ b/src/zen/common/styles/zen-browser-ui.css @@ -4,6 +4,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +body, #tabbrowser-tabpanels { background: transparent !important; } @@ -48,6 +49,7 @@ overflow: clip; isolation: isolate; + contain: content; &::after, &::before { @@ -56,6 +58,7 @@ inset: 0; z-index: 0; pointer-events: none; + will-change: background-color; } &::after { diff --git a/src/zen/common/styles/zen-buttons.css b/src/zen/common/styles/zen-buttons.css index 0c1f6afde..e0f614cf6 100644 --- a/src/zen/common/styles/zen-buttons.css +++ b/src/zen/common/styles/zen-buttons.css @@ -18,48 +18,49 @@ xul|button { } dialog::part(dialog-button) { - --button-background-color: light-dark(white, #302f63); + --button-background-color: light-dark(rgba(28, 28, 28, 0.1), rgba(255, 255, 255, .1)) !important; --button-background-color-hover: color-mix(in srgb, var(--button-background-color) 90%, transparent); --button-background-color-active: color-mix(in srgb, var(--button-background-color) 80%, transparent); - --button-background-color-primary: #3138fc; + --button-background-color-primary: light-dark(#1c1c1c, #dddddd); --button-background-color-primary-hover: color-mix(in srgb, var(--button-background-color-primary) 95%, transparent); - --button-text-color-primary: white; - --button-text-color-primary-hover: var(--button-text-color-primary); - border-color: light-dark(rgba(0, 0, 0, 0.2), rgba(255, 255, 255, 0.2)) !important; - border-bottom-width: 2px !important; + --button-background-color-primary-active: color-mix(in srgb, var(--button-background-color-primary) 90%, transparent); + position: relative; + border: none; + border-radius: 10px; } -dialog[defaultButton="accept"]::part(dialog-button) { - &:is([dlgtype="accept"], [dlgtype="cancel"]) { - padding-inline-end: 3.7em; - @media (-moz-platform: windows) { - padding-inline-end: 4em; - } +@media (-moz-platform: macos) { + dialog[defaultButton="accept"]::part(dialog-button) { + &:is([dlgtype="accept"], [dlgtype="cancel"]) { + padding-inline-end: 3.7em; - &::after { - border-radius: 4px; - font-weight: 600; - position: absolute; - right: 0.6em; - padding: 3px 0; - top: 50%; - transform: translateY(-50%); - font-size: 12px; - width: 34px; - text-align: center; - } + &::after { + border-radius: 4px; + font-weight: 600; + position: absolute; + padding: 3px 0; + top: 50%; + transform: translateY(-50%); + text-align: center; + } - &[dlgtype="accept"]::after { - content: "⏎"; - background: rgba(255, 255, 255, 0.1); - outline: 2px solid color-mix(in srgb, currentColor 20%, transparent); - outline-offset: -2px; - } + &[dlgtype="accept"]::after { + content: "⏎"; + right: 0.6em; + font-size: 12px; + width: 36px; + color: light-dark(rgba(255, 255, 255, 0.80), rgba(0, 0, 0, 0.60)); + } - &[dlgtype="cancel"]::after { - content: "ESC"; - background: light-dark(rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.1)); + &[dlgtype="cancel"]::after { + content: "ESC"; + right: 1em; + font-size: 8px; + width: 30px; + color: light-dark(rgba(28, 28, 28, 0.7), rgba(255, 255, 255, 0.6)) !important; + outline: 1px solid light-dark(rgba(28, 28, 28, 0.1), rgba(255, 255, 255, 0.1)) !important; + } } } } @@ -68,7 +69,8 @@ xul|button:not(#zen-workspaces-button):active { transform: scale(0.98); } -.footer-button { +.footer-button, +dialog::part(dialog-button) { font-weight: 500 !important; transition: scale 0.2s; diff --git a/src/zen/common/styles/zen-omnibox.css b/src/zen/common/styles/zen-omnibox.css index 8b8bc51e5..7a54ffe91 100644 --- a/src/zen/common/styles/zen-omnibox.css +++ b/src/zen/common/styles/zen-omnibox.css @@ -17,12 +17,12 @@ } .urlbar { - --urlbarView-separator-color: light-dark(hsl(0, 0%, 92%), hsl(0, 0%, 20%)); + --urlbarView-separator-color: light-dark(hsl(0, 0%, 80%), hsl(0, 0%, 20%)); --urlbarView-hover-background: var(--toolbarbutton-hover-background); --urlbarView-highlight-background: var(--toolbarbutton-hover-background); border-radius: calc(var(--toolbarbutton-border-radius) - 2px); height: var(--urlbar-height); - --urlbarView-results-padding: 8px !important; + --urlbarView-results-padding: 10px !important; --urlbar-container-height: 48px !important; --urlbar-margin-inline: 5px; @@ -120,6 +120,7 @@ #urlbar .urlbar-input { border-radius: 0 !important; + color: color-mix(in srgb, var(--input-color, FieldText) 70%, transparent); } #urlbar .urlbar-input-box { @@ -139,10 +140,12 @@ } .identity-box-button, - .urlbar-page-action { + .urlbar-page-action, + .unified-extensions-item { opacity: 0; height: 100%; /* To still be able to open popups */ visibility: collapse; + margin: 0 !important; :root:not([supress-primary-adjustment="true"]) & { transition: @@ -189,6 +192,10 @@ } } +:root[zen-single-toolbar="true"] #urlbar[breakout-extend="true"] .unified-extensions-item { + display: none !important; +} + /* stylelint-disable-next-line media-query-no-invalid */ @media -moz-pref("zen.urlbar.single-toolbar-show-copy-url", false) { :root[zen-single-toolbar="true"] #zen-copy-url-button { @@ -262,8 +269,11 @@ & .urlbar-background { --zen-urlbar-background-base: light-dark(#fbfbfb, color-mix(in srgb, hsl(0, 0%, 6.7%), var(--zen-colors-primary) 30%)); /* stylelint-disable-next-line media-query-no-invalid */ - @media -moz-pref("zen.theme.acrylic-elements") { - --zen-urlbar-background-transparent: color-mix(in srgb, var(--zen-urlbar-background-base) 70%, transparent 30%); + @media -moz-pref("zen.theme.acrylic-elements") and (not (prefers-reduced-transparency: reduce)) { + --zen-urlbar-background-transparent: light-dark( + color-mix(in srgb, white 80%, transparent), + color-mix(in srgb, var(--zen-urlbar-background-base) 65%, transparent) + ); } background-color: var(--zen-urlbar-background-transparent, var(--zen-urlbar-background-base)) !important; box-shadow: 0 30px 140px -15px light-dark(rgba(0, 0, 0, 0.8), rgba(0, 0, 0, 0.6)) !important; @@ -273,7 +283,7 @@ /* stylelint-disable-next-line media-query-no-invalid */ @media -moz-pref("zen.theme.acrylic-elements") { - backdrop-filter: blur(42px) saturate(110%) brightness(0.25) contrast(100%) !important; + backdrop-filter: var(--zen-urlbar-filter) !important; } } @@ -300,7 +310,7 @@ :root[zen-single-toolbar="true"] { --urlbar-icon-border-radius: 8px !important; - --urlbar-inner-border-radius: var(--toolbarbutton-border-radius) !important; + --urlbar-inner-border-radius: 6px !important; #identity-permission-box > *:not(#zen-site-data-icon-button) { visibility: collapse; @@ -404,32 +414,6 @@ min-width: 26px; } -/* Notification Stack */ - -.notificationbox-stack { - background: transparent; - - &[notificationside="top"] { - position: fixed; - bottom: calc(var(--zen-element-separation) * 1.5); - right: calc(var(--zen-element-separation) * 1.5); - width: fit-content; - max-width: 30rem !important; - z-index: 9999; - - & notification-message { - background: color-mix(in srgb, var(--zen-colors-tertiary) 70%, transparent 30%); - backdrop-filter: blur(10px); - border: 1px solid var(--arrowpanel-border-color); - border-radius: var(--zen-border-radius); - - &::before { - display: none; - } - } - } -} - #nav-bar, #zen-sidebar-top-buttons { min-height: var(--zen-toolbar-height) !important; @@ -514,6 +498,13 @@ } } +#urlbar[open][zen-floating-urlbar="true"] #identity-icon-box, +:root[zen-single-toolbar="true"] #urlbar[breakout-extend="true"] #identity-icon-box { + --urlbar-box-hover-bgcolor: transparent; + margin-inline: 2px 8px; + transition: none; +} + /* stylelint-disable-next-line media-query-no-invalid */ @media not -moz-pref("zen.urlbar.show-protections-icon") { #tracking-protection-icon-container { @@ -537,7 +528,7 @@ margin-left: 0 !important; margin-right: 12px !important; padding: 6px !important; - border-radius: 4px !important; + border-radius: 3px !important; } .urlbarView-userContext { @@ -604,6 +595,11 @@ position: relative; scale: 0.93; transform-origin: right; + opacity: 0.7; + + .urlbarView-row[selected] & { + opacity: 1; + } &::after { content: ""; @@ -655,7 +651,7 @@ } &[selected] { - --zen-selected-bg: color-mix(in srgb, var(--zen-primary-color) 55%, light-dark(rgba(0, 0, 0, 0.5), rgba(255, 255, 255, 0.2)) 50%); + --zen-selected-bg: color-mix(in srgb, var(--zen-primary-color) 50%, light-dark(rgba(0, 0, 0, 0.5), rgba(255, 255, 255, 0.1))); --zen-selected-color: color-mix(in srgb, var(--zen-selected-bg), black 30%); background-color: var(--zen-selected-bg) !important; @@ -673,7 +669,7 @@ color: var(--zen-selected-color) !important; } - &:where([type="switchtab"], [type="dynamic"], [type^="history"], [type^="autofill_"], [type="top_site"]) .urlbarView-favicon, + &:where([type="switchtab"], [type="dynamic"], [type^="history"], [type^="autofill_"], [type="top_site"], [type="bookmark"]) .urlbarView-favicon, & .urlbarView-shortcutContent { fill: var(--zen-selected-color) !important; background-color: rgba(255, 255, 255, 0.9) !important; @@ -708,7 +704,8 @@ } #identity-box { - margin-inline-end: 0; + margin-inline-end: 3px !important; + margin-inline-start: 2px !important; } } diff --git a/src/zen/common/styles/zen-overflowing-addons.css b/src/zen/common/styles/zen-overflowing-addons.css index cd68ea2f1..5725ed868 100644 --- a/src/zen/common/styles/zen-overflowing-addons.css +++ b/src/zen/common/styles/zen-overflowing-addons.css @@ -4,45 +4,51 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#zen-overflow-extensions-list:not(:empty) { - --uei-icon-size: 14px; - display: flex; - gap: 8px; - padding: 8px 2px; - padding-bottom: 0; +#zen-overflow-extensions-list { + display: none; - & .unified-extensions-item { - flex: 1; - margin: 0; - } + :root[zen-single-toolbar="true"] &:not(:empty) { + --uei-icon-size: 14px; + display: grid; + gap: 8px; + padding: 8px 2px; + padding-bottom: 0; + border-radius: calc(var(--border-radius-medium) - 4px); + grid-template-columns: repeat(auto-fit, minmax(32px, 1fr)); - & .toolbarbutton-badge-stack { - margin: 0; - width: 100%; - height: 100%; - padding: 8px 0; - justify-content: center; - align-items: center; - } + & .unified-extensions-item { + flex: 1; + margin: 0; + } - & .unified-extensions-item-action-button { - background-color: var(--zen-toolbar-element-bg); - height: 30px; - margin: 0; - justify-content: center; - align-items: center; - border-radius: var(--border-radius-medium); - overflow: clip; - padding: 0; + & .toolbarbutton-badge-stack { + margin: 0; + width: 100%; + height: 100%; + justify-content: center; + align-items: center; + } - &:hover { - background-color: var(--zen-toolbar-element-bg-hover); + & .unified-extensions-item-action-button { + appearance: none; + background-color: var(--zen-toolbar-element-bg); + height: 26px; + margin: 0; + justify-content: center; + align-items: center; + border-radius: calc(var(--border-radius-medium) - 2px); + overflow: clip; + padding: 0; + + &:hover { + background-color: var(--zen-toolbar-element-bg-hover); + } + } + + .unified-extensions-item-contents, + .unified-extensions-item-menu-button, + unified-extensions-item-messagebar-wrapper { + display: none; } } - - .unified-extensions-item-contents, - .unified-extensions-item-menu-button, - unified-extensions-item-messagebar-wrapper { - display: none; - } } diff --git a/src/zen/common/styles/zen-panels/dialog.css b/src/zen/common/styles/zen-panels/dialog.css index 4b41dd0dc..b5979bb4c 100644 --- a/src/zen/common/styles/zen-panels/dialog.css +++ b/src/zen/common/styles/zen-panels/dialog.css @@ -4,9 +4,20 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ .dialogBox { - outline: 1px solid light-dark(transparent, #646464); - animation: zen-dialog-fade-in 0.3s ease-out; border-radius: 12px !important; - border: 1px solid light-dark(#a8a8a9, black); + border: 1px solid light-dark(rgba(168, 168, 169, 0.50), var(--zen-dialog-background)) !important; + outline: 1px solid light-dark(transparent, rgba(168, 168, 169, 0.50)) !important; + box-shadow: 0 10px 8px rgba(0, 0 , 0, 0.15) !important; outline-offset: -1.5px; + + @media not (prefers-reduced-motion: reduce) { + animation: zen-dialog-fade-in 0.3s ease-out; + } + + .content-prompt-dialog & { + @media not (prefers-reduced-motion: reduce) { + animation: zen-dialog-fade-in-shifted 0.3s ease-out; + } + transform: translateY(-10%); + } } diff --git a/src/zen/common/styles/zen-popup.css b/src/zen/common/styles/zen-popup.css index 90a3ead44..8c29733b0 100644 --- a/src/zen/common/styles/zen-popup.css +++ b/src/zen/common/styles/zen-popup.css @@ -221,6 +221,7 @@ panel { } .permission-popup-permission-item, +.permission-popup-boost-item, #permission-popup-storage-access-permission-list-header { padding-block: 4px; margin-block: 0; diff --git a/src/zen/common/styles/zen-single-components.css b/src/zen/common/styles/zen-single-components.css index 8d7d17f0f..2544b8b11 100644 --- a/src/zen/common/styles/zen-single-components.css +++ b/src/zen/common/styles/zen-single-components.css @@ -408,17 +408,59 @@ display: none; } -.permission-popup-permission-item { +.zen-permission-popup-boost-editor-button { + transition: + 0.2s opacity ease, + 0.2s background-color ease, + 0.2s transform ease; + opacity: 0; + + width: 30px; + height: 26px; + min-width: 30px; + appearance: none; + background: transparent; + border: none; + + border-radius: 8px; + transform: scale(1); + + -moz-context-properties: fill; + fill: white; + + &:active { + transform: scale(0.95); + } + + & image { + opacity: 0.6; + height: 14px; + width: calc(100% - 2px); + } +} + +.permission-popup-boost-item:hover .zen-permission-popup-boost-editor-button { + opacity: 1; + + &.zen-permission-popup-boost-editor-button:hover { + background-color: var(--button-background-color-ghost-hover); + } +} + +.permission-popup-permission-item, +.permission-popup-boost-item { gap: 8px; overflow: hidden; align-items: center; } -.permission-popup-permission-label-container { +.permission-popup-permission-label-container, +.permission-popup-boost-label-container { min-width: 1px; } -.permission-popup-permission-label { +.permission-popup-permission-label, +.permission-popup-boost-label { margin: 0; font-weight: 500; white-space: nowrap; @@ -427,7 +469,8 @@ max-width: 100%; } -.permission-popup-permission-icon { +.permission-popup-permission-icon, +.permission-popup-boost-icon { fill: var(--button-primary-color); padding: 8px; width: 34px; @@ -459,20 +502,24 @@ opacity 0.12s ease-in-out; } - .permission-popup-permission-item:hover &::before { + .permission-popup-permission-item:hover &::before, + .permission-popup-boost-item:hover:not(:has(.zen-permission-popup-boost-editor-button:hover)) &::before { transform: scale(1.05); } - .permission-popup-permission-item:active:hover &::before { + .permission-popup-permission-item:active:hover &::before, + .permission-popup-boost-item:active:hover:not(:has(.zen-permission-popup-boost-editor-button:hover)) &::before { transform: scale(0.95); } - .permission-popup-permission-item[state="allow"] &::before { + .permission-popup-permission-item[state="allow"] &::before, + .permission-popup-boost-item[state="enabled"] &::before { opacity: 1; } } -.zen-permission-popup-permission-state-label { +.zen-permission-popup-permission-state-label, +.zen-permission-popup-boost-state-label { opacity: 0.6; font-size: smaller; font-weight: 400; @@ -489,6 +536,8 @@ padding-top: 8px; margin: 2px 8px 8px 8px; + gap: 8px; + & toolbarbutton { margin: 0; } @@ -586,7 +635,8 @@ } } -#zen-site-data-settings-list toolbarseparator { +#zen-site-data-settings-list toolbarseparator, +#zen-site-data-boost-list toolbarseparator { margin: 6px 0; } @@ -650,3 +700,90 @@ gap: 4px; } } + + +/* Notification Stack */ + +.notificationbox-stack { + background: transparent; + + &[notificationside="top"] { + position: fixed; + bottom: calc(var(--zen-element-separation) * 1.5); + right: calc(var(--zen-element-separation) * 1.5); + + :root[zen-right-side="true"] & { + right: auto; + left: calc(var(--zen-element-separation) * 1.5); + } + + width: fit-content; + max-width: 30rem !important; + z-index: 9999; + + & notification-message { + background: color-mix(in srgb, var(--zen-colors-tertiary) 70%, transparent 30%); + backdrop-filter: blur(10px); + border: 1px solid var(--arrowpanel-border-color); + border-radius: var(--zen-border-radius); + + &::before { + display: none; + } + } + } +} + +/* Loading progress bar */ +#zen-loading-progress-bar { + --zen-loading-progress-bar-color: color-mix(in srgb, var(--zen-primary-color), light-dark(rgba(0, 0, 0, 0.5), rgba(255, 255, 255, 0.5)) 70%); + + position: fixed; + top: calc(var(--zen-element-separation) / -2); + + /* Minimum -2px, but if its larger, elemenet separation / -2 will be used as top offset, to avoid overlapping with the notification stack */ + :root:is([zen-no-padding="true"], [inDOMFullscreen="true"], :not([zen-single-toolbar="true"])) & { + top: 4px; + } + + left: 50%; + transform: translate(-50%, -50%) scale(0); + background: var(--zen-loading-progress-bar-color); + height: .4rem; + width: 5rem; + z-index: 9; + border-radius: 100px; + transition: opacity .3s ease-in-out, + background-color .3s ease-in-out, + transform .3s ease-in-out; + overflow: clip; + pointer-events: none; + animation: zen-progress-bar-pulse 1s ease-in-out infinite forwards; + animation-direction: alternate; + transform-origin: 0 0; + contain: content; + + &[long-load="true"] { + opacity: 1; + animation: zen-progress-bar-settle .3s ease-out forwards; + background: light-dark(rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.1)); + + &::before { + content: ""; + position: absolute; + top: 0; + height: 100%; + width: 75%; + opacity: 0; + animation: zen-progress-bar-long-load 1s ease-in-out infinite; + animation-delay: 0.3s; + background: var(--zen-loading-progress-bar-color); + border-radius: inherit; + } + } + + /* stylelint-disable-next-line media-query-no-invalid */ + @media not -moz-pref("zen.view.enable-loading-indicator") { + display: none; + } +} diff --git a/src/zen/common/styles/zen-theme.css b/src/zen/common/styles/zen-theme.css index 223763421..1c6571ab3 100644 --- a/src/zen/common/styles/zen-theme.css +++ b/src/zen/common/styles/zen-theme.css @@ -59,7 +59,7 @@ color-mix(in srgb, var(--zen-primary-color) 1%, var(--zen-branding-bg) 99%) ); - --zen-dialog-background: light-dark(#FAFBFF, #161C31); + --zen-dialog-background: light-dark(#fafbff, #1c1c1c); --zen-urlbar-background: light-dark( color-mix(in srgb, var(--zen-primary-color) 3%, #f4f4f4 97%), color-mix(in srgb, var(--zen-primary-color) 4%, rgb(24, 24, 24) 96%) @@ -70,18 +70,18 @@ --in-content-primary-button-background: color-mix( in srgb, - var(--zen-primary-color) 30%, - var(--zen-branding-bg-reverse) 70% + var(--zen-primary-color) 40%, + var(--zen-branding-bg-reverse) ) !important; --in-content-primary-button-background-hover: color-mix( in srgb, - var(--zen-primary-color) 35%, - var(--zen-branding-bg-reverse) 65% + var(--zen-primary-color) 45%, + var(--zen-branding-bg-reverse) ) !important; --in-content-primary-button-background-active: color-mix( in srgb, - var(--zen-primary-color) 40%, - var(--zen-branding-bg-reverse) 60% + var(--zen-primary-color) 50%, + var(--zen-branding-bg-reverse) ) !important; --in-content-primary-button-text-color: var(--zen-branding-bg) !important; --in-content-primary-button-border-color: transparent !important; @@ -113,7 +113,7 @@ --button-border-color: var(--in-content-primary-button-border-color) !important; --button-font-weight: 500 !important; - --button-border-radius: 6px; + --button-border-radius: 8px; --button-text-color-primary: var(--in-content-primary-button-text-color) !important; --button-background-color: var(--in-content-button-background) !important; @@ -214,7 +214,7 @@ --urlbar-margin-inline: 1px !important; --tab-icon-overlay-stroke: light-dark(white, black) !important; - --tab-close-button-padding: 5px !important; + --tab-close-button-padding: 4px !important; --input-border-color: var(--zen-input-border-color) !important; --zen-themed-toolbar-bg-transparent: light-dark(var(--zen-branding-bg), #171717); @@ -233,6 +233,7 @@ --zen-primary-color: rgb(11, 10, 11) !important; /* Make sure its in sync with getToolbarColor */ --toolbox-textcolor: rgba(255, 255, 255, 0.8) !important; + --toolbar-color-scheme: dark !important; } &[zen-unsynced-window='true'] { @@ -249,7 +250,7 @@ --panel-separator-color: color-mix(in srgb, currentColor 15%, transparent) !important; --zen-big-shadow: rgba(0, 0, 0, 0.24) 0px 3px 8px; - --zen-active-tab-scale: 0.98; + --zen-active-tab-scale: 0.985; /* Define tab hover background color */ --tab-hover-background-color: var(--toolbarbutton-hover-background); @@ -257,11 +258,13 @@ /* Sidebar Notifications */ --zen-sidebar-notification-bg: color-mix( in srgb, - var(--zen-primary-color) 15%, + var(--zen-primary-color) 5%, light-dark(white, black) ); --zen-sidebar-notification-shadow: 0 0 6px light-dark(rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.3)); + --zen-sidebar-themed-icon-fill: light-dark(color-mix(in srgb, var(--zen-primary-color) 50%, black), color-mix(in srgb, var(--zen-colors-primary) 15%, #ebebeb)); + --zen-native-inner-radius: var( --zen-webview-border-radius, /* Inner radius calculation: diff --git a/src/zen/common/sys/ZenActorsManager.sys.mjs b/src/zen/common/sys/ZenActorsManager.sys.mjs index 6595d55ee..766193ad3 100644 --- a/src/zen/common/sys/ZenActorsManager.sys.mjs +++ b/src/zen/common/sys/ZenActorsManager.sys.mjs @@ -54,11 +54,29 @@ let JSWINDOWACTORS = { }, }, allFrames: true, - matches: ["*://*/*"], + remoteTypes: ["web", "file"], enablePreference: "zen.glance.enabled", }, }; +if (!Services.appinfo.inSafeMode) { + JSWINDOWACTORS.ZenBoosts = { + parent: { + esModuleURI: "resource:///actors/ZenBoostsParent.sys.mjs", + }, + child: { + esModuleURI: "resource:///actors/ZenBoostsChild.sys.mjs", + events: { + DOMWindowCreated: {}, + unload: {}, + }, + }, + allFrames: true, + remoteTypes: ["web", "file"], + enablePreference: "zen.boosts.enabled", + }; +} + export let gZenActorsManager = { init() { ActorManagerParent.addJSProcessActors(JSPROCESSACTORS); diff --git a/src/zen/common/sys/ui/ZenProgressBar.sys.mjs b/src/zen/common/sys/ui/ZenProgressBar.sys.mjs new file mode 100644 index 000000000..511c68c37 --- /dev/null +++ b/src/zen/common/sys/ui/ZenProgressBar.sys.mjs @@ -0,0 +1,138 @@ +// 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/. + +import { ZenUIComponent } from "resource:///modules/zen/ui/ZenUIComponent.sys.mjs"; + +const WAIT_BEFORE_SHOWING_LONG_LOAD = 3000; + +export class ZenProgressBar extends ZenUIComponent { + #element = null; + #loadingTab = null; + #longLoadTimer = null; + #promise = null; + + init() { + this.listenBrowserTabsProgress(); + this.addEventListener("TabSelect"); + } + + onStateChange(aWebProgress) { + this.#checkBrowserProgress(aWebProgress); + } + + onLocationChange(webProgress) { + this.#checkBrowserProgress(webProgress); + } + + on_TabSelect() { + const gBrowser = this.window.gBrowser; + const selectedTab = gBrowser.selectedTab; + this.onLocationChange(gBrowser.getBrowserForTab(selectedTab)); + } + + get #progressBar() { + if (!this.#loadingTab) { + return null; + } + if (!this.#element) { + this.#element = this.window.document.createXULElement("hbox"); + this.#element.id = "zen-loading-progress-bar"; + } + if ( + this.#element._loadingTab?.deref() !== this.#loadingTab && + this.#loadingTab + ) { + this.#element._loadingTab = new WeakRef(this.#loadingTab); + const container = this.window.document.getElementById( + this.#loadingTab.linkedPanel + ); + container.firstChild.before(this.#element); + this.window.gZenUIManager.elementAnimate( + this.#element, + { + opacity: [0, 0.8], + }, + { + duration: 400, + } + ); + } + return this.#element; + } + + async #checkBrowserProgress(webProgress) { + await this.#promise; + const window = this.window; + const gBrowser = window.gBrowser; + const tab = gBrowser.getTabForBrowser(webProgress); + const isLoading = + tab?.selected && + (tab.hasAttribute("busy") || tab.hasAttribute("progress")); + if (isLoading) { + this.#showProgressBar(tab); + } else { + this.#hideProgressBar(); + } + } + + #hideProgressBar(aInstant = false) { + const progressBar = this.#element; + const window = this.window; + if (this.#longLoadTimer) { + window.clearTimeout(this.#longLoadTimer); + this.#longLoadTimer = null; + } + + this.#loadingTab = null; + if (!progressBar) { + return; + } + let { promise, resolve } = Promise.withResolvers(); + this.#promise = promise; + const callback = () => { + delete progressBar._loadingTab; + progressBar.remove(); + this.#element = null; + resolve(); + }; + if (this.window.gReduceMotion || aInstant) { + callback(); + return; + } + this.window.gZenUIManager + .elementAnimate( + progressBar, + { + transform: ["scaleX(0.8) translate(-50%, -50%)"], + opacity: [0], + }, + { + duration: 300, + } + ) + .then(callback); + } + + #showProgressBar(aTab) { + if (this.#loadingTab === aTab) { + return; + } + if (this.#element) { + return; + } + let { promise, resolve } = Promise.withResolvers(); + this.#promise = promise; + this.#loadingTab = aTab; + const progressBar = this.#progressBar; + progressBar.removeAttribute("fade-out"); + progressBar.removeAttribute("long-load"); + this.#longLoadTimer = this.window.setTimeout(() => { + if (this.#loadingTab === aTab) { + progressBar.setAttribute("long-load", "true"); + } + this.#longLoadTimer = null; + }, WAIT_BEFORE_SHOWING_LONG_LOAD); + resolve(); + } +} diff --git a/src/zen/common/sys/ui/ZenUIComponent.sys.mjs b/src/zen/common/sys/ui/ZenUIComponent.sys.mjs new file mode 100644 index 000000000..7a26773ea --- /dev/null +++ b/src/zen/common/sys/ui/ZenUIComponent.sys.mjs @@ -0,0 +1,61 @@ +// 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/. + +/** + * Base class for UI components in Zen. + * UI components are responsible for managing their own event listeners + * and providing a consistent interface for handling events. + */ +export class ZenUIComponent { + #window = null; + #eventListeners = new Set(); + + constructor(aWindow) { + this.#window = aWindow; + this.init(); + this.#window.addEventListener("unload", () => { + if (typeof this.uninit === "function") { + this.uninit(); + } + for (const { type, options } of this.#eventListeners) { + this.#window.removeEventListener(type, this, options); + } + this.#eventListeners.clear(); + }); + } + + get window() { + return this.#window; + } + + /** + * Adds an event listener to the component that will automatically be removed when the window unloads. + * + * @param {string} type - The event type to listen for. + * @param {object} options - The event listener function or an object containing options. + * @returns {void} + */ + addEventListener(type, options = {}) { + this.#window.addEventListener(type, this, options); + if (options?.once) { + return; + } + this.#eventListeners.add({ type, options }); + } + + listenBrowserTabsProgress() { + this.#window.gBrowser.addTabsProgressListener(this); + } + + listenBrowserProgress() { + this.#window.gBrowser.addProgressListener(this); + } + + handleEvent(event) { + const handlerName = "on_" + event.type; + if (typeof this[handlerName] === "function") { + this[handlerName](event); + } + } +} diff --git a/src/zen/common/zen-sets.js b/src/zen/common/zen-sets.js index 86e5d65d4..4c17cf94f 100644 --- a/src/zen/common/zen-sets.js +++ b/src/zen/common/zen-sets.js @@ -151,6 +151,14 @@ document.addEventListener( ZenLiveFoldersManager.handleEvent(event); break; } + case "cmd_zenDuplicateTab": { + const selectedTabs = gBrowser.selectedTabs; + let insertAt = selectedTabs.at(-1)._tPos + 1; + for (const tab of selectedTabs) { + gBrowser.duplicateTab(tab, true, { tabIndex: insertAt++ }); + } + break; + } default: gZenGlanceManager.handleMainCommandSet(event); if (event.target.id.startsWith("cmd_zenWorkspaceSwitch")) { diff --git a/src/zen/common/zenThemeModifier.js b/src/zen/common/zenThemeModifier.js index 76ab3d656..31449efaa 100644 --- a/src/zen/common/zenThemeModifier.js +++ b/src/zen/common/zenThemeModifier.js @@ -57,10 +57,14 @@ "ZenViewSplitter:SplitViewActivated", "fullscreen", "ZenCompactMode:Toggled", + "MozDOMFullscreen:Entered", + "MozDOMFullscreen:Exited", ]; const separationHandler = this.updateElementSeparation.bind(this); for (let eventName of eventsForSeparation) { - window.addEventListener(eventName, separationHandler); + window.addEventListener(eventName, separationHandler, { + capture: true, + }); } window.addEventListener( @@ -70,7 +74,9 @@ Services.prefs.removeObserver(pref, handleEvent); } for (let eventName of eventsForSeparation) { - window.removeEventListener(eventName, separationHandler); + window.removeEventListener(eventName, separationHandler, { + capture: true, + }); } }, { once: true } @@ -131,11 +137,19 @@ } }, - updateElementSeparation() { + /** + * @param {Event|undefined} event - The event that triggered the update, if any. + * If the event is a fullscreen change event, the element separation will be updated accordingly. + */ + updateElementSeparation(event = undefined) { const kMinElementSeparation = 0.1; // in px let separation = this.elementSeparation; + let domFullscreen = + event?.type === "MozDOMFullscreen:Entered" || + document.documentElement.hasAttribute("inDOMFullscreen"); if ( document.documentElement.hasAttribute("inFullscreen") && + (!domFullscreen || event?.type === "MozDOMFullscreen:Exited") && window.gZenCompactModeManager?.preference && !document .getElementById("tabbrowser-tabbox") @@ -154,6 +168,9 @@ document.documentElement.setAttribute("zen-no-padding", true); } else { document.documentElement.removeAttribute("zen-no-padding"); + if (domFullscreen) { + window.windowUtils.flushLayoutWithoutThrottledAnimations(); + } } }, diff --git a/src/zen/compact-mode/ZenCompactMode.mjs b/src/zen/compact-mode/ZenCompactMode.mjs index c0c40036b..a4b046bf2 100644 --- a/src/zen/compact-mode/ZenCompactMode.mjs +++ b/src/zen/compact-mode/ZenCompactMode.mjs @@ -148,6 +148,7 @@ window.gZenCompactModeManager = { // We dont want the user to be able to spam the button return; } + delete this._isTabBeingDragged; this.sidebar.removeAttribute("zen-user-show"); // We use this element in order to make it persis across restarts, by using the XULStore. // main-window can't store attributes other than window sizes, so we use this instead @@ -185,7 +186,8 @@ window.gZenCompactModeManager = { [ { selector: - ":is([panelopen='true'], [open='true'], [breakout-extend='true']):not(#urlbar[zen-floating-urlbar='true']):not(tab):not(.zen-compact-mode-ignore)", + ":where([panelopen='true'], [open='true'], [breakout-extend='true'])" + + ":not(#urlbar[zen-floating-urlbar='true']):not(tab):not(.zen-compact-mode-ignore)", }, ], "zen-compact-mode-active", @@ -196,7 +198,8 @@ window.gZenCompactModeManager = { [ { selector: - ":is([panelopen='true'], [open='true'], #urlbar:focus-within, [breakout-extend='true']):not(.zen-compact-mode-ignore)", + ":where([panelopen='true'], [open='true'], #urlbar:focus-within, [breakout-extend='true'])" + + ":not(.zen-compact-mode-ignore)", }, ], "zen-compact-mode-active", @@ -233,6 +236,7 @@ window.gZenCompactModeManager = { + `); const idToAction = { @@ -247,7 +251,7 @@ window.gZenCompactModeManager = { } } - document.getElementById("viewToolbarsMenuSeparator").before(fragment); + document.getElementById("toolbar-context-menu").prepend(fragment); this.updateContextMenu(); }, @@ -590,7 +594,7 @@ window.gZenCompactModeManager = { if (!toggle) { return; } - toggle.setAttribute("checked", this.preference); + toggle.toggleAttribute("checked", this.preference); const hideTabBar = this.canHideSidebar; const hideToolbar = this.canHideToolbar; @@ -600,9 +604,9 @@ window.gZenCompactModeManager = { const sidebarItem = document.getElementById(idName + "sidebar"); const toolbarItem = document.getElementById(idName + "toolbar"); const bothItem = document.getElementById(idName + "both"); - sidebarItem.setAttribute("checked", !hideBoth && hideTabBar); - toolbarItem.setAttribute("checked", !hideBoth && hideToolbar); - bothItem.setAttribute("checked", hideBoth); + sidebarItem.toggleAttribute("checked", !hideBoth && hideTabBar); + toolbarItem.toggleAttribute("checked", !hideBoth && hideToolbar); + bothItem.toggleAttribute("checked", hideBoth); }, _removeOpenStateOnUnifiedExtensions() { diff --git a/src/zen/drag-and-drop/ZenDragAndDrop.js b/src/zen/drag-and-drop/ZenDragAndDrop.js index 0e87be0be..360e8f75f 100644 --- a/src/zen/drag-and-drop/ZenDragAndDrop.js +++ b/src/zen/drag-and-drop/ZenDragAndDrop.js @@ -68,6 +68,7 @@ #maxTabsPerRow = 0; #changeSpaceTimer = null; #isAnimatingTabMove = false; + #firstHapticFeedbackPlayed = false; #dragOverSplit = {}; @@ -119,6 +120,12 @@ init() { super.init(); this.handle_windowDragEnter = this.handle_windowDragEnter.bind(this); + if (gZenWorkspaces.workspaceEnabled) { + gZenWorkspaces.workspaceIcons.addEventListener( + "dragover", + this.handle_spaceIconDragOver.bind(this) + ); + } window.addEventListener( "dragleave", this.handle_windowDragLeave.bind(this), @@ -676,6 +683,31 @@ } } + #onSpaceChanged(spaceChanged, dt) { + if (AppConstants.platform !== "macosx") { + // See the hack in #createDragImageForTabs for more details which + // explains why we need to do this on non-macOS platforms. + return; + } + let tabs = this.originalDragImageArgs[0].children; + const { isDarkMode, isExplicitMode } = + gZenThemePicker.getGradientForWorkspace(spaceChanged, { + getGradient: false, + }); + for (let tab of tabs) { + if (isExplicitMode) { + tab.style.colorScheme = isDarkMode ? "dark" : "light"; + } else { + tab.style.colorScheme = ""; + } + } + requestAnimationFrame(() => { + requestAnimationFrame(() => { + dt.updateDragImage(...this.originalDragImageArgs); + }); + }); + } + #handle_sidebarDragOver(event) { const dt = event.dataTransfer; const draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0); @@ -696,28 +728,7 @@ /* Disable wrapping */ true ) .then(spaceChanged => { - if (AppConstants.platform !== "macosx") { - // See the hack in #createDragImageForTabs for more details which - // explains why we need to do this on non-macOS platforms. - return; - } - let tabs = this.originalDragImageArgs[0].children; - const { isDarkMode, isExplicitMode } = - gZenThemePicker.getGradientForWorkspace(spaceChanged, { - getGradient: false, - }); - for (let tab of tabs) { - if (isExplicitMode) { - tab.style.colorScheme = isDarkMode ? "dark" : "light"; - } else { - tab.style.colorScheme = ""; - } - } - requestAnimationFrame(() => { - requestAnimationFrame(() => { - dt.updateDragImage(...this.originalDragImageArgs); - }); - }); + this.#onSpaceChanged(spaceChanged, dt); }); this.#changeSpaceTimer = null; }, this._dndSwitchSpaceDelay); @@ -727,6 +738,27 @@ } } + handle_spaceIconDragOver(event) { + const dt = event.dataTransfer; + const draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0); + if (draggedTab.hasAttribute("zen-essential")) { + return; + } + const target = event.target; + const spaceId = target.getAttribute("zen-workspace-id"); + if (!spaceId) { + return; + } + this.clearDragOverVisuals(); + const currentSpaceId = gZenWorkspaces.activeWorkspace; + if (spaceId === currentSpaceId || gZenWorkspaces._animatingChange) { + return; + } + gZenWorkspaces.changeWorkspaceWithID(spaceId).then(spaceChanged => { + this.#onSpaceChanged(spaceChanged, dt); + }); + } + #handle_tabDragOverToSplit(event) { if (!this._dndSplitEnabled) { return; @@ -912,6 +944,16 @@ } handle_drop(event) { + const ownerGlobal = event.dataTransfer.mozGetDataAt( + TAB_DROP_TYPE, + 0 + )?.ownerGlobal; + if (ownerGlobal?.gZenCompactModeManager) { + // Sometimes, dragend doesn't always get called when dragging + // to different windows, see gh-8643. + delete ownerGlobal.gZenCompactModeManager._isTabBeingDragged; + ownerGlobal.gZenCompactModeManager._clearAllHoverStates(); + } this.clearSpaceSwitchTimer(); gZenFolders.highlightGroupOnDragOver(null); super.handle_drop(event); @@ -976,6 +1018,12 @@ } handle_drop_transition(dropElement, draggedTab, movingTabs, dropBefore) { + if ( + dropElement?.hasAttribute("zen-empty-tab") && + dropElement.group?.isZenFolder + ) { + dropElement = dropElement.group; + } if (isTabGroupLabel(dropElement)) { dropElement = dropElement.group; } @@ -1060,7 +1108,10 @@ ); for (let i = startIndex; i <= endIndex; i++) { let item = items[i]; - if (!movingTabs.includes(item)) { + if ( + !movingTabs.includes(item) && + !(isTabGroupLabel(item) && i == startIndex) + ) { tabsInBetween.push(item); } } @@ -1119,12 +1170,17 @@ // outside of a valid drop target. ownerGlobal.gZenFolders.highlightGroupOnDragOver(null); this.ZenDragAndDropService.onDragEnd(); - super.handle_dragend(event); + try { + super.handle_dragend(event); + } catch (e) { + console.error(e); + } thisFromGlobal.clearDragOverVisuals(); ownerGlobal.gZenPinnedTabManager.removeTabContainersDragoverClass(); thisFromGlobal._clearDragOverSplit(); this.#maybeClearVerticalPinnedGridDragOver(); thisFromGlobal.originalDragImageArgs = []; + this.#firstHapticFeedbackPlayed = false; window.removeEventListener( "dragenter", thisFromGlobal.handle_windowDragEnter, @@ -1242,13 +1298,13 @@ // Only if there are no normal tabs to drop after showIndicatorUnderNewTabButton = lastTab.hasAttribute("zen-empty-tab"); - let useLastPinnd = + let useLastPinned = (hoveringPeriphery || (showIndicatorUnderNewTabButton && !(pinnedTabsCount - gBrowser._numZenEssentials))) && Services.prefs.getBoolPref("zen.view.show-newtab-button-top"); dropElement = - (useLastPinnd + (useLastPinned ? this._tabbrowserTabs.ariaFocusableItems.at(pinnedTabsCount) : this._tabbrowserTabs.ariaFocusableItems.at(-1)) || lastTab; } @@ -1379,6 +1435,12 @@ dropBefore = true; } } + if (shouldPlayHapticFeedback && !this.#firstHapticFeedbackPlayed) { + // The first haptic feedback can often be too annoying, + // so we skip it, but play for subsequent dragovers. + this.#firstHapticFeedbackPlayed = true; + shouldPlayHapticFeedback = false; + } if (shouldPlayHapticFeedback) { // eslint-disable-next-line mozilla/valid-services Services.zen.playHapticFeedback(); diff --git a/src/zen/drag-and-drop/nsZenDragAndDrop.cpp b/src/zen/drag-and-drop/nsZenDragAndDrop.cpp index c8c4fb792..ec2dcc957 100644 --- a/src/zen/drag-and-drop/nsZenDragAndDrop.cpp +++ b/src/zen/drag-and-drop/nsZenDragAndDrop.cpp @@ -29,7 +29,7 @@ nsZenDragAndDrop::nsZenDragAndDrop() { (void)this->OnDragEnd(); } auto nsZenDragAndDrop::GetZenDragAndDropInstance() -> nsCOMPtr { - return do_GetService(ZEN_BOOSTS_BACKEND_CONTRACTID); + return do_GetService(ZEN_DND_MANAGER_CONTRACTID); } NS_IMETHODIMP diff --git a/src/zen/drag-and-drop/nsZenDragAndDrop.h b/src/zen/drag-and-drop/nsZenDragAndDrop.h index 7f672352e..04dde7d26 100644 --- a/src/zen/drag-and-drop/nsZenDragAndDrop.h +++ b/src/zen/drag-and-drop/nsZenDragAndDrop.h @@ -8,7 +8,7 @@ #include "nsIZenDragAndDrop.h" #include "nsCOMPtr.h" -#define ZEN_BOOSTS_BACKEND_CONTRACTID "@mozilla.org/zen/drag-and-drop;1" +#define ZEN_DND_MANAGER_CONTRACTID "@mozilla.org/zen/drag-and-drop;1" namespace zen { diff --git a/src/zen/folders/ZenFolders.mjs b/src/zen/folders/ZenFolders.mjs index ffb57831e..fe2b7c2c7 100644 --- a/src/zen/folders/ZenFolders.mjs +++ b/src/zen/folders/ZenFolders.mjs @@ -65,12 +65,33 @@ class nsZenFolders extends nsZenDOMOperatedFeature { } #initContextMenu() { - const contextMenuItems = window.MozXULElement.parseXULToFragment( - `` - ); + const contextMenuItems = window.MozXULElement.parseXULToFragment(` + + + + + + + `); document.getElementById("context_moveTabToGroup").before(contextMenuItems); const contextMenuItemsToolbar = window.MozXULElement.parseXULToFragment( - `` + ` + + + + + + + ` ); document .getElementById("toolbar-context-openANewTab") @@ -211,6 +232,7 @@ class nsZenFolders extends nsZenDOMOperatedFeature { document .getElementById("zen-context-menu-new-folder-toolbar") .addEventListener("command", onNewFolder); + this.#initMoveTabToFolder(); SessionStore.promiseInitialized.then(() => { gBrowser.tabContainer.addEventListener( "dragstart", @@ -219,6 +241,67 @@ class nsZenFolders extends nsZenDOMOperatedFeature { }); } + #initMoveTabToFolder() { + const moveTabToFolderMenu = document.getElementById( + "context_zenMoveToFolder" + ); + moveTabToFolderMenu.addEventListener("popupshowing", () => { + const separator = moveTabToFolderMenu.querySelector("menuseparator"); + let tabs = TabContextMenu.contextTab?.multiselected + ? gBrowser.selectedTabs + : [TabContextMenu.contextTab]; + let groups = gBrowser.tabGroups.filter(group => { + const isZenFolder = group?.isZenFolder; + const isLiveFolder = group?.isLiveFolder; + const spaceId = group?.getAttribute("zen-workspace-id"); + if ( + !isZenFolder || + isLiveFolder || + spaceId !== gZenWorkspaces.activeWorkspace + ) { + return false; + } + return !tabs.some(tab => tab.group === group); + }); + separator.hidden = groups.length === 0; + for (const group of groups) { + const icon = group.iconURL; + const menuItem = document.createXULElement("menuitem"); + menuItem.setAttribute("label", group.label); + menuItem.classList.add("context-zen-move-to-folder-item"); + if (icon) { + menuItem.setAttribute("image", icon); + } + menuItem._group = group; + separator.before(menuItem); + } + }); + + moveTabToFolderMenu.addEventListener("popuphidden", () => { + const items = moveTabToFolderMenu.querySelectorAll( + ".context-zen-move-to-folder-item" + ); + for (const item of items) { + delete item._group; + item.remove(); + } + }); + + moveTabToFolderMenu.addEventListener("command", event => { + if (!event.target.classList.contains("context-zen-move-to-folder-item")) { + return; + } + const group = event.target._group; + if (!group) { + return; + } + let tabs = TabContextMenu.contextTab?.multiselected + ? gBrowser.selectedTabs + : [TabContextMenu.contextTab]; + group.addTabs(tabs); + }); + } + handleEvent(aEvent) { let methodName = `on_${aEvent.type}`; if (methodName in this) { @@ -728,8 +811,14 @@ class nsZenFolders extends nsZenDOMOperatedFeature { const onKeyDown = event => { // Arrow down and up to navigate through the list - if (event.key === "ArrowDown" || event.key === "ArrowUp") { + if ( + event.key === "ArrowDown" || + event.key === "ArrowUp" || + event.key === "Tab" + ) { event.preventDefault(); + let isUp = + event.key === "ArrowUp" || (event.key === "Tab" && event.shiftKey); const items = Array.from(tabsList.children).filter( item => !item.hidden ); @@ -739,9 +828,9 @@ class nsZenFolders extends nsZenDOMOperatedFeature { let index = items.indexOf( tabsList.querySelector(".folders-tabs-list-item[selected]") ); - if (event.key === "ArrowDown") { + if (!isUp) { index = (index + 1) % items.length; - } else if (event.key === "ArrowUp") { + } else { index = (index - 1 + items.length) % items.length; } items.forEach(item => item.removeAttribute("selected")); diff --git a/src/zen/folders/zen-folders.css b/src/zen/folders/zen-folders.css index cc82d727a..146e94364 100644 --- a/src/zen/folders/zen-folders.css +++ b/src/zen/folders/zen-folders.css @@ -177,7 +177,7 @@ zen-folder { & > label { width: 100% !important; border: none !important; - color: var(--sidebar-text-color) !important; + color: var(--toolbar-color) !important; margin: 0 !important; font-weight: 600; align-self: center !important; diff --git a/src/zen/glance/ZenGlanceManager.mjs b/src/zen/glance/ZenGlanceManager.mjs index 47af5981b..e2d83eb74 100644 --- a/src/zen/glance/ZenGlanceManager.mjs +++ b/src/zen/glance/ZenGlanceManager.mjs @@ -34,13 +34,14 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { // Arc animation configuration #ARC_CONFIG = Object.freeze({ - ARC_STEPS: 400, // Increased for smoother bounce + ARC_STEPS: 80, // Browser interpolates between keyframes natively MAX_ARC_HEIGHT: 25, ARC_HEIGHT_RATIO: 0.2, // Arc height = distance * ratio (capped at MAX_ARC_HEIGHT) }); - #GLANCE_ANIMATION_DURATION = - Services.prefs.getIntPref("zen.glance.animation-duration") / 1000; + #GLANCE_ANIMATION_DURATION = Services.prefs.getIntPref( + "zen.glance.animation-duration" + ); init() { this.#setupEventListeners(); @@ -78,7 +79,11 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { menuitem.setAttribute("data-l10n-id", "zen-open-link-in-glance"); menuitem.addEventListener("command", () => - this.openGlance({ url: gContextMenu.linkURL }) + this.openGlance({ + url: gContextMenu.linkURL, + triggeringPrincipal: + Services.scriptSecurityManager.getSystemPrincipal(), + }) ); document @@ -171,19 +176,20 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { /** * Create a new browser element for a glance * - * @param {string} url - The URL to load + * @param {object} data - Glance data including URL and dimensions * @param {Tab} currentTab - The current tab - * @param {Tab} existingTab - Optional existing tab to reuse + * @param {Tab|null} existingTab - Optional existing tab to reuse * @returns {Browser} The created browser element */ - #createBrowserElement(url, currentTab, existingTab = null) { - const newTabOptions = this.#createTabOptions(currentTab); + #createBrowserElement(data, currentTab, existingTab = null) { + const url = data.url; + const newTabOptions = this.#createTabOptions(currentTab, data); const newUUID = gZenUIManager.generateUuidv4(); currentTab._selected = true; const newTab = existingTab ?? - gBrowser.addTrustedTab(Services.io.newURI(url).spec, newTabOptions); + gBrowser.addTab(Services.io.newURI(url).spec, newTabOptions); this.#configureNewTab(newTab, currentTab, newUUID); this.#registerGlance(newTab, currentTab, newUUID); @@ -196,14 +202,18 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { * Create tab options for a new glance tab * * @param {Tab} currentTab - The current tab + * @param {object} data - Glance data for the new tab * @returns {object} Tab options */ - #createTabOptions(currentTab) { + #createTabOptions(currentTab, data) { return { userContextId: currentTab.getAttribute("usercontextid") || "", skipBackgroundNotify: true, insertTab: true, skipLoad: false, + skipAnimation: true, + ownerTab: currentTab, + triggeringPrincipal: data.triggeringPrincipal, }; } @@ -285,7 +295,7 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { { duration: 0.2, type: "spring", - delay: this.#GLANCE_ANIMATION_DURATION - 0.2, + delay: this.#GLANCE_ANIMATION_DURATION / 1000 - 0.2, bounce: 0, } ); @@ -303,16 +313,18 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { // content process since it does not take into account scroll. This way, we can // be sure that the coordinates are correct. const tabPanelsRect = gBrowser.tabpanels.getBoundingClientRect(); + const zoomLevel = + this.#currentParentTab?.linkedBrowser.browsingContext.fullZoom || 1; const rect = new DOMRect( - data.clientX + tabPanelsRect.left, - data.clientY + tabPanelsRect.top, - data.width, - data.height + data.clientX / zoomLevel + tabPanelsRect.left, + data.clientY / zoomLevel + tabPanelsRect.top, + data.width / zoomLevel, + data.height / zoomLevel ); - return await this.#imageBitmapToBase64( + return await this.#imageBitmapToObjectURL( await window.browsingContext.currentWindowGlobal.drawSnapshot( rect, - 1, + zoomLevel, "transparent", undefined ) @@ -364,7 +376,7 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { this.#setAnimationState(true); const currentTab = ownerTab ?? gBrowser.selectedTab; const browserElement = this.#createBrowserElement( - data.url, + data, currentTab, existingTab ); @@ -393,7 +405,7 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { * @returns {Promise} Promise that resolves to the glance tab */ #animateGlanceOpening(data, browserElement) { - this.#prepareGlanceAnimation(data, browserElement); + this.#prepareGlanceAnimation(data); // FIXME(cheffy): We *must* have the call back async (at least, // until a better solution is found). If we do it inside the requestAnimationFrame, // we see flashing and if we do it directly, the animation does not play at all. @@ -417,15 +429,13 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { * Prepare the glance for animation * * @param {object} data - Glance data - * @param {Browser} browserElement - The browser element */ - #prepareGlanceAnimation(data, browserElement) { + #prepareGlanceAnimation(data) { this.quickOpenGlance(); const newButtons = this.#createNewOverlayButtons(); this.browserWrapper.appendChild(newButtons); this.#setupGlancePositioning(data); - this.#configureBrowserElement(browserElement); } /** @@ -443,7 +453,7 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { opacity: [1, 0.3], }, { - duration: this.#GLANCE_ANIMATION_DURATION, + duration: this.#GLANCE_ANIMATION_DURATION / 1000, type: "spring", bounce: 0.2, } @@ -463,9 +473,6 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { this.overlay.removeAttribute("fade-out"); this.browserWrapper.setAttribute("animate", true); - this.browserWrapper.style.transform = `translate(${left - width / 2}px, ${top - height / 2}px)`; - this.browserWrapper.style.width = `${width}px`; - this.browserWrapper.style.height = `${height}px`; this.#storeOriginalPosition({ top, left, width, height }); this.overlay.style.overflow = "visible"; @@ -507,47 +514,9 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { this.#glances.get(this.#currentGlanceID).elementImageData = data.elementData; - gZenUIManager.motion.animate( - imageDataElement, - { - opacity: [1, 0], - }, - { - duration: this.#GLANCE_ANIMATION_DURATION / 2, - easing: "easeInOut", - } - ); - return imageDataElement; } - /** - * Configure browser element for animation - * - * @param {Browser} browserElement - The browser element - */ - #configureBrowserElement(browserElement) { - const rect = window.windowUtils.getBoundsWithoutFlushing( - this.browserWrapper.parentElement - ); - const minWidth = rect.width * 0.8; - const minHeight = rect.height * 0.8; - - browserElement.style.minWidth = `${minWidth}px`; - browserElement.style.minHeight = `${minHeight}px`; - } - - /** - * Get the transform origin for the animation - * - * @param {object} data - Glance data with position and dimensions - * @returns {string} The transform origin CSS value - */ - #getTransformOrigin(data) { - const { clientX, clientY } = data; - return `${clientX}px ${clientY}px`; - } - /** * Execute the main glance animation * @@ -558,23 +527,25 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { #executeGlanceAnimation(data, browserElement, resolve) { const imageDataElement = this.#handleElementPreview(data); - // Create curved animation sequence - const arcSequence = this.#createGlanceArcSequence(data, "opening"); - const transformOrigin = this.#getTransformOrigin(data); - - this.browserWrapper.style.transformOrigin = transformOrigin; + // Create the curved animation sequence. The transform origin is handled + // separately (for example via CSS on the wrapper). + const arcSequence = this.#createGlanceArcSequence( + data, + "opening", + imageDataElement + ); // Only animate if there is element data, so we can apply a // nice fade-in effect to the content. But if it doesn't exist, // we just fall back to always showing the browser directly. if (data.elementData) { - gZenUIManager.motion - .animate( + gZenUIManager + .elementAnimate( this.contentWrapper, { opacity: [0, 1] }, { duration: this.#GLANCE_ANIMATION_DURATION / 4, - easing: "easeInOut", + easing: "ease-in-out", } ) .then(() => { @@ -591,12 +562,12 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { browserElement.zenModeActive = false; browserElement.docShellIsActive = false; } - gZenUIManager.motion - .animate(this.browserWrapper, arcSequence, { + gZenUIManager + .elementAnimate(this.browserWrapper, arcSequence, { duration: gZenUIManager.testingEnabled ? 0 : this.#GLANCE_ANIMATION_DURATION, - ease: "easeInOut", + easing: "ease-in-out", }) .then(() => { if (shouldDeactivateDocShell) { @@ -612,10 +583,35 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { * * @param {object} data - Glance data with position and dimensions * @param {string} direction - 'opening' or 'closing' + * @param {Element|null} imageDataElement - The image data element for preview (optional) * @returns {object} Animation sequence object */ - #createGlanceArcSequence(data, direction) { - const { clientX, clientY, width, height } = data; + #createGlanceArcSequence(data, direction, imageDataElement = null) { + let { clientX, clientY, width, height } = data; + if (imageDataElement?.parentElement) { + // Since we are animating scale transforms on the wrapper, we need to + // adjust the width/height to match the scaled size of the element preview, + // so the image preview properly matches the size of the animating browser + // during the animation. + // For example: + // +-- wrapper --------------------------+ + // | | + // | +--- element preview -------------+ | + // | | | | + // | +---------------------------------+ | + // | | + // +-------------------------------------+ + // We are scaling the wrapper while having only the element preview size + // in mind, so we need to adjust the width/height to match the size of the element preview + const rect = imageDataElement.getBoundingClientRect(); + const aspectRatio = width / height; + const heightRatio = rect.height / (rect.width / aspectRatio); + const originalHeight = height; + if (heightRatio > 1) { + height *= heightRatio; + clientY -= (height - originalHeight) / 2; + } + } // Calculate start and end positions based on direction let startPosition, endPosition; @@ -654,6 +650,12 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { }; } + // Reference size used as the scale(1, 1) baseline — this matches the + // wrapper's natural CSS size (80% x 100% of the tab panels) so the + // animation can run entirely on the compositor via transform. + const refWidth = tabPanelsRect.width * widthPercent; + const refHeight = tabPanelsRect.height; + // Calculate distance and arc parameters const distance = this.#calculateDistance(startPosition, endPosition); const { arcHeight, shouldArcDownward } = this.#calculateOptimalArc( @@ -663,9 +665,10 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { ); const sequence = { - transform: [], - width: [], - height: [], + x: [], + y: [], + scaleY: [], + scaleX: [], }; const steps = this.#ARC_CONFIG.ARC_STEPS; @@ -695,6 +698,8 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { const currentHeight = startPosition.height + (endPosition.height - startPosition.height) * eased; + const scaleX = currentWidth / refWidth; + const scaleY = currentHeight / refHeight; // Calculate position on arc const distanceX = endPosition.x - startPosition.x; @@ -706,11 +711,12 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { distanceY * eased + arcDirection * arcHeight * (1 - (2 * eased - 1) ** 2); - sequence.transform.push( - `translate(${x - currentWidth / 2}px, ${y - currentHeight / 2}px)` - ); - sequence.width.push(`${currentWidth}px`); - sequence.height.push(`${currentHeight}px`); + let translateX = x - currentWidth / 2; + let translateY = y - currentHeight / 2; + sequence.x.push(translateX); + sequence.y.push(translateY); + sequence.scaleX.push(scaleX); + sequence.scaleY.push(scaleY); } return sequence; @@ -774,19 +780,15 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { imageDataElement.remove(); } - this.browserWrapper.style.transformOrigin = ""; - - browserElement.style.minWidth = ""; - browserElement.style.minHeight = ""; - + // Batch all style/attribute writes together to avoid interleaved + // read/write layout thrashing. this.browserWrapper.style.height = "100%"; this.browserWrapper.style.width = "80%"; - - gBrowser.tabContainer._invalidateCachedTabs(); - this.overlay.style.removeProperty("overflow"); this.browserWrapper.removeAttribute("animate"); this.browserWrapper.setAttribute("has-finished-animation", true); + this.overlay.style.removeProperty("overflow"); + gBrowser.tabContainer._invalidateCachedTabs(); this.#setAnimationState(false); this.#currentTab.dispatchEvent(new Event("GlanceOpen", { bubbles: true })); resolve(this.#currentTab); @@ -972,7 +974,7 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { { duration: 0.2, type: "spring", - bounce: this.#GLANCE_ANIMATION_DURATION - 0.1, + bounce: this.#GLANCE_ANIMATION_DURATION / 1000 - 0.1, } ) .then(() => { @@ -981,19 +983,29 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { } } - #imageBitmapToBase64(imageBitmap) { - // 1. Create a canvas with the same size as the ImageBitmap - const canvas = document.createElement("canvas"); - canvas.width = imageBitmap.width; - canvas.height = imageBitmap.height; - - // 2. Draw the ImageBitmap onto the canvas + async #imageBitmapToObjectURL(imageBitmap) { + // OffscreenCanvas + convertToBlob avoids the synchronous PNG re-encode + // and base64 string copy that toDataURL performs on the main thread. + // Callers must release the URL via #deleteGlance when the glance entry + // is removed so the blob can be freed. + const canvas = new OffscreenCanvas(imageBitmap.width, imageBitmap.height); const ctx = canvas.getContext("2d"); ctx.drawImage(imageBitmap, 0, 0); + const blob = await canvas.convertToBlob({ type: "image/png" }); + imageBitmap.close(); + return URL.createObjectURL(blob); + } - // 3. Convert the canvas content to a Base64 string (PNG by default) - const base64String = canvas.toDataURL("image/png"); - return base64String; + #deleteGlance(glanceID) { + const entry = this.#glances.get(glanceID); + if (!entry) { + return; + } + this.#glances.delete(glanceID); + const url = entry.elementData ?? entry.elementImageData; + if (typeof url === "string") { + URL.revokeObjectURL(url); + } } /** @@ -1010,7 +1022,7 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { opacity: [0.3, 1], }, { - duration: this.#GLANCE_ANIMATION_DURATION / 1.5, + duration: this.#GLANCE_ANIMATION_DURATION / 1000 / 1.5, type: "spring", bounce: 0, } @@ -1038,17 +1050,25 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { this.#currentGlanceID ).elementImageData; - this.#addElementPreview(elementImageData); + const imageDataElement = this.#addElementPreview(elementImageData); // Create curved closing animation sequence const closingData = this.#createClosingDataFromOriginalPosition(originalPosition); - const arcSequence = this.#createGlanceArcSequence(closingData, "closing"); + const arcSequence = this.#createGlanceArcSequence( + closingData, + "closing", + imageDataElement + ); - gZenUIManager.motion - .animate(this.browserWrapper, arcSequence, { + // Batch style writes before starting animation to avoid layout thrashing + this.browserWrapper.style.width = ""; + this.browserWrapper.style.height = ""; + + gZenUIManager + .elementAnimate(this.browserWrapper, arcSequence, { duration: this.#GLANCE_ANIMATION_DURATION, - ease: "easeOut", + easing: "ease-out", }) .then(() => { // Remove element preview after closing animation @@ -1095,6 +1115,7 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { const imageDataElement = this.#createGlancePreviewElement(elementImageData); this.browserWrapper.prepend(imageDataElement); + return imageDataElement; } } @@ -1186,7 +1207,7 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { */ #resetGlanceState(setNewID) { this.#currentParentTab.removeAttribute("glance-id"); - this.#glances.delete(this.#currentGlanceID); + this.#deleteGlance(this.#currentGlanceID); this.#currentGlanceID = setNewID; this.#duringOpening = false; } @@ -1517,6 +1538,7 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { this.openGlance( { url: undefined, + // No need for triggeringPrincipal here }, tab, tab.owner @@ -1534,7 +1556,7 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { this.animatingFullOpen = false; const glanceID = this.#currentGlanceID; this.closeGlance({ noAnimation: true, skipPermitUnload: true }); - this.#glances.delete(glanceID); + this.#deleteGlance(glanceID); } /** @@ -1554,9 +1576,6 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { this.#handleZenFolderPinning(); gBrowser.moveTabAfter(this.#currentTab, this.#currentParentTab); - const browserRect = window.windowUtils.getBoundsWithoutFlushing( - this.browserWrapper - ); this.#prepareTabForFullOpen(); const sidebarButtons = this.browserWrapper.querySelector( @@ -1577,7 +1596,7 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { return; } - await this.#animateFullOpen(browserRect); + await this.#animateFullOpen(); this.finishOpeningGlance(); } @@ -1613,31 +1632,27 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { /** * Animate the full opening process - * - * @param {object} browserRect - The browser rectangle */ - async #animateFullOpen(browserRect) { + async #animateFullOpen() { // Write styles early to avoid flickering - this.browserWrapper.style.opacity = 1; - this.browserWrapper.style.width = `${browserRect.width}px`; - this.browserWrapper.style.height = `${browserRect.height}px`; + this.browserWrapper.style.width = "100%"; + this.browserWrapper.style.height = "100%"; - await gZenUIManager.motion.animate( - this.browserWrapper, + await gZenUIManager.elementAnimate( + this.browserWrapper.parentElement, { - width: ["80%", "100%"], - height: ["100%", "100%"], + scale: [1, 1.005, 1], }, { - duration: this.#GLANCE_ANIMATION_DURATION, - type: "spring", - bounce: 0, + duration: 250, + easing: "ease-in-out", } ); + this.browserWrapper.style.scale = ""; + this.browserWrapper.style.opacity = ""; this.browserWrapper.style.width = ""; this.browserWrapper.style.height = ""; - this.browserWrapper.style.opacity = ""; gZenViewSplitter.deactivateCurrentSplitView({ removeDeckSelected: true }); } @@ -1703,6 +1718,7 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { clientY: top, width: rect.width, height: rect.height, + triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(), }; } @@ -1882,6 +1898,8 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature { ...clickPosition, width: 0, height: 0, + triggeringPrincipal: + Services.scriptSecurityManager.getSystemPrincipal(), }, currentTab, parentTab diff --git a/src/zen/glance/actors/ZenGlanceChild.sys.mjs b/src/zen/glance/actors/ZenGlanceChild.sys.mjs index 6583c9ef2..10d9fff14 100644 --- a/src/zen/glance/actors/ZenGlanceChild.sys.mjs +++ b/src/zen/glance/actors/ZenGlanceChild.sys.mjs @@ -2,6 +2,21 @@ // 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 { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; + +const lazy = {}; + +ChromeUtils.defineESModuleGetters(lazy, { + BrowserUtils: "resource://gre/modules/BrowserUtils.sys.mjs", +}); + +XPCOMUtils.defineLazyPreferenceGetter( + lazy, + "blockJavascript", + "browser.link.alternative_click.block_javascript", + true +); + export class ZenGlanceChild extends JSWindowActorChild { #activationMethod; @@ -26,37 +41,38 @@ export class ZenGlanceChild extends JSWindowActorChild { return !(event.ctrlKey ^ event.altKey ^ event.shiftKey ^ event.metaKey); } - #openGlance(target) { - let url = target.href; - // Add domain to relative URLs - if (!url.match(/^(?:[a-z]+:)?\/\//i)) { - url = this.contentWindow.location.origin + url; - } + #openGlance(href, principal) { this.sendAsyncMessage("ZenGlance:OpenGlance", { - url, + url: href, + triggeringPrincipal: principal, }); } - #sendClickDataToParent(target, element) { - if (!element && !target) { - return; + #sendClickDataToParent(node, originalTarget) { + if (!node) { + node = originalTarget; } - if (!target) { - target = element; + if (!node?.getBoundingClientRect) { + return; } // Get the largest element we can get. If the `A` element // is a parent of the original target, use the anchor element, // otherwise use the original target. - let rect = element.getBoundingClientRect(); - const anchorRect = target.getBoundingClientRect(); - if (anchorRect.width * anchorRect.height > rect.width * rect.height) { - rect = anchorRect; + let rect = node.getBoundingClientRect(); + const originalTargetRect = originalTarget.getBoundingClientRect(); + if ( + originalTargetRect.width * originalTargetRect.height > + rect.width * rect.height + ) { + rect = originalTargetRect; } + // Change the rect to make sure we take into account zoom. + const zoom = this.browsingContext.fullZoom; this.sendAsyncMessage("ZenGlance:RecordLinkClickData", { - clientX: rect.left, - clientY: rect.top, - width: rect.width, - height: rect.height, + clientX: rect.left * zoom, + clientY: rect.top * zoom, + width: rect.width * zoom, + height: rect.height * zoom, }); } @@ -68,29 +84,50 @@ export class ZenGlanceChild extends JSWindowActorChild { */ #getTargetFromEvent(event) { // get closest A element - const target = event.target.closest("A"); - const elementToRecord = event.originalTarget || event.target; + let [href, node, principal] = + lazy.BrowserUtils.hrefAndLinkNodeForClickEvent(event); return { - target, - elementToRecord, + href, + node, + principal, }; } + #checkSecurity(href, principal) { + if ( + lazy.blockJavascript && + Services.io.extractScheme(href) == "javascript" + ) { + // We don't want to open new tabs or windows for javascript: links. + return true; + } + + try { + Services.scriptSecurityManager.checkLoadURIStrWithPrincipal( + principal, + href + ); + } catch (e) { + return true; + } + return false; + } + on_mousedown(event) { - const { target, elementToRecord } = this.#getTargetFromEvent(event); + const { node } = this.#getTargetFromEvent(event); // We record the link data anyway, even if the glance may be invoked // or not. We have some cases where glance would open, for example, // when clicking on a link with a different domain where glance would open. // The problem is that at that stage we don't know the rect or even what // element has been clicked, so we send the data here. - this.#sendClickDataToParent(target, elementToRecord); + this.#sendClickDataToParent(node, event.target); } on_click(event) { - const { target } = this.#getTargetFromEvent(event); + const { node, href, principal } = this.#getTargetFromEvent(event); if ( event.button !== 0 || - !target || + !node || event.defaultPrevented || this.#ensureOnlyKeyModifiers(event) ) { @@ -106,9 +143,12 @@ export class ZenGlanceChild extends JSWindowActorChild { } else if (activationMethod === "meta" && !event.metaKey) { return; } + if (this.#checkSecurity(href, principal)) { + return; + } event.preventDefault(); event.stopPropagation(); - this.#openGlance(target); + this.#openGlance(href, principal); } on_keydown(event) { diff --git a/src/zen/glance/actors/ZenGlanceParent.sys.mjs b/src/zen/glance/actors/ZenGlanceParent.sys.mjs index e5a366867..748c18912 100644 --- a/src/zen/glance/actors/ZenGlanceParent.sys.mjs +++ b/src/zen/glance/actors/ZenGlanceParent.sys.mjs @@ -22,13 +22,15 @@ export class ZenGlanceParent extends JSWindowActorParent { break; } case "ZenGlance:CloseGlance": { - const params = { + // Explicitly allowlist fields from content; never forward + // skipPermitUnload or other privileged flags. + const { noAnimation, setNewID, hasFocused } = message.data ?? {}; + this.browsingContext.topChromeWindow.gZenGlanceManager.closeGlance({ onTabClose: true, - ...message.data, - }; - this.browsingContext.topChromeWindow.gZenGlanceManager.closeGlance( - params - ); + noAnimation: !!noAnimation, + setNewID: typeof setNewID === "string" ? setNewID : null, + hasFocused: !!hasFocused, + }); break; } case "ZenGlance:RecordLinkClickData": { diff --git a/src/zen/glance/tests/GlanceTestUtils.sys.mjs b/src/zen/glance/tests/GlanceTestUtils.sys.mjs index e6d907908..07ff09c99 100644 --- a/src/zen/glance/tests/GlanceTestUtils.sys.mjs +++ b/src/zen/glance/tests/GlanceTestUtils.sys.mjs @@ -12,6 +12,8 @@ export function openGlanceOnTab(window, callback, close = true) { clientY: 0, width: 0, height: 0, + triggeringPrincipal: + Services.scriptSecurityManager.getSystemPrincipal(), }) .then(async glanceTab => { await callback(glanceTab); diff --git a/src/zen/glance/zen-glance.css b/src/zen/glance/zen-glance.css index 02ffbd9bc..0c03d05a6 100644 --- a/src/zen/glance/zen-glance.css +++ b/src/zen/glance/zen-glance.css @@ -9,6 +9,9 @@ display: flex; z-index: 999; + will-change: transform, opacity; + contain: layout style; + top: 15px; padding: 12px; gap: 12px; @@ -80,6 +83,7 @@ & label { max-width: 4rem; margin-left: 8px; + color: white; } & image { @@ -96,12 +100,6 @@ .browserSidebarContainer.zen-glance-background { box-shadow: var(--zen-big-shadow); - - & .browserContainer { - /* For rounding the corners of the content to work while - * applying a transformation to the container. */ - will-change: transform; - } } .browserSidebarContainer.zen-glance-background, @@ -120,14 +118,15 @@ } & .browserContainer { - transform: translate(-50%, -50%); position: fixed; flex: unset !important; width: 80%; height: 100%; &:not([has-finished-animation="true"]) { - will-change: width, height, transform; + will-change: transform; + contain: layout size; + transform-origin: 0 0; #statuspanel { display: none; @@ -170,6 +169,7 @@ position: absolute; pointer-events: none; width: 100%; + height: 100%; max-width: 100%; max-height: 100%; z-index: 0; @@ -177,7 +177,10 @@ top: 50%; left: 50%; translate: -50% -50%; - background: rgba(255, 255, 255, 0.1); + + display: flex; + align-items: center; + justify-content: center; & image { width: 100%; diff --git a/src/zen/images/boost-indicator.svg b/src/zen/images/boost-indicator.svg new file mode 100644 index 000000000..9612fffc7 --- /dev/null +++ b/src/zen/images/boost-indicator.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + diff --git a/src/zen/images/brand-header.svg b/src/zen/images/brand-header.svg deleted file mode 100644 index 21b542840..000000000 --- a/src/zen/images/brand-header.svg +++ /dev/null @@ -1,6 +0,0 @@ - - diff --git a/src/zen/images/favicons/tuta.svg b/src/zen/images/favicons/tuta.svg index fccdcc6bb..8dc5ca458 100644 --- a/src/zen/images/favicons/tuta.svg +++ b/src/zen/images/favicons/tuta.svg @@ -1,7 +1,9 @@ - - + - file, You can obtain one at http://mozilla.org/MPL/2.0/. + --> + diff --git a/src/zen/images/jar.inc.mn b/src/zen/images/jar.inc.mn index 39b41d70c..d5ac5ccd7 100644 --- a/src/zen/images/jar.inc.mn +++ b/src/zen/images/jar.inc.mn @@ -2,7 +2,6 @@ # 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/. - content/browser/zen-images/brand-header.svg (../../zen/images/brand-header.svg) content/browser/zen-images/layouts/collapsed.png (../../zen/images/layouts/collapsed.png) content/browser/zen-images/layouts/multiple-toolbar.png (../../zen/images/layouts/multiple-toolbar.png) content/browser/zen-images/layouts/single-toolbar.png (../../zen/images/layouts/single-toolbar.png) diff --git a/src/zen/images/note-indicator.svg b/src/zen/images/note-indicator.svg index 02ff00771..1c69eae03 100644 --- a/src/zen/images/note-indicator.svg +++ b/src/zen/images/note-indicator.svg @@ -6,13 +6,44 @@ - - - - + + - - - + + - - - + + - - - + + - - - + + - - - + + diff --git a/src/zen/kbs/ZenKeyboardShortcuts.mjs b/src/zen/kbs/ZenKeyboardShortcuts.mjs index bceb9e66b..c7cadbbbc 100644 --- a/src/zen/kbs/ZenKeyboardShortcuts.mjs +++ b/src/zen/kbs/ZenKeyboardShortcuts.mjs @@ -832,7 +832,7 @@ class nsZenKeyboardShortcutsLoader { } class nsZenKeyboardShortcutsVersioner { - static LATEST_KBS_VERSION = 16; + static LATEST_KBS_VERSION = 18; constructor() {} @@ -1196,6 +1196,38 @@ class nsZenKeyboardShortcutsVersioner { } } + if (version < 17) { + // Migrate from version 16 to 17. + // Add shortcut to Duplicate Tab + data.push( + new KeyShortcut( + "zen-duplicate-tab", + "", + "", + "windowAndTabManagement", + nsKeyShortcutModifiers.fromObject({}), + "cmd_zenDuplicateTab", + "zen-duplicate-tab-shortcut" + ) + ); + } + + if (version < 18) { + // Migrate from version 17 to 18. + // Add shortcut to Create New Workspace (unbound by default) + data.push( + new KeyShortcut( + "zen-workspace-create", + "", + "", + ZEN_WORKSPACE_SHORTCUTS_GROUP, + nsKeyShortcutModifiers.fromObject({}), + "cmd_zenOpenWorkspaceCreation", + "zen-workspace-shortcut-create" + ) + ); + } + return data; } } diff --git a/src/zen/live-folders/ZenLiveFolder.sys.mjs b/src/zen/live-folders/ZenLiveFolder.sys.mjs index b0657859b..543f1e4dd 100644 --- a/src/zen/live-folders/ZenLiveFolder.sys.mjs +++ b/src/zen/live-folders/ZenLiveFolder.sys.mjs @@ -123,7 +123,7 @@ export class nsZenLiveFolderProvider { this.manager.saveState(); } - fetch(url, { maxContentLength = 5 * 1024 * 1024 } = {}) { + fetch(url, { maxContentLength = 5 * 1024 * 1024, headers = {} } = {}) { const uri = lazy.NetUtil.newURI(url); // TODO: Support userContextId when fetching, it should be inherited from the folder's // current space context ID. @@ -156,6 +156,10 @@ export class nsZenLiveFolderProvider { triggeringPrincipal: principal, }).QueryInterface(Ci.nsIHttpChannel); + for (const [name, value] of Object.entries(headers)) { + channel.setRequestHeader(name, value, false); + } + let httpStatus = null; let contentType = ""; let headerCharset = null; diff --git a/src/zen/live-folders/providers/GithubLiveFolder.sys.mjs b/src/zen/live-folders/providers/GithubLiveFolder.sys.mjs index 1deeb6f43..b30520da0 100644 --- a/src/zen/live-folders/providers/GithubLiveFolder.sys.mjs +++ b/src/zen/live-folders/providers/GithubLiveFolder.sys.mjs @@ -32,6 +32,30 @@ export class nsGithubLiveFolderProvider extends nsZenLiveFolderProvider { return "zen-live-folder-github-no-filter"; } + if ( + this.state.type === "pull-requests" && + typeof this.state.isJsonApi !== "boolean" && + !Services.prefs.getBoolPref( + "zen.live-folders.github.skip-new-pr-ui-check", + false + ) + ) { + const { text, status } = await this.fetch(this.state.url, { + headers: { + Accept: "application/json,text/html", + }, + }); + if (status === 404) { + return "zen-live-folder-github-no-auth"; + } + try { + JSON.parse(text); + this.state.isJsonApi = true; + } catch { + this.state.isJsonApi = false; + } + } + const queries = this.#buildSearchOptions(); const requests = await Promise.all( queries.map(query => { @@ -56,11 +80,15 @@ export class nsGithubLiveFolderProvider extends nsZenLiveFolderProvider { } if (items) { - items.forEach(item => combinedItems.set(item.id, item)); + for (const item of items) { + combinedItems.set(item.id, item); + } } if (activeRepos) { - activeRepos.forEach(repo => combinedActiveRepos.add(repo)); + for (const repo of activeRepos) { + combinedActiveRepos.add(repo); + } } } @@ -73,39 +101,44 @@ export class nsGithubLiveFolderProvider extends nsZenLiveFolderProvider { } async parsePullRequests(url) { - const { text, status } = await this.fetch(url); + const { text, status } = await this.fetch(url, { + headers: { + Accept: "application/json,text/html", + }, + }); + if (status !== 200) { + return { status }; + } + + let parsedJson = null; try { - const document = new DOMParser().parseFromString(text, "text/html"); - const issues = document.querySelectorAll("div[id^=issue_]"); + parsedJson = JSON.parse(text); + this.state.isJsonApi = true; + } catch { + if (this.state.isJsonApi) { + this.state.isJsonApi = false; + // throw to indicate user to re-try (Url may contain invalid params for non-json /pulls) + throw new Error("Unexpected content type"); + } + } + + if (parsedJson) { + const results = + parsedJson.payload.pullsDashboardSurfaceContentRoute.results; + const items = []; - const activeRepos = []; + const activeRepos = new Set(); - if (issues.length) { - const authors = document.querySelectorAll(".opened-by a"); - const titles = document.querySelectorAll("a[id^=issue_]"); - - for (let i = 0; i < issues.length; i++) { - const author = authors[i].textContent; - const title = titles[i].textContent; - - const repo = titles[i].previousElementSibling.textContent.trim(); - if (repo) { - activeRepos.push(repo); - } - - const idMatch = authors[i].parentElement.textContent - .match(/#[0-9]+/) - .shift(); - - items.push({ - title, - subtitle: author, - icon: "chrome://browser/content/zen-images/favicons/github.svg", - url: new URL(titles[i].href, this.state.url), - id: `${repo}${idMatch}`, - }); - } + for (const pr of results) { + activeRepos.add(pr.repoNameWithOwner); + items.push({ + id: `${pr.repoNameWithOwner}#${pr.number}`, + title: pr.title, + subtitle: pr.author.displayLogin, + icon: "chrome://browser/content/zen-images/favicons/github.svg", + url: pr.permalink, + }); } return { @@ -114,78 +147,109 @@ export class nsGithubLiveFolderProvider extends nsZenLiveFolderProvider { items, activeRepos, }; - } catch (err) { - console.error("Failed to parse Github pull requests", err); - return { - status, - }; } + const document = new DOMParser().parseFromString(text, "text/html"); + const issues = document.querySelectorAll("div[id^=issue_]"); + + const items = []; + const activeRepos = new Set(); + + if (issues.length) { + const authors = document.querySelectorAll(".opened-by a"); + const titles = document.querySelectorAll("a[id^=issue_]"); + + for (let i = 0; i < issues.length; i++) { + const author = authors[i].textContent; + const title = titles[i].textContent; + + const repo = titles[i].previousElementSibling.textContent.trim(); + if (repo) { + activeRepos.add(repo); + } + + const idMatch = authors[i].parentElement.textContent + .match(/#[0-9]+/) + .shift(); + + items.push({ + title, + subtitle: author, + icon: "chrome://browser/content/zen-images/favicons/github.svg", + url: new URL(titles[i].href, this.state.url), + id: `${repo}${idMatch}`, + }); + } + } + + return { + status, + + items, + activeRepos, + }; } async parseIssues(url) { const { text, status } = await this.fetch(url); - try { - const document = new DOMParser().parseFromString(text, "text/html"); - const issues = document.querySelectorAll( - "div[class^=IssueItem-module__defaultRepoContainer]" - ); - const items = []; - const activeRepos = []; - - if (issues.length) { - const authors = document.querySelectorAll( - "a[class^=IssueItem-module__authorCreatedLink]" - ); - const titles = document.querySelectorAll( - "div[class^=Title-module__container]" - ); - const links = document.querySelectorAll( - '[data-testid="issue-pr-title-link"]' - ); - - for (let i = 0; i < issues.length; i++) { - const [rawRepo, rawNumber] = issues[i].childNodes; - const author = authors[i]?.textContent; - const title = titles[i]?.textContent; - const issueUrl = links[i]?.href; - - const repo = rawRepo.textContent?.trim(); - if (repo) { - activeRepos.push(repo); - } - - const numberMatch = rawNumber?.textContent?.match(/[0-9]+/); - const number = numberMatch?.[0] ?? ""; - - items.push({ - title, - subtitle: author, - icon: "chrome://browser/content/zen-images/favicons/github.svg", - url: "https://github.com" + issueUrl, - id: `${repo}#${number}`, - }); - } - } - - return { - status, - - items, - activeRepos, - }; - } catch (err) { - console.error("Failed to parse Github Issues", err); - return { - status, - }; + if (status !== 200) { + return { status }; } + + const document = new DOMParser().parseFromString(text, "text/html"); + const issues = document.querySelectorAll( + "div[class^=IssueItem-module__defaultRepoContainer]" + ); + const items = []; + const activeRepos = new Set(); + + if (issues.length) { + const authors = document.querySelectorAll( + "a[class^=IssueItem-module__authorCreatedLink]" + ); + const titles = document.querySelectorAll( + "div[class^=Title-module__container]" + ); + const links = document.querySelectorAll( + '[data-testid="issue-pr-title-link"]' + ); + + for (let i = 0; i < issues.length; i++) { + const [rawRepo, rawNumber] = issues[i].childNodes; + const author = authors[i]?.textContent; + const title = titles[i]?.textContent; + const issueUrl = links[i]?.href; + + const repo = rawRepo.textContent?.trim(); + if (repo) { + activeRepos.add(repo); + } + + const numberMatch = rawNumber?.textContent?.match(/[0-9]+/); + const number = numberMatch?.[0] ?? ""; + + items.push({ + title, + subtitle: author, + icon: "chrome://browser/content/zen-images/favicons/github.svg", + url: "https://github.com" + issueUrl, + id: `${repo}#${number}`, + }); + } + } + + return { + status, + + items, + activeRepos, + }; } #buildSearchOptions() { const baseQuery = [ this.state.type === "pull-requests" ? "is:pr" : "is:issue", - "state:open", + "is:open", "sort:updated-desc", ]; @@ -219,7 +283,7 @@ export class nsGithubLiveFolderProvider extends nsZenLiveFolderProvider { } const searchParams = []; - if (this.state.type === "pull-requests") { + if (this.state.type === "pull-requests" && !this.state.isJsonApi) { for (const query of queries) { searchParams.push(`${baseQuery.join(" ")} ${query}`); } @@ -227,8 +291,8 @@ export class nsGithubLiveFolderProvider extends nsZenLiveFolderProvider { return searchParams; } - // type: issues - return [`${baseQuery.join(" ")} ${queries.join(" OR ")}`]; + // type: issues or pull requests json api + return [`${baseQuery.join(" ")} (${queries.join(" OR ")})`]; } get options() { diff --git a/src/zen/live-folders/providers/RssLiveFolder.sys.mjs b/src/zen/live-folders/providers/RssLiveFolder.sys.mjs index eff6bed3c..ab99fa4ad 100644 --- a/src/zen/live-folders/providers/RssLiveFolder.sys.mjs +++ b/src/zen/live-folders/providers/RssLiveFolder.sys.mjs @@ -62,6 +62,14 @@ export class nsRssLiveFolderProvider extends nsZenLiveFolderProvider { if (!item.url || !item.date) { return false; } + try { + const parsed = Services.io.newURI(item.url); + if (parsed.scheme !== "http" && parsed.scheme !== "https") { + return false; + } + } catch { + return false; + } if (!this.state.timeRange) { return true; } @@ -73,10 +81,9 @@ export class nsRssLiveFolderProvider extends nsZenLiveFolderProvider { for (let item of items) { if (item.url) { try { - const url = new URL(item.url); - const favicon = await lazy.PlacesUtils.favicons.getFaviconForPage( - Services.io.newURI(url.href) - ); + const url = Services.io.newURI(item.url); + const favicon = + await lazy.PlacesUtils.favicons.getFaviconForPage(url); item.icon = favicon?.dataURI.spec || this.manager.window.gZenEmojiPicker.getSVGURL("logo-rss.svg"); diff --git a/src/zen/media/zen-media-controls.css b/src/zen/media/zen-media-controls.css index 8b23e50ee..9cabf107e 100644 --- a/src/zen/media/zen-media-controls.css +++ b/src/zen/media/zen-media-controls.css @@ -117,16 +117,16 @@ & #zen-media-focus-button::after { content: ""; position: absolute; - width: 110%; - height: 110%; background: url("chrome://browser/content/zen-images/note-indicator.svg") no-repeat; top: -70%; - left: 50%; - transform: translateX(-50%); + left: 0; + right: 0; + bottom: 0; z-index: 0; pointer-events: none; transition: opacity 0.8s ease; opacity: 1; + will-change: opacity; } &:is(:not(.playing:not([muted])), :hover) #zen-media-focus-button::after { diff --git a/src/zen/mods/ZenMods.mjs b/src/zen/mods/ZenMods.mjs index a050c92d3..0fa521b1d 100644 --- a/src/zen/mods/ZenMods.mjs +++ b/src/zen/mods/ZenMods.mjs @@ -7,6 +7,10 @@ import { nsZenMultiWindowFeature, } from "chrome://browser/content/zen-components/ZenCommonUtils.mjs"; +const DOT_RE = /\./g; +const WHITESPACE_RE = /\s/g; +const NON_NAME_RE = /[^A-Za-z_-]+/g; + /** * Zen Mods Manager, handles downloading, updating and applying Zen Mods. * @@ -171,47 +175,57 @@ class nsZenMods extends nsZenPreloadedFeature { } #writeToDom(modsWithPreferences) { - for (const browser of nsZenMultiWindowFeature.browsers) { + // Precompute per-mod data once; values are global prefs and sanitized + // names don't vary per browser window, so this hoists O(windows) work. + const prepared = modsWithPreferences.map( // eslint-disable-next-line no-shadow - for (const { enabled, preferences, name } of modsWithPreferences) { - const sanitizedName = this.sanitizeModName(name); + ({ enabled, preferences, name }) => ({ + enabled, + sanitizedName: this.sanitizeModName(name), + prefs: preferences.map(({ property, type }) => ({ + property, + type, + sanitizedProperty: property?.replaceAll(DOT_RE, "-"), + value: + enabled === undefined || enabled + ? Services.prefs.getStringPref(property, "") + : "", + })), + }) + ); + for (const browser of nsZenMultiWindowFeature.browsers) { + const doc = browser.document; + const root = doc.documentElement; + + for (const { enabled, sanitizedName, prefs } of prepared) { if (enabled !== undefined && !enabled) { - const element = browser.document.getElementById(sanitizedName); - + const element = doc.getElementById(sanitizedName); if (element) { element.remove(); } - for (const { property } of preferences.filter( - ({ type }) => type !== "checkbox" - )) { - const sanitizedProperty = property?.replaceAll(/\./g, "-"); - - browser.document - .querySelector(":root") - .style.removeProperty(`--${sanitizedProperty}`); + for (const { type, sanitizedProperty } of prefs) { + if (type === "checkbox") { + continue; + } + root.style.removeProperty(`--${sanitizedProperty}`); } continue; } - for (const { property, type } of preferences) { - const value = Services.prefs.getStringPref(property, ""); - const sanitizedProperty = property?.replaceAll(/\./g, "-"); - + for (const { type, sanitizedProperty, value } of prefs) { switch (type) { case "dropdown": { if (value !== "") { - let element = browser.document.getElementById(sanitizedName); + let element = doc.getElementById(sanitizedName); if (!element) { - element = browser.document.createElement("div"); - + element = doc.createElement("div"); element.style.display = "none"; element.setAttribute("id", sanitizedName); - - browser.document.body.appendChild(element); + doc.body.appendChild(element); } element.setAttribute(sanitizedProperty, value); @@ -221,13 +235,9 @@ class nsZenMods extends nsZenPreloadedFeature { case "string": { if (value === "") { - browser.document - .querySelector(":root") - .style.removeProperty(`--${sanitizedProperty}`); + root.style.removeProperty(`--${sanitizedProperty}`); } else { - browser.document - .querySelector(":root") - .style.setProperty(`--${sanitizedProperty}`, value); + root.style.setProperty(`--${sanitizedProperty}`, value); } break; } @@ -308,6 +318,19 @@ class nsZenMods extends nsZenPreloadedFeature { } async #downloadUrlToFile(url, path, maxRetries = 3, retryDelayMs = 500) { + // Mod assets must come over HTTPS. Without a signing/hash scheme this + // is the minimum guard against MITM or a store-hosted HTTP redirect + // serving attacker-controlled CSS/JSON into the chrome profile. + let parsed; + try { + parsed = new URL(url); + } catch { + throw new Error(`[ZenMods]: Invalid mod asset URL: ${url}`); + } + if (parsed.protocol !== "https:") { + throw new Error(`[ZenMods]: Refusing non-HTTPS mod asset URL: ${url}`); + } + let attempt = 0; while (attempt < maxRetries) { @@ -377,8 +400,7 @@ class nsZenMods extends nsZenPreloadedFeature { sanitizeModName(aName) { // Do not change to "mod-" for backwards compatibility - // eslint-disable-next-line no-shadow - return `theme-${aName?.replaceAll(/\s/g, "-")?.replaceAll(/[^A-Za-z_-]+/g, "")}`; + return `theme-${aName?.replaceAll(WHITESPACE_RE, "-")?.replaceAll(NON_NAME_RE, "")}`; } get updatePref() { diff --git a/src/zen/mods/nsZenModsBackend.cpp b/src/zen/mods/nsZenModsBackend.cpp index 635bc3331..fc5884c55 100644 --- a/src/zen/mods/nsZenModsBackend.cpp +++ b/src/zen/mods/nsZenModsBackend.cpp @@ -32,10 +32,9 @@ nsZenModsBackend::nsZenModsBackend() { (void)CheckEnabled(); } auto nsZenModsBackend::CheckEnabled() -> void { // Check if the mods backend is enabled based on the preference. - nsCOMPtr appInfo = - do_GetService("@mozilla.org/xre/app-info;1"); bool inSafeMode = false; - if (appInfo) { + if (nsCOMPtr appInfo = + do_GetService("@mozilla.org/xre/app-info;1")) { appInfo->GetInSafeMode(&inSafeMode); } mEnabled = !inSafeMode && diff --git a/src/zen/moz.build b/src/zen/moz.build index d312edac1..18daa2bc5 100644 --- a/src/zen/moz.build +++ b/src/zen/moz.build @@ -7,6 +7,7 @@ EXTRA_PP_COMPONENTS += [ ] DIRS += [ + "boosts", "common", "drag-and-drop", "glance", diff --git a/src/zen/sessionstore/ZenSessionManager.sys.mjs b/src/zen/sessionstore/ZenSessionManager.sys.mjs index ac23ac37c..39f2d0eb2 100644 --- a/src/zen/sessionstore/ZenSessionManager.sys.mjs +++ b/src/zen/sessionstore/ZenSessionManager.sys.mjs @@ -64,6 +64,10 @@ class nsZenSidebarObject { return Cu.cloneInto(this.#sidebar, {}); } + get dataWithoutCloning() { + return this.#sidebar; + } + set data(data) { if (typeof data !== "object") { throw new Error("Sidebar data must be an object"); @@ -100,6 +104,10 @@ export class nsZenSessionManager { path: this.#storeFilePath, compression: "lz4", backupTo, + useSizeHints: Services.prefs.getBoolPref( + "zen.session-store.use-size-hints", + true + ), }); this.log("Session file path:", this.#file.path); this.#deferredBackupTask = new lazy.DeferredTask(async () => { @@ -282,10 +290,13 @@ export class nsZenSessionManager { console.error("ZenSessionManager: Failed to read session file", e); } this.#sidebar = this._dataFromFile || {}; - if (!this.#sidebar.spaces?.length && !this._shouldRunMigration) { + if ( + !this.#sidebarWithoutCloning.spaces?.length && + !this._shouldRunMigration + ) { this.log( "No spaces data found in session file, running migration", - this.#sidebar + this.#sidebarWithoutCloning ); // If we have no spaces data, we should run migration // to restore them from the database. Note we also do a @@ -296,7 +307,7 @@ export class nsZenSessionManager { if ( Services.prefs.getBoolPref("zen.session-store.log-tab-entries", false) ) { - for (const tab of this.#sidebar.tabs || []) { + for (const tab of this.#sidebarWithoutCloning.tabs || []) { this.log("Tab entry in session file:", tab); } } @@ -349,6 +360,11 @@ export class nsZenSessionManager { if (this._shouldRunMigration) { initialState = this.#runStateMigration(initialState); } + // Clear the memory of the groups saved in the session file, + // as we don't really need them anyways. + if (initialState?.savedGroups) { + initialState.savedGroups = []; + } if (!lazy.gWindowSyncEnabled) { if (initialState?.windows?.length && this.#shouldRestoreOnlyPinned) { this.log("Window sync disabled, restoring only pinned tabs"); @@ -412,10 +428,10 @@ export class nsZenSessionManager { if ( this.#shouldRestoreOnlyPinned && !this.#shouldRestoreFromCrash && - this.#sidebar?.tabs + this.#sidebarWithoutCloning?.tabs ) { this.log("Restoring only pinned tabs into windows"); - const sidebar = this.#sidebar; + const sidebar = this.#sidebarWithoutCloning; sidebar.tabs = (sidebar.tabs || []).filter(tab => tab.pinned); this.#sidebar = sidebar; } @@ -449,6 +465,10 @@ export class nsZenSessionManager { return this.#sidebarObject.data; } + get #sidebarWithoutCloning() { + return this.#sidebarObject.dataWithoutCloning; + } + set #sidebar(data) { this.#sidebarObject.data = data; } @@ -475,7 +495,7 @@ export class nsZenSessionManager { delete this._migrationData?.recoveryData; // Restore spaces into the sidebar object if we don't // have any yet. - if (!this.#sidebar.spaces?.length) { + if (!this.#sidebarWithoutCloning.spaces?.length) { this.#sidebar = { ...this.#sidebar, spaces: this._migrationData?.spaces || [], @@ -590,7 +610,7 @@ export class nsZenSessionManager { ); this.#collectWindowData(windows); // This would save the data to disk asynchronously or when quitting the app. - let sidebar = this.#sidebar; + let sidebar = this.#sidebarWithoutCloning; this.#file.data = sidebar; if (soon) { this.#file.saveSoon(); @@ -700,10 +720,7 @@ export class nsZenSessionManager { // We only want to collect the sidebar data once from // a single window, as all windows share the same // sidebar data. - let sidebarData = this.#sidebar; - if (!sidebarData) { - sidebarData = {}; - } + let sidebarData = {}; sidebarData.lastCollected = Date.now(); this.#collectTabsData(sidebarData, aStateWindows); @@ -883,6 +900,7 @@ export class nsZenSessionManager { const newState = { windows: [newWindow] }; this.log(`Cloning window with ${newWindow.tabs.length} tabs`); + aWindow.__isNewZenWindow = true; SessionStoreInternal._deferredInitialState = newState; SessionStoreInternal.initializeWindow(aWindow, newState); } @@ -897,7 +915,7 @@ export class nsZenSessionManager { onNewEmptySession(aWindow) { this.log("Restoring empty session with Zen session data"); aWindow.gZenWorkspaces.restoreWorkspacesFromSessionStore({ - spaces: this.#sidebar.spaces || [], + spaces: this.#sidebarWithoutCloning.spaces || [], }); } @@ -909,7 +927,7 @@ export class nsZenSessionManager { * @returns {Array} The cloned spaces data. */ getClonedSpaces() { - const sidebar = this.#sidebar; + const sidebar = this.#sidebarWithoutCloning; if (!sidebar || !sidebar.spaces) { return []; } diff --git a/src/zen/sessionstore/ZenWindowSync.sys.mjs b/src/zen/sessionstore/ZenWindowSync.sys.mjs index 95b6c8e70..a8b0f69a1 100644 --- a/src/zen/sessionstore/ZenWindowSync.sys.mjs +++ b/src/zen/sessionstore/ZenWindowSync.sys.mjs @@ -97,11 +97,11 @@ class nsZenWindowSync { }; /** - * Promise that resolves when the current docshell swap operation is finished. + * Promise|null that resolves when the current docshell swap operation is finished. * Used to avoid multiple simultaneous swap operations that could interfere with each other. * For example, when focusing a window AND selecting a tab at the same time. */ - #docShellSwitchPromise = Promise.resolve(); + #docShellSwitchPromise = null; /** * Map of sync handlers for different event types. @@ -297,6 +297,18 @@ class nsZenWindowSync { if (tab.pinned && !tab._zenPinnedInitialState) { await this.setPinnedTabState(tab); } + // Lets clear extra values to save some memory, we only really + // care about the URL and title for the initial state, and we want + // to avoid keeping the whole session history around. + if (tab._zenPinnedInitialState) { + tab._zenPinnedInitialState = { + ...tab._zenPinnedInitialState, + entry: { + url: tab._zenPinnedInitialState.entry.url, + title: tab._zenPinnedInitialState.entry.title, + }, + }; + } if ( !lazy.gWindowSyncEnabled || (lazy.gSyncOnlyPinnedTabs && !tab.pinned) @@ -731,7 +743,14 @@ class nsZenWindowSync { return; } await this.#styleSwapedBrowsers(aOurTab, aOtherTab, () => { - this.#swapBrowserDocShellsInner(aOurTab, aOtherTab); + try { + this.#swapBrowserDocShellsInner(aOurTab, aOtherTab); + } catch (e) { + console.error( + `Error swapping browsers for tabs ${aOurTab.id} and ${aOtherTab.id}:`, + e + ); + } }); } @@ -790,7 +809,20 @@ class nsZenWindowSync { // We *shouldn't* care about this scenario since the remoteness should be // the same anyways. if (!aOurTab.linkedBrowser || !aOtherTab.linkedBrowser) { - return true; + this.log( + `Cannot swap browsers between tabs ${aOurTab.id} and ${aOtherTab.id} because one of them doesn't have a linked browser` + ); + return false; + } + // Theoretical case where we are trying to swap two tabs in the same window. + // There has been some reports of this happening in the wild, and while it shouldn't + // cause any critical issues, it can cause some weird states and we should avoid it. + // For example, see gh-13149 + if (aOtherTab.ownerGlobal === aOurTab.ownerGlobal) { + this.log( + `Cannot swap browsers between tabs ${aOurTab.id} and ${aOtherTab.id} because they are in the same window` + ); + return false; } // Can't swap between chrome and content processes. if ( @@ -1053,10 +1085,17 @@ class nsZenWindowSync { continue; } delete tab._zenContentsVisible; - this.#swapBrowserDocShellsInner(targetTab, tab, { - focus: targetTab.selected, - onClose: true, - }); + try { + this.#swapBrowserDocShellsInner(targetTab, tab, { + focus: targetTab.selected, + onClose: true, + }); + } catch (e) { + console.error( + `Error swapping browsers for tabs ${tab.id} and ${targetTab.id} during close:`, + e + ); + } this.#swapedTabsEntriesForWC.set( tab.linkedBrowser.permanentKey, targetTab @@ -1077,9 +1116,9 @@ class nsZenWindowSync { */ async #onTabSwitchOrWindowFocus(aWindow, aPreviousTab = null) { let activeBrowsers = aWindow.gBrowser.selectedBrowsers; - let activeTabs = activeBrowsers.map(browser => - aWindow.gBrowser.getTabForBrowser(browser) - ); + let activeTabs = activeBrowsers + .map(browser => aWindow.gBrowser.getTabForBrowser(browser)) + .filter(tab => tab); // Ignore previous tabs that are still "active". These scenarios could happen for example, // when selecting on a split view tab that was already active. if ( @@ -1188,8 +1227,12 @@ class nsZenWindowSync { activeIndex--; activeIndex = Math.min(activeIndex, entries.length - 1); activeIndex = Math.max(activeIndex, 0); + let entryToUse = (entries[activeIndex] || entries[0]) ?? null; const initialState = { - entry: (entries[activeIndex] || entries[0]) ?? null, + entry: { + url: entryToUse?.url, + title: entryToUse?.title, + }, image, }; this.#runOnAllWindows(null, win => { @@ -1310,6 +1353,9 @@ class nsZenWindowSync { _forZenEmptyTab: tab.hasAttribute("zen-empty-tab"), }); newTab.id = tab.id; + if (!tab.hasAttribute("pending")) { + newTab.removeAttribute("pending"); + } this.#syncItemWithOriginal( tab, newTab, @@ -1439,18 +1485,29 @@ class nsZenWindowSync { ) { return; } - let promise = this.#docShellSwitchPromise; + if (this.#docShellSwitchPromise) { + return; + } + const onTabSelect = event => { + if (event.detail?.previousTab === event.target) { + return; + } + this.#lastSelectedTab = null; + this.on_TabSelect(event, { ignorePromise: true }); + }; this.#lastFocusedWindow = new WeakRef(window); this.#lastSelectedTab = new WeakRef(window.gBrowser.selectedTab); + window.addEventListener("TabSelect", onTabSelect, { once: true }); // eslint-disable-next-line no-async-promise-executor this.#docShellSwitchPromise = new Promise(async resolve => { - await promise; await this.#onTabSwitchOrWindowFocus(window); + window.removeEventListener("TabSelect", onTabSelect); resolve(); + this.#docShellSwitchPromise = null; }); } - on_TabSelect(aEvent) { + on_TabSelect(aEvent, { ignorePromise = false } = {}) { const tab = aEvent.target; if (this.#lastSelectedTab?.deref() === tab) { return; @@ -1458,11 +1515,15 @@ class nsZenWindowSync { this.#lastSelectedTab = new WeakRef(tab); const previousTab = aEvent.detail.previousTab; let promise = this.#docShellSwitchPromise; + if (promise && !ignorePromise) { + return; + } // eslint-disable-next-line no-async-promise-executor this.#docShellSwitchPromise = new Promise(async resolve => { await promise; await this.#onTabSwitchOrWindowFocus(tab.ownerGlobal, previousTab); resolve(); + this.#docShellSwitchPromise = null; }); } diff --git a/src/zen/spaces/ZenGradientGenerator.mjs b/src/zen/spaces/ZenGradientGenerator.mjs index e6d926bc7..b5b7bd289 100644 --- a/src/zen/spaces/ZenGradientGenerator.mjs +++ b/src/zen/spaces/ZenGradientGenerator.mjs @@ -1258,10 +1258,10 @@ export class nsZenThemePicker extends nsZenMultiWindowFeature { let colorToBlendOpacity; if (this.isMica) { colorToBlend = this.isDarkMode ? [0, 0, 0] : [255, 255, 255]; - colorToBlendOpacity = 0.25; + colorToBlendOpacity = 0.12; } else if (AppConstants.platform === "macosx") { colorToBlend = [255, 255, 255]; - colorToBlendOpacity = 0.35; + colorToBlendOpacity = 0.18; } if (colorToBlend) { const blendedAlpha = Math.min( @@ -1470,10 +1470,19 @@ export class nsZenThemePicker extends nsZenMultiWindowFeature { * Get the primary color from a list of colors. * * @param {Array} accentColor The accent color as an array of RGB values. + * @param {boolean} isDarkMode Whether the current theme is in dark mode. * @returns {string} The primary color in hex format. */ - getAccentColorForUI(accentColor) { - return `rgb(${accentColor[0]}, ${accentColor[1]}, ${accentColor[2]})`; + getAccentColorForUI(accentColor, isDarkMode) { + const [h, s, l] = this.rgbToHsl(...accentColor); + if (isDarkMode) { + return `rgb(${accentColor[0]}, ${accentColor[1]}, ${accentColor[2]})`; + } + const saturation = Math.min(1, s + 0.3); + const targetLightness = this.isDarkMode ? 0.62 : 0.42; + const lightness = l * 0.4 + targetLightness * 0.6; + const [r, g, b] = this.hslToRgb(h / 360, saturation, lightness); + return `rgb(${r}, ${g}, ${b})`; } getMostDominantColor(allColors) { @@ -1485,9 +1494,13 @@ export class nsZenThemePicker extends nsZenMultiWindowFeature { return color; } - getToolbarColor(isDarkMode = false) { + getToolbarColor(isDarkMode = false, accentColor = null) { const opacity = 0.8; - return isDarkMode ? [255, 255, 255, opacity] : [0, 0, 0, opacity]; // Default toolbar + let baseColor = isDarkMode ? [255, 255, 255, opacity] : [0, 0, 0, opacity]; // Default toolbar + if (accentColor) { + return this.blendColors(baseColor.slice(0, 3), accentColor, 75).concat(1); + } + return baseColor; } get browserBackgroundElement() { @@ -1723,9 +1736,6 @@ export class nsZenThemePicker extends nsZenMultiWindowFeature { docElement.removeAttribute("zen-default-theme"); } if (dominantColor) { - const primaryColor = this.getAccentColorForUI(dominantColor); - docElement.style.setProperty("--zen-primary-color", primaryColor); - // Should be set to `this.isLegacyVersion` but for some reason it is set to undefined if we open a private window, // so instead get the pref value directly. browser.gZenThemePicker.isLegacyVersion = @@ -1747,12 +1757,23 @@ export class nsZenThemePicker extends nsZenMultiWindowFeature { ); } } + + const primaryColor = this.getAccentColorForUI( + dominantColor, + isDarkMode + ); + docElement.style.setProperty("--zen-primary-color", primaryColor); + // Set `--toolbox-textcolor` to have a contrast with the primary color - const textColor = this.getToolbarColor(isDarkMode); + let textColor = this.getToolbarColor(isDarkMode, dominantColor); docElement.style.setProperty( "--toolbox-textcolor", `rgba(${textColor[0]}, ${textColor[1]}, ${textColor[2]}, ${textColor[3]})` ); + docElement.style.setProperty( + "--toolbar-color-scheme", + isDarkMode ? "dark" : "light" + ); } if (!skipUpdate) { @@ -1760,6 +1781,15 @@ export class nsZenThemePicker extends nsZenMultiWindowFeature { browser.gZenThemePicker.recalculateDots(workspaceTheme.gradientColors); } }); + + // Notify observers that gradient updated + // note: We just notify if we are not skipping the update, + // because otherwise, it can get pretty laggy if we notify on every change + // when the user is dragging a dot. + // TODO(cheff): We should probably find a better way to handle this + if (!skipUpdate) { + Services.obs.notifyObservers(null, "zen-space-gradient-update"); + } } fixTheme(theme) { @@ -1876,8 +1906,8 @@ export class nsZenThemePicker extends nsZenMultiWindowFeature { ); let currentWorkspace = gZenWorkspaces.getActiveWorkspace(); + currentWorkspace.theme = gradient; if (!skipSave) { - currentWorkspace.theme = gradient; gZenWorkspaces.saveWorkspace(currentWorkspace); } @@ -1981,8 +2011,8 @@ export class nsZenThemePicker extends nsZenMultiWindowFeature { grain: theme.texture ?? 0, isDarkMode, isExplicitMode, - toolbarColor: this.getToolbarColor(isDarkMode), - primaryColor: this.getAccentColorForUI(dominantColor), + toolbarColor: this.getToolbarColor(isDarkMode, dominantColor), + primaryColor: this.getAccentColorForUI(dominantColor, isDarkMode), }; this.currentOpacity = previousOpacity; this.#currentLightness = previousLightness; diff --git a/src/zen/spaces/ZenSpaceBookmarksStorage.js b/src/zen/spaces/ZenSpaceBookmarksStorage.js index 59da45f61..acfd1ec2e 100644 --- a/src/zen/spaces/ZenSpaceBookmarksStorage.js +++ b/src/zen/spaces/ZenSpaceBookmarksStorage.js @@ -49,7 +49,7 @@ window.ZenWorkspaceBookmarksStorage = { timestamp INTEGER NOT NULL, UNIQUE(bookmark_guid), FOREIGN KEY(bookmark_guid) REFERENCES moz_bookmarks(guid) ON DELETE CASCADE - ) + ) `); // Create index for changes tracking diff --git a/src/zen/spaces/ZenSpaceCreation.mjs b/src/zen/spaces/ZenSpaceCreation.mjs index 69b8316cf..26690eabf 100644 --- a/src/zen/spaces/ZenSpaceCreation.mjs +++ b/src/zen/spaces/ZenSpaceCreation.mjs @@ -148,6 +148,28 @@ class nsZenWorkspaceCreation extends MozXULElement { this.createButton.disabled = !this.inputName.value.trim(); }); + this.inputName.addEventListener("keydown", event => { + if (event.key === "Enter") { + event.preventDefault(); + event.stopPropagation(); + if (!this.createButton.disabled) { + this.createButton.doCommand(); + } + } + }); + + // Bound on the root so Esc works regardless of which child has focus + // (name input, icon picker trigger, profile button, primary button). + // Open popups consume Esc before it reaches us, so the emoji/profile + // pickers still close as expected. + this.addEventListener("keydown", event => { + if (event.key === "Escape") { + event.preventDefault(); + event.stopPropagation(); + this.cancelButton.doCommand(); + } + }); + this.inputIcon.addEventListener("command", this.onIconCommand.bind(this)); this.profilesPopup = this.querySelector( @@ -199,25 +221,27 @@ class nsZenWorkspaceCreation extends MozXULElement { this.style.visibility = "visible"; gZenCompactModeManager.getAndApplySidebarWidth(); this.resolveInitialized(); - gZenUIManager.motion - .animate( - this.elementsToAnimate, - { - y: [20, 0], - opacity: [0, 1], - filter: ["blur(2px)", "blur(0)"], - }, - { - duration: 0.6, - type: "spring", - bounce: 0, - delay: gZenUIManager.motion.stagger(0.05, { startDelay: 0.2 }), - } - ) - .then(() => { - this.inputName.focus(); - gZenWorkspaces.workspaceElement(this.workspaceId).hidden = false; - }); + let animation = gZenUIManager.motion.animate( + this.elementsToAnimate, + { + y: [20, 0], + opacity: [0, 1], + filter: ["blur(2px)", "blur(0)"], + }, + { + duration: 0.6, + type: "spring", + bounce: 0, + delay: gZenUIManager.motion.stagger(0.05, { startDelay: 0.2 }), + } + ); + if (gReduceMotion) { + animation.complete(); + } + animation.then(() => { + this.inputName.focus(); + gZenWorkspaces.workspaceElement(this.workspaceId).hidden = false; + }); }); } @@ -303,20 +327,22 @@ class nsZenWorkspaceCreation extends MozXULElement { } async #cleanup() { - await gZenUIManager.motion.animate( - this.elementsToAnimate.reverse(), - { - y: [0, 20], - opacity: [1, 0], - filter: ["blur(0)", "blur(2px)"], - }, - { - duration: 0.4, - type: "spring", - bounce: 0, - delay: gZenUIManager.motion.stagger(0.05), - } - ); + if (!gReduceMotion) { + await gZenUIManager.motion.animate( + this.elementsToAnimate.reverse(), + { + y: [0, 20], + opacity: [1, 0], + filter: ["blur(0)", "blur(2px)"], + }, + { + duration: 0.4, + type: "spring", + bounce: 0, + delay: gZenUIManager.motion.stagger(0.05), + } + ); + } document.getElementById("zen-sidebar-splitter").style.pointerEvents = ""; diff --git a/src/zen/spaces/ZenSpaceManager.mjs b/src/zen/spaces/ZenSpaceManager.mjs index 6b8dd64d1..391a60e1b 100644 --- a/src/zen/spaces/ZenSpaceManager.mjs +++ b/src/zen/spaces/ZenSpaceManager.mjs @@ -44,6 +44,10 @@ class nsZenWorkspaces { _workspaceCache = []; #lastScrollTime = 0; + #currentSpaceSwitchContext = { + promise: null, + animations: [], + }; bookmarkMenus = [ "PlacesToolbar", @@ -72,13 +76,6 @@ class nsZenWorkspaces { } async init() { - // Initialize tab selection state - this._tabSelectionState = { - inProgress: false, - lastSelectionTime: 0, - debounceTime: 100, // ms to wait between tab selections - }; - // Initialize workspace change mutex this._workspaceChangeInProgress = false; @@ -140,8 +137,6 @@ class nsZenWorkspaces { document.documentElement.setAttribute("zen-private-window", "true"); } - this.popupOpenHandler = this._popupOpenHandler.bind(this); - window.addEventListener("resize", this.onWindowResize.bind(this)); this.addPopupListeners(); @@ -214,42 +209,7 @@ class nsZenWorkspaces { return true; } - // Safely select a tab with debouncing to prevent race conditions - async _safelySelectTab(tab) { - if (!tab || tab.closing || !tab.ownerGlobal || tab.ownerGlobal.closed) { - return false; - } - - // Check if we need to debounce - const now = Date.now(); - const timeSinceLastSelection = - now - this._tabSelectionState.lastSelectionTime; - - if (timeSinceLastSelection < this._tabSelectionState.debounceTime) { - await new Promise(resolve => - setTimeout( - resolve, - this._tabSelectionState.debounceTime - timeSinceLastSelection - ) - ); - } - - // Mark selection as in progress - this._tabSelectionState.inProgress = true; - - try { - gBrowser.selectedTab = tab; - this._tabSelectionState.lastSelectionTime = Date.now(); - return true; - } catch (e) { - console.error("Error selecting tab:", e); - return false; - } finally { - this._tabSelectionState.inProgress = false; - } - } - - async selectEmptyTab(newTabTarget = null) { + selectEmptyTab(newTabTarget = null) { // Validate browser state first if (!this._validateBrowserState()) { console.warn("Browser state invalid for empty tab selection"); @@ -269,12 +229,7 @@ class nsZenWorkspaces { !this._emptyTab.ownerGlobal.closed && gZenVerticalTabsManager._canReplaceNewTab ) { - // Safely switch to the empty tab using our debounced method - const success = await this._safelySelectTab(this._emptyTab); - if (!success) { - throw new Error("Failed to select empty tab"); - } - + gBrowser.selectedTab = this._emptyTab; return this._emptyTab; } @@ -642,16 +597,6 @@ class nsZenWorkspaces { ); } - _popupOpenHandler() { - // If a popup is opened, we should stop the swipe gesture - if (this._swipeManager?.isGestureActive) { - document.documentElement.removeAttribute("swipe-gesture"); - gZenUIManager.tabsWrapper.style.removeProperty("scrollbar-width"); - this.updateTabsContainers(); - this._cancelSwipeAnimation(); - } - } - get activeWorkspace() { return this.#activeWorkspace; } @@ -857,7 +802,6 @@ class nsZenWorkspaces { return; } await this.promiseInitialized; - let showed = false; let resolveSelectPromise; let selectPromise = new Promise(resolve => { resolveSelectPromise = resolve; @@ -871,13 +815,13 @@ class nsZenWorkspaces { }; let removedEmptyTab = false; - if ( - this._initialTab && - !(this._initialTab._shouldRemove && this._initialTab._veryPossiblyEmpty) - ) { - gBrowser.selectedTab = this._initialTab; - this.moveTabToWorkspace(this._initialTab, this.activeWorkspace); - gBrowser.moveTabTo(this._initialTab, { + let initialTabWasEmpty = false; + if (this._shouldOverrideTabs) { + let initialTab = this._initialTab || gBrowser.selectedTab; + initialTabWasEmpty = !!initialTab._veryPossiblyEmpty; + gBrowser.selectedTab = initialTab; + this.moveTabToWorkspace(initialTab, this.activeWorkspace); + gBrowser.moveTabTo(initialTab, { forceUngrouped: true, tabIndex: 0, }); @@ -901,33 +845,32 @@ class nsZenWorkspaces { tabs[this._tabToSelect] !== this._tabToRemoveForEmpty ) { this.log(`Found tab to select: ${this._tabToSelect}, ${tabs.length}`); - setTimeout(() => { - let tabToUse = gZenGlanceManager.getTabOrGlanceParent( - tabs[this._tabToSelect + 1] || this._emptyTab - ); - gBrowser.selectedTab = tabToUse; - this._removedByStartupPage = true; - gBrowser.removeTab(this._tabToRemoveForEmpty, { - skipSessionStore: true, - }); - cleanup(); - }, 0); + let tabToUse = gZenGlanceManager.getTabOrGlanceParent( + tabs[this._tabToSelect + 1] || this._emptyTab + ); + gBrowser.selectedTab = tabToUse; + this._removedByStartupPage = true; + gBrowser.removeTab(this._tabToRemoveForEmpty, { + skipSessionStore: true, + }); + cleanup(); } else { - this.selectEmptyTab(); - showed = true; - setTimeout(() => { - this._removedByStartupPage = true; - gBrowser.removeTab(this._tabToRemoveForEmpty, { - skipSessionStore: true, - animate: false, - }); - cleanup(); - }, 0); + if (gBrowser.selectedTab === this._tabToRemoveForEmpty) { + this.log( + "Selecting empty tab because startup page didnt select a valid tab" + ); + this.selectEmptyTab(); + } + this.log("Removing empty tab added by startup page"); + this._removedByStartupPage = true; + gBrowser.removeTab(this._tabToRemoveForEmpty, { + skipSessionStore: true, + animate: false, + }); + cleanup(); } } else { - setTimeout(() => { - cleanup(); - }, 0); + cleanup(); } await selectPromise; @@ -939,18 +882,24 @@ class nsZenWorkspaces { delete this._initialTab; } - showed &&= Services.prefs.getBoolPref("zen.urlbar.open-on-startup", true); + const openOnStartup = Services.prefs.getBoolPref( + "zen.urlbar.open-on-startup", + true + ); + let shownEmptyTab = + gBrowser.selectedTab.hasAttribute("zen-empty-tab") && openOnStartup; + initialTabWasEmpty &&= openOnStartup; // Wait for the next event loop to ensure that the startup focus logic by // firefox has finished doing it's thing. setTimeout(() => { - setTimeout(() => { - if (gZenVerticalTabsManager._canReplaceNewTab && showed) { - BrowserCommands.openTab(); - } else if (!showed) { - gBrowser.selectedBrowser.focus(); - } - }); + if (gZenVerticalTabsManager._canReplaceNewTab && shownEmptyTab) { + BrowserCommands.openTab(); + } else if (shownEmptyTab || initialTabWasEmpty) { + openLocation(); + } else { + gBrowser.selectedBrowser.focus(); + } }); if ( @@ -1538,7 +1487,7 @@ class nsZenWorkspaces { !tab.hasAttribute("pending") ); - await gBrowser.explicitUnloadTabs(tabsToUnload); // TODO: unit test this + await gBrowser.explicitUnloadTabs(tabsToUnload); } moveTabToWorkspace(tab, workspaceID) { @@ -1586,7 +1535,6 @@ class nsZenWorkspaces { } #prepareNewWorkspace(space) { - document.documentElement.setAttribute("zen-workspace-id", space.uuid); let tabCount = 0; for (let tab of gBrowser.tabs) { const isEssential = tab.getAttribute("zen-essential") === "true"; @@ -1627,13 +1575,22 @@ class nsZenWorkspaces { async changeWorkspaceWithID(workspaceID, ...args) { const workspace = this.getWorkspaceFromId(workspaceID); - await this.changeWorkspace(workspace, ...args); + return await this.changeWorkspace(workspace, ...args); } async changeWorkspace(workspace, ...args) { - if (!this.workspaceEnabled || this.#inChangingWorkspace) { - return; + if (!this.workspaceEnabled) { + return workspace; } + this.#currentSpaceSwitchContext.animations.forEach(animation => { + animation.complete(); + }); + await this.#currentSpaceSwitchContext.promise; + let { resolve, promise } = Promise.withResolvers(); + this.#currentSpaceSwitchContext = { + promise, + animations: [], + }; this.#inChangingWorkspace = true; try { this.log("Changing workspace to", workspace?.uuid); @@ -1642,10 +1599,12 @@ class nsZenWorkspaces { console.error("gZenWorkspaces: Error changing workspace", e); } this.#inChangingWorkspace = false; + resolve(); + return workspace; } _cancelSwipeAnimation() { - this._animateTabs(this.getActiveWorkspaceFromCache(), true); + this.#animateTabs(this.getActiveWorkspaceFromCache(), true); } async #performWorkspaceChange( @@ -1923,7 +1882,7 @@ class nsZenWorkspaces { } /* eslint-disable complexity */ - async _animateTabs( + async #animateTabs( newWorkspace, shouldAnimate, tabToSelect = null, @@ -1934,7 +1893,9 @@ class nsZenWorkspaces { } = {} ) { gZenUIManager.tabsWrapper.style.scrollbarWidth = "none"; - const kGlobalAnimationDuration = 0.2; + const kGlobalAnimationDuration = + Services.prefs.getIntPref("zen.workspaces.switch-animation-duration") / + 1000; this._animatingChange = true; const animations = []; const workspaces = this.getWorkspaces(); @@ -2022,26 +1983,41 @@ class nsZenWorkspaces { ); const offset = -(newWorkspaceIndex - elementWorkspaceIndex) * 100; const newTransform = `translateX(${offset}%)`; + // Only animate the workspace that is coming in, to avoid having multiple workspaces + // animating off-screen at the same time which can cause performance issues. With an off + // set of 1 or -1, so we animate the current workspace and the next one. + const goingLeft = newWorkspaceIndex < previousWorkspaceIndex; + const willBeVisible = + (goingLeft && + elementWorkspaceIndex >= newWorkspaceIndex && + elementWorkspaceIndex <= previousWorkspaceIndex) || + (!goingLeft && + elementWorkspaceIndex <= newWorkspaceIndex && + elementWorkspaceIndex >= previousWorkspaceIndex); if (shouldAnimate) { - const existingPaddingTop = element.style.paddingTop; - animations.push( - gZenUIManager.motion.animate( - element, - { - transform: existingTransform - ? [existingTransform, newTransform] - : newTransform, - paddingTop: existingTransform - ? [existingPaddingTop, existingPaddingTop] - : existingPaddingTop, - }, - { - type: "spring", - bounce: 0, - duration: kGlobalAnimationDuration, - } - ) - ); + if (!willBeVisible) { + element.style.transform = newTransform; + } else { + const existingPaddingTop = element.style.paddingTop; + animations.push( + gZenUIManager.motion.animate( + element, + { + transform: existingTransform + ? [existingTransform, newTransform] + : newTransform, + paddingTop: existingTransform + ? [existingPaddingTop, existingPaddingTop] + : existingPaddingTop, + }, + { + type: "spring", + bounce: 0, + duration: kGlobalAnimationDuration, + } + ) + ); + } } element.active = offset === 0; if (offset === 0) { @@ -2253,12 +2229,14 @@ class nsZenWorkspaces { let promiseTimeout = new Promise(resolve => setTimeout(resolve, kGlobalAnimationDuration * 1000 + 50) ); + this.#currentSpaceSwitchContext.animations = animations; // See issue https://github.com/zen-browser/desktop/issues/9334, we need to add // some sort of timeout to the animation promise, just in case it gets stuck. // We are doing a race between the timeout and the animations finishing. await Promise.race([Promise.all(animations), promiseTimeout]).catch( console.error ); + this.#currentSpaceSwitchContext.animations = []; document.documentElement.removeAttribute("animating-background"); if (shouldAnimate) { for (const cloned of clonedEssentials) { @@ -2401,9 +2379,6 @@ class nsZenWorkspaces { tabToSelect, { previousWorkspaceIndex, previousWorkspace } = {} ) { - // Update document state - document.documentElement.setAttribute("zen-workspace-id", workspace.uuid); - // Recalculate new tab observers gBrowser.tabContainer.observe( null, @@ -2420,7 +2395,7 @@ class nsZenWorkspaces { gZenUIManager.tabsWrapper.scrollbarWidth = "none"; this.workspaceIcons.activeIndex = workspace.uuid; - await this._animateTabs( + await this.#animateTabs( workspace, !onInit && !this._animatingChange, tabToSelect, @@ -2444,14 +2419,20 @@ class nsZenWorkspaces { } } - // Reset bookmarks - this.#invalidateBookmarkContainers(); + // Avoid forcing a startup toolbar rebuild when there are no + // workspace-specific bookmark assignments to apply. + const hasWorkspaceBookmarks = !!Object.keys( + this._workspaceBookmarksCache?.bookmarks || {} + ).length; + if (!onInit || hasWorkspaceBookmarks) { + this._invalidateBookmarkContainers(); + } // Update workspace indicator await this.updateWorkspaceIndicator(workspace, this.workspaceIndicator); // Fix ctrl+tab behavior. Note, we dont call it with "await" because we dont want to wait for it - this._fixCtrlTabBehavior(); + this.#fixCtrlTabBehavior(); // Bug: When updating from previous versions, we used to hide the tabs not used in the new workspace // we now need to show them again. @@ -2481,12 +2462,13 @@ class nsZenWorkspaces { ); } - async _fixCtrlTabBehavior() { + // Intentionally keep it as async! + async #fixCtrlTabBehavior() { ctrlTab.uninit(); ctrlTab.readPref(); } - #invalidateBookmarkContainers() { + _invalidateBookmarkContainers() { for (let i = 0, len = this.bookmarkMenus.length; i < len; i++) { const element = document.getElementById(this.bookmarkMenus[i]); if (element && element._placesView) { @@ -2940,8 +2922,7 @@ class nsZenWorkspaces { } let nextWorkspace = workspaces[targetIndex]; - await this.changeWorkspace(nextWorkspace, { whileScrolling }); - return nextWorkspace; + return await this.changeWorkspace(nextWorkspace, { whileScrolling }); } #initializeWorkspaceTabContextMenus() { @@ -2965,9 +2946,8 @@ class nsZenWorkspaces { ? gBrowser.selectedTabs : [TabContextMenu.contextTab]; document.getElementById("tabContextMenu").hidePopup(); - const previousWorkspaceID = - document.documentElement.getAttribute("zen-workspace-id"); for (let tab of tabs) { + const previousWorkspaceID = tab.getAttribute("zen-workspace-id"); this.moveTabToWorkspace(tab, workspaceID); if (this.lastSelectedWorkspaceTabs[previousWorkspaceID] === tab) { // This tab is no longer the last selected tab in the previous workspace because it's being moved to @@ -3257,7 +3237,7 @@ class nsZenWorkspaces { } if (!workspaceToSwitch) { console.error("No workspace found for tab, cannot switch"); - await this._safelySelectTab(tab); + gBrowser.selectedTab = tab; return; } @@ -3272,7 +3252,7 @@ class nsZenWorkspaces { } // Safely switch to the tab using our debounced method - await this._safelySelectTab(tab); + gBrowser.selectedTab = tab; } catch (e) { console.error("Error in switchTabIfNeeded:", e); } diff --git a/src/zen/spaces/ZenSpacesSwipe.mjs b/src/zen/spaces/ZenSpacesSwipe.mjs index dca3359b7..eea00519d 100644 --- a/src/zen/spaces/ZenSpacesSwipe.mjs +++ b/src/zen/spaces/ZenSpacesSwipe.mjs @@ -20,24 +20,22 @@ export class ZenSpacesSwipe { }; constructor() { - const elements = [ - gNavToolbox, - // Event handlers do not work on elements inside shadow DOM - // so we need to attach them directly. - document - .getElementById("tabbrowser-arrowscrollbox") - ?.shadowRoot?.querySelector("scrollbox"), - ]; - - for (const element of elements) { - if (!element) { - continue; - } - this._attachWorkspaceSwipeGestures(element); - } + this.#attachWorkspaceSwipeGestures(gNavToolbox); + this._popupOpenHandler = this._popupOpenHandler.bind(this); } - _attachWorkspaceSwipeGestures(element) { + get #stripWidth() { + return ( + window.windowUtils.getBoundsWithoutFlushing( + document.getElementById("navigator-toolbox") + ).width + + window.windowUtils.getBoundsWithoutFlushing( + document.getElementById("zen-sidebar-splitter") + ).width + ); + } + + #attachWorkspaceSwipeGestures(element) { element.addEventListener( "MozSwipeGestureMayStart", this._handleSwipeMayStart.bind(this), @@ -107,7 +105,7 @@ export class ZenSpacesSwipe { gZenFolders.cancelPopupTimer(); document.documentElement.setAttribute("swipe-gesture", "true"); - document.addEventListener("popupshown", ws.popupOpenHandler, { + document.addEventListener("popupshown", this._popupOpenHandler, { once: true, }); @@ -128,18 +126,16 @@ export class ZenSpacesSwipe { return; } + const stripWidth = this.#stripWidth; + event.preventDefault(); event.stopPropagation(); - const delta = event.delta * 300; - const stripWidth = - window.windowUtils.getBoundsWithoutFlushing( - document.getElementById("navigator-toolbox") - ).width + - window.windowUtils.getBoundsWithoutFlushing( - document.getElementById("zen-sidebar-splitter") - ).width * - 2; + const delta = + event.delta * + Services.prefs.getIntPref( + "zen.workspaces.swipe-actions.delta-multiplier" + ); let translateX = this._swipeState.lastDelta + delta; // Add a force multiplier as we are translating the strip depending on how close to the edge we are let forceMultiplier = Math.min( @@ -153,7 +149,8 @@ export class ZenSpacesSwipe { translateX = this._swipeState.lastDelta; } - if (Math.abs(delta) > 0.8) { + if (Math.abs(delta) > 0.9) { + delete ws._hasAnimatedBackgrounds; this._swipeState.direction = delta > 0 ? "left" : "right"; } @@ -177,6 +174,10 @@ export class ZenSpacesSwipe { const rawDirection = moveForward ? 1 : -1; const direction = ws.naturalScroll ? -1 : 1; await ws.changeWorkspaceShortcut(rawDirection * direction, true); + } + + onSwipeGestureAnimationEnd() { + const ws = gZenWorkspaces; // Reset swipe state this._swipeState = { @@ -184,10 +185,6 @@ export class ZenSpacesSwipe { lastDelta: 0, direction: null, }; - } - - onSwipeGestureAnimationEnd() { - const ws = gZenWorkspaces; Services.prefs.setBoolPref("zen.swipe.is-fast-swipe", false); document.documentElement.removeAttribute("swipe-gesture"); @@ -199,11 +196,15 @@ export class ZenSpacesSwipe { ); delete ws._hasAnimatedBackgrounds; ws.updateTabsContainers(); - document.removeEventListener("popupshown", ws.popupOpenHandler, { + document.removeEventListener("popupshown", this._popupOpenHandler, { once: true, }); } + _popupOpenHandler() { + this.onSwipeGestureAnimationEnd(); + } + get isGestureActive() { return this._swipeState?.isGestureActive; } diff --git a/src/zen/spaces/create-workspace-form.css b/src/zen/spaces/create-workspace-form.css index de51a4737..566e81c93 100644 --- a/src/zen/spaces/create-workspace-form.css +++ b/src/zen/spaces/create-workspace-form.css @@ -138,6 +138,11 @@ zen-workspace-creation { margin-left: auto; min-width: unset !important; border-radius: 6px; + + &:focus-visible { + outline: 2px solid var(--zen-colors-border-contrast); + outline-offset: 2px; + } } } @@ -153,6 +158,11 @@ zen-workspace-creation { &:hover { background-color: light-dark(rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.1)); } + + &:focus-visible { + outline: 2px solid var(--zen-colors-border-contrast); + outline-offset: 2px; + } } & .zen-workspace-creation-buttons { diff --git a/src/zen/spaces/zen-workspaces.css b/src/zen/spaces/zen-workspaces.css index e9ebba75e..3cfda3ccb 100644 --- a/src/zen/spaces/zen-workspaces.css +++ b/src/zen/spaces/zen-workspaces.css @@ -11,7 +11,6 @@ align-items: center; display: flex; font-size: x-small; - margin: 0 3px; padding: 0; position: relative; @@ -40,6 +39,8 @@ align-items: center; position: relative; fill-opacity: 0.6; + -moz-context-properties: fill-opacity, fill; + fill: currentColor; & .zen-workspace-icon { pointer-events: none; @@ -71,6 +72,11 @@ width 0.1s, transform 0.2s; + &[active='true'] .zen-workspace-icon { + fill: var(--zen-sidebar-themed-icon-fill); + fill-opacity: 1; + } + &[active='true'], &:hover, &[dragged='true'] { @@ -210,9 +216,9 @@ height: 16px; justify-content: center; align-items: center; - fill-opacity: 0.6; + fill-opacity: 1; -moz-context-properties: fill-opacity, fill; - fill: var(--toolbox-textcolor); + fill: var(--zen-sidebar-themed-icon-fill); font-size: 16px; padding-top: 2px; color: light-dark(rgba(0, 0, 0, 0.8), rgba(255, 255, 255, 0.8)); @@ -309,12 +315,16 @@ /* mark: workspace element */ zen-workspace { flex-direction: column; - transition: padding-top 0.1s; width: calc(100% + var(--zen-toolbox-padding) * 2); position: absolute; height: 100%; overflow: hidden; color: var(--toolbox-textcolor); + will-change: transform; + + @media not (prefers-reduced-motion: reduce) { + transition: padding-top 0.1s; + } :root:not([zen-sidebar-expanded='true']) & { width: 100%; @@ -336,8 +346,12 @@ zen-workspace { overflow-y: auto; } - :root[swipe-gesture] &::part(scrollbox) { - scrollbar-width: none; + :root[swipe-gesture] &{ + pointer-events: none; + + &::part(scrollbox) { + scrollbar-width: none; + } } &[overflowing] { diff --git a/src/zen/split-view/ZenViewSplitter.mjs b/src/zen/split-view/ZenViewSplitter.mjs index 40d0053f9..783f2646d 100644 --- a/src/zen/split-view/ZenViewSplitter.mjs +++ b/src/zen/split-view/ZenViewSplitter.mjs @@ -117,7 +117,6 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature { ); window.addEventListener("TabSelect", this.onTabSelect.bind(this)); this.initializeContextMenu(); - this.insertIntoContextMenu(); window.addEventListener( "AfterWorkspacesSessionRestore", @@ -220,14 +219,8 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature { } const group = this._data[groupIndex]; const tabIndex = group.tabs.indexOf(tab); - group.tabs.splice(tabIndex, 1); - this.resetTabState(tab, forUnsplit); - if (tab.group && tab.group.hasAttribute("split-view-group")) { - gBrowser.ungroupTab(tab); - this.#dispatchItemEvent("ZenTabRemovedFromSplit", tab); - } - if (group.tabs.length < 2) { + if (group.tabs.length < 3) { // We need to remove all remaining tabs from the group when unsplitting let remainingTabs = [...group.tabs]; // Copy array since we'll modify it if (!dontRebuildGrid) { @@ -237,12 +230,20 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature { } this.removeGroup(groupIndex); if (changeTab) { - gBrowser.selectedTab = remainingTabs[remainingTabs.length - 1]; + gBrowser.selectedTab = remainingTabs[0]; document .getElementById("cmd_zenNewEmptySplit") .removeAttribute("disabled"); } } else { + group.tabs.splice(tabIndex, 1); + + this.resetTabState(tab, forUnsplit); + if (tab.group && tab.group.hasAttribute("split-view-group")) { + gBrowser.ungroupTab(tab); + this.#dispatchItemEvent("ZenTabRemovedFromSplit", tab); + } + const node = this.getSplitNodeFromTab(tab); const toUpdate = this.removeNode(node); this.applyGridLayout(toUpdate); @@ -1161,26 +1162,27 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature { /** * context menu item display update */ - insetUpdateContextMenuItems() { - const contentAreaContextMenu = document.getElementById("tabContextMenu"); - contentAreaContextMenu.addEventListener("popupshowing", () => { - let isExistingSplitView = gBrowser.selectedTabs.some(tab => - tab.group?.hasAttribute("split-view-group") - ); - const splitTabCommand = document.getElementById("context_zenSplitTabs"); - document.l10n.setAttributes(splitTabCommand, "tab-zen-split-tabs", { - tabCount: isExistingSplitView ? -1 : gBrowser.selectedTabs.length, - }); - if (isExistingSplitView) { - splitTabCommand.removeAttribute("hidden"); - return; - } - if (!this.contextCanSplitTabs()) { - splitTabCommand.setAttribute("hidden", "true"); - } else { - splitTabCommand.removeAttribute("hidden"); - } + updateContextMenuItems() { + let contextTab = TabContextMenu.contextTab; + let selectedTabs = contextTab.multiselected + ? gBrowser.selectedTabs + : [contextTab]; + let isExistingSplitView = selectedTabs.every(tab => + tab.group?.hasAttribute("split-view-group") + ); + const splitTabCommand = document.getElementById("context_zenSplitTabs"); + document.l10n.setAttributes(splitTabCommand, "tab-zen-split-tabs", { + tabCount: isExistingSplitView ? -1 : selectedTabs.length, }); + if (isExistingSplitView) { + splitTabCommand.removeAttribute("hidden"); + return; + } + if (!this.contextCanSplitTabs()) { + splitTabCommand.setAttribute("hidden", "true"); + } else { + splitTabCommand.removeAttribute("hidden"); + } } /** @@ -1201,7 +1203,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature { */ initializeContextMenu() { this.insertSplitViewTabContextMenu(); - this.insetUpdateContextMenuItems(); + this.insertIntoContextMenu(); } /** @@ -1235,11 +1237,27 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature { /** * Splits the selected tabs. + * + * @param {Tab|null} otherTabHint - An optional hint for another tab to split with (used for glance tabs). */ - contextSplitTabs() { - const tabs = window.gBrowser.selectedTabs; - // If any is already in a split view, we unsplit them first - if (tabs.some(tab => tab.splitView)) { + contextSplitTabs(otherTabHint = null) { + let tabs; + let currentTab = TabContextMenu.contextTab || gBrowser.selectedTab; + if (currentTab.multiselected) { + tabs = gBrowser.selectedTabs; + } else if (!currentTab.selected && !currentTab.splitView) { + tabs = [ + currentTab, + ...gBrowser.selectedTabs.filter(t => t !== currentTab), + ]; + } else { + tabs = [currentTab]; + } + if (otherTabHint && !tabs.includes(otherTabHint)) { + tabs.push(otherTabHint); + } + // If all are already in a split view, we unsplit them first. + if (tabs.every(tab => tab.splitView)) { for (const tab of tabs) { if (tab.splitView) { this.removeTabFromGroup(tab); @@ -1247,6 +1265,11 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature { } return; } + if (tabs.length < 2) { + gBrowser.selectedTab = tabs[0]; + this.createEmptySplit(); + return; + } this.splitTabs(tabs); } @@ -1256,14 +1279,11 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature { * @returns {boolean} True if the tabs can be split, false otherwise. */ contextCanSplitTabs() { - if ( - window.gBrowser.selectedTabs.length < 2 || - window.gBrowser.selectedTabs.length > this.MAX_TABS - ) { + if (window.gBrowser.selectedTabs.length > this.MAX_TABS) { return false; } for (const tab of window.gBrowser.selectedTabs) { - if (tab.splitView || tab.hasAttribute("zen-empty-tab")) { + if (tab.hasAttribute("zen-empty-tab")) { return false; } } @@ -1314,7 +1334,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature { * @param {Array} tabs * @param {Tab} relativeTab */ - _moveTabsToContainer(tabs, relativeTab) { + #moveTabsToContainer(tabs, relativeTab) { const relativeTabIsPinned = relativeTab.pinned; const relativeTabIsEssential = relativeTab.hasAttribute("zen-essential"); @@ -1331,6 +1351,32 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature { } } + #useTabsToSplit(tabs) { + // If there's ANY pinned tab on the list, we clone the pinned tab + // state to all the tabs + const allArePinned = tabs.every(tab => tab.pinned); + const thereIsOnePinned = tabs.some(tab => tab.pinned); + const thereIsOneEssential = tabs.some(tab => + tab.hasAttribute("zen-essential") + ); + const thereIsOneLiveFolder = tabs.some(tab => + tab.hasAttribute("zen-live-folder-item-id") + ); + + if ( + thereIsOneEssential || + (thereIsOnePinned && !allArePinned) || + thereIsOneLiveFolder + ) { + for (let i = 0; i < tabs.length; i++) { + const tab = tabs[i]; + if (tab.pinned) { + tabs[i] = gBrowser.duplicateTab(tab, true); + } + } + } + } + /** * Splits the given tabs. * @@ -1355,17 +1401,20 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature { let shouldActivateSplit = (initialIndex >= 0 || tabs.includes(window.gBrowser.selectedTab)) && !this._sessionRestoring; + if (existingSplitTab) { - this._moveTabsToContainer(tabs, tabs[tabIndexToUse]); const groupIndex = this._data.findIndex(group => group.tabs.includes(existingSplitTab) ); const group = this._data[groupIndex]; - const gridTypeChange = gridType && group.gridType !== gridType; - const newTabsAdded = tabs.find(t => !group.tabs.includes(t)); if (group.tabs.length >= this.MAX_TABS) { + gZenUIManager.showToast("zen-split-view-limit-toast"); return; } + this.#useTabsToSplit(tabs); + this.#moveTabsToContainer(tabs, tabs[tabIndexToUse]); + const gridTypeChange = gridType && group.gridType !== gridType; + const newTabsAdded = tabs.find(t => !group.tabs.includes(t)); if (gridTypeChange && !newTabsAdded) { // reset layout group.gridType = gridType; @@ -1397,30 +1446,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature { return group; } - // We are here if none of the tabs have been previously split - // If there's ANY pinned tab on the list, we clone the pinned tab - // state to all the tabs - const allArePinned = tabs.every(tab => tab.pinned); - const thereIsOnePinned = tabs.some(tab => tab.pinned); - const thereIsOneEssential = tabs.some(tab => - tab.hasAttribute("zen-essential") - ); - const thereIsOneLiveFolder = tabs.some(tab => - tab.hasAttribute("zen-live-folder-item-id") - ); - - if ( - thereIsOneEssential || - (thereIsOnePinned && !allArePinned) || - thereIsOneLiveFolder - ) { - for (let i = 0; i < tabs.length; i++) { - const tab = tabs[i]; - if (tab.pinned) { - tabs[i] = gBrowser.duplicateTab(tab, true); - } - } - } + this.#useTabsToSplit(tabs); gridType ??= "grid"; @@ -2163,7 +2189,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature { splitGroup && (!draggedTab.group || draggedTab.group !== splitGroup) ) { - this._moveTabsToContainer([draggedTab], droppedOnTab); + this.#moveTabsToContainer([draggedTab], droppedOnTab); gBrowser.moveTabToExistingGroup(draggedTab, splitGroup); if (hoverSide === "left" || hoverSide === "top") { try { diff --git a/src/zen/split-view/zen-split-group.inc.css b/src/zen/split-view/zen-split-group.inc.css index a5ab1d382..a153d165c 100644 --- a/src/zen/split-view/zen-split-group.inc.css +++ b/src/zen/split-view/zen-split-group.inc.css @@ -104,7 +104,8 @@ tab-group[split-view-group] { } } - tab-group[split-view-group]:where([hasactivetab]) & { + tab-group[split-view-group]:where([hasactivetab]) &, + &:has(> tab:is([multiselected])) { background-color: var(--tab-selected-bgcolor); box-shadow: var(--tab-selected-shadow); diff --git a/src/zen/split-view/zen-split-view.css b/src/zen/split-view/zen-split-view.css index 4027e4531..6e856e9db 100644 --- a/src/zen/split-view/zen-split-view.css +++ b/src/zen/split-view/zen-split-view.css @@ -238,7 +238,7 @@ width: 200px; height: 250px; border-radius: 16px; - background: black; + background: light-dark(white, black); justify-content: center; align-items: center; padding: 20px; @@ -251,7 +251,7 @@ } & label { - color: white; + color: light-dark(black, white); font-size: 14px; font-weight: bold; text-align: center; diff --git a/src/zen/tabs/ZenPinnedTabManager.mjs b/src/zen/tabs/ZenPinnedTabManager.mjs index 7d26334ca..893c89bb3 100644 --- a/src/zen/tabs/ZenPinnedTabManager.mjs +++ b/src/zen/tabs/ZenPinnedTabManager.mjs @@ -79,8 +79,9 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature { this._zenClickEventListener = this._onTabClick.bind(this); gZenWorkspaces._resolvePinnedInitialized(); - if (lazy.zenPinnedTabRestorePinnedTabsToPinnedUrl) { - gZenWorkspaces.promiseInitialized.then(() => { + gZenWorkspaces.promiseInitialized.then(() => { + gBrowser.addTabsProgressListener(this); + if (lazy.zenPinnedTabRestorePinnedTabsToPinnedUrl) { for (const tab of gZenWorkspaces.allStoredTabs) { try { this.resetPinnedTab(tab); @@ -88,8 +89,8 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature { console.error("Error restoring pinned tab:", ex); } } - }); - } + } + }); } log(message) { @@ -284,8 +285,6 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature { return; } - const selectedTabs = pinnedTabs.filter(tab => tab.selected); - event.stopPropagation(); event.preventDefault(); @@ -365,13 +364,19 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature { return; } } - await gBrowser.explicitUnloadTabs(pinnedTabs); + let successful = await gBrowser.explicitUnloadTabs(pinnedTabs); + if (!successful) { + return; + } for (const tab of pinnedTabs) { tab.removeAttribute("discarded"); } - } - if (selectedTabs.length) { - this._handleTabSwitch(selectedTabs[0]); + } else if (pinnedTabs.some(tab => tab.selected)) { + const selectedTabs = pinnedTabs.filter(tab => tab.selected); + gBrowser.selectedTab = gBrowser._findTabToBlurTo( + selectedTabs[0], + selectedTabs + ); } if (behavior.includes("reset")) { for (const tab of pinnedTabs) { @@ -391,28 +396,6 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature { } } - _handleTabSwitch(selectedTab) { - if (selectedTab !== gBrowser.selectedTab) { - return; - } - const findNextTab = direction => - gBrowser.tabContainer.findNextTab(selectedTab, { - direction, - filter: tab => !tab.hidden && !tab.pinned, - }); - - let nextTab = findNextTab(1) || findNextTab(-1); - - if (!nextTab) { - gZenWorkspaces.selectEmptyTab(); - return; - } - - if (nextTab) { - gBrowser.selectedTab = nextTab; - } - } - #resetTabToStoredState(tab) { const state = this.#getTabState(tab); @@ -422,11 +405,26 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature { } // Remove everything except the entry we want to keep - state.entries = [initialState.entry]; + state.entries = [ + { + ...initialState.entry, + triggeringPrincipal_base64: E10SUtils.serializePrincipal( + Services.scriptSecurityManager.createContentPrincipal( + Services.io.newURI(initialState.entry.url), + {} + ) + ), + }, + ]; state.image = tab.zenStaticIcon || initialState.image; state.index = 0; + // See gh-13024, we need to remove the scroll position from the state, + // otherwise when we reset the pinned tab, it will scroll to the previous position + // which can be confusing for the user, especially if they have a long page. + delete state.scroll; + SessionStore.setTabState(tab, state); this.resetPinChangedUrl(tab); } @@ -666,21 +664,22 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature { ) { let newIndex = dropIndex; let fromDifferentWindow = false; - movingTabs = Array.from(movingTabs || draggedTab) + let ownedTabs = Array.from(movingTabs || draggedTab) .reverse() .map(tab => { if (!gBrowser.isTab(tab)) { return tab; } let workspaceId; + if ( + !tab.hasAttribute("zen-essential") && + tab.getAttribute("zen-workspace-id") != gZenWorkspaces.activeWorkspace + ) { + workspaceId = gZenWorkspaces.activeWorkspace; + } if (tab.ownerGlobal !== window) { fromDifferentWindow = true; - if ( - !tab.hasAttribute("zen-essential") && - tab.getAttribute("zen-workspace-id") != - gZenWorkspaces.activeWorkspace - ) { - workspaceId = gZenWorkspaces.activeWorkspace; + if (workspaceId) { tab.ownerGlobal.gBrowser.selectedTab = tab.ownerGlobal.gBrowser._findTabToBlurTo(tab, movingTabs); tab.ownerGlobal.gZenWorkspaces.moveTabToWorkspace(tab, workspaceId); @@ -695,12 +694,13 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature { if (tab) { ++newIndex; } - if (workspaceId) { - tab.setAttribute("zen-workspace-id", workspaceId); - } + } + if (workspaceId) { + tab.setAttribute("zen-workspace-id", workspaceId); } return tab; }); + movingTabs = [...ownedTabs]; if (fromDifferentWindow) { gBrowser.addRangeToMultiSelectedTabs( gBrowser.tabContainer.dragAndDropElements[dropIndex], @@ -820,18 +820,19 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature { } } } - return moved; } catch (ex) { console.error("Error moving tabs:", ex); - return false; } + return [draggedTab, ownedTabs]; } - onLocationChange(aBrowser, aLocation) { + onLocationChange(aBrowser, aWebProgress, aRequest, aLocationURI) { + // eslint-disable-next-line no-shadow + let location = aLocationURI ? aLocationURI.spec : ""; if ( - (aLocation == "about:blank" && + (location == "about:blank" && BrowserUIUtils.checkEmptyPageOrigin(aBrowser)) || - aLocation == "" + location == "" ) { return; } @@ -846,9 +847,11 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature { } // Remove # and ? from the URL const pinUrl = tab._zenPinnedInitialState.entry.url.split("#")[0]; - const currentUrl = aLocation.split("#")[0]; + const currentUrl = location.split("#")[0]; // Add an indicator that the pin has been changed - if (pinUrl === currentUrl) { + if ( + Services.io.newURI(currentUrl).spec === Services.io.newURI(pinUrl).spec + ) { this.resetPinChangedUrl(tab); return; } diff --git a/src/zen/tabs/zen-tabs/vertical-tabs-topbar.inc.css b/src/zen/tabs/zen-tabs/vertical-tabs-topbar.inc.css index 3107480ea..35752d866 100644 --- a/src/zen/tabs/zen-tabs/vertical-tabs-topbar.inc.css +++ b/src/zen/tabs/zen-tabs/vertical-tabs-topbar.inc.css @@ -31,7 +31,7 @@ z-index: 1; %include ../../compact-mode/windows-captions-fix-default.inc.css } - @media -moz-pref('zen.view.experimental-no-window-controls') { + @media -moz-pref('zen.view.experimental-no-window-controls') or (not -moz-pref("browser.tabs.inTitlebar")) { :root:not([zen-has-bookmarks]) & { max-height: 0 !important; overflow: hidden; diff --git a/src/zen/tabs/zen-tabs/vertical-tabs.css b/src/zen/tabs/zen-tabs/vertical-tabs.css index 027a1997c..d8e117132 100644 --- a/src/zen/tabs/zen-tabs/vertical-tabs.css +++ b/src/zen/tabs/zen-tabs/vertical-tabs.css @@ -130,10 +130,14 @@ height: 22px; border: none; width: 100%; - transition: - height 0.08s ease-in-out, - padding 0.08s ease-in-out, - opacity 0.06s ease-in-out; + + @media not (prefers-reduced-motion: reduce) { + transition: + height 0.08s ease-in-out, + padding 0.08s ease-in-out, + opacity 0.06s ease-in-out; + } + overflow: hidden; position: relative; opacity: 1; @@ -310,11 +314,13 @@ } & .tabbrowser-tab { - &, - & .tab-content > image { - transition: - scale 0.2s ease, - var(--zen-tabbox-element-indent-transition); + @media not (prefers-reduced-motion: reduce) { + &, + & .tab-content > image { + transition: + scale 0.1s ease, + var(--zen-tabbox-element-indent-transition); + } } :root[zen-sidebar-expanded="true"] &:not([zen-glance-tab]) { @@ -329,13 +335,13 @@ #tabbrowser-tabs:not([movingtab]) &:active:not(:has(.tab-content > image:active)) { scale: var(--zen-active-tab-scale); - rotate: 0.02deg; /* Subtle rotation to trigger GPU acceleration and prevent blurriness */ + rotate: 0.01deg; /* Subtle rotation to trigger GPU acceleration and prevent blurriness */ } :root:not([zen-renaming-tab="true"]) #tabbrowser-tabs:not([movingtab]) & .tab-content > image:active { - scale: 0.98; + scale: 0.97; } & .tab-icon-image { @@ -347,6 +353,7 @@ &:-moz-broken { content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='100' height='100'%3E%3C/svg%3E") !important; background: color-mix(in srgb, var(--zen-primary-color) 80%, light-dark(rgb(0, 0, 0), rgb(255, 255, 255))); + opacity: 0.5; } } @@ -468,13 +475,13 @@ overflow-y: auto; height: 100%; - :root[zen-workspace-id][zen-sidebar-expanded="true"] & { + :root[zen-sidebar-expanded="true"] & { margin-left: calc(-1 * var(--zen-toolbox-padding)); width: calc(100% + var(--zen-toolbox-padding) * 2); } } -:root[zen-workspace-id] #pinned-tabs-container { +#pinned-tabs-container { display: none; } @@ -1070,7 +1077,7 @@ #tabs-newtab-button { max-height: var(--tab-min-height); display: flex !important; - transition: scale 0.2s ease; + transition: scale 0.1s ease; #tabbrowser-tabs[movingtab] & { transition: transform 0.1s ease; } @@ -1082,7 +1089,7 @@ &:active, &[open] { - scale: 0.98; + scale: 0.985; } &[in-urlbar] { @@ -1124,6 +1131,7 @@ transition: max-height 0.3s ease-out, grid-template-columns 0.3s ease-out; + will-change: transform; opacity: 1; --min-essentials-width-wrap: calc(var(--tab-min-height) + 4px); grid-template-columns: repeat(auto-fit, minmax(max(23.7%, var(--min-essentials-width-wrap)), 1fr)); @@ -1318,6 +1326,7 @@ padding: 0; outline: none !important; border-radius: 0; + width: 100%; } /* ========================================================================== diff --git a/src/zen/tests/boosts/browser.toml b/src/zen/tests/boosts/browser.toml new file mode 100644 index 000000000..c24f62f6a --- /dev/null +++ b/src/zen/tests/boosts/browser.toml @@ -0,0 +1,12 @@ +# 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/. + +[DEFAULT] +support-files = [ + "head.js", +] + +["browser_boost_selector_basic.js"] +["browser_boost_selector_invalid.js"] +["browser_boost_selector_nthchild.js"] diff --git a/src/zen/tests/boosts/browser_boost_selector_basic.js b/src/zen/tests/boosts/browser_boost_selector_basic.js new file mode 100644 index 000000000..b1ee36098 --- /dev/null +++ b/src/zen/tests/boosts/browser_boost_selector_basic.js @@ -0,0 +1,44 @@ +/* Any copyright is dedicated to the Public Domain. + https://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function test_getSelectionPath_basic() { + const doc = document.implementation.createHTMLDocument("TestDoc"); + + const container = doc.createElement("div"); + container.id = "container"; + const child1 = doc.createElement("p"); + child1.className = "one"; + child1.textContent = "Text 1"; + const child2 = doc.createElement("span"); + child2.className = "two"; + child2.textContent = "Text 2"; + + container.appendChild(child1); + container.appendChild(child2); + doc.body.appendChild(container); + + const component = new SelectorComponent(doc, null, [], () => {}); + + for (let i = 0; i <= 7; i++) { + const path = component.getSelectionPath(doc, i, child2); + ok( + path, + `getSelectionPath should return a path for relatedValueIndex=${i}` + ); + + const selectedElements = doc.querySelectorAll(path); + + if (i === 0) + ok( + selectedElements.length === 1, + "For relatedValueIndex=1 there should be exactly one queried element" + ); + + ok( + selectedElements.length >= 1, + "CSS path should select at least one element" + ); + } +}); diff --git a/src/zen/tests/boosts/browser_boost_selector_invalid.js b/src/zen/tests/boosts/browser_boost_selector_invalid.js new file mode 100644 index 000000000..fac804c92 --- /dev/null +++ b/src/zen/tests/boosts/browser_boost_selector_invalid.js @@ -0,0 +1,30 @@ +/* Any copyright is dedicated to the Public Domain. + https://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function test_getSelectionPath_invalidNode() { + const doc = document.implementation.createHTMLDocument("TestInvalid"); + const component = new SelectorComponent(doc, null, [], () => {}); + + // Null element + Assert.equal( + component.getSelectionPath(doc, 0, null), + null, + "Null element should return null" + ); + + // Body element + Assert.equal( + component.getSelectionPath(doc, 0, doc.body), + null, + "Body element should return null" + ); + + // Html element + Assert.equal( + component.getSelectionPath(doc, 0, doc.documentElement), + null, + "HTML element should return null" + ); +}); diff --git a/src/zen/tests/boosts/browser_boost_selector_nthchild.js b/src/zen/tests/boosts/browser_boost_selector_nthchild.js new file mode 100644 index 000000000..5ff8581a2 --- /dev/null +++ b/src/zen/tests/boosts/browser_boost_selector_nthchild.js @@ -0,0 +1,43 @@ +/* Any copyright is dedicated to the Public Domain. + https://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +add_task(async function test_getSelectionPath_nthchild() { + const doc = document.implementation.createHTMLDocument("TestDoc"); + const childCount = 10; + + const container = doc.createElement("div"); + container.id = "container"; + + for (let i = 0; i < childCount; i++) { + const child = doc.createElement("p"); + child.className = "child"; + child.textContent = `Child ${i}`; + container.appendChild(child); + } + + doc.body.appendChild(container); + + const component = new SelectorComponent(doc, null, [], () => {}); + + for (let i = 0; i < container.children.length; i++) { + const currentNode = container.children[i]; + // Get exact element + const path = component.getSelectionPath(doc, 0, currentNode); + ok(path, "Path should be generated"); + + const selectedElements = doc.querySelectorAll(path); + + ok( + Array.from(selectedElements).includes(currentNode), + "Selector must include the selected node" + ); + + Assert.equal( + selectedElements.length, + 1, + "Selector should uniquely identify the element" + ); + } +}); diff --git a/src/zen/tests/boosts/head.js b/src/zen/tests/boosts/head.js new file mode 100644 index 000000000..7514c6dea --- /dev/null +++ b/src/zen/tests/boosts/head.js @@ -0,0 +1,7 @@ +/* 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 { SelectorComponent } = ChromeUtils.importESModule( + "resource:///modules/zen/boosts/ZenSelectorComponent.sys.mjs" +); diff --git a/src/zen/tests/folders/browser_folder_create.js b/src/zen/tests/folders/browser_folder_create.js index 39627b1a7..070928e49 100644 --- a/src/zen/tests/folders/browser_folder_create.js +++ b/src/zen/tests/folders/browser_folder_create.js @@ -20,6 +20,5 @@ add_task(async function test_Create_Folder() { ok(!folder.collapsed, "Folder is expanded after creation"); await removeFolder(folder); Assert.equal(folder.tabs.length, 0, "Folder is empty after deletion"); - ok(!folder.parentElement, "Folder is removed from the DOM"); ok(tab.closing, "Tab is closing after folder deletion"); }); diff --git a/src/zen/tests/folders/browser_folder_issue_9981.js b/src/zen/tests/folders/browser_folder_issue_9981.js index aacd01d14..3f035c2d0 100644 --- a/src/zen/tests/folders/browser_folder_issue_9981.js +++ b/src/zen/tests/folders/browser_folder_issue_9981.js @@ -25,7 +25,7 @@ add_task(async function test_Issue_9981() { window, "TabGroupCollapse" ); - EventUtils.synthesizeMouseAtCenter(folder.labelElement, {}); + folder.collapsed = true; await collapseEvent; gBrowser.clearMultiSelectedTabs(); diff --git a/src/zen/tests/folders/browser_folder_max_subfolders.js b/src/zen/tests/folders/browser_folder_max_subfolders.js index 62b3b9a22..36013ba04 100644 --- a/src/zen/tests/folders/browser_folder_max_subfolders.js +++ b/src/zen/tests/folders/browser_folder_max_subfolders.js @@ -17,9 +17,8 @@ add_task(async function test_Max_Subfolders() { let currentFolder = folder; for (let i = 1; i < TEST_MAX_FOLDERS; i++) { await openFolderContextMenu(currentFolder); - Assert.notStrictEqual( - subfolderItem.getAttribute("disabled"), - "true", + Assert.ok( + !subfolderItem.hasAttribute("disabled"), `Subfolder item should be enabled` ); const folderCreateEvent = BrowserTestUtils.waitForEvent( @@ -35,9 +34,8 @@ add_task(async function test_Max_Subfolders() { } await openFolderContextMenu(currentFolder); - Assert.equal( - subfolderItem.getAttribute("disabled"), - "true", + Assert.ok( + subfolderItem.hasAttribute("disabled"), `Subfolder item should be disabled after reaching max subfolders` ); diff --git a/src/zen/tests/folders/browser_folder_reset_button.js b/src/zen/tests/folders/browser_folder_reset_button.js index 6ef988773..5e45fce9a 100644 --- a/src/zen/tests/folders/browser_folder_reset_button.js +++ b/src/zen/tests/folders/browser_folder_reset_button.js @@ -25,7 +25,7 @@ add_task(async function test_Issue_() { window, "TabGroupCollapse" ); - EventUtils.synthesizeMouseAtCenter(folder.labelElement, {}); + folder.collapsed = true; await collapseEvent; gBrowser.clearMultiSelectedTabs(); diff --git a/src/zen/tests/live-folders/browser.toml b/src/zen/tests/live-folders/browser.toml index bd75b3725..ccd76d2cb 100644 --- a/src/zen/tests/live-folders/browser.toml +++ b/src/zen/tests/live-folders/browser.toml @@ -3,6 +3,7 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. [DEFAULT] +prefs = ["zen.live-folders.github.skip-new-pr-ui-check=true"] ["browser_github_live_folder.js"] diff --git a/src/zen/tests/live-folders/browser_github_live_folder.js b/src/zen/tests/live-folders/browser_github_live_folder.js index ded5e5116..ee1237438 100644 --- a/src/zen/tests/live-folders/browser_github_live_folder.js +++ b/src/zen/tests/live-folders/browser_github_live_folder.js @@ -65,10 +65,10 @@ add_task(async function test_fetch_items_url_construction() { const fetchedUrl = new URL(instance.fetch.firstCall.args[0]); const searchParams = fetchedUrl.searchParams; - Assert.ok(fetchedUrl.href.startsWith("https://github.com/issues/assigned")); + Assert.ok(fetchedUrl.href.startsWith("https://github.com/pulls")); const query = searchParams.get("q"); - Assert.ok(query.includes("state:open"), "Should include state:open"); + Assert.ok(query.includes("is:open"), "Should include state:open"); Assert.ok(query.includes("is:pr"), "Should include is:PR"); Assert.ok(query.includes("author:@me"), "Should include author:@me"); Assert.ok(!query.includes("assignee:@me"), "Should NOT include assignee:@me"); @@ -176,3 +176,169 @@ add_task(async function test_fetch_network_error() { sandbox.restore(); }); + +add_task(async function test_no_filter_enabled_returns_error() { + info( + "should short-circuit and return the no-filter error when every option is off" + ); + + let sandbox = sinon.createSandbox(); + let instance = getGithubProviderForTest(sandbox, { + type: "pull-requests", + authorMe: false, + assignedMe: false, + reviewRequested: false, + }); + + const result = await instance.fetchItems(); + + Assert.equal( + result, + "zen-live-folder-github-no-filter", + "Should return the no-filter error id" + ); + Assert.ok( + instance.fetch.notCalled, + "Should not issue a fetch when no filter is enabled" + ); + + sandbox.restore(); +}); + +add_task(async function test_404_returns_no_auth() { + info("should treat a 404 as a missing-auth signal"); + + let sandbox = sinon.createSandbox(); + let instance = getGithubProviderForTest(sandbox, { + type: "pull-requests", + authorMe: true, + }); + + instance.fetch.resolves({ status: 404, text: "" }); + + const result = await instance.fetchItems(); + + Assert.equal( + result, + "zen-live-folder-github-no-auth", + "Should return the no-auth error id" + ); + + sandbox.restore(); +}); + +add_task(async function test_repo_excludes_emit_negative_repo_filters() { + info("should add -repo: clauses for each excluded repository"); + + let sandbox = sinon.createSandbox(); + let instance = getGithubProviderForTest(sandbox, { + type: "pull-requests", + authorMe: true, + assignedMe: false, + reviewRequested: false, + repoExcludes: ["zen-browser/desktop", "foo/bar"], + }); + + instance.fetch.resolves({ status: 200, text: "" }); + + await instance.fetchItems(); + + const fetchedUrl = new URL(instance.fetch.firstCall.args[0]); + const query = fetchedUrl.searchParams.get("q"); + + Assert.ok( + query.includes("-repo:zen-browser/desktop"), + "Should exclude zen-browser/desktop from the query" + ); + Assert.ok( + query.includes("-repo:foo/bar"), + "Should exclude foo/bar from the query" + ); + + sandbox.restore(); +}); + +add_task(async function test_pull_requests_json_api_parsing() { + info("should parse the new PR dashboard JSON payload"); + + let sandbox = sinon.createSandbox(); + let instance = getGithubProviderForTest(sandbox, { + type: "pull-requests", + authorMe: true, + }); + + const payload = JSON.stringify({ + payload: { + pullsDashboardSurfaceContentRoute: { + results: [ + { + repoNameWithOwner: "zen-browser/desktop", + number: 42, + title: "Add live folders", + author: { displayLogin: "alice" }, + permalink: "https://github.com/zen-browser/desktop/pull/42", + }, + { + repoNameWithOwner: "zen-browser/desktop", + number: 43, + title: "Fix bug", + author: { displayLogin: "bob" }, + permalink: "https://github.com/zen-browser/desktop/pull/43", + }, + ], + }, + }, + }); + + instance.fetch.resolves({ status: 200, text: payload }); + + const items = await instance.fetchItems(); + + Assert.equal(items.length, 2, "Should parse two PRs from the JSON payload"); + Assert.equal(items[0].id, "zen-browser/desktop#42"); + Assert.equal(items[0].title, "Add live folders"); + Assert.equal(items[0].subtitle, "alice"); + Assert.equal(items[0].url, "https://github.com/zen-browser/desktop/pull/42"); + Assert.equal(items[1].id, "zen-browser/desktop#43"); + Assert.ok( + instance.state.isJsonApi, + "Should mark the provider as using the JSON API" + ); + + sandbox.restore(); +}); + +add_task(async function test_pull_requests_json_api_falls_back_to_html() { + info( + "should fall back to HTML parsing when an HTML response arrives unexpectedly" + ); + + let sandbox = sinon.createSandbox(); + let instance = getGithubProviderForTest(sandbox, { + type: "pull-requests", + authorMe: true, + }); + + // Simulate a previous fetch having locked the provider into JSON-API mode. + instance.state.isJsonApi = true; + + instance.fetch.resolves({ + status: 200, + text: "not JSON", + }); + + const result = await instance.fetchItems(); + + Assert.equal( + instance.state.isJsonApi, + false, + "Should clear isJsonApi after seeing a non-JSON response" + ); + Assert.equal( + result, + "zen-live-folder-failed-fetch", + "Should surface a fetch error so the user is prompted to retry" + ); + + sandbox.restore(); +}); diff --git a/src/zen/tests/live-folders/browser_rss_live_folder.js b/src/zen/tests/live-folders/browser_rss_live_folder.js index aef39b9da..7b28a8f48 100644 --- a/src/zen/tests/live-folders/browser_rss_live_folder.js +++ b/src/zen/tests/live-folders/browser_rss_live_folder.js @@ -162,9 +162,9 @@ add_task(async function test_max_items_limit() { const rssXml = ` - 11${date} - 22${date} - 33${date} + 1https://example.com/1${date} + 2https://example.com/2${date} + 3https://example.com/3${date} `; @@ -218,6 +218,113 @@ add_task(async function test_invalid_dates() { sandbox.restore(); }); +add_task(async function test_item_url_scheme_filtering() { + info("should drop items whose link uses a non-http(s) scheme"); + + let sandbox = sinon.createSandbox(); + let instance = getRssProviderForTest(sandbox, { timeRange: 0 }); + + const date = new Date().toUTCString(); + const rssXml = ` + + + + JavaScript scheme + javascript:alert(1) + ${date} + + + Data scheme + data:text/html,<script>alert(1)</script> + ${date} + + + File scheme + file:///etc/passwd + ${date} + + + about: scheme + about:config + ${date} + + + chrome: scheme + chrome://browser/content/browser.xhtml + ${date} + + + Invalid URL + not a url + ${date} + + + Good https + https://example.com/good + ${date} + + + Good http + http://example.com/good + ${date} + + + + `; + + instance.fetch.resolves({ text: rssXml }); + + const items = await instance.fetchItems(); + + Assert.equal( + items.length, + 2, + "Only http(s) items should survive scheme filtering" + ); + Assert.deepEqual( + items.map(i => i.url).sort(), + ["http://example.com/good", "https://example.com/good"], + "Surviving items should be the http and https links" + ); + + sandbox.restore(); +}); + +add_task(async function test_atom_item_url_scheme_filtering() { + info("should drop Atom entries whose link href uses a non-http(s) scheme"); + + let sandbox = sinon.createSandbox(); + let instance = getRssProviderForTest(sandbox, { timeRange: 0 }); + + const updated = new Date().toISOString(); + const atomXml = ` + + Atom Feed + + Bad scheme + + urn:uuid:bad + ${updated} + + + Good scheme + + urn:uuid:good + ${updated} + + + `; + + instance.fetch.resolves({ text: atomXml }); + + const items = await instance.fetchItems(); + + Assert.equal(items.length, 1, "Only the https Atom entry should remain"); + Assert.equal(items[0].url, "https://example.com/atom-good"); + + sandbox.restore(); +}); + add_task(async function test_fetch_network_error() { info("should return empty array on network error"); diff --git a/src/zen/tests/manifest.toml b/src/zen/tests/manifest.toml index 60c0975dc..fa3d7c84f 100644 --- a/src/zen/tests/manifest.toml +++ b/src/zen/tests/manifest.toml @@ -3,6 +3,16 @@ # 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/. +[readermode] +source = "toolkit/components/reader/tests/browser" +is_direct_path = true +disable = [ + "browser_drag_url_readerMode.js", + "browser_localfile_readerMode.js", + "browser_readerMode.js", + "browser_readerMode_colorSchemePref.js", +] + [safebrowsing] source = "browser/components/safebrowsing/content/test" is_direct_path = true @@ -25,5 +35,12 @@ disable = [ "browser_setDesktopBackgroundPreview.js", ] +[tabMediaIndicator] +source = "browser/components/tabbrowser/test/browser/tabMediaIndicator" +is_direct_path = true + [tooltiptext] source = "toolkit/components/tooltiptext" +disable = [ + "browser_input_file_tooltips.js", +] diff --git a/src/zen/tests/media/browser.toml b/src/zen/tests/media/browser.toml new file mode 100644 index 000000000..52752f239 --- /dev/null +++ b/src/zen/tests/media/browser.toml @@ -0,0 +1,16 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +[DEFAULT] +support-files = [ + "head.js", +] + +["browser_media_metadata.js"] + +["browser_media_mute.js"] + +["browser_media_next_track.js"] + +["browser_media_shows_on_tab_switch.js"] diff --git a/src/zen/tests/media/browser_media_metadata.js b/src/zen/tests/media/browser_media_metadata.js new file mode 100644 index 000000000..a048b8cac --- /dev/null +++ b/src/zen/tests/media/browser_media_metadata.js @@ -0,0 +1,63 @@ +/* Any copyright is dedicated to the Public Domain. + https://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// User flow: +// 1. A page (think Spotify, YouTube Music) plays media and publishes +// title/artist via navigator.mediaSession.metadata. +// 2. User switches off that tab, media bar appears. +// 3. The title and artist labels in the bar show what the page published. +// 4. The page then updates the metadata mid-playback (next song starts). +// 5. The bar updates live, without the user having to switch tabs again. +// +// This is what makes the bar feel connected to the playing page instead of +// a generic "something is playing" indicator. + +add_task(async function test_media_bar_shows_metadata_from_page() { + const originalTab = gBrowser.selectedTab; + const mediaTab = await addMediaTab(); + await BrowserTestUtils.switchTab(gBrowser, mediaTab); + + try { + await setMediaSessionMetadata(mediaTab, { + title: "Sandstorm", + artist: "Darude", + }); + await playVideoIn(mediaTab); + await BrowserTestUtils.switchTab(gBrowser, originalTab); + await waitForMediaBarVisible(); + + const titleEl = document.getElementById("zen-media-title"); + const artistEl = document.getElementById("zen-media-artist"); + + await BrowserTestUtils.waitForCondition( + () => titleEl.textContent === "Sandstorm", + "title label reflects the page's mediaSession metadata" + ); + Assert.equal( + artistEl.textContent, + "Darude", + "artist label reflects the page's mediaSession metadata" + ); + + // Page updates metadata mid-playback. + await setMediaSessionMetadata(mediaTab, { + title: "Levels", + artist: "Avicii", + }); + await BrowserTestUtils.waitForCondition( + () => titleEl.textContent === "Levels", + "title updates live when the page changes its mediaSession metadata" + ); + Assert.equal( + artistEl.textContent, + "Avicii", + "artist updates live alongside the title" + ); + } finally { + await pauseVideoIn(mediaTab); + BrowserTestUtils.removeTab(mediaTab); + gBrowser.selectedTab = originalTab; + } +}); diff --git a/src/zen/tests/media/browser_media_mute.js b/src/zen/tests/media/browser_media_mute.js new file mode 100644 index 000000000..766059fd0 --- /dev/null +++ b/src/zen/tests/media/browser_media_mute.js @@ -0,0 +1,64 @@ +/* Any copyright is dedicated to the Public Domain. + https://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// User flow: +// 1. User plays a video, switches tabs, media bar appears. +// 2. User clicks the mute button on the Zen media bar. +// 3. The underlying tab actually goes silent (browser.audioMuted flips). +// 4. The media bar reflects that with the `muted` attribute so the icon +// changes. +// 5. Clicking again unmutes. +// +// If this breaks, the user sees a mute button that looks toggled but the +// audio keeps playing — or worse, the tab is muted but the button still +// says "unmuted". + +add_task(async function test_mute_from_media_bar() { + const originalTab = gBrowser.selectedTab; + const mediaTab = await addMediaTab(); + await BrowserTestUtils.switchTab(gBrowser, mediaTab); + + try { + await playVideoIn(mediaTab); + await BrowserTestUtils.switchTab(gBrowser, originalTab); + await waitForMediaBarVisible(); + + ok( + !mediaTab.linkedBrowser.audioMuted, + "precondition: playing tab starts unmuted" + ); + ok( + !mediaBar().hasAttribute("muted"), + "precondition: media bar has no muted attribute" + ); + + clickMediaButton("zen-media-mute-button"); + await BrowserTestUtils.waitForCondition( + () => mediaTab.linkedBrowser.audioMuted, + "tab becomes muted after clicking the media bar mute button" + ); + ok( + mediaBar().hasAttribute("muted"), + "media bar reflects the muted state in its attribute" + ); + + clickMediaButton("zen-media-mute-button"); + await BrowserTestUtils.waitForCondition( + () => !mediaTab.linkedBrowser.audioMuted, + "clicking again unmutes the tab" + ); + ok( + !mediaBar().hasAttribute("muted"), + "media bar drops the muted attribute" + ); + } finally { + if (mediaTab.linkedBrowser.audioMuted) { + mediaTab.toggleMuteAudio(); + } + await pauseVideoIn(mediaTab); + BrowserTestUtils.removeTab(mediaTab); + gBrowser.selectedTab = originalTab; + } +}); diff --git a/src/zen/tests/media/browser_media_next_track.js b/src/zen/tests/media/browser_media_next_track.js new file mode 100644 index 000000000..f4797e7e0 --- /dev/null +++ b/src/zen/tests/media/browser_media_next_track.js @@ -0,0 +1,74 @@ +/* Any copyright is dedicated to the Public Domain. + https://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// User flow: +// 1. A music page registers a "nexttrack" action handler (like most +// streaming sites do). +// 2. User is on another tab, media bar is showing with the next-track +// button enabled. +// 3. User clicks next-track. +// 4. The action fires inside the page — the page is responsible for +// loading the next song. Zen's job here is to relay the click. +// +// Also guards the button-enablement logic: if the page does NOT register a +// handler, the next-track button must be disabled. Otherwise clicks go +// nowhere and users think the bar is broken. + +add_task(async function test_next_track_relays_to_page() { + const originalTab = gBrowser.selectedTab; + const mediaTab = await addMediaTab(); + await BrowserTestUtils.switchTab(gBrowser, mediaTab); + + try { + await playVideoIn(mediaTab); + await setMediaSessionActionHandler(mediaTab, "nexttrack"); + + await BrowserTestUtils.switchTab(gBrowser, originalTab); + await waitForMediaBarVisible(); + + const nextButton = document.getElementById("zen-media-nexttrack-button"); + + // supportedkeyschange propagates asynchronously; wait for the bar's + // next-track button to become enabled before clicking. + await BrowserTestUtils.waitForCondition( + () => !nextButton.disabled, + "next-track button becomes enabled once the page registers a handler" + ); + + const actionFired = waitForMediaSessionAction(mediaTab); + clickMediaButton("zen-media-nexttrack-button"); + + const result = await actionFired; + ok(result, "page's nexttrack MediaSession handler was invoked"); + } finally { + await pauseVideoIn(mediaTab); + BrowserTestUtils.removeTab(mediaTab); + gBrowser.selectedTab = originalTab; + } +}); + +add_task(async function test_next_track_button_disabled_without_handler() { + const originalTab = gBrowser.selectedTab; + const mediaTab = await addMediaTab(); + await BrowserTestUtils.switchTab(gBrowser, mediaTab); + + try { + // Deliberately do NOT install a nexttrack handler. + await playVideoIn(mediaTab); + await BrowserTestUtils.switchTab(gBrowser, originalTab); + await waitForMediaBarVisible(); + + const nextButton = document.getElementById("zen-media-nexttrack-button"); + Assert.equal( + nextButton.disabled, + true, + "next-track button stays disabled when the page registers no handler" + ); + } finally { + await pauseVideoIn(mediaTab); + BrowserTestUtils.removeTab(mediaTab); + gBrowser.selectedTab = originalTab; + } +}); diff --git a/src/zen/tests/media/browser_media_shows_on_tab_switch.js b/src/zen/tests/media/browser_media_shows_on_tab_switch.js new file mode 100644 index 000000000..783a88af1 --- /dev/null +++ b/src/zen/tests/media/browser_media_shows_on_tab_switch.js @@ -0,0 +1,74 @@ +/* Any copyright is dedicated to the Public Domain. + https://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// User flow: +// 1. User opens a page with audio and hits play. +// 2. User switches to a different tab. +// 3. The Zen media control bar should appear (so the user can still +// pause/skip without going back to the noisy tab). +// 4. User switches back to the audio tab. +// 5. The media bar should hide again — it's redundant next to the real +// page controls. +// +// This covers the real contract users see: the DOMAudioPlaybackStarted → +// TabSelect → showMediaControls chain in nsZenMediaController, plus the +// inverse path on selecting the playing tab. A regression anywhere in that +// chain (event wiring, the 500ms tab-switch debounce, the hidden attribute +// flip) surfaces as a bar that either never shows or never hides. + +// note: We keep setting timeouts because media player takes a bit to +// get removed (after the animation, more specifically) + +add_task(async function test_media_bar_shows_when_switching_off_playing_tab() { + gZenMediaController.onControllerClose(); + await BrowserTestUtils.waitForCondition( + () => !isMediaBarVisible(), + "media bar hides again once the playing tab regains focus" + ); + + const originalTab = gBrowser.selectedTab; + const mediaTab = await addMediaTab(); + await BrowserTestUtils.switchTab(gBrowser, mediaTab); + + ok( + !isMediaBarVisible(), + "media bar is hidden while the playing tab is the active tab" + ); + + try { + await playVideoIn(mediaTab); + + ok( + !isMediaBarVisible(), + "media bar remains hidden while focused on the playing tab" + ); + + // Switch away. The controller schedules showMediaControls() on a 500ms + // timer; wait for the visibility flip rather than racing it. + await BrowserTestUtils.switchTab(gBrowser, originalTab); + await new Promise(r => setTimeout(r, 1000)); + await BrowserTestUtils.waitForCondition( + isMediaBarVisible, + "media bar becomes visible after switching off the playing tab" + ); + + Assert.equal( + gZenMediaController._currentBrowser?.browserId, + mediaTab.linkedBrowser.browserId, + "media controller is bound to the media tab's browser, not the selected tab" + ); + + await BrowserTestUtils.switchTab(gBrowser, mediaTab); + await new Promise(r => setTimeout(r, 1000)); + await BrowserTestUtils.waitForCondition( + () => !isMediaBarVisible(), + "media bar hides again once the playing tab regains focus" + ); + } finally { + await pauseVideoIn(mediaTab); + BrowserTestUtils.removeTab(mediaTab); + gBrowser.selectedTab = originalTab; + } +}); diff --git a/src/zen/tests/media/head.js b/src/zen/tests/media/head.js new file mode 100644 index 000000000..82de52c6c --- /dev/null +++ b/src/zen/tests/media/head.js @@ -0,0 +1,96 @@ +/* Any copyright is dedicated to the Public Domain. + https://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Shared mozilla-central fixture from the Picture-in-Picture tests: an HTML +// page with two looping