Compare commits

...

80 Commits

Author SHA1 Message Date
mr. m
34c2618ca0 feat: Increase drag over split threshold, b=no-bug, c=split-view 2026-03-04 13:05:07 +01:00
mr. m
84b7cf8ddd fix: Also apply auto appearance to non arrow popups, b=no-bug, c=common 2026-03-04 07:31:01 +01:00
mr. m
0d816b3cc2 chore: Sync external patches, p=#12607 2026-03-03 22:51:57 +01:00
mr. m
ad4eeee55a feat: Make sure to trim down parameters before fetching github PRs, b=no-bug, c=folders 2026-03-03 11:52:12 +01:00
mr. m
ad74b55dbf Revert "fix: github folder not getting pr list, p=#12578, c=folders", p=#12599 2026-03-03 11:28:32 +01:00
mr. m
5ffb2d8d69 feat: Don't search clipboard content when middle clicking the sidebar, b=no-bug, c=no-component 2026-03-03 01:01:19 +01:00
mr. m
5a91c0c70b feat: Add more spacings between the essentials paddings, b=no-bug, c=tabs 2026-03-03 00:49:55 +01:00
mr. m
3831af027e feat: Fixed wrong rendering when animating popover opens, b=no-bug, c=common 2026-03-03 00:46:30 +01:00
Mr. M
ee5c1894eb chore: Streamline surfer implementation to newer versions for win signs, b=no-bug, c=no-component 2026-03-03 00:14:13 +01:00
Kryštof Gärtner
e31d91282b feat: add specific toast for copying URL as Markdown, p=#12588 2026-03-02 19:35:58 +01:00
mr. m
32b355595c feat: Correctly assign containers to new live folder tabs, b=no-bug, c=folders 2026-03-02 18:13:57 +01:00
Slowlife
b5b31c02d0 fix: github folder not getting pr list, p=#12578, c=folders
Co-authored-by: mr. m <mr.m@tuta.com>
2026-03-02 15:03:16 +01:00
mr. m
bcdb905ad6 feat: Correctly mark tab visibility inside active folders, b=no-bug, c=common, folders 2026-03-02 09:58:49 +01:00
mr. m
3044b409fa chore: Always restore normal tabs when updating, b=no-bug, c=no-component 2026-03-02 00:09:10 +01:00
Niraj Yadav
747dd3d4bc feat: use sidebar-keep-hover delay while hiding sidebar, p=#12541
Co-authored-by: mr. m <mr.m@tuta.com>
2026-03-01 18:52:44 +01:00
mr. m
49225cf685 chore: Import external patches with replacements, b=no-bug, c=no-component 2026-03-01 18:09:35 +01:00
mr. m
0f4abf2237 Merge branch 'dev' of https://github.com/zen-browser/desktop into dev 2026-03-01 13:51:24 +01:00
mr. m
657d03821f feat: Start checking for spaces mroe robustly when restoring, b=no-bug, c=common, workspaces 2026-03-01 13:50:28 +01:00
mr. m
8fc2ecbb66 chore: Sync external patches, b=closes https://github.com/zen-browser/desktop/issues/12555, p=#12560 2026-03-01 11:52:59 +01:00
mr-cheffy
d78dcdddaf docs: Update monthly issue metrics, b=(no bug), c={docs} 2026-03-01 03:13:27 +00:00
Andrey Bochkarev
9bc0a4ca92 refactor: Simplify new tab handling in collapsed live folders, p=#12552 2026-02-28 19:38:32 +01:00
Andrey Bochkarev
6aefade7f8 fix: Add position: relative to tab groups for positioning context, p=#12550 2026-02-28 18:51:07 +01:00
mr. m
0226c5879a feat: Don't use native popovers for the theme picker on the onboarding, b=no-bug, c=welcome 2026-02-28 15:04:40 +01:00
mr. m
ee3ec4c1ea feat: Stop using folder refs to prevent leakage, b=no-bug, c=folders 2026-02-28 11:22:55 +01:00
mr. m
881c411a26 feat: Use the correct loading principals for live folders, b=no-bug, c=folders 2026-02-27 18:48:13 +01:00
mr. m
96a52b7354 Merge branch 'dev' of https://github.com/zen-browser/desktop into dev 2026-02-27 16:29:40 +01:00
mr. m
f52eeb0d60 fix: Prevent watermark to run when chrome is hidden, b=bug #12364, c=common, workspaces 2026-02-27 16:27:48 +01:00
dependabot[bot]
942a266184 chore(deps-dev): bump minimatch, p=#12539
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-27 13:13:25 +01:00
mr. m
1a1804d2be feat: Properly animate tab addition to collapsed live folders, b=no-bug, c=folders 2026-02-27 13:12:28 +01:00
mr. m
22fd6133f1 feat: Improved styling for native MacOS popovers and fixed anchor positions, b=no-bug, c=workflows, media, common, folders 2026-02-27 12:54:41 +01:00
Andrey Bochkarev
16b898f4e0 fix: Prevent unnecessary layout reset when new tabs are added, p=#12535 2026-02-26 19:36:59 +01:00
Andrey Bochkarev
d67b4457e8 feat: Allow unsplitting tab without selecting it via Shift-Click, p=#12534 2026-02-26 18:46:58 +01:00
Andrey Bochkarev
b54a7027fd fix: Allow split view creation when split view is selected, p=#12532
* fix: Allow split view creation when split view is selected

* fix: Added tabs are always on the right
2026-02-26 18:34:56 +01:00
Andrey Bochkarev
72a35a5ea5 fix: Prevent live folder tabs from mixing with other tabs, p=#12528 2026-02-26 17:16:44 +01:00
mr. m
c9cad08ae7 feat: Hide AI controls from settings as we never have them enabled, b=no-bug, c=no-component 2026-02-26 14:52:56 +01:00
mr. m
b563e06527 chore: Import external patches, p=#12525 2026-02-26 13:52:19 +01:00
mr. m
88a647a3bb fix: Make sure to not run any switching when quitting, b=no-bug, c=workspaces 2026-02-26 11:28:17 +01:00
mr. m
5a9a9ce51a feat: Change omnibox results to use :where selectors instead of :is selectors, b=no-bug, c=common, workspaces 2026-02-26 00:22:28 +01:00
mr. m
0a5a6d0604 feat: Remove some of the AI features introduced in FF 148, b=no-bug, c=no-component 2026-02-25 23:39:01 +01:00
Andrey Bochkarev
b92d697657 fix: Add guard to prevent invalid split drops, p=#12511 2026-02-25 20:37:53 +01:00
mr. m
5e27368a48 fix: Fixed race conditions when syncing views through windows desktops, b=bug #12390, c=no-component 2026-02-25 20:31:28 +01:00
mr. m
2c740b1abf feat: Start animating folder closes, b=no-bug, c=common 2026-02-25 20:19:32 +01:00
mr. m
bae234867e fix: Fixed active tabs showing when collapsing unrelated folders, b=closes #12509, p=#12517, c=folders 2026-02-25 18:22:39 +01:00
mr. m
9145e36457 Merge branch 'dev' of https://github.com/zen-browser/desktop into dev 2026-02-25 18:22:33 +01:00
mr. m
e30f7e20a6 feat: Small tweaks to live folders popup, b=no-bug, c=folders, workspaces 2026-02-25 18:22:30 +01:00
mr. m
726f6e9132 chore: Sync external patches, p=#12514 2026-02-25 16:27:44 +01:00
mr. m
43384e54e7 Merge branch 'dev' of https://github.com/zen-browser/desktop into dev 2026-02-25 16:23:20 +01:00
mr. m
4ee2e49b27 feat: Install nodejs dependencies when syncing external patches, b=no-bug, c=workflows 2026-02-25 16:22:27 +01:00
mr. m
a5a984922b feat: Install requirements from list for sync workflow, p=#12513, c=workflows 2026-02-25 16:19:58 +01:00
mr. m
34af405cbd fix: Fixed wrong filename being used for external patch import, b=no-bug, c=workflows 2026-02-25 16:15:37 +01:00
mr. m
648e0b1683 Merge branch 'dev' of https://github.com/zen-browser/desktop into dev 2026-02-25 16:12:37 +01:00
mr. m
4ae9f81a68 feat: Add workflow to import expternal patches, b=no-bug, c=workflows, windows 2026-02-25 16:11:41 +01:00
Slowlife
94a55c73c6 fix: wait for tabs state to be restored before init UI, p=#12506, c=live-folder 2026-02-25 12:01:13 +01:00
mr. m
a7c87e6392 chore: Bump release version, b=no-bug, c=common 2026-02-25 02:22:17 +01:00
Andre Nijman
bdf8bc6b33 fix: Hide folder icon remove action when no icon is set, b=closes #12401, p=#12481, c=folders, common
Co-authored-by: andre <andre@localhost>
Co-authored-by: mr. m <91018726+mr-cheffy@users.noreply.github.com>
2026-02-25 00:34:22 +01:00
mr. m
4e3413fea5 Merge branch 'dev' of https://github.com/zen-browser/desktop into dev 2026-02-25 00:26:54 +01:00
mr. m
5714450b60 feat: Change feel for DnD tab splitting, b=no-bug, c=split-view 2026-02-25 00:26:49 +01:00
Harshit Tomar
2758c962b5 feat: Add 'open new blank window' action to zen.desktop, p=#12503 2026-02-24 23:38:31 +01:00
mr. m
7b9bdec209 chore: Sync upstream to Firefox 148.0, p=#12498 2026-02-24 23:37:08 +01:00
mr. m
443a778ea6 Merge branch 'dev' of https://github.com/zen-browser/desktop into dev 2026-02-24 23:34:10 +01:00
mr. m
29165bb1e2 chore: Run lint after to fix deps issues, b=no-bug, c=workflows 2026-02-24 23:34:04 +01:00
Andrey Bochkarev
1554818aa8 feat: Add support for creating split view with dnd, p=#11928, c=tabs
Co-authored-by: mr. m <91018726+mr-cheffy@users.noreply.github.com>
Co-authored-by: mr. m <mr.m@tuta.com>
2026-02-24 23:29:44 +01:00
mr. m
353b65e25b feat: Dont switch permanent keys for window closes, b=closes #12152, c=workflows, flatpak 2026-02-24 23:19:46 +01:00
mr. m
a8ccd1ca3c feat: Make folder search popup ignore outside clicks, b=no-bug, c=folders, common 2026-02-24 12:47:20 +01:00
mr. m
c772f6d795 feat: Improve live folder enter animation, b=no-bug, c=folders 2026-02-24 10:54:13 +01:00
mr. m
e5622d2237 Merge branch 'dev' of https://github.com/zen-browser/desktop into dev 2026-02-24 00:56:07 +01:00
mr. m
250d0c641c feat: Add feature callout for live folders, b=no-bug, c=folders, common 2026-02-24 00:56:01 +01:00
Slowlife
529d557a38 refactor: switch to use DeferredTask in scheduling logic, p=#12480, c=live-folders 2026-02-23 12:36:44 +01:00
mr. m
bbaf779e7a chore: Run lint before downloading firefox for PRs to have an early exit, b=no-bug, c=workflows 2026-02-23 12:36:18 +01:00
mr. m
e59ff43490 Merge branch 'dev' of https://github.com/zen-browser/desktop into dev 2026-02-23 12:19:34 +01:00
mr. m
ca27b4d76b feat: Default back different search engines on private mode, b=closes #12484, c=no-component 2026-02-23 12:11:16 +01:00
mr. m
bd43b86de8 feat: Correctly animate new tabs for collapsed live folders, b=no-bug, c=folders 2026-02-23 12:05:05 +01:00
mr. m
d3d50d0f8c fix: Fixed urlbar closing when window loses focus, b=closes #7248, p=#12477, c=urlbar 2026-02-23 00:59:00 +01:00
Andre Nijman
dd92699ca1 fix: Screenshot actions hidden behind Glance, b=closes https://github.com/zen-browser/desktop/issues/12196, p=#12468, c=glance
Co-authored-by: andre <andre@localhost>
2026-02-22 23:05:10 +01:00
Andre Nijman
4f7a1efdfa fix: Keep app menu fullscreen button visible in localized labels, b=closes #9547, p=#12470, c=common
Co-authored-by: andre <andre@localhost>
2026-02-22 23:04:40 +01:00
Slowlife
08226c518c fix: remove extra slash in github url, p=#12471, c=live-folders, tests 2026-02-22 15:52:22 +01:00
mr. m
e919e723c7 feat: Add native popover UI for macos, p=#12456 2026-02-21 23:07:30 +01:00
mr. m
52d03f43ef feat: Add concurrency checks for lint and PR workflows, p=#12457, c=workflows 2026-02-21 21:30:15 +01:00
mr. m
5ed59fb902 chore: Updated to a better github logo for better live folder visibility, b=no-bug, c=common 2026-02-21 21:08:34 +01:00
mr. m
011fd67248 chore: Bump motion version, b=no-bug, c=common, folders, vendor, workspaces 2026-02-21 20:50:10 +01:00
88 changed files with 2140 additions and 623 deletions

View File

@@ -248,8 +248,8 @@ jobs:
- name: Setup Git
run: |
git config --global user.email "mauro-balades@users.noreply.github.com"
git config --global user.name "mauro-balades"
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
- name: Install dependencies
run: |

View File

@@ -9,6 +9,10 @@ on:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
lint:
runs-on: ubuntu-latest

View File

@@ -70,8 +70,8 @@ jobs:
- name: Setup Git
run: |
git config --global user.email "mauro-balades@users.noreply.github.com"
git config --global user.name "mauro-balades"
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
- name: Install system dependencies
run: |

View File

@@ -68,8 +68,8 @@ jobs:
- name: Setup Git
run: |
git config --global user.email "mauro-balades@users.noreply.github.com"
git config --global user.name "mauro-balades"
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
- name: Install system dependencies
run: |

View File

@@ -38,8 +38,8 @@ jobs:
- name: Setup Git
run: |
git config --global user.email "mauro-balades@users.noreply.github.com"
git config --global user.name "mauro-balades"
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
- name: Install system dependencies
run: |

View File

@@ -7,6 +7,10 @@ on:
branches:
- dev
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
pr-test:
runs-on: ubuntu-latest
@@ -29,8 +33,8 @@ jobs:
ZEN_DOWNLOAD_DONT_INIT_GIT: "1"
run: npm run download
- name: Import patches
run: npm run import
- name: Run linting
run: npm run lint
- name: Import patches
run: npm run import

View File

@@ -0,0 +1,82 @@
name: Sync External Patches
on:
workflow_dispatch:
permissions:
contents: write
jobs:
check_candidates:
name: Sync External Patches
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v3
- name: Setup Python
uses: actions/setup-python@v5
# note: This will use the version defined in '.python-version' by defaultç
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: ".nvmrc"
- name: Setup git
run: |
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
- name: Install requirements
run: pip3 install -r requirements.txt
- name: Import external patches
run: python3 scripts/update_external_patches.py
- name: Check if any files changed
id: git-check
run: |
if [ -n "$(git status --porcelain)" ]; then
echo "files_changed=true" >> $GITHUB_OUTPUT
else
echo "files_changed=false" >> $GITHUB_OUTPUT
fi
- name: Install dependencies
if: steps.git-check.outputs.files_changed == 'true'
run: npm ci
- name: Download Firefox and dependencies
if: steps.git-check.outputs.files_changed == 'true'
run: npm run download
- name: Check if patches got applied
if: steps.git-check.outputs.files_changed == 'true'
id: check-patches
continue-on-error: true
run: |
echo "Checking if patches apply cleanly..."
npm run import
- name: Create pull request
uses: peter-evans/create-pull-request@v7
if: steps.git-check.outputs.files_changed == 'true'
env:
GIT_TRACE: 1
GIT_CURL_VERBOSE: 1
with:
token: ${{ secrets.DEPLOY_KEY }}
commit-message: "chore: Sync external patches"
branch: "chore/sync-external-patches-${{ github.run_id }}"
title: "Sync external patches"
body: |
This PR syncs the external patches automatically.
* ${{ steps.check-patches.outcome == 'failure' && '⚠️ Some patches did not apply cleanly. Please review them carefully.' || '✅ All patches applied cleanly.' }}
@${{ github.actor }} please review and merge this PR. Generated from workflow run [${{ github.run_id }}](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}), branch ${{ github.head_ref }}.
base: dev
git-token: ${{ secrets.DEPLOY_KEY }}
delete-branch: true

View File

@@ -50,8 +50,8 @@ jobs:
- name: Install dependencies
if: steps.check-upstream-branch.outputs.branch_exists == 'false'
run: |
git config --global user.email "mr-cheffy@users.noreply.github.com"
git config --global user.name "mr-cheffy"
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
- name: Setup surfer CI
if: steps.check-upstream-branch.outputs.branch_exists == 'false'
@@ -76,6 +76,9 @@ jobs:
- name: Install autopep8
run: sudo apt install python3-autopep8
- name: Install requirements
run: pip3 install -r requirements.txt
- name: Check if any files changed
id: git-check
run: |
@@ -111,7 +114,7 @@ jobs:
- name: Import external patches
if: steps.git-check.outputs.files_changed == 'true'
run: python3 scripts/import_external_patches.py || true
run: python3 scripts/update_external_patches.py || true
- name: Run formatter
if: steps.check-upstream-branch.outputs.branch_exists == 'false'

View File

@@ -2,7 +2,7 @@ name: Zen Twilight Scheduled Releases
on:
schedule:
- cron: "0 23 * * *"
- cron: "0 23 */2 * *"
workflow_dispatch:
inputs:
create_release:

View File

@@ -41,8 +41,8 @@ jobs:
- name: Setup Git
run: |
git config --global user.email "mauro-balades@users.noreply.github.com"
git config --global user.name "mauro-balades"
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
- name: Install Surfer
run: npm i -g @zen-browser/surfer

View File

@@ -79,8 +79,8 @@ jobs:
- name: Setup Git
run: |
git config --global user.email "mauro-balades@users.noreply.github.com"
git config --global user.name "mauro-balades"
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
- name: Install dependencies
run: |

2
.nvmrc
View File

@@ -1 +1 @@
20
22

View File

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

View File

@@ -11,13 +11,17 @@ StartupNotify=true
Terminal=false
X-MultipleArgs=false
Keywords=Internet;WWW;Browser;Web;Explorer;
Actions=new-window;new-private-window;profilemanager;
Actions=new-window;new-blank-window;new-private-window;profilemanager;
X-AppImage-Version=$VERSION
[Desktop Action new-window]
Name=Open a New Window
Exec=zen %u
[Desktop Action new-blank-window]
Name=Open a New Blank Window
Exec=zen --blank-window %u
[Desktop Action new-private-window]
Name=Open a New Private Window
Exec=zen --private-window %u

View File

@@ -1 +1 @@
b7d21837fa8fcae98dab2a4f016cc9fb3053903d
40d4c2395112d4188721e69d338ee75bded8858a

View File

@@ -25,7 +25,6 @@ finish-args:
- --filesystem=xdg-run/speech-dispatcher:ro
- --device=all
- --talk-name=org.freedesktop.FileManager1
- --talk-name=org.freedesktop.Notifications
- --system-talk-name=org.freedesktop.NetworkManager
- --talk-name=org.a11y.Bus
- --talk-name=org.gtk.vfs.*

View File

