Merge branch 'zen-browser:main' into main

This commit is contained in:
Jan Hereš 2024-09-21 10:42:04 +02:00 committed by GitHub
commit 00f50176c9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 496 additions and 87 deletions

View file

@ -79,7 +79,7 @@ var gZenBrowserManagerSidebar = {
// relative to avoid the top margin
// 20px is the padding
let parentRelativeHeight =
parent.getBoundingClientRect().height - parent.getBoundingClientRect().top + sidebar.getBoundingClientRect().top - 20;
parent.getBoundingClientRect().height - parent.getBoundingClientRect().top + sidebar.getBoundingClientRect().top;
let minHeight = parseInt(computedStyle.getPropertyValue('min-height').replace('px', ''));
if (!this._isDragging) {
// Prevent multiple resizes
@ -304,7 +304,6 @@ var gZenBrowserManagerSidebar = {
let data = this.sidebarData;
let newPos = [];
for (let element of this.__dragingElement.parentNode.children) {
console.log(element);
let panelId = element.getAttribute('zen-sidebar-id');
newPos.push(panelId);
}

98
src/ZenThemesCommon.mjs Normal file
View file

@ -0,0 +1,98 @@
var ZenThemesCommon = {
kZenOSToSmallName: {
WINNT: 'windows',
Darwin: 'macos',
Linux: 'linux',
},
kZenColors: ['#aac7ff', '#74d7cb', '#a0d490', '#dec663', '#ffb787', '#dec1b1', '#ffb1c0', '#ddbfc3', '#f6b0ea', '#d4bbff'],
get currentOperatingSystem() {
let os = Services.appinfo.OS;
return this.kZenOSToSmallName[os];
},
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);
},
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 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 === this.currentOperatingSystem) ||
(os !== '' && os !== this.currentOperatingSystem && !isNegation)
) {
continue;
}
newThemePreferences.push({
property,
label,
type: 'checkbox',
disabledOn: os !== '' ? [os] : [],
});
}
return newThemePreferences;
}
return preferences.filter(({ disabledOn = [] }) => !disabledOn.includes(this.currentOperatingSystem));
},
throttle(mainFunction, delay) {
let timerFlag = null;
return (...args) => {
if (timerFlag === null) {
mainFunction(...args);
timerFlag = setTimeout(() => {
timerFlag = null;
}, delay);
}
};
},
};

View file

