mirror of
https://github.com/zen-browser/desktop.git
synced 2026-05-27 15:25:09 +00:00
Compare commits
80 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
34c2618ca0 | ||
|
|
84b7cf8ddd | ||
|
|
0d816b3cc2 | ||
|
|
ad4eeee55a | ||
|
|
ad74b55dbf | ||
|
|
5ffb2d8d69 | ||
|
|
5a91c0c70b | ||
|
|
3831af027e | ||
|
|
ee5c1894eb | ||
|
|
e31d91282b | ||
|
|
32b355595c | ||
|
|
b5b31c02d0 | ||
|
|
bcdb905ad6 | ||
|
|
3044b409fa | ||
|
|
747dd3d4bc | ||
|
|
49225cf685 | ||
|
|
0f4abf2237 | ||
|
|
657d03821f | ||
|
|
8fc2ecbb66 | ||
|
|
d78dcdddaf | ||
|
|
9bc0a4ca92 | ||
|
|
6aefade7f8 | ||
|
|
0226c5879a | ||
|
|
ee3ec4c1ea | ||
|
|
881c411a26 | ||
|
|
96a52b7354 | ||
|
|
f52eeb0d60 | ||
|
|
942a266184 | ||
|
|
1a1804d2be | ||
|
|
22fd6133f1 | ||
|
|
16b898f4e0 | ||
|
|
d67b4457e8 | ||
|
|
b54a7027fd | ||
|
|
72a35a5ea5 | ||
|
|
c9cad08ae7 | ||
|
|
b563e06527 | ||
|
|
88a647a3bb | ||
|
|
5a9a9ce51a | ||
|
|
0a5a6d0604 | ||
|
|
b92d697657 | ||
|
|
5e27368a48 | ||
|
|
2c740b1abf | ||
|
|
bae234867e | ||
|
|
9145e36457 | ||
|
|
e30f7e20a6 | ||
|
|
726f6e9132 | ||
|
|
43384e54e7 | ||
|
|
4ee2e49b27 | ||
|
|
a5a984922b | ||
|
|
34af405cbd | ||
|
|
648e0b1683 | ||
|
|
4ae9f81a68 | ||
|
|
94a55c73c6 | ||
|
|
a7c87e6392 | ||
|
|
bdf8bc6b33 | ||
|
|
4e3413fea5 | ||
|
|
5714450b60 | ||
|
|
2758c962b5 | ||
|
|
7b9bdec209 | ||
|
|
443a778ea6 | ||
|
|
29165bb1e2 | ||
|
|
1554818aa8 | ||
|
|
353b65e25b | ||
|
|
a8ccd1ca3c | ||
|
|
c772f6d795 | ||
|
|
e5622d2237 | ||
|
|
250d0c641c | ||
|
|
529d557a38 | ||
|
|
bbaf779e7a | ||
|
|
e59ff43490 | ||
|
|
ca27b4d76b | ||
|
|
bd43b86de8 | ||
|
|
d3d50d0f8c | ||
|
|
dd92699ca1 | ||
|
|
4f7a1efdfa | ||
|
|
08226c518c | ||
|
|
e919e723c7 | ||
|
|
52d03f43ef | ||
|
|
5ed59fb902 | ||
|
|
011fd67248 |
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
@@ -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: |
|
||||
|
||||
4
.github/workflows/code-linter.yml
vendored
4
.github/workflows/code-linter.yml
vendored
@@ -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
|
||||
|
||||
4
.github/workflows/linux-release-build.yml
vendored
4
.github/workflows/linux-release-build.yml
vendored
@@ -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: |
|
||||
|
||||
4
.github/workflows/macos-release-build.yml
vendored
4
.github/workflows/macos-release-build.yml
vendored
@@ -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: |
|
||||
|
||||
@@ -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: |
|
||||
|
||||
10
.github/workflows/pr-test.yml
vendored
10
.github/workflows/pr-test.yml
vendored
@@ -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
|
||||
|
||||
82
.github/workflows/sync-external-patches.yml
vendored
Normal file
82
.github/workflows/sync-external-patches.yml
vendored
Normal 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
|
||||
9
.github/workflows/sync-upstream.yml
vendored
9
.github/workflows/sync-upstream.yml
vendored
@@ -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'
|
||||
|
||||
@@ -2,7 +2,7 @@ name: Zen Twilight Scheduled Releases
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 23 * * *"
|
||||
- cron: "0 23 */2 * *"
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
create_release:
|
||||
|
||||
4
.github/workflows/windows-profile-build.yml
vendored
4
.github/workflows/windows-profile-build.yml
vendored
@@ -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
|
||||
|
||||
4
.github/workflows/windows-release-build.yml
vendored
4
.github/workflows/windows-release-build.yml
vendored
@@ -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: |
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1 +1 @@
|
||||
b7d21837fa8fcae98dab2a4f016cc9fb3053903d
|
||||
40d4c2395112d4188721e69d338ee75bded8858a
|
||||
@@ -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.*
|
||||
|
||||
@@ -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) {
|
||||
|
||||
217
docs/issue-metrics/2026_2026-02-01..2026-02-28.md
Normal file
217
docs/issue-metrics/2026_2026-02-01..2026-02-28.md
Normal 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`
|
||||
@@ -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
|
||||
|
||||
@@ -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
22
package-lock.json
generated
@@ -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": {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)"
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
|
||||
- name: image.jxl.enabled
|
||||
value: true
|
||||
locked: true
|
||||
|
||||
- name: svg.context-properties.content.enabled
|
||||
value: true
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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')}")
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
@@ -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],
|
||||
- },
|
||||
- ],
|
||||
- },
|
||||
],
|
||||
},
|
||||
],
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
@@ -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",
|
||||
]),
|
||||
],
|
||||
@@ -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: >
|
||||
@@ -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
|
||||
|
||||
677
src/external-patches/firefox/native_macos_popovers.patch
Normal file
677
src/external-patches/firefox/native_macos_popovers.patch
Normal 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"),
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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.
|
||||
@@ -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),
|
||||
@@ -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
|
||||
@@ -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) {
|
||||
@@ -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",
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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)"],
|
||||
|
||||
@@ -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%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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'] {
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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] {
|
||||
|
||||
@@ -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 = "";
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1137,7 +1137,8 @@
|
||||
position: absolute;
|
||||
|
||||
:root[zen-single-toolbar="true"] &:not(:empty) {
|
||||
padding-top: 4px;
|
||||
padding-top: 6px;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
&[hidden="true"] {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
});
|
||||
|
||||
2
src/zen/vendor/motion.min.mjs
vendored
2
src/zen/vendor/motion.min.mjs
vendored
File diff suppressed because one or more lines are too long
@@ -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");
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user