Compare commits

...

21 Commits

Author SHA1 Message Date
mr. m
c3aba894bb no-bug: Open boosts editor with util window animations 2026-06-08 12:22:58 +02:00
fen4flo
965bb9391d gh-14044: Change to use gDialogBox instead of openDialog (gh-14054)
Co-authored-by: mr. m <mr.m@tuta.com>
2026-06-07 11:33:54 +02:00
mr. m
e7e4c5a452 gh-14045: Fixed sidebar notifications overflowing text (gh-14047) 2026-06-06 17:34:43 +02:00
fen4flo
880d61df16 gh-14044: Implement Space Routing (gh-13981)
Signed-off-by: mr. m <91018726+mr-cheffy@users.noreply.github.com>
2026-06-06 14:11:42 +02:00
Joe Goldin
b1be664f4d gh-13027: fix window sync lockup that left synced tabs blank in new windows (gh-14012) 2026-06-04 13:12:02 +02:00
mr. m
5383737b23 no-bug: Fixed boosts window not resizing back properly (gh-14007) 2026-06-04 00:38:39 +02:00
mr. m
b812bff07e no-bug: Change wine build download (gh-14002) 2026-06-03 12:04:25 +02:00
Rishab Shah
e04e910a6f no-bug: bypass workspace container coercion for extension-opened tabs (gh-14001)
Co-authored-by: pokeshah <89949620+pokeshah@users.noreply.github.com>
2026-06-03 11:06:16 +02:00
Ashvin Jangid
bf365f8043 gh-13439: Implement dark mode boosts panel (gh-13979) 2026-06-02 15:47:37 +02:00
mr. m
0a45b82a6c no-bug: Sync upstream Firefox to version 151.0.3 (gh-13991)
This PR syncs the upstream Firefox to version 151.0.3.

*  All patches applied cleanly.

@mr-cheffy please review and merge this PR.

---------

Signed-off-by: mr. m <91018726+mr-cheffy@users.noreply.github.com>
2026-06-02 15:47:19 +02:00
mr. m
2311e183f1 gh-13745: Disable desktop launcher for windows (gh-13987) 2026-06-02 11:16:29 +02:00
mr. m
8ea65cba48 gh-12112: Fixed auto focus for new windows not working (gh-13976) 2026-06-01 12:24:16 +02:00
mr. m
45075e2fbc gh-13439: Add tests coverage for boosts (gh-13977) 2026-06-01 11:24:45 +02:00
sporocyst
64e2e49a00 gh-13941: Fixed boost button viable when non-effective, CSS part (gh-13975)
Signed-off-by: sporocyst <sporocyst.tw@gmail.com>
2026-06-01 10:59:41 +02:00
fen4flo
feca914821 gh-13949: Secondary dot snapping to center in a new Boost (gh-13965)
Co-authored-by: mr. m <mr.m@tuta.com>
Signed-off-by: fen4flo <75260616+FlorianButz@users.noreply.github.com>
2026-05-31 15:53:33 +02:00
fen4flo
c7818fe355 gh-13839: Fix icons when using custom fonts (gh-13956)
Co-authored-by: mr. m <91018726+mr-cheffy@users.noreply.github.com>
Signed-off-by: mr. m <91018726+mr-cheffy@users.noreply.github.com>
2026-05-30 21:10:37 +02:00
mr. m
2c59265249 gh-13745: Fixed nightly being installed accidentally (gh-13964) 2026-05-30 20:54:55 +02:00
NHClaessens
193d004a37 gh-10594: Change conditional for triggering padding bump (gh-13959) 2026-05-30 17:35:40 +02:00
sporocyst
072d874f78 gh-13941: Fixed boost button viable when non-effective (gh-13953) 2026-05-30 12:26:20 +02:00
mr. m
7aa0ca05ec gh-13947: Fixed loading bar appearing on dom fullscreen (gh-13954) 2026-05-30 11:55:36 +02:00
Ashvin Jangid
d76fa13edd gh-13923: fix empty keyboard shortcut label caused by key_duplicateTab (gh-13924) 2026-05-28 18:33:29 +02:00
132 changed files with 4433 additions and 424 deletions

View File

@@ -129,7 +129,7 @@ jobs:
token: ${{ secrets.DEPLOY_KEY }}
commit-message: "chore: Sync upstream to `Firefox ${{ steps.build-data.outputs.version }}`"
branch: "chore/upstream-sync"
title: "no-bug: Sync upstream Firefox to version ${{ steps.build-data.outputs.version }}"
title: "no-bug: Sync upstream Firefox to version `${{ steps.build-data.outputs.version }}`"
body: |
This PR syncs the upstream Firefox to version ${{ steps.build-data.outputs.version }}.

View File

@@ -154,7 +154,7 @@ jobs:
zlib1g-dev \
aria2
echo Setup wine
aria2c "https://firefox-ci-tc.services.mozilla.com/api/index/v1/task/gecko.cache.level-1.toolchains.v3.linux64-wine.latest/artifacts/public%2Fbuild%2Fwine.tar.zst" -o wine.tar.zst
aria2c "https://firefox-ci-tc.services.mozilla.com/api/queue/v1/task/dQz_aHy8Rl-Lt0xf2WlrMw/artifacts/public/build/wine.tar.zst" -o wine.tar.zst
tar --zstd -xf wine.tar.zst -C ~/win-cross
rm wine.tar.zst
echo Setup Visual Studio

View File

