Compare commits

..

13 Commits

22 changed files with 504 additions and 140 deletions

View File

@@ -1 +0,0 @@
https://zen-browser.app/funding.json

View File

@@ -38,7 +38,7 @@ zen-library-sidebar-workspaces =
zen-library-sidebar-mods =
.label = Mods
zen-toggle-compact-mode-button =
.label = Kompakter Modus
.label = Compact Mode
.tooltiptext = Compact Mode umschalten
# note: Do not translate the "<br/>" tags in the following string

View File

@@ -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/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/ZenWindowSyncing.mjs"></script>

View File

@@ -42,6 +42,7 @@
content/browser/zen-components/ZenWorkspaceIcons.mjs (../../zen/workspaces/ZenWorkspaceIcons.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/ZenWindowSyncing.mjs (../../zen/workspaces/ZenWindowSyncing.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/ZenWorkspacesSync.mjs (../../zen/workspaces/ZenWorkspacesSync.mjs)

View File

@@ -1,5 +1,5 @@
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
+++ b/browser/components/customizableui/CustomizableUI.sys.mjs
@@ -14,6 +14,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
@@ -158,7 +158,7 @@ index d9a059f608779fea7cd8c595a432f6fe95183e0c..31c43bc3d5b05713299c1b822b977490
continue;
}
- 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) {
sum += getInlineSize(child);
}

View File

@@ -1,16 +1,8 @@
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
+++ b/browser/themes/shared/tabbrowser/content-area.css
@@ -134,7 +134,6 @@
}
browser:is([blank], [pendingpaint]) {
- opacity: 0;
}
browser[type="content"] {
@@ -276,7 +275,7 @@
@@ -276,7 +276,7 @@
.dialogStack {
z-index: var(--browser-stack-z-index-dialog-stack);

View File

@@ -24,11 +24,11 @@
const { exists: shouldExist = true } = descendantSelectors;
if (exists === shouldExist) {
if (!element.hasAttribute(stateAttribute)) {
gZenCompactModeManager._setElementExpandAttribute(element, true, stateAttribute);
element.setAttribute(stateAttribute, 'true');
}
} else {
if (element.hasAttribute(stateAttribute)) {
gZenCompactModeManager._setElementExpandAttribute(element, false, stateAttribute);
element.removeAttribute(stateAttribute);
}
}
};

View File

@@ -143,7 +143,6 @@ var gZenCompactModeManager = {
},
addHasPolyfillObserver() {
const attributes = ['panelopen', 'open', 'breakout-extend', 'zen-floating-urlbar'];
this.sidebarObserverId = ZenHasPolyfill.observeSelectorExistence(
this.sidebar,
[
@@ -153,21 +152,8 @@ var gZenCompactModeManager = {
},
],
'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) {
@@ -216,7 +202,7 @@ var gZenCompactModeManager = {
},
updateCompactModeContext(isSingleToolbar) {
const isIllegalState = this.checkIfIllegalState();
isSingleToolbar ||= this.checkIfIllegalState();
const menuitem = document.getElementById('zen-context-menu-compact-mode-toggle');
const menu = document.getElementById('zen-context-menu-compact-mode');
if (isSingleToolbar) {
@@ -226,14 +212,6 @@ var gZenCompactModeManager = {
menu.removeAttribute('hidden');
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() {
@@ -623,7 +601,7 @@ var gZenCompactModeManager = {
},
_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';
if (value) {
element.setAttribute(attr, 'true');
@@ -634,7 +612,8 @@ var gZenCompactModeManager = {
document.documentElement.hasAttribute('zen-has-bookmarks'))) ||
(this.preference &&
Services.prefs.getBoolPref('zen.view.compact.hide-toolbar') &&
!gZenVerticalTabsManager._hasSetSingleToolbar))
!gZenVerticalTabsManager._hasSetSingleToolbar &&
!gURLBar.hasAttribute('breakout-extend')))
) {
gBrowser.tabpanels.setAttribute('has-toolbar-hovered', 'true');
}

View File

@@ -35,8 +35,7 @@
overflow: clip;
& #urlbar:not([breakout-extend='true']) {
/* Sometimes, "opacity: 1" is forced elsewhere */
opacity: 0 !important;
opacity: 0;
pointer-events: none;
transition: opacity var(--zen-hidden-toolbar-transition);
}
@@ -49,18 +48,20 @@
}
}
& #zen-appcontent-navbar-wrapper:is(
[zen-has-hover],
[has-popup-menu],
[zen-compact-mode-active]
) {
& #zen-appcontent-navbar-wrapper[zen-has-hover],
& #zen-appcontent-navbar-wrapper[has-popup-menu],
&
#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);
overflow: inherit;
%include windows-captions-fix-active.inc.css
& #urlbar {
opacity: 1 !important;
opacity: 1;
pointer-events: auto;
}

