-
diff --git a/addon/chrome/content/overlay/multirow.css b/addon/chrome/content/overlay/multirow.css
index 3ae76742..bcb784f8 100644
--- a/addon/chrome/content/overlay/multirow.css
+++ b/addon/chrome/content/overlay/multirow.css
@@ -10,7 +10,7 @@
:root {
--tab_min_width_mlt: 100px;
--tab_max_width_mlt: 200px;
- --tab-min-height_mlt: calc(var(--tab-min-height) + 2 * (var(--proton-tab-block-margin, 0px) + var(--tab-block-margin, 0px)));
+ --tab-min-height_mlt: calc(var(--tabmix-tab-min-height) + 2 * var(--tab-block-margin, 0px)) !important;
--tabmix-scrollbutton-padding: calc(var(--toolbarbutton-inner-padding) + var(--toolbarbutton-outer-padding));
}
diff --git a/addon/chrome/content/preferences/appearance.js b/addon/chrome/content/preferences/appearance.js
index 879775a7..a0f837bd 100644
--- a/addon/chrome/content/preferences/appearance.js
+++ b/addon/chrome/content/preferences/appearance.js
@@ -8,9 +8,10 @@ var gAppearancePane = {
var browserWindow = Tabmix.getTopWin();
// disable options for position the tabbar and scroll mode if TreeStyleTab extension installed
if (browserWindow.Tabmix.tabsUtils.isVerticalTabBar) {
+ const floorpVerticalTabbar = Tabmix.isVersion({fp: "128.0.0"});
const description = document.getElementById("treeStyleTab.msg");
Tabmix.setItem(description, "hidden", null);
- if (browserWindow.gBrowser.tabContainer.verticalMode) {
+ if (browserWindow.gBrowser.tabContainer.verticalMode || floorpVerticalTabbar) {
description.innerHTML = "These preferences are not in use for vertical tabs.";
description.style.width = "25em";
}
@@ -23,7 +24,7 @@ var gAppearancePane = {
Tabmix.setItem("scrollDelay", "disabled", true);
Tabmix.setItem("smoothScroll", "disabled", true);
- if (Tabmix.isVersion(1330)) {
+ if (Tabmix.isVersion(1330) || floorpVerticalTabbar) {
const hideTabbar = $("hideTabbar");
Tabmix.setItem(hideTabbar, "disabled", true);
Tabmix.setItem(hideTabbar.previousElementSibling, "disabled", true);
diff --git a/addon/chrome/content/tab/tab.js b/addon/chrome/content/tab/tab.js
index 6ad5a21e..1367f913 100644
--- a/addon/chrome/content/tab/tab.js
+++ b/addon/chrome/content/tab/tab.js
@@ -322,7 +322,6 @@ Tabmix.tabsUtils = {
_inUpdateVerticalTabStrip: false,
_keepLastTab: false,
_show_newtabbutton: "aftertabs",
- _tabmixPositionalTabs: {},
checkNewtabButtonVisibility: false,
closeButtonsEnabled: false,
initialized: false,
@@ -403,14 +402,8 @@ Tabmix.tabsUtils = {
this.tabBar.mCloseButtons = Tabmix.prefs.getIntPref("tabs.closeButtons");
this._keepLastTab = Tabmix.prefs.getBoolPref("keepLastTab");
this.closeButtonsEnabled = Tabmix.prefs.getBoolPref("tabs.closeButtons.enable");
- this._tabmixPositionalTabs = {
- beforeSelectedTab: null,
- afterSelectedTab: null,
- beforeHoveredTab: null,
- afterHoveredTab: null
- };
- Tabmix.afterTabsButtonsWidth = [35];
+ Tabmix.getAfterTabsButtonsWidth();
Tabmix.tabsNewtabButton = document.getElementById("tabs-newtab-button");
this._show_newtabbutton = "aftertabs";
@@ -463,7 +456,6 @@ Tabmix.tabsUtils = {
if (!this.initialized)
return;
TMP_eventListener.toggleEventListener(this.tabBar, this.events, false, this);
- this._tabmixPositionalTabs = {};
},
handleEvent(aEvent) {
@@ -621,6 +613,11 @@ Tabmix.tabsUtils = {
/** @param {number} width */
const setNewMinWidth = width => {
if (width !== currentMinWidth) {
+ // since we prevent tab animation whe we change min/max width we need to remove closing tabs
+ // to avoid bug 608589
+ for (let tab of gBrowser._removingTabs) {
+ gBrowser._endRemoveTab(tab);
+ }
this.tabBar.setAttribute("no-animation", "");
gTMPprefObserver.dynamicRules.width.style.setProperty(
"min-width",
@@ -672,13 +669,13 @@ Tabmix.tabsUtils = {
if (this._widthCache.minWidth !== newMinWidth) {
this._widthCache.minWidth = newMinWidth;
this._widthCache.maxWidth = newMinWidth;
- this._widthCache[pinnedTabCount] = newMinWidth;
+ this._widthCache[0] = newMinWidth;
}
if (isFirstRowWithPinnedTabs && firstNonPinnedTab) {
const stripWidthWithoutPinned = tabstripWidth - (firstNonPinnedTab.getBoundingClientRect().x - tsboX);
const testNewMinWidth = calcMinWidth(stripWidthWithoutPinned, tabsButtonWidth, newMinWidth);
- if (testNewMinWidth > newMinWidth) {
+ if (testNewMinWidth < newMinWidth) {
const widthForeNonPinnedTabs = calcMinWidth(stripWidthWithoutPinned, tabsButtonWidth);
this._widthCache[pinnedTabCount] = widthForeNonPinnedTabs;
this._widthCache.maxWidth = Math.max(this._widthCache.maxWidth, widthForeNonPinnedTabs);
@@ -699,13 +696,6 @@ Tabmix.tabsUtils = {
return;
}
- // TabmixTabbar.widthFitTitle is false when mTabMinWidth === mTabMaxWidth
- if (this.checkNewtabButtonVisibility && !TabmixTabbar.widthFitTitle) {
- this.updateMinWidth();
- this.disAllowNewtabbutton = false;
- return;
- }
-
if (!this.checkNewtabButtonVisibility) {
this.showNewTabButtonOnSide(this.overflow, "right-side");
return;
@@ -713,8 +703,16 @@ Tabmix.tabsUtils = {
// when Private-tab enabled/disabled we need to reset
// tabsNewtabButton and afterTabsButtonsWidth
- if (!Tabmix.tabsNewtabButton)
+ if (!Tabmix.tabsNewtabButton || !Tabmix.afterTabsButtonsWidthReady) {
Tabmix.getAfterTabsButtonsWidth();
+ }
+
+ // TabmixTabbar.widthFitTitle is false when mTabMinWidth === mTabMaxWidth
+ if (this.checkNewtabButtonVisibility && !TabmixTabbar.widthFitTitle) {
+ this.updateMinWidth();
+ this.disAllowNewtabbutton = false;
+ return;
+ }
const lastTab = Tabmix.visibleTabs.last;
const tsboRect = this.tabBar.arrowScrollbox.scrollbox.getBoundingClientRect();
@@ -1007,7 +1005,7 @@ Tabmix.tabsUtils = {
gBrowser.tabContainer.getAttribute("orient") !== "vertical" &&
TabmixTabbar.visibleRows > 1;
const margin = reduceMargin ? "1px" : "";
- document.documentElement.style.setProperty(this.protonValues.name, margin);
+ document.documentElement.style.setProperty(this.protonValues.name, margin, "important");
},
};
@@ -1879,6 +1877,14 @@ window.gTMPprefObserver = {
);
}
+ this.insertRule(`:root { --tabmix-tab-min-height: var(--tab-min-height);}`, "tabMinHeight");
+ if (Tabmix.isVersion({fp: "128.0.0"})) {
+ window.gFloorpObservePreference("floorp.browser.tabs.tabMinHeight", () => {
+ const height = Services.prefs.getIntPref("floorp.browser.tabs.tabMinHeight", 30);
+ this.dynamicRules.tabMinHeight.style.setProperty("--tabmix-tab-min-height", height + "px");
+ });
+ }
+
this.dynamicProtonRules();
this.toolbarbuttonTopMargin();
},
@@ -1896,6 +1902,14 @@ window.gTMPprefObserver = {
}`);
}
+ const padding = Tabmix.isVersion({fp: "128.0.0"}) ? {
+ block: "3.5px",
+ inline: "3.5px",
+ } : {
+ block: "calc(var(--toolbarbutton-inner-padding) - 3px)",
+ inline: "calc(var(--toolbarbutton-inner-padding) - 6px)",
+ };
+
this.insertRule(
`#tabmix-scrollbox::part(scrollbutton-up),
#tabmix-scrollbox::part(scrollbutton-down) {
@@ -1904,14 +1918,14 @@ window.gTMPprefObserver = {
border: 4px solid transparent;
border-radius: calc(var(--tab-border-radius) + 4px);
margin: ${buttonsMarginBlock};
- padding: calc(var(--toolbarbutton-inner-padding) - 3px) calc(var(--toolbarbutton-inner-padding) - 6px);
+ padding: ${padding.block} ${padding.inline};
}`
);
this.insertRule(
`#tabmix-scrollbox[tabmix-flowing=multibar]::part(scrollbutton-up),
#tabmix-scrollbox[tabmix-flowing=multibar]::part(scrollbutton-down) {
- padding-inline: calc(var(--toolbarbutton-inner-padding) - 5px);
+ padding: ${padding.block};
}`
);
diff --git a/addon/chrome/content/tabmix.js b/addon/chrome/content/tabmix.js
index 97e9ca75..bf4fb06b 100644
--- a/addon/chrome/content/tabmix.js
+++ b/addon/chrome/content/tabmix.js
@@ -73,13 +73,15 @@ Tabmix.sessionInitialized = function() {
}
};
-Tabmix.getAfterTabsButtonsWidth = function TMP_getAfterTabsButtonsWidth() {
- if (!this.afterTabsButtonsWidth) {
- this.afterTabsButtonsWidth = [];
- }
+/** @this {typeof TabmixNS} */ // @ts-ignore
+Tabmix.getAfterTabsButtonsWidth = function() {
+ this.afterTabsButtonsWidthReady = false;
+ this.afterTabsButtonsWidth = this.isVersion({fp: "128.0.0"}) ? [40] : [35];
+ /** @type {number[]} */
+ const buttonWidths = [];
if (gBrowser.tabContainer.getAttribute("orient") == "horizontal") {
const {toolbar, tabBar, collapsed, tabBarCollapsed, toolbarCollapsed} =
- Tabmix.tabsUtils.getCollapsedState;
+ this.tabsUtils.getCollapsedState;
let stripIsHidden = TabmixTabbar.hideMode !== 0 && collapsed;
if (stripIsHidden) {
toolbar.collapsed = false;
@@ -88,7 +90,9 @@ Tabmix.getAfterTabsButtonsWidth = function TMP_getAfterTabsButtonsWidth() {
// save tabsNewtabButton width
this.tabsNewtabButton = document.getElementById("tabs-newtab-button");
this.tabsNewtabButton.setAttribute("force-display", true);
- let openNewTabRect = Tabmix.getBoundsWithoutFlushing(this.tabsNewtabButton);
+ // don't use getBoundsWithoutFlushing here, it will get width zero if the button is hidden
+ // since we get here after the browser was painted we can use getBoundingClientRect
+ let openNewTabRect = this.tabsNewtabButton.getBoundingClientRect();
let style = window.getComputedStyle(this.tabsNewtabButton);
let marginStart = style?.getPropertyValue("margin-left") ?? "0px";
// it doesn't work when marginEnd add to buttonWidth
@@ -96,7 +100,7 @@ Tabmix.getAfterTabsButtonsWidth = function TMP_getAfterTabsButtonsWidth() {
// let buttonWidth = openNewTabRect.width + parseFloat(marginStart) + parseFloat(marginEnd);
let buttonWidth = openNewTabRect.width + parseFloat(marginStart);
if (buttonWidth > 0) {
- this.afterTabsButtonsWidth.push(buttonWidth);
+ buttonWidths.push(buttonWidth);
}
// when privateTab extension installed add its new tab button width
@@ -104,8 +108,8 @@ Tabmix.getAfterTabsButtonsWidth = function TMP_getAfterTabsButtonsWidth() {
// the right button
let openNewPrivateTab = document.getElementById("privateTab-afterTabs-openNewPrivateTab");
if (openNewPrivateTab) {
- let openNewPrivateTabRect = Tabmix.getBoundsWithoutFlushing(openNewPrivateTab);
- this.afterTabsButtonsWidth.push(openNewPrivateTabRect.width);
+ let openNewPrivateTabRect = openNewPrivateTab.getBoundingClientRect();
+ buttonWidths.push(openNewPrivateTabRect.width);
if (openNewPrivateTabRect.right > openNewTabRect.right)
this.tabsNewtabButton = openNewPrivateTab;
}
@@ -114,6 +118,11 @@ Tabmix.getAfterTabsButtonsWidth = function TMP_getAfterTabsButtonsWidth() {
toolbar.collapsed = toolbarCollapsed;
tabBar.collapsed = tabBarCollapsed;
}
+ if (buttonWidths.length) {
+ this.tabsUtils._widthCache = {minWidth: 0, maxWidth: 0};
+ this.afterTabsButtonsWidth = buttonWidths;
+ this.afterTabsButtonsWidthReady = true;
+ }
}
};
@@ -162,8 +171,6 @@ Tabmix.afterDelayedStartup = function() {
TMP_extensionsCompatibility.onDelayedStartup();
- setTimeout(() => Tabmix.getAfterTabsButtonsWidth(), 100);
-
gTMPprefObserver.setMenuIcons();
TabmixTabbar.updateSettings(true);
diff --git a/addon/modules/AutoReload.jsm b/addon/modules/AutoReload.jsm
index e3815a91..c3ce01b4 100644
--- a/addon/modules/AutoReload.jsm
+++ b/addon/modules/AutoReload.jsm
@@ -426,7 +426,7 @@ function doReloadTab(window, browser, tab, data) {
// Also reset DOS mitigations for the basic auth prompt on reload.
delete browser.authPromptAbuseCounter;
- if (TabmixSvc.version(1290) || TabmixSvc.version(1283, "esr")) {
+ if (TabmixSvc.version(1290) || TabmixSvc.version(1283, "esr") || TabmixSvc.version({fp: "128.0.0"})) {
if (window.document.hasValidTransientUserGestureActivation) {
loadFlags |= Ci.nsIWebNavigation.LOAD_FLAGS_USER_ACTIVATION;
}
diff --git a/addon/modules/Floorp.jsm b/addon/modules/Floorp.jsm
new file mode 100644
index 00000000..75b1de87
--- /dev/null
+++ b/addon/modules/Floorp.jsm
@@ -0,0 +1,94 @@
+"use strict";
+
+const EXPORTED_SYMBOLS = ["FloorpPrefsObserver"];
+
+const lazy = {};
+
+ChromeUtils.defineLazyGetter(lazy, "prefs", () => {
+ const {Preferences} = ChromeUtils.importESModule("resource://gre/modules/Preferences.sys.mjs");
+ return new Preferences("");
+});
+
+const PREFS = [
+ "extensions.tabmix.tabBarMode",
+ "floorp.tabbar.style",
+ "userChrome.padding.tabbar_height",
+];
+
+const FloorpPrefsObserver = {
+ init() {
+ if (this._initialized) {
+ return;
+ }
+ this._initialized = true;
+
+ PREFS.forEach(pref => this.onPrefChange(pref));
+ PREFS.forEach(pref => Services.prefs.addObserver(pref, this));
+ Services.obs.addObserver(this, "quit-application");
+ },
+
+ observe(subject, topic, data) {
+ switch (topic) {
+ case "nsPref:changed":
+ this.onPrefChange(data);
+ break;
+ case "quit-application":
+ this.onQuitApplication();
+ break;
+ }
+ },
+
+ onPrefChange(data) {
+ const prefValue = lazy.prefs.get(data);
+
+ // when in vertical mode exit
+ if (Services.prefs.getIntPref("floorp.tabbar.style") === 2 && data !== "floorp.tabbar.style") {
+ return;
+ }
+
+ switch (data) {
+ case "extensions.tabmix.tabBarMode": {
+ const pref = "userChrome.padding.tabbar_height";
+ const backupPref = `${pref}._backup`;
+ if (prefValue === 2) {
+ Services.prefs.setBoolPref(backupPref, Services.prefs.getBoolPref(pref));
+ Services.prefs.setBoolPref(pref, false);
+ Services.prefs.setIntPref("floorp.tabbar.style", 0);
+ } else if (Services.prefs.prefHasUserValue(backupPref)) {
+ Services.prefs.setBoolPref(pref, Services.prefs.getBoolPref(backupPref));
+ Services.prefs.clearUserPref(backupPref);
+ }
+ break;
+ }
+ case "floorp.tabbar.style": {
+ const pref = "extensions.tabmix.tabBarMode";
+ const backupPref = `${pref}._backup`;
+ const tabmixInMultiRow = Services.prefs.getIntPref(pref) === 2;
+ if (prefValue === 0 && Services.prefs.prefHasUserValue(backupPref)) {
+ Services.prefs.setIntPref(pref, Services.prefs.getIntPref(backupPref));
+ Services.prefs.clearUserPref(backupPref);
+ } else if (prefValue === 1 && tabmixInMultiRow) {
+ Services.prefs.setIntPref(data, 0);
+ } else if (prefValue === 2 && tabmixInMultiRow) {
+ Services.prefs.setIntPref(backupPref, 2);
+ Services.prefs.setIntPref(pref, 1);
+ }
+ break;
+ }
+ case "userChrome.padding.tabbar_height": {
+ if (
+ prefValue &&
+ Services.prefs.getIntPref("extensions.tabmix.tabBarMode") === 2
+ ) {
+ Services.prefs.setBoolPref(data, false);
+ }
+ break;
+ }
+ }
+ },
+
+ onQuitApplication() {
+ PREFS.forEach(pref => Services.prefs.removeObserver(pref, this));
+ Services.obs.removeObserver(this, "quit-application");
+ },
+};
diff --git a/addon/modules/TabmixSvc.jsm b/addon/modules/TabmixSvc.jsm
index 4ad469e3..ebf9589d 100644
--- a/addon/modules/TabmixSvc.jsm
+++ b/addon/modules/TabmixSvc.jsm
@@ -15,12 +15,10 @@ ChromeUtils.defineModuleGetter(lazy, "SyncedTabs",
ChromeUtils.defineModuleGetter(lazy, "isVersion",
"chrome://tabmix-resource/content/BrowserVersion.jsm");
-// place holder for load default preferences function
-// eslint-disable-next-line no-unused-vars
-var pref;
-let TabmixSvc;
+ChromeUtils.defineModuleGetter(lazy, "FloorpPrefsObserver",
+ "chrome://tabmix-resource/content/Floorp.jsm");
-TabmixSvc = {
+const TabmixSvc = {
aboutBlank: "about:blank",
aboutNewtab: "about:#".replace("#", "newtab"),
newtabUrl: "browser.#.url".replace("#", "newtab"),
@@ -180,6 +178,10 @@ TabmixSvc = {
Services.prefs.lockPref("extensions.tabmix.tabBarPosition");
}
+ if (lazy.isVersion({fp: "128.0.0"})) {
+ lazy.FloorpPrefsObserver.init();
+ }
+
aWindow.gTMPprefObserver.setLink_openPrefs();
},
diff --git a/types/addon.d.ts b/types/addon.d.ts
index 0efccbb7..c80fd29c 100644
--- a/types/addon.d.ts
+++ b/types/addon.d.ts
@@ -156,6 +156,9 @@ interface Window {
init: () => void;
};
+ /** Floorp */
+ gFloorpObservePreference: (prefName: string, callback: () => void) => void;
+
/** @deprecated - removed from firefox on version 126 */
BrowserOpenTab: (options: {event: MouseEvent; url: string}) => void;
}
@@ -659,11 +662,6 @@ declare namespace TabmixTabbarNS {
}
declare namespace PrivateFunctionsNS {
- namespace TabmixTabbar {
- function _updateAtt(tab: Tab | null | undefined, type: keyof typeof TabsUtils._tabmixPositionalTabs, attrib: string, visible?: boolean): void;
- function _getAttVal(val: boolean | null, hoverAttr?: boolean): "special" | boolean | null;
- }
-
namespace UndocloseTabButtonObserver {
function _removeTab(b: MockedGeckoTypes.TabBrowser, aTab: Tab): void;
}
diff --git a/types/extraTabmixUtils.d.ts b/types/extraTabmixUtils.d.ts
index 30af3165..08bb03b3 100644
--- a/types/extraTabmixUtils.d.ts
+++ b/types/extraTabmixUtils.d.ts
@@ -66,6 +66,7 @@ declare namespace TabmixNS {
let _lastTabOpenedTime: number;
let _deferredInitialized: DeferredPromise;
let afterTabsButtonsWidth: number[];
+ let afterTabsButtonsWidthReady: boolean;
let initialization: typeof TabmixInitialization;
let isFirstWindow: boolean;
let selectedTab: Tab | null;
@@ -74,7 +75,7 @@ declare namespace TabmixNS {
let userTypedValue: string;
function afterDelayedStartup(): void;
function beforeDelayedStartup(): void;
- function getAfterTabsButtonsWidth(this: typeof TabmixNS): void;
+ function getAfterTabsButtonsWidth(): void;
function sessionInitialized(): void;
function startup(): void;
// lazygetters for modules (jsm files)
@@ -434,12 +435,6 @@ declare namespace TabsUtils {
let closeButtonsEnabled: boolean;
let initialized: boolean;
- let _tabmixPositionalTabs: {
- beforeSelectedTab?: Tab | null | undefined;
- afterSelectedTab?: Tab | null | undefined;
- beforeHoveredTab?: Tab | null | undefined;
- afterHoveredTab?: Tab | null | undefined;
- };
const tabBar: MockedGeckoTypes.TabContainer;
const scrollClientRect: DOMRect;
function getInnerbox(): HTMLElement;
diff --git a/types/general.d.ts b/types/general.d.ts
index 00bd2fc6..e3d1748b 100644
--- a/types/general.d.ts
+++ b/types/general.d.ts
@@ -304,7 +304,7 @@ declare namespace MockedGeckoTypes {
visibleTab: BrowserTab;
};
_blurTab: (tab: BrowserTab) => void;
- readonly pinnedTabCount: number;
+ _endRemoveTab: (tab: BrowserTab) => void;
/** @deprecated replaced with pinnedTabCount in Firefox version 133 */
readonly _numPinnedTabs: number;
_lastRelatedTabMap: WeakMap
;
@@ -334,6 +334,7 @@ declare namespace MockedGeckoTypes {
lastMultiSelectedTab: BrowserTab;
moveTabTo: (tab: BrowserTab, index: number, keepRelatedTabs?: boolean) => void;
pinTab: (tab: BrowserTab) => void;
+ readonly pinnedTabCount: number;
preloadedBrowser?: ChromeBrowser;
reloadTab: (tab: BrowserTab) => void;
/// modified by Tab Mix Plus
diff --git a/types/tabmix.d.ts b/types/tabmix.d.ts
index e60d0b1b..6c0ea000 100644
--- a/types/tabmix.d.ts
+++ b/types/tabmix.d.ts
@@ -2,6 +2,8 @@
// Tabmix modules
+type BrowserVersion = (aVersionNo: number | {ff?: number; wf?: string; fp?: string; updateChannel?: string}, updateChannel?: string) => boolean;
+
interface ChromeManifest {
parse: (filename?: string, base?: string) => Promise;
}
@@ -25,7 +27,7 @@ interface CSSRule {
readonly style: CSSStyleDeclaration;
}
-type RulesTYpes = "max-rows" | "visibleRows" | "width";
+type RulesTYpes = "max-rows" | "visibleRows" | "width" | "tabMinHeight";
interface gTMPprefObserver {
_marginStart: string;
_singleWindowUI_initialized: boolean;
@@ -158,7 +160,7 @@ declare namespace TabmixNS {
const prefs: nsIPrefBranchXpcom;
const defaultPrefs: nsIPrefBranchXpcom;
- function isVersion(aVersionNo: number | {ff?: number; wf?: string; bs?: string; updateChannel?: string}, updateChannel?: string): boolean;
+ const isVersion: BrowserVersion;
function isAltKey(event: MouseEvent): boolean;
function debug(aMessage: string, aShowCaller?: boolean): void;
function showItem(aItemOrId: ItemOrId, aShow?: boolean): void;
@@ -281,6 +283,7 @@ declare namespace TabmixModules {
getSMString: (key: string) => string;
i10IdMap: I10Map;
isCyberfox: boolean;
+ isFloorp: boolean;
isFixedGoogleUrl: (url: string) => boolean;
isLinux: boolean;
isMac: boolean;
@@ -315,7 +318,7 @@ declare namespace TabmixModules {
progressMeter: TabStyle & {text: false};
};
URILoadingHelperChanged: boolean;
- version: (aVersionNo: number | {ff?: number; wf?: string; bs?: string; updateChannel?: string}, updateChannel?: string) => boolean;
+ version: BrowserVersion;
whereToOpenLinkChanged: boolean;
windowStartup: {
_initialized: boolean;