Compare commits

...

190 Commits

Author SHA1 Message Date
mr. M
5ffb33b375 Fix: update macOS x64 identifier to x86_64 in surfer.json 2024-11-28 10:46:31 +01:00
mr. M
d1ecbc3f61 Bump version numbers for Zen Browser and Zen Twilight to 1.0.1-a.22 and 1.0.1-t.22 respectively 2024-11-28 10:46:13 +01:00
mr. M
0e02cb14ec Fix: adjust vertical tab background opacity for improved visibility 2024-11-10 20:08:04 +01:00
mr. M
9443169283 Fix: update zen glance animations for smoother transitions and adjust timing 2024-11-10 19:58:24 +01:00
mr. M
259b25ab68 Fix: adjust scale for zen glance background and enhance quickCloseGlance logic 2024-11-10 19:41:16 +01:00
mr. M
c8e363d4f3 Bump version numbers for Zen Browser and Zen Twilight to 1.0.1-a.19 and 1.0.1-t.19 respectively; update subproject commit reference 2024-11-10 19:29:53 +01:00
mr. m 🤙
8ecc8b2e74 Fix zen-browser/desktop/issues/2252 (#2807) (#2846)
Signed-off-by: Jai A P <jai.jap.318@gmail.com>
Co-authored-by: Jai A P <jai.jap.318@gmail.com>
2024-11-10 19:21:21 +01:00
mr. M
62d763e65a Fix: reduce minimum height of tabs to improve layout consistency 2024-11-10 19:20:36 +01:00
mr. M
d54a56e11b Merge branch 'dev' of https://github.com/zen-browser/desktop into dev 2024-11-10 19:13:34 +01:00
mr. M
73b24aa5ae Add floating style for glance tabs in essentials and adjust new tab button border styling 2024-11-10 19:13:30 +01:00
kristijanribaric
110279cf4c (feat) Add workspace switching by scrolling sidebar (#2828)
This commit adds the ability to switch workspaces by horizontally
scrolling the sidebar.  A scroll cooldown and threshold are
implemented to prevent accidental workspace changes.  The
`_setupHoverDetection` method is renamed to `_setupSidebarHandlers`
as it now handles both hover and scroll events.
2024-11-10 19:05:15 +01:00
maraton
ddc4341707 Fix: file manager not opening in many scenarios (#2790) 2024-11-10 18:55:59 +01:00
mr. M
47fdeada01 Fix enabled property check in ZenPinnedTabManager for improved reliability 2024-11-10 18:54:46 +01:00
mr. M
c3fc7d792e Refactor Zen tab management to improve performance and enable conditional functionality 2024-11-10 18:50:10 +01:00
mr. M
33ac29f269 Add zen-compact-mode-ignore class to sidebar button for improved styling in compact mode 2024-11-10 18:25:09 +01:00
mr. M
1c7c6b2a0f Enhance Zen animations and glance functionality with improved styles and closing behavior 2024-11-10 18:20:23 +01:00
mr. M
5ab9ee7c34 Remove support URL for FAQ in browser preferences 2024-11-10 18:05:27 +01:00
mr. M
056de6fc13 Hide toolbar buttons and workspace indicator in specific conditions 2024-11-10 18:04:47 +01:00
mr. M
ded9ee3251 Reduce opacity in vertical tabs and remove background styles in workspaces 2024-11-10 17:53:52 +01:00
mr. M
dc4f8c6c98 Hide workspace indicator icon when no icon is present 2024-11-10 17:50:49 +01:00
mr. M
282c3fab8c Fixed closing glance 2024-11-10 17:44:12 +01:00
mr. M
63a808b306 Refactor sidebar buttons: move profile button to sidebar icons and clean up toolbar structure 2024-11-10 17:34:40 +01:00
mr. M
ca7622027d Merge branch 'dev' of https://github.com/zen-browser/desktop into dev 2024-11-10 17:30:37 +01:00
mr. M
f923078445 Fixed expand on hover sidebar 2024-11-10 17:30:27 +01:00
mr. m 🤙
f81150ea31 Merge pull request #2823 from GunGunGun/patch-6
Support URL to FAQ - 2nd Try 🐷
2024-11-10 14:19:54 +01:00
Gun
0d057ab60b 🐷
🐷

Signed-off-by: Gun <6913739+GunGunGun@users.noreply.github.com>
2024-11-10 20:14:40 +07:00
mr. M
c5a7879c55 Fixed padding for workspace indicator on collapsed mode 2024-11-10 11:55:09 +01:00
mr. M
d24a9a2583 (style) Modify button active state to exclude workspace button 2024-11-09 19:42:54 +01:00
mr. M
6a7ce1639a (style) Update tab background colors for improved visibility 2024-11-09 19:40:41 +01:00
mr. M
e75f6f85e7 last minutes touches yes yes yes 2024-11-09 19:32:16 +01:00
mr. M
9d4a8702cb Merge branch 'dev' of https://github.com/zen-browser/desktop into dev 2024-11-09 18:56:20 +01:00
mr. M
7a91ea7107 (feat) Enable swipe actions for workspace navigation 2024-11-09 18:52:22 +01:00
mr. m 🤙
0e818dfdc8 Merge pull request #2773 from GunGunGun/patch-6
Support URL to FAQ
2024-11-09 18:04:30 +01:00
Gun
2228f00c0a Support URL to FAQ
Support URL to FAQ

Signed-off-by: Gun <6913739+GunGunGun@users.noreply.github.com>
2024-11-10 00:02:24 +07:00
mr. m 🤙
e2e46d675e Merge pull request #2771 from kristijanribaric/feature/change-workspaces-by-pressing-mouse-side-buttons
Add workspace navigation via mouse back/forward buttons
2024-11-09 17:29:12 +01:00
Kristijan Ribarić
14b8a1d8a2 (refactor) Remove debug logging
Removed a debug `console.log` statement that was used
during swipe gesture implementation.  This is no
longer needed.
2024-11-09 17:20:04 +01:00
Kristijan Ribarić
7ab07c69aa Add workspace navigation via mouse back/forward buttons
Adds the ability to navigate workspaces using the mouse
back/forward buttons when hovering over the sidebar.  This
improves the user experience by providing a more intuitive
way to switch between workspaces.

This feature is only enabled when workspaces are enabled.
It intercepts the "AppCommand" events to handle back/forward
navigation.
2024-11-09 17:18:01 +01:00
mr. m 🤙
e9e2e9a711 Merge pull request #2764 from kristijanribaric/fix/update-workspace-gesture-changing-methods
Implement (better) workspace switching via swipe gestures
2024-11-09 17:14:55 +01:00
Kristijan Ribarić
84de31e85b (feat) Implement workspace switching via swipe gestures
This commit implements workspace switching using swipe gestures on the
navigator toolbox and tab bar. It introduces new event listeners for
`MozSwipeGesture*` events to detect and handle horizontal swipes.

The logic determines the swipe direction and switches to the next or
previous workspace accordingly, taking RTL layout into account.  The
implementation uses a state object to track the gesture progress and
prevent unintended workspace switches.
2024-11-09 16:21:46 +01:00
mr. M
31cc0ce7a8 feat: Add swipe gesture support for workspace navigation 2024-11-09 13:50:03 +01:00
mr. m 🤙
5c7dc3097a Merge pull request #2757 from GunGunGun/patch-5
Fix spaghetti width of zen-profile-dialog.css
2024-11-09 11:57:31 +01:00
Gun
3a09999cca Fix spaghetti width of zen-profile-dialog.css
Fix spaghetti width of zen-profile-dialog.css

Signed-off-by: Gun <6913739+GunGunGun@users.noreply.github.com>
2024-11-09 16:55:30 +07:00
mr. M
cf1e468395 style: Adjust toolbar height calculation and update workspace button visibility 2024-11-08 23:56:22 +01:00
mr. M
871e529bf2 Fixed texture slider not working on twilight (closes https://github.com/zen-browser/desktop/issues/2711) 2024-11-08 23:49:13 +01:00
mr. M
5e9ccd440e style: Update padding for workspace indicator in zen-styles 2024-11-08 23:42:40 +01:00
mr. M
657bc32e9b chore: Update subproject commit reference in localization 2024-11-08 20:47:17 +01:00
mr. M
d7a48bd855 refactor: Remove redundant tab refresh during initialization and streamline workspace startup process 2024-11-08 20:07:02 +01:00
mr. M
d2746689f9 feat: Hide new tab button along with all tabs button in customizable UI 2024-11-08 19:55:13 +01:00
mr. M
f093228f71 feat: Update new tab button preferences and improve workspace indicator styles 2024-11-08 19:34:51 +01:00
mr. M
f3e054d15e feat: Enhance new tab button visibility and workspace indicator styles 2024-11-08 18:26:40 +01:00
mr. M
86a3ad4b1f fix: Improve event target checking for popup tracking elements 2024-11-08 18:17:40 +01:00
mr. M
aa5a768ef0 (feat) Add workspace indicator preference and update styles for improved UI 2024-11-08 18:13:05 +01:00
mr. m 🤙
b6cb37db43 Merge pull request #2716 from kristijanribaric/fix/migrate-pinned-tabs-to-updated-version
Fix: Migrate pinned tabs from older Zen versions
2024-11-08 17:58:01 +01:00
mr. m 🤙
3ca25590c7 Merge pull request #2734 from Tomveloper/dev
Update zen-decks.css to fix split view spacing
2024-11-08 17:40:04 +01:00
Tom
c3b902ae65 Update zen-decks.css to fix split view spacing
Fixes the focused split view tab outline being cut out horizontally when vertical tabs are expanded.

Makes the outline offset be -1px so that the split viewfocused tab outline overlays and hides the gray border which shouldn't be still seen when the tab has the focus outline.

Signed-off-by: Tom <71405826+Tomveloper@users.noreply.github.com>
2024-11-07 22:50:57 +02:00
Kristijan Ribarić
014602266b Correctly set the base pinned url and title for migrating from previous version. 2024-11-07 16:20:46 +01:00
Kristijan Ribarić
696a2148f0 (fix) Migrate pinned tabs from older Zen versions
If there are no pinned tabs in the database on startup,
assume it's a migration from an older version and save all
currently pinned tabs to the database.  Previously, Zen
would remove any pinned tabs that didn't have a `zen-pin-id`
attribute, which would occur during migration. This change
ensures that pinned tabs are preserved during upgrades.
2024-11-07 11:40:44 +01:00
mr. M
e086cf56fc Reorder default placements for toolbar buttons in ZenCustomizableUI and update patch for customizable UI 2024-11-06 20:36:03 +01:00
mr. M
6d5dcbe89f Adjust tab height calculations and improve icon state handling in ZenStartup and ZenPinnedTabManager 2024-11-06 20:31:20 +01:00
mr. M
54f5042c3c Bump version numbers for Zen Browser and Zen Twilight to 1.0.1-a.18 and 1.0.1-t.18 respectively 2024-11-06 19:58:24 +01:00
mr. M
927f38e999 Update subproject commit reference in localization 2024-11-06 19:57:53 +01:00
mr. M
638ec9c2ea Center align content in zen essentials container for improved layout consistency 2024-11-06 19:56:57 +01:00
mr. M
27c5119664 Refactor CSS styles for browser container and glance view, improving layout and border handling 2024-11-06 19:55:59 +01:00
mr. M
8542000540 Update gradient handling for main browser and toolbar backgrounds 2024-11-06 19:53:09 +01:00
mr. M
9c5c53f4ae Center align content in zen essentials container for improved layout 2024-11-06 19:52:16 +01:00
mr. M
a2ca9361e1 Refactor toolbar background handling and improve tab styling in compact mode 2024-11-06 19:46:21 +01:00
mr. M
9042baf13d Merge branch 'dev' of https://github.com/zen-browser/desktop into dev 2024-11-06 17:03:32 +01:00
mr. M
957cff3447 Enhance workspace indicator styling and add table drop functionality in ZenPinnedTabsStorage 2024-11-06 17:03:26 +01:00
mr. m 🤙
fbd6d11a5c Merge pull request #2505 from cristiancmoises/mozconfig-reorganize
Update mozconfig
2024-11-06 17:58:34 +02:00
mr. m 🤙
6ccb766b2e Merge pull request #2681 from neurokitti/dev
Fixed: custom colours doubling and colour dots not clearing
2024-11-06 17:39:14 +02:00
Bridget Morningstar
df7be136f8 Update ZenGradientGenerator.mjs
Signed-off-by: Bridget Morningstar <168581144+neurokitti@users.noreply.github.com>
2024-11-06 01:09:43 -06:00
Bridget Morningstar
3abbb08e31 Fixed: custom colours doubling and colour dots getting pulled from other spaces
Signed-off-by: Bridget Morningstar <168581144+neurokitti@users.noreply.github.com>
2024-11-06 00:48:27 -06:00
mr. M
715b6df2fb Add overflow hidden to zen main app wrapper for improved layout 2024-11-05 23:39:15 +01:00
mr. M
a64fe45175 Added icon for zen essentials 2024-11-05 22:41:53 +01:00
mr. M
f0690d0ac5 Refactor CSS styles for improved layout and responsiveness 2024-11-05 22:40:52 +01:00
mr. M
3049bc8215 Added better UI for transparency in windows 2024-11-05 22:27:37 +01:00
mr. M
4f9b59ed6d Fixed using the wrong macos window vibrancy material 2024-11-05 22:08:02 +01:00
mr. M
d05e849299 Fixed not updating workspace name in sidebar when saving 2024-11-05 20:53:22 +01:00
mr. M
76f06e120c Merge branch 'dev' of https://github.com/zen-browser/desktop into dev 2024-11-05 20:34:53 +01:00
mr. M
fdf83eaef8 Added better support for macos transparency 2024-11-05 20:29:27 +01:00
mr. m 🤙
b214364ee9 Merge pull request #2665 from kristijanribaric/feature/add-new-icons-for-essentials-context-action
Feature: Add new icons for essentials context items
2024-11-05 21:18:36 +02:00
Kristijan Ribarić
adfcaa2f09 feat: Add new icons for essentials context items
Updted context menu item showing logic
2024-11-05 15:08:36 +01:00
mr. m 🤙
bd3c8d19fa Merge pull request #2656 from kristijanribaric/fix/pinned-tab-manager-make-sure-contextual-id-is-applied-correctly
Fix: Parse user context ID correctly when pinning tabs
2024-11-05 09:08:35 +02:00
Kristijan Ribarić
7ae2d6d5bb Fix: Parse user context ID correctly when pinning tabs
This commit fixes an issue where the user context ID was not being parsed correctly when pinning tabs.

Previously, the user context ID was being stored as a string, which could lead to unexpected behavior. This commit ensures that the user context ID is parsed as an integer, which is the correct data type.

This change will ensure that pinned tabs are stored and retrieved correctly.
2024-11-05 08:06:54 +01:00
mr. m 🤙
44e5a98188 Merge pull request #2644 from kristijanribaric/correctly-save-essentials-in-db
Ensure that when adding an essential it is saved correctly in the database.
2024-11-05 02:09:07 +02:00
mr. M
093722c7d6 Adjust opacity for current workspace indicator and its name in zen workspaces 2024-11-05 00:38:34 +01:00
mr. M
ca2dd38f9b Add current workspace indicator and update styles in zen workspaces 2024-11-05 00:38:13 +01:00
Kristijan Ribarić
bf0342eb06 Ensure that when adding an essential it is saved correctly in the database. 2024-11-04 22:24:40 +01:00
mr. M
cf7af8e3aa Update tab hover background color and tab height in vertical-tabs.css 2024-11-04 19:55:43 +01:00
mr. M
19f8f58618 Update tab hover background color in vertical-tabs.css 2024-11-04 19:41:27 +01:00
mr. M
649d82425a Added zen essentials! 2024-11-04 19:34:32 +01:00
mr. M
1f45db0fac Update tab hover background color in vertical-tabs.css
``
2024-11-04 18:38:25 +01:00
mr. M
49ceb6f6e5 Update zen-browser-ui.css, zen-compact-mode.css, and zen-theme.css
- Add transparency to the titlebar on macOS
- Apply backdrop filter and border to the compact mode toolbar
- Adjust themed toolbar background for macOS
2024-11-04 18:30:12 +01:00
mr. M
9d0eb9fb08 Update Firefox version to 132.0.1 in README.md and surfer.json 2024-11-04 18:09:50 +01:00
mr. M
ec65a69496 Translated files to a single monorepo 2024-11-04 17:37:02 +01:00
mr. m 🤙
f498a64413 Merge pull request #2625 from kristijanribaric/feature/add-icon-for-global-pin-tab-context-item
feat(ui): Add new "Pin Tab Globally" context menu option
2024-11-04 18:21:33 +02:00
Kristijan Ribarić
4b993c932e feat(ui): Add new "Pin Tab Globally" context menu option
This commit introduces a new context menu option called "Pin Tab Globally" which allows users to pin tabs to the global pin list, making them accessible from any workspace.

The icon for this option is set to the same as the existing "Pin Tab" context menu option.
2024-11-04 08:47:21 +01:00
mr. M
5aed1d0e97 Update subproject commit in zen-components 2024-11-03 23:31:18 +01:00
mr. M
4f70e6b881 Refactor zen-components: Update subproject commit 2024-11-03 21:10:10 +01:00
mr. m 🤙
e3751f5a57 Merge pull request #2613 from kristijanribaric/fix/remove-duplicate-pinned-tab-manager-loading
Fix: remove duplicate script import.
2024-11-03 21:41:26 +02:00
Kristijan Ribarić
46d9280490 Fix: remove duplicate script import. 2024-11-03 20:24:09 +01:00
mr. M
40c0a9bf51 Refactor zen-compact-mode.css: Adjust left position of toolbar dropdown menu 2024-11-03 16:59:53 +01:00
mr. M
0ca828325d Refactor browser preferences and styles 2024-11-03 16:46:24 +01:00
mr. M
aa74a68ca8 Refactor zen-components and zen-styles/zen-tabs/vertical-tabs.css 2024-11-03 13:44:33 +01:00
mr. M
419922a2aa Fixed opening glance inside a previous glance window, leaving the background transparent 2024-11-03 12:06:50 +01:00
mr. M
e970e6cbc5 refactor: Remove ZenPinnedTabsStorage and ZenPinnedTabManager from zen-assets.jar.inc.mn 2024-11-03 11:37:43 +01:00
mr. m 🤙
90b04ddd9b Merge pull request #2599 from kristijanribaric/feature/include-zen-pinned-tab-storage
feat: Include zen pinned tabs storage
2024-11-03 11:57:17 +02:00
Kristijan Ribarić
96ce701f64 feat: Include zen pinned tabs storage 2024-11-03 10:55:32 +01:00
mr. M
b506c94d9a Fixed files being downloaded into a temp folder 2024-11-03 10:25:51 +01:00
mr. M
d4962685d6 refactor: Hide tab label container in vertical-tabs.css 2024-11-03 10:21:55 +01:00
mr. M
24a6013195 refactor: Hide tab label container in vertical-tabs.css 2024-11-03 02:14:20 +01:00
mr. M
de3962412f feat: Add Essentials Toolbar and hide Accordion Header in compact mode
- Added the Essentials Toolbar to the Navigator Toolbox
- Removed the Essentials Toolbar from ZenStartup and created it in browser-places.js
- Updated the zen-styles/zen-sidebar.css file to remove the display of the Menubar
- Updated the navigator-toolbox.inc.xhtml file to add the Essentials Toolbar and hide the Accordion Header in compact mode
- Updated the indicator-private-browsing.svg file to include the private browsing indicator icon
2024-11-03 02:08:51 +01:00
mr. m 🤙
0603c57a60 Merge pull request #2583 from kristijanribaric/feature/essentials
Feature: Essentials
2024-11-03 01:48:47 +02:00
Kristijan Ribarić
71464e6914 feat(zen-tabs): Hide Essentials Toolbar and Accordion Header in compact mode 2024-11-02 21:13:39 +01:00
Kristijan Ribarić
2a96fd466f feat: Add Essentials Toolbar to Navigator Toolbox
This commit introduces a new Essentials Toolbar to the Navigator Toolbox. The toolbar is located within the vertical pinned tabs container and provides quick access to essential features.

The following changes were made:

- Added a new toolbarbutton with the id "essentials-accordion-header" to toggle the Essentials Toolbar visibility.
- Introduced a new `hbox` with the id "EssentialsToolbar" to house the toolbar items.
- Included a `scrollbox` with the id "EssentialsToolbarItems" to handle scrolling for the toolbar items.
- Modified the `arrowscrollbox` to use a vertical orientation (`orient="vertical"`).
- Added a separator between the pinned tabs container and the Essentials Toolbar.
2024-11-02 21:09:08 +01:00
Kristijan Ribarić
d6e27ee9b5 Remove Essentials Toolbar from ZenStartup and call create it in browser-places.js 2024-11-02 21:09:07 +01:00
Kristijan Ribarić
a21511b683 feat: Add Essentials Toolbar
This commit introduces a new Essentials Toolbar feature to Zen. It provides a more streamlined and organized way for users to access frequently used bookmakrs directly from the sidebar.

- Added `ZenEssentialsToolbar.mjs` to provide the logic for the toolbar.
- Updated the sidebar CSS (`zen-sidebar.css`) to style the Essentials Toolbar.
- Modified `ZenStartup.mjs` to initialize the Essentials Toolbar.
- Made changes to `browserPlacesViews-js.patch` to integrate with existing bookmark functionality.
- Added a new preference `zen.essentials.enabled` to enable/disable the Essentials Toolbar.
2024-11-02 21:09:07 +01:00
mr. M
984fa08f14 Fixed being able to unload splitted tabs (closes https://github.com/zen-browser/desktop/issues/2236) 2024-11-02 20:45:10 +01:00
mr. M
fbc2f999bf Fixed web panels being reloaded when unpining them 2024-11-02 20:15:23 +01:00
mr. M
86cb96cd21 Fixed having glance on pinned tabs 2024-11-02 20:07:25 +01:00
mr. M
9bc955a5d4 Fixed urlbar styling inconsistencies 2024-11-02 19:48:58 +01:00
mr. M
49dc6ac30b Fixed having invalid custom gradient colors 2024-11-02 19:29:16 +01:00
mr. M
5b93ce3481 Merge branch 'dev' of https://github.com/zen-browser/desktop into dev 2024-11-02 19:05:35 +01:00
mr. M
369adbdfe9 Fixed custom color input accepting every possible value 2024-11-02 19:00:06 +01:00
mr. M
c5b6376980 Fixed shadow offset and closing last tab when glance is open 2024-11-02 18:34:38 +01:00
mr. m 🤙
64c7bc78af Merge pull request #2580 from Caslus/patch-1
Fix: Broken release notes link
2024-11-02 19:27:39 +02:00
lucas philippe
3a4873aa1b Fix: Broken release notes link
Signed-off-by: lucas philippe <22855640+Caslus@users.noreply.github.com>
2024-11-02 14:09:03 -03:00
mr. M
e0a3b2d8c3 Added weather widget to the newtab 2024-11-02 18:02:45 +01:00
mr. M
10f6e4e751 Fixed toolbar buttons backgrounds 2024-11-02 17:58:38 +01:00
mr. M
3a374dcdba Fixed close tab button having weird paddings 2024-11-02 17:45:15 +01:00
mr. M
b6f4611994 Update subproject commit in zen-components 2024-11-02 17:35:27 +01:00
mr. M
b58f7a0c4d Update subproject commit in zen-components 2024-11-01 17:26:52 +01:00
mr. M
f94b51e410 Fixed closing parent tab on glance (https://github.com/zen-browser/desktop/issues/2526) 2024-11-01 13:47:04 +01:00
mr. M
6185336c3a Update border-radius preference based on the OS 2024-11-01 13:13:54 +01:00
mr. M
bd8829a432 Improved UI theme colors generation and newtab aligment 2024-11-01 13:07:32 +01:00
Cristian Cezar Moisés
89d8d11db3 Update mozconfig
Grouped related options for better organization, Added comments and cleaned up whitespace to enhance readability.


Signed-off-by: Cristian Cezar Moisés <86272521+cristiancmoises@users.noreply.github.com>
2024-11-01 01:20:20 +00:00
mr. M
644baca439 Update display versions in surfer.json 2024-10-31 22:31:00 +01:00
mr. M
3cc111e5dd Update subproject commit in zen-components 2024-10-31 22:26:38 +01:00
mr. M
baa614c094 Update subproject commit in zen-components 2024-10-31 22:25:23 +01:00
mr. M
95cb9460d4 Update subproject commit in zen-components 2024-10-31 22:24:53 +01:00
mr. M
e433c5160a Update subproject commit in zen-components 2024-10-31 22:18:30 +01:00
mr. M
440b7171b0 Fixed compact mode sidebar breaking when open a link from the other subpanels (https://github.com/zen-browser/desktop/issues/2218) 2024-10-31 16:08:15 +01:00
mr. M
cfd2e57950 Allow glance meta key only for macos (https://github.com/zen-browser/desktop/issues/2429) 2024-10-31 15:55:12 +01:00
mr. M
a5d4855530 Fixed changing activation method not applied to tabs (https://github.com/zen-browser/desktop/issues/2429) 2024-10-31 15:52:29 +01:00
mr. M
448a034766 Update subproject commit in zen-components 2024-10-31 15:50:09 +01:00
mr. M
d228a96944 Fixed bookmarks toolbar overflowing the top panel (https://github.com/zen-browser/desktop/issues/2434) 2024-10-31 14:29:18 +01:00
mr. M
51a3fb98e1 Update subproject commit in zen-components 2024-10-31 14:26:41 +01:00
mr. M
04dd345ef4 Fixed unloaded tabs can't be opened (closes #2436) 2024-10-31 14:26:17 +01:00
mr. M
3ba221831d Fixed height changing when having too many tabs 2024-10-31 09:15:20 +01:00
mr. M
b30a65015d Update display versions for Zen Browser and Zen Twilight 2024-10-31 01:37:07 +01:00
mr. M
0ea71db628 Refactor CSS styles for Zen browser UI and tabs
- Update the max-width property for #zen-appcontent-wrapper in zen-browser-ui.css to ensure it takes up the full width.
- Remove the label display for toolbar buttons in zen-glance.css to improve the visual appearance.
- Hide the new tab button in vertical-tabs.css to align with the Zen browser UI design.
2024-10-31 01:35:59 +01:00
mr. M
b64d007315 Update glance activation method for different platforms 2024-10-30 20:58:14 +01:00
mr. M
88f954fc28 Update subproject commit in zen-components 2024-10-30 20:50:45 +01:00
mr. M
f6acb174a4 Update subproject commit in zen-components 2024-10-30 20:49:31 +01:00
mr. M
b8a927b163 Added escape key for glance 2024-10-30 20:49:03 +01:00
mr. M
4e93b06772 Refactor CSS animation translation in zen-animations.css 2024-10-30 20:36:28 +01:00
mr. M
65934f5e00 Fix issue with workspace button width in vertical tabs 2024-10-30 20:35:26 +01:00
mr. M
fb89aab252 Fix issue with workspace button width in vertical tabs 2024-10-30 19:42:20 +01:00
mr. M
db9a0edce5 Update display versions for Zen Browser and Zen Twilight 2024-10-30 18:38:33 +01:00
mr. M
a85baa5cca Fixed Workspace icon jumping in sidebar if not in strip mode (https://github.com/zen-browser/desktop/issues/2413) 2024-10-30 18:25:09 +01:00
mr. M
7d6125c367 Fixed workspaces creation not opening and polished glance! 2024-10-30 18:18:46 +01:00
mr. M
2caeea2a14 Enabled POLLY for macos, increasing speed 2024-10-30 17:30:26 +01:00
mr. M
c3e2447924 Fixed connection issues for some devides, enabling captive portal services 2024-10-30 17:29:08 +01:00
mr. M
f12d888e42 Fixed connection errors when using OCSP 2024-10-30 17:22:16 +01:00
mr. M
f92966d57a Fixed not getting notification permissions dialog (closes https://github.com/zen-browser/desktop/issues/2093) 2024-10-30 17:18:07 +01:00
mr. M
245c75ab2c Update subproject commit hash in l10n and zen-components 2024-10-30 17:16:29 +01:00
mr. M
ef0b6d947e Fixed moving multiple tabs causing glitches sometimes 2024-10-30 15:42:33 +01:00
mr. M
bd511d17e9 Update subproject commit hash in l10n 2024-10-30 15:13:46 +01:00
mr. M
b0b73b5d97 Update zen view compact mode preferences 2024-10-30 15:13:36 +01:00
mr. M
a8d2773f5d Fix animation and layout issues in tabs.js 2024-10-30 13:48:08 +01:00
mr. M
9749a79f7f Update zen-components subproject commit 2024-10-30 13:44:33 +01:00
mr. M
6a7cbf1a47 Added isolation attribute to browser elements (https://github.com/zen-browser/desktop/issues/2375) 2024-10-30 13:30:09 +01:00
mr. M
ff1d1474d4 Update zen-components subproject commit 2024-10-30 13:24:01 +01:00
mr. M
2b18df8398 Fix animation and layout issues in zen-components and zen-styles 2024-10-30 13:18:27 +01:00
mr. M
d978d35533 Added expand to fullscreen for glance 2024-10-30 12:45:22 +01:00
mr. M
10cd70e858 Allow unsigned addons to be installed (closes https://github.com/zen-browser/desktop/issues/2258) 2024-10-30 08:52:48 +01:00
mr. M
0552c2f245 [breaking] changed default layout of the customizable UI 2024-10-29 23:42:05 +01:00
mr. M
a08df8f9ef Added theme color deduction for workspace gradients and fixed macos titlebar buttons display 2024-10-29 20:45:38 +01:00
mr. M
1a906b746e Update firefox version to 132.0 2024-10-29 18:08:56 +01:00
mr. M
c916732f4e Tried to implement solution for https://github.com/zen-browser/desktop/issues/2375 2024-10-29 12:23:08 +01:00
mr. M
22df6d5638 Added support for meta key for glance 2024-10-29 10:50:37 +01:00
mr. M
4783f264df Fixed animations for glance 2024-10-29 10:21:47 +01:00
mr. M
64c3ff4573 Update l10n submodule 2024-10-29 09:51:48 +01:00
mr. M
9c105c988c Added settings page for zen glance 2024-10-29 09:51:36 +01:00
mr. M
c7bf92f20b Added browser loading for glance and animations! 2024-10-29 00:38:10 +01:00
mr. M
15548d9bb2 Added glance animations 2024-10-28 16:59:41 +01:00
mr. M
441379b6e8 Added glance overlay and functionality 2024-10-28 15:25:57 +01:00
mr. M
6e5d117720 Started working on zen glance 2024-10-28 14:44:41 +01:00
mr. M
c1aea93389 Fixed expand on hover not showing gradients background (closes https://github.com/zen-browser/desktop/issues/2360 and closes https://github.com/zen-browser/desktop/issues/2350) 2024-10-28 11:49:28 +01:00
mr. M
e28af61ac2 Made compact mode toolbar also inherit the background colors 2024-10-28 11:23:39 +01:00
93 changed files with 10233 additions and 705 deletions

3
.gitmodules vendored
View File

@@ -1,6 +1,3 @@
[submodule "src/browser/base/content/zen-components"]
path = src/browser/base/content/zen-components
url = https://github.com/zen-browser/components
[submodule "l10n"]
path = l10n
url = https://github.com/zen-browser/l10n-packs

View File

@@ -21,14 +21,14 @@
Documentation
</a>
<a href="https//zen-browser.app/release-notes/latest">
<a href="https://zen-browser.app/release-notes/latest">
Release Notes
</a>
</div>
## 🖥️ Compatibility
Zen is currently built using firefox version `131.0.3`! 🚀
Zen is currently built using firefox version `132.0.1`! 🚀
- Check out the latest [release notes](https://zen-browser.app/release-notes)!
- Part of our mission is to keep Zen up-to-date with the latest version of Firefox, so you can enjoy the latest features and security updates!

View File

@@ -1,24 +1,25 @@
# Setting the compiler based on the existence of clang bin directory
if test -d "$HOME/.mozbuild/clang/bin"; then
export CC="$HOME"/.mozbuild/clang/bin/clang
export CXX="$HOME"/.mozbuild/clang/bin/clang++
export CC="$HOME/.mozbuild/clang/bin/clang"
export CXX="$HOME/.mozbuild/clang/bin/clang++"
else
export CC=clang
export CXX=clang++
fi
if test "$ZEN_RELEASE"; then
# Disable DMD and ELF hacks, enable linker lld
ac_add_options --disable-dmd
ac_add_options --enable-linker=lld
ac_add_options --disable-elf-hack
ac_add_options --disable-necko-wifi
# Stripping options for release builds
ac_add_options --enable-install-strip
ac_add_options --enable-strip
export STRIP_FLAGS="--strip-debug --strip-unneeded"
# Using sccache if available
if [ -f /home/runner/.mozbuild/sccache/sccache ]; then
mk_add_options 'export RUSTC_WRAPPER=/home/runner/.mozbuild/sccache/sccache'
mk_add_options 'export CCACHE_CPP2=yes'
@@ -26,10 +27,12 @@ if test "$ZEN_RELEASE"; then
mk_add_options 'export SCCACHE_GHA_ENABLED=on'
fi
# Enable Profile Guided Optimization
export MOZ_PGO=1
ac_add_options MOZ_PGO=1
if test "$SURFER_COMPAT" = "true"; then
# Optimization flags for SURFER_COMPAT
ac_add_options --enable-optimize="-O3 -march=x86-64"
export CFLAGS="$CFLAGS -O3 -ffp-contract=fast -march=x86-64"
@@ -38,20 +41,23 @@ if test "$ZEN_RELEASE"; then
export LDFLAGS="$LDFLAGS -Wl,-O3 -Wl,-mllvm,-fp-contract=fast -march=x86-64"
export RUSTFLAGS="$RUSTFLAGS -C target-cpu=x86-64 -C codegen-units=1 -Clink-args=--icf=safe"
else
# Optimization flags for general release
ac_add_options --enable-wasm-avx
ac_add_options --enable-optimize="-O3 -march=x86-64-v3 -msse3 -mssse3 -msse4.1 -msse4.2 -mavx -maes -mpopcnt -mpclmul"
export CFLAGS="$CFLAGS -O3 -ffp-contract=fast -march=x86-64-v3 -msse3 -mssse3 -msse4.1 -msse4.2 -mavx -mavx2 -mfma -maes -mpopcnt -mpclmul"
export CFLAGS="$CFLAGS -O3 -ffp-contract=fast -march=x86-64-v3 -msse3 -mssse3 -msse4.1 -msse4.2 -mavx -mavx2 -mfma -maes -mpopcnt -mpclmul"
export CPPFLAGS="$CPPFLAGS -O3 -ffp-contract=fast -march=x86-64-v3 -msse3 -mssse3 -msse4.1 -msse4.2 -mavx -mavx2 -mfma -maes -mpopcnt -mpclmul"
export CXXFLAGS="$CXXFLAGS -O3 -flto=thin -ffp-contract=fast -march=x86-64-v3 -msse3 -mssse3 -msse4.1 -msse4.2 -mavx -mavx2 -mfma -maes -mpopcnt -mpclmul"
export LDFLAGS="$LDFLAGS -Wl,-O3 -Wl,-mllvm,-fp-contract=fast -march=x86-64-v3"
export RUSTFLAGS="$RUSTFLAGS -C target-cpu=x86-64-v3 -C target-feature=+sse4.1 -C target-feature=+avx2 -C codegen-units=1 -Clink-args=--icf=safe"
fi
export VERBOSE=1
# Uncomment if you want to enable Polly optimizations
#export POLLY="-mllvm -polly -mllvm -polly-2nd-level-tiling -mllvm -polly-loopfusion-greedy -mllvm -polly-pattern-matching-based-opts -mllvm -polly-position=before-vectorizer -mllvm -polly-vectorizer=stripmine"
fi
# Common options
ac_add_options --target=x86_64-pc-linux
ac_add_options --enable-alsa
ac_add_options --enable-pulseaudio

View File

@@ -36,4 +36,7 @@ else
export RUSTFLAGS="-C target-feature=+v8.3a -C codegen-units=1 -Ctarget-cpu=apple-m1"
fi
export VERBOSE=1
#export POLLY="-mllvm -polly -mllvm -polly-2nd-level-tiling -mllvm -polly-loopfusion-greedy -mllvm -polly-pattern-matching-based-opts -mllvm -polly-position=before-vectorizer -mllvm -polly-vectorizer=stripmine"
# Enable polly for macos, since they have a more stable set of GPU drivers,
# unlike Linux, which has a lot of different drivers.
export POLLY="-mllvm -polly -mllvm -polly-2nd-level-tiling -mllvm -polly-loopfusion-greedy -mllvm -polly-pattern-matching-based-opts -mllvm -polly-position=before-vectorizer -mllvm -polly-vectorizer=stripmine"

View File

@@ -22,6 +22,7 @@ finish-args:
- --filesystem=xdg-download:rw
- --device=all
- --talk-name=org.freedesktop.FileManager1
- --talk-name=org.freedesktop.portal.Desktop
- --talk-name=org.freedesktop.ScreenSaver
- --own-name=org.mozilla.zen.*
- --own-name=org.mpris.MediaPlayer2.firefox.*

2
l10n

Submodule l10n updated: 3351a875d9...969a257fb4

View File

@@ -66,15 +66,9 @@ pref("dom.security.sanitizer.enabled", true);
pref("urlclassifier.trackingSkipURLs", "*.reddit.com, *.twitter.com, *.twimg.com, *.tiktok.com");
pref("urlclassifier.features.socialtracking.skipURLs", "*.instagram.com, *.twitter.com, *.twimg.com");
pref("network.cookie.sameSite.noneRequiresSecure", true);
pref("browser.download.start_downloads_in_tmp_dir", true);
pref("browser.helperApps.deleteTempFileOnExit", true);
pref("browser.uitour.enabled", false);
/** OCSP & CERTS / HPKP ***/
pref("security.OCSP.enabled", 0);
pref("security.remote_settings.crlite_filters.enabled", true);
pref("security.pki.crlite_mode", 2);
/** SSL / TLS ***/
pref("security.ssl.treat_unsafe_negotiation_as_broken", true);
pref("browser.xul.error_pages.expert_bad_cert", true);
@@ -106,12 +100,6 @@ pref("privacy.userContext.ui.enabled", true);
/** SAFE BROWSING ***/
pref("browser.safebrowsing.downloads.remote.enabled", false);
/** MOZILLA ***/
pref("permissions.default.desktop-notification", 2);
pref("permissions.default.geo", 2);
pref("permissions.manager.defaultsUrl", "");
pref("webchannel.allowObject.urlWhitelist", "");
/** TELEMETRY ***/
pref("datareporting.policy.dataSubmissionEnabled", false, locked);
pref("datareporting.healthreport.uploadEnabled", false, locked);
@@ -141,9 +129,6 @@ pref("browser.tabs.crashReporting.sendReport", false);
pref("browser.crashReports.unsubmittedCheck.autoSubmit2", false);
/** DETECTION ***/
pref("captivedetect.canonicalURL", "");
pref("network.captive-portal-service.enabled", false);
pref("network.connectivity-service.enabled", false);
pref("dom.private-attribution.submission.enabled", false);
/****************************************************************************

File diff suppressed because one or more lines are too long

View File

@@ -2,14 +2,14 @@ export var ZenCustomizableUI = new (class {
constructor() {}
TYPE_TOOLBAR = 'toolbar';
defaultSidebarIcons = ['zen-sidepanel-button', 'zen-workspaces-button', 'new-tab-button'];
defaultSidebarIcons = ['zen-profile-button', 'zen-workspaces-button', 'downloads-button'];
startup(CustomizableUIInternal) {
CustomizableUIInternal.registerArea(
'zen-sidebar-top-buttons',
{
type: this.TYPE_TOOLBAR,
defaultPlacements: ['preferences-button', 'zen-expand-sidebar-button', 'zen-profile-button'],
defaultPlacements: ['preferences-button', 'zen-expand-sidebar-button', 'zen-sidepanel-button'],
defaultCollapsed: null,
},
true
@@ -46,21 +46,7 @@ export var ZenCustomizableUI = new (class {
mode="icons">
<hbox id="zen-sidebar-top-buttons-customization-target" class="customization-target" flex="1">
<toolbarbutton removable="true" class="chromeclass-toolbar-additional toolbarbutton-1 zen-sidebar-action-button" id="zen-expand-sidebar-button" data-l10n-id="sidebar-zen-expand" cui-areatype="toolbar" oncommand="gZenVerticalTabsManager.toggleExpand();"></toolbarbutton>
<toolbarbutton id="zen-profile-button"
class="zen-sidebar-action-button toolbarbutton-1 chromeclass-toolbar-additional"
delegatesanchor="true"
onmousedown="ZenProfileDialogUI.showSubView(this, event)"
onkeypress="ZenProfileDialogUI.showSubView(this, event)"
consumeanchor="zen-profile-button"
closemenu="none"
data-l10n-id="toolbar-button-account"
cui-areatype="toolbar"
badged="true"
removable="true">
<vbox>
<image id="zen-profile-button-icon" />
</vbox>
</toolbarbutton>
<toolbarbutton removable="true" class="toolbarbutton-1 zen-sidebar-action-button zen-compact-mode-ignore" id="zen-sidepanel-button" data-l10n-id="sidebar-zen-sidepanel" onclick="gZenBrowserManagerSidebar.toggle();"></toolbarbutton>
</hbox>
</toolbar>
`);
@@ -97,11 +83,12 @@ export var ZenCustomizableUI = new (class {
}
_hideToolbarButtons(window) {
const elementsToHide = ['alltabs-button'];
const wrapper = window.document.getElementById('zen-sidebar-icons-wrapper');
const elementsToHide = ['alltabs-button', 'new-tab-button'];
for (let id of elementsToHide) {
const elem = window.document.getElementById(id);
if (elem) {
elem.setAttribute('hidden', 'true');
wrapper.prepend(elem);
}
}
}

View File

@@ -35,6 +35,7 @@
// Disable smooth scroll
gBrowser.tabContainer.arrowScrollbox.smoothScroll = false;
gZenPinnedTabManager.initTabs();
ZenWorkspaces.init();
gZenUIManager.init();
gZenVerticalTabsManager.init();
@@ -72,6 +73,7 @@
const tabs = document.getElementById('tabbrowser-arrowscrollbox');
tabs.style.maxHeight = '0px'; // reset to 0
const toolbarRect = toolbarItems.getBoundingClientRect();
let height = toolbarRect.height;
// -5 for the controls padding
let totalHeight = toolbarRect.height - (this.contentElementSeparation * 2) - 5;
// remove the height from other elements that aren't hidden
@@ -121,7 +123,7 @@
}
// remove all styles except for the width, since we are xulstoring the complet style list
const width = toolbox.style.width || '250px';
const width = toolbox.style.width || '270px';
toolbox.removeAttribute('style');
toolbox.style.width = width;

View File

@@ -47,7 +47,9 @@ var gZenUIManager = {
onPopupShowing(showEvent) {
for (const el of this._popupTrackingElements) {
if (!el.contains(event.explicitOriginalTarget)) {
// target may be inside a shadow root, not directly under the element
// we also ignore menus inside panels
if (!el.contains(showEvent.explicitOriginalTarget) || (showEvent.explicitOriginalTarget instanceof Element && showEvent.explicitOriginalTarget?.closest('panel'))) {
continue;
}
document.removeEventListener('mousemove', this.__removeHasPopupAttribute);
@@ -115,7 +117,6 @@ var gZenVerticalTabsManager = {
const onHover = Services.prefs.getBoolPref('zen.view.sidebar-expanded.on-hover');
const expanded = Services.prefs.getBoolPref('zen.view.sidebar-expanded');
const sidebar = this.navigatorToolbox;
if (onHover) {
// if the sidebar is not expanded, and hover detection is enabled, show the sidebar

View File

@@ -1,21 +1,18 @@
diff --git a/browser/base/content/browser-box.inc.xhtml b/browser/base/content/browser-box.inc.xhtml
index 108160d9469d44f47c93a3808402e4b27ff59777..39a7b691302bf13e1b5990f46428cff6807a8e56 100644
index 7f71abe7d80e4c09dd088517ec9ef106c7cb8654..281a3716d56edda243bfca7ebbe0797d75c844e7 100644
--- a/browser/base/content/browser-box.inc.xhtml
+++ b/browser/base/content/browser-box.inc.xhtml
@@ -23,11 +23,16 @@
@@ -22,7 +22,13 @@
<browser id="sidebar" autoscroll="false" disablehistory="true" disablefullscreen="true" tooltip="aHTMLTooltip"/>
</vbox>
<splitter id="sidebar-splitter" class="chromeclass-extrachrome sidebar-splitter" resizebefore="sibling" resizeafter="none" hidden="true"/>
<vbox id="appcontent" flex="1">
+ <html:div id="zen-appcontent-navbar-container"></html:div>
<!-- gNotificationBox will be added here lazily. -->
+ <hbox id="zen-tabbox-wrapper" flex="1">
<tabbox id="tabbrowser-tabbox"
flex="1" tabcontainer="tabbrowser-tabs">
+#include zen-sidebar-panel.inc.xhtml
+#include zen-splitview-overlay.inc.xhtml
<tabpanels id="tabbrowser-tabpanels"
flex="1" selectedIndex="0"/>
</tabbox>
+ </hbox>
</vbox>
+<vbox flex="1" id="zen-appcontent-wrapper">
+<html:div id="zen-appcontent-navbar-container"></html:div>
+<hbox id="zen-tabbox-wrapper" flex="1">
<tabbox id="tabbrowser-tabbox" flex="1" tabcontainer="tabbrowser-tabs">
+ #include zen-tabbrowser-elements.inc.xhtml
<tabpanels id="tabbrowser-tabpanels" flex="1" selectedIndex="0"/>
</tabbox>
</hbox>
+</vbox>
+</hbox>

View File

@@ -1,11 +1,11 @@
diff --git a/browser/base/content/browser-init.js b/browser/base/content/browser-init.js
index bee5309c04775adff8652bfe6c54b2d466e821ac..cfeaf7cf2e98c35e76bdd5451f90b004a04d4474 100644
index 9df41bb3c82919839ee1408aa4d177ea7ee40a56..e37c64fa2c2ea39762be4285a1a7055463ded537 100644
--- a/browser/base/content/browser-init.js
+++ b/browser/base/content/browser-init.js
@@ -143,13 +143,15 @@ var gBrowserInit = {
gNavToolbox.palette = document.getElementById(
"BrowserToolbarPalette"
).content;
@@ -152,13 +152,15 @@ var gBrowserInit = {
// tell CUI to ignore this element when it builds the toolbar areas
elem.setAttribute("skipintoolbarset", "true");
}
+ ZenCustomizableUI.init(window);
for (let area of CustomizableUI.areas) {
let type = CustomizableUI.getAreaType(area);
@@ -16,16 +16,16 @@ index bee5309c04775adff8652bfe6c54b2d466e821ac..cfeaf7cf2e98c35e76bdd5451f90b004
}
}
+ ZenCustomizableUI.registerToolbarNodes(window);
BrowserSearch.initPlaceHolder();
// Hack to ensure that the various initial pages favicon is loaded
@@ -239,6 +241,10 @@ var gBrowserInit = {
for (let elem of nonRemovables) {
elem.setAttribute("removable", "false");
elem.removeAttribute("skipintoolbarset");
@@ -253,6 +255,10 @@ var gBrowserInit = {
gPrivateBrowsingUI.init();
BrowserSearch.init();
BrowserPageActions.init();
+
+ Services.scriptloader.loadSubScript("chrome://browser/content/ZenStartup.mjs", window);
+ Services.scriptloader.loadSubScript("chrome://browser/content/zenThemeModifier.js", window);
+Services.scriptloader.loadSubScript("chrome://browser/content/ZenStartup.mjs", window);
+Services.scriptloader.loadSubScript("chrome://browser/content/zenThemeModifier.js", window);
+
if (gToolbarKeyNavEnabled) {
ToolbarKeyboardNavigator.init();

View File

@@ -1,5 +1,5 @@
diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js
index a29d7a84e84651ea0bdc9be8e4ac650bde2e048a..bc22e55a66686fbae95047686f845f71a0c3aae9 100644
index ccd83c15d0d73a1e53bdbfdfbe6fed43a26c961d..f18a6abf7debb97539a4cdf8422315b4dff08adb 100644
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -32,6 +32,7 @@ ChromeUtils.defineESModuleGetters(this, {
@@ -10,7 +10,7 @@ index a29d7a84e84651ea0bdc9be8e4ac650bde2e048a..bc22e55a66686fbae95047686f845f71
DevToolsSocketStatus:
"resource://devtools/shared/security/DevToolsSocketStatus.sys.mjs",
DownloadUtils: "resource://gre/modules/DownloadUtils.sys.mjs",
@@ -629,6 +630,15 @@ XPCOMUtils.defineLazyPreferenceGetter(
@@ -632,6 +633,15 @@ XPCOMUtils.defineLazyPreferenceGetter(
false
);
@@ -26,27 +26,19 @@ index a29d7a84e84651ea0bdc9be8e4ac650bde2e048a..bc22e55a66686fbae95047686f845f71
customElements.setElementCreationCallback("screenshots-buttons", () => {
Services.scriptloader.loadSubScript(
"chrome://browser/content/screenshots/screenshots-buttons.js",
@@ -3435,6 +3445,10 @@ var XULBrowserWindow = {
@@ -3438,6 +3448,11 @@ var XULBrowserWindow = {
AboutReaderParent.updateReaderButton(gBrowser.selectedBrowser);
TranslationsParent.onLocationChange(gBrowser.selectedBrowser);
+ gZenViewSplitter.onLocationChange(gBrowser.selectedBrowser);
+ ZenWorkspaces.onLocationChange(gBrowser.selectedBrowser);
+ gZenTabUnloader.onLocationChange(gBrowser.selectedBrowser);
+ gZenGlanceManager.onLocationChange(gBrowser.selectedBrowser);
+
PictureInPicture.updateUrlbarToggle(gBrowser.selectedBrowser);
if (!gMultiProcessBrowser) {
@@ -4769,7 +4783,7 @@ function setToolbarVisibility(
);
}
- const overlapAttr = "BookmarksToolbarOverlapsBrowser";
+ const overlapAttr = "BookmarksToolbarOverlapsBrowser__ignore"; // Original string was "BookmarksToolbarOverlapsBrowser" but it's not used and it only bugs the UI.
switch (isVisible) {
case true:
case "always":
@@ -7609,6 +7623,12 @@ var gDialogBox = {
@@ -7289,6 +7304,12 @@ var gDialogBox = {
parentElement.showModal();
this._didOpenHTMLDialog = true;

View File

@@ -1,8 +1,8 @@
diff --git a/browser/base/content/browser-places.js b/browser/base/content/browser-places.js
index 6992d22069643b58b249ae16edfe354304e56a39..38a006799feb5f4f41e582074901bb7226397ae1 100644
index 62b3665394a8256ebedf88a8f10bb6766977cf57..12ac643f913290b20fb7adba8f224f49bb82bf5c 100644
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -243,6 +243,8 @@ var StarUI = {
@@ -252,6 +252,8 @@ var StarUI = {
});
}
@@ -11,7 +11,7 @@ index 6992d22069643b58b249ae16edfe354304e56a39..38a006799feb5f4f41e582074901bb72
let onPanelReady = fn => {
let target = this.panel;
if (target.parentNode) {
@@ -294,6 +296,21 @@ var StarUI = {
@@ -303,6 +305,21 @@ var StarUI = {
}
},

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 18b91c95b88d11ddb257de03f12bc3e4cc75a96a..f117cfa7c9d1f9eb537cebbef42a9d2d945d53ac 100644
index 12fa0cf79aade28581016adf96df85386dabdcef..846add9b040abf0f7378ebaaadef007013f5a1ec 100644
--- a/browser/base/content/navigator-toolbox.inc.xhtml
+++ b/browser/base/content/navigator-toolbox.inc.xhtml
@@ -2,7 +2,7 @@
@@ -11,7 +11,7 @@ index 18b91c95b88d11ddb257de03f12bc3e4cc75a96a..f117cfa7c9d1f9eb537cebbef42a9d2d
<vbox id="titlebar">
<!-- Menu -->
@@ -32,10 +32,11 @@
@@ -32,13 +32,14 @@
<hbox class="titlebar-spacer" type="pre-tabs"/>
@@ -20,11 +20,14 @@ index 18b91c95b88d11ddb257de03f12bc3e4cc75a96a..f117cfa7c9d1f9eb537cebbef42a9d2d
<toolbartabstop/>
<hbox id="TabsToolbar-customization-target" flex="1">
<toolbarbutton id="firefox-view-button"
+ hidden="true"
class="toolbarbutton-1 chromeclass-toolbar-additional"
data-l10n-id="toolbar-button-firefox-view-2"
role="button"
@@ -50,7 +51,7 @@
+ hidden="true"
aria-pressed="false"
oncommand="FirefoxViewHandler.openTab();"
onmousedown="FirefoxViewHandler.openToolbarMouseEvent(event);"
@@ -50,16 +51,21 @@
aria-multiselectable="true"
setfocus="false"
tooltip="tabbrowser-tab-tooltip"
@@ -33,16 +36,22 @@ index 18b91c95b88d11ddb257de03f12bc3e4cc75a96a..f117cfa7c9d1f9eb537cebbef42a9d2d
stopwatchid="FX_TAB_CLICK_MS">
<hbox class="tab-drop-indicator" hidden="true"/>
# If the name (tabbrowser-arrowscrollbox) or structure of this changes
@@ -66,7 +67,7 @@
tooltip="dynamic-shortcut-tooltip"
data-l10n-id="tabs-toolbar-new-tab"/>
</html:div>
- <arrowscrollbox id="tabbrowser-arrowscrollbox" orient="horizontal" flex="1" style="min-width: 1px;" clicktoscroll="" scrolledtostart="" scrolledtoend="">
+ <arrowscrollbox id="tabbrowser-arrowscrollbox" orient="vertical" style="min-width: 1px;" clicktoscroll="" scrolledtostart="" scrolledtoend="">
# significantly, there is an optimization in
# DisplayPortUtils::MaybeCreateDisplayPortInFirstScrollFrameEncountered based
# the current structure that we may want to revisit.
+ <html:div id="zen-essentials-container"></html:div>
+ <hbox id="zen-current-workspace-indicator">
+ <hbox id="zen-current-workspace-indicator-icon"></hbox>
+ <hbox id="zen-current-workspace-indicator-name"></hbox>
+ </hbox>
<html:div id="vertical-pinned-tabs-container"></html:div>
<html:div id="vertical-pinned-tabs-container-separator"></html:div>
- <arrowscrollbox id="tabbrowser-arrowscrollbox" orient="horizontal" flex="1" clicktoscroll="" scrolledtostart="" scrolledtoend="">
+ <arrowscrollbox id="tabbrowser-arrowscrollbox" orient="vertical" flex="1" clicktoscroll="" scrolledtostart="" scrolledtoend="">
<tab is="tabbrowser-tab" class="tabbrowser-tab" selected="true" visuallyselected="" fadein=""/>
<hbox id="tabbrowser-arrowscrollbox-periphery">
<toolbartabstop/>
@@ -113,9 +114,10 @@
@@ -113,9 +119,10 @@
<toolbarbutton id="content-analysis-indicator"
oncommand="ContentAnalysis.showPanel(this, PanelUI);"
class="toolbarbutton-1 content-analysis-indicator-icon"/>
@@ -55,15 +64,7 @@ index 18b91c95b88d11ddb257de03f12bc3e4cc75a96a..f117cfa7c9d1f9eb537cebbef42a9d2d
</toolbar>
</vbox>
@@ -471,6 +473,7 @@
<toolbarbutton id="fxa-toolbar-menu-button" class="toolbarbutton-1 chromeclass-toolbar-additional subviewbutton-nav"
badged="true"
+ hidden="true"
delegatesanchor="true"
onmousedown="gSync.toggleAccountPanel(this, event)"
onkeypress="gSync.toggleAccountPanel(this, event)"
@@ -522,6 +525,7 @@
@@ -531,6 +538,7 @@
consumeanchor="PanelUI-button"
data-l10n-id="appmenu-menu-button-closed2"/>
</toolbaritem>

View File

@@ -1,8 +1,8 @@
diff --git a/browser/base/content/nsContextMenu.sys.mjs b/browser/base/content/nsContextMenu.sys.mjs
index 6c2317b8036378c6b8e0ad9a4fe71388bcb385f5..4bf277b02d3c69efc0f2d46c8b0f9deb7d7fb45a 100644
index 7f3dc585937c5ac96c0d09a786515305afb4fe1a..9216931e7d805743f0b00e74039be042456e63f3 100644
--- a/browser/base/content/nsContextMenu.sys.mjs
+++ b/browser/base/content/nsContextMenu.sys.mjs
@@ -1153,6 +1153,13 @@ export class nsContextMenu {
@@ -1047,6 +1047,13 @@ export class nsContextMenu {
!this.isSecureAboutPage()
);
@@ -13,6 +13,6 @@ index 6c2317b8036378c6b8e0ad9a4fe71388bcb385f5..4bf277b02d3c69efc0f2d46c8b0f9deb
+
+ this.showItem("context-zenSplitLink", this.onLink && !this.onMailtoLink && !this.onTelLink);
+
let copyLinkSeparator = this.document.getElementById(
"context-sep-copylink"
);
let canNotStrip =
lazy.STRIP_ON_SHARE_CAN_DISABLE && !this.#canStripParams();

View File

@@ -16,23 +16,27 @@
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-urlbar.css" />
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-workspaces.css" />
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-decks.css" />
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-glance.css" />
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-profile-dialog.css" />
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-sidebar-panels.css" />
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-popup.css" />
<link rel="stylesheet" type="text/css" href="chrome://browser/content/zen-styles/zen-compact-mode.css" />
<link rel="stylesheet" type="text/css" href="chrome://browser/skin/zen-icons/icons.css" />
</linkset>
<!-- Scripts used all over the browser -->
<script src="chrome://browser/content/zen-components/ZenThemesCommon.mjs" />
<script src="chrome://browser/content/zen-components/ZenActorsManager.mjs" />
<script src="chrome://browser/content/zen-components/ZenGlanceManager.mjs" />
<script src="chrome://browser/content/zen-components/ZenThemesImporter.mjs" />
<script src="chrome://browser/content/zen-components/ZenCompactMode.mjs" />
<script src="chrome://browser/content/ZenUIManager.mjs" />
<script src="chrome://browser/content/zen-components/ZenSidebarManager.mjs"/>
<script src="chrome://browser/content/zen-components/ZenTabUnloader.mjs" />
<script src="chrome://browser/content/zen-components/ZenWorkspaces.mjs" />
<script src="chrome://browser/content/zen-components/ZenPinnedTabsStorage.mjs" />
<script src="chrome://browser/content/zen-components/ZenPinnedTabManager.mjs" />
<script src="chrome://browser/content/zen-components/ZenWorkspacesStorage.mjs" />
<script src="chrome://browser/content/zen-components/ZenWorkspacesSync.mjs" />
<script src="chrome://browser/content/zen-components/ZenGradientGenerator.mjs" />

View File

@@ -4,21 +4,24 @@
content/browser/ZenStartup.mjs (content/ZenStartup.mjs)
content/browser/ZenUIManager.mjs (content/ZenUIManager.mjs)
content/browser/ZenCustomizableUI.sys.mjs (content/ZenCustomizableUI.sys.mjs)
content/browser/zen-components/ZenCompactMode.mjs (content/zen-components/src/ZenCompactMode.mjs)
content/browser/zen-components/ZenViewSplitter.mjs (content/zen-components/src/ZenViewSplitter.mjs)
content/browser/zen-components/ZenThemesCommon.mjs (content/zen-components/src/ZenThemesCommon.mjs)
content/browser/zen-components/ZenWorkspaces.mjs (content/zen-components/src/ZenWorkspaces.mjs)
content/browser/zen-components/ZenWorkspacesStorage.mjs (content/zen-components/src/ZenWorkspacesStorage.mjs)
content/browser/zen-components/ZenWorkspacesSync.mjs (content/zen-components/src/ZenWorkspacesSync.mjs)
content/browser/zen-components/ZenSidebarManager.mjs (content/zen-components/src/ZenSidebarManager.mjs)
content/browser/zen-components/ZenProfileDialogUI.mjs (content/zen-components/src/ZenProfileDialogUI.mjs)
content/browser/zen-components/ZenKeyboardShortcuts.mjs (content/zen-components/src/ZenKeyboardShortcuts.mjs)
content/browser/zen-components/ZenThemeBuilder.mjs (content/zen-components/src/ZenThemeBuilder.mjs)
content/browser/zen-components/ZenThemesImporter.mjs (content/zen-components/src/ZenThemesImporter.mjs)
content/browser/zen-components/ZenTabUnloader.mjs (content/zen-components/src/ZenTabUnloader.mjs)
content/browser/zen-components/ZenPinnedTabManager.mjs (content/zen-components/src/ZenPinnedTabManager.mjs)
content/browser/zen-components/ZenCommonUtils.mjs (content/zen-components/src/ZenCommonUtils.mjs)
content/browser/zen-components/ZenGradientGenerator.mjs (content/zen-components/src/ZenGradientGenerator.mjs)
content/browser/zen-components/ZenCompactMode.mjs (zen-components/ZenCompactMode.mjs)
content/browser/zen-components/ZenViewSplitter.mjs (zen-components/ZenViewSplitter.mjs)
content/browser/zen-components/ZenThemesCommon.mjs (zen-components/ZenThemesCommon.mjs)
content/browser/zen-components/ZenWorkspaces.mjs (zen-components/ZenWorkspaces.mjs)
content/browser/zen-components/ZenWorkspacesStorage.mjs (zen-components/ZenWorkspacesStorage.mjs)
content/browser/zen-components/ZenWorkspacesSync.mjs (zen-components/ZenWorkspacesSync.mjs)
content/browser/zen-components/ZenSidebarManager.mjs (zen-components/ZenSidebarManager.mjs)
content/browser/zen-components/ZenProfileDialogUI.mjs (zen-components/ZenProfileDialogUI.mjs)
content/browser/zen-components/ZenKeyboardShortcuts.mjs (zen-components/ZenKeyboardShortcuts.mjs)
content/browser/zen-components/ZenThemeBuilder.mjs (zen-components/ZenThemeBuilder.mjs)
content/browser/zen-components/ZenThemesImporter.mjs (zen-components/ZenThemesImporter.mjs)
content/browser/zen-components/ZenTabUnloader.mjs (zen-components/ZenTabUnloader.mjs)
content/browser/zen-components/ZenPinnedTabsStorage.mjs (zen-components/ZenPinnedTabsStorage.mjs)
content/browser/zen-components/ZenPinnedTabManager.mjs (zen-components/ZenPinnedTabManager.mjs)
content/browser/zen-components/ZenCommonUtils.mjs (zen-components/ZenCommonUtils.mjs)
content/browser/zen-components/ZenGradientGenerator.mjs (zen-components/ZenGradientGenerator.mjs)
content/browser/zen-components/ZenGlanceManager.mjs (zen-components/ZenGlanceManager.mjs)
content/browser/zen-components/ZenActorsManager.mjs (zen-components/ZenActorsManager.mjs)
content/browser/zen-styles/zen-theme.css (content/zen-styles/zen-theme.css)
content/browser/zen-styles/zen-buttons.css (content/zen-styles/zen-buttons.css)
@@ -32,6 +35,7 @@
content/browser/zen-styles/zen-sidebar.css (content/zen-styles/zen-sidebar.css)
content/browser/zen-styles/zen-toolbar.css (content/zen-styles/zen-toolbar.css)
content/browser/zen-styles/zen-decks.css (content/zen-styles/zen-decks.css)
content/browser/zen-styles/zen-glance.css (content/zen-styles/zen-glance.css)
content/browser/zen-styles/zen-browser-container.css (content/zen-styles/zen-browser-container.css)
content/browser/zen-styles/zen-workspaces.css (content/zen-styles/zen-workspaces.css)
content/browser/zen-styles/zen-profile-dialog.css (content/zen-styles/zen-profile-dialog.css)
@@ -52,5 +56,7 @@
content/browser/zen-images/gradient-display.png (content/zen-images/gradient-display.png)
# Actors
content/browser/zen-components/actors/ZenThemeMarketplaceParent.sys.mjs (content/zen-components/src/actors/ZenThemeMarketplaceParent.sys.mjs)
content/browser/zen-components/actors/ZenThemeMarketplaceChild.sys.mjs (content/zen-components/src/actors/ZenThemeMarketplaceChild.sys.mjs)
content/browser/zen-components/actors/ZenThemeMarketplaceParent.sys.mjs (zen-components/actors/ZenThemeMarketplaceParent.sys.mjs)
content/browser/zen-components/actors/ZenThemeMarketplaceChild.sys.mjs (zen-components/actors/ZenThemeMarketplaceChild.sys.mjs)
content/browser/zen-components/actors/ZenGlanceChild.sys.mjs (zen-components/actors/ZenGlanceChild.sys.mjs)
content/browser/zen-components/actors/ZenGlanceParent.sys.mjs (zen-components/actors/ZenGlanceParent.sys.mjs)

View File

@@ -0,0 +1,4 @@
<vbox id="zen-glance-sidebar-container">
<toolbarbutton id="zen-glance-sidebar-close" class="toolbarbutton-1" oncommand="gZenGlanceManager.closeGlance()"/>
<toolbarbutton id="zen-glance-sidebar-open" class="toolbarbutton-1" oncommand="gZenGlanceManager.fullyOpenGlance()"/>
</vbox>

View File

@@ -4,5 +4,4 @@
<script type="text/javascript">
Services.scriptloader.loadSubScript("chrome://browser/content/zen-components/ZenCommonUtils.mjs", this);
Services.scriptloader.loadSubScript("chrome://browser/content/zen-components/ZenKeyboardShortcuts.mjs", this);
Services.scriptloader.loadSubScript("chrome://browser/content/zen-components/ZenPinnedTabManager.mjs", this);
</script>

View File

@@ -7,5 +7,19 @@
skipintoolbarset="true"
context="toolbar-context-menu"
mode="icons">
<toolbarbutton removeable="true" class="toolbarbutton-1 zen-sidebar-action-button" id="zen-sidepanel-button" data-l10n-id="sidebar-zen-sidepanel" onclick="gZenBrowserManagerSidebar.toggle();"></toolbarbutton>
<toolbarbutton id="zen-profile-button"
class="zen-sidebar-action-button toolbarbutton-1 chromeclass-toolbar-additional"
delegatesanchor="true"
onmousedown="ZenProfileDialogUI.showSubView(this, event)"
onkeypress="ZenProfileDialogUI.showSubView(this, event)"
consumeanchor="zen-profile-button"
closemenu="none"
data-l10n-id="toolbar-button-account"
cui-areatype="toolbar"
badged="true"
removable="true">
<vbox>
<image id="zen-profile-button-icon" />
</vbox>
</toolbarbutton>
</toolbar>

View File

@@ -213,3 +213,121 @@
right: -2px;
}
}
/* Mark: Zen Glance */
@keyframes zen-glance-overlay-animation {
from {
opacity: 0;
transform: scale(0.8);
}
to {
opacity: 1;
transform: scale(1);
}
}
@keyframes zen-glance-overlay-animation-out {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
@keyframes zen-glance-content-animation {
/* make the box appear from initial width/height and x/y coordinates */
0% {
opacity: 1;
}
30% {
top: 50%;
left: 50%;
transform: translate(-50%, -50%) translateZ(0);
}
70% {
/* make the box grow to full width/height */
opacity: 1;
transform: translate(-50%, -50%) translateZ(0);
top: 50%;
left: 50%;
width: 87%;
height: calc(102% + 10px);
}
100% {
/* make the box shrink to final width/height and x/y coordinates */
transform: translate(-50%, -50%) translateZ(0);
opacity: 1;
width: 85%;
height: calc(100% + 10px);
top: 50%;
left: 50%;
}
}
@keyframes zen-glance-content-animation-out {
0% {
border: none;
}
100% {
transform: translate(-50%, -50%);
opacity: 1;
width: 85%;
height: calc(100% + 10px);
top: 50%;
left: 50%;
}
}
@keyframes zen-glance-buttons-animation-full {
from {
width: 85%;
height: 85%;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
to {
width: 100%;
height: 100%;
top: 0;
left: 0;
opacity: 1;
transform: none;
}
}
@keyframes zen-glance-loading-animation {
0% {
opacity: 1;
width: 80%;
}
90% {
width: 100%;
}
100% {
opacity: 0;
}
}
@keyframes zen-glance-buttons-animation {
from {
right: 0;
opacity: 0;
width: fit-content;
}
to {
opacity: 1;
transform: translateX(-100%) translateY(-50%);
}
}

View File

@@ -4,16 +4,19 @@
border-radius: var(--zen-webview-border-radius, var(--zen-border-radius));
position: relative;
box-shadow: 0 0 1px 1px light-dark(rgba(0, 0, 0, 0.1), var(--zen-colors-border));
box-shadow: 0 0 1px 1px light-dark(rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.5));
& .browserContainer {
overflow: hidden;
border-radius: var(--zen-webview-border-radius, var(--zen-border-radius));
}
}
@media (-moz-bool-pref: 'zen.view.experimental-rounded-view') {
&.deck-selected .browserContainer {
mix-blend-mode: multiply;
}
@media (-moz-bool-pref: 'zen.view.experimental-rounded-view') {
#tabbrowser-tabpanels {
mix-blend-mode: multiply;
-moz-osx-font-smoothing: grayscale;
isolation: isolate;
}
}
}

View File

@@ -1,5 +1,4 @@
#browser,
#appcontent,
#tabbrowser-tabpanels {
background: transparent !important;
}
@@ -14,6 +13,7 @@
& #tabbrowser-tabpanels {
padding-right: var(--zen-element-separation);
padding-bottom: var(--zen-element-separation);
padding-left: 1px; /* Shadow offset */
#browser:has(#navigator-toolbox[zen-right-side='true']) & {
padding-right: 0;
@@ -31,17 +31,40 @@
#browser {
width: 100%;
background: var(--zen-main-browser-background);
&::before {
content: '';
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAMAAAAp4XiDAAAAUVBMVEWFhYWDg4N3d3dtbW17e3t1dXWBgYGHh4d5eXlzc3OLi4ubm5uVlZWPj4+NjY19fX2JiYl/f39ra2uRkZGZmZlpaWmXl5dvb29xcXGTk5NnZ2c8TV1mAAAAG3RSTlNAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAvEOwtAAAFVklEQVR4XpWWB67c2BUFb3g557T/hRo9/WUMZHlgr4Bg8Z4qQgQJlHI4A8SzFVrapvmTF9O7dmYRFZ60YiBhJRCgh1FYhiLAmdvX0CzTOpNE77ME0Zty/nWWzchDtiqrmQDeuv3powQ5ta2eN0FY0InkqDD73lT9c9lEzwUNqgFHs9VQce3TVClFCQrSTfOiYkVJQBmpbq2L6iZavPnAPcoU0dSw0SUTqz/GtrGuXfbyyBniKykOWQWGqwwMA7QiYAxi+IlPdqo+hYHnUt5ZPfnsHJyNiDtnpJyayNBkF6cWoYGAMY92U2hXHF/C1M8uP/ZtYdiuj26UdAdQQSXQErwSOMzt/XWRWAz5GuSBIkwG1H3FabJ2OsUOUhGC6tK4EMtJO0ttC6IBD3kM0ve0tJwMdSfjZo+EEISaeTr9P3wYrGjXqyC1krcKdhMpxEnt5JetoulscpyzhXN5FRpuPHvbeQaKxFAEB6EN+cYN6xD7RYGpXpNndMmZgM5Dcs3YSNFDHUo2LGfZuukSWyUYirJAdYbF3MfqEKmjM+I2EfhA94iG3L7uKrR+GdWD73ydlIB+6hgref1QTlmgmbM3/LeX5GI1Ux1RWpgxpLuZ2+I+IjzZ8wqE4nilvQdkUdfhzI5QDWy+kw5Wgg2pGpeEVeCCA7b85BO3F9DzxB3cdqvBzWcmzbyMiqhzuYqtHRVG2y4x+KOlnyqla8AoWWpuBoYRxzXrfKuILl6SfiWCbjxoZJUaCBj1CjH7GIaDbc9kqBY3W/Rgjda1iqQcOJu2WW+76pZC9QG7M00dffe9hNnseupFL53r8F7YHSwJWUKP2q+k7RdsxyOB11n0xtOvnW4irMMFNV4H0uqwS5ExsmP9AxbDTc9JwgneAT5vTiUSm1E7BSflSt3bfa1tv8Di3R8n3Af7MNWzs49hmauE2wP+ttrq+AsWpFG2awvsuOqbipWHgtuvuaAE+A1Z/7gC9hesnr+7wqCwG8c5yAg3AL1fm8T9AZtp/bbJGwl1pNrE7RuOX7PeMRUERVaPpEs+yqeoSmuOlokqw49pgomjLeh7icHNlG19yjs6XXOMedYm5xH2YxpV2tc0Ro2jJfxC50ApuxGob7lMsxfTbeUv07TyYxpeLucEH1gNd4IKH2LAg5TdVhlCafZvpskfncCfx8pOhJzd76bJWeYFnFciwcYfubRc12Ip/ppIhA1/mSZ/RxjFDrJC5xifFjJpY2Xl5zXdguFqYyTR1zSp1Y9p+tktDYYSNflcxI0iyO4TPBdlRcpeqjK/piF5bklq77VSEaA+z8qmJTFzIWiitbnzR794USKBUaT0NTEsVjZqLaFVqJoPN9ODG70IPbfBHKK+/q/AWR0tJzYHRULOa4MP+W/HfGadZUbfw177G7j/OGbIs8TahLyynl4X4RinF793Oz+BU0saXtUHrVBFT/DnA3ctNPoGbs4hRIjTok8i+algT1lTHi4SxFvONKNrgQFAq2/gFnWMXgwffgYMJpiKYkmW3tTg3ZQ9Jq+f8XN+A5eeUKHWvJWJ2sgJ1Sop+wwhqFVijqWaJhwtD8MNlSBeWNNWTa5Z5kPZw5+LbVT99wqTdx29lMUH4OIG/D86ruKEauBjvH5xy6um/Sfj7ei6UUVk4AIl3MyD4MSSTOFgSwsH/QJWaQ5as7ZcmgBZkzjjU1UrQ74ci1gWBCSGHtuV1H2mhSnO3Wp/3fEV5a+4wz//6qy8JxjZsmxxy5+4w9CDNJY09T072iKG0EnOS0arEYgXqYnXcYHwjTtUNAcMelOd4xpkoqiTYICWFq0JSiPfPDQdnt+4/wuqcXY47QILbgAAAABJRU5ErkJggg==);
pointer-events: none;
z-index: 0;
opacity: var(--zen-grainy-background-opacity, 0);
}
}
:root:not([inDOMFullscreen='true']) #appcontent,
#sidebar-box {
/** Sidebar is already hidden in full screen mode */
border: none;
}
#zen-main-app-wrapper {
background: var(--zen-main-browser-background);
@supports (-moz-osx-font-smoothing: auto) {
#zen-main-app-wrapper,
#zen-appcontent-wrapper,
#zen-sidebar-splitter {
appearance: -moz-window-titlebar !important;
}
}
#zen-main-app-wrapper {
background: transparent;
overflow: hidden;
& > * {
z-index: 1;
}
@@ -59,22 +82,14 @@
animation: zen-main-app-wrapper-animation .5s ease-in-out forwards;
border-radius: env(-moz-gtk-csd-titlebar-radius);
}
&::after {
content: '';
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAMAAAAp4XiDAAAAUVBMVEWFhYWDg4N3d3dtbW17e3t1dXWBgYGHh4d5eXlzc3OLi4ubm5uVlZWPj4+NjY19fX2JiYl/f39ra2uRkZGZmZlpaWmXl5dvb29xcXGTk5NnZ2c8TV1mAAAAG3RSTlNAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAvEOwtAAAFVklEQVR4XpWWB67c2BUFb3g557T/hRo9/WUMZHlgr4Bg8Z4qQgQJlHI4A8SzFVrapvmTF9O7dmYRFZ60YiBhJRCgh1FYhiLAmdvX0CzTOpNE77ME0Zty/nWWzchDtiqrmQDeuv3powQ5ta2eN0FY0InkqDD73lT9c9lEzwUNqgFHs9VQce3TVClFCQrSTfOiYkVJQBmpbq2L6iZavPnAPcoU0dSw0SUTqz/GtrGuXfbyyBniKykOWQWGqwwMA7QiYAxi+IlPdqo+hYHnUt5ZPfnsHJyNiDtnpJyayNBkF6cWoYGAMY92U2hXHF/C1M8uP/ZtYdiuj26UdAdQQSXQErwSOMzt/XWRWAz5GuSBIkwG1H3FabJ2OsUOUhGC6tK4EMtJO0ttC6IBD3kM0ve0tJwMdSfjZo+EEISaeTr9P3wYrGjXqyC1krcKdhMpxEnt5JetoulscpyzhXN5FRpuPHvbeQaKxFAEB6EN+cYN6xD7RYGpXpNndMmZgM5Dcs3YSNFDHUo2LGfZuukSWyUYirJAdYbF3MfqEKmjM+I2EfhA94iG3L7uKrR+GdWD73ydlIB+6hgref1QTlmgmbM3/LeX5GI1Ux1RWpgxpLuZ2+I+IjzZ8wqE4nilvQdkUdfhzI5QDWy+kw5Wgg2pGpeEVeCCA7b85BO3F9DzxB3cdqvBzWcmzbyMiqhzuYqtHRVG2y4x+KOlnyqla8AoWWpuBoYRxzXrfKuILl6SfiWCbjxoZJUaCBj1CjH7GIaDbc9kqBY3W/Rgjda1iqQcOJu2WW+76pZC9QG7M00dffe9hNnseupFL53r8F7YHSwJWUKP2q+k7RdsxyOB11n0xtOvnW4irMMFNV4H0uqwS5ExsmP9AxbDTc9JwgneAT5vTiUSm1E7BSflSt3bfa1tv8Di3R8n3Af7MNWzs49hmauE2wP+ttrq+AsWpFG2awvsuOqbipWHgtuvuaAE+A1Z/7gC9hesnr+7wqCwG8c5yAg3AL1fm8T9AZtp/bbJGwl1pNrE7RuOX7PeMRUERVaPpEs+yqeoSmuOlokqw49pgomjLeh7icHNlG19yjs6XXOMedYm5xH2YxpV2tc0Ro2jJfxC50ApuxGob7lMsxfTbeUv07TyYxpeLucEH1gNd4IKH2LAg5TdVhlCafZvpskfncCfx8pOhJzd76bJWeYFnFciwcYfubRc12Ip/ppIhA1/mSZ/RxjFDrJC5xifFjJpY2Xl5zXdguFqYyTR1zSp1Y9p+tktDYYSNflcxI0iyO4TPBdlRcpeqjK/piF5bklq77VSEaA+z8qmJTFzIWiitbnzR794USKBUaT0NTEsVjZqLaFVqJoPN9ODG70IPbfBHKK+/q/AWR0tJzYHRULOa4MP+W/HfGadZUbfw177G7j/OGbIs8TahLyynl4X4RinF793Oz+BU0saXtUHrVBFT/DnA3ctNPoGbs4hRIjTok8i+algT1lTHi4SxFvONKNrgQFAq2/gFnWMXgwffgYMJpiKYkmW3tTg3ZQ9Jq+f8XN+A5eeUKHWvJWJ2sgJ1Sop+wwhqFVijqWaJhwtD8MNlSBeWNNWTa5Z5kPZw5+LbVT99wqTdx29lMUH4OIG/D86ruKEauBjvH5xy6um/Sfj7ei6UUVk4AIl3MyD4MSSTOFgSwsH/QJWaQ5as7ZcmgBZkzjjU1UrQ74ci1gWBCSGHtuV1H2mhSnO3Wp/3fEV5a+4wz//6qy8JxjZsmxxy5+4w9CDNJY09T072iKG0EnOS0arEYgXqYnXcYHwjTtUNAcMelOd4xpkoqiTYICWFq0JSiPfPDQdnt+4/wuqcXY47QILbgAAAABJRU5ErkJggg==);
pointer-events: none;
z-index: 0;
opacity: var(--zen-grainy-background-opacity, 0);
}
}
}
#zen-appcontent-wrapper {
max-width: 100%;
overflow-x: hidden;
}
#tabbrowser-tabbox {
display: flex;
flex-direction: row;
@@ -85,6 +100,8 @@
height: 100%;
}
:root:not([inDOMFullscreen='true']) #tabbrowser-tabbox {
padding: 2px; /* To allow the web view's shadow to be visible */
@media (-moz-platform: macos) {
.titlebar-buttonbox-container {
margin-inline-end: 8px;
}
}

View File

@@ -16,10 +16,13 @@ xul|button {
transition: 0.1s;
min-width: 100px !important;
font-weight: 500 !important;
border: 1px solid var(--zen-colors-border);
box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.05) !important;
}
button:active {
transform: scale(0.95);
button:not(#zen-workspaces-button):active {
transform: scale(0.98);
}
html|button:not(:is(.tab-button, .ghost-button, .button-toggle, .button-edit, .button-add, )),

View File

@@ -28,7 +28,7 @@
#navigator-toolbox {
--zen-toolbox-max-width: 54px !important;
--zen-compact-float: calc(var(--zen-element-separation) + 1px);
--zen-compact-float: calc(var(--zen-element-separation) - 1px);
position: absolute;
z-index: 9;
transition: transform 0.2s ease-in-out, opacity 0.2s ease-in-out;
@@ -41,6 +41,7 @@
padding-top: 0 !important;
#browser:has(#navigator-toolbox[zen-right-side='true']) & {
--zen-compact-float: calc(var(--zen-element-separation) + 1px);
padding-left: unset !important;
padding-right: var(--zen-compact-float) !important;
left: calc(100% - var(--zen-element-separation));
@@ -49,13 +50,19 @@
}
#titlebar {
background: var(--zen-main-browser-background) !important;
background-attachment: fixed !important;
background-size: 2000px !important; /* Dont ask me why */
box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.2);
border-radius: calc(var(--zen-border-radius) + 2px);
box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.1);
border-radius: calc(var(--zen-border-radius) - 2px);
padding: var(--zen-toolbox-padding) !important;
position: relative;
background: var(--zen-dialog-background);
outline: 1px solid light-dark(rgba(0, 0, 0, 0.1), rgba(255, 255, 255, 0.1));
@media (-moz-bool-pref: 'zen.view.compact.color-sidebar') {
background: var(--zen-main-browser-background-toolbar) !important;
background-attachment: fixed !important;
background-size: 2000px !important; /* Dont ask me why */
backdrop-filter: blur(5px) !important;
}
}
/* Mark: toolbox as collapsed */
#zen-tabbox-wrapper > #navigator-toolbox:not(#navigator-toolbox:is(#navigator-toolbox[zen-expanded='true'])) {
@@ -83,7 +90,7 @@
#navigator-toolbox[movingtab],
#mainPopupSet:has(> #appMenu-popup:hover) ~ toolbox,
#navigator-toolbox:has(.tabbrowser-tab:active),
#navigator-toolbox:has(*[open='true']:not(tab):not(#zen-sidepanel-button)) {
#navigator-toolbox:has(*[open='true']:not(tab):not(.zen-compact-mode-ignore)) {
opacity: 1;
transform: translateX(calc(100% - var(--zen-element-separation)));
@@ -112,26 +119,26 @@
}
#zen-appcontent-navbar-container {
--zen-compact-toolbar-offset: 5px;
position: absolute;
top: 0;
transform: translateY(calc(-100% + var(--zen-element-separation) + 1px));
left: 0;
z-index: 10;
background: var(--zen-themed-toolbar-bg);
box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.1) !important;
box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.3) !important;
border-bottom-left-radius: var(--zen-border-radius);
border-bottom-right-radius: var(--zen-border-radius);
border: 1px solid var(--zen-colors-border);
border-top-width: 0px;
transition: all 0.1s ease-in-out;
width: 100%;
opacity: 0;
background: var(--zen-dialog-background);
:root[tabsintitlebar][sizemode='normal']:not([gtktiledwindow='true']) & {
border-top-left-radius: env(-moz-gtk-csd-titlebar-radius);
border-top-right-radius: env(-moz-gtk-csd-titlebar-radius);
border-left-width: 0;
border-right-width: 0;
@media (-moz-bool-pref: 'zen.view.compact.color-toolbar') {
background-attachment: fixed;
backdrop-filter: blur(5px);
background: var(--zen-main-browser-background-toolbar);
background-size: 100% 2000px;
border-bottom: 1px solid var(--zen-colors-border);
}
}

View File

@@ -27,7 +27,7 @@
#tabbrowser-tabpanels[zen-split-view='true'] > [zen-split='true'], #zen-splitview-dropzone {
flex: 1;
margin: calc(var(--zen-split-column-gap) / 2) calc(var(--zen-split-row-gap) / 2);
margin: calc(var(--zen-split-column-gap) / 2) calc(var(--zen-split-row-gap) / 2 + 1px);
position: absolute !important;
overflow: hidden;
}
@@ -45,10 +45,11 @@
#tabbrowser-tabpanels[zen-split-view='true'] .browserSidebarContainer.deck-selected {
outline: 2px solid var(--zen-primary-color) !important;
outline-offset: -1px;
}
#tabbrowser-tabbox {
--zen-split-row-gap: calc(var(--zen-element-separation) + 2px);
--zen-split-row-gap: calc(var(--zen-element-separation) + 1px);
--zen-split-column-gap: calc(var(--zen-element-separation) + 1px);
}
@@ -99,21 +100,22 @@
.zen-split-view-splitter {
visibility: inherit;
cursor: ew-resize;
-moz-subtree-hidden-only-visually: 0;
position: absolute;
pointer-events: all;
}
.zen-split-view-splitter[orient='vertical'] {
width: var(--zen-split-row-gap);
margin-left: calc(var(--zen-split-row-gap) / -2);
cursor: ew-resize;
}
.zen-split-view-splitter[orient='horizontal'] {
height: var(--zen-split-column-gap);
margin-top: calc(var(--zen-split-column-gap) / -2);
cursor: n-resize;
cursor: ns-resize;
}
#zen-split-views-box:not([hidden='true']) {

View File

@@ -0,0 +1,142 @@
.browserSidebarContainer:has([zen-glance-selected]),
.browserSidebarContainer.zen-glance-overlay {
visibility: inherit;
}
#tabbrowser-tabpanels {
transition: transform 0.1s ease-in-out;
}
#tabbrowser-tabpanels:has(.zen-glance-background) {
transform: scale(0.98);
backdrop-filter: blur(5px);
& .zen-glance-background {
opacity: 0.6;
transition: opacity 0.1s ease-in-out;
}
}
#zen-glance-sidebar-container {
display: none;
& toolbarbutton:hover {
background: var(--button-background-color-hover);
}
}
.browserSidebarContainer.zen-glance-overlay {
&[fade-out='true'] {
background: transparent;
opacity: 1;
& .browserContainer {
opacity: 1 !important;
animation: zen-glance-content-animation-out .3s ease-out forwards !important;
animation-direction: reverse !important;
& browser {
opacity: 0;
transition: opacity .1s ease-in-out;
}
& #zen-glance-sidebar-container {
opacity: 0;
transition: opacity .1s ease-in-out;
}
}
}
& .browserContainer {
background: var(--zen-dialog-background);
box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.2);
transform: translate(var(--initial-x), var(--initial-y));
width: var(--initial-width);
height: var(--initial-height);
position: absolute;
border-radius: var(--zen-border-radius);
opacity: 0;
top: 0;
left: 0;
flex: unset !important;
border: 1px solid var(--zen-colors-border);
&[has-finished-animation='true'] {
position: relative !important;
transition: 0s !important;
transform: none !important;
margin: auto !important;
top: -5px !important;
left: 0 !important;
}
& #zen-glance-sidebar-container {
position: fixed;
display: flex;
top: 10%;
left: 0;
transform: translateY(-50%);
opacity: 0;
background: var(--zen-dialog-background);
border: 1px solid var(--zen-colors-border);
border-right: none;
border-top-left-radius: var(--zen-border-radius);
border-bottom-left-radius: var(--zen-border-radius);
padding: 5px;
gap: 6px;
animation: zen-glance-buttons-animation 0.2s ease-in-out forwards;
animation-delay: 0.3s;
& toolbarbutton {
width: 32px;
height: 32px;
& label {
display: none;
}
}
}
& browser {
width: 100%;
height: 100%;
opacity: 1;
transition: opacity 0.2s ease-in-out;
transition-delay: 0.1s;
}
&[animate-full='true'] {
opacity: 1;
animation: zen-glance-buttons-animation-full 0.2s ease-in-out forwards !important;
& #zen-glance-sidebar-container {
opacity: 0 !important;
}
}
&[animate='true'] {
animation: zen-glance-content-animation .3s ease-in-out forwards;
animation-delay: 0.1s;
&:not([animate-end='true']) {
pointer-events: none;
& browser {
opacity: 0;
visibility: hidden;
}
& #zen-glance-sidebar-container {
opacity: 0;
animation: none;
pointer-events: none;
}
}
}
}
}

View File

@@ -1,7 +1,7 @@
panel[type='arrow'][animate][animate='open'] {
animation: zen-jello-animation 0.2s ease-in-out;
panel[type='arrow'][animate][animate='open']::part(content) {
animation: zen-jello-animation 0.3s ease-in-out;
}
panel[type='arrow'][animate]:not([animate='open']) {
animation: zen-jello-out-animation 0.2s ease-in-out;
panel[type='arrow'][animate]:not([animate='open'])::part(content) {
animation: zen-jello-out-animation 0.3s ease-in-out;
}

View File

@@ -5,7 +5,7 @@
}
#PanelUI-zen-profiles-header {
width: 280px;
width: 100%;
height: 130px;
background: color-mix(in srgb, var(--zen-primary-color) 80%, white 20%);
position: relative;

View File

@@ -21,14 +21,15 @@
}
& #zen-profile-button-icon {
width: 16px;
height: 16px;
width: 20px;
height: 20px;
list-style-image: var(--avatar-image-url);
border-radius: 50%;
pointer-events: none;
list-style-image: var(--avatar-image-url);
-moz-context-properties: fill;
fill: currentColor;
border: 1px solid var(--zen-colors-border);
}
}
@@ -72,4 +73,4 @@
/* Menubar */
#toolbar-menubar {
display: none !important;
}
}

View File

@@ -9,6 +9,7 @@
/* Watermark */
#zen-watermark {
--zen-themed-toolbar-bg-transparency: 1 !important;
position: absolute;
top: 0;
left: 0;

View File

@@ -16,7 +16,19 @@
}
#browser {
--zen-toolbox-padding: 5px;
--zen-toolbox-padding: calc(var(--zen-element-separation) / 1.5);
}
#vertical-pinned-tabs-container-separator {
background: light-dark(rgba(1, 1, 1, 0.075), rgba(255, 255, 255, 0.1));
margin: 8px auto;
border: none;
height: 1px;
width: 98%;
#vertical-pinned-tabs-container:not(:has(tab:not([hidden]))) + & {
display: none;
}
}
#navigator-toolbox {
@@ -24,7 +36,7 @@
--tab-border-radius: var(--border-radius-medium);
--zen-toolbox-min-width: 1px;
--tab-hover-background-color: var(--toolbarbutton-hover-background) !important;
--tab-hover-background-color: color-mix(in srgb, var(--toolbarbutton-hover-background) 50%, transparent 50%);
min-width: var(--zen-toolbox-min-width);
margin-top: 0 !important; /* Fix full screen mode */
@@ -32,6 +44,7 @@
padding-top: var(--zen-toolbox-top-align);
padding-bottom: var(--zen-element-separation) !important;
border: none;
order: 0 !important;
display: flex;
@@ -44,6 +57,7 @@
#TabsToolbar-customization-target {
position: relative;
max-width: 100%;
gap: 0;
&::after {
content: '';
@@ -60,7 +74,7 @@
@media (-moz-bool-pref: 'zen.view.show-bottom-border') {
&::after {
background: color-mix(in srgb, var(--zen-colors-border) 70%, transparent 30%);
background: light-dark(rgba(1, 1, 1, 0.075), rgba(255, 255, 255, 0.1));
}
}
@@ -80,24 +94,28 @@
margin-inline-start: 0 !important;
padding-inline-start: 0 !important;
--tab-inner-inline-margin: 0;
position: relative;
border-bottom: 0px solid transparent !important;
--tab-block-margin: 2px;
--tab-selected-bgcolor: light-dark(rgba(255,255,255,.7), color-mix(in srgb, var(--zen-colors-secondary) 50%, transparent 50%));
--tab-selected-bgcolor: light-dark(rgba(255,255,255,.8), rgba(255,255,255,.25));
grid-gap: 0 !important;
&[overflow]::after,
#vertical-tabs-newtab-button {
/* Hide separator they give us, eww */
display: none !important;
}
& .tabbrowser-tab {
animation: zen-slide-in 0.2s ease-in-out;
max-width: unset !important;
max-width: unset;
padding: 0 !important;
position: relative;
height: calc(var(--tab-min-height) + var(--tab-block-margin) * 2) !important;
min-height: calc(var(--tab-min-height) + var(--tab-block-margin) * 2) !important;
border-radius: var(--border-radius-medium);
& .tab-background {
@@ -118,6 +136,39 @@
opacity: 0.5;
}
}
/* We have a tab inside a tab, this means, it's a glance tab */
& .tabbrowser-tab {
border-radius: 5px;
pointer-events: none;
margin: 0;
--toolbarbutton-inner-padding: 0;
& .tab-background {
background: transparent;
box-shadow: none !important;
}
& label { display: none !important; }
& .tab-close-button {
display: none !important;
}
& .tab-icon-image {
--toolbarbutton-inner-padding: 0 !important;
}
}
/* On essentials, glance tabs are floating */
&[zen-essential='true'] .tabbrowser-tab {
position: absolute;
top: 3px;
right: 5px;
--tab-collapsed-width: 15px;
--tab-min-height: 15px;
width: var(--tab-collapsed-width) !important;
z-index: 1;
pointer-events: none;
}
}
}
@@ -145,26 +196,11 @@
#vertical-pinned-tabs-container {
padding-inline-end: 0 !important;
column-gap: 2px !important;
row-gap: 3px !important;
max-height: unset !important;
overflow: visible !important;
display: flex !important;
flex-direction: column;
@media not (prefers-color-scheme: dark) {
--zen-colors-border: var(--zen-colors-tertiary);
}
& .tab-background:not(:hover):not([selected]):not([multiselected]) {
--zen-pinned-tabs-bgcolor: rgba(255, 255, 255, 0.12);
@media not (prefers-color-scheme: dark) {
--zen-pinned-tabs-bgcolor: rgba(1, 1, 1, 0.075);
}
background: var(--zen-pinned-tabs-bgcolor) !important;
backdrop-filter: blur(10px);
}
&[selected] {
box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.05);
& .tabbrowser-tab:not(:hover) .tab-background:not([selected]):not([multiselected]) {
background: transparent !important;
}
& .tabbrowser-tab .tab-content {
@@ -172,6 +208,10 @@
align-items: center;
justify-content: center;
}
.tabbrowser-tab[zen-glance-tab='true'] {
width: fit-content !important;
}
}
/* Mark: toolbox as expanded */
@@ -181,10 +221,11 @@
#navigator-toolbox[zen-user-hover='true'][movingtab],
#navigator-toolbox[zen-user-hover='true'][flash-popup],
#navigator-toolbox[zen-user-hover='true'][has-popup-menu],
#navigator-toolbox[zen-user-hover='true']:has(*[open='true']:not(tab):not(#zen-sidepanel-button)),
#navigator-toolbox[zen-user-hover='true']:has(*[open='true']:not(tab):not(.zen-compact-mode-ignore)),
#navigator-toolbox[zen-expanded='true']:not([zen-user-hover='true'])
) {
--zen-toolbox-min-width: fit-content;
--tab-icon-end-margin: 8.5px;
padding: var(--zen-toolbox-padding);
padding-left: 0;
padding-top: 0;
@@ -193,10 +234,22 @@
min-width: 150px;
}
& #tabbrowser-arrowscrollbox-periphery {
margin-inline: var(--tab-block-margin);
}
& #zen-sidebar-top-buttons {
padding-left: var(--zen-toolbox-padding);
}
& #zen-workspaces-button[as-button='true'] {
width: calc(100% - 10px) !important;
}
& #zen-current-workspace-indicator-icon[no-icon='true'] {
display: none;
}
& #zen-workspaces-button {
margin-left: 2px;
margin-right: 2px;
@@ -207,19 +260,10 @@
}
& #vertical-pinned-tabs-container:has(tab:not([hidden])) {
margin-bottom: 12px;
position: relative;
&::after {
bottom: -6px;
content: '';
display: block;
height: 1px;
background: light-dark(rgba(1, 1, 1, 0.075), rgba(255, 255, 255, 0.075));
width: 99%;
left: 50%;
transform: translateX(-50%);
position: absolute;
& .tabbrowser-tab {
width: 100%;
}
}
@@ -231,8 +275,10 @@
width: 100%;
position: relative;
}
--toolbarbutton-inner-padding: var(--zen-toolbar-button-inner-padding) !important;
& #zen-essentials-container {
--tab-min-height: 42px;
}
/* Mark: Fix separator paddings */
@@ -293,18 +339,26 @@
}
& #tabbrowser-tabs {
--tab-inline-padding: 8px;
& .tabbrowser-tab {
& .tab-label-container {
display: flex;
}
& .tab-background {
@media not (prefers-color-scheme: dark) {
&:is([selected], [multiselected]) {
box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.15) !important;
box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.15);
}
}
margin-inline: var(--tab-block-margin);
width: -moz-available;
}
&:not([pinned]):is(:hover, [visuallyselected]) .tab-close-button {
display: block !important;
&:is(:hover, [visuallyselected]) .tab-close-button {
display: block;
--tab-inline-padding: 0; /* Avoid weird padding */
}
.tab-throbber,
@@ -312,10 +366,8 @@
.tab-icon-image,
.tab-sharing-icon-overlay,
.tab-icon-overlay {
&:not([pinned]) {
margin-inline-end: var(--toolbarbutton-inner-padding) !important;
margin-inline-start: calc(var(--toolbarbutton-inner-padding) / 4) !important;
}
margin-inline-end: var(--toolbarbutton-inner-padding) !important;
margin-inline-start: calc(var(--toolbarbutton-inner-padding) / 4) !important;
}
}
}
@@ -335,16 +387,44 @@
#navigator-toolbox[zen-user-hover='true'][movingtab],
#navigator-toolbox[zen-user-hover='true'][flash-popup],
#navigator-toolbox[zen-user-hover='true'][has-popup-menu],
#navigator-toolbox[zen-user-hover='true']:has(*[open='true']:not(tab):not(#zen-sidepanel-button)),
#navigator-toolbox[zen-user-hover='true']:has(*[open='true']:not(tab):not(.zen-compact-mode-ignore)),
#navigator-toolbox[zen-expanded='true']:not([zen-user-hover='true'])
)
) {
--tab-min-width: 36px !important;
/* Important: When changin this value, make sure expand on hover doesn't break! */
--zen-toolbox-max-width: 49px; /* 1px more because the browser view has one pixel of padding to avoid the border being cut off */
--zen-toolbox-padding: 8px;
--zen-toolbox-padding: calc(var(--zen-element-separation) / 2 + 1px);
--zen-toolbox-max-width: calc(var(--tab-min-width) + var(--zen-toolbox-padding) * 2);
max-width: var(--zen-toolbox-max-width) !important;
min-width: var(--zen-toolbox-max-width) !important;
--zen-tabbar-offset: 1px; /* Fix the tabbar offset, because there's a shadow */
&[zen-right-side='true'] {
margin-left: var(--zen-tabbar-offset);
}
&:not([zen-right-side='true']) {
margin-right: var(--zen-tabbar-offset);
}
& #zen-current-workspace-indicator-name {
display: none;
}
& #zen-current-workspace-indicator {
padding-left: 0;
padding-right: 0;
display: flex;
align-items: center;
justify-content: center;
opacity: .4;
}
& #zen-essentials-container {
justify-content: center;
}
& #vertical-tabs-newtab-button {
padding: 0 !important;
background: transparent !important;
@@ -372,24 +452,23 @@
}
&::before {
width: 60% !important;
width: 90% !important;
}
}
& #EssentialsToolbar {
display: none !important;
}
& #essentials-accordion-header {
display: none !important;
}
& #vertical-pinned-tabs-container:has(tab:not([hidden])) {
margin-bottom: 12px;
position: relative;
&::after {
bottom: -6px;
content: '';
display: block;
height: 1px;
background: light-dark(rgba(1, 1, 1, 0.075), rgba(255, 255, 255, 0.075));
width: 70%;
left: 50%;
transform: translateX(-50%);
position: absolute;
& .tabbrowser-tab {
max-width: var(--tab-min-width);
}
}
@@ -402,16 +481,20 @@
}
& #tabbrowser-tabs {
--tab-min-width: 36px !important;
margin-top: -2px;
& .tabbrowser-tab {
margin: 0 auto;
width: var(--tab-min-width) !important;
height: var(--tab-min-width) !important;
& .tab-background:is([selected], [multiselected]) {
@media not (prefers-color-scheme: dark) {
box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.12) !important;
& .tab-background {
margin-inline: auto !important;
&:is([selected], [multiselected]) {
box-shadow: 0 0 1px 1px rgba(0,0,0,.1);
@media not (prefers-color-scheme: dark) {
box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.12) !important;
}
}
}
@@ -419,22 +502,22 @@
display: flex;
align-content: center;
justify-content: center;
padding: 0 !important;
& .tab-label-container {
display: none;
display: none !important;
}
& .tab-icon-image,
& .tab-icon-pending {
margin-inline-end: 0 !important;
}
}
}
#vertical-pinned-tabs-container {
display: flex;
flex-direction: column;
gap: 0 !important;
/* Hide glances */
& .tabbrowser-tab {
display: none !important;
}
}
}
@media (-moz-bool-pref: 'zen.view.sidebar-collapsed.hide-mute-button') {
@@ -534,8 +617,8 @@
#navigator-toolbox[flash-popup],
#navigator-toolbox[has-popup-menu],
#navigator-toolbox:has(.tabbrowser-tab:active),
#navigator-toolbox:has(*[open='true']:not(tab):not(#zen-sidepanel-button)) {
--zen-toolbox-max-width: 49px;
#navigator-toolbox:has(*[open='true']:not(tab):not(.zen-compact-mode-ignore)) {
--zen-toolbox-max-width: 45px;
max-width: var(--zen-toolbox-max-width) !important;
min-width: var(--zen-toolbox-max-width) !important;
padding: 0 !important;
@@ -543,17 +626,20 @@
& #TabsToolbar {
z-index: 100 !important;
width: 250px !important;
background-color: var(--zen-dialog-background);
border-top-color: var(--zen-colors-border);
background: var(--zen-main-browser-background-toolbar) !important;
background-attachment: fixed !important;
background-size: 2000px !important; /* Dont ask me why */
box-shadow: 0 0 1px 1px rgba(0, 0, 0, 0.2);
position: absolute;
padding: var(--zen-toolbox-padding);
padding-top: 0;
transition: 0 !important;
/*animation: zen-vtabs-animation 0.3s ease-in-out;*/
-moz-window-dragging: no-drag;
overflow: hidden;
transition: width 0.2s !important;
border-right: 1px solid var(--zen-colors-border);
& .tab-throbber,
& .tab-icon-pending,
@@ -564,39 +650,8 @@
}
}
/* Make pinned tabs stay in a single line */
#vertical-pinned-tabs-container {
display: flex;
flex-direction: column;
gap: 0 !important;
position: relative;
& .tabbrowser-tab {
& .tab-label-container {
display: flex;
}
& .tab-throbber,
& .tab-icon-pending,
& .tab-icon-image,
& .tab-sharing-icon-overlay,
& .tab-icon-overlay {
margin-inline-end: var(--toolbarbutton-inner-padding) !important;
}
&:not(:hover):not([selected]):not([multiselected]) .tab-background {
box-shadow: none;
background: transparent !important;
}
}
}
&[zen-right-side='true'] #TabsToolbar {
right: 0;
border-right: 0;
border-left: 1px solid var(--zen-colors-border);
border-top-left-radius: var(--zen-border-radius);
border-top-right-radius: 0;
}
@@ -628,7 +683,7 @@
#tabbrowser-tabs {
& .tabbrowser-tab {
&[pinned] .tab-close-button {
display: none !important;
display: none;
}
&[selected] .tab-background {
@@ -686,7 +741,11 @@
}
}
@media (-moz-bool-pref: 'zen.tabs.show-newtab-under') {
#tabs-newtab-button {
display: none;
}
@media (-moz-bool-pref: 'zen.tabs.show-newtab-vertical') {
#tabs-newtab-button {
display: flex !important;
}
@@ -696,20 +755,79 @@
--zen-colors-border: var(--zen-colors-tertiary);
}
margin-top: 10px;
position: relative;
@media (-moz-bool-pref: 'zen.view.show-newtab-button-top') {
order: -1;
}
&::before {
content: '';
display: block;
height: 1px;
background: light-dark(rgba(1, 1, 1, 0.075), rgba(255, 255, 255, 0.075));
width: 96%;
position: absolute;
top: -5px;
left: 50%;
transform: translateX(-50%);
@media (-moz-bool-pref: 'zen.view.show-newtab-button-border-top') and (not (-moz-bool-pref: 'zen.view.show-newtab-button-top')) {
#tabbrowser-arrowscrollbox:has(tab:not([hidden])) & {
margin-top: 15px;
position: relative;
&::before {
content: '';
display: block;
height: 1px;
background: light-dark(rgba(1, 1, 1, 0.075), rgba(255, 255, 255, 0.1));
width: 98%;
position: absolute;
top: -8px;
left: 50%;
transform: translateX(-50%);
}
}
}
}
}
}
/* Mark: Essentials Toolbar */
#zen-essentials-container {
padding-bottom: var(--zen-toolbox-padding);
overflow: hidden;
gap: 3px;
}
#zen-essentials-container {
overflow: hidden;
transition: max-height 0.3s ease-out;
opacity: 1;
grid-template-columns: repeat(auto-fit, minmax(var(--tab-pinned-min-width-expanded), auto));
overflow-y: auto;
overflow-x: hidden;
scrollbar-width: thin;
display: grid;
padding: 0;
}
#zen-essentials-container .tabbrowser-tab {
--toolbarbutton-inner-padding: 0;
max-width: unset;
width: 100% !important;
border-radius: var(--border-radius-medium);
&:not([selected]) .tab-background {
background: light-dark(rgba(255, 255, 255, 0.3), rgba(255, 255, 255, 0.1));
box-shadow: none;
}
backdrop-filter: blur(10px);
& .tab-content {
display: flex;
justify-content: center;
}
& .tab-label-container {
display: none !important;
}
& .tab-close-button {
display: none !important;
}
& .tab-icon-image,
& .tab-icon-overlay {
margin-inline-end: 0 !important;
}
}

View File

@@ -32,13 +32,16 @@
--in-content-primary-button-background: light-dark(
color-mix(in srgb, var(--zen-primary-color) 35%, black 65%),
color-mix(in srgb, var(--zen-primary-color) 35%, white 65%)
color-mix(in srgb, var(--zen-primary-color) 85%, black 15%)
) !important;
--in-content-primary-button-background-hover: light-dark(
color-mix(in srgb, var(--zen-primary-color) 40%, black 60%),
color-mix(in srgb, var(--zen-primary-color) 40%, white 60%)
) !important;
--in-content-primary-button-background-active: var(--zen-colors-hover-bg) !important;
--in-content-primary-button-background-active: light-dark(
color-mix(in srgb, var(--zen-primary-color) 50%, black 50%),
color-mix(in srgb, var(--zen-primary-color) 80%, rgba(255,255,255,.2) 20%)
) !important;
--in-content-primary-button-text-color: light-dark(white, black) !important;
--in-content-primary-button-border-color: transparent !important;
--in-content-primary-button-border-hover: transparent !important;
@@ -53,7 +56,7 @@
color-mix(in srgb, var(--zen-primary-color) 10%, transparent 90%)
) !important;
--in-content-button-background-hover: light-dark(
color-mix(in srgb, var(--zen-primary-color) 5%, rgba(0, 0, 0, 0.1) 95%),
color-mix(in srgb, var(--zen-primary-color) 10%, rgb(255, 255, 255) 90%),
color-mix(in srgb, var(--zen-primary-color) 15%, transparent 85%)
) !important;
--button-bgcolor: var(--in-content-button-background) !important;
@@ -69,16 +72,22 @@
--button-primary-active-bgcolor: var(--in-content-primary-button-background-active) !important;
--button-primary-color: var(--in-content-primary-button-text-color) !important;
--color-accent-primary-hover: var(--zen-primary-color) !important;
--button-background-color: var(--in-content-button-background) !important;
--button-background-color-hover: var(--in-content-button-background-hover) !important;
--button-background-color-active: var(--in-content-primary-button-background-active) !important;
--color-accent-primary: var(--button-primary-bgcolor) !important;
--color-accent-primary-hover: var(--button-primary-hover-bgcolor) !important;
--color-accent-primary-active: var(--button-primary-active-bgcolor) !important;
--in-content-page-background: var(--zen-colors-tertiary) !important;
--zen-in-content-dialog-background: var(--zen-colors-tertiary);
--zen-button-border-radius: 7px;
--zen-button-border-radius: 5px;
--zen-button-padding: 0.6rem 1.2rem;
/* Toolbar */
--zen-toolbar-height: 39px;
--zen-toolbar-height: 41px;
--zen-toolbar-button-inner-padding: 6px;
--toolbarbutton-outer-padding: 4px;
@@ -90,6 +99,7 @@
/* XUL */
--zen-main-browser-background: light-dark(rgb(235, 235, 235), #1b1b1b);
--zen-main-browser-background-toolbar: var(--zen-main-browser-background);
--zen-browser-gradient-base: color-mix(in srgb, var(--zen-primary-color) 50%, white 50%);
--zen-appcontent-border: 1px solid var(--zen-colors-border);
@@ -113,11 +123,17 @@
--fp-contextmenu-bgcolor: light-dark(Menu, rgb(43 42 51 / 0.95));
--toolbar-bgcolor: transparent;
--toolbarbutton-active-background: var(--zen-colors-border) !important;
--toolbarbutton-active-background: light-dark(rgba(255, 255, 255, .9), color-mix(in srgb, var(--zen-primary-color) 50%, rgba(255, 255, 255, .1)));
--input-bgcolor: var(--zen-colors-tertiary) !important;
--input-border-color: var(--zen-input-border-color) !important;
--zen-themed-toolbar-bg: light-dark(#f7f7f7, var(--zen-colors-tertiary)) !important;
--zen-themed-toolbar-bg: light-dark(#f7f7f7, var(--zen-colors-tertiary));
--zen-themed-toolbar-bg-transparent: light-dark(#f7f7f7, var(--zen-colors-tertiary));
@supports (-moz-osx-font-smoothing: auto) {
--zen-themed-toolbar-bg-transparency: 0.05;
--zen-themed-toolbar-bg-transparent: light-dark(rgba(255, 255, 255, var(--zen-themed-toolbar-bg-transparency)), rgba(0, 0, 0, var(--zen-themed-toolbar-bg-transparency)));
}
--toolbar-field-background-color: var(--zen-colors-input-bg) !important;
--arrowpanel-background: var(--zen-dialog-background) !important;
@@ -174,10 +190,3 @@
}
}
}
@media not (-moz-bool-pref: 'zen.theme.toolbar-themed') {
:root {
--toolbar-bgcolor: light-dark(#e6e6e6, #1b1b1b) !important;
--zen-themed-toolbar-bg: var(--toolbar-bgcolor);
}
}

View File

@@ -5,7 +5,7 @@
}
#urlbar {
--toolbarbutton-border-radius: 10px;
--toolbarbutton-border-radius: 6px;
--urlbarView-separator-color: var(--zen-colors-border);
--urlbarView-hover-background: var(--toolbarbutton-hover-background);
--urlbarView-highlight-background: var(--toolbarbutton-hover-background);
@@ -36,7 +36,7 @@
}
#urlbar:not([focused='true']):not([breakout-extend="true"]) > #urlbar-background {
background: color-mix(in srgb, light-dark(#fff, var(--zen-colors-secondary)) 65%, transparent 35%) !important;
background: color-mix(in srgb, light-dark(#fff, var(--zen-colors-tertiary)) 60%, transparent 40%) !important;
backdrop-filter: blur(10px);
}
@@ -51,25 +51,6 @@
outline-color: none !important;
}
#urlbar #tracking-protection-icon-container {
border-radius: var(--toolbarbutton-border-radius);
}
#urlbar .urlbar-page-action {
border-radius: calc(var(--toolbarbutton-border-radius) / 1.5);
}
#urlbar[breakout-extend='true'] .urlbar-page-action,
#urlbar[breakout-extend='true'] #tracking-protection-icon-container,
#urlbar[breakout-extend='true'] #identity-box #identity-icon-box,
.searchbar-engine-one-off-item {
border-radius: var(--toolbarbutton-border-radius) !important;
}
#urlbar[breakout-extend='true'] {
border-radius: 12px;
}
#identity-icon-box,
#identity-permission-box {
background: color-mix(in srgb, var(--zen-colors-secondary) 50%, transparent 50%) !important;
@@ -111,16 +92,14 @@
.urlbar-page-action,
#tracking-protection-icon-container {
width: calc(var(--urlbar-min-height) - 4px - 4 * var(--urlbar-container-padding)) !important;
height: calc(var(--urlbar-min-height) - 4px - 4 * var(--urlbar-container-padding)) !important;
padding: 0 !important;
justify-content: center !important;
align-items: center !important;
margin: 0 0 0 2px !important;
}
#urlbar[breakout-extend='true'] .urlbar-page-action,
#urlbar[breakout-extend='true'] #tracking-protection-icon-container {
.urlbar-page-action,
#tracking-protection-icon-container {
width: calc(var(--urlbar-min-height) - 4 * var(--urlbar-container-padding)) !important;
height: calc(var(--urlbar-min-height) - 4 * var(--urlbar-container-padding)) !important;
}
@@ -253,7 +232,8 @@ button.popup-notification-dropmarker {
max-width: 30rem !important;
& notification-message {
background: var(--zen-colors-tertiary);
background: color-mix(in srgb, var(--zen-colors-tertiary) 70%, transparent 30%);
backdrop-filter: blur(10px);
border: 1px solid var(--arrowpanel-border-color);
border-radius: var(--zen-border-radius);

View File

@@ -7,11 +7,15 @@
position: relative;
&[dont-show='true'] {
display: none !important;
}
&:not([as-button='true']) {
--toolbarbutton-hover-background: transparent !important;
border-radius: var(--zen-button-border-radius) !important;
background: transparent;
padding: 5px;
padding: 2px;
appearance: unset !important;
height: fit-content;
gap: 3px;
@@ -25,10 +29,6 @@
align-items: center;
position: relative;
& > * {
opacity: 0.6;
}
@media (-moz-bool-pref: 'zen.workspaces.hide-deactivated-workspaces') {
&:not([active='true']):not(:hover) {
&::after {
@@ -79,7 +79,7 @@
}
}
:root:has(#navigator-toolbox:not([zen-has-hover='true'])) &[as-button='true'] {
&[as-button='true'] {
margin: auto;
padding: var(--toolbarbutton-inner-padding) !important;
width: calc(2 * var(--toolbarbutton-inner-padding) + 16px) !important;
@@ -168,7 +168,7 @@
#PanelUI-zen-workspaces panelmultiview panelview {
position: relative;
padding: 15px;
padding: 10px;
width: var(--panel-width);
}
@@ -323,7 +323,7 @@
top: 50%;
transform: translateY(-50%);
left: -2px;
width: 3px;
width: 2px;
height: 16px;
background-color: var(--toolbarbutton-icon-fill-attention);
border-radius: 5px;
@@ -340,9 +340,8 @@
top: 0;
left: 0;
width: 100%;
height: 4px;
height: 2px;
background-color: var(--toolbarbutton-icon-fill-attention);
border-radius: 5px;
}
.zen-workspace-last-place-drop-target.dragover {
@@ -406,3 +405,38 @@
#PanelUI-zen-workspaces-edit-footer button[default='true'] {
width: 100%;
}
/* Mark workspaces indicator */
#zen-current-workspace-indicator {
padding: 15px calc(4px + var(--tab-inline-padding));
font-weight: 600;
align-items: center;
position: relative;
& #zen-current-workspace-indicator-icon {
font-size: 14px;
}
#zen-current-workspace-indicator-name {
font-size: 13px;
opacity: 0.5;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
display: block;
position: absolute;
max-width: 100%;
}
& #zen-current-workspace-indicator-icon:not([hidden]) + #zen-current-workspace-indicator-name {
margin-left: 25px;
}
}
@media not (-moz-bool-pref: 'zen.workspaces.show-workspace-indicator') {
#zen-current-workspace-indicator {
display: none;
}
}

View File

@@ -0,0 +1,3 @@
#include zen-sidebar-panel.inc.xhtml
#include zen-splitview-overlay.inc.xhtml
#include zen-glance.inc.xhtml

View File

@@ -0,0 +1,19 @@
// Utility to register JSWindowActors
var gZenActorsManager = {
_actors: new Set(),
addJSWindowActor(...args) {
if (this._actors.has(args[0])) {
// Actor already registered, nothing to do
return;
}
try {
ChromeUtils.registerWindowActor(...args);
this._actors.add(args[0]);
} catch (e) {
console.warn(`Failed to register JSWindowActor: ${e}`);
}
},
}

View File

@@ -0,0 +1,55 @@
var gZenOperatingSystemCommonUtils = {
kZenOSToSmallName: {
WINNT: 'windows',
Darwin: 'macos',
Linux: 'linux',
},
get currentOperatingSystem() {
let os = Services.appinfo.OS;
return this.kZenOSToSmallName[os];
},
};
class ZenMultiWindowFeature {
constructor() {}
static get browsers() {
return Services.wm.getEnumerator('navigator:browser');
}
static get currentBrowser() {
return Services.wm.getMostRecentWindow('navigator:browser');
}
isActiveWindow() {
return ZenMultiWindowFeature.currentBrowser === window;
}
async foreachWindowAsActive(callback) {
if (!this.isActiveWindow()) {
return;
}
for (const browser of ZenMultiWindowFeature.browsers) {
try {
await callback(browser);
} catch (e) {
console.error(e);
}
}
}
}
class ZenDOMOperatedFeature {
constructor() {
var initBound = this.init.bind(this);
document.addEventListener('DOMContentLoaded', initBound, { once: true });
}
}
class ZenPreloadedFeature {
constructor() {
var initBound = this.init.bind(this);
document.addEventListener('MozBeforeInitialXULLayout', initBound, { once: true });
}
}

View File

@@ -0,0 +1,253 @@
const lazyCompactMode = {};
XPCOMUtils.defineLazyPreferenceGetter(
lazyCompactMode,
'COMPACT_MODE_FLASH_DURATION',
'zen.view.compact.toolbar-flash-popup.duration',
800
);
var gZenCompactModeManager = {
_flashTimeouts: {},
_evenListeners: [],
_removeHoverFrames: {},
init() {
Services.prefs.addObserver('zen.view.compact', this._updateEvent.bind(this));
Services.prefs.addObserver('zen.view.sidebar-expanded.on-hover', this._disableTabsOnHoverIfConflict.bind(this));
Services.prefs.addObserver('zen.tabs.vertical.right-side', this._updateSidebarIsOnRight.bind(this));
gZenUIManager.addPopupTrackingAttribute(this.sidebar);
gZenUIManager.addPopupTrackingAttribute(document.getElementById('zen-appcontent-navbar-container'));
this.addMouseActions();
this.addContextMenu();
},
get prefefence() {
return Services.prefs.getBoolPref('zen.view.compact');
},
set preference(value) {
Services.prefs.setBoolPref('zen.view.compact', value);
return value;
},
get sidebarIsOnRight() {
if (this._sidebarIsOnRight) {
return this._sidebarIsOnRight;
}
return Services.prefs.getBoolPref('zen.tabs.vertical.right-side');
},
get sidebar() {
if (!this._sidebar) {
this._sidebar = document.getElementById('navigator-toolbox');
}
return this._sidebar;
},
addContextMenu() {
const fragment = window.MozXULElement.parseXULToFragment(`
<menu id="zen-context-menu-compact-mode" data-l10n-id="zen-toolbar-context-compact-mode">
<menupopup>
<menuitem id="zen-context-menu-compact-mode-toggle" data-l10n-id="zen-toolbar-context-compact-mode-enable" type="checkbox" oncommand="gZenCompactModeManager.toggle();"/>
<menuseparator/>
<menuitem id="zen-context-menu-compact-mode-hide-sidebar" data-l10n-id="zen-toolbar-context-compact-mode-just-tabs" type="radio" oncommand="gZenCompactModeManager.hideSidebar();"/>
<menuitem id="zen-context-menu-compact-mode-hide-toolbar" data-l10n-id="zen-toolbar-context-compact-mode-just-toolbar" type="radio" oncommand="gZenCompactModeManager.hideToolbar();"/>
<menuitem id="zen-context-menu-compact-mode-hide-both" data-l10n-id="zen-toolbar-context-compact-mode-hide-both" type="radio" oncommand="gZenCompactModeManager.hideBoth();"/>
</menupopup>
</menu>
`);
document.getElementById('viewToolbarsMenuSeparator').before(fragment);
this.updateContextMenu();
},
hideSidebar() {
Services.prefs.setBoolPref('zen.view.compact.hide-tabbar', true);
Services.prefs.setBoolPref('zen.view.compact.hide-toolbar', false);
},
hideToolbar() {
Services.prefs.setBoolPref('zen.view.compact.hide-toolbar', true);
Services.prefs.setBoolPref('zen.view.compact.hide-tabbar', false);
},
hideBoth() {
Services.prefs.setBoolPref('zen.view.compact.hide-tabbar', true);
Services.prefs.setBoolPref('zen.view.compact.hide-toolbar', true);
},
addEventListener(callback) {
this._evenListeners.push(callback);
},
_updateEvent() {
this._evenListeners.forEach((callback) => callback());
this._disableTabsOnHoverIfConflict();
this.updateContextMenu();
},
updateContextMenu() {
document
.getElementById('zen-context-menu-compact-mode-toggle')
.setAttribute('checked', Services.prefs.getBoolPref('zen.view.compact'));
const hideTabBar = Services.prefs.getBoolPref('zen.view.compact.hide-tabbar');
const hideToolbar = Services.prefs.getBoolPref('zen.view.compact.hide-toolbar');
const hideBoth = hideTabBar && hideToolbar;
const idName = 'zen-context-menu-compact-mode-hide-';
document.getElementById(idName + 'sidebar').setAttribute('checked', !hideBoth && hideTabBar);
document.getElementById(idName + 'toolbar').setAttribute('checked', !hideBoth && hideToolbar);
document.getElementById(idName + 'both').setAttribute('checked', hideBoth);
},
_removeOpenStateOnUnifiedExtensions() {
// Fix for bug https://github.com/zen-browser/desktop/issues/1925
const buttons = document.querySelectorAll('toolbarbutton:is(#unified-extensions-button, .webextension-browser-action)');
for (let button of buttons) {
button.removeAttribute('open');
}
},
_disableTabsOnHoverIfConflict() {
if (Services.prefs.getBoolPref('zen.view.compact') && Services.prefs.getBoolPref('zen.view.compact.hide-tabbar')) {
Services.prefs.setBoolPref('zen.view.sidebar-expanded.on-hover', false);
}
},
toggle() {
return (this.preference = !this.prefefence);
},
_updateSidebarIsOnRight() {
this._sidebarIsOnRight = Services.prefs.getBoolPref('zen.tabs.vertical.right-side');
},
toggleSidebar() {
this.sidebar.toggleAttribute('zen-user-show');
},
get hideAfterHoverDuration() {
if (this._hideAfterHoverDuration) {
return this._hideAfterHoverDuration;
}
return Services.prefs.getIntPref('zen.view.compact.toolbar-hide-after-hover.duration');
},
get hoverableElements() {
return [
{
element: this.sidebar,
screenEdge: this.sidebarIsOnRight ? 'right' : 'left',
},
{
element: document.getElementById('zen-appcontent-navbar-container'),
screenEdge: 'top',
},
];
},
flashSidebar(duration = lazyCompactMode.COMPACT_MODE_FLASH_DURATION) {
let tabPanels = document.getElementById('tabbrowser-tabpanels');
if (!tabPanels.matches("[zen-split-view='true']")) {
this.flashElement(this.sidebar, duration, this.sidebar.id);
}
},
flashElement(element, duration, id, attrName = 'flash-popup') {
if (element.matches(':hover')) {
return;
}
if (this._flashTimeouts[id]) {
clearTimeout(this._flashTimeouts[id]);
} else {
requestAnimationFrame(() => element.setAttribute(attrName, 'true'));
}
this._flashTimeouts[id] = setTimeout(() => {
window.requestAnimationFrame(() => {
element.removeAttribute(attrName);
this._flashTimeouts[id] = null;
});
}, duration);
},
clearFlashTimeout(id) {
clearTimeout(this._flashTimeouts[id]);
this._flashTimeouts[id] = null;
},
addMouseActions() {
for (let i = 0; i < this.hoverableElements.length; i++) {
let target = this.hoverableElements[i].element;
target.addEventListener('mouseenter', (event) => {
this.clearFlashTimeout('has-hover' + target.id);
window.requestAnimationFrame(() => target.setAttribute('zen-has-hover', 'true'));
});
target.addEventListener('mouseleave', (event) => {
// If on Mac, ignore mouseleave in the area of window buttons
if (AppConstants.platform == 'macosx') {
const MAC_WINDOW_BUTTONS_X_BORDER = 75;
const MAC_WINDOW_BUTTONS_Y_BORDER = 40;
if (event.clientX < MAC_WINDOW_BUTTONS_X_BORDER && event.clientY < MAC_WINDOW_BUTTONS_Y_BORDER) {
return;
}
}
if (this.hoverableElements[i].keepHoverDuration) {
this.flashElement(target, keepHoverDuration, 'has-hover' + target.id, 'zen-has-hover');
} else {
this._removeHoverFrames[target.id] = window.requestAnimationFrame(() => target.removeAttribute('zen-has-hover'));
}
});
}
document.documentElement.addEventListener('mouseleave', (event) => {
const screenEdgeCrossed = this._getCrossedEdge(event.pageX, event.pageY);
if (!screenEdgeCrossed) return;
for (let entry of this.hoverableElements) {
if (screenEdgeCrossed !== entry.screenEdge) continue;
const target = entry.element;
const boundAxis = entry.screenEdge === 'right' || entry.screenEdge === 'left' ? 'y' : 'x';
if (!this._positionInBounds(boundAxis, target, event.pageX, event.pageY, 7)) {
continue;
}
window.cancelAnimationFrame(this._removeHoverFrames[target.id]);
this.flashElement(target, this.hideAfterHoverDuration, 'has-hover' + target.id, 'zen-has-hover');
document.addEventListener(
'mousemove',
() => {
if (target.matches(':hover')) return;
target.removeAttribute('zen-has-hover');
this.clearFlashTimeout('has-hover' + target.id);
},
{ once: true }
);
}
});
},
_getCrossedEdge(posX, posY, element = document.documentElement, maxDistance = 10) {
const targetBox = element.getBoundingClientRect();
posX = Math.max(targetBox.left, Math.min(posX, targetBox.right));
posY = Math.max(targetBox.top, Math.min(posY, targetBox.bottom));
return ['top', 'bottom', 'left', 'right'].find((edge, i) => {
const distance = Math.abs((i < 2 ? posY : posX) - targetBox[edge]);
return distance <= maxDistance;
});
},
_positionInBounds(axis = 'x', element, x, y, error = 0) {
const bBox = element.getBoundingClientRect();
if (axis === 'y') return bBox.top - error < y && y < bBox.bottom + error;
else return bBox.left - error < x && x < bBox.right + error;
},
toggleToolbar() {
let toolbar = document.getElementById('zen-appcontent-navbar-container');
toolbar.toggleAttribute('zen-user-show');
},
};

View File

@@ -0,0 +1,356 @@
{
class ZenGlanceManager extends ZenDOMOperatedFeature {
#currentBrowser = null;
#currentTab = null;
#animating = false;
init() {
document.documentElement.setAttribute("zen-glance-uuid", gZenUIManager.generateUuidv4());
window.addEventListener("keydown", this.onKeyDown.bind(this));
window.addEventListener("TabClose", this.onTabClose.bind(this));
ChromeUtils.defineLazyGetter(
this,
'sidebarButtons',
() => document.getElementById('zen-glance-sidebar-container')
);
document.getElementById('tabbrowser-tabpanels').addEventListener("click", this.onOverlayClick.bind(this));
Services.obs.addObserver(this, "quit-application-requested");
}
onKeyDown(event) {
if (event.key === "Escape" && this.#currentBrowser) {
event.preventDefault();
event.stopPropagation();
this.closeGlance();
}
}
onOverlayClick(event) {
if (event.target === this.overlay && event.originalTarget !== this.contentWrapper) {
this.closeGlance();
}
}
observe(subject, topic) {
switch (topic) {
case "quit-application-requested":
this.onUnload();
break;
}
}
onUnload() {
// clear everything
if (this.#currentBrowser) {
gBrowser.removeTab(this.#currentTab);
}
}
createBrowserElement(url, currentTab) {
const newTabOptions = {
userContextId: currentTab.getAttribute("usercontextid") || "",
skipBackgroundNotify: true,
insertTab: true,
skipLoad: false,
index: currentTab._tPos + 1,
};
this.currentParentTab = currentTab;
const newTab = gBrowser.addTrustedTab(Services.io.newURI(url).spec, newTabOptions);
gBrowser.selectedTab = newTab;
currentTab.querySelector(".tab-content").appendChild(newTab);
newTab.setAttribute("zen-glance-tab", true);
this.#currentBrowser = newTab.linkedBrowser;
this.#currentTab = newTab;
return this.#currentBrowser;
}
openGlance(data) {
if (this.#currentBrowser) {
return;
}
const initialX = data.x;
const initialY = data.y;
const initialWidth = data.width;
const initialHeight = data.height;
this.browserWrapper?.removeAttribute("animate");
this.browserWrapper?.removeAttribute("animate-end");
this.browserWrapper?.removeAttribute("animate-full");
this.browserWrapper?.removeAttribute("animate-full-end");
this.browserWrapper?.removeAttribute("has-finished-animation");
this.overlay?.removeAttribute("post-fade-out");
const url = data.url;
const currentTab = gBrowser.selectedTab;
this.animatingOpen = true;
const browserElement = this.createBrowserElement(url, currentTab);
this.overlay = browserElement.closest(".browserSidebarContainer");
this.browserWrapper = browserElement.closest(".browserContainer");
this.contentWrapper = browserElement.closest(".browserStack");
this.browserWrapper.prepend(this.sidebarButtons);
this.overlay.classList.add("zen-glance-overlay");
this.browserWrapper.removeAttribute("animate-end");
window.requestAnimationFrame(() => {
this.quickOpenGlance();
this.browserWrapper.style.setProperty("--initial-x", `${initialX}px`);
this.browserWrapper.style.setProperty("--initial-y", `${initialY}px`);
this.browserWrapper.style.setProperty("--initial-width", initialWidth + "px");
this.browserWrapper.style.setProperty("--initial-height", initialHeight + "px");
this.overlay.removeAttribute("fade-out");
this.browserWrapper.setAttribute("animate", true);
this.#animating = true;
setTimeout(() => {
this.browserWrapper.setAttribute("animate-end", true);
this.browserWrapper.setAttribute("has-finished-animation", true);
this.#animating = false;
this.animatingOpen = false;
}, 500);
});
}
closeGlance({ noAnimation = false, onTabClose = false } = {}) {
if (this.#animating || !this.#currentBrowser || this.animatingOpen || this._duringOpening) {
return;
}
this.browserWrapper.removeAttribute("has-finished-animation");
if (noAnimation) {
this.quickCloseGlance({ closeCurrentTab: false });
this.#currentBrowser = null;
this.#currentTab = null;
return;
}
gBrowser._insertTabAtIndex(this.#currentTab, {
index: this.currentParentTab._tPos + 1,
});
let quikcCloseZen = false;
if (onTabClose) {
// Create new tab if no more ex
if (gBrowser.tabs.length === 1) {
gBrowser.selectedTab = gZenUIManager.openAndChangeToTab(Services.prefs.getStringPref('browser.startup.homepage'));
return;
} else if (gBrowser.selectedTab === this.#currentTab) {
this._duringOpening = true;
gBrowser.tabContainer.advanceSelectedTab(1, true); // to skip the current tab
this._duringOpening = false;
quikcCloseZen = true;
}
}
// do NOT touch here, I don't know what it does, but it works...
window.requestAnimationFrame(() => {
this.#currentTab.style.display = "none";
this.browserWrapper.removeAttribute("animate");
this.browserWrapper.removeAttribute("animate-end");
this.overlay.setAttribute("fade-out", true);
window.requestAnimationFrame(() => {
this.quickCloseGlance({ justAnimateParent: true });
this.browserWrapper.setAttribute("animate", true);
setTimeout(() => {
if (!this.currentParentTab) {
return;
}
if (!onTabClose || quikcCloseZen) {
this.quickCloseGlance();
}
this.overlay.removeAttribute("fade-out");
this.browserWrapper.removeAttribute("animate");
this.lastCurrentTab = this.#currentTab;
this.overlay.classList.remove("zen-glance-overlay");
gBrowser._getSwitcher().setTabStateNoAction(this.lastCurrentTab, gBrowser.AsyncTabSwitcher.STATE_UNLOADED);
if (!onTabClose && gBrowser.selectedTab === this.lastCurrentTab) {
this._duringOpening = true;
gBrowser.selectedTab = this.currentParentTab;
}
// reset everything
this.currentParentTab = null;
this.browserWrapper = null;
this.overlay = null;
this.contentWrapper = null;
this.lastCurrentTab.removeAttribute("zen-glance-tab");
this.lastCurrentTab._closingGlance = true;
gBrowser.tabContainer._invalidateCachedTabs();
gBrowser.removeTab(this.lastCurrentTab, { animate: true });
this.#currentTab = null;
this.#currentBrowser = null;
this.lastCurrentTab = null;
this._duringOpening = false;
}, 400);
});
});
}
quickOpenGlance() {
if (!this.#currentBrowser || this._duringOpening) {
return;
}
this._duringOpening = true;
try {
gBrowser.selectedTab = this.#currentTab;
} catch (e) {}
this.currentParentTab.linkedBrowser.closest(".browserSidebarContainer").classList.add("deck-selected", "zen-glance-background");
this.currentParentTab.linkedBrowser.closest(".browserSidebarContainer").classList.remove("zen-glance-overlay");
this.currentParentTab.linkedBrowser.zenModeActive = true;
this.#currentBrowser.zenModeActive = true;
this.currentParentTab.linkedBrowser.docShellIsActive = true;
this.#currentBrowser.docShellIsActive = true;
this.#currentBrowser.setAttribute("zen-glance-selected", true);
this.currentParentTab._visuallySelected = true;
this.overlay.classList.add("deck-selected");
this._duringOpening = false;
}
quickCloseGlance({ closeCurrentTab = true, closeParentTab = true, justAnimateParent = false } = {}) {
const parentHasBrowser = !!(this.currentParentTab.linkedBrowser);
if (!justAnimateParent) {
if (parentHasBrowser) {
if (closeParentTab) {
this.currentParentTab.linkedBrowser.closest(".browserSidebarContainer").classList.remove("deck-selected");
}
this.currentParentTab.linkedBrowser.zenModeActive = false;
}
this.#currentBrowser.zenModeActive = false;
if (closeParentTab && parentHasBrowser) {
this.currentParentTab.linkedBrowser.docShellIsActive = false;
}
if (closeCurrentTab) {
this.#currentBrowser.docShellIsActive = false;
this.overlay.classList.remove("deck-selected");
}
if (!this.currentParentTab._visuallySelected && closeParentTab) {
this.currentParentTab._visuallySelected = false;
}
this.#currentBrowser.removeAttribute("zen-glance-selected");
}
if (parentHasBrowser) {
this.currentParentTab.linkedBrowser.closest(".browserSidebarContainer").classList.remove("zen-glance-background");
}
}
onLocationChange(_) {
if (this._duringOpening) {
return;
}
if (gBrowser.selectedTab === this.#currentTab && !this.animatingOpen && !this._duringOpening && this.#currentBrowser) {
this.quickOpenGlance();
return;
}
if (gBrowser.selectedTab === this.currentParentTab && this.#currentBrowser) {
this.quickOpenGlance();
} else if ((!this.animatingFullOpen || this.animatingOpen) && this.#currentBrowser) {
this.closeGlance();
}
}
onTabClose(event) {
if (event.target === this.currentParentTab) {
this.closeGlance({ onTabClose: true });
}
}
fullyOpenGlance() {
gBrowser._insertTabAtIndex(this.#currentTab, {
index: this.#currentTab._tPos + 1,
});
this.animatingFullOpen = true;
this.currentParentTab._visuallySelected = false;
this.browserWrapper.removeAttribute("has-finished-animation");
this.browserWrapper.setAttribute("animate-full", true);
this.#currentTab.removeAttribute("zen-glance-tab");
gBrowser.selectedTab = this.#currentTab;
this.currentParentTab.linkedBrowser.closest(".browserSidebarContainer").classList.remove("zen-glance-background");
setTimeout(() => {
window.requestAnimationFrame(() => {
this.browserWrapper.setAttribute("animate-full-end", true);
setTimeout(() => {
this.animatingFullOpen = false;
this.closeGlance({ noAnimation: true });
}, 600);
});
}, 300);
}
openGlanceForBookmark(event) {
const activationMethod = Services.prefs.getStringPref('zen.glance.activation-method', 'ctrl');
if (activationMethod === 'ctrl' && !event.ctrlKey) {
return;
} else if (activationMethod === 'alt' && !event.altKey) {
return;
} else if (activationMethod === 'shift' && !event.shiftKey) {
return;
} else if (activationMethod === 'meta' && !event.metaKey) {
return;
}else if (activationMethod === 'mantain' || typeof activationMethod === 'undefined') {
return;
}
event.preventDefault();
event.stopPropagation();
const rect = event.target.getBoundingClientRect();
const data = {
url: event.target._placesNode.uri,
x: rect.left,
y: rect.top,
width: rect.width,
height: rect.height,
};
this.openGlance(data);
return false;
}
}
window.gZenGlanceManager = new ZenGlanceManager();
function registerWindowActors() {
if (Services.prefs.getBoolPref("zen.glance.enabled", true)) {
gZenActorsManager.addJSWindowActor("ZenGlance", {
parent: {
esModuleURI: "chrome://browser/content/zen-components/actors/ZenGlanceParent.sys.mjs",
},
child: {
esModuleURI: "chrome://browser/content/zen-components/actors/ZenGlanceChild.sys.mjs",
events: {
DOMContentLoaded: {},
},
},
});
}
}
registerWindowActors();
}

View File

@@ -0,0 +1,742 @@
{
class ZenThemePicker extends ZenMultiWindowFeature {
static GRADIENT_IMAGE_URL = 'chrome://browser/content/zen-images/gradient.png';
static GRADIENT_DISPLAY_URL = 'chrome://browser/content/zen-images/gradient-display.png';
static MAX_DOTS = 5;
currentOpacity = 0.5;
currentRotation = 45;
numberOfDots = 0;
constructor() {
super();
if (!Services.prefs.getBoolPref('zen.theme.gradient', true) || !ZenWorkspaces.shouldHaveWorkspaces) {
return;
}
this.dragStartPosition = null;
ChromeUtils.defineLazyGetter(this, 'panel', () => document.getElementById('PanelUI-zen-gradient-generator'));
ChromeUtils.defineLazyGetter(this, 'toolbox', () => document.getElementById('TabsToolbar'));
ChromeUtils.defineLazyGetter(this, 'customColorInput', () => document.getElementById('PanelUI-zen-gradient-generator-custom-input'));
ChromeUtils.defineLazyGetter(this, 'customColorList', () => document.getElementById('PanelUI-zen-gradient-generator-custom-list'));
XPCOMUtils.defineLazyPreferenceGetter(
this,
'allowWorkspaceColors',
'zen.theme.color-prefs.use-workspace-colors',
true,
this.onDarkModeChange.bind(this)
)
this.initRotation();
this.initCanvas();
this.initCustomColorInput();
ZenWorkspaces.addChangeListeners(this.onWorkspaceChange.bind(this));
window.matchMedia('(prefers-color-scheme: dark)').addListener(this.onDarkModeChange.bind(this));
}
get isDarkMode() {
return window.matchMedia('(prefers-color-scheme: dark)').matches;
}
async onDarkModeChange(event, skipUpdate = false) {
const currentWorkspace = await ZenWorkspaces.getActiveWorkspace();
this.onWorkspaceChange(currentWorkspace, skipUpdate);
}
initContextMenu() {
const menu = window.MozXULElement.parseXULToFragment(`
<menuitem id="zenToolbarThemePicker"
data-lazy-l10n-id="zen-workspaces-change-gradient"
oncommand="gZenThemePicker.openThemePicker(event);"/>
`);
document.getElementById('toolbar-context-customize').before(menu);
}
openThemePicker(event) {
PanelMultiView.openPopup(this.panel, this.toolbox, {
position: 'topright topleft',
triggerEvent: event,
});
}
initCanvas() {
this.image = new Image();
this.image.src = ZenThemePicker.GRADIENT_IMAGE_URL;
this.canvas = document.createElement('canvas');
this.panel.appendChild(this.canvas);
this.canvasCtx = this.canvas.getContext('2d');
// wait for the image to load
this.image.onload = this.onImageLoad.bind(this);
}
onImageLoad() {
// resize the image to fit the panel
const imageSize = 300 - 20; // 20 is the padding (10px)
const scale = imageSize / Math.max(this.image.width, this.image.height);
this.image.width *= scale;
this.image.height *= scale;
this.canvas.width = this.image.width;
this.canvas.height = this.image.height;
this.canvasCtx.drawImage(this.image, 0, 0);
this.canvas.setAttribute('hidden', 'true');
// Call the rest of the initialization
this.initContextMenu();
this.initThemePicker();
this._hasInitialized = true;
this.onDarkModeChange(null);
}
initRotation() {
this.rotationInput = document.getElementById('PanelUI-zen-gradient-degrees');
this.rotationInputDot = this.rotationInput.querySelector('.dot');
this.rotationInputText = this.rotationInput.querySelector('.text');
this.rotationInputDot.addEventListener('mousedown', this.onRotationMouseDown.bind(this));
this.rotationInput.addEventListener('wheel', this.onRotationWheel.bind(this));
}
onRotationWheel(event) {
event.preventDefault();
const delta = event.deltaY;
const degrees = this.currentRotation + (delta > 0 ? 10 : -10);
this.setRotationInput(degrees);
this.updateCurrentWorkspace();
}
onRotationMouseDown(event) {
event.preventDefault();
this.rotationDragging = true;
this.rotationInputDot.style.zIndex = 2;
this.rotationInputDot.classList.add('dragging');
document.addEventListener('mousemove', this.onRotationMouseMove.bind(this));
document.addEventListener('mouseup', this.onRotationMouseUp.bind(this));
}
onRotationMouseUp(event) {
this.rotationDragging = false;
this.rotationInputDot.style.zIndex = 1;
this.rotationInputDot.classList.remove('dragging');
document.removeEventListener('mousemove', this.onRotationMouseMove.bind(this));
document.removeEventListener('mouseup', this.onRotationMouseUp.bind(this));
}
onRotationMouseMove(event) {
if (this.rotationDragging) {
event.preventDefault();
const rect = this.rotationInput.getBoundingClientRect();
// Make the dot follow the mouse in a circle, it can't go outside or inside the circle
const centerX = rect.left + rect.width / 2;
const centerY = rect.top + rect.height / 2;
const angle = Math.atan2(event.clientY - centerY, event.clientX - centerX);
const distance = Math.sqrt((event.clientX - centerX) ** 2 + (event.clientY - centerY) ** 2);
const radius = rect.width / 2;
let x = centerX + Math.cos(angle) * radius;
let y = centerY + Math.sin(angle) * radius;
if (distance > radius) {
x = event.clientX;
y = event.clientY;
}
const degrees = Math.round(Math.atan2(y - centerY, x - centerX) * 180 / Math.PI);
this.setRotationInput(degrees);
this.updateCurrentWorkspace();
}
}
setRotationInput(degrees) {
let fixedRotation = degrees;
while (fixedRotation < 0) {
fixedRotation += 360;
}
while (fixedRotation >= 360) {
fixedRotation -= 360;
}
this.currentRotation = degrees;
this.rotationInputDot.style.transform = `rotate(${degrees - 20}deg)`;
this.rotationInputText.textContent = `${fixedRotation}°`;
}
initCustomColorInput() {
this.customColorInput.addEventListener('keydown', this.onCustomColorKeydown.bind(this));
}
onCustomColorKeydown(event) {
//checks for enter key for custom colors
if (event.key === 'Enter') {
event.preventDefault();
this.addCustomColor();
}
}
initThemePicker() {
const themePicker = this.panel.querySelector('.zen-theme-picker-gradient');
themePicker.style.setProperty('--zen-theme-picker-gradient-image', `url(${ZenThemePicker.GRADIENT_DISPLAY_URL})`);
themePicker.addEventListener('mousemove', this.onDotMouseMove.bind(this));
themePicker.addEventListener('mouseup', this.onDotMouseUp.bind(this));
themePicker.addEventListener('click', this.onThemePickerClick.bind(this));
}
calculateInitialPosition(color) {
const [r, g, b] = color.c;
const imageData = this.canvasCtx.getImageData(0, 0, this.canvas.width, this.canvas.height);
// Find all pixels that are at least 90% similar to the color
const similarPixels = [];
for (let i = 0; i < imageData.data.length; i += 4) {
const pixelR = imageData.data[i];
const pixelG = imageData.data[i + 1];
const pixelB = imageData.data[i + 2];
if (Math.abs(r - pixelR) < 25 && Math.abs(g - pixelG) < 25 && Math.abs(b - pixelB) < 25) {
similarPixels.push(i);
}
}
// Check if there's an exact match
for (const pixel of similarPixels) {
const x = (pixel / 4) % this.canvas.width;
const y = Math.floor((pixel / 4) / this.canvas.width);
const pixelColor = this.getColorFromPosition(x, y);
if (pixelColor[0] === r && pixelColor[1] === g && pixelColor[2] === b) {
return {x: x / this.canvas.width, y: y / this.canvas.height};
}
}
// If there's no exact match, return the first similar pixel
const pixel = similarPixels[0];
const x = (pixel / 4) % this.canvas.width;
const y = Math.floor((pixel / 4) / this.canvas.width);
return {x: x / this.canvas.width, y: y / this.canvas.height};
}
getColorFromPosition(x, y) {
// get the color from the x and y from the image
const imageData = this.canvasCtx.getImageData(x, y, 1, 1);
return imageData.data;
}
createDot(color, fromWorkspace = false) {
const [r, g, b] = color.c;
const dot = document.createElement('div');
dot.classList.add('zen-theme-picker-dot');
if (color.isCustom) {
if (!color.c) {
return;
}
dot.classList.add('custom');
dot.style.opacity = 0;
dot.style.setProperty('--zen-theme-picker-dot-color', color.c);
} else {
dot.style.setProperty('--zen-theme-picker-dot-color', `rgb(${r}, ${g}, ${b})`);
const { x, y } = this.calculateInitialPosition(color);
dot.style.left = `${x * 100}%`;
dot.style.top = `${y * 100}%`;
dot.addEventListener('mousedown', this.onDotMouseDown.bind(this));
}
this.panel.querySelector('.zen-theme-picker-gradient').appendChild(dot);
if (!fromWorkspace) {
this.updateCurrentWorkspace(true);
}
}
onThemePickerClick(event) {
event.preventDefault();
if (event.button !== 0 || this.dragging ) return;
const gradient = this.panel.querySelector('.zen-theme-picker-gradient');
const rect = gradient.getBoundingClientRect();
const padding = 90; // each side
const centerX = rect.left + rect.width / 2;
const centerY = rect.top + rect.height / 2;
const radius = (rect.width - padding) / 2;
let pixelX = event.clientX;
let pixelY = event.clientY;
// Check if the click is within the circle
const distance = Math.sqrt((pixelX - centerX) ** 2 + (pixelY - centerY) ** 2);
if (distance > radius) {
return; // Don't create a dot if clicking outside the circle
}
// Check if we clicked on an existing dot
const clickedElement = event.target;
const isExistingDot = clickedElement.classList.contains('zen-theme-picker-dot');
// Only proceed if not clicking on an existing dot
if (!isExistingDot) {
const relativeX = event.clientX - rect.left;
const relativeY = event.clientY - rect.top;
const color = this.getColorFromPosition(relativeX, relativeY);
// Create new dot
const dot = document.createElement('div');
dot.classList.add('zen-theme-picker-dot');
dot.addEventListener('mousedown', this.onDotMouseDown.bind(this));
dot.style.left = `${relativeX}px`;
dot.style.top = `${relativeY}px`;
dot.style.setProperty('--zen-theme-picker-dot-color', `rgb(${color[0]}, ${color[1]}, ${color[2]})`);
gradient.appendChild(dot);
this.updateCurrentWorkspace(true);
}
}
onDotMouseDown(event) {
event.preventDefault();
if (event.button === 2) {
return;
}
this.dragging = true;
this.draggedDot = event.target;
this.draggedDot.style.zIndex = 1;
this.draggedDot.classList.add('dragging');
// Store the starting position of the drag
this.dragStartPosition = {
x: event.clientX,
y: event.clientY
};
}
onDotMouseMove(event) {
if (this.dragging) {
event.preventDefault();
const rect = this.panel.querySelector('.zen-theme-picker-gradient').getBoundingClientRect();
const padding = 90; // each side
// do NOT let the ball be draged outside of an imaginary circle. You can drag it anywhere inside the circle
// if the distance between the center of the circle and the dragged ball is bigger than the radius, then the ball
// should be placed on the edge of the circle. If it's inside the circle, then the ball just follows the mouse
const centerX = rect.left + rect.width / 2;
const centerY = rect.top + rect.height / 2;
const radius = (rect.width - padding) / 2;
let pixelX = event.clientX;
let pixelY = event.clientY;
const distance = Math.sqrt((pixelX - centerX) **2 + (pixelY - centerY) **2);
if (distance > radius) {
const angle = Math.atan2(pixelY - centerY, pixelX - centerX);
pixelX = centerX + Math.cos(angle) * radius;
pixelY = centerY + Math.sin(angle) * radius;
}
// set the location of the dot in pixels
const relativeX = pixelX - rect.left;
const relativeY = pixelY - rect.top;
this.draggedDot.style.left = `${relativeX}px`;
this.draggedDot.style.top = `${relativeY}px`;
const color = this.getColorFromPosition(relativeX, relativeY);
this.draggedDot.style.setProperty('--zen-theme-picker-dot-color', `rgb(${color[0]}, ${color[1]}, ${color[2]})`);
this.updateCurrentWorkspace();
}
}
addColorToCustomList(color) {
const listItems = window.MozXULElement.parseXULToFragment(`
<hbox class="zen-theme-picker-custom-list-item">
<html:div class="zen-theme-picker-dot-custom"></html:div>
<label class="zen-theme-picker-custom-list-item-label"></label>
<toolbarbutton class="zen-theme-picker-custom-list-item-remove toolbarbutton-1" oncommand="gZenThemePicker.removeCustomColor(event);"></toolbarbutton>
</hbox>
`);
listItems.querySelector('.zen-theme-picker-custom-list-item').setAttribute('data-color', color);
listItems.querySelector('.zen-theme-picker-dot-custom').style.setProperty('--zen-theme-picker-dot-color', color);
listItems.querySelector('.zen-theme-picker-custom-list-item-label').textContent = color;
this.customColorList.appendChild(listItems);
}
async addCustomColor() {
const color = this.customColorInput.value;
if (!color) {
return;
}
// can be any color format, we just add it to the list as a dot, but hidden
const dot = document.createElement('div');
dot.classList.add('zen-theme-picker-dot', 'hidden', 'custom');
dot.style.opacity = 0;
dot.style.setProperty('--zen-theme-picker-dot-color', color);
this.panel.querySelector('.zen-theme-picker-gradient').appendChild(dot);
this.customColorInput.value = '';
await this.updateCurrentWorkspace();
}
onThemePickerClick(event) {
event.preventDefault();
if (event.button !== 0 || this.dragging) return;
const gradient = this.panel.querySelector('.zen-theme-picker-gradient');
const rect = gradient.getBoundingClientRect();
const padding = 90; // each side
const centerX = rect.left + rect.width / 2;
const centerY = rect.top + rect.height / 2;
const radius = (rect.width - padding) / 2;
let pixelX = event.clientX;
let pixelY = event.clientY;
// Check if the click is within the circle
const distance = Math.sqrt((pixelX - centerX) ** 2 + (pixelY - centerY) ** 2);
if (distance > radius) {
return;
}
const clickedElement = event.target;
const isExistingDot = clickedElement.classList.contains('zen-theme-picker-dot');
if (!isExistingDot && this.numberOfDots < ZenThemePicker.MAX_DOTS) {
const relativeX = event.clientX - rect.left;
const relativeY = event.clientY - rect.top;
const color = this.getColorFromPosition(relativeX, relativeY);
const dot = document.createElement('div');
dot.classList.add('zen-theme-picker-dot');
dot.addEventListener('mousedown', this.onDotMouseDown.bind(this));
dot.style.left = `${relativeX}px`;
dot.style.top = `${relativeY}px`;
dot.style.setProperty('--zen-theme-picker-dot-color', `rgb(${color[0]}, ${color[1]}, ${color[2]})`);
gradient.appendChild(dot);
this.updateCurrentWorkspace(true);
}
}
onDotMouseDown(event) {
event.preventDefault();
if (event.button === 2) {
return;
}
this.dragging = true;
this.draggedDot = event.target;
this.draggedDot.style.zIndex = 1;
this.draggedDot.classList.add('dragging');
// Store the starting position of the drag
this.dragStartPosition = {
x: event.clientX,
y: event.clientY
};
}
onDotMouseUp(event) {
if (event.button === 2) {
if (!event.target.classList.contains('zen-theme-picker-dot')) {
return;
}
event.target.remove();
this.updateCurrentWorkspace();
this.numberOfDots--;
return;
}
if (this.dragging) {
event.preventDefault();
event.stopPropagation();
this.dragging = false;
this.draggedDot.style.zIndex = 1;
this.draggedDot.classList.remove('dragging');
this.draggedDot = null;
this.dragStartPosition = null; // Reset the drag start position
return;
}
this.numberOfDots = this.panel.querySelectorAll('.zen-theme-picker-dot').length;
}
themedColors(colors) {
const isDarkMode = this.isDarkMode;
const factor = isDarkMode ? 0.5 : 1.1;
return colors.map(color => {
return {
c: color.isCustom ? color.c : [
Math.min(255, color.c[0] * factor),
Math.min(255, color.c[1] * factor),
Math.min(255, color.c[2] * factor),
],
isCustom: color.isCustom,
}
});
}
onOpacityChange(event) {
this.currentOpacity = event.target.value;
this.updateCurrentWorkspace();
}
onTextureChange(event) {
this.currentTexture = event.target.value;
this.updateCurrentWorkspace();
}
getSingleRGBColor(color, forToolbar = false) {
if (color.isCustom) {
return color.c;
}
const toolbarBg = forToolbar ? 'var(--zen-themed-toolbar-bg)' : 'var(--zen-themed-toolbar-bg-transparent)';
return `color-mix(in srgb, rgb(${color.c[0]}, ${color.c[1]}, ${color.c[2]}) ${this.currentOpacity * 100}%, ${toolbarBg} ${(1 - this.currentOpacity) * 100}%)`;
}
getGradient(colors, forToolbar = false) {
const themedColors = this.themedColors(colors);
if (themedColors.length === 0) {
return forToolbar ? "var(--zen-themed-toolbar-bg)" : "var(--zen-themed-toolbar-bg-transparent)";
} else if (themedColors.length === 1) {
return this.getSingleRGBColor(themedColors[0], forToolbar);
}
return `linear-gradient(${this.currentRotation}deg, ${themedColors.map(color => this.getSingleRGBColor(color, forToolbar)).join(', ')})`;
}
getTheme(colors, opacity = 0.5, rotation = 45, texture = 0) {
return {
type: 'gradient',
gradientColors: colors.filter(color => color), // remove undefined
opacity,
rotation,
texture,
};
}
//TODO: add a better noise system that adds noise not just changes transparency
updateNoise(texture) {
const wrapper = document.getElementById('zen-main-app-wrapper');
wrapper.style.setProperty('--zen-grainy-background-opacity', texture);
}
hexToRgb(hex) {
if (hex.startsWith('#')) {
hex = hex.substring(1);
}
if (hex.length === 3) {
hex = hex.split('').map(char => char + char).join('');
}
return [
parseInt(hex.substring(0, 2), 16),
parseInt(hex.substring(2, 4), 16),
parseInt(hex.substring(4, 6), 16),
];
}
pSBC=(p,c0,c1,l)=>{
let r,g,b,P,f,t,h,i=parseInt,m=Math.round,a=typeof(c1)=="string";
if(typeof(p)!="number"||p<-1||p>1||typeof(c0)!="string"||(c0[0]!='r'&&c0[0]!='#')||(c1&&!a))return null;
if(!this.pSBCr)this.pSBCr=(d)=>{
let n=d.length,x={};
if(n>9){
[r,g,b,a]=d=d.split(","),n=d.length;
if(n<3||n>4)return null;
x.r=i(r[3]=="a"?r.slice(5):r.slice(4)),x.g=i(g),x.b=i(b),x.a=a?parseFloat(a):-1
}else{
if(n==8||n==6||n<4)return null;
if(n<6)d="#"+d[1]+d[1]+d[2]+d[2]+d[3]+d[3]+(n>4?d[4]+d[4]:"");
d=i(d.slice(1),16);
if(n==9||n==5)x.r=d>>24&255,x.g=d>>16&255,x.b=d>>8&255,x.a=m((d&255)/0.255)/1000;
else x.r=d>>16,x.g=d>>8&255,x.b=d&255,x.a=-1
}return x};
h=c0.length>9,h=a?c1.length>9?true:c1=="c"?!h:false:h,f=this.pSBCr(c0),P=p<0,t=c1&&c1!="c"?this.pSBCr(c1):P?{r:0,g:0,b:0,a:-1}:{r:255,g:255,b:255,a:-1},p=P?p*-1:p,P=1-p;
if(!f||!t)return null;
if(l)r=m(P*f.r+p*t.r),g=m(P*f.g+p*t.g),b=m(P*f.b+p*t.b);
else r=m((P*f.r**2+p*t.r**2)**0.5),g=m((P*f.g**2+p*t.g**2)**0.5),b=m((P*f.b**2+p*t.b**2)**0.5);
a=f.a,t=t.a,f=a>=0||t>=0,a=f?a<0?t:t<0?a:a*P+t*p:0;
if(h)return"rgb"+(f?"a(":"(")+r+","+g+","+b+(f?","+m(a*1000)/1000:"")+")";
else return"#"+(4294967296+r*16777216+g*65536+b*256+(f?m(a*255):0)).toString(16).slice(1,f?undefined:-2)
}
getMostDominantColor(allColors) {
const colors = this.themedColors(allColors);
const themedColors = colors.filter(color => !color.isCustom);
if (themedColors.length === 0 || !this.allowWorkspaceColors) {
return null;
}
// get the most dominant color in the gradient
let dominantColor = themedColors[0].c;
let dominantColorCount = 0;
for (const color of themedColors) {
const count = themedColors.filter(c => c.c[0] === color.c[0] && c.c[1] === color.c[1] && c.c[2] === color.c[2]).length;
if (count > dominantColorCount) {
dominantColorCount = count;
dominantColor = color.c;
}
}
const result = this.pSBC(
this.isDarkMode ? 0.2 : -0.5,
`rgb(${dominantColor[0]}, ${dominantColor[1]}, ${dominantColor[2]})`);
return result?.match(/\d+/g).map(Number);
}
async onWorkspaceChange(workspace, skipUpdate = false, theme = null) {
const uuid = workspace.uuid;
// Use theme from workspace object or passed theme
let workspaceTheme = theme || workspace.theme;
await this.foreachWindowAsActive(async (browser) => {
if (!browser.gZenThemePicker._hasInitialized) {
return;
}
// Do not rebuild if the workspace is not the same as the current one
const windowWorkspace = await browser.ZenWorkspaces.getActiveWorkspace();
if (windowWorkspace.uuid !== uuid && theme !== null) {
return;
}
// get the theme from the window
workspaceTheme = theme || windowWorkspace.theme;
if (!skipUpdate) {
for (const dot of browser.gZenThemePicker.panel.querySelectorAll('.zen-theme-picker-dot')) {
dot.remove();
}
}
const appWrapper = browser.document.getElementById('zen-main-app-wrapper');
if (!skipUpdate) {
appWrapper.removeAttribute('animating');
appWrapper.setAttribute('animating', 'true');
browser.document.body.style.setProperty('--zen-main-browser-background-old',
browser.document.body.style.getPropertyValue('--zen-main-browser-background')
);
browser.window.requestAnimationFrame(() => {
setTimeout(() => {
appWrapper.removeAttribute('animating');
}, 500);
});
}
browser.gZenThemePicker.resetCustomColorList();
if (!workspaceTheme || workspaceTheme.type !== 'gradient') {
const gradient = browser.gZenThemePicker.getGradient([]);
const gradientToolbar = browser.gZenThemePicker.getGradient([], true);
browser.document.documentElement.style.setProperty('--zen-main-browser-background', gradient);
browser.document.documentElement.style.setProperty('--zen-main-browser-background-toolbar', gradientToolbar);
browser.gZenThemePicker.updateNoise(0);
browser.document.documentElement.style.setProperty('--zen-primary-color', this.getNativeAccentColor());
return;
}
browser.gZenThemePicker.currentOpacity = workspaceTheme.opacity ?? 0.5;
browser.gZenThemePicker.currentRotation = workspaceTheme.rotation ?? 45;
browser.gZenThemePicker.currentTexture = workspaceTheme.texture ?? 0;
browser.gZenThemePicker.numberOfDots = workspaceTheme.gradientColors.length;
browser.document.getElementById('PanelUI-zen-gradient-generator-opacity').value = browser.gZenThemePicker.currentOpacity;
browser.document.getElementById('PanelUI-zen-gradient-generator-texture').value = browser.gZenThemePicker.currentTexture;
browser.gZenThemePicker.setRotationInput(browser.gZenThemePicker.currentRotation);
const gradient = browser.gZenThemePicker.getGradient(workspaceTheme.gradientColors);
const gradientToolbar = browser.gZenThemePicker.getGradient(workspaceTheme.gradientColors, true);
browser.gZenThemePicker.updateNoise(workspaceTheme.texture);
for (const dot of workspaceTheme.gradientColors) {
if (dot.isCustom) {
browser.gZenThemePicker.addColorToCustomList(dot.c);
}
}
browser.document.documentElement.style.setProperty('--zen-main-browser-background', gradient);
browser.document.documentElement.style.setProperty('--zen-main-browser-background-toolbar', gradientToolbar);
const dominantColor = this.getMostDominantColor(workspaceTheme.gradientColors);
if (dominantColor) {
browser.document.documentElement.style.setProperty('--zen-primary-color', `rgb(${dominantColor[0]}, ${dominantColor[1]}, ${dominantColor[2]})`);
}
if (!skipUpdate) {
browser.gZenThemePicker.recalculateDots(workspaceTheme.gradientColors);
}
});
}
getNativeAccentColor() {
return Services.prefs.getStringPref('zen.theme.accent-color');
}
resetCustomColorList() {
this.customColorList.innerHTML = '';
}
removeCustomColor(event) {
const target = event.target.closest('.zen-theme-picker-custom-list-item');
const color = target.getAttribute('data-color');
const dots = this.panel.querySelectorAll('.zen-theme-picker-dot');
for (const dot of dots) {
if (dot.style.getPropertyValue('--zen-theme-picker-dot-color') === color) {
dot.remove();
break;
}
}
target.remove();
this.updateCurrentWorkspace();
}
recalculateDots(colors) {
//THIS IS PART OF THE ISSUE
for (const color of colors) {
this.createDot(color, true);
}
}
async updateCurrentWorkspace(skipSave = true) {
this.updated = skipSave;
const dots = this.panel.querySelectorAll('.zen-theme-picker-dot');
const colors = Array.from(dots).map(dot => {
const color = dot.style.getPropertyValue('--zen-theme-picker-dot-color');
if (color === 'undefined') {
return;
}
const isCustom = dot.classList.contains('custom');
return {c: isCustom ? color : color.match(/\d+/g).map(Number), isCustom};
});
const gradient = this.getTheme(colors, this.currentOpacity, this.currentRotation, this.currentTexture);
let currentWorkspace = await ZenWorkspaces.getActiveWorkspace();
if(!skipSave) {
await ZenWorkspacesStorage.saveWorkspaceTheme(currentWorkspace.uuid, gradient);
await ZenWorkspaces._propagateWorkspaceData();
ConfirmationHint.show(document.getElementById("PanelUI-menu-button"), "zen-panel-ui-gradient-generator-saved-message");
currentWorkspace = await ZenWorkspaces.getActiveWorkspace();
}
await this.onWorkspaceChange(currentWorkspace, true, skipSave ? gradient : null);
}
async handlePanelClose() {
if(this.updated) {
await this.updateCurrentWorkspace(false);
}
}
}
window.ZenThemePicker = ZenThemePicker;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,485 @@
{
const lazy = {};
class ZenPinnedTabsObserver {
static ALL_EVENTS = ['TabPinned', 'TabUnpinned', 'TabClose'];
#listeners = [];
constructor() {
XPCOMUtils.defineLazyPreferenceGetter(lazy, 'zenPinnedTabRestorePinnedTabsToPinnedUrl', 'zen.pinned-tab-manager.restore-pinned-tabs-to-pinned-url', false);
XPCOMUtils.defineLazyPreferenceGetter(lazy, 'zenPinnedTabCloseShortcutBehavior', 'zen.pinned-tab-manager.close-shortcut-behavior', 'switch');
ChromeUtils.defineESModuleGetters(lazy, {E10SUtils: "resource://gre/modules/E10SUtils.sys.mjs"});
this.#listenPinnedTabEvents();
}
#listenPinnedTabEvents() {
const eventListener = this.#eventListener.bind(this);
for (const event of ZenPinnedTabsObserver.ALL_EVENTS) {
window.addEventListener(event, eventListener);
}
window.addEventListener('unload', () => {
for (const event of ZenPinnedTabsObserver.ALL_EVENTS) {
window.removeEventListener(event, eventListener);
}
});
}
#eventListener(event) {
for (const listener of this.#listeners) {
listener(event.type, event);
}
}
addPinnedTabListener(listener) {
this.#listeners.push(listener);
}
}
class ZenPinnedTabManager extends ZenPreloadedFeature {
init() {
if (!this.enabled) {
return;
}
this.observer = new ZenPinnedTabsObserver();
this._initClosePinnedTabShortcut();
this._insertItemsIntoTabContextMenu();
this.observer.addPinnedTabListener(this._onPinnedTabEvent.bind(this));
this._zenClickEventListener = this._onTabClick.bind(this);
}
async initTabs() {
if (!this.enabled) {
return;
}
await ZenPinnedTabsStorage.init();
}
get enabled() {
if (typeof this._enabled === 'undefined') {
this._enabled = !(
document.documentElement.hasAttribute('privatebrowsingmode') ||
document.documentElement.getAttribute('chromehidden')?.includes('toolbar') ||
document.documentElement.getAttribute('chromehidden')?.includes('menubar')
);
}
return this._enabled;
}
async _refreshPinnedTabs() {
await this._initializePinsCache();
this._initializePinnedTabs();
}
async _initializePinsCache() {
try {
// Get pin data
const pins = await ZenPinnedTabsStorage.getPins();
// Enhance pins with favicons
const enhancedPins = await Promise.all(pins.map(async pin => {
try {
const faviconData = await PlacesUtils.promiseFaviconData(pin.url);
return {
...pin,
iconUrl: faviconData?.uri?.spec || null
};
} catch(ex) {
// If favicon fetch fails, continue without icon
return {
...pin,
iconUrl: null
};
}
}));
this._pinsCache = enhancedPins.sort((a, b) => {
if (!a.workspaceUuid && b.workspaceUuid) return -1;
if (a.workspaceUuid && !b.workspaceUuid) return 1;
return 0;
});
} catch (ex) {
console.error("Failed to initialize pins cache:", ex);
this._pinsCache = [];
}
return this._pinsCache;
}
_initializePinnedTabs() {
const pins = this._pinsCache;
if (!pins?.length) {
return;
}
const activeTab = gBrowser.selectedTab;
const pinnedTabsByUUID = new Map();
const pinsToCreate = new Set(pins.map(p => p.uuid));
// First pass: identify existing tabs and remove those without pins
for (let tab of gBrowser.tabs) {
const pinId = tab.getAttribute("zen-pin-id");
if (!pinId) {
continue;
}
if (pinsToCreate.has(pinId)) {
// This is a valid pinned tab that matches a pin
pinnedTabsByUUID.set(pinId, tab);
pinsToCreate.delete(pinId);
} else {
// This is a pinned tab that no longer has a corresponding pin
gBrowser.removeTab(tab);
}
}
// Second pass: create new tabs for pins that don't have tabs
for (let pin of pins) {
if (!pinsToCreate.has(pin.uuid)) {
continue; // Skip pins that already have tabs
}
let newTab = gBrowser.addTrustedTab(pin.url, {
skipAnimation: true,
userContextId: pin.containerTabId || 0,
allowInheritPrincipal: false,
createLazyBrowser: true,
skipLoad: true,
});
// Set the favicon from cache
if (!!pin.iconUrl) {
// TODO: Figure out if there is a better way -
// calling gBrowser.setIcon messes shit up and should be avoided. I think this works for now.
newTab.setAttribute("image", pin.iconUrl);
}
newTab.setAttribute("zen-pin-id", pin.uuid);
gBrowser.setInitialTabTitle(newTab, pin.title);
if (pin.workspaceUuid) {
newTab.setAttribute("zen-workspace-id", pin.workspaceUuid);
}
if (pin.isEssential) {
newTab.setAttribute("zen-essential", "true");
}
gBrowser.pinTab(newTab);
}
// Restore active tab
if (!activeTab.closing) {
gBrowser.selectedTab = activeTab;
}
}
_onPinnedTabEvent(action, event) {
if (!this.enabled) return;
const tab = event.target;
switch (action) {
case "TabPinned":
tab._zenClickEventListener = this._zenClickEventListener;
tab.addEventListener("click", tab._zenClickEventListener);
this._setPinnedAttributes(tab);
break;
case "TabUnpinned":
this._removePinnedAttributes(tab);
if (tab._zenClickEventListener) {
tab.removeEventListener("click", tab._zenClickEventListener);
delete tab._zenClickEventListener;
}
break;
case "TabClose":
this._removePinnedAttributes(tab);
break;
default:
console.warn('ZenPinnedTabManager: Unhandled tab event', action);
break;
}
}
_onTabClick(e) {
const tab = e.target?.closest("tab");
if (e.button === 1 && tab) {
this._onCloseTabShortcut(e, tab);
}
}
async resetPinnedTab(tab) {
if (!tab) {
tab = TabContextMenu.contextTab;
}
if (!tab || !tab.pinned) {
return;
}
await this._resetTabToStoredState(tab);
}
async replacePinnedUrlWithCurrent() {
const tab = TabContextMenu.contextTab;
if (!tab || !tab.pinned || !tab.getAttribute("zen-pin-id")) {
return;
}
const browser = tab.linkedBrowser;
const pin = this._pinsCache.find(pin => pin.uuid === tab.getAttribute("zen-pin-id"));
if (!pin) {
return;
}
const userContextId = tab.getAttribute("usercontextid");
pin.title = tab.label || browser.contentTitle;
pin.url = browser.currentURI.spec;
pin.workspaceUuid = tab.getAttribute("zen-workspace-id");
pin.userContextId = userContextId ? parseInt(userContextId, 10) : 0;
await ZenPinnedTabsStorage.savePin(pin);
await this._refreshPinnedTabs();
}
async _setPinnedAttributes(tab) {
if (tab.hasAttribute("zen-pin-id")) {
return;
}
const browser = tab.linkedBrowser;
const uuid = gZenUIManager.generateUuidv4();
const userContextId = tab.getAttribute("usercontextid");
let entry = null;
if(tab.getAttribute("zen-pinned-entry")) {
entry = JSON.parse(tab.getAttribute("zen-pinned-entry"));
}
await ZenPinnedTabsStorage.savePin({
uuid,
title: entry?.title || tab.label || browser.contentTitle,
url: entry?.url || browser.currentURI.spec,
containerTabId: userContextId ? parseInt(userContextId, 10) : 0,
workspaceUuid: tab.getAttribute("zen-workspace-id"),
isEssential: tab.getAttribute("zen-essential") === "true"
});
tab.setAttribute("zen-pin-id", uuid);
// This is used while migrating old pins to new system - we don't want to refresh when migrating
if (tab.getAttribute("zen-pinned-entry")) {
tab.removeAttribute("zen-pinned-entry");
return;
}
await this._refreshPinnedTabs();
}
async _removePinnedAttributes(tab) {
if(!tab.getAttribute("zen-pin-id")) {
return;
}
await ZenPinnedTabsStorage.removePin(tab.getAttribute("zen-pin-id"));
tab.removeAttribute("zen-pin-id");
if(!tab.hasAttribute("zen-workspace-id") && ZenWorkspaces.workspaceEnabled) {
const workspace = await ZenWorkspaces.getActiveWorkspace();
tab.setAttribute("zen-workspace-id", workspace.uuid);
}
await this._refreshPinnedTabs();
}
_initClosePinnedTabShortcut() {
let cmdClose = document.getElementById('cmd_close');
if (cmdClose) {
cmdClose.addEventListener('command', this._onCloseTabShortcut.bind(this));
}
}
_onCloseTabShortcut(event, selectedTab = gBrowser.selectedTab) {
if (
!selectedTab?.pinned
) {
return;
}
event.stopPropagation();
event.preventDefault();
const behavior = lazy.zenPinnedTabCloseShortcutBehavior;
switch (behavior) {
case 'close':
gBrowser.removeTab(selectedTab, { animate: true });
break;
case 'reset-unload-switch':
case 'unload-switch':
case 'reset-switch':
case 'switch':
this._handleTabSwitch(selectedTab);
if (behavior.includes('reset')) {
this._resetTabToStoredState(selectedTab);
}
if (behavior.includes('unload')) {
gBrowser.discardBrowser(selectedTab);
}
break;
case 'reset':
this._resetTabToStoredState(selectedTab);
break;
default:
return;
}
}
_handleTabSwitch(selectedTab) {
if(selectedTab !== gBrowser.selectedTab) {
return;
}
const findNextTab = (direction) =>
gBrowser.tabContainer.findNextTab(selectedTab, {
direction,
filter: tab => !tab.hidden && !tab.pinned,
});
let nextTab = findNextTab(1) || findNextTab(-1);
if (!nextTab) {
ZenWorkspaces._createNewTabForWorkspace({ uuid: ZenWorkspaces.activeWorkspace });
nextTab = findNextTab(1) || findNextTab(-1);
}
if (nextTab) {
gBrowser.selectedTab = nextTab;
}
}
async _resetTabToStoredState(tab) {
const id = tab.getAttribute("zen-pin-id");
if (!id) {
return;
}
const pin = this._pinsCache.find(pin => pin.uuid === id);
if (pin) {
const tabState = SessionStore.getTabState(tab);
const state = JSON.parse(tabState);
let icon = undefined;
try {
icon = await PlacesUtils.promiseFaviconData(pin.url);
} catch (e) {
console.warn("Error trying to get favicon for pinned tab", e);
}
state.entries = [{
url: pin.url,
title: pin.title,
triggeringPrincipal_base64: lazy.E10SUtils.SERIALIZED_SYSTEMPRINCIPAL
}];
if (icon instanceof Ci.nsIURI || typeof icon === "string") {
state.image = icon;
} else {
state.image = null;
}
state.index = 0;
SessionStore.setTabState(tab, state);
}
}
addToEssentials() {
const tabs = TabContextMenu.contextTab.multiselected ? gBrowser.selectedTabs : [TabContextMenu.contextTab];
for (let i = 0; i < tabs.length; i++) {
const tab = tabs[i];
tab.setAttribute("zen-essential", "true");
if(tab.hasAttribute("zen-workspace-id")) {
tab.removeAttribute("zen-workspace-id");
}
if (tab.pinned) {
gBrowser.unpinTab(tab);
}
gBrowser.pinTab(tab);
}
}
removeEssentials() {
const tabs = TabContextMenu.contextTab.multiselected ? gBrowser.selectedTabs : [TabContextMenu.contextTab];
for (let i = 0; i < tabs.length; i++) {
const tab = tabs[i];
tab.removeAttribute("zen-essential");
if(ZenWorkspaces.workspaceEnabled && ZenWorkspaces.getActiveWorkspaceFromCache.uuid) {
tab.setAttribute("zen-workspace-id", ZenWorkspaces.getActiveWorkspaceFromCache.uuid);
}
gBrowser.unpinTab(tab);
}
}
_insertItemsIntoTabContextMenu() {
const elements = window.MozXULElement.parseXULToFragment(`
<menuseparator id="context_zen-pinned-tab-separator" hidden="true"/>
<menuitem id="context_zen-replace-pinned-url-with-current"
data-lazy-l10n-id="tab-context-zen-replace-pinned-url-with-current"
hidden="true"
oncommand="gZenPinnedTabManager.replacePinnedUrlWithCurrent();"/>
<menuitem id="context_zen-reset-pinned-tab"
data-lazy-l10n-id="tab-context-zen-reset-pinned-tab"
hidden="true"
oncommand="gZenPinnedTabManager.resetPinnedTab();"/>
`);
document.getElementById('tabContextMenu').appendChild(elements);
const element = window.MozXULElement.parseXULToFragment(`
<menuitem id="context_zen-add-essential"
data-lazy-l10n-id="tab-context-zen-add-essential"
hidden="true"
oncommand="gZenPinnedTabManager.addToEssentials();"/>
<menuitem id="context_zen-remove-essential"
data-lazy-l10n-id="tab-context-zen-remove-essential"
hidden="true"
oncommand="gZenPinnedTabManager.removeEssentials();"/>
`);
document.getElementById('context_pinTab')?.after(element);
}
resetPinnedTabData(tabData) {
if (lazy.zenPinnedTabRestorePinnedTabsToPinnedUrl && tabData.pinned && tabData.zenPinnedEntry) {
tabData.entries = [JSON.parse(tabData.zenPinnedEntry)];
tabData.image = tabData.zenPinnedIcon;
tabData.index = 0;
}
}
updatePinnedTabContextMenu(contextTab) {
if (!this.enabled) {
return;
}
const isVisible = contextTab.pinned && !contextTab.multiselected;
document.getElementById("context_zen-reset-pinned-tab").hidden = !isVisible || !contextTab.getAttribute("zen-pin-id");
document.getElementById("context_zen-replace-pinned-url-with-current").hidden = !isVisible;
document.getElementById("context_zen-add-essential").hidden = contextTab.getAttribute("zen-essential");
document.getElementById("context_zen-remove-essential").hidden = !contextTab.getAttribute("zen-essential");
document.getElementById("context_unpinTab").hidden = document.getElementById("context_unpinTab").hidden || contextTab.getAttribute("zen-essential");
document.getElementById("context_unpinSelectedTabs").hidden = document.getElementById("context_unpinSelectedTabs").hidden || contextTab.getAttribute("zen-essential");
document.getElementById("context_zen-pinned-tab-separator").hidden = !isVisible;
}
}
window.gZenPinnedTabManager = new ZenPinnedTabManager();
}

View File

@@ -0,0 +1,341 @@
var ZenPinnedTabsStorage = {
async init() {
console.log('ZenPinnedTabsStorage: Initializing...');
await this._ensureTable();
},
async _ensureTable() {
await PlacesUtils.withConnectionWrapper('ZenPinnedTabsStorage._ensureTable', async (db) => {
// Create the pins table if it doesn't exist
await db.execute(`
CREATE TABLE IF NOT EXISTS zen_pins (
id INTEGER PRIMARY KEY,
uuid TEXT UNIQUE NOT NULL,
title TEXT NOT NULL,
url TEXT,
container_id INTEGER,
workspace_uuid TEXT,
position INTEGER NOT NULL DEFAULT 0,
is_essential BOOLEAN NOT NULL DEFAULT 0,
is_group BOOLEAN NOT NULL DEFAULT 0,
parent_uuid TEXT,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
FOREIGN KEY (parent_uuid) REFERENCES zen_pins(uuid) ON DELETE SET NULL
)
`);
// Create indices
await db.execute(`
CREATE INDEX IF NOT EXISTS idx_zen_pins_uuid ON zen_pins(uuid)
`);
await db.execute(`
CREATE INDEX IF NOT EXISTS idx_zen_pins_parent_uuid ON zen_pins(parent_uuid)
`);
// Create the changes tracking table if it doesn't exist
await db.execute(`
CREATE TABLE IF NOT EXISTS zen_pins_changes (
uuid TEXT PRIMARY KEY,
timestamp INTEGER NOT NULL
)
`);
// Create an index on the uuid column for changes tracking table
await db.execute(`
CREATE INDEX IF NOT EXISTS idx_zen_pins_changes_uuid ON zen_pins_changes(uuid)
`);
await gZenPinnedTabManager._refreshPinnedTabs();
});
},
/**
* Private helper method to notify observers with a list of changed UUIDs.
* @param {string} event - The observer event name.
* @param {Array<string>} uuids - Array of changed workspace UUIDs.
*/
_notifyPinsChanged(event, uuids) {
if (uuids.length === 0) return; // No changes to notify
// Convert the array of UUIDs to a JSON string
const data = JSON.stringify(uuids);
Services.obs.notifyObservers(null, event, data);
},
async savePin(pin, notifyObservers = true) {
const changedUUIDs = new Set();
await PlacesUtils.withConnectionWrapper('ZenPinnedTabsStorage.savePin', async (db) => {
await db.executeTransaction(async () => {
const now = Date.now();
let newPosition;
if ('position' in pin && Number.isFinite(pin.position)) {
newPosition = pin.position;
} else {
// Get the maximum position within the same parent group (or null for root level)
const maxPositionResult = await db.execute(`
SELECT MAX("position") as max_position
FROM zen_pins
WHERE COALESCE(parent_uuid, '') = COALESCE(:parent_uuid, '')
`, { parent_uuid: pin.parentUuid || null });
const maxPosition = maxPositionResult[0].getResultByName('max_position') || 0;
newPosition = maxPosition + 1000;
}
// Insert or replace the pin
await db.executeCached(`
INSERT OR REPLACE INTO zen_pins (
uuid, title, url, container_id, workspace_uuid, position,
is_essential, is_group, parent_uuid, created_at, updated_at
) VALUES (
:uuid, :title, :url, :container_id, :workspace_uuid, :position,
:is_essential, :is_group, :parent_uuid,
COALESCE((SELECT created_at FROM zen_pins WHERE uuid = :uuid), :now),
:now
)
`, {
uuid: pin.uuid,
title: pin.title,
url: pin.isGroup ? null : pin.url,
container_id: pin.containerTabId || null,
workspace_uuid: pin.workspaceUuid || null,
position: newPosition,
is_essential: pin.isEssential || false,
is_group: pin.isGroup || false,
parent_uuid: pin.parentUuid || null,
now
});
await db.execute(`
INSERT OR REPLACE INTO zen_pins_changes (uuid, timestamp)
VALUES (:uuid, :timestamp)
`, {
uuid: pin.uuid,
timestamp: Math.floor(now / 1000)
});
changedUUIDs.add(pin.uuid);
await this.updateLastChangeTimestamp(db);
});
});
if (notifyObservers) {
this._notifyPinsChanged("zen-pin-updated", Array.from(changedUUIDs));
}
},
async getPins() {
const db = await PlacesUtils.promiseDBConnection();
const rows = await db.executeCached(`
SELECT * FROM zen_pins
ORDER BY parent_uuid NULLS FIRST, position ASC
`);
return rows.map((row) => ({
uuid: row.getResultByName('uuid'),
title: row.getResultByName('title'),
url: row.getResultByName('url'),
containerTabId: row.getResultByName('container_id'),
workspaceUuid: row.getResultByName('workspace_uuid'),
position: row.getResultByName('position'),
isEssential: Boolean(row.getResultByName('is_essential')),
isGroup: Boolean(row.getResultByName('is_group')),
parentUuid: row.getResultByName('parent_uuid')
}));
},
async getGroupChildren(groupUuid) {
const db = await PlacesUtils.promiseDBConnection();
const rows = await db.executeCached(`
SELECT * FROM zen_pins
WHERE parent_uuid = :groupUuid
ORDER BY position ASC
`, { groupUuid });
return rows.map((row) => ({
uuid: row.getResultByName('uuid'),
title: row.getResultByName('title'),
url: row.getResultByName('url'),
containerTabId: row.getResultByName('container_id'),
workspaceUuid: row.getResultByName('workspace_uuid'),
position: row.getResultByName('position'),
isEssential: Boolean(row.getResultByName('is_essential')),
isGroup: Boolean(row.getResultByName('is_group')),
parentUuid: row.getResultByName('parent_uuid')
}));
},
async removePin(uuid, notifyObservers = true) {
const changedUUIDs = [uuid];
await PlacesUtils.withConnectionWrapper('ZenPinnedTabsStorage.removePin', async (db) => {
await db.executeTransaction(async () => {
// Get all child UUIDs first for change tracking
const children = await db.execute(
`SELECT uuid FROM zen_pins WHERE parent_uuid = :uuid`,
{ uuid }
);
// Add child UUIDs to changedUUIDs array
for (const child of children) {
changedUUIDs.push(child.getResultByName('uuid'));
}
// Delete all children in a single statement
await db.execute(
`DELETE FROM zen_pins WHERE parent_uuid = :uuid`,
{ uuid }
);
// Delete the pin/group itself
await db.execute(
`DELETE FROM zen_pins WHERE uuid = :uuid`,
{ uuid }
);
// Record the changes
const now = Math.floor(Date.now() / 1000);
for (const changedUuid of changedUUIDs) {
await db.execute(`
INSERT OR REPLACE INTO zen_pins_changes (uuid, timestamp)
VALUES (:uuid, :timestamp)
`, {
uuid: changedUuid,
timestamp: now
});
}
await this.updateLastChangeTimestamp(db);
});
});
if (notifyObservers) {
this._notifyPinsChanged("zen-pin-removed", changedUUIDs);
}
},
async wipeAllPins() {
await PlacesUtils.withConnectionWrapper('ZenPinnedTabsStorage.wipeAllPins', async (db) => {
await db.execute(`DELETE FROM zen_pins`);
await db.execute(`DELETE FROM zen_pins_changes`);
await this.updateLastChangeTimestamp(db);
});
},
async markChanged(uuid) {
await PlacesUtils.withConnectionWrapper('ZenPinnedTabsStorage.markChanged', async (db) => {
const now = Date.now();
await db.execute(`
INSERT OR REPLACE INTO zen_pins_changes (uuid, timestamp)
VALUES (:uuid, :timestamp)
`, {
uuid,
timestamp: Math.floor(now / 1000)
});
});
},
async getChangedIDs() {
const db = await PlacesUtils.promiseDBConnection();
const rows = await db.execute(`
SELECT uuid, timestamp FROM zen_pins_changes
`);
const changes = {};
for (const row of rows) {
changes[row.getResultByName('uuid')] = row.getResultByName('timestamp');
}
return changes;
},
async clearChangedIDs() {
await PlacesUtils.withConnectionWrapper('ZenPinnedTabsStorage.clearChangedIDs', async (db) => {
await db.execute(`DELETE FROM zen_pins_changes`);
});
},
shouldReorderPins(before, current, after) {
const minGap = 1; // Minimum allowed gap between positions
return (before !== null && current - before < minGap) || (after !== null && after - current < minGap);
},
async reorderAllPins(db, changedUUIDs) {
const pins = await db.execute(`
SELECT uuid
FROM zen_pins
ORDER BY position ASC
`);
for (let i = 0; i < pins.length; i++) {
const newPosition = (i + 1) * 1000; // Use large increments
await db.execute(`
UPDATE zen_pins
SET position = :newPosition
WHERE uuid = :uuid
`, { newPosition, uuid: pins[i].getResultByName('uuid') });
changedUUIDs.add(pins[i].getResultByName('uuid'));
}
},
async updateLastChangeTimestamp(db) {
const now = Date.now();
await db.execute(`
INSERT OR REPLACE INTO moz_meta (key, value)
VALUES ('zen_pins_last_change', :now)
`, { now });
},
async getLastChangeTimestamp() {
const db = await PlacesUtils.promiseDBConnection();
const result = await db.executeCached(`
SELECT value FROM moz_meta WHERE key = 'zen_pins_last_change'
`);
return result.length ? parseInt(result[0].getResultByName('value'), 10) : 0;
},
async updatePinPositions(pins) {
const changedUUIDs = new Set();
await PlacesUtils.withConnectionWrapper('ZenPinnedTabsStorage.updatePinPositions', async (db) => {
await db.executeTransaction(async () => {
const now = Date.now();
for (let i = 0; i < pins.length; i++) {
const pin = pins[i];
const newPosition = (i + 1) * 1000;
await db.execute(`
UPDATE zen_pins
SET position = :newPosition
WHERE uuid = :uuid
`, { newPosition, uuid: pin.uuid });
changedUUIDs.add(pin.uuid);
// Record the change
await db.execute(`
INSERT OR REPLACE INTO zen_pins_changes (uuid, timestamp)
VALUES (:uuid, :timestamp)
`, {
uuid: pin.uuid,
timestamp: Math.floor(now / 1000)
});
}
await this.updateLastChangeTimestamp(db);
});
});
this._notifyPinsChanged("zen-pin-updated", Array.from(changedUUIDs));
},
async __dropTables() {
await PlacesUtils.withConnectionWrapper('ZenPinnedTabsStorage.__dropTables', async (db) => {
await db.execute(`DROP TABLE IF EXISTS zen_pins`);
await db.execute(`DROP TABLE IF EXISTS zen_pins_changes`);
});
}
};

View File

@@ -0,0 +1,138 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
var ZenProfileDialogUI = {
showSubView(parent, event) {
let element = parent.querySelector('.zen-side-bar-profiles-button-panel-correction') || parent;
PanelUI.showSubView('PanelUI-zen-profiles', element, event);
this._updateProfilesList();
this._updateCurentProfileId();
},
_updateProfilesList() {
let parentList = document.getElementById('PanelUI-zen-profiles-list');
this._emptyUserList(parentList);
if (this._getProfilesSize(ProfileService.profiles) <= 1) {
return;
}
parentList.appendChild(document.createElement('toolbarseparator'));
for (let profile of ProfileService.profiles) {
if (profile == ProfileService.currentProfile) {
continue;
}
let item = document.createElement('div');
item.onclick = () => this._openProfile(profile);
item.className = 'PanelUI-zen-profiles-item';
let avatar = document.createElement('img');
avatar.className = 'PanelUI-zen-profiles-item-avatar';
let name = document.createElement('div');
name.className = 'PanelUI-zen-profiles-item-name';
name.appendChild(document.createTextNode(profile.name));
name.container = true;
avatar.setAttribute('src', ZenThemeModifier._getThemedAvatar(profile.zenAvatarPath));
item.appendChild(avatar);
item.appendChild(name);
parentList.appendChild(item);
}
},
_emptyUserList(element) {
element.innerHTML = '';
},
_updateCurentProfileId() {
let currentProfile = ProfileService.currentProfile;
if (!currentProfile) return;
let nameContainer = document.getElementById('PanelUI-zen-profiles-current-name');
nameContainer.textContent = currentProfile.name;
},
_openProfile(profile) {
Services.startup.createInstanceWithProfile(profile);
},
_getProfilesSize(profiles) {
let size = 0;
for (let _ of profiles) {
size += 1;
}
return size;
},
createProfileWizard() {
// This should be rewritten in HTML eventually.
// TODO: it could be `window.browsingContext.topChromeWindow.gDialogBox.open` but it does not work with the callback?
window.browsingContext.topChromeWindow.openDialog(
'chrome://mozapps/content/profile/createProfileWizard.xhtml',
'',
'centerscreen,chrome,modal,titlebar',
ProfileService,
{
CreateProfile: async (profile) => {
try {
ProfileService.defaultProfile = profile;
this._flush();
this._openProfile(profile);
} catch (e) {
// This can happen on dev-edition.
let [title, msg] = await document.l10n.formatValues([
{ id: 'profiles-cannot-set-as-default-title' },
{ id: 'profiles-cannot-set-as-default-message' },
]);
Services.prompt.alert(window, title, msg);
}
},
}
);
},
async _flush() {
try {
ProfileService.flush();
this._updateProfilesList();
} catch (e) {
let [title, msg, button] = await document.l10n.formatValues([
{ id: 'profiles-flush-fail-title' },
{
id: e.result == Cr.NS_ERROR_DATABASE_CHANGED ? 'profiles-flush-conflict' : 'profiles-flush-failed',
},
{ id: 'profiles-flush-restart-button' },
]);
const PS = Ci.nsIPromptService;
let result = Services.prompt.confirmEx(
window,
title,
msg,
PS.BUTTON_POS_0 * PS.BUTTON_TITLE_CANCEL + PS.BUTTON_POS_1 * PS.BUTTON_TITLE_IS_STRING,
null,
button,
null,
null,
{}
);
if (result == 1) {
this._restart(false);
}
}
},
_restart(safeMode) {
let cancelQuit = Cc['@mozilla.org/supports-PRBool;1'].createInstance(Ci.nsISupportsPRBool);
Services.obs.notifyObservers(cancelQuit, 'quit-application-requested', 'restart');
if (cancelQuit.data) {
return;
}
let flags = Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart;
if (safeMode) {
Services.startup.restartInSafeMode(flags);
} else {
Services.startup.quit(flags);
}
},
};

View File

@@ -0,0 +1,788 @@
class ZenBrowserManagerSidebar extends ZenDOMOperatedFeature {
_sidebarElement = null;
_currentPanel = null;
_lastOpenedPanel = null;
_hasChangedConfig = true;
_splitterElement = null;
_hSplitterElement = null;
_hasRegisteredPinnedClickOutside = false;
_isDragging = false;
contextTab = null;
sidebar = null;
forwardButton = null;
backButton = null;
progressListener = null;
_tabBrowserSet = new WeakMap();
tabBox;
DEFAULT_MOBILE_USER_AGENT = `Mozilla/5.0 (Android 12; Mobile; rv:129.0) Gecko/20100101 Firefox/${AppConstants.ZEN_FIREFOX_VERSION}`;
MAX_SIDEBAR_PANELS = Services.prefs.getIntPref('zen.sidebar.max-webpanels');
init() {
ChromeUtils.defineLazyGetter(this, 'sidebar', () => document.getElementById('zen-sidebar-web-panel'));
ChromeUtils.defineLazyGetter(this, 'forwardButton', () => document.getElementById('zen-sidebar-web-panel-forward'));
ChromeUtils.defineLazyGetter(this, 'backButton', () => document.getElementById('zen-sidebar-web-panel-back'));
ChromeUtils.defineLazyGetter(this, 'tabBox', () => document.getElementById('tabbrowser-tabbox'));
this.onlySafeWidthAndHeight();
this.initProgressListener();
this.update();
this.close(); // avoid caching
this.listenForPrefChanges();
this.insertIntoContextMenu();
this.addPositioningListeners();
}
onlySafeWidthAndHeight() {
const panel = document.getElementById('zen-sidebar-web-panel');
const width = panel.style.width;
const height = panel.style.height;
panel.setAttribute('style', '');
panel.style.width = width;
panel.style.height = height;
}
initProgressListener() {
this.progressListener = {
QueryInterface: ChromeUtils.generateQI(['nsIWebProgressListener', 'nsISupportsWeakReference']),
onLocationChange: function (aWebProgress, aRequest, aLocation, aFlags) {
const browser = this._getCurrentBrowser();
if (!browser) return;
const forwardDisabled = this.forwardButton.hasAttribute('disabled');
const backDisabled = this.backButton.hasAttribute('disabled');
if (browser.canGoForward === forwardDisabled) {
if (browser.canGoForward) {
this.forwardButton.removeAttribute('disabled');
} else {
this.forwardButton.setAttribute('disabled', true);
}
}
if (browser.canGoBack === backDisabled) {
if (browser.canGoBack) {
this.backButton.removeAttribute('disabled');
} else {
this.backButton.setAttribute('disabled', true);
}
}
}.bind(gZenBrowserManagerSidebar),
};
}
get sidebarData() {
let services = Services.prefs.getStringPref('zen.sidebar.data');
if (services === '') {
return {};
}
return JSON.parse(services);
}
get shouldCloseOnBlur() {
return Services.prefs.getBoolPref('zen.sidebar.close-on-blur');
}
listenForPrefChanges() {
Services.prefs.addObserver('zen.sidebar.data', this.handleEvent.bind(this));
Services.prefs.addObserver('zen.sidebar.enabled', this.handleEvent.bind(this));
this.handleEvent();
}
addPositioningListeners() {
this.sidebar
.querySelectorAll('.zen-sidebar-web-panel-splitter')
.forEach((s) => s.addEventListener('mousedown', this.handleSplitterMouseDown.bind(this)));
this.sidebarHeader.addEventListener('mousedown', this.handleDragPanel.bind(this));
window.addEventListener('resize', this.onWindowResize.bind(this));
}
handleSplitterMouseDown(mouseDownEvent) {
if (this._isDragging) return;
this._isDragging = true;
const isHorizontal = mouseDownEvent.target.getAttribute('orient') === 'horizontal';
setCursor(isHorizontal ? 'n-resize' : 'ew-resize');
const reverse = ['left', 'top'].includes(mouseDownEvent.target.getAttribute('side'));
const direction = isHorizontal ? 'height' : 'width';
const axis = isHorizontal ? 'Y' : 'X';
const computedStyle = window.getComputedStyle(this.sidebar);
const maxSize = parseInt(computedStyle.getPropertyValue(`max-${direction}`).match(/(\d+)px/)?.[1]) || Infinity;
const minSize = parseInt(computedStyle.getPropertyValue(`min-${direction}`).match(/(\d+)px/)?.[1]) || 0;
const sidebarSizeStart = this.sidebar.getBoundingClientRect()[direction];
const startPos = mouseDownEvent[`screen${axis}`];
const toAdjust = isHorizontal ? 'top' : 'left';
const sidebarPosStart = parseInt(this.sidebar.style[toAdjust].match(/\d+/));
let mouseMove = function (e) {
let mouseMoved = e[`screen${axis}`] - startPos;
if (reverse) {
mouseMoved *= -1;
}
let newSize = sidebarSizeStart + mouseMoved;
let currentMax = maxSize;
const wrapperBox = this.sidebarWrapper.getBoundingClientRect();
let maxWrapperSize = Infinity;
if (this.isFloating) {
maxWrapperSize = reverse ? sidebarPosStart + sidebarSizeStart : wrapperBox[direction] - sidebarPosStart;
}
newSize = Math.max(minSize, Math.min(currentMax, maxWrapperSize, newSize));
window.requestAnimationFrame(() => {
if (reverse) {
const actualMoved = newSize - sidebarSizeStart;
this.sidebar.style[toAdjust] = sidebarPosStart - actualMoved + 'px';
}
this.sidebar.style[direction] = `${newSize}px`;
});
}.bind(this);
document.addEventListener('mousemove', mouseMove);
document.addEventListener(
'mouseup',
() => {
document.removeEventListener('mousemove', mouseMove);
this._isDragging = false;
setCursor('auto');
},
{ once: true }
);
}
handleDragPanel(mouseDownEvent) {
if (this.sidebarHeaderButtons.find((b) => b.contains(mouseDownEvent.target))) {
return;
}
this._isDragging = true;
const startTop = this.sidebar.style.top?.match(/\d+/)?.[0] || 0;
const startLeft = this.sidebar.style.left?.match(/\d+/)?.[0] || 0;
const sidebarBBox = this.sidebar.getBoundingClientRect();
const sideBarHeight = sidebarBBox.height;
const sideBarWidth = sidebarBBox.width;
const topMouseOffset = startTop - mouseDownEvent.screenY;
const leftMouseOffset = startLeft - mouseDownEvent.screenX;
const moveListener = (mouseMoveEvent) => {
window.requestAnimationFrame(() => {
let top = mouseMoveEvent.screenY + topMouseOffset;
let left = mouseMoveEvent.screenX + leftMouseOffset;
const wrapperBounds = this.sidebarWrapper.getBoundingClientRect();
top = Math.max(0, Math.min(top, wrapperBounds.height - sideBarHeight));
left = Math.max(0, Math.min(left, wrapperBounds.width - sideBarWidth));
this.sidebar.style.top = top + 'px';
this.sidebar.style.left = left + 'px';
});
};
document.addEventListener('mousemove', moveListener);
document.addEventListener(
'mouseup',
() => {
document.removeEventListener('mousemove', moveListener);
this._isDragging = false;
},
{ once: true }
);
}
onWindowResize() {
if (!this.isFloating) return;
const top = parseInt(this.sidebar.style.top?.match(/\d+/)?.[0] || 0);
const left = parseInt(this.sidebar.style.left?.match(/\d+/)?.[0] || 0);
const wrapperRect = this.sidebarWrapper.getBoundingClientRect();
const sidebarRect = this.sidebar.getBoundingClientRect();
if (sidebarRect.height < wrapperRect.height && top + sidebarRect.height > wrapperRect.height) {
this.sidebar.style.top = wrapperRect.height - sidebarRect.height + 'px';
}
if (sidebarRect.width < wrapperRect.width && left + sidebarRect.width > wrapperRect.width) {
this.sidebar.style.left = wrapperRect.width - sidebarRect.width + 'px';
}
}
get isFloating() {
return document.getElementById('zen-sidebar-web-panel').hasAttribute('pinned');
}
handleEvent() {
this._hasChangedConfig = true;
this.update();
this._hasChangedConfig = false;
// https://stackoverflow.com/questions/11565471/removing-event-listener-which-was-added-with-bind
var clickOutsideHandler = this._handleClickOutside.bind(this);
let isFloating = this.isFloating;
if (isFloating && !this._hasRegisteredPinnedClickOutside) {
document.addEventListener('mouseup', clickOutsideHandler);
this._hasRegisteredPinnedClickOutside = true;
} else if (!isFloating && this._hasRegisteredPinnedClickOutside) {
document.removeEventListener('mouseup', clickOutsideHandler);
this._hasRegisteredPinnedClickOutside = false;
}
const button = document.getElementById('zen-sidepanel-button');
if (!button) return;
if (Services.prefs.getBoolPref('zen.sidebar.enabled')) {
button.removeAttribute('hidden');
} else {
button.setAttribute('hidden', 'true');
this._closeSidebarPanel();
return;
}
}
_handleClickOutside(event) {
let sidebar = document.getElementById('zen-sidebar-web-panel');
if (!sidebar.hasAttribute('pinned') || this._isDragging || !this.shouldCloseOnBlur) {
return;
}
let target = event.target;
const closestSelector = [
'#zen-sidebar-web-panel',
'#zen-sidebar-panels-wrapper',
'#zenWebPanelContextMenu',
'#zen-sidebar-web-panel-splitter',
'#contentAreaContextMenu',
].join(', ');
if (target.closest(closestSelector)) {
return;
}
this.close();
}
toggle() {
if (!this._currentPanel) {
this._currentPanel = this._lastOpenedPanel;
}
if (document.getElementById('zen-sidebar-web-panel').hasAttribute('hidden')) {
this.open();
return;
}
this.close();
}
open() {
let sidebar = document.getElementById('zen-sidebar-web-panel');
if (!this.sidebar.hasAttribute('pinned')) {
this.moveToTabBoxWrapper();
}
sidebar.removeAttribute('hidden');
this.update();
}
update() {
this._updateWebPanels();
this._updateSidebarButton();
this._updateWebPanel();
this._updateButtons();
}
_updateSidebarButton() {
let button = document.getElementById('zen-sidepanel-button');
if (!button) return;
if (!document.getElementById('zen-sidebar-web-panel').hasAttribute('hidden')) {
button.setAttribute('open', 'true');
} else {
button.removeAttribute('open');
}
}
_updateWebPanels() {
if (Services.prefs.getBoolPref('zen.sidebar.enabled')) {
this.sidebarElement.removeAttribute('hidden');
} else {
this.sidebarElement.setAttribute('hidden', 'true');
this._closeSidebarPanel();
return;
}
let data = this.sidebarData;
if (!data.data || !data.index) {
return;
}
this.sidebarElement.innerHTML = '';
for (let site of data.index) {
let panel = data.data[site];
if (!panel || !panel.url) {
continue;
}
let button = document.createXULElement('toolbarbutton');
button.classList.add('zen-sidebar-panel-button', 'toolbarbutton-1', 'chromeclass-toolbar-additional');
button.setAttribute('flex', '1');
button.setAttribute('zen-sidebar-id', site);
button.setAttribute('context', 'zenWebPanelContextMenu');
this._getWebPanelIcon(panel.url, button);
button.addEventListener('click', this._handleClick.bind(this));
button.addEventListener('dragstart', this._handleDragStart.bind(this));
button.addEventListener('dragover', this._handleDragOver.bind(this));
button.addEventListener('dragenter', this._handleDragEnter.bind(this));
button.addEventListener('dragend', this._handleDragEnd.bind(this));
this.sidebarElement.appendChild(button);
}
const addButton = document.getElementById('zen-sidebar-add-panel-button');
if (data.index.length < this.MAX_SIDEBAR_PANELS) {
addButton.removeAttribute('hidden');
} else {
addButton.setAttribute('hidden', 'true');
}
}
async _openAddPanelDialog() {
let dialogURL = 'chrome://browser/content/places/zenNewWebPanel.xhtml';
let features = 'centerscreen,chrome,modal,resizable=no';
let aParentWindow = Services.wm.getMostRecentWindow('navigator:browser');
if (aParentWindow?.gDialogBox) {
await aParentWindow.gDialogBox.open(dialogURL, {});
} else {
aParentWindow.openDialog(dialogURL, '', features, {});
}
}
_setPinnedToElements() {
let sidebar = document.getElementById('zen-sidebar-web-panel');
sidebar.setAttribute('pinned', 'true');
document.getElementById('zen-sidebar-web-panel-pinned').setAttribute('pinned', 'true');
}
_removePinnedFromElements() {
let sidebar = document.getElementById('zen-sidebar-web-panel');
sidebar.removeAttribute('pinned');
document.getElementById('zen-sidebar-web-panel-pinned').removeAttribute('pinned');
}
_closeSidebarPanel() {
let sidebar = document.getElementById('zen-sidebar-web-panel');
sidebar.setAttribute('hidden', 'true');
this._lastOpenedPanel = this._currentPanel;
this._currentPanel = null;
}
_handleClick(event) {
let target = event.target;
let panelId = target.getAttribute('zen-sidebar-id');
if (this._currentPanel === panelId) {
return;
}
this._currentPanel = panelId;
this._updateWebPanel();
}
_handleDragStart(event) {
this.__dragingElement = event.target;
this.__dragingIndex = Array.prototype.indexOf.call(event.target.parentNode.children, event.target);
event.target.style.opacity = '0.7';
event.dataTransfer.effectAllowed = 'move';
event.dataTransfer.setData('text/html', event.target.innerHTML);
event.dataTransfer.setData('text/plain', event.target.id);
}
_handleDragOver(event) {}
_handleDragEnter(event) {
if (typeof this.__dragingElement === 'undefined') {
return;
}
const target = event.target;
const elIndex = Array.prototype.indexOf.call(target.parentNode.children, target);
if (elIndex < this.__dragingIndex) {
target.before(this.__dragingElement);
this.__dragingIndex = elIndex - 1;
}
target.after(this.__dragingElement);
this.__dragingIndex = elIndex + 1;
}
_handleDragEnd(event) {
event.target.style.opacity = '1';
let data = this.sidebarData;
let newPos = [];
for (let element of this.__dragingElement.parentNode.children) {
let panelId = element.getAttribute('zen-sidebar-id');
newPos.push(panelId);
}
data.index = newPos;
Services.prefs.setStringPref('zen.sidebar.data', JSON.stringify(data));
this._currentPanel = this.__dragingElement.getAttribute('zen-sidebar-id');
this.open();
this.__dragingElement = undefined;
}
_createNewPanel(url) {
let data = this.sidebarData;
let newName = 'p' + new Date().getTime();
data.index.push(newName);
data.data[newName] = {
url: url,
ua: false,
};
Services.prefs.setStringPref('zen.sidebar.data', JSON.stringify(data));
this._currentPanel = newName;
this.open();
}
_updateButtons() {
for (let button of this.sidebarElement.querySelectorAll('.zen-sidebar-panel-button')) {
if (button.getAttribute('zen-sidebar-id') === this._currentPanel) {
button.setAttribute('selected', 'true');
} else {
button.removeAttribute('selected');
}
}
}
_hideAllWebPanels() {
let sidebar = document.getElementById('zen-sidebar-web-panel');
for (let browser of sidebar.querySelectorAll('browser[zen-sidebar-id]')) {
browser.setAttribute('hidden', 'true');
browser.docShellIsActive = false;
browser.zenModeActive = false;
}
}
get introductionPanel() {
return document.getElementById('zen-sidebar-introduction-panel');
}
_updateWebPanel() {
this._updateButtons();
// let sidebar = document.getElementById("zen-sidebar-web-panel");
this._hideAllWebPanels();
if (!this._currentPanel) {
this.introductionPanel.removeAttribute('hidden');
this.forwardButton.setAttribute('disabled', true);
this.backButton.setAttribute('disabled', true);
return;
}
this.introductionPanel.setAttribute('hidden', 'true');
let existantWebview = this._getCurrentBrowser();
if (existantWebview) {
existantWebview.docShellIsActive = true;
existantWebview.zenModeActive = true;
existantWebview.removeAttribute('hidden');
document.getElementById('zen-sidebar-web-panel-title').textContent = existantWebview.contentTitle;
return;
}
let data = this._getWebPanelData(this._currentPanel);
let browser = this._createWebPanelBrowser(data);
let browserContainers = document.getElementById('zen-sidebar-web-panel-browser-containers');
browserContainers.appendChild(browser);
browser.addProgressListener(this.progressListener, Ci.nsIWebProgress.NOTIFY_LOCATION);
if (data.ua) {
browser.browsingContext.customUserAgent = this.DEFAULT_MOBILE_USER_AGENT;
browser.reload();
}
browser.docShellIsActive = true;
browser.zenModeActive = true;
}
_getWebPanelData(id) {
let data = this.sidebarData;
let panel = data.data[id];
if (!panel || !panel.url) {
return {};
}
return {
id: id,
...panel,
};
}
getTabForBrowser(browser) {
return this._tabBrowserSet.get(browser);
}
setTabForBrowser(browser, tab) {
this._tabBrowserSet.set(browser, tab);
}
removeTabForBrowser(browser) {
this._tabBrowserSet.delete(browser);
}
_createWebPanelBrowser(data) {
const titleContainer = document.getElementById('zen-sidebar-web-panel-title');
titleContainer.textContent = 'Loading...';
let browser = gBrowser.createBrowser({
userContextId: data.userContextId,
});
const tab = this.sidebar.querySelector(`[zen-sidebar-id='${data.id}']`);
this.setTabForBrowser(browser, tab);
tab.linkedBrowser = browser;
tab.permanentKey = browser.permanentKey;
browser.setAttribute('disablefullscreen', 'true');
browser.setAttribute('src', data.url);
browser.setAttribute('zen-sidebar-id', data.id);
browser.addEventListener(
'pagetitlechanged',
function (event) {
let browser = event.target;
let title = browser.contentTitle;
if (!title) {
return;
}
let id = browser.getAttribute('zen-sidebar-id');
if (id === this._currentPanel) {
titleContainer.textContent = title;
}
}.bind(this)
);
return browser;
}
_getWebPanelIcon(url, element) {
let { preferredURI } = Services.uriFixup.getFixupURIInfo(url);
element.setAttribute('image', `page-icon:${preferredURI.spec}`);
fetch(`https://s2.googleusercontent.com/s2/favicons?domain_url=${preferredURI.spec}`).then(async (response) => {
if (response.ok) {
let blob = await response.blob();
let reader = new FileReader();
reader.onload = function () {
element.setAttribute('image', reader.result);
};
reader.readAsDataURL(blob);
}
});
}
_getBrowserById(id) {
let sidebar = document.getElementById('zen-sidebar-web-panel');
return sidebar.querySelector(`browser[zen-sidebar-id="${id}"]`);
}
_getCurrentBrowser() {
return this._getBrowserById(this._currentPanel);
}
reload() {
let browser = this._getCurrentBrowser();
if (browser) {
browser.reload();
}
}
forward() {
let browser = this._getCurrentBrowser();
if (browser) {
browser.goForward();
}
}
back() {
let browser = this._getCurrentBrowser();
if (browser) {
browser.goBack();
}
}
home() {
let browser = this._getCurrentBrowser();
if (browser) {
browser.gotoIndex();
}
}
close() {
this._hideAllWebPanels();
this._closeSidebarPanel();
this._updateSidebarButton();
}
moveToTabBoxWrapper() {
this.tabBox.before(this.sidebarWrapper);
this.sidebarWrapper.style.order = '';
}
moveToTabBox() {
this.tabBox.prepend(this.sidebarWrapper);
}
togglePinned(elem) {
if (this.sidebar.hasAttribute('pinned')) {
this._removePinnedFromElements();
//this.moveToTabBoxWrapper();
} else {
this._setPinnedToElements();
//this.moveToTabBox();
}
this.update();
}
get sidebarElement() {
if (!this._sidebarElement) {
this._sidebarElement = document.getElementById('zen-sidebar-panels-sites');
}
return this._sidebarElement;
}
get splitterElement() {
if (!this._splitterElement) {
this._splitterElement = document.getElementById('zen-sidebar-web-panel-splitter');
}
return this._splitterElement;
}
get hSplitterElement() {
if (!this._hSplitterElement) {
this._hSplitterElement = document.getElementById('zen-sidebar-web-panel-hsplitter');
}
return this._hSplitterElement;
}
get sidebarHeader() {
if (!this._sidebarHeader) {
this._sidebarHeader = document.getElementById('zen-sidebar-web-header');
}
return this._sidebarHeader;
}
get sidebarWrapper() {
if (!this._sideBarWrapper) {
this._sideBarWrapper = document.getElementById('zen-sidebar-web-panel-wrapper');
}
return this._sideBarWrapper;
}
get sidebarHeaderButtons() {
if (!this._sidebarHeaderButtons) {
this._sidebarHeaderButtons = [...this.sidebarHeader.querySelectorAll('.toolbarbutton-1')];
}
return this._sidebarHeaderButtons;
}
// Context menu
updateContextMenu(aPopupMenu) {
let panel =
aPopupMenu.triggerNode && (aPopupMenu.triggerNode || aPopupMenu.triggerNode.closest('toolbarbutton[zen-sidebar-id]'));
if (!panel) {
return;
}
let id = panel.getAttribute('zen-sidebar-id');
this.contextTab = id;
let data = this._getWebPanelData(id);
let browser = this._getBrowserById(id);
let isMuted = browser && browser.audioMuted;
let mutedContextItem = document.getElementById('context_zenToggleMuteWebPanel');
document.l10n.setAttributes(
mutedContextItem,
!isMuted ? 'zen-web-side-panel-context-mute-panel' : 'zen-web-side-panel-context-unmute-panel'
);
if (!isMuted) {
mutedContextItem.setAttribute('muted', 'true');
} else {
mutedContextItem.removeAttribute('muted');
}
document.l10n.setAttributes(
document.getElementById('context_zenToogleUAWebPanel'),
data.ua ? 'zen-web-side-panel-context-disable-ua' : 'zen-web-side-panel-context-enable-ua'
);
if (!browser) {
document.getElementById('context_zenUnloadWebPanel').setAttribute('disabled', 'true');
} else {
document.getElementById('context_zenUnloadWebPanel').removeAttribute('disabled');
}
}
createContainerTabMenu(event) {
let window = event.target.ownerGlobal;
let data = this.sidebarData;
let panelData = data.data[this.contextTab];
return window.createUserContextMenu(event, {
isContextMenu: true,
excludeUserContextId: panelData.userContextId,
showDefaultTab: true,
});
}
contextChangeContainerTab(event) {
let data = this.sidebarData;
let userContextId = parseInt(event.target.getAttribute('data-usercontextid'));
data.data[this.contextTab].userContextId = userContextId;
Services.prefs.setStringPref('zen.sidebar.data', JSON.stringify(data));
let browser = this._getBrowserById(this.contextTab);
if (browser) {
browser.remove();
// We need to re-apply a new browser so it takes the new userContextId
this._updateWebPanel();
}
}
contextOpenNewTab() {
let browser = this._getBrowserById(this.contextTab);
let data = this.sidebarData;
let panel = data.data[this.contextTab];
let url = browser == null ? panel.url : browser.currentURI.spec;
gZenUIManager.openAndChangeToTab(url);
this.close();
}
contextToggleMuteAudio() {
let browser = this._getBrowserById(this.contextTab);
if (browser.audioMuted) {
browser.unmute();
} else {
browser.mute();
}
}
contextToggleUserAgent() {
let browser = this._getBrowserById(this.contextTab);
browser.browsingContext.customUserAgent = browser.browsingContext.customUserAgent ? null : this.DEFAULT_MOBILE_USER_AGENT;
let data = this.sidebarData;
data.data[this.contextTab].ua = !data.data[this.contextTab].ua;
Services.prefs.setStringPref('zen.sidebar.data', JSON.stringify(data));
browser.reload();
}
contextDelete() {
let data = this.sidebarData;
delete data.data[this.contextTab];
data.index = data.index.filter((id) => id !== this.contextTab);
let browser = this._getBrowserById(this.contextTab);
if (browser) {
browser.remove();
document.getElementById('zen-sidebar-web-panel-title').textContent = '';
}
this._currentPanel = null;
this._lastOpenedPanel = null;
this.update();
Services.prefs.setStringPref('zen.sidebar.data', JSON.stringify(data));
}
contextUnload() {
let browser = this._getBrowserById(this.contextTab);
this.removeTabForBrowser(browser);
browser.remove();
document.getElementById('zen-sidebar-web-panel-title').textContent = '';
this._closeSidebarPanel();
this.close();
this._lastOpenedPanel = null;
}
insertIntoContextMenu() {
const sibling = document.getElementById('context-stripOnShareLink');
const menuitem = document.createXULElement('menuitem');
menuitem.setAttribute('id', 'context-zenAddToWebPanel');
menuitem.setAttribute('hidden', 'true');
menuitem.setAttribute('oncommand', 'gZenBrowserManagerSidebar.addPanelFromContextMenu();');
menuitem.setAttribute('data-l10n-id', 'zen-web-side-panel-context-add-to-panel');
sibling.insertAdjacentElement('afterend', menuitem);
}
addPanelFromContextMenu() {
const url = gContextMenu.linkURL || gContextMenu.target.ownerDocument.location.href;
this._createNewPanel(url);
}
}
window.gZenBrowserManagerSidebar = new ZenBrowserManagerSidebar();

View File

@@ -0,0 +1,283 @@
{
const lazy = {};
XPCOMUtils.defineLazyPreferenceGetter(lazy, 'zenTabUnloaderEnabled', 'zen.tab-unloader.enabled', false);
XPCOMUtils.defineLazyPreferenceGetter(lazy, 'zenTabUnloaderTimeout', 'zen.tab-unloader.timeout-minutes', 20);
XPCOMUtils.defineLazyPreferenceGetter(lazy, 'zenTabUnloaderExcludedUrls', 'zen.tab-unloader.excluded-urls', '');
const ZEN_TAB_UNLOADER_DEFAULT_EXCLUDED_URLS = [
'^about:',
'^chrome:',
'^devtools:',
'^file:',
'^resource:',
'^view-source:',
'^view-image:',
];
class ZenTabsObserver {
static ALL_EVENTS = [
'TabAttrModified',
'TabPinned',
'TabUnpinned',
'TabBrowserInserted',
'TabBrowserDiscarded',
'TabShow',
'TabHide',
'TabOpen',
'TabClose',
'TabSelect',
'TabMultiSelect',
];
#listeners = [];
constructor() {
this.#listenAllEvents();
}
#listenAllEvents() {
const eventListener = this.#eventListener.bind(this);
for (const event of ZenTabsObserver.ALL_EVENTS) {
window.addEventListener(event, eventListener);
}
window.addEventListener('unload', () => {
for (const event of ZenTabsObserver.ALL_EVENTS) {
window.removeEventListener(event, eventListener);
}
});
}
#eventListener(event) {
for (const listener of this.#listeners) {
listener(event.type, event);
}
}
addTabsListener(listener) {
this.#listeners.push(listener);
}
}
class ZenTabsIntervalUnloader {
static INTERVAL = 1000 * 60; // 1 minute
interval = null;
unloader = null;
#excludedUrls = [];
#compiledExcludedUrls = [];
constructor(unloader) {
this.unloader = unloader;
this.interval = setInterval(this.intervalListener.bind(this), ZenTabsIntervalUnloader.INTERVAL);
this.#excludedUrls = this.lazyExcludeUrls;
}
get lazyExcludeUrls() {
return [
...ZEN_TAB_UNLOADER_DEFAULT_EXCLUDED_URLS,
...lazy.zenTabUnloaderExcludedUrls.split(',').map((url) => url.trim()),
];
}
arraysEqual(a, b) {
if (a === b) return true;
if (a == null || b == null) return false;
if (a.length !== b.length) return false;
// If you don't care about the order of the elements inside
// the array, you should sort both arrays here.
// Please note that calling sort on an array will modify that array.
// you might want to clone your array first.
for (var i = 0; i < a.length; ++i) {
if (a[i] !== b[i]) return false;
}
return true;
}
get excludedUrls() {
// Check if excludedrls is the same as the pref value
const excludedUrls = this.lazyExcludeUrls;
if (!this.arraysEqual(this.#excludedUrls, excludedUrls) || !this.#compiledExcludedUrls.length) {
this.#excludedUrls = excludedUrls;
this.#compiledExcludedUrls = excludedUrls.map((url) => new RegExp(url));
}
return this.#compiledExcludedUrls;
}
intervalListener() {
if (!lazy.zenTabUnloaderEnabled) {
return;
}
const currentTimestamp = Date.now();
const excludedUrls = this.excludedUrls;
const tabs = gBrowser.tabs;
for (let i = 0; i < tabs.length; i++) {
const tab = tabs[i];
if (this.unloader.canUnloadTab(tab, currentTimestamp, excludedUrls)) {
this.unloader.unload(tab);
}
}
}
}
class ZenTabUnloader extends ZenDOMOperatedFeature {
static ACTIVITY_MODIFIERS = ['muted', 'soundplaying', 'label', 'attention'];
init() {
if (!lazy.zenTabUnloaderEnabled) {
return;
}
this.insertIntoContextMenu();
this.observer = new ZenTabsObserver();
this.intervalUnloader = new ZenTabsIntervalUnloader(this);
this.observer.addTabsListener(this.onTabEvent.bind(this));
}
onTabEvent(action, event) {
const tab = event.target;
switch (action) {
case 'TabPinned':
case 'TabUnpinned':
case 'TabBrowserInserted':
case 'TabBrowserDiscarded':
case 'TabShow':
case 'TabHide':
break;
case 'TabAttrModified':
this.handleTabAttrModified(tab, event);
break;
case 'TabOpen':
this.handleTabOpen(tab);
break;
case 'TabClose':
this.handleTabClose(tab);
break;
case 'TabSelect':
case 'TabMultiSelect':
this.updateTabActivity(tab);
break;
default:
console.warn('ZenTabUnloader: Unhandled tab event', action);
break;
}
}
onLocationChange(browser) {
const tab = browser.ownerGlobal.gBrowser.getTabForBrowser(browser);
this.updateTabActivity(tab);
}
handleTabClose(tab) {
// Nothing yet
}
handleTabOpen(tab) {
this.updateTabActivity(tab);
}
handleTabAttrModified(tab, event) {
for (const modifier of ZenTabUnloader.ACTIVITY_MODIFIERS) {
if (event.detail.changed.includes(modifier)) {
this.updateTabActivity(tab);
break;
}
}
}
updateTabActivity(tab) {
const currentTimestamp = Date.now();
tab.lastActivity = currentTimestamp;
}
get tabs() {
return gBrowser.tabs;
}
insertIntoContextMenu() {
const element = window.MozXULElement.parseXULToFragment(`
<menuseparator/>
<menuitem id="context_zenUnloadTab"
data-lazy-l10n-id="tab-zen-unload"
oncommand="gZenTabUnloader.unloadTab();"/>
<menu data-lazy-l10n-id="zen-tabs-unloader-tab-actions" id="context_zenTabActions">
<menupopup>
<menuitem id="context_zenPreventUnloadTab"
data-lazy-l10n-id="tab-zen-prevent-unload"
oncommand="gZenTabUnloader.preventUnloadTab();"/>
<menuitem id="context_zenIgnoreUnloadTab"
data-lazy-l10n-id="tab-zen-ignore-unload"
oncommand="gZenTabUnloader.ignoreUnloadTab();"/>
</menupopup>
</menu>
`);
document.getElementById('context_closeDuplicateTabs').parentNode.appendChild(element);
}
unload(tab) {
gBrowser.discardBrowser(tab);
tab.removeAttribute('linkedpanel');
}
unloadTab() {
const tabs = TabContextMenu.contextTab.multiselected ? gBrowser.selectedTabs : [TabContextMenu.contextTab];
for (let i = 0; i < tabs.length; i++) {
if (this.canUnloadTab(tabs[i], Date.now(), [], true)) {
this.unload(tabs[i]);
}
}
}
preventUnloadTab() {
const tabs = TabContextMenu.contextTab.multiselected ? gBrowser.selectedTabs : [TabContextMenu.contextTab];
for (let i = 0; i < tabs.length; i++) {
const tab = tabs[i];
tab.zenIgnoreUnload = true;
}
}
ignoreUnloadTab() {
const tabs = TabContextMenu.contextTab.multiselected ? gBrowser.selectedTabs : [TabContextMenu.contextTab];
for (let i = 0; i < tabs.length; i++) {
const tab = tabs[i];
tab.zenIgnoreUnload = false;
}
}
canUnloadTab(tab, currentTimestamp, excludedUrls, ignoreTimestamp = false) {
if (
(tab.pinned && !ignoreTimestamp) ||
tab.selected ||
tab.multiselected ||
tab.hasAttribute('busy') ||
tab.hasAttribute('pending') ||
!tab.linkedPanel ||
tab.splitView ||
tab.attention ||
tab.linkedBrowser?.zenModeActive ||
(tab.pictureinpicture && !ignoreTimestamp) ||
(tab.soundPlaying && !ignoreTimestamp) ||
(tab.zenIgnoreUnload && !ignoreTimestamp) ||
excludedUrls.some((url) => url.test(tab.linkedBrowser.currentURI.spec))
) {
return false;
}
if (ignoreTimestamp) {
return true;
}
const lastActivity = tab.lastActivity;
if (!lastActivity) {
return false;
}
const diff = currentTimestamp - lastActivity;
// Check if the tab has been inactive for more than the timeout
return diff > lazy.zenTabUnloaderTimeout * 60 * 1000;
}
}
window.gZenTabUnloader = new ZenTabUnloader();
}

View File

@@ -0,0 +1,176 @@
const kZenAccentColorConfigKey = 'zen.theme.accent-color';
var gZenThemeBuilder = {
init() {
return; // TODO:
this._mouseMoveListener = this._handleThumbMouseMove.bind(this);
setTimeout(() => {
this._initBuilderUI();
}, 500);
},
get _builderWrapper() {
if (this.__builderWrapper) {
return this.__builderWrapper;
}
this.__builderWrapper = document.getElementById('zen-theme-builder-wrapper');
return this.__builderWrapper;
},
_initBuilderUI() {
let wrapper = this._builderWrapper;
if (!wrapper) {
return;
}
console.info('gZenThemeBuilder: init builder UI');
const kTemplate = `
<html:div id="zen-theme-builder">
<html:div id="zen-theme-builder-color-picker">
<html:canvas id="zen-theme-builder-color-picker-canvas"></html:canvas>
<html:div id="zen-theme-builder-color-picker-deck">
<html:div id="zen-theme-builder-color-picker-thumb"></html:div>
</html:div>
</html:div>
</html:div>
`;
wrapper.innerHTML = kTemplate;
this._initColorPicker();
},
_getPositionFromColor(ctx, color) {
var w = ctx.canvas.width,
h = ctx.canvas.height,
data = ctx.getImageData(0, 0, w, h), /// get image data
buffer = data.data, /// and its pixel buffer
len = buffer.length, /// cache length
x,
y = 0,
p,
px; /// for iterating
/// iterating x/y instead of forward to get position the easy way
for (; y < h; y++) {
/// common value for all x
p = y * 4 * w;
for (x = 0; x < w; x++) {
/// next pixel (skipping 4 bytes as each pixel is RGBA bytes)
px = p + x * 4;
/// if red component match check the others
if (buffer[px] === color[0]) {
if (buffer[px + 1] === color[1] && buffer[px + 2] === color[2]) {
return [x, y];
}
}
}
}
return null;
},
_hexToRgb(hex) {
var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return [parseInt(result[1], 16), parseInt(result[2], 16), parseInt(result[3], 16)];
},
_componentToHex(c) {
var hex = c.toString(16);
return hex.length == 1 ? '0' + hex : hex;
},
_rgbToHex(r, g, b) {
return '#' + this._componentToHex(r) + this._componentToHex(g) + this._componentToHex(b);
},
_initColorPicker() {
const canvas = document.getElementById('zen-theme-builder-color-picker-canvas');
const thumb = document.getElementById('zen-theme-builder-color-picker-thumb');
// A all the main colors are all blended together towards the center.
// But we also add some random gradients to make it look more interesting.
// Instead of using a simple gradient, we use a radial gradient.
const ctx = canvas.getContext('2d');
const size = 180;
canvas.width = size;
canvas.height = size;
const center = size / 2;
const radius = size / 2;
const gradient = ctx.createConicGradient(0, center, center);
gradient.addColorStop(0, '#fff490');
gradient.addColorStop(1 / 12, '#f9e380');
gradient.addColorStop(2 / 12, '#fecc87');
gradient.addColorStop(3 / 12, '#ffa894');
gradient.addColorStop(4 / 12, '#f98089');
gradient.addColorStop(5 / 12, '#f9b7c5');
gradient.addColorStop(6 / 12, '#c193b8');
gradient.addColorStop(7 / 12, '#a8b7e0');
gradient.addColorStop(8 / 12, '#88d2f9');
gradient.addColorStop(9 / 12, '#81e8e5');
gradient.addColorStop(10 / 12, '#b7e5a5');
gradient.addColorStop(11 / 12, '#eaefac');
gradient.addColorStop(1, '#fff490');
const radialGradient = ctx.createRadialGradient(size / 2, size / 2, 0, size / 2, size / 2, size / 2);
radialGradient.addColorStop(0, 'rgba(255,255,255,1)');
radialGradient.addColorStop(1, 'rgba(255,255,255,0)');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, size, size);
//ctx.fillStyle = radialGradient;
//ctx.fillRect(0, 0, size, size);
// Add the thumb.
const accentColor = Services.prefs.getStringPref(kZenAccentColorConfigKey, '#aac7ff');
const pos = this._getPositionFromColor(ctx, this._hexToRgb(accentColor));
let x = pos ? pos[0] : center;
let y = pos ? pos[1] : center;
thumb.style.left = `${x}px`;
thumb.style.top = `${y}px`;
thumb.addEventListener('mousedown', this._handleThumbMouseDown.bind(this));
document.addEventListener('mouseup', this._handleThumbMouseUp.bind(this));
},
_handleThumbMouseDown(e) {
document.addEventListener('mousemove', this._mouseMoveListener);
},
_handleThumbMouseUp(e) {
document.removeEventListener('mousemove', this._mouseMoveListener);
},
_handleThumbMouseMove(e) {
const kThumbOffset = 15;
const deck = document.getElementById('zen-theme-builder-color-picker-deck');
const thumb = document.getElementById('zen-theme-builder-color-picker-thumb');
const rect = deck.getBoundingClientRect();
let x = e.clientX - rect.left;
let y = e.clientY - rect.top;
if (x > rect.width - kThumbOffset) {
x = rect.width - kThumbOffset;
}
if (y > rect.height - kThumbOffset) {
y = rect.height - kThumbOffset;
}
if (x < kThumbOffset) {
x = kThumbOffset;
}
if (y < kThumbOffset) {
y = kThumbOffset;
}
thumb.style.left = `${x}px`;
thumb.style.top = `${y}px`;
const canvas = document.getElementById('zen-theme-builder-color-picker-canvas');
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(x, y, 1, 1);
// Update the accent color.
Services.prefs.setStringPref(kZenAccentColorConfigKey, this._rgbToHex(...imageData.data));
},
};

View File

@@ -0,0 +1,120 @@
var ZenThemesCommon = {
kZenColors: ['#aac7ff', '#74d7cb', '#a0d490', '#dec663', '#ffb787', '#dec1b1', '#ffb1c0', '#ddbfc3', '#f6b0ea', '#d4bbff'],
get browsers() {
return Services.wm.getEnumerator('navigator:browser');
},
get currentBrowser() {
return Services.wm.getMostRecentWindow('navigator:browser');
},
get themesRootPath() {
return PathUtils.join(PathUtils.profileDir, 'chrome', 'zen-themes');
},
get themesDataFile() {
return PathUtils.join(PathUtils.profileDir, 'zen-themes.json');
},
getThemeFolder(themeId) {
return PathUtils.join(this.themesRootPath, themeId);
},
resetThemesCache() {
this.themes = null;
},
async getThemes() {
if (!this.themes) {
if (!(await IOUtils.exists(this.themesDataFile))) {
await IOUtils.writeJSON(this.themesDataFile, {});
}
try {
this.themes = await IOUtils.readJSON(this.themesDataFile);
} catch (e) {
// If we have a corrupted file, reset it
await IOUtils.writeJSON(this.themesDataFile, {});
this.themes = {};
gNotificationBox.appendNotification(
"zen-themes-corrupted",
{
label: { "l10n-id": "zen-themes-corrupted" },
image: "chrome://browser/skin/notification-icons/persistent-storage-blocked.svg",
priority: gNotificationBox.PRIORITY_INFO_MEDIUM,
},
[]
);
}
}
return this.themes;
},
async getThemePreferences(theme) {
const themePath = PathUtils.join(this.themesRootPath, theme.id, 'preferences.json');
if (!(await IOUtils.exists(themePath)) || !theme.preferences) {
return [];
}
const preferences = await IOUtils.readJSON(themePath);
// compat mode for old preferences, all of them can only be checkboxes
if (typeof preferences === 'object' && !Array.isArray(preferences)) {
console.warn(
`[ZenThemes]: Warning, ${theme.name} uses legacy preferences, please migrate them to the new preferences style, as legacy preferences might be removed at a future release. More information at: https://docs.zen-browser.app/themes-store/themes-marketplace-preferences`
);
const newThemePreferences = [];
for (let [entry, label] of Object.entries(preferences)) {
const [_, negation = '', os = '', property] = /(!?)(?:(macos|windows|linux):)?([A-z0-9-_.]+)/g.exec(entry);
const isNegation = negation === '!';
if (
(isNegation && os === gZenOperatingSystemCommonUtils.currentOperatingSystem) ||
(os !== '' && os !== gZenOperatingSystemCommonUtils.currentOperatingSystem && !isNegation)
) {
continue;
}
newThemePreferences.push({
property,
label,
type: 'checkbox',
disabledOn: os !== '' ? [os] : [],
});
}
return newThemePreferences;
}
return preferences.filter(
({ disabledOn = [] }) => !disabledOn.includes(gZenOperatingSystemCommonUtils.currentOperatingSystem)
);
},
throttle(mainFunction, delay) {
let timerFlag = null;
return (...args) => {
if (timerFlag === null) {
mainFunction(...args);
timerFlag = setTimeout(() => {
timerFlag = null;
}, delay);
}
};
},
debounce(mainFunction, wait) {
let timerFlag;
return (...args) => {
clearTimeout(timerFlag);
timerFlag = setTimeout(() => {
mainFunction(...args);
}, wait);
};
},
};

View File

@@ -0,0 +1,320 @@
const kZenStylesheetThemeHeader = '/* Zen Themes - Generated by ZenThemesImporter.';
const kZenStylesheetThemeHeaderBody = `* DO NOT EDIT THIS FILE DIRECTLY!
* Your changes will be overwritten.
* Instead, go to the preferences and edit the themes there.
*/
`;
const kenStylesheetFooter = `
/* End of Zen Themes */
`;
const getCurrentDateTime = () =>
new Intl.DateTimeFormat('en-US', {
dateStyle: 'full',
timeStyle: 'full',
}).format(new Date().getTime());
var gZenStylesheetManager = {
async writeStylesheet(path, themes) {
let content = kZenStylesheetThemeHeader;
content += `\n* FILE GENERATED AT: ${getCurrentDateTime()}\n`;
content += kZenStylesheetThemeHeaderBody;
for (let theme of themes) {
if (theme.enabled !== undefined && !theme.enabled) {
continue;
}
content += this.getThemeCSS(theme);
}
content += kenStylesheetFooter;
const buffer = new TextEncoder().encode(content);
await IOUtils.write(path, buffer);
},
getThemeCSS(theme) {
let css = '\n';
css += `/* Name: ${theme.name} */\n`;
css += `/* Description: ${theme.description} */\n`;
css += `/* Author: @${theme.author} */\n`;
if (theme._readmeURL) {
css += `/* Readme: ${theme.readme} */\n`;
}
css += `@import url("${theme._chromeURL}");\n`;
return css;
},
};
var gZenThemesImporter = new (class {
constructor() {
console.info('[ZenThemesImporter]: Initializing Zen Themes Importer');
try {
window.SessionStore.promiseInitialized.then(async () => {
if (Services.prefs.getBoolPref('zen.themes.disable-all', false)) {
console.log('[ZenThemesImporter]: Disabling all themes.');
return;
}
const themes = await this.getEnabledThemes();
const themesWithPreferences = await Promise.all(
themes.map(async (theme) => {
const preferences = await ZenThemesCommon.getThemePreferences(theme);
return {
name: theme.name,
enabled: theme.enabled,
preferences,
};
})
);
this.writeToDom(themesWithPreferences);
await this.insertStylesheet();
});
console.info('[ZenThemesImporter]: Zen Themes imported');
} catch (e) {
console.error('[ZenThemesImporter]: Error importing Zen Themes: ', e);
}
Services.prefs.addObserver('zen.themes.updated-value-observer', this.rebuildThemeStylesheet.bind(this), false);
Services.prefs.addObserver('zen.themes.disable-all', this.handleDisableThemes.bind(this), false);
}
get sss() {
if (!this._sss) {
this._sss = Cc['@mozilla.org/content/style-sheet-service;1'].getService(Ci.nsIStyleSheetService);
}
return this._sss;
}
get styleSheetPath() {
return PathUtils.join(PathUtils.profileDir, 'chrome', 'zen-themes.css');
}
async handleDisableThemes() {
if (Services.prefs.getBoolPref('zen.themes.disable-all', false)) {
console.log('[ZenThemesImporter]: Disabling themes module.');
await this.removeStylesheet();
} else {
console.log('[ZenThemesImporter]: Enabling themes module.');
await this.rebuildThemeStylesheet();
}
}
get styleSheetURI() {
if (!this._styleSheetURI) {
this._styleSheetURI = Services.io.newFileURI(new FileUtils.File(this.styleSheetPath));
}
return this._styleSheetURI;
}
getStylesheetURIForTheme(theme) {
return Services.io.newFileURI(new FileUtils.File(PathUtils.join(ZenThemesCommon.getThemeFolder(theme.id), 'chrome.css')));
}
async insertStylesheet() {
if (await IOUtils.exists(this.styleSheetPath)) {
await this.sss.loadAndRegisterSheet(this.styleSheetURI, this.sss.AGENT_SHEET);
}
if (this.sss.sheetRegistered(this.styleSheetURI, this.sss.AGENT_SHEET)) {
console.debug('[ZenThemesImporter]: Sheet successfully registered');
}
}
async removeStylesheet() {
await this.sss.unregisterSheet(this.styleSheetURI, this.sss.AGENT_SHEET);
await IOUtils.remove(this.styleSheetPath, { ignoreAbsent: true });
if (!this.sss.sheetRegistered(this.styleSheetURI, this.sss.AGENT_SHEET) && !(await IOUtils.exists(this.styleSheetPath))) {
console.debug('[ZenThemesImporter]: Sheet successfully unregistered');
}
}
async rebuildThemeStylesheet() {
if (Services.focus.activeWindow !== window) {
return;
}
ZenThemesCommon.themes = null;
await this.removeStylesheet();
const themes = await this.getEnabledThemes();
await this.writeStylesheet(themes);
const themesWithPreferences = await Promise.all(
themes.map(async (theme) => {
const preferences = await ZenThemesCommon.getThemePreferences(theme);
return {
name: theme.name,
enabled: theme.enabled,
preferences,
};
})
);
this.setDefaults(themesWithPreferences);
this.writeToDom(themesWithPreferences);
await this.insertStylesheet();
}
async getEnabledThemes() {
const themeObject = await ZenThemesCommon.getThemes();
const themes = Object.values(themeObject).filter((theme) => theme.enabled === undefined || theme.enabled);
const themeList = themes.map(({ name }) => name).join(', ');
const message =
themeList !== ''
? `[ZenThemesImporter]: Loading enabled Zen themes: ${themeList}.`
: '[ZenThemesImporter]: No enabled Zen themes.';
console.log(message);
return themes;
}
setDefaults(themesWithPreferences) {
for (const { preferences, enabled } of themesWithPreferences) {
if (enabled !== undefined && !enabled) {
continue;
}
for (const { type, property, defaultValue } of preferences) {
if (defaultValue === undefined) {
continue;
}
switch (type) {
case 'checkbox': {
const value = Services.prefs.getBoolPref(property, false);
if (typeof defaultValue !== 'boolean') {
console.log(`[ZenThemesImporter]: Warning, invalid data type received for expected type boolean, skipping.`);
continue;
}
if (!value) {
Services.prefs.setBoolPref(property, defaultValue);
}
break;
}
default: {
const value = Services.prefs.getStringPref(property, 'zen-property-no-saved');
if (typeof defaultValue !== 'string' && typeof defaultValue !== 'number') {
console.log(`[ZenThemesImporter]: Warning, invalid data type received (${typeof defaultValue}), skipping.`);
continue;
}
if (value === 'zen-property-no-saved') {
Services.prefs.setStringPref(property, defaultValue.toString());
}
}
}
}
}
}
writeToDom(themesWithPreferences) {
for (const browser of ZenMultiWindowFeature.browsers) {
for (const { enabled, preferences, name } of themesWithPreferences) {
const sanitizedName = `theme-${name?.replaceAll(/\s/g, '-')?.replaceAll(/[^A-z_-]+/g, '')}`;
if (enabled !== undefined && !enabled) {
const element = browser.document.getElementById(sanitizedName);
if (element) {
element.remove();
}
for (const { property } of preferences.filter(({ type }) => type !== 'checkbox')) {
const sanitizedProperty = property?.replaceAll(/\./g, '-');
browser.document.querySelector(':root').style.removeProperty(`--${sanitizedProperty}`);
}
continue;
}
for (const { property, type } of preferences) {
const value = Services.prefs.getStringPref(property, '');
const sanitizedProperty = property?.replaceAll(/\./g, '-');
switch (type) {
case 'dropdown': {
if (value !== '') {
let element = browser.document.getElementById(sanitizedName);
if (!element) {
element = browser.document.createElement('div');
element.style.display = 'none';
element.setAttribute('id', sanitizedName);
browser.document.body.appendChild(element);
}
element.setAttribute(sanitizedProperty, value);
}
break;
}
case 'string': {
if (value === '') {
browser.document.querySelector(':root').style.removeProperty(`--${sanitizedProperty}`);
} else {
browser.document.querySelector(':root').style.setProperty(`--${sanitizedProperty}`, value);
}
break;
}
default: {
}
}
}
}
}
}
async writeStylesheet(themeList = []) {
const themes = [];
ZenThemesCommon.themes = null;
for (let theme of themeList) {
theme._chromeURL = this.getStylesheetURIForTheme(theme).spec;
themes.push(theme);
}
await gZenStylesheetManager.writeStylesheet(this.styleSheetPath, themes);
}
})();
gZenActorsManager.addJSWindowActor("ZenThemeMarketplace", {
parent: {
esModuleURI: "chrome://browser/content/zen-components/actors/ZenThemeMarketplaceParent.sys.mjs",
},
child: {
esModuleURI: "chrome://browser/content/zen-components/actors/ZenThemeMarketplaceChild.sys.mjs",
events: {
DOMContentLoaded: {},
},
},
matches: ["https://*.zen-browser.app/*", "about:preferences"],
allFrames: true,
});

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,407 @@
var ZenWorkspacesStorage = {
async init() {
console.log('ZenWorkspacesStorage: Initializing...');
await this._ensureTable();
},
async _ensureTable() {
await PlacesUtils.withConnectionWrapper('ZenWorkspacesStorage._ensureTable', async (db) => {
// Create the main workspaces table if it doesn't exist
await db.execute(`
CREATE TABLE IF NOT EXISTS zen_workspaces (
id INTEGER PRIMARY KEY,
uuid TEXT UNIQUE NOT NULL,
name TEXT NOT NULL,
icon TEXT,
is_default INTEGER NOT NULL DEFAULT 0,
container_id INTEGER,
position INTEGER NOT NULL DEFAULT 0,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
)
`);
// Add new columns if they don't exist
// SQLite doesn't have a direct "ADD COLUMN IF NOT EXISTS" syntax,
// so we need to check if the columns exist first
const columns = await db.execute(`PRAGMA table_info(zen_workspaces)`);
const columnNames = columns.map(row => row.getResultByName('name'));
// Helper function to add column if it doesn't exist
const addColumnIfNotExists = async (columnName, definition) => {
if (!columnNames.includes(columnName)) {
await db.execute(`ALTER TABLE zen_workspaces ADD COLUMN ${columnName} ${definition}`);
}
};
// Add each new column if it doesn't exist
await addColumnIfNotExists('theme_type', 'TEXT');
await addColumnIfNotExists('theme_colors', 'TEXT');
await addColumnIfNotExists('theme_opacity', 'REAL');
await addColumnIfNotExists('theme_rotation', 'INTEGER');
await addColumnIfNotExists('theme_texture', 'REAL');
// Create an index on the uuid column
await db.execute(`
CREATE INDEX IF NOT EXISTS idx_zen_workspaces_uuid ON zen_workspaces(uuid)
`);
// Create the changes tracking table if it doesn't exist
await db.execute(`
CREATE TABLE IF NOT EXISTS zen_workspaces_changes (
uuid TEXT PRIMARY KEY,
timestamp INTEGER NOT NULL
)
`);
// Create an index on the uuid column for changes tracking table
await db.execute(`
CREATE INDEX IF NOT EXISTS idx_zen_workspaces_changes_uuid ON zen_workspaces_changes(uuid)
`);
if (!Weave.Service.engineManager.get('workspaces')) {
Weave.Service.engineManager.register(ZenWorkspacesEngine);
await ZenWorkspacesStorage.migrateWorkspacesFromJSON();
}
ZenWorkspaces._delayedStartup();
});
},
async migrateWorkspacesFromJSON() {
const oldWorkspacesPath = PathUtils.join(PathUtils.profileDir, 'zen-workspaces', 'Workspaces.json');
if (await IOUtils.exists(oldWorkspacesPath)) {
console.info('ZenWorkspacesStorage: Migrating workspaces from JSON...');
const oldWorkspaces = await IOUtils.readJSON(oldWorkspacesPath);
if (oldWorkspaces.workspaces) {
for (const workspace of oldWorkspaces.workspaces) {
await this.saveWorkspace(workspace);
}
}
await IOUtils.remove(oldWorkspacesPath);
}
},
/**
* Private helper method to notify observers with a list of changed UUIDs.
* @param {string} event - The observer event name.
* @param {Array<string>} uuids - Array of changed workspace UUIDs.
*/
_notifyWorkspacesChanged(event, uuids) {
if (uuids.length === 0) return; // No changes to notify
// Convert the array of UUIDs to a JSON string
const data = JSON.stringify(uuids);
Services.obs.notifyObservers(null, event, data);
},
async saveWorkspace(workspace, notifyObservers = true) {
const changedUUIDs = new Set();
await PlacesUtils.withConnectionWrapper('ZenWorkspacesStorage.saveWorkspace', async (db) => {
await db.executeTransaction(async () => {
const now = Date.now();
// Handle default workspace
if (workspace.default) {
await db.execute(`UPDATE zen_workspaces SET is_default = 0 WHERE uuid != :uuid`, { uuid: workspace.uuid });
const unsetDefaultRows = await db.execute(`SELECT uuid FROM zen_workspaces WHERE is_default = 0 AND uuid != :uuid`, { uuid: workspace.uuid });
for (const row of unsetDefaultRows) {
changedUUIDs.add(row.getResultByName('uuid'));
}
}
let newPosition;
if ('position' in workspace && Number.isFinite(workspace.position)) {
newPosition = workspace.position;
} else {
// Get the maximum position
const maxPositionResult = await db.execute(`SELECT MAX("position") as max_position FROM zen_workspaces`);
const maxPosition = maxPositionResult[0].getResultByName('max_position') || 0;
newPosition = maxPosition + 1000; // Add a large increment to avoid frequent reordering
}
// Insert or replace the workspace
await db.executeCached(`
INSERT OR REPLACE INTO zen_workspaces (
uuid, name, icon, is_default, container_id, created_at, updated_at, "position",
theme_type, theme_colors, theme_opacity, theme_rotation, theme_texture
) VALUES (
:uuid, :name, :icon, :is_default, :container_id,
COALESCE((SELECT created_at FROM zen_workspaces WHERE uuid = :uuid), :now),
:now,
:position,
:theme_type, :theme_colors, :theme_opacity, :theme_rotation, :theme_texture
)
`, {
uuid: workspace.uuid,
name: workspace.name,
icon: workspace.icon || null,
is_default: workspace.default ? 1 : 0,
container_id: workspace.containerTabId || null,
now,
position: newPosition,
theme_type: workspace.theme?.type || null,
theme_colors: workspace.theme ? JSON.stringify(workspace.theme.gradientColors) : null,
theme_opacity: workspace.theme?.opacity || null,
theme_rotation: workspace.theme?.rotation || null,
theme_texture: workspace.theme?.texture || null
});
// Record the change
await db.execute(`
INSERT OR REPLACE INTO zen_workspaces_changes (uuid, timestamp)
VALUES (:uuid, :timestamp)
`, {
uuid: workspace.uuid,
timestamp: Math.floor(now / 1000)
});
changedUUIDs.add(workspace.uuid);
await this.updateLastChangeTimestamp(db);
});
});
if (notifyObservers) {
this._notifyWorkspacesChanged("zen-workspace-updated", Array.from(changedUUIDs));
}
},
async getWorkspaces() {
const db = await PlacesUtils.promiseDBConnection();
const rows = await db.executeCached(`
SELECT * FROM zen_workspaces ORDER BY created_at ASC
`);
return rows.map((row) => ({
uuid: row.getResultByName('uuid'),
name: row.getResultByName('name'),
icon: row.getResultByName('icon'),
default: !!row.getResultByName('is_default'),
containerTabId: row.getResultByName('container_id'),
position: row.getResultByName('position'),
theme: row.getResultByName('theme_type') ? {
type: row.getResultByName('theme_type'),
gradientColors: JSON.parse(row.getResultByName('theme_colors')),
opacity: row.getResultByName('theme_opacity'),
rotation: row.getResultByName('theme_rotation'),
texture: row.getResultByName('theme_texture')
} : null
}));
},
async removeWorkspace(uuid, notifyObservers = true) {
const changedUUIDs = [uuid];
await PlacesUtils.withConnectionWrapper('ZenWorkspacesStorage.removeWorkspace', async (db) => {
await db.execute(
`
DELETE FROM zen_workspaces WHERE uuid = :uuid
`,
{ uuid }
);
// Record the removal as a change
const now = Date.now();
await db.execute(`
INSERT OR REPLACE INTO zen_workspaces_changes (uuid, timestamp)
VALUES (:uuid, :timestamp)
`, {
uuid,
timestamp: Math.floor(now / 1000)
});
await this.updateLastChangeTimestamp(db);
});
if (notifyObservers) {
this._notifyWorkspacesChanged("zen-workspace-removed", changedUUIDs);
}
},
async wipeAllWorkspaces() {
await PlacesUtils.withConnectionWrapper('ZenWorkspacesStorage.wipeAllWorkspaces', async (db) => {
await db.execute(`DELETE FROM zen_workspaces`);
await db.execute(`DELETE FROM zen_workspaces_changes`);
await this.updateLastChangeTimestamp(db);
});
},
async setDefaultWorkspace(uuid, notifyObservers = true) {
const changedUUIDs = [];
await PlacesUtils.withConnectionWrapper('ZenWorkspacesStorage.setDefaultWorkspace', async (db) => {
await db.executeTransaction(async () => {
const now = Date.now();
// Unset the default flag for all other workspaces
await db.execute(`UPDATE zen_workspaces SET is_default = 0 WHERE uuid != :uuid`, { uuid });
// Collect UUIDs of workspaces that were unset as default
const unsetDefaultRows = await db.execute(`SELECT uuid FROM zen_workspaces WHERE is_default = 0 AND uuid != :uuid`, { uuid });
for (const row of unsetDefaultRows) {
changedUUIDs.push(row.getResultByName('uuid'));
}
// Set the default flag for the specified workspace
await db.execute(`UPDATE zen_workspaces SET is_default = 1 WHERE uuid = :uuid`, { uuid });
// Record the change for the specified workspace
await db.execute(`
INSERT OR REPLACE INTO zen_workspaces_changes (uuid, timestamp)
VALUES (:uuid, :timestamp)
`, {
uuid,
timestamp: Math.floor(now / 1000)
});
// Add the main workspace UUID to the changed set
changedUUIDs.push(uuid);
await this.updateLastChangeTimestamp(db);
});
});
if (notifyObservers) {
this._notifyWorkspacesChanged("zen-workspace-updated", changedUUIDs);
}
},
async markChanged(uuid) {
await PlacesUtils.withConnectionWrapper('ZenWorkspacesStorage.markChanged', async (db) => {
const now = Date.now();
await db.execute(`
INSERT OR REPLACE INTO zen_workspaces_changes (uuid, timestamp)
VALUES (:uuid, :timestamp)
`, {
uuid,
timestamp: Math.floor(now / 1000)
});
});
},
async saveWorkspaceTheme(uuid, theme, notifyObservers = true) {
const changedUUIDs = [uuid];
await PlacesUtils.withConnectionWrapper('saveWorkspaceTheme', async (db) => {
await db.execute(`
UPDATE zen_workspaces
SET
theme_type = :type,
theme_colors = :colors,
theme_opacity = :opacity,
theme_rotation = :rotation,
theme_texture = :texture,
updated_at = :now
WHERE uuid = :uuid
`, {
type: theme.type,
colors: JSON.stringify(theme.gradientColors),
opacity: theme.opacity,
rotation: theme.rotation,
texture: theme.texture,
now: Date.now(),
uuid
});
await this.markChanged(uuid);
await this.updateLastChangeTimestamp(db);
});
if (notifyObservers) {
this._notifyWorkspacesChanged("zen-workspace-updated", changedUUIDs);
}
},
async getChangedIDs() {
const db = await PlacesUtils.promiseDBConnection();
const rows = await db.execute(`
SELECT uuid, timestamp FROM zen_workspaces_changes
`);
const changes = {};
for (const row of rows) {
changes[row.getResultByName('uuid')] = row.getResultByName('timestamp');
}
return changes;
},
async clearChangedIDs() {
await PlacesUtils.withConnectionWrapper('ZenWorkspacesStorage.clearChangedIDs', async (db) => {
await db.execute(`DELETE FROM zen_workspaces_changes`);
});
},
shouldReorderWorkspaces(before, current, after) {
const minGap = 1; // Minimum allowed gap between positions
return (before !== null && current - before < minGap) || (after !== null && after - current < minGap);
},
async reorderAllWorkspaces(db, changedUUIDs) {
const workspaces = await db.execute(`
SELECT uuid
FROM zen_workspaces
ORDER BY "position" ASC
`);
for (let i = 0; i < workspaces.length; i++) {
const newPosition = (i + 1) * 1000; // Use large increments
await db.execute(`
UPDATE zen_workspaces
SET "position" = :newPosition
WHERE uuid = :uuid
`, { newPosition, uuid: workspaces[i].getResultByName('uuid') });
changedUUIDs.add(workspaces[i].getResultByName('uuid'));
}
},
async updateLastChangeTimestamp(db) {
const now = Date.now();
await db.execute(`
INSERT OR REPLACE INTO moz_meta (key, value)
VALUES ('zen_workspaces_last_change', :now)
`, { now });
},
async getLastChangeTimestamp() {
const db = await PlacesUtils.promiseDBConnection();
const result = await db.executeCached(`
SELECT value FROM moz_meta WHERE key = 'zen_workspaces_last_change'
`);
return result.length ? parseInt(result[0].getResultByName('value'), 10) : 0;
},
async updateWorkspacePositions(workspaces) {
const changedUUIDs = new Set();
await PlacesUtils.withConnectionWrapper('ZenWorkspacesStorage.updateWorkspacePositions', async (db) => {
await db.executeTransaction(async () => {
const now = Date.now();
for (let i = 0; i < workspaces.length; i++) {
const workspace = workspaces[i];
const newPosition = (i + 1) * 1000;
await db.execute(`
UPDATE zen_workspaces
SET "position" = :newPosition
WHERE uuid = :uuid
`, { newPosition, uuid: workspace.uuid });
changedUUIDs.add(workspace.uuid);
// Record the change
await db.execute(`
INSERT OR REPLACE INTO zen_workspaces_changes (uuid, timestamp)
VALUES (:uuid, :timestamp)
`, {
uuid: workspace.uuid,
timestamp: Math.floor(now / 1000)
});
}
await this.updateLastChangeTimestamp(db);
});
});
this._notifyWorkspacesChanged("zen-workspace-updated", Array.from(changedUUIDs));
},
};

View File

@@ -0,0 +1,452 @@
var { Tracker, Store, SyncEngine } = ChromeUtils.importESModule("resource://services-sync/engines.sys.mjs");
var { CryptoWrapper } = ChromeUtils.importESModule("resource://services-sync/record.sys.mjs");
var { Utils } = ChromeUtils.importESModule("resource://services-sync/util.sys.mjs");
var { SCORE_INCREMENT_XLARGE } = ChromeUtils.importESModule("resource://services-sync/constants.sys.mjs");
// Define ZenWorkspaceRecord
function ZenWorkspaceRecord(collection, id) {
CryptoWrapper.call(this, collection, id);
}
ZenWorkspaceRecord.prototype = Object.create(CryptoWrapper.prototype);
ZenWorkspaceRecord.prototype.constructor = ZenWorkspaceRecord;
ZenWorkspaceRecord.prototype._logName = "Sync.Record.ZenWorkspace";
Utils.deferGetSet(ZenWorkspaceRecord, "cleartext", [
"name",
"icon",
"default",
"containerTabId",
"position",
"theme_type",
"theme_colors",
"theme_opacity",
"theme_rotation",
"theme_texture"
]);
// Define ZenWorkspacesStore
function ZenWorkspacesStore(name, engine) {
Store.call(this, name, engine);
}
ZenWorkspacesStore.prototype = Object.create(Store.prototype);
ZenWorkspacesStore.prototype.constructor = ZenWorkspacesStore;
/**
* Initializes the store by loading the current changeset.
*/
ZenWorkspacesStore.prototype.initialize = async function () {
await Store.prototype.initialize.call(this);
// Additional initialization if needed
};
/**
* Retrieves all workspace IDs from the storage.
* @returns {Object} An object mapping workspace UUIDs to true.
*/
ZenWorkspacesStore.prototype.getAllIDs = async function () {
try {
const workspaces = await ZenWorkspacesStorage.getWorkspaces();
const ids = {};
for (const workspace of workspaces) {
ids[workspace.uuid] = true;
}
return ids;
} catch (error) {
this._log.error("Error fetching all workspace IDs", error);
throw error;
}
};
/**
* Handles changing the ID of a workspace.
* @param {String} oldID - The old UUID.
* @param {String} newID - The new UUID.
*/
ZenWorkspacesStore.prototype.changeItemID = async function (oldID, newID) {
try {
const workspaces = await ZenWorkspacesStorage.getWorkspaces();
const workspace = workspaces.find(ws => ws.uuid === oldID);
if (workspace) {
workspace.uuid = newID;
await ZenWorkspacesStorage.saveWorkspace(workspace,false);
// Mark the new ID as changed for sync
await ZenWorkspacesStorage.markChanged(newID);
}
} catch (error) {
this._log.error(`Error changing workspace ID from ${oldID} to ${newID}`, error);
throw error;
}
};
/**
* Checks if a workspace exists.
* @param {String} id - The UUID of the workspace.
* @returns {Boolean} True if the workspace exists, false otherwise.
*/
ZenWorkspacesStore.prototype.itemExists = async function (id) {
try {
const workspaces = await ZenWorkspacesStorage.getWorkspaces();
return workspaces.some(ws => ws.uuid === id);
} catch (error) {
this._log.error(`Error checking if workspace exists with ID ${id}`, error);
throw error;
}
};
/**
* Creates a record for a workspace.
* @param {String} id - The UUID of the workspace.
* @param {String} collection - The collection name.
* @returns {ZenWorkspaceRecord} The workspace record.
*/
ZenWorkspacesStore.prototype.createRecord = async function (id, collection) {
try {
const workspaces = await ZenWorkspacesStorage.getWorkspaces();
const workspace = workspaces.find(ws => ws.uuid === id);
const record = new ZenWorkspaceRecord(collection, id);
if (workspace) {
record.name = workspace.name;
record.icon = workspace.icon;
record.default = workspace.default;
record.containerTabId = workspace.containerTabId;
record.position = workspace.position;
if (workspace.theme) {
record.theme_type = workspace.theme.type;
record.theme_colors = JSON.stringify(workspace.theme.gradientColors);
record.theme_opacity = workspace.theme.opacity;
record.theme_rotation = workspace.theme.rotation;
record.theme_texture = workspace.theme.texture;
}
record.deleted = false;
} else {
record.deleted = true;
}
return record;
} catch (error) {
this._log.error(`Error creating record for workspace ID ${id}`, error);
throw error;
}
};
/**
* Creates a new workspace.
* @param {ZenWorkspaceRecord} record - The workspace record to create.
*/
ZenWorkspacesStore.prototype.create = async function (record) {
try {
this._validateRecord(record);
const workspace = {
uuid: record.id,
name: record.name,
icon: record.icon,
default: record.default,
containerTabId: record.containerTabId,
position: record.position,
theme: record.theme_type ? {
type: record.theme_type,
gradientColors: JSON.parse(record.theme_colors),
opacity: record.theme_opacity,
rotation: record.theme_rotation,
texture: record.theme_texture
} : null
};
await ZenWorkspacesStorage.saveWorkspace(workspace,false);
} catch (error) {
this._log.error(`Error creating workspace with ID ${record.id}`, error);
throw error;
}
};
/**
* Updates an existing workspace.
* @param {ZenWorkspaceRecord} record - The workspace record to update.
*/
ZenWorkspacesStore.prototype.update = async function (record) {
try {
this._validateRecord(record);
await this.create(record); // Reuse create for update
} catch (error) {
this._log.error(`Error updating workspace with ID ${record.id}`, error);
throw error;
}
};
/**
* Removes a workspace.
* @param {ZenWorkspaceRecord} record - The workspace record to remove.
*/
ZenWorkspacesStore.prototype.remove = async function (record) {
try {
await ZenWorkspacesStorage.removeWorkspace(record.id, false);
} catch (error) {
this._log.error(`Error removing workspace with ID ${record.id}`, error);
throw error;
}
};
/**
* Wipes all workspaces from the storage.
*/
ZenWorkspacesStore.prototype.wipe = async function () {
try {
await ZenWorkspacesStorage.wipeAllWorkspaces();
} catch (error) {
this._log.error("Error wiping all workspaces", error);
throw error;
}
};
/**
* Validates a workspace record.
* @param {ZenWorkspaceRecord} record - The workspace record to validate.
*/
ZenWorkspacesStore.prototype._validateRecord = function (record) {
if (!record.id || typeof record.id !== "string") {
throw new Error("Invalid workspace ID");
}
if (!record.name || typeof record.name !== "string") {
throw new Error(`Invalid workspace name for ID ${record.id}`);
}
if (typeof record.default !== "boolean") {
record.default = false;
}
if (record.icon != null && typeof record.icon !== "string") {
throw new Error(`Invalid icon for workspace ID ${record.id}`);
}
if (record.containerTabId != null && typeof record.containerTabId !== "number") {
throw new Error(`Invalid containerTabId for workspace ID ${record.id}`);
}
if(record.position != null && typeof record.position !== "number") {
throw new Error(`Invalid position for workspace ID ${record.id}`);
}
// Validate theme properties if they exist
if (record.theme_type) {
if (typeof record.theme_type !== "string") {
throw new Error(`Invalid theme_type for workspace ID ${record.id}`);
}
if (!record.theme_colors || typeof record.theme_colors !== "string") {
throw new Error(`Invalid theme_colors for workspace ID ${record.id}`);
}
try {
JSON.parse(record.theme_colors);
} catch (e) {
throw new Error(`Invalid theme_colors JSON for workspace ID ${record.id}`);
}
if (record.theme_opacity != null && typeof record.theme_opacity !== "number") {
throw new Error(`Invalid theme_opacity for workspace ID ${record.id}`);
}
if (record.theme_rotation != null && typeof record.theme_rotation !== "number") {
throw new Error(`Invalid theme_rotation for workspace ID ${record.id}`);
}
if (record.theme_texture != null && typeof record.theme_texture !== "number") {
throw new Error(`Invalid theme_texture for workspace ID ${record.id}`);
}
}
};
/**
* Retrieves changed workspace IDs since the last sync.
* @returns {Object} An object mapping workspace UUIDs to their change timestamps.
*/
ZenWorkspacesStore.prototype.getChangedIDs = async function () {
try {
return await ZenWorkspacesStorage.getChangedIDs();
} catch (error) {
this._log.error("Error retrieving changed IDs from storage", error);
throw error;
}
};
/**
* Clears all recorded changes after a successful sync.
*/
ZenWorkspacesStore.prototype.clearChangedIDs = async function () {
try {
await ZenWorkspacesStorage.clearChangedIDs();
} catch (error) {
this._log.error("Error clearing changed IDs in storage", error);
throw error;
}
};
/**
* Marks a workspace as changed.
* @param {String} uuid - The UUID of the workspace that changed.
*/
ZenWorkspacesStore.prototype.markChanged = async function (uuid) {
try {
await ZenWorkspacesStorage.markChanged(uuid);
} catch (error) {
this._log.error(`Error marking workspace ${uuid} as changed`, error);
throw error;
}
};
/**
* Finalizes the store by ensuring all pending operations are completed.
*/
ZenWorkspacesStore.prototype.finalize = async function () {
await Store.prototype.finalize.call(this);
};
// Define ZenWorkspacesTracker
function ZenWorkspacesTracker(name, engine) {
Tracker.call(this, name, engine);
this._ignoreAll = false;
// Observe profile-before-change to stop the tracker gracefully
Services.obs.addObserver(this.asyncObserver, "profile-before-change");
}
ZenWorkspacesTracker.prototype = Object.create(Tracker.prototype);
ZenWorkspacesTracker.prototype.constructor = ZenWorkspacesTracker;
/**
* Retrieves changed workspace IDs by delegating to the store.
* @returns {Object} An object mapping workspace UUIDs to their change timestamps.
*/
ZenWorkspacesTracker.prototype.getChangedIDs = async function () {
try {
return await this.engine._store.getChangedIDs();
} catch (error) {
this._log.error("Error retrieving changed IDs from store", error);
throw error;
}
};
/**
* Clears all recorded changes after a successful sync.
*/
ZenWorkspacesTracker.prototype.clearChangedIDs = async function () {
try {
await this.engine._store.clearChangedIDs();
} catch (error) {
this._log.error("Error clearing changed IDs in store", error);
throw error;
}
};
/**
* Called when the tracker starts. Registers observers to listen for workspace changes.
*/
ZenWorkspacesTracker.prototype.onStart = function () {
if (this._started) {
return;
}
this._log.trace("Starting tracker");
// Register observers for workspace changes
Services.obs.addObserver(this.asyncObserver, "zen-workspace-added");
Services.obs.addObserver(this.asyncObserver, "zen-workspace-removed");
Services.obs.addObserver(this.asyncObserver, "zen-workspace-updated");
this._started = true;
};
/**
* Called when the tracker stops. Unregisters observers.
*/
ZenWorkspacesTracker.prototype.onStop = function () {
if (!this._started) {
return;
}
this._log.trace("Stopping tracker");
// Unregister observers for workspace changes
Services.obs.removeObserver(this.asyncObserver, "zen-workspace-added");
Services.obs.removeObserver(this.asyncObserver, "zen-workspace-removed");
Services.obs.removeObserver(this.asyncObserver, "zen-workspace-updated");
this._started = false;
};
/**
* Handles observed events and marks workspaces as changed accordingly.
* @param {nsISupports} subject - The subject of the notification.
* @param {String} topic - The topic of the notification.
* @param {String} data - Additional data (JSON stringified array of UUIDs).
*/
ZenWorkspacesTracker.prototype.observe = async function (subject, topic, data) {
if (this.ignoreAll) {
return;
}
try {
switch (topic) {
case "profile-before-change":
await this.stop();
break;
case "zen-workspace-removed":
case "zen-workspace-updated":
case "zen-workspace-added":
let workspaceIDs;
if (data) {
try {
workspaceIDs = JSON.parse(data);
if (!Array.isArray(workspaceIDs)) {
throw new Error("Parsed data is not an array");
}
} catch (parseError) {
this._log.error(`Failed to parse workspace UUIDs from data: ${data}`, parseError);
return;
}
} else {
this._log.error(`No data received for event ${topic}`);
return;
}
this._log.trace(`Observed ${topic} for UUIDs: ${workspaceIDs.join(", ")}`);
// Process each UUID
for (const workspaceID of workspaceIDs) {
if (typeof workspaceID === "string") {
// Inform the store about the change
await this.engine._store.markChanged(workspaceID);
} else {
this._log.warn(`Invalid workspace ID encountered: ${workspaceID}`);
}
}
// Bump the score once after processing all changes
if (workspaceIDs.length > 0) {
this.score += SCORE_INCREMENT_XLARGE;
}
break;
}
} catch (error) {
this._log.error(`Error handling ${topic} in observe method`, error);
}
};
/**
* Finalizes the tracker by ensuring all pending operations are completed.
*/
ZenWorkspacesTracker.prototype.finalize = async function () {
await Tracker.prototype.finalize.call(this);
};
// Define ZenWorkspacesEngine
function ZenWorkspacesEngine(service) {
SyncEngine.call(this, "Workspaces", service);
}
ZenWorkspacesEngine.prototype = Object.create(SyncEngine.prototype);
ZenWorkspacesEngine.prototype.constructor = ZenWorkspacesEngine;
ZenWorkspacesEngine.prototype._storeObj = ZenWorkspacesStore;
ZenWorkspacesEngine.prototype._trackerObj = ZenWorkspacesTracker;
ZenWorkspacesEngine.prototype._recordObj = ZenWorkspaceRecord;
ZenWorkspacesEngine.prototype.version = 2;
ZenWorkspacesEngine.prototype.syncPriority = 10;
ZenWorkspacesEngine.prototype.allowSkippedRecord = false;
Object.setPrototypeOf(ZenWorkspacesEngine.prototype, SyncEngine.prototype);

View File

@@ -0,0 +1,124 @@
export class ZenGlanceChild extends JSWindowActorChild {
constructor() {
super();
this.mouseUpListener = this.handleMouseUp.bind(this);
this.mouseDownListener = this.handleMouseDown.bind(this);
this.clickListener = this.handleClick.bind(this);
}
async handleEvent(event) {
switch (event.type) {
case 'DOMContentLoaded':
await this.initiateGlance();
break;
default:
}
}
async getActivationMethod() {
if (this._activationMethod === undefined) {
this._activationMethod = await this.sendQuery('ZenGlance:GetActivationMethod');
}
return this._activationMethod;
}
async getHoverActivationDelay() {
if (this._hoverActivationDelay === undefined) {
this._hoverActivationDelay = await this.sendQuery('ZenGlance:GetHoverActivationDelay');
}
return this._hoverActivationDelay;
}
async receiveMessage(message) {
switch (message.name) {
}
}
async initiateGlance() {
this.mouseIsDown = false;
const activationMethod = await this.getActivationMethod();
if (activationMethod === 'mantain') {
this.contentWindow.addEventListener('mousedown', this.mouseDownListener);
this.contentWindow.addEventListener('mouseup', this.mouseUpListener);
this.contentWindow.document.removeEventListener('click', this.clickListener);
} else if (activationMethod === 'ctrl' || activationMethod === 'alt' || activationMethod === 'shift') {
this.contentWindow.document.addEventListener('click', this.clickListener);
this.contentWindow.removeEventListener('mousedown', this.mouseDownListener);
this.contentWindow.removeEventListener('mouseup', this.mouseUpListener);
}
}
ensureOnlyKeyModifiers(event) {
return !(event.ctrlKey ^ event.altKey ^ event.shiftKey ^ event.metaKey);
}
openGlance(target) {
let url = target.href;
// Add domain to relative URLs
if (!url.match(/^(?:[a-z]+:)?\/\//i)) {
url = this.contentWindow.location.origin + url;
}
const rect = target.getBoundingClientRect();
this.sendAsyncMessage('ZenGlance:OpenGlance', {
url,
x: rect.left,
y: rect.top,
width: rect.width,
height: rect.height,
});
}
handleMouseUp(event) {
if (this.hasClicked) {
event.preventDefault();
event.stopPropagation();
this.hasClicked = false;
}
this.mouseIsDown = null;
}
async handleMouseDown(event) {
const target = event.target.closest('A');
console.log('target', target);
if (!target) {
return;
}
this.mouseIsDown = target;
const hoverActivationDelay = await this.getHoverActivationDelay();
this.contentWindow.setTimeout(() => {
if (this.mouseIsDown === target) {
this.hasClicked = true;
this.openGlance(target);
}
}, hoverActivationDelay);
}
handleClick(event) {
if (this.ensureOnlyKeyModifiers(event)) {
return;
}
const activationMethod = this._activationMethod;
if (activationMethod === 'ctrl' && !event.ctrlKey) {
return;
} else if (activationMethod === 'alt' && !event.altKey) {
return;
} else if (activationMethod === 'shift' && !event.shiftKey) {
return;
} else if (activationMethod === 'meta' && !event.metaKey) {
return;
}else if (activationMethod === 'mantain' || typeof activationMethod === 'undefined') {
return;
}
// get closest A element
const target = event.target.closest('A');
if (target) {
event.preventDefault();
event.stopPropagation();
this.openGlance(target);
}
}
}

View File

@@ -0,0 +1,24 @@
export class ZenGlanceParent extends JSWindowActorParent {
constructor() {
super();
}
async receiveMessage(message) {
switch (message.name) {
case 'ZenGlance:GetActivationMethod': {
return Services.prefs.getStringPref('zen.glance.activation-method', 'ctrl');
}
case 'ZenGlance:GetHoverActivationDelay': {
return Services.prefs.getIntPref('zen.glance.hold-duration', 500);
}
case 'ZenGlance:OpenGlance': {
this.openGlance(this.browsingContext.topChromeWindow, message.data);
break;
}
}
}
openGlance(window, data) {
window.gZenGlanceManager.openGlance(data);
}
}

View File

@@ -0,0 +1,142 @@
export class ZenThemeMarketplaceChild extends JSWindowActorChild {
constructor() {
super();
}
handleEvent(event) {
switch (event.type) {
case 'DOMContentLoaded':
this.initiateThemeMarketplace();
this.contentWindow.document.addEventListener('ZenCheckForThemeUpdates', this.checkForThemeUpdates.bind(this));
break;
default:
}
}
// This function will be caleld from about:preferences
checkForThemeUpdates(event) {
event.preventDefault();
this.sendAsyncMessage('ZenThemeMarketplace:CheckForUpdates');
}
initiateThemeMarketplace() {
this.contentWindow.setTimeout(() => {
this.addIntallButtons();
}, 0);
}
get actionButton() {
return this.contentWindow.document.getElementById('install-theme');
}
get actionButtonUnnstall() {
return this.contentWindow.document.getElementById('install-theme-uninstall');
}
async receiveMessage(message) {
switch (message.name) {
case 'ZenThemeMarketplace:ThemeChanged': {
const themeId = message.data.themeId;
const actionButton = this.actionButton;
const actionButtonInstalled = this.actionButtonUnnstall;
if (actionButton && actionButtonInstalled) {
actionButton.disabled = false;
actionButtonInstalled.disabled = false;
if (await this.isThemeInstalled(themeId)) {
actionButton.classList.add('hidden');
actionButtonInstalled.classList.remove('hidden');
} else {
actionButton.classList.remove('hidden');
actionButtonInstalled.classList.add('hidden');
}
}
break;
}
case 'ZenThemeMarketplace:CheckForUpdatesFinished': {
const updates = message.data.updates;
this.contentWindow.document.dispatchEvent(
new CustomEvent('ZenThemeMarketplace:CheckForUpdatesFinished', { detail: { updates } })
);
break;
}
case 'ZenThemeMarketplace:GetThemeInfo': {
const themeId = message.data.themeId;
const theme = await this.getThemeInfo(themeId);
return theme;
}
}
}
async addIntallButtons() {
const actionButton = this.actionButton;
const actionButtonUnnstall = this.actionButtonUnnstall;
const errorMessage = this.contentWindow.document.getElementById('install-theme-error');
if (!actionButton || !actionButtonUnnstall) {
return;
}
errorMessage.classList.add('hidden');
const themeId = actionButton.getAttribute('zen-theme-id');
if (await this.isThemeInstalled(themeId)) {
actionButtonUnnstall.classList.remove('hidden');
} else {
actionButton.classList.remove('hidden');
}
actionButton.addEventListener('click', this.installTheme.bind(this));
actionButtonUnnstall.addEventListener('click', this.uninstallTheme.bind(this));
}
async isThemeInstalled(themeId) {
return await this.sendQuery('ZenThemeMarketplace:IsThemeInstalled', { themeId });
}
addTheme(theme) {
this.sendAsyncMessage('ZenThemeMarketplace:InstallTheme', { theme });
}
getThemeAPIUrl(themeId) {
return `https://zen-browser.github.io/theme-store/themes/${themeId}/theme.json`;
}
async getThemeInfo(themeId) {
const url = this.getThemeAPIUrl(themeId);
console.info('ZTM: Fetching theme info from: ', url);
const data = await fetch(url, {
mode: 'no-cors',
});
if (data.ok) {
try {
const obj = await data.json();
return obj;
} catch (e) {
console.error('ZTM: Error parsing theme info: ', e);
}
} else console.log(data.status);
return null;
}
async uninstallTheme(event) {
const button = event.target;
button.disabled = true;
const themeId = button.getAttribute('zen-theme-id');
console.info('ZTM: Uninstalling theme with id: ', themeId);
this.sendAsyncMessage('ZenThemeMarketplace:UninstallTheme', { themeId });
}
async installTheme(event) {
const button = event.target;
button.disabled = true;
const themeId = button.getAttribute('zen-theme-id');
console.info('ZTM: Installing theme with id: ', themeId);
const theme = await this.getThemeInfo(themeId);
if (!theme) {
console.error('ZTM: Error fetching theme info');
return;
}
this.addTheme(theme);
}
}

View File

@@ -0,0 +1,186 @@
export class ZenThemeMarketplaceParent extends JSWindowActorParent {
constructor() {
super();
}
async receiveMessage(message) {
switch (message.name) {
case 'ZenThemeMarketplace:InstallTheme': {
console.info('ZenThemeMarketplaceParent: Updating themes');
const theme = message.data.theme;
theme.enabled = true;
const themes = await this.getThemes();
themes[theme.id] = theme;
this.updateThemes(themes);
this.updateChildProcesses(theme.id);
break;
}
case 'ZenThemeMarketplace:UninstallTheme': {
console.info('ZenThemeMarketplaceParent: Uninstalling theme');
const themeId = message.data.themeId;
const themes = await this.getThemes();
delete themes[themeId];
this.removeTheme(themeId);
this.updateThemes(themes);
this.updateChildProcesses(themeId);
break;
}
case 'ZenThemeMarketplace:IsThemeInstalled': {
const themeId = message.data.themeId;
const themes = await this.getThemes();
return themes[themeId] ? true : false;
}
case 'ZenThemeMarketplace:CheckForUpdates': {
this.checkForThemeUpdates();
break;
}
}
}
compareversion(version1, version2) {
var result = false;
if (typeof version1 !== 'object') {
version1 = version1.toString().split('.');
}
if (typeof version2 !== 'object') {
version2 = version2.toString().split('.');
}
for (var i = 0; i < Math.max(version1.length, version2.length); i++) {
if (version1[i] == undefined) {
version1[i] = 0;
}
if (version2[i] == undefined) {
version2[i] = 0;
}
if (Number(version1[i]) < Number(version2[i])) {
result = true;
break;
}
if (version1[i] != version2[i]) {
break;
}
}
return result;
}
async checkForThemeUpdates() {
console.info('ZenThemeMarketplaceParent: Checking for theme updates');
let updates = [];
this._themes = null;
for (const theme of Object.values(await this.getThemes())) {
const themeInfo = await this.sendQuery('ZenThemeMarketplace:GetThemeInfo', { themeId: theme.id });
if (!themeInfo) {
continue;
}
if (!this.compareversion(themeInfo.version, theme.version || '0.0.0') && themeInfo.version != theme.version) {
console.info('ZenThemeMarketplaceParent: Theme update found', theme.id, theme.version, themeInfo.version);
themeInfo.enabled = theme.enabled;
updates.push(themeInfo);
await this.removeTheme(theme.id, false);
this._themes[themeInfo.id] = themeInfo;
}
}
await this.updateThemes(this._themes);
this.sendAsyncMessage('ZenThemeMarketplace:CheckForUpdatesFinished', { updates });
}
async updateChildProcesses(themeId) {
this.sendAsyncMessage('ZenThemeMarketplace:ThemeChanged', { themeId });
}
async getThemes() {
if (!this._themes) {
if (!(await IOUtils.exists(this.themesDataFile))) {
await IOUtils.writeJSON(this.themesDataFile, {});
}
this._themes = await IOUtils.readJSON(this.themesDataFile);
}
return this._themes;
}
async updateThemes(themes) {
this._themes = themes;
await IOUtils.writeJSON(this.themesDataFile, themes);
await this.checkForThemeChanges();
}
getStyleSheetFullContent(style = '') {
let stylesheet = '@-moz-document url-prefix("chrome:") {';
for (const line of style.split('\n')) {
stylesheet += ` ${line}`;
}
stylesheet += '}';
return stylesheet;
}
async downloadUrlToFile(url, path, isStyleSheet = false) {
try {
const response = await fetch(url);
const data = await response.text();
const content = isStyleSheet ? this.getStyleSheetFullContent(data) : data;
// convert the data into a Uint8Array
let buffer = new TextEncoder().encode(content);
await IOUtils.write(path, buffer);
} catch (e) {
console.error('ZenThemeMarketplaceParent: Error downloading file', url, e);
}
}
async downloadThemeFileContents(theme) {
const themePath = PathUtils.join(this.themesRootPath, theme.id);
await IOUtils.makeDirectory(themePath, { ignoreExisting: true });
await this.downloadUrlToFile(theme.style, PathUtils.join(themePath, 'chrome.css'), true);
await this.downloadUrlToFile(theme.readme, PathUtils.join(themePath, 'readme.md'));
if (theme.preferences) {
await this.downloadUrlToFile(theme.preferences, PathUtils.join(themePath, 'preferences.json'));
}
}
get themesRootPath() {
return PathUtils.join(PathUtils.profileDir, 'chrome', 'zen-themes');
}
get themesDataFile() {
return PathUtils.join(PathUtils.profileDir, 'zen-themes.json');
}
triggerThemeUpdate() {
const pref = 'zen.themes.updated-value-observer';
Services.prefs.setBoolPref(pref, !Services.prefs.getBoolPref(pref));
}
async installTheme(theme) {
await this.downloadThemeFileContents(theme);
}
async checkForThemeChanges() {
const themes = await this.getThemes();
const themeIds = Object.keys(themes);
let changed = false;
for (const themeId of themeIds) {
const theme = themes[themeId];
if (!theme) {
continue;
}
const themePath = PathUtils.join(this.themesRootPath, themeId);
if (!(await IOUtils.exists(themePath))) {
await this.installTheme(theme);
changed = true;
}
}
if (changed) {
this.triggerThemeUpdate();
}
}
async removeTheme(themeId, triggerUpdate = true) {
const themePath = PathUtils.join(this.themesRootPath, themeId);
await IOUtils.remove(themePath, { recursive: true, ignoreAbsent: true });
if (triggerUpdate) {
this.triggerThemeUpdate();
}
}
}

View File

@@ -1,13 +1,13 @@
diff --git a/browser/components/controlcenter/content/identityPanel.inc.xhtml b/browser/components/controlcenter/content/identityPanel.inc.xhtml
index 0344fcb566e8e0bedd92b0d45e42aea38de5c329..0d5296700590dd0c8a90929976b8cbc5370e7b47 100644
index 768768c7d44860e1725f41594da9bcc8c92f4a1d..c04b0d54a41fbf8a22b516524809186d29842b21 100644
--- a/browser/components/controlcenter/content/identityPanel.inc.xhtml
+++ b/browser/components/controlcenter/content/identityPanel.inc.xhtml
@@ -30,7 +30,7 @@
@@ -28,7 +28,7 @@
<toolbarseparator/>
<vbox class="panel-subview-body">
<toolbarbutton id="identity-popup-security-button"
- class="identity-popup-security-connection subviewbutton subviewbutton-iconic"
+ class="identity-popup-security-connection subviewbutton"
oncommand="gIdentityHandler.showSecuritySubView();">
- class="identity-popup-security-connection subviewbutton subviewbutton-iconic">
+ class="identity-popup-security-connection subviewbutton">
<image class="identity-popup-security-connection-icon toolbarbutton-icon"></image>
<hbox class="toolbarbutton-text" flex="1">
<label class="identity-popup-connection-not-secure"

View File

@@ -1,5 +1,5 @@
diff --git a/browser/components/customizableui/CustomizableUI.sys.mjs b/browser/components/customizableui/CustomizableUI.sys.mjs
index 289c3df7dcd6fa6b35681ce61c1e920b6a2651d7..a31dc07c8b756e09f43cd5f81bad8d4a3c5f7fce 100644
index 989e69245aeb1185125752db6b9c58e462d554e4..a4825653b48aa5012e58a277c862caaf0e37abab 100644
--- a/browser/components/customizableui/CustomizableUI.sys.mjs
+++ b/browser/components/customizableui/CustomizableUI.sys.mjs
@@ -13,6 +13,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
@@ -19,12 +19,13 @@ index 289c3df7dcd6fa6b35681ce61c1e920b6a2651d7..a31dc07c8b756e09f43cd5f81bad8d4a
const kSpecialWidgetPfx = "customizableui-special-";
@@ -253,13 +254,14 @@ var CustomizableUIInternal = {
@@ -323,13 +324,14 @@ var CustomizableUIInternal = {
"spring",
"urlbar-container",
"spring",
- "save-to-pocket-button",
"downloads-button",
- "downloads-button",
+// "downloads-button",
AppConstants.MOZ_DEV_EDITION ? "developer-button" : null,
- "fxa-toolbar-menu-button",
+ "wrapper-sidebar-button",
@@ -36,7 +37,16 @@ index 289c3df7dcd6fa6b35681ce61c1e920b6a2651d7..a31dc07c8b756e09f43cd5f81bad8d4a
this.registerArea(
CustomizableUI.AREA_NAVBAR,
{
@@ -288,10 +290,10 @@ var CustomizableUIInternal = {
@@ -338,7 +340,7 @@ var CustomizableUIInternal = {
defaultPlacements: navbarPlacements,
verticalTabsDefaultPlacements: [
"firefox-view-button",
- "new-tab-button",
+// "new-tab-button",
"alltabs-button",
],
defaultCollapsed: false,
@@ -363,10 +365,10 @@ var CustomizableUIInternal = {
{
type: CustomizableUI.TYPE_TOOLBAR,
defaultPlacements: [
@@ -48,9 +58,9 @@ index 289c3df7dcd6fa6b35681ce61c1e920b6a2651d7..a31dc07c8b756e09f43cd5f81bad8d4a
+// "new-tab-button",
+// "alltabs-button",
],
verticalTabsDefaultPlacements: [],
defaultCollapsed: null,
},
@@ -331,6 +333,7 @@ var CustomizableUIInternal = {
@@ -429,6 +431,7 @@ var CustomizableUIInternal = {
CustomizableUI.AREA_NAVBAR,
CustomizableUI.AREA_BOOKMARKS,
CustomizableUI.AREA_TABSTRIP,
@@ -58,7 +68,7 @@ index 289c3df7dcd6fa6b35681ce61c1e920b6a2651d7..a31dc07c8b756e09f43cd5f81bad8d4a
]);
if (AppConstants.platform != "macosx") {
toolbars.add(CustomizableUI.AREA_MENUBAR);
@@ -1033,6 +1036,9 @@ var CustomizableUIInternal = {
@@ -1144,6 +1147,9 @@ var CustomizableUIInternal = {
placements = gPlacements.get(area);
}
@@ -68,7 +78,7 @@ index 289c3df7dcd6fa6b35681ce61c1e920b6a2651d7..a31dc07c8b756e09f43cd5f81bad8d4a
// For toolbars that need it, mark as dirty.
let defaultPlacements = areaProperties.get("defaultPlacements");
if (
@@ -3289,6 +3295,9 @@ var CustomizableUIInternal = {
@@ -3521,6 +3527,9 @@ var CustomizableUIInternal = {
gSeenWidgets.add(widgetId);
}
}

View File

@@ -0,0 +1,30 @@
diff --git a/browser/components/places/content/browserPlacesViews.js b/browser/components/places/content/browserPlacesViews.js
index 1bfa0af16178c9b42172bc1b1e0249d28ff8e9e6..e7e76a6d548b32887c1d39053e42c5e3dafbb839 100644
--- a/browser/components/places/content/browserPlacesViews.js
+++ b/browser/components/places/content/browserPlacesViews.js
@@ -393,6 +393,7 @@ class PlacesViewBase {
"scheme",
PlacesUIUtils.guessUrlSchemeForUI(aPlacesNode.uri)
);
+ element.addEventListener("command", gZenGlanceManager.openGlanceForBookmark.bind(gZenGlanceManager));
} else if (PlacesUtils.containerTypes.includes(type)) {
element = document.createXULElement("menu");
element.setAttribute("container", "true");
@@ -1087,6 +1088,8 @@ class PlacesToolbar extends PlacesViewBase {
"scheme",
PlacesUIUtils.guessUrlSchemeForUI(aChild.uri)
);
+ button.hidden = ZenWorkspaces.isBookmarkInAnotherWorkspace(aChild);
+ button.addEventListener("command", gZenGlanceManager.openGlanceForBookmark.bind(gZenGlanceManager));
}
}
@@ -2235,7 +2238,7 @@ this.PlacesPanelview = class PlacesPanelview extends PlacesViewBase {
PlacesUIUtils.guessUrlSchemeForUI(placesNode.uri)
);
element.setAttribute("label", PlacesUIUtils.getBestTitle(placesNode));
-
+ element.addEventListener("command", gZenGlanceManager.openGlanceForBookmark.bind(gZenGlanceManager));
let icon = placesNode.icon;
if (icon) {
element.setAttribute("image", icon);

View File

@@ -645,6 +645,7 @@ var gZenLooksAndFeel = {
colorElemParen.setAttribute('selected', 'true');
}
colorElemParen.addEventListener('click', () => {
Services.prefs.setBoolPref('zen.theme.color-prefs.use-workspace-colors', false);
Services.prefs.setStringPref('zen.theme.accent-color', color);
});
colorElemParen.appendChild(colorElem);
@@ -674,9 +675,13 @@ var gZenWorkspacesSettings = {
};
Services.prefs.addObserver('zen.workspaces.enabled', this);
Services.prefs.addObserver('zen.tab-unloader.enabled', tabsUnloaderPrefListener);
Services.prefs.addObserver('zen.glance.enabled', tabsUnloaderPrefListener); // We can use the same listener for both prefs
Services.prefs.addObserver('zen.glance.activation-method', tabsUnloaderPrefListener);
window.addEventListener('unload', () => {
Services.prefs.removeObserver('zen.workspaces.enabled', this);
Services.prefs.removeObserver('zen.tab-unloader.enabled', tabsUnloaderPrefListener);
Services.prefs.removeObserver('zen.glance.enabled', tabsUnloaderPrefListener);
Services.prefs.removeObserver('zen.glance.activation-method', tabsUnloaderPrefListener);
});
},
@@ -965,11 +970,6 @@ var gZenCKSSettings = {
};
Preferences.addAll([
{
id: 'zen.theme.toolbar-themed',
type: 'bool',
default: true,
},
{
id: 'zen.sidebar.enabled',
type: 'bool',
@@ -1074,5 +1074,50 @@ Preferences.addAll([
id: 'zen.tabs.show-newtab-under',
type: 'bool',
default: false,
}
},
{
id: "zen.glance.activation-method",
type: "string",
default: "ctrl",
},
{
id: "zen.glance.enabled",
type: "bool",
default: true,
},
{
id: "zen.theme.color-prefs.use-workspace-colors",
type: "bool",
default: false,
},
{
id: "zen.view.compact.color-toolbar",
type: "bool",
default: true,
},
{
id: "zen.view.compact.color-sidebar",
type: "bool",
default: true,
},
{
id: "zen.essentials.enabled",
type: "bool",
default: true,
},
{
id: "zen.tabs.show-newtab-vertical",
type: "bool",
default: true,
},
{
id: "zen.view.show-newtab-button-border-top",
type: "bool",
default: false,
},
{
id: "zen.view.show-newtab-button-top",
type: "bool",
default: true,
},
]);

View File

@@ -18,9 +18,9 @@
<html:div id="zen-theme-builder-wrapper"></html:div>
<box class="indent">
<checkbox id="zenLooksAndFeelUseThemedToolbar"
data-l10n-id="zen-look-and-feel-themed-toolbar"
preference="zen.theme.toolbar-themed"/>
<checkbox id="zenLooksAndFeelUseWorkspaceColors"
data-l10n-id="zen-look-and-feel-use-workspace-colors"
preference="zen.theme.color-prefs.use-workspace-colors"/>
</box>
<hbox id="zenLooksAndFeelGradientPickerParent"></hbox>
@@ -236,6 +236,12 @@
<checkbox id="zenLooksAndFeelEnableToolbarFlashPopup"
data-l10n-id="zen-look-and-feel-compact-toolbar-flash-popup"
preference="zen.view.compact.toolbar-flash-popup"/>
<checkbox id="zenLooksAndFeelEnableThemedToolbarCompactMode"
data-l10n-id="zen-look-and-feel-compact-toolbar-themed"
preference="zen.view.compact.color-toolbar"/>
<checkbox id="zenLooksAndFeelEnableThemedSidebarCompactMode"
data-l10n-id="zen-look-and-feel-compact-sidebar-themed"
preference="zen.view.compact.color-sidebar"/>
</vbox>
</groupbox>
@@ -260,6 +266,36 @@
</hbox>
</groupbox>
<hbox id="zenGlanceCategory"
class="subcategory"
hidden="true"
data-category="paneZenLooks">
<html:h1 data-l10n-id="zen-glance-title"/>
</hbox>
<groupbox id="zenGlanceGroup" data-category="paneZenLooks" hidden="true" class="highlighting-group">
<label><html:h2 data-l10n-id="zen-glance-header"/></label>
<description class="description-deemphasized" data-l10n-id="zen-glance-description" />
<checkbox id="zenLooksAndFeelGlanceEnabled"
data-l10n-id="zen-glance-enabled"
preference="zen.glance.enabled"/>
<hbox align="center">
<label id="zenGlanceTriggerLabel" data-l10n-id="zen-glance-trigger-label"/>
<menulist id="zenGlanceTrigger" preference="zen.glance.activation-method">
<menupopup>
<menuitem data-l10n-id="zen-glance-trigger-ctrl-click" value="ctrl"/>
<menuitem data-l10n-id="zen-glance-trigger-alt-click" value="alt"/>
<menuitem data-l10n-id="zen-glance-trigger-shift-click" value="shift"/>
#ifdef XP_MACOSX
<menuitem data-l10n-id="zen-glance-trigger-meta-click" value="meta"/>
#endif
<menuitem data-l10n-id="zen-glance-trigger-mantain-click" value="mantain" disabled="true"/>
</menupopup>
</menulist>
</hbox>
</groupbox>
<hbox id="zenSplitViewCategory"
class="subcategory"
hidden="true"
@@ -288,13 +324,19 @@
<description class="description-deemphasized" data-l10n-id="zen-vertical-tabs-description" />
<checkbox id="zenLooksAndFeelNewtabUnderLastTab"
data-l10n-id="zen-vertical-tabs-newtab-under-last-tab"
preference="zen.tabs.show-newtab-under"/>
<hbox class="indent">
data-l10n-id="zen-vertical-tabs-newtab-on-tab-list"
preference="zen.tabs.show-newtab-vertical"/>
<vbox class="indent">
<checkbox id="zenLooksAndFeelShowTopNewtabBorder"
data-l10n-id="zen-vertical-tabs-newtab-top-button-border"
preference="zen.view.show-newtab-button-border-top"/>
<checkbox id="zenLooksAndFeelMoveNewtabToTop"
data-l10n-id="zen-vertical-tabs-newtab-top-button-up"
preference="zen.view.show-newtab-button-top"/>
<checkbox id="zenLooksAndFeelShowBottomBorder"
data-l10n-id="zen-vertical-tabs-show-bottom-border"
preference="zen.view.show-bottom-border"/>
</hbox>
</vbox>
<label><html:h2 data-l10n-id="zen-vertical-tabs-expand-tabs-header"/></label>
<description class="description-deemphasized" data-l10n-id="zen-vertical-tabs-expand-tabs-description" />

View File

@@ -1,12 +1,14 @@
diff --git a/browser/components/sessionstore/TabState.sys.mjs b/browser/components/sessionstore/TabState.sys.mjs
index 26f5671c849d9b0a126d79b07bc7d3d7870826ec..21d63856a06cd46424e1c1a3697b2f3d225d5c08 100644
index 26f5671c849d9b0a126d79b07bc7d3d7870826ec..36b535321f17a4e3dfdfdbb677a4e4b869038154 100644
--- a/browser/components/sessionstore/TabState.sys.mjs
+++ b/browser/components/sessionstore/TabState.sys.mjs
@@ -98,6 +98,11 @@ var TabStateInternal = {
@@ -98,6 +98,13 @@ var TabStateInternal = {
tabData.muteReason = tab.muteReason;
}
+ tabData.zenWorkspace = tab.getAttribute("zen-workspace-id");
+ tabData.zenPinnedId = tab.getAttribute("zen-pin-id");
+ tabData.zenEssential = tab.getAttribute("zen-essential");
+ tabData.zenDefaultUserContextId = tab.getAttribute("zenDefaultUserContextId");
+ tabData.zenPinnedEntry = tab.getAttribute("zen-pinned-entry");
+ tabData.zenPinnedIcon = tab.getAttribute("zen-pinned-icon");

View File

@@ -1,8 +1,8 @@
diff --git a/browser/components/sidebar/browser-sidebar.js b/browser/components/sidebar/browser-sidebar.js
index c68a32794996348d95f3b1ee363be2eec587065e..3a163c75ac10a204754648dfd1332c79895799c7 100644
index 4a124003976684e014435854aef69ce29da541d2..61ce44751c36eea3e5ae2ddcc62e42c01459629b 100644
--- a/browser/components/sidebar/browser-sidebar.js
+++ b/browser/components/sidebar/browser-sidebar.js
@@ -521,7 +521,7 @@ var SidebarController = {
@@ -578,7 +578,7 @@ var SidebarController = {
*/
setPosition() {
// First reset all ordinals to match DOM ordering.
@@ -11,12 +11,11 @@ index c68a32794996348d95f3b1ee363be2eec587065e..3a163c75ac10a204754648dfd1332c79
[...browser.children].forEach((node, i) => {
node.style.order = i + 1;
});
@@ -535,9 +535,11 @@ var SidebarController = {
@@ -592,9 +592,10 @@ var SidebarController = {
let boxOrdinal = this._box.style.order;
this._box.style.order = appcontent.style.order;
this._box.style.order = tabbox.style.order;
- appcontent.style.order = boxOrdinal;
+ // appcontent.style.order = boxOrdinal;
- tabbox.style.order = boxOrdinal;
// the launcher should be on the right of the sidebar-box
- sidebarContainer.style.order = parseInt(this._box.style.order) + 1;
+ this._box.style.order = browser.children.length + 2;
@@ -25,7 +24,7 @@ index c68a32794996348d95f3b1ee363be2eec587065e..3a163c75ac10a204754648dfd1332c79
// Indicate we've switched ordering to the box
this._box.setAttribute("positionend", true);
sidebarMain.setAttribute("positionend", true);
@@ -546,6 +548,9 @@ var SidebarController = {
@@ -603,6 +604,9 @@ var SidebarController = {
this._box.removeAttribute("positionend");
sidebarMain.removeAttribute("positionend");
sidebarContainer.removeAttribute("positionend");

View File

@@ -1,13 +0,0 @@
diff --git a/browser/components/tabbrowser/content/tab-hover-preview.mjs b/browser/components/tabbrowser/content/tab-hover-preview.mjs
index b07dba3e060dd730e603997b2c374f218a1eb591..412d35a263d1e6a789927206a506c184a5ac0d46 100644
--- a/browser/components/tabbrowser/content/tab-hover-preview.mjs
+++ b/browser/components/tabbrowser/content/tab-hover-preview.mjs
@@ -7,7 +7,7 @@ var { XPCOMUtils } = ChromeUtils.importESModule(
);
const POPUP_OPTIONS = {
- position: "bottomleft topleft",
+ position: "topright topleft",
x: 0,
y: -2,
};

View File

@@ -1,8 +1,8 @@
diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js
index c89ae2cbb978d6218bd56a059c5ca1e371231607..58c05bd5c935f73e154e84f66b39f58e5741caf6 100644
index 14de79b543cf07b04d06ef5a3f94d9aa988ea39a..d2cd8709007b6d36b055ffff45b103a55f8656ee 100644
--- a/browser/components/tabbrowser/content/tabbrowser.js
+++ b/browser/components/tabbrowser/content/tabbrowser.js
@@ -456,11 +456,26 @@
@@ -462,11 +462,26 @@
return duplicateTabs;
},
@@ -31,7 +31,34 @@ index c89ae2cbb978d6218bd56a059c5ca1e371231607..58c05bd5c935f73e154e84f66b39f58e
}
return i;
},
@@ -1340,6 +1355,7 @@
@@ -860,7 +875,7 @@
if (this.tabContainer.verticalMode) {
let wasFocused = document.activeElement == this.selectedTab;
let oldPosition = aTab._tPos;
- this.verticalPinnedTabsContainer.appendChild(aTab);
+ aTab.hasAttribute("zen-essential") ? document.getElementById("zen-essentials-container").appendChild(aTab) : this.verticalPinnedTabsContainer.appendChild(aTab);
this._updateAfterMoveTabTo(aTab, oldPosition, wasFocused);
} else {
this.moveTabTo(aTab, this._numPinnedTabs);
@@ -1107,6 +1122,7 @@
let LOCAL_PROTOCOLS = ["chrome:", "about:", "resource:", "data:"];
+ try {
if (
aIconURL &&
!aLoadingPrincipal &&
@@ -1117,6 +1133,9 @@
);
return;
}
+ } catch (e) {
+ console.warn(e);
+ }
let browser = this.getBrowserForTab(aTab);
browser.mIconURL = aIconURL;
@@ -1346,6 +1365,7 @@
if (!this._previewMode) {
newTab.recordTimeFromUnloadToReload();
newTab.updateLastAccessed();
@@ -39,7 +66,25 @@ index c89ae2cbb978d6218bd56a059c5ca1e371231607..58c05bd5c935f73e154e84f66b39f58e
oldTab.updateLastAccessed();
// if this is the foreground window, update the last-seen timestamps.
if (this.ownerGlobal == BrowserWindowTracker.getTopWindow()) {
@@ -2705,6 +2721,11 @@
@@ -2431,7 +2451,7 @@
let panel = this.getPanel(browser);
let uniqueId = this._generateUniquePanelID();
- panel.id = uniqueId;
+ if (!panel.id?.startsWith("zen-")) panel.id = uniqueId;
aTab.linkedPanel = uniqueId;
// Inject the <browser> into the DOM if necessary.
@@ -2491,7 +2511,7 @@
// hasSiblings=false on both the existing browser and the new browser.
if (this.tabs.length == 2) {
this.tabs[0].linkedBrowser.browsingContext.hasSiblings = true;
- this.tabs[1].linkedBrowser.browsingContext.hasSiblings = true;
+ if (this.tabs[1].linkedBrowser.browsingContext) this.tabs[1].linkedBrowser.browsingContext.hasSiblings = true;
} else {
aTab.linkedBrowser.browsingContext.hasSiblings = this.tabs.length > 1;
}
@@ -2711,6 +2731,11 @@
);
}
@@ -51,7 +96,7 @@ index c89ae2cbb978d6218bd56a059c5ca1e371231607..58c05bd5c935f73e154e84f66b39f58e
if (!UserInteraction.running("browser.tabs.opening", window)) {
UserInteraction.start("browser.tabs.opening", "initting", window);
}
@@ -2773,6 +2794,9 @@
@@ -2780,6 +2805,9 @@
noInitialLabel,
skipBackgroundNotify,
});
@@ -61,7 +106,7 @@ index c89ae2cbb978d6218bd56a059c5ca1e371231607..58c05bd5c935f73e154e84f66b39f58e
if (insertTab) {
// insert the tab into the tab container in the correct position
this._insertTabAtIndex(t, {
@@ -3262,6 +3286,14 @@
@@ -3291,6 +3319,23 @@
) {
tabWasReused = true;
tab = this.selectedTab;
@@ -69,14 +114,23 @@ index c89ae2cbb978d6218bd56a059c5ca1e371231607..58c05bd5c935f73e154e84f66b39f58e
+ if (tabData.zenWorkspace) {
+ tab.setAttribute("zen-workspace-id", tabData.zenWorkspace);
+ }
+ if (tabData.zenPinnedId) {
+ tab.setAttribute("zen-pin-id", tabData.zenPinnedId);
+ }
+ if (tabData.zenEssential) {
+ tab.setAttribute("zen-essential", "true");
+ }
+ if (tabData.zenDefaultUserContextId) {
+ tab.setAttribute("zenDefaultUserContextId", "true");
+ }
+ if (tabData.zenPinnedEntry) {
+ tab.setAttribute("zen-pinned-entry", tabData.zenPinnedEntry);
+ }
+
if (!tabData.pinned) {
this.unpinTab(tab);
} else {
@@ -3275,6 +3307,9 @@
@@ -3304,6 +3349,9 @@
restoreTabsLazily && !select && !tabData.pinned;
let url = "about:blank";
@@ -86,29 +140,37 @@ index c89ae2cbb978d6218bd56a059c5ca1e371231607..58c05bd5c935f73e154e84f66b39f58e
if (tabData.entries?.length) {
let activeIndex = (tabData.index || tabData.entries.length) - 1;
// Ensure the index is in bounds.
@@ -3311,6 +3346,12 @@
@@ -3340,6 +3388,21 @@
preferredRemoteType,
});
+ if (tabData.zenWorkspace) {
+ tab.setAttribute("zen-workspace-id", tabData.zenWorkspace);
+ }
+ if (tabData.zenPinnedId) {
+ tab.setAttribute("zen-pin-id", tabData.zenPinnedId);
+ }
+ if (tabData.zenEssential) {
+ tab.setAttribute("zen-essential", "true");
+ }
+ if (tabData.zenDefaultUserContextId) {
+ tab.setAttribute("zenDefaultUserContextId", "true");
+ }
+ if (tabData.zenPinnedEntry) {
+ tab.setAttribute("zen-pinned-entry", tabData.zenPinnedEntry);
+ }
if (select) {
tabToSelect = tab;
}
@@ -3345,7 +3386,7 @@
@@ -3374,7 +3437,6 @@
this.tabContainer._invalidateCachedTabs();
}
}
-
+ gZenPinnedTabManager.updatePinnedTabForSessionRestore(tabData, tab);
tab.initialize();
}
@@ -4152,6 +4193,13 @@
@@ -4148,6 +4210,13 @@
TelemetryStopwatch.start("FX_TAB_CLOSE_TIME_NO_ANIM_MS", aTab);
}
@@ -122,15 +184,27 @@ index c89ae2cbb978d6218bd56a059c5ca1e371231607..58c05bd5c935f73e154e84f66b39f58e
// Handle requests for synchronously removing an already
// asynchronously closing tab.
if (!animate && aTab.closing) {
@@ -4198,6 +4246,7 @@
isLastTab ||
aTab.pinned ||
aTab.hidden ||
+ true ||
this._removingTabs.size >
3 /* don't want lots of concurrent animations */ ||
!aTab.hasAttribute(
@@ -5131,10 +5180,10 @@
@@ -4163,6 +4232,10 @@
// state).
let tabWidth = window.windowUtils.getBoundsWithoutFlushing(aTab).width;
+ if (aTab.hasAttribute("zen-glance-tab")) {
+ gZenGlanceManager.closeGlance();
+ return;
+ }
if (
!this._beginRemoveTab(aTab, {
closeWindowFastpath: true,
@@ -4311,7 +4384,7 @@
var closeWindow = false;
var newTab = false;
- if (aTab.visible && this.visibleTabs.length == 1) {
+ if (aTab.visible && this.visibleTabs.length == 1 && !aTab._closingGlance) {
closeWindow =
closeWindowWithLastTab != null
? closeWindowWithLastTab
@@ -5123,10 +5196,10 @@
SessionStore.deleteCustomTabValue(aTab, "hiddenBy");
},
@@ -143,7 +217,7 @@ index c89ae2cbb978d6218bd56a059c5ca1e371231607..58c05bd5c935f73e154e84f66b39f58e
aTab.selected ||
aTab.closing ||
// Tabs that are sharing the screen, microphone or camera cannot be hidden.
@@ -7050,6 +7099,7 @@
@@ -7042,6 +7115,7 @@
aWebProgress.isTopLevel
) {
this.mTab.setAttribute("busy", "true");
@@ -151,7 +225,7 @@ index c89ae2cbb978d6218bd56a059c5ca1e371231607..58c05bd5c935f73e154e84f66b39f58e
gBrowser._tabAttrModified(this.mTab, ["busy"]);
this.mTab._notselectedsinceload = !this.mTab.selected;
gBrowser.syncThrobberAnimations(this.mTab);
@@ -7870,7 +7920,7 @@ var TabContextMenu = {
@@ -7874,7 +7948,7 @@ var TabContextMenu = {
);
contextUnpinSelectedTabs.hidden =
!this.contextTab.pinned || !multiselectionContext;

View File

@@ -1,8 +1,17 @@
diff --git a/browser/components/tabbrowser/content/tabs.js b/browser/components/tabbrowser/content/tabs.js
index 137f1a3854f10aca0d0e3233a82c1c18fbdc2823..419e0ad674b76454e6e3e1ed4ae1a85488137940 100644
index f3a2f226a9056c5a75023281fdeb704cec49b4a6..39c46b141145864fb3b8043d2a9865299561a6cc 100644
--- a/browser/components/tabbrowser/content/tabs.js
+++ b/browser/components/tabbrowser/content/tabs.js
@@ -840,7 +840,7 @@
@@ -894,7 +894,7 @@
let postTransitionCleanup = () => {
tab.removeAttribute("tabdrop-samewindow");
- this._finishAnimateTabMove();
+ this._finishAnimateTabMove(true);
if (dropIndex !== false) {
gBrowser.moveTabTo(tab, dropIndex);
if (incrementDropIndex) {
@@ -904,7 +904,7 @@
gBrowser.syncThrobberAnimations(tab);
};
@@ -11,7 +20,16 @@ index 137f1a3854f10aca0d0e3233a82c1c18fbdc2823..419e0ad674b76454e6e3e1ed4ae1a854
postTransitionCleanup();
} else {
let onTransitionEnd = transitionendEvent => {
@@ -1411,7 +1411,7 @@
@@ -1284,7 +1284,7 @@
}
}
- let allChildren = [...verticalPinnedTabsContainer.children, ...children];
+ let allChildren = [...document.getElementById("zen-essentials-container").children, ...verticalPinnedTabsContainer.children, ...children];
this._allTabs = allChildren;
return allChildren;
}
@@ -1480,7 +1480,7 @@
let rect = ele => {
return window.windowUtils.getBoundsWithoutFlushing(ele);
};
@@ -20,11 +38,11 @@ index 137f1a3854f10aca0d0e3233a82c1c18fbdc2823..419e0ad674b76454e6e3e1ed4ae1a854
if (tab && rect(tab).width <= this._tabClipWidth) {
this.setAttribute("closebuttons", "activetab");
} else {
@@ -1430,10 +1430,12 @@
@@ -1499,10 +1499,12 @@
_handleTabSelect(aInstant) {
let selectedTab = this.selectedItem;
- if (this.hasAttribute("overflow")) {
- if (this.overflowing) {
- this.arrowScrollbox.ensureElementIsVisible(selectedTab, aInstant);
- }
+ this.arrowScrollbox.ensureElementIsVisible(selectedTab, aInstant);
@@ -36,7 +54,7 @@ index 137f1a3854f10aca0d0e3233a82c1c18fbdc2823..419e0ad674b76454e6e3e1ed4ae1a854
selectedTab._notselectedsinceload = false;
}
@@ -1477,7 +1479,7 @@
@@ -1550,7 +1552,7 @@
if (isEndTab && !this._hasTabTempMaxWidth) {
return;
}
@@ -45,7 +63,7 @@ index 137f1a3854f10aca0d0e3233a82c1c18fbdc2823..419e0ad674b76454e6e3e1ed4ae1a854
// Force tabs to stay the same width, unless we're closing the last tab,
// which case we need to let them expand just enough so that the overall
// tabbar width is the same.
@@ -1492,7 +1494,7 @@
@@ -1565,7 +1567,7 @@
let tabsToReset = [];
for (let i = numPinned; i < tabs.length; i++) {
let tab = tabs[i];
@@ -54,10 +72,10 @@ index 137f1a3854f10aca0d0e3233a82c1c18fbdc2823..419e0ad674b76454e6e3e1ed4ae1a854
if (!isEndTab) {
// keep tabs the same width
tab.style.transition = "none";
@@ -1558,9 +1560,9 @@
@@ -1630,13 +1632,13 @@
let verticalTabsContainer = document.getElementById(
"vertical-pinned-tabs-container"
);
let newTabButton = document.getElementById("newtab-button-container");
- let numPinned = gBrowser._numPinnedTabs;
+ let numPinned = gBrowser._numVisiblePinTabs;
@@ -66,25 +84,21 @@ index 137f1a3854f10aca0d0e3233a82c1c18fbdc2823..419e0ad674b76454e6e3e1ed4ae1a854
let tabs = this._getVisibleTabs();
for (let i = 0; i < numPinned; i++) {
tabs[i].style.marginInlineStart = "";
@@ -1568,7 +1570,7 @@
- verticalTabsContainer.appendChild(tabs[i]);
+ tabs[i].hasAttribute("zen-essential") ? document.getElementById("zen-essentials-container").appendChild(tabs[i]) : verticalTabsContainer.appendChild(tabs[i]);
}
}
- newTabButton.toggleAttribute("showborder", gBrowser._numPinnedTabs !== 0);
+ newTabButton.toggleAttribute("showborder", gBrowser._numVisiblePinTabs !== 0);
this.style.removeProperty("--tab-overflow-pinned-tabs-width");
}
@@ -1589,7 +1591,7 @@
@@ -1660,7 +1662,7 @@
_positionPinnedTabs() {
let tabs = this._getVisibleTabs();
- let numPinned = gBrowser._numPinnedTabs;
+ let numPinned = gBrowser._numVisiblePinTabs;
let absPositionHorizontalTabs =
this.hasAttribute("overflow") &&
tabs.length > numPinned &&
@@ -1672,7 +1674,7 @@
this.overflowing && tabs.length > numPinned && numPinned > 0;
@@ -1934,7 +1936,7 @@
}
let pinned = draggedTab.pinned;
@@ -93,7 +107,18 @@ index 137f1a3854f10aca0d0e3233a82c1c18fbdc2823..419e0ad674b76454e6e3e1ed4ae1a854
let tabs = this._getVisibleTabs().slice(
pinned ? 0 : numPinned,
pinned ? numPinned : undefined
@@ -1949,9 +1951,9 @@
@@ -2059,8 +2061,8 @@
}
}
- _finishAnimateTabMove() {
- if (!this.hasAttribute("movingtab")) {
+ _finishAnimateTabMove(always = false) {
+ if (!this.hasAttribute("movingtab") && !always) {
return;
}
@@ -2218,9 +2220,9 @@
function newIndex(aTab, index) {
// Don't allow mixing pinned and unpinned tabs.
if (aTab.pinned) {

View File

@@ -1,68 +0,0 @@
diff --git a/browser/components/tabbrowser/AsyncTabSwitcher.sys.mjs b/browser/components/tabbrowser/AsyncTabSwitcher.sys.mjs
index 9f4aa535e07adab496788165f4089be6732b1444..7b7955c0d1f2f3277cb750652458649d181e90e5 100644
--- a/browser/components/tabbrowser/AsyncTabSwitcher.sys.mjs
+++ b/browser/components/tabbrowser/AsyncTabSwitcher.sys.mjs
@@ -39,6 +39,10 @@ XPCOMUtils.defineLazyPreferenceGetter(
300
);
+function zenSplitViewIsEnabled() {
+ return Services.prefs.getBoolPref("zen.splitView.working", false);
+}
+
/**
* The tab switcher is responsible for asynchronously switching
* tabs in e10s. It waits until the new tab is ready (i.e., the
@@ -279,7 +283,7 @@ export class AsyncTabSwitcher {
browser.docShellIsActive = true;
}
- if (remoteTab) {
+ if (remoteTab && !zenSplitViewIsEnabled()) {
browser.renderLayers = true;
remoteTab.priorityHint = true;
}
@@ -291,7 +295,7 @@ export class AsyncTabSwitcher {
// Setting the docShell to be inactive will also cause it
// to stop rendering layers.
browser.docShellIsActive = false;
- if (remoteTab) {
+ if (remoteTab && !zenSplitViewIsEnabled()) {
remoteTab.priorityHint = false;
}
if (!browser.hasLayers) {
@@ -364,7 +368,7 @@ export class AsyncTabSwitcher {
// constructing BrowserChild's, layer trees, etc, by showing a blank
// tab instead and focusing it immediately.
let shouldBeBlank = false;
- if (requestedBrowser.isRemoteBrowser) {
+ if (requestedBrowser.isRemoteBrowser && !zenSplitViewIsEnabled()) {
// If a tab is remote and the window is not minimized, we can show a
// blank tab instead of a spinner in the following cases:
//
@@ -399,7 +403,7 @@ export class AsyncTabSwitcher {
}
}
- if (requestedBrowser.isRemoteBrowser) {
+ if (requestedBrowser.isRemoteBrowser && !zenSplitViewIsEnabled()) {
this.addLogFlag("isRemote");
}
@@ -825,7 +829,7 @@ export class AsyncTabSwitcher {
`onRemotenessChange(${tab._tPos}, ${tab.linkedBrowser.isRemoteBrowser})`
);
if (!tab.linkedBrowser.isRemoteBrowser) {
- if (this.getTabState(tab) == this.STATE_LOADING) {
+ if (this.getTabState(tab) == this.STATE_LOADING && !zenSplitViewIsEnabled()) {
this.onLayersReady(tab.linkedBrowser);
} else if (this.getTabState(tab) == this.STATE_UNLOADING) {
this.onLayersCleared(tab.linkedBrowser);
@@ -1018,6 +1022,7 @@ export class AsyncTabSwitcher {
lazy.gTabCacheSize > 1 &&
tab.linkedBrowser.isRemoteBrowser &&
tab.linkedBrowser.currentURI.spec != "about:blank"
+ && !zenSplitViewIsEnabled()
) {
let tabIndex = this.tabLayerCache.indexOf(tab);

View File

@@ -1,21 +1,8 @@
diff --git a/browser/themes/BuiltInThemeConfig.sys.mjs b/browser/themes/BuiltInThemeConfig.sys.mjs
index 7cfea4b705a1338b1eb5c4f255808aeac4bdb819..cf7dc720a3bd2b14535d57262ad2297f9478eddc 100644
index f738f7f1e37d20d9d9158f052572334714475107..f4b0e9a7077076f2fbe8ac17fa6aadf586c6b37c 100644
--- a/browser/themes/BuiltInThemeConfig.sys.mjs
+++ b/browser/themes/BuiltInThemeConfig.sys.mjs
@@ -22,24 +22,17 @@ export const BuiltInThemeConfig = new Map([
[
"firefox-compact-light@mozilla.org",
{
- version: "1.2",
+ version: "1.3",
path: "resource://builtin-themes/light/",
},
],
[
"firefox-compact-dark@mozilla.org",
{
- version: "1.2",
+ version: "1.3",
@@ -33,13 +33,6 @@ export const BuiltInThemeConfig = new Map([
path: "resource://builtin-themes/dark/",
},
],

View File

@@ -11,15 +11,6 @@ index c2d5dd7a18895ae8b4afbf386f122e7899c48cda..377d904bbf5adc92bf7cb0aa4c4c6417
);
let activeBuiltInTheme = this.builtInThemeMap.get(activeThemeID);
@@ -82,7 +82,7 @@ class _BuiltInThemes {
lazy.AddonManager.maybeInstallBuiltinAddon(
activeThemeID,
activeBuiltInTheme.version,
- `resource://builtin-themes/${activeBuiltInTheme.path}`
+ `${activeBuiltInTheme.path}`
);
}
}
@@ -166,7 +166,7 @@ class _BuiltInThemes {
async _uninstallExpiredThemes() {
const activeThemeID = Services.prefs.getStringPref(

View File

@@ -10,9 +10,8 @@ index bfebde683d00b0acf26509139a0f662029d37c72..976b553f49e40b7ed02743d79ccc102f
+ "name": "Zen Dark",
"description": "A theme with a dark color scheme.",
- "author": "Mozilla",
- "version": "1.2",
+ "author": "Zen",
+ "version": "1.3",
"version": "1.3.2",
"icons": { "32": "icon.svg" },

View File

@@ -10,9 +10,8 @@ index d490f8f08d203ded55b65fe1a19be105b61ee6c0..eb4349e939b979b3a4d7c525ee0c0f9a
+ "name": "Zen Light",
"description": "A theme with a light color scheme.",
- "author": "Mozilla",
- "version": "1.2",
+ "author": "Zen",
+ "version": "1.3",
"version": "1.3",
"icons": { "32": "icon.svg" },

View File

@@ -1,31 +0,0 @@
diff --git a/browser/themes/shared/browser-custom-colors.css b/browser/themes/shared/browser-custom-colors.css
index 23187312740d440b3cd4b376a06cb2ab2e885604..ac9aafc0ceed04364ae5b468cf7d2b2843aa7fe5 100644
--- a/browser/themes/shared/browser-custom-colors.css
+++ b/browser/themes/shared/browser-custom-colors.css
@@ -6,7 +6,7 @@
@media not (prefers-contrast) {
:root:not([lwtheme]) {
- --button-primary-bgcolor: light-dark(rgb(0, 97, 224), rgb(0, 221, 255));
+ --button-primary-bgcolor: light-dark(#000, #fff);
--button-primary-hover-bgcolor: light-dark(rgb(2, 80, 187), rgb(128, 235, 255));
--button-primary-active-bgcolor: light-dark(rgb(5, 62, 148), rgb(170, 242, 255));
--button-primary-color: light-dark(rgb(251, 251, 254), rgb(43, 42, 51));
@@ -15,7 +15,7 @@
rgba(0, 0, 0, .33)
);
--button-hover-bgcolor: light-dark(
- rgba(207, 207, 216, .66),
+ rgba(228, 228, 235, 0.66),
rgba(207, 207, 216, .20)
);
--button-active-bgcolor: light-dark(
@@ -49,7 +49,7 @@
--tab-icon-overlay-fill: light-dark(rgb(91, 91, 102), rgb(251, 251, 254));
--tabs-navbar-separator-style: none;
- --toolbox-non-lwt-bgcolor: light-dark(rgb(240, 240, 244), rgb(28, 27, 34));
+ --toolbox-non-lwt-bgcolor: light-dark(rgb(210, 210, 213), rgb(28, 27, 34));
--toolbox-non-lwt-textcolor: light-dark(rgb(21, 20, 26), rgb(251, 251, 254));
--toolbox-non-lwt-bgcolor-inactive: var(--toolbox-non-lwt-bgcolor);
--toolbox-non-lwt-textcolor-inactive: var(--toolbox-non-lwt-textcolor);

View File

@@ -2,14 +2,6 @@ diff --git a/browser/themes/shared/browser-shared.css b/browser/themes/shared/br
index f2171eb033a1143870f4708c63f565fabb535c4b..4280bc4b0f7cdbd94179fa2111f8001a331ea42b 100644
--- a/browser/themes/shared/browser-shared.css
+++ b/browser/themes/shared/browser-shared.css
@@ -179,7 +179,6 @@ body {
appearance: none;
/* Toolbar / content area border */
- border-bottom: 0.01px solid var(--chrome-content-separator-color);
background-color: var(--toolbox-non-lwt-bgcolor);
color: var(--toolbox-non-lwt-textcolor);
@@ -188,7 +187,6 @@ body {
transition: background-color var(--inactive-window-transition);

View File

@@ -3,9 +3,9 @@ index 9f7331f2f00a8b0de6ce28a7029358a86eeb8873..2dc56a8455df9009bd1f524b377b8fdf
--- a/browser/themes/shared/tabbrowser/tabs.css
+++ b/browser/themes/shared/tabbrowser/tabs.css
@@ -15,7 +15,7 @@
--tab-border-radius: 4px;
--tab-shadow-max-size: 6px;
--tab-block-margin: 4px;
--tab-icon-end-margin: 5.5px;
--tab-icon-overlay-fill: light-dark(black, white);
--tab-icon-overlay-stroke: light-dark(white, black);
- --tab-loading-fill: #0A84FF;
+ --tab-loading-fill: var(--zen-primary-color);
--tab-hover-background-color: color-mix(in srgb, currentColor 11%, transparent);
@@ -24,8 +24,8 @@ index 9f7331f2f00a8b0de6ce28a7029358a86eeb8873..2dc56a8455df9009bd1f524b377b8fdf
@@ -952,7 +951,7 @@ sidebar-main[expanded] > #vertical-tabs > #tabbrowser-tabs[orient="vertical"] >
toolbarbutton:not(#firefox-view-button),
toolbarpaletteitem:not(#wrapper-firefox-view-button)
) + #tabbrowser-tabs {
- border-inline-start: 1px solid color-mix(in srgb, currentColor 25%, transparent);
) ~ #tabbrowser-tabs {
- border-inline-start: var(--tabstrip-inner-border);
+ border-inline-start: transparent;
padding-inline-start: calc(var(--tab-overflow-pinned-tabs-width) + 2px);
margin-inline-start: 2px;
@@ -35,6 +35,6 @@ index 9f7331f2f00a8b0de6ce28a7029358a86eeb8873..2dc56a8455df9009bd1f524b377b8fdf
}
-#tabbrowser-tabs[hasadjacentnewtabbutton]:not([overflow]) ~ #new-tab-button,
#tabbrowser-tabs[orient="vertical"] > #tabbrowser-arrowscrollbox > #tabbrowser-arrowscrollbox-periphery > #tabs-newtab-button,
#tabbrowser-tabs[overflow] > #tabbrowser-arrowscrollbox > #tabbrowser-arrowscrollbox-periphery > #tabs-newtab-button,
#tabbrowser-tabs:not([hasadjacentnewtabbutton]) > #tabbrowser-arrowscrollbox > #tabbrowser-arrowscrollbox-periphery > #tabs-newtab-button,
#tabbrowser-tabs[orient="horizontal"] > #vertical-tabs-newtab-button,
#tabbrowser-tabs[orient="vertical"]:not([overflow]) > #vertical-tabs-newtab-button,
#tabbrowser-arrowscrollbox[overflowing] > #tabbrowser-arrowscrollbox-periphery > #tabs-newtab-button,

View File

@@ -0,0 +1 @@
<?xml version="1.0" encoding="utf-8"?><svg version="1.1" xmlns="http://www.w3.org/2000/svg" x="0px" y="0px" viewBox="0 0 122.88 109.57" fill="context-fill" fill-opacity="context-fill-opacity" xml:space="preserve"><g><path d="M65.46,19.57c-0.68,0.72-1.36,1.45-2.2,2.32l-2.31,2.41l-2.4-2.33c-0.71-0.69-1.43-1.4-2.13-2.09 c-7.42-7.3-13.01-12.8-24.52-12.95c-0.45-0.01-0.93,0-1.43,0.02c-6.44,0.23-12.38,2.6-16.72,6.65c-4.28,4-7.01,9.67-7.1,16.57 c-0.01,0.43,0,0.88,0.02,1.37c0.69,19.27,19.13,36.08,34.42,50.01c2.95,2.69,5.78,5.27,8.49,7.88l11.26,10.85l14.15-14.04 c2.28-2.26,4.86-4.73,7.62-7.37c4.69-4.5,9.91-9.49,14.77-14.52c3.49-3.61,6.8-7.24,9.61-10.73c2.76-3.42,5.02-6.67,6.47-9.57 c2.38-4.76,3.13-9.52,2.62-13.97c-0.5-4.39-2.23-8.49-4.82-11.99c-2.63-3.55-6.13-6.49-10.14-8.5C96.5,7.29,91.21,6.2,85.8,6.82 C76.47,7.9,71.5,13.17,65.46,19.57L65.46,19.57z M60.77,14.85C67.67,7.54,73.4,1.55,85.04,0.22c6.72-0.77,13.3,0.57,19.03,3.45 c4.95,2.48,9.27,6.1,12.51,10.47c3.27,4.42,5.46,9.61,6.1,15.19c0.65,5.66-0.29,11.69-3.3,17.69c-1.7,3.39-4.22,7.03-7.23,10.76 c-2.95,3.66-6.39,7.44-10,11.17C97.2,74.08,91.94,79.12,87.2,83.66c-2.77,2.65-5.36,5.13-7.54,7.29L63.2,107.28l-2.31,2.29 l-2.34-2.25l-13.6-13.1c-2.49-2.39-5.37-5.02-8.36-7.75C20.38,71.68,0.81,53.85,0.02,31.77C0,31.23,0,30.67,0,30.09 c0.12-8.86,3.66-16.18,9.21-21.36c5.5-5.13,12.97-8.13,21.01-8.42c0.55-0.02,1.13-0.03,1.74-0.02C46,0.48,52.42,6.63,60.77,14.85 L60.77,14.85z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1 @@
<svg data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 122.88 109.57" fill="context-fill" fill-opacity="context-fill-opacity"><defs><style>.cls-1{fill-rule:evenodd;}</style></defs><path class="cls-1" d="M94.26,44.46A26.88,26.88,0,1,1,67.38,71.34,26.88,26.88,0,0,1,94.26,44.46ZM65.45,19.57l-4.52,4.74-4.52-4.43C49,12.58,43.4,7.08,31.89,6.93L30.46,7A25.75,25.75,0,0,0,13.74,13.6,22.58,22.58,0,0,0,6.63,30.17l0,1.37c.69,19.27,19.13,36.08,34.42,50,3,2.69,5.78,5.27,8.49,7.88l11.27,10.85,5.91-5.87c.68.8,1.39,1.58,2.13,2.32l0,0c.82.83,1.69,1.61,2.59,2.36l-10.59,10.5L45,94.22c-2.49-2.39-5.37-5-8.37-7.75C20.37,71.68.81,53.85,0,31.77l0-1.68A29.12,29.12,0,0,1,9.21,8.73a32.42,32.42,0,0,1,21-8.42H32c14,.18,20.45,6.34,28.8,14.55C67.66,7.54,73.39,1.55,85,.21a33.88,33.88,0,0,1,19,3.45,35.14,35.14,0,0,1,12.51,10.48,31.53,31.53,0,0,1,6.1,15.18,31,31,0,0,1-2.86,16.81l-.17-.18a36.18,36.18,0,0,0-5.14-4.27,24.26,24.26,0,0,0,1.57-11.6,25,25,0,0,0-4.83-12,28.62,28.62,0,0,0-10.14-8.5A27.27,27.27,0,0,0,85.79,6.82C76.47,7.89,71.5,13.17,65.45,19.57ZM106.15,68a4,4,0,0,1,1.51.3,3.94,3.94,0,0,1,1.29.86,4,4,0,0,1,.85,1.28l0,.06a4,4,0,0,1,.28,1.38V72a3.82,3.82,0,0,1-.3,1.44,4,4,0,0,1-.86,1.29,3.88,3.88,0,0,1-1.27.85l-.07,0a4.23,4.23,0,0,1-1.43.28H82.37a3.85,3.85,0,0,1-1.51-.31l-.06,0a3.92,3.92,0,0,1-1.23-.83,3.87,3.87,0,0,1-.85-1.28l0-.07A4.23,4.23,0,0,1,78.41,72v0a3.8,3.8,0,0,1,.3-1.51A3.95,3.95,0,0,1,82.34,68Z"/></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -44,6 +44,7 @@
#stop-button,
.close-icon,
#zen-sidebar-web-panel-close,
#zen-glance-sidebar-close,
.zen-theme-picker-custom-list-item-remove {
list-style-image: url('close.svg') !important;
}
@@ -263,6 +264,7 @@
#restore-button,
#fullscreen-button,
#zen-glance-sidebar-open,
#appMenu-fullscreen-button2 {
list-style-image: url('fullscreen.svg') !important;
}
@@ -891,10 +893,18 @@ menuitem[contexttype='fullscreen'][label*='Exit'] {
#context_pinSelectedTabs,
#context_unpinSelectedTabs,
.customize-context-moveToPanel,
#context_zen-replace-pinned-url-with-current {
#context_zen-replace-pinned-url-with-current{
--menu-image: url('pin.svg');
}
#context_zen-add-essential {
--menu-image: url('essential-add.svg');
}
#context_zen-remove-essential {
--menu-image: url('essential-remove.svg');
}
.customize-context-removeFromToolbar {
--menu-image: url('unpin.svg');
}

View File

@@ -36,6 +36,8 @@
skin/classic/browser/zen-icons/edit-select-all.svg (../shared/zen-icons/edit-select-all.svg)
skin/classic/browser/zen-icons/edit-undo.svg (../shared/zen-icons/edit-undo.svg)
skin/classic/browser/zen-icons/edit.svg (../shared/zen-icons/edit.svg)
skin/classic/browser/zen-icons/essential-add.svg (../shared/zen-icons/essential-add.svg)
skin/classic/browser/zen-icons/essential-remove.svg (../shared/zen-icons/essential-remove.svg)
skin/classic/browser/zen-icons/ext-link.svg (../shared/zen-icons/ext-link.svg)
skin/classic/browser/zen-icons/extension-blocked.svg (../shared/zen-icons/extension-blocked.svg)
skin/classic/browser/zen-icons/extension.svg (../shared/zen-icons/extension.svg)

View File

@@ -1,13 +0,0 @@
diff --git a/dom/ipc/BrowserHost.cpp b/dom/ipc/BrowserHost.cpp
index 489f07a612e52a3edb00f59e53495dcd9e16c0f5..1799e0d416f9d7be2c9e34c237d6ff741e5e2420 100644
--- a/dom/ipc/BrowserHost.cpp
+++ b/dom/ipc/BrowserHost.cpp
@@ -106,7 +106,7 @@ void BrowserHost::UpdateEffects(EffectsInfo aEffects) {
/* attribute boolean renderLayers; */
NS_IMETHODIMP
BrowserHost::GetRenderLayers(bool* aRenderLayers) {
- if (!mRoot) {
+ if (!mRoot && !Preferences::GetBool("zen.splitView.working", false)) {
*aRenderLayers = false;
return NS_OK;
}

View File

@@ -1,25 +0,0 @@
diff --git a/toolkit/modules/ActorManagerParent.sys.mjs b/toolkit/modules/ActorManagerParent.sys.mjs
index 71409e58271802fc506999712519df63eff3b1a6..684c9da0139db1dc0962fbb50304ca4f013950ca 100644
--- a/toolkit/modules/ActorManagerParent.sys.mjs
+++ b/toolkit/modules/ActorManagerParent.sys.mjs
@@ -666,6 +666,20 @@ if (!Services.prefs.getBoolPref("browser.pagedata.enabled", false)) {
};
}
+JSWINDOWACTORS.ZenThemeMarketplace = {
+ parent: {
+ esModuleURI: "chrome://browser/content/zen-components/actors/ZenThemeMarketplaceParent.sys.mjs",
+ },
+ child: {
+ esModuleURI: "chrome://browser/content/zen-components/actors/ZenThemeMarketplaceChild.sys.mjs",
+ events: {
+ DOMContentLoaded: {},
+ },
+ },
+ matches: ["https://*.zen-browser.app/*", "about:preferences"],
+ allFrames: true,
+};
+
if (AppConstants.platform != "android") {
// Note that GeckoView has another implementation in mobile/android/actors.
JSWINDOWACTORS.Select = {

View File

@@ -1,5 +1,5 @@
diff --git a/toolkit/moz.configure b/toolkit/moz.configure
index 011eee093dcf77c3c8ad3cd20188b61457d8dc09..d6ffe47cf4d1dfe7a49d7e5f5d84c162b8589cc7 100644
index 06281a55f43ddff4f4d4a09ba05e9a830a221415..269728dccdd8a286b563a5d20da5280b7b3b1639 100644
--- a/toolkit/moz.configure
+++ b/toolkit/moz.configure
@@ -81,6 +81,9 @@ option(
@@ -25,3 +25,12 @@ index 011eee093dcf77c3c8ad3cd20188b61457d8dc09..d6ffe47cf4d1dfe7a49d7e5f5d84c162
return True
@@ -2036,7 +2039,7 @@ set_define("A11Y_LOG", True, when=a11y_log)
# ==============================================================
@depends(milestone)
def require_signing(milestone):
- return milestone.is_release_or_beta and not milestone.is_esr
+ return False
option(

View File

@@ -1,13 +1,15 @@
diff --git a/toolkit/mozapps/extensions/internal/XPIProvider.sys.mjs b/toolkit/mozapps/extensions/internal/XPIProvider.sys.mjs
index 12d4fa1172f4f19df63521fac2bc6f065ae455df..981e3c2e22e59447343cd4b35411041656b2b7ed 100644
index 6aeb6a6dc7fd46eefac6a0cd56fadaee11efc6c0..abbd17afaadfadb3f97a0553764ac35aab67116c 100644
--- a/toolkit/mozapps/extensions/internal/XPIProvider.sys.mjs
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.sys.mjs
@@ -2546,7 +2546,7 @@ export var XPIProvider = {
);
@@ -2535,8 +2535,8 @@ export var XPIProvider = {
}
// Keep version in sync with toolkit/mozapps/extensions/default-theme/manifest.json
this.maybeInstallBuiltinAddon(
- "default-theme@mozilla.org",
- "1.4.1",
+ "firefox-compact-dark@mozilla.org",
"1.3",
+ "1.3",
"resource://default-theme/"
);

View File

@@ -1,8 +1,8 @@
diff --git a/toolkit/profile/nsToolkitProfileService.cpp b/toolkit/profile/nsToolkitProfileService.cpp
index 6e40f51cfc6e76fbd63d87fe7a416481fe9bb2f6..3bc78e3077264387415a06a52a3a8fbc56339484 100644
index 0587b214ddcf4b12877ca3698ca7d4308b2f0af3..3846515a4fa7b849233de6026f55152478c04f03 100644
--- a/toolkit/profile/nsToolkitProfileService.cpp
+++ b/toolkit/profile/nsToolkitProfileService.cpp
@@ -234,7 +234,7 @@ void RemoveProfileFiles(nsIToolkitProfile* aProfile, bool aInBackground) {
@@ -229,7 +229,7 @@ void RemoveProfileFiles(nsIToolkitProfile* aProfile, bool aInBackground) {
}
nsToolkitProfile::nsToolkitProfile(const nsACString& aName, nsIFile* aRootDir,
@@ -11,7 +11,7 @@ index 6e40f51cfc6e76fbd63d87fe7a416481fe9bb2f6..3bc78e3077264387415a06a52a3a8fbc
const nsACString& aStoreID = VoidCString(),
bool aShowProfileSelector = false)
: mName(aName),
@@ -244,7 +244,8 @@ nsToolkitProfile::nsToolkitProfile(const nsACString& aName, nsIFile* aRootDir,
@@ -239,7 +239,8 @@ nsToolkitProfile::nsToolkitProfile(const nsACString& aName, nsIFile* aRootDir,
mShowProfileSelector(aShowProfileSelector),
mLock(nullptr),
mIndex(0),
@@ -21,7 +21,7 @@ index 6e40f51cfc6e76fbd63d87fe7a416481fe9bb2f6..3bc78e3077264387415a06a52a3a8fbc
NS_ASSERTION(aRootDir, "No file!");
RefPtr<nsToolkitProfile> prev =
@@ -259,6 +260,10 @@ nsToolkitProfile::nsToolkitProfile(const nsACString& aName, nsIFile* aRootDir,
@@ -254,6 +255,10 @@ nsToolkitProfile::nsToolkitProfile(const nsACString& aName, nsIFile* aRootDir,
// If this profile isn't in the database already add it.
if (!aFromDB) {
nsINIParser* db = &nsToolkitProfileService::gService->mProfileDB;
@@ -32,7 +32,7 @@ index 6e40f51cfc6e76fbd63d87fe7a416481fe9bb2f6..3bc78e3077264387415a06a52a3a8fbc
db->SetString(mSection.get(), "Name", mName.get());
bool isRelative = false;
@@ -268,6 +273,7 @@ nsToolkitProfile::nsToolkitProfile(const nsACString& aName, nsIFile* aRootDir,
@@ -263,6 +268,7 @@ nsToolkitProfile::nsToolkitProfile(const nsACString& aName, nsIFile* aRootDir,
db->SetString(mSection.get(), "IsRelative", isRelative ? "1" : "0");
db->SetString(mSection.get(), "Path", descriptor.get());
@@ -40,7 +40,7 @@ index 6e40f51cfc6e76fbd63d87fe7a416481fe9bb2f6..3bc78e3077264387415a06a52a3a8fbc
if (!mStoreID.IsVoid()) {
db->SetString(mSection.get(), "StoreID",
PromiseFlatCString(mStoreID).get());
@@ -466,6 +472,8 @@ nsToolkitProfile::SetShowProfileSelector(bool aShowProfileSelector) {
@@ -462,6 +468,8 @@ nsToolkitProfile::SetShowProfileSelector(bool aShowProfileSelector) {
#endif
}
@@ -49,14 +49,13 @@ index 6e40f51cfc6e76fbd63d87fe7a416481fe9bb2f6..3bc78e3077264387415a06a52a3a8fbc
nsresult nsToolkitProfile::RemoveInternal(bool aRemoveFiles,
bool aInBackground) {
NS_ASSERTION(nsToolkitProfileService::gService, "Whoa, my service is gone.");
@@ -1158,8 +1166,15 @@ nsresult nsToolkitProfileService::Init() {
@@ -1145,8 +1153,14 @@ nsresult nsToolkitProfileService::Init() {
}
}
- currentProfile = new nsToolkitProfile(name, rootDir, localDir, true,
- storeID, showProfileSelector);
+ nsAutoCString zenProfileAvatar;
+
+ rv = mProfileDB.GetString(profileID.get(), "ZenAvatarPath", zenProfileAvatar);
+ if (NS_FAILED(rv)) {
+ NS_ERROR("Malformed profiles.ini: ZenAvatarPath= not found");
@@ -67,7 +66,7 @@ index 6e40f51cfc6e76fbd63d87fe7a416481fe9bb2f6..3bc78e3077264387415a06a52a3a8fbc
// If a user has modified the ini file path it may make for a valid profile
// path but not match what we would have serialised and so may not match
@@ -1384,7 +1399,7 @@ nsresult nsToolkitProfileService::CreateDefaultProfile(
@@ -1375,7 +1389,7 @@ nsresult nsToolkitProfileService::CreateDefaultProfile(
if (mUseDevEditionProfile) {
name.AssignLiteral(DEV_EDITION_NAME);
} else if (mUseDedicatedProfile) {
@@ -76,12 +75,12 @@ index 6e40f51cfc6e76fbd63d87fe7a416481fe9bb2f6..3bc78e3077264387415a06a52a3a8fbc
} else {
name.AssignLiteral(DEFAULT_NAME);
}
@@ -2180,7 +2195,7 @@ nsToolkitProfileService::CreateProfile(nsIFile* aRootDir,
@@ -2185,7 +2199,7 @@ nsresult nsToolkitProfileService::CreateProfile(nsIFile* aRootDir,
rv = CreateTimesInternal(rootDir);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIToolkitProfile> profile =
- new nsToolkitProfile(aName, rootDir, localDir, false);
+ new nsToolkitProfile(aName, rootDir, localDir, false, ""_ns);
- profile = new nsToolkitProfile(aName, rootDir, localDir, false);
+ profile = new nsToolkitProfile(aName, rootDir, localDir, false, ""_ns);
if (aName.Equals(DEV_EDITION_NAME)) {
mDevEditionDefault = profile;

View File

@@ -1,13 +1,12 @@
diff --git a/toolkit/themes/linux/global/menu.css b/toolkit/themes/linux/global/menu.css
index da47f78790a251bb573d992476dc5dac9a870857..c7d019df2164b95fca29ec5a72e7fe4188d1e1e6 100644
index da47f78790a251bb573d992476dc5dac9a870857..6c7d6cbdaf3f0bd35a52c80785577a6bc0d55e29 100644
--- a/toolkit/themes/linux/global/menu.css
+++ b/toolkit/themes/linux/global/menu.css
@@ -52,7 +52,7 @@ menulist > menupopup > :is(menuitem, menucaption, menu) {
@@ -52,7 +52,6 @@ menulist > menupopup > :is(menuitem, menucaption, menu) {
.menu-text {
/* This is (18 + the size of end-padding on .menu-iconic-left)px */
- margin-inline-start: 21px;
+ margin-inline-start: 0;
}
.menu-accel,

View File

@@ -1,19 +1,14 @@
diff --git a/toolkit/themes/shared/menulist-shared.css b/toolkit/themes/shared/menulist-shared.css
index fa7e68c7b71b28c6dc1b2f67a9868f9ad4090034..9d082381e2d491d63647f8047a748354918686fe 100644
index 5dd5549674570170a694afbd9ea4526e52e3192a..187fcb57183df6a0ab3701ab79c46d86c5e984b3 100644
--- a/toolkit/themes/shared/menulist-shared.css
+++ b/toolkit/themes/shared/menulist-shared.css
@@ -14,12 +14,13 @@
@@ -14,7 +14,8 @@
:host(:not([native])) {
appearance: none;
- background-color: var(--button-bgcolor, ButtonFace);
- background-color: var(--button-background-color);
+ background-color: var(--zen-colors-tertiary, ButtonFace);
color: var(--button-color, ButtonText);
+ border: 1px solid var(--input-border-color, ThreeDShadow);
color: var(--button-text-color);
border-radius: 4px;
padding-block: 4px;
padding-inline: 12px 8px;
margin: 5px 2px 3px;
+ border: 1px solid var(--input-border-color, ThreeDShadow);
}
:host(:not([native])[size="medium"]) {

View File

@@ -1,20 +0,0 @@
diff --git a/widget/ThemeColors.h b/widget/ThemeColors.h
index 739a7ca0f0ff4d1647c5c204b49ba7cc157c76fd..86a89bc890ec5ef824f0a0295876fddb9e363c7c 100644
--- a/widget/ThemeColors.h
+++ b/widget/ThemeColors.h
@@ -13,8 +13,14 @@
namespace mozilla::widget {
+#ifndef ZEN_DEFAULT_ACCENT_COLOR
+// Like a normal hex code:
+// 0xRRGGBBAA
+#define ZEN_DEFAULT_ACCENT_COLOR 0x000000df
+#endif
+
static constexpr gfx::sRGBColor sDefaultAccent(
- gfx::sRGBColor::UnusualFromARGB(0xff0060df)); // Luminance: 13.69346%
+ gfx::sRGBColor::UnusualFromARGB(ZEN_DEFAULT_ACCENT_COLOR)); // Luminance: 13.69346%
static constexpr gfx::sRGBColor sDefaultAccentText(
gfx::sRGBColor::OpaqueWhite());

View File

@@ -0,0 +1,13 @@
diff --git a/widget/cocoa/VibrancyManager.mm b/widget/cocoa/VibrancyManager.mm
index e8263ee480a0249cae760ee5b9037bb87acafecc..aefc887208659e46191eebfde1f9f74f1eac77a5 100644
--- a/widget/cocoa/VibrancyManager.mm
+++ b/widget/cocoa/VibrancyManager.mm
@@ -39,7 +39,7 @@ static NSVisualEffectMaterial VisualEffectMaterialForVibrancyType(
case VibrancyType::Sidebar:
return NSVisualEffectMaterialSidebar;
case VibrancyType::Titlebar:
- return NSVisualEffectMaterialTitlebar;
+ return NSVisualEffectMaterialUnderWindowBackground;
}
}

View File

@@ -5,7 +5,7 @@
"binaryName": "zen",
"version": {
"product": "firefox",
"version": "131.0.3"
"version": "132.0.1"
},
"buildOptions": {
"generateBranding": true
@@ -18,7 +18,7 @@
"brandShortName": "Zen Browser",
"brandFullName": "Zen Browser",
"release": {
"displayVersion": "1.0.1-a.14",
"displayVersion": "1.0.1-a.22",
"github": {
"repo": "zen-browser/desktop"
},
@@ -26,7 +26,7 @@
"windows": "windows.mar",
"macos-aarch64": "macos-aarch64.mar",
"linux": "linux.mar",
"macos-x64": "macos-x64.mar",
"macos-x64": "macos-x86_64.mar",
"windows-compat": "windows-generic.mar",
"linux-compat": "linux-generic.mar"
}
@@ -38,7 +38,7 @@
"brandShortName": "Zen Twilight",
"brandFullName": "Zen Twilight",
"release": {
"displayVersion": "1.0.1-t.14",
"displayVersion": "1.0.1-t.22",
"github": {
"repo": "zen-browser/desktop"
},