chore: Run lint and fund dependencies, p=#12718

This commit is contained in:
mr. m
2026-03-10 21:34:24 +01:00
committed by GitHub
parent c4948ee0cd
commit 036cfb187c
131 changed files with 10249 additions and 3453 deletions

17
src/-prettierignore.patch Normal file
View File

@@ -0,0 +1,17 @@
diff --git a/.prettierignore b/.prettierignore
index cbca8bb4b36cecc44e6b498e9ef15bc4bdc21871..8f3a14e14a2d58875bdd6f04bd31f57e23073148 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -1795,3 +1795,12 @@ tools/ts/test/baselines/
try_task_config.json
xpcom/idl-parser/xpidl/fixtures/xpctest.d.json
**/package-lock.json
+
+
+*.bundle.min.js
+*.min.js
+*.min.mjs
+*.inc
+*/mochitests/*
+*.svg
+

View File

@@ -1,19 +1,13 @@
diff --git a/.stylelintignore b/.stylelintignore
index 185490999507b8a5032977237af50f5e61c71df1..36f760f473b857e851134ceb62c837cb8d29c966 100644
index 185490999507b8a5032977237af50f5e61c71df1..e887fafa90b881e852a287ed8898638c995861ab 100644
--- a/.stylelintignore
+++ b/.stylelintignore
@@ -106,3 +106,26 @@ build/pgo/blueprint/**/*.css
@@ -106,3 +106,19 @@ build/pgo/blueprint/**/*.css
# under our control or we don't want to modify at this point:
testing/web-platform/mozilla/
testing/web-platform/tests/
+
+**/*.bundle.min.js
+**/*.min.js
+**/*.min.mjs
+
+**/*.svg
+
+**/*.inc.css
+*.inc.css
+
+zen/tests/mochitests/*
+
@@ -22,7 +16,6 @@ index 185490999507b8a5032977237af50f5e61c71df1..36f760f473b857e851134ceb62c837cb
+zen/tabs/zen-tabs.css
+zen/common/styles/zen-theme.css
+zen/compact-mode/zen-compact-mode.css
+zen/common/ZenEmojis.mjs
+
+zen/split-view/zen-decks.css
+zen/workspaces/zen-workspaces.css

View File

@@ -1,12 +1,13 @@
diff --git a/eslint-ignores.config.mjs b/eslint-ignores.config.mjs
index 0cfd7e02ad58c331f48f1ba8e1588777e1ce2595..d85b95b18a7195b6794083af71cbe1947d9f2f9c 100644
index 0cfd7e02ad58c331f48f1ba8e1588777e1ce2595..888674b5ed2b68dbe77eb177ba0947f94ed57c80 100644
--- a/eslint-ignores.config.mjs
+++ b/eslint-ignores.config.mjs
@@ -312,4 +312,7 @@ export default [
@@ -312,4 +312,8 @@ export default [
// Test files for circular import in modules.
"dom/base/test/jsmodules/import_circular.mjs",
"dom/base/test/jsmodules/import_circular_1.mjs",
+
+ "**/*.min.mjs",
+ "zen/common/emojis/ZenEmojisData.min.mjs",
+ "zen/tests/**",
+ "zen/vendor/**",
];

View File

@@ -0,0 +1,13 @@
diff --git a/tools/lint/eslint/__init__.py b/tools/lint/eslint/__init__.py
index cd45822500a8b5e1112efad81ed34e01c0dbcc19..9f47b4a46bf1c36db06b45e047a939ae08bcb703 100644
--- a/tools/lint/eslint/__init__.py
+++ b/tools/lint/eslint/__init__.py
@@ -114,7 +114,7 @@ def lint(paths, config, binary=None, fix=None, rules=[], setup=None, **lintargs)
[
binary,
os.path.join(
- module_path, "node_modules", "prettier", "bin", "prettier.cjs"
+ module_path, "..", "node_modules", "@zen-browser", "prettier", "bin", "prettier.cjs"
),
"--list-different",
"--no-error-on-unmatched-pattern",

View File

@@ -53,9 +53,15 @@ declare global {
dockMenu: nsIStandaloneNativeMenu;
activateApplication(aIgnoreOtherApplications: boolean): void;
badgeText: string;
setBadgeImage(aBadgeImage: imgIContainer, aPaintContext?: nsISVGPaintContext): void;
setBadgeImage(
aBadgeImage: imgIContainer,
aPaintContext?: nsISVGPaintContext
): void;
readonly isAppInDock: boolean;
ensureAppIsPinnedToDock(aAppPath?: string, aAppToReplacePath?: string): boolean;
ensureAppIsPinnedToDock(
aAppPath?: string,
aAppToReplacePath?: string
): boolean;
launchAppBundle(
aAppBundle: nsIFile,
aArgs: string[],
@@ -70,7 +76,10 @@ declare global {
}>;
interface nsIMacFinderProgress extends nsISupports {
init(path: string, canceledCallback: nsIMacFinderProgressCanceledCallback): void;
init(
path: string,
canceledCallback: nsIMacFinderProgressCanceledCallback
): void;
updateProgress(currentProgress: u64, totalProgress: u64): void;
end(): void;
}
@@ -86,7 +95,11 @@ declare global {
// https://searchfox.org/mozilla-central/source/widget/nsIMacUserActivityUpdater.idl
interface nsIMacUserActivityUpdater extends nsISupports {
updateLocation(pageUrl: string, pageTitle: string, window: nsIBaseWindow): void;
updateLocation(
pageUrl: string,
pageTitle: string,
window: nsIBaseWindow
): void;
}
// https://searchfox.org/mozilla-central/source/widget/nsIMacWebAppUtils.idl
@@ -120,7 +133,11 @@ declare global {
readonly STATE_ERROR?: 3;
readonly STATE_PAUSED?: 4;
setProgressState(state: nsTaskbarProgressState, currentValue?: u64, maxValue?: u64): void;
setProgressState(
state: nsTaskbarProgressState,
currentValue?: u64,
maxValue?: u64
): void;
}
// https://searchfox.org/mozilla-central/source/widget/nsITouchBarHelper.idl
@@ -157,11 +174,18 @@ declare global {
// https://searchfox.org/mozilla-central/source/widget/nsITouchBarUpdater.idl
interface nsITouchBarUpdater extends nsISupports {
updateTouchBarInputs(aWindow: nsIBaseWindow, aInputs: nsITouchBarInput[]): void;
updateTouchBarInputs(
aWindow: nsIBaseWindow,
aInputs: nsITouchBarInput[]
): void;
enterCustomizeMode(): void;
isTouchBarInitialized(): boolean;
setTouchBarInitialized(aIsInitialized: boolean): void;
showPopover(aWindow: nsIBaseWindow, aPopover: nsITouchBarInput, aShowing: boolean): void;
showPopover(
aWindow: nsIBaseWindow,
aPopover: nsITouchBarInput,
aShowing: boolean
): void;
}
// https://searchfox.org/mozilla-central/source/xpcom/base/nsIMacPreferencesReader.idl

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -51,7 +51,11 @@ declare global {
readonly STATE_ERROR?: 3;
readonly STATE_PAUSED?: 4;
setProgressState(state: nsTaskbarProgressState, currentValue?: u64, maxValue?: u64): void;
setProgressState(
state: nsTaskbarProgressState,
currentValue?: u64,
maxValue?: u64
): void;
}
interface nsIXPCComponents_Interfaces {

View File

@@ -705,4 +705,5 @@ interface nsIXPCComponents_Results {
NS_ERROR_DOM_QM_CLIENT_INIT_ORIGIN_UNINITIALIZED: 0x80730001;
}
type nsIXPCComponents_Values = nsIXPCComponents_Results[keyof nsIXPCComponents_Results];
type nsIXPCComponents_Values =
nsIXPCComponents_Results[keyof nsIXPCComponents_Results];

View File

@@ -33,11 +33,15 @@ interface Document {
}
type nsIGleanPingNoReason = {
[K in keyof nsIGleanPing]: K extends "submit" ? (_?: never) => void : nsIGleanPing[K];
[K in keyof nsIGleanPing]: K extends "submit"
? (_?: never) => void
: nsIGleanPing[K];
};
type nsIGleanPingWithReason<T> = {
[K in keyof nsIGleanPing]: K extends "submit" ? (reason: T) => void : nsIGleanPing[K];
[K in keyof nsIGleanPing]: K extends "submit"
? (reason: T) => void
: nsIGleanPing[K];
};
interface MessageListenerManagerMixin {

View File

@@ -56,14 +56,18 @@ declare global {
}
interface nsIWindowsAlertNotification
extends nsIAlertNotification,
extends
nsIAlertNotification,
Enums<typeof nsIWindowsAlertNotification_ImagePlacement> {
imagePlacement: nsIWindowsAlertNotification.ImagePlacement;
}
interface nsIWindowsAlertsService extends nsIAlertsService {
handleWindowsTag(aWindowsTag: string): Promise<any>;
getXmlStringForWindowsAlert(aAlert: nsIAlertNotification, aWindowsTag?: string): string;
getXmlStringForWindowsAlert(
aAlert: nsIAlertNotification,
aWindowsTag?: string
): string;
removeAllNotificationsForInstall(): void;
}
@@ -86,9 +90,18 @@ declare global {
aNotificationAction: string,
daysSinceLastAppLaunch: u32
): void;
setDefaultBrowserUserChoice(aAumid: string, aExtraFileExtensions: string[]): void;
setDefaultBrowserUserChoiceAsync(aAumid: string, aExtraFileExtensions: string[]): Promise<any>;
setDefaultExtensionHandlersUserChoice(aAumid: string, aFileExtensions: string[]): void;
setDefaultBrowserUserChoice(
aAumid: string,
aExtraFileExtensions: string[]
): void;
setDefaultBrowserUserChoiceAsync(
aAumid: string,
aExtraFileExtensions: string[]
): Promise<any>;
setDefaultExtensionHandlersUserChoice(
aAumid: string,
aFileExtensions: string[]
): void;
agentDisabled(): boolean;
}
@@ -124,11 +137,13 @@ declare enum nsIWindowsShellService_LaunchOnLoginEnabledEnumerator {
declare global {
namespace nsIWindowsShellService {
type LaunchOnLoginEnabledEnumerator = nsIWindowsShellService_LaunchOnLoginEnabledEnumerator;
type LaunchOnLoginEnabledEnumerator =
nsIWindowsShellService_LaunchOnLoginEnabledEnumerator;
}
interface nsIWindowsShellService
extends nsIShellService,
extends
nsIShellService,
Enums<typeof nsIWindowsShellService_LaunchOnLoginEnabledEnumerator> {
createShortcut(
aBinary: nsIFile,
@@ -149,13 +164,19 @@ declare global {
pinCurrentAppToTaskbarAsync(aPrivateBrowsing: boolean): Promise<any>;
checkPinCurrentAppToTaskbarAsync(aPrivateBrowsing: boolean): Promise<any>;
isCurrentAppPinnedToTaskbarAsync(aumid: string): Promise<any>;
pinShortcutToTaskbar(aAppUserModelId: string, aShortcutPath: string): Promise<any>;
pinShortcutToTaskbar(
aAppUserModelId: string,
aShortcutPath: string
): Promise<any>;
createWindowsIcon(aFile: nsIFile, aContainer: imgIContainer): Promise<any>;
unpinShortcutFromTaskbar(aShortcutPath: string): void;
getTaskbarTabShortcutPath(aShortcutName: string): string;
getTaskbarTabPins(): string[];
classifyShortcut(aPath: string): string;
hasPinnableShortcut(aAUMID: string, aPrivateBrowsing: boolean): Promise<any>;
hasPinnableShortcut(
aAUMID: string,
aPrivateBrowsing: boolean
): Promise<any>;
canSetDefaultBrowserUserChoice(): boolean;
checkAllProgIDsExist(): boolean;
checkBrowserUserChoiceHashes(): boolean;
@@ -241,7 +262,11 @@ declare global {
readonly height: u32;
readonly thumbnailAspectRatio: float;
requestPreview(aCallback: nsITaskbarPreviewCallback): void;
requestThumbnail(aCallback: nsITaskbarPreviewCallback, width: u32, height: u32): void;
requestThumbnail(
aCallback: nsITaskbarPreviewCallback,
width: u32,
height: u32
): void;
onClose(): void;
onActivate(): boolean;
onClick(button: nsITaskbarPreviewButton): void;
@@ -256,7 +281,11 @@ declare global {
readonly STATE_ERROR?: 3;
readonly STATE_PAUSED?: 4;
setProgressState(state: nsTaskbarProgressState, currentValue?: u64, maxValue?: u64): void;
setProgressState(
state: nsTaskbarProgressState,
currentValue?: u64,
maxValue?: u64
): void;
}
// https://searchfox.org/mozilla-central/source/widget/nsITaskbarTabPreview.idl
@@ -288,7 +317,9 @@ declare global {
): nsITaskbarTabPreview;
getTaskbarWindowPreview(shell: nsIDocShell): nsITaskbarWindowPreview;
getTaskbarProgress(shell: nsIDocShell): nsITaskbarProgress;
getOverlayIconController(shell: nsIDocShell): nsITaskbarOverlayIconController;
getOverlayIconController(
shell: nsIDocShell
): nsITaskbarOverlayIconController;
createJumpListBuilder(aPrivateBrowsing: boolean): nsIJumpListBuilder;
getGroupIdForWindow(aParent: mozIDOMWindow): string;
setGroupIdForWindow(aParent: mozIDOMWindow, aIdentifier: string): void;
@@ -304,7 +335,11 @@ declare global {
aSmallIcon: imgIContainer,
aLargeIcon: imgIContainer
): void;
setWindowIconFromExe(aWindow: mozIDOMWindowProxy, aExe: string, aIndex: u16): void;
setWindowIconFromExe(
aWindow: mozIDOMWindowProxy,
aExe: string,
aIndex: u16
): void;
setWindowIconNoData(aWindow: mozIDOMWindowProxy): void;
readonly inWin10TabletMode: boolean;
readonly inWin11TabletMode: boolean;

File diff suppressed because it is too large Load Diff

View File

@@ -88,7 +88,9 @@ declare namespace MockedExports {
*
* Then add the file path to the KnownModules above.
*/
importESModule: <S extends keyof KnownModules>(module: S) => KnownModules[S];
importESModule: <S extends keyof KnownModules>(
module: S
) => KnownModules[S];
defineESModuleGetters: (target: any, mappings: any) => void;
}
@@ -160,7 +162,11 @@ declare namespace MockedExports {
setIntPref: SetPref<number>;
getBoolPref: GetPref<boolean>;
setBoolPref: SetPref<boolean>;
addObserver: (aDomain: string, aObserver: PrefObserver, aHoldWeak?: boolean) => void;
addObserver: (
aDomain: string,
aObserver: PrefObserver,
aHoldWeak?: boolean
) => void;
removeObserver: (aDomain: string, aObserver: PrefObserver) => void;
};
@@ -299,7 +305,11 @@ declare namespace MockedExports {
class nsIFilePicker {}
interface FilePicker {
init: (browsingContext: BrowsingContext, title: string, mode: number) => void;
init: (
browsingContext: BrowsingContext,
title: string,
mode: number
) => void;
open: (callback: (rv: number) => unknown) => void;
// The following are enum values.
modeGetFolder: number;
@@ -330,7 +340,11 @@ declare namespace MockedExports {
* This function sets the attributes data-l10n-id and possibly data-l10n-args
* on the element.
*/
setAttributes(target: Element, id?: string, args?: Record<string, string>): void;
setAttributes(
target: Element,
id?: string,
args?: Record<string, string>
): void;
}
}
@@ -365,7 +379,8 @@ declare interface ChromeDocument extends Document {
* Create a XUL element of a specific type. Right now this function
* only refines iframes, but more tags could be added.
*/
createXULElement: ((type: "iframe") => XULIframeElement) & ((type: string) => XULElement);
createXULElement: ((type: "iframe") => XULIframeElement) &
((type: string) => XULElement);
/**
* This is a fluent instance connected to this document.
@@ -406,7 +421,9 @@ declare interface Window {
userContextId: number;
forceNonPrivate: boolean;
relatedToCurrent: boolean;
resolveOnContentBrowserCreated: (contentBrowser: MockedExports.ChromeBrowser) => unknown;
resolveOnContentBrowserCreated: (
contentBrowser: MockedExports.ChromeBrowser
) => unknown;
}>
) => void;
openTrustedLinkIn: (
@@ -417,7 +434,9 @@ declare interface Window {
userContextId: number;
forceNonPrivate: boolean;
relatedToCurrent: boolean;
resolveOnContentBrowserCreated: (contentBrowser: MockedExports.ChromeBrowser) => unknown;
resolveOnContentBrowserCreated: (
contentBrowser: MockedExports.ChromeBrowser
) => unknown;
}>
) => void;
}

View File

@@ -63,7 +63,9 @@ class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
case "command":
if (event.target.id === "PanelUI-zen-emojis-picker-none") {
this.#selectEmoji(null);
} else if (event.target.id === "PanelUI-zen-emojis-picker-change-emojis") {
} else if (
event.target.id === "PanelUI-zen-emojis-picker-change-emojis"
) {
this.#changePage(false);
} else if (event.target.id === "PanelUI-zen-emojis-picker-change-svg") {
this.#changePage(true);
@@ -104,7 +106,9 @@ class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
#changePage(toSvg = false) {
const itemToScroll = toSvg
? this.svgList
: document.getElementById("PanelUI-zen-emojis-picker-pages").querySelector('[emojis="true"]');
: document
.getElementById("PanelUI-zen-emojis-picker-pages")
.querySelector('[emojis="true"]');
itemToScroll.scrollIntoView({
behavior: "smooth",
block: "nearest",
@@ -137,15 +141,19 @@ class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
const value = input.value.trim().toLowerCase();
// search for emojis.tags and order by emojis.order
const filteredEmojis = this.#emojis
.filter((emoji) => {
return emoji.tags.some((tag) => tag.toLowerCase().includes(value));
.filter(emoji => {
return emoji.tags.some(tag => tag.toLowerCase().includes(value));
})
.sort((a, b) => a.order - b.order);
for (const button of this.emojiList.children) {
const buttonEmoji = button.getAttribute("label");
const emojiObject = filteredEmojis.find((emoji) => emoji.emoji === buttonEmoji);
const emojiObject = filteredEmojis.find(
emoji => emoji.emoji === buttonEmoji
);
if (emojiObject) {
button.hidden = !emojiObject.tags.some((tag) => tag.toLowerCase().includes(value));
button.hidden = !emojiObject.tags.some(tag =>
tag.toLowerCase().includes(value)
);
button.style.order = emojiObject.order;
} else {
button.hidden = true;
@@ -205,7 +213,9 @@ class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
this.svgList.innerHTML = "";
if (!this.#hasSelection) {
this.#currentPromiseReject?.(new Error("Emoji picker closed without selection"));
this.#currentPromiseReject?.(
new Error("Emoji picker closed without selection")
);
} else if (!this.#closeOnSelect) {
this.#currentPromiseResolve?.(this.#lastSelectedEmoji);
}

View File

@@ -78,7 +78,9 @@ export class nsZenDOMOperatedFeature {
export class nsZenPreloadedFeature {
constructor() {
var initBound = this.init.bind(this);
document.addEventListener("MozBeforeInitialXULLayout", initBound, { once: true });
document.addEventListener("MozBeforeInitialXULLayout", initBound, {
once: true,
});
}
}
@@ -92,7 +94,7 @@ window.gZenCommonActions = {
if (Services.zen.canShare() && displaySpec.startsWith("http")) {
button = {
id: "zen-copy-current-url-button",
command: (event) => {
command: event => {
const buttonRect = event.target.getBoundingClientRect();
/* eslint-disable mozilla/valid-services */
Services.zen.share(
@@ -107,7 +109,10 @@ window.gZenCommonActions = {
},
};
}
gZenUIManager.showToast("zen-copy-current-url-confirmation", { button, timeout: 3000 });
gZenUIManager.showToast("zen-copy-current-url-confirmation", {
button,
timeout: 3000,
});
},
copyCurrentURLAsMarkdownToClipboard() {
@@ -115,7 +120,9 @@ window.gZenCommonActions = {
const tabTitle = gBrowser.selectedTab.label;
const markdownLink = `[${tabTitle}](${currentUrl.displaySpec})`;
ClipboardHelper.copyString(markdownLink);
gZenUIManager.showToast("zen-copy-current-url-as-markdown-confirmation", { timeout: 3000 });
gZenUIManager.showToast("zen-copy-current-url-as-markdown-confirmation", {
timeout: 3000,
});
},
throttle(f, delay) {
@@ -134,10 +141,17 @@ window.gZenCommonActions = {
* @returns {boolean} True if the tab should be closed on back
*/
shouldCloseTabOnBack() {
if (!Services.prefs.getBoolPref("zen.tabs.close-on-back-with-no-history", true)) {
if (
!Services.prefs.getBoolPref(
"zen.tabs.close-on-back-with-no-history",
true
)
) {
return false;
}
const tab = gBrowser.selectedTab;
return Boolean(tab.owner && !tab.pinned && !tab.hasAttribute("zen-empty-tab"));
return Boolean(
tab.owner && !tab.pinned && !tab.hasAttribute("zen-empty-tab")
);
},
};

View File

@@ -14,7 +14,12 @@ class nsHasPolyfill {
* @param {string} stateAttribute
* @param {Array<string>} attributeFilter
*/
observeSelectorExistence(element, descendantSelectors, stateAttribute, attributeFilter = []) {
observeSelectorExistence(
element,
descendantSelectors,
stateAttribute,
attributeFilter = []
) {
const updateState = () => {
const exists = descendantSelectors.some(({ selector }) => {
let selected = element.querySelector(selector);
@@ -26,10 +31,18 @@ class nsHasPolyfill {
const { exists: shouldExist = true } = descendantSelectors;
if (exists === shouldExist) {
if (!element.hasAttribute(stateAttribute)) {
gZenCompactModeManager._setElementExpandAttribute(element, true, stateAttribute);
gZenCompactModeManager._setElementExpandAttribute(
element,
true,
stateAttribute
);
}
} else if (element.hasAttribute(stateAttribute)) {
gZenCompactModeManager._setElementExpandAttribute(element, false, stateAttribute);
gZenCompactModeManager._setElementExpandAttribute(
element,
false,
stateAttribute
);
}
};
@@ -46,31 +59,35 @@ class nsHasPolyfill {
}
disconnectObserver(observerId) {
const index = this.observers.findIndex((o) => o.id === observerId);
const index = this.observers.findIndex(o => o.id === observerId);
if (index !== -1) {
this.observers[index].observer.disconnect();
}
}
connectObserver(observerId) {
const observer = this.observers.find((o) => o.id === observerId);
const observer = this.observers.find(o => o.id === observerId);
if (observer) {
observer.observer.observe(observer.element, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: observer.attributeFilter.length ? observer.attributeFilter : undefined,
attributeFilter: observer.attributeFilter.length
? observer.attributeFilter
: undefined,
});
}
}
destroy() {
this.observers.forEach((observer) => observer.observer.disconnect());
this.observers.forEach(observer => observer.observer.disconnect());
this.observers = [];
}
}
const hasPolyfillInstance = new nsHasPolyfill();
window.addEventListener("unload", () => hasPolyfillInstance.destroy(), { once: true });
window.addEventListener("unload", () => hasPolyfillInstance.destroy(), {
once: true,
});
window.ZenHasPolyfill = hasPolyfillInstance;

View File

@@ -38,7 +38,7 @@ export class nsZenMenuBar {
</menupopup>
</menu>`);
const menu = appearanceMenu.querySelector("menu");
menu.addEventListener("command", (event) => {
menu.addEventListener("command", event => {
const type = event.target.getAttribute("data-type");
const schemeValue = WINDOW_SCHEME_MAPPING[type];
Services.prefs.setIntPref(WINDOW_SCHEME_PREF, schemeValue);
@@ -97,15 +97,17 @@ export class nsZenMenuBar {
</menupopup>
</menu>`);
document.getElementById("view-menu").after(spacesMenubar);
document.getElementById("zen-spaces-menubar").addEventListener("popupshowing", () => {
if (AppConstants.platform === "linux") {
// On linux, there seems to be a bug where the menu freezes up and makes the browser
// suppiciously unresponsive if we try to update the menu while it's opening.
// See https://github.com/zen-browser/desktop/issues/12024
return;
}
gZenWorkspaces.updateWorkspacesChangeContextMenu();
});
document
.getElementById("zen-spaces-menubar")
.addEventListener("popupshowing", () => {
if (AppConstants.platform === "linux") {
// On linux, there seems to be a bug where the menu freezes up and makes the browser
// suppiciously unresponsive if we try to update the menu while it's opening.
// See https://github.com/zen-browser/desktop/issues/12024
return;
}
gZenWorkspaces.updateWorkspacesChangeContextMenu();
});
}
#initAppMenu() {
@@ -133,7 +135,10 @@ export class nsZenMenuBar {
if (!Services.prefs.getBoolPref("zen.window-sync.enabled", true)) {
return;
}
const itemsToHide = ["appMenuRecentlyClosedWindows", "historyUndoWindowMenu"];
const itemsToHide = [
"appMenuRecentlyClosedWindows",
"historyUndoWindowMenu",
];
for (const id of itemsToHide) {
const element = PanelMultiView.getViewNode(document, id);
element.setAttribute("hidden", "true");

View File

@@ -9,7 +9,7 @@ class ZenSessionStore extends nsZenPreloadedFeature {
this.#waitAndCleanup();
}
promiseInitialized = new Promise((resolve) => {
promiseInitialized = new Promise(resolve => {
this._resolveInitialized = resolve;
});

View File

@@ -47,19 +47,24 @@ class ZenSidebarNotification extends MozLitElement {
return html`
<link
rel="stylesheet"
href="chrome://browser/content/zen-styles/zen-sidebar-notification.css" />
href="chrome://browser/content/zen-styles/zen-sidebar-notification.css"
/>
<div class="zen-sidebar-notification-header">
<label
class="zen-sidebar-notification-heading"
flex="1"
data-l10n-id=${this.headingL10nId}></label>
<div class="zen-sidebar-notification-close-button" @click=${() => this.remove()}>
data-l10n-id=${this.headingL10nId}
></label>
<div
class="zen-sidebar-notification-close-button"
@click=${() => this.remove()}
>
<img src="chrome://browser/skin/zen-icons/close.svg" />
</div>
</div>
<div class="zen-sidebar-notification-body">
${this.links.map(
(link) => html`
link => html`
<div
class="zen-sidebar-notification-link-container"
data-l10n-id="${link.l10nId}-tooltip"
@@ -70,15 +75,21 @@ class ZenSidebarNotification extends MozLitElement {
return;
}
window.openLinkIn(link.url, "tab", {
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
triggeringPrincipal:
Services.scriptSecurityManager.getSystemPrincipal(),
forceForeground: true,
});
this.remove();
}}>
<img class="zen-sidebar-notification-link-icon" src=${link.icon} />
}}
>
<img
class="zen-sidebar-notification-link-icon"
src=${link.icon}
/>
<label
class="zen-sidebar-notification-link-text"
data-l10n-id="${link.l10nId}-label"></label>
data-l10n-id="${link.l10nId}-label"
></label>
</div>
`
)}

View File

@@ -45,7 +45,9 @@ class ZenStartup {
}
// Fix notification deck
const deckTemplate = document.getElementById("tab-notification-deck-template");
const deckTemplate = document.getElementById(
"tab-notification-deck-template"
);
if (deckTemplate) {
document.getElementById("zen-appcontent-wrapper").prepend(deckTemplate);
}
@@ -88,7 +90,9 @@ class ZenStartup {
// Just in case we didn't get the right size.
gZenUIManager.updateTabsToolbar();
this.closeWatermark();
document.getElementById("tabbrowser-arrowscrollbox").setAttribute("orient", "vertical");
document
.getElementById("tabbrowser-arrowscrollbox")
.setAttribute("orient", "vertical");
this.isReady = true;
});
}
@@ -106,10 +110,14 @@ class ZenStartup {
closeWatermark() {
document.documentElement.removeAttribute("zen-before-loaded");
if (this.#shouldUseWatermark) {
let elementsToIgnore = this.#watermarkIgnoreElements.map((id) => "#" + id).join(", ");
let elementsToIgnore = this.#watermarkIgnoreElements
.map(id => "#" + id)
.join(", ");
gZenUIManager.motion
.animate(
"#browser > *:not(" + elementsToIgnore + "), #urlbar, #tabbrowser-tabbox > *",
"#browser > *:not(" +
elementsToIgnore +
"), #urlbar, #tabbrowser-tabbox > *",
{
opacity: [0, 1],
},
@@ -148,8 +156,14 @@ class ZenStartup {
#checkForWelcomePage() {
if (!Services.prefs.getBoolPref("zen.welcome-screen.seen", false)) {
Services.prefs.setBoolPref("zen.welcome-screen.seen", true);
Services.prefs.setStringPref("zen.updates.last-build-id", Services.appinfo.appBuildID);
Services.prefs.setStringPref("zen.updates.last-version", Services.appinfo.version);
Services.prefs.setStringPref(
"zen.updates.last-build-id",
Services.appinfo.appBuildID
);
Services.prefs.setStringPref(
"zen.updates.last-version",
Services.appinfo.version
);
Services.scriptloader.loadSubScript(
"chrome://browser/content/zen-components/ZenWelcome.mjs",
window

View File

@@ -10,7 +10,10 @@ window.gZenUIManager = {
_hoverPausedForExpand: false,
_hasLoadedDOM: false,
testingEnabled: Services.prefs.getBoolPref("zen.testing.enabled", false),
profilingEnabled: Services.prefs.getBoolPref("zen.testing.profiling.enabled", false),
profilingEnabled: Services.prefs.getBoolPref(
"zen.testing.profiling.enabled",
false
),
_lastClickPosition: null,
@@ -22,7 +25,11 @@ window.gZenUIManager = {
document.addEventListener("popupshowing", this.onPopupShowing.bind(this));
document.addEventListener("popuphidden", this.onPopupHidden.bind(this));
document.addEventListener("mousedown", this.handleMouseDown.bind(this), true);
document.addEventListener(
"mousedown",
this.handleMouseDown.bind(this),
true
);
ChromeUtils.defineLazyGetter(this, "motion", () => {
Services.scriptloader.loadSubScript(
@@ -40,7 +47,9 @@ window.gZenUIManager = {
new ResizeObserver(
gZenCommonActions.throttle(
gZenCompactModeManager.getAndApplySidebarWidth.bind(gZenCompactModeManager),
gZenCompactModeManager.getAndApplySidebarWidth.bind(
gZenCompactModeManager
),
Services.prefs.getIntPref("zen.view.sidebar-height-throttle", 500)
)
).observe(gNavToolbox);
@@ -82,7 +91,10 @@ window.gZenUIManager = {
rawKeyframes = { ...rawKeyframes };
// Convert 'y' property to 'transform' with translateY and 'x' to translateX,
// and 'scale' to 'transform' with scale.
if ((rawKeyframes.y || rawKeyframes.x || rawKeyframes.scale) && !rawKeyframes.transform) {
if (
(rawKeyframes.y || rawKeyframes.x || rawKeyframes.scale) &&
!rawKeyframes.transform
) {
const yValues = rawKeyframes.y || [];
const xValues = rawKeyframes.x || [];
const scaleValues = rawKeyframes.scale || [];
@@ -90,14 +102,23 @@ window.gZenUIManager = {
delete rawKeyframes.x;
delete rawKeyframes.scale;
rawKeyframes.transform = [];
if (yValues.length !== 0 && xValues.length !== 0 && yValues.length !== xValues.length) {
if (
yValues.length !== 0 &&
xValues.length !== 0 &&
yValues.length !== xValues.length
) {
console.error("y and x keyframes must have the same length");
}
const keyframeLength = Math.max(yValues.length, xValues.length, scaleValues.length);
const keyframeLength = Math.max(
yValues.length,
xValues.length,
scaleValues.length
);
for (let i = 0; i < keyframeLength; i++) {
const y = yValues[i] !== undefined ? `translateY(${yValues[i]}px)` : "";
const x = xValues[i] !== undefined ? `translateX(${xValues[i]}px)` : "";
const scale = scaleValues[i] !== undefined ? `scale(${scaleValues[i]})` : "";
const scale =
scaleValues[i] !== undefined ? `scale(${scaleValues[i]})` : "";
rawKeyframes.transform.push(`${x} ${y} ${scale}`.trim());
}
}
@@ -109,7 +130,7 @@ window.gZenUIManager = {
}
keyframes.push(frame);
}
return await new Promise((resolve) => {
return await new Promise(resolve => {
const animation = element.animate(keyframes, ...args);
animation.onfinish = () => resolve();
});
@@ -117,10 +138,19 @@ window.gZenUIManager = {
_addNewCustomizableButtonsIfNeeded() {
const kPref = "zen.ui.migration.compact-mode-button-added";
let navbarPlacements = CustomizableUI.getWidgetIdsInArea("zen-sidebar-top-buttons");
let navbarPlacements = CustomizableUI.getWidgetIdsInArea(
"zen-sidebar-top-buttons"
);
try {
if (!navbarPlacements.length && !Services.prefs.getBoolPref(kPref, false)) {
CustomizableUI.addWidgetToArea("zen-toggle-compact-mode", "zen-sidebar-top-buttons", 0);
if (
!navbarPlacements.length &&
!Services.prefs.getBoolPref(kPref, false)
) {
CustomizableUI.addWidgetToArea(
"zen-toggle-compact-mode",
"zen-sidebar-top-buttons",
0
);
gZenVerticalTabsManager._topButtonsSeparatorElement.before(
document.getElementById("zen-toggle-compact-mode")
);
@@ -138,7 +168,7 @@ window.gZenUIManager = {
// is ran before this function.
document.documentElement.setAttribute("zen-has-bookmarks", "true");
}
bookmarkToolbar.addEventListener("toolbarvisibilitychange", (event) => {
bookmarkToolbar.addEventListener("toolbarvisibilitychange", event => {
const visible = event.detail.visible;
if (visible) {
document.documentElement.setAttribute("zen-has-bookmarks", "true");
@@ -223,8 +253,13 @@ window.gZenUIManager = {
"--zen-urlbar-top",
`${window.innerHeight / 2 - Math.max(kUrlbarHeight, window.windowUtils.getBoundsWithoutFlushing(gURLBar).height) / 2}px`
);
gURLBar.style.setProperty("--zen-urlbar-width", `${Math.min(window.innerWidth / 2, 750)}px`);
gZenVerticalTabsManager.actualWindowButtons.removeAttribute("zen-has-hover");
gURLBar.style.setProperty(
"--zen-urlbar-width",
`${Math.min(window.innerWidth / 2, 750)}px`
);
gZenVerticalTabsManager.actualWindowButtons.removeAttribute(
"zen-has-hover"
);
gZenVerticalTabsManager.recalculateURLBarHeight(true);
if (!this._preventToolbarRebuild) {
setTimeout(() => {
@@ -256,7 +291,10 @@ window.gZenUIManager = {
openAndChangeToTab(url, options) {
if (window.ownerGlobal.parent) {
const tab = window.ownerGlobal.parent.gBrowser.addTrustedTab(url, options);
const tab = window.ownerGlobal.parent.gBrowser.addTrustedTab(
url,
options
);
window.ownerGlobal.parent.gBrowser.selectedTab = tab;
return tab;
}
@@ -270,7 +308,10 @@ window.gZenUIManager = {
},
createValidXULText(text) {
return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
return text
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;");
},
/**
@@ -302,7 +343,11 @@ window.gZenUIManager = {
continue;
}
document.removeEventListener("mousemove", this.__removeHasPopupAttribute);
gZenCompactModeManager._setElementExpandAttribute(el, true, "has-popup-menu");
gZenCompactModeManager._setElementExpandAttribute(
el,
true,
"has-popup-menu"
);
this.__currentPopup = showEvent.target;
this.__currentPopupTrackElement = el;
break;
@@ -315,11 +360,21 @@ window.gZenUIManager = {
}
const element = this.__currentPopupTrackElement;
if (document.getElementById("main-window").matches(":hover")) {
gZenCompactModeManager._setElementExpandAttribute(element, false, "has-popup-menu");
gZenCompactModeManager._setElementExpandAttribute(
element,
false,
"has-popup-menu"
);
} else {
this.__removeHasPopupAttribute = () =>
gZenCompactModeManager._setElementExpandAttribute(element, false, "has-popup-menu");
document.addEventListener("mousemove", this.__removeHasPopupAttribute, { once: true });
gZenCompactModeManager._setElementExpandAttribute(
element,
false,
"has-popup-menu"
);
document.addEventListener("mousemove", this.__removeHasPopupAttribute, {
once: true,
});
}
this.__currentPopup = null;
this.__currentPopupTrackElement = null;
@@ -347,9 +402,11 @@ window.gZenUIManager = {
const input = gURLBar;
if (gURLBar.hasAttribute("breakout-extend") && !this._animatingSearchMode) {
this._animatingSearchMode = true;
this.motion.animate(input, { scale: [1, 0.98, 1] }, { duration: 0.25 }).then(() => {
delete this._animatingSearchMode;
});
this.motion
.animate(input, { scale: [1, 0.98, 1] }, { duration: 0.25 })
.then(() => {
delete this._animatingSearchMode;
});
if (searchMode) {
gURLBar.setAttribute("animate-searchmode", "true");
this._animatingSearchModeTimeout = setTimeout(() => {
@@ -416,7 +473,12 @@ window.gZenUIManager = {
return true;
},
handleNewTab(werePassedURL, searchClipboard, where, overridePreferance = false) {
handleNewTab(
werePassedURL,
searchClipboard,
where,
overridePreferance = false
) {
// Validate browser state first
if (!this._validateBrowserState()) {
console.warn("Browser state invalid for new tab operation");
@@ -548,7 +610,9 @@ window.gZenUIManager = {
if (isFocusedBefore) {
setTimeout(() => {
window.dispatchEvent(
new CustomEvent("ZenURLBarClosed", { detail: { onSwitch, onElementPicked } })
new CustomEvent("ZenURLBarClosed", {
detail: { onSwitch, onElementPicked },
})
);
gURLBar.view.close({ elementPicked: onElementPicked });
gURLBar.updateTextOverflow();
@@ -559,8 +623,15 @@ window.gZenUIManager = {
// Ensure tab and browser are valid before updating state
const selectedTab = gBrowser.selectedTab;
if (selectedTab && selectedTab.linkedBrowser && !selectedTab.closing && onSwitch) {
const browserState = gURLBar.getBrowserState(selectedTab.linkedBrowser);
if (
selectedTab &&
selectedTab.linkedBrowser &&
!selectedTab.closing &&
onSwitch
) {
const browserState = gURLBar.getBrowserState(
selectedTab.linkedBrowser
);
if (browserState) {
browserState.urlbarFocused = false;
}
@@ -574,7 +645,10 @@ window.gZenUIManager = {
if (gURLBar.hasAttribute("breakout-extend")) {
return aURL;
}
if (gZenVerticalTabsManager._hasSetSingleToolbar && this.urlbarShowDomainOnly) {
if (
gZenVerticalTabsManager._hasSetSingleToolbar &&
this.urlbarShowDomainOnly
) {
let url = BrowserUIUtils.removeSingleTrailingSlashFromURL(aURL);
return url.startsWith("https://") ? url.split("/")[2] : url;
}
@@ -639,7 +713,11 @@ window.gZenUIManager = {
return;
}
this.motion
.animate(toast, { opacity: [1, 0], scale: [1, 0.5] }, { duration: 0.2, bounce: 0 })
.animate(
toast,
{ opacity: [1, 0], scale: [1, 0.5] },
{ duration: 0.2, bounce: 0 }
)
.then(() => {
toast.remove();
if (this._toastContainer.children.length === 0) {
@@ -648,7 +726,11 @@ window.gZenUIManager = {
});
};
if (reused) {
await this.motion.animate(toast, { scale: 0.2 }, { duration: 0.1, bounce: 0 });
await this.motion.animate(
toast,
{ scale: 0.2 },
{ duration: 0.1, bounce: 0 }
);
} else {
toast.addEventListener("mouseover", () => {
if (this._toastTimeouts[messageId]) {
@@ -659,17 +741,27 @@ window.gZenUIManager = {
if (this._toastTimeouts[messageId]) {
clearTimeout(this._toastTimeouts[messageId]);
}
this._toastTimeouts[messageId] = setTimeout(timeoutFunction, options.timeout || 2000);
this._toastTimeouts[messageId] = setTimeout(
timeoutFunction,
options.timeout || 2000
);
});
}
if (!toast.style.transform) {
toast.style.transform = "scale(0)";
}
await this.motion.animate(toast, { scale: 1 }, { type: "spring", bounce: 0.2, duration: 0.5 });
await this.motion.animate(
toast,
{ scale: 1 },
{ type: "spring", bounce: 0.2, duration: 0.5 }
);
if (this._toastTimeouts[messageId]) {
clearTimeout(this._toastTimeouts[messageId]);
}
this._toastTimeouts[messageId] = setTimeout(timeoutFunction, options.timeout || 2000);
this._toastTimeouts[messageId] = setTimeout(
timeoutFunction,
options.timeout || 2000
);
},
panelUIPosition(panel, anchor) {
@@ -722,10 +814,12 @@ window.gZenUIManager = {
block = "topleft";
}
if (
(gZenVerticalTabsManager._hasSetSingleToolbar && gZenVerticalTabsManager._prefsRightSide) ||
(gZenVerticalTabsManager._hasSetSingleToolbar &&
gZenVerticalTabsManager._prefsRightSide) ||
(panel?.id === "zen-unified-site-data-panel" &&
!gZenVerticalTabsManager._hasSetSingleToolbar) ||
(panel?.id === "unified-extensions-panel" && gZenVerticalTabsManager._hasSetSingleToolbar)
(panel?.id === "unified-extensions-panel" &&
gZenVerticalTabsManager._hasSetSingleToolbar)
) {
block = "bottomright";
inline = "topright";
@@ -787,14 +881,20 @@ window.gZenVerticalTabsManager = {
return !(
window.AppConstants.platform === "macosx" ||
window.matchMedia("(-moz-gtk-csd-reversed-placement)").matches ||
Services.prefs.getBoolPref("zen.view.experimental-force-window-controls-left")
Services.prefs.getBoolPref(
"zen.view.experimental-force-window-controls-left"
)
);
});
ChromeUtils.defineLazyGetter(this, "hidesTabsToolbar", () => {
return (
document.documentElement.getAttribute("chromehidden")?.includes("toolbar") ||
document.documentElement.getAttribute("chromehidden")?.includes("menubar")
document.documentElement
.getAttribute("chromehidden")
?.includes("toolbar") ||
document.documentElement
.getAttribute("chromehidden")
?.includes("menubar")
);
});
@@ -808,22 +908,35 @@ window.gZenVerticalTabsManager = {
var onPrefChange = this._onPrefChange.bind(this);
this.initializePreferences(onPrefChange);
this._toolbarOriginalParent = document.getElementById("nav-bar").parentElement;
this._toolbarOriginalParent =
document.getElementById("nav-bar").parentElement;
gZenCompactModeManager.addEventListener(updateEvent);
this.initRightSideOrderContextMenu();
window.addEventListener("customizationstarting", this._preCustomize.bind(this));
window.addEventListener("aftercustomization", this._postCustomize.bind(this));
window.addEventListener(
"customizationstarting",
this._preCustomize.bind(this)
);
window.addEventListener(
"aftercustomization",
this._postCustomize.bind(this)
);
this._updateEvent();
if (!this.isWindowsStyledButtons) {
document.documentElement.setAttribute("zen-window-buttons-reversed", true);
document.documentElement.setAttribute(
"zen-window-buttons-reversed",
true
);
}
this._renameTabHalt = this.renameTabHalt.bind(this);
gBrowser.tabContainer.addEventListener("dblclick", this.renameTabStart.bind(this));
gBrowser.tabContainer.addEventListener(
"dblclick",
this.renameTabStart.bind(this)
);
},
toggleExpand() {
@@ -896,7 +1009,7 @@ window.gZenVerticalTabsManager = {
}
)
.then(() => {})
.catch((err) => {
.catch(err => {
console.error(err);
})
.finally(() => {
@@ -905,7 +1018,8 @@ window.gZenVerticalTabsManager = {
aItem.style.removeProperty("opacity");
});
const itemLabel =
aItem.querySelector(".tab-group-label-container") || aItem.querySelector(".tab-content");
aItem.querySelector(".tab-group-label-container") ||
aItem.querySelector(".tab-content");
gZenUIManager.motion
.animate(
itemLabel,
@@ -918,7 +1032,7 @@ window.gZenVerticalTabsManager = {
}
)
.then(() => {})
.catch((err) => {
.catch(err => {
console.error(err);
})
.finally(() => {
@@ -971,7 +1085,7 @@ window.gZenVerticalTabsManager = {
},
async _preCustomize() {
await this._multiWindowFeature.foreachWindowAsActive(async (browser) => {
await this._multiWindowFeature.foreachWindowAsActive(async browser => {
browser.gZenVerticalTabsManager._updateEvent({
forCustomizableMode: true,
dontRebuildAreas: true,
@@ -984,7 +1098,7 @@ window.gZenVerticalTabsManager = {
_postCustomize() {
// No need to use `await` here, because the customization is already done
this._multiWindowFeature.foreachWindowAsActive(async (browser) => {
this._multiWindowFeature.foreachWindowAsActive(async browser => {
browser.gZenVerticalTabsManager._updateEvent({ dontRebuildAreas: true });
});
},
@@ -1028,7 +1142,7 @@ window.gZenVerticalTabsManager = {
},
_initWaitPromise() {
this._waitPromise = new Promise((resolve) => {
this._waitPromise = new Promise(resolve => {
this._resolveWaitPromise = resolve;
});
},
@@ -1037,8 +1151,12 @@ window.gZenVerticalTabsManager = {
this._resolveWaitPromise();
// only run if we are in the active window
await this._multiWindowFeature.foreachWindowAsActive(async (browser) => {
if (browser.gZenVerticalTabsManager._multiWindowFeature.windowIsActive(browser)) {
await this._multiWindowFeature.foreachWindowAsActive(async browser => {
if (
browser.gZenVerticalTabsManager._multiWindowFeature.windowIsActive(
browser
)
) {
return;
}
await browser.gZenVerticalTabsManager._waitPromise;
@@ -1089,17 +1207,22 @@ window.gZenVerticalTabsManager = {
}
const topButtons = document.getElementById("zen-sidebar-top-buttons");
const isCompactMode = gZenCompactModeManager.preference && !forCustomizableMode;
const isCompactMode =
gZenCompactModeManager.preference && !forCustomizableMode;
const isVerticalTabs = this._prefsVerticalTabs || forCustomizableMode;
const isSidebarExpanded = this._prefsSidebarExpanded || !isVerticalTabs;
const isRightSide = this._prefsRightSide && isVerticalTabs;
const isSingleToolbar =
((this._prefsUseSingleToolbar && isVerticalTabs && isSidebarExpanded) || !isVerticalTabs) &&
((this._prefsUseSingleToolbar && isVerticalTabs && isSidebarExpanded) ||
!isVerticalTabs) &&
!forCustomizableMode &&
!this.hidesTabsToolbar;
const titlebar = document.getElementById("titlebar");
gBrowser.tabContainer.setAttribute("orient", isVerticalTabs ? "vertical" : "horizontal");
gBrowser.tabContainer.setAttribute(
"orient",
isVerticalTabs ? "vertical" : "horizontal"
);
gBrowser.tabContainer.arrowScrollbox.setAttribute(
"orient",
isVerticalTabs ? "vertical" : "horizontal"
@@ -1110,7 +1233,9 @@ window.gZenVerticalTabsManager = {
isVerticalTabs ? "vertical" : "horizontal"
);
const buttonsTarget = document.getElementById("zen-sidebar-top-buttons-customization-target");
const buttonsTarget = document.getElementById(
"zen-sidebar-top-buttons-customization-target"
);
if (isRightSide) {
this.navigatorToolbox.setAttribute("zen-right-side", "true");
document.documentElement.setAttribute("zen-right-side", "true");
@@ -1121,7 +1246,9 @@ window.gZenVerticalTabsManager = {
delete this._hadSidebarCollapse;
if (isSidebarExpanded) {
this._hadSidebarCollapse = !document.documentElement.hasAttribute("zen-sidebar-expanded");
this._hadSidebarCollapse = !document.documentElement.hasAttribute(
"zen-sidebar-expanded"
);
this.navigatorToolbox.setAttribute("zen-sidebar-expanded", "true");
document.documentElement.setAttribute("zen-sidebar-expanded", "true");
gBrowser.tabContainer.setAttribute("expanded", "true");
@@ -1131,8 +1258,12 @@ window.gZenVerticalTabsManager = {
gBrowser.tabContainer.removeAttribute("expanded");
}
const appContentNavbarContaienr = document.getElementById("zen-appcontent-navbar-container");
const appContentNavbarWrapper = document.getElementById("zen-appcontent-navbar-wrapper");
const appContentNavbarContaienr = document.getElementById(
"zen-appcontent-navbar-container"
);
const appContentNavbarWrapper = document.getElementById(
"zen-appcontent-navbar-wrapper"
);
appContentNavbarWrapper.style.transition = "none";
let shouldHide = false;
if (
@@ -1148,12 +1279,16 @@ window.gZenVerticalTabsManager = {
}
// Check if the sidebar is in hover mode
if (!this.navigatorToolbox.hasAttribute("zen-right-side") && !isCompactMode) {
if (
!this.navigatorToolbox.hasAttribute("zen-right-side") &&
!isCompactMode
) {
this.navigatorToolbox.prepend(topButtons);
}
let windowButtons = this.actualWindowButtons;
let doNotChangeWindowButtons = !isCompactMode && isRightSide && this.isWindowsStyledButtons;
let doNotChangeWindowButtons =
!isCompactMode && isRightSide && this.isWindowsStyledButtons;
const navBar = document.getElementById("nav-bar");
if (isSingleToolbar) {
@@ -1169,11 +1304,15 @@ window.gZenVerticalTabsManager = {
for (const button of elements) {
this._topButtonsSeparatorElement.after(button);
}
buttonsTarget.prepend(document.getElementById("unified-extensions-button"));
buttonsTarget.prepend(
document.getElementById("unified-extensions-button")
);
const panelUIButton = document.getElementById("PanelUI-button");
buttonsTarget.prepend(panelUIButton);
panelUIButton.setAttribute("overflows", "false");
buttonsTarget.parentElement.append(document.getElementById("nav-bar-overflow-button"));
buttonsTarget.parentElement.append(
document.getElementById("nav-bar-overflow-button")
);
if (this.isWindowsStyledButtons && !doNotChangeWindowButtons) {
appContentNavbarContaienr.append(windowButtons);
}
@@ -1194,7 +1333,9 @@ window.gZenVerticalTabsManager = {
'#zen-sidebar-top-buttons-customization-target > :is([cui-areatype="toolbar"], .chromeclass-toolbar-additional)'
);
for (const button of elements) {
document.getElementById("nav-bar-customization-target").append(button);
document
.getElementById("nav-bar-customization-target")
.append(button);
}
this._topButtonsSeparatorElement.remove();
document.documentElement.removeAttribute("zen-single-toolbar");
@@ -1227,7 +1368,9 @@ window.gZenVerticalTabsManager = {
topButtons.prepend(windowButtons);
}
const canHideTabBarPref = Services.prefs.getBoolPref("zen.view.compact.hide-tabbar");
const canHideTabBarPref = Services.prefs.getBoolPref(
"zen.view.compact.hide-tabbar"
);
const captionsShouldStayOnSidebar =
!canHideTabBarPref &&
((!this.isWindowsStyledButtons && !isRightSide) ||
@@ -1240,7 +1383,12 @@ window.gZenVerticalTabsManager = {
}
// Case: single toolbar, compact mode, right side and windows styled buttons
if (isSingleToolbar && isCompactMode && isRightSide && this.isWindowsStyledButtons) {
if (
isSingleToolbar &&
isCompactMode &&
isRightSide &&
this.isWindowsStyledButtons
) {
topButtons.prepend(windowButtons);
}
@@ -1286,9 +1434,15 @@ window.gZenVerticalTabsManager = {
this._hasSetSingleToolbar &&
Services.prefs.getBoolPref("zen.view.overflow-webext-toolbar", true)
) {
topButtons.setAttribute("addon-webext-overflowtarget", "zen-overflow-extensions-list");
topButtons.setAttribute(
"addon-webext-overflowtarget",
"zen-overflow-extensions-list"
);
} else {
topButtons.setAttribute("addon-webext-overflowtarget", "overflowed-extensions-list");
topButtons.setAttribute(
"addon-webext-overflowtarget",
"overflowed-extensions-list"
);
}
gZenCompactModeManager.updateCompactModeContext(isSingleToolbar);
@@ -1321,11 +1475,15 @@ window.gZenVerticalTabsManager = {
},
rebuildAreas() {
CustomizableUI.zenInternalCU._rebuildRegisteredAreas(/* zenDontRebuildCollapsed */ true);
CustomizableUI.zenInternalCU._rebuildRegisteredAreas(
/* zenDontRebuildCollapsed */ true
);
},
_updateMaxWidth() {
const maxWidth = Services.prefs.getIntPref("zen.view.sidebar-expanded.max-width");
const maxWidth = Services.prefs.getIntPref(
"zen.view.sidebar-expanded.max-width"
);
const toolbox = gNavToolbox;
if (!this._prefsCompactMode) {
toolbox.style.maxWidth = `${maxWidth}px`;
@@ -1398,7 +1556,9 @@ window.gZenVerticalTabsManager = {
);
}
const editorContainer = this._tabEdited.querySelector(".tab-editor-container");
const editorContainer = this._tabEdited.querySelector(
".tab-editor-container"
);
if (editorContainer) {
editorContainer.remove();
}
@@ -1426,21 +1586,30 @@ window.gZenVerticalTabsManager = {
) {
return;
}
if (isTab && !target.closest(".tab-label-container") && event.type === "dblclick") {
if (
isTab &&
!target.closest(".tab-label-container") &&
event.type === "dblclick"
) {
return;
}
this._tabEdited =
target.closest(".tabbrowser-tab") ||
target.closest(".zen-current-workspace-indicator-name") ||
(event.explicit && target.closest(".tab-group-label"));
if (!this._tabEdited || (this._tabEdited.hasAttribute("zen-essential") && isTab)) {
if (
!this._tabEdited ||
(this._tabEdited.hasAttribute("zen-essential") && isTab)
) {
this._tabEdited = null;
return;
}
gZenFolders.cancelPopupTimer();
event.stopPropagation?.();
document.documentElement.setAttribute("zen-renaming-tab", "true");
const label = isTab ? this._tabEdited.querySelector(".tab-label-container") : this._tabEdited;
const label = isTab
? this._tabEdited.querySelector(".tab-label-container")
: this._tabEdited;
label.classList.add("tab-label-container-editing");
if (isTab) {
@@ -1457,7 +1626,9 @@ window.gZenVerticalTabsManager = {
input.addEventListener("keydown", this.renameTabKeydown.bind(this));
if (isTab) {
const containerHtml = this._tabEdited.querySelector(".tab-editor-container");
const containerHtml = this._tabEdited.querySelector(
".tab-editor-container"
);
containerHtml.appendChild(input);
} else {
this._tabEdited.after(input);
@@ -1473,7 +1644,9 @@ window.gZenVerticalTabsManager = {
return;
}
document.documentElement.removeAttribute("zen-renaming-tab");
const editorContainer = this._tabEdited.querySelector(".tab-editor-container");
const editorContainer = this._tabEdited.querySelector(
".tab-editor-container"
);
let input = document.getElementById("tab-label-input");
input.remove();
if (editorContainer) {

View File

@@ -17,12 +17,17 @@ export default function checkForZenUpdates() {
!gZenUIManager.testingEnabled &&
Services.prefs.getBoolPref(ZEN_UPDATE_SHOW, true)
) {
const updateUrl = Services.prefs.getStringPref("app.releaseNotesURL.prompt", "");
const updateUrl = Services.prefs.getStringPref(
"app.releaseNotesURL.prompt",
""
);
createSidebarNotification({
headingL10nId: "zen-sidebar-notification-updated-heading",
links: [
{
url: Services.urlFormatter.formatURL(updateUrl.replace("%VERSION%", version)),
url: Services.urlFormatter.formatURL(
updateUrl.replace("%VERSION%", version)
),
l10nId: "zen-sidebar-notification-updated",
special: true,
icon: "chrome://browser/skin/zen-icons/heart-circle-fill.svg",

View File

@@ -8,7 +8,11 @@ export const ZenCustomizableUI = new (class {
constructor() {}
TYPE_TOOLBAR = "toolbar";
defaultSidebarIcons = ["downloads-button", "zen-workspaces-button", "zen-create-new-button"];
defaultSidebarIcons = [
"downloads-button",
"zen-workspaces-button",
"zen-create-new-button",
];
startup(CustomizableUIInternal) {
CustomizableUIInternal.registerArea(
@@ -39,7 +43,8 @@ export const ZenCustomizableUI = new (class {
}
#addSidebarButtons(window) {
const kDefaultSidebarWidth = AppConstants.platform === "macosx" ? "230px" : "186px";
const kDefaultSidebarWidth =
AppConstants.platform === "macosx" ? "230px" : "186px";
const toolbox = window.gNavToolbox;
// Set a splitter to navigator-toolbox
@@ -81,7 +86,7 @@ export const ZenCustomizableUI = new (class {
</toolbar>
`);
toolbox.prepend(sidebarBox);
new window.MutationObserver((e) => {
new window.MutationObserver(e => {
if (e[0].type !== "attributes" || e[0].attributeName !== "width") {
return;
}
@@ -96,7 +101,7 @@ export const ZenCustomizableUI = new (class {
toolbox.style.width = width;
toolbox.setAttribute("width", width);
splitter.addEventListener("dblclick", (e) => {
splitter.addEventListener("dblclick", e => {
if (e.button !== 0) {
return;
}
@@ -104,7 +109,9 @@ export const ZenCustomizableUI = new (class {
toolbox.setAttribute("width", kDefaultSidebarWidth);
});
const newTab = window.document.getElementById("vertical-tabs-newtab-button");
const newTab = window.document.getElementById(
"vertical-tabs-newtab-button"
);
newTab.classList.add("zen-sidebar-action-button");
for (let id of this.defaultSidebarIcons) {
@@ -124,7 +131,7 @@ export const ZenCustomizableUI = new (class {
// If we use "mousedown" event for private windows (which open a new tab on "click"), we might end up with
// the urlbar flicking and therefore we use "command" event to avoid that.
let isPrivateMode = window.gZenWorkspaces.privateWindowOrDisabled;
button.addEventListener(isPrivateMode ? "command" : "mousedown", (event) => {
button.addEventListener(isPrivateMode ? "command" : "mousedown", event => {
if (isPrivateMode) {
window.document.getElementById("cmd_newNavigatorTab").doCommand();
return;
@@ -146,7 +153,9 @@ export const ZenCustomizableUI = new (class {
}
#moveWindowButtons(window) {
const windowControls = window.document.getElementsByClassName("titlebar-buttonbox-container");
const windowControls = window.document.getElementsByClassName(
"titlebar-buttonbox-container"
);
const toolboxIcons = window.document.getElementById(
"zen-sidebar-top-buttons-customization-target"
);
@@ -173,7 +182,9 @@ export const ZenCustomizableUI = new (class {
wrapper.prepend(elem);
}
}
window.document.getElementById("stop-reload-button").removeAttribute("overflows");
window.document
.getElementById("stop-reload-button")
.removeAttribute("overflows");
}
_dispatchResizeEvent(window) {

View File

@@ -24,7 +24,9 @@ class nsZenUIMigration {
}
this.clearVariables();
if (this.shouldRestart) {
Services.startup.quit(Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart);
Services.startup.quit(
Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestart
);
}
}
@@ -61,13 +63,24 @@ class nsZenUIMigration {
userContentFile.append("userContent.css");
Services.prefs.setBoolPref(
"zen.workspaces.separate-essentials",
Services.prefs.getBoolPref("zen.workspaces.container-specific-essentials-enabled", false)
Services.prefs.getBoolPref(
"zen.workspaces.container-specific-essentials-enabled",
false
)
);
const theme = Services.prefs.getIntPref(
"layout.css.prefers-color-scheme.content-override",
0
);
const theme = Services.prefs.getIntPref("layout.css.prefers-color-scheme.content-override", 0);
Services.prefs.setIntPref("zen.view.window.scheme", theme);
if (userChromeFile.exists() || userContentFile.exists()) {
Services.prefs.setBoolPref("toolkit.legacyUserProfileCustomizations.stylesheets", true);
console.warn("ZenUIMigration: User stylesheets detected, enabling legacy stylesheets.");
Services.prefs.setBoolPref(
"toolkit.legacyUserProfileCustomizations.stylesheets",
true
);
console.warn(
"ZenUIMigration: User stylesheets detected, enabling legacy stylesheets."
);
this.shouldRestart = true;
}
}
@@ -79,7 +92,11 @@ class nsZenUIMigration {
}
_migrateV3() {
if (Services.prefs.getStringPref("zen.theme.accent-color", "").startsWith("system")) {
if (
Services.prefs
.getStringPref("zen.theme.accent-color", "")
.startsWith("system")
) {
Services.prefs.setStringPref("zen.theme.accent-color", "AccentColor");
}
}
@@ -100,20 +117,23 @@ class nsZenUIMigration {
lazy.SessionStore.promiseAllWindowsRestored.then(() => {
const win = Services.wm.getMostRecentWindow("navigator:browser");
win.setTimeout(async () => {
const [title, message, learnMore, accept] = await win.document.l10n.formatMessages([
"zen-window-sync-migration-dialog-title",
"zen-window-sync-migration-dialog-message",
"zen-window-sync-migration-dialog-learn-more",
"zen-window-sync-migration-dialog-accept",
]);
const [title, message, learnMore, accept] =
await win.document.l10n.formatMessages([
"zen-window-sync-migration-dialog-title",
"zen-window-sync-migration-dialog-message",
"zen-window-sync-migration-dialog-learn-more",
"zen-window-sync-migration-dialog-accept",
]);
// buttonPressed will be 0 for cancel, 1 for "more info"
let buttonPressed = Services.prompt.confirmEx(
win,
title.value,
message.value,
Services.prompt.BUTTON_POS_0 * Services.prompt.BUTTON_TITLE_IS_STRING +
Services.prompt.BUTTON_POS_1 * Services.prompt.BUTTON_TITLE_IS_STRING,
Services.prompt.BUTTON_POS_0 *
Services.prompt.BUTTON_TITLE_IS_STRING +
Services.prompt.BUTTON_POS_1 *
Services.prompt.BUTTON_TITLE_IS_STRING,
learnMore.value,
accept.value,
null,
@@ -122,7 +142,10 @@ class nsZenUIMigration {
);
// User has clicked on "Learn More"
if (buttonPressed === 0) {
win.openTrustedLinkIn("https://docs.zen-browser.app/user-manual/window-sync", "tab");
win.openTrustedLinkIn(
"https://docs.zen-browser.app/user-manual/window-sync",
"tab"
);
}
}, 1000);
});

View File

@@ -6,146 +6,152 @@ document.addEventListener(
"MozBeforeInitialXULLayout",
() => {
// <commandset id="mainCommandSet"> defined in browser-sets.inc
// eslint-disable-next-line complexity
document.getElementById("zenCommandSet").addEventListener("command", (event) => {
switch (event.target.id) {
case "cmd_zenCompactModeToggle":
gZenCompactModeManager.toggle();
break;
case "cmd_toggleCompactModeIgnoreHover":
gZenCompactModeManager.toggle(true);
break;
case "cmd_zenCompactModeShowSidebar":
gZenCompactModeManager.toggleSidebar();
break;
case "cmd_zenWorkspaceForward":
gZenWorkspaces.changeWorkspaceShortcut();
break;
case "cmd_zenWorkspaceBackward":
gZenWorkspaces.changeWorkspaceShortcut(-1);
break;
case "cmd_zenSplitViewGrid":
gZenViewSplitter.toggleShortcut("grid");
break;
case "cmd_zenSplitViewVertical":
gZenViewSplitter.toggleShortcut("vsep");
break;
case "cmd_zenSplitViewHorizontal":
gZenViewSplitter.toggleShortcut("hsep");
break;
case "cmd_zenSplitViewUnsplit":
gZenViewSplitter.toggleShortcut("unsplit");
break;
case "cmd_zenSplitViewContextMenu":
gZenViewSplitter.contextSplitTabs();
break;
case "cmd_zenCopyCurrentURLMarkdown":
gZenCommonActions.copyCurrentURLAsMarkdownToClipboard();
break;
case "cmd_zenCopyCurrentURL":
gZenCommonActions.copyCurrentURLToClipboard();
break;
case "cmd_zenPinnedTabReset":
gZenPinnedTabManager.resetPinnedTab(gBrowser.selectedTab);
break;
case "cmd_zenPinnedTabResetNoTab":
gZenPinnedTabManager.resetPinnedTab();
break;
case "cmd_zenToggleSidebar":
gZenVerticalTabsManager.toggleExpand();
break;
case "cmd_zenOpenZenThemePicker":
gZenThemePicker.openThemePicker(event);
break;
case "cmd_zenChangeWorkspaceTab":
gZenWorkspaces.changeTabWorkspace(
event.sourceEvent.target.getAttribute("zen-workspace-id")
);
break;
case "cmd_zenToggleTabsOnRight":
gZenVerticalTabsManager.toggleTabsOnRight();
break;
case "cmd_zenSplitViewLinkInNewTab":
gZenViewSplitter.splitLinkInNewTab();
break;
case "cmd_zenNewEmptySplit":
setTimeout(() => {
gZenViewSplitter.createEmptySplit();
}, 0);
break;
case "cmd_zenReplacePinnedUrlWithCurrent":
gZenPinnedTabManager.replacePinnedUrlWithCurrent();
break;
case "cmd_contextZenAddToEssentials":
gZenPinnedTabManager.addToEssentials();
break;
case "cmd_contextZenRemoveFromEssentials":
gZenPinnedTabManager.removeEssentials();
break;
case "cmd_zenCtxDeleteWorkspace":
gZenWorkspaces.contextDeleteWorkspace(event);
break;
case "cmd_zenChangeWorkspaceName":
gZenVerticalTabsManager.renameTabStart({
target: gZenWorkspaces.activeWorkspaceIndicator.querySelector(
".zen-current-workspace-indicator-name"
),
});
break;
case "cmd_zenChangeWorkspaceIcon":
gZenWorkspaces.changeWorkspaceIcon();
break;
case "cmd_zenReorderWorkspaces":
gZenUIManager.showToast("zen-workspaces-how-to-reorder-title", {
timeout: 9000,
descriptionId: "zen-workspaces-how-to-reorder-desc",
});
break;
case "cmd_zenOpenWorkspaceCreation":
gZenWorkspaces.openWorkspaceCreation(event);
break;
case "cmd_zenOpenFolderCreation":
gZenFolders.createFolder([], {
renameFolder: true,
});
break;
case "cmd_zenTogglePinTab": {
const currentTab = gBrowser.selectedTab;
if (currentTab && !currentTab.hasAttribute("zen-empty-tab")) {
if (currentTab.pinned) {
gBrowser.unpinTab(currentTab);
} else {
gBrowser.pinTab(currentTab);
document
.getElementById("zenCommandSet")
// eslint-disable-next-line complexity
.addEventListener("command", event => {
switch (event.target.id) {
case "cmd_zenCompactModeToggle":
gZenCompactModeManager.toggle();
break;
case "cmd_toggleCompactModeIgnoreHover":
gZenCompactModeManager.toggle(true);
break;
case "cmd_zenCompactModeShowSidebar":
gZenCompactModeManager.toggleSidebar();
break;
case "cmd_zenWorkspaceForward":
gZenWorkspaces.changeWorkspaceShortcut();
break;
case "cmd_zenWorkspaceBackward":
gZenWorkspaces.changeWorkspaceShortcut(-1);
break;
case "cmd_zenSplitViewGrid":
gZenViewSplitter.toggleShortcut("grid");
break;
case "cmd_zenSplitViewVertical":
gZenViewSplitter.toggleShortcut("vsep");
break;
case "cmd_zenSplitViewHorizontal":
gZenViewSplitter.toggleShortcut("hsep");
break;
case "cmd_zenSplitViewUnsplit":
gZenViewSplitter.toggleShortcut("unsplit");
break;
case "cmd_zenSplitViewContextMenu":
gZenViewSplitter.contextSplitTabs();
break;
case "cmd_zenCopyCurrentURLMarkdown":
gZenCommonActions.copyCurrentURLAsMarkdownToClipboard();
break;
case "cmd_zenCopyCurrentURL":
gZenCommonActions.copyCurrentURLToClipboard();
break;
case "cmd_zenPinnedTabReset":
gZenPinnedTabManager.resetPinnedTab(gBrowser.selectedTab);
break;
case "cmd_zenPinnedTabResetNoTab":
gZenPinnedTabManager.resetPinnedTab();
break;
case "cmd_zenToggleSidebar":
gZenVerticalTabsManager.toggleExpand();
break;
case "cmd_zenOpenZenThemePicker":
gZenThemePicker.openThemePicker(event);
break;
case "cmd_zenChangeWorkspaceTab":
gZenWorkspaces.changeTabWorkspace(
event.sourceEvent.target.getAttribute("zen-workspace-id")
);
break;
case "cmd_zenToggleTabsOnRight":
gZenVerticalTabsManager.toggleTabsOnRight();
break;
case "cmd_zenSplitViewLinkInNewTab":
gZenViewSplitter.splitLinkInNewTab();
break;
case "cmd_zenNewEmptySplit":
setTimeout(() => {
gZenViewSplitter.createEmptySplit();
}, 0);
break;
case "cmd_zenReplacePinnedUrlWithCurrent":
gZenPinnedTabManager.replacePinnedUrlWithCurrent();
break;
case "cmd_contextZenAddToEssentials":
gZenPinnedTabManager.addToEssentials();
break;
case "cmd_contextZenRemoveFromEssentials":
gZenPinnedTabManager.removeEssentials();
break;
case "cmd_zenCtxDeleteWorkspace":
gZenWorkspaces.contextDeleteWorkspace(event);
break;
case "cmd_zenChangeWorkspaceName":
gZenVerticalTabsManager.renameTabStart({
target: gZenWorkspaces.activeWorkspaceIndicator.querySelector(
".zen-current-workspace-indicator-name"
),
});
break;
case "cmd_zenChangeWorkspaceIcon":
gZenWorkspaces.changeWorkspaceIcon();
break;
case "cmd_zenReorderWorkspaces":
gZenUIManager.showToast("zen-workspaces-how-to-reorder-title", {
timeout: 9000,
descriptionId: "zen-workspaces-how-to-reorder-desc",
});
break;
case "cmd_zenOpenWorkspaceCreation":
gZenWorkspaces.openWorkspaceCreation(event);
break;
case "cmd_zenOpenFolderCreation":
gZenFolders.createFolder([], {
renameFolder: true,
});
break;
case "cmd_zenTogglePinTab": {
const currentTab = gBrowser.selectedTab;
if (currentTab && !currentTab.hasAttribute("zen-empty-tab")) {
if (currentTab.pinned) {
gBrowser.unpinTab(currentTab);
} else {
gBrowser.pinTab(currentTab);
}
}
break;
}
break;
}
case "cmd_zenCloseUnpinnedTabs":
gZenWorkspaces.closeAllUnpinnedTabs();
break;
case "cmd_zenUnloadWorkspace": {
gZenWorkspaces.unloadWorkspace();
break;
}
case "cmd_zenNewNavigatorUnsynced":
OpenBrowserWindow({ zenSyncedWindow: false });
break;
case "cmd_zenNewLiveFolder": {
const { ZenLiveFoldersManager } = ChromeUtils.importESModule(
"resource:///modules/zen/ZenLiveFoldersManager.sys.mjs"
);
ZenLiveFoldersManager.handleEvent(event);
break;
}
default:
gZenGlanceManager.handleMainCommandSet(event);
if (event.target.id.startsWith("cmd_zenWorkspaceSwitch")) {
const index = parseInt(event.target.id.replace("cmd_zenWorkspaceSwitch", ""), 10) - 1;
gZenWorkspaces.shortcutSwitchTo(index);
case "cmd_zenCloseUnpinnedTabs":
gZenWorkspaces.closeAllUnpinnedTabs();
break;
case "cmd_zenUnloadWorkspace": {
gZenWorkspaces.unloadWorkspace();
break;
}
break;
}
});
case "cmd_zenNewNavigatorUnsynced":
OpenBrowserWindow({ zenSyncedWindow: false });
break;
case "cmd_zenNewLiveFolder": {
const { ZenLiveFoldersManager } = ChromeUtils.importESModule(
"resource:///modules/zen/ZenLiveFoldersManager.sys.mjs"
);
ZenLiveFoldersManager.handleEvent(event);
break;
}
default:
gZenGlanceManager.handleMainCommandSet(event);
if (event.target.id.startsWith("cmd_zenWorkspaceSwitch")) {
const index =
parseInt(
event.target.id.replace("cmd_zenWorkspaceSwitch", ""),
10
) - 1;
gZenWorkspaces.shortcutSwitchTo(index);
}
break;
}
});
},
{ once: true }
);

View File

@@ -38,7 +38,8 @@
* begin listening to changes in preferred color scheme.
*/
init() {
this._inMainBrowserWindow = window.location.href == "chrome://browser/content/browser.xhtml";
this._inMainBrowserWindow =
window.location.href == "chrome://browser/content/browser.xhtml";
this.listenForEvents();
this.updateAllThemeBasics();
},
@@ -91,14 +92,23 @@
},
updateBorderRadius() {
const borderRadius = Services.prefs.getIntPref("zen.theme.border-radius", -1);
const borderRadius = Services.prefs.getIntPref(
"zen.theme.border-radius",
-1
);
// -1 is the default value, will use platform-native values
// otherwise, use the custom value
if (borderRadius == -1) {
if (AppConstants.platform == "macosx") {
const targetRadius = window.matchMedia("(-moz-mac-tahoe-theme)").matches ? 16 : 10;
document.documentElement.style.setProperty("--zen-border-radius", targetRadius + "px");
const targetRadius = window.matchMedia("(-moz-mac-tahoe-theme)")
.matches
? 16
: 10;
document.documentElement.style.setProperty(
"--zen-border-radius",
targetRadius + "px"
);
} else if (AppConstants.platform == "linux") {
// Linux uses GTK CSD titlebar radius, default to 8px
document.documentElement.style.setProperty(
@@ -107,11 +117,17 @@
);
} else {
// Windows defaults to 8px
document.documentElement.style.setProperty("--zen-border-radius", "8px");
document.documentElement.style.setProperty(
"--zen-border-radius",
"8px"
);
}
} else {
// Use the overridden value
document.documentElement.style.setProperty("--zen-border-radius", borderRadius + "px");
document.documentElement.style.setProperty(
"--zen-border-radius",
borderRadius + "px"
);
}
},
@@ -121,14 +137,19 @@
if (
document.documentElement.hasAttribute("inFullscreen") &&
window.gZenCompactModeManager?.preference &&
!document.getElementById("tabbrowser-tabbox")?.hasAttribute("zen-split-view") &&
!document
.getElementById("tabbrowser-tabbox")
?.hasAttribute("zen-split-view") &&
Services.prefs.getBoolPref("zen.view.borderless-fullscreen", true)
) {
separation = 0;
}
// In order to still use it on fullscreen, even if it's 0px, add .1px (almost invisible)
separation = Math.max(kMinElementSeparation, separation);
document.documentElement.style.setProperty("--zen-element-separation", separation + "px");
document.documentElement.style.setProperty(
"--zen-element-separation",
separation + "px"
);
if (separation == kMinElementSeparation) {
document.documentElement.setAttribute("zen-no-padding", true);
} else {
@@ -147,8 +168,13 @@
* Update the accent color.
*/
updateAccentColor() {
const accentColor = Services.prefs.getStringPref("zen.theme.accent-color");
document.documentElement.style.setProperty("--zen-primary-color", accentColor);
const accentColor = Services.prefs.getStringPref(
"zen.theme.accent-color"
);
document.documentElement.style.setProperty(
"--zen-primary-color",
accentColor
);
},
};

View File

@@ -44,14 +44,20 @@ window.gZenCompactModeManager = {
_removeHoverFrames: {},
// Delay to avoid flickering when hovering over the sidebar
HOVER_HACK_DELAY: Services.prefs.getIntPref("zen.view.compact.hover-hack-delay", 0),
HOVER_HACK_DELAY: Services.prefs.getIntPref(
"zen.view.compact.hover-hack-delay",
0
),
preInit() {
this._wasInCompactMode = Services.prefs.getBoolPref(
"zen.view.compact.enable-at-startup",
false
);
this._canDebugLog = Services.prefs.getBoolPref("zen.view.compact.debug", false);
this._canDebugLog = Services.prefs.getBoolPref(
"zen.view.compact.debug",
false
);
this.addContextMenu();
},
@@ -60,12 +66,18 @@ window.gZenCompactModeManager = {
this.addMouseActions();
const tabIsRightObserver = this._updateSidebarIsOnRight.bind(this);
Services.prefs.addObserver("zen.tabs.vertical.right-side", tabIsRightObserver);
Services.prefs.addObserver(
"zen.tabs.vertical.right-side",
tabIsRightObserver
);
window.addEventListener(
"unload",
() => {
Services.prefs.removeObserver("zen.tabs.vertical.right-side", tabIsRightObserver);
Services.prefs.removeObserver(
"zen.tabs.vertical.right-side",
tabIsRightObserver
);
},
{ once: true }
);
@@ -78,7 +90,9 @@ window.gZenCompactModeManager = {
this.addHasPolyfillObserver();
// Clear hover states when window state changes (minimize, maximize, etc.)
window.addEventListener("sizemodechange", () => this._clearAllHoverStates());
window.addEventListener("sizemodechange", () =>
this._clearAllHoverStates()
);
this._canShowBackgroundTabToast = Services.prefs.getBoolPref(
"zen.view.compact.show-background-tab-toast",
@@ -86,7 +100,7 @@ window.gZenCompactModeManager = {
);
if (AppConstants.platform == "macosx") {
window.addEventListener("mouseover", (event) => {
window.addEventListener("mouseover", event => {
const buttons = gZenVerticalTabsManager.actualWindowButtons;
if (event.target.closest(".titlebar-buttonbox-container") === buttons) {
return;
@@ -112,7 +126,9 @@ window.gZenCompactModeManager = {
},
get shouldBeCompact() {
return !document.documentElement.getAttribute("chromehidden")?.includes("toolbar");
return !document.documentElement
.getAttribute("chromehidden")
?.includes("toolbar");
},
set preference(value) {
@@ -147,7 +163,9 @@ window.gZenCompactModeManager = {
if (typeof this._sidebarIsOnRight !== "undefined") {
return this._sidebarIsOnRight;
}
this._sidebarIsOnRight = Services.prefs.getBoolPref("zen.tabs.vertical.right-side");
this._sidebarIsOnRight = Services.prefs.getBoolPref(
"zen.tabs.vertical.right-side"
);
return this._sidebarIsOnRight;
},
@@ -156,7 +174,12 @@ window.gZenCompactModeManager = {
},
addHasPolyfillObserver() {
const attributes = ["panelopen", "open", "breakout-extend", "zen-floating-urlbar"];
const attributes = [
"panelopen",
"open",
"breakout-extend",
"zen-floating-urlbar",
];
this.sidebarObserverId = ZenHasPolyfill.observeSelectorExistence(
this.sidebar,
[
@@ -230,7 +253,9 @@ window.gZenCompactModeManager = {
updateCompactModeContext(isSingleToolbar) {
const isIllegalState = 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");
if (!menu) {
return;
@@ -292,7 +317,8 @@ window.gZenCompactModeManager = {
// on macos: collapsed + left side + only toolbar
// on windows: collapsed + right side + only toolbar
const closelyIllegalState =
(isLeftSideButtons && !isRightSidebar) || (!isLeftSideButtons && isRightSidebar);
(isLeftSideButtons && !isRightSidebar) ||
(!isLeftSideButtons && isRightSidebar);
if (closelyIllegalState && canHideToolbar && !canHideSidebar) {
// This state is illegal
Services.prefs.setBoolPref("zen.view.compact.hide-tabbar", true);
@@ -304,7 +330,7 @@ window.gZenCompactModeManager = {
},
callAllEventListeners() {
this._eventListeners.forEach((callback) => callback());
this._eventListeners.forEach(callback => callback());
},
addEventListener(callback) {
@@ -338,7 +364,9 @@ window.gZenCompactModeManager = {
} else {
ZenHasPolyfill.disconnectObserver(this.sidebarObserverId);
}
window.dispatchEvent(new CustomEvent("ZenCompactMode:Toggled", { detail: this.preference }));
window.dispatchEvent(
new CustomEvent("ZenCompactMode:Toggled", { detail: this.preference })
);
},
// NOTE: Dont actually use event, it's just so we make sure
@@ -350,14 +378,20 @@ window.gZenCompactModeManager = {
}
let sidebarWidth = this.sidebar.getBoundingClientRect().width;
const shouldRecalculate =
this.preference || document.documentElement.hasAttribute("zen-creating-workspace");
const sidebarExpanded = document.documentElement.hasAttribute("zen-sidebar-expanded");
this.preference ||
document.documentElement.hasAttribute("zen-creating-workspace");
const sidebarExpanded = document.documentElement.hasAttribute(
"zen-sidebar-expanded"
);
if (sidebarWidth > 1) {
if (shouldRecalculate && sidebarExpanded) {
sidebarWidth = Math.max(sidebarWidth, 150);
}
// Second variable to get the genuine width of the sidebar
this.sidebar.style.setProperty("--actual-zen-sidebar-width", `${sidebarWidth}px`);
this.sidebar.style.setProperty(
"--actual-zen-sidebar-width",
`${sidebarWidth}px`
);
window.dispatchEvent(new window.Event("resize")); // To recalculate the layout
if (
event &&
@@ -368,7 +402,10 @@ window.gZenCompactModeManager = {
return;
}
delete gZenVerticalTabsManager._hadSidebarCollapse;
this.sidebar.style.setProperty("--zen-sidebar-width", `${sidebarWidth}px`);
this.sidebar.style.setProperty(
"--zen-sidebar-width",
`${sidebarWidth}px`
);
}
return sidebarWidth;
},
@@ -390,14 +427,16 @@ window.gZenCompactModeManager = {
animateCompactMode() {
// Get the splitter width before hiding it (we need to hide it before animating on right)
document.documentElement.setAttribute("zen-compact-animating", "true");
return new Promise((resolve) => {
return new Promise(resolve => {
// We need to set the splitter width before hiding it
let splitterWidth = document
.getElementById("zen-sidebar-splitter")
.getBoundingClientRect().width;
const isCompactMode = this.preference;
const canHideSidebar = this.canHideSidebar;
let canAnimate = lazy.COMPACT_MODE_CAN_ANIMATE_SIDEBAR && !this.isSidebarPotentiallyOpen();
let canAnimate =
lazy.COMPACT_MODE_CAN_ANIMATE_SIDEBAR &&
!this.isSidebarPotentiallyOpen();
if (typeof this._wasInCompactMode !== "undefined") {
canAnimate = false;
delete this._wasInCompactMode;
@@ -443,8 +482,14 @@ window.gZenCompactModeManager = {
.animate(
this.sidebar,
{
marginRight: [0, this.sidebarIsOnRight ? `-${sidebarWidth}px` : 0],
marginLeft: [0, this.sidebarIsOnRight ? 0 : `-${sidebarWidth}px`],
marginRight: [
0,
this.sidebarIsOnRight ? `-${sidebarWidth}px` : 0,
],
marginLeft: [
0,
this.sidebarIsOnRight ? 0 : `-${sidebarWidth}px`,
],
},
{
ease: "easeIn",
@@ -511,7 +556,9 @@ window.gZenCompactModeManager = {
)
.then(() => {
this.sidebar.removeAttribute("animate");
document.getElementById("browser").style.removeProperty("overflow");
document
.getElementById("browser")
.style.removeProperty("overflow");
this.sidebar.style.transition = "none";
this.sidebar.style.removeProperty("margin-right");
this.sidebar.style.removeProperty("margin-left");
@@ -533,7 +580,9 @@ window.gZenCompactModeManager = {
},
updateContextMenu() {
const toggle = document.getElementById("zen-context-menu-compact-mode-toggle");
const toggle = document.getElementById(
"zen-context-menu-compact-mode-toggle"
);
if (!toggle) {
return;
}
@@ -569,7 +618,9 @@ window.gZenCompactModeManager = {
},
_updateSidebarIsOnRight() {
this._sidebarIsOnRight = Services.prefs.getBoolPref("zen.tabs.vertical.right-side");
this._sidebarIsOnRight = Services.prefs.getBoolPref(
"zen.tabs.vertical.right-side"
);
},
toggleSidebar() {
@@ -580,7 +631,9 @@ window.gZenCompactModeManager = {
if (this._hideAfterHoverDuration) {
return this._hideAfterHoverDuration;
}
return Services.prefs.getIntPref("zen.view.compact.toolbar-hide-after-hover.duration");
return Services.prefs.getIntPref(
"zen.view.compact.toolbar-hide-after-hover.duration"
);
},
get hoverableElements() {
@@ -613,7 +666,9 @@ window.gZenCompactModeManager = {
if (this._flashTimeouts[id]) {
clearTimeout(this._flashTimeouts[id]);
} else {
requestAnimationFrame(() => this._setElementExpandAttribute(element, true, attrName));
requestAnimationFrame(() =>
this._setElementExpandAttribute(element, true, attrName)
);
}
this._flashTimeouts[id] = setTimeout(() => {
window.requestAnimationFrame(() => {
@@ -629,11 +684,18 @@ window.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",
"zen-compact-mode-active",
];
const isToolbar = element.id === "zen-appcontent-navbar-wrapper";
this.log("Setting", attr, "to", value, "on element", element?.id);
if (value) {
if (attr === "zen-has-hover" && element !== gZenVerticalTabsManager.actualWindowButtons) {
if (
attr === "zen-has-hover" &&
element !== gZenVerticalTabsManager.actualWindowButtons
) {
element.setAttribute("zen-has-implicit-hover", "true");
if (!lazy.COMPACT_MODE_SHOW_SIDEBAR_AND_TOOLBAR_ON_HOVER) {
return;
@@ -659,7 +721,9 @@ window.gZenCompactModeManager = {
// Only remove if none of the verified attributes are present
if (
isToolbar &&
!kVerifiedAttributes.some((verifiedAttr) => element.hasAttribute(verifiedAttr))
!kVerifiedAttributes.some(verifiedAttr =>
element.hasAttribute(verifiedAttr)
)
) {
gBrowser.tabpanels.removeAttribute("has-toolbar-hovered");
}
@@ -667,11 +731,14 @@ window.gZenCompactModeManager = {
},
addMouseActions() {
gURLBar.addEventListener("mouseenter", (event) => {
gURLBar.addEventListener("mouseenter", event => {
this.log("Mouse entered URL bar:", event.target);
if (event.target.closest("#urlbar[zen-floating-urlbar]")) {
window.requestAnimationFrame(() => {
this._setElementExpandAttribute(gZenVerticalTabsManager.actualWindowButtons, false);
this._setElementExpandAttribute(
gZenVerticalTabsManager.actualWindowButtons,
false
);
});
this._hasHoveredUrlbar = true;
}
@@ -685,7 +752,7 @@ window.gZenCompactModeManager = {
this._setElementExpandAttribute(target, true);
}
const onEnter = (event) => {
const onEnter = event => {
setTimeout(() => {
if (event.type === "mouseenter" && !event.target.matches(":hover")) {
return;
@@ -697,7 +764,9 @@ window.gZenCompactModeManager = {
this.clearFlashTimeout("has-hover" + target.id);
window.requestAnimationFrame(() => {
if (
document.documentElement.getAttribute("supress-primary-adjustment") === "true" ||
document.documentElement.getAttribute(
"supress-primary-adjustment"
) === "true" ||
this._hasHoveredUrlbar ||
this._ignoreNextHover ||
target.hasAttribute("zen-has-hover")
@@ -709,9 +778,10 @@ window.gZenCompactModeManager = {
}, this.HOVER_HACK_DELAY);
};
const onLeave = (event) => {
const onLeave = event => {
if (AppConstants.platform == "macosx") {
const buttonRect = gZenVerticalTabsManager.actualWindowButtons.getBoundingClientRect();
const buttonRect =
gZenVerticalTabsManager.actualWindowButtons.getBoundingClientRect();
const MAC_WINDOW_BUTTONS_X_BORDER = buttonRect.width + buttonRect.x;
const MAC_WINDOW_BUTTONS_Y_BORDER = buttonRect.height + buttonRect.y;
if (
@@ -734,8 +804,12 @@ window.gZenCompactModeManager = {
}
if (
event.explicitOriginalTarget?.closest?.("#urlbar[zen-floating-urlbar]") ||
(document.documentElement.getAttribute("supress-primary-adjustment") === "true" &&
event.explicitOriginalTarget?.closest?.(
"#urlbar[zen-floating-urlbar]"
) ||
(document.documentElement.getAttribute(
"supress-primary-adjustment"
) === "true" &&
gZenVerticalTabsManager._hasSetSingleToolbar) ||
this._hasHoveredUrlbar ||
this._ignoreNextHover ||
@@ -758,8 +832,8 @@ window.gZenCompactModeManager = {
"zen-has-hover"
);
} else {
this._removeHoverFrames[target.id] = window.requestAnimationFrame(() =>
this._setElementExpandAttribute(target, false)
this._removeHoverFrames[target.id] = window.requestAnimationFrame(
() => this._setElementExpandAttribute(target, false)
);
}
}, this.HOVER_HACK_DELAY);
@@ -772,9 +846,12 @@ window.gZenCompactModeManager = {
target.addEventListener("dragleave", onLeave);
}
document.documentElement.addEventListener("mouseleave", (event) => {
document.documentElement.addEventListener("mouseleave", event => {
setTimeout(() => {
const screenEdgeCrossed = this._getCrossedEdge(event.pageX, event.pageY);
const screenEdgeCrossed = this._getCrossedEdge(
event.pageX,
event.pageY
);
if (!screenEdgeCrossed) {
return;
}
@@ -783,8 +860,19 @@ window.gZenCompactModeManager = {
continue;
}
const target = entry.element;
const boundAxis = entry.screenEdge === "right" || entry.screenEdge === "left" ? "y" : "x";
if (!this._positionInBounds(boundAxis, target, event.pageX, event.pageY, 7)) {
const boundAxis =
entry.screenEdge === "right" || entry.screenEdge === "left"
? "y"
: "x";
if (
!this._positionInBounds(
boundAxis,
target,
event.pageX,
event.pageY,
7
)
) {
continue;
}
window.cancelAnimationFrame(this._removeHoverFrames[target.id]);
@@ -821,7 +909,12 @@ window.gZenCompactModeManager = {
});
},
_getCrossedEdge(posX, posY, element = document.documentElement, maxDistance = 10) {
_getCrossedEdge(
posX,
posY,
element = document.documentElement,
maxDistance = 10
) {
const targetBox = element.getBoundingClientRect();
posX = Math.max(targetBox.left, Math.min(posX, targetBox.right));
posY = Math.max(targetBox.top, Math.min(posY, targetBox.bottom));
@@ -843,7 +936,11 @@ window.gZenCompactModeManager = {
// Clear hover attributes from all hoverable elements
for (let entry of this.hoverableElements) {
const target = entry.element;
if (target && !target.matches(":hover") && target.hasAttribute("zen-has-hover")) {
if (
target &&
!target.matches(":hover") &&
target.hasAttribute("zen-has-hover")
) {
this._setElementExpandAttribute(target, false);
this.clearFlashTimeout("has-hover" + target.id);
}

View File

@@ -86,7 +86,9 @@ class nsZenDownloadAnimationElement extends HTMLElement {
);
this.shadowRoot.appendChild(link);
} catch (error) {
console.error(`[${nsZenDownloadAnimationElement.name}] Error loading arc styles: ${error}`);
console.error(
`[${nsZenDownloadAnimationElement.name}] Error loading arc styles: ${error}`
);
}
}
@@ -99,7 +101,8 @@ class nsZenDownloadAnimationElement extends HTMLElement {
}
// Determine animation target position
const { endPosition, isDownloadButtonVisible } = this.#determineEndPosition();
const { endPosition, isDownloadButtonVisible } =
this.#determineEndPosition();
const areTabsPositionedRight = this.#areTabsOnRightSide();
// Create and prepare the arc animation element
@@ -131,7 +134,10 @@ class nsZenDownloadAnimationElement extends HTMLElement {
}
#areTabsOnRightSide() {
const position = Services.prefs.getIntPref("zen.downloads.icon-popup-position", 0);
const position = Services.prefs.getIntPref(
"zen.downloads.icon-popup-position",
0
);
if (position === 1) {
return false;
}
@@ -143,7 +149,8 @@ class nsZenDownloadAnimationElement extends HTMLElement {
#determineEndPosition() {
const downloadsButton = document.getElementById("downloads-button");
const isDownloadButtonVisible = downloadsButton && this.#isElementVisible(downloadsButton);
const isDownloadButtonVisible =
downloadsButton && this.#isElementVisible(downloadsButton);
let endPosition = { clientX: 0, clientY: 0 };
@@ -161,7 +168,9 @@ class nsZenDownloadAnimationElement extends HTMLElement {
const wrapperRect = wrapper.getBoundingClientRect();
endPosition = {
clientX: areTabsPositionedRight ? wrapperRect.right - 42 : wrapperRect.left + 42,
clientX: areTabsPositionedRight
? wrapperRect.right - 42
: wrapperRect.left + 42,
clientY: wrapperRect.bottom - 40,
};
}
@@ -179,7 +188,9 @@ class nsZenDownloadAnimationElement extends HTMLElement {
`;
const fragment = window.MozXULElement.parseXULToFragment(arcAnimationHTML);
const animationElement = fragment.querySelector(".zen-download-arc-animation");
const animationElement = fragment.querySelector(
".zen-download-arc-animation"
);
Object.assign(animationElement.style, {
left: `${startPosition.clientX}px`,
@@ -194,7 +205,10 @@ class nsZenDownloadAnimationElement extends HTMLElement {
#calculateOptimalArc(startPosition, endPosition, distance) {
// Calculate available space for the arc
const availableTopSpace = Math.min(startPosition.clientY, endPosition.clientY);
const availableTopSpace = Math.min(
startPosition.clientY,
endPosition.clientY
);
const viewportHeight = window.innerHeight;
const availableBottomSpace =
viewportHeight - Math.max(startPosition.clientY, endPosition.clientY);
@@ -203,7 +217,9 @@ class nsZenDownloadAnimationElement extends HTMLElement {
const shouldArcDownward = availableBottomSpace > availableTopSpace;
// Use the space in the direction we're arcing
const availableSpace = shouldArcDownward ? availableBottomSpace : availableTopSpace;
const availableSpace = shouldArcDownward
? availableBottomSpace
: availableTopSpace;
// Limit arc height to a percentage of the available space
const arcHeight = Math.min(
@@ -233,19 +249,30 @@ class nsZenDownloadAnimationElement extends HTMLElement {
}
await gZenUIManager.motion.animate(arcAnimationElement, sequence, {
duration: Services.prefs.getIntPref("zen.downloads.download-animation-duration") / 1000,
duration:
Services.prefs.getIntPref(
"zen.downloads.download-animation-duration"
) / 1000,
easing: "cubic-bezier(0.37, 0, 0.63, 1)",
fill: "forwards",
});
this.#cleanArcAnimation(arcAnimationElement);
} catch (error) {
console.error("[nsZenDownloadAnimationElement] Error in animation sequence:", error);
console.error(
"[nsZenDownloadAnimationElement] Error in animation sequence:",
error
);
this.#cleanArcAnimation(arcAnimationElement);
}
}
#createArcAnimationSequence(distanceX, distanceY, arcHeight, shouldArcDownward) {
#createArcAnimationSequence(
distanceX,
distanceY,
arcHeight,
shouldArcDownward
) {
const sequence = { offset: [], opacity: [], transform: [] };
const arcDirection = shouldArcDownward ? 1 : -1;
@@ -284,7 +311,9 @@ class nsZenDownloadAnimationElement extends HTMLElement {
// Position on arc
const x = distanceX * eased;
const y = distanceY * eased + arcDirection * arcHeight * (1 - (2 * eased - 1) ** 2);
const y =
distanceY * eased +
arcDirection * arcHeight * (1 - (2 * eased - 1) ** 2);
// Calculate rotation to point in the direction of movement
let rotation = previousRotation;
@@ -293,10 +322,12 @@ class nsZenDownloadAnimationElement extends HTMLElement {
const prevX = distanceX * prevEased;
const prevAdjustedProgress = prevEased * 2 - 1;
const prevVerticalOffset = arcDirection * arcHeight * (1 - prevAdjustedProgress * 2);
const prevVerticalOffset =
arcDirection * arcHeight * (1 - prevAdjustedProgress * 2);
const prevY = distanceY * prevEased + prevVerticalOffset;
const targetRotation = Math.atan2(y - prevY, x - prevX) * (180 / Math.PI);
const targetRotation =
Math.atan2(y - prevY, x - prevX) * (180 / Math.PI);
rotation += (targetRotation - previousRotation) * 0.01;
previousRotation = rotation;
@@ -353,8 +384,11 @@ class nsZenDownloadAnimationElement extends HTMLElement {
const sideProp = areTabsPositionedRight ? "right" : "left";
const fragment = window.MozXULElement.parseXULToFragment(boxAnimationHTML);
this.#boxAnimationElement = fragment.querySelector(".zen-download-box-animation");
const fragment =
window.MozXULElement.parseXULToFragment(boxAnimationHTML);
this.#boxAnimationElement = fragment.querySelector(
".zen-download-box-animation"
);
Object.assign(this.#boxAnimationElement.style, {
bottom: "24px",
@@ -405,7 +439,10 @@ class nsZenDownloadAnimationElement extends HTMLElement {
}
#getBoxAnimationDurationMs() {
return Services.prefs.getIntPref("zen.downloads.download-animation-duration") + 200;
return (
Services.prefs.getIntPref("zen.downloads.download-animation-duration") +
200
);
}
async #finishBoxAnimation(areTabsPositionedRight) {

View File

@@ -8,9 +8,10 @@
// Wrap in a block to prevent leaking to window scope.
{
const isTab = (element) => gBrowser.isTab(element);
const isTabGroupLabel = (element) => gBrowser.isTabGroupLabel(element);
const isEssentialsPromo = (element) => element?.tagName.toUpperCase() == "ZEN-ESSENTIALS-PROMO";
const isTab = element => gBrowser.isTab(element);
const isTabGroupLabel = element => gBrowser.isTabGroupLabel(element);
const isEssentialsPromo = element =>
element?.tagName.toUpperCase() == "ZEN-ESSENTIALS-PROMO";
/**
* The elements in the tab strip from `this.ariaFocusableItems` that contain
@@ -34,7 +35,7 @@
* @param {MozTabbrowserTab|typeof MozTabbrowserTabGroup.labelElement} element
* @returns {MozTabbrowserTab|vbox}
*/
const elementToMove = (element) => {
const elementToMove = element => {
if (
!element ||
element.closest(".zen-current-workspace-indicator") ||
@@ -118,9 +119,13 @@
init() {
super.init();
this.handle_windowDragEnter = this.handle_windowDragEnter.bind(this);
window.addEventListener("dragleave", this.handle_windowDragLeave.bind(this), {
capture: true,
});
window.addEventListener(
"dragleave",
this.handle_windowDragLeave.bind(this),
{
capture: true,
}
);
}
startTabDrag(event, tab, ...args) {
@@ -133,7 +138,11 @@
tab = tab.group;
}
const draggingTabs = tab.multiselected ? gBrowser.selectedTabs : [tab];
const { offsetX, offsetY } = this.#getDragImageOffset(event, tab, draggingTabs);
const { offsetX, offsetY } = this.#getDragImageOffset(
event,
tab,
draggingTabs
);
const dragImage = this.#createDragImageForTabs(draggingTabs);
this.originalDragImageArgs = [dragImage, offsetX, offsetY];
dt.setDragImage(...this.originalDragImageArgs);
@@ -146,7 +155,9 @@
const periphery = gZenWorkspaces.activeWorkspaceElement.querySelector(
"#tabbrowser-arrowscrollbox-periphery"
);
const tabRect = window.windowUtils.getBoundsWithoutFlushing(movingTabs[0]);
const tabRect = window.windowUtils.getBoundsWithoutFlushing(
movingTabs[0]
);
const wrapper = document.createElement("div");
let movingTabsCount = Math.min(movingTabs.length, 3);
wrapper.style.width = tabRect.width + "px";
@@ -160,7 +171,8 @@
if (tab.hasAttribute("zen-essential")) {
const rect = tab.getBoundingClientRect();
tabClone.style.minWidth = tabClone.style.maxWidth = `${rect.width}px`;
tabClone.style.minHeight = tabClone.style.maxHeight = `${rect.height}px`;
tabClone.style.minHeight =
tabClone.style.maxHeight = `${rect.height}px`;
if (tabClone.hasAttribute("visuallyselected")) {
tabClone.style.transform = "translate(-50%, -50%)";
}
@@ -181,9 +193,16 @@
if (isTab(tabClone)) {
// We need to limit the label content so the drag image doesn't grow too big.
const label = tabClone.textLabel;
const tabLabelParentWidth = label.parentElement.getBoundingClientRect().width;
label.textContent = label.textContent.slice(0, Math.floor(tabLabelParentWidth / 6));
} else if (gBrowser.isTabGroup(tabClone) && tabClone.hasAttribute("split-view-group")) {
const tabLabelParentWidth =
label.parentElement.getBoundingClientRect().width;
label.textContent = label.textContent.slice(
0,
Math.floor(tabLabelParentWidth / 6)
);
} else if (
gBrowser.isTabGroup(tabClone) &&
tabClone.hasAttribute("split-view-group")
) {
let tabs = tab.tabs;
for (let j = 0; j < tabs.length; j++) {
const tabInGroup = tabs[j];
@@ -220,7 +239,10 @@
// eslint-disable-next-line complexity
_animateTabMove(event) {
let draggedTab = event.dataTransfer.mozGetDataAt(TAB_DROP_TYPE, 0);
if (event.target.closest("#zen-essentials") && !isEssentialsPromo(event.target)) {
if (
event.target.closest("#zen-essentials") &&
!isEssentialsPromo(event.target)
) {
if (!isTab(draggedTab)) {
this.clearDragOverVisuals();
return;
@@ -242,7 +264,9 @@
tabs = [...movingTabs];
}
let screen = this._tabbrowserTabs.verticalMode ? event.screenY : event.screenX;
let screen = this._tabbrowserTabs.verticalMode
? event.screenY
: event.screenX;
if (screen == dragData.animLastScreenPos) {
return;
}
@@ -255,9 +279,11 @@
tabs.reverse();
}
let bounds = (ele) => window.windowUtils.getBoundsWithoutFlushing(ele);
let bounds = ele => window.windowUtils.getBoundsWithoutFlushing(ele);
let logicalForward = screenForward != this._rtlMode;
let screenAxis = this._tabbrowserTabs.verticalMode ? "screenY" : "screenX";
let screenAxis = this._tabbrowserTabs.verticalMode
? "screenY"
: "screenX";
let size = this._tabbrowserTabs.verticalMode ? "height" : "width";
let { width: tabWidth, height: tabHeight } = bounds(draggedTab);
let tabSize = this._tabbrowserTabs.verticalMode ? tabHeight : tabWidth;
@@ -275,7 +301,7 @@
);
let lastMovingTab = movingTabs.at(-1);
let firstMovingTab = movingTabs[0];
let endEdge = (ele) => ele[screenAxis] + bounds(ele)[size];
let endEdge = ele => ele[screenAxis] + bounds(ele)[size];
let lastMovingTabScreen = endEdge(lastMovingTab);
let firstMovingTabScreen = firstMovingTab[screenAxis];
let shiftSize = lastMovingTabScreen - firstMovingTabScreen;
@@ -298,7 +324,8 @@
// Center the tab under the cursor if the tab is not under the cursor while dragging
let draggedTabScreenAxis = draggedTab[screenAxis] + translate;
if (
(screen < draggedTabScreenAxis || screen > draggedTabScreenAxis + tabSize) &&
(screen < draggedTabScreenAxis ||
screen > draggedTabScreenAxis + tabSize) &&
draggedTabScreenAxis + tabSize < endBound &&
draggedTabScreenAxis > startBound
) {
@@ -309,7 +336,7 @@
dragData.translatePos = translate;
tabs = tabs.filter((t) => !movingTabsSet.has(t) || t == draggedTab);
tabs = tabs.filter(t => !movingTabsSet.has(t) || t == draggedTab);
/**
* When the `draggedTab` is just starting to move, the `draggedTab` is in
@@ -333,16 +360,23 @@
* @returns {number}
*/
let getTabShift = (item, dropElementIndex) => {
if (item.elementIndex < draggedTab.elementIndex && item.elementIndex >= dropElementIndex) {
if (
item.elementIndex < draggedTab.elementIndex &&
item.elementIndex >= dropElementIndex
) {
return this._rtlMode ? -shiftSize : shiftSize;
}
if (item.elementIndex > draggedTab.elementIndex && item.elementIndex < dropElementIndex) {
if (
item.elementIndex > draggedTab.elementIndex &&
item.elementIndex < dropElementIndex
) {
return this._rtlMode ? shiftSize : -shiftSize;
}
return 0;
};
let oldDropElementIndex = dragData.animDropElementIndex ?? movingTabs[0].elementIndex;
let oldDropElementIndex =
dragData.animDropElementIndex ?? movingTabs[0].elementIndex;
/**
* Returns the higher % by which one element overlaps another
@@ -423,7 +457,9 @@
* time.
*/
let getOverlappedElement = () => {
let point = (screenForward ? lastMovingTabScreen : firstMovingTabScreen) + translate;
let point =
(screenForward ? lastMovingTabScreen : firstMovingTabScreen) +
translate;
let low = 0;
let high = tabs.length - 1;
while (low <= high) {
@@ -433,7 +469,9 @@
}
let element = tabs[mid];
let elementForSize = elementToMove(element);
screen = elementForSize[screenAxis] + getTabShift(element, oldDropElementIndex);
screen =
elementForSize[screenAxis] +
getTabShift(element, oldDropElementIndex);
if (screen > point) {
high = mid - 1;
@@ -489,11 +527,18 @@
// 4) we just started dragging and the `oldDropElementIndex` has its default
// valuë of `movingTabs[0].elementIndex`. In this case, the drop element
// shouldn't be a moving tab, so keep it `undefined`.
let lastPossibleDropElement = this._rtlMode ? tabs.find((t) => t != draggedTab) : undefined;
let maxElementIndexForDropElement = lastPossibleDropElement?.elementIndex;
let lastPossibleDropElement = this._rtlMode
? tabs.find(t => t != draggedTab)
: undefined;
let maxElementIndexForDropElement =
lastPossibleDropElement?.elementIndex;
if (Number.isInteger(maxElementIndexForDropElement)) {
let index = Math.min(oldDropElementIndex, maxElementIndexForDropElement);
let oldDropElementCandidate = this._tabbrowserTabs.ariaFocusableItems.at(index);
let index = Math.min(
oldDropElementIndex,
maxElementIndexForDropElement
);
let oldDropElementCandidate =
this._tabbrowserTabs.ariaFocusableItems.at(index);
if (!movingTabsSet.has(oldDropElementCandidate)) {
dropElement = oldDropElementCandidate;
}
@@ -507,7 +552,8 @@
let dropElementForOverlap = elementToMove(dropElement);
let dropElementScreen = dropElementForOverlap[screenAxis];
let dropElementPos = dropElementScreen + getTabShift(dropElement, oldDropElementIndex);
let dropElementPos =
dropElementScreen + getTabShift(dropElement, oldDropElementIndex);
let dropElementSize = bounds(dropElementForOverlap)[size];
let firstMovingTabPos = firstMovingTabScreen + translate;
overlapPercent = greatestOverlap(
@@ -518,7 +564,9 @@
);
moveOverThreshold = gBrowser._tabGroupsEnabled
? Services.prefs.getIntPref("browser.tabs.dragDrop.moveOverThresholdPercent") / 100
? Services.prefs.getIntPref(
"browser.tabs.dragDrop.moveOverThresholdPercent"
) / 100
: 0.5;
moveOverThreshold = Math.min(1, Math.max(0, moveOverThreshold));
let shouldMoveOver = overlapPercent > moveOverThreshold;
@@ -536,7 +584,8 @@
// Recalculate the overlap with the updated drop index for when the
// drop element moves over.
dropElementPos = dropElementScreen + getTabShift(dropElement, newDropElementIndex);
dropElementPos =
dropElementScreen + getTabShift(dropElement, newDropElementIndex);
overlapPercent = greatestOverlap(
firstMovingTabPos,
shiftSize,
@@ -611,17 +660,25 @@
}
#shouldSwitchSpace(event) {
const padding = Services.prefs.getIntPref("zen.workspaces.dnd-switch-padding");
const padding = Services.prefs.getIntPref(
"zen.workspaces.dnd-switch-padding"
);
// If we are hovering over the edges of the gNavToolbox or the splitter, we
// can change the workspace after a short delay.
const splitter = document.getElementById("zen-sidebar-splitter");
let rect = window.windowUtils.getBoundsWithoutFlushing(gNavToolbox);
if (!(gZenCompactModeManager.preference && gZenCompactModeManager.canHideSidebar)) {
rect.width += window.windowUtils.getBoundsWithoutFlushing(splitter).width;
if (!(
gZenCompactModeManager.preference &&
gZenCompactModeManager.canHideSidebar
)) {
rect.width +=
window.windowUtils.getBoundsWithoutFlushing(splitter).width;
}
const { clientX } = event;
const isNearLeftEdge = clientX >= rect.left - padding && clientX <= rect.left + padding;
const isNearRightEdge = clientX >= rect.right - padding && clientX <= rect.right + padding;
const isNearLeftEdge =
clientX >= rect.left - padding && clientX <= rect.left + padding;
const isNearRightEdge =
clientX >= rect.right - padding && clientX <= rect.right + padding;
return { isNearLeftEdge, isNearRightEdge };
}
@@ -639,14 +696,19 @@
this.clearSpaceSwitchTimer();
return;
}
const { isNearLeftEdge, isNearRightEdge } = this.#shouldSwitchSpace(event);
const { isNearLeftEdge, isNearRightEdge } =
this.#shouldSwitchSpace(event);
if (isNearLeftEdge || isNearRightEdge) {
if (!this.#changeSpaceTimer) {
this.#changeSpaceTimer = setTimeout(() => {
this.clearDragOverVisuals();
gZenWorkspaces
.changeWorkspaceShortcut(isNearLeftEdge ? -1 : 1, false, /* Disable wrapping */ true)
.then((spaceChanged) => {
.changeWorkspaceShortcut(
isNearLeftEdge ? -1 : 1,
false,
/* Disable wrapping */ true
)
.then(spaceChanged => {
if (AppConstants.platform !== "macosx") {
// See the hack in #createDragImageForTabs for more details which
// explains why we need to do this on non-macOS platforms.
@@ -654,7 +716,9 @@
}
let tabs = this.originalDragImageArgs[0].children;
const { isDarkMode, isExplicitMode } =
gZenThemePicker.getGradientForWorkspace(spaceChanged, { getGradient: false });
gZenThemePicker.getGradientForWorkspace(spaceChanged, {
getGradient: false,
});
for (let tab of tabs) {
if (isExplicitMode) {
tab.style.colorScheme = isDarkMode ? "dark" : "light";
@@ -725,7 +789,10 @@
const edgeZoneThreshold = this._dndSplitThreshold / 100;
const overlapRatioY = (clientY - targetTop) / targetHeight;
if (overlapRatioY < edgeZoneThreshold || overlapRatioY > 1 - edgeZoneThreshold) {
if (
overlapRatioY < edgeZoneThreshold ||
overlapRatioY > 1 - edgeZoneThreshold
) {
this._clearDragOverSplit();
return;
}
@@ -815,7 +882,10 @@
return;
}
const { innerWidth: winWidth, innerHeight: winHeight } = window;
let allowedMargin = Services.prefs.getIntPref("zen.tabs.dnd-outside-window-margin", 5);
let allowedMargin = Services.prefs.getIntPref(
"zen.tabs.dnd-outside-window-margin",
5
);
const isOutOfWindow =
clientX <= allowedMargin ||
clientX >= winWidth - allowedMargin ||
@@ -881,7 +951,9 @@
gBrowser.selectedTab = draggedTab;
} else if (isTabGroupLabel(draggedTab)) {
draggedTab = draggedTab.group;
gZenFolders.changeFolderToSpace(draggedTab, activeWorkspace, { hasDndSwitch: true });
gZenFolders.changeFolderToSpace(draggedTab, activeWorkspace, {
hasDndSwitch: true,
});
}
}
}
@@ -922,7 +994,8 @@
}
if (
isTabGroupLabel(draggedTab) ||
(isTab(draggedTab) && draggedTab.group?.hasAttribute("split-view-group"))
(isTab(draggedTab) &&
draggedTab.group?.hasAttribute("split-view-group"))
) {
draggedTab = draggedTab.group;
}
@@ -939,7 +1012,8 @@
!dropElement ||
dropElement.hasAttribute("zen-essential") ||
draggedTab.hasAttribute("zen-essential") ||
draggedTab.getAttribute("zen-workspace-id") != gZenWorkspaces.activeWorkspace ||
draggedTab.getAttribute("zen-workspace-id") !=
gZenWorkspaces.activeWorkspace ||
!dropElement.visible ||
!draggedTab.visible ||
draggedTab.ownerGlobal !== window
@@ -949,7 +1023,7 @@
this.#isAnimatingTabMove = true;
const animateElement = (ele, translateY) => {
ele.style.transform = `translateY(${translateY}px)`;
let animateInternal = (resolve) => {
let animateInternal = resolve => {
const clearStyles = () => {
ele.style.transform = "";
ele.style.zIndex = "";
@@ -960,7 +1034,11 @@
return;
}
gZenUIManager
.elementAnimate(ele, { y: [translateY, 0] }, { duration: 100, easing: "ease-out" })
.elementAnimate(
ele,
{ y: [translateY, 0] },
{ duration: 100, easing: "ease-out" }
)
.then(() => {
clearStyles();
})
@@ -969,7 +1047,7 @@
// Wait for the next event loop tick to ensure the initial transform style is applied.
// We need to ensure the element has already been moved in the DOM before starting the animation.
animations.push(
new Promise((resolve) =>
new Promise(resolve =>
setTimeout(() => {
setTimeout(() => animateInternal(resolve), 0);
})
@@ -1025,7 +1103,9 @@
: -rect.height * tabsInBetween.length;
draggedTabTranslateY +=
extraTranslate *
(focusableDraggedTab.elementIndex > focusableDropElement.elementIndex ? 1 : -1);
(focusableDraggedTab.elementIndex > focusableDropElement.elementIndex
? 1
: -1);
draggedTab.style.zIndex = "9";
animateElement(draggedTab, draggedTabTranslateY);
} catch (e) {
@@ -1043,7 +1123,8 @@
let ownerGlobal = draggedTab?.ownerGlobal;
draggedTab.style.visibility = "";
let thisFromGlobal = ownerGlobal?.gBrowser.tabContainer.tabDragAndDrop;
let currentEssenialContainer = ownerGlobal.gZenWorkspaces.getCurrentEssentialsContainer();
let currentEssenialContainer =
ownerGlobal.gZenWorkspaces.getCurrentEssentialsContainer();
if (currentEssenialContainer?.essentialsPromo) {
currentEssenialContainer.essentialsPromo.remove();
}
@@ -1057,9 +1138,13 @@
thisFromGlobal._clearDragOverSplit();
this.#maybeClearVerticalPinnedGridDragOver();
thisFromGlobal.originalDragImageArgs = [];
window.removeEventListener("dragenter", thisFromGlobal.handle_windowDragEnter, {
capture: true,
});
window.removeEventListener(
"dragenter",
thisFromGlobal.handle_windowDragEnter,
{
capture: true,
}
);
this.#isOutOfWindow = false;
if (thisFromGlobal._browserDragImageWrapper) {
thisFromGlobal._browserDragImageWrapper.remove();
@@ -1119,8 +1204,13 @@
return true;
}
if (folder.isLiveFolder) {
const liveFolderItemId = draggedTab.getAttribute("zen-live-folder-item-id");
if (!liveFolderItemId || !liveFolderItemId.startsWith(`${folder.id}:`)) {
const liveFolderItemId = draggedTab.getAttribute(
"zen-live-folder-item-id"
);
if (
!liveFolderItemId ||
!liveFolderItemId.startsWith(`${folder.id}:`)
) {
return false;
}
}
@@ -1144,12 +1234,16 @@
let hoveringPeriphery = !!event.target.closest(
":is(#tabbrowser-arrowscrollbox-periphery, .pinned-tabs-container-separator)"
);
if (event.target.classList.contains("zen-workspace-empty-space") || hoveringPeriphery) {
if (
event.target.classList.contains("zen-workspace-empty-space") ||
hoveringPeriphery
) {
let lastTab = gBrowser.tabs.at(-1);
let pinnedTabsCount = gBrowser._numVisiblePinTabsWithoutCollapsed;
// Only if there are no normal tabs to drop after
showIndicatorUnderNewTabButton = lastTab.hasAttribute("zen-empty-tab");
showIndicatorUnderNewTabButton =
lastTab.hasAttribute("zen-empty-tab");
let useLastPinnd =
(hoveringPeriphery ||
(showIndicatorUnderNewTabButton &&
@@ -1187,12 +1281,18 @@
let possibleFolderElement = dropElement.parentElement;
let isZenFolder = possibleFolderElement?.isZenFolder;
let canHightlightGroup =
gZenFolders.highlightGroupOnDragOver(possibleFolderElement, movingTabs) || !isZenFolder;
gZenFolders.highlightGroupOnDragOver(
possibleFolderElement,
movingTabs
) || !isZenFolder;
let rect = window.windowUtils.getBoundsWithoutFlushing(dropElement);
const overlapPercent = (event.clientY - rect.top) / rect.height;
// We wan't to leave a small threshold (20% for example) so we can drag tabs below and above
// a folder label without dragging into the folder.
let threshold = Services.prefs.getIntPref("zen.tabs.folder-dragover-threshold-percent") / 100;
let threshold =
Services.prefs.getIntPref(
"zen.tabs.folder-dragover-threshold-percent"
) / 100;
let dropIntoFolder =
isZenFolder &&
(overlapPercent < threshold ||
@@ -1211,7 +1311,8 @@
if (
isTabGroupLabel(draggedTab) &&
draggedTab.group?.isZenFolder &&
(((isTab(dropElement) || dropElement.hasAttribute("split-view-group")) &&
(((isTab(dropElement) ||
dropElement.hasAttribute("split-view-group")) &&
(!dropElement.pinned || dropElement.hasAttribute("zen-essential"))) ||
showIndicatorUnderNewTabButton)
) {
@@ -1226,12 +1327,16 @@
dropElement.hasAttribute("split-view-group")
) {
if (showIndicatorUnderNewTabButton) {
rect = window.windowUtils.getBoundsWithoutFlushing(this.#dragShiftableItems.at(-1));
rect = window.windowUtils.getBoundsWithoutFlushing(
this.#dragShiftableItems.at(-1)
);
}
const indicator = gZenPinnedTabManager.dragIndicator;
let top = 0;
threshold =
Services.prefs.getIntPref("browser.tabs.dragDrop.moveOverThresholdPercent") / 100;
Services.prefs.getIntPref(
"browser.tabs.dragDrop.moveOverThresholdPercent"
) / 100;
if (overlapPercent > threshold || showIndicatorUnderNewTabButton) {
top = Math.round(rect.top + rect.height) + "px";
dropBefore = false;
@@ -1243,23 +1348,36 @@
shouldPlayHapticFeedback = true;
}
indicator.setAttribute("orientation", "horizontal");
indicator.style.setProperty("--indicator-left", rect.left + separation / 2 + "px");
indicator.style.setProperty("--indicator-width", rect.width - separation + "px");
indicator.style.setProperty(
"--indicator-left",
rect.left + separation / 2 + "px"
);
indicator.style.setProperty(
"--indicator-width",
rect.width - separation + "px"
);
indicator.style.top = top;
indicator.style.removeProperty("left");
this.#removeDragOverBackground();
if (!isTab(dropElement) && dropElement?.parentElement?.isZenFolder) {
dropElement = dropElement.parentElement;
}
} else if (dropElement.classList.contains("zen-drop-target") && canHightlightGroup) {
} else if (
dropElement.classList.contains("zen-drop-target") &&
canHightlightGroup
) {
shouldPlayHapticFeedback =
this.#applyDragOverBackground(dropElement) && !gZenPinnedTabManager._dragIndicator;
this.#applyDragOverBackground(dropElement) &&
!gZenPinnedTabManager._dragIndicator;
gZenPinnedTabManager.removeTabContainersDragoverClass();
dropElement = dropElement.parentElement?.labelElement || dropElement;
if (dropElement.classList.contains("zen-current-workspace-indicator")) {
dropElement =
elementToMove(this._tabbrowserTabs.ariaFocusableItems.at(gBrowser._numZenEssentials)) ||
dropElement;
elementToMove(
this._tabbrowserTabs.ariaFocusableItems.at(
gBrowser._numZenEssentials
)
) || dropElement;
dropBefore = true;
}
}
@@ -1305,12 +1423,17 @@
if (!this._fakeEssentialTab) {
const numEssentials = gBrowser._numZenEssentials;
let pinnedTabs = this._tabbrowserTabs.ariaFocusableItems.slice(0, numEssentials);
let pinnedTabs = this._tabbrowserTabs.ariaFocusableItems.slice(
0,
numEssentials
);
this._fakeEssentialTab = document.createXULElement("vbox");
this._fakeEssentialTab.elementIndex = numEssentials;
delete dragData.animDropElementIndex;
if (!draggedTab.hasAttribute("zen-essential")) {
event.target.closest(".zen-essentials-container").appendChild(this._fakeEssentialTab);
event.target
.closest(".zen-essentials-container")
.appendChild(this._fakeEssentialTab);
gZenWorkspaces.updateTabsContainers();
pinnedTabs.push(this._fakeEssentialTab);
this._fakeEssentialTab.getBoundingClientRect(); // Initialize layout
@@ -1318,8 +1441,9 @@
this.#makeDragImageEssential(event);
let tabsPerRow = 0;
let position = RTL_UI
? window.windowUtils.getBoundsWithoutFlushing(this._tabbrowserTabs.pinnedTabsContainer)
.right
? window.windowUtils.getBoundsWithoutFlushing(
this._tabbrowserTabs.pinnedTabsContainer
).right
: 0;
for (let pinnedTab of pinnedTabs) {
let tabPosition;
@@ -1341,13 +1465,19 @@
this.#maxTabsPerRow = tabsPerRow;
}
let usingFakeElement = !!this._fakeEssentialTab.parentElement;
let elementMoving = usingFakeElement ? this._fakeEssentialTab : draggedTab;
let elementMoving = usingFakeElement
? this._fakeEssentialTab
: draggedTab;
if (usingFakeElement) {
movingTabs = [this._fakeEssentialTab];
}
let dragDataScreenX = usingFakeElement ? this._fakeEssentialTab.screenX : dragData.screenX;
let dragDataScreenY = usingFakeElement ? this._fakeEssentialTab.screenY : dragData.screenY;
let dragDataScreenX = usingFakeElement
? this._fakeEssentialTab.screenX
: dragData.screenX;
let dragDataScreenY = usingFakeElement
? this._fakeEssentialTab.screenY
: dragData.screenY;
dragData.animLastScreenX ??= dragDataScreenX;
dragData.animLastScreenY ??= dragDataScreenY;
@@ -1355,11 +1485,17 @@
let screenX = event.screenX;
let screenY = event.screenY;
if (screenY == dragData.animLastScreenY && screenX == dragData.animLastScreenX) {
if (
screenY == dragData.animLastScreenY &&
screenX == dragData.animLastScreenX
) {
return;
}
let tabs = this._tabbrowserTabs.visibleTabs.slice(0, gBrowser._numZenEssentials);
let tabs = this._tabbrowserTabs.visibleTabs.slice(
0,
gBrowser._numZenEssentials
);
if (usingFakeElement) {
tabs.push(this._fakeEssentialTab);
}
@@ -1369,7 +1505,8 @@
dragData.animLastScreenY = screenY;
dragData.animLastScreenX = screenX;
let { width: tabWidth, height: tabHeight } = elementMoving.getBoundingClientRect();
let { width: tabWidth, height: tabHeight } =
elementMoving.getBoundingClientRect();
tabWidth += 4; // Add 6px to account for the gap
tabHeight += 4;
let shiftSizeX = tabWidth;
@@ -1383,11 +1520,16 @@
let lastTab = tabs.at(-1);
if (RTL_UI) {
firstTabInRow =
tabs.length >= this.#maxTabsPerRow ? tabs[this.#maxTabsPerRow - 1] : lastTab;
tabs.length >= this.#maxTabsPerRow
? tabs[this.#maxTabsPerRow - 1]
: lastTab;
lastTabInRow = tabs[0];
} else {
firstTabInRow = tabs[0];
lastTabInRow = tabs.length >= this.#maxTabsPerRow ? tabs[this.#maxTabsPerRow - 1] : lastTab;
lastTabInRow =
tabs.length >= this.#maxTabsPerRow
? tabs[this.#maxTabsPerRow - 1]
: lastTab;
}
let lastMovingTabScreenX = movingTabs.at(-1).screenX;
let lastMovingTabScreenY = movingTabs.at(-1).screenY;
@@ -1436,7 +1578,7 @@
// * We're doing a binary search in order to reduce the amount of
// tabs we need to check.
tabs = tabs.filter((t) => !movingTabs.includes(t) || t == elementMoving);
tabs = tabs.filter(t => !movingTabs.includes(t) || t == elementMoving);
let firstTabCenterX = firstMovingTabScreenX + translateX + tabWidth / 2;
let lastTabCenterX = lastMovingTabScreenX + translateX + tabWidth / 2;
let tabCenterX = directionX ? lastTabCenterX : firstTabCenterX;
@@ -1447,25 +1589,37 @@
let shiftNumber = this.#maxTabsPerRow - movingTabs.length;
let getTabShift = (tab, dropIndex) => {
if (tab.elementIndex < elementMoving.elementIndex && tab.elementIndex >= dropIndex) {
if (
tab.elementIndex < elementMoving.elementIndex &&
tab.elementIndex >= dropIndex
) {
// If tab is at the end of a row, shift back and down
let tabRow = Math.ceil((tab.elementIndex + 1) / this.#maxTabsPerRow);
let shiftedTabRow = Math.ceil(
(tab.elementIndex + 1 + movingTabs.length) / this.#maxTabsPerRow
);
if (tab.elementIndex && tabRow != shiftedTabRow) {
return [RTL_UI ? tabWidth * shiftNumber : -tabWidth * shiftNumber, shiftSizeY];
return [
RTL_UI ? tabWidth * shiftNumber : -tabWidth * shiftNumber,
shiftSizeY,
];
}
return [RTL_UI ? -shiftSizeX : shiftSizeX, 0];
}
if (tab.elementIndex > elementMoving.elementIndex && tab.elementIndex < dropIndex) {
if (
tab.elementIndex > elementMoving.elementIndex &&
tab.elementIndex < dropIndex
) {
// If tab is not index 0 and at the start of a row, shift across and up
let tabRow = Math.floor(tab.elementIndex / this.#maxTabsPerRow);
let shiftedTabRow = Math.floor(
(tab.elementIndex - movingTabs.length) / this.#maxTabsPerRow
);
if (tab.elementIndex && tabRow != shiftedTabRow) {
return [RTL_UI ? -tabWidth * shiftNumber : tabWidth * shiftNumber, -shiftSizeY];
return [
RTL_UI ? -tabWidth * shiftNumber : tabWidth * shiftNumber,
-shiftSizeY,
];
}
return [RTL_UI ? shiftSizeX : -shiftSizeX, 0];
}
@@ -1475,7 +1629,8 @@
let low = 0;
let high = tabs.length - 1;
let newIndex = -1;
let oldIndex = dragData.animDropElementIndex ?? movingTabs[0].elementIndex;
let oldIndex =
dragData.animDropElementIndex ?? movingTabs[0].elementIndex;
while (low <= high) {
let mid = Math.floor((low + high) / 2);
if (tabs[mid] == elementMoving && ++mid > high) {
@@ -1489,9 +1644,13 @@
low = mid + 1;
} else if (screenY > tabCenterY) {
high = mid - 1;
} else if (RTL_UI ? screenX + tabWidth < tabCenterX : screenX > tabCenterX) {
} else if (
RTL_UI ? screenX + tabWidth < tabCenterX : screenX > tabCenterX
) {
high = mid - 1;
} else if (RTL_UI ? screenX > tabCenterX : screenX + tabWidth < tabCenterX) {
} else if (
RTL_UI ? screenX > tabCenterX : screenX + tabWidth < tabCenterX
) {
low = mid + 1;
} else {
newIndex = tabs[mid].elementIndex;
@@ -1523,7 +1682,8 @@
for (let tab of tabs) {
if (tab != draggedTab) {
let [shiftX, shiftY] = getTabShift(tab, newIndex);
tab.style.transform = shiftX || shiftY ? `translate(${shiftX}px, ${shiftY}px)` : "";
tab.style.transform =
shiftX || shiftY ? `translate(${shiftX}px, ${shiftY}px)` : "";
}
}
}
@@ -1532,7 +1692,10 @@
if (this._fakeEssentialTab) {
this._fakeEssentialTab.remove();
delete this._fakeEssentialTab;
for (let tab of this._tabbrowserTabs.visibleTabs.slice(0, gBrowser._numZenEssentials)) {
for (let tab of this._tabbrowserTabs.visibleTabs.slice(
0,
gBrowser._numZenEssentials
)) {
tab.style.transform = "";
}
gZenWorkspaces.updateTabsContainers();
@@ -1551,8 +1714,13 @@
tab.setAttribute("zen-essential", "true");
tab.setAttribute("pinned", "true");
tab.setAttribute("selected", "true");
const draggedTabRect = window.windowUtils.getBoundsWithoutFlushing(this._fakeEssentialTab);
tab.style.minWidth = tab.style.maxWidth = wrapper.style.width = draggedTabRect.width + "px";
const draggedTabRect = window.windowUtils.getBoundsWithoutFlushing(
this._fakeEssentialTab
);
tab.style.minWidth =
tab.style.maxWidth =
wrapper.style.width =
draggedTabRect.width + "px";
tab.style.minHeight =
tab.style.maxHeight =
wrapper.style.height =

View File

@@ -4,7 +4,8 @@
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
ZenLiveFoldersManager: "resource:///modules/zen/ZenLiveFoldersManager.sys.mjs",
ZenLiveFoldersManager:
"resource:///modules/zen/ZenLiveFoldersManager.sys.mjs",
});
export class nsZenFolder extends MozTabbrowserTabGroup {
@@ -77,7 +78,7 @@ export class nsZenFolder extends MozTabbrowserTabGroup {
this.labelElement.parentElement.setAttribute("context", "zenFolderActions");
this.labelElement.onRenameFinished = (newLabel) => {
this.labelElement.onRenameFinished = newLabel => {
this.name = newLabel.trim() || "Folder";
const event = new CustomEvent("ZenFolderRenamed", {
bubbles: true,
@@ -127,7 +128,9 @@ export class nsZenFolder extends MozTabbrowserTabGroup {
get childActiveGroups() {
if (this.tagName === "zen-workspace-collapsible-pins") {
return Array.from(this.parentElement.querySelectorAll("zen-folder[has-active]"));
return Array.from(
this.parentElement.querySelectorAll("zen-folder[has-active]")
);
}
return Array.from(this.querySelectorAll("zen-folder[has-active]"));
}
@@ -193,7 +196,7 @@ export class nsZenFolder extends MozTabbrowserTabGroup {
get allItems() {
return [...this.groupContainer.children].filter(
(child) =>
child =>
!(
child.classList.contains("zen-tab-group-start") ||
child.classList.contains("pinned-tabs-container-separator")
@@ -227,7 +230,9 @@ export class nsZenFolder extends MozTabbrowserTabGroup {
} else {
const folders = new Map();
for (let tab of this._activeTabs) {
const group = tab?.group?.hasAttribute("split-view-group") ? tab?.group?.group : tab?.group;
const group = tab?.group?.hasAttribute("split-view-group")
? tab?.group?.group
: tab?.group;
if (!folders.has(group?.id)) {
folders.set(group?.id, group?.activeGroups?.at(-1));
}
@@ -247,7 +252,10 @@ export class nsZenFolder extends MozTabbrowserTabGroup {
}
get resetButton() {
return this.labelElement.parentElement?.querySelector(".tab-reset-button") ?? null;
return (
this.labelElement.parentElement?.querySelector(".tab-reset-button") ??
null
);
}
unloadAllTabs(event) {
@@ -280,8 +288,13 @@ export class nsZenFolder extends MozTabbrowserTabGroup {
addTabs(tabs) {
super.addTabs(tabs);
if (this.collapsed && !gZenFolders._sessionRestoring && this.isLiveFolder && tabs.length) {
tabs.forEach((tab) => {
if (
this.collapsed &&
!gZenFolders._sessionRestoring &&
this.isLiveFolder &&
tabs.length
) {
tabs.forEach(tab => {
tab.setAttribute("folder-active", "true");
});
gZenFolders.animateCollapse(this);

View File

@@ -36,7 +36,10 @@ function groupIsCollapsiblePins(group) {
}
class nsZenFolders extends nsZenDOMOperatedFeature {
#ZEN_MAX_SUBFOLDERS = Services.prefs.getIntPref("zen.folders.max-subfolders", 5);
#ZEN_MAX_SUBFOLDERS = Services.prefs.getIntPref(
"zen.folders.max-subfolders",
5
);
#popup = null;
#popupTimer = null;
@@ -69,10 +72,12 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
const contextMenuItemsToolbar = window.MozXULElement.parseXULToFragment(
`<menuitem id="zen-context-menu-new-folder-toolbar" data-l10n-id="zen-toolbar-context-new-folder"/>`
);
document.getElementById("toolbar-context-openANewTab").after(contextMenuItemsToolbar);
document
.getElementById("toolbar-context-openANewTab")
.after(contextMenuItemsToolbar);
const folderActionsMenu = document.getElementById("zenFolderActions");
folderActionsMenu.addEventListener("popupshowing", (event) => {
folderActionsMenu.addEventListener("popupshowing", event => {
const target = event.explicitOriginalTarget;
let folder;
if (gBrowser.isTabGroupLabel(target)) {
@@ -93,7 +98,9 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
this.#lastFolderContextMenu = folder;
gZenLiveFoldersUI.buildContextMenu(folder);
const newSubfolderItem = document.getElementById("context_zenFolderNewSubfolder");
const newSubfolderItem = document.getElementById(
"context_zenFolderNewSubfolder"
);
newSubfolderItem.setAttribute(
"disabled",
folder.level >= this.#ZEN_MAX_SUBFOLDERS - 1 ? "true" : "false"
@@ -105,7 +112,7 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
changeFolderSpace.innerHTML = "";
for (const workspace of [...gZenWorkspaces.getWorkspaces()].reverse()) {
const item = gZenWorkspaces.generateMenuItemForWorkspace(workspace);
item.addEventListener("command", (event) => {
item.addEventListener("command", event => {
if (!this.#lastFolderContextMenu) {
return;
}
@@ -120,7 +127,7 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
folderActionsMenu.addEventListener(
"popuphidden",
(event) => {
event => {
if (event.target === folderActionsMenu) {
this.#lastFolderContextMenu = null;
}
@@ -128,7 +135,7 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
{ once: true }
);
folderActionsMenu.addEventListener("command", (event) => {
folderActionsMenu.addEventListener("command", event => {
if (!this.#lastFolderContextMenu) {
return;
}
@@ -198,12 +205,17 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
window.addEventListener("TabSelect", this);
window.addEventListener("TabOpen", this);
const onNewFolder = this.#onNewFolder.bind(this);
document.getElementById("zen-context-menu-new-folder").addEventListener("command", onNewFolder);
document
.getElementById("zen-context-menu-new-folder")
.addEventListener("command", onNewFolder);
document
.getElementById("zen-context-menu-new-folder-toolbar")
.addEventListener("command", onNewFolder);
SessionStore.promiseInitialized.then(() => {
gBrowser.tabContainer.addEventListener("dragstart", this.cancelPopupTimer.bind(this));
gBrowser.tabContainer.addEventListener(
"dragstart",
this.cancelPopupTimer.bind(this)
);
});
}
@@ -234,7 +246,10 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
}
}
if (group.hasAttribute("split-view-group") && group.hasAttribute("zen-pinned-changed")) {
if (
group.hasAttribute("split-view-group") &&
group.hasAttribute("zen-pinned-changed")
) {
// zen-pinned-changed remove it and set it to had-zen-pinned-changed to keep
// track of the original pinned state
group.removeAttribute("zen-pinned-changed");
@@ -258,9 +273,9 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
const isActiveFolder = parentFolder?.activeGroups?.length > 0;
const isSplitView = folder.hasAttribute("split-view-group");
if (isActiveFolder && isSplitView) {
parentFolder.activeTabs = [...new Set([...parentFolder.activeTabs, ...folder.tabs])].sort(
(a, b) => a._tPos > b._tPos
);
parentFolder.activeTabs = [
...new Set([...parentFolder.activeTabs, ...folder.tabs]),
].sort((a, b) => a._tPos > b._tPos);
}
parentFolder.collapsed = isActiveFolder;
}
@@ -314,7 +329,10 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
async on_TabUngrouped(event) {
const tab = event.detail;
const group = event.target;
if (group.hasAttribute("split-view-group") && tab.hasAttribute("had-zen-pinned-changed")) {
if (
group.hasAttribute("split-view-group") &&
tab.hasAttribute("had-zen-pinned-changed")
) {
tab.setAttribute("zen-pinned-changed", true);
tab.removeAttribute("had-zen-pinned-changed");
}
@@ -381,7 +399,8 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
}
#onNewFolder(event) {
const isFromToolbar = event.target.id === "zen-context-menu-new-folder-toolbar";
const isFromToolbar =
event.target.id === "zen-context-menu-new-folder-toolbar";
const contextMenu = event.target.parentElement;
let tabs = TabContextMenu.contextTab?.multiselected
? gBrowser.selectedTabs
@@ -401,7 +420,7 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
}
// Prevent create folder inside Live Folder
const thereIsOneLiveFolderTab = tabs?.some((tab) =>
const thereIsOneLiveFolderTab = tabs?.some(tab =>
tab.hasAttribute("zen-live-folder-item-id")
);
if (thereIsOneLiveFolderTab) {
@@ -423,7 +442,7 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
async #convertFolderToSpace(folder) {
const currentWorkspace = gZenWorkspaces.getActiveWorkspaceFromCache();
let selectedTab = folder.tabs.find((tab) => tab.selected);
let selectedTab = folder.tabs.find(tab => tab.selected);
const icon = folder.icon?.querySelector("svg .icon image");
const newSpace = await gZenWorkspaces.createAndSaveWorkspace(
@@ -432,20 +451,23 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
/* dontChange= */ false,
currentWorkspace.containerTabId,
{
beforeChangeCallback: async (newWorkspace) => {
await new Promise((resolve) => {
beforeChangeCallback: async newWorkspace => {
await new Promise(resolve => {
requestAnimationFrame(async () => {
const workspacePinnedContainer = gZenWorkspaces.workspaceElement(
newWorkspace.uuid
).pinnedTabsContainer;
const tabs = folder.allItems.filter((tab) => !tab.hasAttribute("zen-empty-tab"));
const tabs = folder.allItems.filter(
tab => !tab.hasAttribute("zen-empty-tab")
);
workspacePinnedContainer.append(...tabs);
await folder.delete();
gBrowser.tabContainer._invalidateCachedTabs();
if (selectedTab) {
selectedTab.setAttribute("zen-workspace-id", newWorkspace.uuid);
selectedTab.removeAttribute("folder-active");
gZenWorkspaces.lastSelectedWorkspaceTabs[newWorkspace.uuid] = selectedTab;
gZenWorkspaces.lastSelectedWorkspaceTabs[newWorkspace.uuid] =
selectedTab;
}
resolve();
});
@@ -461,7 +483,9 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
tab.style.height = "";
}
gBrowser.TabStateFlusher.flush(tab.linkedBrowser);
if (gZenWorkspaces.lastSelectedWorkspaceTabs[currentWorkspace.uuid] === tab) {
if (
gZenWorkspaces.lastSelectedWorkspaceTabs[currentWorkspace.uuid] === tab
) {
// This tab is no longer the last selected tab in the previous workspace because it's being moved to
// the current workspace
delete gZenWorkspaces.lastSelectedWorkspaceTabs[currentWorkspace.uuid];
@@ -496,7 +520,9 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
}
}
folder.dispatchEvent(new CustomEvent("ZenFolderChangedWorkspace", { bubbles: true }));
folder.dispatchEvent(
new CustomEvent("ZenFolderChangedWorkspace", { bubbles: true })
);
if (!hasDndSwitch) {
gZenWorkspaces.changeWorkspaceWithID(workspaceId).then(() => {
@@ -513,8 +539,8 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
createFolder(tabs = [], options = {}) {
const filteredTabs = tabs
.filter((tab) => !tab.hasAttribute("zen-essential"))
.map((tab) => {
.filter(tab => !tab.hasAttribute("zen-essential"))
.map(tab => {
gBrowser.pinTab(tab);
if (tab?.group?.hasAttribute("split-view-group")) {
tab = tab.group;
@@ -526,9 +552,12 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
options.workspaceId
)?.pinnedTabsContainer;
const pinnedContainer =
options.workspaceId && workspacePinned ? workspacePinned : gZenWorkspaces.pinnedTabsContainer;
options.workspaceId && workspacePinned
? workspacePinned
: gZenWorkspaces.pinnedTabsContainer;
const insertBefore =
options.insertBefore || pinnedContainer.querySelector(".pinned-tabs-container-separator");
options.insertBefore ||
pinnedContainer.querySelector(".pinned-tabs-container-separator");
const emptyTab = gBrowser.addTab("about:blank", {
skipAnimation: true,
pinned: true,
@@ -557,7 +586,7 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
// deliberate user action indicating the tab has importance for the user.
// Without this, it is not possible to save and close a tab group with
// a short lifetime.
folder.tabs.forEach((tab) => {
folder.tabs.forEach(tab => {
gBrowser.TabStateFlusher.flush(tab.linkedBrowser);
});
@@ -572,7 +601,9 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
}
_createFolderNode(options = {}) {
const folder = document.createXULElement("zen-folder", { is: "zen-folder" });
const folder = document.createXULElement("zen-folder", {
is: "zen-folder",
});
let id = options.id;
if (!id) {
// Note: If this changes, make sure to also update the
@@ -587,14 +618,17 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
folder.color = "zen-workspace-color";
folder.isLiveFolder = options.isLiveFolder;
folder.setAttribute("zen-workspace-id", options.workspaceId || gZenWorkspaces.activeWorkspace);
folder.setAttribute(
"zen-workspace-id",
options.workspaceId || gZenWorkspaces.activeWorkspace
);
// note: We set if the folder is collapsed some time after creation.
// we do this to ensure marginBottom is set correctly in the case
// that we want it to initially be collapsed.
setTimeout(
// eslint-disable-next-line no-shadow
(folder) => {
folder => {
folder.collapsed = !!options.collapsed;
},
0,
@@ -618,7 +652,10 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
gBrowser.pinTab(otherTab);
}
this._piningFolder = false;
gBrowser.pinnedTabsContainer.insertBefore(group, gBrowser.pinnedTabsContainer.lastChild);
gBrowser.pinnedTabsContainer.insertBefore(
group,
gBrowser.pinnedTabsContainer.lastChild
);
gBrowser.tabContainer._invalidateCachedTabs();
return true;
}
@@ -649,13 +686,18 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
openTabsPopup(event) {
event.stopPropagation();
if (document.documentElement.getAttribute("zen-renaming-tab") || gURLBar.focused) {
if (
document.documentElement.getAttribute("zen-renaming-tab") ||
gURLBar.focused
) {
return;
}
const activeGroup = event.target.parentElement;
if (
activeGroup.tabs.filter((tab) => this.#shouldAppearOnTabSearch(tab, activeGroup)).length === 0
activeGroup.tabs.filter(tab =>
this.#shouldAppearOnTabSearch(tab, activeGroup)
).length === 0
) {
// If the group has no tabs, we don't show the popup
return;
@@ -679,31 +721,38 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
foundTabs++;
}
}
document.getElementById("zen-folder-tabs-search-no-results").hidden = foundTabs > 0;
document.getElementById("zen-folder-tabs-search-no-results").hidden =
foundTabs > 0;
};
search.addEventListener("input", onSearchInput);
const onKeyDown = (event) => {
const onKeyDown = event => {
// Arrow down and up to navigate through the list
if (event.key === "ArrowDown" || event.key === "ArrowUp") {
event.preventDefault();
const items = Array.from(tabsList.children).filter((item) => !item.hidden);
const items = Array.from(tabsList.children).filter(
item => !item.hidden
);
if (items.length === 0) {
return;
}
let index = items.indexOf(tabsList.querySelector(".folders-tabs-list-item[selected]"));
let index = items.indexOf(
tabsList.querySelector(".folders-tabs-list-item[selected]")
);
if (event.key === "ArrowDown") {
index = (index + 1) % items.length;
} else if (event.key === "ArrowUp") {
index = (index - 1 + items.length) % items.length;
}
items.forEach((item) => item.removeAttribute("selected"));
items.forEach(item => item.removeAttribute("selected"));
const targetItem = items[index];
targetItem.setAttribute("selected", "true");
targetItem.scrollIntoView({ block: "start", behavior: "smooth" });
} else if (event.key === "Enter") {
// Enter to select the currently highlighted item
const highlightedItem = tabsList.querySelector(".folders-tabs-list-item[selected]");
const highlightedItem = tabsList.querySelector(
".folders-tabs-list-item[selected]"
);
if (highlightedItem) {
highlightedItem.click();
}
@@ -714,7 +763,7 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
const target = event.target;
target.setAttribute("open", true);
const handlePopupHidden = (event) => {
const handlePopupHidden = event => {
if (event.target !== this.#popup) {
return;
}
@@ -733,14 +782,19 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
{ once: true }
);
this.#popup.addEventListener("popuphidden", handlePopupHidden, { once: true });
this.#popup.addEventListener("popuphidden", handlePopupHidden, {
once: true,
});
this.#popup.openPopup(target, this.#searchPopupOptions);
}
get #searchPopupOptions() {
const isRightSide = gZenVerticalTabsManager._prefsRightSide;
const position = isRightSide ? "topleft topright" : "topright topleft";
let size = Math.min(this.#popup.querySelector("#zen-folder-tabs-list").children.length, 6);
let size = Math.min(
this.#popup.querySelector("#zen-folder-tabs-list").children.length,
6
);
size *= 48;
return {
position,
@@ -756,7 +810,9 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
// account for the visibility of the tab itself, it's just a literal
// representation of the `hidden` attribute.
const tabIsInActiveGroup = group.activeTabs.includes(tab);
return !tabIsInActiveGroup && !(tab.hidden || tab.hasAttribute("zen-empty-tab"));
return (
!tabIsInActiveGroup && !(tab.hidden || tab.hasAttribute("zen-empty-tab"))
);
}
#populateTabsList(group) {
@@ -786,7 +842,8 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
// We don't need to do anything if the URL is invalid. e.g. about:blank
}
let tabLabel = tab.label || "";
let iconURL = gBrowser.getIcon(tab) || PlacesUtils.favicons.defaultFavicon.spec;
let iconURL =
gBrowser.getIcon(tab) || PlacesUtils.favicons.defaultFavicon.spec;
icon.src = iconURL;
@@ -809,7 +866,10 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
item.setAttribute("selected", "true");
}
item.setAttribute("data-label", `${tabLabel.toLowerCase()} ${tabURL.toLowerCase()}`);
item.setAttribute(
"data-label",
`${tabLabel.toLowerCase()} ${tabURL.toLowerCase()}`
);
item.addEventListener("click", () => {
gBrowser.selectedTab = tab;
@@ -846,11 +906,17 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
}
// eslint-disable-next-line complexity
setFolderIndentation(tabs, groupElem = undefined, forCollapse = true, animate = true) {
setFolderIndentation(
tabs,
groupElem = undefined,
forCollapse = true,
animate = true
) {
if (!gZenPinnedTabManager.expandedSidebarMode) {
return;
}
const isSpaceCollapsed = gZenWorkspaces.activeWorkspaceElement?.hasCollapsedPinnedTabs;
const isSpaceCollapsed =
gZenWorkspaces.activeWorkspaceElement?.hasCollapsedPinnedTabs;
let tab = tabs[0];
let isTab = false;
@@ -863,7 +929,9 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
}
if (
gBrowser.isTab(groupElem) &&
(!(groupElem.hasAttribute("zen-empty-tab") && groupElem.group === tab.group) ||
(!(
groupElem.hasAttribute("zen-empty-tab") && groupElem.group === tab.group
) ||
groupElem?.hasAttribute("zen-empty-tab"))
) {
groupElem = groupElem.group;
@@ -893,7 +961,10 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
}
}
for (const tabItem of tabs) {
if (gBrowser.isTabGroupLabel(tabItem) || tabItem.group?.hasAttribute("split-view-group")) {
if (
gBrowser.isTabGroupLabel(tabItem) ||
tabItem.group?.hasAttribute("split-view-group")
) {
tabItem.group.style.setProperty("--zen-folder-indent", `${spacing}px`);
continue;
}
@@ -915,9 +986,11 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
onlySvgIcons: true,
allowNone: Boolean(group.iconURL),
closeOnSelect: false,
onSelect: (icon) => {
onSelect: icon => {
this.setFolderUserIcon(group, icon);
group.dispatchEvent(new CustomEvent("TabGroupUpdate", { bubbles: true }));
group.dispatchEvent(
new CustomEvent("TabGroupUpdate", { bubbles: true })
);
},
});
}
@@ -948,7 +1021,7 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
const labelContainer = group.querySelector(".tab-group-label-container");
// Setup mouseenter/mouseleave events for the folder
labelContainer.addEventListener("mouseenter", (event) => {
labelContainer.addEventListener("mouseenter", event => {
if (
!group.collapsed ||
!Services.prefs.getBoolPref("zen.folders.search.enabled") ||
@@ -977,7 +1050,9 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
}
storeDataForSessionStore() {
const folders = Array.from(gBrowser.tabContainer.querySelectorAll("zen-folder"));
const folders = Array.from(
gBrowser.tabContainer.querySelectorAll("zen-folder")
);
const splitGroups = Array.from(
gBrowser.tabContainer.querySelectorAll("tab-group[split-view-group]")
);
@@ -1004,8 +1079,8 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
continue;
}
const emptyFolderTabs = folder.tabs
.filter((tab) => tab.hasAttribute("zen-empty-tab"))
.map((tab) => tab.getAttribute("id"));
.filter(tab => tab.hasAttribute("zen-empty-tab"))
.map(tab => tab.getAttribute("id"));
let prevSiblingInfo = null;
const prevSibling = folder.previousElementSibling;
@@ -1014,7 +1089,10 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
if (prevSibling) {
if (gBrowser.isTabGroup(prevSibling)) {
prevSiblingInfo = { type: "group", id: prevSibling.id };
} else if (gBrowser.isTab(prevSibling) && prevSibling.hasAttribute("id")) {
} else if (
gBrowser.isTab(prevSibling) &&
prevSibling.hasAttribute("id")
) {
prevSiblingInfo = { type: "tab", id: prevSibling.getAttribute("id") };
} else {
prevSiblingInfo = { type: "start", id: null };
@@ -1061,8 +1139,10 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
tabFolderWorkingData.set(folderData.id, workingData);
const oldGroup = document.getElementById(folderData.id);
folderData.emptyTabIds.forEach((id) => {
oldGroup?.querySelector(`tab[id="${id}"]`)?.setAttribute("zen-empty-tab", true);
folderData.emptyTabIds.forEach(id => {
oldGroup
?.querySelector(`tab[id="${id}"]`)
?.setAttribute("zen-empty-tab", true);
});
if (gBrowser.isTabGroup(oldGroup)) {
if (!folderData.splitViewGroup) {
@@ -1097,7 +1177,10 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
}
}
for (const { node, containingTabsFragment } of tabFolderWorkingData.values()) {
for (const {
node,
containingTabsFragment,
} of tabFolderWorkingData.values()) {
if (node) {
node.appendChild(containingTabsFragment);
}
@@ -1111,7 +1194,9 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
switch (stateData?.prevSiblingInfo?.type) {
case "tab":
case "group": {
const item = document.getElementById(stateData.prevSiblingInfo.id);
const item = document.getElementById(
stateData.prevSiblingInfo.id
);
if (item) {
item.after(node);
break;
@@ -1124,7 +1209,8 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
}
default: {
// Should insert after zen-empty-tab
const start = parentWorkingData.node.groupStartElement.nextElementSibling;
const start =
parentWorkingData.node.groupStartElement.nextElementSibling;
start.after(node);
}
}
@@ -1161,10 +1247,11 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
}
if (
folder?.isZenFolder &&
(!folder.hasAttribute("split-view-group") || !folder.hasAttribute("selected")) &&
(!folder.hasAttribute("split-view-group") ||
!folder.hasAttribute("selected")) &&
!(
folder.level >= this.#ZEN_MAX_SUBFOLDERS &&
movingTabs?.some((t) => gBrowser.isTabGroupLabel(t))
movingTabs?.some(t => gBrowser.isTabGroupLabel(t))
)
) {
if (folder.collapsed) {
@@ -1189,8 +1276,8 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
#normalizeGroupItems(items) {
return items
.filter((item) => !item.hasAttribute("zen-empty-tab"))
.map((item) => {
.filter(item => !item.hasAttribute("zen-empty-tab"))
.map(item => {
if (gBrowser.isTabGroup(item)) {
item = item.firstChild;
} else if (gBrowser.isTabGroupLabel(item)) {
@@ -1217,11 +1304,15 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
}
#collectGroupItems(group, opts = {}) {
const { selectedTabs = [], splitViewIds = new Set(), activeFoldersIds = new Set() } = opts;
const {
selectedTabs = [],
splitViewIds = new Set(),
activeFoldersIds = new Set(),
} = opts;
const folders = new Map();
return group.childGroupsAndTabs
.filter((item) => !item.hasAttribute("zen-empty-tab"))
.map((item) => {
.filter(item => !item.hasAttribute("zen-empty-tab"))
.map(item => {
const isSplitView = item.group?.hasAttribute?.("split-view-group");
const itemGroup = isSplitView ? item.group.group : item.group;
if (!folders.has(itemGroup?.id)) {
@@ -1255,7 +1346,7 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
#createAnimation(items, targetState, opts, callback = () => {}) {
items = Array.isArray(items) ? items : [items];
return items.map((item) =>
return items.map(item =>
gZenUIManager.motion.animate(item, targetState, opts).then(callback)
);
}
@@ -1265,7 +1356,8 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
if (selectedTabs.length) {
return heightShift;
}
heightShift += window.windowUtils.getBoundsWithoutFlushing(tabsContainer).height;
heightShift +=
window.windowUtils.getBoundsWithoutFlushing(tabsContainer).height;
if (tabsContainer.separatorElement) {
heightShift -= window.windowUtils.getBoundsWithoutFlushing(
tabsContainer.separatorElement
@@ -1296,7 +1388,10 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
splitViewIds,
activeFoldersIds,
});
const collapsedHeight = this.#calculateHeightShift(tabsContainer, selectedTabs);
const collapsedHeight = this.#calculateHeightShift(
tabsContainer,
selectedTabs
);
if (selectedTabs.length) {
for (let i = 0; i < groupItems.length; i++) {
@@ -1316,7 +1411,10 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
if (activeFolderId && activeFoldersIds.has(activeFolderId)) {
// If item is tab-group-label-container we should hide it.
// Other items between tab-group-labe-container and folder-active tab should be visible cuz they are hidden by margin-top
if (item.parentElement.id !== activeFolderId && !item.hasAttribute("folder-active")) {
if (
item.parentElement.id !== activeFolderId &&
!item.hasAttribute("folder-active")
) {
continue;
}
}
@@ -1329,7 +1427,7 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
group.setAttribute("has-active", "true");
group.activeTabs = selectedTabs;
selectedTabs.forEach((tab) => {
selectedTabs.forEach(tab => {
this.setFolderIndentation([tab], group, /* for collapse = */ true);
});
}
@@ -1346,7 +1444,10 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
...this.#createAnimation(
groupStart,
{
marginTop: -(collapsedHeight + 4 * (selectedTabs.length === 0 ? 1 : 0)),
marginTop: -(
collapsedHeight +
4 * (selectedTabs.length === 0 ? 1 : 0)
),
},
{ duration, ease: "easeInOut" }
)
@@ -1438,7 +1539,11 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
}
let activeGroup = folders.get(tabGroup?.id);
if (activeGroup) {
this.setFolderIndentation([tab], activeGroup, /* for collapse = */ true);
this.setFolderIndentation(
[tab],
activeGroup,
/* for collapse = */ true
);
} else {
// Since the folder is now expanded, we should remove active attribute
// to the tab that was previously visible
@@ -1536,7 +1641,10 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
async animateUnload(group, tabToUnload, ungroup = false) {
const isSplitView = tabToUnload.group?.hasAttribute("split-view-group");
if ((!group?.isZenFolder || !isSplitView) && !tabToUnload.hasAttribute("folder-active")) {
if (
(!group?.isZenFolder || !isSplitView) &&
!tabToUnload.hasAttribute("folder-active")
) {
return;
}
const animations = [];
@@ -1544,7 +1652,7 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
const activeGroups = group.activeGroups;
for (const folder of activeGroups) {
folder.activeTabs = folder.activeTabs.filter((tab) => tab !== tabToUnload);
folder.activeTabs = folder.activeTabs.filter(tab => tab !== tabToUnload);
if (folder.activeTabs.length === 0) {
lastTab = true;
@@ -1561,7 +1669,10 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
// the correct container size in the DOM
tabsContainer.offsetHeight;
tabsContainer.setAttribute("hidden", true);
const collapsedHeight = this.#calculateHeightShift(tabsContainer, []);
const collapsedHeight = this.#calculateHeightShift(
tabsContainer,
[]
);
groupStart.style.marginTop = `${-(collapsedHeight + 4)}px`;
};
@@ -1612,7 +1723,9 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
// Await the tab unload animation first
await Promise.all(tabUnloadAnimations);
await Promise.all(animations.map((item) => (typeof item === "function" ? item() : item)));
await Promise.all(
animations.map(item => (typeof item === "function" ? item() : item))
);
this.#animationCount -= 1;
gBrowser.tabContainer._invalidateCachedTabs();
}
@@ -1639,7 +1752,9 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
? tab.group.group
: tab?.group;
while (currentGroup) {
const activeTabs = selectedTabs.filter((t) => currentGroup.tabs.includes(t));
const activeTabs = selectedTabs.filter(t =>
currentGroup.tabs.includes(t)
);
if (activeTabs.length) {
if (currentGroup.collapsed) {
if (currentGroup.hasAttribute("has-active")) {
@@ -1717,7 +1832,7 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
// FIXME: This is a hack to fix the animations not working properly
this.styleCleanup(itemsToShow);
itemsToHide.forEach((item) => {
itemsToHide.forEach(item => {
item.style.opacity = 0;
item.style.height = 0;
});
@@ -1764,7 +1879,9 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
}
const groupStart = group.groupStartElement;
const tabsContainer = group.groupContainer;
const heightContainer = expand ? 0 : this.#calculateHeightShift(tabsContainer, []);
const heightContainer = expand
? 0
: this.#calculateHeightShift(tabsContainer, []);
tabsContainer.style.overflow = "clip";
this.#createAnimation(
@@ -1777,7 +1894,7 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
}
styleCleanup(items) {
items.forEach((item) => {
items.forEach(item => {
item.style.removeProperty("opacity");
item.style.removeProperty("height");
});

View File

@@ -37,7 +37,8 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
ARC_HEIGHT_RATIO: 0.2, // Arc height = distance * ratio (capped at MAX_ARC_HEIGHT)
});
#GLANCE_ANIMATION_DURATION = Services.prefs.getIntPref("zen.glance.animation-duration") / 1000;
#GLANCE_ANIMATION_DURATION =
Services.prefs.getIntPref("zen.glance.animation-duration") / 1000;
init() {
this.#setupEventListeners();
@@ -74,9 +75,13 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
menuitem.setAttribute("hidden", "true");
menuitem.setAttribute("data-l10n-id", "zen-open-link-in-glance");
menuitem.addEventListener("command", () => this.openGlance({ url: gContextMenu.linkURL }));
menuitem.addEventListener("command", () =>
this.openGlance({ url: gContextMenu.linkURL })
);
document.getElementById("context-sep-open").insertAdjacentElement("beforebegin", menuitem);
document
.getElementById("context-sep-open")
.insertAdjacentElement("beforebegin", menuitem);
}
/**
@@ -175,7 +180,8 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
currentTab._selected = true;
const newTab =
existingTab ?? gBrowser.addTrustedTab(Services.io.newURI(url).spec, newTabOptions);
existingTab ??
gBrowser.addTrustedTab(Services.io.newURI(url).spec, newTabOptions);
this.#configureNewTab(newTab, currentTab, newUUID);
this.#registerGlance(newTab, currentTab, newUUID);
@@ -355,7 +361,11 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
this.#setAnimationState(true);
const currentTab = ownerTab ?? gBrowser.selectedTab;
const browserElement = this.#createBrowserElement(data.url, currentTab, existingTab);
const browserElement = this.#createBrowserElement(
data.url,
currentTab,
existingTab
);
this.fillOverlay(browserElement);
this.overlay.classList.add("zen-glance-overlay");
@@ -386,7 +396,7 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
// until a better solution is found). If we do it inside the requestAnimationFrame,
// we see flashing and if we do it directly, the animation does not play at all.
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve) => {
return new Promise(async resolve => {
// Recalculate location. When opening from pinned tabs,
// view splitter doesn't catch if the tab is a glance tab or not.
gZenViewSplitter.onLocationChange(browserElement);
@@ -496,7 +506,8 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
const imageDataElement = this.#createGlancePreviewElement(data.elementData);
this.browserWrapper.prepend(imageDataElement);
this.#glances.get(this.#currentGlanceID).elementImageData = data.elementData;
this.#glances.get(this.#currentGlanceID).elementImageData =
data.elementData;
gZenUIManager.motion.animate(
imageDataElement,
@@ -518,7 +529,9 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
* @param {Browser} browserElement - The browser element
*/
#configureBrowserElement(browserElement) {
const rect = window.windowUtils.getBoundsWithoutFlushing(this.browserWrapper.parentElement);
const rect = window.windowUtils.getBoundsWithoutFlushing(
this.browserWrapper.parentElement
);
const minWidth = rect.width * 0.85;
const minHeight = rect.height * 0.85;
@@ -574,7 +587,9 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
this.#animateParentBackground();
gZenUIManager.motion
.animate(this.browserWrapper, arcSequence, {
duration: gZenUIManager.testingEnabled ? 0 : this.#GLANCE_ANIMATION_DURATION,
duration: gZenUIManager.testingEnabled
? 0
: this.#GLANCE_ANIMATION_DURATION,
ease: "easeInOut",
})
.then(() => {
@@ -595,7 +610,9 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
// Calculate start and end positions based on direction
let startPosition, endPosition;
const tabPanelsRect = window.windowUtils.getBoundsWithoutFlushing(gBrowser.tabpanels);
const tabPanelsRect = window.windowUtils.getBoundsWithoutFlushing(
gBrowser.tabpanels
);
const widthPercent = 0.85;
if (direction === "opening") {
@@ -658,12 +675,17 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
// First, create the main animation steps
for (let i = 0; i <= steps; i++) {
const progress = i / steps;
const eased = direction === "opening" ? easeOutBack(progress) : easeOutCubic(progress);
const eased =
direction === "opening"
? easeOutBack(progress)
: easeOutCubic(progress);
// Calculate size interpolation
const currentWidth = startPosition.width + (endPosition.width - startPosition.width) * eased;
const currentWidth =
startPosition.width + (endPosition.width - startPosition.width) * eased;
const currentHeight =
startPosition.height + (endPosition.height - startPosition.height) * eased;
startPosition.height +
(endPosition.height - startPosition.height) * eased;
// Calculate position on arc
const distanceX = endPosition.x - startPosition.x;
@@ -671,7 +693,9 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
const x = startPosition.x + distanceX * eased;
const y =
startPosition.y + distanceY * eased + arcDirection * arcHeight * (1 - (2 * eased - 1) ** 2);
startPosition.y +
distanceY * eased +
arcDirection * arcHeight * (1 - (2 * eased - 1) ** 2);
sequence.top.push(`${y}px`);
sequence.left.push(`${x}px`);
@@ -707,13 +731,16 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
// Calculate available space for the arc
const availableTopSpace = Math.min(startPosition.y, endPosition.y);
const viewportHeight = window.innerHeight;
const availableBottomSpace = viewportHeight - Math.max(startPosition.y, endPosition.y);
const availableBottomSpace =
viewportHeight - Math.max(startPosition.y, endPosition.y);
// Determine if we should arc downward or upward based on available space
const shouldArcDownward = availableBottomSpace > availableTopSpace;
// Use the space in the direction we're arcing
const availableSpace = shouldArcDownward ? availableBottomSpace : availableTopSpace;
const availableSpace = shouldArcDownward
? availableBottomSpace
: availableTopSpace;
// Limit arc height to a percentage of the available space
const arcHeight = Math.min(
@@ -792,12 +819,17 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
return;
}
const browserSidebarContainer = this.#currentParentTab?.linkedBrowser?.closest(
".browserSidebarContainer"
const browserSidebarContainer =
this.#currentParentTab?.linkedBrowser?.closest(
".browserSidebarContainer"
);
const sidebarButtons = this.browserWrapper.querySelector(
".zen-glance-sidebar-container"
);
const sidebarButtons = this.browserWrapper.querySelector(".zen-glance-sidebar-container");
if (this.#handleConfirmationTimeout(onTabClose, hasFocused, sidebarButtons)) {
if (
this.#handleConfirmationTimeout(onTabClose, hasFocused, sidebarButtons)
) {
return;
}
@@ -851,8 +883,15 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
* @returns {boolean} True if should return early
*/
#handleConfirmationTimeout(onTabClose, hasFocused, sidebarButtons) {
if (onTabClose && hasFocused && !this.#confirmationTimeout && sidebarButtons) {
const cancelButton = sidebarButtons.querySelector(".zen-glance-sidebar-close");
if (
onTabClose &&
hasFocused &&
!this.#confirmationTimeout &&
sidebarButtons
) {
const cancelButton = sidebarButtons.querySelector(
".zen-glance-sidebar-close"
);
cancelButton.setAttribute("waitconfirmation", true);
this.#confirmationTimeout = setTimeout(() => {
cancelButton.removeAttribute("waitconfirmation");
@@ -871,7 +910,12 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
* @param {Element} sidebarButtons - The sidebar buttons
* @param {string} setNewID - New glance ID to set
*/
#animateGlanceClosing(onTabClose, browserSidebarContainer, sidebarButtons, setNewID) {
#animateGlanceClosing(
onTabClose,
browserSidebarContainer,
sidebarButtons,
setNewID
) {
if (this.closingGlance) {
return;
}
@@ -976,14 +1020,19 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
* @returns {Promise} Promise that resolves when complete
*/
#executeClosingAnimation(setNewID, onTabClose) {
return new Promise((resolve) => {
const originalPosition = this.#glances.get(this.#currentGlanceID).originalPosition;
const elementImageData = this.#glances.get(this.#currentGlanceID).elementImageData;
return new Promise(resolve => {
const originalPosition = this.#glances.get(
this.#currentGlanceID
).originalPosition;
const elementImageData = this.#glances.get(
this.#currentGlanceID
).elementImageData;
this.#addElementPreview(elementImageData);
// Create curved closing animation sequence
const closingData = this.#createClosingDataFromOriginalPosition(originalPosition);
const closingData =
this.#createClosingDataFromOriginalPosition(originalPosition);
const arcSequence = this.#createGlanceArcSequence(closingData, "closing");
gZenUIManager.motion
@@ -993,7 +1042,9 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
})
.then(() => {
// Remove element preview after closing animation
const elementPreview = this.browserWrapper.querySelector(".zen-glance-element-preview");
const elementPreview = this.browserWrapper.querySelector(
".zen-glance-element-preview"
);
if (elementPreview) {
elementPreview.remove();
}
@@ -1031,7 +1082,8 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
*/
#addElementPreview(elementImageData) {
if (elementImageData) {
const imageDataElement = this.#createGlancePreviewElement(elementImageData);
const imageDataElement =
this.#createGlancePreviewElement(elementImageData);
this.browserWrapper.prepend(imageDataElement);
}
}
@@ -1081,7 +1133,10 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
this.overlay.classList.remove("zen-glance-overlay");
gBrowser
._getSwitcher()
.setTabStateNoAction(lastCurrentTab, gBrowser.AsyncTabSwitcher.STATE_UNLOADED);
.setTabStateNoAction(
lastCurrentTab,
gBrowser.AsyncTabSwitcher.STATE_UNLOADED
);
if (!this.#currentParentTab.selected) {
this.#currentParentTab._visuallySelected = false;
@@ -1107,7 +1162,10 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
this.#ignoreClose = true;
lastCurrentTab.dispatchEvent(new Event("GlanceClose", { bubbles: true }));
gBrowser.removeTab(lastCurrentTab, { animate: true, skipPermitUnload: true });
gBrowser.removeTab(lastCurrentTab, {
animate: true,
skipPermitUnload: true,
});
gBrowser.tabContainer._invalidateCachedTabs();
}
@@ -1192,7 +1250,12 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
this.#removeParentBackground(parentHasBrowser, browserContainer);
if (!justAnimateParent && this.overlay) {
this.#resetGlanceStates(closeCurrentTab, closeParentTab, parentHasBrowser, browserContainer);
this.#resetGlanceStates(
closeCurrentTab,
closeParentTab,
parentHasBrowser,
browserContainer
);
}
if (clearID) {
@@ -1220,8 +1283,16 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
* @param {boolean} parentHasBrowser - Whether parent has browser
* @param {Element} browserContainer - The browser container
*/
#resetGlanceStates(closeCurrentTab, closeParentTab, parentHasBrowser, browserContainer) {
if (parentHasBrowser && !this.#currentParentTab.hasAttribute("split-view")) {
#resetGlanceStates(
closeCurrentTab,
closeParentTab,
parentHasBrowser,
browserContainer
) {
if (
parentHasBrowser &&
!this.#currentParentTab.hasAttribute("split-view")
) {
if (closeParentTab) {
browserContainer.classList.remove("deck-selected");
}
@@ -1258,7 +1329,9 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
this.quickOpenGlance();
if (prevTab && prevTab.linkedBrowser) {
prevTab.linkedBrowser.docShellIsActive = false;
prevTab.linkedBrowser.closest(".browserSidebarContainer").classList.remove("deck-selected");
prevTab.linkedBrowser
.closest(".browserSidebarContainer")
.classList.remove("deck-selected");
}
}
}
@@ -1284,7 +1357,10 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
return;
}
if (this.#currentGlanceID && this.#currentGlanceID !== tab.getAttribute("glance-id")) {
if (
this.#currentGlanceID &&
this.#currentGlanceID !== tab.getAttribute("glance-id")
) {
this.quickCloseGlance();
}
@@ -1375,7 +1451,11 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
* @returns {boolean} True if valid
*/
#isValidGlanceUrl(urlSpec) {
return urlSpec.startsWith("http") || urlSpec.startsWith("https") || urlSpec.startsWith("file");
return (
urlSpec.startsWith("http") ||
urlSpec.startsWith("https") ||
urlSpec.startsWith("file")
);
}
/**
@@ -1465,10 +1545,14 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
this.#handleZenFolderPinning();
gBrowser.moveTabAfter(this.#currentTab, this.#currentParentTab);
const browserRect = window.windowUtils.getBoundsWithoutFlushing(this.browserWrapper);
const browserRect = window.windowUtils.getBoundsWithoutFlushing(
this.browserWrapper
);
this.#prepareTabForFullOpen();
const sidebarButtons = this.browserWrapper.querySelector(".zen-glance-sidebar-container");
const sidebarButtons = this.browserWrapper.querySelector(
".zen-glance-sidebar-container"
);
if (sidebarButtons) {
sidebarButtons.remove();
}
@@ -1493,7 +1577,10 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
*/
#handleZenFolderPinning() {
const isZenFolder = this.#currentParentTab?.group?.isZenFolder;
if (Services.prefs.getBoolPref("zen.folders.owned-tabs-in-folder") && isZenFolder) {
if (
Services.prefs.getBoolPref("zen.folders.owned-tabs-in-folder") &&
isZenFolder
) {
gBrowser.pinTab(this.#currentTab);
}
}
@@ -1551,7 +1638,10 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
* @param {Event} event - The bookmark click event
*/
openGlanceForBookmark(event) {
const activationMethod = Services.prefs.getStringPref("zen.glance.activation-method", "ctrl");
const activationMethod = Services.prefs.getStringPref(
"zen.glance.activation-method",
"ctrl"
);
if (!this.#isActivationKeyPressed(event, activationMethod)) {
return;
@@ -1590,7 +1680,9 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
*/
#createGlanceDataFromBookmark(event) {
const rect = window.windowUtils.getBoundsWithoutFlushing(event.target);
const tabPanelRect = window.windowUtils.getBoundsWithoutFlushing(gBrowser.tabpanels);
const tabPanelRect = window.windowUtils.getBoundsWithoutFlushing(
gBrowser.tabpanels
);
// the bookmark is most likely outisde the tabpanel, so we need to give a negative number
// so it can be corrected later
// eslint-disable-next-line no-shadow
@@ -1631,7 +1723,9 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
gZenViewSplitter.splitTabs([currentTab, currentParentTab], "vsep", 1);
const browserContainer = currentTab.linkedBrowser?.closest(".browserSidebarContainer");
const browserContainer = currentTab.linkedBrowser?.closest(
".browserSidebarContainer"
);
if (!gReduceMotion && browserContainer) {
gZenViewSplitter.animateBrowserDrop(browserContainer);
}
@@ -1644,7 +1738,10 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
*/
#handleZenFolderPinningForSplit(parentTab) {
const isZenFolder = parentTab?.group?.isZenFolder;
if (Services.prefs.getBoolPref("zen.folders.owned-tabs-in-folder") && isZenFolder) {
if (
Services.prefs.getBoolPref("zen.folders.owned-tabs-in-folder") &&
isZenFolder
) {
gBrowser.pinTab(this.#currentTab);
}
}
@@ -1657,7 +1754,9 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
*/
getTabOrGlanceParent(tab) {
if (tab?.hasAttribute("glance-id") && this.#glances) {
const parentTab = this.#glances.get(tab.getAttribute("glance-id"))?.parentTab;
const parentTab = this.#glances.get(
tab.getAttribute("glance-id")
)?.parentTab;
if (parentTab) {
return parentTab;
}
@@ -1702,7 +1801,8 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
if (currentGlanceID && oldGlanceID) {
return (
currentGlanceID === oldGlanceID && oldPanel.classList.contains("zen-glance-background")
currentGlanceID === oldGlanceID &&
oldPanel.classList.contains("zen-glance-background")
);
}
@@ -1752,7 +1852,9 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
* @param {Tab} parentTab - Parent tab
*/
#openGlanceForSearch(currentTab, parentTab) {
const browserRect = window.windowUtils.getBoundsWithoutFlushing(gBrowser.tabbox);
const browserRect = window.windowUtils.getBoundsWithoutFlushing(
gBrowser.tabbox
);
const clickPosition = gZenUIManager._lastClickPosition || {
clientX: browserRect.width / 2,
clientY: browserRect.height / 2,

View File

@@ -17,7 +17,9 @@ export class ZenGlanceChild extends JSWindowActorChild {
}
async #initActivationMethod() {
this.#activationMethod = await this.sendQuery("ZenGlance:GetActivationMethod");
this.#activationMethod = await this.sendQuery(
"ZenGlance:GetActivationMethod"
);
}
#ensureOnlyKeyModifiers(event) {
@@ -114,7 +116,9 @@ export class ZenGlanceChild extends JSWindowActorChild {
return;
}
this.sendAsyncMessage("ZenGlance:CloseGlance", {
hasFocused: this.contentWindow.document.activeElement !== this.contentWindow.document.body,
hasFocused:
this.contentWindow.document.activeElement !==
this.contentWindow.document.body,
});
}

View File

@@ -12,7 +12,10 @@ export class ZenGlanceParent extends JSWindowActorParent {
async receiveMessage(message) {
switch (message.name) {
case "ZenGlance:GetActivationMethod": {
return Services.prefs.getStringPref("zen.glance.activation-method", "ctrl");
return Services.prefs.getStringPref(
"zen.glance.activation-method",
"ctrl"
);
}
case "ZenGlance:OpenGlance": {
this.openGlance(this.browsingContext.topChromeWindow, message.data);
@@ -23,11 +26,14 @@ export class ZenGlanceParent extends JSWindowActorParent {
onTabClose: true,
...message.data,
};
this.browsingContext.topChromeWindow.gZenGlanceManager.closeGlance(params);
this.browsingContext.topChromeWindow.gZenGlanceManager.closeGlance(
params
);
break;
}
case "ZenGlance:RecordLinkClickData": {
this.browsingContext.topChromeWindow.gZenGlanceManager.lastLinkClickData = message.data;
this.browsingContext.topChromeWindow.gZenGlanceManager.lastLinkClickData =
message.data;
break;
}
default:

View File

@@ -4,7 +4,7 @@
export function openGlanceOnTab(window, callback, close = true) {
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve) => {
return new Promise(async resolve => {
window.gZenGlanceManager
.openGlance({
url: "https://example.com",
@@ -13,7 +13,7 @@ export function openGlanceOnTab(window, callback, close = true) {
width: 0,
height: 0,
})
.then(async (glanceTab) => {
.then(async glanceTab => {
await callback(glanceTab);
if (close) {
window.gZenGlanceManager

View File

@@ -111,16 +111,15 @@ const defaultKeyboardGroups = {
"zen-bidi-switch-direction-shortcut",
"zen-screenshot-shortcut",
],
devTools: [
/*Filled automatically*/
],
devTools: [/*Filled automatically*/],
};
const fixedL10nIds = {
cmd_findPrevious: "zen-search-find-again-shortcut-prev",
"Browser:ReloadSkipCache": "zen-nav-reload-shortcut-skip-cache",
cmd_close: "zen-close-tab-shortcut",
"History:RestoreLastClosedTabOrWindowOrSession": "zen-restore-last-closed-tab-shortcut",
"History:RestoreLastClosedTabOrWindowOrSession":
"zen-restore-last-closed-tab-shortcut",
};
const ZEN_MAIN_KEYSET_ID = "mainKeyset";
@@ -191,7 +190,13 @@ export class nsKeyShortcutModifiers {
}
// used to avoid any future changes to the object
static fromObject({ ctrl = false, alt = false, shift = false, meta = false, accel = false }) {
static fromObject({
ctrl = false,
alt = false,
shift = false,
meta = false,
accel = false,
}) {
return new nsKeyShortcutModifiers(ctrl, alt, shift, meta, accel);
}
@@ -270,7 +275,9 @@ export class nsKeyShortcutModifiers {
}
areAnyActive() {
return this.#control || this.#alt || this.#shift || this.#meta || this.#accel;
return (
this.#control || this.#alt || this.#shift || this.#meta || this.#accel
);
}
get control() {
@@ -385,7 +392,9 @@ class KeyShortcut {
KeyShortcut.sanitizeL10nId(key.getAttribute("data-l10n-id")),
key.getAttribute("id")
),
nsKeyShortcutModifiers.parseFromXHTMLAttribute(key.getAttribute("modifiers")),
nsKeyShortcutModifiers.parseFromXHTMLAttribute(
key.getAttribute("modifiers")
),
key.getAttribute("command"),
key.getAttribute("data-l10n-id"),
key.getAttribute("disabled") == "true",
@@ -589,7 +598,11 @@ class KeyShortcut {
}
isUserEditable() {
if (!this.#id || this.#internal || (this.#group == FIREFOX_SHORTCUTS_GROUP && this.#disabled)) {
if (
!this.#id ||
this.#internal ||
(this.#group == FIREFOX_SHORTCUTS_GROUP && this.#disabled)
) {
return false;
}
return true;
@@ -653,10 +666,14 @@ class nsZenKeyboardShortcutsLoader {
let keySet = document.getElementById(ZEN_MAIN_KEYSET_ID);
let newShortcutList = [];
const correctDefaultShortcut = (shortcut) => {
const correctDefaultShortcut = shortcut => {
if (shortcut.getID() === "key_savePage") {
shortcut.setModifiers(
nsKeyShortcutModifiers.fromObject({ accel: true, alt: true, shift: true })
nsKeyShortcutModifiers.fromObject({
accel: true,
alt: true,
shift: true,
})
);
}
};
@@ -801,7 +818,10 @@ class nsZenKeyboardShortcutsLoader {
}
let parsed = KeyShortcut.parseFromXHTML(key, { group: "devTools" });
// Move "inspector" shortcut to use "L" key instead of "I"
if (parsed.getID() == "key_inspector" || parsed.getID() == "key_inspectorMac") {
if (
parsed.getID() == "key_inspector" ||
parsed.getID() == "key_inspectorMac"
) {
parsed.setNewBinding("L");
}
newShortcutList.push(parsed);
@@ -869,7 +889,7 @@ class nsZenKeyboardShortcutsVersioner {
fillDefaultIfNotPresent(data) {
for (let shortcut of nsZenKeyboardShortcutsLoader.zenGetDefaultShortcuts()) {
// If it has an ID and we dont find it in the data, we add it
if (shortcut.getID() && !data.find((s) => s.getID() == shortcut.getID())) {
if (shortcut.getID() && !data.find(s => s.getID() == shortcut.getID())) {
data.push(shortcut);
}
}
@@ -919,7 +939,8 @@ class nsZenKeyboardShortcutsVersioner {
// detection for internal keys was not working properly, so every internal
// shortcut was being saved as a user-editable shortcut.
// This migration will fix this issue.
const defaultShortcuts = nsZenKeyboardShortcutsLoader.zenGetDefaultShortcuts();
const defaultShortcuts =
nsZenKeyboardShortcutsLoader.zenGetDefaultShortcuts();
// Get the default shortcut, compare the id and set the internal flag if needed
for (let shortcut of data) {
for (let defaultShortcut of defaultShortcuts) {
@@ -933,7 +954,7 @@ class nsZenKeyboardShortcutsVersioner {
// Migrate from 3 to 4
// In this new version, we are just removing the 'zen-toggle-sidebar' shortcut
// since it's not used anymore.
data = data.filter((shortcut) => shortcut.getID() != "zen-toggle-sidebar");
data = data.filter(shortcut => shortcut.getID() != "zen-toggle-sidebar");
}
if (version < 5) {
// Migrate from 4 to 5
@@ -968,11 +989,14 @@ class nsZenKeyboardShortcutsVersioner {
if (version < 7) {
// Migrate from 6 to 7
// In this new version, we add the devtools shortcuts
const listener = (event) => {
const listener = event => {
event.stopPropagation();
const devToolsShortcuts = nsZenKeyboardShortcutsLoader.zenGetDefaultDevToolsShortcuts();
gZenKeyboardShortcutsManager.updatedDefaultDevtoolsShortcuts(devToolsShortcuts);
const devToolsShortcuts =
nsZenKeyboardShortcutsLoader.zenGetDefaultDevToolsShortcuts();
gZenKeyboardShortcutsManager.updatedDefaultDevtoolsShortcuts(
devToolsShortcuts
);
window.removeEventListener("zen-devtools-keyset-added", listener);
};
@@ -991,7 +1015,11 @@ class nsZenKeyboardShortcutsVersioner {
"C",
"",
ZEN_OTHER_SHORTCUTS_GROUP,
nsKeyShortcutModifiers.fromObject({ accel: true, shift: true, alt: true }),
nsKeyShortcutModifiers.fromObject({
accel: true,
shift: true,
alt: true,
}),
"cmd_zenCopyCurrentURLMarkdown",
"zen-text-action-copy-url-markdown-shortcut"
)
@@ -1001,7 +1029,9 @@ class nsZenKeyboardShortcutsVersioner {
// Migrate from version 8 to 9
// Due to security concerns, replace "code:" actions with corresponding <command> IDs
// we also remove 'zen-toggle-web-panel' since it's not used anymore
data = data.filter((shortcut) => shortcut.getID() != "zen-toggle-web-panel");
data = data.filter(
shortcut => shortcut.getID() != "zen-toggle-web-panel"
);
for (let shortcut of data) {
if (shortcut.getAction()?.startsWith("code:")) {
const id = shortcut.getID();
@@ -1084,7 +1114,11 @@ class nsZenKeyboardShortcutsVersioner {
// - Remove the built-in "Open File" keybinding; menu item remains available
// - Remove default "Bookmark All Tabs" keybinding (Ctrl+Shift+D) to avoid conflict
// - Remove "Stop" keybinding to avoid conflict with Firefox's built-in binding
const shouldBeEmptyShortcuts = ["openFileKb", "bookmarkAllTabsKb", "key_stop"];
const shouldBeEmptyShortcuts = [
"openFileKb",
"bookmarkAllTabsKb",
"key_stop",
];
for (let shortcut of data) {
if (shouldBeEmptyShortcuts.includes(shortcut.getID?.())) {
shortcut.shouldBeEmpty = true;
@@ -1092,7 +1126,9 @@ class nsZenKeyboardShortcutsVersioner {
}
// Also remove zen-compact-mode-show-toolbar
data = data.filter((shortcut) => shortcut.getID() != "zen-compact-mode-show-toolbar");
data = data.filter(
shortcut => shortcut.getID() != "zen-compact-mode-show-toolbar"
);
}
if (version < 13) {
@@ -1130,7 +1166,10 @@ class nsZenKeyboardShortcutsVersioner {
let emptySplitFound = false,
undoCloseWindowFound = false;
for (let shortcut of data) {
if (shortcut.getID() == "zen-new-empty-split-view" && AppConstants.platform == "macosx") {
if (
shortcut.getID() == "zen-new-empty-split-view" &&
AppConstants.platform == "macosx"
) {
if (shortcut.getKeyName() == "+") {
shortcut.setNewBinding("*");
}
@@ -1179,7 +1218,10 @@ window.gZenKeyboardShortcutsManager = {
"zen.keyboard.shortcuts.disable-mainkeyset-clear",
false
);
window.addEventListener("zen-devtools-keyset-added", this._hasAddedDevtoolShortcuts.bind(this));
window.addEventListener(
"zen-devtools-keyset-added",
this._hasAddedDevtoolShortcuts.bind(this)
);
this.init();
},
@@ -1188,11 +1230,14 @@ window.gZenKeyboardShortcutsManager = {
if (this.inBrowserView) {
const loadedShortcuts = await this._loadSaved();
this._currentShortcutList = this.versioner.fixedKeyboardShortcuts(loadedShortcuts);
this._currentShortcutList =
this.versioner.fixedKeyboardShortcuts(loadedShortcuts);
this._applyShortcuts();
await this._saveShortcuts();
window.dispatchEvent(new Event("ZenKeyboardShortcutsReady", { bubbles: true }));
window.dispatchEvent(
new Event("ZenKeyboardShortcutsReady", { bubbles: true })
);
}
},
@@ -1210,12 +1255,16 @@ window.gZenKeyboardShortcutsManager = {
try {
return KeyShortcut.parseFromSaved(data);
} catch (e) {
console.error("Zen CKS: Error parsing saved shortcuts. Resetting to defaults...", e);
console.error(
"Zen CKS: Error parsing saved shortcuts. Resetting to defaults...",
e
);
gNotificationBox.appendNotification(
"zen-shortcuts-corrupted",
{
label: { "l10n-id": "zen-shortcuts-corrupted" },
image: "chrome://browser/skin/notification-icons/persistent-storage-blocked.svg",
image:
"chrome://browser/skin/notification-icons/persistent-storage-blocked.svg",
priority: gNotificationBox.PRIORITY_WARNING_HIGH,
},
[]
@@ -1325,12 +1374,17 @@ window.gZenKeyboardShortcutsManager = {
if (!browser.gZenKeyboardShortcutsManager?._hasToLoadDevtools) {
return;
}
let devtoolsKeyset = browser.gZenKeyboardShortcutsManager.getZenDevtoolsKeyset(browser);
let devtoolsKeyset =
browser.gZenKeyboardShortcutsManager.getZenDevtoolsKeyset(browser);
for (let key of this._currentShortcutList) {
if (key.getGroup() != "devTools") {
continue;
}
if (nsZenKeyboardShortcutsLoader.IGNORED_DEVTOOLS_SHORTCUTS.includes(key.getID())) {
if (
nsZenKeyboardShortcutsLoader.IGNORED_DEVTOOLS_SHORTCUTS.includes(
key.getID()
)
) {
continue;
}
const originalKey = browser.document.getElementById(key.getID());
@@ -1345,7 +1399,9 @@ window.gZenKeyboardShortcutsManager = {
}
}
const originalDevKeyset = browser.document.getElementById(ZEN_DEVTOOLS_KEYSET_ID);
const originalDevKeyset = browser.document.getElementById(
ZEN_DEVTOOLS_KEYSET_ID
);
originalDevKeyset.after(devtoolsKeyset);
},

View File

@@ -5,7 +5,8 @@ const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
NetUtil: "resource://gre/modules/NetUtil.sys.mjs",
DeferredTask: "resource://gre/modules/DeferredTask.sys.mjs",
NetworkHelper: "resource://devtools/shared/network-observer/NetworkHelper.sys.mjs",
NetworkHelper:
"resource://devtools/shared/network-observer/NetworkHelper.sys.mjs",
});
export class nsZenLiveFolderProvider {
@@ -135,7 +136,10 @@ export class nsZenLiveFolderProvider {
userContextId = space.containerTabId || 0;
}
}
const principal = Services.scriptSecurityManager.createContentPrincipal(uri, { userContextId });
const principal = Services.scriptSecurityManager.createContentPrincipal(
uri,
{ userContextId }
);
const channel = lazy.NetUtil.newChannel({
uri,
@@ -169,7 +173,7 @@ export class nsZenLiveFolderProvider {
byteChunks.push(lazy.NetUtil.readInputStream(stream, count));
}
},
onStartRequest: (request) => {
onStartRequest: request => {
const http = request.QueryInterface(Ci.nsIHttpChannel);
try {
@@ -182,7 +186,10 @@ export class nsZenLiveFolderProvider {
contentType = http.getResponseHeader("content-type");
} catch (ex) {}
if (contentType && !lazy.NetworkHelper.isTextMimeType(contentType.split(";")[0].trim())) {
if (
contentType &&
!lazy.NetworkHelper.isTextMimeType(contentType.split(";")[0].trim())
) {
request.cancel(Cr.NS_ERROR_FILE_UNKNOWN_TYPE);
}
@@ -215,7 +222,9 @@ export class nsZenLiveFolderProvider {
let effectiveCharset = "utf-8";
const mimeType = contentType ? contentType.split(";")[0].trim().toLowerCase() : "";
const mimeType = contentType
? contentType.split(";")[0].trim().toLowerCase()
: "";
if (mimeType === "text/html") {
effectiveCharset = this.sniffCharset(bytes, headerCharset);
} else if (headerCharset) {
@@ -252,7 +261,12 @@ export class nsZenLiveFolderProvider {
*/
sniffCharset(bytes, headerCharset = "") {
// 1. BOM detection (highest priority)
if (bytes.length >= 3 && bytes[0] === 0xef && bytes[1] === 0xbb && bytes[2] === 0xbf) {
if (
bytes.length >= 3 &&
bytes[0] === 0xef &&
bytes[1] === 0xbb &&
bytes[2] === 0xbf
) {
return "utf-8";
}
if (bytes.length >= 2) {
@@ -269,7 +283,9 @@ export class nsZenLiveFolderProvider {
// is more likely to be correct.
try {
const headLen = Math.min(bytes.length, 8192);
const head = new TextDecoder("windows-1252").decode(bytes.subarray(0, headLen));
const head = new TextDecoder("windows-1252").decode(
bytes.subarray(0, headLen)
);
const metaCharsetRegex = /<meta\s+charset\s*=\s*["']?([a-z0-9_-]+)/i;
let match = head.match(metaCharsetRegex);

View File

@@ -51,7 +51,9 @@ class nsZenLiveFoldersManager {
}
for (const provider of providers) {
const module = ChromeUtils.importESModule(provider.path, { global: "current" });
const module = ChromeUtils.importESModule(provider.path, {
global: "current",
});
const ProviderClass = module[provider.module];
this.registry.set(ProviderClass.type, ProviderClass);
}
@@ -114,7 +116,8 @@ class nsZenLiveFoldersManager {
#onTabDismiss(event) {
const itemIdAttr = "zen-live-folder-item-id";
const itemId =
event.target.getAttribute(itemIdAttr) || event.detail?.getAttribute?.(itemIdAttr);
event.target.getAttribute(itemIdAttr) ||
event.detail?.getAttribute?.(itemIdAttr);
if (itemId) {
if (event.type === "TabUngrouped") {
@@ -139,7 +142,9 @@ class nsZenLiveFoldersManager {
#onActionButtonClick(event) {
const liveFolderId = event.target.getAttribute("live-folder-action");
this.getFolder(liveFolderId)?.onActionButtonClick(event.target.getAttribute("data-l10n-id"));
this.getFolder(liveFolderId)?.onActionButtonClick(
event.target.getAttribute("data-l10n-id")
);
}
#onTabGroupRemoved(event) {
@@ -317,7 +322,9 @@ class nsZenLiveFoldersManager {
// Remove the dismissed items associated with the folder from the set
this.dismissedItems = new Set(
Array.from(this.dismissedItems).filter((itemId) => !itemId.startsWith(prefix))
Array.from(this.dismissedItems).filter(
itemId => !itemId.startsWith(prefix)
)
);
if (deleteFolder) {
@@ -351,7 +358,9 @@ class nsZenLiveFoldersManager {
}
// itemid -> id:itemid
const itemIds = new Set(items.map((item) => this.#makeCompositeId(liveFolder.id, item.id)));
const itemIds = new Set(
items.map(item => this.#makeCompositeId(liveFolder.id, item.id))
);
const outdatedTabs = [];
const existingItemIds = new Set();
@@ -377,7 +386,10 @@ class nsZenLiveFoldersManager {
// Remove the dismissed items that are no longer in the given list
for (const dismissedItemId of this.dismissedItems) {
if (dismissedItemId.startsWith(`${liveFolder.id}:`) && !itemIds.has(dismissedItemId)) {
if (
dismissedItemId.startsWith(`${liveFolder.id}:`) &&
!itemIds.has(dismissedItemId)
) {
this.dismissedItems.delete(dismissedItemId);
}
}
@@ -392,11 +404,14 @@ class nsZenLiveFoldersManager {
// Only add the items that are not already in the folder and was not dismissed by the user
const newItems = items
.filter((item) => {
.filter(item => {
const compositeId = this.#makeCompositeId(liveFolder.id, item.id);
return !existingItemIds.has(compositeId) && !this.dismissedItems.has(compositeId);
return (
!existingItemIds.has(compositeId) &&
!this.dismissedItems.has(compositeId)
);
})
.map((item) => {
.map(item => {
const tab = this.window.gBrowser.addTrustedTab(item.url, {
createLazyBrowser: true,
inBackground: true,
@@ -418,7 +433,10 @@ class nsZenLiveFoldersManager {
});
}
}
tab.setAttribute("zen-live-folder-item-id", this.#makeCompositeId(liveFolder.id, item.id));
tab.setAttribute(
"zen-live-folder-item-id",
this.#makeCompositeId(liveFolder.id, item.id)
);
if (item.subtitle) {
tab.setAttribute("zen-show-sublabel", item.subtitle);
const tabLabel = tab.querySelector(".zen-tab-sublabel");
@@ -471,7 +489,10 @@ class nsZenLiveFoldersManager {
if (!this.window) {
return null;
}
const folder = lazy.ZenWindowSync.getItemFromWindow(this.window, liveFolder.id);
const folder = lazy.ZenWindowSync.getItemFromWindow(
this.window,
liveFolder.id
);
if (folder?.isZenFolder) {
return folder;
}
@@ -516,7 +537,7 @@ class nsZenLiveFoldersManager {
let data = [];
for (let [id, liveFolder] of this.liveFolders) {
const prefix = `${id}:`;
const dismissedItems = Array.from(this.dismissedItems).filter((itemId) =>
const dismissedItems = Array.from(this.dismissedItems).filter(itemId =>
itemId.startsWith(prefix)
);
@@ -568,7 +589,7 @@ class nsZenLiveFoldersManager {
continue;
}
const folder = folders.find((x) => x.id === entry.id);
const folder = folders.find(x => x.id === entry.id);
if (!folder) {
// No point restore if the live folder can't find its folder
continue;
@@ -585,7 +606,7 @@ class nsZenLiveFoldersManager {
liveFolder.tabsState = entry.tabsState || [];
liveFolder.state.lastErrorId = entry.data.state.lastErrorId;
if (entry.dismissedItems && Array.isArray(entry.dismissedItems)) {
entry.dismissedItems.forEach((id) => this.dismissedItems.add(id));
entry.dismissedItems.forEach(id => this.dismissedItems.add(id));
}
liveFolder.start();

View File

@@ -4,7 +4,8 @@
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
ZenLiveFoldersManager: "resource:///modules/zen/ZenLiveFoldersManager.sys.mjs",
ZenLiveFoldersManager:
"resource:///modules/zen/ZenLiveFoldersManager.sys.mjs",
});
class nsZenLiveFoldersUI {
@@ -13,7 +14,7 @@ class nsZenLiveFoldersUI {
.getElementById("context_zenLiveFolderOptions")
.querySelector("menupopup");
popup.addEventListener("command", (event) => {
popup.addEventListener("command", event => {
const option = event.target;
const folderId = option.getAttribute("option-folder");
@@ -36,7 +37,9 @@ class nsZenLiveFoldersUI {
}
#restoreUIStateForLiveFolder(liveFolder) {
const folder = window.gZenWorkspaces.allTabGroups.find((x) => x.id === liveFolder.id);
const folder = window.gZenWorkspaces.allTabGroups.find(
x => x.id === liveFolder.id
);
if (!folder) {
return;
}
@@ -47,7 +50,9 @@ class nsZenLiveFoldersUI {
}
for (const { itemId, label } of liveFolder.tabsState) {
const tab = folder.tabs.find((t) => t.getAttribute("zen-live-folder-item-id") === itemId);
const tab = folder.tabs.find(
t => t.getAttribute("zen-live-folder-item-id") === itemId
);
if (tab && label) {
const tabLabel = tab.querySelector(".zen-tab-sublabel");
tab.setAttribute("zen-show-sublabel", label);
@@ -122,7 +127,9 @@ class nsZenLiveFoldersUI {
}
buildContextMenu(folder) {
const optionsElement = document.getElementById("context_zenLiveFolderOptions");
const optionsElement = document.getElementById(
"context_zenLiveFolderOptions"
);
let hidden = true;
if (folder.isLiveFolder) {
@@ -141,8 +148,9 @@ class nsZenLiveFoldersUI {
intervals.push({ hours });
}
intervals = intervals.map((entry) => {
const ms = "mins" in entry ? entry.mins * MINUTE_MS : entry.hours * HOUR_MS;
intervals = intervals.map(entry => {
const ms =
"mins" in entry ? entry.mins * MINUTE_MS : entry.hours * HOUR_MS;
return {
l10nId:
@@ -162,7 +170,8 @@ class nsZenLiveFoldersUI {
const contextMenuItems = [
{
key: "lastFetched",
l10nId: liveFolder.state.lastErrorId || "zen-live-folder-last-fetched",
l10nId:
liveFolder.state.lastErrorId || "zen-live-folder-last-fetched",
l10nArgs: { time: this.#timeAgo(liveFolder.state.lastFetched) },
disabled: true,
},
@@ -194,7 +203,9 @@ class nsZenLiveFoldersUI {
return "-";
}
const rtf = new Intl.RelativeTimeFormat(Services.locale.appLocaleAsBCP47, { numeric: "auto" });
const rtf = new Intl.RelativeTimeFormat(Services.locale.appLocaleAsBCP47, {
numeric: "auto",
});
const secondsDiff = (date - Date.now()) / 1000;
const absSeconds = Math.abs(secondsDiff);

View File

@@ -47,9 +47,15 @@ export class nsGithubLiveFolderProvider extends nsZenLiveFolderProvider {
const activeRepos = new Set();
if (issues.length) {
const authors = document.querySelectorAll("a[class^=IssueItem-module__authorCreatedLink]");
const titles = document.querySelectorAll("div[class^=Title-module__container]");
const links = document.querySelectorAll('[data-testid="issue-pr-title-link"]');
const authors = document.querySelectorAll(
"a[class^=IssueItem-module__authorCreatedLink]"
);
const titles = document.querySelectorAll(
"div[class^=Title-module__container]"
);
const links = document.querySelectorAll(
'[data-testid="issue-pr-title-link"]'
);
for (let i = 0; i < issues.length; i++) {
const [rawRepo, rawNumber] = issues[i].childNodes;
@@ -131,7 +137,7 @@ export class nsGithubLiveFolderProvider extends nsZenLiveFolderProvider {
let outputString = "";
for (const option of options) {
if (Array.isArray(option)) {
const enabledOptions = option.filter((x) => x.enabled).map((x) => x.value);
const enabledOptions = option.filter(x => x.enabled).map(x => x.value);
if (enabledOptions.length) {
outputString += ` (${enabledOptions.join(" OR ")}) `;
}
@@ -151,7 +157,7 @@ export class nsGithubLiveFolderProvider extends nsZenLiveFolderProvider {
const excluded = this.state.options.repoExcludes;
const repoOptions = Array.from(this.state.repos.union(excluded))
.sort((a, b) => a.localeCompare(b))
.map((repo) => ({
.map(repo => ({
l10nId: "zen-live-folder-github-option-repo",
l10nArgs: { repo },
@@ -204,7 +210,7 @@ export class nsGithubLiveFolderProvider extends nsZenLiveFolderProvider {
const key = option.getAttribute("option-key");
const checked = option.getAttribute("checked") === "true";
if (!this.options.some((x) => x.key === key)) {
if (!this.options.some(x => x.key === key)) {
return;
}
@@ -235,7 +241,9 @@ export class nsGithubLiveFolderProvider extends nsZenLiveFolderProvider {
switch (errorId) {
case "zen-live-folder-github-no-auth": {
const tab = this.manager.window.gBrowser.addTrustedTab("https://github.com/login");
const tab = this.manager.window.gBrowser.addTrustedTab(
"https://github.com/login"
);
this.manager.window.gBrowser.selectedTab = tab;
break;
}

View File

@@ -39,29 +39,35 @@ export class nsRssLiveFolderProvider extends nsZenLiveFolderProvider {
const elements = doc.querySelectorAll(selector);
const items = Array.from(elements)
.map((item) => {
.map(item => {
const title = item.querySelector("title")?.textContent || "";
const linkNode = item.querySelector("link");
const url =
isAtom && linkNode ? linkNode.getAttribute("href") : linkNode?.textContent || "";
isAtom && linkNode
? linkNode.getAttribute("href")
: linkNode?.textContent || "";
const guid = item.querySelector(isAtom ? "id" : "guid")?.textContent;
const id = guid || url;
const dateStr = item.querySelector(isAtom ? "updated" : "pubDate")?.textContent;
const dateStr = item.querySelector(
isAtom ? "updated" : "pubDate"
)?.textContent;
const date = dateStr ? new Date(dateStr) : null;
return { title, url, id, date };
})
.filter((item) => {
.filter(item => {
if (!item.url || !item.date) {
return false;
}
if (!this.state.timeRange) {
return true;
}
return !isNaN(item.date.getTime()) && item.date.getTime() >= cutoffTime;
return (
!isNaN(item.date.getTime()) && item.date.getTime() >= cutoffTime
);
})
.slice(0, this.state.maxItems);
for (let item of items) {
@@ -99,7 +105,7 @@ export class nsRssLiveFolderProvider extends nsZenLiveFolderProvider {
_buildItemLimitOptions() {
const entries = [5, 10, 25, 50];
return entries.map((entry) => {
return entries.map(entry => {
return this._buildRadioOption({
key: "maxItems",
value: entry,
@@ -128,13 +134,15 @@ export class nsRssLiveFolderProvider extends nsZenLiveFolderProvider {
l10nId: "zen-live-folder-time-range-all-time",
}),
{ type: "separator" },
...entries.map((entry) => {
...entries.map(entry => {
const isDays = "days" in entry;
return this._buildRadioOption({
key: "timeRange",
value: entry.ms,
l10nId: isDays ? "zen-live-folder-time-range-days" : "zen-live-folder-time-range-hours",
l10nId: isDays
? "zen-live-folder-time-range-days"
: "zen-live-folder-time-range-hours",
l10nArgs: isDays ? { days: entry.days } : { hours: entry.hours },
});
}),
@@ -165,7 +173,10 @@ export class nsRssLiveFolderProvider extends nsZenLiveFolderProvider {
try {
const response = await fetch(url);
if (!response.ok) {
return { label: "", icon: window.gZenEmojiPicker.getSVGURL("logo-rss.svg") };
return {
label: "",
icon: window.gZenEmojiPicker.getSVGURL("logo-rss.svg"),
};
}
const text = await response.text();
@@ -175,14 +186,20 @@ export class nsRssLiveFolderProvider extends nsZenLiveFolderProvider {
const title = (
isAtom
? doc.querySelector("feed > title")?.textContent
: doc.querySelector("rss > channel > title, channel > title")?.textContent
: doc.querySelector("rss > channel > title, channel > title")
?.textContent
)?.trim();
const linkNode = isAtom
? doc.querySelector("feed > link[rel='alternate'][href], feed > link[href]")
? doc.querySelector(
"feed > link[rel='alternate'][href], feed > link[href]"
)
: doc.querySelector("rss > channel > link, channel > link");
const feedLink =
(isAtom ? linkNode?.getAttribute("href") : linkNode?.textContent)?.trim() || "";
(isAtom
? linkNode?.getAttribute("href")
: linkNode?.textContent
)?.trim() || "";
const faviconPageUrl = feedLink ? new URL(feedLink, url).href : url;
let favicon = await lazy.PlacesUtils.favicons.getFaviconForPage(
@@ -191,16 +208,23 @@ export class nsRssLiveFolderProvider extends nsZenLiveFolderProvider {
return {
label: title || "",
icon: favicon?.dataURI.spec || window.gZenEmojiPicker.getSVGURL("logo-rss.svg"),
icon:
favicon?.dataURI.spec ||
window.gZenEmojiPicker.getSVGURL("logo-rss.svg"),
};
} catch (e) {
return { label: "", icon: window.gZenEmojiPicker.getSVGURL("logo-rss.svg") };
return {
label: "",
icon: window.gZenEmojiPicker.getSVGURL("logo-rss.svg"),
};
}
}
static async promptForFeedUrl(window, initialUrl = "") {
const input = { value: initialUrl };
const [prompt] = await lazy.l10n.formatValues(["zen-live-folder-rss-prompt-feed-url"]);
const [prompt] = await lazy.l10n.formatValues([
"zen-live-folder-rss-prompt-feed-url",
]);
const promptOk = Services.prompt.prompt(window, prompt, null, input, null, {
value: null,
});
@@ -228,7 +252,10 @@ export class nsRssLiveFolderProvider extends nsZenLiveFolderProvider {
}
async getMetadata() {
return nsRssLiveFolderProvider.getMetadata(this.state.url, this.manager.window);
return nsRssLiveFolderProvider.getMetadata(
this.state.url,
this.manager.window
);
}
async onOptionTrigger(option) {
@@ -237,7 +264,7 @@ export class nsRssLiveFolderProvider extends nsZenLiveFolderProvider {
const key = option.getAttribute("option-key");
const value = option.getAttribute("option-value");
if (!this.options.some((x) => x.key === key)) {
if (!this.options.some(x => x.key === key)) {
return;
}

View File

@@ -43,12 +43,16 @@ class nsZenMediaController {
this.mediaTitle = document.querySelector("#zen-media-title");
this.mediaArtist = document.querySelector("#zen-media-artist");
this.mediaControlBar = document.querySelector("#zen-media-controls-toolbar");
this.mediaControlBar = document.querySelector(
"#zen-media-controls-toolbar"
);
this.mediaProgressBar = document.querySelector("#zen-media-progress-bar");
this.mediaCurrentTime = document.querySelector("#zen-media-current-time");
this.mediaDuration = document.querySelector("#zen-media-duration");
this.mediaFocusButton = document.querySelector("#zen-media-focus-button");
this.mediaProgressBarContainer = document.querySelector("#zen-media-progress-hbox");
this.mediaProgressBarContainer = document.querySelector(
"#zen-media-progress-hbox"
);
this.onPositionstateChange = this._onPositionstateChange.bind(this);
this.onPlaybackstateChange = this._onPlaybackstateChange.bind(this);
@@ -61,14 +65,14 @@ class nsZenMediaController {
}
#initEventListeners() {
this.mediaControlBar.addEventListener("mousedown", (event) => {
this.mediaControlBar.addEventListener("mousedown", event => {
if (event.target.closest(":is(toolbarbutton,#zen-media-progress-hbox)")) {
return;
}
this.onMediaFocus();
});
this.mediaControlBar.addEventListener("command", (event) => {
this.mediaControlBar.addEventListener("command", event => {
const button = event.target.closest("toolbarbutton");
if (!button) {
return;
@@ -104,10 +108,16 @@ class nsZenMediaController {
}
});
this.mediaProgressBar.addEventListener("input", this.onMediaSeekDrag.bind(this));
this.mediaProgressBar.addEventListener("change", this.onMediaSeekComplete.bind(this));
this.mediaProgressBar.addEventListener(
"input",
this.onMediaSeekDrag.bind(this)
);
this.mediaProgressBar.addEventListener(
"change",
this.onMediaSeekComplete.bind(this)
);
window.addEventListener("TabSelect", (event) => {
window.addEventListener("TabSelect", event => {
if (this.isSharing) {
return;
}
@@ -140,7 +150,7 @@ class nsZenMediaController {
window.addEventListener("TabClose", onTabDiscardedOrClosed);
window.addEventListener("TabBrowserDiscarded", onTabDiscardedOrClosed);
window.addEventListener("DOMAudioPlaybackStarted", (event) => {
window.addEventListener("DOMAudioPlaybackStarted", event => {
setTimeout(() => {
if (
this._currentMediaController?.isPlaying &&
@@ -154,15 +164,21 @@ class nsZenMediaController {
}
}, 1000);
this.activateMediaControls(event.target.browsingContext.mediaController, event.target);
this.activateMediaControls(
event.target.browsingContext.mediaController,
event.target
);
});
window.addEventListener("DOMAudioPlaybackStopped", () => this.updateMuteState());
window.addEventListener("DOMAudioPlaybackStopped", () =>
this.updateMuteState()
);
}
onTabDiscardedOrClosed(event) {
const { linkedBrowser } = event.target;
const isCurrentBrowser = linkedBrowser?.browserId === this._currentBrowser?.browserId;
const isCurrentBrowser =
linkedBrowser?.browserId === this._currentBrowser?.browserId;
if (isCurrentBrowser) {
this.isSharing = false;
@@ -186,11 +202,26 @@ class nsZenMediaController {
shouldHide = true
) {
if (shouldForget && mediaController) {
mediaController.removeEventListener("pictureinpicturemodechange", this.onPipModeChange);
mediaController.removeEventListener("positionstatechange", this.onPositionstateChange);
mediaController.removeEventListener("playbackstatechange", this.onPlaybackstateChange);
mediaController.removeEventListener("supportedkeyschange", this.onSupportedKeysChange);
mediaController.removeEventListener("metadatachange", this.onMetadataChange);
mediaController.removeEventListener(
"pictureinpicturemodechange",
this.onPipModeChange
);
mediaController.removeEventListener(
"positionstatechange",
this.onPositionstateChange
);
mediaController.removeEventListener(
"playbackstatechange",
this.onPlaybackstateChange
);
mediaController.removeEventListener(
"supportedkeyschange",
this.onSupportedKeysChange
);
mediaController.removeEventListener(
"metadatachange",
this.onMetadataChange
);
mediaController.removeEventListener("deactivated", this.onDeactivated);
this.mediaControllersMap.delete(mediaController.id);
@@ -219,7 +250,10 @@ class nsZenMediaController {
set isSharing(value) {
if (this._currentBrowser?.browsingContext && !value) {
const webRTC = this._currentBrowser.browsingContext.currentWindowGlobal.getActor("WebRTC");
const webRTC =
this._currentBrowser.browsingContext.currentWindowGlobal.getActor(
"WebRTC"
);
webRTC.sendAsyncMessage("webrtc:UnmuteMicrophone");
webRTC.sendAsyncMessage("webrtc:UnmuteCamera");
}
@@ -281,7 +315,9 @@ class nsZenMediaController {
this.mediaControlBar.removeAttribute("hidden");
window.requestAnimationFrame(() => {
this.mediaControlBar.style.height =
this.mediaControlBar.querySelector("toolbaritem").getBoundingClientRect().height + "px";
this.mediaControlBar
.querySelector("toolbaritem")
.getBoundingClientRect().height + "px";
this.mediaControlBar.style.opacity = 0;
gZenUIManager.updateTabsToolbar();
gZenUIManager.motion.animate(
@@ -326,7 +362,8 @@ class nsZenMediaController {
}
const iconURL =
this._currentBrowser.mIconURL || `page-icon:${this._currentBrowser.currentURI.spec}`;
this._currentBrowser.mIconURL ||
`page-icon:${this._currentBrowser.currentURI.spec}`;
this.mediaFocusButton.style.listStyleImage = `url(${iconURL})`;
this.mediaTitle.textContent = metadata.title || "";
@@ -341,8 +378,11 @@ class nsZenMediaController {
this.updateMediaPosition();
for (const key of this.supportedKeys) {
const button = this.mediaControlBar.querySelector(`#zen-media-${key}-button`);
button.disabled = !this._currentMediaController.supportedKeys.includes(key);
const button = this.mediaControlBar.querySelector(
`#zen-media-${key}-button`
);
button.disabled =
!this._currentMediaController.supportedKeys.includes(key);
}
}
@@ -350,7 +390,10 @@ class nsZenMediaController {
this.updateMuteState();
this.switchController();
if (!mediaController.isActive || this._currentBrowser?.browserId === browser.browserId) {
if (
!mediaController.isActive ||
this._currentBrowser?.browserId === browser.browserId
) {
return;
}
@@ -370,21 +413,36 @@ class nsZenMediaController {
this.setupMediaControlUI(metadata, positionState);
}
mediaController.addEventListener("pictureinpicturemodechange", this.onPipModeChange);
mediaController.addEventListener("positionstatechange", this.onPositionstateChange);
mediaController.addEventListener("playbackstatechange", this.onPlaybackstateChange);
mediaController.addEventListener("supportedkeyschange", this.onSupportedKeysChange);
mediaController.addEventListener(
"pictureinpicturemodechange",
this.onPipModeChange
);
mediaController.addEventListener(
"positionstatechange",
this.onPositionstateChange
);
mediaController.addEventListener(
"playbackstatechange",
this.onPlaybackstateChange
);
mediaController.addEventListener(
"supportedkeyschange",
this.onSupportedKeysChange
);
mediaController.addEventListener("metadatachange", this.onMetadataChange);
mediaController.addEventListener("deactivated", this.onDeactivated);
}
activateMediaDeviceControls(browser) {
if (browser?.browsingContext.currentWindowGlobal.hasActivePeerConnections()) {
if (
browser?.browsingContext.currentWindowGlobal.hasActivePeerConnections()
) {
this.mediaControlBar.removeAttribute("can-pip");
this._currentBrowser = browser;
const tab = window.gBrowser.getTabForBrowser(browser);
const iconURL = browser.mIconURL || `page-icon:${browser.currentURI.spec}`;
const iconURL =
browser.mIconURL || `page-icon:${browser.currentURI.spec}`;
this.isSharing = true;
@@ -401,14 +459,16 @@ class nsZenMediaController {
for (const browser of window.gBrowser.browsers) {
const isMatch = browser.innerWindowID === windowId;
const isCurrentBrowser = this._currentBrowser?.browserId === browser.browserId;
const isCurrentBrowser =
this._currentBrowser?.browserId === browser.browserId;
const shouldShow = showCameraIndicator || showMicrophoneIndicator;
if (!isMatch) {
continue;
}
if (shouldShow && !(isCurrentBrowser && this.isSharing)) {
const webRTC = browser.browsingContext.currentWindowGlobal.getActor("WebRTC");
const webRTC =
browser.browsingContext.currentWindowGlobal.getActor("WebRTC");
webRTC.sendAsyncMessage("webrtc:UnmuteMicrophone");
webRTC.sendAsyncMessage("webrtc:UnmuteCamera");
@@ -417,9 +477,11 @@ class nsZenMediaController {
}
if (this._currentMediaController) {
this._currentMediaController.pause();
this.deinitMediaController(this._currentMediaController, true, true).then(() =>
this.activateMediaDeviceControls(browser)
);
this.deinitMediaController(
this._currentMediaController,
true,
true
).then(() => this.activateMediaDeviceControls(browser));
} else {
this.activateMediaDeviceControls(browser);
}
@@ -457,7 +519,9 @@ class nsZenMediaController {
return;
}
for (const key of this.supportedKeys) {
const button = this.mediaControlBar.querySelector(`#zen-media-${key}-button`);
const button = this.mediaControlBar.querySelector(
`#zen-media-${key}-button`
);
button.disabled = !event.target.supportedKeys.includes(key);
}
}
@@ -505,7 +569,7 @@ class nsZenMediaController {
if (!this._currentMediaController?.isPlaying || force) {
const nextController = Array.from(this.mediaControllersMap.values())
.filter(
(ctrl) =>
ctrl =>
ctrl.controller.isPlaying &&
gBrowser.selectedBrowser.browserId !== ctrl.browser.browserId &&
ctrl.controller.id !== this._currentMediaController?.id
@@ -514,13 +578,23 @@ class nsZenMediaController {
.shift();
if (nextController) {
this.deinitMediaController(this._currentMediaController, false, true).then(() => {
this.setupMediaController(nextController.controller, nextController.browser);
const elapsedTime = Math.floor((Date.now() - nextController.lastUpdated) / 1000);
this.deinitMediaController(
this._currentMediaController,
false,
true
).then(() => {
this.setupMediaController(
nextController.controller,
nextController.browser
);
const elapsedTime = Math.floor(
(Date.now() - nextController.lastUpdated) / 1000
);
this.setupMediaControlUI(nextController.controller.getMetadata(), {
position:
nextController.position + (nextController.controller.isPlaying ? elapsedTime : 0),
nextController.position +
(nextController.controller.isPlaying ? elapsedTime : 0),
duration: nextController.duration,
playbackRate: nextController.playbackRate,
});
@@ -550,9 +624,14 @@ class nsZenMediaController {
return;
}
this.mediaCurrentTime.textContent = this.formatSecondsToTime(this._currentPosition);
this.mediaDuration.textContent = this.formatSecondsToTime(this._currentDuration);
this.mediaProgressBar.value = (this._currentPosition / this._currentDuration) * 100;
this.mediaCurrentTime.textContent = this.formatSecondsToTime(
this._currentPosition
);
this.mediaDuration.textContent = this.formatSecondsToTime(
this._currentDuration
);
this.mediaProgressBar.value =
(this._currentPosition / this._currentDuration) * 100;
this._mediaUpdateInterval = setInterval(() => {
if (this._currentMediaController?.isPlaying) {
@@ -560,8 +639,11 @@ class nsZenMediaController {
if (this._currentPosition > this._currentDuration) {
this._currentPosition = this._currentDuration;
}
this.mediaCurrentTime.textContent = this.formatSecondsToTime(this._currentPosition);
this.mediaProgressBar.value = (this._currentPosition / this._currentDuration) * 100;
this.mediaCurrentTime.textContent = this.formatSecondsToTime(
this._currentPosition
);
this.mediaProgressBar.value =
(this._currentPosition / this._currentDuration) * 100;
} else {
clearInterval(this._mediaUpdateInterval);
this._mediaUpdateInterval = null;
@@ -730,7 +812,10 @@ class nsZenMediaController {
if (!this._currentBrowser) {
return;
}
this.mediaControlBar.toggleAttribute("muted", this._currentBrowser.audioMuted);
this.mediaControlBar.toggleAttribute(
"muted",
this._currentBrowser.audioMuted
);
}
updatePipButton() {
@@ -741,10 +826,11 @@ class nsZenMediaController {
return;
}
const { totalPipCount, totalPipDisabled } = PictureInPicture.getEligiblePipVideoCount(
this._currentBrowser
);
const canPip = totalPipCount === 1 || (totalPipDisabled > 0 && lazy.RESPECT_PIP_DISABLED);
const { totalPipCount, totalPipDisabled } =
PictureInPicture.getEligiblePipVideoCount(this._currentBrowser);
const canPip =
totalPipCount === 1 ||
(totalPipDisabled > 0 && lazy.RESPECT_PIP_DISABLED);
this.mediaControlBar.toggleAttribute("can-pip", canPip);
}

View File

@@ -38,7 +38,9 @@ class nsZenMods extends nsZenPreloadedFeature {
get #modsBackend() {
if (!this.#_modsBackend) {
this.#_modsBackend = Cc["@mozilla.org/zen/mods-backend;1"].getService(Ci.nsIZenModsBackend);
this.#_modsBackend = Cc["@mozilla.org/zen/mods-backend;1"].getService(
Ci.nsIZenModsBackend
);
}
return this.#_modsBackend;
}
@@ -78,7 +80,7 @@ class nsZenMods extends nsZenPreloadedFeature {
await this.#writeStylesheet(mods);
const modsWithPreferences = await Promise.all(
mods.map(async (mod) => {
mods.map(async mod => {
const preferences = await this.getModPreferences(mod);
return {
@@ -103,7 +105,7 @@ class nsZenMods extends nsZenPreloadedFeature {
}
const modsObject = await this.getMods();
const mods = Object.values(modsObject).filter(
(mod) => mod.enabled === undefined || mod.enabled
mod => mod.enabled === undefined || mod.enabled
);
// eslint-disable-next-line no-shadow
@@ -131,9 +133,13 @@ class nsZenMods extends nsZenPreloadedFeature {
}
const getProperty =
type === "checkbox" ? Services.prefs.getBoolPref : Services.prefs.getStringPref;
type === "checkbox"
? Services.prefs.getBoolPref
: Services.prefs.getStringPref;
const setProperty =
type === "checkbox" ? Services.prefs.setBoolPref : Services.prefs.setStringPref;
type === "checkbox"
? Services.prefs.setBoolPref
: Services.prefs.setStringPref;
try {
getProperty(property);
@@ -155,7 +161,9 @@ class nsZenMods extends nsZenPreloadedFeature {
setProperty(
property,
typeof defaultValue === "boolean" ? defaultValue : defaultValue.toString()
typeof defaultValue === "boolean"
? defaultValue
: defaultValue.toString()
);
}
}
@@ -175,10 +183,14 @@ class nsZenMods extends nsZenPreloadedFeature {
element.remove();
}
for (const { property } of preferences.filter(({ type }) => type !== "checkbox")) {
for (const { property } of preferences.filter(
({ type }) => type !== "checkbox"
)) {
const sanitizedProperty = property?.replaceAll(/\./g, "-");
browser.document.querySelector(":root").style.removeProperty(`--${sanitizedProperty}`);
browser.document
.querySelector(":root")
.style.removeProperty(`--${sanitizedProperty}`);
}
continue;
@@ -303,7 +315,9 @@ class nsZenMods extends nsZenPreloadedFeature {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status} for url: ${url}`);
throw new Error(
`HTTP error! status: ${response.status} for url: ${url}`
);
}
const data = await response.text();
@@ -316,14 +330,18 @@ class nsZenMods extends nsZenPreloadedFeature {
} catch (e) {
attempt++;
if (attempt >= maxRetries) {
console.error("[ZenMods]: Error downloading file after retries", url, e);
console.error(
"[ZenMods]: Error downloading file after retries",
url,
e
);
} else {
console.warn(
`[ZenMods]: Download failed (attempt ${attempt} of ${maxRetries}), retrying in ${retryDelayMs}ms...`,
url,
e
);
await new Promise((res) => setTimeout(res, retryDelayMs));
await new Promise(res => setTimeout(res, retryDelayMs));
}
}
}
@@ -409,7 +427,11 @@ class nsZenMods extends nsZenPreloadedFeature {
}
async getModPreferences(mod) {
const modPath = PathUtils.join(this.modsRootPath, mod.id, "preferences.json");
const modPath = PathUtils.join(
this.modsRootPath,
mod.id,
"preferences.json"
);
if (!(await IOUtils.exists(modPath)) || !mod.preferences) {
return [];
@@ -419,10 +441,15 @@ class nsZenMods extends nsZenPreloadedFeature {
const preferences = await IOUtils.readJSON(modPath);
return preferences.filter(({ disabledOn = [] }) => {
return !disabledOn.includes(gZenOperatingSystemCommonUtils.currentOperatingSystem);
return !disabledOn.includes(
gZenOperatingSystemCommonUtils.currentOperatingSystem
);
});
} catch (e) {
console.error(`[ZenMods]: Error reading mod preferences for ${mod.name}:`, e);
console.error(
`[ZenMods]: Error reading mod preferences for ${mod.name}:`,
e
);
return [];
}
}
@@ -443,7 +470,7 @@ class nsZenMods extends nsZenPreloadedFeature {
const mods = await this.#getEnabledMods();
const modsWithPreferences = await Promise.all(
mods.map(async (mod) => {
mods.map(async mod => {
const preferences = await this.getModPreferences(mod);
return {
@@ -475,21 +502,38 @@ class nsZenMods extends nsZenPreloadedFeature {
console.error("[ZenMods]: Error loading Zen Mods:", e);
}
Services.prefs.addObserver(this.updatePref, this.#rebuildModsStylesheet.bind(this));
Services.prefs.addObserver("zen.themes.disable-all", this.#handleDisableMods.bind(this));
Services.prefs.addObserver(
this.updatePref,
this.#rebuildModsStylesheet.bind(this)
);
Services.prefs.addObserver(
"zen.themes.disable-all",
this.#handleDisableMods.bind(this)
);
}
#setNewMilestoneIfNeeded() {
const previousMilestone = Services.prefs.getStringPref("zen.mods.milestone", "");
const previousMilestone = Services.prefs.getStringPref(
"zen.mods.milestone",
""
);
if (previousMilestone != Services.appinfo.version) {
Services.prefs.setStringPref("zen.mods.milestone", Services.appinfo.version);
Services.prefs.setStringPref(
"zen.mods.milestone",
Services.appinfo.version
);
Services.prefs.clearUserPref("zen.mods.last-update");
}
}
#shouldAutoUpdate() {
const daysBeforeUpdate = Services.prefs.getIntPref("zen.mods.auto-update-days");
const lastUpdatedSec = Services.prefs.getIntPref("zen.mods.last-update", -1);
const daysBeforeUpdate = Services.prefs.getIntPref(
"zen.mods.auto-update-days"
);
const lastUpdatedSec = Services.prefs.getIntPref(
"zen.mods.last-update",
-1
);
const nowSec = Math.floor(Date.now() / 1000);
const daysSinceUpdate = (nowSec - lastUpdatedSec) / (60 * 60 * 24);
@@ -504,7 +548,7 @@ class nsZenMods extends nsZenPreloadedFeature {
const mods = await this.getMods();
const updates = await Promise.all(
Object.values(mods).map(async (currentMod) => {
Object.values(mods).map(async currentMod => {
try {
const possibleNewModVersion = await this.requestMod(currentMod.id);
@@ -513,7 +557,10 @@ class nsZenMods extends nsZenPreloadedFeature {
}
if (
!this.#compareVersions(possibleNewModVersion.version, currentMod.version ?? "0.0.0") &&
!this.#compareVersions(
possibleNewModVersion.version,
currentMod.version ?? "0.0.0"
) &&
possibleNewModVersion.version != currentMod.version
) {
console.warn(
@@ -539,8 +586,11 @@ class nsZenMods extends nsZenPreloadedFeature {
);
await this.updateMods(mods);
Services.prefs.setIntPref("zen.mods.last-update", Math.floor(Date.now() / 1000));
return updates.filter((update) => {
Services.prefs.setIntPref(
"zen.mods.last-update",
Math.floor(Date.now() / 1000)
);
return updates.filter(update => {
return update !== null;
});
}
@@ -595,7 +645,10 @@ class nsZenMods extends nsZenPreloadedFeature {
}
triggerModsUpdate() {
Services.prefs.setBoolPref(this.updatePref, !Services.prefs.getBoolPref(this.updatePref));
Services.prefs.setBoolPref(
this.updatePref,
!Services.prefs.getBoolPref(this.updatePref)
);
}
async installMod(mod) {
@@ -603,11 +656,20 @@ class nsZenMods extends nsZenPreloadedFeature {
const modPath = PathUtils.join(this.modsRootPath, mod.id);
await IOUtils.makeDirectory(modPath, { ignoreExisting: true });
await this.#downloadUrlToFile(mod.style, PathUtils.join(modPath, "chrome.css"));
await this.#downloadUrlToFile(mod.readme, PathUtils.join(modPath, "readme.md"));
await this.#downloadUrlToFile(
mod.style,
PathUtils.join(modPath, "chrome.css")
);
await this.#downloadUrlToFile(
mod.readme,
PathUtils.join(modPath, "readme.md")
);
if (mod.preferences) {
await this.#downloadUrlToFile(mod.preferences, PathUtils.join(modPath, "preferences.json"));
await this.#downloadUrlToFile(
mod.preferences,
PathUtils.join(modPath, "preferences.json")
);
}
} catch (e) {
console.error("[ZenMods]: Error installing mod", mod.id, e);
@@ -652,7 +714,10 @@ class nsZenMods extends nsZenPreloadedFeature {
console.error(`[ZenMods]: Error parsing mod ${modId} info:`, e);
}
} else {
console.error(`[ZenMods]: Error fetching mod ${modId} info:`, data.status);
console.error(
`[ZenMods]: Error fetching mod ${modId} info:`,
data.status
);
}
return null;

View File

@@ -45,11 +45,15 @@ export class ZenModsMarketplaceChild extends JSWindowActorChild {
}
get actionButtonUninstall() {
return this.contentWindow.document.getElementById("install-theme-uninstall");
return this.contentWindow.document.getElementById(
"install-theme-uninstall"
);
}
async isThemeInstalled(themeId) {
return await this.sendQuery("ZenModsMarketplace:IsModInstalled", { themeId });
return await this.sendQuery("ZenModsMarketplace:IsModInstalled", {
themeId,
});
}
async receiveMessage(message) {
@@ -79,7 +83,9 @@ export class ZenModsMarketplaceChild extends JSWindowActorChild {
const updates = message.data.updates;
this.contentWindow.document.dispatchEvent(
new CustomEvent("ZenModsMarketplace:CheckForUpdatesFinished", { detail: { updates } })
new CustomEvent("ZenModsMarketplace:CheckForUpdatesFinished", {
detail: { updates },
})
);
break;
@@ -88,15 +94,21 @@ export class ZenModsMarketplaceChild extends JSWindowActorChild {
}
injectMarketplaceAPI() {
Cu.exportFunction(this.handleModInstallationEvent.bind(this), this.contentWindow, {
defineAs: "ZenInstallMod",
});
Cu.exportFunction(
this.handleModInstallationEvent.bind(this),
this.contentWindow,
{
defineAs: "ZenInstallMod",
}
);
}
async addButtons() {
const actionButton = this.actionButton;
const actionButtonUninstall = this.actionButtonUninstall;
const errorMessage = this.contentWindow.document.getElementById("install-theme-error");
const errorMessage = this.contentWindow.document.getElementById(
"install-theme-error"
);
if (!actionButton || !actionButtonUninstall) {
return;
}
@@ -110,8 +122,14 @@ export class ZenModsMarketplaceChild extends JSWindowActorChild {
actionButton.classList.remove("hidden");
}
actionButton.addEventListener("click", this.handleModInstallationEvent.bind(this));
actionButtonUninstall.addEventListener("click", this.handleModUninstallEvent.bind(this));
actionButton.addEventListener(
"click",
this.handleModInstallationEvent.bind(this)
);
actionButtonUninstall.addEventListener(
"click",
this.handleModUninstallEvent.bind(this)
);
}
async handleModUninstallEvent(event) {

View File

@@ -46,7 +46,9 @@ export class ZenModsMarketplaceParent extends JSWindowActorParent {
}
case "ZenModsMarketplace:CheckForUpdates": {
const updates = await this.modsManager.checkForModsUpdates();
this.sendAsyncMessage("ZenModsMarketplace:CheckForUpdatesFinished", { updates });
this.sendAsyncMessage("ZenModsMarketplace:CheckForUpdatesFinished", {
updates,
});
break;
}

View File

@@ -8,7 +8,8 @@ import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
ZenLiveFoldersManager: "resource:///modules/zen/ZenLiveFoldersManager.sys.mjs",
ZenLiveFoldersManager:
"resource:///modules/zen/ZenLiveFoldersManager.sys.mjs",
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
SessionStore: "resource:///modules/sessionstore/SessionStore.sys.mjs",
SessionStartup: "resource:///modules/sessionstore/SessionStartup.sys.mjs",
@@ -17,7 +18,12 @@ ChromeUtils.defineESModuleGetters(lazy, {
DeferredTask: "resource://gre/modules/DeferredTask.sys.mjs",
});
XPCOMUtils.defineLazyPreferenceGetter(lazy, "gShouldLog", "zen.session-store.log", true);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"gShouldLog",
"zen.session-store.log",
true
);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"gMaxSessionBackups",
@@ -31,7 +37,10 @@ XPCOMUtils.defineLazyPreferenceGetter(
3
);
const SHOULD_BACKUP_FILE = Services.prefs.getBoolPref("zen.session-store.backup-file", true);
const SHOULD_BACKUP_FILE = Services.prefs.getBoolPref(
"zen.session-store.backup-file",
true
);
const FILE_NAME = "zen-sessions.jsonlz4";
const LAST_BUILD_ID_PREF = "zen.session-store.last-build-id";
@@ -120,7 +129,7 @@ export class nsZenSessionManager {
try {
let files = await IOUtils.getChildren(this.#backupFolderPath);
files = files
.filter((file) => file.startsWith(prefix))
.filter(file => file.startsWith(prefix))
.sort()
.reverse();
backupFiles.push(files[0]);
@@ -142,9 +151,11 @@ export class nsZenSessionManager {
);
const db = await PlacesUtils.promiseDBConnection();
let data = {};
let rows = await db.execute("SELECT * FROM zen_workspaces ORDER BY created_at ASC");
let rows = await db.execute(
"SELECT * FROM zen_workspaces ORDER BY created_at ASC"
);
try {
data.spaces = rows.map((row) => ({
data.spaces = rows.map(row => ({
uuid: row.getResultByName("uuid"),
name: row.getResultByName("name"),
icon: row.getResultByName("icon"),
@@ -162,11 +173,14 @@ export class nsZenSessionManager {
}));
} catch (e) {
/* ignore errors reading spaces data, as it is not critical and we want to migrate even if we fail to read it */
console.error("Failed to read spaces data from database during migration", e);
console.error(
"Failed to read spaces data from database during migration",
e
);
}
try {
rows = await db.execute("SELECT * FROM zen_pins ORDER BY position ASC");
data.pins = rows.map((row) => ({
data.pins = rows.map(row => ({
uuid: row.getResultByName("uuid"),
title: row.getResultByName("title"),
url: row.getResultByName("url"),
@@ -178,11 +192,16 @@ export class nsZenSessionManager {
parentUuid: row.getResultByName("folder_parent_uuid"),
editedTitle: Boolean(row.getResultByName("edited_title")),
folderIcon: row.getResultByName("folder_icon"),
isFolderCollapsed: Boolean(row.getResultByName("is_folder_collapsed")),
isFolderCollapsed: Boolean(
row.getResultByName("is_folder_collapsed")
),
}));
} catch (e) {
/* ignore errors reading pins data, as it is not critical and we want to migrate even if we fail to read it */
console.error("Failed to read pins data from database during migration", e);
console.error(
"Failed to read pins data from database during migration",
e
);
}
try {
data.recoveryData = await IOUtils.readJSON(
@@ -263,14 +282,19 @@ export class nsZenSessionManager {
}
this.#sidebar = this._dataFromFile || {};
if (!this.#sidebar.spaces?.length && !this._shouldRunMigration) {
this.log("No spaces data found in session file, running migration", this.#sidebar);
this.log(
"No spaces data found in session file, running migration",
this.#sidebar
);
// If we have no spaces data, we should run migration
// to restore them from the database. Note we also do a
// check if we already planned to run migration for optimization.
this._shouldRunMigration = true;
await this.#getDataFromDBForMigration();
}
if (Services.prefs.getBoolPref("zen.session-store.log-tab-entries", false)) {
if (
Services.prefs.getBoolPref("zen.session-store.log-tab-entries", false)
) {
for (const tab of this.#sidebar.tabs || []) {
this.log("Tab entry in session file:", tab);
}
@@ -285,15 +309,19 @@ export class nsZenSessionManager {
if (buildIdChanged) {
// If the build ID has changed since the last session, it means the user has updated the app,
// so we should not remove the unpinned tabs as they might want to keep them after the update.
this.log("Build ID has changed since last session, not restoring only pinned tabs", {
buildId,
lastBuildId,
});
this.log(
"Build ID has changed since last session, not restoring only pinned tabs",
{
buildId,
lastBuildId,
}
);
Services.prefs.setStringPref(LAST_BUILD_ID_PREF, buildId);
return false;
}
return (
Services.prefs.getIntPref("browser.startup.page", 1) !== BROWSER_STARTUP_RESUME_SESSION ||
Services.prefs.getIntPref("browser.startup.page", 1) !==
BROWSER_STARTUP_RESUME_SESSION ||
lazy.PrivateBrowsingUtils.permanentPrivateBrowsing
);
}
@@ -325,7 +353,7 @@ export class nsZenSessionManager {
this.log("Window sync disabled, restoring only pinned tabs");
for (let i = 0; i < initialState.windows.length; i++) {
let winData = initialState.windows[i];
winData.tabs = (winData.tabs || []).filter((tab) => tab.pinned);
winData.tabs = (winData.tabs || []).filter(tab => tab.pinned);
}
}
return initialState;
@@ -335,7 +363,7 @@ export class nsZenSessionManager {
true
);
if (initialState?.windows?.length && !allowRestoreUnsynced) {
initialState.windows = initialState.windows.filter((win) => {
initialState.windows = initialState.windows.filter(win => {
if (win.isZenUnsynced) {
this.log("Skipping unsynced window during restore");
}
@@ -348,7 +376,11 @@ export class nsZenSessionManager {
// This would happen on first run after having a single private window
// open when quitting the app, for example.
let normalWindowsExist = initialState?.windows?.some(
(win) => !win.isPrivate && !win.isPopup && !win.isTaskbarTab && !win.isZenUnsynced
win =>
!win.isPrivate &&
!win.isPopup &&
!win.isTaskbarTab &&
!win.isZenUnsynced
);
if (!initialState?.windows?.length || !normalWindowsExist) {
this.log("No windows found in initial state, creating an empty one");
@@ -376,20 +408,31 @@ export class nsZenSessionManager {
}
// When we don't have browser.startup.page set to resume session,
// we only want to restore the pinned tabs into the new windows.
if (this.#shouldRestoreOnlyPinned && !this.#shouldRestoreFromCrash && this.#sidebar?.tabs) {
if (
this.#shouldRestoreOnlyPinned &&
!this.#shouldRestoreFromCrash &&
this.#sidebar?.tabs
) {
this.log("Restoring only pinned tabs into windows");
const sidebar = this.#sidebar;
sidebar.tabs = (sidebar.tabs || []).filter((tab) => tab.pinned);
sidebar.tabs = (sidebar.tabs || []).filter(tab => tab.pinned);
this.#sidebar = sidebar;
}
// Restore all windows with the same sidebar object, this will
// guarantee that all tabs, groups, folders and split view data
// are properly synced across all windows.
if (!this._shouldRunMigration) {
this.log(`Restoring Zen session data into ${initialState.windows?.length || 0} windows`);
this.log(
`Restoring Zen session data into ${initialState.windows?.length || 0} windows`
);
for (let i = 0; i < initialState.windows.length; i++) {
let winData = initialState.windows[i];
if (winData.isZenUnsynced || winData.isPrivate || winData.isPopup || winData.isTaskbarTab) {
if (
winData.isZenUnsynced ||
winData.isPrivate ||
winData.isPopup ||
winData.isTaskbarTab
) {
continue;
}
this.#restoreWindowData(winData);
@@ -441,7 +484,9 @@ export class nsZenSessionManager {
!initialState?.windows?.length &&
(initialState?.lastSessionState || initialState?.deferredInitialState)
) {
initialState = { ...(initialState.lastSessionState || initialState.deferredInitialState) };
initialState = {
...(initialState.lastSessionState || initialState.deferredInitialState),
};
}
// There might be cases where there are no windows in the
// initial state, for example if the user had 'restore previous
@@ -449,7 +494,7 @@ export class nsZenSessionManager {
// to restore the last closed normal window.
if (!initialState?.windows?.length) {
let normalClosedWindow = initialState?._closedWindows?.find(
(win) => !win.isPopup && !win.isTaskbarTab && !win.isPrivate
win => !win.isPopup && !win.isTaskbarTab && !win.isPrivate
);
if (normalClosedWindow) {
initialState.windows = [Cu.cloneInto(normalClosedWindow, {})];
@@ -466,15 +511,21 @@ export class nsZenSessionManager {
}
for (const winData of initialState?.windows || []) {
winData.spaces =
(winData.spaces?.length ? winData.spaces : this._migrationData?.spaces) || [];
(winData.spaces?.length
? winData.spaces
: this._migrationData?.spaces) || [];
if (winData.tabs) {
for (const tabData of winData.tabs) {
let storeId = tabData.zenSyncId || tabData.zenPinnedId;
const pinData = this._migrationData?.pins?.find((pin) => pin.uuid === storeId);
const pinData = this._migrationData?.pins?.find(
pin => pin.uuid === storeId
);
// We need to migrate the static label from the pin data as this information
// was not stored in the session file before.
if (pinData) {
tabData.zenStaticLabel = pinData.editedTitle ? pinData.title : undefined;
tabData.zenStaticLabel = pinData.editedTitle
? pinData.title
: undefined;
}
}
}
@@ -497,8 +548,8 @@ export class nsZenSessionManager {
* @param {object} aWindow - The window data object to filter.
*/
#filterUnpinnedTabs(aWindow) {
aWindow.tabs = aWindow.tabs.filter((tab) => tab.pinned);
aWindow.groups = aWindow.groups?.filter((group) => group.pinned);
aWindow.tabs = aWindow.tabs.filter(tab => tab.pinned);
aWindow.groups = aWindow.groups?.filter(group => group.pinned);
}
/**
@@ -508,7 +559,9 @@ export class nsZenSessionManager {
* @returns {boolean} True if the window is saveable, false otherwise.
*/
#isWindowSaveable(aWinData) {
return !aWinData.isPopup && !aWinData.isTaskbarTab && !aWinData.isZenUnsynced;
return (
!aWinData.isPopup && !aWinData.isTaskbarTab && !aWinData.isZenUnsynced
);
}
/**
@@ -521,17 +574,19 @@ export class nsZenSessionManager {
*/
saveState(state, soon = false) {
let windows = state?.windows || [];
windows = windows.filter((win) => this.#isWindowSaveable(win));
windows = windows.filter(win => this.#isWindowSaveable(win));
if (!windows.length) {
// Don't save (or even collect) anything in permanent private
// browsing mode. We also don't want to save if there are no windows.
return;
}
const cleanPath = PathUtils.join(this.#backupFolderPath, "clean.jsonlz4");
IOUtils.copy(this.#storeFilePath, cleanPath, { recursive: true }).catch(() => {
/* ignore errors creating clean backup, as it is not critical and
* we want to save the session even if we fail to create it */
});
IOUtils.copy(this.#storeFilePath, cleanPath, { recursive: true }).catch(
() => {
/* ignore errors creating clean backup, as it is not critical and
* we want to save the session even if we fail to create it */
}
);
this.#collectWindowData(windows);
// This would save the data to disk asynchronously or when quitting the app.
let sidebar = this.#sidebar;
@@ -591,19 +646,24 @@ export class nsZenSessionManager {
const todayFilePath = PathUtils.join(backupFolder, todayFileName);
const sessionFilePath = this.#file.path;
this.log(`Backing up session file to ${todayFileName}`);
await IOUtils.copy(sessionFilePath, todayFilePath, { noOverwrite: false });
await IOUtils.copy(sessionFilePath, todayFilePath, {
noOverwrite: false,
});
// Now we need to check if we have exceeded the maximum
// number of backups allowed, and delete the oldest ones
// if needed.
let prefix = PathUtils.join(backupFolder, "zen-sessions-");
let files = await IOUtils.getChildren(backupFolder);
files = files.filter((file) => file.startsWith(prefix)).sort();
files = files.filter(file => file.startsWith(prefix)).sort();
for (let i = 0; i < files.length - lazy.gMaxSessionBackups; i++) {
this.log(`Deleting old backup file ${files[i]}`);
await IOUtils.remove(files[i]);
}
} catch (e) {
console.error("ZenSessionManager: Failed to create session file backups", e);
console.error(
"ZenSessionManager: Failed to create session file backups",
e
);
}
}
@@ -618,7 +678,12 @@ export class nsZenSessionManager {
// We only want to save the *last* normal window that is closed.
// If its not the last window, we can still update the sidebar object
// based on other open windows.
if (aWinData.isPopup || aWinData.isTaskbarTab || aWinData.isZenUnsynced || !isLastWindow) {
if (
aWinData.isPopup ||
aWinData.isTaskbarTab ||
aWinData.isZenUnsynced ||
!isLastWindow
) {
return;
}
this.log("Saving closed window session data into Zen session store");
@@ -652,7 +717,7 @@ export class nsZenSessionManager {
* @returns {Array} The filtered array of tab data objects.
*/
#filterUnusedTabs(tabs) {
return tabs.filter((tab) => {
return tabs.filter(tab => {
// We need to ignore empty tabs with no group association
// as they are not useful to restore.
return !(tab.zenIsEmpty && !tab.groupId);
@@ -673,13 +738,18 @@ export class nsZenSessionManager {
// state when multiple windows are open. Note that if we a tab without
// this flag set in any other window, we just add it anyway.
for (const tabData of window.tabs || []) {
if (!tabIdRelationMap.has(tabData.zenSyncId) || tabData._zenIsActiveTab) {
if (
!tabIdRelationMap.has(tabData.zenSyncId) ||
tabData._zenIsActiveTab
) {
tabIdRelationMap.set(tabData.zenSyncId, tabData);
}
}
}
sidebarData.tabs = this.#filterUnusedTabs(Array.from(tabIdRelationMap.values()));
sidebarData.tabs = this.#filterUnusedTabs(
Array.from(tabIdRelationMap.values())
);
let firstWindow = aStateWindows[0];
sidebarData.folders = firstWindow.folders;
@@ -704,10 +774,10 @@ export class nsZenSessionManager {
// tabs in the window data and keep the pinned tabs from the window data,
// as they should be the same as the ones in the sidebar.
if (lazy.gSyncOnlyPinnedTabs) {
let pinnedTabs = (sidebar.tabs || []).filter((tab) => tab.pinned);
let pinnedTabs = (sidebar.tabs || []).filter(tab => tab.pinned);
let unpinedWindowTabs = [];
if (!this.#shouldRestoreOnlyPinned) {
unpinedWindowTabs = (aWindowData.tabs || []).filter((tab) => !tab.pinned);
unpinedWindowTabs = (aWindowData.tabs || []).filter(tab => !tab.pinned);
}
aWindowData.tabs = [...pinnedTabs, ...unpinedWindowTabs];
@@ -719,7 +789,10 @@ export class nsZenSessionManager {
];
// Same thing with groups, we restore all the groups from the sidebar, if they don't have any
// existing tabs in the window, they should be a no-op.
aWindowData.groups = [...(sidebar.groups || []), ...(aWindowData.groups || [])];
aWindowData.groups = [
...(sidebar.groups || []),
...(aWindowData.groups || []),
];
} else {
aWindowData.tabs = sidebar.tabs || [];
aWindowData.splitViewData = sidebar.splitViewData;
@@ -755,11 +828,16 @@ export class nsZenSessionManager {
this.log("Restoring new window with Zen session data");
const state = lazy.SessionStore.getCurrentState(true);
const windows = (state.windows || []).filter(
(win) => !win.isPrivate && !win.isPopup && !win.isTaskbarTab && !win.isZenUnsynced
win =>
!win.isPrivate &&
!win.isPopup &&
!win.isTaskbarTab &&
!win.isZenUnsynced
);
let windowToClone = windows[0] || {};
let newWindow = Cu.cloneInto(windowToClone, {});
let shouldRestoreOnlyPinned = !lazy.gWindowSyncEnabled || lazy.gSyncOnlyPinnedTabs;
let shouldRestoreOnlyPinned =
!lazy.gWindowSyncEnabled || lazy.gSyncOnlyPinnedTabs;
if (windows.length < 2) {
// We only want to restore the sidebar object if we found
// only one normal window to clone from (which is the one

View File

@@ -19,16 +19,29 @@ ChromeUtils.defineESModuleGetters(lazy, {
RunState: "resource:///modules/sessionstore/RunState.sys.mjs",
});
XPCOMUtils.defineLazyPreferenceGetter(lazy, "gWindowSyncEnabled", "zen.window-sync.enabled", true);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"gWindowSyncEnabled",
"zen.window-sync.enabled",
true
);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"gSyncOnlyPinnedTabs",
"zen.window-sync.sync-only-pinned-tabs",
true
);
XPCOMUtils.defineLazyPreferenceGetter(lazy, "gShouldLog", "zen.window-sync.log", true);
XPCOMUtils.defineLazyPreferenceGetter(
lazy,
"gShouldLog",
"zen.window-sync.log",
true
);
const OBSERVING = ["browser-window-before-show", "sessionstore-windows-restored"];
const OBSERVING = [
"browser-window-before-show",
"sessionstore-windows-restored",
];
const INSTANT_EVENTS = ["SSWindowClosing", "TabSelect", "focus"];
const UNSYNCED_WINDOW_EVENTS = ["TabOpen"];
const EVENTS = [
@@ -131,7 +144,11 @@ class nsZenWindowSync {
#browserWindows = {
*[Symbol.iterator]() {
for (let window of lazy.BrowserWindowTracker.orderedWindows) {
if (window.__SSi && !window.closed && !window.gZenWorkspaces?.privateWindowOrDisabled) {
if (
window.__SSi &&
!window.closed &&
!window.gZenWorkspaces?.privateWindowOrDisabled
) {
yield window;
}
}
@@ -223,8 +240,13 @@ class nsZenWindowSync {
aWindow.arguments.length > 1 &&
!!this.#browserWindowsList.length))
) {
this.log("Not syncing new window due to unsynced argument or existing synced windows");
aWindow.document.documentElement.setAttribute("zen-unsynced-window", "true");
this.log(
"Not syncing new window due to unsynced argument or existing synced windows"
);
aWindow.document.documentElement.setAttribute(
"zen-unsynced-window",
"true"
);
for (let eventName of UNSYNCED_WINDOW_EVENTS) {
aWindow.addEventListener(eventName, this, true);
}
@@ -244,7 +266,7 @@ class nsZenWindowSync {
// assign one and sync it to other windows.
// This should only happen really when updating from an older version
// that didn't have this feature.
await this.#runOnAllWindowsAsync(null, async (aWindow) => {
await this.#runOnAllWindowsAsync(null, async aWindow => {
const { gZenWorkspaces } = aWindow;
this.#onWindowBeforeShow(aWindow);
await gZenWorkspaces.promiseInitialized;
@@ -255,7 +277,10 @@ class nsZenWindowSync {
if (tab.pinned && !tab._zenPinnedInitialState) {
await this.setPinnedTabState(tab);
}
if (!lazy.gWindowSyncEnabled || (lazy.gSyncOnlyPinnedTabs && !tab.pinned)) {
if (
!lazy.gWindowSyncEnabled ||
(lazy.gSyncOnlyPinnedTabs && !tab.pinned)
) {
tab._zenContentsVisible = true;
}
}
@@ -331,14 +356,20 @@ class nsZenWindowSync {
) {
return;
}
if (!lazy.gWindowSyncEnabled && !UNSYNCED_WINDOW_EVENTS.includes(aEvent.type)) {
if (
!lazy.gWindowSyncEnabled &&
!UNSYNCED_WINDOW_EVENTS.includes(aEvent.type)
) {
return;
}
if (INSTANT_EVENTS.includes(aEvent.type)) {
this.#handleNextEventInternal(aEvent);
return;
}
if (this.#eventHandlingContext.window && this.#eventHandlingContext.window !== window) {
if (
this.#eventHandlingContext.window &&
this.#eventHandlingContext.window !== window
) {
// We're already handling an event for another window.
// To avoid re-entrancy issues, we skip this event.
return;
@@ -347,7 +378,7 @@ class nsZenWindowSync {
this.#eventHandlingContext.eventCount++;
this.#eventHandlingContext.window = window;
let resolveNewPromise;
this.#eventHandlingContext.lastHandlerPromise = new Promise((resolve) => {
this.#eventHandlingContext.lastHandlerPromise = new Promise(resolve => {
resolveNewPromise = resolve;
});
// Wait for the last handler to finish before processing the next event.
@@ -433,7 +464,10 @@ class nsZenWindowSync {
*/
#maybeSyncAttributeChange(aOriginalItem, aTargetItem, aAttributeName) {
if (aOriginalItem.hasAttribute(aAttributeName)) {
aTargetItem.setAttribute(aAttributeName, aOriginalItem.getAttribute(aAttributeName));
aTargetItem.setAttribute(
aAttributeName,
aOriginalItem.getAttribute(aAttributeName)
);
} else {
aTargetItem.removeAttribute(aAttributeName);
}
@@ -475,12 +509,24 @@ class nsZenWindowSync {
}
}
if (flags & SYNC_FLAG_MOVE && !aTargetItem.hasAttribute("zen-empty-tab")) {
this.#maybeSyncAttributeChange(aOriginalItem, aTargetItem, "zen-workspace-id");
this.#maybeSyncAttributeChange(
aOriginalItem,
aTargetItem,
"zen-workspace-id"
);
this.#syncItemPosition(aOriginalItem, aTargetItem, aWindow);
}
if (aOriginalItem.hasAttribute("zen-live-folder-item-id")) {
this.#maybeSyncAttributeChange(aOriginalItem, aTargetItem, "zen-live-folder-item-id");
this.#maybeSyncAttributeChange(aOriginalItem, aTargetItem, "zen-show-sublabel");
this.#maybeSyncAttributeChange(
aOriginalItem,
aTargetItem,
"zen-live-folder-item-id"
);
this.#maybeSyncAttributeChange(
aOriginalItem,
aTargetItem,
"zen-show-sublabel"
);
this.#syncTabSubtitle(aWindow, aOriginalItem, aTargetItem);
} else if (aTargetItem.hasAttribute("zen-live-folder-item-id")) {
aTargetItem.removeAttribute("zen-live-folder-item-id");
@@ -527,7 +573,10 @@ class nsZenWindowSync {
if (originalIsEssential) {
gZenPinnedTabManager.addToEssentials(aTargetItem);
} else {
gZenPinnedTabManager.removeEssentials(aTargetItem, /* unpin= */ !targetIsPinned);
gZenPinnedTabManager.removeEssentials(
aTargetItem,
/* unpin= */ !targetIsPinned
);
}
} else if (originalIsPinned !== targetIsPinned) {
if (originalIsPinned) {
@@ -556,7 +605,12 @@ class nsZenWindowSync {
* @param {boolean} options.isEssential - Indicates if the item is essential.
* @param {boolean} options.isPinned - Indicates if the item is pinned.
*/
#moveItemToMatchOriginal(aOriginalItem, aTargetItem, aWindow, { isEssential, isPinned }) {
#moveItemToMatchOriginal(
aOriginalItem,
aTargetItem,
aWindow,
{ isEssential, isPinned }
) {
const { gBrowser, gZenWorkspaces } = aWindow;
let originalSibling = aOriginalItem.previousElementSibling;
if (originalSibling?.classList.contains("space-fake-collapsible-start")) {
@@ -564,9 +618,13 @@ class nsZenWindowSync {
originalSibling = originalSibling.previousElementSibling;
}
let isFirstTab = true;
if (gBrowser.isTabGroup(originalSibling) || gBrowser.isTab(originalSibling)) {
if (
gBrowser.isTabGroup(originalSibling) ||
gBrowser.isTab(originalSibling)
) {
isFirstTab =
!originalSibling.hasAttribute("id") || originalSibling.hasAttribute("zen-empty-tab");
!originalSibling.hasAttribute("id") ||
originalSibling.hasAttribute("zen-empty-tab");
}
gBrowser.zenHandleTabMove(aTargetItem, () => {
@@ -574,7 +632,10 @@ class nsZenWindowSync {
let container;
const parentGroup = aOriginalItem.group;
if (parentGroup?.hasAttribute("id")) {
container = this.getItemFromWindow(aWindow, parentGroup.getAttribute("id"));
container = this.getItemFromWindow(
aWindow,
parentGroup.getAttribute("id")
);
if (container) {
if (container?.tabs?.length) {
// First tab in folders is the empty tab placeholder.
@@ -622,8 +683,13 @@ class nsZenWindowSync {
*/
#syncItemForAllWindows(aItem, flags = 0) {
const window = aItem.ownerGlobal;
this.#runOnAllWindows(window, (win) => {
this.#syncItemWithOriginal(aItem, this.getItemFromWindow(win, aItem.id), win, flags);
this.#runOnAllWindows(window, win => {
this.#syncItemWithOriginal(
aItem,
this.getItemFromWindow(win, aItem.id),
win,
flags
);
});
}
@@ -678,7 +744,12 @@ class nsZenWindowSync {
// Restore the listeners for the swapped in tab.
if (!onClose && filter) {
tabListener = new otherTabBrowser.zenTabProgressListener(aTab, otherBrowser, true, false);
tabListener = new otherTabBrowser.zenTabProgressListener(
aTab,
otherBrowser,
true,
false
);
otherTabBrowser._tabListeners.set(aTab, tabListener);
const notifyAll = Ci.nsIWebProgress.NOTIFY_ALL;
@@ -702,7 +773,10 @@ class nsZenWindowSync {
return true;
}
// Can't swap between chrome and content processes.
if (aOurTab.linkedBrowser.isRemoteBrowser != aOtherTab.linkedBrowser.isRemoteBrowser) {
if (
aOurTab.linkedBrowser.isRemoteBrowser !=
aOtherTab.linkedBrowser.isRemoteBrowser
) {
return false;
}
return true;
@@ -717,7 +791,11 @@ class nsZenWindowSync {
* @param {boolean} options.focus - Indicates if the tab should be focused after the swap.
* @param {boolean} options.onClose - Indicates if the swap is done during a tab close operation.
*/
#swapBrowserDocShellsInner(aOurTab, aOtherTab, { focus = true, onClose = false } = {}) {
#swapBrowserDocShellsInner(
aOurTab,
aOtherTab,
{ focus = true, onClose = false } = {}
) {
// Can't swap between chrome and content processes.
if (!this.#canSwapBrowsers(aOurTab, aOtherTab)) {
this.log(
@@ -732,7 +810,11 @@ class nsZenWindowSync {
aOtherTab,
() => {
this.log(`Swapping docshells between windows for tab ${aOurTab.id}`);
aOurTab.ownerGlobal.gBrowser.swapBrowsersAndCloseOther(aOurTab, aOtherTab, false);
aOurTab.ownerGlobal.gBrowser.swapBrowsersAndCloseOther(
aOurTab,
aOtherTab,
false
);
// Swap permanent keys
if (!onClose) {
@@ -764,16 +846,25 @@ class nsZenWindowSync {
(aOtherTab.linkedBrowser?.currentURI.spec !== "about:blank" ||
aOtherTab.hasAttribute("busy"))
) {
this.log(`Loading about:blank in our tab ${aOtherTab.id} before swap`);
this.log(
`Loading about:blank in our tab ${aOtherTab.id} before swap`
);
aOtherTab.linkedBrowser.loadURI(Services.io.newURI("about:blank"), {
triggeringPrincipal: Services.scriptSecurityManager.getSystemPrincipal(),
triggeringPrincipal:
Services.scriptSecurityManager.getSystemPrincipal(),
loadFlags: Ci.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY,
});
}
},
onClose
);
const kAttributesToRemove = ["muted", "soundplaying", "sharing", "pictureinpicture", "busy"];
const kAttributesToRemove = [
"muted",
"soundplaying",
"sharing",
"pictureinpicture",
"busy",
];
// swapBrowsersAndCloseOther already takes care of transferring attributes like 'muted',
// but we need to manually remove some attributes from the other tab.
for (let attr of kAttributesToRemove) {
@@ -799,16 +890,17 @@ class nsZenWindowSync {
const ourBrowser = aOurTab.linkedBrowser;
const otherBrowser = aOtherTab.linkedBrowser;
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve) => {
return new Promise(async resolve => {
if (callback) {
const browserBlob = await aOtherTab.ownerGlobal.PageThumbs.captureToBlob(
aOtherTab.linkedBrowser,
{
fullScale: true,
fullViewport: true,
backgroundColor: "transparent",
}
);
const browserBlob =
await aOtherTab.ownerGlobal.PageThumbs.captureToBlob(
aOtherTab.linkedBrowser,
{
fullScale: true,
fullViewport: true,
backgroundColor: "transparent",
}
);
let mySrc = await new Promise((r, re) => {
const reader = new FileReader();
@@ -851,7 +943,7 @@ class nsZenWindowSync {
const img = doc.createElement("img");
img.className = "zen-pseudo-browser-image";
img.src = aSrc;
let promise = new Promise((resolve) => {
let promise = new Promise(resolve => {
if (img.complete) {
resolve();
return;
@@ -874,9 +966,11 @@ class nsZenWindowSync {
* @param {object} aBrowser - The browser element to remove the pseudo image for.
*/
#maybeRemovePseudoImageForBrowser(aBrowser) {
const elements = aBrowser.parentNode?.querySelectorAll(".zen-pseudo-browser-image");
const elements = aBrowser.parentNode?.querySelectorAll(
".zen-pseudo-browser-image"
);
if (elements) {
elements.forEach((element) => element.remove());
elements.forEach(element => element.remove());
}
}
@@ -889,8 +983,12 @@ class nsZenWindowSync {
* @param {Function} filter - A function to filter the tabs.
* @returns {object | null} The active tab from other windows if found, otherwise null.
*/
#getActiveTabFromOtherWindows(aWindow, aTabId, filter = (tab) => tab?._zenContentsVisible) {
return this.#runOnAllWindows(aWindow, (win) => {
#getActiveTabFromOtherWindows(
aWindow,
aTabId,
filter = tab => tab?._zenContentsVisible
) {
return this.#runOnAllWindows(aWindow, win => {
const tab = this.getItemFromWindow(win, aTabId);
if (filter(tab)) {
return tab;
@@ -905,13 +1003,16 @@ class nsZenWindowSync {
* @param {Window} aWindow - The window to move active tabs from.
*/
#moveAllActiveTabsToOtherWindowsForClose(aWindow) {
const mostRecentWindow = this.#browserWindowsList.find((win) => win !== aWindow);
const mostRecentWindow = this.#browserWindowsList.find(
win => win !== aWindow
);
if (!mostRecentWindow || !aWindow.gZenWorkspaces) {
return;
}
const activeTabsOnClosedWindow = aWindow.gZenWorkspaces.allStoredTabs.filter(
(tab) => tab._zenContentsVisible
);
const activeTabsOnClosedWindow =
aWindow.gZenWorkspaces.allStoredTabs.filter(
tab => tab._zenContentsVisible
);
for (let tab of activeTabsOnClosedWindow) {
const targetTab = this.getItemFromWindow(mostRecentWindow, tab.id);
if (targetTab) {
@@ -925,7 +1026,10 @@ class nsZenWindowSync {
focus: targetTab.selected,
onClose: true,
});
this.#swapedTabsEntriesForWC.set(tab.linkedBrowser.permanentKey, targetTab);
this.#swapedTabsEntriesForWC.set(
tab.linkedBrowser.permanentKey,
targetTab
);
// We can animate later, whats important is to always stay on the same
// process and avoid async operations here to avoid the closed window
// being unloaded before the swap is done.
@@ -942,16 +1046,24 @@ class nsZenWindowSync {
*/
async #onTabSwitchOrWindowFocus(aWindow, aPreviousTab = null) {
let activeBrowsers = aWindow.gBrowser.selectedBrowsers;
let activeTabs = activeBrowsers.map((browser) => aWindow.gBrowser.getTabForBrowser(browser));
let activeTabs = activeBrowsers.map(browser =>
aWindow.gBrowser.getTabForBrowser(browser)
);
// Ignore previous tabs that are still "active". These scenarios could happen for example,
// when selecting on a split view tab that was already active.
if (aPreviousTab?._zenContentsVisible && !activeTabs.includes(aPreviousTab)) {
if (
aPreviousTab?._zenContentsVisible &&
!activeTabs.includes(aPreviousTab)
) {
let tabsToSwap = aPreviousTab.group?.hasAttribute("split-view-group")
? aPreviousTab.group.tabs
: [aPreviousTab];
for (const tab of tabsToSwap) {
const otherTabToShow = this.#getActiveTabFromOtherWindows(aWindow, tab.id, (t) =>
t?.splitView ? t.group.tabs.some((st) => st.selected) : t?.selected
const otherTabToShow = this.#getActiveTabFromOtherWindows(
aWindow,
tab.id,
t =>
t?.splitView ? t.group.tabs.some(st => st.selected) : t?.selected
);
if (otherTabToShow) {
otherTabToShow._zenContentsVisible = true;
@@ -962,14 +1074,22 @@ class nsZenWindowSync {
}
let promises = [];
for (const selectedTab of activeTabs) {
if (selectedTab._zenContentsVisible || selectedTab.hasAttribute("zen-empty-tab")) {
if (
selectedTab._zenContentsVisible ||
selectedTab.hasAttribute("zen-empty-tab")
) {
continue;
}
const otherSelectedTab = this.#getActiveTabFromOtherWindows(aWindow, selectedTab.id);
const otherSelectedTab = this.#getActiveTabFromOtherWindows(
aWindow,
selectedTab.id
);
selectedTab._zenContentsVisible = true;
if (otherSelectedTab) {
delete otherSelectedTab._zenContentsVisible;
promises.push(this.#swapBrowserDocShellsAsync(selectedTab, otherSelectedTab));
promises.push(
this.#swapBrowserDocShellsAsync(selectedTab, otherSelectedTab)
);
}
}
await Promise.all(promises);
@@ -1000,7 +1120,9 @@ class nsZenWindowSync {
if (aTab.linkedBrowser) {
cachedState = lazy.TabStateCache.get(aTab.linkedBrowser.permanentKey);
}
return cachedState?.history?.entries ? Cu.cloneInto(cachedState.history, {}) : { entries: [] };
return cachedState?.history?.entries
? Cu.cloneInto(cachedState.history, {})
: { entries: [] };
}
/**
@@ -1028,7 +1150,8 @@ class nsZenWindowSync {
return this.#maybeFlushTabState(aTab).finally(() => {
this.log(`Setting pinned initial state for tab ${aTab.id}`);
let { entries, index } = this.#getTabEntriesFromCache(aTab);
let image = aTab.getAttribute("image") || aTab.ownerGlobal.gBrowser.getIcon(aTab);
let image =
aTab.getAttribute("image") || aTab.ownerGlobal.gBrowser.getIcon(aTab);
let activeIndex = typeof index === "number" ? index : entries.length;
// Tab state cache gives us the index starting from 1 instead of 0.
activeIndex--;
@@ -1038,7 +1161,7 @@ class nsZenWindowSync {
entry: (entries[activeIndex] || entries[0]) ?? null,
image,
};
this.#runOnAllWindows(null, (win) => {
this.#runOnAllWindows(null, win => {
const targetTab = this.getItemFromWindow(win, aTab.id);
if (targetTab) {
targetTab._zenPinnedInitialState = initialState;
@@ -1053,7 +1176,7 @@ class nsZenWindowSync {
* @param {Array} aWorkspaces - The workspaces to propagate.
*/
propagateWorkspacesToAllWindows(aWorkspaces) {
this.#runOnAllWindows(null, (win) => {
this.#runOnAllWindows(null, win => {
win.gZenWorkspaces.propagateWorkspaces(aWorkspaces);
});
}
@@ -1067,7 +1190,7 @@ class nsZenWindowSync {
*/
moveTabsToSyncedWorkspace(aWindow, aWorkspaceId) {
const tabsToMove = aWindow.gZenWorkspaces.allStoredTabs.filter(
(tab) => !tab.hasAttribute("zen-empty-tab")
tab => !tab.hasAttribute("zen-empty-tab")
);
const selectedTab = aWindow.gBrowser.selectedTab;
let win = this.firstSyncedWindow;
@@ -1093,7 +1216,11 @@ class nsZenWindowSync {
};
if (!win) {
this.log("No synced window found, creating a new one");
win = aWindow.gBrowser.replaceTabWithWindow(selectedTab, {}, /* zenForceSync = */ true);
win = aWindow.gBrowser.replaceTabWithWindow(
selectedTab,
{},
/* zenForceSync = */ true
);
win.gZenWorkspaces.promiseInitialized.then(() => {
moveAllTabsToWindow();
});
@@ -1111,8 +1238,9 @@ class nsZenWindowSync {
if (!aTab?._zenPinnedInitialState || aTab._zenPinnedInitialState.image) {
return;
}
let image = aTab.getAttribute("image") || aTab.ownerGlobal.gBrowser.getIcon(aTab);
this.#runOnAllWindows(null, (win) => {
let image =
aTab.getAttribute("image") || aTab.ownerGlobal.gBrowser.getIcon(aTab);
this.#runOnAllWindows(null, win => {
const targetTab = this.getItemFromWindow(win, aTab.id);
if (targetTab) {
targetTab._zenPinnedInitialState.image = image;
@@ -1138,7 +1266,7 @@ class nsZenWindowSync {
if (isUnsyncedWindow || !lazy.gWindowSyncEnabled) {
return;
}
this.#runOnAllWindows(window, (win) => {
this.#runOnAllWindows(window, win => {
const newTab = win.gBrowser.addTrustedTab("about:blank", {
animate: true,
createLazyBrowser: true,
@@ -1180,7 +1308,7 @@ class nsZenWindowSync {
if (lazy.gSyncOnlyPinnedTabs && !tab.pinned) {
return;
}
this.#runOnAllWindows(window, (win) => {
this.#runOnAllWindows(window, win => {
const targetTab = this.getItemFromWindow(win, tab.id);
if (targetTab) {
targetTab.ownerGlobal.gBrowser.hideTab(targetTab);
@@ -1194,7 +1322,7 @@ class nsZenWindowSync {
if (lazy.gSyncOnlyPinnedTabs && !tab.pinned) {
return;
}
this.#runOnAllWindows(window, (win) => {
this.#runOnAllWindows(window, win => {
const targetTab = this.getItemFromWindow(win, tab.id);
if (targetTab) {
targetTab.ownerGlobal.gBrowser.showTab(targetTab);
@@ -1228,7 +1356,7 @@ class nsZenWindowSync {
on_TabUnpinned(aEvent) {
const tab = aEvent.target;
this.#runOnAllWindows(null, (win) => {
this.#runOnAllWindows(null, win => {
const targetTab = this.getItemFromWindow(win, tab.id);
if (targetTab) {
delete targetTab._zenPinnedInitialState;
@@ -1252,7 +1380,7 @@ class nsZenWindowSync {
on_TabClose(aEvent) {
const tab = aEvent.target;
const window = tab.ownerGlobal;
this.#runOnAllWindows(window, (win) => {
this.#runOnAllWindows(window, win => {
const targetTab = this.getItemFromWindow(win, tab.id);
if (targetTab) {
win.gBrowser.removeTab(targetTab, { animate: true });
@@ -1278,7 +1406,7 @@ class nsZenWindowSync {
this.#lastFocusedWindow = new WeakRef(window);
this.#lastSelectedTab = new WeakRef(window.gBrowser.selectedTab);
// eslint-disable-next-line no-async-promise-executor
this.#docShellSwitchPromise = new Promise(async (resolve) => {
this.#docShellSwitchPromise = new Promise(async resolve => {
await promise;
await this.#onTabSwitchOrWindowFocus(window);
resolve();
@@ -1294,7 +1422,7 @@ class nsZenWindowSync {
const previousTab = aEvent.detail.previousTab;
let promise = this.#docShellSwitchPromise;
// eslint-disable-next-line no-async-promise-executor
this.#docShellSwitchPromise = new Promise(async (resolve) => {
this.#docShellSwitchPromise = new Promise(async resolve => {
await promise;
await this.#onTabSwitchOrWindowFocus(tab.ownerGlobal, previousTab);
resolve();
@@ -1332,7 +1460,9 @@ class nsZenWindowSync {
// flush the tab state, the title might not be correct.
if (activePageData && win?.gBrowser) {
win.gBrowser.setInitialTabTitle(tab, activePageData.title, {
isContentTitle: activePageData.title && activePageData.title != activePageData.url,
isContentTitle:
activePageData.title &&
activePageData.title != activePageData.url,
});
}
} catch (e) {
@@ -1359,7 +1489,7 @@ class nsZenWindowSync {
return; // Split view groups are synced via ZenSplitViewTabsSplit event.
}
// Tab groups already have an ID upon creation.
this.#runOnAllWindows(window, (win) => {
this.#runOnAllWindows(window, win => {
// Check if a group with this ID already exists in the target window.
const existingGroup = this.getItemFromWindow(win, tabGroup.id);
if (existingGroup) {
@@ -1386,7 +1516,7 @@ class nsZenWindowSync {
on_TabGroupRemoved(aEvent) {
const tabGroup = aEvent.target;
const window = tabGroup.ownerGlobal;
this.#runOnAllWindows(window, (win) => {
this.#runOnAllWindows(window, win => {
const targetGroup = this.getItemFromWindow(win, tabGroup.id);
if (targetGroup) {
if (targetGroup.isZenFolder) {
@@ -1403,7 +1533,10 @@ class nsZenWindowSync {
}
on_TabGroupUpdate(aEvent) {
return this.#delegateGenericSyncEvent(aEvent, SYNC_FLAG_ICON | SYNC_FLAG_LABEL);
return this.#delegateGenericSyncEvent(
aEvent,
SYNC_FLAG_ICON | SYNC_FLAG_LABEL
);
}
on_TabUngrouped() {
@@ -1417,7 +1550,7 @@ class nsZenWindowSync {
on_ZenTabRemovedFromSplit(aEvent) {
const tab = aEvent.target;
const window = tab.ownerGlobal;
this.#runOnAllWindows(window, (win) => {
this.#runOnAllWindows(window, win => {
const targetTab = this.getItemFromWindow(win, tab.id);
if (targetTab && win.gZenViewSplitter) {
win.gZenViewSplitter.removeTabFromGroup(targetTab);
@@ -1429,23 +1562,33 @@ class nsZenWindowSync {
const tabGroup = aEvent.target;
const window = tabGroup.ownerGlobal;
const tabs = tabGroup.tabs;
this.#runOnAllWindows(window, (win) => {
this.#runOnAllWindows(window, win => {
const otherWindowTabs = tabs
.map((tab) => this.getItemFromWindow(win, tab.id))
.map(tab => this.getItemFromWindow(win, tab.id))
.filter(Boolean);
if (otherWindowTabs.length && win.gZenViewSplitter) {
const group = win.gZenViewSplitter.splitTabs(otherWindowTabs, undefined, -1, {
groupFetchId: tabGroup.id,
});
const group = win.gZenViewSplitter.splitTabs(
otherWindowTabs,
undefined,
-1,
{
groupFetchId: tabGroup.id,
}
);
if (group) {
let otherTabGroup = group.tabs[0].group;
otherTabGroup.id = tabGroup.id;
this.#syncItemWithOriginal(aEvent.target, otherTabGroup, win, SYNC_FLAG_MOVE);
this.#syncItemWithOriginal(
aEvent.target,
otherTabGroup,
win,
SYNC_FLAG_MOVE
);
}
}
});
return new Promise((resolve) => {
return new Promise(resolve => {
lazy.setTimeout(() => {
this.#onTabSwitchOrWindowFocus(window, null).finally(resolve);
}, 0);

View File

@@ -51,7 +51,7 @@ class nsSplitNode extends nsSplitLeafNode {
set children(children) {
if (children) {
children.forEach((c) => (c.parent = this));
children.forEach(c => (c.parent = this));
}
this._children = children;
}
@@ -111,7 +111,10 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
);
window.addEventListener("TabClose", this.handleTabClose.bind(this));
window.addEventListener("TabBrowserDiscarded", this.handleTabBrowserDiscarded.bind(this));
window.addEventListener(
"TabBrowserDiscarded",
this.handleTabBrowserDiscarded.bind(this)
);
window.addEventListener("TabSelect", this.onTabSelect.bind(this));
this.initializeContextMenu();
this.insertIntoContextMenu();
@@ -127,7 +130,10 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
// Add drag over listener to the browser view
if (Services.prefs.getBoolPref("zen.splitView.enable-tab-drop")) {
const tabBox = document.getElementById("tabbrowser-tabbox");
tabBox.addEventListener("dragover", this.onBrowserDragOverToSplit.bind(this));
tabBox.addEventListener(
"dragover",
this.onBrowserDragOverToSplit.bind(this)
);
this.onBrowserDragEndToSplit = this.onBrowserDragEndToSplit.bind(this);
}
}
@@ -151,7 +157,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
if (tab === this._lastOpenedTab) {
this._lastOpenedTab = null;
}
const groupIndex = this._data.findIndex((group) => group.tabs.includes(tab));
const groupIndex = this._data.findIndex(group => group.tabs.includes(tab));
if (groupIndex < 0) {
return;
}
@@ -206,7 +212,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
{ forUnsplit = false, dontRebuildGrid = false, changeTab = true } = {}
) {
if (typeof groupIndex === "undefined") {
groupIndex = this._data.findIndex((group) => group.tabs.includes(tab));
groupIndex = this._data.findIndex(group => group.tabs.includes(tab));
}
// If groupIndex === -1, so `this._data.findIndex` couldn't find the split group
if (groupIndex < 0) {
@@ -232,7 +238,9 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
this.removeGroup(groupIndex);
if (changeTab) {
gBrowser.selectedTab = remainingTabs[remainingTabs.length - 1];
document.getElementById("cmd_zenNewEmptySplit").removeAttribute("disabled");
document
.getElementById("cmd_zenNewEmptySplit")
.removeAttribute("disabled");
}
} else {
const node = this.getSplitNodeFromTab(tab);
@@ -272,10 +280,22 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
const quarterHeight = height / 4;
const edges = [
{ side: "left", dist: clientX - panelsRect.left, threshold: quarterWidth },
{ side: "right", dist: panelsRect.right - clientX, threshold: quarterWidth },
{
side: "left",
dist: clientX - panelsRect.left,
threshold: quarterWidth,
},
{
side: "right",
dist: panelsRect.right - clientX,
threshold: quarterWidth,
},
{ side: "top", dist: clientY - panelsRect.top, threshold: quarterHeight },
{ side: "bottom", dist: panelsRect.bottom - clientY, threshold: quarterHeight },
{
side: "bottom",
dist: panelsRect.bottom - clientY,
threshold: quarterHeight,
},
];
let closestEdge = null;
@@ -309,7 +329,9 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
) {
return;
}
gBrowser.tabContainer.tabDragAndDrop.finishMoveTogetherSelectedTabs(draggedTab);
gBrowser.tabContainer.tabDragAndDrop.finishMoveTogetherSelectedTabs(
draggedTab
);
} else {
return;
}
@@ -326,7 +348,10 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
if (!draggedTab || this._canDrop || this._hasAnimated || this.fakeBrowser) {
return;
}
if (draggedTab.splitView || draggedTab.hasAttribute("zen-live-folder-item-id")) {
if (
draggedTab.splitView ||
draggedTab.hasAttribute("zen-live-folder-item-id")
) {
return;
}
const currentView = this._data[this._lastOpenedTab.splitViewValue];
@@ -349,19 +374,18 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
return;
}
// first quarter or last quarter of the screen, but not the middle
if (
!(
event.clientX < panelsRect.left + panelsWidth / 4 ||
event.clientX > panelsRect.left + (panelsWidth / 4) * 3 ||
event.clientY < panelsRect.top + panelsHeight / 4 ||
event.clientY > panelsRect.top + (panelsHeight / 4) * 3
)
) {
if (!(
event.clientX < panelsRect.left + panelsWidth / 4 ||
event.clientX > panelsRect.left + (panelsWidth / 4) * 3 ||
event.clientY < panelsRect.top + panelsHeight / 4 ||
event.clientY > panelsRect.top + (panelsHeight / 4) * 3
)) {
return;
}
dt.mozCursor = "default";
if (!this._dndElement) {
const originalDNDArgs = gBrowser.tabContainer.tabDragAndDrop.originalDragImageArgs;
const originalDNDArgs =
gBrowser.tabContainer.tabDragAndDrop.originalDragImageArgs;
requestAnimationFrame(() => {
dt.updateDragImage(
this.#getDragImageForSplit(draggedTab),
@@ -397,7 +421,8 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
if (!browser) {
continue;
}
const { width: browserWidth, height: browserHeight } = browser.getBoundingClientRect();
const { width: browserWidth, height: browserHeight } =
browser.getBoundingClientRect();
// Only apply it to the left side because if we add it to the right side,
// we wont be able to move the element to the left.
// FIXME: This is a workaround, we should find a better way to do this
@@ -411,7 +436,9 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
}
}
this.fakeBrowser = document.createXULElement("vbox");
window.addEventListener("dragend", this.onBrowserDragEndToSplit, { once: true });
window.addEventListener("dragend", this.onBrowserDragEndToSplit, {
once: true,
});
const padding = ZenThemeModifier.elementSeparation;
this.fakeBrowser.setAttribute("flex", "1");
this.fakeBrowser.id = "zen-split-view-fake-browser";
@@ -479,7 +506,10 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
.closest(".browserSidebarContainer")
.classList.remove("deck-selected");
}
this.fakeBrowser.addEventListener("dragleave", this.onBrowserDragEndToSplit);
this.fakeBrowser.addEventListener(
"dragleave",
this.onBrowserDragEndToSplit
);
this._canDrop = true;
});
}
@@ -490,7 +520,8 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
return;
}
const panelsRect = gBrowser.tabbox.getBoundingClientRect();
const fakeBrowserRect = this.fakeBrowser && this.fakeBrowser.getBoundingClientRect();
const fakeBrowserRect =
this.fakeBrowser && this.fakeBrowser.getBoundingClientRect();
if (
((fakeBrowserRect &&
event.clientX > fakeBrowserRect.left &&
@@ -595,7 +626,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
parent.children.splice(childIndex, 1);
if (parent.children.length !== 1) {
const otherNodeIncrease = 100 / (100 - toRemove.sizeInParent);
parent.children.forEach((c) => (c.sizeInParent *= otherNodeIncrease));
parent.children.forEach(c => (c.sizeInParent *= otherNodeIncrease));
return parent;
}
// node that is not a leaf cannot have less than 2 children, this makes for better resizing
@@ -609,7 +640,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
parent.parent.children[idx] = leftOverChild;
} else {
// node cannot have same direction as it's parent
leftOverChild.children.forEach((c) => {
leftOverChild.children.forEach(c => {
c.sizeInParent *= leftOverChild.sizeInParent / 100;
c.parent = parent.parent;
});
@@ -619,7 +650,9 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
this._removeNodeSplitters(parent, false);
return parent.parent;
}
const viewData = Object.values(this._data).find((s) => s.layoutTree === parent);
const viewData = Object.values(this._data).find(
s => s.layoutTree === parent
);
viewData.layoutTree = leftOverChild;
leftOverChild.positionToRoot = null;
leftOverChild.parent = null;
@@ -632,13 +665,13 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
* @private
*/
_removeNodeSplitters(node, recursive) {
this.getSplitters(node)?.forEach((s) => s.remove());
this.getSplitters(node)?.forEach(s => s.remove());
this._splitNodeToSplitters.delete(node);
if (!recursive) {
return;
}
if (node && node.children) {
node.children.forEach((c) => this._removeNodeSplitters(c));
node.children.forEach(c => this._removeNodeSplitters(c));
}
}
@@ -666,20 +699,28 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
this._thumnailCanvas.width = 280 * devicePixelRatio;
this._thumnailCanvas.height = 140 * devicePixelRatio;
}
const browsers = this._data[this.currentView].tabs.map((t) => t.linkedBrowser);
browsers.forEach((b) => {
const browsers = this._data[this.currentView].tabs.map(
t => t.linkedBrowser
);
browsers.forEach(b => {
b.style.pointerEvents = "none";
b.style.opacity = ".85";
});
if (!tabDrag) {
this.tabBrowserPanel.addEventListener("dragstart", this.onBrowserDragStart);
this.tabBrowserPanel.addEventListener(
"dragstart",
this.onBrowserDragStart
);
this.tabBrowserPanel.addEventListener("dragend", this.onBrowserDragEnd);
}
this.tabBrowserPanel.addEventListener("dragover", this.onBrowserDragOver);
this.tabBrowserPanel.addEventListener("drop", this.onBrowserDrop);
this.tabBrowserPanel.addEventListener("click", this.disableTabRearrangeView);
this.tabBrowserPanel.addEventListener(
"click",
this.disableTabRearrangeView
);
window.addEventListener("keydown", this.disableTabRearrangeView);
}
@@ -704,13 +745,24 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
return;
}
this.tabBrowserPanel.removeEventListener("dragstart", this.onBrowserDragStart);
this.tabBrowserPanel.removeEventListener("dragover", this.onBrowserDragOver);
this.tabBrowserPanel.removeEventListener(
"dragstart",
this.onBrowserDragStart
);
this.tabBrowserPanel.removeEventListener(
"dragover",
this.onBrowserDragOver
);
this.tabBrowserPanel.removeEventListener("drop", this.onBrowserDrop);
this.tabBrowserPanel.removeEventListener("click", this.disableTabRearrangeView);
this.tabBrowserPanel.removeEventListener(
"click",
this.disableTabRearrangeView
);
window.removeEventListener("keydown", this.disableTabRearrangeView);
const browsers = this._data[this.rearrangeViewView].tabs.map((t) => t.linkedBrowser);
browsers.forEach((b) => {
const browsers = this._data[this.rearrangeViewView].tabs.map(
t => t.linkedBrowser
);
browsers.forEach(b => {
b.style.pointerEvents = "";
b.style.opacity = "";
});
@@ -718,7 +770,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
this.rearrangeViewView = null;
};
onBrowserDragStart = (event) => {
onBrowserDragStart = event => {
if (!this.splitViewActive) {
return;
}
@@ -726,7 +778,9 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
let browser;
let isSplitHeaderDrag = false;
const container = event.target.closest(".browserSidebarContainer[zen-split]");
const container = event.target.closest(
".browserSidebarContainer[zen-split]"
);
if (container && event.target.closest(".zen-tab-rearrange-button")) {
// Split tab header drag case
const containerRect = container.getBoundingClientRect();
@@ -765,14 +819,20 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
}
browser.style.opacity = ".2";
event.dataTransfer.setData("text/plain", browser.closest(".browserSidebarContainer").id);
event.dataTransfer.setData(
"text/plain",
browser.closest(".browserSidebarContainer").id
);
this._draggingTab = tab;
// Canvas setup for drag image
let scale = window.devicePixelRatio;
let canvas = this._dndCanvas;
if (!canvas) {
this._dndCanvas = canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
this._dndCanvas = canvas = document.createElementNS(
"http://www.w3.org/1999/xhtml",
"canvas"
);
canvas.style.width = "100%";
canvas.style.height = "100%";
}
@@ -792,7 +852,11 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
// using updateDragImage. On Linux, we can use a panel.
if (platform === "win" || platform === "macosx") {
captureListener = () => {
event.dataTransfer.updateDragImage(canvas, dragImageOffset, dragImageOffset);
event.dataTransfer.updateDragImage(
canvas,
dragImageOffset,
dragImageOffset
);
};
} else {
// Create a panel to use it in setDragImage
@@ -803,7 +867,10 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
this._dndPanel = document.createXULElement("panel");
this._dndPanel.className = "dragfeedback-tab";
this._dndPanel.setAttribute("type", "drag");
let wrapper = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
let wrapper = document.createElementNS(
"http://www.w3.org/1999/xhtml",
"div"
);
wrapper.style.width = "160px";
wrapper.style.height = "90px";
wrapper.appendChild(canvas);
@@ -816,18 +883,18 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
// since we can update the image during the dnd.
PageThumbs.captureToCanvas(browser, canvas)
.then(captureListener)
.catch((e) => console.error(e));
.catch(e => console.error(e));
} else {
// For the non e10s case we can just use PageThumbs
// sync, so let's use the canvas for setDragImage.
PageThumbs.captureToCanvas(browser, canvas).catch((e) => console.error(e));
PageThumbs.captureToCanvas(browser, canvas).catch(e => console.error(e));
dragImageOffset = dragImageOffset * scale;
}
event.dataTransfer.setDragImage(toDrag, dragImageOffset, dragImageOffset);
return true;
};
onBrowserDragOver = (event) => {
onBrowserDragOver = event => {
event.preventDefault();
const browser = event.target.querySelector("browser");
if (!browser) {
@@ -850,23 +917,32 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
const posToRoot = { ...splitNode.positionToRoot };
const browserRect = browser.getBoundingClientRect();
const hoverSide = this.calculateHoverSide(event.clientX, event.clientY, browserRect);
const hoverSide = this.calculateHoverSide(
event.clientX,
event.clientY,
browserRect
);
if (hoverSide !== "center") {
const isVertical = hoverSide === "top" || hoverSide === "bottom";
const browserSize =
100 - (isVertical ? posToRoot.top + posToRoot.bottom : posToRoot.right + posToRoot.left);
100 -
(isVertical
? posToRoot.top + posToRoot.bottom
: posToRoot.right + posToRoot.left);
const reduce = browserSize * 0.5;
posToRoot[this._oppositeSide(hoverSide)] += reduce;
}
const newInset = `${posToRoot.top}% ${posToRoot.right}% ${posToRoot.bottom}% ${posToRoot.left}%`;
if (this.dropZone.style.inset !== newInset) {
window.requestAnimationFrame(() => (this.dropZone.style.inset = newInset));
window.requestAnimationFrame(
() => (this.dropZone.style.inset = newInset)
);
}
};
onBrowserDragEnd = (event) => {
onBrowserDragEnd = event => {
this.dropZone?.removeAttribute("enabled");
// If we don't have drag state, just clean up what we can
@@ -908,8 +984,10 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
}
calculateHoverSide(x, y, elementRect) {
const hPixelHoverSize = ((elementRect.right - elementRect.left) * this._edgeHoverSize) / 100;
const vPixelHoverSize = ((elementRect.bottom - elementRect.top) * this._edgeHoverSize) / 100;
const hPixelHoverSize =
((elementRect.right - elementRect.left) * this._edgeHoverSize) / 100;
const vPixelHoverSize =
((elementRect.bottom - elementRect.top) * this._edgeHoverSize) / 100;
if (x <= elementRect.left + hPixelHoverSize) {
return "left";
}
@@ -925,7 +1003,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
return "center";
}
onBrowserDrop = (event) => {
onBrowserDrop = event => {
const browserDroppedOn = event.target.querySelector("browser");
if (!browserDroppedOn) {
return;
@@ -935,7 +1013,9 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
if (!droppedTab) {
return;
}
const droppedOnTab = gBrowser.getTabForBrowser(event.target.querySelector("browser"));
const droppedOnTab = gBrowser.getTabForBrowser(
event.target.querySelector("browser")
);
if (droppedTab === droppedOnTab) {
return;
}
@@ -981,7 +1061,8 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
* @param {number} sizeOfInsertedNode percentage of node width or height that nodeToInsert will take
*/
splitIntoNode(node, nodeToInsert, side, sizeOfInsertedNode) {
const splitDirection = side === "left" || side === "right" ? "row" : "column";
const splitDirection =
side === "left" || side === "right" ? "row" : "column";
const splitPosition = side === "left" || side === "top" ? 0 : 1;
let nodeSize;
@@ -997,7 +1078,9 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
const nodeIndex = node.parent.children.indexOf(node);
node.parent.children[nodeIndex] = newParent;
} else {
const viewData = Object.values(this._data).find((s) => s.layoutTree === node);
const viewData = Object.values(this._data).find(
s => s.layoutTree === node
);
viewData.layoutTree = newParent;
}
newParent.addChild(node);
@@ -1084,7 +1167,9 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
const tabCountInfo = JSON.stringify({
tabCount: window.gBrowser.selectedTabs.length,
});
document.getElementById("context_zenSplitTabs").setAttribute("data-l10n-args", tabCountInfo);
document
.getElementById("context_zenSplitTabs")
.setAttribute("data-l10n-args", tabCountInfo);
const splitTabs = document.getElementById("context_zenSplitTabs");
if (!this.contextCanSplitTabs()) {
splitTabs.setAttribute("hidden", "true");
@@ -1137,7 +1222,9 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
window.gContextMenu.mediaURL ||
window.gContextMenu.contentData.docLocation ||
window.gContextMenu.target.ownerDocument.location.href;
const currentTab = gZenGlanceManager.getTabOrGlanceParent(window.gBrowser.selectedTab);
const currentTab = gZenGlanceManager.getTabOrGlanceParent(
window.gBrowser.selectedTab
);
const newTab = this.openAndSwitchToTab(url, { inBackground: false });
this.splitTabs([currentTab, newTab], undefined, 1);
}
@@ -1199,7 +1286,8 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
}
this._maybeRemoveFakeBrowser();
{
const shouldDisableEmptySplits = tab.hasAttribute("zen-empty-tab") || tab.splitView;
const shouldDisableEmptySplits =
tab.hasAttribute("zen-empty-tab") || tab.splitView;
const command = document.getElementById("cmd_zenNewEmptySplit");
if (shouldDisableEmptySplits) {
command.setAttribute("disabled", "true");
@@ -1245,21 +1333,23 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
const tabIndexToUse = Math.max(0, initialIndex);
return this.#withoutSplitViewTransition(() => {
// TODO: Add support for splitting essential tabs
tabs = tabs.filter((t) => !t.hidden && !t.hasAttribute("zen-empty-tab"));
tabs = tabs.filter(t => !t.hidden && !t.hasAttribute("zen-empty-tab"));
if (tabs.length < 2 || tabs.length > this.MAX_TABS) {
return;
}
const existingSplitTab = tabs.find((tab) => tab.splitView);
const existingSplitTab = tabs.find(tab => tab.splitView);
let shouldActivateSplit =
(initialIndex >= 0 || tabs.includes(window.gBrowser.selectedTab)) &&
!this._sessionRestoring;
if (existingSplitTab) {
this._moveTabsToContainer(tabs, tabs[tabIndexToUse]);
const groupIndex = this._data.findIndex((group) => group.tabs.includes(existingSplitTab));
const groupIndex = this._data.findIndex(group =>
group.tabs.includes(existingSplitTab)
);
const group = this._data[groupIndex];
const gridTypeChange = gridType && group.gridType !== gridType;
const newTabsAdded = tabs.find((t) => !group.tabs.includes(t));
const newTabsAdded = tabs.find(t => !group.tabs.includes(t));
if (group.tabs.length >= this.MAX_TABS) {
return;
}
@@ -1275,7 +1365,10 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
for (let i = 0; i < tabs.length; i++) {
const tab = tabs[i];
if (!group.tabs.includes(tab)) {
gBrowser.moveTabToExistingGroup(tab, this._getSplitViewGroup(tabs, groupFetchId));
gBrowser.moveTabToExistingGroup(
tab,
this._getSplitViewGroup(tabs, groupFetchId)
);
group.tabs.push(tab);
this.addTabToSplit(tab, group.layoutTree, false);
tab.splitView = true;
@@ -1294,12 +1387,20 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
// We are here if none of the tabs have been previously split
// If there's ANY pinned tab on the list, we clone the pinned tab
// state to all the tabs
const allArePinned = tabs.every((tab) => tab.pinned);
const thereIsOnePinned = tabs.some((tab) => tab.pinned);
const thereIsOneEssential = tabs.some((tab) => tab.hasAttribute("zen-essential"));
const thereIsOneLiveFolder = tabs.some((tab) => tab.hasAttribute("zen-live-folder-item-id"));
const allArePinned = tabs.every(tab => tab.pinned);
const thereIsOnePinned = tabs.some(tab => tab.pinned);
const thereIsOneEssential = tabs.some(tab =>
tab.hasAttribute("zen-essential")
);
const thereIsOneLiveFolder = tabs.some(tab =>
tab.hasAttribute("zen-live-folder-item-id")
);
if (thereIsOneEssential || (thereIsOnePinned && !allArePinned) || thereIsOneLiveFolder) {
if (
thereIsOneEssential ||
(thereIsOnePinned && !allArePinned) ||
thereIsOneLiveFolder
) {
for (let i = 0; i < tabs.length; i++) {
const tab = tabs[i];
if (tab.pinned) {
@@ -1345,7 +1446,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
addTabToSplit(tab, splitNode, prepend = true) {
const reduce = splitNode.children.length / (splitNode.children.length + 1);
splitNode.children.forEach((c) => (c.sizeInParent *= reduce));
splitNode.children.forEach(c => (c.sizeInParent *= reduce));
splitNode.addChild(new nsSplitLeafNode(tab, (1 - reduce) * 100), prepend);
}
@@ -1356,7 +1457,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
*/
updateSplitView(tab) {
const oldView = this.currentView;
const newView = this._data.findIndex((group) => group.tabs.includes(tab));
const newView = this._data.findIndex(group => group.tabs.includes(tab));
if (newView === oldView && oldView < 0) {
return;
@@ -1386,11 +1487,15 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
}
this.removeSplitters();
this.tabBrowserPanel.removeAttribute("zen-split-view");
document.getElementById("tabbrowser-tabbox").removeAttribute("zen-split-view");
document
.getElementById("tabbrowser-tabbox")
.removeAttribute("zen-split-view");
this.currentView = -1;
this.toggleWrapperDisplay(false);
this.maybeDisableOpeningTabOnSplitView();
window.dispatchEvent(new CustomEvent("ZenViewSplitter:SplitViewDeactivated"));
window.dispatchEvent(
new CustomEvent("ZenViewSplitter:SplitViewDeactivated")
);
}
/**
@@ -1409,7 +1514,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
if (reset) {
this.removeSplitters();
}
splitData.tabs.forEach((tab) => {
splitData.tabs.forEach(tab => {
if (tab.hasAttribute("pending")) {
gBrowser.getBrowserForTab(tab).reload();
}
@@ -1422,7 +1527,9 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
this.applyGridToTabs(splitData.tabs);
this.tabBrowserPanel.setAttribute("zen-split-view", "true");
document.getElementById("tabbrowser-tabbox").setAttribute("zen-split-view", "true");
document
.getElementById("tabbrowser-tabbox")
.setAttribute("zen-split-view", "true");
this.applyGridLayout(splitData.layoutTree);
this.setTabsDocShellState(splitData.tabs, true);
@@ -1434,10 +1541,14 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
let rootNode;
if (gridType === "vsep" || (tabs.length === 2 && gridType === "grid")) {
rootNode = new nsSplitNode("row");
rootNode.children = tabs.map((tab) => new nsSplitLeafNode(tab, 100 / tabs.length));
rootNode.children = tabs.map(
tab => new nsSplitLeafNode(tab, 100 / tabs.length)
);
} else if (gridType === "hsep") {
rootNode = new nsSplitNode("column");
rootNode.children = tabs.map((tab) => new nsSplitLeafNode(tab, 100 / tabs.length));
rootNode.children = tabs.map(
tab => new nsSplitLeafNode(tab, 100 / tabs.length)
);
} else if (gridType === "grid") {
rootNode = new nsSplitNode("row");
const rowWidth = 100 / Math.ceil(tabs.length / 2);
@@ -1450,7 +1561,10 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
rootNode.addChild(columnNode, false);
}
if (tabs.length % 2 !== 0) {
rootNode.addChild(new nsSplitLeafNode(tabs[tabs.length - 1], rowWidth), false);
rootNode.addChild(
new nsSplitLeafNode(tabs[tabs.length - 1], rowWidth),
false
);
}
}
@@ -1463,7 +1577,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
* @param {Tab[]} tabs - The tabs to apply the grid layout to.
*/
applyGridToTabs(tabs) {
tabs.forEach((tab) => {
tabs.forEach(tab => {
tab.splitView = true;
tab.splitViewValue = this.currentView;
tab.setAttribute("split-view", "true");
@@ -1496,7 +1610,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
header.classList.add("zen-view-splitter-header");
const removeButton = document.createXULElement("toolbarbutton");
removeButton.classList.add("zen-tab-unsplit-button");
removeButton.addEventListener("click", (event) => {
removeButton.addEventListener("click", event => {
this.removeTabFromSplit(event, container);
});
const rearrangeButton = document.createXULElement("toolbarbutton");
@@ -1508,7 +1622,9 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
}
_removeHeader(container) {
const header = container.querySelector(".zen-view-splitter-header-container");
const header = container.querySelector(
".zen-view-splitter-header-container"
);
if (header) {
header.remove();
}
@@ -1525,14 +1641,18 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
}
const nodeRootPosition = splitNode.positionToRoot;
if (!splitNode.children) {
const browserContainer = splitNode.tab.linkedBrowser.closest(".browserSidebarContainer");
const browserContainer = splitNode.tab.linkedBrowser.closest(
".browserSidebarContainer"
);
browserContainer.style.inset = `${nodeRootPosition.top}% ${nodeRootPosition.right}% ${nodeRootPosition.bottom}% ${nodeRootPosition.left}%`;
this._tabToSplitNode.set(splitNode.tab, splitNode);
return;
}
const rootToNodeWidthRatio = (100 - nodeRootPosition.right - nodeRootPosition.left) / 100;
const rootToNodeHeightRatio = (100 - nodeRootPosition.bottom - nodeRootPosition.top) / 100;
const rootToNodeWidthRatio =
(100 - nodeRootPosition.right - nodeRootPosition.left) / 100;
const rootToNodeHeightRatio =
(100 - nodeRootPosition.bottom - nodeRootPosition.top) / 100;
const splittersNeeded = splitNode.children.length - 1;
const currentSplitters = this.getSplitters(splitNode, splittersNeeded);
@@ -1542,8 +1662,10 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
splitNode.children.forEach((childNode, i) => {
const childRootPosition = {
top: topOffset,
right: 100 - (leftOffset + childNode.widthInParent * rootToNodeWidthRatio),
bottom: 100 - (topOffset + childNode.heightInParent * rootToNodeHeightRatio),
right:
100 - (leftOffset + childNode.widthInParent * rootToNodeWidthRatio),
bottom:
100 - (topOffset + childNode.heightInParent * rootToNodeHeightRatio),
left: leftOffset,
};
childNode.positionToRoot = childRootPosition;
@@ -1604,7 +1726,9 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
currentSplitters[i].parentSplitNode = parentNode;
}
if (currentSplitters.length > splittersNeeded) {
currentSplitters.slice(splittersNeeded - currentSplitters.length).forEach((s) => s.remove());
currentSplitters
.slice(splittersNeeded - currentSplitters.length)
.forEach(s => s.remove());
currentSplitters = currentSplitters.slice(0, splittersNeeded);
}
this._splitNodeToSplitters.set(parentNode, currentSplitters);
@@ -1613,8 +1737,8 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
removeSplitters() {
[...this.overlay.children]
.filter((c) => c.classList.contains("zen-split-view-splitter"))
.forEach((s) => s.remove());
.filter(c => c.classList.contains("zen-split-view-splitter"))
.forEach(s => s.remove());
this._splitNodeToSplitters.clear();
}
@@ -1640,20 +1764,20 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
*
* @param {Event} event - The event.
*/
_handleTabEvent = (event) => {
_handleTabEvent = event => {
if (this.rearrangeViewEnabled) {
return;
}
const container = event.currentTarget.closest(".browserSidebarContainer");
const tab = window.gBrowser.tabs.find(
(t) => t.linkedBrowser?.closest(".browserSidebarContainer") === container
t => t.linkedBrowser?.closest(".browserSidebarContainer") === container
);
if (tab) {
window.gBrowser.selectedTab = tab;
}
};
handleSplitterMouseDown = (event) => {
handleSplitterMouseDown = event => {
this.tabBrowserPanel.setAttribute("zen-split-resizing", true);
const isVertical = event.target.getAttribute("orient") === "vertical";
const dimension = isVertical ? "width" : "height";
@@ -1664,15 +1788,21 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
const splitNode = event.target.parentSplitNode;
let rootToNodeSize;
if (isVertical) {
rootToNodeSize = 100 / (100 - splitNode.positionToRoot.right - splitNode.positionToRoot.left);
rootToNodeSize =
100 /
(100 - splitNode.positionToRoot.right - splitNode.positionToRoot.left);
} else {
rootToNodeSize = 100 / (100 - splitNode.positionToRoot.bottom - splitNode.positionToRoot.top);
rootToNodeSize =
100 /
(100 - splitNode.positionToRoot.bottom - splitNode.positionToRoot.top);
}
const originalSizes = splitNode.children.map((c) => c.sizeInParent);
const originalSizes = splitNode.children.map(c => c.sizeInParent);
const dragFunc = (dEvent) => {
const dragFunc = dEvent => {
requestAnimationFrame(() => {
originalSizes.forEach((s, i) => (splitNode.children[i].sizeInParent = s)); // reset changes
originalSizes.forEach(
(s, i) => (splitNode.children[i].sizeInParent = s)
); // reset changes
const movement = dEvent[clientAxis] - startPosition;
let movementPercent =
@@ -1687,7 +1817,10 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
i += movementPercent < 0 ? -1 : 1
) {
const current = originalSizes[i];
const newSize = Math.max(this.minResizeWidth, current - reducingMovement);
const newSize = Math.max(
this.minResizeWidth,
current - reducingMovement
);
splitNode.children[i].sizeInParent = newSize;
const amountReduced = current - newSize;
reducingMovement -= amountReduced;
@@ -1695,7 +1828,8 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
break;
}
}
const increasingMovement = Math.max(movementPercent, -movementPercent) - reducingMovement;
const increasingMovement =
Math.max(movementPercent, -movementPercent) - reducingMovement;
const increaseIndex = gridIdx + (movementPercent < 0 ? 1 : 0);
splitNode.children[increaseIndex].sizeInParent =
originalSizes[increaseIndex] + increasingMovement;
@@ -1772,9 +1906,14 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
*/
updatePanelUI(panel) {
for (const gridType of ["hsep", "vsep", "grid", "unsplit"]) {
const selector = panel.querySelector(`.zen-split-view-modifier-preview.${gridType}`);
const selector = panel.querySelector(
`.zen-split-view-modifier-preview.${gridType}`
);
selector.classList.remove("active");
if (this.currentView >= 0 && this._data[this.currentView].gridType === gridType) {
if (
this.currentView >= 0 &&
this._data[this.currentView].gridType === gridType
) {
selector.classList.add("active");
}
}
@@ -1818,13 +1957,13 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
let nextTabIndex = tabs.indexOf(gBrowser.selectedTab) + 1;
if (nextTabIndex >= tabs.length) {
// Find the first non-hidden tab
nextTabIndex = tabs.findIndex((tab) => !tab.hidden);
nextTabIndex = tabs.findIndex(tab => !tab.hidden);
} else if (nextTabIndex < 0) {
// reverse find the first non-hidden tab
nextTabIndex = tabs
.slice()
.reverse()
.findIndex((tab) => !tab.hidden);
.findIndex(tab => !tab.hidden);
}
const selected_tabs = gBrowser.selectedTab.multiselected
? gBrowser.selectedTabs
@@ -1834,7 +1973,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
if (this.currentView >= 0) {
const splitViewId = this._data[this.currentView].groupId;
const sameSplitView = selected_tabs.every(
(tab) => !tab?.group || tab.group.id === splitViewId
tab => !tab?.group || tab.group.id === splitViewId
);
if (!sameSplitView) {
return;
@@ -1854,7 +1993,9 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
if (browser) {
const tab = gBrowser.getTabForBrowser(browser);
if (tab) {
const groupIndex = this._data.findIndex((group) => group.tabs.includes(tab));
const groupIndex = this._data.findIndex(group =>
group.tabs.includes(tab)
);
this.deactivateCurrentSplitView();
if (groupIndex >= 0) {
this.removeTabFromGroup(tab, groupIndex, { forUnsplit: true });
@@ -1948,7 +2089,9 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
return false;
}
let droppedOnTab = gZenGlanceManager.getTabOrGlanceParent(gBrowser.getTabForBrowser(browser));
let droppedOnTab = gZenGlanceManager.getTabOrGlanceParent(
gBrowser.getTabForBrowser(browser)
);
if (droppedOnTab === this._draggingTab) {
this.createEmptySplit(dropSide);
return true;
@@ -1956,7 +2099,9 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
gBrowser.selectedTab = this._draggingTab;
this._draggingTab = null;
const browserContainer = draggedTab.linkedBrowser?.closest(".browserSidebarContainer");
const browserContainer = draggedTab.linkedBrowser?.closest(
".browserSidebarContainer"
);
if (browserContainer) {
browserContainer.style.opacity = "0";
}
@@ -1971,9 +2116,11 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
// If there's ANY pinned tab on the list, we clone the pinned tab
// state to all the tabs
let tempTabs = [draggedTab, droppedOnTab];
const allArePinned = tempTabs.every((tab) => tab.pinned);
const thereIsOnePinned = tempTabs.some((tab) => tab.pinned);
const thereIsOneEssential = tempTabs.some((tab) => tab.hasAttribute("zen-essential"));
const allArePinned = tempTabs.every(tab => tab.pinned);
const thereIsOnePinned = tempTabs.some(tab => tab.pinned);
const thereIsOneEssential = tempTabs.some(tab =>
tab.hasAttribute("zen-essential")
);
if (thereIsOneEssential || (thereIsOnePinned && !allArePinned)) {
for (let i = 0; i < tempTabs.length; i++) {
@@ -1988,13 +2135,21 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
if (droppedOnTab.splitView) {
// Add to existing split view
const groupIndex = this._data.findIndex((group) => group.tabs.includes(droppedOnTab));
const groupIndex = this._data.findIndex(group =>
group.tabs.includes(droppedOnTab)
);
const group = this._data[groupIndex];
if (!group.tabs.includes(draggedTab) && group.tabs.length < this.MAX_TABS) {
if (
!group.tabs.includes(draggedTab) &&
group.tabs.length < this.MAX_TABS
) {
// First move the tab to the split view group
let splitGroup = droppedOnTab.group;
if (splitGroup && (!draggedTab.group || draggedTab.group !== splitGroup)) {
if (
splitGroup &&
(!draggedTab.group || draggedTab.group !== splitGroup)
) {
this._moveTabsToContainer([draggedTab], droppedOnTab);
gBrowser.moveTabToExistingGroup(draggedTab, splitGroup);
if (hoverSide === "left" || hoverSide === "top") {
@@ -2012,7 +2167,8 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
// If dropping on a side, wrap entire layout in a new split at the root level
if (hoverSide !== "center") {
const splitDirection = hoverSide === "left" || hoverSide === "right" ? "row" : "column";
const splitDirection =
hoverSide === "left" || hoverSide === "right" ? "row" : "column";
const rootNode = group.layoutTree;
const prepend = hoverSide === "left" || hoverSide === "top";
@@ -2021,7 +2177,12 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
this.addTabToSplit(draggedTab, rootNode, prepend);
} else {
// Different direction, wrap root in a new split node
this.splitIntoNode(rootNode, new nsSplitLeafNode(draggedTab, 50), hoverSide, 0.5);
this.splitIntoNode(
rootNode,
new nsSplitLeafNode(draggedTab, 50),
hoverSide,
0.5
);
}
} else {
this.addTabToSplit(draggedTab, group.layoutTree);
@@ -2031,7 +2192,8 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
}
} else {
// Create new split view with layout based on drop position
const gridType = dropSide === "top" || dropSide === "bottom" ? "hsep" : "vsep";
const gridType =
dropSide === "top" || dropSide === "bottom" ? "hsep" : "vsep";
const topOrLeft = dropSide === "top" || dropSide === "left";
// Put tabs always as if it was dropped from the left
@@ -2112,12 +2274,14 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
* @returns {TabGroup} The tab group for split view tabs
*/
_getSplitViewGroup(tabs, id = null) {
if (tabs.some((tab) => tab.hasAttribute("zen-essential"))) {
if (tabs.some(tab => tab.hasAttribute("zen-essential"))) {
return null;
}
// Try to find an existing split view group
let splitGroup = tabs?.find((tab) => tab.group?.hasAttribute("split-view-group"))?.group;
let splitGroup = tabs?.find(tab =>
tab.group?.hasAttribute("split-view-group")
)?.group;
if (splitGroup) {
return splitGroup;
}
@@ -2138,7 +2302,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
}
storeDataForSessionStore() {
const serializeNode = (node) => {
const serializeNode = node => {
if (node.tab) {
return {
type: "leaf",
@@ -2151,17 +2315,17 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
type: "splitter",
direction: node.direction,
sizeInParent: node.sizeInParent,
children: node._children.map((child) => serializeNode(child)),
children: node._children.map(child => serializeNode(child)),
};
};
return this._data.map((group) => {
return this._data.map(group => {
const serializedTree = serializeNode(group.layoutTree);
return {
groupId: group.groupId,
gridType: group.gridType,
layoutTree: serializedTree,
tabs: group.tabs.map((tab) => tab.id),
tabs: group.tabs.map(tab => tab.id),
};
});
}
@@ -2188,7 +2352,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
return;
}
const deserializeNode = (nodeData) => {
const deserializeNode = nodeData => {
if (nodeData.type === "leaf") {
const tab = document.getElementById(nodeData.tabId);
if (!tab) {
@@ -2197,7 +2361,10 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
return new nsSplitLeafNode(tab, nodeData.sizeInParent);
}
const splitter = new nsSplitNode(nodeData.direction, nodeData.sizeInParent);
const splitter = new nsSplitNode(
nodeData.direction,
nodeData.sizeInParent
);
splitter._children = [];
for (const childData of nodeData.children) {
@@ -2291,9 +2458,11 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
setTimeout(() => {
window.addEventListener(
"ZenURLBarClosed",
(event) => {
event => {
const { onElementPicked, onSwitch } = event.detail;
const groupIndex = this._data.findIndex((group) => group.tabs.includes(emptyTab));
const groupIndex = this._data.findIndex(group =>
group.tabs.includes(emptyTab)
);
const newSelectedTab = gBrowser.selectedTab;
const cleanup = () => {
this.removeTabFromGroup(emptyTab, groupIndex, {
@@ -2313,11 +2482,15 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
cleanup();
return;
}
this.removeTabFromGroup(emptyTab, groupIndex, { forUnsplit: true });
this.removeTabFromGroup(emptyTab, groupIndex, {
forUnsplit: true,
});
gBrowser.selectedTab = selectedTab;
this.resetTabState(emptyTab, false);
this.splitTabs(
topOrLeft ? [newSelectedTab, selectedTab] : [selectedTab, newSelectedTab],
topOrLeft
? [newSelectedTab, selectedTab]
: [selectedTab, newSelectedTab],
gridType,
topOrLeft ? 0 : 1
);
@@ -2336,7 +2509,7 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
if (this.currentView < 0) {
return [];
}
return this._data[this.currentView].tabs.map((tab) => tab.linkedBrowser);
return this._data[this.currentView].tabs.map(tab => tab.linkedBrowser);
}
}

View File

@@ -67,7 +67,10 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
if (!this.enabled) {
return;
}
this._canLog = Services.prefs.getBoolPref("zen.pinned-tab-manager.debug", false);
this._canLog = Services.prefs.getBoolPref(
"zen.pinned-tab-manager.debug",
false
);
this.observer = new ZenPinnedTabsObserver();
this._initClosePinnedTabShortcut();
this._insertItemsIntoTabContextMenu();
@@ -97,7 +100,9 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
}
onTabIconChanged(tab, url = null) {
tab.dispatchEvent(new CustomEvent("ZenTabIconChanged", { bubbles: true, detail: { tab } }));
tab.dispatchEvent(
new CustomEvent("ZenTabIconChanged", { bubbles: true, detail: { tab } })
);
if (tab.hasAttribute("zen-essential")) {
this.setEssentialTabIcon(tab, url);
}
@@ -158,7 +163,9 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
const tab = e.target?.closest("tab");
if (e.button === 1 && tab) {
await this.onCloseTabShortcut(e, tab, {
closeIfPending: Services.prefs.getBoolPref("zen.pinned-tab-manager.wheel-close-if-pending"),
closeIfPending: Services.prefs.getBoolPref(
"zen.pinned-tab-manager.wheel-close-if-pending"
),
});
}
}
@@ -211,13 +218,13 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
const pinnedTabs = [
...new Set(
tabs
.flatMap((tab) => {
.flatMap(tab => {
if (tab.group?.hasAttribute("split-view-group")) {
return tab.group.tabs;
}
return tab;
})
.filter((tab) => tab?.pinned)
.filter(tab => tab?.pinned)
),
];
@@ -225,7 +232,7 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
return;
}
const selectedTabs = pinnedTabs.filter((tab) => tab.selected);
const selectedTabs = pinnedTabs.filter(tab => tab.selected);
event.stopPropagation();
event.preventDefault();
@@ -234,8 +241,13 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
behavior = "unload-switch";
}
if (alwaysUnload && ["close", "reset", "switch", "reset-switch"].includes(behavior)) {
behavior = behavior.contains("reset") ? "reset-unload-switch" : "unload-switch";
if (
alwaysUnload &&
["close", "reset", "switch", "reset-switch"].includes(behavior)
) {
behavior = behavior.contains("reset")
? "reset-unload-switch"
: "unload-switch";
}
switch (behavior) {
@@ -256,19 +268,23 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
// before we used to just ignore it but now we need to fully close
// it as well.
gZenGlanceManager.manageTabClose(tab.glanceTab);
await new Promise((resolve) => {
await new Promise(resolve => {
let hasRan = false;
const onGlanceClose = () => {
hasRan = true;
resolve();
};
window.addEventListener("GlanceClose", onGlanceClose, { once: true });
window.addEventListener("GlanceClose", onGlanceClose, {
once: true,
});
// Set a timeout to resolve the promise if the event doesn't fire.
// We do this to prevent any future issues where glance woudnt close such as
// glance requering to ask for permit unload.
setTimeout(() => {
if (!hasRan) {
console.warn("GlanceClose event did not fire within 3 seconds");
console.warn(
"GlanceClose event did not fire within 3 seconds"
);
resolve();
}
}, 3000);
@@ -285,11 +301,15 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
await gZenFolders.animateUnloadAll(folderToUnload);
}
const allAreUnloaded = pinnedTabs.every(
(tab) => tab.hasAttribute("pending") && !tab.hasAttribute("zen-essential")
tab =>
tab.hasAttribute("pending") &&
!tab.hasAttribute("zen-essential")
);
for (const tabItem of pinnedTabs) {
if (allAreUnloaded && closeIfPending) {
await this.onCloseTabShortcut(event, tabItem, { behavior: "close" });
await this.onCloseTabShortcut(event, tabItem, {
behavior: "close",
});
return;
}
}
@@ -323,10 +343,10 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
if (selectedTab !== gBrowser.selectedTab) {
return;
}
const findNextTab = (direction) =>
const findNextTab = direction =>
gBrowser.tabContainer.findNextTab(selectedTab, {
direction,
filter: (tab) => !tab.hidden && !tab.pinned,
filter: tab => !tab.hidden && !tab.pinned,
});
let nextTab = findNextTab(1) || findNextTab(-1);
@@ -441,8 +461,14 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
// eslint-disable-next-line no-shadow
const tab = tabs[i];
tab.removeAttribute("zen-essential");
if (gZenWorkspaces.workspaceEnabled && gZenWorkspaces.getActiveWorkspaceFromCache().uuid) {
tab.setAttribute("zen-workspace-id", gZenWorkspaces.getActiveWorkspaceFromCache().uuid);
if (
gZenWorkspaces.workspaceEnabled &&
gZenWorkspaces.getActiveWorkspaceFromCache().uuid
) {
tab.setAttribute(
"zen-workspace-id",
gZenWorkspaces.getActiveWorkspaceFromCache().uuid
);
}
if (unpin) {
gBrowser.unpinTab(tab);
@@ -500,28 +526,32 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
`);
document.getElementById("context_pinTab")?.before(element);
document.getElementById("context_zen-edit-tab-title").addEventListener("command", (event) => {
gZenVerticalTabsManager.renameTabStart(event);
});
document.getElementById("context_zen-edit-tab-icon").addEventListener("command", () => {
const tab = TabContextMenu.contextTab;
gZenEmojiPicker.open(tab.iconImage, {
emojiAsSVG: true,
closeOnSelect: false,
allowNone: Boolean(tab.zenStaticIcon),
onSelect: (icon) => {
if (icon) {
tab.zenStaticIcon = icon;
} else {
delete tab.zenStaticIcon;
}
gBrowser.setIcon(tab, icon);
lazy.TabStateCache.update(tab.permanentKey, {
image: null,
});
},
document
.getElementById("context_zen-edit-tab-title")
.addEventListener("command", event => {
gZenVerticalTabsManager.renameTabStart(event);
});
document
.getElementById("context_zen-edit-tab-icon")
.addEventListener("command", () => {
const tab = TabContextMenu.contextTab;
gZenEmojiPicker.open(tab.iconImage, {
emojiAsSVG: true,
closeOnSelect: false,
allowNone: Boolean(tab.zenStaticIcon),
onSelect: icon => {
if (icon) {
tab.zenStaticIcon = icon;
} else {
delete tab.zenStaticIcon;
}
gBrowser.setIcon(tab, icon);
lazy.TabStateCache.update(tab.permanentKey, {
image: null,
});
},
});
});
});
}
updatePinnedTabContextMenu(contextTab) {
@@ -531,12 +561,16 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
}
const isVisible = contextTab.pinned && !contextTab.multiselected;
const isEssential = contextTab.getAttribute("zen-essential");
const zenAddEssential = document.getElementById("context_zen-add-essential");
const zenResetPinnedTab = document.getElementById("context_zen-reset-pinned-tab");
const zenAddEssential = document.getElementById(
"context_zen-add-essential"
);
const zenResetPinnedTab = document.getElementById(
"context_zen-reset-pinned-tab"
);
const zenReplacePinnedUrl = document.getElementById(
"context_zen-replace-pinned-url-with-current"
);
[zenResetPinnedTab, zenReplacePinnedUrl].forEach((element) => {
[zenResetPinnedTab, zenReplacePinnedUrl].forEach(element => {
if (element) {
element.hidden = !isVisible;
document.l10n.setArgs(element, { isEssential });
@@ -548,19 +582,23 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
num: gBrowser._numZenEssentials,
max: this.maxEssentialTabs,
})
.then((badgeText) => {
.then(badgeText => {
zenAddEssential.setAttribute("badge", badgeText);
});
document
.getElementById("cmd_contextZenAddToEssentials")
.setAttribute("disabled", !this.canEssentialBeAdded(contextTab));
document.getElementById("context_closeTab").hidden = contextTab.hasAttribute("zen-essential");
document.getElementById("context_zen-remove-essential").hidden = !isEssential;
document.getElementById("context_closeTab").hidden =
contextTab.hasAttribute("zen-essential");
document.getElementById("context_zen-remove-essential").hidden =
!isEssential;
document.getElementById("context_unpinTab").hidden =
document.getElementById("context_unpinTab").hidden || isEssential;
document.getElementById("context_unpinSelectedTabs").hidden =
document.getElementById("context_unpinSelectedTabs").hidden || isEssential;
document.getElementById("context_zen-pinned-tab-separator").hidden = !isVisible;
document.getElementById("context_unpinSelectedTabs").hidden ||
isEssential;
document.getElementById("context_zen-pinned-tab-separator").hidden =
!isVisible;
document.getElementById("context_zen-edit-tab-title").hidden =
isEssential ||
!Services.prefs.getBoolPref("zen.tabs.rename-tabs") ||
@@ -568,12 +606,17 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
}
// eslint-disable-next-line complexity
moveToAnotherTabContainerIfNecessary(event, draggedTab, movingTabs, dropIndex) {
moveToAnotherTabContainerIfNecessary(
event,
draggedTab,
movingTabs,
dropIndex
) {
let newIndex = dropIndex;
let fromDifferentWindow = false;
movingTabs = Array.from(movingTabs || draggedTab)
.reverse()
.map((tab) => {
.map(tab => {
if (!gBrowser.isTab(tab)) {
return tab;
}
@@ -582,13 +625,12 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
fromDifferentWindow = true;
if (
!tab.hasAttribute("zen-essential") &&
tab.getAttribute("zen-workspace-id") != gZenWorkspaces.activeWorkspace
tab.getAttribute("zen-workspace-id") !=
gZenWorkspaces.activeWorkspace
) {
workspaceId = gZenWorkspaces.activeWorkspace;
tab.ownerGlobal.gBrowser.selectedTab = tab.ownerGlobal.gBrowser._findTabToBlurTo(
tab,
movingTabs
);
tab.ownerGlobal.gBrowser.selectedTab =
tab.ownerGlobal.gBrowser._findTabToBlurTo(tab, movingTabs);
tab.ownerGlobal.gZenWorkspaces.moveTabToWorkspace(tab, workspaceId);
}
// Move the tabs into this window. To avoid multiple tab-switches in
@@ -617,14 +659,17 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
const pinnedTabsTarget = event.target.closest(
":is(.zen-current-workspace-indicator, .zen-workspace-pinned-tabs-section)"
);
const essentialTabsTarget = event.target.closest(".zen-essentials-container");
const essentialTabsTarget = event.target.closest(
".zen-essentials-container"
);
const tabsTarget = !pinnedTabsTarget;
let currentEssenialContainer = gZenWorkspaces.getCurrentEssentialsContainer();
let currentEssenialContainer =
gZenWorkspaces.getCurrentEssentialsContainer();
if (currentEssenialContainer?.essentialsPromo) {
currentEssenialContainer.essentialsPromo.remove();
}
movingTabs = movingTabs.filter((tab) =>
movingTabs = movingTabs.filter(tab =>
gBrowser.isTabGroupLabel(tab) && tab.group?.isZenFolder
? !tabsTarget && !essentialTabsTarget
: true
@@ -651,7 +696,10 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
let isRegularTabs = false;
// Check for essentials container
if (essentialTabsTarget) {
if (!tab.hasAttribute("zen-essential") && !tab?.group?.hasAttribute("split-view-group")) {
if (
!tab.hasAttribute("zen-essential") &&
!tab?.group?.hasAttribute("split-view-group")
) {
moved = true;
isVertical = false;
hasActuallyMoved = this.addToEssentials(tab);
@@ -775,7 +823,10 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
} else {
tab.setAttribute("zen-pinned-changed", "true");
}
tab.style.setProperty("--zen-original-tab-icon", `url(${tab._zenPinnedInitialState.image})`);
tab.style.setProperty(
"--zen-original-tab-icon",
`url(${tab._zenPinnedInitialState.image})`
);
}
removeTabContainersDragoverClass(hideIndicator = true) {
@@ -796,7 +847,9 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
}
get expandedSidebarMode() {
return document.documentElement.getAttribute("zen-sidebar-expanded") === "true";
return (
document.documentElement.getAttribute("zen-sidebar-expanded") === "true"
);
}
canEssentialBeAdded(tab) {
@@ -823,16 +876,24 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
this.removeTabContainersDragoverClass();
return;
}
const pinnedTabsTarget = event.target.closest(".zen-workspace-pinned-tabs-section");
const essentialTabsTarget = event.target.closest(".zen-essentials-container");
const tabsTarget = event.target.closest(".zen-workspace-normal-tabs-section");
const pinnedTabsTarget = event.target.closest(
".zen-workspace-pinned-tabs-section"
);
const essentialTabsTarget = event.target.closest(
".zen-essentials-container"
);
const tabsTarget = event.target.closest(
".zen-workspace-normal-tabs-section"
);
const folderTarget = event.target.closest("zen-folder");
let targetTab = event.target.closest(".tabbrowser-tab");
targetTab = targetTab?.group || targetTab;
draggedTab = draggedTab?.group?.hasAttribute("split-view-group")
? draggedTab.group
: draggedTab;
const isHoveringIndicator = !!event.target.closest(".zen-current-workspace-indicator");
const isHoveringIndicator = !!event.target.closest(
".zen-current-workspace-indicator"
);
if (isHoveringIndicator) {
this.removeTabContainersDragoverClass(false);
gZenWorkspaces.activeWorkspaceIndicator?.setAttribute("open", true);
@@ -848,7 +909,10 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
// Decide whether we should show a dragover class for the given target
if (essentialTabsTarget) {
if (!draggedTab.hasAttribute("zen-essential") && this.canEssentialBeAdded(draggedTab)) {
if (
!draggedTab.hasAttribute("zen-essential") &&
this.canEssentialBeAdded(draggedTab)
) {
shouldAddDragOverElement = true;
isVertical = false;
}
@@ -862,7 +926,11 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
}
}
if (!shouldAddDragOverElement || (!targetTab && !folderTarget) || !targetTab) {
if (
!shouldAddDragOverElement ||
(!targetTab && !folderTarget) ||
!targetTab
) {
this.removeTabContainersDragoverClass(!isHoveringIndicator);
return;
}
@@ -885,8 +953,14 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
shouldPlayHapticFeedback = true;
}
indicator.setAttribute("orientation", "horizontal");
indicator.style.setProperty("--indicator-left", rect.left + separation / 2 + "px");
indicator.style.setProperty("--indicator-width", rect.width - separation + "px");
indicator.style.setProperty(
"--indicator-left",
rect.left + separation / 2 + "px"
);
indicator.style.setProperty(
"--indicator-width",
rect.width - separation + "px"
);
indicator.style.top = top;
indicator.style.removeProperty("left");
} else {
@@ -903,8 +977,14 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
shouldPlayHapticFeedback = true;
}
indicator.setAttribute("orientation", "vertical");
indicator.style.setProperty("--indicator-top", rect.top + separation / 2 + "px");
indicator.style.setProperty("--indicator-height", rect.height - separation + "px");
indicator.style.setProperty(
"--indicator-top",
rect.top + separation / 2 + "px"
);
indicator.style.setProperty(
"--indicator-height",
rect.height - separation + "px"
);
indicator.style.left = left;
indicator.style.removeProperty("top");
}
@@ -915,7 +995,9 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
}
onTabLabelChanged(tab) {
tab.dispatchEvent(new CustomEvent("ZenTabLabelChanged", { bubbles: true, detail: { tab } }));
tab.dispatchEvent(
new CustomEvent("ZenTabLabelChanged", { bubbles: true, detail: { tab } })
);
}
}

View File

@@ -5,7 +5,7 @@
function goToRightSideTabs(callback) {
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve) => {
return new Promise(async resolve => {
await SpecialPowers.pushPrefEnv({
set: [["zen.tabs.vertical.right-side", true]],
});
@@ -23,13 +23,13 @@ function goToRightSideTabs(callback) {
async function testSidebarWidth() {
let resolvePromise;
const promise = new Promise((resolve) => {
const promise = new Promise(resolve => {
resolvePromise = resolve;
});
let hasRan = false;
const ogSize = gNavToolbox.getBoundingClientRect().width;
const onCompactChanged = (_event) => {
const onCompactChanged = _event => {
if (hasRan) {
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
setTimeout(() => {
@@ -40,7 +40,9 @@ async function testSidebarWidth() {
}
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
setTimeout(() => {
const newSize = gNavToolbox.style.getPropertyValue("--zen-sidebar-width").replace("px", "");
const newSize = gNavToolbox.style
.getPropertyValue("--zen-sidebar-width")
.replace("px", "");
Assert.equal(
newSize,
ogSize,

View File

@@ -4,7 +4,12 @@
"use strict";
add_task(async function test_Container_Essentials_Auto_Swithc() {
await gZenWorkspaces.createAndSaveWorkspace("Container Profile 1", undefined, false, 1);
await gZenWorkspaces.createAndSaveWorkspace(
"Container Profile 1",
undefined,
false,
1
);
const workspaces = gZenWorkspaces.getWorkspaces();
Assert.strictEqual(workspaces.length, 2, "Two workspaces should exist.");
@@ -15,12 +20,14 @@ add_task(async function test_Container_Essentials_Auto_Swithc() {
ok(newTab, "New tab should be opened.");
gZenPinnedTabManager.addToEssentials(newTab);
ok(
newTab.hasAttribute("zen-essential") && newTab.parentNode.getAttribute("container") == "1",
newTab.hasAttribute("zen-essential") &&
newTab.parentNode.getAttribute("container") == "1",
"New tab should be marked as essential."
);
ok(
gBrowser.tabs.find(
(t) => t.hasAttribute("zen-essential") && t.getAttribute("usercontextid") == 1
t =>
t.hasAttribute("zen-essential") && t.getAttribute("usercontextid") == 1
),
"New tab should be marked as essential."
);

View File

@@ -4,7 +4,12 @@
"use strict";
add_task(async function test_Check_Creation() {
await gZenWorkspaces.createAndSaveWorkspace("Container Profile 1", undefined, false, 1);
await gZenWorkspaces.createAndSaveWorkspace(
"Container Profile 1",
undefined,
false,
1
);
const workspaces = gZenWorkspaces.getWorkspaces();
Assert.strictEqual(workspaces.length, 2, "Two workspaces should exist.");
@@ -16,12 +21,14 @@ add_task(async function test_Check_Creation() {
ok(newTab, "New tab should be opened.");
gZenPinnedTabManager.addToEssentials(newTab);
ok(
newTab.hasAttribute("zen-essential") && newTab.parentNode.getAttribute("container") == "1",
newTab.hasAttribute("zen-essential") &&
newTab.parentNode.getAttribute("container") == "1",
"New tab should be marked as essential."
);
ok(
gBrowser.tabs.find(
(t) => t.hasAttribute("zen-essential") && t.getAttribute("usercontextid") == 1
t =>
t.hasAttribute("zen-essential") && t.getAttribute("usercontextid") == 1
),
"New tab should be marked as essential."
);
@@ -31,7 +38,8 @@ add_task(async function test_Check_Creation() {
await gZenWorkspaces.changeWorkspace(workspaces[0]);
ok(
!gBrowser.tabs.find(
(t) => t.hasAttribute("zen-essential") && t.getAttribute("usercontextid") == 1
t =>
t.hasAttribute("zen-essential") && t.getAttribute("usercontextid") == 1
),
"No essential tabs should be found in the original workspace."
);

View File

@@ -11,15 +11,31 @@ add_task(async function test_Folder_Density() {
let tabRect = tab.getBoundingClientRect();
let folderRect = folder.labelElement.parentElement.getBoundingClientRect();
Assert.equal(tabRect.height, folderRect.height, "Folder height matches tab height");
Assert.equal(tabRect.width, folderRect.width, "Folder width matches tab width");
Assert.equal(
tabRect.height,
folderRect.height,
"Folder height matches tab height"
);
Assert.equal(
tabRect.width,
folderRect.width,
"Folder width matches tab width"
);
gUIDensity.update(gUIDensity.MODE_TOUCH);
tabRect = tab.getBoundingClientRect();
folderRect = folder.getBoundingClientRect();
Assert.equal(tabRect.height, folderRect.height, "Folder height matches tab height");
Assert.equal(tabRect.width, folderRect.width, "Folder width matches tab width");
Assert.equal(
tabRect.height,
folderRect.height,
"Folder height matches tab height"
);
Assert.equal(
tabRect.width,
folderRect.width,
"Folder width matches tab width"
);
gUIDensity.update();
await removeFolder(folder);

View File

@@ -4,17 +4,26 @@
"use strict";
add_task(async function test_Empty_Tab_First() {
const [tab1, tab2] = await Promise.all([addTabTo(gBrowser), addTabTo(gBrowser)]);
const [tab1, tab2] = await Promise.all([
addTabTo(gBrowser),
addTabTo(gBrowser),
]);
const folder = await gZenFolders.createFolder([tab1], {
renameFolder: false,
});
Assert.equal(folder.tabs.length, 2, "Folder should contain the original tab");
ok(folder.tabs[0].hasAttribute("zen-empty-tab"), "First tab should be an empty tab");
ok(
folder.tabs[0].hasAttribute("zen-empty-tab"),
"First tab should be an empty tab"
);
folder.appendChild(tab2);
Assert.equal(folder.tabs.length, 3, "Folder should contain the second tab");
ok(folder.tabs[0].hasAttribute("zen-empty-tab"), "First tab should be an empty tab");
ok(
folder.tabs[0].hasAttribute("zen-empty-tab"),
"First tab should be an empty tab"
);
await removeFolder(folder);
});

View File

@@ -21,7 +21,10 @@ add_task(async function test_Issue_9885() {
subfolder.labelElement.click();
ok(subfolder.collapsed, "Subfolder should be collapsed after clicking on it");
ok(!parent.collapsed, "Parent folder should be collapsed after clicking on subfolder");
ok(
!parent.collapsed,
"Parent folder should be collapsed after clicking on subfolder"
);
await removeFolder(subfolder);
await removeFolder(parent);

View File

@@ -14,10 +14,17 @@ add_task(async function test_Issue_9981() {
gBrowser.addRangeToMultiSelectedTabs(tab1, tab2);
ok(tab1.multiselected, "Tab 1 should be multiselected");
ok(tab2.multiselected, "Tab 2 should be multiselected");
Assert.equal(gBrowser.multiSelectedTabsCount, 2, "There should be 2 multiselected tabs");
await new Promise((resolve) => setTimeout(resolve, 0));
Assert.equal(
gBrowser.multiSelectedTabsCount,
2,
"There should be 2 multiselected tabs"
);
await new Promise(resolve => setTimeout(resolve, 0));
const collapseEvent = BrowserTestUtils.waitForEvent(window, "TabGroupCollapse");
const collapseEvent = BrowserTestUtils.waitForEvent(
window,
"TabGroupCollapse"
);
EventUtils.synthesizeMouseAtCenter(folder.labelElement, {});
await collapseEvent;
@@ -26,15 +33,27 @@ add_task(async function test_Issue_9981() {
Assert.equal(folder.activeTabs.length, 2, "Folder should have 2 active tabs");
ok(tab1.hasAttribute("folder-active"), "Tab 1 should be in the active folder");
ok(tab2.hasAttribute("folder-active"), "Tab 2 should be in the active folder");
ok(
tab1.hasAttribute("folder-active"),
"Tab 1 should be in the active folder"
);
ok(
tab2.hasAttribute("folder-active"),
"Tab 2 should be in the active folder"
);
const tab2ResetButton = tab2.querySelector(".tab-reset-button");
tab2ResetButton.style.display = "flex";
EventUtils.synthesizeMouseAtCenter(tab2ResetButton, {});
ok(tab1.hasAttribute("folder-active"), "Tab 1 should be in the active folder");
ok(!tab2.hasAttribute("folder-active"), "Tab 2 should not be in the active folder");
ok(
tab1.hasAttribute("folder-active"),
"Tab 1 should be in the active folder"
);
ok(
!tab2.hasAttribute("folder-active"),
"Tab 2 should not be in the active folder"
);
await removeFolder(folder);
});

View File

@@ -11,7 +11,9 @@ add_task(async function test_Max_Subfolders() {
renameFolder: false,
});
const subfolderItem = document.getElementById("context_zenFolderNewSubfolder");
const subfolderItem = document.getElementById(
"context_zenFolderNewSubfolder"
);
let currentFolder = folder;
for (let i = 1; i < TEST_MAX_FOLDERS; i++) {
await openFolderContextMenu(currentFolder);
@@ -20,7 +22,10 @@ add_task(async function test_Max_Subfolders() {
"true",
`Subfolder item should be enabled`
);
const folderCreateEvent = BrowserTestUtils.waitForEvent(window, "TabGroupCreate");
const folderCreateEvent = BrowserTestUtils.waitForEvent(
window,
"TabGroupCreate"
);
EventUtils.synthesizeMouseAtCenter(subfolderItem, {});
await folderCreateEvent;
const items = currentFolder.allItems;

View File

@@ -12,14 +12,25 @@ add_task(async function test_Folder_Multiselected_Tabs() {
gBrowser.addRangeToMultiSelectedTabs(tab1, tab2);
ok(tab1.multiselected, "Tab 1 should be multiselected");
ok(tab2.multiselected, "Tab 2 should be multiselected");
Assert.greater(gBrowser.multiSelectedTabsCount, 1, "There should be 2 multiselected tabs");
Assert.greater(
gBrowser.multiSelectedTabsCount,
1,
"There should be 2 multiselected tabs"
);
const collapseEvent = BrowserTestUtils.waitForEvent(window, "TabGroupCollapse");
const collapseEvent = BrowserTestUtils.waitForEvent(
window,
"TabGroupCollapse"
);
folder.collapsed = true;
await collapseEvent;
ok(tab2.multiselected, "Tab 2 should not be multiselected");
Assert.equal(gBrowser.multiSelectedTabsCount, 3, "There should be 3 multiselected tabs");
Assert.equal(
gBrowser.multiSelectedTabsCount,
3,
"There should be 3 multiselected tabs"
);
for (const t of [tab1, tab2]) {
BrowserTestUtils.removeTab(t);

View File

@@ -13,7 +13,8 @@ add_task(async function test_Duplicate_Tab_Inside_Folder() {
renameFolder: false,
});
gBrowser.selectedTab = tab;
const triggeringPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
const triggeringPrincipal =
Services.scriptSecurityManager.getSystemPrincipal();
gBrowser.addTab("https://example.com", {
tabIndex: undefined,
relatedToCurrent: true,
@@ -28,7 +29,7 @@ add_task(async function test_Duplicate_Tab_Inside_Folder() {
);
/* eslint-disable mozilla/no-arbitrary-setTimeout */
await new Promise((resolve) => setTimeout(resolve, 100));
await new Promise(resolve => setTimeout(resolve, 100));
for (const t of folder.tabs) {
ok(t.pinned, "All tabs in the folder should be pinned");
@@ -49,7 +50,8 @@ add_task(async function test_Duplicate_Tab_Inside_Folder_Unpinned() {
renameFolder: false,
});
gBrowser.selectedTab = tab;
const triggeringPrincipal = Services.scriptSecurityManager.getSystemPrincipal();
const triggeringPrincipal =
Services.scriptSecurityManager.getSystemPrincipal();
let newTab = gBrowser.addTab("https://example.com", {
tabIndex: undefined,
relatedToCurrent: true,

View File

@@ -14,10 +14,17 @@ add_task(async function test_Issue_() {
gBrowser.addRangeToMultiSelectedTabs(tab1, tab2);
ok(tab1.multiselected, "Tab 1 should be multiselected");
ok(tab2.multiselected, "Tab 2 should be multiselected");
Assert.equal(gBrowser.multiSelectedTabsCount, 2, "There should be 2 multiselected tabs");
await new Promise((resolve) => setTimeout(resolve, 0));
Assert.equal(
gBrowser.multiSelectedTabsCount,
2,
"There should be 2 multiselected tabs"
);
await new Promise(resolve => setTimeout(resolve, 0));
const collapseEvent = BrowserTestUtils.waitForEvent(window, "TabGroupCollapse");
const collapseEvent = BrowserTestUtils.waitForEvent(
window,
"TabGroupCollapse"
);
EventUtils.synthesizeMouseAtCenter(folder.labelElement, {});
await collapseEvent;
@@ -26,16 +33,28 @@ add_task(async function test_Issue_() {
Assert.equal(folder.activeTabs.length, 2, "Folder should have 2 active tabs");
ok(tab1.hasAttribute("folder-active"), "Tab 1 should be in the active folder");
ok(tab2.hasAttribute("folder-active"), "Tab 2 should be in the active folder");
ok(
tab1.hasAttribute("folder-active"),
"Tab 1 should be in the active folder"
);
ok(
tab2.hasAttribute("folder-active"),
"Tab 2 should be in the active folder"
);
EventUtils.synthesizeMouseAtCenter(folder.resetButton, {});
await new Promise((resolve) =>
await new Promise(resolve =>
/* eslint-disable mozilla/no-arbitrary-setTimeout */
setTimeout(() => {
ok(!tab1.hasAttribute("folder-active"), "Tab 1 should not be in the active folder");
ok(!tab2.hasAttribute("folder-active"), "Tab 2 should not be in the active folder");
ok(
!tab1.hasAttribute("folder-active"),
"Tab 1 should not be in the active folder"
);
ok(
!tab2.hasAttribute("folder-active"),
"Tab 2 should not be in the active folder"
);
resolve();
}, 500)
);

View File

@@ -33,7 +33,10 @@ add_task(async function test_Visible_Selected() {
gBrowser.selectedTab = tab;
folder.collapsed = true;
ok(tab.visible, "Tab is visible in the folder when collapsed");
ok(tab.hasAttribute("folder-active"), "Tab is marked as active in the folder when selected");
ok(
tab.hasAttribute("folder-active"),
"Tab is marked as active in the folder when selected"
);
ok(
tab.group.hasAttribute("has-active"),
"Tab group is marked as active when the tab is selected"
@@ -63,7 +66,10 @@ add_task(async function test_Visible_Not_Selected() {
folder.collapsed = true;
gBrowser.selectedTab = originalTab;
ok(tab.visible, "Tab is visible in the folder when collapsed");
ok(tab.hasAttribute("folder-active"), "Tab is marked as active in the folder when selected");
ok(
tab.hasAttribute("folder-active"),
"Tab is marked as active in the folder when selected"
);
ok(
tab.group.hasAttribute("has-active"),
"Tab group is marked as active when the tab is selected"

View File

@@ -21,7 +21,11 @@ async function openFolderContextMenu(folder) {
await menuEvent;
}
async function addTabTo(targetBrowser, url = "http://mochi.test:8888/", params = {}) {
async function addTabTo(
targetBrowser,
url = "http://mochi.test:8888/",
params = {}
) {
params.skipAnimation = true;
const tab = BrowserTestUtils.addTab(targetBrowser, url, params);
const browser = targetBrowser.getBrowserForTab(tab);

View File

@@ -4,7 +4,7 @@
"use strict";
add_task(async function test_Glance_Basic_Open() {
await openGlanceOnTab(async (glanceTab) => {
await openGlanceOnTab(async glanceTab => {
ok(
glanceTab.hasAttribute("zen-glance-tab"),
"The glance tab should have the zen-glance-tab attribute"

View File

@@ -5,13 +5,13 @@
add_task(async function test_Glance_Basic_Close() {
const currentTab = gBrowser.selectedTab;
await openGlanceOnTab(async (glanceTab) => {
await openGlanceOnTab(async glanceTab => {
ok(
currentTab.hasAttribute("glance-id"),
"The glance tab should have the zen-glance-tab attribute"
);
await BrowserTestUtils.removeTab(glanceTab);
await new Promise((resolve) => {
await new Promise(resolve => {
/* eslint-disable mozilla/no-arbitrary-setTimeout */
setTimeout(() => {
resolve();

View File

@@ -4,7 +4,7 @@
"use strict";
async function openAndCloseGlance() {
await openGlanceOnTab(async (glanceTab) => {
await openGlanceOnTab(async glanceTab => {
ok(
glanceTab.hasAttribute("zen-glance-tab"),
"The glance tab should have the zen-glance-tab attribute"
@@ -15,7 +15,11 @@ async function openAndCloseGlance() {
add_task(async function test_Glance_Close_No_Tabs() {
const currentTab = gBrowser.selectedTab;
await openAndCloseGlance();
Assert.equal(gBrowser.selectedTab, currentTab, "The original tab should be selected");
Assert.equal(
gBrowser.selectedTab,
currentTab,
"The original tab should be selected"
);
ok(currentTab.selected, "The original tab should be visually selected");
});
@@ -27,7 +31,11 @@ add_task(async function test_Glance_Close_With_Next_Tab() {
const selectedTab = gBrowser.selectedTab;
Assert.notEqual(selectedTab, originalTab, "A new tab should be selected");
await openAndCloseGlance();
Assert.equal(gBrowser.selectedTab, selectedTab, "The new tab should still be selected");
Assert.equal(
gBrowser.selectedTab,
selectedTab,
"The new tab should still be selected"
);
ok(selectedTab.selected, "The new tab should be visually selected");
gBrowser.selectedTab = originalTab;

View File

@@ -5,14 +5,14 @@
add_task(async function test_Glance_Basic_Open() {
const selectedTab = gBrowser.selectedTab;
await openGlanceOnTab(async (glanceTab) => {
await openGlanceOnTab(async glanceTab => {
await gZenGlanceManager.fullyOpenGlance();
ok(
!glanceTab.hasAttribute("zen-glance-tab"),
"The glance tab should not have the zen-glance-tab attribute"
);
Assert.strictEqual(
gBrowser.tabs.filter((tab) => tab.hasAttribute("zen-glance-tab")).length,
gBrowser.tabs.filter(tab => tab.hasAttribute("zen-glance-tab")).length,
0,
"There should be no zen-glance-tab attribute on any tab"
);
@@ -28,14 +28,18 @@ add_task(async function test_Glance_Basic_Open() {
add_task(async function test_Glance_Open_Sibling() {
const tabsToRemove = [];
for (let i = 0; i < 5; i++) {
await BrowserTestUtils.openNewForegroundTab(window.gBrowser, "https://example.com/", true);
await BrowserTestUtils.openNewForegroundTab(
window.gBrowser,
"https://example.com/",
true
);
tabsToRemove.push(gBrowser.selectedTab);
}
gBrowser.selectedTab = gBrowser.tabs[2];
const selectedTab = gBrowser.selectedTab;
await openGlanceOnTab(async (glanceTab) => {
await openGlanceOnTab(async glanceTab => {
await gZenGlanceManager.fullyOpenGlance();
Assert.equal(
glanceTab._tPos,
@@ -53,18 +57,22 @@ add_task(async function test_Glance_Open_Sibling() {
add_task(async function test_Glance_Basic_Open() {
const tabsToRemove = [];
for (let i = 0; i < 3; i++) {
await BrowserTestUtils.openNewForegroundTab(window.gBrowser, "https://example.com/", true);
await BrowserTestUtils.openNewForegroundTab(
window.gBrowser,
"https://example.com/",
true
);
gBrowser.pinTab(gBrowser.selectedTab);
tabsToRemove.push(gBrowser.selectedTab);
}
gBrowser.selectedTab = gBrowser.tabs.find((tab) => tab.pinned);
gBrowser.selectedTab = gBrowser.tabs.find(tab => tab.pinned);
await openGlanceOnTab(async (glanceTab) => {
await openGlanceOnTab(async glanceTab => {
await gZenGlanceManager.fullyOpenGlance();
Assert.equal(
glanceTab,
gBrowser.visibleTabs.find((tab) => !tab.pinned),
gBrowser.visibleTabs.find(tab => !tab.pinned),
"The glance tab should be the first normal tab (Ignoring empty tabs)"
);
BrowserTestUtils.removeTab(glanceTab);
@@ -79,18 +87,21 @@ add_task(async function test_Glance_New_From_essential() {
ok(true, "todo:");
return; // TODO: Fix this test, it currently fails
/* eslint-disable no-unreachable, no-unused-vars */
await BrowserTestUtils.withNewTab({ gBrowser, url: "https://example.com/" }, async (browser) => {
const selectedTab = gBrowser.selectedTab;
gZenPinnedTabManager.addToEssentials(selectedTab);
await openGlanceOnTab(async (glanceTab) => {
await gZenGlanceManager.fullyOpenGlance();
ok(!glanceTab.pinned, "The glance tab should not be pinned");
ok(
!glanceTab.parentNode.hasAttribute("container"),
"The glance tab should not be in an essentials container"
);
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
await BrowserTestUtils.removeTab(glanceTab);
}, false);
});
await BrowserTestUtils.withNewTab(
{ gBrowser, url: "https://example.com/" },
async browser => {
const selectedTab = gBrowser.selectedTab;
gZenPinnedTabManager.addToEssentials(selectedTab);
await openGlanceOnTab(async glanceTab => {
await gZenGlanceManager.fullyOpenGlance();
ok(!glanceTab.pinned, "The glance tab should not be pinned");
ok(
!glanceTab.parentNode.hasAttribute("container"),
"The glance tab should not be in an essentials container"
);
await BrowserTestUtils.removeTab(gBrowser.selectedTab);
await BrowserTestUtils.removeTab(glanceTab);
}, false);
}
);
});

View File

@@ -5,14 +5,19 @@
add_task(async function test_Glance_Next_Tab() {
const tabToCheck = gBrowser.selectedTab;
await BrowserTestUtils.openNewForegroundTab(window.gBrowser, "https://example.com/", true, {
skipAnimation: true,
});
await openGlanceOnTab(async (glanceTab) => {
await BrowserTestUtils.openNewForegroundTab(
window.gBrowser,
"https://example.com/",
true,
{
skipAnimation: true,
}
);
await openGlanceOnTab(async glanceTab => {
gBrowser.tabContainer.advanceSelectedTab(1);
const nextTab = gBrowser.selectedTab;
gBrowser.selectedTab = glanceTab;
await new Promise((resolve) => {
await new Promise(resolve => {
setTimeout(() => {
Assert.equal(nextTab, tabToCheck, "Next glance tab should equal");
resolve();

View File

@@ -4,16 +4,21 @@
"use strict";
add_task(async function test_Glance_Prev_Tab() {
await openGlanceOnTab(async (glanceTab) => {
await BrowserTestUtils.openNewForegroundTab(window.gBrowser, "https://example.com/", true, {
skipAnimation: true,
});
await openGlanceOnTab(async glanceTab => {
await BrowserTestUtils.openNewForegroundTab(
window.gBrowser,
"https://example.com/",
true,
{
skipAnimation: true,
}
);
const tabToCheck = gBrowser.selectedTab;
gBrowser.selectedTab = glanceTab;
gBrowser.tabContainer.advanceSelectedTab(-1);
const prevTab = gBrowser.selectedTab;
gBrowser.selectedTab = glanceTab;
await new Promise((resolve) => {
await new Promise(resolve => {
setTimeout(() => {
Assert.equal(prevTab, tabToCheck, "Previous glance tab should equal");
resolve();

View File

@@ -4,14 +4,19 @@
"use strict";
add_task(async function test_Glance_Select_Parent() {
await openGlanceOnTab(async (glanceTab) => {
await openGlanceOnTab(async glanceTab => {
ok(
glanceTab.hasAttribute("zen-glance-tab"),
"The glance tab should have the zen-glance-tab attribute"
);
await BrowserTestUtils.openNewForegroundTab(window.gBrowser, "https://example.com/", true, {
skipAnimation: true,
});
await BrowserTestUtils.openNewForegroundTab(
window.gBrowser,
"https://example.com/",
true,
{
skipAnimation: true,
}
);
const tabToRemove = gBrowser.selectedTab;
gBrowser.selectedTab = gZenGlanceManager.getTabOrGlanceParent(glanceTab);
await BrowserTestUtils.waitForCondition(() => {

View File

@@ -5,7 +5,8 @@
ChromeUtils.defineESModuleGetters(this, {
sinon: "resource://testing-common/Sinon.sys.mjs",
nsGithubLiveFolderProvider: "resource:///modules/zen/GithubLiveFolder.sys.mjs",
nsGithubLiveFolderProvider:
"resource:///modules/zen/GithubLiveFolder.sys.mjs",
});
function getGithubProviderForTest(sandbox, customOptions = {}) {
@@ -39,7 +40,9 @@ function getGithubProviderForTest(sandbox, customOptions = {}) {
}
add_task(async function test_fetch_items_url_construction() {
info("should construct the correct GitHub search URL based on default options");
info(
"should construct the correct GitHub search URL based on default options"
);
let sandbox = sinon.createSandbox();
@@ -69,7 +72,10 @@ add_task(async function test_fetch_items_url_construction() {
Assert.ok(query.includes("is:pr"), "Should include is:PR");
Assert.ok(query.includes("author:@me"), "Should include author:@me");
Assert.ok(!query.includes("assignee:@me"), "Should NOT include assignee:@me");
Assert.ok(!query.includes("review-requested:@me"), "Should NOT include review-requested");
Assert.ok(
!query.includes("review-requested:@me"),
"Should NOT include review-requested"
);
sandbox.restore();
});
@@ -97,7 +103,10 @@ add_task(async function test_fetch_items_url_complex_options() {
Assert.ok(query.includes("author:@me"), "Should include author");
Assert.ok(query.includes("assignee:@me"), "Should include assignee");
Assert.ok(query.includes("review-requested:@me"), "Should include review-requested");
Assert.ok(
query.includes("review-requested:@me"),
"Should include review-requested"
);
Assert.ok(query.includes(" OR "), "Should contain OR operators");
sandbox.restore();
@@ -159,7 +168,11 @@ add_task(async function test_fetch_network_error() {
instance.fetch.rejects(new Error("Network down"));
const errorId = await instance.fetchItems();
Assert.equal(errorId, "zen-live-folder-failed-fetch", "Should return an error on failed fetch");
Assert.equal(
errorId,
"zen-live-folder-failed-fetch",
"Should return an error on failed fetch"
);
sandbox.restore();
});

View File

@@ -9,7 +9,7 @@ ChromeUtils.defineESModuleGetters(this, {
});
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
return new Promise(resolve => setTimeout(resolve, ms));
}
describe("Zen Live Folder Scheduling", () => {
@@ -64,7 +64,11 @@ describe("Zen Live Folder Scheduling", () => {
const startSpy = sandbox.spy(instance, "start");
await sleep(INTERVAL + INTERVAL_OFFSET);
Assert.equal(fetchStub.callCount, 1, "Should have fetched once after the first interval");
Assert.equal(
fetchStub.callCount,
1,
"Should have fetched once after the first interval"
);
await sleep(INTERVAL + INTERVAL_OFFSET);
Assert.equal(fetchStub.callCount, 2, "Should have fetched 2 times");
@@ -107,7 +111,11 @@ describe("Zen Live Folder Scheduling", () => {
Assert.equal(fetchStub.callCount, 1, "Should have fetched once");
await sleep(INTERVAL + INTERVAL_OFFSET);
Assert.equal(fetchStub.callCount, 2, "Should have fetched once with normal interval");
Assert.equal(
fetchStub.callCount,
2,
"Should have fetched once with normal interval"
);
});
it("should re-start the timer if interval was changed", async () => {
@@ -121,7 +129,11 @@ describe("Zen Live Folder Scheduling", () => {
sinon.assert.notCalled(fetchStub);
await sleep(INTERVAL + INTERVAL_OFFSET);
Assert.equal(fetchStub.callCount, 1, "Should have fetched once after the first interval");
Assert.equal(
fetchStub.callCount,
1,
"Should have fetched once after the first interval"
);
const NEW_INTERVAL = 500;
instance.state.interval = NEW_INTERVAL;
@@ -130,6 +142,10 @@ describe("Zen Live Folder Scheduling", () => {
instance.start();
await sleep(NEW_INTERVAL + INTERVAL_OFFSET);
Assert.equal(fetchStub.callCount, 2, "Should have once after the new interval");
Assert.equal(
fetchStub.callCount,
2,
"Should have once after the new interval"
);
});
});

View File

@@ -210,7 +210,11 @@ add_task(async function test_invalid_dates() {
const items = await instance.fetchItems();
Assert.equal(items.length, 0, "Items with invalid/missing dates should be filtered");
Assert.equal(
items.length,
0,
"Items with invalid/missing dates should be filtered"
);
sandbox.restore();
});
@@ -223,7 +227,11 @@ add_task(async function test_fetch_network_error() {
instance.fetch.rejects(new Error("Network down"));
const items = await instance.fetchItems();
Assert.equal(items, "zen-live-folder-failed-fetch", "Should return an error on failed fetch");
Assert.equal(
items,
"zen-live-folder-failed-fetch",
"Should return an error on failed fetch"
);
sandbox.restore();
});

View File

@@ -8,7 +8,10 @@ const { TabStateFlusher } = ChromeUtils.importESModule(
);
async function makeNewEmptyTab() {
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank");
let tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
"about:blank"
);
gBrowser.selectedTab = tab;
return tab;
}
@@ -55,7 +58,10 @@ add_task(async function test_Restore_Essential_Tab() {
await TabStateFlusher.flushWindow(window);
SessionWindowUI.restoreLastClosedTabOrWindowOrSession(window);
tab = gBrowser.selectedTab;
ok(tab.hasAttribute("zen-essential"), "The tab should be marked as essential after restore");
ok(
tab.hasAttribute("zen-essential"),
"The tab should be marked as essential after restore"
);
ok(
tab.parentElement.closest(".zen-essentials-container"),
"The tab should be in the essentials tabs section after restore"

View File

@@ -5,28 +5,38 @@
add_task(async function test_Changed_Pinned() {
let resolvePromise;
const promise = new Promise((resolve) => {
const promise = new Promise(resolve => {
resolvePromise = resolve;
});
await BrowserTestUtils.withNewTab({ gBrowser, url: "https://example.com/1" }, async (browser) => {
const tab = gBrowser.getTabForBrowser(browser);
gBrowser.pinTab(tab);
await BrowserTestUtils.withNewTab(
{ gBrowser, url: "https://example.com/1" },
async browser => {
const tab = gBrowser.getTabForBrowser(browser);
gBrowser.pinTab(tab);
ok(tab.pinned, "The tab should be pinned after calling gBrowser.pinTab()");
await gBrowser.TabStateFlusher.flush(browser);
await new Promise((r) => setTimeout(r, 500));
BrowserTestUtils.startLoadingURIString(browser, "https://example.com/2");
await BrowserTestUtils.browserLoaded(browser, false, "https://example.com/2");
setTimeout(() => {
ok(
tab.hasAttribute("zen-pinned-changed"),
"The tab should have a zen-pinned-changed attribute after being pinned"
tab.pinned,
"The tab should be pinned after calling gBrowser.pinTab()"
);
resolvePromise();
}, 500);
await promise;
});
await gBrowser.TabStateFlusher.flush(browser);
await new Promise(r => setTimeout(r, 500));
BrowserTestUtils.startLoadingURIString(browser, "https://example.com/2");
await BrowserTestUtils.browserLoaded(
browser,
false,
"https://example.com/2"
);
setTimeout(() => {
ok(
tab.hasAttribute("zen-pinned-changed"),
"The tab should have a zen-pinned-changed attribute after being pinned"
);
resolvePromise();
}, 500);
await promise;
}
);
});

View File

@@ -9,20 +9,23 @@ add_task(async function test_Unload_NoReset_Pinned() {
});
let resolvePromise;
const promise = new Promise((resolve) => {
const promise = new Promise(resolve => {
resolvePromise = resolve;
});
await BrowserTestUtils.withNewTab({ gBrowser, url: "https://example.com/1" }, async (browser) => {
const tab = gBrowser.getTabForBrowser(browser);
gBrowser.pinTab(tab);
await BrowserTestUtils.withNewTab(
{ gBrowser, url: "https://example.com/1" },
async browser => {
const tab = gBrowser.getTabForBrowser(browser);
gBrowser.pinTab(tab);
document.getElementById("cmd_close").doCommand();
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
setTimeout(() => {
ok(tab.closing, "The tab should be closing after being closed");
resolvePromise();
}, 100);
await promise;
});
document.getElementById("cmd_close").doCommand();
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
setTimeout(() => {
ok(tab.closing, "The tab should be closing after being closed");
resolvePromise();
}, 100);
await promise;
}
);
});

View File

@@ -5,11 +5,15 @@
add_task(async function test_Create_Pinned() {
let resolvePromise;
const promise = new Promise((resolve) => {
const promise = new Promise(resolve => {
resolvePromise = resolve;
});
await BrowserTestUtils.openNewForegroundTab(window.gBrowser, "https://example.com/", true);
await BrowserTestUtils.openNewForegroundTab(
window.gBrowser,
"https://example.com/",
true
);
const newTab = gBrowser.selectedTab;
gBrowser.pinTab(newTab);
@@ -17,9 +21,12 @@ add_task(async function test_Create_Pinned() {
ok(newTab.pinned, "The tab should be pinned after calling gBrowser.pinTab()");
await new Promise((r) => setTimeout(r, 500));
await new Promise(r => setTimeout(r, 500));
const pinObject = newTab._zenPinnedInitialState;
ok(pinObject, "The pin object should be created in the tab's _zenPinnedInitialState");
ok(
pinObject,
"The pin object should be created in the tab's _zenPinnedInitialState"
);
Assert.equal(
pinObject.entry.url,
"https://example.com/",

View File

@@ -9,38 +9,48 @@ add_task(async function test_NoUnload_Changed_Pinned() {
});
let resolvePromise;
const promise = new Promise((resolve) => {
const promise = new Promise(resolve => {
resolvePromise = resolve;
});
await BrowserTestUtils.withNewTab({ gBrowser, url: "https://example.com/1" }, async (browser) => {
const tab = gBrowser.getTabForBrowser(browser);
gBrowser.pinTab(tab);
await BrowserTestUtils.withNewTab(
{ gBrowser, url: "https://example.com/1" },
async browser => {
const tab = gBrowser.getTabForBrowser(browser);
gBrowser.pinTab(tab);
BrowserTestUtils.startLoadingURIString(browser, "https://example.com/2");
await BrowserTestUtils.browserLoaded(browser, false, "https://example.com/2");
await gBrowser.TabStateFlusher.flush(browser);
setTimeout(() => {
ok(
tab.hasAttribute("zen-pinned-changed"),
"The tab should have a zen-pinned-changed attribute after being pinned"
BrowserTestUtils.startLoadingURIString(browser, "https://example.com/2");
await BrowserTestUtils.browserLoaded(
browser,
false,
"https://example.com/2"
);
document.getElementById("cmd_close").doCommand();
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
await gBrowser.TabStateFlusher.flush(browser);
setTimeout(() => {
ok(
!tab.hasAttribute("zen-pinned-changed"),
"The tab should not have a zen-pinned-changed attribute after being closed"
tab.hasAttribute("zen-pinned-changed"),
"The tab should have a zen-pinned-changed attribute after being pinned"
);
ok(!tab.hasAttribute("discarded"), "The tab should not be discarded after being closed");
Assert.notEqual(
tab,
gBrowser.selectedTab,
"The tab should not be selected after being closed"
);
resolvePromise();
}, 100);
}, 0);
await promise;
});
document.getElementById("cmd_close").doCommand();
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
setTimeout(() => {
ok(
!tab.hasAttribute("zen-pinned-changed"),
"The tab should not have a zen-pinned-changed attribute after being closed"
);
ok(
!tab.hasAttribute("discarded"),
"The tab should not be discarded after being closed"
);
Assert.notEqual(
tab,
gBrowser.selectedTab,
"The tab should not be selected after being closed"
);
resolvePromise();
}, 100);
}, 0);
await promise;
}
);
});

View File

@@ -9,38 +9,48 @@ add_task(async function test_Unload_NoReset_Pinned() {
});
let resolvePromise;
const promise = new Promise((resolve) => {
const promise = new Promise(resolve => {
resolvePromise = resolve;
});
await BrowserTestUtils.withNewTab({ gBrowser, url: "https://example.com/1" }, async (browser) => {
const tab = gBrowser.getTabForBrowser(browser);
gBrowser.pinTab(tab);
await BrowserTestUtils.withNewTab(
{ gBrowser, url: "https://example.com/1" },
async browser => {
const tab = gBrowser.getTabForBrowser(browser);
gBrowser.pinTab(tab);
BrowserTestUtils.startLoadingURIString(browser, "https://example.com/2");
await BrowserTestUtils.browserLoaded(browser, false, "https://example.com/2");
await gBrowser.TabStateFlusher.flush(browser);
setTimeout(() => {
ok(
tab.hasAttribute("zen-pinned-changed"),
"The tab should have a zen-pinned-changed attribute after being pinned"
BrowserTestUtils.startLoadingURIString(browser, "https://example.com/2");
await BrowserTestUtils.browserLoaded(
browser,
false,
"https://example.com/2"
);
document.getElementById("cmd_close").doCommand();
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
await gBrowser.TabStateFlusher.flush(browser);
setTimeout(() => {
ok(
!tab.hasAttribute("zen-pinned-changed"),
"The tab should not have a zen-pinned-changed attribute after being closed"
tab.hasAttribute("zen-pinned-changed"),
"The tab should have a zen-pinned-changed attribute after being pinned"
);
ok(!tab.hasAttribute("discarded"), "The tab should not be discarded after being closed");
Assert.strictEqual(
tab,
gBrowser.selectedTab,
"The tab should not be selected after being closed"
);
resolvePromise();
}, 100);
}, 0);
await promise;
});
document.getElementById("cmd_close").doCommand();
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
setTimeout(() => {
ok(
!tab.hasAttribute("zen-pinned-changed"),
"The tab should not have a zen-pinned-changed attribute after being closed"
);
ok(
!tab.hasAttribute("discarded"),
"The tab should not be discarded after being closed"
);
Assert.strictEqual(
tab,
gBrowser.selectedTab,
"The tab should not be selected after being closed"
);
resolvePromise();
}, 100);
}, 0);
await promise;
}
);
});

View File

@@ -9,38 +9,48 @@ add_task(async function test_Unload_NoReset_Pinned() {
});
let resolvePromise;
const promise = new Promise((resolve) => {
const promise = new Promise(resolve => {
resolvePromise = resolve;
});
await BrowserTestUtils.withNewTab({ gBrowser, url: "https://example.com/1" }, async (browser) => {
const tab = gBrowser.getTabForBrowser(browser);
gBrowser.pinTab(tab);
await BrowserTestUtils.withNewTab(
{ gBrowser, url: "https://example.com/1" },
async browser => {
const tab = gBrowser.getTabForBrowser(browser);
gBrowser.pinTab(tab);
BrowserTestUtils.startLoadingURIString(browser, "https://example.com/2");
await BrowserTestUtils.browserLoaded(browser, false, "https://example.com/2");
await gBrowser.TabStateFlusher.flush(browser);
setTimeout(() => {
ok(
tab.hasAttribute("zen-pinned-changed"),
"The tab should have a zen-pinned-changed attribute after being pinned"
BrowserTestUtils.startLoadingURIString(browser, "https://example.com/2");
await BrowserTestUtils.browserLoaded(
browser,
false,
"https://example.com/2"
);
document.getElementById("cmd_close").doCommand();
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
await gBrowser.TabStateFlusher.flush(browser);
setTimeout(() => {
ok(
tab.hasAttribute("zen-pinned-changed"),
"The tab should not have a zen-pinned-changed attribute after being closed"
"The tab should have a zen-pinned-changed attribute after being pinned"
);
ok(!tab.hasAttribute("discarded"), "The tab should not be discarded after being closed");
Assert.notEqual(
tab,
gBrowser.selectedTab,
"The tab should not be selected after being closed"
);
resolvePromise();
}, 100);
}, 0);
await promise;
});
document.getElementById("cmd_close").doCommand();
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
setTimeout(() => {
ok(
tab.hasAttribute("zen-pinned-changed"),
"The tab should not have a zen-pinned-changed attribute after being closed"
);
ok(
!tab.hasAttribute("discarded"),
"The tab should not be discarded after being closed"
);
Assert.notEqual(
tab,
gBrowser.selectedTab,
"The tab should not be selected after being closed"
);
resolvePromise();
}, 100);
}, 0);
await promise;
}
);
});

View File

@@ -5,11 +5,15 @@
add_task(async function test_Pinned_To_Essential() {
let resolvePromise;
const promise = new Promise((resolve) => {
const promise = new Promise(resolve => {
resolvePromise = resolve;
});
await BrowserTestUtils.openNewForegroundTab(window.gBrowser, "https://example.com/", true);
await BrowserTestUtils.openNewForegroundTab(
window.gBrowser,
"https://example.com/",
true
);
const newTab = gBrowser.selectedTab;
gBrowser.pinTab(newTab);
@@ -18,7 +22,8 @@ add_task(async function test_Pinned_To_Essential() {
gZenPinnedTabManager.addToEssentials(newTab);
ok(
newTab.hasAttribute("zen-essential") && newTab.parentNode.getAttribute("container") == "0",
newTab.hasAttribute("zen-essential") &&
newTab.parentNode.getAttribute("container") == "0",
"New tab should be marked as essential."
);

View File

@@ -5,43 +5,55 @@
add_task(async function test_Unload_Changed_Pinned() {
await SpecialPowers.pushPrefEnv({
set: [["zen.pinned-tab-manager.close-shortcut-behavior", "reset-unload-switch"]],
set: [
["zen.pinned-tab-manager.close-shortcut-behavior", "reset-unload-switch"],
],
});
let resolvePromise;
const promise = new Promise((resolve) => {
const promise = new Promise(resolve => {
resolvePromise = resolve;
});
await BrowserTestUtils.withNewTab({ gBrowser, url: "https://example.com/1" }, async (browser) => {
const tab = gBrowser.getTabForBrowser(browser);
gBrowser.pinTab(tab);
await BrowserTestUtils.withNewTab(
{ gBrowser, url: "https://example.com/1" },
async browser => {
const tab = gBrowser.getTabForBrowser(browser);
gBrowser.pinTab(tab);
BrowserTestUtils.startLoadingURIString(browser, "https://example.com/2");
await BrowserTestUtils.browserLoaded(browser, false, "https://example.com/2");
await gBrowser.TabStateFlusher.flush(browser);
setTimeout(() => {
ok(
tab.hasAttribute("zen-pinned-changed"),
"The tab should have a zen-pinned-changed attribute after being pinned"
BrowserTestUtils.startLoadingURIString(browser, "https://example.com/2");
await BrowserTestUtils.browserLoaded(
browser,
false,
"https://example.com/2"
);
document.getElementById("cmd_close").doCommand();
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
await gBrowser.TabStateFlusher.flush(browser);
setTimeout(() => {
ok(
!tab.hasAttribute("zen-pinned-changed"),
"The tab should not have a zen-pinned-changed attribute after being closed"
tab.hasAttribute("zen-pinned-changed"),
"The tab should have a zen-pinned-changed attribute after being pinned"
);
document.getElementById("cmd_close").doCommand();
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
setTimeout(() => {
ok(
!tab.hasAttribute("zen-pinned-changed"),
"The tab should not have a zen-pinned-changed attribute after being closed"
);
ok(tab.hasAttribute("discarded"), "The tab should not be discarded after being closed");
Assert.notEqual(
tab,
gBrowser.selectedTab,
"The tab should not be selected after being closed"
);
resolvePromise();
}, 100);
}, 0);
await promise;
});
ok(
tab.hasAttribute("discarded"),
"The tab should not be discarded after being closed"
);
Assert.notEqual(
tab,
gBrowser.selectedTab,
"The tab should not be selected after being closed"
);
resolvePromise();
}, 100);
}, 0);
await promise;
}
);
});

View File

@@ -9,39 +9,49 @@ add_task(async function test_Unload_NoReset_Pinned() {
});
let resolvePromise;
const promise = new Promise((resolve) => {
const promise = new Promise(resolve => {
resolvePromise = resolve;
});
await BrowserTestUtils.withNewTab({ gBrowser, url: "https://example.com/1" }, async (browser) => {
const tab = gBrowser.getTabForBrowser(browser);
gBrowser.pinTab(tab);
await BrowserTestUtils.withNewTab(
{ gBrowser, url: "https://example.com/1" },
async browser => {
const tab = gBrowser.getTabForBrowser(browser);
gBrowser.pinTab(tab);
BrowserTestUtils.startLoadingURIString(browser, "https://example.com/2");
await BrowserTestUtils.browserLoaded(browser, false, "https://example.com/2");
await gBrowser.TabStateFlusher.flush(browser);
/* eslint-disable-next-line mozilla/no-arbitrary-setTimeout */
setTimeout(() => {
ok(
tab.hasAttribute("zen-pinned-changed"),
"The tab should have a zen-pinned-changed attribute after being pinned"
BrowserTestUtils.startLoadingURIString(browser, "https://example.com/2");
await BrowserTestUtils.browserLoaded(
browser,
false,
"https://example.com/2"
);
document.getElementById("cmd_close").doCommand();
await gBrowser.TabStateFlusher.flush(browser);
/* eslint-disable-next-line mozilla/no-arbitrary-setTimeout */
setTimeout(() => {
ok(
tab.hasAttribute("zen-pinned-changed"),
"The tab should not have a zen-pinned-changed attribute after being closed"
"The tab should have a zen-pinned-changed attribute after being pinned"
);
ok(tab.hasAttribute("discarded"), "The tab should not be discarded after being closed");
Assert.notEqual(
tab,
gBrowser.selectedTab,
"The tab should not be selected after being closed"
);
resolvePromise();
}, 100);
}, 0);
await promise;
});
document.getElementById("cmd_close").doCommand();
/* eslint-disable-next-line mozilla/no-arbitrary-setTimeout */
setTimeout(() => {
ok(
tab.hasAttribute("zen-pinned-changed"),
"The tab should not have a zen-pinned-changed attribute after being closed"
);
ok(
tab.hasAttribute("discarded"),
"The tab should not be discarded after being closed"
);
Assert.notEqual(
tab,
gBrowser.selectedTab,
"The tab should not be selected after being closed"
);
resolvePromise();
}, 100);
}, 0);
await promise;
}
);
});

View File

@@ -19,7 +19,8 @@ add_task(async function test_Basic_Split_View() {
add_task(async function test_Browser_Elements_Attributes() {
await basicSplitNTabs(() => {
Assert.equal(
document.querySelectorAll('.browserSidebarContainer[zen-split="true"]').length,
document.querySelectorAll('.browserSidebarContainer[zen-split="true"]')
.length,
2,
"There should be two split browser sidebars"
);

View File

@@ -17,20 +17,29 @@ add_task(async function test_Basic_Split_View_Duplication() {
"There should be four tabs after pinning the second tab"
);
await createSplitView([normal, pinned], "grid");
ok(!pinned.group, "The pinned tab should not be in a split group after duplication");
ok(
!pinned.group,
"The pinned tab should not be in a split group after duplication"
);
ok(
normal.group.hasAttribute("split-view-group"),
"The normal tab should be in a split group after duplication"
);
const group = normal.group;
for (const tab of group.tabs) {
Assert.ok(!tab.pinned, "All tabs in the split group should not be pinned after duplication");
Assert.ok(
!tab.pinned,
"All tabs in the split group should not be pinned after duplication"
);
Assert.ok(
tab.splitView,
"All tabs in the split group should be in a split view after duplication"
);
}
Assert.ok(!group.pinned, "The split group should not be pinned after duplication");
Assert.ok(
!group.pinned,
"The split group should not be pinned after duplication"
);
for (const tab of [pinned, ...group.tabs]) {
await BrowserTestUtils.removeTab(tab);
}
@@ -46,9 +55,16 @@ add_task(async function test_Split_View_Duplication_Both_Pinned() {
gBrowser.pinTab(tab1);
gBrowser.pinTab(tab2);
await Promise.all([pinEvent1, pinEvent2]);
Assert.strictEqual(gBrowser.tabs.length, 4, "There should be four tabs after pinning both tabs");
Assert.strictEqual(
gBrowser.tabs.length,
4,
"There should be four tabs after pinning both tabs"
);
await createSplitView([tab1, tab2], "grid");
ok(tab1.group, "The first pinned tab should be in a split group after duplication");
ok(
tab1.group,
"The first pinned tab should be in a split group after duplication"
);
Assert.strictEqual(
tab2.group,
tab1.group,
@@ -60,13 +76,19 @@ add_task(async function test_Split_View_Duplication_Both_Pinned() {
"There should not be any duplicate tabs after pinning both tabs"
);
for (const tab of tab1.group.tabs) {
Assert.ok(tab.pinned, "All tabs in the split group should be pinned after duplication");
Assert.ok(
tab.pinned,
"All tabs in the split group should be pinned after duplication"
);
Assert.ok(
tab.splitView,
"All tabs in the split group should be in a split view after duplication"
);
}
Assert.ok(tab1.group.pinned, "The split group should be pinned after duplication of both tabs");
Assert.ok(
tab1.group.pinned,
"The split group should be pinned after duplication of both tabs"
);
for (const tab of tab1.group.tabs) {
await BrowserTestUtils.removeTab(tab);
}
@@ -116,7 +138,7 @@ add_task(async function test_Split_View_Duplication_Essential() {
const essentials = await Promise.all(
[...Array(2)].map((_, i) => addTabTo(gBrowser, getUrlForNthTab(i + 1)))
);
essentials.forEach((tab) => {
essentials.forEach(tab => {
gZenPinnedTabManager.addToEssentials(tab);
});
Assert.strictEqual(
@@ -131,8 +153,14 @@ add_task(async function test_Split_View_Duplication_Essential() {
"There should be six tabs after creating a split view with two essential tabs"
);
for (const tab of essentials) {
ok(!tab.group, "Each essential tab should not be in a split group after duplication");
ok(!tab.splitView, "Each essential tab should not be in a split view after duplication");
ok(
!tab.group,
"Each essential tab should not be in a split group after duplication"
);
ok(
!tab.splitView,
"Each essential tab should not be in a split view after duplication"
);
}
for (const tab of gBrowser.tabs) {
if (existingTabs.includes(tab)) {

View File

@@ -4,21 +4,34 @@
"use strict";
add_task(async function test_Basic_Split_Groups() {
await basicSplitNTabs(async (tabs) => {
ok(tabs[0].group.hasAttribute("split-view-group"), "The first tab should be in a split group");
Assert.equal(tabs[0].group.tabs.length, 2, "The first split group should contain two tabs");
await basicSplitNTabs(async tabs => {
ok(
tabs[0].group.hasAttribute("split-view-group"),
"The first tab should be in a split group"
);
Assert.equal(
tabs[0].group.tabs.length,
2,
"The first split group should contain two tabs"
);
});
});
add_task(async function test_Basic_Split_Groups_Pinning() {
await basicSplitNTabs(async (tabs) => {
await basicSplitNTabs(async tabs => {
const group = tabs[0].group;
ok(group.hasAttribute("split-view-group"), "The first tab should be in a split group");
ok(
group.hasAttribute("split-view-group"),
"The first tab should be in a split group"
);
const pinEvent = BrowserTestUtils.waitForEvent(tabs[0], "TabPinned");
gBrowser.pinTab(tabs[0]);
await pinEvent;
for (const tab of tabs) {
ok(tab.pinned, "All tabs in the split group should be pinned after pinning the first tab");
ok(
tab.pinned,
"All tabs in the split group should be pinned after pinning the first tab"
);
Assert.strictEqual(
tab.group,
group,
@@ -40,15 +53,21 @@ add_task(async function test_Basic_Split_Groups_Pinning() {
"All tabs in the split group should remain in the same group after unpinning"
);
}
ok(!group.pinned, "The split group should be unpinned after unpinning a tab");
ok(
!group.pinned,
"The split group should be unpinned after unpinning a tab"
);
});
});
add_task(async function test_Basic_Unsplit_Group_Removed() {
let group;
await basicSplitNTabs(async (tabs) => {
await basicSplitNTabs(async tabs => {
group = tabs[0].group;
});
ok(group, "The split group should exist");
ok(!group.parentNode, "The split group should be removed from the DOM after unsplitting");
ok(
!group.parentNode,
"The split group should be removed from the DOM after unsplitting"
);
});

View File

@@ -6,7 +6,9 @@
add_task(async function test_Basic_Split_View_Inset() {
let viewsToCheck = [];
await basicSplitNTabs(() => {
viewsToCheck = document.querySelectorAll('.browserSidebarContainer[zen-split="true"]');
viewsToCheck = document.querySelectorAll(
'.browserSidebarContainer[zen-split="true"]'
);
ok(viewsToCheck.length, "There should be split views present");
Assert.equal(
viewsToCheck[0].style.inset,
@@ -20,13 +22,19 @@ add_task(async function test_Basic_Split_View_Inset() {
);
});
for (const view of viewsToCheck) {
Assert.equal(view.style.inset, "", "The unsplit view should not have correct inset style");
Assert.equal(
view.style.inset,
"",
"The unsplit view should not have correct inset style"
);
}
});
add_task(async function test_Horizontal_Split_Inset() {
await basicSplitNTabs(() => {
const viewsToCheck = document.querySelectorAll('.browserSidebarContainer[zen-split="true"]');
const viewsToCheck = document.querySelectorAll(
'.browserSidebarContainer[zen-split="true"]'
);
ok(viewsToCheck.length, "There should be split views present");
Assert.equal(
viewsToCheck[0].style.inset,
@@ -44,7 +52,9 @@ add_task(async function test_Horizontal_Split_Inset() {
add_task(async function test_3_Splits_Grid_Inset() {
await basicSplitNTabs(
() => {
const viewsToCheck = document.querySelectorAll('.browserSidebarContainer[zen-split="true"]');
const viewsToCheck = document.querySelectorAll(
'.browserSidebarContainer[zen-split="true"]'
);
ok(viewsToCheck.length, "There should be split views present");
Assert.equal(
viewsToCheck[0].style.inset,

View File

@@ -25,7 +25,7 @@ add_task(async function test_Split_View_Empty() {
let result = await UrlbarTestUtils.getDetailsOfResultAt(window, 0);
EventUtils.synthesizeMouseAtCenter(result.element.row, {});
await waitForActivationPromise;
await new Promise((resolve) => {
await new Promise(resolve => {
/* eslint-disable mozilla/no-arbitrary-setTimeout */
setTimeout(async () => {
resolve();
@@ -36,8 +36,14 @@ add_task(async function test_Split_View_Empty() {
gBrowser.tabpanels.hasAttribute("zen-split-view"),
"The split view should not have crashed with two tabs in it"
);
ok(!gZenWorkspaces._emptyTab.splitView, "The empty tab should not be in split view");
ok(!gZenWorkspaces._emptyTab.group, "The empty tab should not be in a group");
ok(
!gZenWorkspaces._emptyTab.splitView,
"The empty tab should not be in split view"
);
ok(
!gZenWorkspaces._emptyTab.group,
"The empty tab should not be in a group"
);
ok(selectedTab.splitView, "The selected tab should be in split view");
ok(originalTab.splitView, "The original tab should be in split view");
Assert.equal(

View File

@@ -13,13 +13,25 @@ add_task(async function test_Split_View_Inside_Folder() {
gBrowser.pinTab(tab1);
gBrowser.pinTab(tab2);
await Promise.all([pinEvent1, pinEvent2]);
Assert.strictEqual(gBrowser.tabs.length, 4, "There should be four tabs after pinning both tabs");
Assert.strictEqual(
gBrowser.tabs.length,
4,
"There should be four tabs after pinning both tabs"
);
const folder = await gZenFolders.createFolder([tab1, tab2], {
renameFolder: false,
label: "test",
});
Assert.equal(tab1.group, folder, "The first pinned tab should be in the folder group");
Assert.equal(tab2.group, folder, "The second pinned tab should be in the folder group");
Assert.equal(
tab1.group,
folder,
"The first pinned tab should be in the folder group"
);
Assert.equal(
tab2.group,
folder,
"The second pinned tab should be in the folder group"
);
await createSplitView([tab1, tab2], "grid");
Assert.strictEqual(
tab2.group,
@@ -30,8 +42,15 @@ add_task(async function test_Split_View_Inside_Folder() {
tab1.group.hasAttribute("split-view-group"),
"The first pinned tab should be in a split group after duplication"
);
Assert.ok(tab1.group.pinned, "The split group should be pinned after duplication of both tabs");
Assert.equal(tab1.group.group, folder, "The split group should be the folder after duplication");
Assert.ok(
tab1.group.pinned,
"The split group should be pinned after duplication of both tabs"
);
Assert.equal(
tab1.group.group,
folder,
"The split group should be the folder after duplication"
);
const removeEvent = BrowserTestUtils.waitForEvent(window, "TabGroupRemoved");
folder.delete();
await removeEvent;

View File

@@ -9,7 +9,7 @@ const { openGlanceOnTab } = ChromeUtils.importESModule(
add_task(async function test_Basic_Split_View_Glance() {
await basicSplitNTabs(async () => {
await openGlanceOnTab(window, async (glanceTab) => {
await openGlanceOnTab(window, async glanceTab => {
ok(
glanceTab.hasAttribute("zen-glance-tab"),
"The glance tab should have the zen-glance-tab attribute"
@@ -23,23 +23,28 @@ add_task(async function test_Basic_Split_View_Glance() {
});
add_task(async function test_Basic_Split_View_Glance_Expand() {
await basicSplitNTabs(async (tabs) => {
await basicSplitNTabs(async tabs => {
await openGlanceOnTab(
window,
async (glanceTab) => {
async glanceTab => {
await gZenGlanceManager.fullyOpenGlance();
ok(
!glanceTab.hasAttribute("zen-glance-tab"),
"The glance tab should not have the zen-glance-tab attribute after expanding"
);
ok(!glanceTab.group, "The glance tab should not be in a split group after expanding");
ok(
!glanceTab.group,
"The glance tab should not be in a split group after expanding"
);
for (const tab of tabs) {
ok(
tab.group.hasAttribute("split-view-group"),
"All tabs in the split view should still be in a split group after expanding glance"
);
}
const selectedBrowser = document.querySelectorAll(".browserSidebarContainer.deck-selected");
const selectedBrowser = document.querySelectorAll(
".browserSidebarContainer.deck-selected"
);
Assert.equal(
selectedBrowser.length,
1,
@@ -57,7 +62,9 @@ add_task(async function test_Basic_Split_View_Glance_No_More_Split() {
async () => {
await openGlanceOnTab(window, () => {
Assert.strictEqual(
document.getElementById("cmd_zenGlanceSplit").getAttribute("disabled"),
document
.getElementById("cmd_zenGlanceSplit")
.getAttribute("disabled"),
"true",
"The split command should be disabled when glance is open"
);
@@ -73,7 +80,7 @@ add_task(async function test_Basic_Split_View_Glance_Split() {
gBrowser.selectedTab = tab;
await openGlanceOnTab(
window,
async (glanceTab) => {
async glanceTab => {
const waitForSplitPromise = BrowserTestUtils.waitForEvent(
window,
"ZenViewSplitter:SplitViewActivated"

View File

@@ -3,7 +3,11 @@
"use strict";
async function addTabTo(targetBrowser, url = "http://mochi.test:8888/", params = {}) {
async function addTabTo(
targetBrowser,
url = "http://mochi.test:8888/",
params = {}
) {
params.skipAnimation = true;
const tab = BrowserTestUtils.addTab(targetBrowser, url, params);
const browser = targetBrowser.getBrowserForTab(tab);
@@ -22,7 +26,7 @@ async function createSplitView(tabs, type = "grid") {
);
gZenViewSplitter.splitTabs(tabs, type);
await waitForActivationPromise;
await new Promise((resolve) => {
await new Promise(resolve => {
setTimeout(async () => {
resolve();
}, 100);

View File

@@ -9,8 +9,12 @@ const URL3 = "data:text/plain,tab3";
const threshold = Math.min(
1.0,
Math.max(0.5, Services.prefs.getIntPref("browser.tabs.dragDrop.moveOverThresholdPercent") / 100) +
0.01
Math.max(
0.5,
Services.prefs.getIntPref(
"browser.tabs.dragDrop.moveOverThresholdPercent"
) / 100
) + 0.01
);
/**
@@ -36,7 +40,7 @@ async function drop(source, target, clientX, clientY, win) {
* @param {Element} el
* @returns {DOMRect}
*/
const bounds = (el) => window.windowUtils.getBoundsWithoutFlushing(el);
const bounds = el => window.windowUtils.getBoundsWithoutFlushing(el);
/**
* Virtually drag and drop one tab strip item after another.
@@ -71,7 +75,9 @@ async function dropBefore(itemToDrag, itemToDropBefore, win) {
const midline = rect.left + 0.5 * rect.width;
// Point where top edge of `itemToDrag` overlaps `itemToDropBefore` enough
// for `itemToDrag` to come before.
const beforePoint = Math.floor(rect.top + (1 - threshold - 0.5) * rect.height);
const beforePoint = Math.floor(
rect.top + (1 - threshold - 0.5) * rect.height
);
const dragTo = beforePoint + sourceRect.height / 2;
await drop(itemToDrag, itemToDropBefore, midline, dragTo, win);
}
@@ -81,7 +87,9 @@ async function dropBefore(itemToDrag, itemToDropBefore, win) {
*/
async function ensureNotOverflowing() {
const tabHeight = Number.parseFloat(
getComputedStyle(gBrowser.tabs[0]).getPropertyValue("--tab-height-with-margin-padding")
getComputedStyle(gBrowser.tabs[0]).getPropertyValue(
"--tab-height-with-margin-padding"
)
);
const requiredTabSpace = tabHeight * gBrowser.tabs.length;
const scrollboxWidth = gBrowser.tabContainer.arrowScrollbox.scrollSize;
@@ -118,7 +126,11 @@ add_setup(async () => {
BrowserTestUtils.removeTab(tabToRemove);
const emptyTab = gBrowser.tabs[0];
Assert.deepEqual(gBrowser.tabs, [emptyTab, tab1, tab2, tab3], "confirm tabs' starting order");
Assert.deepEqual(
gBrowser.tabs,
[emptyTab, tab1, tab2, tab3],
"confirm tabs' starting order"
);
await ensureNotOverflowing();

View File

@@ -16,7 +16,10 @@ const URL6 = "data:text/plain,tab6";
* @param {MozTabbrowserTab} tab - tab to select
*/
async function selectTab(tab) {
const onSelect = BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "TabSelect");
const onSelect = BrowserTestUtils.waitForEvent(
gBrowser.tabContainer,
"TabSelect"
);
gBrowser.selectedTab = tab;
await onSelect;
}
@@ -37,22 +40,29 @@ add_setup(async () => {
gZenPinnedTabManager.addToEssentials(tabs.slice(0, 3));
await BrowserTestUtils.waitForCondition(
() => tabs.slice(0, 3).every((tab) => tab.hasAttribute("zen-essential")),
() => tabs.slice(0, 3).every(tab => tab.hasAttribute("zen-essential")),
"all essentials ready"
);
const essentialTabs = gBrowser.tabs.filter((tab) => tab.hasAttribute("zen-essential"));
const essentialTabs = gBrowser.tabs.filter(tab =>
tab.hasAttribute("zen-essential")
);
Assert.equal(essentialTabs.length, 3, "3 essential tabs created");
const workspaceTabs = gBrowser.tabs.filter(
(tab) => !tab.hasAttribute("zen-essential") && !tab.hasAttribute("zen-empty-tab")
tab =>
!tab.hasAttribute("zen-essential") && !tab.hasAttribute("zen-empty-tab")
);
Assert.equal(
workspaceTabs.length,
3,
"3 workspace tabs created, excluding empty tab"
);
Assert.equal(workspaceTabs.length, 3, "3 workspace tabs created, excluding empty tab");
registerCleanupFunction(async () => {
// replace the default new tab in the test window
addTabTo(gBrowser, "about:blank");
tabs.forEach((element) => {
tabs.forEach(element => {
BrowserTestUtils.removeTab(element);
});
await SpecialPowers.popPrefEnv();
@@ -64,7 +74,9 @@ add_task(async function cycleTabsByAttribute() {
set: [["zen.tabs.ctrl-tab.ignore-essential-tabs", true]],
});
const essentialTabs = gBrowser.tabs.filter((tab) => tab.hasAttribute("zen-essential"));
const essentialTabs = gBrowser.tabs.filter(tab =>
tab.hasAttribute("zen-essential")
);
await selectTab(essentialTabs[0]);
gBrowser.tabContainer.advanceSelectedTab(1, true);
@@ -77,7 +89,8 @@ add_task(async function cycleTabsByAttribute() {
);
const workspaceTabs = gBrowser.tabs.filter(
(tab) => !tab.hasAttribute("zen-essential") && !tab.hasAttribute("zen-empty-tab")
tab =>
!tab.hasAttribute("zen-essential") && !tab.hasAttribute("zen-empty-tab")
);
await selectTab(workspaceTabs[0]);

View File

@@ -7,7 +7,10 @@ add_task(async function test_Empty_Tab_Transparent() {
const emptyTab = gZenWorkspaces._emptyTab;
ok(emptyTab, "Empty tab should exist");
ok(emptyTab.parentElement, "Empty tab should be in the DOM");
ok(emptyTab.hasAttribute("zen-empty-tab"), "Empty tab should have the zen-empty-tab attribute");
ok(
emptyTab.hasAttribute("zen-empty-tab"),
"Empty tab should have the zen-empty-tab attribute"
);
ok(
emptyTab.linkedBrowser.hasAttribute("transparent"),
"Empty tab should have the transparent attribute"
@@ -15,14 +18,20 @@ add_task(async function test_Empty_Tab_Transparent() {
});
add_task(async function test_Empty_Tab_Always_First() {
ok(gBrowser.tabs[0].hasAttribute("zen-empty-tab"), "First tab should be the empty tab");
ok(
gBrowser.tabs[0].hasAttribute("zen-empty-tab"),
"First tab should be the empty tab"
);
await BrowserTestUtils.withNewTab(
{
gBrowser,
url: "https://example.com",
},
async () => {
ok(gBrowser.tabs[0].hasAttribute("zen-empty-tab"), "First tab should be the empty tab");
ok(
gBrowser.tabs[0].hasAttribute("zen-empty-tab"),
"First tab should be the empty tab"
);
}
);
});

View File

@@ -37,7 +37,11 @@ function updateTabContextMenu(tab) {
var evt = new Event("");
tab.dispatchEvent(evt);
menu.openPopup(tab, "end_after", 0, 0, true, false, evt);
is(window.TabContextMenu.contextTab, tab, "TabContextMenu context is the expected tab");
is(
window.TabContextMenu.contextTab,
tab,
"TabContextMenu context is the expected tab"
);
menu.hidePopup();
}
@@ -63,7 +67,11 @@ async function addTab(url = "http://mochi.test:8888/", params) {
return addTabTo(gBrowser, url, params);
}
async function addTabTo(targetBrowser, url = "http://mochi.test:8888/", params = {}) {
async function addTabTo(
targetBrowser,
url = "http://mochi.test:8888/",
params = {}
) {
params.skipAnimation = true;
const tab = BrowserTestUtils.addTab(targetBrowser, url, params);
const browser = targetBrowser.getBrowserForTab(tab);
@@ -106,7 +114,7 @@ async function wait_for_tab_playing_event(tab, expectPlaying) {
ok(true, "The tab should " + (expectPlaying ? "" : "not ") + "be playing");
return true;
}
return BrowserTestUtils.waitForEvent(tab, "TabAttrModified", false, (event) => {
return BrowserTestUtils.waitForEvent(tab, "TabAttrModified", false, event => {
if (event.detail.changed.includes("soundplaying")) {
is(
tab.hasAttribute("soundplaying"),
@@ -126,20 +134,29 @@ async function wait_for_tab_playing_event(tab, expectPlaying) {
async function wait_for_tab_media_blocked_event(tab, expectMediaBlocked) {
if (tab.activeMediaBlocked == expectMediaBlocked) {
ok(true, "The tab should " + (expectMediaBlocked ? "" : "not ") + "be activemedia-blocked");
ok(
true,
"The tab should " +
(expectMediaBlocked ? "" : "not ") +
"be activemedia-blocked"
);
return true;
}
return BrowserTestUtils.waitForEvent(tab, "TabAttrModified", false, (event) => {
return BrowserTestUtils.waitForEvent(tab, "TabAttrModified", false, event => {
if (event.detail.changed.includes("activemedia-blocked")) {
is(
tab.hasAttribute("activemedia-blocked"),
expectMediaBlocked,
"The tab should " + (expectMediaBlocked ? "" : "not ") + "be activemedia-blocked"
"The tab should " +
(expectMediaBlocked ? "" : "not ") +
"be activemedia-blocked"
);
is(
tab.activeMediaBlocked,
expectMediaBlocked,
"The tab should " + (expectMediaBlocked ? "" : "not ") + "be activemedia-blocked"
"The tab should " +
(expectMediaBlocked ? "" : "not ") +
"be activemedia-blocked"
);
return true;
}
@@ -212,7 +229,7 @@ function leave_icon(icon) {
let everMutedTabs = new WeakSet();
function get_wait_for_mute_promise(tab, expectMuted) {
return BrowserTestUtils.waitForEvent(tab, "TabAttrModified", false, (event) => {
return BrowserTestUtils.waitForEvent(tab, "TabAttrModified", false, event => {
if (
event.detail.changed.includes("muted") ||
event.detail.changed.includes("activemedia-blocked")
@@ -232,7 +249,11 @@ function get_wait_for_mute_promise(tab, expectMuted) {
everMutedTabs.add(tab);
is(tab.muteReason, null, "The tab should have a null muteReason value");
} else {
is(tab.muteReason, undefined, "The tab should have an undefined muteReason value");
is(
tab.muteReason,
undefined,
"The tab should have an undefined muteReason value"
);
}
return true;
}
@@ -306,7 +327,10 @@ async function dragAndDrop(
return tab1.elementIndex != originalIndex;
}, "Waiting for tab position to be updated");
} else if (destWindow != origWindow) {
await BrowserTestUtils.waitForCondition(() => tab1.closing, "Waiting for tab closing");
await BrowserTestUtils.waitForCondition(
() => tab1.closing,
"Waiting for tab closing"
);
}
}
@@ -360,7 +384,8 @@ function test_url_for_process_types({
const CHROME_PROCESS = E10SUtils.NOT_REMOTE;
const WEB_CONTENT_PROCESS = E10SUtils.WEB_REMOTE_TYPE;
const PRIVILEGEDABOUT_CONTENT_PROCESS = E10SUtils.PRIVILEGEDABOUT_REMOTE_TYPE;
const PRIVILEGEDMOZILLA_CONTENT_PROCESS = E10SUtils.PRIVILEGEDMOZILLA_REMOTE_TYPE;
const PRIVILEGEDMOZILLA_CONTENT_PROCESS =
E10SUtils.PRIVILEGEDMOZILLA_REMOTE_TYPE;
const EXTENSION_PROCESS = E10SUtils.EXTENSION_REMOTE_TYPE;
is(
@@ -369,33 +394,57 @@ function test_url_for_process_types({
"Check URL in chrome process."
);
is(
E10SUtils.canLoadURIInRemoteType(url, /* fission */ false, WEB_CONTENT_PROCESS),
E10SUtils.canLoadURIInRemoteType(
url,
/* fission */ false,
WEB_CONTENT_PROCESS
),
webContentResult,
"Check URL in web content process."
);
is(
E10SUtils.canLoadURIInRemoteType(url, /* fission */ false, PRIVILEGEDABOUT_CONTENT_PROCESS),
E10SUtils.canLoadURIInRemoteType(
url,
/* fission */ false,
PRIVILEGEDABOUT_CONTENT_PROCESS
),
privilegedAboutContentResult,
"Check URL in privileged about content process."
);
is(
E10SUtils.canLoadURIInRemoteType(url, /* fission */ false, PRIVILEGEDMOZILLA_CONTENT_PROCESS),
E10SUtils.canLoadURIInRemoteType(
url,
/* fission */ false,
PRIVILEGEDMOZILLA_CONTENT_PROCESS
),
privilegedMozillaContentResult,
"Check URL in privileged mozilla content process."
);
is(
E10SUtils.canLoadURIInRemoteType(url, /* fission */ false, EXTENSION_PROCESS),
E10SUtils.canLoadURIInRemoteType(
url,
/* fission */ false,
EXTENSION_PROCESS
),
extensionProcessResult,
"Check URL in extension process."
);
is(
E10SUtils.canLoadURIInRemoteType(url + "#foo", /* fission */ false, CHROME_PROCESS),
E10SUtils.canLoadURIInRemoteType(
url + "#foo",
/* fission */ false,
CHROME_PROCESS
),
chromeResult,
"Check URL with ref in chrome process."
);
is(
E10SUtils.canLoadURIInRemoteType(url + "#foo", /* fission */ false, WEB_CONTENT_PROCESS),
E10SUtils.canLoadURIInRemoteType(
url + "#foo",
/* fission */ false,
WEB_CONTENT_PROCESS
),
webContentResult,
"Check URL with ref in web content process."
);
@@ -418,18 +467,30 @@ function test_url_for_process_types({
"Check URL with ref in privileged mozilla content process."
);
is(
E10SUtils.canLoadURIInRemoteType(url + "#foo", /* fission */ false, EXTENSION_PROCESS),
E10SUtils.canLoadURIInRemoteType(
url + "#foo",
/* fission */ false,
EXTENSION_PROCESS
),
extensionProcessResult,
"Check URL with ref in extension process."
);
is(
E10SUtils.canLoadURIInRemoteType(url + "?foo", /* fission */ false, CHROME_PROCESS),
E10SUtils.canLoadURIInRemoteType(
url + "?foo",
/* fission */ false,
CHROME_PROCESS
),
chromeResult,
"Check URL with query in chrome process."
);
is(
E10SUtils.canLoadURIInRemoteType(url + "?foo", /* fission */ false, WEB_CONTENT_PROCESS),
E10SUtils.canLoadURIInRemoteType(
url + "?foo",
/* fission */ false,
WEB_CONTENT_PROCESS
),
webContentResult,
"Check URL with query in web content process."
);
@@ -452,18 +513,30 @@ function test_url_for_process_types({
"Check URL with query in privileged mozilla content process."
);
is(
E10SUtils.canLoadURIInRemoteType(url + "?foo", /* fission */ false, EXTENSION_PROCESS),
E10SUtils.canLoadURIInRemoteType(
url + "?foo",
/* fission */ false,
EXTENSION_PROCESS
),
extensionProcessResult,
"Check URL with query in extension process."
);
is(
E10SUtils.canLoadURIInRemoteType(url + "?foo#bar", /* fission */ false, CHROME_PROCESS),
E10SUtils.canLoadURIInRemoteType(
url + "?foo#bar",
/* fission */ false,
CHROME_PROCESS
),
chromeResult,
"Check URL with query and ref in chrome process."
);
is(
E10SUtils.canLoadURIInRemoteType(url + "?foo#bar", /* fission */ false, WEB_CONTENT_PROCESS),
E10SUtils.canLoadURIInRemoteType(
url + "?foo#bar",
/* fission */ false,
WEB_CONTENT_PROCESS
),
webContentResult,
"Check URL with query and ref in web content process."
);
@@ -486,7 +559,11 @@ function test_url_for_process_types({
"Check URL with query and ref in privileged mozilla content process."
);
is(
E10SUtils.canLoadURIInRemoteType(url + "?foo#bar", /* fission */ false, EXTENSION_PROCESS),
E10SUtils.canLoadURIInRemoteType(
url + "?foo#bar",
/* fission */ false,
EXTENSION_PROCESS
),
extensionProcessResult,
"Check URL with query and ref in extension process."
);
@@ -505,7 +582,10 @@ function fileURL(filename) {
* Get a http URL for the local file name.
*/
function httpURL(filename, host = "https://example.com/") {
let root = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", host);
let root = getRootDirectory(gTestPath).replace(
"chrome://mochitests/content/",
host
);
return root + filename;
}
@@ -533,9 +613,16 @@ async function getContextMenu(triggerNode, contextMenuId) {
let win = triggerNode.ownerGlobal;
triggerNode.scrollIntoView({ behavior: "instant" });
const contextMenu = win.document.getElementById(contextMenuId);
const contextMenuShown = BrowserTestUtils.waitForPopupEvent(contextMenu, "shown");
const contextMenuShown = BrowserTestUtils.waitForPopupEvent(
contextMenu,
"shown"
);
EventUtils.synthesizeMouseAtCenter(triggerNode, { type: "contextmenu", button: 2 }, win);
EventUtils.synthesizeMouseAtCenter(
triggerNode,
{ type: "contextmenu", button: 2 },
win
);
await contextMenuShown;
return contextMenu;
}

View File

@@ -20,10 +20,16 @@ add_task(async function test_Ub_Actions_Search() {
waitForFocus,
value: label,
});
await new Promise((resolve) =>
await new Promise(resolve =>
setTimeout(async () => {
let index = typeof action.suggestedIndex === "number" ? action.suggestedIndex : Infinity;
let { result } = await UrlbarTestUtils.getRowAt(window, Math.min(index, 1));
let index =
typeof action.suggestedIndex === "number"
? action.suggestedIndex
: Infinity;
let { result } = await UrlbarTestUtils.getRowAt(
window,
Math.min(index, 1)
);
Assert.equal(result.providerName, "ZenUrlbarProviderGlobalActions");
Assert.equal(result.payload.title, label);
resolve();

View File

@@ -18,7 +18,10 @@ add_task(async function test_Floating_Urlbar() {
value: "https://example.com/",
});
ok(gURLBar.textbox.hasAttribute("zen-floating-urlbar"), "URL bar should be in floating mode");
ok(
gURLBar.textbox.hasAttribute("zen-floating-urlbar"),
"URL bar should be in floating mode"
);
});
add_task(async function test_Click_Shoudnt_FLoat_Urlbar() {

View File

@@ -21,7 +21,11 @@ add_task(async function test_Selection_Remains_Double_Toolbar() {
await selectWithMouseDrag(100, 200);
Assert.greater(gURLBar.selectionStart, 0, "Selection start is positive.");
Assert.greater(gURLBar.selectionEnd, gURLBar.selectionStart, "Selection is not empty.");
Assert.greater(
gURLBar.selectionEnd,
gURLBar.selectionStart,
"Selection is not empty."
);
Assert.equal(gURLBar.value, untrimmedValue, `Value should be untrimmed`);

View File

@@ -35,13 +35,19 @@ function selectWithMouseDrag(fromX, toX, win = window) {
{ type: "mousemove" },
target.ownerGlobal
);
EventUtils.synthesizeMouse(target, toX, rect.height / 2, { type: "mouseup" }, target.ownerGlobal);
EventUtils.synthesizeMouse(
target,
toX,
rect.height / 2,
{ type: "mouseup" },
target.ownerGlobal
);
return promise;
}
function goToMultipleLayouts(callback) {
// eslint-disable-next-line no-async-promise-executor
return new Promise(async (resolve) => {
return new Promise(async resolve => {
await SpecialPowers.pushPrefEnv({
set: [["zen.view.use-single-toolbar", false]],
});

View File

@@ -7,7 +7,7 @@
add_task(async function test_Welcome_Steps() {
const selectedTab = gBrowser.selectedTab;
await new Promise((resolve) => {
await new Promise(resolve => {
setTimeout(async () => {
await waitForFocus();
await EventUtils.synthesizeMouseAtCenter(
@@ -28,7 +28,9 @@ add_task(async function test_Welcome_Steps() {
const welcomeContent = document.getElementById("zen-welcome-page-content");
for (const button of document.querySelectorAll("#zen-welcome-page-sidebar-buttons button")) {
for (const button of document.querySelectorAll(
"#zen-welcome-page-sidebar-buttons button"
)) {
Assert.notStrictEqual(
getComputedStyle(button).pointerEvents,
"none",
@@ -54,11 +56,14 @@ add_task(async function test_Welcome_Steps() {
await EventUtils.synthesizeMouseAtCenter(welcomeContent.children[1], {});
await new Promise((resolve) => {
await new Promise(resolve => {
setTimeout(async () => {
let engineName = await Services.search.getDefault();
const selectedLabel = welcomeContent.children[1];
ok(selectedLabel.querySelector("input").checked, "The selected label should be checked");
ok(
selectedLabel.querySelector("input").checked,
"The selected label should be checked"
);
Assert.equal(
engineName.name,
selectedLabel.querySelector("label").textContent.trim(),
@@ -71,7 +76,7 @@ add_task(async function test_Welcome_Steps() {
await goNextWelcomePage("zen-generic-next");
ok(true, "Welcome Search Step Test Finished");
await new Promise((resolve) => {
await new Promise(resolve => {
setTimeout(async () => {
const essentials = welcomeContent.querySelector(
"#zen-welcome-initial-essentials-browser-sidebar-essentials"
@@ -102,12 +107,15 @@ add_task(async function test_Welcome_Steps() {
essentials[2].getAttribute("data-url"),
];
for (const url of urlsToCheck) {
ok(url.startsWith("https://"), `The URL "${url}" should start with "https://"`);
ok(
url.startsWith("https://"),
`The URL "${url}" should start with "https://"`
);
}
await goNextWelcomePage("zen-generic-next");
await new Promise((r) => {
await new Promise(r => {
setTimeout(async () => {
for (const url of urlsToCheck) {
ok(
@@ -128,7 +136,7 @@ add_task(async function test_Welcome_Steps() {
await goNextWelcomePage("zen-welcome-start-browsing");
ok(true, "Welcome Finish Step Test Finished");
await new Promise((resolve) => {
await new Promise(resolve => {
setTimeout(async () => {
Assert.greater(
gBrowser._numZenEssentials,
@@ -136,14 +144,20 @@ add_task(async function test_Welcome_Steps() {
"There should be more than 3 Zen Essentials after the welcome process"
);
Assert.equal(
gBrowser.tabs.filter((tab) => tab.pinned && !tab.hasAttribute("zen-essential")).length,
gBrowser.tabs.filter(
tab => tab.pinned && !tab.hasAttribute("zen-essential")
).length,
3,
"There should be 3 pinned tabs after the welcome process"
);
gBrowser.selectedTab = selectedTab;
const groups = gBrowser.tabGroups;
Assert.equal(groups.length, 1, "There should be one tab group after the welcome process");
Assert.equal(
groups.length,
1,
"There should be one tab group after the welcome process"
);
const group = groups[0];
Assert.equal(
group.tabs.length,
@@ -165,7 +179,11 @@ add_task(async function test_Welcome_Steps() {
tab.hasAttribute("zen-workspace-id"),
"Pinned tabs should have a zen-workspace-id attribute"
);
Assert.equal(tab.group, group, "Pinned tabs should belong to the first tab group");
Assert.equal(
tab.group,
group,
"Pinned tabs should belong to the first tab group"
);
}
}
}

View File

@@ -4,7 +4,7 @@
async function goNextWelcomePage(l10nId) {
/* eslint-disable-next-line no-async-promise-executor */
await new Promise(async (resolve) => {
await new Promise(async resolve => {
const button = document.querySelector(
`#zen-welcome-page-sidebar-buttons button[data-l10n-id="${l10nId}"]`
);
@@ -21,5 +21,5 @@ async function goNextWelcomePage(l10nId) {
}
async function waitForFocus(...args) {
await new Promise((resolve) => SimpleTest.waitForFocus(resolve, ...args));
await new Promise(resolve => SimpleTest.waitForFocus(resolve, ...args));
}

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