@@ -23,6 +23,9 @@ mkdir windsign-temp -ErrorAction SilentlyContinue
# echo "Downloaded git objects repo to"
#} -Verbose -ArgumentList $PWD -Debug
$env:SURFER_MOZCONFIG_ONLY="1"
$env:SURFER_SIGNING_MODE=""
Start-Job -Name "DownloadGitl10n" -ScriptBlock {
param($PWD)
cd $PWD
@@ -31,14 +34,11 @@ Start-Job -Name "DownloadGitl10n" -ScriptBlock {
echo "Fetched l10n and Firefox's one"
} -Verbose -ArgumentList $PWD -Debug
Start-Job -Name "SurferInit" -ScriptBlock {
param($PWD)
cd $PWD
npm run import -- --verbose
$surferJson = Get-Content surfer.json | ConvertFrom-Json
$version = $surferJson.brands.release.release.displayVersion
npm run ci -- $version
} -Verbose -ArgumentList $PWD -Debug
$surferJson = Get-Content surfer.json | ConvertFrom-Json
$version = $surferJson.brands.release.release.displayVersion
npm run ci -- $version
npm run import -- --verbose
npm run build
echo "Downloading artifacts info"
$artifactsInfo=gh api repos/zen-browser/desktop/actions/runs/$GithubRunId/artifacts
@@ -120,7 +120,6 @@ signtool.exe sign /n "$SignIdentity" /t http://time.certum.pl/ /fd sha256 /v $fi
$env:ZEN_RELEASE="true"
$env:SURFER_SIGNING_MODE="true"
$env:SCCACHE_GHA_ENABLED="false"
Wait-Job -Name "SurferInit"
Wait-Job -Name "DownloadGitl10n"
function SignAndPackage($name) {

View File

@@ -0,0 +1,217 @@
# Issue Metrics
| Metric | Average | Median | 90th percentile |
| --- | --- | --- | ---: |
| Time to first response | 1 day, 1:26:40 | 2:07:50 | 2 days, 5:33:54 |
| Time to close | 1 day, 12:18:56 | 3:30:42 | 4 days, 7:35:38 |
| Metric | Count |
| --- | ---: |
| Number of items that remain open | 97 |
| Number of items closed | 102 |
| Total number of items created | 199 |
| Title | URL | Time to first response | Time to close |
| --- | --- | --- | --- |
| Zen browser highlight elements by itself | https://github.com/zen-browser/desktop/issues/12551 | 5:54:36 | None |
| pasted url revert to old one automaticaly ? | https://github.com/zen-browser/desktop/issues/12548 | 6:47:54 | None |
| chat.mistral.ai - site is not usable | https://github.com/zen-browser/desktop/issues/12547 | 10:45:42 | None |
| Horizontal Scrolling with mouse wheel is not working | https://github.com/zen-browser/desktop/issues/12545 | None | 1:30:19 |
| The Text color of search bar when press the "Ctrl + f" | https://github.com/zen-browser/desktop/issues/12537 | None | None |
| Buggy Native Popover for Tab Previews on Twilight | https://github.com/zen-browser/desktop/issues/12529 | 0:06:41 | 0:06:41 |
| Can't drag files directly from Downloads panel | https://github.com/zen-browser/desktop/issues/12520 | 11:56:10 | 11:56:24 |
| Repeated SIGSEGV in libxul.so on Linux (1.18.10b, Fedora 43, AMD) | https://github.com/zen-browser/desktop/issues/12516 | None | None |
| Previously closed windows recovery option missing | https://github.com/zen-browser/desktop/issues/12515 | 1:40:19 | 1:40:20 |
| Empty Spaces after waking laptop | https://github.com/zen-browser/desktop/issues/12512 | 4:14:32 | 1 day, 1:42:28 |
| New tab shortcut shows search input instead of tab input | https://github.com/zen-browser/desktop/issues/12510 | 8:46:21 | 8:46:21 |
| Folder tree breaks with live folders fetch | https://github.com/zen-browser/desktop/issues/12509 | 0:08:50 | 8:11:04 |
| Tabs bar shakes in compact mode | https://github.com/zen-browser/desktop/issues/12505 | 0:02:08 | None |
| Youtube Playlist issue | https://github.com/zen-browser/desktop/issues/12502 | None | 18:54:24 |
| Maps with very much data are very laggy | https://github.com/zen-browser/desktop/issues/12501 | 3:09:58 | 16:56:17 |
| Allow changing interface font for generic Linux tarball | https://github.com/zen-browser/desktop/issues/12500 | 1:05:58 | None |
| The transparent gradient background suddenly covers the current tab | https://github.com/zen-browser/desktop/issues/12497 | 8:59:58 | 8:59:58 |
| Obscene memory leak on Mac | https://github.com/zen-browser/desktop/issues/12496 | 0:35:11 | None |
| top tool bar and close/minimize buttons on title bar overlap on windows | https://github.com/zen-browser/desktop/issues/12495 | 1 day, 16:34:34 | None |
| Tabs in essentials disappear after closing Zen | https://github.com/zen-browser/desktop/issues/12493 | 14:53:54 | None |
| When you open a new window using the taskbar icon, it mirrors the tabs of another window. | https://github.com/zen-browser/desktop/issues/12492 | 0:11:21 | 0:11:21 |
| using my GoBack mouse button switches workspaces | https://github.com/zen-browser/desktop/issues/12491 | 0:03:40 | None |
| closing Folder tabs doesn't close them | https://github.com/zen-browser/desktop/issues/12490 | 0:08:26 | 0:08:26 |
| Zen compact hover mode time increase not respected | https://github.com/zen-browser/desktop/issues/12489 | 0:06:19 | 0:06:19 |
| White dots spreading everywhere on any website visited in Zen Browser, reset when interacting with UI layer; duplicate contour lines appearing at the same time as the dots on the Zen Browser UI - Windows 11 | https://github.com/zen-browser/desktop/issues/12488 | None | 2 days, 13:52:50 |
| What just happened to my tab??? (Also when I close Zen browser while I watching YouTube, the audio still exist, wth?) | https://github.com/zen-browser/desktop/issues/12486 | 0:27:29 | None |
| Reddit multi word enter search won't work | https://github.com/zen-browser/desktop/issues/12485 | None | None |
| Use a different default search engine in private windows defaults to true | https://github.com/zen-browser/desktop/issues/12484 | None | 1:52:44 |
| ALSA support disabled, no sound on ALSA only systems | https://github.com/zen-browser/desktop/issues/12483 | 10:13:00 | None |
| Addressbar closing when inputing german Umlauts with us_intl keymap | https://github.com/zen-browser/desktop/issues/12482 | 2:18:36 | 2:18:36 |
| Zen Browser Tabs: Backgroung Image is not working | https://github.com/zen-browser/desktop/issues/12479 | 7:28:03 | 17:51:35 |
| bookmarks keep vanishing | https://github.com/zen-browser/desktop/issues/12478 | 8:34:20 | None |
| The menu for my browser history button is moving down in KDE Plasma. | https://github.com/zen-browser/desktop/issues/12475 | None | 1:43:31 |
| Zen Browser is being blocked by BattlEye anti-cheat during game launch on Windows 11. | https://github.com/zen-browser/desktop/issues/12473 | 0:06:23 | None |
| CRUNCHYROLL NOT WORKING | https://github.com/zen-browser/desktop/issues/12467 | 0:05:42 | 0:05:42 |
| Holding rightclick will caues wrong mouse position | https://github.com/zen-browser/desktop/issues/12466 | None | None |
| Battery Drain MacOS - High CPU usage | https://github.com/zen-browser/desktop/issues/12464 | 3 days, 19:23:37 | None |
| Logged out of all Google accounts and can't log back in. | https://github.com/zen-browser/desktop/issues/12462 | 20:50:59 | None |
| Blank Window and Session Loss After Update | https://github.com/zen-browser/desktop/issues/12460 | 0:05:20 | None |
| Sometimes web lagging for a few second | https://github.com/zen-browser/desktop/issues/12458 | 0:00:47 | None |
| Mods don't work anymore | https://github.com/zen-browser/desktop/issues/12455 | 0:36:29 | 2:08:08 |
| Auto Update Failing To Update (Message Update Failed) | https://github.com/zen-browser/desktop/issues/12454 | 2:55:11 | 2:55:11 |
| Setting up keyboard shortcuts does not recognize azerty keyboard | https://github.com/zen-browser/desktop/issues/12451 | 0:12:44 | None |
| External links open new window with duplicated tabs when browser is minimized on Windows | https://github.com/zen-browser/desktop/issues/12450 | 6:32:08 | None |
| Browser doesn't know where mouse is until clicking out of the browser | https://github.com/zen-browser/desktop/issues/12447 | None | None |
| Beginning w/ v1.18.8b (?) my navigation started floating/popping/hiding; never asked for that. Then v1.18.9b (?) removed all my tabs incl auto/pinned | https://github.com/zen-browser/desktop/issues/12443 | 1:13:56 | 1:18:00 |
| Bookmarks not shown on site pages | https://github.com/zen-browser/desktop/issues/12442 | 0:01:11 | 0:01:11 |
| Side bar flickering | https://github.com/zen-browser/desktop/issues/12441 | None | None |
| Sidebar should not hide when mouse pointer is still at the edge of the screen | https://github.com/zen-browser/desktop/issues/12439 | 0:08:24 | None |
| Zen not respecting output audio device switching in Windows | https://github.com/zen-browser/desktop/issues/12438 | None | None |
| New tab does not make a new tab - it only activates the URL bar | https://github.com/zen-browser/desktop/issues/12432 | 0:26:23 | 0:26:23 |
| ZEN Ignoring DNS over HTTPS OFF | https://github.com/zen-browser/desktop/issues/12429 | 0:29:20 | None |
| The search window does not appear until you start typing | https://github.com/zen-browser/desktop/issues/12428 | 1:42:23 | 5 days, 22:42:30 |
| Toolbar icons overflow after uninstalling extension (requires restart to fix) | https://github.com/zen-browser/desktop/issues/12427 | 6:05:59 | 6:05:58 |
| Side Tab Flickering when minimized from full view - faced in mac desktop browser | https://github.com/zen-browser/desktop/issues/12425 | 4:54:13 | None |
| buggy Tabs drag and drop | https://github.com/zen-browser/desktop/issues/12423 | 0:28:12 | None |
| Spaces dissapear suddently | https://github.com/zen-browser/desktop/issues/12422 | 0:07:36 | 4 days, 0:53:47 |
| Tabs become New Tab when using Window Sync | https://github.com/zen-browser/desktop/issues/12421 | 2 days, 13:53:49 | 3 days, 15:49:00 |
| Closed the browser after a glitch, now the app remains full white | https://github.com/zen-browser/desktop/issues/12420 | 0:04:32 | 1 day, 2:01:15 |
| Search bar overflow over main screen from pining to overflow menu but also breaks ui in recreating | https://github.com/zen-browser/desktop/issues/12419 | None | None |
| Closing Sidebar Causes Unexpected Border Increase on the Other Side. | https://github.com/zen-browser/desktop/issues/12418 | 0:25:14 | None |
| Persistent Favicon Inconsistency/Tint on GitHub Bookmarks | https://github.com/zen-browser/desktop/issues/12415 | 1 day, 22:54:53 | None |
| Sometimes, the right-click menu for links will keep shifting to the right | https://github.com/zen-browser/desktop/issues/12413 | 6:45:26 | 10:55:38 |
| AppImage lacks version metadata, preventing AppImage update detection | https://github.com/zen-browser/desktop/issues/12412 | 13:17:50 | 8:27:29 |
| [Windows] Launching a new window (Ctrl + N) replicates the same tabs on the new window | https://github.com/zen-browser/desktop/issues/12411 | 11:10:43 | 11:10:43 |
| Tab preview text illegible in front of light background | https://github.com/zen-browser/desktop/issues/12410 | 11:14:21 | None |
| Temporary Containers broke with update | https://github.com/zen-browser/desktop/issues/12409 | 1:57:05 | 14:06:01 |
| Zen doesn't work with Simple Tab Groups | https://github.com/zen-browser/desktop/issues/12408 | 4:58:31 | None |
| Unable to set "Copy Current URL" to ctrl+shift+INSERT | https://github.com/zen-browser/desktop/issues/12406 | None | None |
| Browser Toolbox Error Loading | https://github.com/zen-browser/desktop/issues/12405 | 5:30:27 | 5:30:27 |
| Restoring Split Tabs Ignored | https://github.com/zen-browser/desktop/issues/12403 | 0:14:37 | None |
| Windows: Scroll bar in tab list causes layout shift when always show scroll bars is enabled | https://github.com/zen-browser/desktop/issues/12402 | 2 days, 3:59:15 | None |
| Trash icon appears in Change Icon menu for folders with no icon | https://github.com/zen-browser/desktop/issues/12401 | None | 8 days, 11:45:52 |
| Window sync has been forced back on | https://github.com/zen-browser/desktop/issues/12400 | 0:47:07 | 0:47:07 |
| Toolbar not opening when approaching with mouse to fast | https://github.com/zen-browser/desktop/issues/12398 | None | None |
| Can't use Proton Pass because of second password | https://github.com/zen-browser/desktop/issues/12393 | None | None |
| Can't Customize toolbar on "only sidebar" configuration | https://github.com/zen-browser/desktop/issues/12391 | None | None |
| Sync deletes tab (kind of) | https://github.com/zen-browser/desktop/issues/12390 | 9 days, 7:25:30 | None |
| Please allow us to increase the font size on the left tabs. | https://github.com/zen-browser/desktop/issues/12388 | 0:52:26 | 0:52:26 |
| Vertical Sidebar starts 'oscillating' when it is reduced in size. | https://github.com/zen-browser/desktop/issues/12387 | 0:05:02 | 0:05:41 |
| [Regression] Severe stuttering and long loading on Bilibili (4K) in v1.18.7b | https://github.com/zen-browser/desktop/issues/12386 | 8 days, 5:05:17 | None |
| [macOS] Now Playing widget completely non-functional (works in Firefox) | https://github.com/zen-browser/desktop/issues/12385 | 1:16:07 | 1 day, 23:03:30 |
| responsive design mode can't be enlarged by zooming | https://github.com/zen-browser/desktop/issues/12381 | 11 days, 16:50:20 | None |
| Pinned tab under a folder, opens and redirect to a new regular tab | https://github.com/zen-browser/desktop/issues/12379 | 2:37:39 | None |
| xdg-open fails to open files in existing Zen instance (Arch Linux and Hyprland) | https://github.com/zen-browser/desktop/issues/12378 | 23:23:25 | None |
| Sidebar coloring issues in private windows | https://github.com/zen-browser/desktop/issues/12377 | None | None |
| [Bug] Youtube shorts notplaying properly, and when using the shortcut "I' it gives an error. | https://github.com/zen-browser/desktop/issues/12376 | 13:44:07 | None |
| Sometimes, when opening a new tab, M4 and M5 buttons change spaces | https://github.com/zen-browser/desktop/issues/12375 | 0:41:41 | 0:41:41 |
| Private Windows don't function with Window Sync | https://github.com/zen-browser/desktop/issues/12374 | 0:33:47 | 0:33:47 |
| Passkey from qr code not showing on widows 10 os | https://github.com/zen-browser/desktop/issues/12372 | None | None |
| Relatively Low Audio Volume | https://github.com/zen-browser/desktop/issues/12370 | None | None |
| Workspace icon highlight missing after latest update | https://github.com/zen-browser/desktop/issues/12368 | 1:49:10 | 1:49:15 |
| Prime Video | https://github.com/zen-browser/desktop/issues/12365 | 0:18:36 | 0:18:36 |
| Taskbar Tabs create semi-transparent, empty, non-closeable windows | https://github.com/zen-browser/desktop/issues/12364 | 1:17:21 | None |
| [Bug] Enterprise Policies via macOS Managed Preferences (.plist) are not recognized | https://github.com/zen-browser/desktop/issues/12363 | None | None |
| Misaligned video fullscreen mode for 2 monitors | https://github.com/zen-browser/desktop/issues/12362 | 2 days, 5:21:33 | None |
| Choppy audio on YouTube when using a external DAC | https://github.com/zen-browser/desktop/issues/12361 | 2 days, 5:21:25 | 2 days, 5:47:20 |
| Performance difference between appimage and Flatpak | https://github.com/zen-browser/desktop/issues/12357 | 0:01:34 | None |
| "Close Pinned Tab Shortcut Behavior" ignored for "Places" tab | https://github.com/zen-browser/desktop/issues/12353 | 15 days, 10:51:28 | None |
| Menu flickers when pressed | https://github.com/zen-browser/desktop/issues/12352 | 9 days, 0:49:56 | None |
| Closing, Minimizing, and Maximizing Buttons squished | https://github.com/zen-browser/desktop/issues/12351 | 0:09:14 | 0:09:14 |
| New Tab bug on mac | https://github.com/zen-browser/desktop/issues/12350 | 0:02:33 | 0:02:32 |
| Redirect notification toast overlaps right sidebar | https://github.com/zen-browser/desktop/issues/12348 | None | 13:39:22 |
| macOS: close/minimize/maximize buttons are not displayed correctly | https://github.com/zen-browser/desktop/issues/12347 | 0:15:49 | 0:15:49 |
| After updating to 1.18.6b, minimize, maximize and close buttons are squished in Windows | https://github.com/zen-browser/desktop/issues/12346 | 0:09:07 | 0:09:56 |
| Layout bug for minimize/maximize/close window buttons on Windows 11 | https://github.com/zen-browser/desktop/issues/12345 | 0:11:27 | 0:11:28 |
| Disabling windows sync still results in same tabs being displayed throught multiple window sessions when restarting/restoring | https://github.com/zen-browser/desktop/issues/12344 | None | 0:20:21 |
| Picture-in-picture not working as intended | https://github.com/zen-browser/desktop/issues/12342 | 3:39:41 | 1 day, 2:32:57 |
| Icons aren't displaying correctly | https://github.com/zen-browser/desktop/issues/12340 | None | 5:45:13 |
| Workspaces aren't syncing to a new device | https://github.com/zen-browser/desktop/issues/12339 | 1:07:18 | 6:26:52 |
| All tabs and folders are missing after update to 1.18.5b | https://github.com/zen-browser/desktop/issues/12337 | 8:49:51 | 8:49:51 |
| macOS profile lost pinned tabs and Essentials after Windows sync folders remain but are empty | https://github.com/zen-browser/desktop/issues/12336 | 9:29:24 | None |
| Zen misses some color support | https://github.com/zen-browser/desktop/issues/12335 | 0:45:02 | 0:45:02 |
| Disabled "sync tabs" loose tabs | https://github.com/zen-browser/desktop/issues/12333 | 4:05:24 | None |
| MacOs: Intel-based Mac Pro - Zen fails to launch (GPU subprocess / IPC timeouts) | https://github.com/zen-browser/desktop/issues/12331 | None | None |
| Zen launch error | https://github.com/zen-browser/desktop/issues/12330 | None | 0:39:18 |
| When I attempted to rename the workspace, the compact mode failed and I was unable to enter the compact mode again. | https://github.com/zen-browser/desktop/issues/12329 | 3:21:05 | 3:21:05 |
| Cannot properly position or resize Zen with certain window managers | https://github.com/zen-browser/desktop/issues/12327 | None | None |
| Space resets when closing window (but not quitting) after clearing browser history | https://github.com/zen-browser/desktop/issues/12326 | 0:46:52 | 14:27:39 |
| New tabs don't change tab title | https://github.com/zen-browser/desktop/issues/12318 | 10:51:08 | None |
| Weird reopen closed tab behaviour for folders | https://github.com/zen-browser/desktop/issues/12316 | None | None |
| Per Workspace Essentials No Longer Work. | https://github.com/zen-browser/desktop/issues/12313 | 0:54:50 | 2:25:53 |
| Are Spaces supposed to sync between different devices? | https://github.com/zen-browser/desktop/issues/12311 | 1:26:09 | 1:26:09 |
| Open previous windows and tabs settings / continue where you left off being ignored. | https://github.com/zen-browser/desktop/issues/12307 | 12:51:38 | 13:53:11 |
| local access disappeared after 18.5 | https://github.com/zen-browser/desktop/issues/12305 | 16:24:35 | None |
| Transparency is weird on macOS | https://github.com/zen-browser/desktop/issues/12303 | 1:16:12 | None |
| Vertical Sidebar flickering/jittering during interaction | https://github.com/zen-browser/desktop/issues/12299 | 3:23:55 | None |
| "Sync only pinned tabs in workspaces" breaks "Open previous windows and tabs" | https://github.com/zen-browser/desktop/issues/12297 | 0:05:40 | 10:53:40 |
| Github Actions Artifacts downloads do not work on Zen | https://github.com/zen-browser/desktop/issues/12296 | None | None |
| Zen menu bar items take excessive horizontal space compared to other apps | https://github.com/zen-browser/desktop/issues/12294 | None | None |
| Workspaces lost after upgrade to 1.18.5b (Firefox 147.0.3) (64-bit) on Windows 11 Pro Intel | https://github.com/zen-browser/desktop/issues/12293 | 3:33:29 | 3:33:29 |
| Kernel panic on macOS Sequoia 15.7.3 when running Zen | https://github.com/zen-browser/desktop/issues/12290 | None | None |
| Opening a GitHub CodeSpace in the Browser throws a "Oh No, it looks like youre offline!" error tho i have a stable Internet connection, and it worked on older Versions of Zen before. Chrome on the same device/internet works. | https://github.com/zen-browser/desktop/issues/12288 | None | None |
| Zen does not read privacy.fingerprintingProtection from user.js | https://github.com/zen-browser/desktop/issues/12286 | 1:44:38 | 7:13:24 |
| Closing a window lead sometimes to window sync to stop working | https://github.com/zen-browser/desktop/issues/12284 | None | None |
| Pinned tabs reset to incorrect URL (pinned URL changes) | https://github.com/zen-browser/desktop/issues/12281 | 0:00:55 | None |
| Compact mode non hide stuck | https://github.com/zen-browser/desktop/issues/12279 | 15:23:06 | None |
| Recently closed windows/undo close window missing | https://github.com/zen-browser/desktop/issues/12278 | 4 days, 8:20:17 | 4 days, 8:20:17 |
| Opening about: pages causes 'new tab' bug with window sync | https://github.com/zen-browser/desktop/issues/12277 | 9 days, 16:39:47 | None |
| arm64 Flatpak compositor crash on ChromeOS Crostini (virgl / Mesa 25.x regression) | https://github.com/zen-browser/desktop/issues/12276 | 8 days, 18:31:53 | None |
| YouTube freezes when switching audio output in fxSound | https://github.com/zen-browser/desktop/issues/12275 | None | None |
| Button missing: Hamburger Menu > History > Restore Previous Session | https://github.com/zen-browser/desktop/issues/12272 | 0:10:35 | None |
| Next/Prev Tab Shortcut Conflict | https://github.com/zen-browser/desktop/issues/12267 | 0:03:09 | 0:03:09 |
| 1.18.5b Release Download Not Found | https://github.com/zen-browser/desktop/issues/12266 | 0:13:04 | 0:13:04 |
| Viedo streaming websites become unresponsive when multiple instances are open | https://github.com/zen-browser/desktop/issues/12265 | None | None |
| All workspaces deleted upon v1.18.5b installation | https://github.com/zen-browser/desktop/issues/12261 | 1:07:39 | 3:40:07 |
| Zen fails to start with sync only pinned tabs | https://github.com/zen-browser/desktop/issues/12260 | 0:04:34 | 0:41:21 |
| Thunderbird cannot open a new Zen window when clicking a link | https://github.com/zen-browser/desktop/issues/12259 | 14:35:21 | None |
| zen.urlbar.replace-newtab = False issues with new windows and tab syncing | https://github.com/zen-browser/desktop/issues/12258 | 2 days, 7:24:59 | None |
| Shortcuts Icon's not working | https://github.com/zen-browser/desktop/issues/12257 | 1 day, 7:22:00 | 15 days, 8:21:20 |
| ADD A METHOD TO DISABLE UPDATES , IM LITERALLY USING A TASK SCHEDULER TO DELETE YOUR STUPID UPDATES FOLDER AND YOU STILL FIND A WAY | https://github.com/zen-browser/desktop/issues/12256 | 2:34:58 | 0:05:06 |
| Broken essential tab URL after switching with keyboard shortcuts | https://github.com/zen-browser/desktop/issues/12255 | 0:19:28 | None |
| Closing glance bug with multiple windows | https://github.com/zen-browser/desktop/issues/12254 | None | None |
| Dragging tabs into folders shows selected folder highlight at incorrect position in some view modes. | https://github.com/zen-browser/desktop/issues/12252 | 1:31:56 | 3:52:46 |
| "New split view" in command bar (on a page accessed from an Essential tab) opens a new tab instead | https://github.com/zen-browser/desktop/issues/12251 | 0:10:54 | None |
| Zen starts as blank widows on macOS Tahoe Version 26.3 (25D122) beta | https://github.com/zen-browser/desktop/issues/12250 | 0:38:21 | 3 days, 23:19:20 |
| Pined extensions icons no longer visible in compact sidebar since last release | https://github.com/zen-browser/desktop/issues/12249 | 2 days, 20:44:58 | None |
| Facing Glitches On Start Opens 2 Windows for Same tabs. | https://github.com/zen-browser/desktop/issues/12248 | 1:25:01 | None |
| Separate windows mirroring | https://github.com/zen-browser/desktop/issues/12247 | 0:12:51 | 0:12:51 |
| H.264, HEVC, and AAC Not Supported in Kubuntu App Center Version (1.17.14b) | https://github.com/zen-browser/desktop/issues/12245 | 1 day, 12:07:19 | None |
| Private and blank windows ignore CloseWindowWithLastTab setting | https://github.com/zen-browser/desktop/issues/12242 | 9:22:25 | 2 days, 8:59:22 |
| Bookmark temporarily removes | https://github.com/zen-browser/desktop/issues/12241 | None | None |
| Multiple Windows Launch on Startup after Multiple Windows Have Been Closed | https://github.com/zen-browser/desktop/issues/12238 | 16:04:12 | None |
| Duplicated Toggle Full Screen and View Full Screen for keyboard shortcut | https://github.com/zen-browser/desktop/issues/12237 | 10 days, 20:34:41 | 11 days, 11:58:31 |
| Container no longer displayed when moving tabs between windows on different spaces | https://github.com/zen-browser/desktop/issues/12235 | 10 days, 13:27:01 | 10 days, 18:33:05 |
| Multi account container settings reset | https://github.com/zen-browser/desktop/issues/12234 | None | None |
| Clicking the “Copy” button opens an “Open With” dialog on KDE Plasma Wayland. | https://github.com/zen-browser/desktop/issues/12232 | 13 days, 7:34:57 | 14 days, 19:58:57 |
| Can't open a new window with clear context | https://github.com/zen-browser/desktop/issues/12229 | None | 0:01:26 |
| Can't play DRM content in Linux | https://github.com/zen-browser/desktop/issues/12228 | 4:21:30 | 6:06:50 |
| Missing icons in address bar: padlock (https) and star (to bookmark current page) | https://github.com/zen-browser/desktop/issues/12226 | None | 0:08:28 |
| Broken extension/browser tools sidebar keybind | https://github.com/zen-browser/desktop/issues/12225 | 5:45:59 | 9 days, 20:16:58 |
| Sidebar empty after copying profile from Firefox | https://github.com/zen-browser/desktop/issues/12224 | 0:25:56 | 0:25:56 |
| Lost all my folders with the latest update | https://github.com/zen-browser/desktop/issues/12223 | 0:55:54 | 0:55:54 |
| History window doesnt follow default system or browser theme | https://github.com/zen-browser/desktop/issues/12222 | None | None |
| when i have multiple windows and restart zen , all the windows will be the same , have the same tabs | https://github.com/zen-browser/desktop/issues/12221 | 0:35:11 | 0:22:16 |
| Deleting All Data | https://github.com/zen-browser/desktop/issues/12220 | 0:59:01 | None |
| Move to Space in Tab Context Menu not available | https://github.com/zen-browser/desktop/issues/12219 | 0:12:00 | None |
| When opening Zen after closing multiple windows, only one window has workspaces | https://github.com/zen-browser/desktop/issues/12218 | 0:59:47 | None |
| Forms don't allow to be processed | https://github.com/zen-browser/desktop/issues/12217 | 6:15:33 | 6:15:33 |
| Cannot drag and drop to create a split view | https://github.com/zen-browser/desktop/issues/12216 | 0:37:06 | 0:37:06 |
| Glance does not work via keybind+click in Reader VIew | https://github.com/zen-browser/desktop/issues/12214 | None | None |
| "New Blank Window" is not capitalized in Mac OS menu bar | https://github.com/zen-browser/desktop/issues/12213 | None | 7 days, 4:10:17 |
| Zen changes tab IDs without sending tabs.onReplaced events | https://github.com/zen-browser/desktop/issues/12212 | 1:12:47 | 1:12:47 |
| Private window is adopting Blank windows styling | https://github.com/zen-browser/desktop/issues/12211 | None | 0:19:10 |
| The loading indicator on tabs does not appear when a tab is loading. | https://github.com/zen-browser/desktop/issues/12210 | 1:23:55 | 1:23:55 |
| Page content cannot scroll fully to top; top area is hidden behind browser UI | https://github.com/zen-browser/desktop/issues/12209 | 13:06:36 | None |
| TURN OFF WINDOW SYNC | https://github.com/zen-browser/desktop/issues/12206 | 0:47:53 | 1:23:13 |
| Essentials not working correctly | https://github.com/zen-browser/desktop/issues/12205 | 3:18:34 | 3:27:55 |
| Tab titles not updating | https://github.com/zen-browser/desktop/issues/12204 | 3:40:45 | 5:54:14 |
| Folders disappeared after updating to the Zen version 1.18.3b (Firefox 147.0.2) (aarch64) | https://github.com/zen-browser/desktop/issues/12203 | 9:12:45 | 9:12:45 |
| Some websites don't load | https://github.com/zen-browser/desktop/issues/12202 | 1 day, 10:47:54 | None |
| Website favicons have the wrong icons | https://github.com/zen-browser/desktop/issues/12201 | 2:36:06 | 0:04:20 |
| tab indexes are off by 2 | https://github.com/zen-browser/desktop/issues/12199 | None | 3:02:10 |
| Zen doesn't identify itself in the extensions API | https://github.com/zen-browser/desktop/issues/12198 | 1 day, 21:45:16 | 6 days, 22:02:36 |
| Part of the screenshot overlay is rendered below glance windows | https://github.com/zen-browser/desktop/issues/12196 | None | 21 days, 5:30:56 |
| Glance opens without keyboard shortcut in Essential tabs | https://github.com/zen-browser/desktop/issues/12194 | 8:27:58 | 8:27:58 |
| Context menu extensions multiply | https://github.com/zen-browser/desktop/issues/12192 | None | None |
| Tab folder not closing properly when a tab is opened | https://github.com/zen-browser/desktop/issues/12190 | None | 10:15:16 |
| Can't set a theme | https://github.com/zen-browser/desktop/issues/12189 | 1:38:06 | None |
_This report was generated with the [Issue Metrics Action](https://github.com/github/issue-metrics)_
Search query used to find these items: `repo:zen-browser/desktop is:issue created:2026-02-01..2026-02-28`

View File

@@ -47,6 +47,7 @@ pictureinpicture-minimize-btn =
zen-panel-ui-gradient-generator-custom-color = Custom Color
zen-copy-current-url-confirmation = Copied current URL!
zen-copy-current-url-as-markdown-confirmation = Copied current URL as Markdown!
zen-general-cancel-label =
.label = Cancel

View File

@@ -96,3 +96,6 @@ zen-live-folder-github-issues =
zen-live-folder-github-option-repo-list-note =
.label = This list is generated based on your currently active pull requests.
zen-live-folders-promotion-title = Live Folder Created!
zen-live-folders-promotion-description = Latest content from your RSS feeds or GitHub pull requests will appear here automatically.

22
package-lock.json generated
View File

@@ -1464,13 +1464,13 @@
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
"version": "9.0.9",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
"integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.1"
"brace-expansion": "^2.0.2"
},
"engines": {
"node": ">=16 || 14 >=14.17"
@@ -3622,13 +3622,13 @@
}
},
"node_modules/eslint-plugin-n/node_modules/minimatch": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
"version": "9.0.9",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
"integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.1"
"brace-expansion": "^2.0.2"
},
"engines": {
"node": ">=16 || 14 >=14.17"
@@ -5818,9 +5818,9 @@
}
},
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
"dev": true,
"license": "ISC",
"dependencies": {

View File

@@ -73,5 +73,9 @@
- name: browser.tabs.notes.enabled
value: false
- name: browser.tabs.dragDrop.dragToPin.enabled
value: false
locked: true
- name: browser.tabs.dragDrop.moveOverThresholdPercent
value: 50 # Percentage of tab height to trigger move over on drag-and-drop

View File

@@ -32,9 +32,6 @@
- name: browser.search.suggest.enabled.private
value: false
- name: browser.search.separatePrivateDefault.ui.enabled
value: true
- name: browser.urlbar.quicksuggest.enabled
value: false
locked: true
@@ -59,3 +56,8 @@
- name: browser.urlbar.suggest.topsites
value: true
locked: true
# See https://github.com/zen-browser/desktop/issues/7248
# We've submitted a patch to Firefox at https://bugzilla.mozilla.org/show_bug.cgi?id=2018499
- name: browser.urlbar.closeOnWindowBlur
value: false

View File

@@ -17,6 +17,9 @@
- name: zen.view.compact.toolbar-hide-after-hover.duration
value: 1000
- name: zen.view.compact.sidebar-keep-hover.duration
value: 150
- name: zen.view.compact.animate-sidebar
value: true

View File

@@ -20,3 +20,10 @@
cpptype: uint32_t
mirror: always
type: static
# Enable native popovers on macOS, we've sent this patch to upstream
# but we enable it by default anyways in case they decide to not use it,
# or to disable it by default.
- name: widget.macos.native-popovers
value: true
condition: "defined(XP_MACOSX)"

View File

@@ -4,7 +4,6 @@
- name: image.jxl.enabled
value: true
locked: true
- name: svg.context-properties.content.enabled
value: true

View File

@@ -10,3 +10,12 @@
- name: zen.splitView.rearrange-hover-size
value: 24
- name: zen.splitView.enable-drag-over-split
value: true
- name: zen.splitView.drag-over-split-delayMC
value: 1000
- name: zen.splitView.drag-over-split-threshold
value: 40

View File

@@ -59,6 +59,15 @@ def main():
output_file = os.path.join(OUTPUT_DIR, "firefox", f"{name}.patch")
print(f"Processing Phabricator patch: {phab_id} -> {output_file}")
download_phab_patch(phab_id, output_file)
replaces = patch.get("replaces", {})
for replace in replaces.keys():
value = replaces[replace]
with open(output_file, 'r') as f:
content = f.read()
if replace not in content:
die(f"Replace string '{replace}' not found in {output_file}")
with open(output_file, 'w') as f:
f.write(content.replace(replace, value))
expected_files.add(output_file)
elif patch.get("type") == "local":
print(f"Local patch: {patch.get('path')}")

View File

@@ -1,5 +1,5 @@
diff --git a/browser/base/content/navigator-toolbox.js b/browser/base/content/navigator-toolbox.js
index 15469e9d9b91c1eaef2578c9e43b6999edac3392..553402b41bc15f7cd99bf87c54416dc66d7c03e7 100644
index 15469e9d9b91c1eaef2578c9e43b6999edac3392..95ae5036b57baeb5237603c0921f1e9252af6919 100644
--- a/browser/base/content/navigator-toolbox.js
+++ b/browser/base/content/navigator-toolbox.js
@@ -6,7 +6,7 @@
@@ -19,15 +19,25 @@ index 15469e9d9b91c1eaef2578c9e43b6999edac3392..553402b41bc15f7cd99bf87c54416dc6
#picture-in-picture-button,
#urlbar-zoom-button,
#star-button-box,
@@ -206,6 +207,7 @@ document.addEventListener(
case "vertical-tabs-newtab-button":
case "tabs-newtab-button":
case "new-tab-button":
+ case "zen-tabs-wrapper":
@@ -209,6 +210,17 @@ document.addEventListener(
gBrowser.handleNewTabMiddleClick(element, event);
break;
@@ -318,6 +320,7 @@ document.addEventListener(
+ case "zen-tabs-wrapper":
+ if (event.button == 1) {
+ BrowserCommands.openTab();
+ // Stop the propagation of the click event, to prevent the event from being
+ // handled more than once.
+ // E.g. see https://bugzilla.mozilla.org/show_bug.cgi?id=1657992#c4
+ event.stopPropagation();
+ event.preventDefault();
+ }
+ break;
+
case "back-button":
case "forward-button":
case "reload-button":
@@ -318,6 +330,7 @@ document.addEventListener(
#downloads-button,
#fxa-toolbar-menu-button,
#unified-extensions-button,
@@ -35,7 +45,7 @@ index 15469e9d9b91c1eaef2578c9e43b6999edac3392..553402b41bc15f7cd99bf87c54416dc6
#library-button,
#split-view-button
`);
@@ -401,6 +404,16 @@ document.addEventListener(
@@ -401,6 +414,16 @@ document.addEventListener(
gUnifiedExtensions.togglePanel(event);
break;

View File

@@ -41,14 +41,13 @@
<script type="text/javascript" src="chrome://browser/content/ZenPreloadedScripts.js"></script>
# Scripts used all over the browser
<script type="module" src="chrome://browser/content/zen-components/ZenFolder.mjs"></script>
<script type="module" src="chrome://browser/content/zen-components/ZenMediaController.mjs"></script>
<script type="module" src="chrome://browser/content/zen-components/ZenWorkspaceCreation.mjs"></script>
<script type="module" src="chrome://browser/content/zen-components/ZenGlanceManager.mjs"></script>
<script type="module" src="chrome://browser/content/zen-components/ZenPinnedTabManager.mjs"></script>
<script type="module" src="chrome://browser/content/zen-components/ZenViewSplitter.mjs"></script>
<script type="module" src="chrome://browser/content/zen-components/ZenFolders.mjs"></script>
<script type="module" src="chrome://browser/content/zen-components/ZenFolder.mjs"></script>
<script type="module" src="chrome://browser/content/zen-components/ZenFolders.mjs"></script>s
<script type="module" src="chrome://browser/content/zen-components/ZenDownloadAnimation.mjs"></script>
<script type="module" src="chrome://browser/content/zen-components/ZenEmojiPicker.mjs"></script>
<script type="module" src="chrome://browser/content/zen-components/ZenLiveFoldersUI.mjs"></script>

View File

@@ -2,7 +2,12 @@
# 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/.
<panel id="zen-folder-tabs-popup" type="arrow" orient="vertical" side="left">
<panel id="zen-folder-tabs-popup"
nonnativepopover="true"
type="arrow"
orient="vertical"
side="left"
consumeoutsideclicks="never">
<hbox class="tabs-list-header" flex="1">
<image class="zen-folder-tabs-list-search-icon" src="chrome://global/skin/icons/search-glass.svg"/>
<html:input id="zen-folder-tabs-list-search"

View File

@@ -2,7 +2,14 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
<panel flip="side" type="arrow" popupalign="center" orient="vertical" id="PanelUI-zen-gradient-generator" position="bottomright topright" mainview="true" side="left">
<panel flip="side" type="arrow"
opupalign="center"
orient="vertical"
id="PanelUI-zen-gradient-generator"
position="bottomright topright"
mainview="true"
hidepopovertail="true"
side="left">
<panelmultiview id="PanelUI-zen-gradient-generator-multiview" mainViewId="PanelUI-zen-gradient-generator-view">
<panelview id="PanelUI-zen-gradient-generator-view" class="PanelUI-subView zen-theme-picker" role="document" mainview-with-header="true" has-custom-header="true">
<hbox class="zen-theme-picker-gradient">

View File

@@ -8,11 +8,11 @@
<menuitem
data-l10n-id="zen-live-folder-github-pull-requests"
command="cmd_zenNewLiveFolder"
image="chrome://browser/content/zen-images/favicons/github.svg" />
image="chrome://browser/skin/zen-icons/selectable/logo-github.svg" />
<menuitem
data-l10n-id="zen-live-folder-github-issues"
command="cmd_zenNewLiveFolder"
image="chrome://browser/content/zen-images/favicons/github.svg" />
image="chrome://browser/skin/zen-icons/selectable/logo-github.svg" />
<menuitem
data-l10n-id="zen-live-folder-type-rss"
command="cmd_zenNewLiveFolder"

View File

@@ -0,0 +1,40 @@
diff --git a/browser/components/aboutwelcome/content/aboutwelcome.css b/browser/components/aboutwelcome/content/aboutwelcome.css
index 0e1985aa519d66b047c5d40977bb12099d966b18..76b59891b4c4464b38d6973858f13fff0285737d 100644
--- a/browser/components/aboutwelcome/content/aboutwelcome.css
+++ b/browser/components/aboutwelcome/content/aboutwelcome.css
@@ -329,6 +329,11 @@ panel#feature-callout {
--panel-shadow-margin: 6px;
--panel-arrow-space: calc(var(--panel-shadow-margin) + var(--arrow-visible-height) - 1.5px);
--panel-margin-offset: calc(-1 * (var(--panel-shadow-margin) + var(--arrow-corner-distance) + (var(--arrow-width) / 2)));
+
+ @media -moz-pref("widget.macos.native-popovers") and (-moz-platform: macos) {
+ --panel-shadow-margin: 0;
+ --panel-arrow-space: 0;
+ }
}
panel#feature-callout::part(content) {
@@ -512,6 +517,12 @@ div#feature-callout.hidden {
width: 25em;
gap: 16px;
background: var(--fc-background);
+
+ @media -moz-pref("widget.macos.native-popovers") and (-moz-platform: macos) {
+ --fc-background: transparent;
+ box-shadow: none;
+ border-width: 0;
+ }
}
#feature-callout .screen[pos=callout] .section-main .main-content .main-content-inner {
gap: 12px;
@@ -818,6 +829,10 @@ panel#feature-callout::part(content) {
overflow: visible;
transform: rotate(45deg);
transform-style: preserve-3d;
+
+ @media -moz-pref("widget.macos.native-popovers") and (-moz-platform: macos) {
+ display: none;
+ }
}
#feature-callout:not([arrow-position]) .arrow-box, #feature-callout[hide-arrow] .arrow-box {
display: none;

View File

@@ -0,0 +1,57 @@
diff --git a/browser/components/preferences/config/aiFeatures.mjs b/browser/components/preferences/config/aiFeatures.mjs
index f00c1e20a8ca06108fc41c97badd234167430aee..f7d553cc10492f9447c7b0f801f36b2d66b95d43 100644
--- a/browser/components/preferences/config/aiFeatures.mjs
+++ b/browser/components/preferences/config/aiFeatures.mjs
@@ -634,52 +634,6 @@ SettingGroupManager.registerGroups({
},
],
},
- {
- control: "moz-box-item",
- items: [
- {
- id: "aiControlPdfjsAltTextSelect",
- l10nId: "preferences-ai-controls-pdfjs-control",
- control: "moz-select",
- controlAttrs: {
- inputlayout: "inline-end",
- },
- supportPage: "pdf-alt-text",
- options: [...AI_CONTROL_OPTIONS],
- },
- ],
- },
- {
- control: "moz-box-item",
- items: [
- {
- id: "aiControlSmartTabGroupsSelect",
- l10nId:
- "preferences-ai-controls-tab-group-suggestions-control",
- control: "moz-select",
- controlAttrs: {
- inputlayout: "inline-end",
- },
- supportPage: "how-use-ai-enhanced-tab-groups",
- options: [...AI_CONTROL_OPTIONS],
- },
- ],
- },
- {
- control: "moz-box-item",
- items: [
- {
- id: "aiControlLinkPreviewKeyPointsSelect",
- l10nId: "preferences-ai-controls-key-points-control",
- control: "moz-select",
- controlAttrs: {
- inputlayout: "inline-end",
- },
- supportPage: "use-link-previews-firefox",
- options: [...AI_CONTROL_OPTIONS],
- },
- ],
- },
],
},
],

View File

@@ -1,5 +1,5 @@
diff --git a/browser/components/tabbrowser/content/tab.js b/browser/components/tabbrowser/content/tab.js
index 836bee14d2b63604688ebe477a5d915a5e99b305..a675aed711560b4a44604fc17478cffa7fb68439 100644
index 836bee14d2b63604688ebe477a5d915a5e99b305..53b6cb827b1199b314b0e6543055fe7b074b0dfa 100644
--- a/browser/components/tabbrowser/content/tab.js
+++ b/browser/components/tabbrowser/content/tab.js
@@ -21,6 +21,7 @@
@@ -65,11 +65,11 @@ index 836bee14d2b63604688ebe477a5d915a5e99b305..a675aed711560b4a44604fc17478cffa
+ return false;
+ }
+ // Selected tabs are always visible
+ if (this.selected || this.multiselected || this.hasAttribute("folder-active")) return true;
+ if (this.selected || this.multiselected) return true;
+ // Recursively check all parent groups
+ let currentParent = this.group;
+ while (currentParent) {
+ if (currentParent.collapsed) {
+ if (currentParent.collapsed && !currentParent.activeTabs.includes(this)) {
+ return false;
+ }
+ currentParent = currentParent.group;

View File

@@ -1,5 +1,5 @@
diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js
index d7765e0adb37216d35f2125abf96025cbb150bab..27202066cef98d9826884806f71e70812457bd78 100644
index d7765e0adb37216d35f2125abf96025cbb150bab..d787c39f68ea4507b2cb902df325498dd65b1fba 100644
--- a/browser/components/tabbrowser/content/tabbrowser.js
+++ b/browser/components/tabbrowser/content/tabbrowser.js
@@ -405,6 +405,7 @@
@@ -411,9 +411,12 @@ index d7765e0adb37216d35f2125abf96025cbb150bab..27202066cef98d9826884806f71e7081
});
}
@@ -4122,7 +4247,7 @@
@@ -4120,9 +4245,9 @@
}
// Add a new tab if needed.
if (!tab) {
- if (!tab) {
+ if (!tab || tab?._markedForReplacement) {
let createLazyBrowser =
- restoreTabsLazily && !select && !tabData.pinned;
+ restoreTabsLazily && !(tabData.pinned && !Services.prefs.getBoolPref("browser.sessionstore.restore_pinned_tabs_on_demand"));
@@ -470,10 +473,10 @@ index d7765e0adb37216d35f2125abf96025cbb150bab..27202066cef98d9826884806f71e7081
+ gZenWorkspaces._initialTab._shouldRemove = true;
+ }
+ }
+ }
}
+ else {
+ gZenWorkspaces._tabToRemoveForEmpty = this.selectedTab;
}
+ }
+ this._hasAlreadyInitializedZenSessionStore = true;
if (tabs.length > 1 || !tabs[0].selected) {
@@ -605,7 +608,7 @@ index d7765e0adb37216d35f2125abf96025cbb150bab..27202066cef98d9826884806f71e7081
aTab._closeTimeAnimTimerId = null;
- this._endRemoveTab(aTab);
+ if (animate && !gReduceMotion && !(gZenUIManager.testingEnabled && !gZenUIManager.profilingEnabled)) {
+ gZenVerticalTabsManager.animateTabClose(aTab, (animate && !gReduceMotion)).then(() => {
+ gZenVerticalTabsManager.animateItemClose(aTab, (animate && !gReduceMotion)).then(() => {
+ this._endRemoveTab(aTab);
+ });
+ } else {

View File

@@ -1,5 +1,5 @@
diff --git a/browser/components/tabbrowser/content/tabgroup.js b/browser/components/tabbrowser/content/tabgroup.js
index ebcc072abca51ba9936d0e6d97bbd329427c0231..62e8209bbc38fb209340279e5ed3e2cc2441c48c 100644
index ebcc072abca51ba9936d0e6d97bbd329427c0231..2b0980a745e2339d3220bb701092f4f621876985 100644
--- a/browser/components/tabbrowser/content/tabgroup.js
+++ b/browser/components/tabbrowser/content/tabgroup.js
@@ -14,11 +14,11 @@
@@ -100,7 +100,17 @@ index ebcc072abca51ba9936d0e6d97bbd329427c0231..62e8209bbc38fb209340279e5ed3e2cc
resetDefaultGroupName = () => {
this.#defaultGroupName = "";
@@ -211,7 +234,10 @@
@@ -166,7 +189,9 @@
this.dispatchEvent(
new CustomEvent("TabGroupRemoved", { bubbles: true })
);
+ gZenVerticalTabsManager.animateItemClose(this).then(() => {
this.remove();
+ });
Services.obs.notifyObservers(
this,
"browser-tabgroup-removed-from-dom"
@@ -211,7 +236,10 @@
}
});
}
@@ -112,7 +122,7 @@ index ebcc072abca51ba9936d0e6d97bbd329427c0231..62e8209bbc38fb209340279e5ed3e2cc
}
get color() {
@@ -305,6 +331,9 @@
@@ -305,6 +333,9 @@
}
set collapsed(val) {
@@ -122,7 +132,7 @@ index ebcc072abca51ba9936d0e6d97bbd329427c0231..62e8209bbc38fb209340279e5ed3e2cc
if (!!val == this.collapsed) {
return;
}
@@ -391,7 +420,6 @@
@@ -391,7 +422,6 @@
tabGroupName,
})
.then(result => {
@@ -130,7 +140,7 @@ index ebcc072abca51ba9936d0e6d97bbd329427c0231..62e8209bbc38fb209340279e5ed3e2cc
});
}
@@ -466,13 +494,65 @@
@@ -466,13 +496,65 @@
* @returns {MozTabbrowserTab[]}
*/
get tabs() {
@@ -145,9 +155,8 @@ index ebcc072abca51ba9936d0e6d97bbd329427c0231..62e8209bbc38fb209340279e5ed3e2cc
+ tabsCollect.push(item);
+ if (gBrowser.isTabGroup(item)) {
+ tabsCollect.push(...item.tabs);
}
}
- return childrenArray.filter(node => node.matches("tab"));
+ }
+ }
+ return tabsCollect.filter(node => node.matches("tab"));
+ }
+
@@ -173,8 +182,9 @@ index ebcc072abca51ba9936d0e6d97bbd329427c0231..62e8209bbc38fb209340279e5ed3e2cc
+ result.push(labelContainer);
+ }
+ result.push(...item.childGroupsAndTabs);
+ }
+ }
}
}
- return childrenArray.filter(node => node.matches("tab"));
+ return result;
+ }
+
@@ -201,7 +211,7 @@ index ebcc072abca51ba9936d0e6d97bbd329427c0231..62e8209bbc38fb209340279e5ed3e2cc
}
/**
@@ -569,7 +649,6 @@
@@ -569,7 +651,6 @@
);
} else {
if (tabOrSplitView.pinned) {
@@ -209,7 +219,7 @@ index ebcc072abca51ba9936d0e6d97bbd329427c0231..62e8209bbc38fb209340279e5ed3e2cc
}
let tabToMove =
this.ownerGlobal === tabOrSplitView.ownerGlobal
@@ -634,7 +713,7 @@
@@ -634,7 +715,7 @@
*/
on_click(event) {
let isToggleElement =
@@ -218,7 +228,7 @@ index ebcc072abca51ba9936d0e6d97bbd329427c0231..62e8209bbc38fb209340279e5ed3e2cc
event.target === this.#overflowCountLabel;
if (isToggleElement && event.button === 0) {
event.preventDefault();
@@ -705,5 +784,6 @@
@@ -705,5 +786,6 @@
}
}

View File

@@ -501,6 +501,9 @@ groupbox h2 {
#support-firefox,
#tabGroupSuggestions,
#web-appearance-manage-themes-link,
#setting-control-sidebarChatbotFieldset,
#aiControlsDescription,
#category-ai-features,
.mission-message {
display: none !important;
}

View File

@@ -2,4 +2,4 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
<svg fill="context-fill" fill-opacity="context-fill-opacity" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg"><path d="M256 32C132.3 32 32 134.9 32 261.7c0 101.5 64.2 187.5 153.2 217.9a17.56 17.56 0 003.8.4c8.3 0 11.5-6.1 11.5-11.4 0-5.5-.2-19.9-.3-39.1a102.4 102.4 0 01-22.6 2.7c-43.1 0-52.9-33.5-52.9-33.5-10.2-26.5-24.9-33.6-24.9-33.6-19.5-13.7-.1-14.1 1.4-14.1h.1c22.5 2 34.3 23.8 34.3 23.8 11.2 19.6 26.2 25.1 39.6 25.1a63 63 0 0025.6-6c2-14.8 7.8-24.9 14.2-30.7-49.7-5.8-102-25.5-102-113.5 0-25.1 8.7-45.6 23-61.6-2.3-5.8-10-29.2 2.2-60.8a18.64 18.64 0 015-.5c8.1 0 26.4 3.1 56.6 24.1a208.21 208.21 0 01112.2 0c30.2-21 48.5-24.1 56.6-24.1a18.64 18.64 0 015 .5c12.2 31.6 4.5 55 2.2 60.8 14.3 16.1 23 36.6 23 61.6 0 88.2-52.4 107.6-102.3 113.3 8 7.1 15.2 21.1 15.2 42.5 0 30.7-.3 55.5-.3 63 0 5.4 3.1 11.5 11.4 11.5a19.35 19.35 0 004-.4C415.9 449.2 480 363.1 480 261.7 480 134.9 379.7 32 256 32z"/></svg>
<svg fill="context-fill" fill-opacity="context-fill-opacity" viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg" xmlns:svg="http://www.w3.org/2000/svg"><path d="M219.797 355.243c-60.522-7.339-103.146-50.88-103.146-107.264 0-22.912 8.256-47.68 21.994-64.171-5.952-15.125-5.034-47.232 1.835-60.523 18.347-2.282 43.093 7.339 57.771 20.63 17.408-5.504 35.754-8.235 58.197-8.235s40.811 2.731 57.301 7.787c14.208-12.843 39.424-22.464 57.771-20.182 6.4 12.395 7.339 44.48 1.365 60.054 14.678 17.429 22.464 40.81 22.464 64.64 0 56.384-42.624 99.008-104.042 106.794 15.573 10.091 26.112 32.086 26.112 57.302v47.68c0 13.738 11.456 21.546 25.216 16.042 82.965-31.637 148.053-114.602 148.053-217.28 0-129.728-105.429-235.605-235.136-235.605-129.728 0-234.24 105.877-234.24 235.605a231.255 231.255 0 0 0 151.723 217.728c12.373 4.587 24.298-3.669 24.298-16.042v-36.672a59.7 59.7 0 0 1-22.016 4.586c-30.25 0-48.128-16.49-60.949-47.21-5.056-12.374-10.56-19.712-21.099-21.078-5.504-.469-7.338-2.752-7.338-5.504 0-5.504 9.173-9.621 18.346-9.621 13.291 0 24.747 8.235 36.672 25.195 9.174 13.29 18.795 19.264 30.251 19.264s18.795-4.139 29.333-14.678c7.787-7.786 13.76-14.656 19.264-19.242"/></svg>

View File

@@ -1,12 +0,0 @@
diff --git a/devtools/server/actors/animation-type-longhand.js b/devtools/server/actors/animation-type-longhand.js
index f6cd7b4599ab74fbdc301f316fd7ecc2e2eb841c..8279a943803ca75fe0a231606ae26a678fcc0b60 100644
--- a/devtools/server/actors/animation-type-longhand.js
+++ b/devtools/server/actors/animation-type-longhand.js
@@ -342,6 +342,7 @@ exports.ANIMATION_TYPE_FOR_LONGHANDS = [
"transform-origin",
"translate",
"-moz-window-transform",
+ "-zen-window-transform-origin",
"-webkit-line-clamp",
]),
],

View File

@@ -1,50 +0,0 @@
diff --git a/dom/base/use_counter_metrics.yaml b/dom/base/use_counter_metrics.yaml
index 84aa1ac40d01bc73f5039bd5c589c3fdf3a6c8ce..090f9e1eef46ed45c0a98a73013ad59faeb3414f 100644
--- a/dom/base/use_counter_metrics.yaml
+++ b/dom/base/use_counter_metrics.yaml
@@ -22106,6 +22106,22 @@ use.counter.css.page:
send_in_pings:
- use-counters
+ css_zen_window_transform_origin:
+ type: counter
+ description: >
+ Whether a page used the CSS property -zen-window-transform-origin.
+ Compare against `use.counter.top_level_content_documents_destroyed`
+ to calculate the rate.
+ bugs:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
+ data_reviews:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
+ notification_emails:
+ - dom-core@mozilla.com
+ expires: never
+ send_in_pings:
+ - use-counters
+
css_transform_origin:
type: counter
description: >
@@ -34076,6 +34092,22 @@ use.counter.css.doc:
send_in_pings:
- use-counters
+ css_zen_window_transform_origin:
+ type: counter
+ description: >
+ Whether a document used the CSS property -zen-window-transform-origin.
+ Compare against `use.counter.content_documents_destroyed`
+ to calculate the rate.
+ bugs:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
+ data_reviews:
+ - https://bugzilla.mozilla.org/show_bug.cgi?id=1852098
+ notification_emails:
+ - dom-core@mozilla.com
+ expires: never
+ send_in_pings:
+ - use-counters
+
css_transform_origin:
type: counter
description: >

View File

@@ -0,0 +1,75 @@
diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -411,10 +411,13 @@
pref("browser.urlbar.maxRichResults", 10);
// The maximum number of historical search results to show.
pref("browser.urlbar.maxHistoricalSearchSuggestions", 2);
+// Whether to close the urlbar when blurring the window while the urlbar is focused.
+pref("browser.urlbar.closeOnWindowBlur", true);
+
// The default behavior for the urlbar can be configured to use any combination
// of the match filters with each additional filter adding more results (union).
pref("browser.urlbar.suggest.bookmark", true);
pref("browser.urlbar.suggest.clipboard", true);
pref("browser.urlbar.suggest.history", true);
diff --git a/browser/components/urlbar/UrlbarPrefs.sys.mjs b/browser/components/urlbar/UrlbarPrefs.sys.mjs
--- a/browser/components/urlbar/UrlbarPrefs.sys.mjs
+++ b/browser/components/urlbar/UrlbarPrefs.sys.mjs
@@ -152,10 +152,13 @@
["filter.javascript", true],
// Feature gate pref for flight status suggestions in the urlbar.
["flightStatus.featureGate", false],
+ // Whether to close the urlbar when blurring the window while the urlbar is focused.
+ ["closeOnWindowBlur", true],
+
// The minimum prefix length of a flight status keyword the user must type to
// trigger the suggestion. 0 means the min length should be taken from Nimbus
// or remote settings.
["flightStatus.minKeywordLength", 0],
diff --git a/browser/components/urlbar/UrlbarView.sys.mjs b/browser/components/urlbar/UrlbarView.sys.mjs
--- a/browser/components/urlbar/UrlbarView.sys.mjs
+++ b/browser/components/urlbar/UrlbarView.sys.mjs
@@ -3909,10 +3909,16 @@
}
}
}
on_blur() {
+ if (
+ this.document.commandDispatcher.focusedElement == this.input.inputField &&
+ !lazy.UrlbarPrefs.get("closeOnWindowBlur")
+ ) {
+ return;
+ }
// If the view is open without the input being focused, it will not close
// automatically when the window loses focus. We might be in this state
// after a Search Tip is shown on an engine homepage.
if (!lazy.UrlbarPrefs.get("ui.popup.disable_autohide")) {
this.close();
diff --git a/browser/components/urlbar/content/UrlbarInput.mjs b/browser/components/urlbar/content/UrlbarInput.mjs
--- a/browser/components/urlbar/content/UrlbarInput.mjs
+++ b/browser/components/urlbar/content/UrlbarInput.mjs
@@ -4783,10 +4783,16 @@
}
}
_on_blur(event) {
lazy.logger.debug("Blur Event");
+ if (
+ this.document.commandDispatcher.focusedElement == this.inputField &&
+ !lazy.UrlbarPrefs.get("closeOnWindowBlur")
+ ) {
+ return;
+ }
// We cannot count every blur events after a missed engagement as abandoment
// because the user may have clicked on some view element that executes
// a command causing a focus change. For example opening preferences from
// the oneoff settings button.
// For now we detect that case by discarding the event on command, but we

View File

@@ -0,0 +1,677 @@
diff --git a/browser/base/content/main-popupset.inc.xhtml b/browser/base/content/main-popupset.inc.xhtml
--- a/browser/base/content/main-popupset.inc.xhtml
+++ b/browser/base/content/main-popupset.inc.xhtml
@@ -193,10 +193,11 @@
<!-- Starting point for selection actions -->
<panel class="panel-no-padding"
id="selection-shortcut-action-panel"
noautofocus="true"
consumeoutsideclicks="never"
+ nonnativepopover="true"
type="arrow">
<hbox class="panel-subview-body">
<html:moz-button id="ai-action-button"/>
</hbox>
</panel>
@@ -204,10 +205,11 @@
<!-- Shortcut options for Gen AI action -->
<panel class="panel-no-padding"
id="chat-shortcuts-options-panel"
noautofocus="true"
+ nonnativepopover="true"
type="arrow">
<vbox class="panel-subview-body"/>
</panel>
<html:template id="screenshotsPagePanelTemplate">
@@ -556,10 +558,11 @@
type="arrow"
orient="vertical"
noautofocus="true"
norolluponanchor="true"
consumeoutsideclicks="false"
+ nonnativepopover="true"
role="tooltip">
<html:div class="tab-preview-text-container">
<html:div class="tab-preview-title"></html:div>
<html:div class="tab-preview-uri"></html:div>
<html:div class="tab-preview-pid-activeness">
diff --git a/browser/components/asrouter/modules/FeatureCallout.sys.mjs b/browser/components/asrouter/modules/FeatureCallout.sys.mjs
--- a/browser/components/asrouter/modules/FeatureCallout.sys.mjs
+++ b/browser/components/asrouter/modules/FeatureCallout.sys.mjs
@@ -1046,10 +1046,11 @@
noautofocus="true"
flip="slide"
type="arrow"
consumeoutsideclicks="never"
norolluponanchor="true"
+
position="${panel_position.panel_position_string}"
${hide_arrow ? "" : 'show-arrow=""'}
${autohide ? "" : 'noautohide="true"'}
${ignorekeys ? 'ignorekeys="true"' : ""}
${no_open_on_anchor ? 'no-open-on-anchor=""' : ""}
diff --git a/browser/components/customizableui/content/panelUI.inc.xhtml b/browser/components/customizableui/content/panelUI.inc.xhtml
--- a/browser/components/customizableui/content/panelUI.inc.xhtml
+++ b/browser/components/customizableui/content/panelUI.inc.xhtml
@@ -276,10 +276,11 @@
role="group"
type="arrow"
hidden="true"
flip="slide"
position="bottomright topright"
+ hidepopovertail="true"
noautofocus="true">
<panelmultiview id="appMenu-multiView" mainViewId="appMenu-mainView"
viewCacheId="appMenu-viewCache">
</panelmultiview>
</panel>
diff --git a/dom/xul/XULPopupElement.cpp b/dom/xul/XULPopupElement.cpp
--- a/dom/xul/XULPopupElement.cpp
+++ b/dom/xul/XULPopupElement.cpp
@@ -84,10 +84,15 @@
void XULPopupElement::OpenPopupAtScreen(int32_t aXPos, int32_t aYPos,
bool aIsContextMenu,
Event* aTriggerEvent) {
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
+ // TODO(cheff): We do the same at nsCocoaWindow::Show but it doesn't seem
+ // to trigger a restyle so `appearance: auto` doesn't apply the native
+ // popover style. We should remove this and use the other implementation
+ // because this is a bit of a hack, not sure how reliable it is.
+ SetXULBoolAttr(nsGkAtoms::nonnativepopover, true, IgnoreErrors());
if (pm) {
pm->ShowPopupAtScreen(this, aXPos, aYPos, aIsContextMenu, aTriggerEvent);
}
}
@@ -96,10 +101,14 @@
int32_t aWidth, int32_t aHeight,
bool aIsContextMenu,
bool aAttributesOverride,
Event* aTriggerEvent) {
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
+ // TODO: See OpenPopupAtScreen. We should remove this and use the other
+ // implementation because this is a bit of a hacky way to determine whether to
+ // use a native popover or not.
+ SetXULBoolAttr(nsGkAtoms::nonnativepopover, true, IgnoreErrors());
if (pm) {
pm->ShowPopupAtScreenRect(
this, aPosition, nsIntRect(aXPos, aYPos, aWidth, aHeight),
aIsContextMenu, aAttributesOverride, aTriggerEvent);
}
diff --git a/layout/xul/nsMenuPopupFrame.h b/layout/xul/nsMenuPopupFrame.h
--- a/layout/xul/nsMenuPopupFrame.h
+++ b/layout/xul/nsMenuPopupFrame.h
@@ -516,18 +516,10 @@
// Move the popup to the position specified in its |left| and |top|
// attributes.
void MoveToAttributePosition();
- // Returns true if the popup should try to remain at the same relative
- // location as the anchor while it is open. If the anchor becomes hidden
- // either directly or indirectly because a parent popup or other element
- // is no longer visible, or a parent deck page is changed, the popup hides
- // as well. The second variation also sets the anchor rectangle, relative to
- // the popup frame.
- bool ShouldFollowAnchor() const;
-
nsIFrame* GetAnchorFrame() const;
public:
/**
* Return whether the popup direction should be RTL.
@@ -536,10 +528,18 @@
*
* Return whether the popup direction should be RTL.
*/
bool IsDirectionRTL() const;
+ // Returns true if the popup should try to remain at the same relative
+ // location as the anchor while it is open. If the anchor becomes hidden
+ // either directly or indirectly because a parent popup or other element
+ // is no longer visible, or a parent deck page is changed, the popup hides
+ // as well. The second variation also sets the anchor rectangle, relative to
+ // the popup frame.
+ bool ShouldFollowAnchor() const;
+
bool ShouldFollowAnchor(nsRect& aRect);
// Returns parent menu widget for submenus that are in the same
// frame hierarchy, it's needed for Linux/Wayland which demands
// strict popup windows hierarchy.
diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml
--- a/modules/libpref/init/StaticPrefList.yaml
+++ b/modules/libpref/init/StaticPrefList.yaml
@@ -19311,10 +19311,19 @@
value: true
mirror: always
#ifdef XP_MACOSX
+# If true, use native NSPopover for arrow panel popups (type="arrow") on macOS
+# Someone like Zen browser will use this pref by default, so
+# please take this into account when you change the behavior of
+# native popover. Note: Only panels with type="arrow" will use NSPopover.
+- name: widget.macos.native-popovers
+ type: RelaxedAtomicBool
+ value: false
+ mirror: always
+
# Whether to shift by the menubar height on fullscreen mode.
# 0: never
# 1: always
# 2: auto (tries to detect when it is needed)
- name: widget.macos.shift-by-menubar-on-fullscreen
diff --git a/toolkit/themes/shared/global-shared.css b/toolkit/themes/shared/global-shared.css
--- a/toolkit/themes/shared/global-shared.css
+++ b/toolkit/themes/shared/global-shared.css
@@ -102,10 +102,22 @@
--menuitem-border-radius: var(--arrowpanel-menuitem-border-radius);
--menuitem-padding: var(--arrowpanel-menuitem-padding);
--menuitem-margin: var(--arrowpanel-menuitem-margin);
}
+/* stylelint-disable-next-line media-query-no-invalid */
+@media -moz-pref("widget.macos.native-popovers") and (-moz-platform: macos) {
+ panel:not(:where([nonnativepopover="true"])) {
+ background-color: transparent;
+ --panel-background: transparent;
+ --panel-shadow: none;
+ --panel-border-color: transparent;
+ --panel-shadow-margin: 0px;
+ --panel-padding: 0px;
+ }
+}
+
/* Lightweight theme roots */
:root[lwtheme] {
.browser-toolbox-background,
toolbar,
diff --git a/widget/cocoa/nsCocoaWindow.h b/widget/cocoa/nsCocoaWindow.h
--- a/widget/cocoa/nsCocoaWindow.h
+++ b/widget/cocoa/nsCocoaWindow.h
@@ -133,23 +133,38 @@
// to create its "frame view".
+ (Class)frameViewClassForStyleMask:(NSUInteger)styleMask;
@end
-@interface PopupWindow : BaseWindow {
+@interface PopupWindow : BaseWindow <NSPopoverDelegate> {
@private
BOOL mIsContextMenu;
+
+ // NSPopover support for native appearance
+ NSPopover* mPopover;
+ NSViewController* mPopoverViewController;
+ BOOL mUsePopover;
}
- (id)initWithContentRect:(NSRect)contentRect
styleMask:(NSUInteger)styleMask
backing:(NSBackingStoreType)bufferingType
defer:(BOOL)deferCreation;
- (BOOL)isContextMenu;
- (void)setIsContextMenu:(BOOL)flag;
- (BOOL)canBecomeMainWindow;
+// NSPopover support
+- (void)setAllowPopover;
+- (BOOL)usePopover;
+- (void)showPopoverRelativeToRect:(NSRect)positioningRect
+ ofView:(NSView*)positioningView
+ preferredEdge:(NSRectEdge)preferredEdge
+ hiddenAnchor:(BOOL)hiddenAnchor;
+- (void)closePopover;
+- (void)updatePopoverContent;
+
@end
@interface BorderlessWindow : BaseWindow {
}
@@ -201,10 +216,14 @@
typedef nsIWidget Inherited;
public:
nsCocoaWindow();
+ // Check if this window should use NSPopover for popup/menu display
+ bool ShouldUseNSPopover() const;
+ bool ShouldShowAsNSPopover() const override;
+
[[nodiscard]] nsresult Create(nsIWidget* aParent, const DesktopIntRect& aRect,
const InitData&) override;
[[nodiscard]] nsresult Create(nsIWidget* aParent,
const LayoutDeviceIntRect& aRect,
diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm
--- a/widget/cocoa/nsCocoaWindow.mm
+++ b/widget/cocoa/nsCocoaWindow.mm
@@ -5,10 +5,13 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsCocoaWindow.h"
#include "nsArrayUtils.h"
+#include "nsMenuPopupFrame.h"
+#include "nsDeviceContext.h"
+#include "mozilla/dom/XULPopupElement.h"
#include "MOZDynamicCursor.h"
#include "nsIAppStartup.h"
#include "nsIDOMWindowUtils.h"
#include "nsILocalFileMac.h"
#include "CocoaCompositorWidget.h"
@@ -4958,10 +4961,15 @@
if (mWindowType == WindowType::Popup) {
SetPopupWindowLevel();
mWindow.backgroundColor = NSColor.clearColor;
mWindow.opaque = NO;
+ // Enable NSPopover for panel popup types when preference is enabled
+ if ([mWindow isKindOfClass:[PopupWindow class]] && ShouldUseNSPopover()) {
+ [(PopupWindow*)mWindow setAllowPopover];
+ }
+
// When multiple spaces are in use and the browser is assigned to a
// particular space, override the "Assign To" space and display popups on
// the active space. Does not work with multiple displays. See
// NeedsRecreateToReshow() for multi-display with multi-space workaround.
mWindow.collectionBehavior = mWindow.collectionBehavior |
@@ -5163,10 +5171,57 @@
NS_OBJC_END_TRY_IGNORE_BLOCK;
}
bool nsCocoaWindow::IsRunningAppModal() { return [NSApp _isRunningAppModal]; }
+static NSRectEdge AlignmentPositionToNSRectEdge(int8_t aPosition) {
+ switch (aPosition) {
+ case POPUPPOSITION_BEFORESTART:
+ case POPUPPOSITION_BEFOREEND:
+ return NSRectEdgeMaxY;
+ case POPUPPOSITION_AFTERSTART:
+ case POPUPPOSITION_AFTEREND:
+ return NSRectEdgeMinY;
+ case POPUPPOSITION_STARTBEFORE:
+ case POPUPPOSITION_STARTAFTER:
+ return NSRectEdgeMaxX;
+ case POPUPPOSITION_ENDBEFORE:
+ case POPUPPOSITION_ENDAFTER:
+ return NSRectEdgeMinX;
+ default:
+ return NSRectEdgeMinY;
+ }
+}
+
+static void SyncPopoverBounds(NSPopover* aPopover,
+ nsMenuPopupFrame* aPopupFrame) {
+ if (!aPopover || !aPopover.shown || !aPopupFrame) {
+ return;
+ }
+ NSWindow* popoverWindow = aPopover.contentViewController.view.window;
+ if (!popoverWindow) {
+ return;
+ }
+
+ // Synchronize the popup frame's internal bounds with the actual bounds that
+ // macOS calculated for the popover.
+ NSView* contentView = popoverWindow.contentView;
+ NSRect contentFrame = [contentView convertRect:contentView.bounds toView:nil];
+ NSRect windowFrame = [popoverWindow convertRectToScreen:contentFrame];
+
+ CGFloat backingScale = popoverWindow.backingScaleFactor;
+ mozilla::LayoutDeviceIntRect devPixRect =
+ nsCocoaUtils::CocoaRectToGeckoRectDevPix(windowFrame, backingScale);
+
+ nsPresContext* presContext = aPopupFrame->PresContext();
+ mozilla::CSSIntPoint cssPos =
+ presContext->DevPixelsToIntCSSPixels(devPixRect.TopLeft());
+
+ aPopupFrame->MoveTo(mozilla::CSSPoint(cssPos.x, cssPos.y),
+ /* aUpdateAttrs */ false);
+}
+
// Hide or show this window
void nsCocoaWindow::Show(bool aState) {
NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
if (!mWindow) {
@@ -5185,10 +5240,17 @@
}
NSWindow* nativeParentWindow =
mParent ? (NSWindow*)mParent->GetNativeData(NS_NATIVE_WINDOW) : nil;
+ bool shouldUseNativePopover = false;
+ if (mWindowType == WindowType::Popup && aState) {
+ nsMenuPopupFrame* popupFrame = GetPopupFrame();
+ popupFrame->PopupElement().SetXULBoolAttr(
+ nsGkAtoms::nonnativepopover, !ShouldShowAsNSPopover(), IgnoreErrors());
+ }
+
if (aState && !mBounds.IsEmpty()) {
// If we had set the activationPolicy to accessory, then right now we won't
// have a dock icon. Make sure that we undo that and show a dock icon now
// that we're going to show a window.
if (NSApp.activationPolicy != NSApplicationActivationPolicyRegular) {
@@ -5227,10 +5289,54 @@
mWindow.contentView.needsDisplay = YES;
if (!nativeParentWindow || mPopupLevel != PopupLevel::Parent) {
[mWindow orderFront:nil];
}
NS_OBJC_END_TRY_IGNORE_BLOCK;
+ if (ShouldShowAsNSPopover()) {
+ nsMenuPopupFrame* popupFrame = GetPopupFrame();
+ if (nativeParentWindow) {
+ NSRectEdge preferredEdge =
+ AlignmentPositionToNSRectEdge(popupFrame->GetAlignmentPosition());
+ nsRect anchorRectAppUnits = popupFrame->GetUntransformedAnchorRect();
+ nsPresContext* pc = popupFrame->PresContext();
+ int32_t appUnitsPerDevPixel = pc->AppUnitsPerDevPixel();
+ mozilla::DesktopToLayoutDeviceScale desktopToLayoutScale =
+ pc->DeviceContext()->GetDesktopToDeviceScale();
+ mozilla::DesktopIntRect popupAnchorRectScaled =
+ mozilla::DesktopIntRect::RoundOut(
+ mozilla::LayoutDeviceRect::FromAppUnits(anchorRectAppUnits,
+ appUnitsPerDevPixel) /
+ desktopToLayoutScale);
+ // Taking the now correctly scaled anchor rect and turning it into a
+ // gecko rect this accounts for the y-axis inversion that cocoa needs,
+ // as the origin is in the bottom left. This rect is in screen space
+ NSRect cocoaScreenRect =
+ nsCocoaUtils::GeckoRectToCocoaRect(popupAnchorRectScaled);
+ // We take the screen space rect and convert it to window space
+ // coordinates, as NSPopover requires the coordinates to be in view
+ // space and inside the view. If the coordinates are outside our view,
+ // the popover will fail silently
+ NSRect windowRect =
+ [nativeParentWindow convertRectFromScreen:cocoaScreenRect];
+ NSView* parentView = [nativeParentWindow contentView];
+ // We take the window space rect and convert it to view space for the
+ // specific parent view
+ NSRect positioningRect = [parentView convertRect:windowRect
+ fromView:nil];
+ BOOL shouldHideAnchor = NO;
+ auto& element = popupFrame->PopupElement();
+ if (element.GetBoolAttr(nsGkAtoms::hidepopovertail)) {
+ shouldHideAnchor = YES;
+ }
+ [(PopupWindow*)mWindow showPopoverRelativeToRect:positioningRect
+ ofView:parentView
+ preferredEdge:preferredEdge
+ hiddenAnchor:shouldHideAnchor];
+ SyncPopoverBounds([(PopupWindow*)mWindow popover], popupFrame);
+ }
+ return;
+ }
// If our popup window is a non-native context menu, tell the OS (and
// other programs) that a menu has opened. This is how the OS knows to
// close other programs' context menus when ours open.
if ([mWindow isKindOfClass:[PopupWindow class]] &&
[(PopupWindow*)mWindow isContextMenu]) {
@@ -5301,10 +5407,15 @@
// of a window it hides the parent window.
if (mWindowType == WindowType::Popup && nativeParentWindow) {
[nativeParentWindow removeChildWindow:mWindow];
}
+ // Handle NSPopover hiding or traditional window hiding
+ if ([mWindow isKindOfClass:[PopupWindow class]] &&
+ [(PopupWindow*)mWindow usePopover]) {
+ [(PopupWindow*)mWindow closePopover];
+ }
[mWindow orderOut:nil];
// If our popup window is a non-native context menu, tell the OS (and
// other programs) that a menu has closed.
if ([mWindow isKindOfClass:[PopupWindow class]] &&
[(PopupWindow*)mWindow isContextMenu]) {
@@ -5351,10 +5462,28 @@
return false;
}
return nsIWidget::ShouldUseOffMainThreadCompositing();
}
+bool nsCocoaWindow::ShouldUseNSPopover() const {
+ // Use NSPopover for panel popups when the preference is enabled
+ // But not for detached popups - they should use traditional window logic
+ return (mWindowType == WindowType::Popup && mPopupType == PopupType::Panel &&
+ mozilla::StaticPrefs::widget_macos_native_popovers());
+}
+
+bool nsCocoaWindow::ShouldShowAsNSPopover() const {
+ if (!ShouldUseNSPopover()) {
+ return false;
+ }
+ nsMenuPopupFrame* popupFrame = GetPopupFrame();
+ return [mWindow isKindOfClass:[PopupWindow class]] &&
+ [(PopupWindow*)mWindow usePopover] && popupFrame &&
+ popupFrame->ShouldFollowAnchor() &&
+ !popupFrame->PopupElement().GetBoolAttr(nsGkAtoms::nonnativepopover);
+}
+
TransparencyMode nsCocoaWindow::GetTransparencyMode() {
NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
return mWindow.isOpaque ? TransparencyMode::Opaque
: TransparencyMode::Transparent;
@@ -6313,10 +6442,20 @@
// We ignore aRepaint -- we have to call display:YES, otherwise the
// title bar doesn't immediately get repainted and is displayed in
// the wrong place, leading to a visual jump.
[mWindow setFrame:newFrame display:YES];
+ if (ShouldUseNSPopover() && [(PopupWindow*)mWindow usePopover]) {
+ [(PopupWindow*)mWindow updatePopoverContent];
+ // A popover won't resize by setting the frame
+ // as it's size is calculated based on the content size
+ // Therefor the content size has to be changed as well
+ NSSize contentSize = NSMakeSize(aWidth, aHeight);
+ [[(PopupWindow*)mWindow popover] setContentSize:contentSize];
+ SyncPopoverBounds([(PopupWindow*)mWindow popover], GetPopupFrame());
+ }
+
NS_OBJC_END_TRY_IGNORE_BLOCK;
}
void nsCocoaWindow::Resize(const DesktopRect& aRect, bool aRepaint) {
DoResize(aRect.x, aRect.y, aRect.width, aRect.height, aRepaint, false);
@@ -8277,18 +8416,27 @@
backing:(NSBackingStoreType)bufferingType
defer:(BOOL)deferCreation {
NS_OBJC_BEGIN_TRY_BLOCK_RETURN;
mIsContextMenu = false;
+ mPopover = nil;
+ mPopoverViewController = nil;
+ mUsePopover = NO;
return [super initWithContentRect:contentRect
styleMask:styleMask
backing:bufferingType
defer:deferCreation];
NS_OBJC_END_TRY_BLOCK_RETURN(nil);
}
+- (void)dealloc {
+ [mPopover release];
+ [mPopoverViewController release];
+ [super dealloc];
+}
+
// Override the private API _backdropBleedAmount. This determines how much the
// desktop wallpaper contributes to the vibrancy backdrop.
// Return 0 in order to match what the system does for sheet windows and
// _NSPopoverWindows.
- (CGFloat)_backdropBleedAmount {
@@ -8342,10 +8490,120 @@
- (void)setIsContextMenu:(BOOL)flag {
mIsContextMenu = flag;
}
+- (void)setAllowPopover {
+ mUsePopover = YES;
+
+ if (!mPopover) {
+ mPopover = [[NSPopover alloc] init];
+
+ // Use NSPopoverBehaviorApplicationDefined to prevent auto-closing
+ // when other popovers are opened, and to respect the disable_autohide
+ // preference
+ mPopover.behavior = NSPopoverBehaviorApplicationDefined;
+ mPopover.delegate = self;
+
+ // Create view controller that will contain our content view
+ mPopoverViewController = [[NSViewController alloc] init];
+
+ NSView* contentView = self.contentView;
+ if (contentView) {
+ // Ensure the content view is properly configured
+ [contentView
+ setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
+
+ mPopoverViewController.view = contentView;
+ mPopover.contentViewController = mPopoverViewController;
+
+ // Set popover size to match our window content size
+ NSRect contentRect = [contentView frame];
+ if (contentRect.size.width > 0 && contentRect.size.height > 0) {
+ [mPopover setContentSize:contentRect.size];
+ }
+ }
+ }
+}
+
+- (BOOL)usePopover {
+ return mUsePopover && !mIsContextMenu;
+}
+
+- (void)showPopoverRelativeToRect:(NSRect)positioningRect
+ ofView:(NSView*)positioningView
+ preferredEdge:(NSRectEdge)preferredEdge
+ hiddenAnchor:(BOOL)hiddenAnchor {
+ NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
+ if (!mPopover) {
+ return;
+ }
+
+ // Close existing popover if it's already shown
+ if (mPopover.shown) {
+ [mPopover close];
+ }
+
+ // Force content update before showing
+ [self updatePopoverContent];
+
+ if (mPopoverViewController.view) {
+ mPopover.behavior = NSPopoverBehaviorApplicationDefined;
+
+ // This is a hidden API that prevents the popover from showing its arrow
+ // pointing to the anchor.
+ [mPopover setShouldHideAnchor:hiddenAnchor];
+
+ [mPopover showRelativeToRect:positioningRect
+ ofView:positioningView
+ preferredEdge:preferredEdge];
+ }
+
+ NSWindow* popoverWindow = mPopover.contentViewController.view.window;
+ [popoverWindow setAcceptsMouseMovedEvents:YES];
+
+ NS_OBJC_END_TRY_IGNORE_BLOCK;
+}
+
+- (void)closePopover {
+ NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
+
+ if (mPopover && mPopover.shown) {
+ [mPopover close];
+ }
+
+ NS_OBJC_END_TRY_IGNORE_BLOCK;
+}
+
+- (void)updatePopoverContent {
+ NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
+
+ if (mPopover && mPopoverViewController) {
+ NSView* contentView = self.contentView;
+ if (contentView) {
+ // Ensure proper hit testing for hover events
+ [contentView setWantsLayer:YES];
+ [contentView setAcceptsTouchEvents:YES];
+
+ // Update the popover content view to match current window content
+ mPopoverViewController.view = contentView;
+
+ // Update popover size to match content
+ NSRect contentRect = [contentView frame];
+ if (contentRect.size.width > 0 && contentRect.size.height > 0) {
+ mPopover.contentSize = contentRect.size;
+ }
+ }
+ }
+
+ NS_OBJC_END_TRY_IGNORE_BLOCK;
+}
+
+- (NSPopover*)popover {
+ return mPopover;
+}
+
- (BOOL)canBecomeMainWindow {
// This is overriden because the default is 'yes' when a titlebar is present.
return NO;
}
diff --git a/widget/nsIWidget.h b/widget/nsIWidget.h
--- a/widget/nsIWidget.h
+++ b/widget/nsIWidget.h
@@ -836,10 +836,15 @@
virtual void SuppressAnimation(bool aSuppress) {}
/** Sets windows-specific mica backdrop on this widget. */
virtual void SetMicaBackdrop(bool) {}
+ /**
+ * Determine whether this widget should be shown as an NSPopover.
+ */
+ virtual bool ShouldShowAsNSPopover() const { return false; }
+
/**
* Return size mode (minimized, maximized, normalized).
* Returns a value from nsSizeMode (see nsIWidgetListener.h)
*/
virtual nsSizeMode SizeMode() = 0;
diff --git a/xpcom/ds/StaticAtoms.py b/xpcom/ds/StaticAtoms.py
--- a/xpcom/ds/StaticAtoms.py
+++ b/xpcom/ds/StaticAtoms.py
@@ -535,10 +535,11 @@
Atom("hgroup", "hgroup"),
Atom("hidden", "hidden"),
Atom("hidechrome", "hidechrome"),
Atom("hidecolumnpicker", "hidecolumnpicker"),
Atom("hidetitlebarseparator", "hidetitlebarseparator"),
+ Atom("hidepopovertail", "hidepopovertail"),
Atom("hide_popover", "hide-popover"),
Atom("high", "high"),
Atom("highest", "highest"),
Atom("horizontal", "horizontal"),
Atom("hover", "hover"),
@@ -756,10 +757,11 @@
Atom("nohref", "nohref"),
Atom("noinitialselection", "noinitialselection"),
Atom("nomodule", "nomodule"),
Atom("nonce", "nonce"),
Atom("none", "none"),
+ Atom("nonnativepopover", "nonnativepopover"),
Atom("noresize", "noresize"),
Atom("normal", "normal"),
Atom("normalizeSpace", "normalize-space"),
Atom("noscript", "noscript"),
Atom("noshade", "noshade"),

View File

@@ -7,6 +7,26 @@
"id": "D279007",
"name": "Fix MacOS Crash on Shutdown Firefox 149"
},
{
"type": "phabricator",
"id": "D284084",
"name": "Native MacOS popovers",
"replaces": {
// The patch we sent was for firefox 149, but we are currently in 148, so theres
// a conflict in the patch. See https://bugzilla.mozilla.org/show_bug.cgi?id=2015354
"overridden": "overriden",
// Specifically trying to target FeatureCallout.sys.mjs's change.
// IMPORTANT: Make sure Feature callouts STILL use native popopvers when
// syncing from upstream, as this is a critical part of the patch.
"+ nonnativepopover=\"true\"": "+ ",
"body,": ".browser-toolbox-background,"
}
},
{
"type": "phabricator",
"id": "D284404",
"name": "Add urlbar closeOnWindowBlur preference"
},
{
"type": "local",
"path": "firefox/no_liquid_glass_icon.patch"

View File

@@ -1,16 +0,0 @@
diff --git a/layout/generic/nsIFrame.cpp b/layout/generic/nsIFrame.cpp
index 67ae256038fae1d61824d58f5e544f65505a1ed5..b6e70376f1d9230df9beb01216092969e9322c8f 100644
--- a/layout/generic/nsIFrame.cpp
+++ b/layout/generic/nsIFrame.cpp
@@ -11973,6 +11973,11 @@ gfx::Matrix nsIFrame::ComputeWidgetTransform() const {
gfx::Matrix4x4 matrix = nsStyleTransformMatrix::ReadTransforms(
uiReset->mMozWindowTransform, refBox, float(appUnitsPerDevPixel));
+ const StyleTransformOrigin& origin = uiReset->mWindowTransformOrigin;
+ Point transformOrigin = nsStyleTransformMatrix::Convert2DPosition(
+ origin.horizontal, origin.vertical, refBox, appUnitsPerDevPixel);
+ matrix.ChangeBasis(Point3D(transformOrigin.x, transformOrigin.y, 0));
+
gfx::Matrix result2d;
if (!matrix.CanDraw2D(&result2d)) {
// FIXME: It would be preferable to reject non-2D transforms at parse time.

View File

@@ -1,22 +0,0 @@
diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp
index e5883b377afee44626a2928a97205382e6d07e40..57ae3b0567fbd7afbcfb60a2cd662f611339e5ad 100644
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -3287,6 +3287,9 @@ nsStyleUIReset::nsStyleUIReset()
mWindowShadow(StyleWindowShadow::Auto),
mWindowOpacity(1.0),
mMozWindowInputRegionMargin(StyleLength::Zero()),
+ mWindowTransformOrigin{LengthPercentage::FromPercentage(0.5),
+ LengthPercentage::FromPercentage(0.5),
+ {0.}},
mTransitions(
nsStyleAutoArray<StyleTransition>::WITH_SINGLE_INITIAL_ELEMENT),
mTransitionTimingFunctionCount(1),
@@ -3331,6 +3334,7 @@ nsStyleUIReset::nsStyleUIReset(const nsStyleUIReset& aSource)
mWindowOpacity(aSource.mWindowOpacity),
mMozWindowInputRegionMargin(aSource.mMozWindowInputRegionMargin),
mMozWindowTransform(aSource.mMozWindowTransform),
+ mWindowTransformOrigin(aSource.mWindowTransformOrigin),
mTransitions(aSource.mTransitions.Clone()),
mTransitionTimingFunctionCount(aSource.mTransitionTimingFunctionCount),
mTransitionDurationCount(aSource.mTransitionDurationCount),

View File

@@ -1,12 +0,0 @@
diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h
index 14c24446880b7342f6c194bb068f5fd54657ec85..8a0067b2694029242a0fbe6d806b1dd0153f1715 100644
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -2094,6 +2094,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleUIReset {
// The margin of the window region that should be transparent to events.
mozilla::StyleLength mMozWindowInputRegionMargin;
mozilla::StyleTransform mMozWindowTransform;
+ mozilla::StyleTransformOrigin mWindowTransformOrigin;
nsStyleAutoArray<mozilla::StyleTransition> mTransitions;
// The number of elements in mTransitions that are not from repeating

View File

@@ -1,12 +0,0 @@
diff --git a/layout/style/test/ListCSSProperties.cpp b/layout/style/test/ListCSSProperties.cpp
index 1870bfc3735255f2d41356b00dc52faf95d9d9f2..59ad4fb11c7d48d9ba84dceef09a67a42b486ed8 100644
--- a/layout/style/test/ListCSSProperties.cpp
+++ b/layout/style/test/ListCSSProperties.cpp
@@ -110,6 +110,7 @@ const char* gInaccessibleProperties[] = {
"-moz-window-opacity", // chrome-only internal properties
"-moz-window-transform", // chrome-only internal properties
"-moz-window-shadow", // chrome-only internal properties
+ "-zen-window-transform-origin", // chrome-only internal properties
};
inline int is_inaccessible(const char* aPropName) {

View File

@@ -1,23 +0,0 @@
diff --git a/servo/components/style/properties/longhands/ui.mako.rs b/servo/components/style/properties/longhands/ui.mako.rs
index 24e1e1b7e8fe4d4b70aaa4f5f022500c3dcfcbae..1176bf5d12885b4d96abb2f72288f6c631072578 100644
--- a/servo/components/style/properties/longhands/ui.mako.rs
+++ b/servo/components/style/properties/longhands/ui.mako.rs
@@ -284,6 +284,18 @@ ${helpers.predefined_type(
affects="",
)}
+${helpers.predefined_type(
+ "-zen-window-transform-origin",
+ "TransformOrigin",
+ "computed::TransformOrigin::initial_value()",
+ engines="gecko",
+ gecko_ffi_name="mWindowTransformOrigin",
+ boxed=True,
+ spec="None (Nonstandard internal property)",
+ enabled_in="chrome",
+ affects="overflow",
+)}
+
${helpers.predefined_type(
"animation-fill-mode",
"AnimationFillMode",

View File

@@ -1,5 +1,5 @@
diff --git a/toolkit/themes/shared/popup.css b/toolkit/themes/shared/popup.css
index c2618b86d7e8bb71f9f7f71ef9916c8140ca1ead..96a602f9cbfb77ee0a52151ce8ccf7ba17cf5c43 100644
index c2618b86d7e8bb71f9f7f71ef9916c8140ca1ead..9e2006015e22f1ac043c46066c4c9a85fe053a21 100644
--- a/toolkit/themes/shared/popup.css
+++ b/toolkit/themes/shared/popup.css
@@ -22,8 +22,8 @@ panel {
@@ -13,24 +13,3 @@ index c2618b86d7e8bb71f9f7f71ef9916c8140ca1ead..96a602f9cbfb77ee0a52151ce8ccf7ba
-moz-window-input-region-margin: var(--panel-shadow-margin);
margin: calc(-1 * var(--panel-shadow-margin));
@@ -143,11 +143,7 @@ panel[type="arrow"] {
-moz-window-transform,
-moz-window-opacity;
@media not (prefers-reduced-motion) {
- -moz-window-transform: translateY(-70px);
-
- &:where([side="bottom"]) {
- -moz-window-transform: translateY(70px);
- }
+ -moz-window-transform: scale(0);
}
/* Only do the fade-in animation on pre-Big Sur to avoid missing shadows on
* Big Sur+, see bug 1672091. */
@@ -161,7 +157,6 @@ panel[type="arrow"] {
}
&[animate="cancel"] {
- -moz-window-transform: none;
transform: none;
}

View File

@@ -35,6 +35,10 @@ class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
#anchor;
#emojiAsSVG = false;
#closeOnSelect = true;
#onSelect = null;
#hasSelection = false;
#lastSelectedEmoji = null;
#currentPromise = null;
#currentPromiseResolve = null;
@@ -120,6 +124,14 @@ class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
delete this._emojis;
}
#setAllowNone(allowNone) {
if (allowNone) {
this.#panel.removeAttribute("hide-none-option");
return;
}
this.#panel.setAttribute("hide-none-option", "true");
}
#onSearchInput(event) {
const input = event.target;
const value = input.value.trim().toLowerCase();
@@ -192,13 +204,19 @@ class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
this.svgList.innerHTML = "";
if (this.#currentPromiseReject) {
this.#currentPromiseReject(new Error("Emoji picker closed without selection"));
if (!this.#hasSelection) {
this.#currentPromiseReject?.(new Error("Emoji picker closed without selection"));
} else if (!this.#closeOnSelect) {
this.#currentPromiseResolve?.(this.#lastSelectedEmoji);
}
this.#currentPromise = null;
this.#currentPromiseResolve = null;
this.#currentPromiseReject = null;
this.#onSelect = null;
this.#closeOnSelect = true;
this.#hasSelection = false;
this.#lastSelectedEmoji = null;
this.#anchor.removeAttribute("zen-emoji-open");
this.#anchor.parentElement.removeAttribute("zen-emoji-open");
@@ -213,15 +231,35 @@ class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
)}</text></svg>`
)}`;
}
this.#setAllowNone(Boolean(emoji));
this.#hasSelection = true;
this.#lastSelectedEmoji = emoji;
this.#onSelect?.(emoji);
if (!this.#closeOnSelect) {
return;
}
this.#currentPromiseResolve?.(emoji);
this.#panel.hidePopup();
}
open(anchor, { onlySvgIcons = false, emojiAsSVG = false } = {}) {
open(
anchor,
{
onlySvgIcons = false,
emojiAsSVG = false,
allowNone = true,
closeOnSelect = true,
onSelect = null,
} = {}
) {
if (this.#currentPromise) {
return null;
}
this.#emojiAsSVG = emojiAsSVG;
this.#closeOnSelect = closeOnSelect;
this.#onSelect = onSelect;
this.#hasSelection = false;
this.#lastSelectedEmoji = null;
this.#currentPromise = new Promise((resolve, reject) => {
this.#currentPromiseResolve = resolve;
this.#currentPromiseReject = reject;
@@ -234,6 +272,7 @@ class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
} else {
this.#panel.removeAttribute("only-svg-icons");
}
this.#setAllowNone(allowNone);
this.#panel.openPopup(anchor, "after_start", 0, 0, false, false);
return this.#currentPromise;
}

View File

@@ -115,7 +115,7 @@ window.gZenCommonActions = {
const tabTitle = gBrowser.selectedTab.label;
const markdownLink = `[${tabTitle}](${currentUrl.displaySpec})`;
ClipboardHelper.copyString(markdownLink);
gZenUIManager.showToast("zen-copy-current-url-confirmation", { timeout: 3000 });
gZenUIManager.showToast("zen-copy-current-url-as-markdown-confirmation", { timeout: 3000 });
},
throttle(f, delay) {

View File

@@ -19,6 +19,13 @@ class ZenStartup {
this.#zenInitBrowserLayout();
}
get #shouldUseWatermark() {
return (
Services.prefs.getBoolPref("zen.watermark.enabled", false) &&
gZenWorkspaces.shouldHaveWorkspaces
);
}
#initBrowserBackground() {
const background = document.createXULElement("box");
background.id = "zen-browser-background";
@@ -102,7 +109,7 @@ class ZenStartup {
}
openWatermark() {
if (!Services.prefs.getBoolPref("zen.watermark.enabled", false)) {
if (!this.#shouldUseWatermark) {
document.documentElement.removeAttribute("zen-before-loaded");
return;
}
@@ -113,7 +120,7 @@ class ZenStartup {
closeWatermark() {
document.documentElement.removeAttribute("zen-before-loaded");
if (Services.prefs.getBoolPref("zen.watermark.enabled", false)) {
if (this.#shouldUseWatermark) {
let elementsToIgnore = this.#watermarkIgnoreElements.map((id) => "#" + id).join(", ");
gZenUIManager.motion
.animate(

View File

@@ -929,15 +929,20 @@ window.gZenVerticalTabsManager = {
}
},
animateTabClose(aTab) {
if (aTab.hasAttribute("zen-essential") || aTab.group?.hasAttribute("split-view-group")) {
animateItemClose(aItem) {
if (
aItem.hasAttribute("zen-essential") ||
aItem.group?.hasAttribute("split-view-group") ||
!gZenUIManager.motion ||
gReduceMotion
) {
return Promise.resolve();
}
const height = aTab.getBoundingClientRect().height;
const height = aItem.getBoundingClientRect().height;
const visibleItems = gBrowser.tabContainer.ariaFocusableItems;
const isLastItem = visibleItems[visibleItems.length - 1] === aTab;
const isLastItem = visibleItems[visibleItems.length - 1] === aItem;
return gZenUIManager.motion.animate(
aTab,
aItem,
{
opacity: [1, 0],
transform: ["scale(1)", "scale(0.95)"],

View File

@@ -4,41 +4,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
@keyframes zen-theme-picker-dot-animation {
from {
transform: scale(0.8) translate(-50%, -50%);
}
50% {
transform: scale(1.2) translate(-50%, -50%);
}
to {
transform: scale(1) translate(-50%, -50%);
}
}
/* Mark: Zen Glance */
@keyframes zen-glance-overlay-animation {
from {
opacity: 0;
transform: scale(0.8);
}
to {
opacity: 1;
transform: scale(1);
}
}
@keyframes zen-glance-overlay-animation-out {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
@keyframes zen-back-and-forth-text {
0%,
10% {
@@ -82,3 +47,12 @@
transform: translateY(0);
}
}
@keyframes zen-text-gradient {
0% {
background-position: 0% 50%;
}
100% {
background-position: -400% 50%;
}
}

View File

@@ -183,7 +183,7 @@
background: transparent !important;
}
:root:is([zen-single-toolbar="true"], [zen-has-empty-tab="true"]) {
:root:where([zen-single-toolbar="true"], [zen-has-empty-tab="true"]) {
#zen-copy-url-button[disabled] {
display: none !important;
}
@@ -394,7 +394,7 @@
/* Border radius on hover */
#urlbar .urlbar-page-action,
#urlbar #tracking-protection-icon-container,
#urlbar:not([breakout-extend="true"]) #identity-box:is(:not(.chromeUI), [pageproxystate="invalid"]) #identity-icon-box {
#urlbar:not([breakout-extend="true"]) #identity-box:where(:not(.chromeUI), [pageproxystate="invalid"]) #identity-icon-box {
border-radius: var(--urlbar-icon-border-radius) !important;
min-width: 26px;
}
@@ -667,7 +667,7 @@
color: var(--zen-selected-color) !important;
}
&:is([type="switchtab"], [type="dynamic"], [type="history_serp"], [type="autofill_origin"], [type="top_site"]) .urlbarView-favicon,
&:where([type="switchtab"], [type="dynamic"], [type^="history"], [type^="autofill_"], [type="top_site"]) .urlbarView-favicon,
& .urlbarView-shortcutContent {
fill: var(--zen-selected-color) !important;
background-color: rgba(255, 255, 255, 0.9) !important;

View File

@@ -3,38 +3,6 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
panel[type="arrow"] {
@media (-moz-platform: macos) and (-moz-panel-animations) {
&[side="bottom"] {
/* Animate from the bottom */
-zen-window-transform-origin: 0 100%;
}
&[side="left"] {
/* Animate from the left and center */
-zen-window-transform-origin: 0 50%;
}
&[side="right"] {
/* Animate from the right */
-zen-window-transform-origin: 100% 50%;
}
:root[zen-right-side="true"] & {
/* Animate from the right */
-zen-window-transform-origin: 100% 0;
&[side="bottom"] {
/* Animate from the bottom right */
-zen-window-transform-origin: 100% 100%;
}
&[side="left"] {
/* Animate from the right */
-zen-window-transform-origin: 100% 50%;
}
&[side="right"] {
/* Animate from the right */
-zen-window-transform-origin: 100% 50%;
}
}
}
}
menupopup,
panel[type="arrow"]:not(#feature-callout) {
@@ -62,12 +30,15 @@ panel[type="arrow"]:not(#feature-callout) {
}
@media (-moz-platform: macos) {
appearance: auto !important;
-moz-default-appearance: menupopup;
/* We set the default background here, rather than on ::part(content),
* because otherwise it'd interfere with the native look. Non-native-looking
* popups should get their background via --panel-background */
background-color: Menu;
&:where([nonnativepopover="true"], :not([type="arrow"])) {
appearance: auto !important;
-moz-default-appearance: menupopup;
/* We set the default background here, rather than on ::part(content),
* because otherwise it'd interfere with the native look. Non-native-looking
* popups should get their background via --panel-background */
background-color: Menu !important;
}
--panel-shadow-margin: 0px !important;
--panel-background: transparent !important;
--panel-border-color: transparent;

View File

@@ -80,6 +80,14 @@ panel {
padding-inline-start: calc(16px + var(--uc-arrowpanel-menuicon-margin-inline));
}
/* Allow localized zoom labels to shrink so the fullscreen button stays visible. */
#appMenu-zoom-controls > .toolbarbutton-text {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* Firefox profile avatar in appmenu */
#appMenu-fxa-label2::before {
content: "";
@@ -123,7 +131,7 @@ panel {
/* #appMenu-zoomReduce-button2, */
#appMenu-zoom-controls > #appMenu-fullscreen-button2 {
margin-left: calc((var(--panel-separator-margin-vertical) + var(--uc-arrowpanel-menuitem-margin-block)) * 2 + 1px);
margin-inline-start: calc((var(--panel-separator-margin-vertical) + var(--uc-arrowpanel-menuitem-margin-block)) * 2 + 1px);
}
#appMenu-zoom-controls > #appMenu-fullscreen-button2::before {

View File

@@ -9,6 +9,7 @@
position: absolute !important;
top: 3%;
right: 1.5%;
z-index: 2;
}
#zen-workspaces-button .zen-workspace-sidebar-name {
@@ -86,6 +87,13 @@
}
}
&[hide-none-option="true"] {
& #PanelUI-zen-emojis-picker-none {
opacity: 0;
pointer-events: none;
}
}
#PanelUI-zen-emojis-picker-none label {
display: none;
}
@@ -163,6 +171,7 @@
width: 22px;
height: 22px;
appearance: none;
transition: opacity 0.08s ease-in-out;
&:hover {
background-color: light-dark(rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.1));
@@ -613,3 +622,26 @@
:root:not([zen-sidebar-expanded="true"]) zen-sidebar-notification {
display: none;
}
/* Feature allouts */
#feature-callout {
#mainContentHeader {
--special-color: color-mix(in srgb, light-dark(white, black), currentColor 50%);
--specia-color-2: color-mix(in srgb, light-dark(white, black), currentColor 90%);
background: linear-gradient(135deg, var(--specia-color-2), var(--special-color), var(--specia-color-2));
background-size: 400%;
background-clip: text;
/* Still works on firefox */
-webkit-text-fill-color: transparent;
animation: zen-text-gradient 5s linear infinite;
font-size: initial;
}
#mainContentSubheader {
text-wrap: balance;
}
#multi-stage-message-welcome-text {
gap: 4px;
}
}

View File

@@ -230,8 +230,9 @@
&[zen-private-window='true'] {
--zen-main-browser-background: color-mix(in srgb, rgb(11, 10, 11) 90%, var(--zen-themed-toolbar-bg-transparent));
--zen-main-browser-background-toolbar: var(--zen-main-browser-background);
--zen-primary-color: light-dark(rgb(93, 42, 107), rgb(110, 48, 125)) !important;
--toolbox-textcolor: color-mix(in srgb, currentColor 95%, transparent) !important;
--zen-primary-color: rgb(11, 10, 11) !important;
/* Make sure its in sync with getToolbarColor */
--toolbox-textcolor: rgba(255, 255, 255, 0.8) !important;
}
&[zen-unsynced-window='true'] {

View File

@@ -589,7 +589,9 @@ window.gZenCompactModeManager = {
{
element: this.sidebar,
screenEdge: this.sidebarIsOnRight ? "right" : "left",
keepHoverDuration: 100,
keepHoverDuration: Services.prefs.getIntPref(
"zen.view.compact.sidebar-keep-hover.duration"
),
},
{
element: document.getElementById("zen-appcontent-navbar-wrapper"),

View File

@@ -68,6 +68,8 @@
#changeSpaceTimer = null;
#isAnimatingTabMove = false;
#dragOverSplit = {};
constructor(tabbrowserTabs) {
super(tabbrowserTabs);
@@ -78,6 +80,24 @@
Ci.nsIZenDragAndDrop
);
XPCOMUtils.defineLazyPreferenceGetter(
this,
"_dndSplitEnabled",
"zen.splitView.enable-drag-over-split",
true
);
XPCOMUtils.defineLazyPreferenceGetter(
this,
"_dndSplitThreshold",
"zen.splitView.drag-over-split-threshold",
25
);
XPCOMUtils.defineLazyPreferenceGetter(
this,
"_dndSplitDelay",
"zen.splitView.drag-over-split-delayMC",
300
);
XPCOMUtils.defineLazyPreferenceGetter(
this,
"_dndSwitchSpaceDelay",
@@ -587,6 +607,7 @@
return;
}
this.#handle_sidebarDragOver(event);
this.#handle_tabDragOverToSplit(event);
}
#shouldSwitchSpace(event) {
@@ -655,6 +676,122 @@
}
}
#handle_tabDragOverToSplit(event) {
if (!this._dndSplitEnabled) {
return;
}
const dt = event.dataTransfer;
const draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
if (!isTab(draggedTab)) {
return;
}
const dragData = draggedTab._dragData;
const movingTabsSet = dragData.movingTabsSet;
const dropElement = event.target.closest(".tabbrowser-tab");
// TODO: After Cheff adds split view support for essentials, don't forget to remove the check
if (
!dropElement ||
!isTab(dropElement) ||
dropElement.hasAttribute("zen-essential") ||
dropElement.hasAttribute("zen-glance-tab") ||
dropElement?.group?.hasAttribute("split-view-group") ||
movingTabsSet.size > 1
) {
this._clearDragOverSplit();
return;
}
if (
movingTabsSet.has(dropElement) ||
!isTab(draggedTab) ||
draggedTab?.group?.hasAttribute("split-view-group") ||
draggedTab.hasAttribute("zen-live-folder-item-id") ||
dropElement.hasAttribute("zen-live-folder-item-id")
) {
this._clearDragOverSplit();
return;
}
const rect = window.windowUtils.getBoundsWithoutFlushing(dropElement);
const { clientX, clientY } = event;
const targetX = rect.x;
const targetTop = rect.top;
const targetWidth = rect.width;
const targetHeight = rect.height;
const edgeZoneThreshold = this._dndSplitThreshold / 100;
const overlapRatioY = (clientY - targetTop) / targetHeight;
if (overlapRatioY < edgeZoneThreshold || overlapRatioY > 1 - edgeZoneThreshold) {
this._clearDragOverSplit();
return;
}
const isLeft = clientX < targetX + targetWidth / 2;
const dropSide = isLeft ? "left" : "right";
// If the drop side or element changes, clear dragOverSplit
if (
this.#dragOverSplit.data?.dropElement !== dropElement ||
this.#dragOverSplit.data?.dropSide !== dropSide
) {
this._clearDragOverSplit();
}
if (
this.#dragOverSplit.timer &&
this.#dragOverSplit.data?.dropElement === dropElement &&
this.#dragOverSplit.data?.dropSide === dropSide
) {
// Timer already running for the same target and side, do nothing
return;
}
this.#dragOverSplit.data = {
dropElement,
dropSide,
};
this.#dragOverSplit.timer = setTimeout(() => {
this.#createFakeTabSplit(dropElement, dropSide);
}, this._dndSplitDelay);
}
#createFakeTabSplit(dropElement, dropSide) {
// Remove drop indicator
this.clearDragOverVisuals();
// Remove any existing fake tab
if (this.#dragOverSplit.fakeTab) {
this.#dragOverSplit.fakeTab.remove();
}
const element = document.createXULElement("zen-split-fake-tab");
const firstChild = dropElement.firstChild;
if (dropSide === "left") {
firstChild.before(element);
} else {
firstChild.after(element);
}
this.#dragOverSplit.fakeTab = element;
this.#dragOverSplit.canDrop = true;
}
_clearDragOverSplit() {
if (this.#dragOverSplit.timer) {
clearTimeout(this.#dragOverSplit.timer);
}
this.#dragOverSplit.fakeTab?.remove();
this.#dragOverSplit.timer = null;
this.#dragOverSplit.fakeTab = null;
this.#dragOverSplit.data = null;
this.#dragOverSplit.canDrop = null;
}
handle_windowDragEnter(event) {
if (!this.#isMovingTab() || !this.#isOutOfWindow) {
return;
@@ -722,6 +859,12 @@
gZenFolders.highlightGroupOnDragOver(null);
super.handle_drop(event);
this.#maybeClearVerticalPinnedGridDragOver();
this.#handle_dropSwitchSpace(event);
this.#handle_dropCreateSplit(event);
this._clearDragOverSplit();
}
#handle_dropSwitchSpace(event) {
const dt = event.dataTransfer;
const activeWorkspace = gZenWorkspaces.activeWorkspace;
let draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
@@ -745,6 +888,34 @@
gZenWorkspaces.updateTabsContainers();
}
#handle_dropCreateSplit(event) {
if (!this.#dragOverSplit.canDrop) {
return;
}
const dragData = this.#dragOverSplit.data;
const dt = event.dataTransfer;
const draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
if (!dragData || !draggedTab) {
return;
}
this._dontAnimateTabMove = true;
const droppedOnTab = dragData.dropElement;
const dropSide = dragData.dropSide;
// Clear any visuals and timer
this._clearDragOverSplit();
const isLeft = dropSide === "left";
gZenViewSplitter.splitTabs(
isLeft ? [draggedTab, droppedOnTab] : [droppedOnTab, draggedTab],
"vsep",
isLeft ? 0 : 1
);
}
handle_drop_transition(dropElement, draggedTab, movingTabs, dropBefore) {
if (isTabGroupLabel(dropElement)) {
dropElement = dropElement.group;
@@ -779,11 +950,19 @@
const animateElement = (ele, translateY) => {
ele.style.transform = `translateY(${translateY}px)`;
let animateInternal = (resolve) => {
const clearStyles = () => {
ele.style.transform = "";
ele.style.zIndex = "";
};
if (this._dontAnimateTabMove) {
clearStyles();
resolve();
return;
}
gZenUIManager
.elementAnimate(ele, { y: [translateY, 0] }, { duration: 100, easing: "ease-out" })
.then(() => {
ele.style.transform = "";
ele.style.zIndex = "";
clearStyles();
})
.finally(resolve);
};
@@ -854,6 +1033,7 @@
}
Promise.all(animations).finally(() => {
this.#isAnimatingTabMove = false;
delete this._dontAnimateTabMove;
});
}
@@ -874,6 +1054,7 @@
super.handle_dragend(event);
thisFromGlobal.clearDragOverVisuals();
ownerGlobal.gZenPinnedTabManager.removeTabContainersDragoverClass();
thisFromGlobal._clearDragOverSplit();
this.#maybeClearVerticalPinnedGridDragOver();
thisFromGlobal.originalDragImageArgs = [];
window.removeEventListener("dragenter", thisFromGlobal.handle_windowDragEnter, {
@@ -948,6 +1129,10 @@
// eslint-disable-next-line complexity
#applyDragoverIndicator(event, dropElement, movingTabs, draggedTab) {
// Doesn't show indicator when dragOverSplit
if (this.#dragOverSplit.canDrop) {
return;
}
const separation = 4;
const dropZoneSelector = ":is(.zen-drop-target)";
let shouldPlayHapticFeedback = false;

View File

@@ -48,8 +48,8 @@ export class nsZenFolder extends MozTabbrowserTabGroup {
<rect class="front" x="5.625" y="9.625" width="16.75" height="12.75" rx="2.375" style="stroke-width: 1.5px; stroke: var(--zen-folder-stroke); fill: url(#gradient-1); fill-opacity: 0.1;">
</rect>
<!--Icon (g)-->
<g class="icon" style="fill: var(--zen-folder-stroke);">
<image href="" height="10" width="10"/>
<g class="icon">
<image href="" height="11" width="11"/>
</g>
<!--End Icon (g)-->
<g class="dots" style="fill: var(--zen-folder-stroke);">
@@ -278,6 +278,16 @@ export class nsZenFolder extends MozTabbrowserTabGroup {
super.on_click(event);
}
addTabs(tabs) {
super.addTabs(tabs);
if (this.collapsed && !gZenFolders._sessionRestoring && this.isLiveFolder && tabs.length) {
tabs.forEach((tab) => {
tab.setAttribute("folder-active", "true");
});
gZenFolders.animateCollapse(this);
}
}
/**
* Get the root most collapsed folder in the tree.
*

View File

@@ -241,25 +241,8 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
group.setAttribute("had-zen-pinned-changed", true);
}
if (group.collapsed && !this._sessionRestoring) {
if (group.isLiveFolder) {
if (!group.hasAttribute("has-active")) {
let groupStart = group.groupStartElement;
let marginTop = groupStart.style.marginTop ? parseInt(groupStart.style.marginTop) : 0;
if (marginTop < 0) {
groupStart.style.marginTop = `${marginTop + 4}px`;
}
}
tab.setAttribute("folder-active", "true");
group.setAttribute("has-active", "true");
group.groupContainer.removeAttribute("hidden");
group.activeTabs = [...new Set([...group.activeTabs, tab])].sort(
(a, b) => a._tPos > b._tPos
);
} else {
group.collapsed = group.hasAttribute("has-active");
}
if (group.collapsed && !this._sessionRestoring && !group.isLiveFolder) {
group.collapsed = group.hasAttribute("has-active");
}
}
@@ -417,6 +400,14 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
tabs = [];
}
// Prevent create folder inside Live Folder
const thereIsOneLiveFolderTab = tabs?.some((tab) =>
tab.hasAttribute("zen-live-folder-item-id")
);
if (thereIsOneLiveFolderTab) {
return;
}
const canInsertBefore =
!isFromToolbar &&
!triggerTab.hasAttribute("zen-essential") &&
@@ -748,12 +739,12 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
get #searchPopupOptions() {
const isRightSide = gZenVerticalTabsManager._prefsRightSide;
const position = isRightSide ? "start_before" : "start_before";
const position = isRightSide ? "topleft topright" : "topright topleft";
let size = Math.min(this.#popup.querySelector("#zen-folder-tabs-list").children.length, 6);
size *= 48;
return {
position,
x: -10,
x: isRightSide ? -10 : 10,
y: size / -2,
};
}
@@ -920,15 +911,15 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
return;
}
gZenEmojiPicker
.open(group.icon, { onlySvgIcons: true })
.then((icon) => {
gZenEmojiPicker.open(group.icon, {
onlySvgIcons: true,
allowNone: Boolean(group.iconURL),
closeOnSelect: false,
onSelect: (icon) => {
this.setFolderUserIcon(group, icon);
group.dispatchEvent(new CustomEvent("TabGroupUpdate", { bubbles: true }));
})
.catch((err) => {
console.error(err);
});
},
});
}
setFolderUserIcon(group, icon) {
@@ -961,7 +952,8 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
if (
!group.collapsed ||
!Services.prefs.getBoolPref("zen.folders.search.enabled") ||
gBrowser.tabContainer.hasAttribute("movingtab")
gBrowser.tabContainer.hasAttribute("movingtab") ||
event.target.classList.contains("tab-reset-button")
) {
return;
}
@@ -976,7 +968,7 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
}
this.#mouseTimer = setTimeout(() => {
// If popup is focused don't hide it
if (this.#popup.matches(":hover")) {
if (this.#popup.matches(":hover") || labelContainer.matches(":hover")) {
return;
}
this.#popup.hidePopup(true);
@@ -1117,20 +1109,16 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
const parentWorkingData = tabFolderWorkingData.get(stateData.parentId);
if (parentWorkingData && parentWorkingData.node) {
switch (stateData?.prevSiblingInfo?.type) {
case "tab": {
const tab = document.getElementById(stateData.prevSiblingInfo.id);
tab.after(node);
break;
}
case "tab":
case "group": {
const folder = document.getElementById(stateData.prevSiblingInfo.id);
if (folder) {
folder.after(node);
const item = document.getElementById(stateData.prevSiblingInfo.id);
if (item) {
item.after(node);
break;
}
// If we didn't find the group, we should debug it and continue to default case.
console.warn(
`Zen Folders: Could not find previous sibling group with id ${stateData.prevSiblingInfo.id} while restoring session.`
console.error(
`Zen Folders: Could not find previous sibling with id ${stateData.prevSiblingInfo.id} while restoring session.`
);
// @eslint-disable-next-line no-fallthrough
}
@@ -1152,9 +1140,7 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
}
gBrowser.tabContainer._invalidateCachedTabs();
setTimeout(() => {
delete this._sessionRestoring;
}, 0);
delete this._sessionRestoring;
}
/**
@@ -1218,6 +1204,18 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
});
}
#shouldTabBeActive(tab, contextGroup) {
if (tab.multiselected || tab.selected) {
return true;
}
// See https://github.com/zen-browser/desktop/issues/12509.
// We can't just blindly check for the tab's active state
// because it would mean that all tabs in a collapsed group that
// are active means they should be active for contextGroup as well,
// even if they are active because of another group they belong to.
return tab.hasAttribute("folder-active") && contextGroup === tab.group;
}
#collectGroupItems(group, opts = {}) {
const { selectedTabs = [], splitViewIds = new Set(), activeFoldersIds = new Set() } = opts;
const folders = new Map();
@@ -1233,7 +1231,7 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
const activeFolderId = lastActiveFolder?.id;
const splitViewId = isSplitView ? item?.group?.id : null;
if (item.multiselected || item.selected || item.hasAttribute("folder-active")) {
if (this.#shouldTabBeActive(item, group)) {
selectedTabs.push(item);
if (splitViewId) {
splitViewIds.add(splitViewId);
@@ -1278,7 +1276,7 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
}
get #folderAnimationDuration() {
return this._sessionRestoring ? 0 : 0.12;
return this._dontAnimateFolder ? 0 : 0.12;
}
async animateCollapse(group) {
@@ -1533,7 +1531,7 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
this.#animationCount += 1;
await Promise.all(animations);
this.#animationCount -= 1;
gBrowser.tabContainer._invalidateCachedTabs();
gBrowser.tabContainer._invalidateCachedVisibleTabs();
}
async animateUnload(group, tabToUnload, ungroup = false) {
@@ -1696,7 +1694,10 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
for (let i = 0; i < groupItems.length; i++) {
const { item, splitViewId } = groupItems[i];
itemsToShow.push(item);
let itemVisible = item.visible;
if (itemVisible) {
itemsToShow.push(item);
}
// Skip selected items
if (selectedTabs.includes(item)) {
@@ -1708,10 +1709,8 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
continue;
}
if (!item.hasAttribute?.("folder-active")) {
if (!itemsToHide.includes(item)) {
itemsToHide.push(item);
}
if (!itemVisible && !itemsToHide.includes(item)) {
itemsToHide.push(item);
}
}
}
@@ -1756,7 +1755,6 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
}
// Cleanup
this.styleCleanup(itemsToHide);
this.styleCleanup(selectedTabs);
}

View File

@@ -111,8 +111,8 @@ zen-folder {
& image {
fill-opacity: 0.9;
-moz-context-properties: fill, fill-opacity;
fill: var(--zen-folder-stroke);
transform: translate(9px, 11px);
fill: color-mix(in srgb, var(--zen-folder-stroke) 50%, currentColor);
transform: translate(8.5px, 10.5px);
}
& g,
@@ -177,7 +177,6 @@ zen-folder {
& > label {
width: 100% !important;
background: transparent !important;
border: none !important;
color: var(--sidebar-text-color) !important;
margin: 0 !important;
@@ -191,6 +190,13 @@ zen-folder {
padding: 0 !important;
max-width: 100% !important;
}
&[live-folder-animation] {
background: linear-gradient(270deg, var(--sidebar-text-color), color-mix(in srgb, var(--sidebar-text-color) 25%, transparent));
background-size: 400%;
background-clip: text;
-webkit-text-fill-color: transparent;
}
}
.tab-reset-button[live-folder-action] {

View File

@@ -3,17 +3,13 @@
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
setTimeout: "resource://gre/modules/Timer.sys.mjs",
clearTimeout: "resource://gre/modules/Timer.sys.mjs",
requestIdleCallback: "resource://gre/modules/Timer.sys.mjs",
cancelIdleCallback: "resource://gre/modules/Timer.sys.mjs",
NetUtil: "resource://gre/modules/NetUtil.sys.mjs",
DeferredTask: "resource://gre/modules/DeferredTask.sys.mjs",
NetworkHelper: "resource://devtools/shared/network-observer/NetworkHelper.sys.mjs",
});
export class nsZenLiveFolderProvider {
#timerHandle = null;
#idleCallbackHandle = null;
#task = null;
constructor({ id, manager, state }) {
this.id = id;
@@ -30,56 +26,50 @@ export class nsZenLiveFolderProvider {
}
async refresh() {
this.stop();
const result = await this.#internalFetch();
this.start();
this.#task.disarm();
const result = await this.#fetchLiveFolder();
this.#task.arm();
return result;
}
start() {
const now = Date.now();
const lastFetched = this.state.lastFetched;
start(checkDelay = true) {
const interval = this.state.interval;
const timeSinceLast = now - lastFetched;
let delay = interval - timeSinceLast;
if (delay <= 0) {
delay = 0;
if (this.#task) {
this.#task.finalize();
}
this.#scheduleNext(delay);
if (checkDelay) {
const now = Date.now();
const lastFetched = this.state.lastFetched;
const timeSinceLast = now - lastFetched;
let delay = interval - timeSinceLast;
if (delay <= 0) {
delay = 0;
}
this.#task = new lazy.DeferredTask(async () => {
await this.#fetchLiveFolder();
this.start(false);
}, delay);
} else {
this.#task = new lazy.DeferredTask(async () => {
await this.#fetchLiveFolder();
this.#task.arm();
}, interval);
}
this.#task.arm();
}
stop() {
if (this.#timerHandle) {
lazy.clearTimeout(this.#timerHandle);
this.#timerHandle = null;
}
if (this.#idleCallbackHandle) {
lazy.cancelIdleCallback(this.#idleCallbackHandle);
this.#idleCallbackHandle = null;
if (this.#task) {
this.#task.disarm();
}
}
#scheduleNext(delay) {
if (this.#timerHandle) {
lazy.clearTimeout(this.#timerHandle);
}
this.#timerHandle = lazy.setTimeout(() => {
const fetchWhenIdle = () => {
this.#internalFetch();
this.#idleCallbackHandle = null;
};
this.#idleCallbackHandle = lazy.requestIdleCallback(fetchWhenIdle);
this.#scheduleNext(this.state.interval);
}, delay);
}
async #internalFetch() {
async #fetchLiveFolder() {
try {
const items = await this.fetchItems();
this.state.lastFetched = Date.now();
@@ -145,24 +135,20 @@ export class nsZenLiveFolderProvider {
userContextId = space.containerTabId || 0;
}
}
const principal = Services.scriptSecurityManager.createContentPrincipal(uri, {
userContextId,
});
const principal = Services.scriptSecurityManager.createContentPrincipal(uri, { userContextId });
const securityFlags =
Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT |
Ci.nsILoadInfo.SEC_COOKIES_INCLUDE;
const channel = Services.io
.newChannelFromURI(
uri,
this.manager.window.document,
principal,
principal,
securityFlags,
Ci.nsIContentPolicy.TYPE_DOCUMENT
)
.QueryInterface(Ci.nsIHttpChannel);
const channel = lazy.NetUtil.newChannel({
uri,
// Use TYPE_SAVEAS_DOWNLOAD instead of TYPE_DOCUMENT because otherwise,
// the loading principal will be inherited from the loadingNode, which doesn't exist.
// Meaning that we will never properly inject cookies. For some reason thouh,
// using TYPE_SAVEAS_DOWNLOAD doesn't have this issue and correctly uses the provided principal.
contentPolicyType: Ci.nsIContentPolicy.TYPE_SAVEAS_DOWNLOAD,
loadingPrincipal: principal,
securityFlags:
Ci.nsILoadInfo.SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT | Ci.nsILoadInfo.SEC_COOKIES_INCLUDE,
triggeringPrincipal: principal,
}).QueryInterface(Ci.nsIHttpChannel);
let httpStatus = null;
let contentType = "";

View File

@@ -8,6 +8,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
setTimeout: "resource://gre/modules/Timer.sys.mjs",
TabStateCache: "resource:///modules/sessionstore/TabStateCache.sys.mjs",
ZenWindowSync: "resource:///modules/zen/ZenWindowSync.sys.mjs",
FeatureCallout: "resource:///modules/asrouter/FeatureCallout.sys.mjs",
});
ChromeUtils.defineLazyGetter(
@@ -33,11 +34,11 @@ class nsZenLiveFoldersManager {
#saveFilename = "zen-live-folders.jsonlz4";
#file = null;
stateRestored = Promise.withResolvers();
constructor() {
this.liveFolders = new Map();
this.registry = new Map();
this.dismissedItems = new Set();
this.folderRefs = new WeakMap();
}
get window() {
@@ -192,8 +193,11 @@ class nsZenLiveFoldersManager {
const folder = this.window.gZenFolders.createFolder([], {
label,
isLiveFolder: true,
collapsed: true,
});
this.#maybeShowPromotion(folder, icon);
if (icon) {
this.window.gZenFolders.setFolderUserIcon(folder, icon);
}
@@ -212,7 +216,6 @@ class nsZenLiveFoldersManager {
});
this.liveFolders.set(folder.id, liveFolder);
this.folderRefs.set(liveFolder, folder);
liveFolder.start();
this.saveState();
@@ -220,6 +223,87 @@ class nsZenLiveFoldersManager {
return folder.id;
}
#maybeShowPromotion(folder, icon) {
let labelElement = folder.labelElement;
labelElement.setAttribute("live-folder-animation", "true");
labelElement.style.backgroundPositionX = "0%";
folder.ownerGlobal.gZenUIManager.motion
.animate(
labelElement,
{
backgroundPositionX: ["0%", "-50%"],
},
{
duration: 1,
}
)
.then(() => {
labelElement.style.backgroundPositionX = "";
labelElement.removeAttribute("live-folder-animation");
});
if (Services.prefs.getBoolPref("zen.live-folders.promotion.shown", false)) {
return;
}
Services.prefs.setBoolPref("zen.live-folders.promotion.shown", true);
let window = this.window;
let gBrowser = window.gBrowser;
let isRightSide = window.gZenVerticalTabsManager._prefsRightSide;
const callout = new lazy.FeatureCallout({
win: this.window,
location: "chrome",
context: "chrome",
browser: gBrowser.selectedBrowser,
theme: { preset: "chrome" },
});
callout.showFeatureCallout({
id: "ZEN_LIVE_FOLDERS_CALLOUT",
template: "feature_callout",
groups: ["cfr"],
content: {
id: "ZEN_LIVE_FOLDERS_CALLOUT",
template: "spotlight",
backdrop: "transparent",
transitions: true,
autohide: true,
screens: [
{
id: "ZEN_LIVE_FOLDERS_CALLOUT",
anchors: [
{
selector: `[id="${folder.id}"] > .tab-group-label-container`,
panel_position: {
anchor_attachment: isRightSide ? "leftcenter" : "rightcenter",
callout_attachment: isRightSide ? "topright" : "topleft",
},
},
],
content: {
width: "310px",
position: "callout",
title_logo: {
imageURL: icon,
width: "18px",
height: "18px",
marginInline: "0 8px",
},
title: {
string_id: "zen-live-folders-promotion-title",
},
subtitle: {
string_id: "zen-live-folders-promotion-description",
},
},
},
],
},
});
lazy.setTimeout(() => {
callout.endTour();
}, 10000);
}
deleteFolder(id, deleteFolder = true) {
const liveFolder = this.liveFolders.get(id);
if (!liveFolder) {
@@ -286,7 +370,10 @@ class nsZenLiveFoldersManager {
existingItemIds.add(itemId);
}
this.window.gBrowser.removeTabs(outdatedTabs);
this.window.gBrowser.removeTabs(outdatedTabs, {
skipSessionStore: true,
animate: !folder.collapsed,
});
// Remove the dismissed items that are no longer in the given list
for (const dismissedItemId of this.dismissedItems) {
@@ -295,6 +382,14 @@ class nsZenLiveFoldersManager {
}
}
let userContextId = 0;
let space = folder.ownerGlobal.gZenWorkspaces.getWorkspaceFromId(
folder.getAttribute("zen-workspace-id")
);
if (space) {
userContextId = space.containerTabId || 0;
}
// Only add the items that are not already in the folder and was not dismissed by the user
const newItems = items
.filter((item) => {
@@ -308,6 +403,7 @@ class nsZenLiveFoldersManager {
skipAnimation: true,
noInitialLabel: true,
lazyTabTitle: item.title,
userContextId,
});
// createLazyBrowser can't be pinned by default
this.window.gBrowser.pinTab(tab);
@@ -369,20 +465,14 @@ class nsZenLiveFoldersManager {
}
getFolderForLiveFolder(liveFolder) {
if (this.folderRefs.has(liveFolder)) {
return this.folderRefs.get(liveFolder);
}
if (!this.window) {
return null;
}
const folder = lazy.ZenWindowSync.getItemFromWindow(this.window, liveFolder.id);
if (folder?.isZenFolder) {
this.folderRefs.set(liveFolder, folder);
return folder;
}
return folder;
return null;
}
#makeCompositeId(folderId, itemId) {
@@ -443,7 +533,6 @@ class nsZenLiveFoldersManager {
tabsState.push({
itemId,
label: tab.getAttribute("zen-show-sublabel"),
icon: tab.iconImage.src,
});
}
@@ -490,9 +579,7 @@ class nsZenLiveFoldersManager {
});
this.liveFolders.set(entry.id, liveFolder);
this.folderRefs.set(liveFolder, folder);
liveFolder.tabsState = entry.tabsState;
liveFolder.tabsState = entry.tabsState || [];
liveFolder.state.lastErrorId = entry.data.state.lastErrorId;
if (entry.dismissedItems && Array.isArray(entry.dismissedItems)) {
entry.dismissedItems.forEach((id) => this.dismissedItems.add(id));
@@ -500,6 +587,8 @@ class nsZenLiveFoldersManager {
liveFolder.start();
}
this.stateRestored.resolve();
}
}

View File

@@ -25,10 +25,11 @@ class nsZenLiveFoldersUI {
}
});
window.gZenWorkspaces.promiseInitialized.finally(() => {
const manager = lazy.ZenLiveFoldersManager;
for (const liveFolder of manager.liveFolders.values()) {
Promise.all([
window.gZenWorkspaces.promiseInitialized,
lazy.ZenLiveFoldersManager.stateRestored.promise,
]).then(() => {
for (const liveFolder of lazy.ZenLiveFoldersManager.liveFolders.values()) {
this.#restoreUIStateForLiveFolder(liveFolder);
}
});

View File

@@ -69,7 +69,7 @@ export class nsGithubLiveFolderProvider extends nsZenLiveFolderProvider {
title,
subtitle: author,
icon: "chrome://browser/content/zen-images/favicons/github.svg",
url: `https://github.com/${issueUrl}`,
url: "https://github.com" + issueUrl,
id: `${repo}#${number}`,
});
}
@@ -78,7 +78,8 @@ export class nsGithubLiveFolderProvider extends nsZenLiveFolderProvider {
this.state.repos = activeRepos;
return items;
} catch {
} catch (error) {
console.error("Error fetching or parsing GitHub issues:", error);
return "zen-live-folder-failed-fetch";
}
}
@@ -142,7 +143,7 @@ export class nsGithubLiveFolderProvider extends nsZenLiveFolderProvider {
}
}
searchParams.set("q", outputString);
searchParams.set("q", outputString.trim().replace(/ +(?= )/g, ""));
return searchParams.toString();
}

