Compare commits

..

88 Commits

Author SHA1 Message Date
mr. m
15a908739d feat: Add simple routing class, b=no-bug, c=no-component 2026-02-10 12:48:58 +01:00
mr. m
f842c945ce feat: Initial commit, b=no-bug, c=no-component 2026-02-10 12:31:33 +01:00
mr. m
4d1d4ab1b6 feat: Add pref to force synced windows to open on DnD, b=no-bug, c=no-component 2026-02-10 11:50:23 +01:00
mr. m
2990de8366 fix: Fixed wrong capitalized text for MacOS menu bar, b=closes #12213, c=common 2026-02-09 23:38:13 +01:00
mr. m
76966ee1c3 feat: Make sure to always restore tabs from a crash, b=no-bug, c=no-component 2026-02-09 23:14:51 +01:00
mr. m
4f9a932e77 fix: Fixed unpinned tabs not being cleared when the pref is off, b=closes #12307, c=no-component 2026-02-09 11:37:10 +01:00
mr. m
f2603521e5 chore: Update external patches, b=no-bug, c=folders 2026-02-09 11:34:02 +01:00
mr. m
24f695c452 Merge branch 'dev' of https://github.com/zen-browser/desktop into dev 2026-02-09 09:02:55 +01:00
mr. m
f5effd4dcd feat: Add preference to focus on urlbar on startup, b=no-bug, c=workspaces 2026-02-09 09:02:39 +01:00
Mr. M
b5bb7d7c8b feat: Make sure to use the proper parent reference for XDG paths, b=bug #11917, c=no-component 2026-02-08 20:07:14 +01:00
mr. m
604365dd38 chore: Update external mochitests, b=no-bug, c=tests, scripts, common 2026-02-08 19:20:36 +01:00
TogiFerretFerret
466d089fc0 feat: Add zen identification info, b=closes #12198, p=#12295
* feat: add zen identification to extension runtime for zen-specific features

* chore: add branding to nightly to assist with certain testing (e.g. browser identification)

* test: assert zen info in runtime.getBrowserInfo

* Revert "test: assert zen info in runtime.getBrowserInfo"

This reverts commit 94cfeff29f as the tests fail even without this commit.

* Discard changes to surfer.json

* chore: remove platform from getBrowserInfo (as requested)

---------

Co-authored-by: mr. m <91018726+mr-cheffy@users.noreply.github.com>
2026-02-08 17:48:04 +01:00
Roman
e28a10f6a9 fix: Use optional chaining and nullish coalescing in resetButton, p=#12300
Signed-off-by: Roman <58283566+rmdeuce@users.noreply.github.com>
2026-02-08 13:15:57 +01:00
mr. m
102be6cd90 fix: Remove psueod hidden browser background on close, b=no-bug, c=no-component 2026-02-08 13:15:42 +01:00
mr. m
6e1e1d061b fix: Fixed clicking on the 'add' button not closing the panel, b=no-bug, c=common, workspaces 2026-02-08 11:19:46 +01:00
mr. m
43250d6d37 fix: Fixed bookmarks not being synced with mozilla account, b=bug #12133, c=workspaces 2026-02-08 10:48:12 +01:00
mr. m
12f0c455d1 fix: Fixed 'restore previous tabs' ignored when only pinned sync is enabled, b=closes #12297, c=tabs 2026-02-08 10:35:05 +01:00
Andrey Bochkarev
049c3c6f54 feat: Implement vertical dnd tab splitting, p=#12289 2026-02-07 23:59:28 +01:00
Samuel Akhaze
658ac94334 fix: Fixed Incorrect (Reset pinned tab/Replace pinned url) Text on Essential tab context menu, p=#12283 2026-02-07 23:59:15 +01:00
mr. m
09a90099c7 Merge branch 'dev' of https://github.com/zen-browser/desktop into dev 2026-02-07 23:52:51 +01:00
mr. m
051470f139 feat: Small polishing changes, b=no-bug, c=common, workspaces 2026-02-07 21:25:24 +01:00
dependabot[bot]
8f1cb88c11 chore(deps-dev): bump lodash, p=#12292
Bumps the npm_and_yarn group with 1 update in the / directory: [lodash](https://github.com/lodash/lodash).


Updates `lodash` from 4.17.21 to 4.17.23
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.21...4.17.23)

---
updated-dependencies:
- dependency-name: lodash
  dependency-version: 4.17.23
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-02-07 18:49:21 +01:00
mr. m
3ef233a4c2 Merge branch 'dev' of https://github.com/zen-browser/desktop into dev 2026-02-07 18:48:46 +01:00
mr. m
38fcd7e872 fix: Fixed private windows closing when all tabs are cleared, b=closes #12242, c=common, folders, tabs, workspaces 2026-02-07 18:47:06 +01:00
mr. m
76f17c3a57 chore: Automatically import patches from phabricator and librewolf, p=#12271
* chore: Automatically import patches from phabricator and librewolf, b=no-bug, c=workflows, scripts

Signed-off-by: mr. m <91018726+mr-cheffy@users.noreply.github.com>

* feat: Remove legacy flag, b=no-bug, c=common, configs

---------
2026-02-07 18:02:40 +01:00
mr. m
b5c2451525 fix: Fixed overflowing addons not opening the panel at the correct anchor, b=no-bug, c=common 2026-02-07 16:51:44 +01:00
mr. m
ba2a854784 feat: Overflow web extensions below the urlbar, p=#12273
* feat: Overflow web extensions below the urlbar, b=no-bug, c=common

* chore: Ignore toolbar CSS, b=no-bug, c=no-component
2026-02-06 22:56:11 +01:00
mr. m
c9474cecc0 feat: Remove null-setting for split view data on restore, b=no-bug, c=no-component 2026-02-06 14:10:02 +01:00
mr. m
531be839c9 fix: Fixed frozen window when restore split data is null, b=closes #12260, c=no-component 2026-02-06 14:04:04 +01:00
mr. m
226486b973 fix: Fixed selecting tabs when pinned tabs are collapsed, b=no-bug, c=no-component 2026-02-06 12:45:24 +01:00
mr. m
5a137c95e5 fix: Fixed wrong highlight possition in compact mode, b=closes #12252, c=compact-mode 2026-02-05 23:23:17 +01:00
mr. m
e6154ceb99 feat: Make sure we properly restore split views when only pinned sync is enabled, b=no-bug, c=no-component 2026-02-05 23:01:49 +01:00
mr. m
1ea5d2fa6f feat: Import zen.window-sync.sync-only-pinned-tabs to the prefs list, b=no-bug, c=no-component 2026-02-05 11:18:02 +01:00
mr. m
97d5ebc21e feat: Add pref observer for zen.window-sync.sync-only-pinned-tabs setting, b=no-bug, c=no-component 2026-02-05 11:14:48 +01:00
mr. m
442ec2a5d1 chore: Bump version and format, b=no-bug, c=tests 2026-02-05 11:13:02 +01:00
mr. m
43ae672b44 feat: Revert migration step for syncing only pinned tabs, b=no-bug, c=common 2026-02-05 11:12:01 +01:00
mr. m
84a54c9dbb feat: Add option to only sync up pinned tabs, b=closes #12173, p=#12246, c=common, tabs 2026-02-05 11:05:37 +01:00
mr. m
6c53c3bf37 chore: Sync upstream Firefox to version 147.0.3, p=#12243
* chore: Sync upstream to `Firefox 147.0.3`

* Update candidate version to 147.0.3

Signed-off-by: mr. m <91018726+mr-cheffy@users.noreply.github.com>

* Update Twilight version to RC 147.0.3 in README

Signed-off-by: mr. m <91018726+mr-cheffy@users.noreply.github.com>

---------

Signed-off-by: mr. m <91018726+mr-cheffy@users.noreply.github.com>
2026-02-05 11:04:53 +01:00
mr. m
66686ab7f8 New Crowdin updates, p=#11954
* New translations zen-general.ftl (Lithuanian)

* New translations zen-general.ftl (Portuguese)

* New translations zen-preferences.ftl (Lithuanian)

* New translations zen-preferences.ftl (Swedish)

* New translations zen-preferences.ftl (Chinese Traditional)

* New translations zen-preferences.ftl (Welsh)

* New translations zen-menubar.ftl (French)

* New translations zen-menubar.ftl (Catalan)

* New translations zen-menubar.ftl (Hebrew)

* New translations zen-menubar.ftl (Hungarian)

* New translations zen-menubar.ftl (Korean)

* New translations zen-menubar.ftl (Lithuanian)

* New translations zen-menubar.ftl (Portuguese)

* New translations zen-menubar.ftl (Swedish)

* New translations zen-menubar.ftl (Turkish)

* New translations zen-menubar.ftl (Chinese Traditional)

* New translations zen-menubar.ftl (Icelandic)

* New translations zen-menubar.ftl (Portuguese, Brazilian)

* New translations zen-menubar.ftl (Welsh)

* New translations zen-preferences.ftl (Catalan)

* New translations zen-preferences.ftl (Russian)

* New translations zen-preferences.ftl (Turkish)

* New translations zen-general.ftl (Indonesian)

* New translations zen-workspaces.ftl (Chinese Traditional)

* New translations zen-menubar.ftl (Chinese Traditional)

* New translations zen-menubar.ftl (Indonesian)

* New translations zen-workspaces.ftl (Indonesian)

* New translations zen-preferences.ftl (Indonesian)

* New translations zen-general.ftl (Welsh)

* New translations zen-menubar.ftl (French)

* New translations zen-menubar.ftl (Irish)
2026-02-05 11:00:01 +01:00
mr. m
0c684a9bdb fix: Fixed drag and dropping folders into different spaces, b=no-bug, c=tabs 2026-02-03 20:51:52 +01:00
mr. m
26dabffb96 feat: Use the correct drag event to detect window enters, b=no-bug, c=no-component 2026-02-03 00:44:36 +01:00
mr. m
ead077d16e fix: Fixed private windows adopting Blank windows styling, b=closes #12211, c=no-component 2026-02-02 18:23:25 +01:00
mr. m
a72fa3f79e Merge branch 'dev' of https://github.com/zen-browser/desktop into dev 2026-02-02 14:57:49 +01:00
mr. m
33846dbce0 fix: Fixed drag and drop not calculating window bounds correctly, b=closes #12156, closes https://github.com/zen-browser/desktop/issues/11582, closes https://github.com/zen-browser/desktop/issues/12204, c=tabs 2026-02-02 14:57:24 +01:00
mr. m
32fffaabe7 fix: Dont allow essentials promo to be shown on unsynced windows, b=closes #12205, c=tabs 2026-02-02 12:41:42 +01:00
mr. m
bf96b512cb Merge branch 'dev' of https://github.com/zen-browser/desktop into dev 2026-02-02 01:10:55 +01:00
mr. m
32a42e797c feat: Fixed split view not working on windows when dropping, b=closes #12143, c=no-component 2026-02-02 01:10:48 +01:00
mr-cheffy
29b8555c66 docs: Update monthly issue metrics, b=(no bug), c={docs} 2026-02-01 03:13:23 +00:00
mr. m
ab7698b16f feat: Make sure to reset pinned changed url on unpin, b=no-bug, c=tabs 2026-01-31 20:03:13 +01:00
mr. m
cd0ef01fa1 Merge branch 'dev' of https://github.com/zen-browser/desktop into dev 2026-01-31 18:49:26 +01:00
mr. m
41d97ddd21 fix: Dont update the spaces menu for GTK, b=closes #12024, c=common 2026-01-31 18:34:55 +01:00
mr. m
a4386de00d fix: Fixed dragging tabs to different windows causing freeze, b=closes #12178, c=tabs 2026-01-31 18:29:37 +01:00
rain capsule
28a2e8697a fix: Update notification popup icon styles to account for showing state, p=#12033 2026-01-31 11:33:50 +01:00
TogiFerretFerret
2ae623b15c fix(split-view): correct backwards navigation in grid layout, p=#12160 2026-01-30 17:58:07 +01:00
TogiFerretFerret
8e7ce1c12b fix(ui): prevent closeTabByDblclick pref from blocking context menu rename, b=https://github.com/zen-browser/desktop/issues/12060, p=#12162 2026-01-30 17:50:11 +01:00
mr. m
b5efe62739 fix: Make sure drop indicator is under the last item when newtab button is below, b=no-bug, c=common 2026-01-29 11:05:43 +01:00
mr. m
e34ed3e882 feat: Fixed unpinning an essential tab directly from shortcuts, b=no-bug, c=folders, split-view, tabs, workspaces 2026-01-29 07:39:09 +01:00
mr. m
658e35ebde feat: Add more migration options, b=no-bug, c=no-component 2026-01-28 20:45:45 +01:00
mr. m
1176f5ac8b fix: Fixed auto focus not working on popup windows, b=closes #11377, c=common, workspaces 2026-01-28 12:12:52 +01:00
mr. m
4de01a9bc9 fix: Fixed drag-and-dropping tabs between windows not working with WS off, b=closes #12103, c=no-component 2026-01-28 10:31:19 +01:00
mr. m
35a9a9ea2a fix: Fixed low accesibility on text on sidenar, b=closes #12095, c=tests, workspaces 2026-01-27 23:53:25 +01:00
TogiFerretFerret
e78e2488ff fix(sync): prevent window shrinking by resetting scale/size in pseudo-image, p=#12082 2026-01-27 16:15:28 +01:00
mr. m
1c23ad92b3 Sync upstream Firefox to version 147.0.2, p=#12079 2026-01-27 15:03:38 +01:00
mr. m
32ba6ef81b fix: Fixed small QA issues, b=no-bug, c=common, compact-mode, split-view, tabs, tests 2026-01-27 14:35:27 +01:00
mr. m
a8559f84c1 Merge branch 'dev' of https://github.com/zen-browser/desktop into dev 2026-01-27 11:59:10 +01:00
mr. m
0e07f65bc0 feat: Add select-recently-used-on-close to the preferences page, b=no-bug, c=no-component 2026-01-27 11:59:04 +01:00
mr. m
4046dd01a1 chore: Sync upstream to Firefox 147.0.2, p=#12067 2026-01-27 09:00:57 +01:00
mr. m
e51592898e fix: Fixed restoring pinned tab to base url on startup not working, b=closes #12055, c=tabs 2026-01-26 18:42:54 +01:00
mr. m
9afbd1befd feat: Make sure to run a migration for deferred sessions as well, b=no-bug, c=tabs, workspaces 2026-01-26 17:24:12 +01:00
mr. m
ccc1e998da fix: Fixed 'Restore previous session' not working winth wsync disabled, b=closes #12034, c=common 2026-01-26 09:43:31 +01:00
mr. m
3a51b36a05 feat: Add debug log for last session state object, b=no-bug, c=no-component 2026-01-26 02:41:56 +01:00
mr. m
e05cb0e0dc feat: Make inspector shortcut use L key by default, b=no-bug, c=kbs 2026-01-26 02:20:20 +01:00
mr. m
a892721306 fix: Fixed dropping tabs into the same instance, b=closes #12032, c=no-component 2026-01-26 01:47:54 +01:00
mr. m
950f6dfb66 Revert "feat: Implement new wizzard watermarks, b=no-bug, c=configs"
This reverts commit 956841a76e.
2026-01-26 01:47:28 +01:00
mr. m
9b6937650d chore: Format project, b=no-bug, c=split-view 2026-01-25 20:02:16 +01:00
mr. m
2c63376782 fix: Fixed handling split data that may be undefined, b=no-bug, c=split-view 2026-01-25 19:58:18 +01:00
mr. m
f5356dbcb6 feat: Hide essentials visibility on the same thread, b=no-bug, c=no-component 2026-01-25 19:35:03 +01:00
mr. m
45ecacfb04 feat: Make sure to prioritize last session state before closed windows, b=no-bug, c=no-component 2026-01-25 18:23:07 +01:00
mr. m
4327684f9f feat: Make sure focus event only accepts the window object, b=no-bug, c=no-component 2026-01-25 18:10:26 +01:00
mr. m
8997a24996 fix: Fixed migration not working for previous states, b=bug #11994, c=no-component 2026-01-25 18:07:25 +01:00
mr. m
5a6a565b0f Merge branch 'dev' of https://github.com/zen-browser/desktop into dev 2026-01-25 17:44:08 +01:00
mr. m
d39fff2e4a fix: Fixed dimmed text color for linux, b=no-bug, c=glance, workspaces 2026-01-25 17:43:47 +01:00
Andrey Bochkarev
ab12e72cc5 fix: Prevent sidebar from flickering when moving a tab, p=#12016 2026-01-25 14:57:12 +01:00
mr. m
8a2e015048 fix: Fixed dragging split views into blank windows, b=no-bug, c=no-component 2026-01-25 12:26:26 +01:00
mr. m
83de087b0b feat: Dont bring unpinned tabs to new windows if window sync is disabled, b=no-bug, c=no-component 2026-01-25 12:22:53 +01:00
mr. m
5dea28a3cb feat: Fixed windows not initializing when window sync is disabled, b=closes #11999, p=#12015 2026-01-25 12:12:56 +01:00
mr. m
956841a76e feat: Implement new wizzard watermarks, b=no-bug, c=configs 2026-01-25 09:51:17 +01:00
mr. m
3f03ffb27b fix: Fixed Inconsistant capitalization in blank window locales, b=closes #12009, c=kbs 2026-01-25 00:05:49 +01:00
101 changed files with 1782 additions and 744 deletions

View File

@@ -73,15 +73,13 @@ jobs:
npm run sync
fi
- name: Run formatter
if: steps.check-upstream-branch.outputs.branch_exists == 'false'
run: |
sudo apt install python3-autopep8
npm run pretty
- name: Install autopep8
run: sudo apt install python3-autopep8
- name: Check if any files changed
id: git-check
run: |
npm run pretty
if [ -n "$(git status --porcelain)" ]; then
echo "files_changed=true" >> $GITHUB_OUTPUT
else
@@ -111,6 +109,15 @@ jobs:
if: steps.git-check.outputs.files_changed == 'true'
run: python3 scripts/import_external_tests.py || true
- name: Import external patches
if: steps.git-check.outputs.files_changed == 'true'
run: python3 scripts/import_external_patches.py || true
- name: Run formatter
if: steps.check-upstream-branch.outputs.branch_exists == 'false'
run: |
npm run pretty
- name: Create pull request
uses: peter-evans/create-pull-request@v7
if: steps.git-check.outputs.files_changed == 'true'

View File

@@ -37,5 +37,6 @@ src/zen/common/ZenEmojis.mjs
src/zen/split-view/zen-decks.css
src/zen/workspaces/zen-workspaces.css
src/zen/common/styles/zen-toolbar.css
*.inc

View File