@ -8,14 +8,22 @@ const kZenStylesheetThemeHeader = `
const kenStylesheetFooter = `
/* End of Zen Themes */
`;
var gZenStylesheetManager = {
async writeStylesheet(path, themes) {
let content = kZenStylesheetThemeHeader;
for (let theme of themes) {
if (theme.enabled !== undefined && !theme.enabled) {
continue;
}
content += this.getThemeCSS(theme);
}
content += kenStylesheetFooter;
let buffer = new TextEncoder().encode(content);
const buffer = new TextEncoder().encode(content);
await IOUtils.write(path, buffer);
},
@ -36,8 +44,22 @@ var gZenThemeImporter = new (class {
constructor() {
console.info('ZenThemeImporter: Initiating Zen theme importer');
try {
window.SessionStore.promiseInitialized.then(() => {
window.SessionStore.promiseInitialized.then(async () => {
this.insertStylesheet();
const themesWithPreferences = await Promise.all(
Object.values(await ZenThemesCommon.getThemes()).map(async (theme) => {
const preferences = await ZenThemesCommon.getThemePreferences(theme);
return {
name: theme.name,
enabled: theme.enabled,
preferences,
};
})
);
this.writeToDom(themesWithPreferences);
});
console.info('ZenThemeImporter: Zen theme imported');
} catch (e) {
@ -57,31 +79,9 @@ var gZenThemeImporter = new (class {
return PathUtils.join(PathUtils.profileDir, 'chrome', 'zen-themes.css');
}
get themesRootPath() {
return PathUtils.join(PathUtils.profileDir, 'chrome', 'zen-themes');
}
get themesDataFile() {
return PathUtils.join(PathUtils.profileDir, 'zen-themes.json');
}
getThemeFolder(theme) {
return PathUtils.join(this.themesRootPath, theme.id);
}
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;
}
rebuildThemeStylesheet() {
this._themes = null;
this.updateStylesheet();
async rebuildThemeStylesheet() {
ZenThemesCommon.themes = null;
await this.updateStylesheet();
}
get styleSheetURI() {
@ -92,32 +92,155 @@ var gZenThemeImporter = new (class {
}
getStylesheetURIForTheme(theme) {
return Services.io.newFileURI(new FileUtils.File(PathUtils.join(this.getThemeFolder(theme), 'chrome.css')));
return Services.io.newFileURI(new FileUtils.File(PathUtils.join(ZenThemesCommon.getThemeFolder(theme.id), 'chrome.css')));
}
insertStylesheet() {
if (IOUtils.exists(this.styleSheetPath)) {
this.sss.loadAndRegisterSheet(this.styleSheetURI, this.sss.AGENT_SHEET);
async insertStylesheet() {
if (await IOUtils.exists(this.styleSheetPath)) {
await this.sss.loadAndRegisterSheet(this.styleSheetURI, this.sss.AGENT_SHEET);
}
}
removeStylesheet() {
this.sss.unregisterSheet(this.styleSheetURI, this.sss.AGENT_SHEET);
async removeStylesheet() {
await this.sss.unregisterSheet(this.styleSheetURI, this.sss.AGENT_SHEET);
}
async updateStylesheet() {
this.removeStylesheet();
await this.writeStylesheet();
this.insertStylesheet();
await this.removeStylesheet();
const themes = Object.values(await ZenThemesCommon.getThemes());
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 writeStylesheet() {
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) {
const browser = ZenThemesCommon.currentBrowser;
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 = [];
this._themes = null;
for (let theme of Object.values(await this.getThemes())) {
ZenThemesCommon.themes = null;
for (let theme of themeList) {
theme._chromeURL = this.getStylesheetURIForTheme(theme).spec;
themes.push(theme);
}
await gZenStylesheetManager.writeStylesheet(this.styleSheetPath, themes);
}
})();

View file

@ -536,8 +536,9 @@ var gZenViewSplitter = new (class {
if (this.currentView < 0) return;
const currentTab = window.gBrowser.selectedTab;
const tabs = this._data[this.currentView].tabs;
for (const tab of tabs) {
this.handleTabClose({ target: tab, forUnsplit: true });
// note: This MUST be an index loop, as we are removing tabs from the array
for (let i = tabs.length - 1; i >= 0; i--) {
this.handleTabClose({ target: tabs[i], forUnsplit: true });
}
window.gBrowser.selectedTab = currentTab;
this.updateSplitViewButton(true);
@ -562,7 +563,7 @@ var gZenViewSplitter = new (class {
this.unsplitCurrentView();
return;
}
const tabs = gBrowser.tabs;
const tabs = gBrowser.visibleTabs;
if (tabs.length < 2) {
return;
}

View file

@ -1,11 +1,12 @@
var ZenWorkspaces = {
async init() {
let docElement = document.documentElement;
if (
docElement.getAttribute('chromehidden').includes('toolbar') ||
docElement.getAttribute('chromehidden').includes('menubar') ||
docElement.hasAttribute('privatebrowsingmode')
) {
/**
* Stores workspace IDs and their last selected tabs.
*/
_lastSelectedWorkspaceTabs: {},
init() {
if (!this.shouldHaveWorkspaces) {
console.warn('ZenWorkspaces: !!! ZenWorkspaces is disabled in hidden windows !!!');
return; // We are in a hidden window, don't initialize ZenWorkspaces
}
@ -16,8 +17,29 @@ var ZenWorkspaces = {
});
},
get shouldShowIconStrip() {
delete this.shouldShowIconStrip;
this.shouldShowIconStrip = Services.prefs.getBoolPref('zen.workspaces.show-icon-strip', true);
return this.shouldShowIconStrip;
},
get shouldHaveWorkspaces() {
delete this.shouldHaveWorkspaces;
let docElement = document.documentElement;
this.shouldHaveWorkspaces = !(docElement.hasAttribute('privatebrowsingmode')
|| docElement.getAttribute('chromehidden').includes('toolbar')
|| docElement.getAttribute('chromehidden').includes('menubar'));
return this.shouldHaveWorkspaces;
},
get workspaceEnabled() {
return Services.prefs.getBoolPref('zen.workspaces.enabled', false);
delete this.workspaceEnabled;
this.workspaceEnabled = Services.prefs.getBoolPref('zen.workspaces.enabled', false) && this.shouldHaveWorkspaces;
return this.workspaceEnabled;
},
getActiveWorkspaceFromCache() {
return this._workspaceCache.workspaces.find((workspace) => workspace.used);
},
// Wrorkspaces saving/loading
@ -47,15 +69,25 @@ var ZenWorkspaces = {
}
},
async onWorkspacesIconStripChanged() {
this.shouldShowIconStrip = Services.prefs.getBoolPref('zen.workspaces.show-icon-strip', true);
await this._expandWorkspacesStrip();
},
async initializeWorkspaces() {
Services.prefs.addObserver('zen.workspaces.enabled', this.onWorkspacesEnabledChanged.bind(this));
this.initializeWorkspacesButton();
Services.prefs.addObserver('zen.workspaces.show-icon-strip', this.onWorkspacesIconStripChanged.bind(this));
let file = new FileUtils.File(this._storeFile);
if (!file.exists()) {
await IOUtils.writeJSON(this._storeFile, {});
}
await this.initializeWorkspacesButton();
if (this.workspaceEnabled) {
this._initializeWorkspaceCreationIcons();
this._initializeWorkspaceEditIcons();
this._initializeWorkspaceTabContextMenus();
window.addEventListener('TabClose', this.handleTabClose.bind(this));
window.addEventListener('TabBrowserInserted', this.onTabBrowserInserted.bind(this));
let workspaces = await this._workspaces();
if (workspaces.workspaces.length === 0) {
await this.createAndSaveWorkspace('Default Workspace', true);
@ -73,9 +105,6 @@ var ZenWorkspaces = {
}
this.changeWorkspace(activeWorkspace, true);
}
this._initializeWorkspaceCreationIcons();
this._initializeWorkspaceEditIcons();
this._initializeWorkspaceTabContextMenus();
}
},
@ -91,12 +120,12 @@ var ZenWorkspaces = {
if (tabs.length === 1) {
this._createNewTabForWorkspace({ uuid: workspaceID });
// We still need to close other tabs in the workspace
this.changeWorkspace({ uuid: workspaceID });
this.changeWorkspace({ uuid: workspaceID }, true);
}
}
},
_kIcons: ['🏠', '📄', '💹', '💼', '📧', '✅', '👥'],
_kIcons: JSON.parse(Services.prefs.getStringPref("zen.workspaces.icons")).map((icon) => icon),
_initializeWorkspaceCreationIcons() {
let container = document.getElementById('PanelUI-zen-workspaces-create-icons-container');
@ -160,6 +189,7 @@ var ZenWorkspaces = {
console.info('ZenWorkspaces: Removing workspace', windowID);
await this.changeWorkspace(json.workspaces.find((workspace) => workspace.uuid !== windowID));
this._deleteAllTabsInWorkspace(windowID);
delete this._lastSelectedWorkspaceTabs[windowID];
json.workspaces = json.workspaces.filter((workspace) => workspace.uuid !== windowID);
await this.unsafeSaveWorkspaces(json);
await this._propagateWorkspaceData();
@ -219,20 +249,41 @@ var ZenWorkspaces = {
return workspace.name[0].toUpperCase();
},
async _propagateWorkspaceData() {
async _propagateWorkspaceData({
ignoreStrip = false
} = {}) {
let currentContainer = document.getElementById('PanelUI-zen-workspaces-current-info');
let workspaceList = document.getElementById('PanelUI-zen-workspaces-list');
if (!ignoreStrip) {
await this._expandWorkspacesStrip();
}
const createWorkspaceElement = (workspace) => {
let element = document.createXULElement('toolbarbutton');
element.className = 'subviewbutton';
element.setAttribute('tooltiptext', workspace.name);
element.setAttribute('zen-workspace-id', workspace.uuid);
//element.setAttribute("context", "zenWorkspaceActionsMenu");
if (workspace.used) {
element.setAttribute('active', 'true');
}
if (workspace.default) {
element.setAttribute('default', 'true');
}
const containerGroup = ContextualIdentityService.getPublicIdentities().find(
(container) => container.userContextId === workspace.containerTabId
);
if (containerGroup) {
element.classList.add('identity-color-' + containerGroup.color);
element.setAttribute('data-usercontextid', containerGroup.userContextId);
}
let childs = window.MozXULElement.parseXULToFragment(`
<div class="zen-workspace-icon">
</div>
<div class="zen-workspace-name">
</div>
<vbox>
<div class="zen-workspace-name">
</div>
<div class="zen-workspace-container" ${containerGroup ? '' : 'hidden="true"'}>
</div>
</vbox>
<toolbarbutton closemenu="none" class="toolbarbutton-1 zen-workspace-actions">
<image class="toolbarbutton-icon" id="zen-workspace-actions-menu-icon"></image>
</toolbarbutton>
@ -241,6 +292,11 @@ var ZenWorkspaces = {
// use text content instead of innerHTML to avoid XSS
childs.querySelector('.zen-workspace-icon').textContent = this.getWorkspaceIcon(workspace);
childs.querySelector('.zen-workspace-name').textContent = workspace.name;
if (containerGroup) {
childs.querySelector('.zen-workspace-container').textContent = ContextualIdentityService.getUserContextLabel(
containerGroup.userContextId
);
}
childs.querySelector('.zen-workspace-actions').addEventListener('command', (event) => {
let button = event.target;
@ -290,14 +346,16 @@ var ZenWorkspaces = {
}
let target = event.target;
let panel = document.getElementById('PanelUI-zen-workspaces');
await this._propagateWorkspaceData();
await this._propagateWorkspaceData({
ignoreStrip: true
});
PanelMultiView.openPopup(panel, target, {
position: 'bottomright topright',
triggerEvent: event,
}).catch(console.error);
},
initializeWorkspacesButton() {
async initializeWorkspacesButton() {
if (!this.workspaceEnabled) {
return;
} else if (document.getElementById('zen-workspaces-button')) {
@ -305,14 +363,60 @@ var ZenWorkspaces = {
button.removeAttribute('hidden');
return;
}
let browserTabs = document.getElementById('tabbrowser-tabs');
let button = document.createElement('toolbarbutton');
button.id = 'zen-workspaces-button';
button.className = 'toolbarbutton-1 chromeclass-toolbar-additional';
button.setAttribute('label', 'Workspaces');
button.setAttribute('tooltiptext', 'Workspaces');
button.onclick = this.openWorkspacesDialog.bind(this);
browserTabs.insertAdjacentElement('beforebegin', button);
const nextSibling = document.getElementById('zen-sidepanel-button');
const wrapper = document.createXULElement('toolbarbutton');
wrapper.id = 'zen-workspaces-button';
nextSibling.after(wrapper);
await this._expandWorkspacesStrip();
},
async _expandWorkspacesStrip() {
let workspaces = await this._workspaces();
let workspaceList = document.getElementById('zen-workspaces-button');
const newWorkspacesButton = document.createXULElement(this.shouldShowIconStrip ? 'hbox' : 'toolbarbutton');
newWorkspacesButton.id = 'zen-workspaces-button';
newWorkspacesButton.setAttribute('removable', 'false');
newWorkspacesButton.setAttribute('showInPrivateBrowsing', 'false');
newWorkspacesButton.setAttribute('tooltiptext', 'Workspaces');
if (this.shouldShowIconStrip) {
for (let workspace of workspaces.workspaces) {
let button = document.createXULElement('toolbarbutton');
button.className = 'subviewbutton';
button.setAttribute('tooltiptext', workspace.name);
button.setAttribute('zen-workspace-id', workspace.uuid);
if (workspace.used) {
button.setAttribute('active', 'true');
}
if (workspace.default) {
button.setAttribute('default', 'true');
}
button.onclick = (async (_, event) => {
// Make sure it's not a context menu event
if (event.button !== 0) {
return;
}
await this.changeWorkspace(workspace);
}).bind(this, workspace);
let icon = document.createXULElement('div');
icon.className = 'zen-workspace-icon';
icon.textContent = this.getWorkspaceIcon(workspace);
button.appendChild(icon);
newWorkspacesButton.appendChild(button);
}
// Listen for context menu events and open the all workspaces dialog
newWorkspacesButton.addEventListener('contextmenu', (event) => {
event.preventDefault();
this.openWorkspacesDialog(event);
});
}
workspaceList.after(newWorkspacesButton);
workspaceList.remove();
if (!this.shouldShowIconStrip) {
await this._updateWorkspacesButton();
}
},
async _updateWorkspacesButton() {
@ -322,20 +426,33 @@ var ZenWorkspaces = {
}
let activeWorkspace = (await this._workspaces()).workspaces.find((workspace) => workspace.used);
if (activeWorkspace) {
button.innerHTML = `
<div class="zen-workspace-sidebar-icon">
</div>
<div class="zen-workspace-sidebar-name">
</div>
`;
button.setAttribute('as-button', 'true');
button.classList.add('toolbarbutton-1', 'zen-sidebar-action-button');
button.addEventListener('click', this.openWorkspacesDialog.bind(this));
const wrapper = document.createXULElement('hbox');
wrapper.className = 'zen-workspace-sidebar-wrapper';
const icon = document.createElement('div');
icon.className = 'zen-workspace-sidebar-icon';
icon.textContent = this.getWorkspaceIcon(activeWorkspace);
// use text content instead of innerHTML to avoid XSS
button.querySelector('.zen-workspace-sidebar-name').textContent = activeWorkspace.name;
button.querySelector('.zen-workspace-sidebar-icon').textContent = this.getWorkspaceIcon(activeWorkspace);
const name = document.createElement('div');
name.className = 'zen-workspace-sidebar-name';
name.textContent = activeWorkspace.name;
if (!this.workspaceHasIcon(activeWorkspace)) {
button.querySelector('.zen-workspace-sidebar-icon').setAttribute('no-icon', 'true');
icon.setAttribute('no-icon', 'true');
}
wrapper.appendChild(icon);
wrapper.appendChild(name);
button.innerHTML = '';
button.appendChild(wrapper);
}
},
@ -442,24 +559,39 @@ var ZenWorkspaces = {
button.removeAttribute('disabled');
},
get _shouldAllowPinTab() {
return Services.prefs.getBoolPref('zen.workspaces.individual-pinned-tabs');
},
get tabContainer() {
delete this.tabContainer;
return (this.tabContainer = document.getElementById("tabbrowser-tabs"));
},
async changeWorkspace(window, onInit = false) {
if (!this.workspaceEnabled) {
return;
}
this.tabContainer._invalidateCachedTabs();
const shouldAllowPinnedTabs = this._shouldAllowPinTab;
let firstTab = undefined;
let workspaces = await this._workspaces();
for (let workspace of workspaces.workspaces) {
if (workspace.uuid === window.uuid && workspace.used) {
// If the workspace is already active, do nothing
return;
}
workspace.used = workspace.uuid === window.uuid;
}
this.unsafeSaveWorkspaces(workspaces);
await this.unsafeSaveWorkspaces(workspaces);
console.info('ZenWorkspaces: Changing workspace to', window.uuid);
for (let tab of gBrowser.tabs) {
if ((tab.getAttribute('zen-workspace-id') === window.uuid && !tab.pinned) || !tab.hasAttribute('zen-workspace-id')) {
if ((tab.getAttribute('zen-workspace-id') === window.uuid && !(tab.pinned && !shouldAllowPinnedTabs)) || !tab.hasAttribute('zen-workspace-id')) {
if (!firstTab) {
firstTab = tab;
} else if (gBrowser.selectedTab === tab) {
// If the selected tab is already in the workspace, we don't want to change it
firstTab = undefined;
firstTab = null; // note: Do not add "undefined" here, a new tab would be created
}
gBrowser.showTab(tab);
if (!tab.hasAttribute('zen-workspace-id')) {
@ -470,21 +602,26 @@ var ZenWorkspaces = {
}
}
if (firstTab) {
gBrowser.selectedTab = firstTab;
gBrowser.selectedTab = this._lastSelectedWorkspaceTabs[window.uuid] ?? firstTab;
}
if (typeof firstTab === 'undefined' && !onInit) {
this._createNewTabForWorkspace(window);
}
for (let tab of gBrowser.tabs) {
if (tab.getAttribute('zen-workspace-id') !== window.uuid) {
gBrowser.hideTab(tab);
// FOR UNLOADING TABS:
// gBrowser.discardBrowser(tab, true);
gBrowser.hideTab(tab, undefined, shouldAllowPinnedTabs);
}
}
this.tabContainer._invalidateCachedTabs();
document.documentElement.setAttribute('zen-workspace-id', window.uuid);
await this.saveWorkspaces();
await this._updateWorkspacesButton();
await this._propagateWorkspaceData();
await this._updateWorkspacesChangeContextMenu();
document.getElementById('tabbrowser-tabs')._positionPinnedTabs();
},
async _updateWorkspacesChangeContextMenu() {
@ -530,6 +667,19 @@ var ZenWorkspaces = {
await this.changeWorkspace(workspaceData);
},
async onTabBrowserInserted(event) {
let tab = event.originalTarget;
if (tab.getAttribute('zen-workspace-id') || !this.workspaceEnabled) {
return;
}
let workspaces = await this._workspaces();
let activeWorkspace = workspaces.workspaces.find((workspace) => workspace.used);
if (!activeWorkspace) {
return;
}
tab.setAttribute('zen-workspace-id', activeWorkspace.uuid);
},
async onLocationChange(browser) {
let tab = gBrowser.getTabForBrowser(browser);
let workspaceID = tab.getAttribute('zen-workspace-id');
@ -540,7 +690,9 @@ var ZenWorkspaces = {
return;
}
tab.setAttribute('zen-workspace-id', activeWorkspace.uuid);
workspaceID = activeWorkspace.uuid;
}
this._lastSelectedWorkspaceTabs[workspaceID] = tab;
},
// Context menu management
@ -575,6 +727,15 @@ var ZenWorkspaces = {
}
},
async contextChangeContainerTab(event) {
let workspaces = await this._workspaces();
let workspace = workspaces.workspaces.find((workspace) => workspace.uuid === this._contextMenuId);
let userContextId = parseInt(event.target.getAttribute('data-usercontextid'));
workspace.containerTabId = userContextId;
await this.saveWorkspace(workspace);
await this._propagateWorkspaceData();
},
onContextMenuClose() {
let target = document.querySelector(
`#PanelUI-zen-workspaces [zen-workspace-id="${this._contextMenuId}"] .zen-workspace-actions`
@ -637,12 +798,37 @@ var ZenWorkspaces = {
async changeTabWorkspace(workspaceID) {
const tabs = TabContextMenu.contextTab.multiselected ? gBrowser.selectedTabs : [TabContextMenu.contextTab];
const previousWorkspaceID = document.documentElement.getAttribute('zen-workspace-id');
for (let tab of tabs) {
tab.setAttribute('zen-workspace-id', workspaceID);
if (this._lastSelectedWorkspaceTabs[previousWorkspaceID] === tab) {
// This tab is no longer the last selected tab in the previous workspace because it's being moved to
// the current workspace
delete this._lastSelectedWorkspaceTabs[previousWorkspaceID];
}
}
const workspaces = await this._workspaces();
await this.changeWorkspace(workspaces.workspaces.find((workspace) => workspace.uuid === workspaceID));
},
// Tab browser utilities
createContainerTabMenu(event) {
let window = event.target.ownerGlobal;
const workspace = this._workspaceCache.workspaces.find((workspace) => this._contextMenuId === workspace.uuid);
let containerTabId = workspace.containerTabId;
return window.createUserContextMenu(event, {
isContextMenu: true,
excludeUserContextId: containerTabId,
showDefaultTab: true,
});
},
getContextIdIfNeeded(userContextId) {
if (typeof userContextId !== 'undefined' || !this.workspaceEnabled) {
return [userContextId, false];
}
const activeWorkspace = this.getActiveWorkspaceFromCache();
return [activeWorkspace?.containerTabId, true];
},
};
ZenWorkspaces.init();

View file

@ -8,6 +8,7 @@ export class ZenThemeMarketplaceParent extends JSWindowActorParent {
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);
@ -73,6 +74,7 @@ export class ZenThemeMarketplaceParent extends JSWindowActorParent {
}
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;