View File

@@ -80,7 +80,8 @@ export class nsRssLiveFolderProvider extends nsZenLiveFolderProvider {
}
}
return items;
} catch {
} catch (error) {
console.error("Error fetching or parsing RSS feed:", error);
return "zen-live-folder-failed-fetch";
}
}

View File

@@ -34,6 +34,8 @@ XPCOMUtils.defineLazyPreferenceGetter(
const SHOULD_BACKUP_FILE = Services.prefs.getBoolPref("zen.session-store.backup-file", true);
const FILE_NAME = "zen-sessions.jsonlz4";
const LAST_BUILD_ID_PREF = "zen.session-store.last-build-id";
// 'browser.startup.page' preference value to resume the previous session.
const BROWSER_STARTUP_RESUME_SESSION = 3;
@@ -221,7 +223,7 @@ export class nsZenSessionManager {
try {
await this.#file.load();
this._dataFromFile = this.#file.data;
if (!this._dataFromFile?.spaces) {
if (!this._dataFromFile?.spaces?.length) {
// Go to the catch block to try to recover from backup files
// if the file is empty or has invalid data, as it can happen if the app
// crashes while writing the session file.
@@ -232,6 +234,9 @@ export class nsZenSessionManager {
try {
let data = await IOUtils.readJSON(backupFile, { decompress: true });
this.log(`Recovered data from backup file ${backupFile}`);
if (!data?.spaces?.length) {
continue;
}
this._dataFromFile = data;
break;
} catch (e) {
@@ -274,6 +279,19 @@ export class nsZenSessionManager {
}
get #shouldRestoreOnlyPinned() {
let buildId = Services.appinfo.platformBuildID;
let lastBuildId = Services.prefs.getStringPref(LAST_BUILD_ID_PREF, "");
let buildIdChanged = buildId !== lastBuildId;
if (buildIdChanged) {
// If the build ID has changed since the last session, it means the user has updated the app,
// so we should not remove the unpinned tabs as they might want to keep them after the update.
this.log("Build ID has changed since last session, not restoring only pinned tabs", {
buildId,
lastBuildId,
});
Services.prefs.setStringPref(LAST_BUILD_ID_PREF, buildId);
return false;
}
return (
Services.prefs.getIntPref("browser.startup.page", 1) !== BROWSER_STARTUP_RESUME_SESSION ||
lazy.PrivateBrowsingUtils.permanentPrivateBrowsing
@@ -447,7 +465,8 @@ export class nsZenSessionManager {
];
}
for (const winData of initialState?.windows || []) {
winData.spaces = winData.spaces || this._migrationData?.spaces || [];
winData.spaces =
(winData.spaces?.length ? winData.spaces : this._migrationData?.spaces) || [];
if (winData.tabs) {
for (const tabData of winData.tabs) {
let storeId = tabData.zenSyncId || tabData.zenPinnedId;
@@ -710,6 +729,12 @@ export class nsZenSessionManager {
// Folders are always pinned, so we dont need to check for the pinned state here.
aWindowData.folders = sidebar.folders;
aWindowData.spaces = sidebar.spaces;
this.log("Restored sidebar data into window", {
tabs: aWindowData.tabs?.length || 0,
groups: aWindowData.groups?.length || 0,
folders: aWindowData.folders?.length || 0,
spaces: aWindowData.spaces?.length || 0,
});
}
/**

View File

@@ -16,6 +16,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
TabStateCache: "resource:///modules/sessionstore/TabStateCache.sys.mjs",
setTimeout: "resource://gre/modules/Timer.sys.mjs",
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
RunState: "resource:///modules/sessionstore/RunState.sys.mjs",
});
XPCOMUtils.defineLazyPreferenceGetter(lazy, "gWindowSyncEnabled", "zen.window-sync.enabled", true);
@@ -217,7 +218,8 @@ class nsZenWindowSync {
if (
!forcedSync &&
(hasUnsyncedArg ||
(typeof aWindow.arguments[0] === "string" &&
!aWindow.gZenWorkspaces.shouldHaveWorkspaces ||
(typeof aWindow.arguments?.[0] === "string" &&
aWindow.arguments.length > 1 &&
!!this.#browserWindowsList.length))
) {
@@ -734,12 +736,14 @@ class nsZenWindowSync {
aOurTab.ownerGlobal.gBrowser.swapBrowsersAndCloseOther(aOurTab, aOtherTab, false);
// Swap permanent keys
const ourPermanentKey = aOurTab.linkedBrowser.permanentKey;
const otherPermanentKey = aOtherTab.linkedBrowser.permanentKey;
aOurTab.linkedBrowser.permanentKey = otherPermanentKey;
aOtherTab.linkedBrowser.permanentKey = ourPermanentKey;
aOurTab.permanentKey = otherPermanentKey;
aOtherTab.permanentKey = ourPermanentKey;
if (!onClose) {
const ourPermanentKey = aOurTab.linkedBrowser.permanentKey;
const otherPermanentKey = aOtherTab.linkedBrowser.permanentKey;
aOurTab.linkedBrowser.permanentKey = otherPermanentKey;
aOtherTab.linkedBrowser.permanentKey = ourPermanentKey;
aOurTab.permanentKey = otherPermanentKey;
aOtherTab.permanentKey = ourPermanentKey;
}
// Since we are moving progress listeners around, there's a chance that we
// trigger a load while making the switch, and since we remove the previous
@@ -1257,37 +1261,45 @@ class nsZenWindowSync {
});
}
async on_focus(aEvent) {
on_focus(aEvent) {
if (typeof aEvent.target !== "object") {
return;
}
await this.#docShellSwitchPromise;
const window = Services.focus.activeWindow;
if (
!window?.gBrowser ||
this.#lastFocusedWindow?.deref() === window ||
window.closing ||
!window.toolbar.visible
!window.toolbar.visible ||
lazy.RunState.isQuitting
) {
return;
}
let promise = this.#docShellSwitchPromise;
this.#lastFocusedWindow = new WeakRef(window);
this.#lastSelectedTab = new WeakRef(window.gBrowser.selectedTab);
return (this.#docShellSwitchPromise = this.#onTabSwitchOrWindowFocus(window));
// eslint-disable-next-line no-async-promise-executor
this.#docShellSwitchPromise = new Promise(async (resolve) => {
await promise;
await this.#onTabSwitchOrWindowFocus(window);
resolve();
});
}
async on_TabSelect(aEvent) {
await this.#docShellSwitchPromise;
on_TabSelect(aEvent) {
const tab = aEvent.target;
if (this.#lastSelectedTab?.deref() === tab) {
return;
}
this.#lastSelectedTab = new WeakRef(tab);
const previousTab = aEvent.detail.previousTab;
return (this.#docShellSwitchPromise = this.#onTabSwitchOrWindowFocus(
aEvent.target.ownerGlobal,
previousTab
));
let promise = this.#docShellSwitchPromise;
// eslint-disable-next-line no-async-promise-executor
this.#docShellSwitchPromise = new Promise(async (resolve) => {
await promise;
await this.#onTabSwitchOrWindowFocus(tab.ownerGlobal, previousTab);
resolve();
});
}
on_SSWindowClosing(aEvent) {

View File

@@ -318,14 +318,15 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
(this._lastOpenedTab.getAttribute("zen-workspace-id") !==
draggedTab.getAttribute("zen-workspace-id") &&
!this._lastOpenedTab.hasAttribute("zen-essential") &&
!draggedTab.hasAttribute("zen-essential"))
!draggedTab.hasAttribute("zen-essential")) ||
this._lastOpenedTab.hasAttribute("zen-live-folder-item-id")
) {
this._lastOpenedTab = gBrowser.selectedTab;
}
if (!draggedTab || this._canDrop || this._hasAnimated || this.fakeBrowser) {
return;
}
if (draggedTab.splitView) {
if (draggedTab.splitView || draggedTab.hasAttribute("zen-live-folder-item-id")) {
return;
}
const currentView = this._data[this._lastOpenedTab.splitViewValue];
@@ -1262,7 +1263,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
if (group.tabs.length >= this.MAX_TABS) {
return;
}
if (gridTypeChange || !newTabsAdded) {
if (gridTypeChange && !newTabsAdded) {
// reset layout
group.gridType = gridType;
group.layoutTree = this.calculateLayoutTree(
@@ -1276,7 +1277,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
if (!group.tabs.includes(tab)) {
gBrowser.moveTabToExistingGroup(tab, this._getSplitViewGroup(tabs, groupFetchId));
group.tabs.push(tab);
this.addTabToSplit(tab, group.layoutTree);
this.addTabToSplit(tab, group.layoutTree, false);
tab.splitView = true;
}
}
@@ -1296,8 +1297,9 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
const allArePinned = tabs.every((tab) => tab.pinned);
const thereIsOnePinned = tabs.some((tab) => tab.pinned);
const thereIsOneEssential = tabs.some((tab) => tab.hasAttribute("zen-essential"));
const thereIsOneLiveFolder = tabs.some((tab) => tab.hasAttribute("zen-live-folder-item-id"));
if (thereIsOneEssential || (thereIsOnePinned && !allArePinned)) {
if (thereIsOneEssential || (thereIsOnePinned && !allArePinned) || thereIsOneLiveFolder) {
for (let i = 0; i < tabs.length; i++) {
const tab = tabs[i];
if (tab.pinned) {
@@ -1494,8 +1496,8 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
header.classList.add("zen-view-splitter-header");
const removeButton = document.createXULElement("toolbarbutton");
removeButton.classList.add("zen-tab-unsplit-button");
removeButton.addEventListener("click", () => {
this.removeTabFromSplit(container);
removeButton.addEventListener("click", (event) => {
this.removeTabFromSplit(event, container);
});
const rearrangeButton = document.createXULElement("toolbarbutton");
rearrangeButton.classList.add("zen-tab-rearrange-button");
@@ -1810,7 +1812,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
return;
}
const tabs = gBrowser.visibleTabs;
if (tabs.length < 2 || this.currentView >= 0) {
if (tabs.length < 2) {
return;
}
let nextTabIndex = tabs.indexOf(gBrowser.selectedTab) + 1;
@@ -1827,14 +1829,27 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
const selected_tabs = gBrowser.selectedTab.multiselected
? gBrowser.selectedTabs
: [gBrowser.selectedTab, tabs[nextTabIndex]];
// Check if tabs from split view they must be from the same group
if (this.currentView >= 0) {
const splitViewId = this._data[this.currentView].groupId;
const sameSplitView = selected_tabs.every(
(tab) => !tab?.group || tab.group.id === splitViewId
);
if (!sameSplitView) {
return;
}
}
this.splitTabs(selected_tabs, gridType);
}
/**
* @description removes the tab from the split
* @param {Event} event - The click event
* @param {Element} container - The container element
*/
removeTabFromSplit = (container) => {
removeTabFromSplit = (event, container) => {
const browser = container.querySelector("browser");
if (browser) {
const tab = gBrowser.getTabForBrowser(browser);
@@ -1844,8 +1859,10 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
if (groupIndex >= 0) {
this.removeTabFromGroup(tab, groupIndex, { forUnsplit: true });
}
gBrowser.selectedTab = tab;
tab._selected = true;
if (!event.shiftKey) {
gBrowser.selectedTab = tab;
tab._selected = true;
}
}
}
};

View File

@@ -8,13 +8,13 @@
%include zen-split-group.inc.css
#tabbrowser-tabpanels[zen-split-view='true'] {
#tabbrowser-tabpanels[zen-split-view="true"] {
display: flex;
flex-direction: row;
margin-top: calc(var(--zen-split-column-gap) * -1);
}
#tabbrowser-tabpanels[zen-split-view='true'] > *:not([zen-split='true']) {
#tabbrowser-tabpanels[zen-split-view="true"] > *:not([zen-split="true"]) {
flex: 0;
margin: 0;
}
@@ -25,11 +25,11 @@
display: none;
background-color: color-mix(in srgb, var(--zen-colors-secondary) 30%, transparent 70%);
}
#zen-splitview-dropzone[enabled='true'] {
#zen-splitview-dropzone[enabled="true"] {
display: initial;
}
#tabbrowser-tabpanels[zen-split-view='true'] > [zen-split='true'],
#tabbrowser-tabpanels[zen-split-view="true"] > [zen-split="true"],
#zen-splitview-dropzone {
flex: 1;
overflow: hidden;
@@ -40,7 +40,7 @@
}
}
.browserSidebarContainer[is-zen-split='true'],
.browserSidebarContainer[is-zen-split="true"],
#zen-splitview-dropzone {
position: absolute !important;
@@ -48,7 +48,7 @@
margin-bottom: 0;
margin-left: 0;
&.browserSidebarContainer:not([zen-split='true']) {
&.browserSidebarContainer:not([zen-split="true"]) {
margin-top: 0;
visibility: hidden;
}
@@ -58,7 +58,7 @@
margin-top: 0 !important;
}
#tabbrowser-tabpanels[zen-split-view='true']:not(.zen-split-view-no-transition):not([zen-split-resizing]) > [zen-split='true'] {
#tabbrowser-tabpanels[zen-split-view="true"]:not(.zen-split-view-no-transition):not([zen-split-resizing]) > [zen-split="true"] {
--zen-active-split-outline-color: light-dark(var(--zen-primary-color), var(--button-background-color-primary));
transition: inset 0.09s ease-out !important;
@@ -67,7 +67,7 @@
}
}
#tabbrowser-tabpanels[zen-split-view='true'] .browserSidebarContainer.deck-selected {
#tabbrowser-tabpanels[zen-split-view="true"] .browserSidebarContainer.deck-selected {
&:not(.zen-glance-overlay) {
outline: 2px solid var(--zen-active-split-outline-color) !important;
}
@@ -84,8 +84,8 @@
--zen-split-column-gap: calc(var(--zen-element-separation) + 1px);
}
#tabbrowser-tabbox[zen-split-view='true'] {
:root[zen-no-padding='true'] & {
#tabbrowser-tabbox[zen-split-view="true"] {
:root[zen-no-padding="true"] & {
--zen-element-separation: 8px;
}
margin-right: calc(-1 * var(--zen-split-column-gap));
@@ -121,7 +121,7 @@
pointer-events: all;
}
.zen-split-view-splitter[orient='horizontal'] {
.zen-split-view-splitter[orient="horizontal"] {
height: var(--zen-split-column-gap);
cursor: ns-resize;
}
@@ -152,9 +152,7 @@
border-top-right-radius: 0;
}
:root:not([inDOMFullscreen='true'])
.browserSidebarContainer:hover
.zen-view-splitter-header-container,
:root:not([inDOMFullscreen="true"]) .browserSidebarContainer:hover .zen-view-splitter-header-container,
.zen-view-splitter-header-container:hover {
pointer-events: all;
opacity: 1;
@@ -204,24 +202,24 @@
overflow: hidden;
will-change: width, margin-left;
&[side='top'],
&[side='bottom'] {
&[side="top"],
&[side="bottom"] {
width: 100%;
&[has-split-view='true'] {
&[has-split-view="true"] {
width: calc(100% - var(--zen-element-separation));
}
}
&[side='right'] {
&[side="right"] {
right: 0;
&[has-split-view='true'] {
&[has-split-view="true"] {
right: var(--zen-element-separation);
}
}
&[side='bottom'] {
&[side="bottom"] {
bottom: 0;
}
}
@@ -249,3 +247,10 @@
text-align: center;
}
}
zen-split-fake-tab {
border-radius: var(--border-radius-medium);
background-color: color-mix(in srgb, var(--button-background-color-primary), transparent 40%);
margin: var(--tab-block-margin);
flex: 1;
}

View File

@@ -6,6 +6,7 @@
tab-group[split-view-group] {
display: block;
position: relative;
@media (prefers-reduced-motion: no-preference) {
transition: var(--zen-tabbox-element-indent-transition);

View File

@@ -504,9 +504,11 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
});
document.getElementById("context_zen-edit-tab-icon").addEventListener("command", () => {
const tab = TabContextMenu.contextTab;
gZenEmojiPicker
.open(tab.iconImage, { emojiAsSVG: true })
.then((icon) => {
gZenEmojiPicker.open(tab.iconImage, {
emojiAsSVG: true,
closeOnSelect: false,
allowNone: Boolean(tab.zenStaticIcon),
onSelect: (icon) => {
if (icon) {
tab.zenStaticIcon = icon;
} else {
@@ -516,10 +518,8 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
lazy.TabStateCache.update(tab.permanentKey, {
image: null,
});
})
.catch((err) => {
console.error(err);
});
},
});
});
}

View File

@@ -1137,7 +1137,8 @@
position: absolute;
:root[zen-single-toolbar="true"] &:not(:empty) {
padding-top: 4px;
padding-top: 6px;
padding-bottom: 2px;
}
&[hidden="true"] {

View File

@@ -116,13 +116,13 @@ add_task(async function test_html_parsing_logic() {
<div class="IssueItem-module__defaultRepoContainer"><span>mozilla/zen</span><span>#101</span></div>
<a class="IssueItem-module__authorCreatedLink">UserA</a>
<div class="Title-module__container">Fix the login bug</div>
<a data-testid="issue-pr-title-link" href="issues/101"></a>
<a data-testid="issue-pr-title-link" href="/issues/101"></a>
</div>
<div>
<div class="IssueItem-module__defaultRepoContainer"><span>mozilla/zen</span><span>#102</span></div>
<a class="IssueItem-module__authorCreatedLink">UserB</a>
<div class="Title-module__container">Add dark mode</div>
<a data-testid="issue-pr-title-link" href="pull/102"></a>
<a data-testid="issue-pr-title-link" href="/pull/102"></a>
</div>
</body>
</html>

View File

@@ -13,10 +13,29 @@ function sleep(ms) {
}
describe("Zen Live Folder Scheduling", () => {
const INTERVAL = 250;
const INTERVAL_OFFSET = 50;
let instance;
let sandbox;
let mockManager;
function createInstance({ id, interval, lastFetched }) {
instance = new nsZenLiveFolderProvider({
id,
manager: mockManager,
state: {
interval,
lastFetched,
},
});
const fetchStub = sandbox.stub(instance, "fetchItems").resolves(["item1"]);
sandbox.stub(instance, "getMetadata").returns({});
return { fetchStub };
}
beforeEach(() => {
sandbox = sinon.createSandbox();
mockManager = {
@@ -33,51 +52,84 @@ describe("Zen Live Folder Scheduling", () => {
});
it("should fetch correctly at an interval", async () => {
const INTERVAL = 250;
instance = new nsZenLiveFolderProvider({
const { fetchStub } = createInstance({
id: "test-folder",
manager: mockManager,
state: {
interval: INTERVAL,
lastFetched: Date.now(),
},
interval: INTERVAL,
lastFetched: Date.now(),
});
const fetchStub = sandbox.stub(instance, "fetchItems").resolves(["item1"]);
sandbox.stub(instance, "getMetadata").returns({});
instance.start();
sinon.assert.notCalled(fetchStub);
await sleep(INTERVAL / 2);
sinon.assert.notCalled(fetchStub);
await sleep(INTERVAL * 2);
Assert.ok(fetchStub.callCount > 1, "Should have fetched more than once");
const startSpy = sandbox.spy(instance, "start");
await sleep(INTERVAL + INTERVAL_OFFSET);
Assert.equal(fetchStub.callCount, 1, "Should have fetched once after the first interval");
await sleep(INTERVAL + INTERVAL_OFFSET);
Assert.equal(fetchStub.callCount, 2, "Should have fetched 2 times");
Assert.deepEqual(
startSpy.firstCall.args,
[false],
"Start should have been called once with false"
);
await sleep(INTERVAL + INTERVAL_OFFSET);
Assert.equal(fetchStub.callCount, 3, "Should have fetched 3 times");
Assert.equal(startSpy.callCount, 1, "Start should not been called");
sinon.assert.called(mockManager.saveState);
sinon.assert.called(mockManager.onLiveFolderFetch);
});
it("should fetch immediately if overdue", async () => {
const INTERVAL = 500;
instance = new nsZenLiveFolderProvider({
const { fetchStub } = createInstance({
id: "test-folder-overdue",
manager: mockManager,
state: {
interval: INTERVAL,
lastFetched: Date.now() - 3600000,
},
interval: INTERVAL,
lastFetched: Date.now() - 3600000,
});
const fetchStub = sandbox.stub(instance, "fetchItems").resolves(["item1"]);
sandbox.stub(instance, "getMetadata").returns({});
instance.start();
await sleep(20);
await sleep(INTERVAL_OFFSET);
sinon.assert.calledOnce(fetchStub);
});
it("should fetch with correct offset", async () => {
const { fetchStub } = createInstance({
id: "test-folder-delay",
interval: INTERVAL,
lastFetched: Date.now() - INTERVAL / 2,
});
instance.start();
await sleep(INTERVAL / 2 + INTERVAL_OFFSET);
Assert.equal(fetchStub.callCount, 1, "Should have fetched once");
await sleep(INTERVAL + INTERVAL_OFFSET);
Assert.equal(fetchStub.callCount, 2, "Should have fetched once with normal interval");
});
it("should re-start the timer if interval was changed", async () => {
const { fetchStub } = createInstance({
id: "test-folder-interval-change",
interval: INTERVAL,
lastFetched: Date.now(),
});
instance.start();
sinon.assert.notCalled(fetchStub);
await sleep(INTERVAL + INTERVAL_OFFSET);
Assert.equal(fetchStub.callCount, 1, "Should have fetched once after the first interval");
const NEW_INTERVAL = 500;
instance.state.interval = NEW_INTERVAL;
instance.stop();
instance.start();
await sleep(NEW_INTERVAL + INTERVAL_OFFSET);
Assert.equal(fetchStub.callCount, 2, "Should have once after the new interval");
});
});

File diff suppressed because one or more lines are too long

View File

@@ -628,6 +628,7 @@
document.getElementById("zen-welcome-page-content").appendChild(anchor);
gZenThemePicker.panel.setAttribute("noautohide", "true");
gZenThemePicker.panel.setAttribute("consumeoutsideclicks", "false");
gZenThemePicker.panel.setAttribute("nonnativepopover", "true");
gZenThemePicker.panel.addEventListener(
"popupshowing",
() => {
@@ -646,6 +647,7 @@
async fadeOut() {
gZenThemePicker.panel.removeAttribute("noautohide");
gZenThemePicker.panel.removeAttribute("consumeoutsideclicks");
gZenThemePicker.panel.removeAttribute("nonnativepopover");
await animate(gZenThemePicker.panel, { opacity: [1, 0] });
gZenThemePicker.panel.hidePopup();
gZenThemePicker.panel.removeAttribute("style");

View File

@@ -181,8 +181,9 @@ export class nsZenThemePicker extends nsZenMultiWindowFeature {
const fromForm = event.explicitOriginalTarget?.classList?.contains(
"zen-workspace-creation-edit-theme-button"
);
const isRightSide = window.gZenVerticalTabsManager._prefsRightSide;
PanelMultiView.openPopup(this.panel, this.toolbox, {
position: "start_before",
position: isRightSide ? "topleft topright" : "topright topleft",
triggerEvent: event,
y: fromForm ? -160 : 0,
x: -10,

View File

@@ -218,23 +218,21 @@ class nsZenWorkspaceCreation extends MozXULElement {
}
onIconCommand(event) {
gZenEmojiPicker
.open(event.target)
.then(async (emoji) => {
const isSvg = emoji && emoji.endsWith(".svg");
gZenEmojiPicker.open(event.target, {
closeOnSelect: false,
onSelect: async (icon) => {
const isSvg = icon && icon.endsWith(".svg");
if (isSvg) {
this.inputIcon.label = "";
this.inputIcon.image = emoji;
this.inputIcon.image = icon;
this.inputIcon.setAttribute("has-svg-icon", "true");
} else {
this.inputIcon.image = "";
this.inputIcon.label = emoji || "";
this.inputIcon.label = icon || "";
this.inputIcon.removeAttribute("has-svg-icon");
}
})
.catch((error) => {
console.warn("Error changing workspace icon:", error);
});
},
});
}
set currentProfile(profile) {

View File

@@ -373,7 +373,7 @@ class nsZenWorkspaces {
const defaultSelectedContainer = this.workspaceElement(this.activeWorkspace)?.querySelector(
".zen-workspace-normal-tabs-section"
);
const pinnedContainer = this.workspaceElement(this.activeWorkspace).querySelector(
const pinnedContainer = this.workspaceElement(this.activeWorkspace)?.querySelector(
".zen-workspace-pinned-tabs-section"
);
// New profile with no workspaces does not have a default selected container
@@ -450,16 +450,13 @@ class nsZenWorkspaces {
workspaceWrapper.hidden = true; // Hide workspace while creating it
}
container.appendChild(workspaceWrapper);
this.#organizeTabsToWorkspaceSections(
workspace,
workspaceWrapper.tabsContainer,
workspaceWrapper.pinnedTabsContainer,
tabs
);
this.#organizeTabsToWorkspaceSections(workspace, workspaceWrapper, tabs);
workspaceWrapper.checkPinsExistence();
}
#organizeTabsToWorkspaceSections(workspace, section, pinnedSection, tabs) {
#organizeTabsToWorkspaceSections(workspace, spaceElement, tabs) {
let section = spaceElement.tabsContainer;
let pinnedSection = spaceElement.pinnedTabsContainer;
const workspaceTabs = Array.from(tabs).filter(
(tab) =>
tab.getAttribute("zen-workspace-id") === workspace.uuid &&
@@ -781,7 +778,6 @@ class nsZenWorkspaces {
this._shouldHaveWorkspaces =
chromeFlags & Ci.nsIWebBrowserChrome.CHROME_TOOLBAR ||
chromeFlags & Ci.nsIWebBrowserChrome.CHROME_MENUBAR;
return this._shouldHaveWorkspaces;
}
return this._shouldHaveWorkspaces && !document.documentElement.hasAttribute("taskbartab");
}
@@ -899,7 +895,9 @@ class nsZenWorkspaces {
// We don't want to depend on this by mistake
delete workspace.hasCollapsedPinnedTabs;
}
this.#hasInitialized = true;
promise.finally(() => {
this.#hasInitialized = true;
});
return promise;
}
@@ -1062,6 +1060,7 @@ class nsZenWorkspaces {
}
// note: We cant access `gZenVerticalTabsManager._canReplaceNewTab` this early
if (isEmpty && Services.prefs.getBoolPref("zen.urlbar.replace-newtab", true)) {
tab._markedForReplacement = true;
this._tabToRemoveForEmpty = tab;
} else {
this._initialTab = tab;
@@ -1085,23 +1084,19 @@ class nsZenWorkspaces {
if (hasNoIcon) {
anchor.textContent = "";
}
gZenEmojiPicker
.open(anchor)
.then(async (emoji) => {
gZenEmojiPicker.open(anchor, {
closeOnSelect: false,
allowNone: hasNoIcon,
onSelect: async (icon) => {
const workspace = this.getWorkspaceFromId(workspaceId);
if (!workspace) {
console.warn("No active workspace found to change icon");
return;
}
workspace.icon = emoji;
workspace.icon = icon;
await this.saveWorkspace(workspace);
})
.catch((error) => {
console.warn("Error changing workspace icon:", error);
if (hasNoIcon) {
anchor.setAttribute("no-icon", "true");
}
});
},
});
}
shouldCloseWindow() {
@@ -1609,7 +1604,7 @@ class nsZenWorkspaces {
this.#inChangingWorkspace = true;
try {
this.log("Changing workspace to", workspace?.uuid);
await this._performWorkspaceChange(workspace, ...args);
await this.#performWorkspaceChange(workspace, ...args);
} catch (e) {
console.error("gZenWorkspaces: Error changing workspace", e);
}
@@ -1620,7 +1615,7 @@ class nsZenWorkspaces {
this._animateTabs(this.getActiveWorkspaceFromCache(), true);
}
async _performWorkspaceChange(
async #performWorkspaceChange(
workspace,
{ onInit = false, alwaysChange = false, whileScrolling = false } = {}
) {
@@ -1661,7 +1656,7 @@ class nsZenWorkspaces {
// Update UI and state
const previousWorkspaceIndex = workspaces.findIndex((w) => w.uuid === previousWorkspace.uuid);
await this._updateWorkspaceState(workspace, onInit, tabToSelect, {
await this.#updateWorkspaceState(workspace, onInit, tabToSelect, {
previousWorkspaceIndex,
previousWorkspace,
});
@@ -2027,8 +2022,10 @@ class nsZenWorkspaces {
Math.abs(newWorkspaceIndex - (isGoingLeft ? firstWorkspaceIndex : lastWorkspaceIndex)) +
1;
const usingSameContainer =
newWorkspaceEssentialsContainer.workspaces.some((w) => w.uuid === newWorkspace.uuid) &&
newWorkspaceEssentialsContainer.workspaces.some((w) => w.uuid === previousWorkspace.uuid);
newWorkspaceEssentialsContainer?.workspaces.some((w) => w.uuid === newWorkspace.uuid) &&
newWorkspaceEssentialsContainer?.workspaces.some(
(w) => w.uuid === previousWorkspace.uuid
);
let newOffset =
-(
newWorkspaceIndex -
@@ -2266,7 +2263,7 @@ class nsZenWorkspaces {
return tabToSelect;
}
async _updateWorkspaceState(
async #updateWorkspaceState(
workspace,
onInit,
tabToSelect,

View File

@@ -266,7 +266,6 @@
}
cursor: pointer;
border: 3px solid #ffffff;
animation: zen-theme-picker-dot-animation 0.5s;
transform: translate(-50%, -50%);
pointer-events: none;
transform-origin: left top;

View File

@@ -46,7 +46,11 @@
pointer-events: none;
line-height: 0;
position: absolute;
font-size: 14px;
font-size: 12px;
@media (-moz-platform: macos) {
font-size: 14px;
}
&:is(img) {
width: 14px;
@@ -61,7 +65,7 @@
}
filter: grayscale(1);
opacity: 0.5;
opacity: 0.7;
transition:
filter 0.2s,
opacity 0.2s,

View File

@@ -5,7 +5,7 @@
"binaryName": "zen",
"version": {
"product": "firefox",
"version": "147.0.4",
"version": "148.0",
"candidate": "148.0",
"candidateBuild": 1
},
@@ -20,7 +20,7 @@
"brandShortName": "Zen",
"brandFullName": "Zen Browser",
"release": {
"displayVersion": "1.18.10b",
"displayVersion": "1.19.1b",
"github": {
"repo": "zen-browser/desktop"
},
@@ -40,7 +40,7 @@
"brandShortName": "Twilight",
"brandFullName": "Zen Twilight",
"release": {
"displayVersion": "1.19t",
"displayVersion": "1.20t",
"github": {
"repo": "zen-browser/desktop"
}