View File

@@ -24,9 +24,6 @@
#duringOpening = false;
#ignoreClose = false;
// Click handling
#lastLinkClickData = { clientX: 0, clientY: 0, height: 0, width: 0 };
// Arc animation configuration
#ARC_CONFIG = Object.freeze({
ARC_STEPS: 70, // Increased for smoother bounce
@@ -271,31 +268,10 @@
data.height
);
return await this.#imageBitmapToBase64(
await window.browsingContext.currentWindowGlobal.drawSnapshot(
rect,
1,
'transparent',
undefined
)
await window.browsingContext.currentWindowGlobal.drawSnapshot(rect, 1, 'transparent', true)
);
}
/**
* 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
* @param {Object} data - Glance data including URL, position, and dimensions
@@ -313,13 +289,6 @@
return;
}
if (!data.height || !data.width) {
data = {
...data,
...this.lastLinkClickData,
};
}
this.#setAnimationState(true);
const currentTab = ownerTab ?? gBrowser.selectedTab;
const browserElement = this.#createBrowserElement(data.url, currentTab, existingTab);
@@ -356,13 +325,12 @@
gZenViewSplitter.onLocationChange(browserElement);
this.#prepareGlanceAnimation(data, browserElement);
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);
}
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();
this.browserWrapper.appendChild(newButtons);
this.#animateParentBackground();
this.#setupGlancePositioning(data);
this.#configureBrowserElement(browserElement);
}
@@ -519,6 +488,7 @@
// nice fade-in effect to the content. But if it doesn't exist,
// we just fall back to always showing the browser directly.
if (data.elementData) {
this.contentWrapper.style.opacity = 0;
gZenUIManager.motion
.animate(
this.contentWrapper,
@@ -533,7 +503,6 @@
});
}
this.#animateParentBackground();
gZenUIManager.motion
.animate(this.browserWrapper, arcSequence, {
duration: gZenUIManager.testingEnabled ? 0 : 0.4,
@@ -1021,7 +990,7 @@
if (!onTabClose) {
this.quickCloseGlance({ clearID: false });
}
this.overlay.style.display = 'none';
this.browserWrapper.style.display = 'none';
this.overlay.removeAttribute('fade-out');
this.browserWrapper.removeAttribute('animate');
@@ -1388,9 +1357,18 @@
* @param {Tab} tab - The tab to open glance for
*/
#openGlanceForTab(tab) {
const browserRect = window.windowUtils.getBoundsWithoutFlushing(gBrowser.tabbox);
const clickPosition = gZenUIManager._lastClickPosition || {
clientX: browserRect.width / 2,
clientY: browserRect.height / 2,
};
this.openGlance(
{
url: undefined,
...clickPosition,
width: 0,
height: 0,
},
tab,
tab.owner

View File

@@ -35,30 +35,22 @@ export class ZenGlanceChild extends JSWindowActorChild {
return !(event.ctrlKey ^ event.altKey ^ event.shiftKey ^ event.metaKey);
}
#openGlance(target) {
openGlance(target, originalTarget) {
let url = target.href;
// Add domain to relative URLs
if (!url.match(/^(?:[a-z]+:)?\/\//i)) {
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
// is a parent of the original target, use the anchor element,
// otherwise use the original target.
let rect = element.getBoundingClientRect();
let rect = originalTarget.getBoundingClientRect();
const anchorRect = target.getBoundingClientRect();
if (anchorRect.width * anchorRect.height > rect.width * rect.height) {
rect = anchorRect;
}
this.sendAsyncMessage('ZenGlance:RecordLinkClickData', {
this.sendAsyncMessage('ZenGlance:OpenGlance', {
url,
clientX: rect.left,
clientY: rect.top,
width: rect.width,
@@ -67,19 +59,7 @@ export class ZenGlanceChild extends JSWindowActorChild {
}
handleClick(event) {
if (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)) {
if (this.ensureOnlyKeyModifiers(event) || event.button !== 0 || event.defaultPrevented) {
return;
}
const activationMethod = this.#activationMethod;
@@ -92,11 +72,13 @@ export class ZenGlanceChild extends JSWindowActorChild {
} else if (activationMethod === 'meta' && !event.metaKey) {
return;
}
// get closest A element
const target = event.target.closest('A');
if (target) {
event.preventDefault();
event.stopPropagation();
this.#openGlance(target);
this.openGlance(target, event.originalTarget || event.target);
}
}

View File

@@ -23,10 +23,6 @@ export class ZenGlanceParent extends JSWindowActorParent {
this.browsingContext.topChromeWindow.gZenGlanceManager.closeGlance(params);
break;
}
case 'ZenGlance:RecordLinkClickData': {
this.browsingContext.topChromeWindow.gZenGlanceManager.lastLinkClickData = message.data;
break;
}
default:
console.warn(`[glance]: Unknown message: ${message.name}`);
}

View File

@@ -171,10 +171,10 @@
position: absolute;
pointer-events: none;
width: 100%;
height: 100%;
z-index: 0;
border-radius: var(--zen-native-inner-radius);
top: 0%;
left: 50%;
translate: -50% 0%;
inset: 50%;
translate: -50% -50%;
will-change: transform, opacity;
}

View 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 });
}
}

View 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();

View 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;
}
}