@@ -34,8 +34,8 @@ 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 `151.0.2`! 🚀
- [`Twilight`](https://zen-browser.app/download?twilight) - Is currently built using Firefox version `RC 151.0.2`!
- [`Release`](https://zen-browser.app/download) - Is currently built using Firefox version `151.0.3`! 🚀
- [`Twilight`](https://zen-browser.app/download?twilight) - Is currently built using Firefox version `RC 151.0.3`!
### Contributing

View File

@@ -1 +1 @@
05272df13c2e4f435b4e0a706715f302b09ef829
5c4d14a559bf26eb4ab3e136d2084310ebe51ac0

View File

@@ -16,9 +16,9 @@ if test "$ZEN_CROSS_COMPILING"; then
CROSS_COMPILE=1
if test "$SURFER_COMPAT" = "aarch64"; then
export WIN32_REDIST_DIR="$(echo ~)/win-cross/vs2026/VC/Redist/MSVC/14.50.35710/arm64/Microsoft.VC145.CRT"
export WIN32_REDIST_DIR="$WINSYSROOT/VC/Redist/MSVC/14.50.35710/arm64/Microsoft.VC145.CRT"
else
export WIN32_REDIST_DIR="$(echo ~)/win-cross/vs2026/VC/Redist/MSVC/14.50.35710/x64/Microsoft.VC145.CRT"
export WIN32_REDIST_DIR="$WINSYSROOT/VC/Redist/MSVC/14.50.35710/x64/Microsoft.VC145.CRT"
fi
fi

View File

@@ -22,3 +22,5 @@ files:
translation: browser/browser/zen-folders.ftl
- source: en-US/browser/browser/zen-boosts.ftl
translation: browser/browser/zen-boosts.ftl
- source: en-US/browser/browser/zen-space-routing.ftl
translation: browser/browser/zen-space-routing.ftl

View File

@@ -29,9 +29,9 @@ 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-boost-color-contrast = Contrast
zen-boost-color-brightness = Brightness
zen-boost-color-original-saturation = Original Saturation
zen-add-zap-helper = Click elements on the page to <b>Zap</b> them
zen-remove-zap-helper = ← Click to Unzap
zen-select-this = Insert selector for this

View File

@@ -29,9 +29,9 @@ 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-boost-color-contrast = Contrast
zen-boost-color-brightness = Brightness
zen-boost-color-original-saturation = Original Saturation
zen-add-zap-helper = Click elements on the page to <b>Zap</b> them
zen-remove-zap-helper = ← Click to Unzap
zen-select-this = Insert selector for this

View File

@@ -29,9 +29,9 @@ 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-boost-color-contrast = Contrast
zen-boost-color-brightness = Brightness
zen-boost-color-original-saturation = Original Saturation
zen-add-zap-helper = Click elements on the page to <b>Zap</b> them
zen-remove-zap-helper = ← Click to Unzap
zen-select-this = Insert selector for this

View File

@@ -29,9 +29,9 @@ zen-boost-css-picker =
.tooltiptext = Trieu el selector
zen-boost-css-inspector =
.tooltiptext = Obre l'inspector
zen-bootst-color-contrast = Contrast
zen-bootst-color-brightness = Brillantor
zen-bootst-color-original-saturation = Saturació original
zen-boost-color-contrast = Contrast
zen-boost-color-brightness = Brillantor
zen-boost-color-original-saturation = Saturació original
zen-add-zap-helper = Feu clic als elements de la pàgina per <b>amagar-los</b>
zen-remove-zap-helper = ← Feu clic per mostrar
zen-select-this = Insereix un selector per a això

View File

@@ -29,9 +29,9 @@ 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-boost-color-contrast = Contrast
zen-boost-color-brightness = Brightness
zen-boost-color-original-saturation = Original Saturation
zen-add-zap-helper = Click elements on the page to <b>Zap</b> them
zen-remove-zap-helper = ← Click to Unzap
zen-select-this = Insert selector for this

View File

@@ -29,9 +29,9 @@ 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-boost-color-contrast = Contrast
zen-boost-color-brightness = Brightness
zen-boost-color-original-saturation = Original Saturation
zen-add-zap-helper = Click elements on the page to <b>Zap</b> them
zen-remove-zap-helper = ← Click to Unzap
zen-select-this = Insert selector for this

View File

@@ -29,9 +29,9 @@ 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-boost-color-contrast = Contrast
zen-boost-color-brightness = Brightness
zen-boost-color-original-saturation = Original Saturation
zen-add-zap-helper = Click elements on the page to <b>Zap</b> them
zen-remove-zap-helper = ← Click to Unzap
zen-select-this = Insert selector for this

View File

@@ -29,9 +29,9 @@ zen-boost-css-picker =
.tooltiptext = Selektor auswählen
zen-boost-css-inspector =
.tooltiptext = Inspektor öffnen
zen-bootst-color-contrast = Kontrast
zen-bootst-color-brightness = Helligkeit
zen-bootst-color-original-saturation = Ausgangssättigung
zen-boost-color-contrast = Kontrast
zen-boost-color-brightness = Helligkeit
zen-boost-color-original-saturation = Ausgangssättigung
zen-add-zap-helper = Klicke auf Elemente auf der Seite, um sie mit <b>Zap</b> zu markieren
zen-remove-zap-helper = ← Erneut klicken zum Wiederherstellen
zen-select-this = Selektor für dieses Element einfügen

View File

@@ -29,9 +29,9 @@ 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-boost-color-contrast = Contrast
zen-boost-color-brightness = Brightness
zen-boost-color-original-saturation = Original Saturation
zen-add-zap-helper = Click elements on the page to <b>Zap</b> them
zen-remove-zap-helper = ← Click to Unzap
zen-select-this = Insert selector for this

View File

@@ -29,9 +29,9 @@ 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-boost-color-contrast = Contrast
zen-boost-color-brightness = Brightness
zen-boost-color-original-saturation = Original Saturation
zen-add-zap-helper = Click elements on the page to <b>Zap</b> them
zen-remove-zap-helper = ← Click to Unzap
zen-select-this = Insert selector for this

View File

@@ -29,9 +29,9 @@ 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-boost-color-contrast = Contrast
zen-boost-color-brightness = Brightness
zen-boost-color-original-saturation = Original Saturation
zen-add-zap-helper = Click elements on the page to <b>Zap</b> them
zen-remove-zap-helper = ← Click to Unzap
zen-select-this = Insert selector for this

View File

@@ -0,0 +1,25 @@
# 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-space-routing-settings =
.label = Space Routing Settings
zen-space-routing-rulepanel-placeholder = Routes let you choose where specific sites open inside Zen. For example, you can route YouTube links to always open inside your Personal space.
zen-space-routing-dialog-title = Space Routing Settings
zen-space-routing-external-default = Default route for external links
zen-space-routing-new-route = New Route
zen-space-routing-open-in-space = Open in Space
zen-space-routing-most-recent-space = Most recent Space
zen-space-routing-close-button =
.aria-label = Close
.tooltiptext = Close
zen-space-routing-contains =
.label = Contains
zen-space-routing-equal-to =
.label = Is Equal To
zen-space-routing-regex =
.label = RegEx
zen-space-routing-open-in = Open In
zen-space-routing-url = URL

View File

@@ -29,9 +29,9 @@ zen-boost-css-picker =
.tooltiptext = Escoger selector
zen-boost-css-inspector =
.tooltiptext = Abrir inspector
zen-bootst-color-contrast = Contraste
zen-bootst-color-brightness = Brillo
zen-bootst-color-original-saturation = Saturación original
zen-boost-color-contrast = Contraste
zen-boost-color-brightness = Brillo
zen-boost-color-original-saturation = Saturación original
zen-add-zap-helper = Haga clic en los elementos de la página para <b>borrarlos</b>
zen-remove-zap-helper = ← Clic para deshacer
zen-select-this = Insertar selector para esto

View File

@@ -29,9 +29,9 @@ 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-boost-color-contrast = Contrast
zen-boost-color-brightness = Brightness
zen-boost-color-original-saturation = Original Saturation
zen-add-zap-helper = Click elements on the page to <b>Zap</b> them
zen-remove-zap-helper = ← Click to Unzap
zen-select-this = Insert selector for this

View File

@@ -29,9 +29,9 @@ 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-boost-color-contrast = Contrast
zen-boost-color-brightness = Brightness
zen-boost-color-original-saturation = Original Saturation
zen-add-zap-helper = Click elements on the page to <b>Zap</b> them
zen-remove-zap-helper = ← Click to Unzap
zen-select-this = Insert selector for this

View File

@@ -29,9 +29,9 @@ 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-boost-color-contrast = Contrast
zen-boost-color-brightness = Brightness
zen-boost-color-original-saturation = Original Saturation
zen-add-zap-helper = Click elements on the page to <b>Zap</b> them
zen-remove-zap-helper = ← Click to Unzap
zen-select-this = Insert selector for this

View File

@@ -29,9 +29,9 @@ 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-boost-color-contrast = Contrast
zen-boost-color-brightness = Brightness
zen-boost-color-original-saturation = Original Saturation
zen-add-zap-helper = Click elements on the page to <b>Zap</b> them
zen-remove-zap-helper = ← Click to Unzap
zen-select-this = Insert selector for this

View File

@@ -29,9 +29,9 @@ zen-boost-css-picker =
.tooltiptext = Choisir le sélecteur
zen-boost-css-inspector =
.tooltiptext = Ouvrir l'inspecteur
zen-bootst-color-contrast = Contraste
zen-bootst-color-brightness = Luminosité
zen-bootst-color-original-saturation = Saturation originale
zen-boost-color-contrast = Contraste
zen-boost-color-brightness = Luminosité
zen-boost-color-original-saturation = Saturation originale
zen-add-zap-helper = Cliquez sur des éléments de la page pour les <b>zapper</b>
zen-remove-zap-helper = ← Cliquez pour démasquer
zen-select-this = Insérer un sélecteur pour ceci

View File

@@ -29,9 +29,9 @@ zen-boost-css-picker =
.tooltiptext = Roghnóir Roghnaigh
zen-boost-css-inspector =
.tooltiptext = Oscail an Cigire
zen-bootst-color-contrast = Codarsnacht
zen-bootst-color-brightness = Gile
zen-bootst-color-original-saturation = Sáithiú Bunaidh
zen-boost-color-contrast = Codarsnacht
zen-boost-color-brightness = Gile
zen-boost-color-original-saturation = Sáithiú Bunaidh
zen-add-zap-helper = Cliceáil ar eilimintí ar an leathanach chun iad a <b>Zapáil</b>
zen-remove-zap-helper = ← Cliceáil chun Dízipáil
zen-select-this = Cuir roghnóir isteach don seo

View File

@@ -29,9 +29,9 @@ zen-boost-css-picker =
.tooltiptext = Pick Selector
zen-boost-css-inspector =
.tooltiptext = Open Inspector
zen-bootst-color-contrast = ניגודיות
zen-bootst-color-brightness = בהירות
zen-bootst-color-original-saturation = Original Saturation
zen-boost-color-contrast = ניגודיות
zen-boost-color-brightness = בהירות
zen-boost-color-original-saturation = Original Saturation
zen-add-zap-helper = Click elements on the page to <b>Zap</b> them
zen-remove-zap-helper = ← Click to Unzap
zen-select-this = Insert selector for this

View File

@@ -29,9 +29,9 @@ zen-boost-css-picker =
.tooltiptext = Pick Selector
zen-boost-css-inspector =
.tooltiptext = Vizsgáló megnyitása
zen-bootst-color-contrast = Kontraszt
zen-bootst-color-brightness = Fényerő
zen-bootst-color-original-saturation = Eredeti szaturáció
zen-boost-color-contrast = Kontraszt
zen-boost-color-brightness = Fényerő
zen-boost-color-original-saturation = Eredeti szaturáció
zen-add-zap-helper = Click elements on the page to <b>Zap</b> them
zen-remove-zap-helper = ← Click to Unzap
zen-select-this = Insert selector for this

View File

@@ -29,9 +29,9 @@ 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-boost-color-contrast = Contrast
zen-boost-color-brightness = Brightness
zen-boost-color-original-saturation = Original Saturation
zen-add-zap-helper = Click elements on the page to <b>Zap</b> them
zen-remove-zap-helper = ← Click to Unzap
zen-select-this = Insert selector for this

View File

@@ -29,9 +29,9 @@ zen-boost-css-picker =
.tooltiptext = Veljari
zen-boost-css-inspector =
.tooltiptext = Opna rýni
zen-bootst-color-contrast = Birtuskil
zen-bootst-color-brightness = Birtustig
zen-bootst-color-original-saturation = Upprunaleg litmettun
zen-boost-color-contrast = Birtuskil
zen-boost-color-brightness = Birtustig
zen-boost-color-original-saturation = Upprunaleg litmettun
zen-add-zap-helper = Smelltu á atriði á síðunni til að <b>einangra</b> þau (zap)
zen-remove-zap-helper = ← Smelltu til að taka úr einangrun
zen-select-this = Setja inn veljara fyrir þetta

View File

@@ -29,9 +29,9 @@ 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-boost-color-contrast = Contrast
zen-boost-color-brightness = Brightness
zen-boost-color-original-saturation = Original Saturation
zen-add-zap-helper = Click elements on the page to <b>Zap</b> them
zen-remove-zap-helper = ← Click to Unzap
zen-select-this = Insert selector for this

View File

@@ -29,9 +29,9 @@ zen-boost-css-picker =
.tooltiptext = セレクターを選択
zen-boost-css-inspector =
.tooltiptext = インスペクタを開く
zen-bootst-color-contrast = コントラスト
zen-bootst-color-brightness = 明るさ
zen-bootst-color-original-saturation = 元の彩度
zen-boost-color-contrast = コントラスト
zen-boost-color-brightness = 明るさ
zen-boost-color-original-saturation = 元の彩度
zen-add-zap-helper = <b>Zap</b> にページ上の要素をクリックします
zen-remove-zap-helper = ←クリックして解除
zen-select-this = このセレクターを挿入

View File

@@ -29,9 +29,9 @@ zen-boost-css-picker =
.tooltiptext = 선택자 선택
zen-boost-css-inspector =
.tooltiptext = 검사기 열기
zen-bootst-color-contrast = 대비
zen-bootst-color-brightness = 밝기
zen-bootst-color-original-saturation = 원본 채도
zen-boost-color-contrast = 대비
zen-boost-color-brightness = 밝기
zen-boost-color-original-saturation = 원본 채도
zen-add-zap-helper = 페이지 내의 요소를 클릭해서 <b>날려</b>버리세요
zen-remove-zap-helper = ← 클릭하여 되살리기
zen-select-this = 이 요소의 선택자 삽입

View File

@@ -29,9 +29,9 @@ zen-boost-css-picker =
.tooltiptext = Pick Selector
zen-boost-css-inspector =
.tooltiptext = Open Inspector
zen-bootst-color-contrast = Kontrastas
zen-bootst-color-brightness = Šviesumas
zen-bootst-color-original-saturation = Original Saturation
zen-boost-color-contrast = Kontrastas
zen-boost-color-brightness = Šviesumas
zen-boost-color-original-saturation = Original Saturation
zen-add-zap-helper = Click elements on the page to <b>Zap</b> them
zen-remove-zap-helper = ← Click to Unzap
zen-select-this = Insert selector for this

View File

@@ -29,9 +29,9 @@ zen-boost-css-picker =
.tooltiptext = Velgselektor
zen-boost-css-inspector =
.tooltiptext = Åpne inspektør
zen-bootst-color-contrast = Kontrast
zen-bootst-color-brightness = Lysstyrke
zen-bootst-color-original-saturation = Opprinnelig fargemetning
zen-boost-color-contrast = Kontrast
zen-boost-color-brightness = Lysstyrke
zen-boost-color-original-saturation = Opprinnelig fargemetning
zen-add-zap-helper = Klikk elementer på siden for å <b>Zappe</b> dem
zen-remove-zap-helper = ← Klikk for å avzappe
zen-select-this = Sett inn slektor for dette

View File

@@ -29,9 +29,9 @@ 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-boost-color-contrast = Contrast
zen-boost-color-brightness = Brightness
zen-boost-color-original-saturation = Original Saturation
zen-add-zap-helper = Click elements on the page to <b>Zap</b> them
zen-remove-zap-helper = ← Click to Unzap
zen-select-this = Insert selector for this

View File

@@ -29,9 +29,9 @@ 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-boost-color-contrast = Contrast
zen-boost-color-brightness = Brightness
zen-boost-color-original-saturation = Original Saturation
zen-add-zap-helper = Click elements on the page to <b>Zap</b> them
zen-remove-zap-helper = ← Click to Unzap
zen-select-this = Insert selector for this

View File

@@ -29,9 +29,9 @@ zen-boost-css-picker =
.tooltiptext = Wybierz selektor
zen-boost-css-inspector =
.tooltiptext = Otwórz Inspektor
zen-bootst-color-contrast = Kontrast
zen-bootst-color-brightness = Jasność
zen-bootst-color-original-saturation = Oryginalne nasycenie
zen-boost-color-contrast = Kontrast
zen-boost-color-brightness = Jasność
zen-boost-color-original-saturation = Oryginalne nasycenie
zen-add-zap-helper = Kliknij elementy na stronie, aby je <b>ukryć</b>
zen-remove-zap-helper = ← Kliknij, aby cofnąć ukrycie
zen-select-this = Wstaw selektor dla tego elementu

View File

@@ -29,9 +29,9 @@ zen-boost-css-picker =
.tooltiptext = Ferramenta de Seleção
zen-boost-css-inspector =
.tooltiptext = Abrir Inspetor
zen-bootst-color-contrast = Contraste
zen-bootst-color-brightness = Brilho
zen-bootst-color-original-saturation = Saturação Original
zen-boost-color-contrast = Contraste
zen-boost-color-brightness = Brilho
zen-boost-color-original-saturation = Saturação Original
zen-add-zap-helper = Clique em elementos da página para dar um <b>Zap</b> neles
zen-remove-zap-helper = Clique para Deszapar
zen-select-this = Inserir seletor para isto

View File

@@ -29,9 +29,9 @@ 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-boost-color-contrast = Contrast
zen-boost-color-brightness = Brightness
zen-boost-color-original-saturation = Original Saturation
zen-add-zap-helper = Click elements on the page to <b>Zap</b> them
zen-remove-zap-helper = ← Click to Unzap
zen-select-this = Insert selector for this

View File

@@ -29,9 +29,9 @@ 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-boost-color-contrast = Contrast
zen-boost-color-brightness = Brightness
zen-boost-color-original-saturation = Original Saturation
zen-add-zap-helper = Click elements on the page to <b>Zap</b> them
zen-remove-zap-helper = ← Click to Unzap
zen-select-this = Insert selector for this

View File

@@ -29,9 +29,9 @@ 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-boost-color-contrast = Contrast
zen-boost-color-brightness = Brightness
zen-boost-color-original-saturation = Original Saturation
zen-add-zap-helper = Click elements on the page to <b>Zap</b> them
zen-remove-zap-helper = ← Click to Unzap
zen-select-this = Insert selector for this

View File

@@ -29,9 +29,9 @@ 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-boost-color-contrast = Contrast
zen-boost-color-brightness = Brightness
zen-boost-color-original-saturation = Original Saturation
zen-add-zap-helper = Click elements on the page to <b>Zap</b> them
zen-remove-zap-helper = ← Click to Unzap
zen-select-this = Insert selector for this

View File

@@ -29,9 +29,9 @@ zen-boost-css-picker =
.tooltiptext = Välj väljare
zen-boost-css-inspector =
.tooltiptext = Öppna inspektör
zen-bootst-color-contrast = Kontrast
zen-bootst-color-brightness = Ljusstyrka
zen-bootst-color-original-saturation = Ursprunglig mättnad
zen-boost-color-contrast = Kontrast
zen-boost-color-brightness = Ljusstyrka
zen-boost-color-original-saturation = Ursprunglig mättnad
zen-add-zap-helper = Klicka på element på sidan för att <b>zappa</b> dem
zen-remove-zap-helper = ← Klicka för att avzappa
zen-select-this = Infoga väljare för detta

View File

@@ -29,9 +29,9 @@ 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-boost-color-contrast = Contrast
zen-boost-color-brightness = Brightness
zen-boost-color-original-saturation = Original Saturation
zen-add-zap-helper = Click elements on the page to <b>Zap</b> them
zen-remove-zap-helper = ← Click to Unzap
zen-select-this = Insert selector for this

View File

@@ -29,9 +29,9 @@ zen-boost-css-picker =
.tooltiptext = Seçici seç
zen-boost-css-inspector =
.tooltiptext = Denetleyiciyi aç
zen-bootst-color-contrast = Kontrast
zen-bootst-color-brightness = Parlaklık
zen-bootst-color-original-saturation = Orijinal doygunluk
zen-boost-color-contrast = Kontrast
zen-boost-color-brightness = Parlaklık
zen-boost-color-original-saturation = Orijinal doygunluk
zen-add-zap-helper = Sayfadaki ögelere tıklayarak onları <b>Zap</b> ile kaldırın
zen-remove-zap-helper = ← Unzap için tıklayın
zen-select-this = Bu öge için seçici ekle

View File

@@ -29,9 +29,9 @@ zen-boost-css-picker =
.tooltiptext = Вибір селектора
zen-boost-css-inspector =
.tooltiptext = Відкрити інспектора
zen-bootst-color-contrast = Контраст
zen-bootst-color-brightness = Яскравість
zen-bootst-color-original-saturation = Оригінальна насиченість
zen-boost-color-contrast = Контраст
zen-boost-color-brightness = Яскравість
zen-boost-color-original-saturation = Оригінальна насиченість
zen-add-zap-helper = Клацніть на елементи на сторінці, щоби <b>сховати</b> їх
zen-remove-zap-helper = ← Клацніть, аби знову показати
zen-select-this = Вставити селектор для цього

View File

@@ -29,9 +29,9 @@ zen-boost-css-picker =
.tooltiptext = Chọn phần tử
zen-boost-css-inspector =
.tooltiptext = Mở trình kiểm tra
zen-bootst-color-contrast = Độ tương phản
zen-bootst-color-brightness = Độ sáng
zen-bootst-color-original-saturation = Độ bão hòa gốc
zen-boost-color-contrast = Độ tương phản
zen-boost-color-brightness = Độ sáng
zen-boost-color-original-saturation = Độ bão hòa gốc
zen-add-zap-helper = Nhấp vào các phần tử trên trang để <b>Khử</b> chúng
zen-remove-zap-helper = ← Nhấp để khôi phục
zen-select-this = Nhập bộ chọn cho phần tử này

View File

@@ -29,9 +29,9 @@ zen-boost-css-picker =
.tooltiptext = 选取选择器
zen-boost-css-inspector =
.tooltiptext = 打开查看器
zen-bootst-color-contrast = 对比度
zen-bootst-color-brightness = 亮度
zen-bootst-color-original-saturation = 初始饱和度
zen-boost-color-contrast = 对比度
zen-boost-color-brightness = 亮度
zen-boost-color-original-saturation = 初始饱和度
zen-add-zap-helper = 点击页面上的元素以将其<b>屏蔽</b>
zen-remove-zap-helper = ← 点击以取消屏蔽
zen-select-this = 插入此元素的选择器

View File

@@ -29,9 +29,9 @@ zen-boost-css-picker =
.tooltiptext = 汲取選擇器
zen-boost-css-inspector =
.tooltiptext = 開啟檢測器
zen-bootst-color-contrast = 對比
zen-bootst-color-brightness = 亮度
zen-bootst-color-original-saturation = 飽和度
zen-boost-color-contrast = 對比
zen-boost-color-brightness = 亮度
zen-boost-color-original-saturation = 飽和度
zen-add-zap-helper = 選擇要 <b>Zap</b>的元素
zen-remove-zap-helper = ← 按此取消zap
zen-select-this = 加入此元素的選擇器

View File

@@ -28,7 +28,7 @@
"surfer": "surfer",
"test": "python3 scripts/run_tests.py",
"test:dbg": "python3 scripts/run_tests.py --jsdebugger --debug-on-failure",
"test:gtest": "cd engine && ./mach gtest",
"test:gtest": "cd engine && ./mach gtest Zen*",
"ffprefs": "cd tools/ffprefs && cargo run --bin ffprefs -- ../../",
"lc": "surfer license-check",
"lc:fix": "surfer license-check --fix",

View File

@@ -20,3 +20,4 @@
#include ../../../zen/fonts/jar.inc.mn
#include ../../../zen/boosts/jar.inc.mn
#include ../../../zen/live-folders/jar.inc.mn
#include ../../../zen/space-routing/jar.inc.mn

View File

@@ -41,6 +41,7 @@
<command id="cmd_zenCtxDeleteWorkspace" />
<command id="cmd_zenUnloadWorkspace" />
<command id="cmd_zenUnloadAllOtherWorkspace" />
<command id="cmd_zenOpenSpaceRoutingSettings" />
<command id="cmd_zenChangeWorkspaceName" />
<command id="cmd_zenChangeWorkspaceIcon" />
<command id="cmd_zenReorderWorkspaces" />

View File

@@ -11,4 +11,5 @@
<link rel="localization" href="browser/zen-folders.ftl"/>
<link rel="localization" href="browser/zen-boosts.ftl"/>
<link rel="localization" href="browser/zen-live-folders.ftl"/>
<link rel="localization" href="browser/zen-space-routing.ftl"/>
</linkset>

View File

@@ -33,6 +33,8 @@
<menuseparator/>
<menuitem data-l10n-id="zen-panel-ui-workspaces-create" command="cmd_zenOpenWorkspaceCreation"/>
<menuitem id="context_zenDeleteWorkspace" data-l10n-id="zen-workspaces-panel-context-delete" command="cmd_zenCtxDeleteWorkspace"/>
<menuseparator/>
<menuitem id="context_zenSpaceRoutingSettings" data-l10n-id="zen-space-routing-settings" command="cmd_zenOpenSpaceRoutingSettings"/>
</menupopup>
<menupopup id="zenFolderActions">

View File

@@ -825,6 +825,7 @@ var zenIgnoreKeyboardShortcutIDs = [
"key_enterFullScreen_compat",
"key_exitFullScreen_old",
"key_exitFullScreen_compat",
"key_duplicateTab",
];
var zenIgnoreKeyboardShortcutL10n = [

View File

@@ -1,5 +1,5 @@
diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js
index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88f4eec282 100644
index 43fb79a3060e20f671ae6ffc26350c7abf497702..2da16e06541438ece4a3ae3a1663a1559780fe22 100644
--- a/browser/components/tabbrowser/content/tabbrowser.js
+++ b/browser/components/tabbrowser/content/tabbrowser.js
@@ -502,6 +502,7 @@
@@ -264,24 +264,33 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
focusUrlBar: true,
});
resolve(this.selectedBrowser);
@@ -3285,6 +3369,9 @@
@@ -3285,6 +3369,10 @@
schemelessInput,
hasValidUserGestureActivation = false,
textDirectiveUserActivation = false,
+ _forZenEmptyTab,
+ essential,
+ zenWorkspaceId,
+ skipRoute = false,
} = {}
) {
// all callers of addTab that pass a params object need to pass
@@ -3295,10 +3382,17 @@
@@ -3295,10 +3383,25 @@
);
}
+ const beforeRouteResult = window.gZenSpaceRoutingManager.onBeforeAddTab(uriString, { skipRoute, pinned, tabGroup, fromExternal }, window);
+ if (beforeRouteResult.shouldEarlyExit) {
+ return null;
+ }
+
+ let hasZenDefaultUserContextId = false;
+ let zenForcedWorkspaceId = undefined;
+ if (typeof gZenWorkspaces !== "undefined" && !_forZenEmptyTab) {
+ [userContextId, hasZenDefaultUserContextId, zenForcedWorkspaceId] = gZenWorkspaces.getContextIdIfNeeded(userContextId, fromExternal);
+ if (beforeRouteResult.userContextId) {
+ userContextId = beforeRouteResult.userContextId;
+ hasZenDefaultUserContextId = true;
+ } else if (typeof gZenWorkspaces !== "undefined" && !_forZenEmptyTab) {
+ [userContextId, hasZenDefaultUserContextId, zenForcedWorkspaceId] = gZenWorkspaces.getContextIdIfNeeded(userContextId, fromExternal, triggeringPrincipal);
+ }
+
if (!UserInteraction.running("browser.tabs.opening", window)) {
@@ -292,7 +301,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
// If we're opening a foreground tab, set the owner by default.
ownerTab ??= inBackground ? null : this.selectedTab;
@@ -3306,6 +3400,7 @@
@@ -3306,6 +3409,7 @@
if (this.selectedTab.owner) {
this.selectedTab.owner = null;
}
@@ -300,7 +309,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
// Find the tab that opened this one, if any. This is used for
// determining positioning, and inherited attributes such as the
@@ -3358,6 +3453,22 @@
@@ -3358,6 +3462,22 @@
noInitialLabel,
skipBackgroundNotify,
});
@@ -323,7 +332,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
if (insertTab) {
// Insert the tab into the tab container in the correct position.
this.#insertTabAtIndex(t, {
@@ -3366,6 +3477,7 @@
@@ -3366,6 +3486,7 @@
ownerTab,
openerTab,
pinned,
@@ -331,7 +340,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
bulkOrderedOpen,
tabGroup: tabGroup ?? openerTab?.group,
});
@@ -3384,6 +3496,7 @@
@@ -3384,6 +3505,7 @@
openWindowInfo,
skipLoad,
triggeringRemoteType,
@@ -339,7 +348,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
}));
if (focusUrlBar) {
@@ -3508,6 +3621,12 @@
@@ -3508,6 +3630,12 @@
}
}
@@ -352,7 +361,17 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
// Additionally send pinned tab events
if (pinned) {
this.#notifyPinnedStatus(t);
@@ -3750,6 +3869,7 @@
@@ -3518,6 +3646,9 @@
if (!inBackground) {
this.selectedTab = t;
}
+
+ window.gZenSpaceRoutingManager.onAfterAddTab(uriString, t, { skipRoute: skipRoute || _forZenEmptyTab, fromExternal, pinned, tabGroup }, window, beforeRouteResult);
+
return t;
}
@@ -3750,6 +3881,7 @@
isAdoptingGroup = false,
isUserTriggered = false,
telemetryUserCreateSource = "unknown",
@@ -360,7 +379,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
} = {}
) {
if (
@@ -3760,9 +3880,6 @@
@@ -3760,9 +3892,6 @@
!this.isSplitViewWrapper(tabOrSplitView)
)
) {
@@ -370,7 +389,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
}
if (!color) {
@@ -3783,9 +3900,14 @@
@@ -3783,9 +3912,14 @@
label,
isAdoptingGroup
);
@@ -387,7 +406,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
);
group.addTabs(tabsAndSplitViews);
@@ -3906,7 +4028,7 @@
@@ -3906,7 +4040,7 @@
}
this.#handleTabMove(tab, () =>
@@ -396,7 +415,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
);
}
@@ -3990,6 +4112,7 @@
@@ -3990,6 +4124,7 @@
color: group.color,
insertBefore: newTabs[0],
isAdoptingGroup: true,
@@ -404,7 +423,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
});
}
@@ -4200,6 +4323,7 @@
@@ -4200,6 +4335,7 @@
openWindowInfo,
skipLoad,
triggeringRemoteType,
@@ -412,7 +431,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
}
) {
// If we don't have a preferred remote type (or it is `NOT_REMOTE`), and
@@ -4269,6 +4393,7 @@
@@ -4269,6 +4405,7 @@
openWindowInfo,
name,
skipLoad,
@@ -420,7 +439,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
});
}
@@ -4482,9 +4607,9 @@
@@ -4482,9 +4619,9 @@
}
// Add a new tab if needed.
@@ -432,7 +451,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
let url = "about:blank";
if (tabData.entries?.length) {
@@ -4521,8 +4646,10 @@
@@ -4521,8 +4658,10 @@
insertTab: false,
skipLoad: true,
preferredRemoteType,
@@ -444,7 +463,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
if (select) {
tabToSelect = tab;
}
@@ -4544,7 +4671,8 @@
@@ -4544,7 +4683,8 @@
this.pinTab(tab);
// Then ensure all the tab open/pinning information is sent.
this._fireTabOpen(tab, {});
@@ -454,7 +473,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
let { groupId } = tabData;
const tabGroup = tabGroupWorkingData.get(groupId);
// if a tab refers to a tab group we don't know, skip any group
@@ -4564,7 +4692,10 @@
@@ -4564,7 +4704,10 @@
tabGroup.stateData.id,
tabGroup.stateData.color,
tabGroup.stateData.collapsed,
@@ -466,7 +485,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
);
tabsFragment.appendChild(tabGroup.node);
}
@@ -4619,9 +4750,21 @@
@@ -4619,9 +4762,21 @@
// to remove the old selected tab.
if (tabToSelect) {
let leftoverTab = this.selectedTab;
@@ -488,7 +507,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
if (tabs.length > 1 || !tabs[0].selected) {
this._updateTabsAfterInsert();
@@ -4812,11 +4955,14 @@
@@ -4812,11 +4967,14 @@
if (ownerTab) {
tab.owner = ownerTab;
}
@@ -504,7 +523,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
if (
!bulkOrderedOpen &&
((openerTab &&
@@ -4828,7 +4974,7 @@
@@ -4828,7 +4986,7 @@
let lastRelatedTab =
openerTab && this._lastRelatedTabMap.get(openerTab);
let previousTab = lastRelatedTab || openerTab || this.selectedTab;
@@ -513,7 +532,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
tabGroup = previousTab.group;
}
if (
@@ -4844,7 +4990,7 @@
@@ -4844,7 +5002,7 @@
previousTab.splitview
) + 1;
} else if (previousTab.visible) {
@@ -522,7 +541,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
} else if (previousTab == FirefoxViewHandler.tab) {
elementIndex = 0;
}
@@ -4872,14 +5018,14 @@
@@ -4872,14 +5030,14 @@
}
// Ensure index is within bounds.
if (tab.pinned) {
@@ -541,7 +560,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
if (pinned && !itemAfter?.pinned) {
itemAfter = null;
@@ -4896,7 +5042,7 @@
@@ -4896,7 +5054,7 @@
this.tabContainer._invalidateCachedTabs();
@@ -550,7 +569,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
if (
(this.isTab(itemAfter) && itemAfter.group == tabGroup) ||
this.isSplitViewWrapper(itemAfter)
@@ -4927,7 +5073,11 @@
@@ -4927,7 +5085,11 @@
const tabContainer = pinned
? this.tabContainer.pinnedTabsContainer
: this.tabContainer;
@@ -562,7 +581,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
}
if (tab.group?.collapsed) {
@@ -4942,6 +5092,7 @@
@@ -4942,6 +5104,7 @@
if (pinned) {
this._updateTabBarForPinnedTabs();
}
@@ -570,7 +589,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
TabBarVisibility.update();
}
@@ -5490,6 +5641,7 @@
@@ -5490,6 +5653,7 @@
telemetrySource,
} = {}
) {
@@ -578,7 +597,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
// When 'closeWindowWithLastTab' pref is enabled, closing all tabs
// can be considered equivalent to closing the window.
if (
@@ -5579,6 +5731,7 @@
@@ -5579,6 +5743,7 @@
if (lastToClose) {
this.removeTab(lastToClose, aParams);
}
@@ -586,7 +605,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
} catch (e) {
console.error(e);
}
@@ -5624,6 +5777,14 @@
@@ -5624,6 +5789,14 @@
return;
}
@@ -601,7 +620,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
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
@@ -5631,6 +5792,9 @@
@@ -5631,6 +5804,9 @@
// state).
let tabWidth = window.windowUtils.getBoundsWithoutFlushing(aTab).width;
let isLastTab = this.#isLastTabInWindow(aTab);
@@ -611,7 +630,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
if (
!this._beginRemoveTab(aTab, {
closeWindowFastpath: true,
@@ -5642,13 +5806,14 @@
@@ -5642,13 +5818,14 @@
telemetrySource,
})
) {
@@ -627,7 +646,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
let lockTabSizing =
!this.tabContainer.verticalMode &&
!aTab.pinned &&
@@ -5679,7 +5844,13 @@
@@ -5679,7 +5856,13 @@
// We're not animating, so we can cancel the animation stopwatch.
Glean.browserTabclose.timeAnim.cancel(aTab._closeTimeAnimTimerId);
aTab._closeTimeAnimTimerId = null;
@@ -642,7 +661,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
return;
}
@@ -5813,7 +5984,7 @@
@@ -5813,7 +5996,7 @@
closeWindowWithLastTab != null
? closeWindowWithLastTab
: !window.toolbar.visible ||
@@ -651,7 +670,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
if (closeWindow) {
// We've already called beforeunload on all the relevant tabs if we get here,
@@ -5837,6 +6008,7 @@
@@ -5837,6 +6020,7 @@
newTab = true;
}
@@ -659,7 +678,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
aTab._endRemoveArgs = [closeWindow, newTab];
// swapBrowsersAndCloseOther will take care of closing the window without animation.
@@ -5877,13 +6049,7 @@
@@ -5877,13 +6061,7 @@
aTab._mouseleave();
if (newTab) {
@@ -674,7 +693,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
} else {
TabBarVisibility.update();
}
@@ -6016,6 +6182,7 @@
@@ -6016,6 +6194,7 @@
this.tabs[i]._tPos = i;
}
@@ -682,7 +701,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
if (!this._windowIsClosing) {
// update tab close buttons state
this.tabContainer._updateCloseButtons();
@@ -6201,6 +6368,7 @@
@@ -6201,6 +6380,7 @@
memory_after: await getTotalMemoryUsage(),
time_to_unload_in_ms: timeElapsed,
});
@@ -690,7 +709,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
}
/**
@@ -6246,6 +6414,7 @@
@@ -6246,6 +6426,7 @@
}
let excludeTabs = new Set(aExcludeTabs);
@@ -698,7 +717,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
// If this tab has a successor, it should be selectable, since
// hiding or closing a tab removes that tab as a successor.
@@ -6258,15 +6427,22 @@
@@ -6258,15 +6439,22 @@
!excludeTabs.has(aTab.owner) &&
Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose")
) {
@@ -723,7 +742,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
let tab = this.tabContainer.findNextTab(aTab, {
direction: 1,
filter: _tab => remainingTabs.includes(_tab),
@@ -6280,7 +6456,7 @@
@@ -6280,7 +6468,7 @@
}
if (tab) {
@@ -732,7 +751,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
}
// If no qualifying visible tab was found, see if there is a tab in
@@ -6301,7 +6477,7 @@
@@ -6301,7 +6489,7 @@
});
}
@@ -741,7 +760,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
}
_blurTab(aTab) {
@@ -6312,7 +6488,7 @@
@@ -6312,7 +6500,7 @@
* @returns {boolean}
* False if swapping isn't permitted, true otherwise.
*/
@@ -750,7 +769,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
// Do not allow transfering a private tab to a non-private window
// and vice versa.
if (
@@ -6366,6 +6542,7 @@
@@ -6366,6 +6554,7 @@
// fire the beforeunload event in the process. Close the other
// window if this was its last tab.
if (
@@ -758,7 +777,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
!remoteBrowser._beginRemoveTab(aOtherTab, {
adoptedByTab: aOurTab,
closeWindowWithLastTab: true,
@@ -6377,7 +6554,7 @@
@@ -6377,7 +6566,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.
@@ -767,7 +786,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
if (closeWindow) {
let win = aOtherTab.ownerGlobal;
win.windowUtils.suppressAnimation(true);
@@ -6511,11 +6688,13 @@
@@ -6511,11 +6700,13 @@
}
// Finish tearing down the tab that's going away.
@@ -781,7 +800,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
this.setTabTitle(aOurTab);
@@ -6717,10 +6896,10 @@
@@ -6717,10 +6908,10 @@
SessionStore.deleteCustomTabValue(aTab, "hiddenBy");
}
@@ -794,7 +813,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
aTab.selected ||
aTab.closing ||
// Tabs that are sharing the screen, microphone or camera cannot be hidden.
@@ -6780,7 +6959,8 @@
@@ -6780,7 +6971,8 @@
* @param {object} [aOptions={}]
* Key-value pairs that will be serialized into the features string.
*/
@@ -804,7 +823,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
if (this.tabs.length == 1) {
return null;
}
@@ -6797,7 +6977,7 @@
@@ -6797,7 +6989,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);
@@ -813,7 +832,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
private: PrivateBrowsingUtils.isWindowPrivate(window),
features: Object.entries(aOptions)
.map(([key, value]) => `${key}=${value}`)
@@ -6805,6 +6985,8 @@
@@ -6805,6 +6997,8 @@
openerWindow: window,
args,
});
@@ -822,7 +841,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
}
/**
@@ -6917,7 +7099,7 @@
@@ -6917,7 +7111,7 @@
* `true` if element is a `<tab-group>`
*/
isTabGroup(element) {
@@ -831,7 +850,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
}
/**
@@ -7002,8 +7184,8 @@
@@ -7002,8 +7196,8 @@
}
// Don't allow mixing pinned and unpinned tabs.
@@ -842,7 +861,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
} else {
tabIndex = Math.max(tabIndex, this.pinnedTabCount);
}
@@ -7049,8 +7231,8 @@
@@ -7049,8 +7243,8 @@
this.#handleTabMove(
element,
() => {
@@ -853,7 +872,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
neighbor = neighbor.group;
}
if (neighbor?.splitview) {
@@ -7061,6 +7243,12 @@
@@ -7061,6 +7255,12 @@
return;
}
}
@@ -866,7 +885,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
if (movingForwards && neighbor) {
neighbor.after(element);
@@ -7119,23 +7307,31 @@
@@ -7119,23 +7319,31 @@
#moveTabNextTo(element, targetElement, moveBefore = false, metricsContext) {
if (this.isTabGroupLabel(targetElement)) {
targetElement = targetElement.group;
@@ -904,7 +923,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
} 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
@@ -7148,12 +7344,35 @@
@@ -7148,12 +7356,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.
@@ -941,7 +960,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
// 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.
@@ -7162,6 +7381,7 @@
@@ -7162,6 +7393,7 @@
}
let getContainer = () =>
@@ -949,7 +968,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
element.pinned
? this.tabContainer.pinnedTabsContainer
: this.tabContainer;
@@ -7170,11 +7390,15 @@
@@ -7170,11 +7402,15 @@
element,
() => {
if (moveBefore) {
@@ -966,7 +985,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
}
},
metricsContext
@@ -7248,11 +7472,15 @@
@@ -7248,11 +7484,15 @@
* @param {TabMetricsContext} [metricsContext]
*/
moveTabToExistingGroup(aTab, aGroup, metricsContext) {
@@ -985,7 +1004,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
}
if (aTab.group && aTab.group.id === aGroup.id) {
return;
@@ -7324,6 +7552,7 @@
@@ -7324,6 +7564,7 @@
let state = {
tabIndex: tab._tPos,
@@ -993,7 +1012,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
};
if (tab.visible) {
state.elementIndex = tab.elementIndex;
@@ -7355,7 +7584,7 @@
@@ -7355,7 +7596,7 @@
let changedSplitView =
previousTabState.splitViewId != currentTabState.splitViewId;
@@ -1002,7 +1021,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
tab.dispatchEvent(
new CustomEvent("TabMove", {
bubbles: true,
@@ -7402,6 +7631,10 @@
@@ -7402,6 +7643,10 @@
moveActionCallback();
@@ -1013,7 +1032,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
// Clear tabs cache after moving nodes because the order of tabs may have
// changed.
this.tabContainer._invalidateCachedTabs();
@@ -7452,7 +7685,22 @@
@@ -7452,7 +7697,22 @@
* @returns {object}
* The new tab in the current window, null if the tab couldn't be adopted.
*/
@@ -1037,7 +1056,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
// 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
@@ -7495,6 +7743,8 @@
@@ -7495,6 +7755,8 @@
}
params.skipLoad = true;
let newTab = this.addWebTab("about:blank", params);
@@ -1046,7 +1065,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
aTab.container.tabDragAndDrop.finishAnimateTabMove();
@@ -8205,7 +8455,7 @@
@@ -8205,7 +8467,7 @@
// preventDefault(). It will still raise the window if appropriate.
return;
}
@@ -1055,7 +1074,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
window.focus();
aEvent.preventDefault();
}
@@ -8222,7 +8472,6 @@
@@ -8222,7 +8484,6 @@
on_TabGroupCollapse(aEvent) {
aEvent.target.tabs.forEach(tab => {
@@ -1063,7 +1082,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
});
}
@@ -8556,7 +8805,9 @@
@@ -8556,7 +8817,9 @@
let filter = this._tabFilters.get(tab);
if (filter) {
@@ -1073,7 +1092,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
let listener = this._tabListeners.get(tab);
if (listener) {
@@ -9359,6 +9610,7 @@
@@ -9359,6 +9622,7 @@
aWebProgress.isTopLevel
) {
this.mTab.setAttribute("busy", "true");
@@ -1081,7 +1100,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
gBrowser._tabAttrModified(this.mTab, ["busy"]);
this.mTab._notselectedsinceload = !this.mTab.selected;
}
@@ -9439,6 +9691,7 @@
@@ -9439,6 +9703,7 @@
// known defaults. Note we use the original URL since about:newtab
// redirects to a prerendered page.
const shouldRemoveFavicon =
@@ -1089,7 +1108,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
!this.mBrowser.mIconURL &&
!ignoreBlank &&
!(originalLocation.spec in FAVICON_DEFAULTS);
@@ -9613,13 +9866,6 @@
@@ -9613,13 +9878,6 @@
this.mBrowser.originalURI = aRequest.originalURI;
}
@@ -1103,7 +1122,7 @@ index 43fb79a3060e20f671ae6ffc26350c7abf497702..146b1559b8430773bd4ec173a8f4fe88
}
let userContextId = this.mBrowser.getAttribute("usercontextid") || 0;
@@ -10507,7 +10753,8 @@ var TabContextMenu = {
@@ -10507,7 +10765,8 @@ var TabContextMenu = {
);
contextUnpinSelectedTabs.hidden =
!this.contextTab.pinned || !this.multiselected;

View File

@@ -1,5 +1,5 @@
diff --git a/browser/components/urlbar/content/UrlbarInput.mjs b/browser/components/urlbar/content/UrlbarInput.mjs
index d6615ec5a29f3e3327ac4171f3fc5d9a69bd09fe..e1128b0d7f5accfd48af27f99e3b2e6463b45191 100644
index d6615ec5a29f3e3327ac4171f3fc5d9a69bd09fe..c166b7de23c35716bf8c51b6b9c72f771f0a75a8 100644
--- a/browser/components/urlbar/content/UrlbarInput.mjs
+++ b/browser/components/urlbar/content/UrlbarInput.mjs
@@ -98,6 +98,13 @@ const lazy = XPCOMUtils.declareLazy({
@@ -132,15 +132,17 @@ index d6615ec5a29f3e3327ac4171f3fc5d9a69bd09fe..e1128b0d7f5accfd48af27f99e3b2e64
// 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")) {
@@ -2891,6 +2966,27 @@ ${
@@ -2891,6 +2966,29 @@ ${
return;
}
+ if (this._zenHandleUrlbarClose) {
+ this._zenHandleUrlbarClose();
+ } else if (!this._untrimmedValue || this.searchMode) {
+ } else if (!this._untrimmedValue || this.searchMode || this.window.gZenVerticalTabsManager._hasSetSingleToolbar) {
+ // Restore the current page URL when the urlbar is empty on blur
+ this.handleRevert();
+ this.window.requestAnimationFrame(() => {
+ this.handleRevert();
+ });
+ }
+
+ // Arc like URLbar: Blur the input on exit
@@ -160,7 +162,7 @@ index d6615ec5a29f3e3327ac4171f3fc5d9a69bd09fe..e1128b0d7f5accfd48af27f99e3b2e64
this.removeAttribute("breakout-extend");
this.#updateTextboxPosition();
}
@@ -2921,7 +3017,7 @@ ${
@@ -2921,7 +3019,7 @@ ${
forceUnifiedSearchButtonAvailable = false
) {
let prevState = this.getAttribute("pageproxystate");
@@ -169,7 +171,7 @@ index d6615ec5a29f3e3327ac4171f3fc5d9a69bd09fe..e1128b0d7f5accfd48af27f99e3b2e64
this.setAttribute("pageproxystate", state);
this._inputContainer.setAttribute("pageproxystate", state);
this._identityBox?.setAttribute("pageproxystate", state);
@@ -3198,10 +3294,12 @@ ${
@@ -3198,10 +3296,12 @@ ${
return;
}
this.style.top = px(
@@ -182,7 +184,7 @@ index d6615ec5a29f3e3327ac4171f3fc5d9a69bd09fe..e1128b0d7f5accfd48af27f99e3b2e64
);
}
@@ -3260,9 +3358,10 @@ ${
@@ -3260,9 +3360,10 @@ ${
return;
}
@@ -194,7 +196,7 @@ index d6615ec5a29f3e3327ac4171f3fc5d9a69bd09fe..e1128b0d7f5accfd48af27f99e3b2e64
);
this.style.setProperty(
"--urlbar-height",
@@ -3768,6 +3867,7 @@ ${
@@ -3768,6 +3869,7 @@ ${
}
_toggleActionOverride(event) {
@@ -202,7 +204,7 @@ index d6615ec5a29f3e3327ac4171f3fc5d9a69bd09fe..e1128b0d7f5accfd48af27f99e3b2e64
if (
event.keyCode == KeyEvent.DOM_VK_SHIFT ||
event.keyCode == KeyEvent.DOM_VK_ALT ||
@@ -3880,8 +3980,8 @@ ${
@@ -3880,8 +3982,8 @@ ${
if (!this.#isAddressbar) {
return val;
}
@@ -213,7 +215,7 @@ index d6615ec5a29f3e3327ac4171f3fc5d9a69bd09fe..e1128b0d7f5accfd48af27f99e3b2e64
: val;
// Only trim value if the directionality doesn't change to RTL and we're not
// showing a strikeout https protocol.
@@ -4180,6 +4280,7 @@ ${
@@ -4180,6 +4282,7 @@ ${
resultDetails = null,
browser = this.window.gBrowser.selectedBrowser
) {
@@ -221,7 +223,7 @@ index d6615ec5a29f3e3327ac4171f3fc5d9a69bd09fe..e1128b0d7f5accfd48af27f99e3b2e64
if (this.#isAddressbar) {
this.#prepareAddressbarLoad(
url,
@@ -4291,6 +4392,10 @@ ${
@@ -4291,6 +4394,10 @@ ${
}
reuseEmpty = true;
}
@@ -232,7 +234,7 @@ index d6615ec5a29f3e3327ac4171f3fc5d9a69bd09fe..e1128b0d7f5accfd48af27f99e3b2e64
if (
where == "tab" &&
reuseEmpty &&
@@ -4298,6 +4403,9 @@ ${
@@ -4298,6 +4405,9 @@ ${
) {
where = "current";
}
@@ -242,7 +244,7 @@ index d6615ec5a29f3e3327ac4171f3fc5d9a69bd09fe..e1128b0d7f5accfd48af27f99e3b2e64
return where;
}
@@ -4552,6 +4660,7 @@ ${
@@ -4552,6 +4662,7 @@ ${
this.setResultForCurrentValue(null);
this.handleCommand();
this.controller.clearLastQueryContextCache();
@@ -250,7 +252,7 @@ index d6615ec5a29f3e3327ac4171f3fc5d9a69bd09fe..e1128b0d7f5accfd48af27f99e3b2e64
this._suppressStartQuery = false;
});
@@ -4559,7 +4668,6 @@ ${
@@ -4559,7 +4670,6 @@ ${
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.
@@ -258,7 +260,7 @@ index d6615ec5a29f3e3327ac4171f3fc5d9a69bd09fe..e1128b0d7f5accfd48af27f99e3b2e64
let controller =
this.document.commandDispatcher.getControllerForCommand("cmd_paste");
@@ -4715,7 +4823,11 @@ ${
@@ -4715,7 +4825,11 @@ ${
if (!engineName && !source && !this.hasAttribute("searchmode")) {
return;
}
@@ -271,7 +273,7 @@ index d6615ec5a29f3e3327ac4171f3fc5d9a69bd09fe..e1128b0d7f5accfd48af27f99e3b2e64
if (this._searchModeIndicatorTitle) {
this._searchModeIndicatorTitle.textContent = "";
this._searchModeIndicatorTitle.removeAttribute("data-l10n-id");
@@ -5031,6 +5143,7 @@ ${
@@ -5031,6 +5145,7 @@ ${
this.document.l10n.setAttributes(
this.inputField,
@@ -279,7 +281,7 @@ index d6615ec5a29f3e3327ac4171f3fc5d9a69bd09fe..e1128b0d7f5accfd48af27f99e3b2e64
l10nId,
l10nId == "urlbar-placeholder-with-name"
? { name: engineName }
@@ -5156,6 +5269,11 @@ ${
@@ -5156,6 +5271,11 @@ ${
}
_on_click(event) {
@@ -291,7 +293,7 @@ index d6615ec5a29f3e3327ac4171f3fc5d9a69bd09fe..e1128b0d7f5accfd48af27f99e3b2e64
switch (event.target) {
case this.inputField:
case this._inputContainer:
@@ -5242,7 +5360,7 @@ ${
@@ -5242,7 +5362,7 @@ ${
}
}
@@ -300,7 +302,7 @@ index d6615ec5a29f3e3327ac4171f3fc5d9a69bd09fe..e1128b0d7f5accfd48af27f99e3b2e64
this.view.autoOpen({ event });
} else {
if (this._untrimOnFocusAfterKeydown) {
@@ -5282,9 +5400,16 @@ ${
@@ -5282,9 +5402,16 @@ ${
}
_on_mousedown(event) {
@@ -318,7 +320,7 @@ index d6615ec5a29f3e3327ac4171f3fc5d9a69bd09fe..e1128b0d7f5accfd48af27f99e3b2e64
if (
event.composedTarget != this.inputField &&
event.composedTarget != this._inputContainer
@@ -5294,6 +5419,10 @@ ${
@@ -5294,6 +5421,10 @@ ${
this.focusedViaMousedown = !this.focused;
this.#preventClickSelectsAll = this.focused;
@@ -329,7 +331,7 @@ index d6615ec5a29f3e3327ac4171f3fc5d9a69bd09fe..e1128b0d7f5accfd48af27f99e3b2e64
// Keep the focus status, since the attribute may be changed
// upon calling this.focus().
@@ -5329,7 +5458,7 @@ ${
@@ -5329,7 +5460,7 @@ ${
}
// 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.
@@ -338,7 +340,7 @@ index d6615ec5a29f3e3327ac4171f3fc5d9a69bd09fe..e1128b0d7f5accfd48af27f99e3b2e64
break;
}
@@ -5636,7 +5765,7 @@ ${
@@ -5636,7 +5767,7 @@ ${
// When we are in actions search mode we can show more results so
// increase the limit.
let maxResults =

View File

@@ -51,7 +51,8 @@
}
#PanelUI-zen-gradient-generator-color-remove,
#zen-gradient-generator-color-remove {
#zen-gradient-generator-color-remove,
.sr-remove {
list-style-image: url("unpin.svg") !important;
}
@@ -134,6 +135,10 @@
list-style-image: url("arrow-right.svg");
}
.sr-open-in-icon {
list-style-image: url("arrow-corner-down-right.svg");
}
#PanelUI-menu-button,
#appMenu-more-button2,
.zen-workspaces-actions,
@@ -520,7 +525,8 @@
list-style-image: url("permissions-fill.svg");
}
&[boosting] image {
color: var(--color-accent-primary);
fill-opacity: 1 !important;
color: var(--zen-sidebar-themed-icon-fill);
list-style-image: url("permissions-fill.svg");
}
@@ -537,7 +543,9 @@
display: flex;
width: 100%;
height: 100%;
color: var(--color-accent-primary);
color: var(--zen-sidebar-themed-icon-fill);
-moz-context-properties: fill;
fill: currentColor;
list-style-image: url("chrome://browser/content/zen-images/boost-indicator.svg");
transform: translateX(-20%);
z-index: 0;
@@ -1003,7 +1011,8 @@
}
}
#zen-copy-url-button image {
#zen-copy-url-button image,
.sr-url-icon {
list-style-image: url("link.svg");
fill-opacity: 0.65;
}
@@ -1087,3 +1096,7 @@
#zen-boost-load {
list-style-image: url("open.svg");
}
.sr-airplane {
list-style-image: url("selectable/airplane.svg");
}

View File

@@ -4,6 +4,7 @@
#ifdef XP_WIN
* skin/classic/browser/zen-icons/algorithm.svg (../shared/zen-icons/nucleo/algorithm.svg)
* skin/classic/browser/zen-icons/arrow-corner-down-right.svg (../shared/zen-icons/nucleo/arrow-corner-down-right.svg)
* 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)
@@ -153,6 +154,7 @@
#endif
#ifdef XP_MACOSX
* skin/classic/browser/zen-icons/algorithm.svg (../shared/zen-icons/nucleo/algorithm.svg)
* skin/classic/browser/zen-icons/arrow-corner-down-right.svg (../shared/zen-icons/nucleo/arrow-corner-down-right.svg)
* 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)
@@ -302,6 +304,7 @@
#endif
#ifdef XP_LINUX
* skin/classic/browser/zen-icons/algorithm.svg (../shared/zen-icons/nucleo/algorithm.svg)
* skin/classic/browser/zen-icons/arrow-corner-down-right.svg (../shared/zen-icons/nucleo/arrow-corner-down-right.svg)
* 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)

View File

@@ -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/.
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="context-fill" fill-opacity="context-fill-opacity" viewBox="0 0 18 18"><path d="M2.75 3a.75.75 0 0 1 .75.75v4C3.5 8.44 4.06 9 4.75 9h8.69l-2.97-2.97a.75.75 0 1 1 1.06-1.06l4.25 4.25a.8.8 0 0 1 .118.16q.025.04.044.083a.75.75 0 0 1-.078.715 1 1 0 0 1-.084.102l-4.25 4.25a.75.75 0 0 1-1.06-1.06l2.97-2.97H4.75A2.75 2.75 0 0 1 2 7.75v-4A.75.75 0 0 1 2.75 3"/></svg>

View File

@@ -1,8 +1,8 @@
diff --git a/toolkit/moz.configure b/toolkit/moz.configure
index 226d0c5a93a9a2404e1974001da4e34b7b670067..b73277448f7d2706d316df2505e17d232f392d47 100644
index 0f5dab192533aa42df97ee3bd4176a9f44d4d568..d45ad2409f6d36106fce5a380545aaae9397ae84 100644
--- a/toolkit/moz.configure
+++ b/toolkit/moz.configure
@@ -22,6 +22,7 @@ def check_moz_app_id(moz_app_id, build_project):
@@ -20,6 +20,7 @@ def check_moz_app_id(moz_app_id, build_project):
project_flag(
env="MOZ_APP_VENDOR",
@@ -10,7 +10,7 @@ index 226d0c5a93a9a2404e1974001da4e34b7b670067..b73277448f7d2706d316df2505e17d23
nargs=1,
help='Used for application.ini\'s "Vendor" field, which also impacts profile location and user-visible fields',
)
@@ -35,6 +36,7 @@ project_flag(
@@ -33,6 +34,7 @@ project_flag(
project_flag(
"MOZ_APP_PROFILE",
@@ -18,7 +18,7 @@ index 226d0c5a93a9a2404e1974001da4e34b7b670067..b73277448f7d2706d316df2505e17d23
nargs=1,
help='Used for application.ini\'s "Profile" field, which controls profile location',
)
@@ -86,10 +88,13 @@ option(
@@ -84,10 +86,13 @@ option(
)
set_config("MOZ_INCLUDE_SOURCE_INFO", True, when="MOZ_INCLUDE_SOURCE_INFO")
@@ -33,7 +33,7 @@ index 226d0c5a93a9a2404e1974001da4e34b7b670067..b73277448f7d2706d316df2505e17d23
help="Set distribution-specific id",
)
set_config("MOZ_DISTRIBUTION_ID", depends("--with-distribution-id")(lambda v: v[0]))
@@ -931,9 +936,9 @@ set_config("MOZ_SYSTEM_AV1", True, when="--with-system-av1")
@@ -874,9 +879,9 @@ set_config("MOZ_SYSTEM_AV1", True, when="--with-system-av1")
option("--disable-jxl", help="Disable jxl image support")
@@ -46,7 +46,7 @@ index 226d0c5a93a9a2404e1974001da4e34b7b670067..b73277448f7d2706d316df2505e17d23
return True
@@ -2070,7 +2075,7 @@ set_define("A11Y_LOG", True, when=a11y_log)
@@ -2028,7 +2033,7 @@ set_define("A11Y_LOG", True, when=a11y_log)
# ==============================================================
@depends(milestone)
def require_signing(milestone):
@@ -55,7 +55,7 @@ index 226d0c5a93a9a2404e1974001da4e34b7b670067..b73277448f7d2706d316df2505e17d23
option(
@@ -3903,7 +3908,7 @@ with only_when(compile_environment):
@@ -3912,7 +3917,7 @@ with only_when(compile_environment):
return "Mozilla"
elif target.os == "Android":
return ".mozilla"
@@ -64,3 +64,12 @@ index 226d0c5a93a9a2404e1974001da4e34b7b670067..b73277448f7d2706d316df2505e17d23
option(
"--with-user-appdir",
@@ -4325,7 +4330,7 @@ with only_when(target_is_windows):
@depends(target.abi)
def desktop_launcher_enabled(abi):
- return abi == "msvc"
+ return False # See gh-13745
set_config("DESKTOP_LAUNCHER_ENABLED", True, when=desktop_launcher_enabled)
set_define("DESKTOP_LAUNCHER_ENABLED", True, when=desktop_launcher_enabled)

View File

@@ -0,0 +1,14 @@
diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm
index 3e58a40d747fb3e05bd67518b41b6467725cfe42..995419b6390e8abe663f71f8fb26281eae56aa84 100644
--- a/widget/cocoa/nsCocoaWindow.mm
+++ b/widget/cocoa/nsCocoaWindow.mm
@@ -5329,6 +5329,9 @@ static unsigned int WindowMaskForBorderStyle(BorderStyle aBorderStyle) {
case nsIWidget::eDocumentWindowAnimation:
behavior = NSWindowAnimationBehaviorDocumentWindow;
break;
+ case nsIWidget::eZenUtilityWindowAnimation:
+ behavior = NSWindowAnimationBehaviorUtilityWindow;
+ break;
default:
MOZ_FALLTHROUGH_ASSERT("unexpected mAnimationType value");
case nsIWidget::eGenericWindowAnimation:

View File

@@ -0,0 +1,12 @@
diff --git a/widget/nsIWidget.h b/widget/nsIWidget.h
index 4a5d933d865585ea772bafd233f7d4ebce3ac02b..3eae20d579330a71196c83eeb20d8fb2cd9f5fae 100644
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -1118,6 +1118,7 @@ class nsIWidget : public nsSupportsWeakReference {
virtual void SetSupportsNativeFullscreen(bool aSupportsNativeFullscreen) {}
enum WindowAnimationType {
+ eZenUtilityWindowAnimation,
eGenericWindowAnimation,
eDocumentWindowAnimation
};

View File

@@ -1,8 +1,17 @@
diff --git a/xpfe/appshell/AppWindow.cpp b/xpfe/appshell/AppWindow.cpp
index d980bca7b42bb8d81817756215067771d2793bfe..94992e9d96348e4c88e089e4cdc3234076efb656 100644
index d980bca7b42bb8d81817756215067771d2793bfe..06818048ce6752a8b55faa3144c934fadbc46a68 100644
--- a/xpfe/appshell/AppWindow.cpp
+++ b/xpfe/appshell/AppWindow.cpp
@@ -1847,7 +1847,7 @@ nsresult AppWindow::MaybeSaveEarlyWindowPersistentValues(
@@ -1564,6 +1564,8 @@ void AppWindow::SyncAttributesToWidget() {
windowElement->GetAttribute(u"macanimationtype"_ns, attr);
if (attr.EqualsLiteral("document")) {
mWindow->SetWindowAnimationType(nsIWidget::eDocumentWindowAnimation);
+ } else if (attr.EqualsLiteral("zen:utilities")) {
+ mWindow->SetWindowAnimationType(nsIWidget::eZenUtilityWindowAnimation);
}
// Check if the client size did change and if we want to restore it.
@@ -1847,7 +1849,7 @@ nsresult AppWindow::MaybeSaveEarlyWindowPersistentValues(
}
}

View File

@@ -82,7 +82,7 @@ export class nsZenBoostStyles {
if (fontCase != "" || fontFamily != "") {
style += `/* Text Format */\n`;
style += `body * {\n`;
style += `body *:not(.google-symbols, gf-load-icon-font, mat-icon, .google-material-icons) {\n`;
style += `${fontFamily}\n`;
style += `${fontCase}\n`;
style += `}\n`;

View File

@@ -44,6 +44,7 @@ export class nsZenBoostEditor {
this.lastDotSetPos = { x: 0, y: 0 };
this.currentBoostData = null;
this.boostInfo = null;
this.isDarkMode = this.openerWindow.gZenThemePicker.isDarkMode;
this.killOtherEditorInstances();
@@ -52,6 +53,7 @@ export class nsZenBoostEditor {
});
this.init();
this.initColorScheme();
this.initColorPicker();
this.initFonts();
this.loadBoost(domain);
@@ -184,6 +186,17 @@ export class nsZenBoostEditor {
}
}
/**
* Initializes the color scheme of the editor window based on the current theme (dark or light mode)
*/
initColorScheme() {
if (this.isDarkMode) {
this.doc.documentElement.style.colorScheme = "dark";
} else {
this.doc.documentElement.style.colorScheme = "light";
}
}
/**
* Initializes the code editor for the css editor
*/
@@ -205,7 +218,7 @@ export class nsZenBoostEditor {
const editor = new Editor({
mode: Editor.modes.css,
lineNumbers: true,
theme: "default", // default is light theme
theme: this.isDarkMode ? "mozilla" : "default",
readOnly: false,
gutters: ["CodeMirror-linenumbers"],
});
@@ -361,12 +374,6 @@ export class nsZenBoostEditor {
}
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.requestAnimationFrame(() => {
this.editorWindow.resizeTo(
this._codeEditorWidth,
@@ -400,7 +407,11 @@ export class nsZenBoostEditor {
}
windowElem.setAttribute("editor", "boost");
this.editorWindow.requestAnimationFrame(() => {
this.doc.getElementById("zen-boost-editor-root").style.display = "flex";
this.doc.getElementById("zen-boost-code-editor-root").style.display =
"none";
this.editorWindow.promiseDocumentFlushed(() => {
this.editorWindow.resizeTo(
this._boostEditorWidth,
this.editorWindow.outerHeight
@@ -411,10 +422,6 @@ export class nsZenBoostEditor {
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
@@ -824,13 +831,19 @@ ${cssSelector} {
const dotSec = this.doc.querySelector(
"#zen-boost-color-picker-dot-secondary"
);
const dotDistance = this.currentBoostData.dotDistance;
const dotAngleDeg = this.currentBoostData.dotAngleDeg;
const secondaryDotAngleDelta =
this.currentBoostData.secondaryDotAngleDegDelta ?? 0;
dot.style.setProperty(
"--zen-theme-picker-dot-color",
`hsl(${this.currentBoostData.dotAngleDeg}deg, ${this.currentBoostData.dotDistance * 100}%, 55%)`
`hsl(${dotAngleDeg}deg, ${dotDistance * 100}%, 55%)`
);
dotSec.style.setProperty(
"--zen-theme-picker-dot-color",
`hsl(${this.currentBoostData.dotAngleDeg + this.currentBoostData.secondaryDotAngleDegDelta}deg, ${this.currentBoostData.dotDistance * 100}%, 20%)`
`hsl(${dotAngleDeg + secondaryDotAngleDelta}deg, ${dotDistance * 100}%, 20%)`
);
}
@@ -854,22 +867,23 @@ ${cssSelector} {
const centerY = rect.top + rect.height / 2;
const radius = (rect.width - padding) / 2;
const dotDistance = this.currentBoostData.dotDistance;
const primaryDotAngleDeg = this.currentBoostData.dotAngleDeg;
let angle = null;
if (!pixelX || !pixelY) {
if (pixelX == null || pixelY == null) {
pixelX = centerX;
pixelY = centerY;
angle = this.currentBoostData.secondaryDotAngleDegDelta;
} else {
angle = Math.atan2(pixelY - centerY, pixelX - centerX);
pixelX =
centerX + Math.cos(angle) * this.currentBoostData.dotDistance * radius;
pixelY =
centerY + Math.sin(angle) * this.currentBoostData.dotDistance * radius;
pixelX = centerX + Math.cos(angle) * dotDistance * radius;
pixelY = centerY + Math.sin(angle) * dotDistance * radius;
}
// Rad to degree
this.currentBoostData.secondaryDotAngleDegDelta =
((angle * 180) / Math.PI + 100 - this.currentBoostData.dotAngleDeg) % 360;
((angle * 180) / Math.PI + 100 - primaryDotAngleDeg) % 360;
if (this.currentBoostData.secondaryDotAngleDegDelta < 0) {
this.currentBoostData.secondaryDotAngleDegDelta += 360;
}
@@ -902,14 +916,19 @@ ${cssSelector} {
const cx = rect.width / 2;
const cy = rect.height / 2;
const dotDistance = this.currentBoostData.dotDistance;
const dotAngleDeg = this.currentBoostData.dotAngleDeg;
const secondaryDotAngleDelta =
this.currentBoostData.secondaryDotAngleDegDelta ?? 0;
// Updating the circle size to match the distance of the point
const circle = this.doc.querySelector(".zen-boost-color-picker-circle");
circle.setAttribute("animated", "false");
circle.style.width = `${this.currentBoostData.dotDistance * radius * 2}px`;
circle.style.height = `${this.currentBoostData.dotDistance * radius * 2}px`;
circle.style.width = `${dotDistance * radius * 2}px`;
circle.style.height = `${dotDistance * radius * 2}px`;
const dotColor = `hsl(${this.currentBoostData.dotAngleDeg}deg, ${this.currentBoostData.dotDistance * 100}%, 55%)`;
const dotColorSec = `hsl(${this.currentBoostData.dotAngleDeg + this.currentBoostData.secondaryDotAngleDegDelta}deg, ${this.currentBoostData.dotDistance * 100}%, 20%)`;
const dotColor = `hsl(${dotAngleDeg}deg, ${dotDistance * 100}%, 55%)`;
const dotColorSec = `hsl(${dotAngleDeg + secondaryDotAngleDelta}deg, ${dotDistance * 100}%, 20%)`;
this.updateArcFill(cx, cy, radius, dotColor, dotColorSec);
}
@@ -1154,12 +1173,6 @@ ${cssSelector} {
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 {

View File

@@ -114,9 +114,12 @@ class nsZenBoostsManager {
boostData: {
boostName: "My Boost",
dotAngleDeg: 0,
/* These initial values depend on
each other. Changing one means having to
recalculate all of them manually. */
dotAngleDeg: 131.61,
dotPos: { x: 0.76, y: 0.66 },
dotDistance: 0,
dotDistance: 0.91,
secondaryDotAngleDegDelta: 55,
secondaryDotPos: { x: 0.5, y: 0.81 },
@@ -637,21 +640,10 @@ class nsZenBoostsManager {
}
}
const editor = Services.ww.openWindow(
parentWindow,
const editor = parentWindow.openDialog(
"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,
}
"",
`left=${left},top=${top},chrome,alwaysontop,resizable=no,minimizable=no,dependent,dialog=yes`
);
const progressListener = {
@@ -668,7 +660,6 @@ class nsZenBoostsManager {
// Give the domain
editor.domain = domain;
editor.openerWindow = parentWindow;
editor.focus();
// Make boost active
this.makeBoostActiveForDomain(domain, boost.id);

View File

@@ -0,0 +1,104 @@
/* 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 "gtest/gtest.h"
#include "mozilla/nsZenBoostsBackend.h"
using zen::detail::AccentCacheSize;
using zen::detail::EnsureCachedAccent;
using zen::detail::IsAccentCached;
using zen::detail::ResetAccentCache;
namespace {
class ZenBoostsAccentCache : public ::testing::Test {
protected:
void SetUp() override { ResetAccentCache(); }
void TearDown() override { ResetAccentCache(); }
};
constexpr nscolor kAccentA = NS_RGBA(80, 120, 200, 200);
constexpr nscolor kAccentB = NS_RGBA(200, 80, 80, 200);
constexpr nscolor kAccentC = NS_RGBA(80, 200, 120, 200);
constexpr nscolor kAccentD = NS_RGBA(200, 200, 80, 200);
constexpr nscolor kAccentE = NS_RGBA(120, 80, 200, 200);
} // namespace
TEST_F(ZenBoostsAccentCache, SizeIsAtLeastFour) {
EXPECT_GE(AccentCacheSize(), 4u);
}
TEST_F(ZenBoostsAccentCache, EmptyAfterReset) {
EnsureCachedAccent(kAccentA, 0.0f);
ResetAccentCache();
EXPECT_FALSE(IsAccentCached(kAccentA, 0.0f));
}
TEST_F(ZenBoostsAccentCache, SameKeyIsCachedAfterEnsure) {
EXPECT_FALSE(IsAccentCached(kAccentA, 0.0f));
EnsureCachedAccent(kAccentA, 0.0f);
EXPECT_TRUE(IsAccentCached(kAccentA, 0.0f));
}
// Keying on accent alone would silently serve a stale complementary accent
// when the rotation changes.
TEST_F(ZenBoostsAccentCache, DifferentRotationOccupiesDistinctEntry) {
EnsureCachedAccent(kAccentA, 0.0f);
EnsureCachedAccent(kAccentA, 90.0f);
EXPECT_TRUE(IsAccentCached(kAccentA, 0.0f));
EXPECT_TRUE(IsAccentCached(kAccentA, 90.0f));
}
TEST_F(ZenBoostsAccentCache, DifferentAccentOccupiesDistinctEntry) {
EnsureCachedAccent(kAccentA, 30.0f);
EnsureCachedAccent(kAccentB, 30.0f);
EXPECT_TRUE(IsAccentCached(kAccentA, 30.0f));
EXPECT_TRUE(IsAccentCached(kAccentB, 30.0f));
}
TEST_F(ZenBoostsAccentCache, RoundRobinEvictsOldestEntry) {
ASSERT_EQ(AccentCacheSize(), 4u);
EnsureCachedAccent(kAccentA, 0.0f);
EnsureCachedAccent(kAccentB, 0.0f);
EnsureCachedAccent(kAccentC, 0.0f);
EnsureCachedAccent(kAccentD, 0.0f);
EXPECT_TRUE(IsAccentCached(kAccentA, 0.0f));
EXPECT_TRUE(IsAccentCached(kAccentB, 0.0f));
EXPECT_TRUE(IsAccentCached(kAccentC, 0.0f));
EXPECT_TRUE(IsAccentCached(kAccentD, 0.0f));
EnsureCachedAccent(kAccentE, 0.0f);
EXPECT_FALSE(IsAccentCached(kAccentA, 0.0f));
EXPECT_TRUE(IsAccentCached(kAccentB, 0.0f));
EXPECT_TRUE(IsAccentCached(kAccentC, 0.0f));
EXPECT_TRUE(IsAccentCached(kAccentD, 0.0f));
EXPECT_TRUE(IsAccentCached(kAccentE, 0.0f));
}
// A cache hit must not consume a fresh slot, otherwise repeated paints with
// the same accent would evict their own neighbours.
TEST_F(ZenBoostsAccentCache, RepeatEnsureDoesNotChurnTheCache) {
ASSERT_EQ(AccentCacheSize(), 4u);
EnsureCachedAccent(kAccentA, 0.0f);
EnsureCachedAccent(kAccentB, 0.0f);
EnsureCachedAccent(kAccentC, 0.0f);
for (int i = 0; i < 16; ++i) {
EnsureCachedAccent(kAccentA, 0.0f);
}
EXPECT_TRUE(IsAccentCached(kAccentA, 0.0f));
EXPECT_TRUE(IsAccentCached(kAccentB, 0.0f));
EXPECT_TRUE(IsAccentCached(kAccentC, 0.0f));
EnsureCachedAccent(kAccentD, 0.0f);
EnsureCachedAccent(kAccentE, 0.0f);
EXPECT_FALSE(IsAccentCached(kAccentA, 0.0f));
}

View File

@@ -0,0 +1,42 @@
/* 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 "gtest/gtest.h"
#include "mozilla/nsZenBoostsBackend.h"
using zen::nsZenBoostsBackend;
namespace {
const nscolor kResolveColors[] = {
NS_RGBA(0, 0, 0, 255), NS_RGBA(255, 255, 255, 255),
NS_RGBA(128, 128, 128, 255), NS_RGBA(255, 0, 0, 255),
NS_RGBA(0, 255, 0, 255), NS_RGBA(0, 0, 255, 255),
NS_RGBA(40, 44, 52, 255), NS_RGBA(248, 248, 248, 255),
NS_RGBA(20, 22, 28, 255), NS_RGBA(80, 80, 80, 200),
NS_RGBA(240, 17, 99, 1), NS_RGBA(0, 0, 0, 0),
};
} // namespace
// Removing the null-frame guard would crash chrome-process callers that
// legitimately pass nullptr (canvas getComputedStyle, font-palette binding,
// the StyleColor(nscolor)/StyleColor(StyleAbsoluteColor) overloads).
TEST(ZenBoostsResolveStyleColor, NullFrameIsIdentity)
{
for (nscolor c : kResolveColors) {
EXPECT_EQ(nsZenBoostsBackend::ResolveStyleColor(c, nullptr), c);
}
}
TEST(ZenBoostsResolveStyleColor, NullFrameIsIdempotent)
{
for (nscolor c : kResolveColors) {
nscolor once = nsZenBoostsBackend::ResolveStyleColor(c, nullptr);
nscolor twice = nsZenBoostsBackend::ResolveStyleColor(once, nullptr);
EXPECT_EQ(once, c);
EXPECT_EQ(twice, c);
}
}

View File

@@ -3,7 +3,9 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
UNIFIED_SOURCES += [
"TestZenBoostsAccentCache.cpp",
"TestZenBoostsColorFilter.cpp",
"TestZenBoostsResolveStyleColor.cpp",
]
FINAL_LIBRARY = "xul-gtest"

View File

@@ -450,11 +450,9 @@ inline static void GetZenBoostsDataForFrame(const nsIFrame* aFrame,
} // namespace
#ifdef ENABLE_TESTS
namespace detail {
// Thin forwarders that give unit tests access to the pure color math without
// pulling in the singleton / BrowsingContext. They are defined here, after the
// anonymous namespace, so they can reach those file-local implementations.
nsZenAccentOklab PrecomputeAccent(nscolor aAccentColor) {
return zenPrecomputeAccent(aAccentColor);
}
@@ -474,7 +472,33 @@ nscolor InvertColorChannel(nscolor aColor) {
return zenInvertColorChannel(aColor);
}
size_t AccentCacheSize() { return kAccentCacheSize; }
void ResetAccentCache() {
for (auto& entry : sAccentCache) {
entry.valid = false;
entry.accentNS = 0;
entry.rotationDeg = 0.0f;
}
sAccentCacheNext = 0;
}
bool IsAccentCached(nscolor aAccentNS, float aRotationDeg) {
for (const auto& entry : sAccentCache) {
if (entry.valid && entry.accentNS == aAccentNS &&
entry.rotationDeg == aRotationDeg) {
return true;
}
}
return false;
}
void EnsureCachedAccent(nscolor aAccentNS, float aRotationDeg) {
(void)GetCachedAccent(aAccentNS, aRotationDeg);
}
} // namespace detail
#endif // ENABLE_TESTS
static mozilla::StaticRefPtr<nsZenBoostsBackend> sZenBoostsBackend;

View File

@@ -27,10 +27,9 @@ struct nsZenAccentOklab {
float contrastFactor;
};
#ifdef ENABLE_TESTS
// Test-only forwarders into the file-local color math and accent cache.
namespace detail {
// Pure color-math primitives, exposed for unit testing. These have no
// dependency on the singleton, the BrowsingContext, or the process type, so
// they can be exercised directly from gtest.
nsZenAccentOklab PrecomputeAccent(nscolor aAccentColor);
nsZenAccentOklab RotateAccent(const nsZenAccentOklab& aBase,
float aRotationDeg);
@@ -38,7 +37,13 @@ nscolor FilterColorChannel(nscolor aOriginalColor,
const nsZenAccentOklab& aAccent,
const nsZenAccentOklab& aComplementary);
nscolor InvertColorChannel(nscolor aColor);
size_t AccentCacheSize();
void ResetAccentCache();
bool IsAccentCached(nscolor aAccentNS, float aRotationDeg);
void EnsureCachedAccent(nscolor aAccentNS, float aRotationDeg);
} // namespace detail
#endif // ENABLE_TESTS
class nsZenBoostsBackend final : public nsISupports {
public:

View File

@@ -5,11 +5,10 @@
*/
#zen-boost-advanced-color-options-panel {
color-scheme: light;
--panel-padding: 15px;
& p {
color: #3a3a3b;
color: var(--zen-boosts-primary-color);
}
& input {

View File

@@ -18,7 +18,7 @@
customtitlebar="true"
sizemode="normal"
scrolling="false"
macanimationtype="document"
macanimationtype="zen:utilities"
windowsmica="true"
data-l10n-sync="true">
<head>
@@ -34,14 +34,6 @@
<link rel="stylesheet" href="chrome://browser/content/zen-styles/zen-advanced-color-options.css" />
<link rel="localization" href="browser/zen-boosts.ftl"/>
<!-- Loading in the window module -->
<script>
const { nsZenBoostEditor } = ChromeUtils.importESModule( "resource:///modules/zen/boosts/ZenBoostsEditor.mjs" );
window.addEventListener("load", () => {
window.boostEditor = new nsZenBoostEditor(document, window.domain, window, window.openerWindow);
});
</script>
</head>
<html:body xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<vbox flex="1" id="zen-boost-editor-root">
@@ -120,15 +112,15 @@
<popupset id="mainPopupSet">
<panel type="arrow" popupalign="topmiddle" id="zen-boost-advanced-color-options-panel">
<vbox>
<p data-l10n-id="zen-bootst-color-contrast"></p>
<p data-l10n-id="zen-boost-color-contrast"></p>
<html:input id="zen-boost-color-contrast" type="range" min="0.05" max="0.9" value="0.75" step="0.01"/>
</vbox>
<vbox>
<p data-l10n-id="zen-bootst-color-brightness"></p>
<p data-l10n-id="zen-boost-color-brightness"></p>
<html:input id="zen-boost-color-brightness" type="range" min="0" max="1" value="0.5" step="0.01"/>
</vbox>
<vbox>
<p data-l10n-id="zen-bootst-color-original-saturation"></p>
<p data-l10n-id="zen-boost-color-original-saturation"></p>
<html:input id="zen-boost-color-saturation" type="range" min="0" max="1" value="0.5" step="0.01"/>
</vbox>
</panel>
@@ -164,5 +156,10 @@
<command id="cmd_zenBoostDelete"
oncommand="window.boostEditor.deleteBoost();" />
</commandset>
<script>
const { nsZenBoostEditor } = ChromeUtils.importESModule( "resource:///modules/zen/boosts/ZenBoostsEditor.mjs" );
window.boostEditor = new nsZenBoostEditor(document, window.domain, window, window.openerWindow);
</script>
</html:body>
</html>

View File

@@ -5,10 +5,33 @@
*/
#zenBoostWindow {
/* For the mica effect we want a white tint */
@media not (-moz-platform: linux) {
color-scheme: light;
}
--zen-boosts-primary-background: light-dark(#fcfcfe, #171717);
--zen-boosts-secondary-background: light-dark(#f6f6f8, #1c1c1e);
--zen-boosts-mica-background: light-dark(#f6f6f8c0, #1c1c1ec0);
--zen-boosts-primary-color: light-dark(#3a3a3b, #f3f3f3);
--zen-boosts-secondary-color: light-dark(#727272, #b1b1b1);
--zen-boosts-button-background: light-dark(#ebebed, #262626);
--mod-button-c1: light-dark(#ebebed, #262626);
--mod-button-c2: light-dark(#ebebed, #262626);
--zen-boosts-active-button-background: light-dark(#3a3a3a, #cccccc);
--zen-boosts-active-button-hover-background: light-dark(#5b5b5c, #c9c9c9);
--zen-boosts-active-button-color: light-dark(#fcfcfe, #1c1c1e);
--zen-boosts-back-button-hover-background: light-dark(#e3e3e6, #3a3a3a);
--zen-boosts-magic-theme-background: light-dark(white, #3a3a3a);
--zen-boosts-magic-theme-active-background: light-dark(#3a3a3a, white);
--zen-boosts-font-wrapper-background: light-dark(white, #262626);
--zen-boosts-primary-border-color: light-dark(#ededef, #3a3a3a);
--zen-boosts-color-picker-background: light-dark(#fbfbfdea, #1c1c1eea);
--zen-boosts-color-picker-pattern-color: light-dark(#e3e9e4, #3a3a3a);
appearance: none;
border: none;
@@ -40,12 +63,12 @@
width: 100%;
height: 40px;
background-color: #f6f6f8;
background-color: var(--zen-boosts-secondary-background);
@media (-moz-windows-mica) {
background-color: #f6f6f8c0;
background-color: var(--zen-boosts-mica-background);
}
border: solid 0 #ededef;
border: solid 0 var(--zen-boosts-primary-border-color);
border-bottom-width: 1px;
}
@@ -58,6 +81,9 @@
margin-left: 4px;
border-radius: 8px;
-moz-context-properties: fill;
fill: var(--zen-boosts-primary-color);
opacity: 0.75;
background-color: transparent;
@@ -79,12 +105,12 @@
width: 100%;
height: 60px;
background-color: #f6f6f8;
background-color: var(--zen-boosts-secondary-background);
@media (-moz-windows-mica) {
background-color: #f6f6f8c0;
background-color: var(--zen-boosts-mica-background);
}
border: solid 0 #ededef;
border: solid 0 var(--zen-boosts-primary-border-color);
border-top-width: 1px;
display: flex;
@@ -113,6 +139,7 @@ body {
user-select: none;
width: 100%;
min-height: 582px;
height: 100%;
padding: 0;
margin: 0;
@@ -148,7 +175,7 @@ body {
}
.subviewbutton {
color: #3a3a3b;
color: var(--zen-boosts-primary-color);
}
#zen-boost-editor-view {
@@ -166,11 +193,11 @@ body {
min-height: 40px;
max-height: 40px;
align-items: center;
background-color: #f6f6f8;
border: solid 1px #e7e7e7ab;
background-color: var(--zen-boosts-secondary-background);
border: solid 1px var(--zen-boosts-primary-border-color);
@media (-moz-windows-mica) {
background-color: #f6f6f8c0;
background-color: var(--zen-boosts-mica-background);
border: none;
}
@@ -232,6 +259,7 @@ body {
padding: 2px;
& button {
color: var(--zen-boosts-primary-color) !important;
padding: auto;
margin: auto;
background-color: transparent;
@@ -243,7 +271,7 @@ body {
gap: 14px;
padding-top: 6px;
background-color: #fcfcfe;
background-color: var(--zen-boosts-primary-background);
-moz-window-dragging: drag;
& > * {
@@ -328,7 +356,7 @@ body {
border-radius: 12px;
}
background-color: #ebebed;
background-color: var(--zen-boosts-button-background);
transition:
0.4s background-color cubic-bezier(0.075, 0.82, 0.165, 1),
@@ -348,7 +376,7 @@ body {
}
#zen-boost-case:not([case-mode="none"]) {
background-color: #ebebed;
background-color: var(--zen-boosts-button-background);
font-weight: 600 !important;
}
@@ -373,7 +401,7 @@ body {
font-size: 8pt;
text-indent: 2px;
vertical-align: middle;
color: #3a3a3b;
color: var(--zen-boosts-primary-color);
justify-content: center;
}
@@ -397,12 +425,16 @@ body {
}
.zen-boost-button-active {
background-color: #3a3a3a;
color: #fcfcfe;
background-color: var(--zen-boosts-active-button-background);
color: var(--zen-boosts-active-button-color);
&#zen-boost-magic-theme {
background-color: var(--zen-boosts-magic-theme-active-background);
}
}
.zen-boost-button-active:hover {
background-color: #5b5b5c;
background-color: var(--zen-boosts-active-button-hover-background);
}
.zen-boost-button-active-transparent {
@@ -430,12 +462,13 @@ body {
}
&:not(.zen-boost-button-active) {
background: white;
background: var(--zen-boosts-magic-theme-background);
color: var(--zen-boosts-primary-color);
}
}
.footer {
background-color: #F6F6F8;
background-color: var(--zen-boosts-secondary-background);
padding: 20px;
}
@@ -471,7 +504,7 @@ body {
transition: 0.2s opacity ease-in-out;
color: #727272;
color: var(--zen-boosts-secondary-color);
background: none;
font-size: 9pt;
@@ -490,7 +523,7 @@ body {
&[has-selection="true"] {
opacity: 1;
background-color: #ebebed;
background-color: var(--zen-boosts-button-background);
}
}
@@ -513,7 +546,7 @@ body {
#zen-boost-font-wrapper {
box-shadow: 0 2px 6px rgba(0,0,0,.15);
background-color: #ffffff;
background-color: var(--zen-boosts-font-wrapper-background);
border-radius: 6px;
@media (-moz-platform: macos) {
@@ -564,35 +597,35 @@ body {
@property --mod-button-c1 {
syntax: "<color>";
inherits: false;
initial-value: #ebebed;
inherits: true;
initial-value: transparent;
}
@property --mod-button-c2 {
syntax: "<color>";
inherits: false;
initial-value: #ebebed;
inherits: true;
initial-value: transparent;
}
.mod-button[mode="orange"] {
color: #e3e9e4;
--mod-button-c1: #ffbb5d;
--mod-button-c2: #ffa01d;
--mod-button-c1: light-dark(#ffbb5d, #c37a3f);
--mod-button-c2: light-dark(#ffa01d, #9f5a2a);
}
.mod-button[mode="orange-red"] {
color: #e3e9e4;
--mod-button-c1: #ff8758;
--mod-button-c2: #ff5b1b;
--mod-button-c1: light-dark(#ff8758, #bd6048);
--mod-button-c2: light-dark(#ff5b1b, #9b422b);
}
.mod-button[mode="red"] {
color: #e3e9e4;
--mod-button-c1: #ff595f;
--mod-button-c2: #ff121b;
--mod-button-c1: light-dark(#ff595f, #b94a50);
--mod-button-c2: light-dark(#ff121b, #95272e);
}
.mod-button[mode="blue"] {
color: #e3e9e4;
--mod-button-c1: #6650fc;
--mod-button-c2: #4125ff;
--mod-button-c1: light-dark(#6650fc, #5d56ca);
--mod-button-c2: light-dark(#4125ff, #453aa9);
}
.mod-button[mode] {
background: linear-gradient(180deg, var(--mod-button-c1) 0%, var(--mod-button-c2) 100%) border-box;
@@ -608,7 +641,7 @@ body {
background-color: transparent;
&:hover {
background-color: #e3e3e6;
background-color: var(--zen-boosts-back-button-hover-background);
}
}
@@ -635,17 +668,17 @@ body {
z-index: 2;
background: #fbfbfdea;
background: var(--zen-boosts-color-picker-background);
background-position: -23px -23px;
backdrop-filter: saturate(2) blur(15px);
background-size: 6px 6px;
background-image: radial-gradient(#e3e9e4, 1px, transparent 0);
background-image: radial-gradient(var(--zen-boosts-color-picker-pattern-color), 1px, transparent 0);
@media (-moz-platform: macos) {
background-size: 4px 4px;
background-image: radial-gradient(#e3e9e4 0.5px, transparent 0);
background-image: radial-gradient(var(--zen-boosts-color-picker-pattern-color) 0.5px, transparent 0);
}
}

View File

@@ -16,6 +16,12 @@
}
#select-component {
--zen-boosts-selector-background: light-dark(#f5f7fb, #27272a);
--zen-boosts-selector-preview-background: light-dark(#e0e2e63d, #27272a3d);
--zen-boosts-selector-preview-color: light-dark(rgb(76, 78, 80), rgb(207, 200, 200));
--zen-boosts-selector-button-color: light-dark(#dadada, #f1f1f1);
--zen-boosts-selector-outline-color: light-dark(#e0e2e6ae, #474749);
width: min-content;
font-family: system-ui;
@@ -29,7 +35,7 @@
position: fixed;
z-index: 9999;
background-color: #f5f7fb;
background-color: var(--zen-boosts-selector-background);
margin: 4px;
border-radius: 12px;
@@ -126,17 +132,29 @@
width: 250px !important;
--related-elements-value: 100%;
background: linear-gradient(to top, rgb(247, 66, 0), rgb(245, 134, 86));
background: linear-gradient(
to top,
light-dark(#ff5b1b, #9b422b),
light-dark(#ff8758, #bd6048)
);
border: none;
color: #dadada;
color: var(--zen-boosts-selector-button-color);
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));
linear-gradient(
to right,
transparent var(--related-elements-value),
light-dark(gray, #363636) var(--related-elements-value)
),
linear-gradient(
to top,
light-dark(#ff5b1b, #9b422b),
light-dark(#ff8758, #bd6048)
);
@media not (-moz-platform: windows) {
box-shadow: 0 0 20px #00000077;
@@ -147,30 +165,34 @@
#select-this {
appearance: none;
background: linear-gradient(0deg, rgba(246, 27, 25, 1) 0%, rgba(254, 67, 59, 1) 100%);
background: linear-gradient(
0deg,
light-dark(#ff121b, #95272e),
light-dark(#ff595f, #b94a50)
);
border: none;
color: #dadada;
color: var(--zen-boosts-selector-button-color);
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%);
}
background: linear-gradient(
0deg,
light-dark(#525355, #373739) 0%,
light-dark(#6c6e70, #4b4c4e) 100%
);
border: none;
color: #dadada;
color: var(--zen-boosts-selector-button-color);
box-shadow: 0 0 15px #00000052;
}
#selector-preview {
background-color: #e0e2e63d;
outline: 1px solid #e0e2e6ae;
background-color: var(--zen-boosts-selector-preview-background);
outline: 1px solid var(--zen-boosts-selector-outline-color);
width: 100%;
height: 34px;
text-indent: 18px;
@@ -182,7 +204,7 @@
#selector-element-preview-text {
font-family: Arial, Helvetica, sans-serif;
font-size: 9pt;
color: rgb(76, 78, 80);
color: var(--zen-boosts-selector-preview-color);
}
#hover-div {

View File

@@ -5,6 +5,11 @@
// prettier-ignore
// eslint-disable-next-line no-lone-blocks
{
ChromeUtils.defineESModuleGetters(this, {
gZenSpaceRoutingManager:
"resource:///modules/zen/spacerouting/ZenSpaceRoutingManager.sys.mjs",
});
Services.scriptloader.loadSubScript("chrome://browser/content/zen-components/ZenSpaceBookmarksStorage.js", this);
let scripts = [

View File

@@ -5,10 +5,10 @@
*/
.dialogBox {
border-radius: 12px !important;
border: 1px solid light-dark(rgba(168, 168, 169, 0.50), var(--zen-dialog-background)) !important;
border: 0.5px solid light-dark(rgba(0, 0, 0, 0.4), 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;
outline-offset: -2px;
@media not (prefers-reduced-motion: reduce) {
animation: zen-dialog-fade-in 0.3s ease-out;

View File

@@ -27,7 +27,7 @@
display: flex;
position: relative;
flex: 1;
padding: 8px;
padding: 16px;
border-bottom: 1px solid color-mix(in srgb, currentColor 10%, transparent);
}
@@ -38,6 +38,12 @@
white-space: nowrap;
font-weight: 500;
flex: 1;
position: absolute;
top: 50%;
left: 0;
transform: translateY(-50%);
width: calc(100% - 40px);
padding: 0 16px;
}
.zen-sidebar-notification-close-button {

View File

@@ -495,7 +495,7 @@
border-radius: 99px;
width: var(--size-item-large);
height: var(--size-item-large);
background: var(--button-background-color-primary);
background: var(--zen-sidebar-themed-icon-fill);
opacity: 0.6;
transition:
transform 0.12s ease-in-out,
@@ -540,6 +540,10 @@
& toolbarbutton {
margin: 0;
&[disabled] {
visibility: hidden;
}
}
}
@@ -621,7 +625,7 @@
color: var(--button-primary-color);
&::before {
background: var(--button-background-color-primary);
background: var(--zen-sidebar-themed-icon-fill);
}
}
@@ -786,4 +790,8 @@
@media not -moz-pref("zen.view.enable-loading-indicator") {
display: none;
}
:root[inDOMFullscreen="true"] & {
display: none;
}
}

View File

@@ -133,6 +133,10 @@ document.addEventListener(
gZenWorkspaces.unloadAllOtherWorkspaces();
break;
}
case "cmd_zenOpenSpaceRoutingSettings": {
gZenSpaceRoutingManager.openSpaceRoutingDialog(window);
break;
}
case "cmd_zenNewNavigatorUnsynced":
OpenBrowserWindow({ zenSyncedWindow: false });
break;

View File

@@ -168,17 +168,17 @@
document.documentElement.setAttribute("zen-no-padding", true);
} else {
document.documentElement.removeAttribute("zen-no-padding");
if (domFullscreen) {
const selectedBrowser = gBrowser.selectedBrowser;
selectedBrowser.style.paddingRight = "0.5px";
window.addEventListener(
"MozAfterPaint",
() => {
selectedBrowser.style.paddingRight = "";
},
{ once: true }
);
}
}
if (domFullscreen) {
const selectedBrowser = gBrowser.selectedBrowser;
selectedBrowser.style.paddingRight = "0.5px";
window.addEventListener(
"MozAfterPaint",
() => {
selectedBrowser.style.paddingRight = "";
},
{ once: true }
);
}
},

View File

@@ -214,6 +214,7 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
skipAnimation: true,
ownerTab: currentTab,
triggeringPrincipal: data.triggeringPrincipal,
skipRoute: true,
};
}

View File

@@ -19,4 +19,5 @@ DIRS += [
"sessionstore",
"share",
"spaces",
"space-routing",
]

View File

@@ -521,10 +521,13 @@ class nsZenWindowSync {
if (flags & SYNC_FLAG_ICON) {
aTargetItem.zenStaticIcon = aOriginalItem.zenStaticIcon;
if (gBrowser.isTab(aOriginalItem)) {
gBrowser.setIcon(
aTargetItem,
aOriginalItem.getAttribute("image") || gBrowser.getIcon(aOriginalItem)
);
try {
gBrowser.setIcon(
aTargetItem,
aOriginalItem.getAttribute("image") ||
gBrowser.getIcon(aOriginalItem)
);
} catch {}
} else if (aOriginalItem.isZenFolder) {
// Icons are a zen-only feature for tab groups.
gZenFolders.setFolderUserIcon(aTargetItem, aOriginalItem.iconURL);
@@ -1542,6 +1545,7 @@ class nsZenWindowSync {
console.error(`Error moving active tabs to other windows on close:`, e);
}
resolve();
this.#docShellSwitchPromise = null;
}
on_WindowCloseAndBrowserFlushed(aBrowsers) {

View File

@@ -0,0 +1,462 @@
/* 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/. */
// eslint-disable-next-line no-shadow
const { gZenSpaceRoutingManager } = ChromeUtils.importESModule(
"resource:///modules/zen/spacerouting/ZenSpaceRoutingManager.sys.mjs"
);
export class nsZenSpaceRoutingDialog {
doc = null;
editorWindow = null;
openerWindow = null;
static OBSERVERS = ["zen-space-routing-kill"];
/**
* Creates a new Space Routing dialog controller.
*
* @param {Document} doc - The document object for the dialog window.
* @param {Window} editorWindow - The Space Routing dialog window.
* @param {Window} openerWindow - The browser window that opened the dialog.
*/
constructor(doc, editorWindow, openerWindow) {
this.doc = doc;
this.editorWindow = editorWindow;
this.openerWindow = openerWindow;
this.killOtherShareInstances();
nsZenSpaceRoutingDialog.OBSERVERS.forEach(observe => {
Services.obs.addObserver(this, observe);
});
this.init();
this.editorWindow.addEventListener("unload", () => this.uninit(), {
once: true,
});
}
/**
* Initializes the boost share instance by setting up event listeners for all UI controls.
*/
init() {
this.editorWindow.addEventListener("unload", () => this.handleClose(), {
once: true,
});
this.doc
.getElementById("sr-close")
.addEventListener("click", this.onClosePressed.bind(this));
this.doc
.getElementById("sr-new-route")
.addEventListener("click", this.onNewRoutePressed.bind(this));
const defaultRouteSelect = this.doc.getElementById(
"sr-default-external-open-in"
);
this.createOpenInList(
defaultRouteSelect,
gZenSpaceRoutingManager.getDefaultExternalRoute()
);
defaultRouteSelect.addEventListener("command", e =>
this.onRouteDefaultExternalChange(e.target.value)
);
this.doc.addEventListener("keydown", event => {
if (
event.key === "Escape" ||
(event.key === "w" && (event.ctrlKey || event.metaKey))
) {
this.onClosePressed();
}
});
this.initRouteList();
this.initialized = true;
}
/**
* Initializes the routes list and loads all current routes from the disk
*/
initRouteList() {
const allRoutes = gZenSpaceRoutingManager.getAllRoutes();
allRoutes.forEach(r => this.createRouteElement(r));
}
/**
* Will create a new route and update the route list
*/
onNewRoutePressed() {
const newRoute = gZenSpaceRoutingManager.createNewRoute();
this.createRouteElement(newRoute);
}
/**
* Will remove a route and update the list
*
* @param {string} routeId - The unique ID of the affected route
* @param {string} containerElement - The container element of the route in the list
*/
onRemoveRoutePressed(routeId, containerElement) {
gZenSpaceRoutingManager.removeRoute(routeId);
containerElement.remove();
this.updateShowNoRouteText();
}
/**
* Will create the rule element content and inject it into the ui
*
* @param {object} route - The target route
* @returns {Element} The created element for the route
*/
createRouteElement(route) {
const container = this.doc.getElementById("sr-content");
const root = this.doc.createXULElement("vbox");
root.setAttribute("routeId", route.id);
root.className = "sr-rule-container";
// ---- Top row
const topRow = this.doc.createXULElement("hbox");
topRow.className = "sr-rule-row sr-rule-top";
const topLabelContainer = this.doc.createXULElement("hbox");
topLabelContainer.className = "sr-label-container";
const urlIcon = this.doc.createXULElement("image");
urlIcon.className = "sr-url-icon";
const urlLabel = this.doc.createXULElement("label");
urlLabel.className = "sr-label";
urlLabel.setAttribute("data-l10n-id", "zen-space-routing-url");
topLabelContainer.append(urlIcon, urlLabel);
// Match type
const matchTypeMenulist = this.doc.createXULElement("menulist");
matchTypeMenulist.className = "select match-type-select";
const matchTypePopup = this.doc.createXULElement("menupopup");
matchTypeMenulist.appendChild(matchTypePopup);
["contains", "equal-to", "regex"].forEach(id => {
const menuItem = this.doc.createXULElement("menuitem");
menuItem.setAttribute("data-l10n-id", `zen-space-routing-${id}`);
menuItem.setAttribute("value", id);
matchTypePopup.appendChild(menuItem);
});
matchTypeMenulist.value = route.matchType;
// Input domain
const input = this.doc.createElement("input");
input.className = "input";
input.value = route.reference;
this.updateInputPlaceholder(route.matchType, input);
const removeButton = this.doc.createXULElement("button");
removeButton.className = "sr-remove";
topRow.append(topLabelContainer, matchTypeMenulist, input, removeButton);
// ---- Bottom row
const bottomRow = this.doc.createXULElement("hbox");
bottomRow.className = "sr-rule-row sr-rule-bottom";
const bottomLabelContainer = this.doc.createXULElement("hbox");
bottomLabelContainer.className = "sr-label-container";
const openInIcon = this.doc.createXULElement("image");
openInIcon.className = "sr-open-in-icon";
const openInLabel = this.doc.createXULElement("label");
openInLabel.className = "sr-label";
openInLabel.setAttribute("data-l10n-id", "zen-space-routing-open-in");
bottomLabelContainer.append(openInIcon, openInLabel);
// Open in
const openInMenulist = this.doc.createXULElement("menulist");
openInMenulist.className = "select open-in-select";
const openInMenupopup = this.doc.createXULElement("menupopup");
openInMenulist.appendChild(openInMenupopup);
this.createOpenInList(openInMenulist, route.openIn);
bottomRow.append(bottomLabelContainer, openInMenulist);
root.append(topRow, bottomRow);
root.style.display = "none";
container.appendChild(root);
// Wait for l10n to catch up and then show the element to avoid flickering.
this.editorWindow.promiseDocumentFlushed(() =>
this.editorWindow.requestAnimationFrame(() => {
root.style.display = "";
input.focus();
})
);
removeButton.addEventListener("click", () => {
this.onRemoveRoutePressed(route.id, root);
});
input.addEventListener("input", e =>
this.onRouteReferenceChange(e.target.value, route.id, input)
);
matchTypeMenulist.addEventListener("command", e =>
this.onRouteMatchTypeChange(e.target.value, route.id, input)
);
openInMenulist.addEventListener("command", e =>
this.onRouteOpenInChange(e.target.value, route.id)
);
this.updateShowNoRouteText();
return root;
}
/**
* Checks if the text for when no routes are
* created should be displayed
*/
updateShowNoRouteText() {
const container = this.doc.getElementById("sr-content");
const noRoutesText = this.doc.getElementById("sr-empty-content");
// One because of the element itself
noRoutesText.style.display =
container.children.length == 1 ? "flex" : "none";
}
/**
* Callback for when the reference text changes
*
* @param {string} value - The new value
* @param {string} routeId - The ID of the affected route
* @param {Element} input - The input element
*/
onRouteReferenceChange(value, routeId, input) {
const route = gZenSpaceRoutingManager.getRoute(routeId);
route.reference = value;
this.updateInputPlaceholder(route.matchType, input);
// Don't update the route if the regex is invalid
if (route.matchType == "regex") {
if (!this.onCheckRegexValid(input)) {
return;
}
}
gZenSpaceRoutingManager.updateRoute(route);
}
/**
* Callback for when the open in attribute changes
*
* @param {string} value - The new value
* @param {string} routeId - The ID of the affected route
*/
onRouteOpenInChange(value, routeId) {
const route = gZenSpaceRoutingManager.getRoute(routeId);
route.openIn = value;
gZenSpaceRoutingManager.updateRoute(route);
}
/**
* Callback for when the route match type changes
*
* @param {string} value - The new value
* @param {string} routeId - The ID of the affected route
* @param {Element} input - The text input
*/
onRouteMatchTypeChange(value, routeId, input) {
const route = gZenSpaceRoutingManager.getRoute(routeId);
route.matchType = value;
this.updateInputPlaceholder(route.matchType, input);
// Don't update the route if the regex is invalid
if (route.matchType == "regex") {
if (!this.onCheckRegexValid(input)) {
return;
}
}
gZenSpaceRoutingManager.updateRoute(route);
}
/**
* Updates the input placeholder based on the
* current route match type
*
* @param {string} matchType - The match type (e.g. "contains", "equal-to", "regex")
* @param {Element} input - The input element
*/
updateInputPlaceholder(matchType, input) {
switch (matchType) {
case "regex":
input.placeholder = "zen-browser\\.app";
break;
default:
input.placeholder = "zen-browser.app";
break;
}
}
/**
* Will validate and return the validity of the
* regex. Applies a tint to the input if an error occurs.
*
* @param {Element} input - The input element for the regex
* @returns {bool} True if regex is valid
*/
onCheckRegexValid(input) {
const reference = input.value;
// Ignore empty
if (reference.trim() == "") {
input.classList.remove("invalid");
return true;
}
try {
new RegExp(reference);
} catch (e) {
input.classList.add("invalid");
return false;
}
input.classList.remove("invalid");
return true;
}
/**
* Callback for when the default external route changes
*
* @param {string} value - The new value
*/
onRouteDefaultExternalChange(value) {
gZenSpaceRoutingManager.setDefaultExternalRoute(value);
}
/**
* Creates the options list selects
*
* @param {Element} selectElement - The menulist element
* @param {string} value - The initial value
*/
async createOpenInList(selectElement, value) {
const popupElement =
selectElement.querySelector("menupopup") || selectElement;
popupElement.replaceChildren(); // Clear existing
const [openInSpace, mostRecentSpace] = await this.doc.l10n.formatMessages([
"zen-space-routing-open-in-space",
"zen-space-routing-most-recent-space",
]);
const sectionHeader = this.doc.createXULElement("menuitem");
sectionHeader.setAttribute("label", openInSpace.value);
sectionHeader.setAttribute("disabled", "true");
sectionHeader.classList.add("menu-section-header");
popupElement.appendChild(sectionHeader);
let availOptions = [];
let createXulItem = (text, id, iconPath = null) => {
if (text === "sep") {
popupElement.appendChild(this.doc.createXULElement("menuseparator"));
return;
}
availOptions.push(id || text);
const menuItem = this.doc.createXULElement("menuitem");
menuItem.setAttribute("label", text);
menuItem.setAttribute("value", id || text);
if (iconPath) {
if (iconPath.startsWith("chrome://")) {
menuItem.setAttribute("class", "menuitem-iconic");
menuItem.setAttribute("image", iconPath);
} else {
menuItem.setAttribute("label", `${iconPath} ${text}`);
}
}
popupElement.appendChild(menuItem);
};
const workspaces = this.openerWindow.gZenWorkspaces.getWorkspaces();
createXulItem(mostRecentSpace.value, "most-recent-space");
createXulItem("sep");
workspaces.forEach(workspace => {
createXulItem(workspace.name, workspace.uuid, workspace.icon);
});
// Check if the workspace still exists, if not use default
if (availOptions.includes(value)) {
selectElement.value = value;
} else {
selectElement.value = "most-recent-space";
}
}
/**
* Uninitializes the boost editor by cleaning up event listeners and observers.
*/
uninit() {
nsZenSpaceRoutingDialog.OBSERVERS.forEach(observe => {
Services.obs.removeObserver(this, observe);
});
}
/**
* Kills all other Space Routing dialog instances
*/
killOtherShareInstances() {
Services.obs.notifyObservers(null, "zen-space-routing-kill");
}
/**
* Observer callback that handles notifications from the observer service.
* Closes the control window when a 'zen-space-routing-kill' notification is received.
*
* @param {object} subject - The subject of the notification.
* @param {string} topic - The topic of the notification.
*/
observe(subject, topic) {
switch (topic) {
case "zen-space-routing-kill":
this.editorWindow.close();
break;
}
}
/**
* Callback for when the user presses the close button
*/
onClosePressed() {
this.editorWindow.close();
}
/**
* Handles the window close event
*/
handleClose() {
gZenSpaceRoutingManager.saveRoutes();
}
}

View File

@@ -0,0 +1,413 @@
/* 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";
class nsZenSpaceRoutingManager {
#file = null;
#saveFilename = "zen-space-routing.jsonlz4";
static SKIP_TYPE = {
NONE: "none",
SKIPPED_TAB: "skipped_tab",
RESTORED_TAB: "restored_tab",
};
constructor() {
this.#readFromDisk();
}
/**
* Callback that will be executed from tabbrowser.js
* This method can be used to stop the tab from being created.
*
* @param {string} uriString - The URI as a string
* @param {object} options - The tab creation options
* @param {Window} win - The window which the tab will be added to
* @returns {object} Returns an object with { shouldEarlyExit, userContextId, isRouteFound, targetRoute }
*/
onBeforeAddTab(uriString, options, win) {
let userContextId = null;
let isRouteFound = false;
let targetRoute = null;
if (
this.#shouldSkipProcessing(options, win) !=
nsZenSpaceRoutingManager.SKIP_TYPE.NONE
) {
return {
shouldEarlyExit: false,
userContextId,
isRouteFound,
targetRoute,
};
}
targetRoute = this.routeUri(uriString, options);
switch (targetRoute) {
case "most-recent-space":
break;
default: {
const targetWorkspace =
win?.gZenWorkspaces?.getWorkspaceFromId(targetRoute);
if (targetWorkspace) {
userContextId = targetWorkspace.containerTabId;
isRouteFound = true;
}
}
}
return { shouldEarlyExit: false, userContextId, isRouteFound, targetRoute };
}
/**
* Callback that will be executed from tabbrowser.js
*
* @param {string} uriString - The URI as a string
* @param {Element} newTab - The tab element
* @param {object} options - The tab creation options
* @param {Window} win - The window which the tab was added to
* @param {object} [beforeResult] - The result returned by onBeforeAddTab for
* this tab. When present its precomputed targetRoute is reused instead of
* running routeUri() a second time.
*/
onAfterAddTab(uriString, newTab, options, win, beforeResult) {
const targetRoute = beforeResult?.targetRoute;
if (!targetRoute) {
return;
}
this.#routeToWorkspace(targetRoute, newTab, win);
}
/**
* Checks if the tab should be processed or not
*
* @param {object} options - The tab creation options
* @param {Window} win - The owning window
* @returns {SKIP_TYPE} The type of skip or null if not skipped
*/
#shouldSkipProcessing(options, win) {
if (options.skipRoute || options.pinned || options.tabGroup) {
return nsZenSpaceRoutingManager.SKIP_TYPE.SKIPPED_TAB;
}
// addTab() is being called when the session restores.
// To avoid automatically routing these tabs,
// a check if the restore is already complete is needed
if (!win.gZenStartup.isReady) {
return nsZenSpaceRoutingManager.SKIP_TYPE.RESTORED_TAB;
}
return nsZenSpaceRoutingManager.SKIP_TYPE.NONE;
}
/**
* Will route the given tab to a space if a rule applies
*
* @param {string} targetRoute - The precomputed route for the tab
* @param {Element} newTab - The tab element
* @param {Window} win - The window which the tab was added to
* @private
*/
async #routeToWorkspace(targetRoute, newTab, win) {
try {
if (!newTab || !newTab.parentNode) {
return;
}
switch (targetRoute) {
case "most-recent-space":
break;
default: {
const workspaces = win?.gZenWorkspaces;
const targetWorkspace = workspaces?.getWorkspaceFromId?.(targetRoute);
if (targetWorkspace) {
workspaces.moveTabToWorkspace(newTab, targetWorkspace.uuid);
const mostRecentWindow =
Services.wm.getMostRecentWindow("navigator:browser");
const isOriginatingWindow = win === mostRecentWindow;
if (isOriginatingWindow) {
win.gZenWorkspaces.lastSelectedWorkspaceTabs[
targetWorkspace.uuid
] = newTab;
await win.gZenWorkspaces.changeWorkspace(targetWorkspace);
}
}
}
}
} catch (err) {
console.error("[ZenSpaceRouting]: Error moving tab to workspace:", err);
}
}
/**
* This will give the id of the workspace this uri will
* route to, or "most-recent-space"
*
* @param {string} uriString - The uri which will be routed
* @param {object} options - The tab creation options
* @returns {string} Route instructions
*/
routeUri(uriString, options) {
const isExternal = options.fromExternal;
// Go over all routes and return the open type for the first match
const allRoutes = this.getAllRoutes();
for (const route of allRoutes) {
if (this.isRouteMatching(uriString, route)) {
return route.openIn;
}
}
// If nothing matches and it's an external link,
// use the default external route
if (isExternal) {
return this.getDefaultExternalRoute();
}
// If nothing matches, open in most recent space
return "most-recent-space";
}
/**
* Checks if a given rule matches a uriString
*
* @param {string} uriString - The uri
* @param {object} route - The route
* @returns {boolean} True if the rule matches
*/
isRouteMatching(uriString, route) {
if (typeof uriString !== "string" || typeof route?.reference !== "string") {
return false;
}
let reference = route.reference.toLowerCase();
if (reference.trim() == "") {
return false;
}
const uri = uriString.toLowerCase();
switch (route.matchType) {
case "contains":
if (uri.includes(reference)) {
return true;
}
break;
case "equal-to":
if (this.#normalizeURL(uri) == this.#normalizeURL(reference)) {
return true;
}
break;
case "regex": {
let unmodifiedReference = route.reference;
try {
// Use unmodified parameters for the regex test
const regex = new RegExp(unmodifiedReference);
if (regex.test(uriString)) {
return true;
}
} catch (e) {
console.error(
"[ZenSpaceRouting] Failed to resolve regular expression:",
unmodifiedReference,
e
);
}
break;
}
}
return false;
}
/**
* Will remove any protocol sequences to normalize the url
*
* @param {string} uriString - The url
* @returns {string} The normalized url
*/
#normalizeURL(uriString) {
if (!uriString) {
return "";
}
let clean = uriString.trim();
// Remove protocol sequences with regex
clean = clean.replace(/^https?:\/\//i, "");
clean = clean.replace(/^www\./i, "");
// If there is a trailing slash, remove
if (clean.endsWith("/")) {
clean = clean.slice(0, -1);
}
return clean;
}
/**
* Opens the Space Routing editor in a new popup window.
*
* @param {Window} parentWindow - The parent browser window
* @returns {Window|null} The instanced editor window
*/
async openSpaceRoutingDialog(parentWindow) {
await parentWindow.gDialogBox.open(
"chrome://browser/content/zen-components/windows/zen-space-routing.xhtml",
{
features: "resizable=no",
sizeTo: "available",
allowDuplicateDialogs: false,
parentWindow,
}
);
}
/**
* @returns {object} Returns a new empty Space Routing route
*/
getEmptyRoute() {
return {
id: crypto.randomUUID(),
reference: "",
openIn: "most-recent-space",
matchType: "contains",
};
}
/**
* @returns {Array<object>} A copy of the routes list
*/
getAllRoutes() {
if (!this.#file?.data?.routes) {
return [];
}
return structuredClone(this.#file.data.routes);
}
/**
* Returns a specific route
*
* @param {string} id - The ID of the given route
* @returns {object|null} The route, or null if no route has the given id
*/
getRoute(id) {
const idx = this.#file.data.routes.findIndex(r => r.id === id);
if (idx === -1) {
return null;
}
return structuredClone(this.#file.data.routes[idx]);
}
/**
* Will update an existing route
*
* @param {object} route - The updated route
*/
updateRoute(route) {
const idx = this.#file.data.routes.findIndex(r => r.id === route.id);
if (idx === -1) {
return;
}
this.#file.data.routes[idx] = structuredClone(route);
}
/**
* Creates a new route and returns it
*
* @returns {object} Returns the empty route
*/
createNewRoute() {
const newRoute = this.getEmptyRoute();
this.#file.data.routes.push(newRoute);
return structuredClone(newRoute);
}
/**
* Removes an existing route with the given id
*
* @param {string} id - The given id
*/
removeRoute(id) {
const objWithIdIndex = this.#file.data.routes.findIndex(r => r.id === id);
if (objWithIdIndex === -1) {
return;
}
this.#file.data.routes.splice(objWithIdIndex, 1);
}
/**
* @returns {string} Returns the default route type for external links
*/
getDefaultExternalRoute() {
return this.#file?.data?.defaultRouteExternal ?? "most-recent-space";
}
/**
* @param {string} routeType - Sets the default route type for external links
*/
setDefaultExternalRoute(routeType) {
this.#file.data.defaultRouteExternal = routeType;
}
/**
* Saves all routes
*/
saveRoutes() {
this.#writeToDisk();
}
/**
* Writes the Space Routing data back onto the disk.
*
* @private
*/
#writeToDisk() {
this.#file.saveSoon();
}
/**
* Reads Space Routing data from disk and decompresses it.
*
* @returns {Promise<Map>} A promise that resolves to an array of Space Routing rules.
* @private
*/
async #readFromDisk() {
this.#file = new JSONFile({
path: this.#storePath,
compression: "lz4",
dataPostProcessor(data) {
if (!data || typeof data !== "object") {
data = {};
}
if (!Array.isArray(data.routes)) {
data.routes = [];
}
if (typeof data.defaultRouteExternal !== "string") {
data.defaultRouteExternal = "most-recent-space";
}
return data;
},
});
await this.#file.load();
}
/**
* Gets the file path where Space Routing data is stored in the user's profile directory.
*
* @returns {string} The full path to the Space Routing storage file.
* @private
*/
get #storePath() {
const profilePath = PathUtils.profileDir;
return PathUtils.join(profilePath, this.#saveFilename);
}
}
export const gZenSpaceRoutingManager = new nsZenSpaceRoutingManager();

View File

@@ -0,0 +1,9 @@
# 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/.
# Styles
content/browser/zen-styles/zen-space-routing.css (../../zen/space-routing/zen-space-routing.css)
# Windows
* content/browser/zen-components/windows/zen-space-routing.xhtml (../../zen/space-routing/zen-space-routing.inc.xhtml)

View File

@@ -0,0 +1,8 @@
# 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.spacerouting += [
"ZenSpaceRoutingDialog.mjs",
"ZenSpaceRoutingManager.sys.mjs",
]

View File

@@ -0,0 +1,304 @@
/* 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/. */
@namespace html "http://www.w3.org/1999/xhtml";
@namespace xul "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
:root {
--background-color-canvas: light-dark(white, #212223) !important;
--select-background-color: light-dark(#eceef0, #3e3f43);
--input-background-color: light-dark(#f1f2f4, #2c2d31);
--hr-color: light-dark(#cdced4, #313235);
--sr-width: 510px;
--sr-height: 500px;
--content-padding: 26px;
--content-padding-vertical: 16px;
--text-color: light-dark(#4c4c4c, #dbdcdf);
--text-color-secondary: light-dark(#5c5e65, #8b8e98);
--text-color-error: light-dark(#9d2222, #d03535);
--rules-gap: 14px;
--sr-border-radius: 12px;
}
.dialog-button-box {
/* Remove default dialog buttons */
display: none !important;
}
window {
height: var(--sr-height);
max-height: var(--sr-height);
min-height: var(--sr-height);
width: var(--sr-width);
max-width: var(--sr-width);
min-width: var(--sr-width);
}
#sr-container {
height: var(--sr-height);
width: var(--sr-width);
border-radius: var(--sr-border-radius);
overflow: hidden;
font-family: system-ui !important;
position: relative;
display: flex;
}
#sr-empty-content {
display: flex;
flex-direction: column;
text-align: center;
gap: 24px;
position: absolute;
left: 84px;
right: 84px;
bottom: 100px;
top: 100px;
justify-content: center;
& image {
aspect-ratio: 1;
height: 75px;
min-height: 0;
max-height: none;
max-width: none;
min-width: 0;
color: var(--text-color-secondary);
-moz-context-properties: fill, fill-opacity;
fill: currentColor;
fill-opacity: 0.65;
}
& p {
font-size: small;
opacity: 0.8;
color: var(--text-color);
}
}
#sr-new-route {
padding: 4px 6px !important;
}
html|hr {
border-color: var(--hr-color) !important;
padding: 0;
margin: 0;
margin-left: 22px;
margin-right: 22px;
border-width: 0.5px;
}
h3 {
font-size: medium;
color: var(--text-color);
font-weight: normal;
}
h4 {
font-size: small;
color: var(--text-color-secondary);
}
p {
color: var(--text-color);
}
.select {
width: 150px;
min-height: unset;
height: 26px;
padding: 4px;
}
.select,
.select * {
background-color: var(--select-background-color) !important;
color: var(--text-color);
margin: 0;
&.match-type-select {
width: 100px;
}
&.open-in-select {
width: 165px;
}
}
menulist[image] .menulist-icon,
menulist[image]::part(icon) {
width: 16px;
height: 16px;
min-width: 16px;
min-height: 16px;
-moz-context-properties: fill, stroke;
fill: currentColor;
margin-inline-end: 0;
padding-left: 2px;
}
menulist {
-moz-box-align: center;
}
.sr-rule-row {
display: flex;
align-items: center;
gap: 8px;
}
.sr-rule-container {
display: flex;
flex-direction: column;
gap: 4px;
margin: 0 24px;
& p {
color: var(--text-color-secondary);
}
}
.sr-rule-bottom {
margin-left: 87px;
}
.input,
.input * {
background-color: var(--input-background-color);
color: var(--text-color);
margin: 0;
flex-grow: 1;
}
.invalid,
.invalid * {
color: var(--text-color-error) !important;
text-decoration-line: underline;
text-decoration-style: wavy;
text-decoration-thickness: 1px;
text-decoration-color: var(--text-color-error);
}
button {
background-color: var(--select-background-color) !important;
min-height: unset !important;
padding: 2px !important;
display: flex;
justify-content: center;
margin: 0;
& hbox {
display: none;
}
}
.sr-remove,
.close-icon {
& hbox {
display: flex;
}
opacity: 0.5;
appearance: none !important;
border: none !important;
height: 32px;
width: 32px;
padding: 3px;
min-width: 0 !important;
color: var(--text-color-secondary);
-moz-context-properties: fill, fill-opacity;
fill: currentColor;
&:not(:hover) {
background: none !important;
}
}
#sr-header {
width: 100%;
padding: var(--content-padding) 20px;
align-items: center;
display: flex;
flex-direction: row;
}
.sr-left {
margin-left: auto;
display: flex;
flex-direction: row;
align-items: center;
gap: 6px;
& button {
color: var(--text-color);
opacity: 1;
&:hover .sr-remove {
opacity: 0.8;
}
}
}
#sr-footer {
width: 100%;
padding: var(--content-padding);
align-items: center;
display: flex;
flex-direction: row;
}
#sr-content {
width: 100%;
overflow: scroll;
padding: 20px var(--content-padding);
flex-grow: 1;
display: flex;
flex-direction: column;
gap: var(--rules-gap);
}
.sr-label-container {
display: flex;
align-items: center;
gap: 6px;
color: var(--text-color-secondary);
}
.sr-label {
margin: 0;
}
.sr-url-icon,
.sr-open-in-icon {
width: 16px;
height: 16px;
-moz-context-properties: fill, fill-opacity;
fill: currentColor;
fill-opacity: 0.65;
}

View File

@@ -0,0 +1,96 @@
#filter substitution
<?xml version="1.0"?>
# -*- 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/.
<!DOCTYPE window>
<window
xmlns:html="http://www.w3.org/1999/xhtml"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
windowtype="Zen:SpaceRouting"
customtitlebar="true"
id="zenSpaceRoutingDialog"
dialogroot="true"
>
<linkset>
<html:link
rel="stylesheet"
href="chrome://browser/content/zen-styles/zen-space-routing.css"
type="text/css"
/>
<html:link
rel="stylesheet"
href="chrome://global/skin/global.css"
type="text/css"
/>
<html:link
rel="stylesheet"
href="chrome://browser/content/zen-styles/zen-theme.css"
type="text/css"
/>
<html:link
rel="stylesheet"
href="chrome://browser/skin/zen-icons/icons.css"
type="text/css"
/>
<html:link
rel="stylesheet"
type="text/css"
href="chrome://browser/content/zen-styles/zen-popup.css"
/>
<html:link rel="localization" href="browser/zen-space-routing.ftl" />
</linkset>
<vbox id="sr-container">
<hbox id="sr-header">
<h3 data-l10n-id="zen-space-routing-dialog-title"></h3>
<html:div class="sr-left">
<button
id="sr-new-route"
data-l10n-id="zen-space-routing-new-route"
></button>
<button
class="close-icon"
data-l10n-id="zen-space-routing-close-button"
id="sr-close"
></button>
</html:div>
</hbox>
<html:hr />
<vbox id="sr-content">
<vbox id="sr-empty-content">
<image class="sr-airplane"></image>
<p data-l10n-id="zen-space-routing-rulepanel-placeholder"></p>
</vbox>
# All rules will be injected here later
</vbox>
<html:hr />
<hbox id="sr-footer">
<h4 data-l10n-id="zen-space-routing-external-default"></h4>
<html:div class="sr-left">
<menulist class="select" id="sr-default-external-open-in">
<menupopup id="sr-default-external-open-in-popup">
# Select open in types will be injected here
</menupopup>
</menulist>
</html:div>
</hbox>
</vbox>
<script>
const { nsZenSpaceRoutingDialog } = ChromeUtils.importESModule(
"resource:///modules/zen/spacerouting/ZenSpaceRoutingDialog.mjs",
);
const args = window.arguments?.[0] || {};
window.spaceroutingDialog = new nsZenSpaceRoutingDialog(
document,
window,
args.parentWindow,
);
</script>
</window>

View File

@@ -861,6 +861,7 @@ class nsZenWorkspaces {
"Selecting empty tab because startup page didnt select a valid tab"
);
this.selectEmptyTab();
initialTabWasEmpty = true;
}
this.log("Removing empty tab added by startup page");
this._removedByStartupPage = true;
@@ -954,7 +955,7 @@ class nsZenWorkspaces {
}
gZenEmojiPicker.open(anchor, {
closeOnSelect: false,
allowNone: hasNoIcon,
allowNone: !hasNoIcon,
onSelect: async icon => {
const workspace = this.getWorkspaceFromId(workspaceId);
if (!workspace) {
@@ -2978,11 +2979,18 @@ class nsZenWorkspaces {
// Tab browser utilities
getContextIdIfNeeded(userContextId, fromExternal) {
getContextIdIfNeeded(userContextId, fromExternal, triggeringPrincipal) {
if (!this.workspaceEnabled) {
return [userContextId, false, undefined];
}
if (
triggeringPrincipal &&
triggeringPrincipal.isAddonOrExpandedAddonPrincipal
) {
return [userContextId, false, undefined];
}
if (
this.shouldForceContainerTabsToWorkspace &&
typeof userContextId !== "undefined" &&

View File

@@ -322,7 +322,6 @@ zen-workspace {
height: 100%;
overflow: hidden;
color: var(--toolbox-textcolor);
will-change: transform;
@media not (prefers-reduced-motion: reduce) {
transition: padding-top 0.1s;

View File

@@ -1226,7 +1226,10 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
const currentTab = gZenGlanceManager.getTabOrGlanceParent(
window.gBrowser.selectedTab
);
const newTab = this.openAndSwitchToTab(url, { inBackground: false });
const newTab = this.openAndSwitchToTab(url, {
skipRoute: true,
inBackground: false,
});
this.splitTabs([currentTab, newTab], undefined, 1);
}

View File

@@ -1131,7 +1131,6 @@
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));

View File

@@ -8,6 +8,41 @@ support-files = [
]
["browser_boost_selector_basic.js"]
["browser_boost_selector_escaping.js"]
["browser_boost_selector_invalid.js"]
["browser_boost_selector_nthchild.js"]
["browser_boosts_animation.js"]
["browser_boosts_background.js"]
["browser_boosts_border.js"]
["browser_boosts_gradient.js"]
["browser_boosts_inline_svg.js"]
["browser_boosts_input_text.js"]
["browser_boosts_invert.js"]
["browser_boosts_outline.js"]
["browser_boosts_placeholder.js"]
["browser_boosts_pseudo_before.js"]
["browser_boosts_shadow.js"]
["browser_boosts_svg_background_image.js"]
["browser_boosts_svg_image.js"]
["browser_boosts_svg_linear_gradient.js"]
["browser_boosts_svg_use_sprite.js"]
["browser_boosts_text_color.js"]

View File

@@ -0,0 +1,52 @@
/* 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/. */
"use strict";
// The compositor animation path in AnimationInfo.cpp resolves a colour with
// the host frame, then either takes it as-is (currentColor keyframe) or
// passes it through ResolveStyleColor (absolute keyframe). Both paths must
// end up at the same boosted colour as the static equivalent. We pin a
// background-color animation at 50% via animation-delay and compare against
// a static element that holds the interpolated colour.
add_task(async function animated_background_is_boosted() {
// Paused-at-50% animation between #000 and #fff → mid grey at the sampled
// time. We compare against a static rgb(128,128,128) swatch and require
// both to land at the same colour after boost.
const html = `
<style>
html, body { margin: 0; padding: 0; background: white; }
.swatch { width: 200px; height: 200px; display: inline-block;
vertical-align: top; }
#static { background-color: rgb(128, 128, 128); }
@keyframes fade { from { background-color: black; } to { background-color: white; } }
#animated {
background-color: black;
animation: fade 4s linear infinite;
animation-delay: -2s;
animation-play-state: paused;
}
</style>
<div id="static" class="swatch"></div>
<div id="animated" class="swatch"></div>`;
await BrowserTestUtils.withNewTab(dataUrl(html), async browser => {
await waitForRepaint(browser);
await setBoost(browser, {
accent: PAGE_ACCENT,
complementaryRotation: PAGE_COMPLEMENTARY_ROTATION,
});
const staticBoosted = await pixelInElement(browser, "#static");
const animBoosted = await pixelInElement(browser, "#animated");
Assert.ok(
pixelsClose(staticBoosted, animBoosted, 6),
`animated and static mid-grey must land at the same boosted colour; ` +
`static=${JSON.stringify(staticBoosted)} animated=${JSON.stringify(
animBoosted
)}`
);
});
});

View File

@@ -0,0 +1,50 @@
/* 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/. */
"use strict";
// Verifies that activating a boost on a tab moves the painted colour of a
// plain CSS `background-color` block. Catches regressions where the per-color
// boost in StyleAbsoluteColor::ToColor or CalcColor is bypassed for
// backgrounds, and the gap that would surface if nsCSSRendering ever stopped
// passing the frame through GetVisitedDependentColor.
add_task(async function bg_color_is_tinted() {
const html = `
<style>
html, body { margin: 0; padding: 0; }
#bg { width: 200px; height: 200px; background-color: rgb(120, 120, 120); }
</style>
<div id="bg"></div>`;
await BrowserTestUtils.withNewTab(dataUrl(html), async browser => {
await waitForRepaint(browser);
await setBoost(browser, { accent: 0 });
const baseline = await pixelInElement(browser, "#bg");
Assert.equal(baseline.r, 120, "baseline R is the literal background");
Assert.equal(baseline.g, 120, "baseline G is the literal background");
Assert.equal(baseline.b, 120, "baseline B is the literal background");
await setBoost(browser, {
accent: PAGE_ACCENT,
complementaryRotation: PAGE_COMPLEMENTARY_ROTATION,
});
const boosted = await pixelInElement(browser, "#bg");
Assert.ok(
pixelsDiffer(baseline, boosted, 3),
`boost should tint the background; got baseline=${JSON.stringify(
baseline
)} boosted=${JSON.stringify(boosted)}`
);
// Sanity: clear the boost and the painted colour returns home.
await setBoost(browser, { accent: 0 });
const cleared = await pixelInElement(browser, "#bg");
Assert.ok(
pixelsClose(cleared, baseline, 2),
`clearing boost should restore original; got ${JSON.stringify(cleared)}`
);
});
});

View File

@@ -0,0 +1,53 @@
/* 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/. */
"use strict";
// Borders go through nsCSSRendering::ComputeBorderColors → CalcColor with the
// frame, so the boost should reach them. Use a thick solid border and sample
// inside the border band (which is fully the border colour) rather than the
// element interior.
add_task(async function border_color_is_tinted() {
const html = `
<style>
html, body { margin: 0; padding: 0; background: white; }
#b { width: 200px; height: 200px; margin: 50px;
background: white;
border: 30px solid rgb(120, 120, 120); }
</style>
<div id="b"></div>`;
await BrowserTestUtils.withNewTab(dataUrl(html), async browser => {
await waitForRepaint(browser);
// Sample a point inside the top border band: x = centre of element, y just
// below the top edge (well inside the 30px-wide border).
const point = await SpecialPowers.spawn(browser, [], () => {
const r = content.document.querySelector("#b").getBoundingClientRect();
return {
x: Math.round(r.left + r.width / 2),
y: Math.round(r.top + 15),
};
});
await setBoost(browser, { accent: 0 });
const baseline = await pixelAt(browser, point.x, point.y);
Assert.ok(
pixelsClose(baseline, { r: 120, g: 120, b: 120 }, 5),
`baseline border colour ≈ rgb(120,120,120); got ${JSON.stringify(baseline)}`
);
await setBoost(browser, {
accent: PAGE_ACCENT,
complementaryRotation: PAGE_COMPLEMENTARY_ROTATION,
});
const boosted = await pixelAt(browser, point.x, point.y);
Assert.ok(
pixelsDiffer(baseline, boosted, 3),
`border colour should be tinted; baseline=${JSON.stringify(baseline)} ` +
`boosted=${JSON.stringify(boosted)}`
);
});
});

View File

@@ -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/. */
"use strict";
// CSS linear-gradient stops are resolved via the nsCSSGradientRenderer +
// ColorStopInterpolator path, which threads the frame down into
// gfxUtils::ToDeviceColor(StyleAbsoluteColor, frame). If that threading
// regresses, gradient stops paint without the boost while everything else
// around them gets tinted — a particularly visible regression.
add_task(async function linear_gradient_stops_are_boosted() {
// Use a two-stop horizontal gradient so a sample near the left edge is
// dominated by the first stop and a sample near the right edge by the
// second. The element is sized 400×200 so we have generous sample regions.
const html = `
<style>
html, body { margin: 0; padding: 0; background: white; }
#g { width: 400px; height: 200px;
background: linear-gradient(to right,
rgb(180, 60, 60),
rgb(60, 60, 180)); }
</style>
<div id="g"></div>`;
await BrowserTestUtils.withNewTab(dataUrl(html), async browser => {
await waitForRepaint(browser);
// Sample 5% in (mostly first stop) and 95% in (mostly last stop).
const points = await SpecialPowers.spawn(browser, [], () => {
const r = content.document.querySelector("#g").getBoundingClientRect();
return {
left: {
x: Math.round(r.left + r.width * 0.05),
y: Math.round(r.top + r.height / 2),
},
right: {
x: Math.round(r.left + r.width * 0.95),
y: Math.round(r.top + r.height / 2),
},
};
});
await setBoost(browser, { accent: 0 });
const leftBaseline = await pixelAt(browser, points.left.x, points.left.y);
const rightBaseline = await pixelAt(
browser,
points.right.x,
points.right.y
);
await setBoost(browser, {
accent: PAGE_ACCENT,
complementaryRotation: PAGE_COMPLEMENTARY_ROTATION,
});
const leftBoosted = await pixelAt(browser, points.left.x, points.left.y);
const rightBoosted = await pixelAt(browser, points.right.x, points.right.y);
Assert.ok(
pixelsDiffer(leftBaseline, leftBoosted, 3),
`left gradient stop must tint; baseline=${JSON.stringify(
leftBaseline
)} boosted=${JSON.stringify(leftBoosted)}`
);
Assert.ok(
pixelsDiffer(rightBaseline, rightBoosted, 3),
`right gradient stop must tint; baseline=${JSON.stringify(
rightBaseline
)} boosted=${JSON.stringify(rightBoosted)}`
);
// The two stops must remain distinguishable after boost — otherwise the
// gradient has flattened, which would be a separate regression (e.g.,
// ToDeviceColor losing per-stop frame context and collapsing to a single
// tinted value).
Assert.ok(
pixelsDiffer(leftBoosted, rightBoosted, 8),
`boosted gradient endpoints collapsed; left=${JSON.stringify(
leftBoosted
)} right=${JSON.stringify(rightBoosted)}`
);
});
});

Some files were not shown because too many files have changed in this diff Show More