@@ -34,8 +34,8 @@ Zen is a firefox-based browser with the aim of pushing your productivity to a ne
### Firefox Versions
- [`Release`](https://zen-browser.app/download) - Is currently built using Firefox version `147.0.1`! 🚀
- [`Twilight`](https://zen-browser.app/download?twilight) - Is currently built using Firefox version `RC 147.0.1`!
- [`Release`](https://zen-browser.app/download) - Is currently built using Firefox version `147.0.3`! 🚀
- [`Twilight`](https://zen-browser.app/download?twilight) - Is currently built using Firefox version `RC 147.0.3`!
### Contributing

View File

@@ -1 +1 @@
783b222b1b93fb9e8c52eb57558ef7f07bca6e7e
b1e00b74d3791a5a7bbd6ba3d32b69b60471e73b

View File

@@ -9,9 +9,6 @@ ac_add_options --with-app-basename=Zen
# Localization (Must be an absolute path)
ac_add_options --with-l10n-base="${topsrcdir}/browser/locales"
# See https://github.com/zen-browser/desktop/issues/11917 for future plans.
# We should be removing this at some point and start supporting XDG dirs.
ac_add_options --with-user-appdir=".${binName}"
export MOZ_APP_BASENAME=Zen
export MOZ_BRANDING_DIRECTORY=${brandingDir}
export MOZ_OFFICIAL_BRANDING_DIRECTORY=${brandingDir}
@@ -85,6 +82,11 @@ if test "$ZEN_RELEASE"; then
export MOZ_PACKAGE_JSSHELL=1
ac_add_options --disable-crashreporter
# Experimental flag, enabled only on nightly for Firefox.
# Should bring in some nice performance improvements,
# but may cause stability issues.
ac_add_options --enable-replace-malloc
fi
ac_add_options --enable-unverified-updates

View File

@@ -0,0 +1,262 @@
# Issue Metrics
| Metric | Average | Median | 90th percentile |
| --- | --- | --- | ---: |
| Time to first response | 14:19:40 | 2:07:00 | 1 day, 4:29:24 |
| Time to close | 23:19:13 | 6:44:05 | 2 days, 5:35:23 |
| Metric | Count |
| --- | ---: |
| Number of items that remain open | 103 |
| Number of items closed | 141 |
| Total number of items created | 244 |
| Title | URL | Time to first response | Time to close |
| --- | --- | --- | --- |
| Filling in the search, query does not work with Ecosia | https://github.com/zen-browser/desktop/issues/12186 | None | None |
| Workspaces are not synchronized between Zen on Windows11 and macOS. | https://github.com/zen-browser/desktop/issues/12184 | 0:13:05 | 0:13:05 |
| the 1st 2 workspaces aren't visible in the workspace bar at the bottom (SOLVED) | https://github.com/zen-browser/desktop/issues/12182 | None | 0:52:19 |
| Broken web player and descript as part of page | https://github.com/zen-browser/desktop/issues/12181 | None | None |
| Websites having wrong icons in history panel | https://github.com/zen-browser/desktop/issues/12179 | 4:54:50 | None |
| Dragging and Dropping Tabs between Windows causes weird issues with Tab Sync enabled | https://github.com/zen-browser/desktop/issues/12178 | None | 16:59:50 |
| Incorrect Text - Resetting an Essential Tab should be called "Reset Essential Tab" instead of "Reset Pinned Tab" | https://github.com/zen-browser/desktop/issues/12177 | None | None |
| Windows management is broken - they keep cloning each other. | https://github.com/zen-browser/desktop/issues/12176 | 1:26:12 | 11:12:54 |
| Application menu visual glitches on KDE Plasma 5.27.5 and Kwin | https://github.com/zen-browser/desktop/issues/12175 | None | None |
| Middle mouse button does not delete tabs inside folders | https://github.com/zen-browser/desktop/issues/12174 | None | None |
| Window Sync Extents to Unpinned and Non Essential Tabs (unexpected) | https://github.com/zen-browser/desktop/issues/12173 | 0:48:02 | None |
| Clearing all tabs in window closes window | https://github.com/zen-browser/desktop/issues/12172 | 0:52:12 | None |
| Disabling window sync prevents tabs from being re-named and dragging out tab to new window will use the blank default window instead the previous behaviour. | https://github.com/zen-browser/desktop/issues/12171 | None | None |
| Update deleted all my tabs | https://github.com/zen-browser/desktop/issues/12170 | 0:40:34 | 21:14:31 |
| Right-click > Move Tab does not always show your Workspaces | https://github.com/zen-browser/desktop/issues/12168 | 1 day, 4:29:24 | None |
| background play isnt working | https://github.com/zen-browser/desktop/issues/12167 | None | None |
| Windows 11 - Update to 1.18.3b loses all tabs and essentials | https://github.com/zen-browser/desktop/issues/12166 | 0:49:13 | 0:49:13 |
| Zen wants to set itself as default browser after each update even if it already is | https://github.com/zen-browser/desktop/issues/12164 | None | None |
| 1.18.3b AutoUpdate Lost all Tabs including Essentials | https://github.com/zen-browser/desktop/issues/12163 | 0:55:04 | 19:43:42 |
| Gaming mouse's back/forward button keeps moving Workspaces instead of page | https://github.com/zen-browser/desktop/issues/12161 | 0:27:35 | 0:46:07 |
| Dragging multiple tabs from one window to another flips their order | https://github.com/zen-browser/desktop/issues/12159 | None | None |
| The pop-up UI button options for saving or saving passwords are not functional | https://github.com/zen-browser/desktop/issues/12158 | None | None |
| I lost all my folder and essentials due to the uptdate | https://github.com/zen-browser/desktop/issues/12157 | 0:35:43 | 0:35:43 |
| Dragging tabs between windows still doesn't work right | https://github.com/zen-browser/desktop/issues/12156 | 1:46:33 | None |
| Discord Camera playback | https://github.com/zen-browser/desktop/issues/12155 | None | None |
| Custom url homepage not working | https://github.com/zen-browser/desktop/issues/12154 | 13:36:48 | None |
| Numerous "New Tabs" when restoring session | https://github.com/zen-browser/desktop/issues/12152 | 0:17:33 | None |
| Feature Request: Allow manual editing of pinned tab URLs | https://github.com/zen-browser/desktop/issues/12151 | 1:19:36 | 1:19:36 |
| "Find in page" bar has white background wth black theme | https://github.com/zen-browser/desktop/issues/12150 | None | 0:00:43 |
| Open a local index.html with zen | https://github.com/zen-browser/desktop/issues/12149 | 4:53:15 | 8:41:06 |
| No synchronization of tabs between computers | https://github.com/zen-browser/desktop/issues/12148 | 0:41:19 | 1:04:28 |
| Cursor stuck as `default` everywhere after updating to 1.18.3b | https://github.com/zen-browser/desktop/issues/12145 | None | 21:06:24 |
| An issue with custom keyboard shortcuts; Particular keys fail to work | https://github.com/zen-browser/desktop/issues/12144 | 13:30:14 | None |
| 1.18.3b broke the drag to split tabs function (with a recording to show this in TROUBLE SHOOTING mode) | https://github.com/zen-browser/desktop/issues/12143 | 0:23:19 | None |
| Loaded tabs are faded out in new windows | https://github.com/zen-browser/desktop/issues/12142 | 8:30:17 | None |
| Window Sync with multi monitors is a nightmare. | https://github.com/zen-browser/desktop/issues/12141 | 0:36:59 | 8:04:12 |
| Multiple Windows On Launch In Wayland | https://github.com/zen-browser/desktop/issues/12140 | 0:12:51 | None |
| Update Broken, Cont. | https://github.com/zen-browser/desktop/issues/12139 | 14:23:05 | None |
| Some Settings Are Not Persisting Between Restarts | https://github.com/zen-browser/desktop/issues/12138 | 2:27:50 | None |
| "What's New" page text error for 1.18b | https://github.com/zen-browser/desktop/issues/12137 | 5:46:25 | 17:40:01 |
| Grouping Tabs Together has become bugged on new update | https://github.com/zen-browser/desktop/issues/12136 | 2:38:32 | None |
| Supabase Dashboard is not loading | https://github.com/zen-browser/desktop/issues/12135 | 6:13:09 | 20:45:26 |
| Workspace bookmark | https://github.com/zen-browser/desktop/issues/12134 | None | None |
| Broken Firefox Sync: Bookmarks no longer syncing between Zen Desktop and Firefox Mobile after recent update | https://github.com/zen-browser/desktop/issues/12133 | 1:22:35 | None |
| 软件启动的时候会弹出两个窗口 | https://github.com/zen-browser/desktop/issues/12132 | 2:08:29 | 2:08:29 |
| Latest Update deleted all my Tabs | https://github.com/zen-browser/desktop/issues/12131 | 0:08:05 | 0:07:30 |
| Cannot drag tabs from window with 2 or more tabs into another window | https://github.com/zen-browser/desktop/issues/12130 | 0:06:58 | 0:06:58 |
| Window sync completely breaks my workflow | https://github.com/zen-browser/desktop/issues/12127 | 0:11:09 | 1:42:54 |
| Full screen video content cut off when browser window in full screen | https://github.com/zen-browser/desktop/issues/12126 | None | 0:39:59 |
| Passkeys do not work on GitHub | https://github.com/zen-browser/desktop/issues/12125 | None | None |
| Apple login does not work via apple.com's oauth API | https://github.com/zen-browser/desktop/issues/12124 | None | None |
| "Open previous windows and tabs" setting breaks when zen.window-sync.prefer-unsynced-windows is set to false | https://github.com/zen-browser/desktop/issues/12121 | 0:54:22 | 8:23:30 |
| Zen struggle rendering large DOM lists containing lots of child elements | https://github.com/zen-browser/desktop/issues/12119 | 2 days, 21:36:13 | None |
| Drag and drop tabs to different workspaces between two different zen windows causes the tabs to disappear after restart | https://github.com/zen-browser/desktop/issues/12116 | 0:04:16 | None |
| Windows Sync and Tabs | https://github.com/zen-browser/desktop/issues/12115 | 0:05:26 | 0:05:26 |
| Loss of open tabs after update 1.18.x | https://github.com/zen-browser/desktop/issues/12114 | 1:10:05 | 1:59:02 |
| Tab focus returns to parent tab instead of adjacent tab when closing multiple results | https://github.com/zen-browser/desktop/issues/12113 | 11:47:07 | 14:11:10 |
| New window does not auto focus url bar with zen.urlbar.replace-newtab set to false | https://github.com/zen-browser/desktop/issues/12112 | None | None |
| 1.18b completely breaks Simple Tab Groups | https://github.com/zen-browser/desktop/issues/12111 | 0:06:51 | 0:06:51 |
| Essentials icon keep disappearing | https://github.com/zen-browser/desktop/issues/12110 | 0:13:16 | 0:13:16 |
| Random Infinite Loading/Crashes when watching a YouTube video | https://github.com/zen-browser/desktop/issues/12109 | 1:23:12 | None |
| Zen icon deleted after an update | https://github.com/zen-browser/desktop/issues/12108 | 0:08:52 | 0:08:52 |
| Bug with folders with split tabs after update 1.18.2b (Mac) | https://github.com/zen-browser/desktop/issues/12107 | 0:17:10 | None |
| Weird bug when repoen zen the tabs keeps recover | https://github.com/zen-browser/desktop/issues/12106 | 0:33:42 | None |
| Notification can't be closed when tabs are on the right side (mailto notify) | https://github.com/zen-browser/desktop/issues/12104 | 2:40:15 | None |
| Cannot move tab from one window to another | https://github.com/zen-browser/desktop/issues/12103 | 0:12:36 | 0:13:32 |
| on Essential Tabs , when i recieve a message on whatsapp it gets hidden, i do get notification but it doesnt show | https://github.com/zen-browser/desktop/issues/12101 | 0:08:34 | 0:08:36 |
| Continue where you let off for bookmarks | https://github.com/zen-browser/desktop/issues/12100 | 1:16:25 | 3:48:13 |
| Essential tabs disappearing after excessive clicks | https://github.com/zen-browser/desktop/issues/12099 | 4:13:22 | 4:13:22 |
| Enhanced Tracking Protection reverts back to strict on launch | https://github.com/zen-browser/desktop/issues/12096 | 7:40:37 | 19:27:51 |
| Sidebar tab title color / contrast changed in v1.18 | https://github.com/zen-browser/desktop/issues/12095 | None | 0:13:40 |
| New window does not open correct space. | https://github.com/zen-browser/desktop/issues/12094 | 0:01:55 | None |
| Bug: Essentials tabs disappear after rapid activation | https://github.com/zen-browser/desktop/issues/12092 | 0:31:21 | 1:23:25 |
| Tabs being cloned to all windows | https://github.com/zen-browser/desktop/issues/12088 | 0:06:01 | 0:47:56 |
| Pinned extensions disappear when more than 1 is pinned | https://github.com/zen-browser/desktop/issues/12087 | 1 day, 14:00:23 | None |
| Bookmark icons not showing properly | https://github.com/zen-browser/desktop/issues/12084 | 0:03:11 | None |
| There is a problem with Zen when opening tabs: it is slow | https://github.com/zen-browser/desktop/issues/12083 | None | None |
| Jump to a blank page after closing all normal tabs | https://github.com/zen-browser/desktop/issues/12081 | None | 1 day, 21:13:35 |
| Dragging and Dropping tabs within the sidebar. Tab pops out as white box. | https://github.com/zen-browser/desktop/issues/12080 | 2:07:00 | None |
| Essential tabs dissapear from sidebar UI | https://github.com/zen-browser/desktop/issues/12078 | 0:01:44 | 0:01:44 |
| F6 doesn't float url bar | https://github.com/zen-browser/desktop/issues/12076 | 0:16:04 | 3:38:13 |
| Blank window. Completely unusable | https://github.com/zen-browser/desktop/issues/12075 | 0:05:13 | 1:07:49 |
| View Page Source opens in No Container Tab | https://github.com/zen-browser/desktop/issues/12074 | 0:04:07 | None |
| Zen starting out blank with corrupted tabs | https://github.com/zen-browser/desktop/issues/12073 | 0:05:16 | 2 days, 10:11:43 |
| Essentials becoming invisible / absent until browser restarted | https://github.com/zen-browser/desktop/issues/12072 | 0:06:59 | 0:06:59 |
| Proxmox UI Shell renders unreadable. Previously worked. | https://github.com/zen-browser/desktop/issues/12071 | None | None |
| Grid Split navigations is backwards | https://github.com/zen-browser/desktop/issues/12070 | 22:32:57 | 3 days, 7:21:01 |
| Bookmarks no longer synchronize | https://github.com/zen-browser/desktop/issues/12069 | None | None |
| Can't use MacOs specific video features (Portrait, Studio Light, Background) | https://github.com/zen-browser/desktop/issues/12068 | None | None |
| Turning off zen.window-sync.enabled to false leads to broken new window | https://github.com/zen-browser/desktop/issues/12066 | 0:37:44 | 1:57:03 |
| Can future versions please not clear out tabs and folder trees when updating/upgrade? | https://github.com/zen-browser/desktop/issues/12064 | None | 4:57:15 |
| "New Blank Window" uses wrong theme color | https://github.com/zen-browser/desktop/issues/12062 | 9:49:13 | 11:15:18 |
| [linux] Edit label/title for tab doesn't work | https://github.com/zen-browser/desktop/issues/12060 | 2:25:26 | 3 days, 18:16:00 |
| Missing option to disable synced windows | https://github.com/zen-browser/desktop/issues/12059 | None | 0:01:21 |
| Disable Window Sync option in settings? | https://github.com/zen-browser/desktop/issues/12058 | 0:12:41 | 0:12:41 |
| Tab titles not updating when window sync is disabled | https://github.com/zen-browser/desktop/issues/12057 | 0:05:48 | 0:10:29 |
| Essentials entry randomly disappeared from UI | https://github.com/zen-browser/desktop/issues/12056 | 0:12:56 | 0:12:56 |
| Pinned tabs not resseting on startup | https://github.com/zen-browser/desktop/issues/12055 | 2:00:07 | 2:30:05 |
| [Wayland] Zen browser shows white blank panel on startup | https://github.com/zen-browser/desktop/issues/12054 | 1:24:35 | 1 day, 12:37:36 |
| [Tab Sync] Tab in Glance mode opens as regular tab on other window. | https://github.com/zen-browser/desktop/issues/12051 | 1 day, 19:56:04 | None |
| workspace tabs desync across windows | https://github.com/zen-browser/desktop/issues/12050 | 0:11:59 | None |
| Essential tab stop working | https://github.com/zen-browser/desktop/issues/12045 | 0:46:10 | 0:46:10 |
| PWA apps render issue | https://github.com/zen-browser/desktop/issues/12044 | 1:32:16 | 3 days, 1:18:16 |
| MacOS - Wrong tab selected when going backwards | https://github.com/zen-browser/desktop/issues/12043 | 0:04:04 | None |
| Problem with important tabs | https://github.com/zen-browser/desktop/issues/12042 | 2 days, 4:15:21 | 2 days, 20:49:00 |
| Pinned and essential tabs randomly disappear | https://github.com/zen-browser/desktop/issues/12040 | 0:34:50 | 0:34:50 |
| Sidebar overflows in compact mode | https://github.com/zen-browser/desktop/issues/12039 | 1 day, 4:05:41 | None |
| Dragging tabs between worksapce is buggy [I think due to sync] | https://github.com/zen-browser/desktop/issues/12037 | 5 days, 11:35:22 | 5 days, 11:35:22 |
| Sidebar doesnt close automatically after creating a new tab in Compact Mode | https://github.com/zen-browser/desktop/issues/12036 | 2 days, 6:04:10 | None |
| The extension items in the context menu multiplying with every right click | https://github.com/zen-browser/desktop/issues/12035 | 3:50:26 | None |
| Disabling zen.window-sync.enabled breaks "Open previous windows and tabs" | https://github.com/zen-browser/desktop/issues/12034 | 4:38:30 | 6:11:16 |
| Tab cannot be grabbed and taken into a different Zen browser instance | https://github.com/zen-browser/desktop/issues/12032 | 0:11:57 | 1:21:44 |
| Window sync with zoom | https://github.com/zen-browser/desktop/issues/12031 | 3:33:29 | 1 day, 17:09:02 |
| New Window doesn't work with window sync disabled. | https://github.com/zen-browser/desktop/issues/12030 | None | 1:21:07 |
| Move to space not listed in context menu | https://github.com/zen-browser/desktop/issues/12029 | 1:28:17 | 1:28:17 |
| All my workspaces and tabs just gone after update, wtf ? :) | https://github.com/zen-browser/desktop/issues/12028 | 0:31:31 | 1:41:41 |
| Global menu show but unresponsive. | https://github.com/zen-browser/desktop/issues/12024 | 0:06:03 | 6 days, 1:49:53 |
| Debian 13 &#124; Passkey doesn't show QR code | https://github.com/zen-browser/desktop/issues/12022 | 10:08:54 | None |
| Performance: Swiping across spaces with multiple tabs open is laggy | https://github.com/zen-browser/desktop/issues/12020 | 0:09:27 | 0:09:27 |
| Move to Space | https://github.com/zen-browser/desktop/issues/12019 | 0:08:00 | 0:08:00 |
| [UI Error] Zen Browser Settings UI Error | https://github.com/zen-browser/desktop/issues/12014 | 0:41:54 | 6:44:05 |
| Full-Screen PIP Mode Retains Dimmed State Indefinitely Due to Hover and System Inactivity | https://github.com/zen-browser/desktop/issues/12013 | None | None |
| "Check for Updates" Not Working | https://github.com/zen-browser/desktop/issues/12012 | 1:54:04 | 1:54:09 |
| Website lolalytics.com not working properly after updating to 1.18.1b | https://github.com/zen-browser/desktop/issues/12011 | 10:35:46 | 11:32:35 |
| No "Open a new Blank Window" action when right-clicking zen on taskbar | https://github.com/zen-browser/desktop/issues/12010 | 9:50:45 | None |
| Inconsistant capitalization in application menu | https://github.com/zen-browser/desktop/issues/12009 | None | 0:15:37 |
| Disable window/tab sync by default | https://github.com/zen-browser/desktop/issues/12008 | 0:03:34 | 17:22:46 |
| Sync not working + pinned tabs do not resert url | https://github.com/zen-browser/desktop/issues/12006 | 0:15:54 | 7:59:09 |
| Some UI elements not renderring correctly | https://github.com/zen-browser/desktop/issues/12005 | 3:40:46 | 4:36:15 |
| installer prolbem | https://github.com/zen-browser/desktop/issues/12004 | 2:13:28 | 2:13:28 |
| Issues with Sidebar Dragging Exiting Full-Screen & Legacy Ctrl+Tab Behavior | https://github.com/zen-browser/desktop/issues/12003 | 4:33:38 | None |
| Cannot Rename “New Folder” in the Sidebar | https://github.com/zen-browser/desktop/issues/12002 | 0:58:42 | 1:48:21 |
| Custom font doesn't work | https://github.com/zen-browser/desktop/issues/12001 | 1 day, 8:53:16 | 1 day, 8:53:16 |
| Windows accent coloring issue with zen | https://github.com/zen-browser/desktop/issues/12000 | 1:11:28 | 1:11:28 |
| Disabling zen.window-sync.enabled breaks new zen instances | https://github.com/zen-browser/desktop/issues/11999 | 4:34:06 | 22:44:50 |
| Pinned tabs don't scale: hundreds of pins freeze browser | https://github.com/zen-browser/desktop/issues/11997 | 6:50:35 | 1 day, 13:33:03 |
| All tabs are gone (also pinned) when updating to 1.18b | https://github.com/zen-browser/desktop/issues/11994 | 4:42:08 | None |
| "blank window" at startup | https://github.com/zen-browser/desktop/issues/11993 | 0:02:32 | 0:52:40 |
| Crashing when playing WASMGC game | https://github.com/zen-browser/desktop/issues/11990 | 0:31:55 | None |
| Pinned Tabs disappearing/invisible | https://github.com/zen-browser/desktop/issues/11989 | 11:31:45 | 11:31:45 |
| Zen process opens but the window doesn't | https://github.com/zen-browser/desktop/issues/11985 | 0:22:24 | None |
| Zen has recently got really inconsistent performance (especially after the last few updates). | https://github.com/zen-browser/desktop/issues/11982 | 0:52:04 | None |
| Twitch.tv Video Streams Not Loading | https://github.com/zen-browser/desktop/issues/11980 | 0:19:27 | None |
| Page rendering is blank white | https://github.com/zen-browser/desktop/issues/11979 | 15:04:14 | 15:04:14 |
| Zen is unusable on Cosmic Desktop Environment (unable to remove a checkbox that spawns and hinders using the browser) | https://github.com/zen-browser/desktop/issues/11978 | 0:42:40 | 17:46:00 |
| Searching with # is not searching bookmarks by tag or title | https://github.com/zen-browser/desktop/issues/11976 | 1:54:07 | 11:45:13 |
| Conflict Between Browser Page Search Shortcut and Confluence Search | https://github.com/zen-browser/desktop/issues/11975 | None | None |
| Updater repeatedly reinstalls latest twilight build despite no version change. | https://github.com/zen-browser/desktop/issues/11974 | 0:19:47 | 6:53:06 |
| Randomly Control button stop functioning | https://github.com/zen-browser/desktop/issues/11972 | None | None |
| Low refresh rate on Zen Windows version after OS update | https://github.com/zen-browser/desktop/issues/11967 | 21:19:01 | None |
| Switching workspaces to a split view will cause the area surround the view to turn black for a second | https://github.com/zen-browser/desktop/issues/11965 | 17:19:30 | None |
| [Bug] Previous session permanently lost after closing and reopening Zen Browser | https://github.com/zen-browser/desktop/issues/11964 | 4:00:54 | 4:01:24 |
| Closing a normal zen window when an incognito zen browser is opened, removes all the tabs across all my workspaces. | https://github.com/zen-browser/desktop/issues/11963 | 2:10:13 | 2:10:13 |
| Only sidebar layout -- Always show bookmarks tool bar + window buttons | https://github.com/zen-browser/desktop/issues/11962 | 0:06:53 | 3:10:47 |
| Zen randomly crashes on macOS | https://github.com/zen-browser/desktop/issues/11958 | 3:36:36 | None |
| Essentials and pinned tabs don't load automatically on open | https://github.com/zen-browser/desktop/issues/11957 | 12:57:52 | 12:57:52 |
| Readme reports Firefox 147.0.1 for Release but latest release 1.17.15b (2025-12-20) is 146.0.1 | https://github.com/zen-browser/desktop/issues/11952 | 8:47:04 | 8:47:03 |
| Non-default container indicator not showing in workspace | https://github.com/zen-browser/desktop/issues/11951 | 0:13:01 | None |
| Zen not displaying websites on Intel HD graphics 2000 | https://github.com/zen-browser/desktop/issues/11950 | None | None |
| Private window tabs are mirrored in the main (non-private) window | https://github.com/zen-browser/desktop/issues/11949 | 2:34:54 | 4:47:55 |
| Media session metadata (title/artist/cover) does not update on site without page refresh | https://github.com/zen-browser/desktop/issues/11948 | 6:32:56 | 1 day, 6:51:54 |
| Nvidia overlay "Plugin-container is preventing the desktop capture" | https://github.com/zen-browser/desktop/issues/11947 | None | None |
| Login to self-hosted Bitwarden fails after initialization in multi-account setting | https://github.com/zen-browser/desktop/issues/11946 | 1 day, 14:57:08 | None |
| Accessing any URL under bitbucket.org returns 404 | https://github.com/zen-browser/desktop/issues/11944 | 2:26:35 | 1 day, 20:58:15 |
| Acrylic turns grey | https://github.com/zen-browser/desktop/issues/11937 | 0:01:44 | None |
| lang change | https://github.com/zen-browser/desktop/issues/11934 | 2:27:46 | 3:29:15 |
| MX Master 3 Forward/Back mouse buttons don't work if zen.workspaces.swipe-actions is false | https://github.com/zen-browser/desktop/issues/11923 | 1:53:09 | 1:53:09 |
| Tabs UI bug | https://github.com/zen-browser/desktop/issues/11922 | None | None |
| Positioning of titlebar window control buttons | https://github.com/zen-browser/desktop/issues/11918 | 11:28:41 | 11:28:41 |
| Zen won't follow XDG base directories | https://github.com/zen-browser/desktop/issues/11917 | 12:43:08 | None |
| Titlebar expands on hover even if Titlebar setting is Disabled from "Configure Toolbar" in Sidebar only mode of look and feel. | https://github.com/zen-browser/desktop/issues/11915 | None | None |
| Multiple window sessions result in same tabs open in all windows | https://github.com/zen-browser/desktop/issues/11914 | 1 day, 1:41:36 | 1 day, 1:41:36 |
| Misplaced Tab (Placed a tab above the new tab button but below the line that separates the pinned tabs) | https://github.com/zen-browser/desktop/issues/11911 | None | 4:09:21 |
| Sidebar width inconsistency | https://github.com/zen-browser/desktop/issues/11909 | None | None |
| Cannot expand / fullscreen twitter videos | https://github.com/zen-browser/desktop/issues/11908 | 2 days, 5:35:23 | 2 days, 5:35:23 |
| Menu shows unsecure connections as secure | https://github.com/zen-browser/desktop/issues/11905 | 2:21:24 | 3:35:25 |
| Top Toolbar appearing when using shortcut "Toggle Sidebar's Width" | https://github.com/zen-browser/desktop/issues/11903 | 5:08:25 | 5:08:25 |
| Only shows default icons for Google Messages for web | https://github.com/zen-browser/desktop/issues/11902 | None | 2:39:30 |
| The default section disappear from dot list at the below of the tab when swiching spaces | https://github.com/zen-browser/desktop/issues/11901 | 0:50:18 | None |
| Context menu partially hidden when right-clicking on a Bookmarks folder in the toolbar | https://github.com/zen-browser/desktop/issues/11898 | None | None |
| Refined Github(extension): Issues interface with notification header flickers / flashes | https://github.com/zen-browser/desktop/issues/11896 | None | None |
| Glance should work only for links | https://github.com/zen-browser/desktop/issues/11895 | None | 7:53:12 |
| Auto Unload Does Not And Has Never Unloaded a Single Tab Ever. | https://github.com/zen-browser/desktop/issues/11894 | None | 6:58:48 |
| Visual Bug: Line through sidebar and settings page | https://github.com/zen-browser/desktop/issues/11893 | None | 12:24:36 |
| 1password integration - not working | https://github.com/zen-browser/desktop/issues/11892 | 2:39:46 | 13:40:23 |
| Hardware acceleration worse on Zen vs others (CachyOS - NVIDIA - Wayland) | https://github.com/zen-browser/desktop/issues/11890 | 2:18:58 | 8:44:12 |
| Some tabs (inside a folder) are not drawn in the sidebar | https://github.com/zen-browser/desktop/issues/11888 | 18:13:05 | 18:13:05 |
| (macos) (twilight) pinning splitview tabs in folders causes addl tab group labels on restart/update | https://github.com/zen-browser/desktop/issues/11887 | None | 23:27:16 |
| How can I achieve window transparency on Windows (so that I can see through to the windows or desktop behind it)?/如何实现Windows下窗口透明化可以看到后面的窗口或桌面 | https://github.com/zen-browser/desktop/issues/11885 | 11:12:00 | 11:12:00 |
| Form validation checkbox message is white text on white background | https://github.com/zen-browser/desktop/issues/11884 | None | None |
| Fullscreen with F11 doesn't work as intended | https://github.com/zen-browser/desktop/issues/11883 | 6:30:42 | 6:30:46 |
| PiP window stays open empty and buttons dont work | https://github.com/zen-browser/desktop/issues/11881 | None | None |
| When switching browser layouts, extensions fixed to the toolbar may have display bugs | https://github.com/zen-browser/desktop/issues/11880 | None | 6:31:27 |
| Zen startup window has a transition from dark background (light background in light mode) + a flash before the mica kicks on windows 11. | https://github.com/zen-browser/desktop/issues/11876 | 1 day, 22:21:29 | None |
| Zen Hangs/Crashes - Have not been able to use for several months. | https://github.com/zen-browser/desktop/issues/11875 | 2 days, 0:58:56 | None |
| Settings modal is not in the right place! | https://github.com/zen-browser/desktop/issues/11873 | None | None |
| Macos: extention pop-ups open up at a different desktop in fullscreen | https://github.com/zen-browser/desktop/issues/11872 | 7:25:14 | 7:25:14 |
| Close Tab shortcut can close the window | https://github.com/zen-browser/desktop/issues/11871 | 2 days, 6:59:58 | 2 days, 6:59:58 |
| When on the login page, the extension icon in the address bar cannot be clicked | https://github.com/zen-browser/desktop/issues/11870 | 2 days, 8:04:14 | 19 days, 3:16:59 |
| Showing multiple empthy pages | https://github.com/zen-browser/desktop/issues/11869 | 4:22:38 | None |
| Control + click is not opening links in a new tab. | https://github.com/zen-browser/desktop/issues/11868 | 3 days, 21:19:59 | None |
| Folders and tabs of a workspace are not synced. | https://github.com/zen-browser/desktop/issues/11865 | 1:58:13 | 1:58:13 |
| No save button | https://github.com/zen-browser/desktop/issues/11864 | 0:15:11 | None |
| No Icon For Files | https://github.com/zen-browser/desktop/issues/11860 | 5:02:04 | 20:21:37 |
| Compatibility issue with aarch64 AppImage starting from v1.17b (GLIBCXX_3.4.26 not found) | https://github.com/zen-browser/desktop/issues/11859 | None | None |
| Adding New Bookmark in Compact Mode No Longer Pops Up Edit Panel | https://github.com/zen-browser/desktop/issues/11858 | 1 day, 18:54:21 | None |
| 什么时候能支持网页驱动 | https://github.com/zen-browser/desktop/issues/11857 | 12:28:29 | 12:28:29 |
| Workspace Switching Becomes Laggy with 4+ Workspaces and many pinned Tabs on Windows (NVIDIA GPU). | https://github.com/zen-browser/desktop/issues/11851 | 2:00:50 | None |
| Closing a recently opened tab returns to the top tab, not the previously focused tab | https://github.com/zen-browser/desktop/issues/11845 | 19:10:53 | 1 day, 17:02:53 |
| Wrong italian translation of "Workspace forward" in shortcuts settings | https://github.com/zen-browser/desktop/issues/11841 | 1:17:18 | None |
| Cannot close pinned tabs | https://github.com/zen-browser/desktop/issues/11838 | 0:21:08 | 0:23:33 |
| Copy URL shortcut does not copy URL before page has loaded | https://github.com/zen-browser/desktop/issues/11835 | None | None |
| Extensions not showing up in toolbar. | https://github.com/zen-browser/desktop/issues/11834 | 1:33:29 | 9:20:29 |
| Multiple duplicated Bookmarks | https://github.com/zen-browser/desktop/issues/11832 | None | None |
| [BUG] Closing and re-opening zen twighlight turns a bunch of tabs into 'New Tab's | https://github.com/zen-browser/desktop/issues/11831 | 15:57:31 | 7 days, 8:27:19 |
| Indent issue on active pinned tab in collapsed workspace | https://github.com/zen-browser/desktop/issues/11830 | 17:46:11 | 1 day, 6:07:21 |
| High Ram Usage | https://github.com/zen-browser/desktop/issues/11828 | None | 0:20:29 |
| Unable to install extentions in compact mode | https://github.com/zen-browser/desktop/issues/11827 | None | 1:11:29 |
| Extension icons display inconsitently when switching toolbar modes or resizing window | https://github.com/zen-browser/desktop/issues/11826 | 2:31:13 | None |
| MS Entra auth not working | https://github.com/zen-browser/desktop/issues/11825 | 3:14:28 | 20:29:41 |
| "Restore pinned tabs to their originally pinned URL on startup' option doesn't reset Pinned Tabs go back to Originally Pinned URL, i.e. doesn't reset them | https://github.com/zen-browser/desktop/issues/11823 | 2 days, 12:40:16 | None |
| Crashes when signing into aws console MFA passkey via lastpassword | https://github.com/zen-browser/desktop/issues/11820 | None | None |
| Using the dev console in glance causes weird ui | https://github.com/zen-browser/desktop/issues/11815 | 1:43:58 | None |
| The VS Code integrated extension “Live Server” does not automatically detect the Zen Browser, even when it is set as the default browser. | https://github.com/zen-browser/desktop/issues/11813 | 2:56:35 | 2 days, 12:21:43 |
| Ctrl+T does not open new tab if shortcuts are removed | https://github.com/zen-browser/desktop/issues/11811 | 0:40:23 | None |
| Sidebar title is overlapping with essential tabs after screen unlock | https://github.com/zen-browser/desktop/issues/11808 | None | 15:50:26 |
| button alignment does not change in pop-up window | https://github.com/zen-browser/desktop/issues/11805 | 0:10:14 | None |
| "Focus on <space>" action not available | https://github.com/zen-browser/desktop/issues/11804 | 0:03:34 | 1 day, 0:35:28 |
| Split View and Floating URL bugs when a Youtube video is in Full Screen and Ctrl + Shift + * is pressed. | https://github.com/zen-browser/desktop/issues/11803 | None | 7 days, 0:58:59 |
| Trackpad diagonal scroll unintentionally switches workspaces instead of scrolling tabs | https://github.com/zen-browser/desktop/issues/11802 | 2:20:42 | None |
| New tab opens when Alt+Tabbing or pressing Windows key while YouTube is playing | https://github.com/zen-browser/desktop/issues/11801 | 8:57:44 | None |
| Broken Installer (fixed) | https://github.com/zen-browser/desktop/issues/11800 | None | 1 day, 22:57:03 |
| subfolder collapse state issue when collapsing pinned area | https://github.com/zen-browser/desktop/issues/11799 | 11:27:53 | 1 day, 12:52:10 |
| Glance opens a new window for local file URLs (file://) | https://github.com/zen-browser/desktop/issues/11797 | None | None |
| acronis.com nginx a header or cookie larger than permitted | https://github.com/zen-browser/desktop/issues/11795 | None | None |
| Cannot listen to songs using the spotify integration on Musixmatch for lyric syncing in Zen | https://github.com/zen-browser/desktop/issues/11792 | 0:15:31 | 0:15:31 |
| Adding Split Tab Groups to a folder Makes the folder disappear | https://github.com/zen-browser/desktop/issues/11791 | 15 days, 4:38:37 | 15 days, 4:38:37 |
| Clear tabs button's separator is missing on Linux | https://github.com/zen-browser/desktop/issues/11789 | None | 3 days, 10:41:55 |
| [Twilight] Cannot add tab to bottom of folder | https://github.com/zen-browser/desktop/issues/11788 | 21:08:12 | 21:08:12 |
| Flathub version doesnt work on Debian 13 | https://github.com/zen-browser/desktop/issues/11787 | 25 days, 12:50:21 | None |
| Split View sidebar shows extra tab after restoring a closed tab (Ctrl+Shift+T) | https://github.com/zen-browser/desktop/issues/11785 | None | None |
| Cursor moved far right of window does not grab scrollbar when window is maximised | https://github.com/zen-browser/desktop/issues/11783 | 6:07:23 | 6:07:23 |
| HMR Not Working Correctly for Frontend Development | https://github.com/zen-browser/desktop/issues/11782 | 7:57:34 | 9:05:06 |
| Failing to obtain location | https://github.com/zen-browser/desktop/issues/11781 | 15:43:36 | 1 day, 17:23:10 |
_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-01-01..2026-01-31`

View File

@@ -314,4 +314,4 @@ zen-devtools-toggle-storage-shortcut = Commuta l'emmagatzematge
zen-devtools-toggle-dom-shortcut = Commuta el DOM
zen-devtools-toggle-accessibility-shortcut = Commuta l'accessibilitat
zen-close-all-unpinned-tabs-shortcut = Tanca totes les pestanyes no fixades
zen-new-unsynced-window-shortcut = Nova finestra no sincronitzada
zen-new-unsynced-window-shortcut = Nova finestra en blanc

View File

@@ -19,4 +19,4 @@ zen-menubar-appearance-light =
zen-menubar-appearance-dark =
.label = Fosca
zen-menubar-new-unsynced-window =
.label = New Blank Window
.label = Nova finestra en blanc

View File

@@ -314,4 +314,4 @@ zen-devtools-toggle-storage-shortcut = Toglo Storio
zen-devtools-toggle-dom-shortcut = Toglo DOM
zen-devtools-toggle-accessibility-shortcut = Toglo Hygyrchedd
zen-close-all-unpinned-tabs-shortcut = Cau Pob Tab Heb ei Binio
zen-new-unsynced-window-shortcut = Ffenestr Newydd Heb ei Chydweddu
zen-new-unsynced-window-shortcut = Ffenestr Wag Newydd

View File

@@ -2,8 +2,8 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
zen-panel-ui-current-profile-text = proffil cyfredol
unified-extensions-description = Mae ychwanegiadau'n cael ei defnyddio er mwyn ychwanegu fwy o weithrediadau i { -brand-short-name }.
zen-panel-ui-current-profile-text = y proffil cyfredol
unified-extensions-description = Mae estyniadau'n cael ei defnyddio er mwyn ychwanegu fwy o swyddogaeth i { -brand-short-name }.
tab-context-zen-reset-pinned-tab =
.label = Ailosod Tab wedi'i Binio
.accesskey = A
@@ -109,7 +109,7 @@ zen-sidebar-notification-updated-tooltip =
zen-sidebar-notification-restart-safe-mode-label = Rhywbeth wedi torri?
zen-sidebar-notification-restart-safe-mode-tooltip =
.title = Ailgychwyn yn y Modd Diogel
zen-window-sync-migration-dialog-title = Keep Your Windows in Sync
zen-window-sync-migration-dialog-message = Zen now syncs windows on the same device, so changes in one window are reflected across the others instantly.
zen-window-sync-migration-dialog-learn-more = Learn More
zen-window-sync-migration-dialog-accept = Got It
zen-window-sync-migration-dialog-title = Cadw Eich Ffenestr Wedi'u Cydweddu
zen-window-sync-migration-dialog-message = Mae Zen bellach yn cydweddu ffenestri ar yr un ddyfais, felly mae newidiadau mewn un ffenestr yn cael eu dangos ar y lleill yn syth.
zen-window-sync-migration-dialog-learn-more = Dysgu Rhagor
zen-window-sync-migration-dialog-accept = Iawn

View File

@@ -19,4 +19,4 @@ zen-menubar-appearance-light =
zen-menubar-appearance-dark =
.label = Tywyll
zen-menubar-new-unsynced-window =
.label = New Blank Window
.label = Ffenestr Wag Newydd

View File

@@ -53,12 +53,15 @@ category-zen-workspaces =
.tooltiptext = { pane-zen-tabs-title }
pane-settings-workspaces-title = Workspaces
zen-tabs-unloader-enabled =
.label = Enable Tab Unloader
zen-tabs-select-recently-used-on-close =
.label = When closing a tab, switch to the most recently used tab instead of the next tab
zen-tabs-close-on-back-with-no-history =
.label = Close tab and switch to its owner tab (or most recently used tab) when going back with no history
zen-settings-workspaces-sync-unpinned-tabs =
.label = Sync only pinned tabs in workspaces
zen-tabs-cycle-by-attribute =
.label = Ctrl+Tab cycles within Essential or Workspace tabs only
zen-tabs-cycle-ignore-pending-tabs =

View File

@@ -6,7 +6,11 @@ zen-panel-ui-current-profile-text = current profile
unified-extensions-description = Extensions are used to bring more extra functionality into { -brand-short-name }.
tab-context-zen-reset-pinned-tab =
.label = Reset Pinned Tab
.label =
{ $isEssential ->
[true] Reset Essential Tab
*[false] Reset Pinned Tab
}
.accesskey = R
tab-context-zen-add-essential =
.label = Add to Essentials
@@ -16,7 +20,11 @@ tab-context-zen-remove-essential =
.label = Remove from Essentials
.accesskey = R
tab-context-zen-replace-pinned-url-with-current =
.label = Replace Pinned URL with Current
.label =
{ $isEssential ->
[true] Replace Essential URL with Current
*[false] Replace Pinned URL with Current
}
.accesskey = C
tab-context-zen-edit-title =
.label = Change Label...
@@ -137,3 +145,7 @@ zen-window-sync-migration-dialog-title = Keep Your Windows in Sync
zen-window-sync-migration-dialog-message = Zen now syncs windows on the same device, so changes in one window are reflected across the others instantly.
zen-window-sync-migration-dialog-learn-more = Learn More
zen-window-sync-migration-dialog-accept = Got It
zen-appmenu-new-blank-window =
.label = New blank window

View File

@@ -22,5 +22,5 @@ zen-menubar-appearance-light =
zen-menubar-appearance-dark =
.label = Dark
zen-menubar-new-unsynced-window =
.label = New Blank Window
zen-menubar-new-blank-window =
.label = New Blank Window

View File

@@ -19,4 +19,4 @@ zen-menubar-appearance-light =
zen-menubar-appearance-dark =
.label = Sombre
zen-menubar-new-unsynced-window =
.label = New Blank Window
.label = Nouvelle fenêtre non synchronisée

View File

@@ -19,4 +19,4 @@ zen-menubar-appearance-light =
zen-menubar-appearance-dark =
.label = Dorcha
zen-menubar-new-unsynced-window =
.label = New Blank Window
.label = Fuinneog Nua Bán

View File

@@ -19,4 +19,4 @@ zen-menubar-appearance-light =
zen-menubar-appearance-dark =
.label = כהה
zen-menubar-new-unsynced-window =
.label = New Blank Window
.label = חלון ריק חדש

View File

@@ -19,4 +19,4 @@ zen-menubar-appearance-light =
zen-menubar-appearance-dark =
.label = Sötét
zen-menubar-new-unsynced-window =
.label = New Blank Window
.label = Új üres lap

View File

@@ -49,8 +49,8 @@ zen-tabs-close-on-back-with-no-history =
zen-tabs-cycle-by-attribute =
.label = Ctrl+Tab berputar hanya dalam tab Esensial atau Ruang Kerja
zen-tabs-cycle-ignore-pending-tabs =
.label = Ignore Pending tabs when cycling with Ctrl+Tab
zen-tabs-cycle-by-attribute-warning = Ctrl+Tab will cycle by recently used order, as it is enabled
.label = Lewati tab tak termuat saat beralih dengan Ctrl+Tab
zen-tabs-cycle-by-attribute-warning = Ctrl+Tab akan beralih berdasarkan urutan terakhir digunakan
zen-look-and-feel-compact-toolbar-themed =
.label = Gunakan latar belakang bertema untuk bilah atas dalam mode ringkas
zen-workspace-continue-where-left-off =
@@ -314,4 +314,4 @@ zen-devtools-toggle-storage-shortcut = Ubah Penyimpanan
zen-devtools-toggle-dom-shortcut = Ubah DOM
zen-devtools-toggle-accessibility-shortcut = Ubah Aksesibilitas
zen-close-all-unpinned-tabs-shortcut = Tutup Semua Tab yang Tidak Disematkan
zen-new-unsynced-window-shortcut = New Unsynced Window
zen-new-unsynced-window-shortcut = Jendela Kosong Baru

View File

@@ -60,8 +60,8 @@ zen-site-data-settings = Pengaturan
zen-generic-manage = Kelola
zen-generic-more = Selengkapnya
zen-generic-next = Lanjut
zen-essentials-promo-label = Add to Essentials
zen-essentials-promo-sublabel = Keep your favorite tabs just a click away
zen-essentials-promo-label = Tambahkan ke Essentials
zen-essentials-promo-sublabel = Akses tab favorit Anda hanya dengan sekali klik
# These labels will be used for the site data panel settings
zen-site-data-setting-allow = Diizinkan
zen-site-data-setting-block = Diblokir
@@ -109,7 +109,7 @@ zen-sidebar-notification-updated-tooltip =
zen-sidebar-notification-restart-safe-mode-label = Ada yang rusak?
zen-sidebar-notification-restart-safe-mode-tooltip =
.title = Mulai Ulang dalam Mode Aman
zen-window-sync-migration-dialog-title = Keep Your Windows in Sync
zen-window-sync-migration-dialog-message = Zen now syncs windows on the same device, so changes in one window are reflected across the others instantly.
zen-window-sync-migration-dialog-learn-more = Learn More
zen-window-sync-migration-dialog-accept = Got It
zen-window-sync-migration-dialog-title = Jaga Jendela Anda Tetap Sinkron
zen-window-sync-migration-dialog-message = Zen kini menyinkronkan jendela pada perangkat yang sama, sehingga perubahan di satu jendela akan langsung terlihat di jendela lainnya.
zen-window-sync-migration-dialog-learn-more = Pelajari Lebih Lanjut
zen-window-sync-migration-dialog-accept = Oke!

View File

@@ -4,19 +4,19 @@
zen-menubar-toggle-pinned-tabs =
.label =
{ $pinnedAreCollapsed ->
[true] Expand Pinned Tabs
*[false] Collapse Pinned Tabs
{ $pinnedAreCollapsed ->
[true] Perluas Tab yang Disematkan
*[false] Ciutkan Tab yang Disematkan
}
zen-menubar-appearance =
.label = Appearance
.label = Tampilan
zen-menubar-appearance-description =
.label = Websites will use:
.label = Situs web akan menggunakan:
zen-menubar-appearance-auto =
.label = Automatic
.label = Otomatis
zen-menubar-appearance-light =
.label = Light
.label = Terang
zen-menubar-appearance-dark =
.label = Dark
.label = Gelap
zen-menubar-new-unsynced-window =
.label = New Blank Window
.label = Jendela Kosong Baru

View File

@@ -4,13 +4,13 @@
zen-panel-ui-workspaces-text = Ruang Kerja Lainnya
zen-panel-ui-spaces-label =
.label = Spaces
.label = Ruang
zen-panel-ui-workspaces-create =
.label = Buat Ruang
zen-panel-ui-folder-create =
.label = Buat Folder
zen-panel-ui-new-empty-split =
.label = New Split
.label = Split Baru
zen-workspaces-panel-context-delete =
.label = Hapus Ruang
.accesskey = D
@@ -66,6 +66,6 @@ zen-workspaces-close-all-unpinned-tabs-title =
.label = Bersihkan
.tooltiptext = Tutup semua tab yang tidak disematkan
zen-panel-ui-workspaces-change-forward =
.label = Next Space
.label = Ruang Selanjutnya
zen-panel-ui-workspaces-change-back =
.label = Previous Space
.label = Ruang Sebelumnya

View File

@@ -19,4 +19,4 @@ zen-menubar-appearance-light =
zen-menubar-appearance-dark =
.label = Dökkt
zen-menubar-new-unsynced-window =
.label = New Blank Window
.label = Nýr auður gluggi

View File

@@ -19,4 +19,4 @@ zen-menubar-appearance-light =
zen-menubar-appearance-dark =
.label = 다크
zen-menubar-new-unsynced-window =
.label = New Blank Window
.label = 새 빈 창

View File

@@ -314,4 +314,4 @@ zen-devtools-toggle-storage-shortcut = Perjungti saugyklą
zen-devtools-toggle-dom-shortcut = Perjungti DOM
zen-devtools-toggle-accessibility-shortcut = Perjungti prieinamumą
zen-close-all-unpinned-tabs-shortcut = Užverti visas neprisegtas korteles
zen-new-unsynced-window-shortcut = Naujas nesinchronizuotas langas
zen-new-unsynced-window-shortcut = Naujas tuščias langas

View File

@@ -60,8 +60,8 @@ zen-site-data-settings = Nustatymai
zen-generic-manage = Tvarkyti
zen-generic-more = Daugiau
zen-generic-next = Sekantis
zen-essentials-promo-label = Add to Essentials
zen-essentials-promo-sublabel = Keep your favorite tabs just a click away
zen-essentials-promo-label = Įtraukti į būtiniausius
zen-essentials-promo-sublabel = Laikykite mėgstamas korteles vos nuo vienu paspaudimu
# These labels will be used for the site data panel settings
zen-site-data-setting-allow = Leidžiama
zen-site-data-setting-block = Užblokuota
@@ -109,7 +109,7 @@ zen-sidebar-notification-updated-tooltip =
zen-sidebar-notification-restart-safe-mode-label = Kažkas sugedo?
zen-sidebar-notification-restart-safe-mode-tooltip =
.title = Paleisti iš naujo saugioje režime
zen-window-sync-migration-dialog-title = Keep Your Windows in Sync
zen-window-sync-migration-dialog-message = Zen now syncs windows on the same device, so changes in one window are reflected across the others instantly.
zen-window-sync-migration-dialog-learn-more = Learn More
zen-window-sync-migration-dialog-accept = Got It
zen-window-sync-migration-dialog-title = Išlaikykite savo langus sinchronizuotus
zen-window-sync-migration-dialog-message = Zen“ dabar sinchronizuoja langus tame pačiame įrenginyje, todėl viename lange atlikti pakeitimai iš karto atsispindi ir kituose.
zen-window-sync-migration-dialog-learn-more = Sužinoti daugiau
zen-window-sync-migration-dialog-accept = Supratau

View File

@@ -19,4 +19,4 @@ zen-menubar-appearance-light =
zen-menubar-appearance-dark =
.label = Tamsų
zen-menubar-new-unsynced-window =
.label = New Blank Window
.label = Naujas tuščias langas

View File

@@ -19,4 +19,4 @@ zen-menubar-appearance-light =
zen-menubar-appearance-dark =
.label = Escuro
zen-menubar-new-unsynced-window =
.label = New Blank Window
.label = Nova janela em branco

View File

@@ -18,9 +18,9 @@ tab-context-zen-replace-pinned-url-with-current =
.label = Substituir o URL Fixado pelo URL Atual
.accesskey = C
tab-context-zen-edit-title =
.label = Change Label...
.label = Alterar etiqueta...
tab-context-zen-edit-icon =
.label = Change Icon...
.label = Alterar ícone...
zen-themes-corrupted = O seu ficheiro de modificações do { -brand-short-name } está corrompido. Elas foram redefinidas como iguais às do tema padrão.
zen-shortcuts-corrupted = O seu ficheiro de atalhos do { -brand-short-name } está corrompido. Eles foram redefinidos para os atalhos padrão.
# note: Do not translate the "<br/>" tags in the following string
@@ -109,7 +109,7 @@ zen-sidebar-notification-updated-tooltip =
zen-sidebar-notification-restart-safe-mode-label = Falhou alguma coisa?
zen-sidebar-notification-restart-safe-mode-tooltip =
.title = Reiniciar em Modo de Segurança
zen-window-sync-migration-dialog-title = Keep Your Windows in Sync
zen-window-sync-migration-dialog-message = Zen now syncs windows on the same device, so changes in one window are reflected across the others instantly.
zen-window-sync-migration-dialog-title = Mantenha As Suas Janelas Sincronizadas
zen-window-sync-migration-dialog-message = Agora, o Zen sincroniza as janelas no dispositivo, pelo que alterações numa janela são refletidas instantaneamente nas outras.
zen-window-sync-migration-dialog-learn-more = Learn More
zen-window-sync-migration-dialog-accept = Got It
zen-window-sync-migration-dialog-accept = Entendido

View File

@@ -19,4 +19,4 @@ zen-menubar-appearance-light =
zen-menubar-appearance-dark =
.label = Escuro
zen-menubar-new-unsynced-window =
.label = New Blank Window
.label = Nova janela sem sincronização

View File

@@ -314,4 +314,4 @@ zen-devtools-toggle-storage-shortcut = Переключить хранилище
zen-devtools-toggle-dom-shortcut = Переключить DOM
zen-devtools-toggle-accessibility-shortcut = Включить/выключить специальные возможности
zen-close-all-unpinned-tabs-shortcut = Закрыть все не закреплённые вкладки
zen-new-unsynced-window-shortcut = Новое несинхронизированное окно
zen-new-unsynced-window-shortcut = Новое пустое окно

View File

@@ -314,4 +314,4 @@ zen-devtools-toggle-storage-shortcut = Växla lagring
zen-devtools-toggle-dom-shortcut = Växla DOM
zen-devtools-toggle-accessibility-shortcut = Växla tillgänglighetsinställningar
zen-close-all-unpinned-tabs-shortcut = Stäng alla flikar som inte är fästa
zen-new-unsynced-window-shortcut = Nytt osynkroniserat fönster
zen-new-unsynced-window-shortcut = Nytt tomt fönster

View File

@@ -19,4 +19,4 @@ zen-menubar-appearance-light =
zen-menubar-appearance-dark =
.label = Mörkt
zen-menubar-new-unsynced-window =
.label = New Blank Window
.label = Nytt tomt fönster

View File

@@ -314,4 +314,4 @@ zen-devtools-toggle-storage-shortcut = Depolamayı Aç/Kapat
zen-devtools-toggle-dom-shortcut = DOM'u Aç/Kapat
zen-devtools-toggle-accessibility-shortcut = Erişilebilirliği Aç/Kapat
zen-close-all-unpinned-tabs-shortcut = Sabitlenmemiş Tüm Sekmeleri Kapat
zen-new-unsynced-window-shortcut = Senkronize Edilmemiş Yeni Pencere
zen-new-unsynced-window-shortcut = Yeni Boş Pencere

View File

@@ -19,4 +19,4 @@ zen-menubar-appearance-light =
zen-menubar-appearance-dark =
.label = Koyu
zen-menubar-new-unsynced-window =
.label = New Blank Window
.label = Yeni Boş Pencere

View File

@@ -314,4 +314,4 @@ zen-devtools-toggle-storage-shortcut = 開啟儲存空間檢測器
zen-devtools-toggle-dom-shortcut = 開啟 DOM
zen-devtools-toggle-accessibility-shortcut = 開啟輔助功能
zen-close-all-unpinned-tabs-shortcut = 關閉所有未釘選的分頁
zen-new-unsynced-window-shortcut = 開新未同步視窗
zen-new-unsynced-window-shortcut = 開新初始視窗

View File

@@ -19,4 +19,4 @@ zen-menubar-appearance-light =
zen-menubar-appearance-dark =
.label = 暗色
zen-menubar-new-unsynced-window =
.label = 新初始視窗
.label = 新初始視窗

View File

@@ -49,7 +49,7 @@ zen-panel-ui-gradient-click-to-add = 點擊新增顏色
zen-workspace-creation-name =
.placeholder = 工作區名稱
zen-move-tab-to-workspace-button =
.label = 移動至…
.label = 移動至…
.tooltiptext = 將此視窗所有頁面移至單一工作區
zen-workspaces-panel-context-reorder =
.label = 排序工作區

6
package-lock.json generated
View File

@@ -5610,9 +5610,9 @@
}
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"version": "4.17.23",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
"integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
"dev": true,
"license": "MIT"
},

View File

@@ -6,7 +6,7 @@
value: true
- name: zen.session-store.log
value: "@IS_TWILIGHT@"
value: true
- name: zen.session-store.restore-unsynced-windows
value: true

View File

@@ -54,3 +54,6 @@
- name: zen.view.draggable-sidebar
value: true
- name: zen.view.overflow-webext-toolbar
value: true

View File

@@ -13,3 +13,6 @@
- name: zen.window-sync.open-link-in-new-unsynced-window
value: true
- name: zen.window-sync.sync-only-pinned-tabs
value: false

View File

@@ -32,6 +32,9 @@
- name: zen.urlbar.show-pip-button
value: false
- name: zen.urlbar.open-on-startup
value: true
# Mark: Zen suggestions controls
- name: zen.urlbar.suggestions.quick-actions

View File

@@ -1,41 +0,0 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import os
import sys
import requests
BASE_URI = "https://phabricator.services.mozilla.com"
OUTPUT_DIR = os.path.join(os.getcwd(), "src", "firefox-patches")
def download_phab_patch(phab_id, output_file):
"""Download a Phabricator patch by its ID and save it to output_file."""
patch_url = f"{BASE_URI}/{phab_id}?download=true"
try:
print(f"Downloading patch from {patch_url}")
response = requests.get(patch_url)
response.raise_for_status() # Raise an error for bad responses
with open(output_file, 'wb') as f:
f.write(response.content)
print(f"Patch saved to {output_file}")
except requests.RequestException as e:
print(f"Error downloading patch: {e}")
sys.exit(1)
def main():
if len(sys.argv) < 2:
print("Usage: python download_phab_patch.py <PHABRICATOR_ID> [output_file]", file=sys.stderr)
sys.exit(1)
phab_id = sys.argv[1]
output_file = sys.argv[2] if len(sys.argv) > 2 else f"phab_{phab_id}"
output_file = os.path.join(OUTPUT_DIR, output_file + ".patch")
download_phab_patch(phab_id, output_file)
if __name__ == "__main__":
main()

View File

@@ -18,10 +18,18 @@ FILE_PREFIX = """
# This file is autogenerated by scripts/import_external_tests.py
# Do not edit manually.
"""
BROWSER_MANIFEST_LIST_PREFIX = """
BROWSER_CHROME_MANIFESTS += [
"""
XPCSHELL_MANIFESTS_LIST_PREFIX = """
XPCSHELL_TESTS_MANIFESTS += [
"""
FILE_SUFFIX = "]"
VALID_MANIFEST_FILES = ["browser.toml", "xpcshell.toml"]
def get_tests_manifest():
@@ -38,12 +46,17 @@ def validate_tests_path(path, files, ignore_list):
for ignore in ignore_list:
if ignore not in files:
die_with_error(f"Ignore file '{ignore}' not found in tests folder '{path}'")
if "browser.toml" not in files or "browser.js" in ignore_list:
die_with_error(f"'browser.toml' not found in tests folder '{path}'")
if not any(manifest_file in files for manifest_file in VALID_MANIFEST_FILES):
die_with_error(f"None of the valid manifest files {VALID_MANIFEST_FILES} found in tests folder '{path}'")
def disable_and_replace_manifest(manifest, output_path):
toml_file = os.path.join(output_path, "browser.toml")
toml_file = None
for manifest_file in VALID_MANIFEST_FILES:
candidate = os.path.join(output_path, manifest_file)
if os.path.exists(candidate):
toml_file = candidate
break
disabled_tests = manifest.get("disable", [])
with open(toml_file, "r") as f:
data = f.read()
@@ -90,8 +103,17 @@ def write_moz_build_file(manifest):
print(f"Writing moz.build file to '{moz_build_path}'")
with open(moz_build_path, "w") as f:
f.write(FILE_PREFIX)
f.write(BROWSER_MANIFEST_LIST_PREFIX)
for test_suite in manifest.keys():
f.write(f'\t"{test_suite}/browser.toml",\n')
# add for browser.toml first
if not manifest[test_suite].get("xpcshell", False):
f.write(f'\t"{test_suite}/browser.toml",\n')
f.write(FILE_SUFFIX)
f.write(XPCSHELL_MANIFESTS_LIST_PREFIX)
for test_suite in manifest.keys():
# add for xpcshell.toml
if manifest[test_suite].get("xpcshell", False):
f.write(f'\t"{test_suite}/xpcshell.toml",\n')
f.write(FILE_SUFFIX)

View File

@@ -56,7 +56,7 @@ def main():
os.chdir(engine_dir)
def run_mach_with_paths(test_paths):
command = ['./mach', 'mochitest'] + other_args + test_paths
command = ['./mach', 'test'] + other_args + test_paths
# Replace the current process with the mach command
os.execvp(command[0], command)

View File

@@ -0,0 +1,99 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import os
import sys
import json
import requests
from json_with_comments import JSONWithCommentsDecoder
BASE_URI = "https://phabricator.services.mozilla.com"
OUTPUT_DIR = os.path.join(os.getcwd(), "src", "external-patches")
def die(message):
print(f"Error: {message}")
sys.exit(1)
def download_phab_patch(phab_id, output_file):
"""Download a Phabricator patch by its ID and save it to output_file."""
patch_url = f"{BASE_URI}/{phab_id}?download=true"
try:
print(f"Downloading patch from {patch_url}")
response = requests.get(patch_url)
response.raise_for_status() # Raise an error for bad responses
with open(output_file, 'wb') as f:
f.write(response.content)
print(f"Patch saved to {output_file}")
except requests.RequestException as e:
die(f"Failed to download patch {phab_id}: {e}")
def download_patch_from_url(url, output_file):
"""Download a patch from a given URL and save it to output_file."""
try:
print(f"Downloading patch from {url}")
response = requests.get(url)
response.raise_for_status() # Raise an error for bad responses
with open(output_file, 'wb') as f:
f.write(response.content)
print(f"Patch saved to {output_file}")
except requests.RequestException as e:
die(f"Failed to download patch from {url}: {e}")
def main():
with open(os.path.join(OUTPUT_DIR, "manifest.json"), 'r') as f:
manifest = json.load(f, cls=JSONWithCommentsDecoder)
expected_files = set()
for patch in manifest:
if patch.get("type") == "phabricator":
phab_id = patch.get("id")
name = patch.get("name")
if not phab_id or not name:
die(f"Patch entry missing 'id' or 'name': {patch}")
name = name.replace(" ", "_").lower()
output_file = os.path.join(OUTPUT_DIR, "firefox", f"{name}.patch")
print(f"Processing Phabricator patch: {phab_id} -> {output_file}")
download_phab_patch(phab_id, output_file)
expected_files.add(output_file)
elif patch.get("type") == "local":
print(f"Local patch: {patch.get('path')}")
expected_files.add(os.path.join(OUTPUT_DIR, patch.get("path")))
elif patch.get("type") == "patch":
url = patch.get("url")
dest = patch.get("dest")
if not url or not dest:
die(f"Patch entry missing 'url' or 'dest': {patch}")
filename = url.split("/")[-1]
output_file = os.path.join(OUTPUT_DIR, dest, filename)
download_patch_from_url(url, 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)
else:
die(f"Unknown patch type: {patch.get('type')}")
# Check for unexpected files in the output directory
# and remove them if they are not in the expected_files set.
for root, dirs, files in os.walk(OUTPUT_DIR):
for file in files:
if file.endswith(".patch"):
file_path = os.path.join(root, file)
if file_path not in expected_files:
print(f"Removing unexpected patch file: {file_path}")
os.remove(file_path)
if __name__ == "__main__":
main()

View File

@@ -1,5 +1,5 @@
diff --git a/browser/base/content/navigator-toolbox.inc.xhtml b/browser/base/content/navigator-toolbox.inc.xhtml
index 68c24f730d56f548cf1e286198a04f8363529378..71418c93ce7216d71412f2fa67295322bb73abad 100644
index 68c24f730d56f548cf1e286198a04f8363529378..eb9aa5e77cf549062d8d3770f8057ceafe67c317 100644
--- a/browser/base/content/navigator-toolbox.inc.xhtml
+++ b/browser/base/content/navigator-toolbox.inc.xhtml
@@ -2,7 +2,7 @@
@@ -22,16 +22,17 @@ index 68c24f730d56f548cf1e286198a04f8363529378..71418c93ce7216d71412f2fa67295322
<toolbar id="TabsToolbar"
class="browser-toolbar browser-titlebar"
fullscreentoolbar="true"
@@ -62,6 +61,8 @@
@@ -62,6 +61,9 @@
<html:sidebar-pins-promo id="drag-to-pin-promo-card"></html:sidebar-pins-promo>
<arrowscrollbox id="pinned-tabs-container" orient="horizontal" clicktoscroll=""></arrowscrollbox>
<splitter orient="vertical" id="vertical-pinned-tabs-splitter" resizebefore="sibling" resizeafter="none" hidden="true"/>
+<html:div id="zen-overflow-extensions-list" skipintoolbarset="true"></html:div>
+<html:div id="zen-essentials" skipintoolbarset="true"></html:div>
+<html:div id="zen-tabs-wrapper">
<hbox class="tab-drop-indicator" hidden="true"/>
<arrowscrollbox id="tabbrowser-arrowscrollbox" orient="horizontal" flex="1" clicktoscroll="" scrolledtostart="" scrolledtoend="">
<tab is="tabbrowser-tab" class="tabbrowser-tab" selected="true" visuallyselected="" fadein=""/>
@@ -81,6 +82,7 @@
@@ -81,6 +83,7 @@
tooltip="dynamic-shortcut-tooltip"
data-l10n-id="tabs-toolbar-new-tab"/>
<html:span id="tabbrowser-tab-a11y-desc" hidden="true"/>
@@ -39,7 +40,7 @@ index 68c24f730d56f548cf1e286198a04f8363529378..71418c93ce7216d71412f2fa67295322
</tabs>
<toolbarbutton id="new-tab-button"
@@ -106,9 +108,10 @@
@@ -106,9 +109,10 @@
#include private-browsing-indicator.inc.xhtml
<toolbarbutton class="content-analysis-indicator toolbarbutton-1 content-analysis-indicator-icon"/>

View File

@@ -1,5 +1,5 @@
diff --git a/browser/components/customizableui/CustomizableUI.sys.mjs b/browser/components/customizableui/CustomizableUI.sys.mjs
index 9a98f56d83ee38e0f1aa41467b4ddf215c3d90f7..39e947ce083ce3b293337f5dbb40cd0b46db12e2 100644
index 9a98f56d83ee38e0f1aa41467b4ddf215c3d90f7..c50781a1e8fd1a71baf497ba64d85292fa1347f4 100644
--- a/browser/components/customizableui/CustomizableUI.sys.mjs
+++ b/browser/components/customizableui/CustomizableUI.sys.mjs
@@ -14,6 +14,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
@@ -153,7 +153,17 @@ index 9a98f56d83ee38e0f1aa41467b4ddf215c3d90f7..39e947ce083ce3b293337f5dbb40cd0b
/**
* Add a widget to an area.
* If the area to which you try to add is not known to CustomizableUI,
@@ -7858,7 +7858,7 @@ class OverflowableToolbar {
@@ -7798,7 +7798,9 @@ class OverflowableToolbar {
);
if (webExtList && CustomizableUI.isWebExtensionWidget(child.id)) {
+ if (webExtList.id !== "zen-overflow-extensions-list") {
child.setAttribute("cui-anchorid", webExtButtonID);
+ }
webExtList.insertBefore(child, webExtList.firstElementChild);
} else {
child.setAttribute("cui-anchorid", this.#defaultListButton.id);
@@ -7858,7 +7860,7 @@ class OverflowableToolbar {
) {
continue;
}
@@ -162,7 +172,7 @@ index 9a98f56d83ee38e0f1aa41467b4ddf215c3d90f7..39e947ce083ce3b293337f5dbb40cd0b
if (child != aExceptChild) {
sum += getInlineSize(child);
}
@@ -7882,11 +7882,11 @@ class OverflowableToolbar {
@@ -7882,11 +7884,11 @@ class OverflowableToolbar {
parseFloat(style.paddingLeft) -
parseFloat(style.paddingRight) -
toolbarChildrenWidth;
@@ -176,7 +186,7 @@ index 9a98f56d83ee38e0f1aa41467b4ddf215c3d90f7..39e947ce083ce3b293337f5dbb40cd0b
});
lazy.log.debug(
@@ -7901,7 +7901,14 @@ class OverflowableToolbar {
@@ -7901,7 +7903,14 @@ class OverflowableToolbar {
Math.max(targetWidth, targetChildrenWidth)
);
totalAvailWidth = Math.ceil(totalAvailWidth);
@@ -192,7 +202,7 @@ index 9a98f56d83ee38e0f1aa41467b4ddf215c3d90f7..39e947ce083ce3b293337f5dbb40cd0b
return { isOverflowing, targetContentWidth, totalAvailWidth };
}
@@ -7962,7 +7969,11 @@ class OverflowableToolbar {
@@ -7962,7 +7971,11 @@ class OverflowableToolbar {
return;
}
}
@@ -205,7 +215,7 @@ index 9a98f56d83ee38e0f1aa41467b4ddf215c3d90f7..39e947ce083ce3b293337f5dbb40cd0b
lazy.log.debug(
`Need ${minSize} but width is ${totalAvailWidth} so bailing`
);
@@ -7995,7 +8006,7 @@ class OverflowableToolbar {
@@ -7995,7 +8008,7 @@ class OverflowableToolbar {
}
}
if (!inserted) {
@@ -214,7 +224,27 @@ index 9a98f56d83ee38e0f1aa41467b4ddf215c3d90f7..39e947ce083ce3b293337f5dbb40cd0b
}
child.removeAttribute("cui-anchorid");
child.removeAttribute("overflowedItem");
@@ -8340,7 +8351,7 @@ class OverflowableToolbar {
@@ -8121,6 +8134,9 @@ class OverflowableToolbar {
* if no such list exists.
*/
get #webExtList() {
+ if (this.#toolbar.getAttribute("addon-webext-overflowtarget") !== this.#webExtListRef?.id) {
+ this.#webExtListRef = null;
+ }
if (!this.#webExtListRef) {
let targetID = this.#toolbar.getAttribute("addon-webext-overflowtarget");
if (!targetID) {
@@ -8132,6 +8148,9 @@ class OverflowableToolbar {
let win = this.#toolbar.ownerGlobal;
let { panel } = win.gUnifiedExtensions;
this.#webExtListRef = panel.querySelector(`#${targetID}`);
+ if (!this.#webExtListRef) {
+ this.#webExtListRef = win.document.getElementById(targetID);
+ }
}
return this.#webExtListRef;
}
@@ -8340,7 +8359,7 @@ class OverflowableToolbar {
break;
}
case "mousedown": {

View File

@@ -736,6 +736,7 @@ var gZenWorkspacesSettings = {
Services.prefs.addObserver("zen.glance.enabled", tabsUnloaderPrefListener); // We can use the same listener for both prefs
Services.prefs.addObserver("zen.workspaces.separate-essentials", tabsUnloaderPrefListener);
Services.prefs.addObserver("zen.glance.activation-method", tabsUnloaderPrefListener);
Services.prefs.addObserver("zen.window-sync.sync-only-pinned-tabs", tabsUnloaderPrefListener);
Services.prefs.addObserver(
"zen.tabs.ctrl-tab.ignore-essential-tabs",
toggleZenCycleByAttrWarning
@@ -745,6 +746,10 @@ var gZenWorkspacesSettings = {
Services.prefs.removeObserver("zen.glance.enabled", tabsUnloaderPrefListener);
Services.prefs.removeObserver("zen.glance.activation-method", tabsUnloaderPrefListener);
Services.prefs.removeObserver("zen.workspaces.separate-essentials", tabsUnloaderPrefListener);
Services.prefs.removeObserver(
"zen.window-sync.sync-only-pinned-tabs",
tabsUnloaderPrefListener
);
Services.prefs.removeObserver(
"zen.tabs.ctrl-tab.ignore-essential-tabs",
toggleZenCycleByAttrWarning
@@ -1193,6 +1198,16 @@ Preferences.addAll([
type: "bool",
default: false,
},
{
id: "zen.tabs.select-recently-used-on-close",
type: "bool",
default: true,
},
{
id: "zen.window-sync.sync-only-pinned-tabs",
type: "bool",
default: false,
},
]);
Preferences.addSetting({

View File

@@ -20,6 +20,9 @@
<label><html:h2 data-l10n-id="zen-settings-workspaces-header"/></label>
<description class="description-deemphasized" data-l10n-id="zen-settings-workspaces-description" />
<checkbox id="zenWorkspacesSyncUnpinnedTabs"
data-l10n-id="zen-settings-workspaces-sync-unpinned-tabs"
preference="zen.window-sync.sync-only-pinned-tabs"/>
<checkbox id="zenWorkspacesHideDefaultContainer"
data-l10n-id="zen-settings-workspaces-hide-default-container-indicator"
preference="zen.workspaces.hide-default-container-indicator"/>
@@ -37,15 +40,10 @@
class="description-deemphasized"
data-l10n-id="zen-tabs-cycle-by-attribute-warning"
hidden="true"/>
<checkbox data-l10n-id="zen-tabs-select-recently-used-on-close"
preference="zen.tabs.select-recently-used-on-close"/>
</groupbox>
<hbox id="zenTabsUnloadCategory"
class="subcategory"
hidden="true"
data-category="paneZenTabManagement">
<html:h1 data-l10n-id="pane-zen-tabs-unloader-title"/>
</hbox>
<hbox id="zenPinnedTabsManagerCategory"
class="subcategory"
hidden="true"

View File

@@ -1,5 +1,5 @@
diff --git a/browser/components/sessionstore/SessionStartup.sys.mjs b/browser/components/sessionstore/SessionStartup.sys.mjs
index 86600ffb5178599ab23270a964064ca657a3283f..e7014e5bce547d37ec92377a95bad5be4d52152e 100644
index 86600ffb5178599ab23270a964064ca657a3283f..97975af7822a157eafe02ec8bbebdf1a6b564ccb 100644
--- a/browser/components/sessionstore/SessionStartup.sys.mjs
+++ b/browser/components/sessionstore/SessionStartup.sys.mjs
@@ -40,6 +40,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
@@ -28,7 +28,16 @@ index 86600ffb5178599ab23270a964064ca657a3283f..e7014e5bce547d37ec92377a95bad5be
if (this._initialState == null) {
// No valid session found.
this._sessionType = this.NO_SESSION;
@@ -336,12 +339,7 @@ export var SessionStartup = {
@@ -276,6 +279,8 @@ export var SessionStartup = {
`Previous shutdown ok? ${this._previousSessionCrashed}, reason: ${previousSessionCrashedReason}`
);
+ lazy.ZenSessionStore.onCrashCheckpoints(this._initialState);
+
Services.obs.addObserver(this, "sessionstore-windows-restored", true);
if (this.sessionType == this.NO_SESSION) {
@@ -336,12 +341,7 @@ export var SessionStartup = {
isAutomaticRestoreEnabled() {
if (this._resumeSessionEnabled === null) {
this._resumeSessionEnabled =

View File

@@ -1,5 +1,5 @@
diff --git a/browser/components/sessionstore/SessionStore.sys.mjs b/browser/components/sessionstore/SessionStore.sys.mjs
index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96faedf6f0b 100644
index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2061ef7ac60371a563b4e4cd77ceab586f767a5e 100644
--- a/browser/components/sessionstore/SessionStore.sys.mjs
+++ b/browser/components/sessionstore/SessionStore.sys.mjs
@@ -127,6 +127,9 @@ const TAB_EVENTS = [
@@ -79,7 +79,15 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
!lazy.SessionStartup.willRestore()
) {
// We want to split the window up into pinned tabs and unpinned tabs.
@@ -2239,6 +2248,15 @@ var SessionStoreInternal = {
@@ -2211,6 +2220,7 @@ var SessionStoreInternal = {
}
if (newWindowState) {
+ lazy.ZenSessionStore.onRestoringClosedWindow(newWindowState);
// Ensure that the window state isn't hidden
this._restoreCount = 1;
let state = { windows: [newWindowState] };
@@ -2239,6 +2249,15 @@ var SessionStoreInternal = {
});
this._shouldRestoreLastSession = false;
}
@@ -95,7 +103,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
if (this._restoreLastWindow && aWindow.toolbar.visible) {
// always reset (if not a popup window)
@@ -2383,7 +2401,7 @@ var SessionStoreInternal = {
@@ -2383,7 +2402,7 @@ var SessionStoreInternal = {
var tabbrowser = aWindow.gBrowser;
@@ -104,7 +112,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
TAB_EVENTS.forEach(function (aEvent) {
tabbrowser.tabContainer.removeEventListener(aEvent, this, true);
@@ -2434,7 +2452,7 @@ var SessionStoreInternal = {
@@ -2434,7 +2453,7 @@ var SessionStoreInternal = {
let isLastRegularWindow =
Object.values(this._windows).filter(
@@ -113,7 +121,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
).length == 1;
this._log.debug(
`onClose, closing window isLastRegularWindow? ${isLastRegularWindow}`
@@ -2491,8 +2509,8 @@ var SessionStoreInternal = {
@@ -2491,8 +2510,8 @@ var SessionStoreInternal = {
// 2) Flush the window.
// 3) When the flush is complete, revisit our decision to store the window
// in _closedWindows, and add/remove as necessary.
@@ -124,7 +132,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
}
completionPromise = lazy.TabStateFlusher.flushWindow(aWindow).then(() => {
@@ -2512,8 +2530,9 @@ var SessionStoreInternal = {
@@ -2512,8 +2531,9 @@ var SessionStoreInternal = {
// Save non-private windows if they have at
// least one saveable tab or are the last window.
@@ -136,7 +144,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
if (!isLastWindow && winData.closedId > -1) {
this._addClosedAction(
@@ -2589,7 +2608,7 @@ var SessionStoreInternal = {
@@ -2589,7 +2609,7 @@ var SessionStoreInternal = {
* to call this method again asynchronously (for example, after
* a window flush).
*/
@@ -145,7 +153,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
// Make sure SessionStore is still running, and make sure that we
// haven't chosen to forget this window.
if (
@@ -2606,6 +2625,7 @@ var SessionStoreInternal = {
@@ -2606,6 +2626,7 @@ var SessionStoreInternal = {
// _closedWindows from a previous call to this function.
let winIndex = this._closedWindows.indexOf(winData);
let alreadyStored = winIndex != -1;
@@ -153,7 +161,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
// If sidebar command is truthy, i.e. sidebar is open, store sidebar settings
let shouldStore = hasSaveableTabs || isLastWindow;
@@ -3408,7 +3428,7 @@ var SessionStoreInternal = {
@@ -3408,7 +3429,7 @@ var SessionStoreInternal = {
if (!isPrivateWindow && tabState.isPrivate) {
return;
}
@@ -162,7 +170,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
return;
}
@@ -4129,6 +4149,12 @@ var SessionStoreInternal = {
@@ -4129,6 +4150,12 @@ var SessionStoreInternal = {
Math.min(tabState.index, tabState.entries.length)
);
tabState.pinned = false;
@@ -175,7 +183,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
if (inBackground === false) {
aWindow.gBrowser.selectedTab = newTab;
@@ -4565,6 +4591,8 @@ var SessionStoreInternal = {
@@ -4565,6 +4592,8 @@ var SessionStoreInternal = {
// Append the tab if we're opening into a different window,
tabIndex: aSource == aTargetWindow ? pos : Infinity,
pinned: state.pinned,
@@ -184,7 +192,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
userContextId: state.userContextId,
skipLoad: true,
preferredRemoteType,
@@ -5414,7 +5442,7 @@ var SessionStoreInternal = {
@@ -5414,7 +5443,7 @@ var SessionStoreInternal = {
for (let i = tabbrowser.pinnedTabCount; i < tabbrowser.tabs.length; i++) {
let tab = tabbrowser.tabs[i];
@@ -193,7 +201,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
removableTabs.push(tab);
}
}
@@ -5525,7 +5553,7 @@ var SessionStoreInternal = {
@@ -5525,7 +5554,7 @@ var SessionStoreInternal = {
// collect the data for all windows
for (ix in this._windows) {
@@ -202,7 +210,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
// window data is still in _statesToRestore
continue;
}
@@ -5668,11 +5696,12 @@ var SessionStoreInternal = {
@@ -5668,11 +5697,12 @@ var SessionStoreInternal = {
}
let tabbrowser = aWindow.gBrowser;
@@ -216,7 +224,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
// update the internal state data for this window
for (let tab of tabs) {
if (tab == aWindow.FirefoxViewHandler.tab) {
@@ -5683,6 +5712,9 @@ var SessionStoreInternal = {
@@ -5683,6 +5713,9 @@ var SessionStoreInternal = {
tabsData.push(tabData);
}
@@ -226,7 +234,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
// update tab group state for this window
winData.groups = [];
for (let tabGroup of aWindow.gBrowser.tabGroups) {
@@ -5695,7 +5727,7 @@ var SessionStoreInternal = {
@@ -5695,7 +5728,7 @@ var SessionStoreInternal = {
// a window is closed, point to the first item in the tab strip instead (it will never be the Firefox View tab,
// since it's only inserted into the tab strip after it's selected).
if (aWindow.FirefoxViewHandler.tab?.selected) {
@@ -235,7 +243,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
winData.title = tabbrowser.tabs[0].label;
}
winData.selected = selectedIndex;
@@ -5810,8 +5842,8 @@ var SessionStoreInternal = {
@@ -5810,8 +5843,8 @@ var SessionStoreInternal = {
// selectTab represents.
let selectTab = 0;
if (overwriteTabs) {
@@ -246,7 +254,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
selectTab = Math.min(selectTab, winData.tabs.length);
}
@@ -5833,6 +5865,7 @@ var SessionStoreInternal = {
@@ -5833,6 +5866,7 @@ var SessionStoreInternal = {
if (overwriteTabs) {
for (let i = tabbrowser.browsers.length - 1; i >= 0; i--) {
if (!tabbrowser.tabs[i].selected) {
@@ -254,7 +262,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
tabbrowser.removeTab(tabbrowser.tabs[i]);
}
}
@@ -5866,6 +5899,12 @@ var SessionStoreInternal = {
@@ -5866,6 +5900,12 @@ var SessionStoreInternal = {
savedTabGroup => !openTabGroupIdsInWindow.has(savedTabGroup.id)
);
}
@@ -267,7 +275,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
// Move the originally open tabs to the end.
if (initialTabs) {
@@ -6419,6 +6458,25 @@ var SessionStoreInternal = {
@@ -6419,6 +6459,25 @@ var SessionStoreInternal = {
// Most of tabData has been restored, now continue with restoring
// attributes that may trigger external events.
@@ -293,7 +301,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
if (tabData.pinned) {
tabbrowser.pinTab(tab);
@@ -6567,6 +6625,9 @@ var SessionStoreInternal = {
@@ -6567,6 +6626,9 @@ var SessionStoreInternal = {
aWindow.gURLBar.readOnly = false;
}
}
@@ -303,7 +311,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
let promiseParts = Promise.withResolvers();
aWindow.setTimeout(() => {
@@ -7343,7 +7404,7 @@ var SessionStoreInternal = {
@@ -7343,7 +7405,7 @@ var SessionStoreInternal = {
let groupsToSave = new Map();
for (let tIndex = 0; tIndex < window.tabs.length; ) {
@@ -312,7 +320,7 @@ index 2a055f0c5f34f0a2667f659185120c07d38f4e41..2f63071f78782dbc30bde25b3bcaa96f
// Adjust window.selected
if (tIndex + 1 < window.selected) {
window.selected -= 1;
@@ -7358,7 +7419,7 @@ var SessionStoreInternal = {
@@ -7358,7 +7420,7 @@ var SessionStoreInternal = {
);
// We don't want to increment tIndex here.
continue;

View File

@@ -1,5 +1,5 @@
diff --git a/browser/components/tabbrowser/content/drag-and-drop.js b/browser/components/tabbrowser/content/drag-and-drop.js
index 57800333445ec7850742145527e04ae8d504b0bb..65361da4791b6418705f364f750f409b1f2bf82b 100644
index 57800333445ec7850742145527e04ae8d504b0bb..a13875436a72f89178455a09c8665b9a1ef240b5 100644
--- a/browser/components/tabbrowser/content/drag-and-drop.js
+++ b/browser/components/tabbrowser/content/drag-and-drop.js
@@ -35,6 +35,9 @@
@@ -39,7 +39,7 @@ index 57800333445ec7850742145527e04ae8d504b0bb..65361da4791b6418705f364f750f409b
event.stopPropagation();
+ if (draggedTab && dropEffect == "move") {
+ this.handle_drop_transition?.(draggedTab._dragData.dropElement, draggedTab, movingTabs, draggedTab._dragData.dropBefore);
+ gZenPinnedTabManager.moveToAnotherTabContainerIfNecessary(event, movingTabs);
+ gZenPinnedTabManager.moveToAnotherTabContainerIfNecessary(event, draggedTab, movingTabs, this._getDropIndex(event));
+ }
if (draggedTab && dropEffect == "copy") {
let duplicatedDraggedTab;
@@ -102,7 +102,36 @@ index 57800333445ec7850742145527e04ae8d504b0bb..65361da4791b6418705f364f750f409b
postTransitionCleanup();
} else {
let onTransitionEnd = transitionendEvent => {
@@ -584,6 +597,7 @@
@@ -513,7 +526,7 @@
if (tab.selected) {
selectedTab = tab;
indexForSelectedTab = newIndex;
- } else {
+ } else if (false) {
const newTab = gBrowser.adoptTab(tab, {
elementIndex: newIndex,
selectTab: tab == draggedTab,
@@ -523,7 +536,7 @@
}
}
}
- if (selectedTab) {
+ if (false) {
const newTab = gBrowser.adoptTab(selectedTab, {
elementIndex: indexForSelectedTab,
selectTab: selectedTab == draggedTab,
@@ -534,10 +547,6 @@
}
// Restore tab selection
- gBrowser.addRangeToMultiSelectedTabs(
- this._tabbrowserTabs.dragAndDropElements[dropIndex],
- this._tabbrowserTabs.dragAndDropElements[newIndex - 1]
- );
} else {
// Pass true to disallow dropping javascript: or data: urls
let links;
@@ -584,6 +593,7 @@
let nextItem = this._tabbrowserTabs.dragAndDropElements[newIndex];
let tabGroup = isTab(nextItem) && nextItem.group;
@@ -110,7 +139,7 @@ index 57800333445ec7850742145527e04ae8d504b0bb..65361da4791b6418705f364f750f409b
gBrowser.loadTabs(urls, {
inBackground,
replace,
@@ -621,7 +635,16 @@
@@ -621,7 +631,16 @@
this._expandGroupOnDrop(draggedTab);
}
this._resetTabsAfterDrop(draggedTab.ownerDocument);
@@ -128,7 +157,7 @@ index 57800333445ec7850742145527e04ae8d504b0bb..65361da4791b6418705f364f750f409b
if (
dt.mozUserCancelled ||
dt.dropEffect != "none" ||
@@ -825,7 +848,10 @@
@@ -825,7 +844,10 @@
_getDragTarget(event, { ignoreSides = false } = {}) {
let { target } = event;
while (target) {
@@ -140,7 +169,7 @@ index 57800333445ec7850742145527e04ae8d504b0bb..65361da4791b6418705f364f750f409b
break;
}
target = target.parentNode;
@@ -842,14 +868,17 @@
@@ -842,14 +864,17 @@
return null;
}
}
@@ -160,7 +189,7 @@ index 57800333445ec7850742145527e04ae8d504b0bb..65361da4791b6418705f364f750f409b
!this._tabbrowserTabs.expandOnHover
);
}
@@ -880,7 +909,8 @@
@@ -880,7 +905,8 @@
isTabGroupLabel(draggedTab) &&
draggedTab._dragData?.expandGroupOnDrop
) {
@@ -170,7 +199,7 @@ index 57800333445ec7850742145527e04ae8d504b0bb..65361da4791b6418705f364f750f409b
}
}
@@ -1058,7 +1088,6 @@
@@ -1058,7 +1084,6 @@
// using updateDragImage. On Linux, we can use a panel.
if (platform == "win" || platform == "macosx") {
captureListener = function () {
@@ -178,7 +207,7 @@ index 57800333445ec7850742145527e04ae8d504b0bb..65361da4791b6418705f364f750f409b
};
} else {
// Create a panel to use it in setDragImage
@@ -1096,7 +1125,6 @@
@@ -1096,7 +1121,6 @@
);
dragImageOffset = dragImageOffset * scale;
}
@@ -186,7 +215,7 @@ index 57800333445ec7850742145527e04ae8d504b0bb..65361da4791b6418705f364f750f409b
// _dragData.offsetX/Y give the coordinates that the mouse should be
// positioned relative to the corner of the new window created upon
@@ -1115,7 +1143,7 @@
@@ -1115,7 +1139,7 @@
let dropEffect = this.getDropEffectForTabDrag(event);
let isMovingInTabStrip = !fromTabList && dropEffect == "move";
let collapseTabGroupDuringDrag =
@@ -195,7 +224,7 @@ index 57800333445ec7850742145527e04ae8d504b0bb..65361da4791b6418705f364f750f409b
tab._dragData = {
offsetX: this._tabbrowserTabs.verticalMode
@@ -1125,7 +1153,7 @@
@@ -1125,7 +1149,7 @@
? event.screenY - window.screenY - tabOffset
: event.screenY - window.screenY,
scrollPos:
@@ -204,7 +233,7 @@ index 57800333445ec7850742145527e04ae8d504b0bb..65361da4791b6418705f364f750f409b
? this._tabbrowserTabs.pinnedTabsContainer.scrollPosition
: this._tabbrowserTabs.arrowScrollbox.scrollPosition,
screenX: event.screenX,
@@ -1152,6 +1180,7 @@
@@ -1152,6 +1176,7 @@
if (collapseTabGroupDuringDrag) {
tab.group.collapsed = true;
@@ -212,7 +241,7 @@ index 57800333445ec7850742145527e04ae8d504b0bb..65361da4791b6418705f364f750f409b
}
}
}
@@ -1176,6 +1205,7 @@
@@ -1176,6 +1201,7 @@
if (tabStripItemElement.hasAttribute("dragtarget")) {
return;
}
@@ -220,7 +249,7 @@ index 57800333445ec7850742145527e04ae8d504b0bb..65361da4791b6418705f364f750f409b
let isPinned = tab.pinned;
let numPinned = gBrowser.pinnedTabCount;
let dragAndDropElements = this._tabbrowserTabs.dragAndDropElements;
@@ -1601,7 +1631,6 @@
@@ -1601,7 +1627,6 @@
for (let item of this._tabbrowserTabs.dragAndDropElements) {
item = elementToMove(item);
@@ -228,7 +257,7 @@ index 57800333445ec7850742145527e04ae8d504b0bb..65361da4791b6418705f364f750f409b
item.removeAttribute("multiselected-move-together");
delete item._moveTogetherSelectedTabsData;
}
@@ -2429,7 +2458,6 @@
@@ -2429,7 +2454,6 @@
for (let item of this._tabbrowserTabs.dragAndDropElements) {
this._resetGroupTarget(item);
item = elementToMove(item);
@@ -236,7 +265,7 @@ index 57800333445ec7850742145527e04ae8d504b0bb..65361da4791b6418705f364f750f409b
}
this._tabbrowserTabs.removeAttribute("movingtab-group");
this._tabbrowserTabs.removeAttribute("movingtab-ungroup");
@@ -2460,17 +2488,14 @@
@@ -2460,17 +2484,14 @@
tab.style.left = "";
tab.style.top = "";
tab.style.maxWidth = "";

View File

@@ -1,5 +1,5 @@
diff --git a/browser/components/tabbrowser/content/tab.js b/browser/components/tabbrowser/content/tab.js
index 6528da245d922e2ccdb59cbf8106055bc34d1e74..1cd3395961a80e3b48582794b5b5365c9b8386b3 100644
index 6528da245d922e2ccdb59cbf8106055bc34d1e74..b5d5ac633c06d7d9c909ec9456a5f3c165a6b925 100644
--- a/browser/components/tabbrowser/content/tab.js
+++ b/browser/components/tabbrowser/content/tab.js
@@ -21,6 +21,7 @@
@@ -52,7 +52,7 @@ index 6528da245d922e2ccdb59cbf8106055bc34d1e74..1cd3395961a80e3b48582794b5b5365c
return;
}
@@ -224,11 +227,21 @@
@@ -224,11 +227,23 @@
}
get visible() {
@@ -64,7 +64,6 @@ index 6528da245d922e2ccdb59cbf8106055bc34d1e74..1cd3395961a80e3b48582794b5b5365c
+ if (!this.isOpen || this.hidden || this.hasAttribute("zen-empty-tab")) {
+ return false;
+ }
+
+ // Selected tabs are always visible
+ if (this.selected || this.multiselected || this.hasAttribute("folder-active")) return true;
+ // Recursively check all parent groups
@@ -75,11 +74,14 @@ index 6528da245d922e2ccdb59cbf8106055bc34d1e74..1cd3395961a80e3b48582794b5b5365c
+ }
+ currentParent = currentParent.group;
+ }
+ if (this.pinned && !this.hasAttribute("zen-essential") && gZenWorkspaces.activeWorkspaceElement?.hasCollapsedPinnedTabs) {
+ return false;
+ }
+ return true;
}
get hidden() {
@@ -307,7 +320,7 @@
@@ -307,7 +322,7 @@
return false;
}
@@ -88,7 +90,7 @@ index 6528da245d922e2ccdb59cbf8106055bc34d1e74..1cd3395961a80e3b48582794b5b5365c
}
get lastAccessed() {
@@ -384,7 +397,18 @@
@@ -384,7 +399,18 @@
}
get group() {
@@ -108,7 +110,7 @@ index 6528da245d922e2ccdb59cbf8106055bc34d1e74..1cd3395961a80e3b48582794b5b5365c
}
get splitview() {
@@ -489,6 +513,8 @@
@@ -489,6 +515,8 @@
this.style.MozUserFocus = "ignore";
} else if (
event.target.classList.contains("tab-close-button") ||
@@ -117,7 +119,7 @@ index 6528da245d922e2ccdb59cbf8106055bc34d1e74..1cd3395961a80e3b48582794b5b5365c
event.target.classList.contains("tab-icon-overlay") ||
event.target.classList.contains("tab-audio-button")
) {
@@ -543,6 +569,10 @@
@@ -543,6 +571,10 @@
this.style.MozUserFocus = "";
}
@@ -128,7 +130,7 @@ index 6528da245d922e2ccdb59cbf8106055bc34d1e74..1cd3395961a80e3b48582794b5b5365c
on_click(event) {
if (event.button != 0) {
return;
@@ -603,6 +633,14 @@
@@ -603,6 +635,14 @@
// (see tabbrowser-tabs 'click' handler).
gBrowser.tabContainer._blockDblClick = true;
}
@@ -143,7 +145,7 @@ index 6528da245d922e2ccdb59cbf8106055bc34d1e74..1cd3395961a80e3b48582794b5b5365c
}
on_dblclick(event) {
@@ -626,6 +664,8 @@
@@ -626,6 +666,8 @@
animate: true,
triggeringEvent: event,
});

View File

@@ -1,5 +1,5 @@
diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js
index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a1417570d4f1266 100644
index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..c7c7e6dd8cacf5d2d13c48ad9bed8195d677420f 100644
--- a/browser/components/tabbrowser/content/tabbrowser.js
+++ b/browser/components/tabbrowser/content/tabbrowser.js
@@ -398,6 +398,7 @@
@@ -119,7 +119,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
aTab.setAttribute("pinned", "true");
this._updateTabBarForPinnedTabs();
@@ -931,11 +994,18 @@
@@ -931,11 +994,19 @@
}
this.#handleTabMove(aTab, () => {
@@ -133,13 +133,14 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
// and back into arrowscrollbox.
aTab.removeAttribute("pinned");
- this.tabContainer.arrowScrollbox.prepend(aTab);
+ aTab.removeAttribute("zen-essential");
+ if (!handled) {
+ gZenWorkspaces.activeWorkspaceStrip.prepend(aTab);
+ }
});
aTab.style.marginInlineStart = "";
@@ -1112,6 +1182,9 @@
@@ -1112,6 +1183,9 @@
let LOCAL_PROTOCOLS = ["chrome:", "about:", "resource:", "data:"];
@@ -149,7 +150,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
if (
aIconURL &&
!LOCAL_PROTOCOLS.some(protocol => aIconURL.startsWith(protocol))
@@ -1121,6 +1194,9 @@
@@ -1121,6 +1195,9 @@
);
return;
}
@@ -159,7 +160,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
let browser = this.getBrowserForTab(aTab);
browser.mIconURL = aIconURL;
@@ -1393,7 +1469,6 @@
@@ -1393,7 +1470,6 @@
// Preview mode should not reset the owner
if (!this._previewMode && !oldTab.selected) {
@@ -167,7 +168,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
}
let lastRelatedTab = this._lastRelatedTabMap.get(oldTab);
@@ -1484,6 +1559,7 @@
@@ -1484,6 +1560,7 @@
if (!this._previewMode) {
newTab.recordTimeFromUnloadToReload();
newTab.updateLastAccessed();
@@ -175,7 +176,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
oldTab.updateLastAccessed();
// if this is the foreground window, update the last-seen timestamps.
if (this.ownerGlobal == BrowserWindowTracker.getTopWindow()) {
@@ -1636,6 +1712,9 @@
@@ -1636,6 +1713,9 @@
}
let activeEl = document.activeElement;
@@ -185,7 +186,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
// If focus is on the old tab, move it to the new tab.
if (activeEl == oldTab) {
newTab.focus();
@@ -1959,6 +2038,11 @@
@@ -1959,6 +2039,11 @@
}
_setTabLabel(aTab, aLabel, { beforeTabOpen, isContentTitle, isURL } = {}) {
@@ -197,7 +198,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
if (!aLabel || aLabel.includes("about:reader?")) {
return false;
}
@@ -2067,7 +2151,7 @@
@@ -2067,7 +2152,7 @@
newIndex = this.selectedTab._tPos + 1;
}
@@ -206,7 +207,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
if (this.isTabGroupLabel(targetTab)) {
throw new Error(
"Replacing a tab group label with a tab is not supported"
@@ -2342,6 +2426,7 @@
@@ -2342,6 +2427,7 @@
uriIsAboutBlank,
userContextId,
skipLoad,
@@ -214,7 +215,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
} = {}) {
let b = document.createXULElement("browser");
// Use the JSM global to create the permanentKey, so that if the
@@ -2415,8 +2500,7 @@
@@ -2415,8 +2501,7 @@
// we use a different attribute name for this?
b.setAttribute("name", name);
}
@@ -224,7 +225,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
b.setAttribute("transparent", "true");
}
@@ -2581,7 +2665,7 @@
@@ -2581,7 +2666,7 @@
let panel = this.getPanel(browser);
let uniqueId = this._generateUniquePanelID();
@@ -233,7 +234,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
aTab.linkedPanel = uniqueId;
// Inject the <browser> into the DOM if necessary.
@@ -2640,8 +2724,8 @@
@@ -2640,8 +2725,8 @@
// If we transitioned from one browser to two browsers, we need to set
// hasSiblings=false on both the existing browser and the new browser.
if (this.tabs.length == 2) {
@@ -244,7 +245,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
} else {
aTab.linkedBrowser.browsingContext.hasSiblings = this.tabs.length > 1;
}
@@ -2828,7 +2912,6 @@
@@ -2828,7 +2913,6 @@
this.selectedTab = this.addTrustedTab(BROWSER_NEW_TAB_URL, {
tabIndex: tab._tPos + 1,
userContextId: tab.userContextId,
@@ -252,7 +253,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
focusUrlBar: true,
});
resolve(this.selectedBrowser);
@@ -2938,6 +3021,9 @@
@@ -2938,6 +3022,9 @@
schemelessInput,
hasValidUserGestureActivation = false,
textDirectiveUserActivation = false,
@@ -262,7 +263,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
} = {}
) {
// all callers of addTab that pass a params object need to pass
@@ -2948,10 +3034,17 @@
@@ -2948,10 +3035,17 @@
);
}
@@ -280,7 +281,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
// If we're opening a foreground tab, set the owner by default.
ownerTab ??= inBackground ? null : this.selectedTab;
@@ -2959,6 +3052,7 @@
@@ -2959,6 +3053,7 @@
if (this.selectedTab.owner) {
this.selectedTab.owner = null;
}
@@ -288,7 +289,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
// Find the tab that opened this one, if any. This is used for
// determining positioning, and inherited attributes such as the
@@ -3011,6 +3105,22 @@
@@ -3011,6 +3106,22 @@
noInitialLabel,
skipBackgroundNotify,
});
@@ -311,7 +312,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
if (insertTab) {
// Insert the tab into the tab container in the correct position.
this.#insertTabAtIndex(t, {
@@ -3019,6 +3129,7 @@
@@ -3019,6 +3130,7 @@
ownerTab,
openerTab,
pinned,
@@ -319,7 +320,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
bulkOrderedOpen,
tabGroup: tabGroup ?? openerTab?.group,
});
@@ -3037,6 +3148,7 @@
@@ -3037,6 +3149,7 @@
openWindowInfo,
skipLoad,
triggeringRemoteType,
@@ -327,7 +328,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
}));
if (focusUrlBar) {
@@ -3161,6 +3273,12 @@
@@ -3161,6 +3274,12 @@
}
}
@@ -340,7 +341,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
// Additionally send pinned tab events
if (pinned) {
this.#notifyPinnedStatus(t);
@@ -3375,6 +3493,7 @@
@@ -3375,6 +3494,7 @@
isAdoptingGroup = false,
isUserTriggered = false,
telemetryUserCreateSource = "unknown",
@@ -348,7 +349,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
} = {}
) {
if (
@@ -3385,9 +3504,6 @@
@@ -3385,9 +3505,6 @@
!this.isSplitViewWrapper(tabOrSplitView)
)
) {
@@ -358,7 +359,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
}
if (!color) {
@@ -3408,9 +3524,14 @@
@@ -3408,9 +3525,14 @@
label,
isAdoptingGroup
);
@@ -375,7 +376,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
);
group.addTabs(tabsAndSplitViews);
@@ -3531,7 +3652,7 @@
@@ -3531,7 +3653,7 @@
}
this.#handleTabMove(tab, () =>
@@ -384,7 +385,15 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
);
}
@@ -3746,6 +3867,7 @@
@@ -3599,6 +3721,7 @@
color: group.color,
insertBefore: newTabs[0],
isAdoptingGroup: true,
+ forSplitView: group.hasAttribute('split-view-group'),
});
}
@@ -3746,6 +3869,7 @@
openWindowInfo,
skipLoad,
triggeringRemoteType,
@@ -392,7 +401,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
}
) {
// If we don't have a preferred remote type (or it is `NOT_REMOTE`), and
@@ -3815,6 +3937,7 @@
@@ -3815,6 +3939,7 @@
openWindowInfo,
name,
skipLoad,
@@ -400,7 +409,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
});
}
@@ -4003,7 +4126,7 @@
@@ -4003,7 +4128,7 @@
// Add a new tab if needed.
if (!tab) {
let createLazyBrowser =
@@ -409,7 +418,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
let url = "about:blank";
if (tabData.entries?.length) {
@@ -4040,8 +4163,10 @@
@@ -4040,8 +4165,10 @@
insertTab: false,
skipLoad: true,
preferredRemoteType,
@@ -421,7 +430,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
if (select) {
tabToSelect = tab;
}
@@ -4053,7 +4178,8 @@
@@ -4053,7 +4180,8 @@
this.pinTab(tab);
// Then ensure all the tab open/pinning information is sent.
this._fireTabOpen(tab, {});
@@ -431,7 +440,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
let { groupId } = tabData;
const tabGroup = tabGroupWorkingData.get(groupId);
// if a tab refers to a tab group we don't know, skip any group
@@ -4067,7 +4193,10 @@
@@ -4067,7 +4195,10 @@
tabGroup.stateData.id,
tabGroup.stateData.color,
tabGroup.stateData.collapsed,
@@ -443,7 +452,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
);
tabsFragment.appendChild(tabGroup.node);
}
@@ -4112,9 +4241,23 @@
@@ -4112,9 +4243,23 @@
// to remove the old selected tab.
if (tabToSelect) {
let leftoverTab = this.selectedTab;
@@ -459,15 +468,15 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
+ gZenWorkspaces._initialTab._shouldRemove = true;
+ }
+ }
}
+ }
+ else {
+ gZenWorkspaces._tabToRemoveForEmpty = this.selectedTab;
+ }
}
+ this._hasAlreadyInitializedZenSessionStore = true;
if (tabs.length > 1 || !tabs[0].selected) {
this._updateTabsAfterInsert();
@@ -4305,11 +4448,14 @@
@@ -4305,11 +4450,14 @@
if (ownerTab) {
tab.owner = ownerTab;
}
@@ -483,7 +492,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
if (
!bulkOrderedOpen &&
((openerTab &&
@@ -4321,7 +4467,7 @@
@@ -4321,7 +4469,7 @@
let lastRelatedTab =
openerTab && this._lastRelatedTabMap.get(openerTab);
let previousTab = lastRelatedTab || openerTab || this.selectedTab;
@@ -492,7 +501,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
tabGroup = previousTab.group;
}
if (
@@ -4337,7 +4483,7 @@
@@ -4337,7 +4485,7 @@
previousTab.splitview
) + 1;
} else if (previousTab.visible) {
@@ -501,7 +510,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
} else if (previousTab == FirefoxViewHandler.tab) {
elementIndex = 0;
}
@@ -4365,14 +4511,14 @@
@@ -4365,14 +4513,14 @@
}
// Ensure index is within bounds.
if (tab.pinned) {
@@ -520,7 +529,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
if (pinned && !itemAfter?.pinned) {
itemAfter = null;
@@ -4385,7 +4531,7 @@
@@ -4385,7 +4533,7 @@
this.tabContainer._invalidateCachedTabs();
@@ -529,7 +538,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
if (
(this.isTab(itemAfter) && itemAfter.group == tabGroup) ||
this.isSplitViewWrapper(itemAfter)
@@ -4416,7 +4562,11 @@
@@ -4416,7 +4564,11 @@
const tabContainer = pinned
? this.tabContainer.pinnedTabsContainer
: this.tabContainer;
@@ -541,7 +550,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
}
if (tab.group?.collapsed) {
@@ -4431,6 +4581,7 @@
@@ -4431,6 +4583,7 @@
if (pinned) {
this._updateTabBarForPinnedTabs();
}
@@ -549,7 +558,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
TabBarVisibility.update();
}
@@ -4983,6 +5134,7 @@
@@ -4983,6 +5136,7 @@
telemetrySource,
} = {}
) {
@@ -557,7 +566,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
// When 'closeWindowWithLastTab' pref is enabled, closing all tabs
// can be considered equivalent to closing the window.
if (
@@ -5072,6 +5224,7 @@
@@ -5072,6 +5226,7 @@
if (lastToClose) {
this.removeTab(lastToClose, aParams);
}
@@ -565,7 +574,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
} catch (e) {
console.error(e);
}
@@ -5110,6 +5263,12 @@
@@ -5110,6 +5265,12 @@
aTab._closeTimeNoAnimTimerId = Glean.browserTabclose.timeNoAnim.start();
}
@@ -578,7 +587,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
// Handle requests for synchronously removing an already
// asynchronously closing tab.
if (!animate && aTab.closing) {
@@ -5124,6 +5283,9 @@
@@ -5124,6 +5285,9 @@
// state).
let tabWidth = window.windowUtils.getBoundsWithoutFlushing(aTab).width;
let isLastTab = this.#isLastTabInWindow(aTab);
@@ -588,7 +597,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
if (
!this._beginRemoveTab(aTab, {
closeWindowFastpath: true,
@@ -5172,7 +5334,13 @@
@@ -5172,7 +5336,13 @@
// We're not animating, so we can cancel the animation stopwatch.
Glean.browserTabclose.timeAnim.cancel(aTab._closeTimeAnimTimerId);
aTab._closeTimeAnimTimerId = null;
@@ -603,7 +612,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
return;
}
@@ -5306,7 +5474,7 @@
@@ -5306,7 +5476,7 @@
closeWindowWithLastTab != null
? closeWindowWithLastTab
: !window.toolbar.visible ||
@@ -612,7 +621,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
if (closeWindow) {
// We've already called beforeunload on all the relevant tabs if we get here,
@@ -5330,6 +5498,7 @@
@@ -5330,6 +5500,7 @@
newTab = true;
}
@@ -620,7 +629,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
aTab._endRemoveArgs = [closeWindow, newTab];
// swapBrowsersAndCloseOther will take care of closing the window without animation.
@@ -5370,13 +5539,7 @@
@@ -5370,13 +5541,7 @@
aTab._mouseleave();
if (newTab) {
@@ -635,7 +644,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
} else {
TabBarVisibility.update();
}
@@ -5509,6 +5672,7 @@
@@ -5509,6 +5674,7 @@
this.tabs[i]._tPos = i;
}
@@ -643,7 +652,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
if (!this._windowIsClosing) {
// update tab close buttons state
this.tabContainer._updateCloseButtons();
@@ -5732,6 +5896,7 @@
@@ -5732,6 +5898,7 @@
}
let excludeTabs = new Set(aExcludeTabs);
@@ -651,7 +660,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
// If this tab has a successor, it should be selectable, since
// hiding or closing a tab removes that tab as a successor.
@@ -5744,15 +5909,22 @@
@@ -5744,15 +5911,22 @@
!excludeTabs.has(aTab.owner) &&
Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose")
) {
@@ -676,7 +685,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
let tab = this.tabContainer.findNextTab(aTab, {
direction: 1,
filter: _tab => remainingTabs.includes(_tab),
@@ -5766,7 +5938,7 @@
@@ -5766,7 +5940,7 @@
}
if (tab) {
@@ -685,7 +694,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
}
// If no qualifying visible tab was found, see if there is a tab in
@@ -5787,7 +5959,7 @@
@@ -5787,7 +5961,7 @@
});
}
@@ -694,7 +703,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
}
_blurTab(aTab) {
@@ -5798,7 +5970,7 @@
@@ -5798,7 +5972,7 @@
* @returns {boolean}
* False if swapping isn't permitted, true otherwise.
*/
@@ -703,7 +712,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
// Do not allow transfering a private tab to a non-private window
// and vice versa.
if (
@@ -5852,6 +6024,7 @@
@@ -5852,6 +6026,7 @@
// fire the beforeunload event in the process. Close the other
// window if this was its last tab.
if (
@@ -711,7 +720,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
!remoteBrowser._beginRemoveTab(aOtherTab, {
adoptedByTab: aOurTab,
closeWindowWithLastTab: true,
@@ -5863,7 +6036,7 @@
@@ -5863,7 +6038,7 @@
// If this is the last tab of the window, hide the window
// immediately without animation before the docshell swap, to avoid
// about:blank being painted.
@@ -720,7 +729,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
if (closeWindow) {
let win = aOtherTab.ownerGlobal;
win.windowUtils.suppressAnimation(true);
@@ -5987,11 +6160,13 @@
@@ -5987,11 +6162,13 @@
}
// Finish tearing down the tab that's going away.
@@ -734,7 +743,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
this.setTabTitle(aOurTab);
@@ -6193,10 +6368,10 @@
@@ -6193,10 +6370,10 @@
SessionStore.deleteCustomTabValue(aTab, "hiddenBy");
}
@@ -747,7 +756,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
aTab.selected ||
aTab.closing ||
// Tabs that are sharing the screen, microphone or camera cannot be hidden.
@@ -6254,7 +6429,8 @@
@@ -6254,7 +6431,8 @@
*
* @param {MozTabbrowserTab|MozTabbrowserTabGroup|MozTabbrowserTabGroup.labelElement} aTab
*/
@@ -757,7 +766,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
if (this.tabs.length == 1) {
return null;
}
@@ -6278,12 +6454,14 @@
@@ -6278,12 +6456,14 @@
}
// tell a new window to take the "dropped" tab
@@ -768,12 +777,12 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
options,
aTab
);
+ win._zenStartupSyncFlag = zenForceSync ? 'synced' : 'unsynced';
+ win._zenStartupSyncFlag = (zenForceSync || !Services.prefs.getBoolPref("zen.tabs.dnd-open-blank-window", true)) ? 'synced' : 'unsynced';
+ return win;
}
/**
@@ -6388,7 +6566,7 @@
@@ -6388,7 +6568,7 @@
* `true` if element is a `<tab-group>`
*/
isTabGroup(element) {
@@ -782,7 +791,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
}
/**
@@ -6473,8 +6651,8 @@
@@ -6473,8 +6653,8 @@
}
// Don't allow mixing pinned and unpinned tabs.
@@ -793,7 +802,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
} else {
tabIndex = Math.max(tabIndex, this.pinnedTabCount);
}
@@ -6500,10 +6678,16 @@
@@ -6500,10 +6680,16 @@
this.#handleTabMove(
element,
() => {
@@ -812,7 +821,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
if (neighbor && this.isTab(element) && tabIndex > element._tPos) {
neighbor.after(element);
} else {
@@ -6561,23 +6745,31 @@
@@ -6561,23 +6747,31 @@
#moveTabNextTo(element, targetElement, moveBefore = false, metricsContext) {
if (this.isTabGroupLabel(targetElement)) {
targetElement = targetElement.group;
@@ -850,7 +859,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
} else if (!element.pinned && targetElement && targetElement.pinned) {
// If the caller asks to move an unpinned element next to a pinned
// tab, move the unpinned element to be the first unpinned element
@@ -6590,14 +6782,34 @@
@@ -6590,14 +6784,38 @@
// move the tab group right before the first unpinned tab.
// 4. Moving a tab group and the first unpinned tab is grouped:
// move the tab group right before the first unpinned tab's tab group.
@@ -864,6 +873,10 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
+ if (!gZenFolders.canDropElement(element, targetElement)) {
+ element = element.group;
+ }
+ if (!element.hasAttribute('zen-essential') && targetElement?.hasAttribute('zen-essential')) {
+ targetElement = null;
+ moveBefore = false;
+ }
+ // It is necessary to place the check below to avoid inserting an element
+ // inside when the split group is the last element.
+ if (targetElement?.group?.hasAttribute("split-view-group")) {
@@ -886,7 +899,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
element.pinned
? this.tabContainer.pinnedTabsContainer
: this.tabContainer;
@@ -6606,7 +6818,7 @@
@@ -6606,11 +6824,15 @@
element,
() => {
if (moveBefore) {
@@ -895,7 +908,15 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
} else if (targetElement) {
targetElement.after(element);
} else {
@@ -6676,10 +6888,10 @@
+ if (element.pinned) {
+ getContainer().prepend(element);
+ } else {
getContainer().appendChild(element);
+ }
}
},
metricsContext
@@ -6676,10 +6898,10 @@
* @param {TabMetricsContext} [metricsContext]
*/
moveTabToExistingGroup(aTab, aGroup, metricsContext) {
@@ -908,7 +929,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
return;
}
if (aTab.group && aTab.group.id === aGroup.id) {
@@ -6751,6 +6963,7 @@
@@ -6751,6 +6973,7 @@
let state = {
tabIndex: tab._tPos,
@@ -916,7 +937,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
};
if (tab.visible) {
state.elementIndex = tab.elementIndex;
@@ -6777,7 +6990,7 @@
@@ -6777,7 +7000,7 @@
let changedTabGroup =
previousTabState.tabGroupId != currentTabState.tabGroupId;
@@ -925,7 +946,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
tab.dispatchEvent(
new CustomEvent("TabMove", {
bubbles: true,
@@ -6818,6 +7031,10 @@
@@ -6818,6 +7041,10 @@
moveActionCallback();
@@ -936,11 +957,11 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
// Clear tabs cache after moving nodes because the order of tabs may have
// changed.
this.tabContainer._invalidateCachedTabs();
@@ -6869,6 +7086,19 @@
@@ -6869,6 +7096,18 @@
* The new tab in the current window, null if the tab couldn't be adopted.
*/
adoptTab(aTab, { elementIndex, tabIndex, selectTab = false } = {}) {
+ if (window.gZenWorkspaces.currentWindowIsSyncing === aTab.ownerGlobal.gZenWorkspaces.currentWindowIsSyncing) {
+ if (window.gZenWorkspaces.currentWindowIsSyncing && aTab.ownerGlobal.gZenWorkspaces?.currentWindowIsSyncing) {
+ const tabId = aTab.id;
+ const thisTab = window.gZenWindowSync.getItemFromWindow(window, tabId);
+ if (thisTab) {
@@ -951,12 +972,11 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
+ }
+ return thisTab;
+ }
+ return;
+ }
// Swap the dropped tab with a new one we create and then close
// it in the other window (making it seem to have moved between
// windows). We also ensure that the tab we create to swap into has
@@ -6910,6 +7140,8 @@
@@ -6910,6 +7149,8 @@
params.userContextId = aTab.getAttribute("usercontextid");
}
let newTab = this.addWebTab("about:blank", params);
@@ -965,7 +985,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
let newBrowser = this.getBrowserForTab(newTab);
aTab.container.tabDragAndDrop.finishAnimateTabMove();
@@ -7718,7 +7950,7 @@
@@ -7718,7 +7959,7 @@
// preventDefault(). It will still raise the window if appropriate.
break;
}
@@ -974,7 +994,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
window.focus();
aEvent.preventDefault();
break;
@@ -7735,7 +7967,6 @@
@@ -7735,7 +7976,6 @@
}
case "TabGroupCollapse":
aEvent.target.tabs.forEach(tab => {
@@ -982,7 +1002,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
});
break;
case "TabGroupCreateByUser":
@@ -7895,7 +8126,9 @@
@@ -7895,7 +8135,9 @@
let filter = this._tabFilters.get(tab);
if (filter) {
@@ -992,7 +1012,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
let listener = this._tabListeners.get(tab);
if (listener) {
@@ -8698,6 +8931,7 @@
@@ -8698,6 +8940,7 @@
aWebProgress.isTopLevel
) {
this.mTab.setAttribute("busy", "true");
@@ -1000,7 +1020,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
gBrowser._tabAttrModified(this.mTab, ["busy"]);
this.mTab._notselectedsinceload = !this.mTab.selected;
}
@@ -8778,6 +9012,7 @@
@@ -8778,6 +9021,7 @@
// known defaults. Note we use the original URL since about:newtab
// redirects to a prerendered page.
const shouldRemoveFavicon =
@@ -1008,7 +1028,7 @@ index 0eaca7a58e0026237b71b2ad515efe84d9e8c779..9c1fa86151f3d5896c45fef31a141757
!this.mBrowser.mIconURL &&
!ignoreBlank &&
!(originalLocation.spec in FAVICON_DEFAULTS);
@@ -9803,7 +10038,7 @@ var TabContextMenu = {
@@ -9803,7 +10047,7 @@ var TabContextMenu = {
);
contextUnpinSelectedTabs.hidden =
!this.contextTab.pinned || !this.multiselected;

View File

@@ -0,0 +1,3 @@
# Patches imported from LibreWolf
Firefox sometimes makes changes without considering the impact on forks. This is the case of the patches that are imported from LibreWolf, which are already present in LibreWolf but not yet (or never) present in Firefox. Thanks to LibreWolf for making these patches available and for their work in maintaining them. We will keep these patches as long as they are needed, and we will remove them once they are no longer necessary (for example, when they are merged into Firefox or when the issue they solve is no longer present).

View File

@@ -0,0 +1,12 @@
diff --git a/toolkit/moz.configure b/toolkit/moz.configure
--- a/toolkit/moz.configure
+++ b/toolkit/moz.configure
@@ -28,7 +28,7 @@ project_flag(
project_flag(
env="MOZ_APP_UA_NAME",
- default="",
+ default="Firefox",
nargs=1,
help="Application name in the User Agent string",
)

View File

@@ -0,0 +1,24 @@
// 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/.
[
{
"type": "phabricator",
"id": "D279007",
"name": "Fix MacOS Crash on Shutdown Firefox 149"
},
{
"type": "local",
"path": "firefox/no_liquid_glass_icon.patch"
},
{
"type": "patch",
"url": "https://codeberg.org/librewolf/source/raw/branch/main/patches/firefox-in-ua.patch",
"dest": "librewolf",
"replaces": {
// They don't correctly export this patch, so we need to replace
// the parameter's help description with the correct one.
"application": "Application"
}
}
]

View File

@@ -1,13 +0,0 @@
diff --git a/netwerk/protocol/http/moz.build b/netwerk/protocol/http/moz.build
index d2330003caf35c43d6831fb0d356ece513906f78..76c2faf822ddaf645eb03d93380c58b4e3b510c0 100644
--- a/netwerk/protocol/http/moz.build
+++ b/netwerk/protocol/http/moz.build
@@ -227,7 +227,7 @@ LOCAL_INCLUDES += [
"/netwerk/url-classifier",
]
-DEFINES["MOZ_APP_UA_NAME"] = f'"{CONFIG["MOZ_APP_UA_NAME"]}"'
+DEFINES["MOZ_APP_UA_NAME"] = f'"Firefox"'
if CONFIG["MOZ_AUTH_EXTENSION"]:
LOCAL_INCLUDES += [

View File

@@ -1,5 +1,5 @@
diff --git a/toolkit/components/extensions/parent/ext-runtime.js b/toolkit/components/extensions/parent/ext-runtime.js
index 0d7a3e505b6bd30548c6dda1504dd343a517b083..54400def5e02e886765fab68c3854a6b3c24ef2b 100644
index 0d7a3e505b6bd30548c6dda1504dd343a517b083..fb6c6b4ef3eae24995a02f708ec41afd31d812ef 100644
--- a/toolkit/components/extensions/parent/ext-runtime.js
+++ b/toolkit/components/extensions/parent/ext-runtime.js
@@ -333,7 +333,7 @@ this.runtime = class extends ExtensionAPIPersistent {
@@ -7,7 +7,7 @@ index 0d7a3e505b6bd30548c6dda1504dd343a517b083..54400def5e02e886765fab68c3854a6b
getBrowserInfo: function () {
const { name, vendor, version, appBuildID } = Services.appinfo;
- const info = { name, vendor, version, buildID: appBuildID };
+ const info = { name: 'firefox', vendor, version: AppConstants.ZEN_FIREFOX_VERSION, buildID: appBuildID };
+ const info = { name: 'Firefox', vendor, version: AppConstants.ZEN_FIREFOX_VERSION, buildID: appBuildID, zen: {version: AppConstants.MOZ_APP_VERSION_DISPLAY} };
return Promise.resolve(info);
},

View File

@@ -0,0 +1,40 @@
diff --git a/toolkit/xre/nsXREDirProvider.cpp b/toolkit/xre/nsXREDirProvider.cpp
index 64456439499d449ce7f2861b1a5addbeecd61721..d0acdb3082b4805e2b8903f8044c97ddf29419bb 100644
--- a/toolkit/xre/nsXREDirProvider.cpp
+++ b/toolkit/xre/nsXREDirProvider.cpp
@@ -1317,9 +1317,11 @@ nsresult nsXREDirProvider::AppendFromAppData(nsIFile* aFile, bool aIsDotted) {
// Similar to nsXREDirProvider::AppendProfilePath.
// TODO: Bug 1990407 - Evaluate if refactoring might be required there in the
// future?
- if (gAppData->profile) {
+ // Use aIsDotted for a different purpose here, will probably break in the future
+ if (gAppData->profile && aIsDotted) {
nsAutoCString profile;
profile = gAppData->profile;
+ profile = "."_ns + nsDependentCString(gAppData->profile);
MOZ_TRY(aFile->AppendRelativeNativePath(profile));
} else {
nsAutoCString vendor;
@@ -1329,8 +1331,6 @@ nsresult nsXREDirProvider::AppendFromAppData(nsIFile* aFile, bool aIsDotted) {
ToLowerCase(vendor);
ToLowerCase(appName);
- MOZ_TRY(aFile->AppendRelativeNativePath(aIsDotted ? ("."_ns + vendor)
- : vendor));
MOZ_TRY(aFile->AppendRelativeNativePath(appName));
}
@@ -1498,13 +1498,8 @@ nsresult nsXREDirProvider::GetLegacyOrXDGHomePath(const char* aHomeDir,
// If the build was made against a specific profile name, MOZ_APP_PROFILE=
// then make sure we respect this and dont move to XDG directory
- if (gAppData->profile) {
- MOZ_TRY(NS_NewNativeLocalFile(nsDependentCString(aHomeDir),
- getter_AddRefs(localDir)));
- } else {
MOZ_TRY(GetLegacyOrXDGConfigHome(aHomeDir, getter_AddRefs(localDir)));
MOZ_TRY(localDir->Clone(getter_AddRefs(parentDir)));
- }
MOZ_TRY(AppendFromAppData(localDir, false));
}

View File

@@ -25,7 +25,7 @@
content/browser/zen-styles/zen-panel-ui.css (../../zen/common/styles/zen-panel-ui.css)
content/browser/zen-styles/zen-single-components.css (../../zen/common/styles/zen-single-components.css)
content/browser/zen-styles/zen-sidebar.css (../../zen/common/styles/zen-sidebar.css)
content/browser/zen-styles/zen-toolbar.css (../../zen/common/styles/zen-toolbar.css)
* content/browser/zen-styles/zen-toolbar.css (../../zen/common/styles/zen-toolbar.css)
content/browser/zen-styles/zen-browser-container.css (../../zen/common/styles/zen-browser-container.css)
content/browser/zen-styles/zen-omnibox.css (../../zen/common/styles/zen-omnibox.css)
content/browser/zen-styles/zen-popup.css (../../zen/common/styles/zen-popup.css)

View File

@@ -98,6 +98,12 @@ export class nsZenMenuBar {
</menu>`);
document.getElementById("view-menu").after(spacesMenubar);
document.getElementById("zen-spaces-menubar").addEventListener("popupshowing", () => {
if (AppConstants.platform === "linux") {
// On linux, there seems to be a bug where the menu freezes up and makes the browser
// suppiciously unresponsive if we try to update the menu while it's opening.
// See https://github.com/zen-browser/desktop/issues/12024
return;
}
gZenWorkspaces.updateWorkspacesChangeContextMenu();
});
}
@@ -106,7 +112,7 @@ export class nsZenMenuBar {
const openUnsyncedWindowItem = window.MozXULElement.parseXULToFragment(
`<toolbarbutton id="appMenu-new-zen-unsynced-window-button"
class="subviewbutton"
data-l10n-id="zen-menubar-new-unsynced-window"
data-l10n-id="zen-appmenu-new-blank-window"
key="zen-new-unsynced-window"
command="cmd_zenNewNavigatorUnsynced"/>`
).querySelector("toolbarbutton");
@@ -117,13 +123,16 @@ export class nsZenMenuBar {
window.MozXULElement.parseXULToFragment(`
<menuitem id="menu_new_zen_unsynced_window"
class="subviewbutton"
data-l10n-id="zen-menubar-new-unsynced-window"
data-l10n-id="zen-menubar-new-blank-window"
key="zen-new-unsynced-window"
command="cmd_zenNewNavigatorUnsynced"/>`)
);
}
#hideWindowRestoreMenus() {
if (!Services.prefs.getBoolPref("zen.window-sync.enabled", true)) {
return;
}
const itemsToHide = ["appMenuRecentlyClosedWindows", "historyUndoWindowMenu"];
for (const id of itemsToHide) {
const element = PanelMultiView.getViewNode(document, id);

View File

@@ -87,7 +87,6 @@ class ZenStartup {
await delayedStartupPromise;
await SessionStore.promiseAllWindowsRestored;
delete gZenUIManager.promiseInitialized;
this.#initSearchBar();
gZenCompactModeManager.init();
// Fix for https://github.com/zen-browser/desktop/issues/7605, specially in compact mode
if (gURLBar.hasAttribute("breakout-extend")) {
@@ -154,11 +153,6 @@ class ZenStartup {
}
}
#initSearchBar() {
// Only focus the url bar
gURLBar.focus();
}
#checkForWelcomePage() {
if (!Services.prefs.getBoolPref("zen.welcome-screen.seen", false)) {
Services.prefs.setBoolPref("zen.welcome-screen.seen", true);

View File

@@ -891,8 +891,8 @@ window.gZenVerticalTabsManager = {
marginBottom: isLastItem() ? ["0px", "0px"] : [transform, "0px"],
},
{
duration: 0.11,
easing: "ease-out",
duration: 0.075,
easing: "easeOut",
}
)
.then(() => {})
@@ -913,8 +913,8 @@ window.gZenVerticalTabsManager = {
filter: ["blur(1px)", "blur(0px)"],
},
{
duration: 0.11,
easing: "ease-out",
duration: 0.075,
easing: "easeOut",
}
)
.then(() => {})
@@ -949,7 +949,7 @@ window.gZenVerticalTabsManager = {
},
{
duration: 0.075,
easing: "ease-out",
easing: "easeOut",
}
);
},
@@ -1055,7 +1055,7 @@ window.gZenVerticalTabsManager = {
if (!this._hasSetSingleToolbar) {
height = AppConstants.platform == "macosx" ? 34 : 32;
} else if (gURLBar.getAttribute("breakout-extend") !== "true") {
height = 40;
height = 38;
}
if (typeof height !== "undefined") {
gURLBar.style.setProperty("--urlbar-height", `${height}px`);
@@ -1275,6 +1275,15 @@ window.gZenVerticalTabsManager = {
appContentNavbarContaienr.append(windowButtons);
}
if (
this._hasSetSingleToolbar &&
Services.prefs.getBoolPref("zen.view.overflow-webext-toolbar", true)
) {
topButtons.setAttribute("addon-webext-overflowtarget", "zen-overflow-extensions-list");
} else {
topButtons.setAttribute("addon-webext-overflowtarget", "overflowed-extensions-list");
}
gZenCompactModeManager.updateCompactModeContext(isSingleToolbar);
// Always move the splitter next to the sidebar
@@ -1362,7 +1371,7 @@ window.gZenVerticalTabsManager = {
// Check if name is blank, reset if so
// Always remove, so we can always rename and if it's empty,
// it will reset to the original name anyway
if (hasChanged) {
if (hasChanged || (this._tabEdited.zenStaticLabel && newName)) {
this._tabEdited.zenStaticLabel = newName;
gBrowser._setTabLabel(this._tabEdited, newName);
gZenUIManager.showToast("zen-tabs-renamed");
@@ -1403,7 +1412,8 @@ window.gZenVerticalTabsManager = {
if (
this._tabEdited ||
((!Services.prefs.getBoolPref("zen.tabs.rename-tabs") ||
Services.prefs.getBoolPref("browser.tabs.closeTabByDblclick")) &&
(Services.prefs.getBoolPref("browser.tabs.closeTabByDblclick") &&
event.type === "dblclick")) &&
isTab) ||
!gZenVerticalTabsManager._prefsSidebarExpanded
) {

View File

@@ -61,6 +61,9 @@
.zen-pseudo-browser-image {
position: absolute;
transform: scale(1);
width: 100%;
height: 100%;
inset: 0;
opacity: 0.4;
pointer-events: none;

View File

@@ -132,6 +132,7 @@
}
#nav-bar {
overflow: clip;
border-top-color: transparent !important;
:root[zen-single-toolbar="true"] & {

View File

@@ -80,7 +80,7 @@
#urlbar:not([breakout-extend="true"]) {
&:hover .urlbar-background {
background-color: light-dark(rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.2)) !important;
background-color: var(--zen-toolbar-element-bg-hover) !important;
}
}
@@ -309,7 +309,7 @@
order: 2;
}
#notification-popup-box:not([open]) {
#notification-popup-box:not([open]):not(:has(> [showing="true"])) {
margin-inline-start: calc(-10px - 2 * var(--urlbar-icon-padding));
opacity: 0;
transition: all 0.2s;

View File

@@ -0,0 +1,48 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#zen-overflow-extensions-list:not(:empty) {
--uei-icon-size: 14px;
display: flex;
gap: 8px;
padding: 8px 2px;
padding-bottom: 0px;
& .unified-extensions-item {
flex: 1;
margin: 0;
}
& .toolbarbutton-badge-stack {
margin: 0;
width: 100%;
height: 100%;
padding: 8px 0;
justify-content: center;
align-items: center;
}
& .unified-extensions-item-action-button {
background-color: var(--zen-toolbar-element-bg);
height: 30px;
margin: 0;
justify-content: center;
align-items: center;
border-radius: var(--border-radius-medium);
overflow: clip;
padding: 0;
&:hover {
background-color: var(--zen-toolbar-element-bg-hover);
}
}
.unified-extensions-item-contents,
.unified-extensions-item-menu-button,
unified-extensions-item-messagebar-wrapper {
display: none;
}
}

View File

@@ -177,6 +177,7 @@
--toolbarbutton-border-radius: var(--tab-border-radius);
--toolbarbutton-inner-padding: 6px;
--toolbarbutton-outer-padding: 2px;
color: color-mix(in srgb, currentColor 60%, transparent);
transition:
background-color 0.1s,
@@ -191,6 +192,10 @@
}
}
#zen-sidebar-top-buttons toolbarbutton {
padding: 0;
}
.zen-interactive-button {
background: color-mix(in srgb, currentColor 6%, transparent) !important;
transition:

View File

@@ -9,7 +9,7 @@
*/
:host(:is(.anonymous-content-host, notification-message)),
:root {
:root:not([windowtype^="Browser:"]) {
/* Default values */
--zen-border-radius: 7px;
--zen-primary-color: AccentColor;
@@ -141,14 +141,11 @@
--zen-button-padding: 0.6rem 1.2rem;
--zen-toolbar-element-bg: light-dark(
color-mix(in oklch, var(--toolbox-textcolor) 10%, transparent),
color-mix(in oklch, var(--toolbox-textcolor) 8%, transparent),
color-mix(in oklch, var(--toolbox-textcolor) 15%, transparent)
);
--zen-toolbar-element-bg-hover: light-dark(
color-mix(in srgb, var(--zen-toolbar-element-bg) 75%, transparent),
color-mix(in srgb, var(--zen-toolbar-element-bg) 60%, transparent)
);
--zen-toolbar-element-bg-hover: light-dark(rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.2));
/* Toolbar */
--tab-selected-color-scheme: inherit !important;
@@ -222,7 +219,7 @@
--input-border-color: var(--zen-input-border-color) !important;
--zen-themed-toolbar-bg-transparent: light-dark(var(--zen-branding-bg), #171717);
--zen-workspace-indicator-height: 46px;
--zen-workspace-indicator-height: 44px;
&:not([zen-sidebar-expanded='true']) {
--zen-workspace-indicator-height: 38px;
@@ -231,10 +228,7 @@
--toolbar-field-color: var(--toolbox-textcolor) !important;
&[zen-private-window='true'] {
--zen-main-browser-background: linear-gradient(130deg,
color-mix(in srgb, rgb(10, 6, 11) 80%, var(--zen-themed-toolbar-bg-transparent)) 0%,
color-mix(in srgb, rgb(19, 7, 22) 80%, var(--zen-themed-toolbar-bg-transparent)) 100%
);
--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;

View File

@@ -3,6 +3,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
#nav-bar,
#zen-sidebar-top-buttons {
background: transparent;
@@ -11,3 +12,5 @@
:root[inDOMFullscreen="true"] #zen-appcontent-navbar-wrapper {
visibility: collapse;
}
%include zen-overflowing-addons.css

View File

@@ -121,8 +121,11 @@ export const ZenCustomizableUI = new (class {
#initCreateNewButton(window) {
const button = window.document.getElementById("zen-create-new-button");
button.addEventListener("command", (event) => {
if (window.gZenWorkspaces.privateWindowOrDisabled) {
// If we use "mousedown" event for private windows (which open a new tab on "click"), we might end up with
// the urlbar flicking and therefore we use "command" event to avoid that.
let isPrivateMode = window.gZenWorkspaces.privateWindowOrDisabled;
button.addEventListener(isPrivateMode ? "command" : "mousedown", (event) => {
if (isPrivateMode) {
window.document.getElementById("cmd_newNavigatorTab").doCommand();
return;
}

View File

@@ -743,6 +743,10 @@ window.gZenCompactModeManager = {
return;
}
if (this._isTabBeingDragged) {
return;
}
if (this.hoverableElements[i].keepHoverDuration) {
this.flashElement(
target,

View File

@@ -10,8 +10,14 @@
}
#zen-dragover-background {
width: calc(100% - var(--zen-toolbox-padding) * 2 - 5px);
width: calc(100% - var(--zen-toolbox-padding) * 2 - 7px);
left: unset;
right: calc(var(--zen-toolbox-padding) + 3px);
transform: translateY(-4px);
:root:not([zen-single-toolbar="true"]) & {
transform: translateY(calc(-100% - 2px));
}
}
#zen-tabbox-wrapper {

View File

@@ -105,7 +105,7 @@
startTabDrag(event, tab, ...args) {
this.ZenDragAndDropService.onDragStart(1);
gZenCompactModeManager._isTabBeingDragged = true;
super.startTabDrag(event, tab, ...args);
const dt = event.dataTransfer;
if (isTabGroupLabel(tab)) {
@@ -117,9 +117,7 @@
this.originalDragImageArgs = [dragImage, offsetX, offsetY];
dt.setDragImage(...this.originalDragImageArgs);
if (tab.hasAttribute("zen-essential")) {
setTimeout(() => {
tab.style.visibility = "hidden";
}, 0);
tab.style.visibility = "hidden";
}
}
@@ -145,7 +143,7 @@
if (tabClone.hasAttribute("visuallyselected")) {
tabClone.style.transform = "translate(-50%, -50%)";
}
} else if (AppConstants.platform !== "macosx") {
} else if (AppConstants.platform !== "macosx" && !tab.isZenFolder) {
// On windows and linux, we still don't add some extra opaqueness
// for the tab to be more visible. This is a hacky workaround.
// TODO: Make windows and linux DnD use nsZenDragAndDrop::mDragImageOpacity
@@ -674,12 +672,20 @@
if (!isTab(draggedTab)) {
return;
}
const { clientX, clientY } = event;
const { innerWidth, innerHeight } = window;
let { screenX, clientX, screenY, clientY } = event;
if (!screenX && !screenY) {
return;
}
const { innerWidth: winWidth, innerHeight: winHeight } = window;
let allowedMargin = Services.prefs.getIntPref("zen.tabs.dnd-outside-window-margin", 5);
const isOutOfWindow =
clientX < 0 || clientX > innerWidth || clientY < 0 || clientY > innerHeight;
clientX <= allowedMargin ||
clientX >= winWidth - allowedMargin ||
clientY <= allowedMargin ||
clientY >= winHeight - allowedMargin;
if (isOutOfWindow && !this.#isOutOfWindow) {
this.#isOutOfWindow = true;
gZenViewSplitter.onBrowserDragEndToSplit(event, true);
this.#maybeClearVerticalPinnedGridDragOver();
this.clearSpaceSwitchTimer();
this.clearDragOverVisuals();
@@ -703,7 +709,7 @@
this.originalDragImageArgs[1],
this.originalDragImageArgs[2]
);
window.addEventListener("dragover", this.handle_windowDragEnter, {
window.addEventListener("dragenter", this.handle_windowDragEnter, {
once: true,
capture: true,
});
@@ -718,20 +724,22 @@
const dt = event.dataTransfer;
const activeWorkspace = gZenWorkspaces.activeWorkspace;
let draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
if (
isTab(draggedTab) &&
!draggedTab.hasAttribute("zen-essential") &&
draggedTab.getAttribute("zen-workspace-id") != activeWorkspace
) {
const movingTabs = draggedTab._dragData?.movingTabs || [draggedTab];
for (let tab of movingTabs) {
tab.setAttribute("zen-workspace-id", activeWorkspace);
if (draggedTab.ownerGlobal === window) {
if (
!draggedTab.hasAttribute("zen-essential") &&
draggedTab.getAttribute("zen-workspace-id") != activeWorkspace
) {
if (isTab(draggedTab)) {
const movingTabs = draggedTab._dragData?.movingTabs || [draggedTab];
for (let tab of movingTabs) {
tab.setAttribute("zen-workspace-id", activeWorkspace);
}
gBrowser.selectedTab = draggedTab;
} else if (isTabGroupLabel(draggedTab)) {
draggedTab = draggedTab.group;
gZenFolders.changeFolderToSpace(draggedTab, activeWorkspace, { hasDndSwitch: true });
}
}
gBrowser.selectedTab = draggedTab;
}
if (isTabGroupLabel(draggedTab)) {
draggedTab = draggedTab.group;
gZenFolders.changeFolderToSpace(draggedTab, activeWorkspace, { hasDndSwitch: true });
}
gZenWorkspaces.updateTabsContainers();
}
@@ -761,7 +769,8 @@
draggedTab.hasAttribute("zen-essential") ||
draggedTab.getAttribute("zen-workspace-id") != gZenWorkspaces.activeWorkspace ||
!dropElement.visible ||
!draggedTab.visible
!draggedTab.visible ||
draggedTab.ownerGlobal !== window
) {
return;
}
@@ -850,21 +859,22 @@
handle_dragend(event) {
const dt = event.dataTransfer;
const draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
let ownerGlobal = draggedTab?.ownerGlobal;
draggedTab.style.visibility = "";
let currentEssenialContainer = gZenWorkspaces.getCurrentEssentialsContainer();
let currentEssenialContainer = ownerGlobal.gZenWorkspaces.getCurrentEssentialsContainer();
if (currentEssenialContainer?.essentialsPromo) {
currentEssenialContainer.essentialsPromo.remove();
}
// We also call it here to ensure we clear any highlight if the drop happened
// outside of a valid drop target.
gZenFolders.highlightGroupOnDragOver(null);
ownerGlobal.gZenFolders.highlightGroupOnDragOver(null);
this.ZenDragAndDropService.onDragEnd();
super.handle_dragend(event);
this.#removeDragOverBackground();
gZenPinnedTabManager.removeTabContainersDragoverClass();
ownerGlobal.gZenPinnedTabManager.removeTabContainersDragoverClass();
this.#maybeClearVerticalPinnedGridDragOver();
this.originalDragImageArgs = [];
window.removeEventListener("dragover", this.handle_windowDragEnter, { capture: true });
window.removeEventListener("dragenter", this.handle_windowDragEnter, { capture: true });
this.#isOutOfWindow = false;
if (this._browserDragImageWrapper) {
this._browserDragImageWrapper.remove();
@@ -874,6 +884,10 @@
this._tempDragImageParent.remove();
delete this._tempDragImageParent;
}
delete ownerGlobal.gZenCompactModeManager._isTabBeingDragged;
if (dt.dropEffect !== "move") {
ownerGlobal.gZenCompactModeManager._clearAllHoverStates();
}
}
#applyDragOverBackground(element) {
@@ -922,11 +936,13 @@
let dropElementFromEvent = event.target.closest(dropZoneSelector);
dropElement = dropElementFromEvent || dropElement;
if (!dropElementFromEvent) {
let hoveringPeriphery = !!event.target.closest("#tabbrowser-arrowscrollbox-periphery");
let hoveringPeriphery = !!event.target.closest(
":is(#tabbrowser-arrowscrollbox-periphery, .pinned-tabs-container-separator)"
);
if (event.target.classList.contains("zen-workspace-empty-space") || hoveringPeriphery) {
let lastTab = gBrowser.tabs.at(-1);
dropElement =
(hoveringPeriphery
(hoveringPeriphery && Services.prefs.getBoolPref("zen.view.show-newtab-button-top")
? this._tabbrowserTabs.ariaFocusableItems.at(
gBrowser._numVisiblePinTabsWithoutCollapsed
)
@@ -946,7 +962,8 @@
dropElement = dragData.dropElement;
dropBefore = dragData.dropBefore;
}
if (!dropElement) {
// Essentials should be properly handled by ::animateVerticalPinnedGridDragOver
if (!dropElement || dropElement.hasAttribute("zen-essential")) {
this.clearDragOverVisuals();
return null;
}
@@ -973,11 +990,11 @@
(possibleFolderElement.collapsed ||
possibleFolderElement.childGroupsAndTabs.length < 2)));
if (
(isTabGroupLabel(draggedTab) &&
draggedTab.group?.isZenFolder &&
(isTab(dropElement) || dropElement.hasAttribute("split-view-group")) &&
isTabGroupLabel(draggedTab) &&
draggedTab.group?.isZenFolder &&
(((isTab(dropElement) || dropElement.hasAttribute("split-view-group")) &&
(!dropElement.pinned || dropElement.hasAttribute("zen-essential"))) ||
showIndicatorUnderNewTabButton
showIndicatorUnderNewTabButton)
) {
dropElement = null;
this.clearDragOverVisuals();
@@ -1275,6 +1292,9 @@
return;
}
// eslint-disable-next-line mozilla/valid-services
Services.zen.playHapticFeedback();
dragData.animDropElementIndex = newIndex;
dragData.dropElement = tabs[Math.min(newIndex, tabs.length - 1)];
dragData.dropBefore = newIndex < tabs.length;

View File

@@ -21,7 +21,7 @@ export class nsZenFolder extends MozTabbrowserTabGroup {
static rawIcon = new DOMParser().parseFromString(
`
<svg width="28" height="28" viewBox="0 0 28 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<svg width="28" height="28" viewBox="0 0 27 27" fill="none" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient gradientUnits="userSpaceOnUse" x1="14" y1="5.625" x2="14" y2="22.375" id="gradient-0">
<stop offset="0" style="stop-color: rgb(255, 255, 255)"/>
@@ -242,7 +242,7 @@ export class nsZenFolder extends MozTabbrowserTabGroup {
}
get resetButton() {
return this.labelElement.parentElement.querySelector(".tab-reset-button");
return this.labelElement.parentElement?.querySelector(".tab-reset-button") ?? null;
}
unloadAllTabs(event) {

View File

@@ -1038,43 +1038,47 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
const tabFolderWorkingData = new Map();
for (const folderData of data) {
const workingData = {
stateData: folderData,
node: null,
containingTabsFragment: document.createDocumentFragment(),
};
tabFolderWorkingData.set(folderData.id, workingData);
try {
const workingData = {
stateData: folderData,
node: null,
containingTabsFragment: document.createDocumentFragment(),
};
tabFolderWorkingData.set(folderData.id, workingData);
const oldGroup = document.getElementById(folderData.id);
folderData.emptyTabIds.forEach((id) => {
oldGroup?.querySelector(`tab[id="${id}"]`)?.setAttribute("zen-empty-tab", true);
});
if (gBrowser.isTabGroup(oldGroup)) {
if (!folderData.splitViewGroup) {
const folder = this._createFolderNode({
id: folderData.id,
label: folderData.name,
collapsed: folderData.collapsed,
pinned: folderData.pinned,
saveOnWindowClose: folderData.saveOnWindowClose,
workspaceId: folderData.workspaceId,
});
folder.setAttribute("id", folderData.id);
workingData.node = folder;
oldGroup.before(folder);
} else {
workingData.node = oldGroup;
}
while (oldGroup.tabs.length) {
const tab = oldGroup.tabs[0];
if (folderData.workspaceId) {
tab.setAttribute("zen-workspace-id", folderData.workspaceId);
const oldGroup = document.getElementById(folderData.id);
folderData.emptyTabIds.forEach((id) => {
oldGroup?.querySelector(`tab[id="${id}"]`)?.setAttribute("zen-empty-tab", true);
});
if (gBrowser.isTabGroup(oldGroup)) {
if (!folderData.splitViewGroup) {
const folder = this._createFolderNode({
id: folderData.id,
label: folderData.name,
collapsed: folderData.collapsed,
pinned: folderData.pinned,
saveOnWindowClose: folderData.saveOnWindowClose,
workspaceId: folderData.workspaceId,
});
folder.setAttribute("id", folderData.id);
workingData.node = folder;
oldGroup.before(folder);
} else {
workingData.node = oldGroup;
}
while (oldGroup.tabs.length) {
const tab = oldGroup.tabs[0];
if (folderData.workspaceId) {
tab.setAttribute("zen-workspace-id", folderData.workspaceId);
}
workingData.containingTabsFragment.appendChild(tab);
}
if (!folderData.splitViewGroup) {
oldGroup.remove();
}
workingData.containingTabsFragment.appendChild(tab);
}
if (!folderData.splitViewGroup) {
oldGroup.remove();
}
} catch (e) {
console.error("Error restoring Zen Folders session data:", e);
}
}

View File

@@ -640,7 +640,6 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
left: [],
width: [],
height: [],
transform: [],
};
const steps = this.#ARC_CONFIG.ARC_STEPS;
@@ -674,7 +673,6 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
const y =
startPosition.y + distanceY * eased + arcDirection * arcHeight * (1 - (2 * eased - 1) ** 2);
sequence.transform.push(`translate(-50%, -50%)`);
sequence.top.push(`${y}px`);
sequence.left.push(`${x}px`);
sequence.width.push(`${currentWidth}px`);

View File

@@ -800,6 +800,10 @@ class nsZenKeyboardShortcutsLoader {
continue;
}
let parsed = KeyShortcut.parseFromXHTML(key, { group: "devTools" });
// Move "inspector" shortcut to use "L" key instead of "I"
if (parsed.getID() == "key_inspector" || parsed.getID() == "key_inspectorMac") {
parsed.setNewBinding("L");
}
newShortcutList.push(parsed);
}
@@ -1109,7 +1113,7 @@ class nsZenKeyboardShortcutsVersioner {
if (version < 15) {
// Migrate from version 13 to 14
// Add shortcut to open a new unsynced window: Default accelt+option+N (Ctrl+Alt+N on non-macOS)
// Add shortcut to open a new unsynced window: Default accelt+shift+N
data.push(
new KeyShortcut(
"zen-new-unsynced-window",

View File

@@ -15,5 +15,6 @@ DIRS += [
"urlbar",
"toolkit",
"sessionstore",
"space-routing",
"share",
]

View File

@@ -10,7 +10,9 @@ const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
SessionStore: "resource:///modules/sessionstore/SessionStore.sys.mjs",
SessionStartup: "resource:///modules/sessionstore/SessionStartup.sys.mjs",
gWindowSyncEnabled: "resource:///modules/zen/ZenWindowSync.sys.mjs",
gSyncOnlyPinnedTabs: "resource:///modules/zen/ZenWindowSync.sys.mjs",
DeferredTask: "resource://gre/modules/DeferredTask.sys.mjs",
});
@@ -51,6 +53,9 @@ class nsZenSidebarObject {
}
set data(data) {
if (typeof data !== "object") {
throw new Error("Sidebar data must be an object");
}
this.#sidebar = data;
}
}
@@ -84,6 +89,7 @@ export class nsZenSessionManager {
compression: "lz4",
backupFile,
});
this.log("Session file path:", this.#file.path);
this.#deferredBackupTask = new lazy.DeferredTask(async () => {
await this.#createBackupsIfNeeded();
}, REGENERATION_DEBOUNCE_RATE_MS);
@@ -150,6 +156,34 @@ export class nsZenSessionManager {
folderIcon: row.getResultByName("folder_icon"),
isFolderCollapsed: Boolean(row.getResultByName("is_folder_collapsed")),
}));
try {
data.recoveryData = await IOUtils.readJSON(
PathUtils.join(
Services.dirsvc.get("ProfD", Ci.nsIFile).path,
"sessionstore-backups",
"recovery.jsonlz4"
),
{ decompress: true }
);
this.log("Recovered recovery data from sessionstore-backups");
} catch {
/* ignore errors reading recovery data */
}
if (!data.recoverYData) {
try {
data.recoveryData = await IOUtils.readJSON(
PathUtils.join(
Services.dirsvc.get("ProfD", Ci.nsIFile).path,
"sessionstore-backups",
"recovery.jsonlz4"
),
{ decompress: true }
);
this.log("Recovered recovery data from sessionstore-backups");
} catch {
/* ignore errors reading recovery data */
}
}
this._migrationData = data;
} catch {
/* ignore errors during migration */
@@ -165,6 +199,7 @@ export class nsZenSessionManager {
async readFile() {
let fileExists = await IOUtils.exists(this.#storeFilePath);
if (!fileExists) {
this.log("Session file does not exist, running migration", this.#storeFilePath);
this._shouldRunMigration = true;
}
this.init();
@@ -181,6 +216,7 @@ export class nsZenSessionManager {
}
this.#sidebar = this.#file.data || {};
if (!this.#sidebar.spaces?.length && !this._shouldRunMigration) {
this.log("No spaces data found in session file, running migration", this.#sidebar);
// If we have no spaces data, we should run migration
// to restore them from the database. Note we also do a
// check if we already planned to run migration for optimization.
@@ -189,6 +225,20 @@ export class nsZenSessionManager {
}
}
get #shouldRestoreOnlyPinned() {
return (
Services.prefs.getIntPref("browser.startup.page", 1) !== BROWSER_STARTUP_RESUME_SESSION ||
lazy.PrivateBrowsingUtils.permanentPrivateBrowsing
);
}
get #shouldRestoreFromCrash() {
return (
lazy.SessionStartup.previousSessionCrashed &&
Services.prefs.getBoolPref("browser.sessionstore.resume_from_crash")
);
}
/**
* Called when the session file is read. Restores the sidebar data
* into all windows.
@@ -197,9 +247,6 @@ export class nsZenSessionManager {
* The initial session state read from the session file.
*/
onFileRead(initialState) {
if (!lazy.gWindowSyncEnabled) {
return initialState;
}
// For the first time after migration, we restore the tabs
// That where going to be restored by SessionStore. The sidebar
// object will always be empty after migration because we haven't
@@ -207,6 +254,16 @@ export class nsZenSessionManager {
if (this._shouldRunMigration) {
initialState = this.#runStateMigration(initialState);
}
if (!lazy.gWindowSyncEnabled) {
if (initialState?.windows?.length && this.#shouldRestoreOnlyPinned) {
this.log("Window sync disabled, restoring only pinned tabs");
for (let i = 0; i < initialState.windows.length; i++) {
let winData = initialState.windows[i];
winData.tabs = (winData.tabs || []).filter((tab) => tab.pinned);
}
}
return initialState;
}
// If there are no windows, we create an empty one. By default,
// firefox would create simply a new empty window, but we want
// to make sure that the sidebar object is properly initialized.
@@ -221,12 +278,22 @@ export class nsZenSessionManager {
},
];
}
return initialState;
}
/**
* Called after @onFileRead, when session startup has crash checkpoint information available.
* Restores the sidebar data into all windows, and runs any crash checkpoint related logic,
* such as restoring only pinned tabs if the previous session was not crashed and the user
* preference is set to do so.
*
* @param {object} initialState
* The initial session state read from the session file, possibly modified by onFileRead.
*/
onCrashCheckpoints(initialState) {
// When we don't have browser.startup.page set to resume session,
// we only want to restore the pinned tabs into the new windows.
const shouldRestoreOnlyPinned =
Services.prefs.getIntPref("browser.startup.page", 1) !== BROWSER_STARTUP_RESUME_SESSION ||
lazy.PrivateBrowsingUtils.permanentPrivateBrowsing;
if (shouldRestoreOnlyPinned && this.#sidebar?.tabs) {
if (this.#shouldRestoreOnlyPinned && !this.#shouldRestoreFromCrash && this.#sidebar?.tabs) {
this.log("Restoring only pinned tabs into windows");
const sidebar = this.#sidebar;
sidebar.tabs = (sidebar.tabs || []).filter((tab) => tab.pinned);
@@ -253,12 +320,11 @@ export class nsZenSessionManager {
}
this.#restoreWindowData(winData);
}
} else {
} else if (initialState) {
this.log("Saving windata state after migration");
this.saveState(initialState);
this.saveState(Cu.cloneInto(initialState, {}));
}
delete this._shouldRunMigration;
return initialState;
}
get #sidebar() {
@@ -276,8 +342,19 @@ export class nsZenSessionManager {
* @param {object} initialState
* The initial session state read from the session file.
*/
// eslint-disable-next-line complexity
#runStateMigration(initialState) {
this.log("Restoring tabs from Places DB after migration", initialState, this._migrationData);
this.log(
"Restoring tabs from Places DB after migration",
initialState,
initialState?.lastSessionState,
this._migrationData
);
if (!initialState?.windows?.length && this._migrationData?.recoveryData) {
this.log("Using recovery data for migration");
initialState = this._migrationData.recoveryData;
}
delete this._migrationData?.recoveryData;
// Restore spaces into the sidebar object if we don't
// have any yet.
if (!this.#sidebar.spaces?.length) {
@@ -286,6 +363,12 @@ export class nsZenSessionManager {
spaces: this._migrationData?.spaces || [],
};
}
if (
!initialState?.windows?.length &&
(initialState?.lastSessionState || initialState?.deferredInitialState)
) {
initialState = { ...(initialState.lastSessionState || initialState.deferredInitialState) };
}
// There might be cases where there are no windows in the
// initial state, for example if the user had 'restore previous
// session' disabled before migration. In that case, we try
@@ -321,12 +404,28 @@ export class nsZenSessionManager {
}
}
}
// Save the state to the sidebar object so that it gets written
// to the session file.
delete this._migrationData;
return initialState;
}
onRestoringClosedWindow(aWinData) {
// We only want to save all pinned tabs if the user preference allows it.
// See https://github.com/zen-browser/desktop/issues/12307
if (this.#shouldRestoreOnlyPinned && aWinData?.tabs?.length) {
this.log("Restoring only pinned tabs for closed window");
this.#filterUnpinnedTabs(aWinData);
}
}
/**
* Filters out all unpinned tabs and groups from the given window data object.
*
* @param {object} aWindow - The window data object to filter.
*/
#filterUnpinnedTabs(aWindow) {
aWindow.tabs = aWindow.tabs.filter((tab) => tab.pinned);
aWindow.groups = aWindow.groups?.filter((group) => group.pinned);
}
/**
* Determines if a given window data object is saveable.
*
@@ -345,7 +444,7 @@ export class nsZenSessionManager {
saveState(state) {
let windows = state?.windows || [];
windows = windows.filter((win) => this.#isWindowSaveable(win));
if (!windows.length || !lazy.gWindowSyncEnabled) {
if (!windows.length) {
// Don't save (or even collect) anything in permanent private
// browsing mode. We also don't want to save if there are no windows.
return;
@@ -353,10 +452,11 @@ export class nsZenSessionManager {
this.#collectWindowData(windows);
// This would save the data to disk asynchronously or when
// quitting the app.
this.#file.data = this.#sidebar;
let sidebar = this.#sidebar;
this.#file.data = sidebar;
this.#file.saveSoon();
this.#debounceRegeneration();
this.log(`Saving Zen session data with ${this.#sidebar.tabs?.length || 0} tabs`);
this.log(`Saving Zen session data with ${sidebar.tabs?.length || 0} tabs`);
}
/**
@@ -457,6 +557,13 @@ export class nsZenSessionManager {
this.#sidebar = sidebarData;
}
/**
* Filters out tabs that are not useful to restore, such as empty tabs with no group association.
* If removeUnpinnedTabs is true, it also filters out unpinned tabs.
*
* @param {Array} tabs - The array of tab data objects to filter.
* @returns {Array} The filtered array of tab data objects.
*/
#filterUnusedTabs(tabs) {
return tabs.filter((tab) => {
// We need to ignore empty tabs with no group association
@@ -506,10 +613,34 @@ export class nsZenSessionManager {
if (!sidebar) {
return;
}
aWindowData.tabs = sidebar.tabs || [];
aWindowData.splitViewData = sidebar.splitViewData;
// If we should only sync the pinned tabs, we should only edit the unpinned
// tabs in the window data and keep the pinned tabs from the window data,
// as they should be the same as the ones in the sidebar.
if (lazy.gSyncOnlyPinnedTabs) {
let pinnedTabs = (sidebar.tabs || []).filter((tab) => tab.pinned);
let unpinedWindowTabs = [];
if (!this.#shouldRestoreOnlyPinned) {
unpinedWindowTabs = (aWindowData.tabs || []).filter((tab) => !tab.pinned);
}
aWindowData.tabs = [...pinnedTabs, ...unpinedWindowTabs];
// We restore ALL the split view data in the sidebar, if the group doesn't exist in the window,
// it should be a no-op anyways.
aWindowData.splitViewData = [
...(sidebar.splitViewData || []),
...(aWindowData.splitViewData || []),
];
// Same thing with groups, we restore all the groups from the sidebar, if they don't have any
// existing tabs in the window, they should be a no-op.
aWindowData.groups = [...(sidebar.groups || []), ...(aWindowData.groups || [])];
} else {
aWindowData.tabs = sidebar.tabs || [];
aWindowData.splitViewData = sidebar.splitViewData;
aWindowData.groups = sidebar.groups;
}
// Folders are always pinned, so we dont need to check for the pinned state here.
aWindowData.folders = sidebar.folders;
aWindowData.groups = sidebar.groups;
aWindowData.spaces = sidebar.spaces;
}
@@ -525,7 +656,7 @@ export class nsZenSessionManager {
* Whether this new window is being restored from a closed window.
*/
restoreNewWindow(aWindow, SessionStoreInternal, fromClosedWindow = false) {
if (aWindow.gZenWorkspaces?.privateWindowOrDisabled || !lazy.gWindowSyncEnabled) {
if (aWindow.gZenWorkspaces?.privateWindowOrDisabled) {
return;
}
this.log("Restoring new window with Zen session data");
@@ -535,14 +666,20 @@ export class nsZenSessionManager {
);
let windowToClone = windows[0] || {};
let newWindow = Cu.cloneInto(windowToClone, {});
let shouldRestoreOnlyPinned = !lazy.gWindowSyncEnabled || lazy.gSyncOnlyPinnedTabs;
if (windows.length < 2) {
// We only want to restore the sidebar object if we found
// only one normal window to clone from (which is the one
// we are opening).
this.log("Restoring sidebar data into new window");
this.#restoreWindowData(newWindow);
shouldRestoreOnlyPinned ||= this.#shouldRestoreOnlyPinned;
}
newWindow.tabs = this.#filterUnusedTabs(newWindow.tabs || []);
if (shouldRestoreOnlyPinned) {
// Don't bring over any unpinned tabs if window sync is disabled or if syncing only pinned tabs.
this.#filterUnpinnedTabs(newWindow);
}
// These are window-specific from the previous window state that
// we don't want to restore into the new window. Otherwise, new

View File

@@ -15,9 +15,16 @@ ChromeUtils.defineESModuleGetters(lazy, {
ZenSessionStore: "resource:///modules/zen/ZenSessionManager.sys.mjs",
TabStateCache: "resource:///modules/sessionstore/TabStateCache.sys.mjs",
setTimeout: "resource://gre/modules/Timer.sys.mjs",
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
});
XPCOMUtils.defineLazyPreferenceGetter(lazy, "gWindowSyncEnabled", "zen.window-sync.enabled");
XPCOMUtils.defineLazyPreferenceGetter(lazy, "gWindowSyncEnabled", "zen.window-sync.enabled", true);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"gSyncOnlyPinnedTabs",
"zen.window-sync.sync-only-pinned-tabs",
true
);
XPCOMUtils.defineLazyPreferenceGetter(lazy, "gShouldLog", "zen.window-sync.log", true);
const OBSERVING = ["browser-window-before-show", "sessionstore-windows-restored"];
@@ -140,7 +147,7 @@ class nsZenWindowSync {
}
init() {
if (!lazy.gWindowSyncEnabled || this.#initialized) {
if (this.#initialized) {
return;
}
this.#initialized = true;
@@ -189,6 +196,10 @@ class nsZenWindowSync {
// to avoid confusing the old private window behavior.
let forcedSync = !aWindow.gZenWorkspaces?.privateWindowOrDisabled;
let hasUnsyncedArg = false;
// See issue https://github.com/zen-browser/desktop/issues/12211
if (lazy.PrivateBrowsingUtils.isWindowPrivate(aWindow)) {
aWindow._zenStartupSyncFlag = "synced";
}
if (aWindow._zenStartupSyncFlag === "synced") {
forcedSync = true;
} else if (aWindow._zenStartupSyncFlag === "unsynced") {
@@ -236,6 +247,9 @@ class nsZenWindowSync {
if (tab.pinned && !tab._zenPinnedInitialState) {
await this.setPinnedTabState(tab);
}
if (!lazy.gWindowSyncEnabled || (lazy.gSyncOnlyPinnedTabs && !tab.pinned)) {
tab._zenContentsVisible = true;
}
}
});
}
@@ -309,8 +323,11 @@ class nsZenWindowSync {
) {
return;
}
if (!lazy.gWindowSyncEnabled && !UNSYNCED_WINDOW_EVENTS.includes(aEvent.type)) {
return;
}
if (INSTANT_EVENTS.includes(aEvent.type)) {
this.#handleNextEvent(aEvent);
this.#handleNextEventInternal(aEvent);
return;
}
if (this.#eventHandlingContext.window && this.#eventHandlingContext.window !== window) {
@@ -357,30 +374,31 @@ class nsZenWindowSync {
this.#syncHandlers.delete(aHandler);
}
#handleNextEventInternal(aEvent) {
const handler = `on_${aEvent.type}`;
if (typeof this[handler] !== "function") {
throw new Error(`No handler for event type: ${aEvent.type}`);
}
return this[handler](aEvent);
}
/**
* Handles the next event by calling the appropriate handler method.
*
* @param {Event} aEvent - The event to handle.
*/
#handleNextEvent(aEvent) {
const handler = `on_${aEvent.type}`;
async #handleNextEvent(aEvent) {
try {
if (typeof this[handler] === "function") {
let promise = this[handler](aEvent) || Promise.resolve();
promise.then(() => {
for (let syncHandler of this.#syncHandlers) {
try {
syncHandler(aEvent);
} catch (e) {
console.error(e);
}
}
});
return promise;
}
throw new Error(`No handler for event type: ${aEvent.type}`);
await this.#handleNextEventInternal(aEvent);
} catch (e) {
return Promise.reject(e);
console.error(e);
}
for (let syncHandler of this.#syncHandlers) {
try {
syncHandler(aEvent);
} catch (e) {
console.error(e);
}
}
}
@@ -514,7 +532,11 @@ class nsZenWindowSync {
*/
#moveItemToMatchOriginal(aOriginalItem, aTargetItem, aWindow, { isEssential, isPinned }) {
const { gBrowser, gZenWorkspaces } = aWindow;
const originalSibling = aOriginalItem.previousElementSibling;
let originalSibling = aOriginalItem.previousElementSibling;
if (originalSibling?.classList.contains("space-fake-collapsible-start")) {
// Skip space fake elements.
originalSibling = originalSibling.previousElementSibling;
}
let isFirstTab = true;
if (gBrowser.isTabGroup(originalSibling) || gBrowser.isTab(originalSibling)) {
isFirstTab =
@@ -769,39 +791,41 @@ class nsZenWindowSync {
#styleSwapedBrowsers(aOurTab, aOtherTab, callback = undefined, promiseToWait = null) {
const ourBrowser = aOurTab.linkedBrowser;
const otherBrowser = aOtherTab.linkedBrowser;
return new Promise((resolve) => {
aOurTab.ownerGlobal.requestAnimationFrame(async () => {
if (callback) {
const browserBlob = await aOtherTab.ownerGlobal.PageThumbs.captureToBlob(
aOtherTab.linkedBrowser,
{
fullScale: true,
fullViewport: true,
}
);
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve) => {
if (callback) {
const browserBlob = await aOtherTab.ownerGlobal.PageThumbs.captureToBlob(
aOtherTab.linkedBrowser,
{
fullScale: true,
fullViewport: true,
}
);
let mySrc = await new Promise((r, re) => {
const reader = new FileReader();
reader.readAsDataURL(browserBlob);
reader.onloadend = function () {
// result includes identifier 'data:image/png;base64,' plus the base64 data
r(reader.result);
};
reader.onerror = function () {
re(new Error("Failed to read blob as data URL"));
};
});
this.#createPseudoImageForBrowser(otherBrowser, mySrc);
otherBrowser.setAttribute("zen-pseudo-hidden", "true");
await promiseToWait;
callback();
}
let mySrc = await new Promise((r, re) => {
const reader = new FileReader();
reader.readAsDataURL(browserBlob);
reader.onloadend = function () {
// result includes identifier 'data:image/png;base64,' plus the base64 data
r(reader.result);
};
reader.onerror = function () {
re(new Error("Failed to read blob as data URL"));
};
});
await promiseToWait;
this.#createPseudoImageForBrowser(otherBrowser, mySrc);
this.#maybeRemovePseudoImageForBrowser(ourBrowser);
ourBrowser.removeAttribute("zen-pseudo-hidden");
resolve();
});
otherBrowser.setAttribute("zen-pseudo-hidden", "true");
callback();
} else {
this.#maybeRemovePseudoImageForBrowser(ourBrowser);
ourBrowser.removeAttribute("zen-pseudo-hidden");
}
resolve();
});
}
@@ -903,7 +927,9 @@ class nsZenWindowSync {
// Ignore previous tabs that are still "active". These scenarios could happen for example,
// when selecting on a split view tab that was already active.
if (aPreviousTab?._zenContentsVisible && !activeTabs.includes(aPreviousTab)) {
let tabsToSwap = aPreviousTab.splitView ? aPreviousTab.group.tabs : [aPreviousTab];
let tabsToSwap = aPreviousTab.group?.hasAttribute("split-view-group")
? aPreviousTab.group.tabs
: [aPreviousTab];
for (const tab of tabsToSwap) {
const otherTabToShow = this.#getActiveTabFromOtherWindows(aWindow, tab.id, (t) =>
t?.splitView ? t.group.tabs.some((st) => st.selected) : t?.selected
@@ -938,6 +964,9 @@ class nsZenWindowSync {
*/
#delegateGenericSyncEvent(aEvent, flags = 0) {
const item = aEvent.target;
if (lazy.gSyncOnlyPinnedTabs && !item.pinned) {
return;
}
this.#syncItemForAllWindows(item, flags);
}
@@ -1074,17 +1103,20 @@ class nsZenWindowSync {
/* Mark: Event Handlers */
on_TabOpen(aEvent) {
on_TabOpen(aEvent, { duringPinning = false } = {}) {
const tab = aEvent.target;
const window = tab.ownerGlobal;
const isUnsyncedWindow = window.gZenWorkspaces.privateWindowOrDisabled;
if (tab.id) {
if (tab.id && !duringPinning) {
// This tab was opened as part of a sync operation.
return;
}
tab._zenContentsVisible = true;
tab.id = this.#newTabSyncId;
if (isUnsyncedWindow) {
if (lazy.gSyncOnlyPinnedTabs && !tab.pinned) {
return;
}
if (isUnsyncedWindow || !lazy.gWindowSyncEnabled) {
return;
}
this.#runOnAllWindows(window, (win) => {
@@ -1101,6 +1133,9 @@ class nsZenWindowSync {
SYNC_FLAG_ICON | SYNC_FLAG_LABEL | SYNC_FLAG_MOVE
);
});
if (duringPinning && tab?.splitView) {
this.on_ZenSplitViewTabsSplit({ target: tab.group });
}
this.#maybeFlushTabState(tab);
}
@@ -1122,7 +1157,8 @@ class nsZenWindowSync {
}
on_TabMove(aEvent) {
return this.#delegateGenericSyncEvent(aEvent, SYNC_FLAG_MOVE);
this.#delegateGenericSyncEvent(aEvent, SYNC_FLAG_MOVE);
return Promise.resolve();
}
on_TabPinned(aEvent) {
@@ -1134,7 +1170,14 @@ class nsZenWindowSync {
if (!tab._zenPinnedInitialState) {
tabStatePromise = this.setPinnedTabState(tab);
}
return Promise.all([tabStatePromise, this.on_TabMove(aEvent)]);
return Promise.all([
tabStatePromise,
this.on_TabMove(aEvent).then(() => {
if (lazy.gSyncOnlyPinnedTabs) {
this.on_TabOpen({ target: tab }, { duringPinning: true });
}
}),
]);
}
on_TabUnpinned(aEvent) {
@@ -1145,7 +1188,11 @@ class nsZenWindowSync {
delete targetTab._zenPinnedInitialState;
}
});
return this.on_TabMove(aEvent);
return this.on_TabMove(aEvent).then(() => {
if (lazy.gSyncOnlyPinnedTabs) {
this.on_TabClose({ target: tab });
}
});
}
on_TabAddedToEssentials(aEvent) {
@@ -1168,6 +1215,9 @@ class nsZenWindowSync {
}
on_focus(aEvent) {
if (typeof aEvent.target !== "object") {
return;
}
const { ownerGlobal: window } = aEvent.target;
if (
!window?.gBrowser ||
@@ -1333,4 +1383,6 @@ class nsZenWindowSync {
// eslint-disable-next-line mozilla/valid-lazy
export const gWindowSyncEnabled = lazy.gWindowSyncEnabled;
// eslint-disable-next-line mozilla/valid-lazy
export const gSyncOnlyPinnedTabs = lazy.gSyncOnlyPinnedTabs;
export const ZenWindowSync = new nsZenWindowSync();

View File

@@ -0,0 +1,17 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
import { JSONFile } from "resource://gre/modules/JSONFile.sys.mjs";
class nsZenSpaceRouting {
#jsonFile = null;
constructor() {
this.#jsonFile = new JSONFile({
path: PathUtils.join(PathUtils.profileDir, "space-routing.json"),
});
}
}
export var ZenSpaceRouting = new nsZenSpaceRouting();

View File

@@ -0,0 +1,7 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXTRA_JS_MODULES.zen += [
"ZenSpaceRouting.sys.mjs",
]

View File

@@ -208,6 +208,10 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
if (typeof groupIndex === "undefined") {
groupIndex = this._data.findIndex((group) => group.tabs.includes(tab));
}
// If groupIndex === -1, so `this._data.findIndex` couldn't find the split group
if (groupIndex < 0) {
return;
}
const group = this._data[groupIndex];
const tabIndex = group.tabs.indexOf(tab);
group.tabs.splice(tabIndex, 1);
@@ -259,6 +263,33 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
return element;
}
_calculateDropSide(event, panelsRect) {
const { width, height } = panelsRect;
const { clientX, clientY } = event;
// TODO(octaviusz): Maybe we should add this as preference
// `zen.splitView.tab-drop-treshold`
const quarterWidth = width / 4;
const quarterHeight = height / 4;
const edges = [
{ side: "left", dist: clientX - panelsRect.left, threshold: quarterWidth },
{ side: "right", dist: panelsRect.right - clientX, threshold: quarterWidth },
{ side: "top", dist: clientY - panelsRect.top, threshold: quarterHeight },
{ side: "bottom", dist: panelsRect.bottom - clientY, threshold: quarterHeight },
];
let closestEdge = null;
let minDist = Infinity;
for (const edge of edges) {
if (edge.dist < edge.threshold && edge.dist < minDist) {
minDist = edge.dist;
closestEdge = edge;
}
}
return closestEdge ? closestEdge.side : null;
}
// eslint-disable-next-line complexity
onBrowserDragOverToSplit(event) {
gBrowser.tabContainer.tabDragAndDrop.clearSpaceSwitchTimer();
@@ -303,6 +334,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
}
const panelsRect = gBrowser.tabbox.getBoundingClientRect();
const panelsWidth = panelsRect.width;
const panelsHeight = panelsRect.height;
if (
event.clientX > panelsRect.left + panelsWidth - 10 ||
event.clientX < panelsRect.left + 10 ||
@@ -311,11 +343,17 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
) {
return;
}
const dropSide = this._calculateDropSide(event, panelsRect);
if (!dropSide) {
return;
}
// first quarter or last quarter of the screen, but not the middle
if (
!(
event.clientX < panelsRect.left + panelsWidth / 4 ||
event.clientX > panelsRect.left + (panelsWidth / 4) * 3
event.clientX > panelsRect.left + (panelsWidth / 4) * 3 ||
event.clientY < panelsRect.top + panelsHeight / 4 ||
event.clientY > panelsRect.top + (panelsHeight / 4) * 3
)
) {
return;
@@ -336,93 +374,113 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
this._canDrop = true;
// eslint-disable-next-line mozilla/valid-services
Services.zen.playHapticFeedback();
{
this._draggingTab = draggedTab;
gBrowser.selectedTab = oldTab;
this._hasAnimated = true;
this.tabBrowserPanel.setAttribute("dragging-split", "true");
// Add a min width to all the browser elements to prevent them from resizing
// eslint-disable-next-line no-shadow
const panelsWidth = gBrowser.tabbox.getBoundingClientRect().width;
let numOfTabsToDivide = 2;
if (currentView) {
numOfTabsToDivide = currentView.tabs.length + 1;
this._draggingTab = draggedTab;
gBrowser.selectedTab = oldTab;
this._hasAnimated = true;
this.tabBrowserPanel.setAttribute("dragging-split", "true");
this._animateDropEdge(dropSide, currentView, draggedTab, oldTab);
}
_animateDropEdge(dropSide, currentView, draggedTab, oldTab) {
// Add a min width to all the browser elements to prevent them from resizing
// eslint-disable-next-line no-shadow
const { height, width } = gBrowser.tabbox.getBoundingClientRect();
let numOfTabsToDivide = 2;
if (currentView) {
numOfTabsToDivide = currentView.tabs.length + 1;
}
const halfWidth = width / numOfTabsToDivide;
const halfHeight = height / numOfTabsToDivide;
const side = dropSide;
for (const browser of gBrowser.browsers) {
if (!browser) {
continue;
}
const halfWidth = panelsWidth / numOfTabsToDivide;
let threshold =
gNavToolbox.getBoundingClientRect().width *
(gZenVerticalTabsManager._prefsRightSide ? 0 : 1);
if (gZenCompactModeManager.preference) {
threshold = 0;
const { width: browserWidth, height: browserHeight } = browser.getBoundingClientRect();
// Only apply it to the left side because if we add it to the right side,
// we wont be able to move the element to the left.
// FIXME: This is a workaround, we should find a better way to do this
switch (side) {
case "left":
browser.style.minWidth = `${browserWidth}px`;
break;
case "top":
browser.style.minHeight = `${browserHeight}px`;
break;
}
const side = event.clientX - threshold > halfWidth ? "right" : "left";
for (const browser of gBrowser.browsers) {
if (!browser) {
continue;
}
this.fakeBrowser = document.createXULElement("vbox");
window.addEventListener("dragend", this.onBrowserDragEndToSplit, { once: true });
const padding = ZenThemeModifier.elementSeparation;
this.fakeBrowser.setAttribute("flex", "1");
this.fakeBrowser.id = "zen-split-view-fake-browser";
if (oldTab.splitView) {
this.fakeBrowser.setAttribute("has-split-view", "true");
}
gBrowser.tabbox.appendChild(this.fakeBrowser);
this.fakeBrowser.setAttribute("side", side);
let animateTabBox = null;
let animateFakeBrowser = null;
switch (side) {
case "left":
animateTabBox = {
padding: [0, `0 0 0 ${halfWidth}px`],
};
animateFakeBrowser = {
width: [0, `${halfWidth - padding}px`],
margin: [0, `0 0 0 ${-halfWidth}px`],
};
break;
case "right":
animateTabBox = {
padding: [0, `0 ${halfWidth}px 0 0`],
};
animateFakeBrowser = {
width: [0, `${halfWidth - padding}px`],
};
break;
case "top":
animateTabBox = {
padding: [0, `${halfHeight}px 0 0 0`],
};
animateFakeBrowser = {
height: [0, `${halfHeight - padding}px`],
margin: [0, `${-halfHeight}px 0 0 0`],
};
break;
case "bottom":
animateTabBox = {
padding: [0, `0 0 ${halfHeight}px 0`],
};
animateFakeBrowser = {
height: [0, `${halfHeight - padding}px`],
};
break;
}
this._finishAllAnimatingPromise = Promise.all([
gZenUIManager.motion.animate(gBrowser.tabbox, animateTabBox, {
duration: 0.1,
easing: "ease-out",
}),
gZenUIManager.motion.animate(this.fakeBrowser, animateFakeBrowser, {
duration: 0.1,
easing: "ease-out",
}),
]);
if (this._finishAllAnimatingPromise) {
this._finishAllAnimatingPromise.then(() => {
if (draggedTab !== oldTab) {
draggedTab.linkedBrowser.docShellIsActive = false;
draggedTab.linkedBrowser
.closest(".browserSidebarContainer")
.classList.remove("deck-selected");
}
const width = browser.getBoundingClientRect().width;
// Only apply it to the left side because if we add it to the right side,
// we wont be able to move the element to the left.
// FIXME: This is a workaround, we should find a better way to do this
if (side === "left") {
browser.style.minWidth = `${width}px`;
}
}
this.fakeBrowser = document.createXULElement("vbox");
window.addEventListener("dragend", this.onBrowserDragEndToSplit, { once: true });
const padding = ZenThemeModifier.elementSeparation;
this.fakeBrowser.setAttribute("flex", "1");
this.fakeBrowser.id = "zen-split-view-fake-browser";
if (oldTab.splitView) {
this.fakeBrowser.setAttribute("has-split-view", "true");
}
gBrowser.tabbox.appendChild(this.fakeBrowser);
this.fakeBrowser.setAttribute("side", side);
this._finishAllAnimatingPromise = Promise.all([
gZenUIManager.motion.animate(
gBrowser.tabbox,
side === "left"
? {
paddingLeft: [0, `${halfWidth}px`],
paddingRight: 0,
}
: {
paddingRight: [0, `${halfWidth}px`],
paddingLeft: 0,
},
{
duration: 0.1,
easing: "ease-out",
}
),
gZenUIManager.motion.animate(
this.fakeBrowser,
{
width: [0, `${halfWidth - padding}px`],
...(side === "left"
? {
marginLeft: [0, `${-halfWidth}px`],
}
: {}),
},
{
duration: 0.1,
easing: "ease-out",
}
),
]);
if (this._finishAllAnimatingPromise) {
this._finishAllAnimatingPromise.then(() => {
if (draggedTab !== oldTab) {
draggedTab.linkedBrowser.docShellIsActive = false;
draggedTab.linkedBrowser
.closest(".browserSidebarContainer")
.classList.remove("deck-selected");
}
this.fakeBrowser.addEventListener("dragleave", this.onBrowserDragEndToSplit);
this._canDrop = true;
});
}
this.fakeBrowser.addEventListener("dragleave", this.onBrowserDragEndToSplit);
this._canDrop = true;
});
}
}
@@ -447,12 +505,14 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
return;
}
const panelsWidth = panelsRect.width;
const panelsHeight = panelsRect.height;
let numOfTabsToDivide = 2;
const currentView = this._data[this._lastOpenedTab.splitViewValue];
if (currentView) {
numOfTabsToDivide = currentView.tabs.length + 1;
}
const halfWidth = panelsWidth / numOfTabsToDivide;
const halfHeight = panelsHeight / numOfTabsToDivide;
const padding = ZenThemeModifier.elementSeparation;
if (!this.fakeBrowser) {
return;
@@ -464,39 +524,60 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
...gBrowser.tabContainer.tabDragAndDrop.originalDragImageArgs
);
this._canDrop = false;
Promise.all([
gZenUIManager.motion.animate(
gBrowser.tabbox,
side === "left"
? {
paddingLeft: [`${halfWidth}px`, 0],
}
: {
paddingRight: [`${halfWidth}px`, 0],
},
{
duration: 0.1,
easing: "ease-out",
}
),
gZenUIManager.motion.animate(
this.fakeBrowser,
{
width: [`${halfWidth - padding * 2}px`, 0],
...(side === "left"
? {
marginLeft: [`${-halfWidth}px`, 0],
}
: {}),
},
{
duration: 0.1,
easing: "ease-out",
}
),
]).finally(() => {
this._maybeRemoveFakeBrowser();
});
let animateTabBox = null;
let animateFakeBrowser = null;
switch (side) {
case "left":
animateTabBox = {
padding: [`0 0 0 ${halfWidth}px`, 0],
};
animateFakeBrowser = {
width: [`${halfWidth - padding}px`, 0],
margin: [`0 0 0 ${-halfWidth}px`, 0],
};
break;
case "right":
animateTabBox = {
padding: [`0 ${halfWidth}px 0 0`, 0],
};
animateFakeBrowser = {
width: [`${halfWidth - padding}px`, 0],
};
break;
case "top":
animateTabBox = {
padding: [`${halfHeight}px 0 0 0`, 0],
};
animateFakeBrowser = {
height: [`${halfHeight - padding}px`, 0],
margin: [`${-halfHeight}px 0 0 0`, 0],
};
break;
case "bottom":
animateTabBox = {
padding: [`0 0 ${halfHeight}px 0`, 0],
};
animateFakeBrowser = {
height: [`${halfHeight - padding}px`, 0],
};
break;
}
this._finishAllAnimatingPromise = Promise.all([
gZenUIManager.motion.animate(gBrowser.tabbox, animateTabBox, {
duration: 0.1,
easing: "ease-out",
}),
gZenUIManager.motion.animate(this.fakeBrowser, animateFakeBrowser, {
duration: 0.1,
easing: "ease-out",
}),
]);
if (this._finishAllAnimatingPromise) {
this._finishAllAnimatingPromise.then(() => {
this._maybeRemoveFakeBrowser();
});
}
}
/**
@@ -1386,10 +1467,10 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
new nsSplitLeafNode(tabs[i], 50),
new nsSplitLeafNode(tabs[i + 1], 50),
];
rootNode.addChild(columnNode);
rootNode.addChild(columnNode, false);
}
if (tabs.length % 2 !== 0) {
rootNode.addChild(new nsSplitLeafNode(tabs[tabs.length - 1], rowWidth));
rootNode.addChild(new nsSplitLeafNode(tabs[tabs.length - 1], rowWidth), false);
}
}
@@ -1845,12 +1926,24 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
const dropSide = this.fakeBrowser?.getAttribute("side");
const containerRect = this.fakeBrowser.getBoundingClientRect();
const padding = ZenThemeModifier.elementSeparation;
const dropTarget = document.elementFromPoint(
dropSide === "left"
? containerRect.left + containerRect.width + padding + 5
: containerRect.left - padding - 5,
event.clientY
);
let targetX = event.clientX;
let targetY = event.clientY;
switch (dropSide) {
case "left":
targetX = containerRect.left + containerRect.width + padding + 5;
break;
case "right":
targetX = containerRect.left - padding - 5;
break;
case "top":
targetY = containerRect.top + containerRect.height + padding + 5;
break;
case "bottom":
targetY = containerRect.top - padding - 5;
break;
}
const dropTarget = document.elementFromPoint(targetX, targetY);
const browser =
dropTarget?.closest("browser") ??
dropTarget?.closest(".browserSidebarContainer")?.querySelector("browser");
@@ -1862,7 +1955,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
let droppedOnTab = gZenGlanceManager.getTabOrGlanceParent(gBrowser.getTabForBrowser(browser));
if (droppedOnTab === this._draggingTab) {
this.createEmptySplit(dropSide == "right");
this.createEmptySplit(dropSide);
return true;
}
@@ -1919,29 +2012,21 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
}
}
}
const droppedOnSplitNode = this.getSplitNodeFromTab(droppedOnTab);
const parentNode = droppedOnSplitNode.parent;
// Then add the tab to the split view
group.tabs.push(draggedTab);
// If dropping on a side, create a new split in that direction
// If dropping on a side, wrap entire layout in a new split at the root level
if (hoverSide !== "center") {
const splitDirection = hoverSide === "left" || hoverSide === "right" ? "row" : "column";
if (parentNode.direction !== splitDirection) {
this.splitIntoNode(
droppedOnSplitNode,
new nsSplitLeafNode(draggedTab, 50),
hoverSide,
0.5
);
const rootNode = group.layoutTree;
const prepend = hoverSide === "left" || hoverSide === "top";
if (rootNode.direction === splitDirection) {
// Root has the same direction, add as a new child of the root
this.addTabToSplit(draggedTab, rootNode, prepend);
} else {
this.addTabToSplit(
draggedTab,
parentNode,
/* prepend = */ hoverSide === "left" || hoverSide === "top"
);
// Different direction, wrap root in a new split node
this.splitIntoNode(rootNode, new nsSplitLeafNode(draggedTab, 50), hoverSide, 0.5);
}
} else {
this.addTabToSplit(draggedTab, group.layoutTree);
@@ -1951,13 +2036,14 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
}
} else {
// Create new split view with layout based on drop position
let gridType = "vsep";
const gridType = dropSide === "top" || dropSide === "bottom" ? "hsep" : "vsep";
const topOrLeft = dropSide === "top" || dropSide === "left";
// Put tabs always as if it was dropped from the left
this.splitTabs(
dropSide == "left" ? [draggedTab, droppedOnTab] : [droppedOnTab, draggedTab],
topOrLeft ? [draggedTab, droppedOnTab] : [droppedOnTab, draggedTab],
gridType,
dropSide == "left" ? 0 : 1
topOrLeft ? 0 : 1
);
}
@@ -2093,45 +2179,53 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
this._sessionRestoring = true;
for (const groupData of data) {
const group = document.getElementById(groupData.groupId);
if (!gBrowser.isTabGroup(group)) {
continue;
}
// Backwards compatibility
group.setAttribute("split-view-group", "true");
if (!groupData?.layoutTree) {
this.splitTabs(group.tabs, group.gridType);
delete this._sessionRestoring;
return;
}
const deserializeNode = (nodeData) => {
if (nodeData.type === "leaf") {
const tab = document.getElementById(nodeData.tabId);
if (!tab) {
return null;
}
return new nsSplitLeafNode(tab, nodeData.sizeInParent);
try {
const group = document.getElementById(groupData.groupId);
if (!gBrowser.isTabGroup(group)) {
continue;
}
const splitter = new nsSplitNode(nodeData.direction, nodeData.sizeInParent);
splitter._children = [];
for (const childData of nodeData.children) {
const childNode = deserializeNode(childData);
if (childNode) {
childNode.parent = splitter;
splitter._children.push(childNode);
}
// Backwards compatibility
group.setAttribute("split-view-group", "true");
if (!groupData?.layoutTree) {
this.splitTabs(group.tabs, group.gridType);
delete this._sessionRestoring;
return;
}
return splitter;
};
const deserializeNode = (nodeData) => {
if (nodeData.type === "leaf") {
const tab = document.getElementById(nodeData.tabId);
if (!tab) {
return null;
}
return new nsSplitLeafNode(tab, nodeData.sizeInParent);
}
const layout = deserializeNode(groupData.layoutTree);
const splitData = this.splitTabs(group.tabs, groupData.gridType, -1);
splitData.layoutTree = layout;
const splitter = new nsSplitNode(nodeData.direction, nodeData.sizeInParent);
splitter._children = [];
for (const childData of nodeData.children) {
const childNode = deserializeNode(childData);
if (childNode) {
childNode.parent = splitter;
splitter._children.push(childNode);
}
}
return splitter;
};
const layout = deserializeNode(groupData.layoutTree);
const splitData = this.splitTabs(group.tabs, groupData.gridType, -1);
if (splitData) {
splitData.layoutTree = layout;
} else {
gBrowser.removeTabGroup(group);
}
} catch (e) {
console.error("Error restoring split view session data:", e);
}
}
delete this._sessionRestoring;
@@ -2184,14 +2278,16 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
}
}
createEmptySplit(rightSide = true) {
createEmptySplit(side = "right") {
const selectedTab = gBrowser.selectedTab;
const emptyTab = gZenWorkspaces._emptyTab;
let tabs = rightSide ? [selectedTab, emptyTab] : [emptyTab, selectedTab];
const gridType = side === "top" || side === "bottom" ? "hsep" : "vsep";
const topOrLeft = side === "top" || side === "left";
let tabs = topOrLeft ? [emptyTab, selectedTab] : [selectedTab, emptyTab];
const data = {
tabs,
gridType: "grid",
layoutTree: this.calculateLayoutTree(tabs, "grid"),
gridType,
layoutTree: this.calculateLayoutTree(tabs, gridType),
};
this.#withoutSplitViewTransition(() => {
this._data.push(data);
@@ -2226,9 +2322,9 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
gBrowser.selectedTab = selectedTab;
this.resetTabState(emptyTab, false);
this.splitTabs(
rightSide ? [selectedTab, newSelectedTab] : [newSelectedTab, selectedTab],
"grid",
rightSide ? 1 : 0
topOrLeft ? [newSelectedTab, selectedTab] : [selectedTab, newSelectedTab],
gridType,
topOrLeft ? 0 : 1
);
} else {
cleanup();

View File

@@ -44,12 +44,12 @@
#zen-splitview-dropzone {
position: absolute !important;
margin: var(--zen-split-column-gap) var(--zen-split-row-gap) !important;
margin-bottom: 0 !important;
margin-left: 0 !important;
margin: var(--zen-split-column-gap) var(--zen-split-row-gap);
margin-bottom: 0;
margin-left: 0;
&.browserSidebarContainer:not([zen-split='true']) {
margin-top: 0 !important;
margin-top: 0;
visibility: hidden;
}
}
@@ -204,6 +204,15 @@
overflow: hidden;
will-change: width, margin-left;
&[side='top'],
&[side='bottom'] {
width: 100%;
&[has-split-view='true'] {
width: calc(100% - var(--zen-element-separation));
}
}
&[side='right'] {
right: 0;
@@ -211,6 +220,10 @@
right: var(--zen-element-separation);
}
}
&[side='bottom'] {
bottom: 0;
}
}
#zen-split-view-drag-image {

View File

@@ -76,6 +76,17 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
this._zenClickEventListener = this._onTabClick.bind(this);
gZenWorkspaces._resolvePinnedInitialized();
if (lazy.zenPinnedTabRestorePinnedTabsToPinnedUrl) {
gZenWorkspaces.promiseInitialized.then(() => {
for (const tab of gZenWorkspaces.allStoredTabs) {
try {
this.resetPinnedTab(tab);
} catch (ex) {
console.error("Error restoring pinned tab:", ex);
}
}
});
}
}
log(message) {
@@ -130,6 +141,7 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
tab.removeEventListener("click", tab._zenClickEventListener);
delete tab._zenClickEventListener;
}
this.resetPinChangedUrl(tab);
break;
default:
console.warn("ZenPinnedTabManager: Unhandled tab event", action);
@@ -162,7 +174,7 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
this._resetTabToStoredState(tab);
}
async replacePinnedUrlWithCurrent(tab = undefined) {
replacePinnedUrlWithCurrent(tab = undefined) {
tab ??= TabContextMenu.contextTab;
if (!tab || !tab.pinned) {
return;
@@ -519,8 +531,16 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
const isVisible = contextTab.pinned && !contextTab.multiselected;
const isEssential = contextTab.getAttribute("zen-essential");
const zenAddEssential = document.getElementById("context_zen-add-essential");
document.getElementById("context_zen-reset-pinned-tab").hidden = !isVisible;
document.getElementById("context_zen-replace-pinned-url-with-current").hidden = !isVisible;
const zenResetPinnedTab = document.getElementById("context_zen-reset-pinned-tab");
const zenReplacePinnedUrl = document.getElementById(
"context_zen-replace-pinned-url-with-current"
);
[zenResetPinnedTab, zenReplacePinnedUrl].forEach((element) => {
if (element) {
element.hidden = !isVisible;
document.l10n.setArgs(element, { isEssential });
}
});
zenAddEssential.hidden = isEssential || !!contextTab.group;
document.l10n
.formatValue("tab-context-zen-add-essential-badge", {
@@ -547,13 +567,50 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
}
// eslint-disable-next-line complexity
moveToAnotherTabContainerIfNecessary(event, movingTabs) {
if (!this.enabled) {
return false;
moveToAnotherTabContainerIfNecessary(event, draggedTab, movingTabs, dropIndex) {
let newIndex = dropIndex;
let fromDifferentWindow = false;
movingTabs = Array.from(movingTabs || draggedTab)
.reverse()
.map((tab) => {
if (!gBrowser.isTab(tab)) {
return tab;
}
let workspaceId;
if (tab.ownerGlobal !== window) {
fromDifferentWindow = true;
if (
!tab.hasAttribute("zen-essential") &&
tab.getAttribute("zen-workspace-id") != gZenWorkspaces.activeWorkspace
) {
workspaceId = gZenWorkspaces.activeWorkspace;
tab.ownerGlobal.gBrowser.selectedTab = tab.ownerGlobal.gBrowser._findTabToBlurTo(
tab,
movingTabs
);
tab.ownerGlobal.gZenWorkspaces.moveTabToWorkspace(tab, workspaceId);
}
// Move the tabs into this window. To avoid multiple tab-switches in
// the original window, the selected tab should be adopted last.
tab = gBrowser.adoptTab(tab, {
elementIndex: newIndex,
selectTab: tab == draggedTab,
});
if (tab) {
++newIndex;
}
if (workspaceId) {
tab.setAttribute("zen-workspace-id", workspaceId);
}
}
return tab;
});
if (fromDifferentWindow) {
gBrowser.addRangeToMultiSelectedTabs(
gBrowser.tabContainer.dragAndDropElements[dropIndex],
gBrowser.tabContainer.dragAndDropElements[newIndex - 1]
);
}
movingTabs = movingTabs.map((tab) => {
return tab.ownerGlobal !== window ? gBrowser.adoptTab(tab) : tab;
});
try {
const pinnedTabsTarget = event.target.closest(
":is(.zen-current-workspace-indicator, .zen-workspace-pinned-tabs-section)"
@@ -566,8 +623,8 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
}
movingTabs = movingTabs.filter((tab) =>
gBrowser.isTabGroupLabel(tab)
? tab.group?.isZenFolder && !tabsTarget && !essentialTabsTarget
gBrowser.isTabGroupLabel(tab) && tab.group?.isZenFolder
? !tabsTarget && !essentialTabsTarget
: true
);
@@ -575,9 +632,9 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
// Remove group labels from the moving tabs and replace it
// with the sub tabs
for (let i = 0; i < movingTabs.length; i++) {
const draggedTab = movingTabs[i];
if (gBrowser.isTabGroupLabel(draggedTab)) {
const group = draggedTab.group;
const tab = movingTabs[i];
if (gBrowser.isTabGroupLabel(tab)) {
const group = tab.group;
// remove label and add sub tabs to moving tabs
if (group) {
movingTabs.splice(i, 1, ...group.tabs);
@@ -588,32 +645,32 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
let isVertical = this.expandedSidebarMode;
let moved = false;
let hasActuallyMoved;
for (const draggedTab of movingTabs) {
for (const tab of movingTabs) {
let isRegularTabs = false;
// Check for essentials container
if (essentialTabsTarget) {
if (!draggedTab.hasAttribute("zen-essential") && !draggedTab?.group) {
if (!tab.hasAttribute("zen-essential") && !tab?.group?.hasAttribute("split-view-group")) {
moved = true;
isVertical = false;
hasActuallyMoved = this.addToEssentials(draggedTab);
hasActuallyMoved = this.addToEssentials(tab);
}
}
// Check for pinned tabs container
else if (pinnedTabsTarget) {
if (!draggedTab.pinned) {
gBrowser.pinTab(draggedTab);
} else if (draggedTab.hasAttribute("zen-essential")) {
this.removeEssentials(draggedTab, false);
if (!tab.pinned) {
gBrowser.pinTab(tab);
} else if (tab.hasAttribute("zen-essential")) {
this.removeEssentials(tab, false);
moved = true;
}
}
// Check for normal tabs container
else if (tabsTarget || event.target.id === "zen-tabs-wrapper") {
if (draggedTab.pinned && !draggedTab.hasAttribute("zen-essential")) {
gBrowser.unpinTab(draggedTab);
if (tab.pinned && !tab.hasAttribute("zen-essential")) {
gBrowser.unpinTab(tab);
isRegularTabs = true;
} else if (draggedTab.hasAttribute("zen-essential")) {
this.removeEssentials(draggedTab);
} else if (tab.hasAttribute("zen-essential")) {
this.removeEssentials(tab);
moved = true;
isRegularTabs = true;
}
@@ -654,7 +711,7 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
elementIndex++;
}
gBrowser.moveTabTo(draggedTab, {
gBrowser.moveTabTo(tab, {
elementIndex,
forceUngrouped: targetElem?.group?.collapsed !== false,
});

View File

@@ -22,7 +22,7 @@ zen-essentials-promo {
outline: 1px dashed currentColor;
}
:root:not([zen-sidebar-expanded="true"]) & {
:root:is(:not([zen-sidebar-expanded="true"]), [zen-unsynced-window="true"]) & {
display: none !important;
}

View File

@@ -209,7 +209,7 @@
}
:root[zen-unsynced-window="true"] & {
transform: translateY(-4px);
display: none !important;
}
}

View File

@@ -6,9 +6,12 @@
# This file is autogenerated by scripts/import_external_tests.py
# Do not edit manually.
BROWSER_CHROME_MANIFESTS += [
"safebrowsing/browser.toml",
"sandbox/browser.toml",
"shell/browser.toml",
"tooltiptext/browser.toml",
]
XPCSHELL_TESTS_MANIFESTS += [
]

View File

@@ -1348,7 +1348,8 @@ export class nsZenThemePicker extends nsZenMultiWindowFeature {
}
getToolbarColor(isDarkMode = false) {
return isDarkMode ? [255, 255, 255, 0.6] : [0, 0, 0, 0.6]; // Default toolbar
const opacity = 0.8;
return isDarkMode ? [255, 255, 255, opacity] : [0, 0, 0, opacity]; // Default toolbar
}
onWorkspaceChange(workspace, skipUpdate = false, theme = null) {

View File

@@ -57,12 +57,6 @@ window.ZenWorkspaceBookmarksStorage = {
CREATE INDEX IF NOT EXISTS idx_bookmarks_workspaces_changes
ON zen_bookmarks_workspaces_changes(bookmark_guid, workspace_uuid)
`);
// Before, workspace_uuid was a FOREIGN KEY, not anymore, so we need to drop the constraint
// This is a no-op if the constraint doesn't exist
await db.execute(`
PRAGMA foreign_keys = OFF;
`);
}
);
},

View File

@@ -6,6 +6,12 @@
import { nsZenThemePicker } from "chrome://browser/content/zen-components/ZenGradientGenerator.mjs";
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
ZenSessionStore: "resource:///modules/zen/ZenSessionManager.sys.mjs",
});
/**
* Zen Spaces manager. This class is mainly responsible for the UI
* and user interactions but it also contains some logic to manage
@@ -225,7 +231,7 @@ class nsZenWorkspaces {
}
}
async selectEmptyTab(newTabTarget = null, selectURLBar = true) {
async selectEmptyTab(newTabTarget = null) {
// Validate browser state first
if (!this._validateBrowserState()) {
console.warn("Browser state invalid for empty tab selection");
@@ -245,30 +251,6 @@ class nsZenWorkspaces {
!this._emptyTab.ownerGlobal.closed &&
gZenVerticalTabsManager._canReplaceNewTab
) {
// Only set up URL bar selection if we're switching to a different tab
if (gBrowser.selectedTab !== this._emptyTab && selectURLBar) {
const tabSelectListener = () => {
// Remove the event listener first to prevent any chance of multiple executions
window.removeEventListener("TabSelect", tabSelectListener);
// Use requestAnimationFrame to ensure DOM is updated
requestAnimationFrame(() => {
// Then use setTimeout to ensure browser has time to process tab switch
setTimeout(() => {
if (gURLBar) {
try {
gURLBar.select();
} catch (e) {
console.warn("Error selecting URL bar:", e);
}
}
}, 50);
});
};
window.addEventListener("TabSelect", tabSelectListener, { once: true });
}
// Safely switch to the empty tab using our debounced method
const success = await this._safelySelectTab(this._emptyTab);
if (!success) {
@@ -801,7 +783,7 @@ class nsZenWorkspaces {
chromeFlags & Ci.nsIWebBrowserChrome.CHROME_MENUBAR;
return this._shouldHaveWorkspaces;
}
return this._shouldHaveWorkspaces;
return this._shouldHaveWorkspaces && !document.documentElement.hasAttribute("taskbartab");
}
get isPrivateWindow() {
@@ -888,6 +870,13 @@ class nsZenWorkspaces {
return Promise.resolve();
}
const spacesFromStore = aWinData.spaces || [];
if (
!this.privateWindowOrDisabled &&
spacesFromStore.length === 0 &&
lazy.ZenSessionStore._migrationData
) {
spacesFromStore.push(...lazy.ZenSessionStore._migrationData.spaces);
}
this._workspaceCache = spacesFromStore.length
? [...spacesFromStore]
: [this.#createWorkspaceData("Space", undefined)];
@@ -1031,11 +1020,19 @@ class nsZenWorkspaces {
delete this._initialTab;
}
if (gZenVerticalTabsManager._canReplaceNewTab && showed) {
BrowserCommands.openTab();
} else if (!showed) {
gBrowser.selectedBrowser.focus();
}
showed &&= Services.prefs.getBoolPref("zen.urlbar.open-on-startup", true);
// Wait for the next event loop to ensure that the startup focus logic by
// firefox has finished doing it's thing.
setTimeout(() => {
setTimeout(() => {
if (gZenVerticalTabsManager._canReplaceNewTab && showed) {
BrowserCommands.openTab();
} else if (!showed) {
gBrowser.selectedBrowser.focus();
}
});
});
if (
!gZenVerticalTabsManager._canReplaceNewTab &&
@@ -1111,7 +1108,7 @@ class nsZenWorkspaces {
return (
!window.toolbar.visible ||
Services.prefs.getBoolPref("browser.tabs.closeWindowWithLastTab") ||
this.privateWindowOrDisabled
(this.privateWindowOrDisabled && !this.isPrivateWindow)
);
}
@@ -2417,7 +2414,10 @@ class nsZenWorkspaces {
if (!this.currentWindowIsSyncing) {
containerTabId = parseInt(gBrowser.selectedTab.getAttribute("usercontextid")) || 0;
let label = ContextualIdentityService.getUserContextLabel(containerTabId) || "Default";
name = this.isPrivateWindow ? "Private " + name : label;
name = this.isPrivateWindow ? "Incognito" : label;
if (this.isPrivateWindow) {
icon = gZenEmojiPicker.getSVGURL("eye.svg");
}
}
let workspace = {
uuid: gZenUIManager.generateUuidv4(),

View File

@@ -156,7 +156,7 @@
/* Mark workspaces indicator */
.zen-current-workspace-indicator {
--indicator-gap: calc(var(--toolbarbutton-inner-padding) - 1px);
padding: calc(2px + var(--tab-inline-padding) + var(--zen-toolbox-padding));
padding: calc(3px + var(--tab-inline-padding) + var(--zen-toolbox-padding));
font-weight: 500;
position: relative;
max-height: var(--zen-workspace-indicator-height);
@@ -177,10 +177,10 @@
pointer-events: none;
content: '';
position: absolute;
top: var(--zen-toolbox-padding);
top: 4px;
left: calc(var(--zen-toolbox-padding) + 2px);
width: calc(100% - var(--zen-toolbox-padding) * 3);
height: calc(100% - var(--zen-toolbox-padding) * 2);
width: calc(100% - var(--zen-toolbox-padding) * 2.5);
height: calc(100% - 8px);
}
:root[zen-private-window] & {

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