View File

@@ -101,6 +101,7 @@
}
onTabIconChanged(tab, url = null) {
tab.dispatchEvent(new CustomEvent('ZenTabIconChanged', { bubbles: true, detail: { tab } }));
const iconUrl = url ?? tab.iconImage.src;
if (!iconUrl && tab.hasAttribute('zen-pin-id')) {
try {
@@ -1098,7 +1099,7 @@
const element = window.MozXULElement.parseXULToFragment(`
<menuitem id="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"
disabled="true"
command="cmd_contextZenAddToEssentials"/>
@@ -1555,6 +1556,7 @@
}
async onTabLabelChanged(tab) {
tab.dispatchEvent(new CustomEvent('ZenTabLabelChanged', { detail: { tab } }));
if (!this._pinsCache) {
return;
}

View File

@@ -22,7 +22,9 @@ z-index: 1;
%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);
opacity: 0;
& #zen-appcontent-navbar-container {

View File

@@ -618,6 +618,12 @@
--tab-min-width: 48px !important;
--zen-toolbox-padding: 6px !important;
--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']) {
@@ -700,6 +706,7 @@
& #titlebar {
display: grid;
overflow: clip;
}
& #zen-sidebar-top-buttons-customization-target {
@@ -956,7 +963,7 @@
:root[zen-single-toolbar='true'] & {
--zen-toolbar-height: 36px;
@media (-moz-platform: macos) {
--zen-toolbar-height: 38px;
--zen-toolbar-height: 42px;
}
& #PanelUI-button {

View File

@@ -1305,13 +1305,12 @@
// Do not rebuild if the workspace is not the same as the current one
const windowWorkspace = await browser.gZenWorkspaces.getActiveWorkspace();
if (windowWorkspace.uuid !== uuid) {
if (windowWorkspace.uuid !== uuid && theme !== null) {
return;
}
// get the theme from the window
workspaceTheme = this.fixTheme(theme || windowWorkspace.theme);
const docElement = browser.document.documentElement;
if (!skipUpdate) {
for (const dot of browser.gZenThemePicker.panel.querySelectorAll(
@@ -1329,15 +1328,17 @@
}
if (!skipUpdate) {
docElement.style.setProperty(
browser.document.documentElement.style.setProperty(
'--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',
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',
browser.gZenThemePicker.previousBackgroundOpacity ?? 1
);
@@ -1460,9 +1461,16 @@
}
}
docElement.style.setProperty('--zen-main-browser-background-toolbar', gradientToolbar);
docElement.style.setProperty('--zen-main-browser-background', gradient);
browser.document.documentElement.style.setProperty(
'--zen-main-browser-background-toolbar',
gradientToolbar
);
browser.document.documentElement.style.setProperty(
'--zen-main-browser-background',
gradient
);
const isDarkModeWindow = browser.gZenThemePicker.isDarkMode;
const docElement = browser.document.documentElement;
if (isDefaultTheme) {
docElement.setAttribute('zen-default-theme', 'true');
} else {
@@ -1491,7 +1499,7 @@
}
// Set `--toolbox-textcolor` to have a contrast with the primary color
const textColor = this.getToolbarColor(isDarkMode);
docElement.style.setProperty(
document.documentElement.style.setProperty(
'--toolbox-textcolor',
`rgba(${textColor[0]}, ${textColor[1]}, ${textColor[2]}, ${textColor[3]})`
);

View 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();
}

View File

@@ -1225,13 +1225,9 @@ var gZenWorkspaces = new (class extends nsZenMultiWindowFeature {
};
const workspaceName = document.getElementById('context_zenEditWorkspace');
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 =
isCollapsed ||
(this.#contextMenuData.workspaceId &&
this.#contextMenuData.workspaceId !== this.activeWorkspace);
this.#contextMenuData.workspaceId &&
this.#contextMenuData.workspaceId !== this.activeWorkspace;
themePicker.hidden =
this.#contextMenuData.workspaceId &&
this.#contextMenuData.workspaceId !== this.activeWorkspace;