mirror of
https://github.com/zen-browser/desktop.git
synced 2026-03-31 21:01:55 +00:00
chore: Run lint and fund dependencies, p=#12718
This commit is contained in:
17
src/-prettierignore.patch
Normal file
17
src/-prettierignore.patch
Normal 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
|
||||
+
|
||||
@@ -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
|
||||
|
||||
@@ -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/**",
|
||||
];
|
||||
|
||||
13
src/tools/lint/eslint/__init__-py.patch
Normal file
13
src/tools/lint/eslint/__init__-py.patch
Normal 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",
|
||||
38
src/zen/@types/lib.gecko.darwin.d.ts
vendored
38
src/zen/@types/lib.gecko.darwin.d.ts
vendored
@@ -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
|
||||
|
||||
2827
src/zen/@types/lib.gecko.dom.d.ts
vendored
2827
src/zen/@types/lib.gecko.dom.d.ts
vendored
File diff suppressed because it is too large
Load Diff
1250
src/zen/@types/lib.gecko.glean.d.ts
vendored
1250
src/zen/@types/lib.gecko.glean.d.ts
vendored
File diff suppressed because it is too large
Load Diff
6
src/zen/@types/lib.gecko.linux.d.ts
vendored
6
src/zen/@types/lib.gecko.linux.d.ts
vendored
@@ -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 {
|
||||
|
||||
3
src/zen/@types/lib.gecko.nsresult.d.ts
vendored
3
src/zen/@types/lib.gecko.nsresult.d.ts
vendored
@@ -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];
|
||||
|
||||
8
src/zen/@types/lib.gecko.tweaks.d.ts
vendored
8
src/zen/@types/lib.gecko.tweaks.d.ts
vendored
@@ -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 {
|
||||
|
||||
61
src/zen/@types/lib.gecko.win32.d.ts
vendored
61
src/zen/@types/lib.gecko.win32.d.ts
vendored
@@ -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;
|
||||
|
||||
2136
src/zen/@types/lib.gecko.xpcom.d.ts
vendored
2136
src/zen/@types/lib.gecko.xpcom.d.ts
vendored
File diff suppressed because it is too large
Load Diff
33
src/zen/@types/zen.d.ts
vendored
33
src/zen/@types/zen.d.ts
vendored
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -9,7 +9,7 @@ class ZenSessionStore extends nsZenPreloadedFeature {
|
||||
this.#waitAndCleanup();
|
||||
}
|
||||
|
||||
promiseInitialized = new Promise((resolve) => {
|
||||
promiseInitialized = new Promise(resolve => {
|
||||
this._resolveInitialized = resolve;
|
||||
});
|
||||
|
||||
|
||||
@@ -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>
|
||||
`
|
||||
)}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||
return text
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">");
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -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) {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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 }
|
||||
);
|
||||
|
||||
@@ -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
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 =
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
},
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 } })
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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."
|
||||
);
|
||||
|
||||
@@ -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."
|
||||
);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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(() => {
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
@@ -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"
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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/",
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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."
|
||||
);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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"
|
||||
);
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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"
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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]);
|
||||
|
||||
|
||||
@@ -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"
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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`);
|
||||
|
||||
|
||||
@@ -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]],
|
||||
});
|
||||
|
||||
@@ -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"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user