mirror of
				https://github.com/zen-browser/desktop.git
				synced 2025-10-26 12:27:50 +00:00 
			
		
		
		
	Compare commits
	
		
			13 Commits
		
	
	
		
			1.17b
			...
			window-syn
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | af20a65fa1 | ||
|   | 4a7f8fc9c0 | ||
|   | a738a829de | ||
|   | 240a031e38 | ||
|   | 9bc7b9ce4e | ||
|   | 86006c8891 | ||
|   | a55b1c7495 | ||
|   | 6e6337a95b | ||
|   | 6b12153c8a | ||
|   | f6922ef2ba | ||
|   | 91f5d58fbc | ||
|   | 7a4cdaa45c | ||
|   | 81e854a89f | 
| @@ -1 +0,0 @@ | |||||||
| https://zen-browser.app/funding.json |  | ||||||
| @@ -38,7 +38,7 @@ zen-library-sidebar-workspaces = | |||||||
| zen-library-sidebar-mods =  | zen-library-sidebar-mods =  | ||||||
|     .label = Mods |     .label = Mods | ||||||
| zen-toggle-compact-mode-button =  | zen-toggle-compact-mode-button =  | ||||||
|     .label = Kompakter Modus |     .label = Compact Mode | ||||||
|     .tooltiptext = Compact Mode umschalten |     .tooltiptext = Compact Mode umschalten | ||||||
|  |  | ||||||
| # note: Do not translate the "<br/>" tags in the following string | # note: Do not translate the "<br/>" tags in the following string | ||||||
|   | |||||||
| @@ -58,3 +58,4 @@ | |||||||
| <script type="text/javascript" src="chrome://browser/content/zen-components/ZenDownloadAnimation.mjs"></script> | <script type="text/javascript" src="chrome://browser/content/zen-components/ZenDownloadAnimation.mjs"></script> | ||||||
| <script type="text/javascript" src="chrome://browser/content/zen-components/ZenEmojiPicker.mjs"></script> | <script type="text/javascript" src="chrome://browser/content/zen-components/ZenEmojiPicker.mjs"></script> | ||||||
| <script type="text/javascript" src="chrome://browser/content/zen-components/ZenWorkspaceCreation.mjs"></script> | <script type="text/javascript" src="chrome://browser/content/zen-components/ZenWorkspaceCreation.mjs"></script> | ||||||
|  | <script type="text/javascript" src="chrome://browser/content/zen-components/ZenWindowSyncing.mjs"></script> | ||||||
|   | |||||||
| @@ -42,6 +42,7 @@ | |||||||
|         content/browser/zen-components/ZenWorkspaceIcons.mjs                    (../../zen/workspaces/ZenWorkspaceIcons.mjs) |         content/browser/zen-components/ZenWorkspaceIcons.mjs                    (../../zen/workspaces/ZenWorkspaceIcons.mjs) | ||||||
|         content/browser/zen-components/ZenWorkspace.mjs                         (../../zen/workspaces/ZenWorkspace.mjs) |         content/browser/zen-components/ZenWorkspace.mjs                         (../../zen/workspaces/ZenWorkspace.mjs) | ||||||
|         content/browser/zen-components/ZenWorkspaces.mjs                        (../../zen/workspaces/ZenWorkspaces.mjs) |         content/browser/zen-components/ZenWorkspaces.mjs                        (../../zen/workspaces/ZenWorkspaces.mjs) | ||||||
|  |         content/browser/zen-components/ZenWindowSyncing.mjs                     (../../zen/workspaces/ZenWindowSyncing.mjs) | ||||||
|         content/browser/zen-components/ZenWorkspaceCreation.mjs                 (../../zen/workspaces/ZenWorkspaceCreation.mjs) |         content/browser/zen-components/ZenWorkspaceCreation.mjs                 (../../zen/workspaces/ZenWorkspaceCreation.mjs) | ||||||
|         content/browser/zen-components/ZenWorkspacesStorage.mjs                 (../../zen/workspaces/ZenWorkspacesStorage.mjs) |         content/browser/zen-components/ZenWorkspacesStorage.mjs                 (../../zen/workspaces/ZenWorkspacesStorage.mjs) | ||||||
|         content/browser/zen-components/ZenWorkspacesSync.mjs                    (../../zen/workspaces/ZenWorkspacesSync.mjs) |         content/browser/zen-components/ZenWorkspacesSync.mjs                    (../../zen/workspaces/ZenWorkspacesSync.mjs) | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| diff --git a/browser/components/customizableui/CustomizableUI.sys.mjs b/browser/components/customizableui/CustomizableUI.sys.mjs | diff --git a/browser/components/customizableui/CustomizableUI.sys.mjs b/browser/components/customizableui/CustomizableUI.sys.mjs | ||||||
| index d9a059f608779fea7cd8c595a432f6fe95183e0c..31c43bc3d5b05713299c1b822b9774909445e862 100644 | index d9a059f608779fea7cd8c595a432f6fe95183e0c..09a7c4045afd0b96027d0bbbad22e02e52fd7b22 100644 | ||||||
| --- a/browser/components/customizableui/CustomizableUI.sys.mjs | --- a/browser/components/customizableui/CustomizableUI.sys.mjs | ||||||
| +++ b/browser/components/customizableui/CustomizableUI.sys.mjs | +++ b/browser/components/customizableui/CustomizableUI.sys.mjs | ||||||
| @@ -14,6 +14,7 @@ ChromeUtils.defineESModuleGetters(lazy, { | @@ -14,6 +14,7 @@ ChromeUtils.defineESModuleGetters(lazy, { | ||||||
| @@ -158,7 +158,7 @@ index d9a059f608779fea7cd8c595a432f6fe95183e0c..31c43bc3d5b05713299c1b822b977490 | |||||||
|            continue; |            continue; | ||||||
|          } |          } | ||||||
| -        sum += parseFloat(style.marginLeft) + parseFloat(style.marginRight); | -        sum += parseFloat(style.marginLeft) + parseFloat(style.marginRight); | ||||||
| +        sum += parseFloat(style.marginLeft) + (win.gZenVerticalTabsManager._hasSetSingleToolbar ? Math.max(0, parseFloat(style.marginRight)) : parseFloat(style.marginRight)); | +        sum += parseFloat(style.marginLeft) + Math.max(0, parseFloat(style.marginRight)); | ||||||
|          if (child != aExceptChild) { |          if (child != aExceptChild) { | ||||||
|            sum += getInlineSize(child); |            sum += getInlineSize(child); | ||||||
|          } |          } | ||||||
|   | |||||||
| @@ -1,16 +1,8 @@ | |||||||
| diff --git a/browser/themes/shared/tabbrowser/content-area.css b/browser/themes/shared/tabbrowser/content-area.css | diff --git a/browser/themes/shared/tabbrowser/content-area.css b/browser/themes/shared/tabbrowser/content-area.css | ||||||
| index e06addf1602dc26ff4e75a8db6251231690f3f80..ffac005d5040852eda8f574f65f2eadf5ecbd642 100644 | index e06addf1602dc26ff4e75a8db6251231690f3f80..86e2cd0194bb37fa140a2f93eccfdd61419a9aec 100644 | ||||||
| --- a/browser/themes/shared/tabbrowser/content-area.css | --- a/browser/themes/shared/tabbrowser/content-area.css | ||||||
| +++ b/browser/themes/shared/tabbrowser/content-area.css | +++ b/browser/themes/shared/tabbrowser/content-area.css | ||||||
| @@ -134,7 +134,6 @@ | @@ -276,7 +276,7 @@ | ||||||
|    } |  | ||||||
|   |  | ||||||
|    browser:is([blank], [pendingpaint]) { |  | ||||||
| -    opacity: 0; |  | ||||||
|    } |  | ||||||
|   |  | ||||||
|    browser[type="content"] { |  | ||||||
| @@ -276,7 +275,7 @@ |  | ||||||
|   |   | ||||||
|  .dialogStack { |  .dialogStack { | ||||||
|    z-index: var(--browser-stack-z-index-dialog-stack); |    z-index: var(--browser-stack-z-index-dialog-stack); | ||||||
|   | |||||||
| @@ -24,11 +24,11 @@ | |||||||
|         const { exists: shouldExist = true } = descendantSelectors; |         const { exists: shouldExist = true } = descendantSelectors; | ||||||
|         if (exists === shouldExist) { |         if (exists === shouldExist) { | ||||||
|           if (!element.hasAttribute(stateAttribute)) { |           if (!element.hasAttribute(stateAttribute)) { | ||||||
|             gZenCompactModeManager._setElementExpandAttribute(element, true, stateAttribute); |             element.setAttribute(stateAttribute, 'true'); | ||||||
|           } |           } | ||||||
|         } else { |         } else { | ||||||
|           if (element.hasAttribute(stateAttribute)) { |           if (element.hasAttribute(stateAttribute)) { | ||||||
|             gZenCompactModeManager._setElementExpandAttribute(element, false, stateAttribute); |             element.removeAttribute(stateAttribute); | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|       }; |       }; | ||||||
|   | |||||||
| @@ -143,7 +143,6 @@ var gZenCompactModeManager = { | |||||||
|   }, |   }, | ||||||
|  |  | ||||||
|   addHasPolyfillObserver() { |   addHasPolyfillObserver() { | ||||||
|     const attributes = ['panelopen', 'open', 'breakout-extend', 'zen-floating-urlbar']; |  | ||||||
|     this.sidebarObserverId = ZenHasPolyfill.observeSelectorExistence( |     this.sidebarObserverId = ZenHasPolyfill.observeSelectorExistence( | ||||||
|       this.sidebar, |       this.sidebar, | ||||||
|       [ |       [ | ||||||
| @@ -153,21 +152,8 @@ var gZenCompactModeManager = { | |||||||
|         }, |         }, | ||||||
|       ], |       ], | ||||||
|       'zen-compact-mode-active', |       'zen-compact-mode-active', | ||||||
|       attributes |       ['panelopen', 'open', 'breakout-extend', 'zen-floating-urlbar'] | ||||||
|     ); |     ); | ||||||
|     this.toolbarObserverId = ZenHasPolyfill.observeSelectorExistence( |  | ||||||
|       document.getElementById('zen-appcontent-navbar-wrapper'), |  | ||||||
|       [ |  | ||||||
|         { |  | ||||||
|           selector: |  | ||||||
|             ":is([panelopen='true'], [open='true'], #urlbar:focus-within, [breakout-extend='true']):not(.zen-compact-mode-ignore)", |  | ||||||
|         }, |  | ||||||
|       ], |  | ||||||
|       'zen-compact-mode-active', |  | ||||||
|       attributes |  | ||||||
|     ); |  | ||||||
|     // Always connect this observer, we need it even if compact mode is disabled |  | ||||||
|     ZenHasPolyfill.connectObserver(this.toolbarObserverId); |  | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
|   flashSidebarIfNecessary(aInstant = false) { |   flashSidebarIfNecessary(aInstant = false) { | ||||||
| @@ -216,7 +202,7 @@ var gZenCompactModeManager = { | |||||||
|   }, |   }, | ||||||
|  |  | ||||||
|   updateCompactModeContext(isSingleToolbar) { |   updateCompactModeContext(isSingleToolbar) { | ||||||
|     const isIllegalState = this.checkIfIllegalState(); |     isSingleToolbar ||= this.checkIfIllegalState(); | ||||||
|     const menuitem = document.getElementById('zen-context-menu-compact-mode-toggle'); |     const menuitem = document.getElementById('zen-context-menu-compact-mode-toggle'); | ||||||
|     const menu = document.getElementById('zen-context-menu-compact-mode'); |     const menu = document.getElementById('zen-context-menu-compact-mode'); | ||||||
|     if (isSingleToolbar) { |     if (isSingleToolbar) { | ||||||
| @@ -226,14 +212,6 @@ var gZenCompactModeManager = { | |||||||
|       menu.removeAttribute('hidden'); |       menu.removeAttribute('hidden'); | ||||||
|       menu.querySelector('menupopup').prepend(menuitem); |       menu.querySelector('menupopup').prepend(menuitem); | ||||||
|     } |     } | ||||||
|     const hideToolbarMenuItem = document.getElementById( |  | ||||||
|       'zen-context-menu-compact-mode-hide-toolbar' |  | ||||||
|     ); |  | ||||||
|     if (isIllegalState) { |  | ||||||
|       hideToolbarMenuItem.setAttribute('disabled', 'true'); |  | ||||||
|     } else { |  | ||||||
|       hideToolbarMenuItem.removeAttribute('disabled'); |  | ||||||
|     } |  | ||||||
|   }, |   }, | ||||||
|  |  | ||||||
|   hideSidebar() { |   hideSidebar() { | ||||||
| @@ -623,7 +601,7 @@ var gZenCompactModeManager = { | |||||||
|   }, |   }, | ||||||
|  |  | ||||||
|   _setElementExpandAttribute(element, value, attr = 'zen-has-hover') { |   _setElementExpandAttribute(element, value, attr = 'zen-has-hover') { | ||||||
|     const kVerifiedAttributes = ['zen-has-hover', 'has-popup-menu', 'zen-compact-mode-active']; |     const kVerifiedAttributes = ['zen-has-hover', 'has-popup-menu']; | ||||||
|     const isToolbar = element.id === 'zen-appcontent-navbar-wrapper'; |     const isToolbar = element.id === 'zen-appcontent-navbar-wrapper'; | ||||||
|     if (value) { |     if (value) { | ||||||
|       element.setAttribute(attr, 'true'); |       element.setAttribute(attr, 'true'); | ||||||
| @@ -634,7 +612,8 @@ var gZenCompactModeManager = { | |||||||
|             document.documentElement.hasAttribute('zen-has-bookmarks'))) || |             document.documentElement.hasAttribute('zen-has-bookmarks'))) || | ||||||
|           (this.preference && |           (this.preference && | ||||||
|             Services.prefs.getBoolPref('zen.view.compact.hide-toolbar') && |             Services.prefs.getBoolPref('zen.view.compact.hide-toolbar') && | ||||||
|             !gZenVerticalTabsManager._hasSetSingleToolbar)) |             !gZenVerticalTabsManager._hasSetSingleToolbar && | ||||||
|  |             !gURLBar.hasAttribute('breakout-extend'))) | ||||||
|       ) { |       ) { | ||||||
|         gBrowser.tabpanels.setAttribute('has-toolbar-hovered', 'true'); |         gBrowser.tabpanels.setAttribute('has-toolbar-hovered', 'true'); | ||||||
|       } |       } | ||||||
|   | |||||||
| @@ -35,8 +35,7 @@ | |||||||
|     overflow: clip; |     overflow: clip; | ||||||
|  |  | ||||||
|     & #urlbar:not([breakout-extend='true']) { |     & #urlbar:not([breakout-extend='true']) { | ||||||
|       /* Sometimes, "opacity: 1" is forced elsewhere */ |       opacity: 0; | ||||||
|       opacity: 0 !important; |  | ||||||
|       pointer-events: none; |       pointer-events: none; | ||||||
|       transition: opacity var(--zen-hidden-toolbar-transition); |       transition: opacity var(--zen-hidden-toolbar-transition); | ||||||
|     } |     } | ||||||
| @@ -49,18 +48,20 @@ | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   & #zen-appcontent-navbar-wrapper:is( |   & #zen-appcontent-navbar-wrapper[zen-has-hover], | ||||||
|     [zen-has-hover], |   & #zen-appcontent-navbar-wrapper[has-popup-menu], | ||||||
|     [has-popup-menu], |   & | ||||||
|     [zen-compact-mode-active] |     #zen-appcontent-navbar-wrapper:has( | ||||||
|   ) { |       *:is([panelopen='true'], [open='true'], #urlbar:focus-within, [breakout-extend='true']):not(.zen-compact-mode-ignore) | ||||||
|  |     ) { | ||||||
|  |  | ||||||
|     height: var(--zen-toolbar-height-with-bookmarks); |     height: var(--zen-toolbar-height-with-bookmarks); | ||||||
|     overflow: inherit; |     overflow: inherit; | ||||||
|  |  | ||||||
| %include windows-captions-fix-active.inc.css | %include windows-captions-fix-active.inc.css | ||||||
|  |  | ||||||
|     & #urlbar { |     & #urlbar { | ||||||
|       opacity: 1 !important; |       opacity: 1; | ||||||
|       pointer-events: auto; |       pointer-events: auto; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -24,9 +24,6 @@ | |||||||
|     #duringOpening = false; |     #duringOpening = false; | ||||||
|     #ignoreClose = false; |     #ignoreClose = false; | ||||||
|  |  | ||||||
|     // Click handling |  | ||||||
|     #lastLinkClickData = { clientX: 0, clientY: 0, height: 0, width: 0 }; |  | ||||||
|  |  | ||||||
|     // Arc animation configuration |     // Arc animation configuration | ||||||
|     #ARC_CONFIG = Object.freeze({ |     #ARC_CONFIG = Object.freeze({ | ||||||
|       ARC_STEPS: 70, // Increased for smoother bounce |       ARC_STEPS: 70, // Increased for smoother bounce | ||||||
| @@ -271,31 +268,10 @@ | |||||||
|         data.height |         data.height | ||||||
|       ); |       ); | ||||||
|       return await this.#imageBitmapToBase64( |       return await this.#imageBitmapToBase64( | ||||||
|         await window.browsingContext.currentWindowGlobal.drawSnapshot( |         await window.browsingContext.currentWindowGlobal.drawSnapshot(rect, 1, 'transparent', true) | ||||||
|           rect, |  | ||||||
|           1, |  | ||||||
|           'transparent', |  | ||||||
|           undefined |  | ||||||
|         ) |  | ||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Set the last link click data |  | ||||||
|      * @param {Object} data - The link click data |  | ||||||
|      */ |  | ||||||
|     set lastLinkClickData(data) { |  | ||||||
|       this.#lastLinkClickData = data; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |  | ||||||
|      * Get the last link click data |  | ||||||
|      * @returns {Object} The last link click data |  | ||||||
|      */ |  | ||||||
|     get lastLinkClickData() { |  | ||||||
|       return this.#lastLinkClickData; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Open a glance overlay with the specified data |      * Open a glance overlay with the specified data | ||||||
|      * @param {Object} data - Glance data including URL, position, and dimensions |      * @param {Object} data - Glance data including URL, position, and dimensions | ||||||
| @@ -313,13 +289,6 @@ | |||||||
|         return; |         return; | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       if (!data.height || !data.width) { |  | ||||||
|         data = { |  | ||||||
|           ...data, |  | ||||||
|           ...this.lastLinkClickData, |  | ||||||
|         }; |  | ||||||
|       } |  | ||||||
|  |  | ||||||
|       this.#setAnimationState(true); |       this.#setAnimationState(true); | ||||||
|       const currentTab = ownerTab ?? gBrowser.selectedTab; |       const currentTab = ownerTab ?? gBrowser.selectedTab; | ||||||
|       const browserElement = this.#createBrowserElement(data.url, currentTab, existingTab); |       const browserElement = this.#createBrowserElement(data.url, currentTab, existingTab); | ||||||
| @@ -356,13 +325,12 @@ | |||||||
|         gZenViewSplitter.onLocationChange(browserElement); |         gZenViewSplitter.onLocationChange(browserElement); | ||||||
|         this.#prepareGlanceAnimation(data, browserElement); |         this.#prepareGlanceAnimation(data, browserElement); | ||||||
|         if (data.width && data.height) { |         if (data.width && data.height) { | ||||||
|           // It is guaranteed that we will animate this opacity later on |  | ||||||
|           // when we start animating the glance. |  | ||||||
|           this.contentWrapper.style.opacity = 0; |  | ||||||
|           data.elementData = await this.#getElementPreviewData(data); |           data.elementData = await this.#getElementPreviewData(data); | ||||||
|         } |         } | ||||||
|         this.#glances.get(this.#currentGlanceID).elementData = data.elementData; |         this.#glances.get(this.#currentGlanceID).elementData = data.elementData; | ||||||
|         this.#executeGlanceAnimation(data, browserElement, resolve); |         window.requestAnimationFrame(() => { | ||||||
|  |           this.#executeGlanceAnimation(data, browserElement, resolve); | ||||||
|  |         }); | ||||||
|       }); |       }); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -376,6 +344,7 @@ | |||||||
|       const newButtons = this.#createNewOverlayButtons(); |       const newButtons = this.#createNewOverlayButtons(); | ||||||
|       this.browserWrapper.appendChild(newButtons); |       this.browserWrapper.appendChild(newButtons); | ||||||
|  |  | ||||||
|  |       this.#animateParentBackground(); | ||||||
|       this.#setupGlancePositioning(data); |       this.#setupGlancePositioning(data); | ||||||
|       this.#configureBrowserElement(browserElement); |       this.#configureBrowserElement(browserElement); | ||||||
|     } |     } | ||||||
| @@ -519,6 +488,7 @@ | |||||||
|       // nice fade-in effect to the content. But if it doesn't exist, |       // nice fade-in effect to the content. But if it doesn't exist, | ||||||
|       // we just fall back to always showing the browser directly. |       // we just fall back to always showing the browser directly. | ||||||
|       if (data.elementData) { |       if (data.elementData) { | ||||||
|  |         this.contentWrapper.style.opacity = 0; | ||||||
|         gZenUIManager.motion |         gZenUIManager.motion | ||||||
|           .animate( |           .animate( | ||||||
|             this.contentWrapper, |             this.contentWrapper, | ||||||
| @@ -533,7 +503,6 @@ | |||||||
|           }); |           }); | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       this.#animateParentBackground(); |  | ||||||
|       gZenUIManager.motion |       gZenUIManager.motion | ||||||
|         .animate(this.browserWrapper, arcSequence, { |         .animate(this.browserWrapper, arcSequence, { | ||||||
|           duration: gZenUIManager.testingEnabled ? 0 : 0.4, |           duration: gZenUIManager.testingEnabled ? 0 : 0.4, | ||||||
| @@ -1021,7 +990,7 @@ | |||||||
|       if (!onTabClose) { |       if (!onTabClose) { | ||||||
|         this.quickCloseGlance({ clearID: false }); |         this.quickCloseGlance({ clearID: false }); | ||||||
|       } |       } | ||||||
|       this.overlay.style.display = 'none'; |       this.browserWrapper.style.display = 'none'; | ||||||
|       this.overlay.removeAttribute('fade-out'); |       this.overlay.removeAttribute('fade-out'); | ||||||
|       this.browserWrapper.removeAttribute('animate'); |       this.browserWrapper.removeAttribute('animate'); | ||||||
|  |  | ||||||
| @@ -1388,9 +1357,18 @@ | |||||||
|      * @param {Tab} tab - The tab to open glance for |      * @param {Tab} tab - The tab to open glance for | ||||||
|      */ |      */ | ||||||
|     #openGlanceForTab(tab) { |     #openGlanceForTab(tab) { | ||||||
|  |       const browserRect = window.windowUtils.getBoundsWithoutFlushing(gBrowser.tabbox); | ||||||
|  |       const clickPosition = gZenUIManager._lastClickPosition || { | ||||||
|  |         clientX: browserRect.width / 2, | ||||||
|  |         clientY: browserRect.height / 2, | ||||||
|  |       }; | ||||||
|  |  | ||||||
|       this.openGlance( |       this.openGlance( | ||||||
|         { |         { | ||||||
|           url: undefined, |           url: undefined, | ||||||
|  |           ...clickPosition, | ||||||
|  |           width: 0, | ||||||
|  |           height: 0, | ||||||
|         }, |         }, | ||||||
|         tab, |         tab, | ||||||
|         tab.owner |         tab.owner | ||||||
|   | |||||||
| @@ -35,30 +35,22 @@ export class ZenGlanceChild extends JSWindowActorChild { | |||||||
|     return !(event.ctrlKey ^ event.altKey ^ event.shiftKey ^ event.metaKey); |     return !(event.ctrlKey ^ event.altKey ^ event.shiftKey ^ event.metaKey); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   #openGlance(target) { |   openGlance(target, originalTarget) { | ||||||
|     let url = target.href; |     let url = target.href; | ||||||
|     // Add domain to relative URLs |     // Add domain to relative URLs | ||||||
|     if (!url.match(/^(?:[a-z]+:)?\/\//i)) { |     if (!url.match(/^(?:[a-z]+:)?\/\//i)) { | ||||||
|       url = this.contentWindow.location.origin + url; |       url = this.contentWindow.location.origin + url; | ||||||
|     } |     } | ||||||
|     this.sendAsyncMessage('ZenGlance:OpenGlance', { |  | ||||||
|       url, |  | ||||||
|     }); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   #sendClickDataToParent(target, element) { |  | ||||||
|     if (!element || !target) { |  | ||||||
|       return; |  | ||||||
|     } |  | ||||||
|     // Get the largest element we can get. If the `A` element |     // Get the largest element we can get. If the `A` element | ||||||
|     // is a parent of the original target, use the anchor element, |     // is a parent of the original target, use the anchor element, | ||||||
|     // otherwise use the original target. |     // otherwise use the original target. | ||||||
|     let rect = element.getBoundingClientRect(); |     let rect = originalTarget.getBoundingClientRect(); | ||||||
|     const anchorRect = target.getBoundingClientRect(); |     const anchorRect = target.getBoundingClientRect(); | ||||||
|     if (anchorRect.width * anchorRect.height > rect.width * rect.height) { |     if (anchorRect.width * anchorRect.height > rect.width * rect.height) { | ||||||
|       rect = anchorRect; |       rect = anchorRect; | ||||||
|     } |     } | ||||||
|     this.sendAsyncMessage('ZenGlance:RecordLinkClickData', { |     this.sendAsyncMessage('ZenGlance:OpenGlance', { | ||||||
|  |       url, | ||||||
|       clientX: rect.left, |       clientX: rect.left, | ||||||
|       clientY: rect.top, |       clientY: rect.top, | ||||||
|       width: rect.width, |       width: rect.width, | ||||||
| @@ -67,19 +59,7 @@ export class ZenGlanceChild extends JSWindowActorChild { | |||||||
|   } |   } | ||||||
|  |  | ||||||
|   handleClick(event) { |   handleClick(event) { | ||||||
|     if (event.button !== 0 || event.defaultPrevented) { |     if (this.ensureOnlyKeyModifiers(event) || event.button !== 0 || event.defaultPrevented) { | ||||||
|       return; |  | ||||||
|     } |  | ||||||
|     // get closest A element |  | ||||||
|     const target = event.target.closest('A'); |  | ||||||
|     const elementToRecord = event.originalTarget || event.target; |  | ||||||
|     // We record the link data anyway, even if the glance may be invoked |  | ||||||
|     // or not. We have some cases where glance would open, for example, |  | ||||||
|     // when clicking on a link with a different domain where glance would open. |  | ||||||
|     // The problem is that at that stage we don't know the rect or even what |  | ||||||
|     // element has been clicked, so we send the data here. |  | ||||||
|     this.#sendClickDataToParent(target, elementToRecord); |  | ||||||
|     if (this.ensureOnlyKeyModifiers(event)) { |  | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|     const activationMethod = this.#activationMethod; |     const activationMethod = this.#activationMethod; | ||||||
| @@ -92,11 +72,13 @@ export class ZenGlanceChild extends JSWindowActorChild { | |||||||
|     } else if (activationMethod === 'meta' && !event.metaKey) { |     } else if (activationMethod === 'meta' && !event.metaKey) { | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
|  |     // get closest A element | ||||||
|  |     const target = event.target.closest('A'); | ||||||
|     if (target) { |     if (target) { | ||||||
|       event.preventDefault(); |       event.preventDefault(); | ||||||
|       event.stopPropagation(); |       event.stopPropagation(); | ||||||
|  |  | ||||||
|       this.#openGlance(target); |       this.openGlance(target, event.originalTarget || event.target); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -23,10 +23,6 @@ export class ZenGlanceParent extends JSWindowActorParent { | |||||||
|         this.browsingContext.topChromeWindow.gZenGlanceManager.closeGlance(params); |         this.browsingContext.topChromeWindow.gZenGlanceManager.closeGlance(params); | ||||||
|         break; |         break; | ||||||
|       } |       } | ||||||
|       case 'ZenGlance:RecordLinkClickData': { |  | ||||||
|         this.browsingContext.topChromeWindow.gZenGlanceManager.lastLinkClickData = message.data; |  | ||||||
|         break; |  | ||||||
|       } |  | ||||||
|       default: |       default: | ||||||
|         console.warn(`[glance]: Unknown message: ${message.name}`); |         console.warn(`[glance]: Unknown message: ${message.name}`); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -171,10 +171,10 @@ | |||||||
|   position: absolute; |   position: absolute; | ||||||
|   pointer-events: none; |   pointer-events: none; | ||||||
|   width: 100%; |   width: 100%; | ||||||
|  |   height: 100%; | ||||||
|   z-index: 0; |   z-index: 0; | ||||||
|   border-radius: var(--zen-native-inner-radius); |   border-radius: var(--zen-native-inner-radius); | ||||||
|   top: 0%; |   inset: 50%; | ||||||
|   left: 50%; |   translate: -50% -50%; | ||||||
|   translate: -50% 0%; |  | ||||||
|   will-change: transform, opacity; |   will-change: transform, opacity; | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										27
									
								
								src/zen/sessionstore/ZenSessionFile.sys.mjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/zen/sessionstore/ZenSessionFile.sys.mjs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | |||||||
|  | // 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/. | ||||||
|  |  | ||||||
|  | const FILE_NAME = 'zen-sessions.jsonlz4'; | ||||||
|  |  | ||||||
|  | export class nsZenSessionFile { | ||||||
|  |   #path; | ||||||
|  |  | ||||||
|  |   #windows; | ||||||
|  |  | ||||||
|  |   constructor() { | ||||||
|  |     this.#path = PathUtils.join(profileDir, FILE_NAME); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   async read() { | ||||||
|  |     try { | ||||||
|  |       return await IOUtils.readJSON(this.#path, { compress: true }); | ||||||
|  |     } catch (e) { | ||||||
|  |       return {}; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   async write(data) { | ||||||
|  |     await IOUtils.writeJSON(this.#path, data, { compress: true }); | ||||||
|  |   } | ||||||
|  | } | ||||||
							
								
								
									
										50
									
								
								src/zen/sessionstore/ZenSessionManager.sys.mjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								src/zen/sessionstore/ZenSessionManager.sys.mjs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | |||||||
|  | // This Source Code Form is subject to the terms of the Mozilla Public | ||||||
|  | // License, v. 2.0. If a copy of the MPL was not distributed with this | ||||||
|  | // file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||||
|  |  | ||||||
|  | import { | ||||||
|  |   cancelIdleCallback, | ||||||
|  |   clearTimeout, | ||||||
|  |   requestIdleCallback, | ||||||
|  |   setTimeout, | ||||||
|  | } from 'resource://gre/modules/Timer.sys.mjs'; | ||||||
|  |  | ||||||
|  | const lazy = {}; | ||||||
|  |  | ||||||
|  | ChromeUtils.defineESModuleGetters(lazy, { | ||||||
|  |   ZenSessionFile: 'resource://gre/modules/ZenSessionFile.sys.mjs', | ||||||
|  |   PrivateBrowsingUtils: 'resource://gre/modules/PrivateBrowsingUtils.sys.mjs', | ||||||
|  |   RunState: 'resource:///modules/sessionstore/RunState.sys.mjs', | ||||||
|  | }); | ||||||
|  |  | ||||||
|  | class nsZenSessionManager { | ||||||
|  |   #file; | ||||||
|  |  | ||||||
|  |   constructor() { | ||||||
|  |     this.#file = null; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   get file() { | ||||||
|  |     if (!this.#file) { | ||||||
|  |       this.#file = lazy.ZenSessionFile; | ||||||
|  |     } | ||||||
|  |     return this.#file; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    * Saves the current session state. Collects data and writes to disk. | ||||||
|  |    * | ||||||
|  |    * @param forceUpdateAllWindows (optional) | ||||||
|  |    *        Forces us to recollect data for all windows and will bypass and | ||||||
|  |    *        update the corresponding caches. | ||||||
|  |    */ | ||||||
|  |   saveState(forceUpdateAllWindows = false) { | ||||||
|  |     if (lazy.PrivateBrowsingUtils.permanentPrivateBrowsing) { | ||||||
|  |       // Don't save (or even collect) anything in permanent private | ||||||
|  |       // browsing mode | ||||||
|  |       return Promise.resolve(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export const ZenSessionStore = new nsZenSessionManager(); | ||||||
							
								
								
									
										35
									
								
								src/zen/sessionstore/ZenSessionWindow.sys.mjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/zen/sessionstore/ZenSessionWindow.sys.mjs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | |||||||
|  | // 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/. | ||||||
|  |  | ||||||
|  | export class ZenSessionWindow { | ||||||
|  |   #id; | ||||||
|  |   #selectedWorkspace; | ||||||
|  |   #selectedTab; | ||||||
|  |  | ||||||
|  |   constructor(id) { | ||||||
|  |     this.#id = id; | ||||||
|  |     this.#selectedWorkspace = null; | ||||||
|  |     this.#selectedTab = null; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   get id() { | ||||||
|  |     return this.#id; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   get selectedWorkspace() { | ||||||
|  |     return this.#selectedWorkspace; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   set selectedWorkspace(workspace) { | ||||||
|  |     this.#selectedWorkspace = workspace; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   get selectedTab() { | ||||||
|  |     return this.#selectedTab; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   set selectedTab(tab) { | ||||||
|  |     this.#selectedTab = tab; | ||||||
|  |   } | ||||||
|  | } | ||||||
| @@ -101,6 +101,7 @@ | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     onTabIconChanged(tab, url = null) { |     onTabIconChanged(tab, url = null) { | ||||||
|  |       tab.dispatchEvent(new CustomEvent('ZenTabIconChanged', { bubbles: true, detail: { tab } })); | ||||||
|       const iconUrl = url ?? tab.iconImage.src; |       const iconUrl = url ?? tab.iconImage.src; | ||||||
|       if (!iconUrl && tab.hasAttribute('zen-pin-id')) { |       if (!iconUrl && tab.hasAttribute('zen-pin-id')) { | ||||||
|         try { |         try { | ||||||
| @@ -1098,7 +1099,7 @@ | |||||||
|       const element = window.MozXULElement.parseXULToFragment(` |       const element = window.MozXULElement.parseXULToFragment(` | ||||||
|             <menuitem id="context_zen-add-essential" |             <menuitem id="context_zen-add-essential" | ||||||
|                       data-l10n-id="tab-context-zen-add-essential" |                       data-l10n-id="tab-context-zen-add-essential" | ||||||
|                       data-l10n-args='{"num": "0", "max": "${this.maxEssentialTabs}"}' |                       data-l10n-args='{"num": "0", "max": "12"}' | ||||||
|                       hidden="true" |                       hidden="true" | ||||||
|                       disabled="true" |                       disabled="true" | ||||||
|                       command="cmd_contextZenAddToEssentials"/> |                       command="cmd_contextZenAddToEssentials"/> | ||||||
| @@ -1555,6 +1556,7 @@ | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     async onTabLabelChanged(tab) { |     async onTabLabelChanged(tab) { | ||||||
|  |       tab.dispatchEvent(new CustomEvent('ZenTabLabelChanged', { detail: { tab } })); | ||||||
|       if (!this._pinsCache) { |       if (!this._pinsCache) { | ||||||
|         return; |         return; | ||||||
|       } |       } | ||||||
|   | |||||||
| @@ -22,7 +22,9 @@ z-index: 1; | |||||||
|  |  | ||||||
| %include ../../compact-mode/windows-captions-fix-active.inc.css | %include ../../compact-mode/windows-captions-fix-active.inc.css | ||||||
|  |  | ||||||
|   &:not([zen-has-hover='true']):not([has-popup-menu]):not([zen-compact-mode-active]) { |   &:not([zen-has-hover='true']):not([has-popup-menu]):not(:focus-within):not( | ||||||
|  |       :has(*:is([panelopen='true'], [open='true'])) | ||||||
|  |     ) { | ||||||
|     height: var(--zen-element-separation); |     height: var(--zen-element-separation); | ||||||
|     opacity: 0; |     opacity: 0; | ||||||
|     & #zen-appcontent-navbar-container { |     & #zen-appcontent-navbar-container { | ||||||
|   | |||||||
| @@ -618,6 +618,12 @@ | |||||||
|   --tab-min-width: 48px !important; |   --tab-min-width: 48px !important; | ||||||
|   --zen-toolbox-padding: 6px !important; |   --zen-toolbox-padding: 6px !important; | ||||||
|   --zen-toolbox-max-width: calc(var(--tab-min-width) + var(--zen-toolbox-padding) * 2); |   --zen-toolbox-max-width: calc(var(--tab-min-width) + var(--zen-toolbox-padding) * 2); | ||||||
|  |  | ||||||
|  |   /* We can't show the rename input properly in collapsed state, | ||||||
|  |      so hide the workspace edit input */ | ||||||
|  |   #context_zenEditWorkspace { | ||||||
|  |     display: none; | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
| #navigator-toolbox:not([zen-sidebar-expanded='true']) { | #navigator-toolbox:not([zen-sidebar-expanded='true']) { | ||||||
| @@ -700,6 +706,7 @@ | |||||||
|  |  | ||||||
|   & #titlebar { |   & #titlebar { | ||||||
|     display: grid; |     display: grid; | ||||||
|  |     overflow: clip; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   & #zen-sidebar-top-buttons-customization-target { |   & #zen-sidebar-top-buttons-customization-target { | ||||||
| @@ -956,7 +963,7 @@ | |||||||
|   :root[zen-single-toolbar='true'] & { |   :root[zen-single-toolbar='true'] & { | ||||||
|     --zen-toolbar-height: 36px; |     --zen-toolbar-height: 36px; | ||||||
|     @media (-moz-platform: macos) { |     @media (-moz-platform: macos) { | ||||||
|       --zen-toolbar-height: 38px; |       --zen-toolbar-height: 42px; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     & #PanelUI-button { |     & #PanelUI-button { | ||||||
|   | |||||||
| @@ -1305,13 +1305,12 @@ | |||||||
|  |  | ||||||
|         // Do not rebuild if the workspace is not the same as the current one |         // Do not rebuild if the workspace is not the same as the current one | ||||||
|         const windowWorkspace = await browser.gZenWorkspaces.getActiveWorkspace(); |         const windowWorkspace = await browser.gZenWorkspaces.getActiveWorkspace(); | ||||||
|         if (windowWorkspace.uuid !== uuid) { |         if (windowWorkspace.uuid !== uuid && theme !== null) { | ||||||
|           return; |           return; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // get the theme from the window |         // get the theme from the window | ||||||
|         workspaceTheme = this.fixTheme(theme || windowWorkspace.theme); |         workspaceTheme = this.fixTheme(theme || windowWorkspace.theme); | ||||||
|         const docElement = browser.document.documentElement; |  | ||||||
|  |  | ||||||
|         if (!skipUpdate) { |         if (!skipUpdate) { | ||||||
|           for (const dot of browser.gZenThemePicker.panel.querySelectorAll( |           for (const dot of browser.gZenThemePicker.panel.querySelectorAll( | ||||||
| @@ -1329,15 +1328,17 @@ | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (!skipUpdate) { |         if (!skipUpdate) { | ||||||
|           docElement.style.setProperty( |           browser.document.documentElement.style.setProperty( | ||||||
|             '--zen-main-browser-background-old', |             '--zen-main-browser-background-old', | ||||||
|             docElement.style.getPropertyValue('--zen-main-browser-background') |             browser.document.documentElement.style.getPropertyValue('--zen-main-browser-background') | ||||||
|           ); |           ); | ||||||
|           docElement.style.setProperty( |           browser.document.documentElement.style.setProperty( | ||||||
|             '--zen-main-browser-background-toolbar-old', |             '--zen-main-browser-background-toolbar-old', | ||||||
|             docElement.style.getPropertyValue('--zen-main-browser-background-toolbar') |             browser.document.documentElement.style.getPropertyValue( | ||||||
|  |               '--zen-main-browser-background-toolbar' | ||||||
|  |             ) | ||||||
|           ); |           ); | ||||||
|           docElement.style.setProperty( |           browser.document.documentElement.style.setProperty( | ||||||
|             '--zen-background-opacity', |             '--zen-background-opacity', | ||||||
|             browser.gZenThemePicker.previousBackgroundOpacity ?? 1 |             browser.gZenThemePicker.previousBackgroundOpacity ?? 1 | ||||||
|           ); |           ); | ||||||
| @@ -1460,9 +1461,16 @@ | |||||||
|           } |           } | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         docElement.style.setProperty('--zen-main-browser-background-toolbar', gradientToolbar); |         browser.document.documentElement.style.setProperty( | ||||||
|         docElement.style.setProperty('--zen-main-browser-background', gradient); |           '--zen-main-browser-background-toolbar', | ||||||
|  |           gradientToolbar | ||||||
|  |         ); | ||||||
|  |         browser.document.documentElement.style.setProperty( | ||||||
|  |           '--zen-main-browser-background', | ||||||
|  |           gradient | ||||||
|  |         ); | ||||||
|         const isDarkModeWindow = browser.gZenThemePicker.isDarkMode; |         const isDarkModeWindow = browser.gZenThemePicker.isDarkMode; | ||||||
|  |         const docElement = browser.document.documentElement; | ||||||
|         if (isDefaultTheme) { |         if (isDefaultTheme) { | ||||||
|           docElement.setAttribute('zen-default-theme', 'true'); |           docElement.setAttribute('zen-default-theme', 'true'); | ||||||
|         } else { |         } else { | ||||||
| @@ -1491,7 +1499,7 @@ | |||||||
|           } |           } | ||||||
|           // Set `--toolbox-textcolor` to have a contrast with the primary color |           // Set `--toolbox-textcolor` to have a contrast with the primary color | ||||||
|           const textColor = this.getToolbarColor(isDarkMode); |           const textColor = this.getToolbarColor(isDarkMode); | ||||||
|           docElement.style.setProperty( |           document.documentElement.style.setProperty( | ||||||
|             '--toolbox-textcolor', |             '--toolbox-textcolor', | ||||||
|             `rgba(${textColor[0]}, ${textColor[1]}, ${textColor[2]}, ${textColor[3]})` |             `rgba(${textColor[0]}, ${textColor[1]}, ${textColor[2]}, ${textColor[3]})` | ||||||
|           ); |           ); | ||||||
|   | |||||||
							
								
								
									
										308
									
								
								src/zen/workspaces/ZenWindowSyncing.mjs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										308
									
								
								src/zen/workspaces/ZenWindowSyncing.mjs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,308 @@ | |||||||
|  | // 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/. | ||||||
|  | { | ||||||
|  |   class nsZenWorkspaceWindowSync extends nsZenMultiWindowFeature { | ||||||
|  |     #ignoreNextEvents = false; | ||||||
|  |     #waitForPromise = null; | ||||||
|  |  | ||||||
|  |     constructor() { | ||||||
|  |       super(); | ||||||
|  |       if (!window.closed) { | ||||||
|  |         this.init(); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async init() { | ||||||
|  |       await gZenWorkspaces.promiseInitialized; | ||||||
|  |       this.#makeSureAllTabsHaveIds(); | ||||||
|  |       this.#setUpEventListeners(); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #makeSureAllTabsHaveIds() { | ||||||
|  |       const allTabs = gZenWorkspaces.allStoredTabs; | ||||||
|  |       for (const tab of allTabs) { | ||||||
|  |         if (!tab.hasAttribute('zen-sync-id') && !tab.hasAttribute('zen-empty-tab')) { | ||||||
|  |           const tabId = gZenUIManager.generateUuidv4(); | ||||||
|  |           tab.setAttribute('zen-sync-id', tabId); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #setUpEventListeners() { | ||||||
|  |       const kEvents = [ | ||||||
|  |         'TabClose', | ||||||
|  |         'TabOpen', | ||||||
|  |         'TabMove', | ||||||
|  |  | ||||||
|  |         'TabPinned', | ||||||
|  |         'TabUnpinned', | ||||||
|  |  | ||||||
|  |         'TabAddedToEssentials', | ||||||
|  |         'TabRemovedFromEssentials', | ||||||
|  |  | ||||||
|  |         'TabHide', | ||||||
|  |         'TabShow', | ||||||
|  |  | ||||||
|  |         'ZenTabIconChanged', | ||||||
|  |         'ZenTabLabelChanged', | ||||||
|  |  | ||||||
|  |         'TabGroupCreate', | ||||||
|  |         'TabGroupRemoved', | ||||||
|  |         'TabGrouped', | ||||||
|  |         'TabUngrouped', | ||||||
|  |         'TabGroupMoved', | ||||||
|  |       ]; | ||||||
|  |       const eventListener = this.#handleEvent.bind(this); | ||||||
|  |       for (const event of kEvents) { | ||||||
|  |         window.addEventListener(event, eventListener); | ||||||
|  |       } | ||||||
|  |  | ||||||
|  |       window.addEventListener('unload', () => { | ||||||
|  |         for (const event of kEvents) { | ||||||
|  |           window.removeEventListener(event, eventListener); | ||||||
|  |         } | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #handleEvent(event) { | ||||||
|  |       this.#propagateToOtherWindows(event); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async #propagateToOtherWindows(event) { | ||||||
|  |       if (this.#ignoreNextEvents) { | ||||||
|  |         return; | ||||||
|  |       } | ||||||
|  |       if (this.#waitForPromise) { | ||||||
|  |         await this.#waitForPromise; | ||||||
|  |       } | ||||||
|  |       this.#waitForPromise = new Promise((resolve) => { | ||||||
|  |         this.foreachWindowAsActive(async (browser) => { | ||||||
|  |           if (browser.gZenWorkspaceWindowSync && !this.windowIsActive(browser)) { | ||||||
|  |             await browser.gZenWorkspaceWindowSync.onExternalTabEvent(event); | ||||||
|  |           } | ||||||
|  |         }).then(() => { | ||||||
|  |           resolve(); | ||||||
|  |         }); | ||||||
|  |       }); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async onExternalTabEvent(event) { | ||||||
|  |       this.#ignoreNextEvents = true; | ||||||
|  |       switch (event.type) { | ||||||
|  |         case 'TabClose': | ||||||
|  |           this.#onTabClose(event); | ||||||
|  |           break; | ||||||
|  |         case 'TabOpen': | ||||||
|  |           await this.#onTabOpen(event); | ||||||
|  |           break; | ||||||
|  |         case 'TabPinned': | ||||||
|  |           this.#onTabPinned(event); | ||||||
|  |           break; | ||||||
|  |         case 'TabUnpinned': | ||||||
|  |           this.#onTabUnpinned(event); | ||||||
|  |           break; | ||||||
|  |         case 'TabAddedToEssentials': | ||||||
|  |           this.#onTabAddedToEssentials(event); | ||||||
|  |           break; | ||||||
|  |         case 'TabRemovedFromEssentials': | ||||||
|  |           this.#onTabRemovedFromEssentials(event); | ||||||
|  |           break; | ||||||
|  |         case 'TabHide': | ||||||
|  |           this.#onTabHide(event); | ||||||
|  |           break; | ||||||
|  |         case 'TabShow': | ||||||
|  |           this.#onTabShow(event); | ||||||
|  |           break; | ||||||
|  |         case 'TabMove': | ||||||
|  |         case 'TabGroupMoved': | ||||||
|  |           this.#onTabMove(event); | ||||||
|  |           break; | ||||||
|  |         case 'ZenTabIconChanged': | ||||||
|  |           this.#onTabIconChanged(event); | ||||||
|  |           break; | ||||||
|  |         case 'ZenTabLabelChanged': | ||||||
|  |           this.#onTabLabelChanged(event); | ||||||
|  |           break; | ||||||
|  |         case 'TabGroupCreate': | ||||||
|  |           this.#onTabGroupCreate(event); | ||||||
|  |           break; | ||||||
|  |         case 'TabGroupRemoved': | ||||||
|  |         case 'TabGrouped': | ||||||
|  |         case 'TabUngrouped': | ||||||
|  |           // Tab grouping changes are automatically synced by Firefox | ||||||
|  |           break; | ||||||
|  |         default: | ||||||
|  |           console.warn(`Unhandled event type: ${event.type}`); | ||||||
|  |           break; | ||||||
|  |       } | ||||||
|  |       this.#ignoreNextEvents = false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #getTabId(tab) { | ||||||
|  |       return tab.getAttribute('zen-sync-id'); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #getTabWithId(tabId) { | ||||||
|  |       for (const tab of gZenWorkspaces.allStoredTabs) { | ||||||
|  |         if (this.#getTabId(tab) === tabId) { | ||||||
|  |           return tab; | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       return null; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #onTabClose(event) { | ||||||
|  |       const targetTab = event.target; | ||||||
|  |       const tabId = this.#getTabId(targetTab); | ||||||
|  |       const tabToClose = this.#getTabWithId(tabId); | ||||||
|  |       if (tabToClose) { | ||||||
|  |         gBrowser.removeTab(tabToClose); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #onTabPinned(event) { | ||||||
|  |       const targetTab = event.target; | ||||||
|  |       if (targetTab.hasAttribute('zen-essential')) { | ||||||
|  |         return this.#onTabAddedToEssentials(event); | ||||||
|  |       } | ||||||
|  |       const tabId = this.#getTabId(targetTab); | ||||||
|  |       const elementIndex = targetTab.elementIndex; | ||||||
|  |       const tabToPin = this.#getTabWithId(tabId); | ||||||
|  |       if (tabToPin) { | ||||||
|  |         gBrowser.pinTab(tabToPin); | ||||||
|  |         gBrowser.moveTabTo(tabToPin, { elementIndex, forceUngrouped: !!targetTab.group }); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #onTabUnpinned(event) { | ||||||
|  |       const targetTab = event.target; | ||||||
|  |       const tabId = this.#getTabId(targetTab); | ||||||
|  |       const tabToUnpin = this.#getTabWithId(tabId); | ||||||
|  |       if (tabToUnpin) { | ||||||
|  |         gBrowser.unpinTab(tabToUnpin); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #onTabIconChanged(event) { | ||||||
|  |       this.#updateTabIconAndLabel(event); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #onTabLabelChanged(event) { | ||||||
|  |       this.#updateTabIconAndLabel(event); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #updateTabIconAndLabel(event) { | ||||||
|  |       const targetTab = event.target; | ||||||
|  |       const tabId = this.#getTabId(targetTab); | ||||||
|  |       const tabToChange = this.#getTabWithId(tabId); | ||||||
|  |       if (tabToChange && tabToChange.hasAttribute('pending')) { | ||||||
|  |         gBrowser.setIcon(tabToChange, gBrowser.getIcon(targetTab)); | ||||||
|  |         gBrowser._setTabLabel(tabToChange, targetTab.label); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #onTabAddedToEssentials(event) { | ||||||
|  |       const targetTab = event.target; | ||||||
|  |       const tabId = this.#getTabId(targetTab); | ||||||
|  |       const tabToAdd = this.#getTabWithId(tabId); | ||||||
|  |       if (tabToAdd) { | ||||||
|  |         gZenPinnedTabManager.addToEssentials(tabToAdd); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #onTabRemovedFromEssentials(event) { | ||||||
|  |       const targetTab = event.target; | ||||||
|  |       const tabId = this.#getTabId(targetTab); | ||||||
|  |       const tabToRemove = this.#getTabWithId(tabId); | ||||||
|  |       if (tabToRemove) { | ||||||
|  |         gZenPinnedTabManager.removeFromEssentials(tabToRemove); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #onTabHide(event) { | ||||||
|  |       const targetTab = event.target; | ||||||
|  |       const tabId = this.#getTabId(targetTab); | ||||||
|  |       const tabToHide = this.#getTabWithId(tabId); | ||||||
|  |       if (tabToHide) { | ||||||
|  |         gBrowser.hideTab(tabToHide); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #onTabShow(event) { | ||||||
|  |       const targetTab = event.target; | ||||||
|  |       const tabId = this.#getTabId(targetTab); | ||||||
|  |       const tabToShow = this.#getTabWithId(tabId); | ||||||
|  |       if (tabToShow) { | ||||||
|  |         gBrowser.showTab(tabToShow); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #onTabMove(event) { | ||||||
|  |       const targetTab = event.target; | ||||||
|  |       const tabId = this.#getTabId(targetTab); | ||||||
|  |       const tabToMove = this.#getTabWithId(tabId); | ||||||
|  |       const workspaceId = targetTab.getAttribute('zen-workspace-id'); | ||||||
|  |       const isEssential = targetTab.hasAttribute('zen-essential'); | ||||||
|  |       if (tabToMove) { | ||||||
|  |         let tabSibling = targetTab.previousElementSibling; | ||||||
|  |         let isFirst = false; | ||||||
|  |         if (!tabSibling?.hasAttribute('zen-sync-id')) { | ||||||
|  |           isFirst = true; | ||||||
|  |         } | ||||||
|  |         gBrowser.zenHandleTabMove(tabToMove, () => { | ||||||
|  |           if (isFirst) { | ||||||
|  |             let container; | ||||||
|  |             if (isEssential) { | ||||||
|  |               container = gZenWorkspaces.getEssentialsSection(tabToMove); | ||||||
|  |             } else { | ||||||
|  |               const workspaceElement = gZenWorkspaces.workspaceElement(workspaceId); | ||||||
|  |               container = tabToMove.pinned | ||||||
|  |                 ? workspaceElement.pinnedTabsContainer | ||||||
|  |                 : workspaceElement.tabsContainer; | ||||||
|  |             } | ||||||
|  |             container.insertBefore(tabToMove, container.firstChild); | ||||||
|  |           } else { | ||||||
|  |             let relativeTab = gZenWorkspaces.allStoredTabs.find((tab) => { | ||||||
|  |               return this.#getTabId(tab) === this.#getTabId(tabSibling); | ||||||
|  |             }); | ||||||
|  |             if (relativeTab) { | ||||||
|  |               relativeTab.after(tabToMove); | ||||||
|  |             } | ||||||
|  |           } | ||||||
|  |         }); | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     async #onTabOpen(event) { | ||||||
|  |       const targetTab = event.target; | ||||||
|  |       const isPinned = targetTab.pinned; | ||||||
|  |       const isEssential = isPinned && targetTab.hasAttribute('zen-essential'); | ||||||
|  |       if (!this.#getTabId(targetTab) && !targetTab.hasAttribute('zen-empty-tab')) { | ||||||
|  |         const tabId = gZenUIManager.generateUuidv4(); | ||||||
|  |         targetTab.setAttribute('zen-sync-id', tabId); | ||||||
|  |       } | ||||||
|  |       const duplicatedTab = gBrowser.addTrustedTab(targetTab.linkedBrowser.currentURI.spec, { | ||||||
|  |         createLazyBrowser: true, | ||||||
|  |         essential: isEssential, | ||||||
|  |         pinned: isPinned, | ||||||
|  |       }); | ||||||
|  |       if (!isEssential) { | ||||||
|  |         gZenWorkspaces.moveTabToWorkspace( | ||||||
|  |           duplicatedTab, | ||||||
|  |           targetTab.getAttribute('zen-workspace-id') | ||||||
|  |         ); | ||||||
|  |       } | ||||||
|  |       duplicatedTab.setAttribute('zen-pin-id', targetTab.getAttribute('zen-pin-id')); | ||||||
|  |       duplicatedTab.setAttribute('zen-sync-id', targetTab.getAttribute('zen-sync-id')); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     #onTabGroupCreate(event) { | ||||||
|  |       const targetGroup = event.target; | ||||||
|  |       const isSplitView = targetGroup.classList.contains('zen-split-view'); | ||||||
|  |       const isFolder = targetGroup.isZenFolder; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   window.gZenWorkspaceWindowSync = new nsZenWorkspaceWindowSync(); | ||||||
|  | } | ||||||
| @@ -1225,13 +1225,9 @@ var gZenWorkspaces = new (class extends nsZenMultiWindowFeature { | |||||||
|     }; |     }; | ||||||
|     const workspaceName = document.getElementById('context_zenEditWorkspace'); |     const workspaceName = document.getElementById('context_zenEditWorkspace'); | ||||||
|     const themePicker = document.getElementById('context_zenChangeWorkspaceTheme'); |     const themePicker = document.getElementById('context_zenChangeWorkspaceTheme'); | ||||||
|     /* We can't show the rename input properly in collapsed state, |  | ||||||
|     so hide the workspace edit input */ |  | ||||||
|     const isCollapsed = !Services.prefs.getBoolPref('zen.view.sidebar-expanded'); |  | ||||||
|     workspaceName.hidden = |     workspaceName.hidden = | ||||||
|       isCollapsed || |       this.#contextMenuData.workspaceId && | ||||||
|       (this.#contextMenuData.workspaceId && |       this.#contextMenuData.workspaceId !== this.activeWorkspace; | ||||||
|         this.#contextMenuData.workspaceId !== this.activeWorkspace); |  | ||||||
|     themePicker.hidden = |     themePicker.hidden = | ||||||
|       this.#contextMenuData.workspaceId && |       this.#contextMenuData.workspaceId && | ||||||
|       this.#contextMenuData.workspaceId !== this.activeWorkspace; |       this.#contextMenuData.workspaceId !== this.activeWorkspace; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user