forked from ZenBrowserMirrors/zen-desktop
Compare commits
27 commits
zen-librar
...
dev
Author | SHA1 | Date | |
---|---|---|---|
|
7c7a911d1e | ||
|
663243264b | ||
|
03ca00748c | ||
|
cbb1a4bc44 | ||
|
21f3ab23d3 | ||
|
18944d5ed8 | ||
|
5c6e5f7361 | ||
|
bd72aebd98 | ||
|
0278aea4f7 | ||
|
3c01004641 | ||
|
b8213569e5 | ||
|
4d27f9d741 | ||
|
3b56abf090 | ||
|
590ba6de1b | ||
|
4aa215e091 | ||
|
de175bff11 | ||
|
a6bc8d7105 | ||
|
e48e7caef1 | ||
|
015cdad2df | ||
|
ef6cf5fae1 | ||
|
797152da89 | ||
|
316ff45859 | ||
|
e0ac9ba424 | ||
|
09ca430b88 | ||
|
dda1dab6f3 | ||
|
fbf411c096 | ||
|
4b0c6f2ca5 |
76 changed files with 1821 additions and 1890 deletions
|
@ -29,7 +29,7 @@
|
||||||
|
|
||||||
## 🖥️ Compatibility
|
## 🖥️ Compatibility
|
||||||
|
|
||||||
Zen is currently built using Firefox version `138.0.4`! 🚀
|
Zen is currently built using Firefox version `139.0`! 🚀
|
||||||
|
|
||||||
- [`Zen Twilight`](https://zen-browser.app/download?twilight) - Is currently built using Firefox version `RC 139.0`!
|
- [`Zen Twilight`](https://zen-browser.app/download?twilight) - Is currently built using Firefox version `RC 139.0`!
|
||||||
- Check out the latest [release notes](https://zen-browser.app/release-notes)!
|
- Check out the latest [release notes](https://zen-browser.app/release-notes)!
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
82a08ea3ce2d17f21f3d45f4b5607a37590b0158
|
da30619f3ea895b356ded705b8dff9e4f271198f
|
2
l10n
2
l10n
|
@ -1 +1 @@
|
||||||
Subproject commit 644474b8c92e306288d835698eb6714081a650d8
|
Subproject commit ebecb32da8929e4f14f9a20f40acb9dab401101c
|
8
package-lock.json
generated
8
package-lock.json
generated
|
@ -9,7 +9,7 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@zen-browser/surfer": "^1.11.12"
|
"@zen-browser/surfer": "^1.11.13"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/preset-typescript": "^7.27.0",
|
"@babel/preset-typescript": "^7.27.0",
|
||||||
|
@ -817,9 +817,9 @@
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@zen-browser/surfer": {
|
"node_modules/@zen-browser/surfer": {
|
||||||
"version": "1.11.12",
|
"version": "1.11.13",
|
||||||
"resolved": "https://registry.npmjs.org/@zen-browser/surfer/-/surfer-1.11.12.tgz",
|
"resolved": "https://registry.npmjs.org/@zen-browser/surfer/-/surfer-1.11.13.tgz",
|
||||||
"integrity": "sha512-wny52xOFvZe5aPXxLVxEcAzDNEiWWoDiCZFlzsNxkyQ5Lw6vzqroMWpjQPJwBRJOc/JssgiXMdd1uwl2LLnovQ==",
|
"integrity": "sha512-D0TyunAWYtTdJkuUkYeOc6VBXzE9aoDs58kWu/Oi/aU3vd8IbqXPbZZfYwj5FWPWDReMrJUNkkKAEdbL44y9aw==",
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@resvg/resvg-js": "^1.4.0",
|
"@resvg/resvg-js": "^1.4.0",
|
||||||
|
|
|
@ -42,7 +42,7 @@
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/zen-browser/desktop#readme",
|
"homepage": "https://github.com/zen-browser/desktop#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@zen-browser/surfer": "^1.11.12"
|
"@zen-browser/surfer": "^1.11.13"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/preset-typescript": "^7.27.0",
|
"@babel/preset-typescript": "^7.27.0",
|
||||||
|
|
|
@ -16,6 +16,7 @@ pref('browser.toolbars.bookmarks.visibility', 'never');
|
||||||
pref("browser.bookmarks.openInTabClosesMenu", false);
|
pref("browser.bookmarks.openInTabClosesMenu", false);
|
||||||
pref("browser.menu.showViewImageInfo", true);
|
pref("browser.menu.showViewImageInfo", true);
|
||||||
pref("findbar.highlightAll", true);
|
pref("findbar.highlightAll", true);
|
||||||
|
|
||||||
pref("layout.word_select.eat_space_to_next_word", false);
|
pref("layout.word_select.eat_space_to_next_word", false);
|
||||||
|
|
||||||
// Better Windows theming
|
// Better Windows theming
|
||||||
|
|
|
@ -22,13 +22,17 @@ pref('zen.mediacontrols.enabled', true);
|
||||||
// Exposure:
|
// Exposure:
|
||||||
pref('zen.haptic-feedback.enabled', true);
|
pref('zen.haptic-feedback.enabled', true);
|
||||||
|
|
||||||
|
pref('zen.mods.auto-update-days', 20); // In days
|
||||||
#ifdef MOZILLA_OFFICIAL
|
#ifdef MOZILLA_OFFICIAL
|
||||||
|
pref('zen.mods.auto-update', true);
|
||||||
pref('zen.rice.api.url', 'https://share.zen-browser.app', locked);
|
pref('zen.rice.api.url', 'https://share.zen-browser.app', locked);
|
||||||
pref('zen.injections.match-urls', 'https://zen-browser.app/*,https://share.zen-browser.app/*', locked);
|
pref('zen.injections.match-urls', 'https://zen-browser.app/*,https://share.zen-browser.app/*', locked);
|
||||||
#else
|
#else
|
||||||
|
pref('zen.mods.auto-update', false);
|
||||||
pref('zen.rice.api.url', "http://localhost", locked);
|
pref('zen.rice.api.url', "http://localhost", locked);
|
||||||
pref('zen.injections.match-urls', 'http://localhost/*', locked);
|
pref('zen.injections.match-urls', 'http://localhost/*', locked);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
pref('zen.rice.share.notice.accepted', false);
|
pref('zen.rice.share.notice.accepted', false);
|
||||||
|
|
||||||
#ifdef XP_MACOSX
|
#ifdef XP_MACOSX
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
diff --git a/browser/base/content/aboutDialog.js b/browser/base/content/aboutDialog.js
|
diff --git a/browser/base/content/aboutDialog.js b/browser/base/content/aboutDialog.js
|
||||||
index f6e1391baf12abb91c85a95107bb3923118746c0..76c7b75a4e29056110f1631a50047c4ddd8b1f4a 100644
|
index f6e1391baf12abb91c85a95107bb3923118746c0..cac04aa288e8a305d0c8b28e0c919abce87658e5 100644
|
||||||
--- a/browser/base/content/aboutDialog.js
|
--- a/browser/base/content/aboutDialog.js
|
||||||
+++ b/browser/base/content/aboutDialog.js
|
+++ b/browser/base/content/aboutDialog.js
|
||||||
@@ -52,7 +52,7 @@ function init() {
|
@@ -52,7 +52,7 @@ function init() {
|
||||||
|
@ -20,3 +20,18 @@ index f6e1391baf12abb91c85a95107bb3923118746c0..76c7b75a4e29056110f1631a50047c4d
|
||||||
versionIdKey += "-nightly";
|
versionIdKey += "-nightly";
|
||||||
let buildID = Services.appinfo.appBuildID;
|
let buildID = Services.appinfo.appBuildID;
|
||||||
let year = buildID.slice(0, 4);
|
let year = buildID.slice(0, 4);
|
||||||
|
@@ -125,14 +125,6 @@ function init() {
|
||||||
|
window.close();
|
||||||
|
});
|
||||||
|
if (AppConstants.MOZ_UPDATER) {
|
||||||
|
- document
|
||||||
|
- .getElementById("aboutDialogHelpLink")
|
||||||
|
- .addEventListener("click", () => {
|
||||||
|
- openHelpLink("firefox-help");
|
||||||
|
- });
|
||||||
|
- document
|
||||||
|
- .getElementById("submit-feedback")
|
||||||
|
- .addEventListener("click", openFeedbackPage);
|
||||||
|
document
|
||||||
|
.getElementById("checkForUpdatesButton")
|
||||||
|
.addEventListener("command", () => {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
diff --git a/browser/base/content/aboutDialog.xhtml b/browser/base/content/aboutDialog.xhtml
|
diff --git a/browser/base/content/aboutDialog.xhtml b/browser/base/content/aboutDialog.xhtml
|
||||||
index c64980810570fcea84e33fdc2d66ac42a79f4e46..b7198e810a7510fa82cc6801cfd01c88a08d42c1 100644
|
index c64980810570fcea84e33fdc2d66ac42a79f4e46..6ef9bf4b88f0a0539d833f662c4dd890fd1fde93 100644
|
||||||
--- a/browser/base/content/aboutDialog.xhtml
|
--- a/browser/base/content/aboutDialog.xhtml
|
||||||
+++ b/browser/base/content/aboutDialog.xhtml
|
+++ b/browser/base/content/aboutDialog.xhtml
|
||||||
@@ -35,6 +35,7 @@
|
@@ -35,6 +35,7 @@
|
||||||
|
@ -10,7 +10,18 @@ index c64980810570fcea84e33fdc2d66ac42a79f4e46..b7198e810a7510fa82cc6801cfd01c88
|
||||||
</linkset>
|
</linkset>
|
||||||
|
|
||||||
<html:div id="aboutDialogContainer">
|
<html:div id="aboutDialogContainer">
|
||||||
@@ -125,21 +126,23 @@
|
@@ -102,10 +103,6 @@
|
||||||
|
<label id="version" class="update"/>
|
||||||
|
<label id="releasenotes" is="text-link" hidden="true" data-l10n-id="releaseNotes-link"/>
|
||||||
|
</hbox>
|
||||||
|
- <description class="text-blurb">
|
||||||
|
- <label id="aboutDialogHelpLink" is="text-link" data-l10n-id="aboutdialog-help-user"/>
|
||||||
|
- <label id="submit-feedback" is="text-link" data-l10n-id="aboutdialog-submit-feedback"/>
|
||||||
|
- </description>
|
||||||
|
</vbox>
|
||||||
|
#endif
|
||||||
|
</hbox>
|
||||||
|
@@ -125,21 +122,17 @@
|
||||||
</description>
|
</description>
|
||||||
</vbox>
|
</vbox>
|
||||||
<description class="text-blurb" id="communityDesc" data-l10n-id="community-2">
|
<description class="text-blurb" id="communityDesc" data-l10n-id="community-2">
|
||||||
|
@ -18,12 +29,10 @@ index c64980810570fcea84e33fdc2d66ac42a79f4e46..b7198e810a7510fa82cc6801cfd01c88
|
||||||
+ <label is="text-link" href="https://github.com/zen-browser/desktop" data-l10n-name="community-mozillaLink"/>
|
+ <label is="text-link" href="https://github.com/zen-browser/desktop" data-l10n-name="community-mozillaLink"/>
|
||||||
<label is="text-link" useoriginprincipal="true" href="about:credits" data-l10n-name="community-creditsLink"/>
|
<label is="text-link" useoriginprincipal="true" href="about:credits" data-l10n-name="community-creditsLink"/>
|
||||||
</description>
|
</description>
|
||||||
+#if 0
|
- <description class="text-blurb" id="contributeDesc" data-l10n-id="helpus">
|
||||||
<description class="text-blurb" id="contributeDesc" data-l10n-id="helpus">
|
- <label is="text-link" href="https://foundation.mozilla.org/?form=firefox-about" data-l10n-name="helpus-donateLink"/>
|
||||||
<label is="text-link" href="https://foundation.mozilla.org/?form=firefox-about" data-l10n-name="helpus-donateLink"/>
|
- <label is="text-link" href="https://www.mozilla.org/contribute/?utm_source=firefox-browser&utm_medium=firefox-desktop&utm_campaign=about-dialog" data-l10n-name="helpus-getInvolvedLink"/>
|
||||||
<label is="text-link" href="https://www.mozilla.org/contribute/?utm_source=firefox-browser&utm_medium=firefox-desktop&utm_campaign=about-dialog" data-l10n-name="helpus-getInvolvedLink"/>
|
- </description>
|
||||||
</description>
|
|
||||||
+#endif
|
|
||||||
</vbox>
|
</vbox>
|
||||||
</vbox>
|
</vbox>
|
||||||
</hbox>
|
</hbox>
|
||||||
|
|
|
@ -1,8 +1,35 @@
|
||||||
diff --git a/browser/base/content/browser-addons.js b/browser/base/content/browser-addons.js
|
diff --git a/browser/base/content/browser-addons.js b/browser/base/content/browser-addons.js
|
||||||
index 992d07daaef1abc4554a43aa654888f66963c575..73e620b70b7ed14e9d140e875c2cd5f5ac31456b 100644
|
index 73593191936cc345ee8e2c28cb251dc13f4c2fd4..e6c459c1ebc60a1f3930a55e212570f696bf07a0 100644
|
||||||
--- a/browser/base/content/browser-addons.js
|
--- a/browser/base/content/browser-addons.js
|
||||||
+++ b/browser/base/content/browser-addons.js
|
+++ b/browser/base/content/browser-addons.js
|
||||||
@@ -2105,18 +2105,20 @@ var gUnifiedExtensions = {
|
@@ -735,7 +735,7 @@ var gXPInstallObserver = {
|
||||||
|
persistent: true,
|
||||||
|
hideClose: true,
|
||||||
|
popupOptions: {
|
||||||
|
- position: "bottomright topright",
|
||||||
|
+ position: gZenUIManager.panelUIPosition,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
@@ -942,7 +942,7 @@ var gXPInstallObserver = {
|
||||||
|
hideClose: true,
|
||||||
|
timeout: Date.now() + 30000,
|
||||||
|
popupOptions: {
|
||||||
|
- position: "bottomright topright",
|
||||||
|
+ position: gZenUIManager.panelUIPosition,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
@@ -2125,7 +2125,7 @@ var gUnifiedExtensions = {
|
||||||
|
|
||||||
|
panel.hidden = false;
|
||||||
|
PanelMultiView.openPopup(panel, this._button, {
|
||||||
|
- position: "bottomright topright",
|
||||||
|
+ position: gZenUIManager.panelUIPosition,
|
||||||
|
triggerEvent: aEvent,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@@ -2294,18 +2294,20 @@ var gUnifiedExtensions = {
|
||||||
this._maybeMoveWidgetNodeBack(widgetId);
|
this._maybeMoveWidgetNodeBack(widgetId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,10 +29,9 @@
|
||||||
</linkset>
|
</linkset>
|
||||||
|
|
||||||
# Scripts used all over the browser
|
# Scripts used all over the browser
|
||||||
<script src="chrome://browser/content/ZenUIManager.mjs"></script>
|
<script src="chrome://browser/content/ZenUIManager.mjs"></script>
|
||||||
<script src="chrome://browser/content/zen-components/ZenFolders.mjs"></script>
|
<script src="chrome://browser/content/zen-components/ZenFolders.mjs"></script>
|
||||||
<script src="chrome://browser/content/zen-components/ZenThemesCommon.mjs"></script>
|
<script src="chrome://browser/content/zen-components/ZenMods.mjs"></script>
|
||||||
<script src="chrome://browser/content/zen-components/ZenThemesImporter.mjs"></script>
|
|
||||||
<script src="chrome://browser/content/zen-components/ZenCompactMode.mjs"></script>
|
<script src="chrome://browser/content/zen-components/ZenCompactMode.mjs"></script>
|
||||||
<script src="chrome://browser/content/zen-components/ZenPinnedTabsStorage.mjs"></script>
|
<script src="chrome://browser/content/zen-components/ZenPinnedTabsStorage.mjs"></script>
|
||||||
<script src="chrome://browser/content/zen-components/ZenWorkspacesStorage.mjs"></script>
|
<script src="chrome://browser/content/zen-components/ZenWorkspacesStorage.mjs"></script>
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
content/browser/ZenCustomizableUI.sys.mjs (../../zen/common/ZenCustomizableUI.sys.mjs)
|
content/browser/ZenCustomizableUI.sys.mjs (../../zen/common/ZenCustomizableUI.sys.mjs)
|
||||||
content/browser/zen-components/ZenUIMigration.mjs (../../zen/common/ZenUIMigration.mjs)
|
content/browser/zen-components/ZenUIMigration.mjs (../../zen/common/ZenUIMigration.mjs)
|
||||||
content/browser/zen-components/ZenCommonUtils.mjs (../../zen/common/ZenCommonUtils.mjs)
|
content/browser/zen-components/ZenCommonUtils.mjs (../../zen/common/ZenCommonUtils.mjs)
|
||||||
|
content/browser/zen-components/ZenSessionStore.mjs (../../zen/common/ZenSessionStore.mjs)
|
||||||
|
|
||||||
content/browser/zen-styles/zen-theme.css (../../zen/common/styles/zen-theme.css)
|
content/browser/zen-styles/zen-theme.css (../../zen/common/styles/zen-theme.css)
|
||||||
content/browser/zen-styles/zen-buttons.css (../../zen/common/styles/zen-buttons.css)
|
content/browser/zen-styles/zen-buttons.css (../../zen/common/styles/zen-buttons.css)
|
||||||
|
@ -34,10 +35,7 @@
|
||||||
content/browser/zen-components/ZenViewSplitter.mjs (../../zen/split-view/ZenViewSplitter.mjs)
|
content/browser/zen-components/ZenViewSplitter.mjs (../../zen/split-view/ZenViewSplitter.mjs)
|
||||||
content/browser/zen-styles/zen-decks.css (../../zen/split-view/zen-decks.css)
|
content/browser/zen-styles/zen-decks.css (../../zen/split-view/zen-decks.css)
|
||||||
|
|
||||||
content/browser/zen-components/ZenThemesCommon.mjs (../../zen/mods/ZenThemesCommon.mjs)
|
content/browser/zen-components/ZenMods.mjs (../../zen/mods/ZenMods.mjs)
|
||||||
content/browser/zen-components/ZenThemesImporter.mjs (../../zen/mods/ZenThemesImporter.mjs)
|
|
||||||
content/browser/zen-components/actors/ZenThemeMarketplaceParent.sys.mjs (../../zen/mods/actors/ZenThemeMarketplaceParent.sys.mjs)
|
|
||||||
content/browser/zen-components/actors/ZenThemeMarketplaceChild.sys.mjs (../../zen/mods/actors/ZenThemeMarketplaceChild.sys.mjs)
|
|
||||||
|
|
||||||
content/browser/zen-components/ZenWorkspaceIcons.mjs (../../zen/workspaces/ZenWorkspaceIcons.mjs)
|
content/browser/zen-components/ZenWorkspaceIcons.mjs (../../zen/workspaces/ZenWorkspaceIcons.mjs)
|
||||||
content/browser/zen-components/ZenWorkspace.mjs (../../zen/workspaces/ZenWorkspace.mjs)
|
content/browser/zen-components/ZenWorkspace.mjs (../../zen/workspaces/ZenWorkspace.mjs)
|
||||||
|
@ -57,8 +55,6 @@
|
||||||
|
|
||||||
content/browser/zen-components/ZenGlanceManager.mjs (../../zen/glance/ZenGlanceManager.mjs)
|
content/browser/zen-components/ZenGlanceManager.mjs (../../zen/glance/ZenGlanceManager.mjs)
|
||||||
content/browser/zen-styles/zen-glance.css (../../zen/glance/zen-glance.css)
|
content/browser/zen-styles/zen-glance.css (../../zen/glance/zen-glance.css)
|
||||||
content/browser/zen-components/actors/ZenGlanceChild.sys.mjs (../../zen/glance/actors/ZenGlanceChild.sys.mjs)
|
|
||||||
content/browser/zen-components/actors/ZenGlanceParent.sys.mjs (../../zen/glance/actors/ZenGlanceParent.sys.mjs)
|
|
||||||
|
|
||||||
content/browser/zen-components/ZenFolders.mjs (../../zen/folders/ZenFolders.mjs)
|
content/browser/zen-components/ZenFolders.mjs (../../zen/folders/ZenFolders.mjs)
|
||||||
content/browser/zen-styles/zen-folders.css (../../zen/folders/zen-folders.css)
|
content/browser/zen-styles/zen-folders.css (../../zen/folders/zen-folders.css)
|
||||||
|
@ -74,7 +70,7 @@
|
||||||
content/browser/zen-styles/zen-download-box-animation.css (../../zen/downloads/zen-download-box-animation.css)
|
content/browser/zen-styles/zen-download-box-animation.css (../../zen/downloads/zen-download-box-animation.css)
|
||||||
|
|
||||||
|
|
||||||
# Images
|
# Images
|
||||||
content/browser/zen-images/gradient.png (../../zen/images/gradient.png)
|
content/browser/zen-images/gradient.png (../../zen/images/gradient.png)
|
||||||
content/browser/zen-images/brand-header.svg (../../zen/images/brand-header.svg)
|
content/browser/zen-images/brand-header.svg (../../zen/images/brand-header.svg)
|
||||||
content/browser/zen-images/layouts/collapsed.png (../../zen/images/layouts/collapsed.png)
|
content/browser/zen-images/layouts/collapsed.png (../../zen/images/layouts/collapsed.png)
|
||||||
|
@ -86,7 +82,7 @@
|
||||||
content/browser/zen-images/downloads/download.svg (../../zen/images/downloads/download.svg)
|
content/browser/zen-images/downloads/download.svg (../../zen/images/downloads/download.svg)
|
||||||
content/browser/zen-images/downloads/archive.svg (../../zen/images/downloads/archive.svg)
|
content/browser/zen-images/downloads/archive.svg (../../zen/images/downloads/archive.svg)
|
||||||
|
|
||||||
# Fonts
|
# Fonts
|
||||||
content/browser/zen-fonts/JunicodeVF-Italic.woff2 (../../zen/fonts/JunicodeVF-Italic.woff2)
|
content/browser/zen-fonts/JunicodeVF-Italic.woff2 (../../zen/fonts/JunicodeVF-Italic.woff2)
|
||||||
content/browser/zen-fonts/JunicodeVF-Roman.woff2 (../../zen/fonts/JunicodeVF-Roman.woff2)
|
content/browser/zen-fonts/JunicodeVF-Roman.woff2 (../../zen/fonts/JunicodeVF-Roman.woff2)
|
||||||
|
|
||||||
|
@ -104,4 +100,4 @@
|
||||||
content/browser/zen-images/favicons/slack.ico (../../zen/images/favicons/slack.ico)
|
content/browser/zen-images/favicons/slack.ico (../../zen/images/favicons/slack.ico)
|
||||||
content/browser/zen-images/favicons/reddit.ico (../../zen/images/favicons/reddit.ico)
|
content/browser/zen-images/favicons/reddit.ico (../../zen/images/favicons/reddit.ico)
|
||||||
content/browser/zen-images/favicons/x.ico (../../zen/images/favicons/x.ico)
|
content/browser/zen-images/favicons/x.ico (../../zen/images/favicons/x.ico)
|
||||||
content/browser/zen-images/favicons/trello.ico (../../zen/images/favicons/trello.ico)
|
content/browser/zen-images/favicons/trello.ico (../../zen/images/favicons/trello.ico)
|
||||||
|
|
|
@ -9,3 +9,5 @@
|
||||||
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenWorkspaces.mjs"></script>
|
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenWorkspaces.mjs"></script>
|
||||||
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenWorkspacesSync.mjs"></script>
|
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenWorkspacesSync.mjs"></script>
|
||||||
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenActorsManager.mjs"></script>
|
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenActorsManager.mjs"></script>
|
||||||
|
<script type="text/javascript" src="chrome://browser/content/zen-components/ZenSessionStore.mjs"></script>
|
||||||
|
|
||||||
|
|
|
@ -14,30 +14,37 @@ var gZenMarketplaceManager = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!window.gZenMods) {
|
||||||
|
window.gZenMods = ZenMultiWindowFeature.currentBrowser.gZenMods;
|
||||||
|
}
|
||||||
|
|
||||||
header.appendChild(this._initDisableAll());
|
header.appendChild(this._initDisableAll());
|
||||||
|
|
||||||
this._initImportExport();
|
this._initImportExport();
|
||||||
|
|
||||||
this.__hasInitializedEvents = true;
|
this.__hasInitializedEvents = true;
|
||||||
|
|
||||||
await this._buildThemesList();
|
await this._buildModsList();
|
||||||
|
|
||||||
Services.prefs.addObserver(this.updatePref, this);
|
Services.prefs.addObserver(gZenMods.updatePref, this);
|
||||||
|
|
||||||
const checkForUpdateClick = (event) => {
|
const checkForUpdateClick = (event) => {
|
||||||
if (event.target === checkForUpdates) {
|
if (event.target === checkForUpdates) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
this._checkForThemeUpdates(event);
|
this._checkForThemeUpdates(event);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
checkForUpdates.addEventListener('click', checkForUpdateClick);
|
checkForUpdates.addEventListener('click', checkForUpdateClick);
|
||||||
|
|
||||||
document.addEventListener('ZenThemeMarketplace:CheckForUpdatesFinished', (event) => {
|
document.addEventListener('ZenModsMarketplace:CheckForUpdatesFinished', (event) => {
|
||||||
checkForUpdates.disabled = false;
|
checkForUpdates.disabled = false;
|
||||||
|
|
||||||
const updates = event.detail.updates;
|
const updates = event.detail.updates;
|
||||||
const success = document.getElementById('zenThemeMarketplaceUpdatesSuccess');
|
const success = document.getElementById('zenThemeMarketplaceUpdatesSuccess');
|
||||||
const error = document.getElementById('zenThemeMarketplaceUpdatesFailure');
|
const error = document.getElementById('zenThemeMarketplaceUpdatesFailure');
|
||||||
|
|
||||||
if (updates) {
|
if (updates) {
|
||||||
success.hidden = false;
|
success.hidden = false;
|
||||||
error.hidden = true;
|
error.hidden = true;
|
||||||
|
@ -48,13 +55,16 @@ var gZenMarketplaceManager = {
|
||||||
});
|
});
|
||||||
|
|
||||||
window.addEventListener('unload', () => {
|
window.addEventListener('unload', () => {
|
||||||
Services.prefs.removeObserver(this.updatePref, this);
|
Services.prefs.removeObserver(gZenMods.updatePref, this);
|
||||||
this.__hasInitializedEvents = false;
|
this.__hasInitializedEvents = false;
|
||||||
document.removeEventListener('ZenThemeMarketplace:CheckForUpdatesFinished', this);
|
|
||||||
document.removeEventListener('ZenCheckForThemeUpdates', this);
|
document.removeEventListener('ZenModsMarketplace:CheckForUpdatesFinished', this);
|
||||||
|
document.removeEventListener('ZenCheckForModUpdates', this);
|
||||||
|
|
||||||
checkForUpdates.removeEventListener('click', checkForUpdateClick);
|
checkForUpdates.removeEventListener('click', checkForUpdateClick);
|
||||||
this.themesList.innerHTML = '';
|
|
||||||
this._doNotRebuildThemesList = false;
|
this.modsList.innerHTML = '';
|
||||||
|
this._doNotRebuildModsList = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -63,36 +73,32 @@ var gZenMarketplaceManager = {
|
||||||
const exportButton = document.getElementById('zenThemeMarketplaceExport');
|
const exportButton = document.getElementById('zenThemeMarketplaceExport');
|
||||||
|
|
||||||
if (importButton) {
|
if (importButton) {
|
||||||
importButton.addEventListener('click', async () => {
|
importButton.addEventListener('click', this._importThemes.bind(this));
|
||||||
await this._importThemes();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exportButton) {
|
if (exportButton) {
|
||||||
exportButton.addEventListener('click', async () => {
|
exportButton.addEventListener('click', this._exportThemes.bind(this));
|
||||||
await this._exportThemes();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_initDisableAll() {
|
_initDisableAll() {
|
||||||
const areThemesDisabled = Services.prefs.getBoolPref('zen.themes.disable-all', false);
|
const areModsDisabled = Services.prefs.getBoolPref('zen.themes.disable-all', false);
|
||||||
const browser = ZenThemesCommon.currentBrowser;
|
const browser = ZenMultiWindowFeature.currentBrowser;
|
||||||
const mozToggle = document.createElement('moz-toggle');
|
const mozToggle = document.createElement('moz-toggle');
|
||||||
|
|
||||||
mozToggle.className =
|
mozToggle.className =
|
||||||
'zenThemeMarketplaceItemPreferenceToggle zenThemeMarketplaceDisableAllToggle';
|
'zenThemeMarketplaceItemPreferenceToggle zenThemeMarketplaceDisableAllToggle';
|
||||||
mozToggle.pressed = !areThemesDisabled;
|
mozToggle.pressed = !areModsDisabled;
|
||||||
|
|
||||||
browser.document.l10n.setAttributes(
|
browser.document.l10n.setAttributes(
|
||||||
mozToggle,
|
mozToggle,
|
||||||
`zen-theme-disable-all-${!areThemesDisabled ? 'enabled' : 'disabled'}`
|
`zen-theme-disable-all-${!areModsDisabled ? 'enabled' : 'disabled'}`
|
||||||
);
|
);
|
||||||
|
|
||||||
mozToggle.addEventListener('toggle', async (event) => {
|
mozToggle.addEventListener('toggle', async (event) => {
|
||||||
const { pressed = false } = event.target || {};
|
const { pressed = false } = event.target || {};
|
||||||
|
|
||||||
this.themesList.style.display = pressed ? '' : 'none';
|
this.modsList.style.display = pressed ? '' : 'none';
|
||||||
Services.prefs.setBoolPref('zen.themes.disable-all', !pressed);
|
Services.prefs.setBoolPref('zen.themes.disable-all', !pressed);
|
||||||
browser.document.l10n.setAttributes(
|
browser.document.l10n.setAttributes(
|
||||||
mozToggle,
|
mozToggle,
|
||||||
|
@ -100,90 +106,65 @@ var gZenMarketplaceManager = {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (areThemesDisabled) {
|
if (areModsDisabled) {
|
||||||
this.themesList.style.display = 'none';
|
this.modsList.style.display = 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
return mozToggle;
|
return mozToggle;
|
||||||
},
|
},
|
||||||
|
|
||||||
async observe() {
|
async observe() {
|
||||||
await this._buildThemesList();
|
await this._buildModsList();
|
||||||
},
|
},
|
||||||
|
|
||||||
_checkForThemeUpdates(event) {
|
_checkForThemeUpdates(event) {
|
||||||
// Send a message to the child to check for theme updates.
|
// Send a message to the child to check for theme updates.
|
||||||
event.target.disabled = true;
|
event.target.disabled = true;
|
||||||
// send an event that will be listened by the child process.
|
// send an event that will be listened by the child process.
|
||||||
document.dispatchEvent(new CustomEvent('ZenCheckForThemeUpdates'));
|
document.dispatchEvent(new CustomEvent('ZenCheckForModUpdates'));
|
||||||
},
|
},
|
||||||
|
|
||||||
get updatePref() {
|
get modsList() {
|
||||||
return 'zen.themes.updated-value-observer';
|
if (!this._modsList) {
|
||||||
},
|
this._modsList = document.getElementById('zenThemeMarketplaceList');
|
||||||
|
|
||||||
triggerThemeUpdate() {
|
|
||||||
Services.prefs.setBoolPref(this.updatePref, !Services.prefs.getBoolPref(this.updatePref));
|
|
||||||
},
|
|
||||||
|
|
||||||
get themesList() {
|
|
||||||
if (!this._themesList) {
|
|
||||||
this._themesList = document.getElementById('zenThemeMarketplaceList');
|
|
||||||
}
|
}
|
||||||
return this._themesList;
|
return this._modsList;
|
||||||
},
|
},
|
||||||
|
|
||||||
async removeTheme(themeId) {
|
async removeMod(modId) {
|
||||||
const themePath = ZenThemesCommon.getThemeFolder(themeId);
|
await gZenMods.removeMod(modId);
|
||||||
|
|
||||||
console.info(`[ZenThemeMarketplaceParent:settings]: Removing theme ${themePath}`);
|
gZenMods.triggerModsUpdate();
|
||||||
|
|
||||||
await IOUtils.remove(themePath, { recursive: true, ignoreAbsent: true });
|
|
||||||
|
|
||||||
const themes = await ZenThemesCommon.getThemes();
|
|
||||||
delete themes[themeId];
|
|
||||||
await IOUtils.writeJSON(ZenThemesCommon.themesDataFile, themes);
|
|
||||||
|
|
||||||
this.triggerThemeUpdate();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
async disableTheme(themeId) {
|
async disableMod(modId) {
|
||||||
const themes = await ZenThemesCommon.getThemes();
|
await gZenMods.disableMod(modId);
|
||||||
const theme = themes[themeId];
|
|
||||||
|
|
||||||
console.log(`[ZenThemeMarketplaceParent:settings]: Disabling theme ${theme.name}`);
|
this._doNotRebuildModsList = true;
|
||||||
|
gZenMods.triggerModsUpdate();
|
||||||
theme.enabled = false;
|
|
||||||
|
|
||||||
await IOUtils.writeJSON(ZenThemesCommon.themesDataFile, themes);
|
|
||||||
this._doNotRebuildThemesList = true;
|
|
||||||
this.triggerThemeUpdate();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
async enableTheme(themeId) {
|
async enableMod(modId) {
|
||||||
const themes = await ZenThemesCommon.getThemes();
|
await gZenMods.enableMod(modId);
|
||||||
const theme = themes[themeId];
|
|
||||||
|
|
||||||
console.log(`[ZenThemeMarketplaceParent:settings]: Enabling theme ${theme.name}`);
|
this._doNotRebuildModsList = true;
|
||||||
|
gZenMods.triggerModsUpdate();
|
||||||
theme.enabled = true;
|
|
||||||
|
|
||||||
await IOUtils.writeJSON(ZenThemesCommon.themesDataFile, themes);
|
|
||||||
this._doNotRebuildThemesList = true;
|
|
||||||
this.triggerThemeUpdate();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_triggerBuildUpdateWithoutRebuild() {
|
_triggerBuildUpdateWithoutRebuild() {
|
||||||
this._doNotRebuildThemesList = true;
|
this._doNotRebuildModsList = true;
|
||||||
this.triggerThemeUpdate();
|
gZenMods.triggerModsUpdate();
|
||||||
},
|
},
|
||||||
|
|
||||||
async _importThemes() {
|
async _importThemes() {
|
||||||
const errorBox = document.getElementById('zenThemeMarketplaceImportFailure');
|
const errorBox = document.getElementById('zenThemeMarketplaceImportFailure');
|
||||||
const successBox = document.getElementById('zenThemeMarketplaceImportSuccess');
|
const successBox = document.getElementById('zenThemeMarketplaceImportSuccess');
|
||||||
|
|
||||||
successBox.hidden = true;
|
successBox.hidden = true;
|
||||||
errorBox.hidden = true;
|
errorBox.hidden = true;
|
||||||
|
|
||||||
const input = document.createElement('input');
|
const input = document.createElement('input');
|
||||||
|
|
||||||
input.type = 'file';
|
input.type = 'file';
|
||||||
input.accept = '.json';
|
input.accept = '.json';
|
||||||
input.style.display = 'none';
|
input.style.display = 'none';
|
||||||
|
@ -191,37 +172,52 @@ var gZenMarketplaceManager = {
|
||||||
input.setAttribute('accept', '.json');
|
input.setAttribute('accept', '.json');
|
||||||
|
|
||||||
let timeout;
|
let timeout;
|
||||||
|
|
||||||
const filePromise = new Promise((resolve) => {
|
const filePromise = new Promise((resolve) => {
|
||||||
input.addEventListener('change', (event) => {
|
input.addEventListener('change', (event) => {
|
||||||
if (timeout) clearTimeout(timeout);
|
if (timeout) {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
}
|
||||||
|
|
||||||
const file = event.target.files[0];
|
const file = event.target.files[0];
|
||||||
resolve(file);
|
resolve(file);
|
||||||
});
|
});
|
||||||
|
|
||||||
timeout = setTimeout(() => {
|
timeout = setTimeout(() => {
|
||||||
console.warn('[ZenThemeMarketplaceParent:settings]: Import timeout reached, aborting.');
|
console.warn('[ZenSettings:ZenMods]: Import timeout reached, aborting.');
|
||||||
resolve(null);
|
resolve(null);
|
||||||
}, 60000);
|
}, 60000);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
input.addEventListener('cancel', () => {
|
||||||
|
console.warn('[ZenSettings:ZenMods]: Import cancelled by user.');
|
||||||
|
clearTimeout(timeout);
|
||||||
|
});
|
||||||
|
|
||||||
input.click();
|
input.click();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const file = await filePromise;
|
const file = await filePromise;
|
||||||
|
|
||||||
if (!file) {
|
if (!file) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const content = await file.text();
|
const content = await file.text();
|
||||||
|
|
||||||
const themes = JSON.parse(content);
|
const mods = JSON.parse(content);
|
||||||
for (const theme of Object.values(themes)) {
|
|
||||||
theme.themeId = theme.id;
|
for (const mod of Object.values(mods)) {
|
||||||
window.ZenInstallTheme(theme);
|
mod.modId = mod.id;
|
||||||
|
await window.ZenInstallMod(mod);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[ZenThemeMarketplaceParent:settings]: Error while importing themes:', error);
|
console.error('[ZenSettings:ZenMods]: Error while importing mods:', error);
|
||||||
errorBox.hidden = false;
|
errorBox.hidden = false;
|
||||||
} finally {
|
}
|
||||||
if (input) input.remove();
|
|
||||||
|
if (input) {
|
||||||
|
input.remove();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -232,51 +228,54 @@ var gZenMarketplaceManager = {
|
||||||
successBox.hidden = true;
|
successBox.hidden = true;
|
||||||
errorBox.hidden = true;
|
errorBox.hidden = true;
|
||||||
|
|
||||||
let a, url;
|
let temporalAnchor, temporalUrl;
|
||||||
try {
|
try {
|
||||||
const themes = await ZenThemesCommon.getThemes();
|
const mods = await gZenMods.getMods();
|
||||||
const themesJson = JSON.stringify(themes, null, 2);
|
const modsJson = JSON.stringify(mods, null, 2);
|
||||||
const blob = new Blob([themesJson], { type: 'application/json' });
|
const blob = new Blob([modsJson], { type: 'application/json' });
|
||||||
url = URL.createObjectURL(blob);
|
|
||||||
// Creating a link to download the JSON file
|
temporalUrl = URL.createObjectURL(blob);
|
||||||
a = document.createElement('a');
|
// Creating a link to download the JSON file
|
||||||
a.href = url;
|
temporalAnchor = document.createElement('a');
|
||||||
a.download = 'zen-themes-export.json';
|
temporalAnchor.href = temporalUrl;
|
||||||
|
temporalAnchor.download = 'zen-mods-export.json';
|
||||||
|
|
||||||
|
document.body.appendChild(temporalAnchor);
|
||||||
|
temporalAnchor.click();
|
||||||
|
temporalAnchor.remove();
|
||||||
|
|
||||||
document.body.appendChild(a);
|
|
||||||
a.click();
|
|
||||||
a.remove();
|
|
||||||
successBox.hidden = false;
|
successBox.hidden = false;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[ZenThemeMarketplaceParent:settings]: Error while exporting themes:', error);
|
console.error('[ZenSettings:ZenMods]: Error while exporting mods:', error);
|
||||||
errorBox.hidden = false;
|
errorBox.hidden = false;
|
||||||
} finally {
|
}
|
||||||
if (a) {
|
|
||||||
a.remove();
|
if (temporalAnchor) {
|
||||||
}
|
temporalAnchor.remove();
|
||||||
if (url) {
|
}
|
||||||
URL.revokeObjectURL(url);
|
|
||||||
}
|
if (temporalUrl) {
|
||||||
|
URL.revokeObjectURL(temporalUrl);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async _buildThemesList() {
|
async _buildModsList() {
|
||||||
if (!this.themesList) {
|
if (!this.modsList) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._doNotRebuildThemesList) {
|
if (this._doNotRebuildModsList) {
|
||||||
this._doNotRebuildThemesList = false;
|
this._doNotRebuildModsList = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const themes = await ZenThemesCommon.getThemes();
|
const mods = await gZenMods.getMods();
|
||||||
const browser = ZenMultiWindowFeature.currentBrowser;
|
const browser = ZenMultiWindowFeature.currentBrowser;
|
||||||
const themeList = document.createElement('div');
|
const modList = document.createElement('div');
|
||||||
|
|
||||||
for (const theme of Object.values(themes).sort((a, b) => a.name.localeCompare(b.name))) {
|
for (const mod of Object.values(mods).sort((a, b) => a.name.localeCompare(b.name))) {
|
||||||
const sanitizedName = `theme-${theme.name?.replaceAll(/\s/g, '-')?.replaceAll(/[^A-Za-z_-]+/g, '')}`;
|
const sanitizedName = gZenMods.sanitizeModName(mod.name);
|
||||||
const isThemeEnabled = theme.enabled === undefined || theme.enabled;
|
const isModEnabled = mod.enabled === undefined || mod.enabled;
|
||||||
const fragment = window.MozXULElement.parseXULToFragment(`
|
const fragment = window.MozXULElement.parseXULToFragment(`
|
||||||
<vbox class="zenThemeMarketplaceItem">
|
<vbox class="zenThemeMarketplaceItem">
|
||||||
<vbox class="zenThemeMarketplaceItemContent">
|
<vbox class="zenThemeMarketplaceItemContent">
|
||||||
|
@ -286,14 +285,14 @@ var gZenMarketplaceManager = {
|
||||||
<description class="description-deemphasized zenThemeMarketplaceItemDescription"></description>
|
<description class="description-deemphasized zenThemeMarketplaceItemDescription"></description>
|
||||||
</vbox>
|
</vbox>
|
||||||
<hbox class="zenThemeMarketplaceItemActions">
|
<hbox class="zenThemeMarketplaceItemActions">
|
||||||
${theme.preferences ? `<button id="zenThemeMarketplaceItemConfigureButton-${sanitizedName}" class="zenThemeMarketplaceItemConfigureButton" hidden="true"></button>` : ''}
|
${mod.preferences ? `<button id="zenThemeMarketplaceItemConfigureButton-${sanitizedName}" class="zenThemeMarketplaceItemConfigureButton" hidden="true"></button>` : ''}
|
||||||
${theme.homepage ? `<button id="zenThemeMarketplaceItemHomePageLink-${sanitizedName}" class="zenThemeMarketplaceItemHomepageButton" zen-theme-id="${theme.id}"></button>` : ''}
|
${mod.homepage ? `<button id="zenThemeMarketplaceItemHomePageLink-${sanitizedName}" class="zenThemeMarketplaceItemHomepageButton" zen-mod-id="${mod.id}"></button>` : ''}
|
||||||
<button class="zenThemeMarketplaceItemUninstallButton" data-l10n-id="zen-theme-marketplace-remove-button" zen-theme-id="${theme.id}"></button>
|
<button class="zenThemeMarketplaceItemUninstallButton" data-l10n-id="zen-theme-marketplace-remove-button" zen-mod-id="${mod.id}"></button>
|
||||||
</hbox>
|
</hbox>
|
||||||
</vbox>
|
</vbox>
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const themeName = `${theme.name} (v${theme.version || '1.0.0'})`;
|
const modName = `${mod.name} (v${mod.version ?? '1.0.0'})`;
|
||||||
|
|
||||||
const base = fragment.querySelector('.zenThemeMarketplaceItem');
|
const base = fragment.querySelector('.zenThemeMarketplaceItem');
|
||||||
const baseHeader = fragment.querySelector('#zenThemeMarketplaceItemContentHeader');
|
const baseHeader = fragment.querySelector('#zenThemeMarketplaceItemContentHeader');
|
||||||
|
@ -308,7 +307,7 @@ var gZenMarketplaceManager = {
|
||||||
|
|
||||||
mainDialogDiv.className = 'zenThemeMarketplaceItemPreferenceDialog';
|
mainDialogDiv.className = 'zenThemeMarketplaceItemPreferenceDialog';
|
||||||
headerDiv.className = 'zenThemeMarketplaceItemPreferenceDialogTopBar';
|
headerDiv.className = 'zenThemeMarketplaceItemPreferenceDialogTopBar';
|
||||||
headerTitle.textContent = themeName;
|
headerTitle.textContent = modName;
|
||||||
browser.document.l10n.setAttributes(headerTitle, 'zen-theme-marketplace-theme-header-title', {
|
browser.document.l10n.setAttributes(headerTitle, 'zen-theme-marketplace-theme-header-title', {
|
||||||
name: sanitizedName,
|
name: sanitizedName,
|
||||||
});
|
});
|
||||||
|
@ -319,10 +318,10 @@ var gZenMarketplaceManager = {
|
||||||
contentDiv.className = 'zenThemeMarketplaceItemPreferenceDialogContent';
|
contentDiv.className = 'zenThemeMarketplaceItemPreferenceDialogContent';
|
||||||
mozToggle.className = 'zenThemeMarketplaceItemPreferenceToggle';
|
mozToggle.className = 'zenThemeMarketplaceItemPreferenceToggle';
|
||||||
|
|
||||||
mozToggle.pressed = isThemeEnabled;
|
mozToggle.pressed = isModEnabled;
|
||||||
browser.document.l10n.setAttributes(
|
browser.document.l10n.setAttributes(
|
||||||
mozToggle,
|
mozToggle,
|
||||||
`zen-theme-marketplace-toggle-${isThemeEnabled ? 'enabled' : 'disabled'}-button`
|
`zen-theme-marketplace-toggle-${isModEnabled ? 'enabled' : 'disabled'}-button`
|
||||||
);
|
);
|
||||||
|
|
||||||
baseHeader.appendChild(mozToggle);
|
baseHeader.appendChild(mozToggle);
|
||||||
|
@ -340,34 +339,34 @@ var gZenMarketplaceManager = {
|
||||||
});
|
});
|
||||||
|
|
||||||
mozToggle.addEventListener('toggle', async (event) => {
|
mozToggle.addEventListener('toggle', async (event) => {
|
||||||
const themeId = event.target
|
const modId = event.target
|
||||||
.closest('.zenThemeMarketplaceItem')
|
.closest('.zenThemeMarketplaceItem')
|
||||||
.querySelector('.zenThemeMarketplaceItemUninstallButton')
|
.querySelector('.zenThemeMarketplaceItemUninstallButton')
|
||||||
.getAttribute('zen-theme-id');
|
.getAttribute('zen-mod-id');
|
||||||
event.target.setAttribute('disabled', true);
|
event.target.setAttribute('disabled', true);
|
||||||
|
|
||||||
if (!event.target.hasAttribute('pressed')) {
|
if (!event.target.hasAttribute('pressed')) {
|
||||||
await this.disableTheme(themeId);
|
await this.disableMod(modId);
|
||||||
|
|
||||||
browser.document.l10n.setAttributes(
|
browser.document.l10n.setAttributes(
|
||||||
mozToggle,
|
mozToggle,
|
||||||
'zen-theme-marketplace-toggle-disabled-button'
|
'zen-theme-marketplace-toggle-disabled-button'
|
||||||
);
|
);
|
||||||
|
|
||||||
if (theme.preferences) {
|
if (mod.preferences) {
|
||||||
document
|
document
|
||||||
.getElementById(`zenThemeMarketplaceItemConfigureButton-${sanitizedName}`)
|
.getElementById(`zenThemeMarketplaceItemConfigureButton-${sanitizedName}`)
|
||||||
.setAttribute('hidden', true);
|
.setAttribute('hidden', true);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await this.enableTheme(themeId);
|
await this.enableMod(modId);
|
||||||
|
|
||||||
browser.document.l10n.setAttributes(
|
browser.document.l10n.setAttributes(
|
||||||
mozToggle,
|
mozToggle,
|
||||||
'zen-theme-marketplace-toggle-enabled-button'
|
'zen-theme-marketplace-toggle-enabled-button'
|
||||||
);
|
);
|
||||||
|
|
||||||
if (theme.preferences) {
|
if (mod.preferences) {
|
||||||
document
|
document
|
||||||
.getElementById(`zenThemeMarketplaceItemConfigureButton-${sanitizedName}`)
|
.getElementById(`zenThemeMarketplaceItemConfigureButton-${sanitizedName}`)
|
||||||
.removeAttribute('hidden');
|
.removeAttribute('hidden');
|
||||||
|
@ -379,8 +378,8 @@ var gZenMarketplaceManager = {
|
||||||
}, 400);
|
}, 400);
|
||||||
});
|
});
|
||||||
|
|
||||||
fragment.querySelector('.zenThemeMarketplaceItemTitle').textContent = themeName;
|
fragment.querySelector('.zenThemeMarketplaceItemTitle').textContent = modName;
|
||||||
fragment.querySelector('.zenThemeMarketplaceItemDescription').textContent = theme.description;
|
fragment.querySelector('.zenThemeMarketplaceItemDescription').textContent = mod.description;
|
||||||
fragment
|
fragment
|
||||||
.querySelector('.zenThemeMarketplaceItemUninstallButton')
|
.querySelector('.zenThemeMarketplaceItemUninstallButton')
|
||||||
.addEventListener('click', async (event) => {
|
.addEventListener('click', async (event) => {
|
||||||
|
@ -392,34 +391,34 @@ var gZenMarketplaceManager = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.removeTheme(event.target.getAttribute('zen-theme-id'));
|
await this.removeMod(event.target.getAttribute('zen-mod-id'));
|
||||||
});
|
});
|
||||||
|
|
||||||
if (theme.homepage) {
|
if (mod.homepage) {
|
||||||
const homepageButton = fragment.querySelector('.zenThemeMarketplaceItemHomepageButton');
|
const homepageButton = fragment.querySelector('.zenThemeMarketplaceItemHomepageButton');
|
||||||
homepageButton.addEventListener('click', () => {
|
homepageButton.addEventListener('click', () => {
|
||||||
// open the homepage url in a new tab
|
// open the homepage url in a new tab
|
||||||
const url = theme.homepage;
|
const url = mod.homepage;
|
||||||
|
|
||||||
window.open(url, '_blank');
|
window.open(url, '_blank');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (theme.preferences) {
|
if (mod.preferences) {
|
||||||
fragment
|
fragment
|
||||||
.querySelector('.zenThemeMarketplaceItemConfigureButton')
|
.querySelector('.zenThemeMarketplaceItemConfigureButton')
|
||||||
.addEventListener('click', () => {
|
.addEventListener('click', () => {
|
||||||
dialog.showModal();
|
dialog.showModal();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (isThemeEnabled) {
|
if (isModEnabled) {
|
||||||
fragment
|
fragment
|
||||||
.querySelector('.zenThemeMarketplaceItemConfigureButton')
|
.querySelector('.zenThemeMarketplaceItemConfigureButton')
|
||||||
.removeAttribute('hidden');
|
.removeAttribute('hidden');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const preferences = await ZenThemesCommon.getThemePreferences(theme);
|
const preferences = await gZenMods.getModPreferences(mod);
|
||||||
|
|
||||||
if (preferences.length > 0) {
|
if (preferences.length > 0) {
|
||||||
const preferencesWrapper = document.createXULElement('vbox');
|
const preferencesWrapper = document.createXULElement('vbox');
|
||||||
|
@ -471,7 +470,7 @@ var gZenMarketplaceManager = {
|
||||||
|
|
||||||
if (!['string', 'number'].includes(valueType)) {
|
if (!['string', 'number'].includes(valueType)) {
|
||||||
console.log(
|
console.log(
|
||||||
`[ZenThemeMarketplaceParent:settings]: Warning, invalid data type received (${valueType}), skipping.`
|
`[ZenSettings:ZenMods]: Warning, invalid data type received (${valueType}), skipping.`
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -583,7 +582,7 @@ var gZenMarketplaceManager = {
|
||||||
|
|
||||||
input.addEventListener(
|
input.addEventListener(
|
||||||
'change',
|
'change',
|
||||||
ZenThemesCommon.debounce((event) => {
|
gZenMods.debounce((event) => {
|
||||||
const value = event.target.value;
|
const value = event.target.value;
|
||||||
|
|
||||||
Services.prefs.setStringPref(property, value);
|
Services.prefs.setStringPref(property, value);
|
||||||
|
@ -617,18 +616,18 @@ var gZenMarketplaceManager = {
|
||||||
|
|
||||||
default:
|
default:
|
||||||
console.log(
|
console.log(
|
||||||
`[ZenThemeMarketplaceParent:settings]: Warning, unknown preference type received (${type}), skipping.`
|
`[ZenSettings:ZenMods]: Warning, unknown preference type received (${type}), skipping.`
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
contentDiv.appendChild(preferencesWrapper);
|
contentDiv.appendChild(preferencesWrapper);
|
||||||
}
|
}
|
||||||
themeList.appendChild(fragment);
|
modList.appendChild(fragment);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.themesList.replaceChildren(...themeList.children);
|
this.modsList.replaceChildren(...modList.children);
|
||||||
themeList.remove();
|
modList.remove();
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -636,6 +635,19 @@ const kZenExtendedSidebar = 'zen.view.sidebar-expanded';
|
||||||
const kZenSingleToolbar = 'zen.view.use-single-toolbar';
|
const kZenSingleToolbar = 'zen.view.use-single-toolbar';
|
||||||
|
|
||||||
var gZenLooksAndFeel = {
|
var gZenLooksAndFeel = {
|
||||||
|
kZenColors: [
|
||||||
|
'#aac7ff',
|
||||||
|
'#74d7cb',
|
||||||
|
'#a0d490',
|
||||||
|
'#dec663',
|
||||||
|
'#ffb787',
|
||||||
|
'#dec1b1',
|
||||||
|
'#ffb1c0',
|
||||||
|
'#ddbfc3',
|
||||||
|
'#f6b0ea',
|
||||||
|
'#d4bbff',
|
||||||
|
],
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
if (this.__hasInitialized) return;
|
if (this.__hasInitialized) return;
|
||||||
this.__hasInitialized = true;
|
this.__hasInitialized = true;
|
||||||
|
@ -737,7 +749,8 @@ var gZenLooksAndFeel = {
|
||||||
_initializeColorPicker(accentColor) {
|
_initializeColorPicker(accentColor) {
|
||||||
let elem = document.getElementById('zenLooksAndFeelColorOptions');
|
let elem = document.getElementById('zenLooksAndFeelColorOptions');
|
||||||
elem.innerHTML = '';
|
elem.innerHTML = '';
|
||||||
for (let color of ZenThemesCommon.kZenColors) {
|
|
||||||
|
for (let color of this.kZenColors) {
|
||||||
let colorElemParen = document.createElement('div');
|
let colorElemParen = document.createElement('div');
|
||||||
let colorElem = document.createElement('div');
|
let colorElem = document.createElement('div');
|
||||||
colorElemParen.classList.add('zenLooksAndFeelColorOptionParen');
|
colorElemParen.classList.add('zenLooksAndFeelColorOptionParen');
|
||||||
|
@ -761,7 +774,7 @@ var gZenLooksAndFeel = {
|
||||||
},
|
},
|
||||||
|
|
||||||
_getInitialAccentColor() {
|
_getInitialAccentColor() {
|
||||||
return Services.prefs.getStringPref('zen.theme.accent-color', ZenThemesCommon.kZenColors[0]);
|
return Services.prefs.getStringPref('zen.theme.accent-color', this.kZenColors[0]);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1220,7 +1233,7 @@ Preferences.addAll([
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'browser.tabs.unloadOnLowMemory',
|
id: 'zen.mods.auto-update',
|
||||||
type: 'bool',
|
type: 'bool',
|
||||||
default: true,
|
default: true,
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<script src="chrome://browser/content/zen-components/ZenCommonUtils.mjs" defer=""/>
|
<script src="chrome://browser/content/zen-components/ZenCommonUtils.mjs" defer=""/>
|
||||||
<script src="chrome://browser/content/zen-components/ZenThemesCommon.mjs" defer=""/>
|
|
||||||
<html:template id="template-paneZenMarketplace">
|
<html:template id="template-paneZenMarketplace">
|
||||||
<hbox id="ZenMarketplaceCategory"
|
<hbox id="ZenMarketplaceCategory"
|
||||||
class="subcategory"
|
class="subcategory"
|
||||||
|
@ -21,6 +20,10 @@
|
||||||
<button id="zenThemeMarketplaceCheckForUpdates" data-l10n-id="zen-theme-marketplace-check-for-updates-button" />
|
<button id="zenThemeMarketplaceCheckForUpdates" data-l10n-id="zen-theme-marketplace-check-for-updates-button" />
|
||||||
</hbox>
|
</hbox>
|
||||||
|
|
||||||
|
<checkbox id="zenWorkspacesForceContainerTabsToWorkspace"
|
||||||
|
data-l10n-id="zen-themes-auto-update"
|
||||||
|
preference="zen.mods.auto-update"/>
|
||||||
|
|
||||||
<description class="description-deemphasized" data-l10n-id="zen-theme-marketplace-updates-success" hidden="true" id="zenThemeMarketplaceUpdatesSuccess" />
|
<description class="description-deemphasized" data-l10n-id="zen-theme-marketplace-updates-success" hidden="true" id="zenThemeMarketplaceUpdatesSuccess" />
|
||||||
<description class="description-deemphasized" data-l10n-id="zen-theme-marketplace-updates-failure" hidden="true" id="zenThemeMarketplaceUpdatesFailure" />
|
<description class="description-deemphasized" data-l10n-id="zen-theme-marketplace-updates-failure" hidden="true" id="zenThemeMarketplaceUpdatesFailure" />
|
||||||
|
|
||||||
|
|
|
@ -31,15 +31,6 @@
|
||||||
<html:h1 data-l10n-id="pane-zen-tabs-unloader-title"/>
|
<html:h1 data-l10n-id="pane-zen-tabs-unloader-title"/>
|
||||||
</hbox>
|
</hbox>
|
||||||
|
|
||||||
<groupbox id="zenTabsUnloadGroup" data-category="paneZenTabManagement" hidden="true" class="highlighting-group">
|
|
||||||
<label><html:h2 data-l10n-id="zen-tabs-unloader-header"/></label>
|
|
||||||
<description class="description-deemphasized" data-l10n-id="zen-tabs-unloader-description" />
|
|
||||||
|
|
||||||
<checkbox id="zenTabsUnloadActivate"
|
|
||||||
data-l10n-id="zen-tabs-unloader-enabled"
|
|
||||||
preference="browser.tabs.unloadOnLowMemory"/>
|
|
||||||
</groupbox>
|
|
||||||
|
|
||||||
<hbox id="zenPinnedTabsManagerCategory"
|
<hbox id="zenPinnedTabsManagerCategory"
|
||||||
class="subcategory"
|
class="subcategory"
|
||||||
hidden="true"
|
hidden="true"
|
||||||
|
|
13
src/browser/components/search/SearchUIUtils-sys-mjs.patch
Normal file
13
src/browser/components/search/SearchUIUtils-sys-mjs.patch
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
diff --git a/browser/components/search/SearchUIUtils.sys.mjs b/browser/components/search/SearchUIUtils.sys.mjs
|
||||||
|
index ecebaad93acfc9eb7dfd9d9f56fec2e1a4abe392..8bb1348b3258dbc518d23ec39181a81f87fc8c1e 100644
|
||||||
|
--- a/browser/components/search/SearchUIUtils.sys.mjs
|
||||||
|
+++ b/browser/components/search/SearchUIUtils.sys.mjs
|
||||||
|
@@ -403,7 +403,7 @@ export var SearchUIUtils = {
|
||||||
|
triggeringSearchEngine: engine.name,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
-
|
||||||
|
+ window.gZenGlanceManager?.onSearchSelectCommand(where);
|
||||||
|
return { engine, url: submission.uri };
|
||||||
|
},
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
diff --git a/browser/components/sessionstore/SessionStore.sys.mjs b/browser/components/sessionstore/SessionStore.sys.mjs
|
diff --git a/browser/components/sessionstore/SessionStore.sys.mjs b/browser/components/sessionstore/SessionStore.sys.mjs
|
||||||
index 8c6047e1ada5a22e57e1e665965237c9e22641d7..ccd2779d66eda9d034ca51cc3200d81447514e2c 100644
|
index 8c6047e1ada5a22e57e1e665965237c9e22641d7..8d0585e738a5a758ebbddfa0787c71d634dadd4d 100644
|
||||||
--- a/browser/components/sessionstore/SessionStore.sys.mjs
|
--- a/browser/components/sessionstore/SessionStore.sys.mjs
|
||||||
+++ b/browser/components/sessionstore/SessionStore.sys.mjs
|
+++ b/browser/components/sessionstore/SessionStore.sys.mjs
|
||||||
@@ -2088,7 +2088,6 @@ var SessionStoreInternal = {
|
@@ -2088,7 +2088,6 @@ var SessionStoreInternal = {
|
||||||
|
@ -31,17 +31,19 @@ index 8c6047e1ada5a22e57e1e665965237c9e22641d7..ccd2779d66eda9d034ca51cc3200d814
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3925,6 +3922,9 @@ var SessionStoreInternal = {
|
@@ -3925,6 +3922,11 @@ var SessionStoreInternal = {
|
||||||
Math.min(tabState.index, tabState.entries.length)
|
Math.min(tabState.index, tabState.entries.length)
|
||||||
);
|
);
|
||||||
tabState.pinned = false;
|
tabState.pinned = false;
|
||||||
+ tabState.zenEssential = false;
|
+ tabState.zenEssential = false;
|
||||||
+ tabState.zenPinnedId = null;
|
+ tabState.zenPinnedId = null;
|
||||||
|
+ tabState.zenIsGlance = false;
|
||||||
|
+ tabState.zenGlanceId = null;
|
||||||
+ tabState.zenHasStaticLabel = false;
|
+ tabState.zenHasStaticLabel = false;
|
||||||
|
|
||||||
if (inBackground === false) {
|
if (inBackground === false) {
|
||||||
aWindow.gBrowser.selectedTab = newTab;
|
aWindow.gBrowser.selectedTab = newTab;
|
||||||
@@ -5239,7 +5239,7 @@ var SessionStoreInternal = {
|
@@ -5239,7 +5241,7 @@ var SessionStoreInternal = {
|
||||||
}
|
}
|
||||||
|
|
||||||
let workspaceID = aWindow.getWorkspaceID();
|
let workspaceID = aWindow.getWorkspaceID();
|
||||||
|
@ -50,7 +52,7 @@ index 8c6047e1ada5a22e57e1e665965237c9e22641d7..ccd2779d66eda9d034ca51cc3200d814
|
||||||
winData.workspaceID = workspaceID;
|
winData.workspaceID = workspaceID;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -5430,14 +5430,15 @@ var SessionStoreInternal = {
|
@@ -5430,14 +5432,15 @@ var SessionStoreInternal = {
|
||||||
}
|
}
|
||||||
|
|
||||||
let tabbrowser = aWindow.gBrowser;
|
let tabbrowser = aWindow.gBrowser;
|
||||||
|
@ -68,7 +70,7 @@ index 8c6047e1ada5a22e57e1e665965237c9e22641d7..ccd2779d66eda9d034ca51cc3200d814
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let tabData = lazy.TabState.collect(tab, TAB_CUSTOM_VALUES.get(tab));
|
let tabData = lazy.TabState.collect(tab, TAB_CUSTOM_VALUES.get(tab));
|
||||||
@@ -5456,8 +5457,8 @@ var SessionStoreInternal = {
|
@@ -5456,8 +5459,8 @@ var SessionStoreInternal = {
|
||||||
// We don't store the Firefox View tab in Session Store, so if it was the last selected "tab" when
|
// We don't store the Firefox View tab in Session Store, so if it was the last selected "tab" when
|
||||||
// a window is closed, point to the first item in the tab strip instead (it will never be the Firefox View tab,
|
// a window is closed, point to the first item in the tab strip instead (it will never be the Firefox View tab,
|
||||||
// since it's only inserted into the tab strip after it's selected).
|
// since it's only inserted into the tab strip after it's selected).
|
||||||
|
@ -79,7 +81,7 @@ index 8c6047e1ada5a22e57e1e665965237c9e22641d7..ccd2779d66eda9d034ca51cc3200d814
|
||||||
winData.title = tabbrowser.tabs[0].label;
|
winData.title = tabbrowser.tabs[0].label;
|
||||||
}
|
}
|
||||||
winData.selected = selectedIndex;
|
winData.selected = selectedIndex;
|
||||||
@@ -5569,8 +5570,8 @@ var SessionStoreInternal = {
|
@@ -5569,8 +5572,8 @@ var SessionStoreInternal = {
|
||||||
// selectTab represents.
|
// selectTab represents.
|
||||||
let selectTab = 0;
|
let selectTab = 0;
|
||||||
if (overwriteTabs) {
|
if (overwriteTabs) {
|
||||||
|
@ -90,7 +92,7 @@ index 8c6047e1ada5a22e57e1e665965237c9e22641d7..ccd2779d66eda9d034ca51cc3200d814
|
||||||
selectTab = Math.min(selectTab, winData.tabs.length);
|
selectTab = Math.min(selectTab, winData.tabs.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5613,6 +5614,7 @@ var SessionStoreInternal = {
|
@@ -5613,6 +5616,7 @@ var SessionStoreInternal = {
|
||||||
winData.tabs,
|
winData.tabs,
|
||||||
winData.groups ?? []
|
winData.groups ?? []
|
||||||
);
|
);
|
||||||
|
@ -98,12 +100,13 @@ index 8c6047e1ada5a22e57e1e665965237c9e22641d7..ccd2779d66eda9d034ca51cc3200d814
|
||||||
this._log.debug(
|
this._log.debug(
|
||||||
`restoreWindow, createTabsForSessionRestore returned ${tabs.length} tabs`
|
`restoreWindow, createTabsForSessionRestore returned ${tabs.length} tabs`
|
||||||
);
|
);
|
||||||
@@ -6162,8 +6164,23 @@ var SessionStoreInternal = {
|
@@ -6162,6 +6166,22 @@ var SessionStoreInternal = {
|
||||||
|
|
||||||
// Most of tabData has been restored, now continue with restoring
|
// Most of tabData has been restored, now continue with restoring
|
||||||
// attributes that may trigger external events.
|
// attributes that may trigger external events.
|
||||||
+ if (tabData.zenEssential) {
|
+ if (tabData.zenEssential) {
|
||||||
+ tab.setAttribute("zen-essential", "true");
|
+ tab.setAttribute("zen-essential", "true");
|
||||||
|
+ tabData.pinned = true; // Essential tabs are always pinned.
|
||||||
+ }
|
+ }
|
||||||
+ if (tabData.zenIsEmpty) {
|
+ if (tabData.zenIsEmpty) {
|
||||||
+ tab.setAttribute("zen-empty-tab", "true");
|
+ tab.setAttribute("zen-empty-tab", "true");
|
||||||
|
@ -118,8 +121,5 @@ index 8c6047e1ada5a22e57e1e665965237c9e22641d7..ccd2779d66eda9d034ca51cc3200d814
|
||||||
+ tab.setAttribute("zenDefaultUserContextId", true);
|
+ tab.setAttribute("zenDefaultUserContextId", true);
|
||||||
+ }
|
+ }
|
||||||
|
|
||||||
- if (tabData.pinned) {
|
if (tabData.pinned) {
|
||||||
+ if (tabData.pinned || tabData.zenEssential) {
|
|
||||||
tabbrowser.pinTab(tab);
|
tabbrowser.pinTab(tab);
|
||||||
} else {
|
|
||||||
tabbrowser.unpinTab(tab);
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
diff --git a/browser/components/sessionstore/TabState.sys.mjs b/browser/components/sessionstore/TabState.sys.mjs
|
diff --git a/browser/components/sessionstore/TabState.sys.mjs b/browser/components/sessionstore/TabState.sys.mjs
|
||||||
index 8f7ed557e6aa61e7e16ed4a8d785ad5fe651b3d8..254849e13f7566029dc780c45e376e0f0d427cb5 100644
|
index 8f7ed557e6aa61e7e16ed4a8d785ad5fe651b3d8..76f4cf5aef30cb580ef0295fe6928b5a6a362f4b 100644
|
||||||
--- a/browser/components/sessionstore/TabState.sys.mjs
|
--- a/browser/components/sessionstore/TabState.sys.mjs
|
||||||
+++ b/browser/components/sessionstore/TabState.sys.mjs
|
+++ b/browser/components/sessionstore/TabState.sys.mjs
|
||||||
@@ -84,6 +84,16 @@ class _TabState {
|
@@ -84,6 +84,18 @@ class _TabState {
|
||||||
tabData.groupId = tab.group.id;
|
tabData.groupId = tab.group.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,8 @@ index 8f7ed557e6aa61e7e16ed4a8d785ad5fe651b3d8..254849e13f7566029dc780c45e376e0f
|
||||||
+ tabData.zenPinnedIcon = tab.getAttribute("zen-pinned-icon");
|
+ tabData.zenPinnedIcon = tab.getAttribute("zen-pinned-icon");
|
||||||
+ tabData.zenIsEmpty = tab.hasAttribute("zen-empty-tab");
|
+ tabData.zenIsEmpty = tab.hasAttribute("zen-empty-tab");
|
||||||
+ tabData.zenHasStaticLabel = tab.hasAttribute("zen-has-static-label");
|
+ tabData.zenHasStaticLabel = tab.hasAttribute("zen-has-static-label");
|
||||||
|
+ tabData.zenGlanceId = tab.getAttribute("glance-id");
|
||||||
|
+ tabData.zenIsGlance = tab.hasAttribute("zen-glance-tab");
|
||||||
+
|
+
|
||||||
tabData.searchMode = tab.ownerGlobal.gURLBar.getSearchMode(browser, true);
|
tabData.searchMode = tab.ownerGlobal.gURLBar.getSearchMode(browser, true);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js
|
diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js
|
||||||
index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b032cf200f1 100644
|
index d5aa64842a35c6697263c63fd3a0571b64b01344..14f5bc046f2e54109bd3fd0402a8f8b598a513c2 100644
|
||||||
--- a/browser/components/tabbrowser/content/tabbrowser.js
|
--- a/browser/components/tabbrowser/content/tabbrowser.js
|
||||||
+++ b/browser/components/tabbrowser/content/tabbrowser.js
|
+++ b/browser/components/tabbrowser/content/tabbrowser.js
|
||||||
@@ -413,11 +413,41 @@
|
@@ -413,11 +413,41 @@
|
||||||
|
@ -292,37 +292,17 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||||
|
|
||||||
let url = "about:blank";
|
let url = "about:blank";
|
||||||
if (tabData.entries?.length) {
|
if (tabData.entries?.length) {
|
||||||
@@ -3598,7 +3675,29 @@
|
@@ -3598,7 +3675,8 @@
|
||||||
skipLoad: true,
|
skipLoad: true,
|
||||||
preferredRemoteType,
|
preferredRemoteType,
|
||||||
});
|
});
|
||||||
|
-
|
||||||
+ tab._originalUrl = url;
|
+ tab._originalUrl = url;
|
||||||
|
+ gZenSessionStore.restoreInitialTabData(tab, tabData);
|
||||||
+ if (tabData.zenWorkspace) {
|
|
||||||
+ tab.setAttribute("zen-workspace-id", tabData.zenWorkspace);
|
|
||||||
+ }
|
|
||||||
+ if (tabData.zenPinnedId) {
|
|
||||||
+ tab.setAttribute("zen-pin-id", tabData.zenPinnedId);
|
|
||||||
+ }
|
|
||||||
+ if (tabData.zenIsEmpty) {
|
|
||||||
+ tab.setAttribute("zen-empty-tab", "true");
|
|
||||||
+ }
|
|
||||||
+ if (tabData.zenHasStaticLabel) {
|
|
||||||
+ tab.setAttribute("zen-has-static-label", "true");
|
|
||||||
+ }
|
|
||||||
+ if (tabData.zenEssential) {
|
|
||||||
+ tab.setAttribute("zen-essential", "true");
|
|
||||||
+ }
|
|
||||||
+ if (tabData.zenDefaultUserContextId) {
|
|
||||||
+ tab.setAttribute("zenDefaultUserContextId", "true");
|
|
||||||
+ }
|
|
||||||
+ if (tabData.zenPinnedEntry) {
|
|
||||||
+ tab.setAttribute("zen-pinned-entry", tabData.zenPinnedEntry);
|
|
||||||
+ }
|
|
||||||
if (select) {
|
if (select) {
|
||||||
tabToSelect = tab;
|
tabToSelect = tab;
|
||||||
}
|
}
|
||||||
@@ -3622,7 +3721,8 @@
|
@@ -3622,7 +3700,8 @@
|
||||||
// needs calling:
|
// needs calling:
|
||||||
shouldUpdateForPinnedTabs = true;
|
shouldUpdateForPinnedTabs = true;
|
||||||
}
|
}
|
||||||
|
@ -332,7 +312,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||||
let { groupId } = tabData;
|
let { groupId } = tabData;
|
||||||
const tabGroup = tabGroupWorkingData.get(groupId);
|
const tabGroup = tabGroupWorkingData.get(groupId);
|
||||||
// if a tab refers to a tab group we don't know, skip any group
|
// if a tab refers to a tab group we don't know, skip any group
|
||||||
@@ -3636,7 +3736,10 @@
|
@@ -3636,7 +3715,10 @@
|
||||||
tabGroup.stateData.id,
|
tabGroup.stateData.id,
|
||||||
tabGroup.stateData.color,
|
tabGroup.stateData.color,
|
||||||
tabGroup.stateData.collapsed,
|
tabGroup.stateData.collapsed,
|
||||||
|
@ -344,7 +324,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||||
);
|
);
|
||||||
tabsFragment.appendChild(tabGroup.node);
|
tabsFragment.appendChild(tabGroup.node);
|
||||||
}
|
}
|
||||||
@@ -3684,8 +3787,16 @@
|
@@ -3684,8 +3766,16 @@
|
||||||
// to remove the old selected tab.
|
// to remove the old selected tab.
|
||||||
if (tabToSelect) {
|
if (tabToSelect) {
|
||||||
let leftoverTab = this.selectedTab;
|
let leftoverTab = this.selectedTab;
|
||||||
|
@ -363,7 +343,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tabs.length > 1 || !tabs[0].selected) {
|
if (tabs.length > 1 || !tabs[0].selected) {
|
||||||
@@ -3881,7 +3992,7 @@
|
@@ -3881,7 +3971,7 @@
|
||||||
// Ensure we have an index if one was not provided.
|
// Ensure we have an index if one was not provided.
|
||||||
if (typeof elementIndex != "number" && typeof tabIndex != "number") {
|
if (typeof elementIndex != "number" && typeof tabIndex != "number") {
|
||||||
// Move the new tab after another tab if needed, to the end otherwise.
|
// Move the new tab after another tab if needed, to the end otherwise.
|
||||||
|
@ -372,7 +352,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||||
if (
|
if (
|
||||||
!bulkOrderedOpen &&
|
!bulkOrderedOpen &&
|
||||||
((openerTab &&
|
((openerTab &&
|
||||||
@@ -3904,7 +4015,7 @@
|
@@ -3904,7 +3994,7 @@
|
||||||
) {
|
) {
|
||||||
elementIndex = Infinity;
|
elementIndex = Infinity;
|
||||||
} else if (previousTab.visible) {
|
} else if (previousTab.visible) {
|
||||||
|
@ -381,7 +361,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||||
} else if (previousTab == FirefoxViewHandler.tab) {
|
} else if (previousTab == FirefoxViewHandler.tab) {
|
||||||
elementIndex = 0;
|
elementIndex = 0;
|
||||||
}
|
}
|
||||||
@@ -3932,10 +4043,10 @@
|
@@ -3932,14 +4022,14 @@
|
||||||
}
|
}
|
||||||
// Ensure index is within bounds.
|
// Ensure index is within bounds.
|
||||||
if (tab.pinned) {
|
if (tab.pinned) {
|
||||||
|
@ -395,7 +375,12 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||||
index = Math.min(index, allItems.length);
|
index = Math.min(index, allItems.length);
|
||||||
}
|
}
|
||||||
/** @type {MozTabbrowserTab|undefined} */
|
/** @type {MozTabbrowserTab|undefined} */
|
||||||
@@ -3947,7 +4058,7 @@
|
- let itemAfter = allItems.at(index);
|
||||||
|
+ let itemAfter = gZenGlanceManager.getTabOrGlanceParent(allItems.at(index));
|
||||||
|
|
||||||
|
// Prevent a flash of unstyled content by setting up the tab content
|
||||||
|
// and inherited attributes before appending it (see Bug 1592054):
|
||||||
|
@@ -3947,7 +4037,7 @@
|
||||||
|
|
||||||
this.tabContainer._invalidateCachedTabs();
|
this.tabContainer._invalidateCachedTabs();
|
||||||
|
|
||||||
|
@ -404,7 +389,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||||
if (this.isTab(itemAfter) && itemAfter.group == tabGroup) {
|
if (this.isTab(itemAfter) && itemAfter.group == tabGroup) {
|
||||||
// Place at the front of, or between tabs in, the same tab group
|
// Place at the front of, or between tabs in, the same tab group
|
||||||
this.tabContainer.insertBefore(tab, itemAfter);
|
this.tabContainer.insertBefore(tab, itemAfter);
|
||||||
@@ -4268,6 +4379,9 @@
|
@@ -4268,6 +4358,9 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -414,7 +399,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||||
this.removeTabs(selectedTabs, { telemetrySource });
|
this.removeTabs(selectedTabs, { telemetrySource });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4520,6 +4634,7 @@
|
@@ -4520,6 +4613,7 @@
|
||||||
telemetrySource,
|
telemetrySource,
|
||||||
} = {}
|
} = {}
|
||||||
) {
|
) {
|
||||||
|
@ -422,7 +407,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||||
// When 'closeWindowWithLastTab' pref is enabled, closing all tabs
|
// When 'closeWindowWithLastTab' pref is enabled, closing all tabs
|
||||||
// can be considered equivalent to closing the window.
|
// can be considered equivalent to closing the window.
|
||||||
if (
|
if (
|
||||||
@@ -4604,6 +4719,7 @@
|
@@ -4604,6 +4698,7 @@
|
||||||
if (lastToClose) {
|
if (lastToClose) {
|
||||||
this.removeTab(lastToClose, aParams);
|
this.removeTab(lastToClose, aParams);
|
||||||
}
|
}
|
||||||
|
@ -430,7 +415,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
}
|
}
|
||||||
@@ -4641,6 +4757,12 @@
|
@@ -4641,6 +4736,12 @@
|
||||||
aTab._closeTimeNoAnimTimerId = Glean.browserTabclose.timeNoAnim.start();
|
aTab._closeTimeNoAnimTimerId = Glean.browserTabclose.timeNoAnim.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -443,7 +428,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||||
// Handle requests for synchronously removing an already
|
// Handle requests for synchronously removing an already
|
||||||
// asynchronously closing tab.
|
// asynchronously closing tab.
|
||||||
if (!animate && aTab.closing) {
|
if (!animate && aTab.closing) {
|
||||||
@@ -4655,7 +4777,9 @@
|
@@ -4655,7 +4756,9 @@
|
||||||
// frame created for it (for example, by updating the visually selected
|
// frame created for it (for example, by updating the visually selected
|
||||||
// state).
|
// state).
|
||||||
let tabWidth = window.windowUtils.getBoundsWithoutFlushing(aTab).width;
|
let tabWidth = window.windowUtils.getBoundsWithoutFlushing(aTab).width;
|
||||||
|
@ -454,7 +439,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||||
if (
|
if (
|
||||||
!this._beginRemoveTab(aTab, {
|
!this._beginRemoveTab(aTab, {
|
||||||
closeWindowFastpath: true,
|
closeWindowFastpath: true,
|
||||||
@@ -4821,7 +4945,7 @@
|
@@ -4821,7 +4924,7 @@
|
||||||
closeWindowWithLastTab != null
|
closeWindowWithLastTab != null
|
||||||
? closeWindowWithLastTab
|
? closeWindowWithLastTab
|
||||||
: !window.toolbar.visible ||
|
: !window.toolbar.visible ||
|
||||||
|
@ -463,7 +448,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||||
|
|
||||||
if (closeWindow) {
|
if (closeWindow) {
|
||||||
// We've already called beforeunload on all the relevant tabs if we get here,
|
// We've already called beforeunload on all the relevant tabs if we get here,
|
||||||
@@ -4845,6 +4969,7 @@
|
@@ -4845,6 +4948,7 @@
|
||||||
|
|
||||||
newTab = true;
|
newTab = true;
|
||||||
}
|
}
|
||||||
|
@ -471,7 +456,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||||
aTab._endRemoveArgs = [closeWindow, newTab];
|
aTab._endRemoveArgs = [closeWindow, newTab];
|
||||||
|
|
||||||
// swapBrowsersAndCloseOther will take care of closing the window without animation.
|
// swapBrowsersAndCloseOther will take care of closing the window without animation.
|
||||||
@@ -4885,9 +5010,7 @@
|
@@ -4885,9 +4989,7 @@
|
||||||
aTab._mouseleave();
|
aTab._mouseleave();
|
||||||
|
|
||||||
if (newTab) {
|
if (newTab) {
|
||||||
|
@ -482,7 +467,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||||
} else {
|
} else {
|
||||||
TabBarVisibility.update();
|
TabBarVisibility.update();
|
||||||
}
|
}
|
||||||
@@ -5016,6 +5139,8 @@
|
@@ -5016,6 +5118,8 @@
|
||||||
this.tabs[i]._tPos = i;
|
this.tabs[i]._tPos = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -491,7 +476,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||||
if (!this._windowIsClosing) {
|
if (!this._windowIsClosing) {
|
||||||
if (wasPinned) {
|
if (wasPinned) {
|
||||||
this.tabContainer._positionPinnedTabs();
|
this.tabContainer._positionPinnedTabs();
|
||||||
@@ -5230,6 +5355,7 @@
|
@@ -5230,6 +5334,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
let excludeTabs = new Set(aExcludeTabs);
|
let excludeTabs = new Set(aExcludeTabs);
|
||||||
|
@ -499,7 +484,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||||
|
|
||||||
// If this tab has a successor, it should be selectable, since
|
// If this tab has a successor, it should be selectable, since
|
||||||
// hiding or closing a tab removes that tab as a successor.
|
// hiding or closing a tab removes that tab as a successor.
|
||||||
@@ -5242,13 +5368,13 @@
|
@@ -5242,13 +5347,13 @@
|
||||||
!excludeTabs.has(aTab.owner) &&
|
!excludeTabs.has(aTab.owner) &&
|
||||||
Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose")
|
Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose")
|
||||||
) {
|
) {
|
||||||
|
@ -515,7 +500,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||||
);
|
);
|
||||||
|
|
||||||
let tab = this.tabContainer.findNextTab(aTab, {
|
let tab = this.tabContainer.findNextTab(aTab, {
|
||||||
@@ -5264,7 +5390,7 @@
|
@@ -5264,7 +5369,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tab) {
|
if (tab) {
|
||||||
|
@ -524,7 +509,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no qualifying visible tab was found, see if there is a tab in
|
// If no qualifying visible tab was found, see if there is a tab in
|
||||||
@@ -5285,7 +5411,7 @@
|
@@ -5285,7 +5390,7 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -533,7 +518,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||||
}
|
}
|
||||||
|
|
||||||
_blurTab(aTab) {
|
_blurTab(aTab) {
|
||||||
@@ -5686,10 +5812,10 @@
|
@@ -5686,10 +5791,10 @@
|
||||||
SessionStore.deleteCustomTabValue(aTab, "hiddenBy");
|
SessionStore.deleteCustomTabValue(aTab, "hiddenBy");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -546,7 +531,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||||
aTab.selected ||
|
aTab.selected ||
|
||||||
aTab.closing ||
|
aTab.closing ||
|
||||||
// Tabs that are sharing the screen, microphone or camera cannot be hidden.
|
// Tabs that are sharing the screen, microphone or camera cannot be hidden.
|
||||||
@@ -5986,7 +6112,7 @@
|
@@ -5986,7 +6091,7 @@
|
||||||
|
|
||||||
// Don't allow mixing pinned and unpinned tabs.
|
// Don't allow mixing pinned and unpinned tabs.
|
||||||
if (this.isTab(element) && element.pinned) {
|
if (this.isTab(element) && element.pinned) {
|
||||||
|
@ -555,7 +540,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||||
} else {
|
} else {
|
||||||
tabIndex = Math.max(tabIndex, this.pinnedTabCount);
|
tabIndex = Math.max(tabIndex, this.pinnedTabCount);
|
||||||
}
|
}
|
||||||
@@ -6012,10 +6138,16 @@
|
@@ -6012,10 +6117,16 @@
|
||||||
this.#handleTabMove(
|
this.#handleTabMove(
|
||||||
element,
|
element,
|
||||||
() => {
|
() => {
|
||||||
|
@ -574,7 +559,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||||
if (neighbor && this.isTab(element) && tabIndex > element._tPos) {
|
if (neighbor && this.isTab(element) && tabIndex > element._tPos) {
|
||||||
neighbor.after(element);
|
neighbor.after(element);
|
||||||
} else {
|
} else {
|
||||||
@@ -6084,17 +6216,26 @@
|
@@ -6084,17 +6195,29 @@
|
||||||
targetElement = targetElement.group;
|
targetElement = targetElement.group;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -583,8 +568,12 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||||
+ element = element.group;
|
+ element = element.group;
|
||||||
+ }
|
+ }
|
||||||
// Don't allow mixing pinned and unpinned tabs.
|
// Don't allow mixing pinned and unpinned tabs.
|
||||||
if (element.pinned && !targetElement?.pinned) {
|
- if (element.pinned && !targetElement?.pinned) {
|
||||||
- targetElement = this.tabs[this.pinnedTabCount - 1];
|
- targetElement = this.tabs[this.pinnedTabCount - 1];
|
||||||
|
+ if (element.hasAttribute('zen-essential') && !targetElement?.hasAttribute('zen-essential')) {
|
||||||
|
+ targetElement = this.tabs.filter(tab => !tab.hasAttribute('zen-glance-tab'))[this._numZenEssentials - 1];
|
||||||
|
+ moveBefore = false;
|
||||||
|
+ } else if (element.pinned && !targetElement?.pinned) {
|
||||||
+ targetElement = this.tabs.filter(tab => !tab.hasAttribute('zen-glance-tab'))[this.pinnedTabCount - 1];
|
+ targetElement = this.tabs.filter(tab => !tab.hasAttribute('zen-glance-tab'))[this.pinnedTabCount - 1];
|
||||||
moveBefore = false;
|
moveBefore = false;
|
||||||
} else if (!element.pinned && targetElement && targetElement.pinned) {
|
} else if (!element.pinned && targetElement && targetElement.pinned) {
|
||||||
|
@ -604,7 +593,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||||
if (element.pinned && this.tabContainer.verticalMode) {
|
if (element.pinned && this.tabContainer.verticalMode) {
|
||||||
return this.tabContainer.verticalPinnedTabsContainer;
|
return this.tabContainer.verticalPinnedTabsContainer;
|
||||||
}
|
}
|
||||||
@@ -6154,7 +6295,7 @@
|
@@ -6154,7 +6277,7 @@
|
||||||
if (!this.isTab(aTab)) {
|
if (!this.isTab(aTab)) {
|
||||||
throw new Error("Can only move a tab into a tab group");
|
throw new Error("Can only move a tab into a tab group");
|
||||||
}
|
}
|
||||||
|
@ -613,7 +602,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (aTab.group && aTab.group.id === aGroup.id) {
|
if (aTab.group && aTab.group.id === aGroup.id) {
|
||||||
@@ -6248,6 +6389,10 @@
|
@@ -6248,6 +6371,10 @@
|
||||||
|
|
||||||
moveActionCallback();
|
moveActionCallback();
|
||||||
|
|
||||||
|
@ -624,7 +613,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||||
// Clear tabs cache after moving nodes because the order of tabs may have
|
// Clear tabs cache after moving nodes because the order of tabs may have
|
||||||
// changed.
|
// changed.
|
||||||
this.tabContainer._invalidateCachedTabs();
|
this.tabContainer._invalidateCachedTabs();
|
||||||
@@ -7145,7 +7290,7 @@
|
@@ -7145,7 +7272,7 @@
|
||||||
// preventDefault(). It will still raise the window if appropriate.
|
// preventDefault(). It will still raise the window if appropriate.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -633,7 +622,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||||
window.focus();
|
window.focus();
|
||||||
aEvent.preventDefault();
|
aEvent.preventDefault();
|
||||||
break;
|
break;
|
||||||
@@ -8044,6 +8189,7 @@
|
@@ -8044,6 +8171,7 @@
|
||||||
aWebProgress.isTopLevel
|
aWebProgress.isTopLevel
|
||||||
) {
|
) {
|
||||||
this.mTab.setAttribute("busy", "true");
|
this.mTab.setAttribute("busy", "true");
|
||||||
|
@ -641,7 +630,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||||
gBrowser._tabAttrModified(this.mTab, ["busy"]);
|
gBrowser._tabAttrModified(this.mTab, ["busy"]);
|
||||||
this.mTab._notselectedsinceload = !this.mTab.selected;
|
this.mTab._notselectedsinceload = !this.mTab.selected;
|
||||||
}
|
}
|
||||||
@@ -9009,7 +9155,7 @@ var TabContextMenu = {
|
@@ -9009,7 +9137,7 @@ var TabContextMenu = {
|
||||||
);
|
);
|
||||||
contextUnpinSelectedTabs.hidden =
|
contextUnpinSelectedTabs.hidden =
|
||||||
!this.contextTab.pinned || !this.multiselected;
|
!this.contextTab.pinned || !this.multiselected;
|
||||||
|
@ -650,7 +639,7 @@ index d5aa64842a35c6697263c63fd3a0571b64b01344..6943dc7f1d3d95ab1da315cbdbda0b03
|
||||||
// Move Tab items
|
// Move Tab items
|
||||||
let contextMoveTabOptions = document.getElementById(
|
let contextMoveTabOptions = document.getElementById(
|
||||||
"context_moveTabOptions"
|
"context_moveTabOptions"
|
||||||
@@ -9278,6 +9424,7 @@ var TabContextMenu = {
|
@@ -9278,6 +9406,7 @@ var TabContextMenu = {
|
||||||
telemetrySource: gBrowser.TabMetrics.METRIC_SOURCE.TAB_STRIP,
|
telemetrySource: gBrowser.TabMetrics.METRIC_SOURCE.TAB_STRIP,
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
13
src/browser/components/tabbrowser/content/tabgroup-js.patch
Normal file
13
src/browser/components/tabbrowser/content/tabgroup-js.patch
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
diff --git a/browser/components/tabbrowser/content/tabgroup.js b/browser/components/tabbrowser/content/tabgroup.js
|
||||||
|
index 6dc774ea335b0c5dba7dcf76cdb23728faae1343..b0b9ef236c2e8517db4bcf3270596456bbefe11d 100644
|
||||||
|
--- a/browser/components/tabbrowser/content/tabgroup.js
|
||||||
|
+++ b/browser/components/tabbrowser/content/tabgroup.js
|
||||||
|
@@ -301,7 +301,7 @@
|
||||||
|
*/
|
||||||
|
addTabs(tabs, metricsContext) {
|
||||||
|
for (let tab of tabs) {
|
||||||
|
- if (tab.pinned) {
|
||||||
|
+ if (tab.pinned !== this.pinned) {
|
||||||
|
tab.ownerGlobal.gBrowser.unpinTab(tab);
|
||||||
|
}
|
||||||
|
let tabToMove =
|
|
@ -1,5 +1,5 @@
|
||||||
diff --git a/browser/components/tabbrowser/content/tabs.js b/browser/components/tabbrowser/content/tabs.js
|
diff --git a/browser/components/tabbrowser/content/tabs.js b/browser/components/tabbrowser/content/tabs.js
|
||||||
index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea9596403cef 100644
|
index 84d633471c89230b981d8a07babef4e0c76c0338..de8b1ecf7cb844f6cf3e66a41b6024c574dfc103 100644
|
||||||
--- a/browser/components/tabbrowser/content/tabs.js
|
--- a/browser/components/tabbrowser/content/tabs.js
|
||||||
+++ b/browser/components/tabbrowser/content/tabs.js
|
+++ b/browser/components/tabbrowser/content/tabs.js
|
||||||
@@ -83,7 +83,7 @@
|
@@ -83,7 +83,7 @@
|
||||||
|
@ -46,6 +46,15 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||||
expandGroupOnDrop = true;
|
expandGroupOnDrop = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -868,7 +869,7 @@
|
||||||
|
? event.screenY - window.screenY - tabOffset
|
||||||
|
: event.screenY - window.screenY,
|
||||||
|
scrollPos:
|
||||||
|
- this.verticalMode && tab.pinned
|
||||||
|
+ this.verticalMode && tab.pinned && false
|
||||||
|
? this.verticalPinnedTabsContainer.scrollPosition
|
||||||
|
: this.arrowScrollbox.scrollPosition,
|
||||||
|
screenX: event.screenX,
|
||||||
@@ -921,6 +922,10 @@
|
@@ -921,6 +922,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,16 +100,23 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||||
);
|
);
|
||||||
let size = this.verticalMode ? "height" : "width";
|
let size = this.verticalMode ? "height" : "width";
|
||||||
let screenAxis = this.verticalMode ? "screenY" : "screenX";
|
let screenAxis = this.verticalMode ? "screenY" : "screenX";
|
||||||
@@ -1211,7 +1229,7 @@
|
@@ -1135,8 +1153,14 @@
|
||||||
item.removeAttribute("tabdrop-samewindow");
|
(lastMovingTabScreen + tabSize);
|
||||||
resolve();
|
|
||||||
};
|
if (this.verticalMode) {
|
||||||
- if (gReduceMotion) {
|
+ if (oldTranslateY > 0 && translateOffsetY > tabHeight / 2) {
|
||||||
+ if (true || gReduceMotion) {
|
+ newTranslateY += tabHeight;
|
||||||
postTransitionCleanup();
|
+ }
|
||||||
} else {
|
+ if (oldTranslateY < 0 && -translateOffsetY > tabHeight / 2) {
|
||||||
let onTransitionEnd = transitionendEvent => {
|
+ newTranslateY -= tabHeight;
|
||||||
@@ -1337,6 +1355,7 @@
|
+ }
|
||||||
|
newTranslateY = Math.min(
|
||||||
|
- Math.max(oldTranslateY, firstBound),
|
||||||
|
+ Math.max(newTranslateY, firstBound),
|
||||||
|
lastBound
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
@@ -1337,6 +1361,7 @@
|
||||||
|
|
||||||
let nextItem = this.ariaFocusableItems[newIndex];
|
let nextItem = this.ariaFocusableItems[newIndex];
|
||||||
let tabGroup = isTab(nextItem) && nextItem.group;
|
let tabGroup = isTab(nextItem) && nextItem.group;
|
||||||
|
@ -108,7 +124,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||||
gBrowser.loadTabs(urls, {
|
gBrowser.loadTabs(urls, {
|
||||||
inBackground,
|
inBackground,
|
||||||
replace,
|
replace,
|
||||||
@@ -1369,6 +1388,17 @@
|
@@ -1369,6 +1394,17 @@
|
||||||
|
|
||||||
this.finishMoveTogetherSelectedTabs(draggedTab);
|
this.finishMoveTogetherSelectedTabs(draggedTab);
|
||||||
this.finishAnimateTabMove();
|
this.finishAnimateTabMove();
|
||||||
|
@ -126,7 +142,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||||
this.#expandGroupOnDrop(draggedTab);
|
this.#expandGroupOnDrop(draggedTab);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
@@ -1597,7 +1627,7 @@
|
@@ -1597,7 +1633,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
get newTabButton() {
|
get newTabButton() {
|
||||||
|
@ -135,7 +151,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||||
}
|
}
|
||||||
|
|
||||||
get verticalMode() {
|
get verticalMode() {
|
||||||
@@ -1621,29 +1651,54 @@
|
@@ -1621,29 +1657,54 @@
|
||||||
if (this.#allTabs) {
|
if (this.#allTabs) {
|
||||||
return this.#allTabs;
|
return this.#allTabs;
|
||||||
}
|
}
|
||||||
|
@ -179,7 +195,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||||
+ if (glanceTab) {
|
+ if (glanceTab) {
|
||||||
+ // insert right after the parent tab. note: it must be inserted before
|
+ // insert right after the parent tab. note: it must be inserted before
|
||||||
+ // the last pinned tab so it can be inserted in the correct order
|
+ // the last pinned tab so it can be inserted in the correct order
|
||||||
+ allTabs.splice(Math.max(i++, lastPinnedTabIdx), 0, glanceTab);
|
+ allTabs.splice(Math.max(i++ + 1, lastPinnedTabIdx), 0, glanceTab);
|
||||||
+ } else if (tab.classList.contains("vertical-pinned-tabs-container-separator")) {
|
+ } else if (tab.classList.contains("vertical-pinned-tabs-container-separator")) {
|
||||||
+ // remove the separator from the list
|
+ // remove the separator from the list
|
||||||
+ allTabs.splice(i, 1);
|
+ allTabs.splice(i, 1);
|
||||||
|
@ -198,7 +214,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1698,23 +1753,18 @@
|
@@ -1698,23 +1759,18 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
let elementIndex = 0;
|
let elementIndex = 0;
|
||||||
|
@ -226,7 +242,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||||
let visibleTabsInGroup = child.tabs.filter(tab => tab.visible);
|
let visibleTabsInGroup = child.tabs.filter(tab => tab.visible);
|
||||||
visibleTabsInGroup.forEach(tab => {
|
visibleTabsInGroup.forEach(tab => {
|
||||||
tab.elementIndex = elementIndex++;
|
tab.elementIndex = elementIndex++;
|
||||||
@@ -1724,10 +1774,7 @@
|
@@ -1724,10 +1780,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -238,7 +254,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||||
|
|
||||||
return this.#focusableItems;
|
return this.#focusableItems;
|
||||||
}
|
}
|
||||||
@@ -1735,6 +1782,7 @@
|
@@ -1735,6 +1788,7 @@
|
||||||
_invalidateCachedTabs() {
|
_invalidateCachedTabs() {
|
||||||
this.#allTabs = null;
|
this.#allTabs = null;
|
||||||
this._invalidateCachedVisibleTabs();
|
this._invalidateCachedVisibleTabs();
|
||||||
|
@ -246,7 +262,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||||
}
|
}
|
||||||
|
|
||||||
_invalidateCachedVisibleTabs() {
|
_invalidateCachedVisibleTabs() {
|
||||||
@@ -1749,8 +1797,8 @@
|
@@ -1749,8 +1803,8 @@
|
||||||
#isContainerVerticalPinnedGrid(tab) {
|
#isContainerVerticalPinnedGrid(tab) {
|
||||||
return (
|
return (
|
||||||
this.verticalMode &&
|
this.verticalMode &&
|
||||||
|
@ -257,7 +273,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||||
!this.expandOnHover
|
!this.expandOnHover
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1766,7 +1814,7 @@
|
@@ -1766,7 +1820,7 @@
|
||||||
|
|
||||||
if (node == null) {
|
if (node == null) {
|
||||||
// We have a container for non-tab elements at the end of the scrollbox.
|
// We have a container for non-tab elements at the end of the scrollbox.
|
||||||
|
@ -266,7 +282,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||||
}
|
}
|
||||||
|
|
||||||
node.before(tab);
|
node.before(tab);
|
||||||
@@ -1861,7 +1909,7 @@
|
@@ -1861,7 +1915,7 @@
|
||||||
// There are separate "new tab" buttons for horizontal tabs toolbar, vertical tabs and
|
// There are separate "new tab" buttons for horizontal tabs toolbar, vertical tabs and
|
||||||
// for when the tab strip is overflowed (which is shared by vertical and horizontal tabs);
|
// for when the tab strip is overflowed (which is shared by vertical and horizontal tabs);
|
||||||
// Attach the long click popup to all of them.
|
// Attach the long click popup to all of them.
|
||||||
|
@ -275,7 +291,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||||
const newTab2 = this.newTabButton;
|
const newTab2 = this.newTabButton;
|
||||||
const newTabVertical = document.getElementById(
|
const newTabVertical = document.getElementById(
|
||||||
"vertical-tabs-newtab-button"
|
"vertical-tabs-newtab-button"
|
||||||
@@ -1956,10 +2004,12 @@
|
@@ -1956,10 +2010,12 @@
|
||||||
|
|
||||||
_handleTabSelect(aInstant) {
|
_handleTabSelect(aInstant) {
|
||||||
let selectedTab = this.selectedItem;
|
let selectedTab = this.selectedItem;
|
||||||
|
@ -288,7 +304,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||||
selectedTab._notselectedsinceload = false;
|
selectedTab._notselectedsinceload = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2132,6 +2182,7 @@
|
@@ -2132,6 +2188,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
_positionPinnedTabs() {
|
_positionPinnedTabs() {
|
||||||
|
@ -296,7 +312,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||||
let tabs = this.visibleTabs;
|
let tabs = this.visibleTabs;
|
||||||
let numPinned = gBrowser.pinnedTabCount;
|
let numPinned = gBrowser.pinnedTabCount;
|
||||||
let absPositionHorizontalTabs =
|
let absPositionHorizontalTabs =
|
||||||
@@ -2206,7 +2257,7 @@
|
@@ -2206,7 +2263,7 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -305,16 +321,25 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||||
|
|
||||||
let directionX = screenX > dragData.animLastScreenX;
|
let directionX = screenX > dragData.animLastScreenX;
|
||||||
let directionY = screenY > dragData.animLastScreenY;
|
let directionY = screenY > dragData.animLastScreenY;
|
||||||
@@ -2214,7 +2265,7 @@
|
@@ -2215,6 +2272,8 @@
|
||||||
dragData.animLastScreenX = screenX;
|
|
||||||
|
|
||||||
let { width: tabWidth, height: tabHeight } =
|
let { width: tabWidth, height: tabHeight } =
|
||||||
- draggedTab.getBoundingClientRect();
|
draggedTab.getBoundingClientRect();
|
||||||
+ (draggedTab.group?.hasAttribute("split-view-group") ? draggedTab.group : draggedTab).getBoundingClientRect();
|
+ tabWidth += 4; // Add 4px to account for the gap
|
||||||
|
+ tabHeight += 4;
|
||||||
let shiftSizeX = tabWidth * movingTabs.length;
|
let shiftSizeX = tabWidth * movingTabs.length;
|
||||||
let shiftSizeY = tabHeight;
|
let shiftSizeY = tabHeight;
|
||||||
dragData.tabWidth = tabWidth;
|
dragData.tabWidth = tabWidth;
|
||||||
@@ -2389,12 +2440,16 @@
|
@@ -2244,7 +2303,7 @@
|
||||||
|
let translateX = screenX - dragData.screenX;
|
||||||
|
let translateY = screenY - dragData.screenY;
|
||||||
|
translateY +=
|
||||||
|
- this.verticalPinnedTabsContainer.scrollPosition - dragData.scrollPos;
|
||||||
|
+ dragData.scrollPos;
|
||||||
|
let firstBoundX = firstTabInRow.screenX - firstMovingTabScreenX;
|
||||||
|
let firstBoundY = firstTabInRow.screenY - firstMovingTabScreenY;
|
||||||
|
let lastBoundX =
|
||||||
|
@@ -2389,12 +2448,16 @@
|
||||||
|
|
||||||
this.#clearDragOverCreateGroupTimer();
|
this.#clearDragOverCreateGroupTimer();
|
||||||
|
|
||||||
|
@ -335,7 +360,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||||
|
|
||||||
if (this.#rtlMode) {
|
if (this.#rtlMode) {
|
||||||
tabs.reverse();
|
tabs.reverse();
|
||||||
@@ -2408,7 +2463,7 @@
|
@@ -2408,7 +2471,7 @@
|
||||||
let size = this.verticalMode ? "height" : "width";
|
let size = this.verticalMode ? "height" : "width";
|
||||||
let translateAxis = this.verticalMode ? "translateY" : "translateX";
|
let translateAxis = this.verticalMode ? "translateY" : "translateX";
|
||||||
let scrollDirection = this.verticalMode ? "scrollTop" : "scrollLeft";
|
let scrollDirection = this.verticalMode ? "scrollTop" : "scrollLeft";
|
||||||
|
@ -344,7 +369,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||||
let translateX = event.screenX - dragData.screenX;
|
let translateX = event.screenX - dragData.screenX;
|
||||||
let translateY = event.screenY - dragData.screenY;
|
let translateY = event.screenY - dragData.screenY;
|
||||||
|
|
||||||
@@ -2422,12 +2477,21 @@
|
@@ -2422,12 +2485,21 @@
|
||||||
let lastTab = tabs.at(-1);
|
let lastTab = tabs.at(-1);
|
||||||
let lastMovingTab = movingTabs.at(-1);
|
let lastMovingTab = movingTabs.at(-1);
|
||||||
let firstMovingTab = movingTabs[0];
|
let firstMovingTab = movingTabs[0];
|
||||||
|
@ -367,7 +392,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||||
translate +=
|
translate +=
|
||||||
this.arrowScrollbox.scrollbox[scrollDirection] - dragData.scrollPos;
|
this.arrowScrollbox.scrollbox[scrollDirection] - dragData.scrollPos;
|
||||||
} else if (isPinned && this.verticalMode) {
|
} else if (isPinned && this.verticalMode) {
|
||||||
@@ -2446,6 +2510,9 @@
|
@@ -2446,6 +2518,9 @@
|
||||||
// Shift the `.tab-group-label-container` to shift the label element.
|
// Shift the `.tab-group-label-container` to shift the label element.
|
||||||
item = item.parentElement;
|
item = item.parentElement;
|
||||||
}
|
}
|
||||||
|
@ -377,7 +402,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||||
item.style.transform = `${translateAxis}(${translate}px)`;
|
item.style.transform = `${translateAxis}(${translate}px)`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2583,6 +2650,9 @@
|
@@ -2583,6 +2658,9 @@
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let element = tabs[mid];
|
let element = tabs[mid];
|
||||||
|
@ -387,7 +412,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||||
let elementForSize = isTabGroupLabel(element)
|
let elementForSize = isTabGroupLabel(element)
|
||||||
? element.parentElement
|
? element.parentElement
|
||||||
: element;
|
: element;
|
||||||
@@ -2605,6 +2675,10 @@
|
@@ -2605,6 +2683,10 @@
|
||||||
if (!dropElement) {
|
if (!dropElement) {
|
||||||
dropElement = this.ariaFocusableItems[oldDropElementIndex];
|
dropElement = this.ariaFocusableItems[oldDropElementIndex];
|
||||||
}
|
}
|
||||||
|
@ -398,7 +423,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||||
let newDropElementIndex = dropElement
|
let newDropElementIndex = dropElement
|
||||||
? dropElement.elementIndex
|
? dropElement.elementIndex
|
||||||
: oldDropElementIndex;
|
: oldDropElementIndex;
|
||||||
@@ -2613,7 +2687,7 @@
|
@@ -2613,7 +2695,7 @@
|
||||||
let shouldCreateGroupOnDrop;
|
let shouldCreateGroupOnDrop;
|
||||||
let dropBefore;
|
let dropBefore;
|
||||||
if (dropElement) {
|
if (dropElement) {
|
||||||
|
@ -407,7 +432,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||||
? dropElement.parentElement
|
? dropElement.parentElement
|
||||||
: dropElement;
|
: dropElement;
|
||||||
|
|
||||||
@@ -2675,12 +2749,12 @@
|
@@ -2675,12 +2757,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -422,7 +447,16 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||||
dropElement != draggedTab &&
|
dropElement != draggedTab &&
|
||||||
isTab(dropElement) &&
|
isTab(dropElement) &&
|
||||||
!dropElement?.group &&
|
!dropElement?.group &&
|
||||||
@@ -2750,7 +2824,7 @@
|
@@ -2720,7 +2802,7 @@
|
||||||
|
// Dropping right before the tab group.
|
||||||
|
dropElement = dropElementGroup;
|
||||||
|
colorCode = undefined;
|
||||||
|
- } else if (dropElementGroup.collapsed) {
|
||||||
|
+ } else if (dropElement?.group?.hasAttribute("split-view-group")) {
|
||||||
|
// Dropping right after the collapsed tab group.
|
||||||
|
dropElement = dropElementGroup;
|
||||||
|
colorCode = undefined;
|
||||||
|
@@ -2750,7 +2832,7 @@
|
||||||
// Shift background tabs to leave a gap where the dragged tab
|
// Shift background tabs to leave a gap where the dragged tab
|
||||||
// would currently be dropped.
|
// would currently be dropped.
|
||||||
for (let item of tabs) {
|
for (let item of tabs) {
|
||||||
|
@ -431,7 +465,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2759,6 +2833,9 @@
|
@@ -2759,6 +2841,9 @@
|
||||||
if (isTabGroupLabel(item)) {
|
if (isTabGroupLabel(item)) {
|
||||||
// Shift the `.tab-group-label-container` to shift the label element.
|
// Shift the `.tab-group-label-container` to shift the label element.
|
||||||
item = item.parentElement;
|
item = item.parentElement;
|
||||||
|
@ -441,7 +475,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||||
}
|
}
|
||||||
item.style.transform = transform;
|
item.style.transform = transform;
|
||||||
}
|
}
|
||||||
@@ -2811,8 +2888,9 @@
|
@@ -2811,8 +2896,9 @@
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -453,7 +487,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2824,6 +2902,12 @@
|
@@ -2824,6 +2910,12 @@
|
||||||
item = item.parentElement;
|
item = item.parentElement;
|
||||||
}
|
}
|
||||||
item.style.transform = "";
|
item.style.transform = "";
|
||||||
|
@ -466,7 +500,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||||
item.removeAttribute("dragover-createGroup");
|
item.removeAttribute("dragover-createGroup");
|
||||||
}
|
}
|
||||||
this.removeAttribute("movingtab-createGroup");
|
this.removeAttribute("movingtab-createGroup");
|
||||||
@@ -2870,7 +2954,7 @@
|
@@ -2870,7 +2962,7 @@
|
||||||
let postTransitionCleanup = () => {
|
let postTransitionCleanup = () => {
|
||||||
movingTab._moveTogetherSelectedTabsData.animate = false;
|
movingTab._moveTogetherSelectedTabsData.animate = false;
|
||||||
};
|
};
|
||||||
|
@ -475,7 +509,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||||
postTransitionCleanup();
|
postTransitionCleanup();
|
||||||
} else {
|
} else {
|
||||||
let onTransitionEnd = transitionendEvent => {
|
let onTransitionEnd = transitionendEvent => {
|
||||||
@@ -3043,7 +3127,7 @@
|
@@ -3043,7 +3135,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
_notifyBackgroundTab(aTab) {
|
_notifyBackgroundTab(aTab) {
|
||||||
|
@ -484,7 +518,7 @@ index 84d633471c89230b981d8a07babef4e0c76c0338..1a9c56846ff27d68c16b939fb759ea95
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3169,6 +3253,9 @@
|
@@ -3169,6 +3261,9 @@
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
diff --git a/browser/components/urlbar/UrlbarValueFormatter.sys.mjs b/browser/components/urlbar/UrlbarValueFormatter.sys.mjs
|
||||||
|
index dfa91b76ad3890ceadb1b1b5d7a63b7074fbb776..6369fa1cdb242de32338bbce6debcdab2a04ca02 100644
|
||||||
|
--- a/browser/components/urlbar/UrlbarValueFormatter.sys.mjs
|
||||||
|
+++ b/browser/components/urlbar/UrlbarValueFormatter.sys.mjs
|
||||||
|
@@ -585,6 +585,7 @@ export class UrlbarValueFormatter {
|
||||||
|
this.window.requestAnimationFrame(() => {
|
||||||
|
if (instance == this._resizeInstance) {
|
||||||
|
this.#ensureFormattedHostVisible();
|
||||||
|
+ this._formatURL();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, 100);
|
|
@ -60,18 +60,6 @@
|
||||||
list-style-image: url('sidebars-right.svg') !important;
|
list-style-image: url('sidebars-right.svg') !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#context_zenSplitTabs {
|
|
||||||
--menu-image: url('sidebars-right.svg') !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-zen-change-workspace-tab {
|
|
||||||
--menu-image: url('move-tab.svg') !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-zenSplitLink {
|
|
||||||
--menu-image: url('split.svg') !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#sidebar-button:-moz-locale-dir(ltr):not([positionend]),
|
#sidebar-button:-moz-locale-dir(ltr):not([positionend]),
|
||||||
#sidebar-button:-moz-locale-dir(rtl)[positionend] {
|
#sidebar-button:-moz-locale-dir(rtl)[positionend] {
|
||||||
list-style-image: url('chrome://browser/skin/sidebars.svg') !important;
|
list-style-image: url('chrome://browser/skin/sidebars.svg') !important;
|
||||||
|
@ -133,6 +121,10 @@
|
||||||
list-style-image: url('tab.svg') !important;
|
list-style-image: url('tab.svg') !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#context-navigation > menuitem {
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
#history-panelmenu,
|
#history-panelmenu,
|
||||||
.urlbarView-row[source='history']
|
.urlbarView-row[source='history']
|
||||||
> .urlbarView-row-inner
|
> .urlbarView-row-inner
|
||||||
|
@ -173,15 +165,6 @@
|
||||||
list-style-image: url('open.svg') !important;
|
list-style-image: url('open.svg') !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#context_zenOpenWorkspace {
|
|
||||||
--menu-image: url('open.svg') !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#context_zenEditWorkspace,
|
|
||||||
#zenToolbarThemePicker {
|
|
||||||
--menu-image: url('edit-theme.svg') !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#add-ons-button,
|
#add-ons-button,
|
||||||
#appMenu-extensions-themes-button,
|
#appMenu-extensions-themes-button,
|
||||||
#unified-extensions-button {
|
#unified-extensions-button {
|
||||||
|
@ -303,6 +286,11 @@
|
||||||
list-style-image: url('home.svg') !important;
|
list-style-image: url('home.svg') !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#toggle_toolbar-menubar,
|
||||||
|
#appMenu_menu_openHelp {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
#library-button {
|
#library-button {
|
||||||
list-style-image: url('library.svg') !important;
|
list-style-image: url('library.svg') !important;
|
||||||
}
|
}
|
||||||
|
@ -447,6 +435,11 @@
|
||||||
|
|
||||||
#zen-glance-sidebar-split {
|
#zen-glance-sidebar-split {
|
||||||
list-style-image: url('split.svg');
|
list-style-image: url('split.svg');
|
||||||
|
|
||||||
|
&[disabled='true'] {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#sidebar-box[sidebarcommand='viewTabsSidebar']
|
#sidebar-box[sidebarcommand='viewTabsSidebar']
|
||||||
|
@ -635,371 +628,6 @@
|
||||||
list-style-image: url('manage.svg') !important;
|
list-style-image: url('manage.svg') !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Context Menu Icons */
|
|
||||||
|
|
||||||
#context-video-pictureinpicture:not([checked='true']) .menu-iconic-icon {
|
|
||||||
list-style-image: url('media-pip.svg') !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-media-loop:not([checked='true']) .menu-iconic-icon {
|
|
||||||
list-style-image: url('media-loop.svg') !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
:not(:not(menubar) > menu, #ContentSelectDropdown)
|
|
||||||
> menupopup
|
|
||||||
> menuitem:not(
|
|
||||||
.menuitem-iconic,
|
|
||||||
[type='checkbox'],
|
|
||||||
[type='radio'],
|
|
||||||
.in-menulist,
|
|
||||||
.in-menulist menuitem,
|
|
||||||
.unified-nav-current
|
|
||||||
),
|
|
||||||
:not(:not(menubar) > menu, #ContentSelectDropdown)
|
|
||||||
> menupopup
|
|
||||||
> menu:not(
|
|
||||||
.menu-iconic,
|
|
||||||
[type='checkbox'],
|
|
||||||
[type='radio'],
|
|
||||||
.in-menulist,
|
|
||||||
.in-menulist menu,
|
|
||||||
.unified-nav-current
|
|
||||||
),
|
|
||||||
#toggle_toolbar-menubar,
|
|
||||||
#PanelUI-history toolbarbutton,
|
|
||||||
#unified-extensions-context-menu menuitem {
|
|
||||||
background-image: var(--menu-image) !important;
|
|
||||||
background-size: 16px !important;
|
|
||||||
background-position: var(--zen-contextmenu-menuitem-padding-inline) center !important;
|
|
||||||
background-repeat: no-repeat !important;
|
|
||||||
-moz-context-properties: fill, fill-opacity !important;
|
|
||||||
fill: currentColor !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media not (-moz-platform: windows) {
|
|
||||||
menu > .menu-iconic-text,
|
|
||||||
menuitem > .menu-iconic-text {
|
|
||||||
padding-inline-start: var(--zen-contextmenu-menuicon-margin-inline) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-savepage {
|
|
||||||
--menu-image: url('save.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-selectall,
|
|
||||||
.textbox-contextmenu menuitem[cmd*='selectAll'],
|
|
||||||
#context_selectAllTabs,
|
|
||||||
#toolbar-context-selectAllTabs {
|
|
||||||
--menu-image: url('edit-select-all.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-undo,
|
|
||||||
.textbox-contextmenu menuitem[cmd*='undo'],
|
|
||||||
#context_undoCloseTab,
|
|
||||||
#toolbar-context-undoCloseTab {
|
|
||||||
--menu-image: url('edit-undo.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#toggle_toolbar-menubar {
|
|
||||||
--menu-image: url('menu-bar.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-redo,
|
|
||||||
.textbox-contextmenu menuitem[cmd*='redo'] {
|
|
||||||
--menu-image: url('edit-redo.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-copy,
|
|
||||||
.textbox-contextmenu menuitem[cmd*='copy'],
|
|
||||||
.textbox-contextmenu #strip-on-share,
|
|
||||||
#placesContext_copy {
|
|
||||||
--menu-image: url('edit-copy.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-paste,
|
|
||||||
.textbox-contextmenu menuitem[cmd*='paste'],
|
|
||||||
#placesContext_paste_group {
|
|
||||||
--menu-image: url('edit-paste.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-cut,
|
|
||||||
.textbox-contextmenu menuitem[cmd*='cut'],
|
|
||||||
#placesContext_cut {
|
|
||||||
--menu-image: url('edit-cut.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-delete,
|
|
||||||
.customize-context-removeExtension,
|
|
||||||
.unified-extensions-context-menu-remove-extension,
|
|
||||||
.textbox-contextmenu menuitem[cmd*='delete'],
|
|
||||||
menuitem[id='placesContext_deleteBookmark'],
|
|
||||||
menuitem[id='placesContext_deleteFolder'],
|
|
||||||
menuitem[id='placesContext_delete'],
|
|
||||||
menuitem[id='placesContext_delete_history'],
|
|
||||||
menuitem[id='placesContext_deleteHost'],
|
|
||||||
#context_zenDeleteWebPanel,
|
|
||||||
#context_zenDeleteWorkspace {
|
|
||||||
--menu-image: url('edit-delete.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#paste-and-go {
|
|
||||||
--menu-image: url('paste-and-go.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-print-selection {
|
|
||||||
--menu-image: url('print.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-take-screenshot {
|
|
||||||
--menu-image: url('screenshot.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-viewsource {
|
|
||||||
--menu-image: url('source-code.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-inspect-a11y {
|
|
||||||
--menu-image: url('accessibility.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-inspect {
|
|
||||||
--menu-image: url('inspect.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-searchselect {
|
|
||||||
--menu-image: url('search-glass.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-viewimage {
|
|
||||||
--menu-image: url('image-open.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-viewimageinfo {
|
|
||||||
--menu-image: url('info.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-saveimage,
|
|
||||||
#context-video-saveimage {
|
|
||||||
--menu-image: url('image-save.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-savevideo {
|
|
||||||
--menu-image: url('video-save.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-viewvideo {
|
|
||||||
--menu-image: url('video-open.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-saveaudio {
|
|
||||||
--menu-image: url('audio-save.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-copyimage-contents {
|
|
||||||
--menu-image: url('image-copy.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-copyimage,
|
|
||||||
#context-copyvideourl,
|
|
||||||
#context-copylink,
|
|
||||||
#context-stripOnShareLink,
|
|
||||||
#context_zenOpenNewTabWebPanel,
|
|
||||||
#context-pdfjs-copy {
|
|
||||||
--menu-image: url('link.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-openlinkincurrent {
|
|
||||||
--menu-image: url('ext-link.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-viewsource,
|
|
||||||
#context-viewframesource,
|
|
||||||
#context-viewpartialsource-selection {
|
|
||||||
--menu-image: url('source-code.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-sendimage,
|
|
||||||
#context-sendvideo,
|
|
||||||
#context-sendaudio {
|
|
||||||
--menu-image: url('mail.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-setDesktopBackground,
|
|
||||||
.viewCustomizeToolbar {
|
|
||||||
--menu-image: url('customize.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-reloadimage,
|
|
||||||
#context_reloadTab,
|
|
||||||
#context_reloadSelectedTabs,
|
|
||||||
#toolbar-context-reloadSelectedTab,
|
|
||||||
#toolbar-context-reloadSelectedTabs,
|
|
||||||
#context_zen-reset-pinned-tab {
|
|
||||||
--menu-image: url('reload.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-sendlinktodevice,
|
|
||||||
#context_sendTabToDevice,
|
|
||||||
#context-sendpagetodevice {
|
|
||||||
--menu-image: url('send-to-device.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-openlinkintab,
|
|
||||||
#context-openlinkincontainertab,
|
|
||||||
#context_zenWorkspacesOpenInContainerTab,
|
|
||||||
#context_zenWebPanelContextInContainer,
|
|
||||||
menuitem[id='placesContext_open:newtab'],
|
|
||||||
menuitem[id='placesContext_openLinks:tabs'],
|
|
||||||
menuitem[id='placesContext_openBookmarkLinks:tabs'],
|
|
||||||
menuitem[id='placesContext_openBookmarkContainer:tabs'] {
|
|
||||||
--menu-image: url('tab.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context_openANewTab,
|
|
||||||
#toolbar-context-openANewTab {
|
|
||||||
--menu-image: url('new-tab-image.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-openlinkinusercontext-menu,
|
|
||||||
menu[id='placesContext_open:newcontainertab'],
|
|
||||||
menu[id='placesContext_openContainer:tabs'] {
|
|
||||||
--menu-image: url('container-tab.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-openlink,
|
|
||||||
menuitem[id='placesContext_open:newwindow'] {
|
|
||||||
--menu-image: url('window.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-openlinkprivate,
|
|
||||||
menuitem[id='placesContext_open:newprivatewindow'] {
|
|
||||||
--menu-image: url('private-window.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-savelink {
|
|
||||||
--menu-image: url('downloads.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#spell-add-to-dictionary {
|
|
||||||
--menu-image: url('add-to-dictionary.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#manage-saved-logins {
|
|
||||||
--menu-image: url('passwords.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-media-play,
|
|
||||||
#context_playTab,
|
|
||||||
#context_playSelectedTabs {
|
|
||||||
--menu-image: url('media-play.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-media-pause {
|
|
||||||
--menu-image: url('media-pause.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-media-mute,
|
|
||||||
#context_toggleMuteTab,
|
|
||||||
#context_toggleMuteSelectedTabs,
|
|
||||||
#context_zenToggleMuteWebPanel {
|
|
||||||
--menu-image: url('media-mute.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-media-unmute,
|
|
||||||
#context_toggleMuteTab[muted],
|
|
||||||
#context_toggleMuteSelectedTabs[muted],
|
|
||||||
#context_zenToggleMuteWebPanel[muted] {
|
|
||||||
--menu-image: url('media-unmute.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-media-playbackrate {
|
|
||||||
--menu-image: url('media-speed.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-video-fullscreen {
|
|
||||||
--menu-image: url('fullscreen.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-leave-dom-fullscreen,
|
|
||||||
menuitem[contexttype='fullscreen'][label*='Exit'] {
|
|
||||||
--menu-image: url('fullscreen-exit.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-media-hidecontrols,
|
|
||||||
#context-media-showcontrols {
|
|
||||||
--menu-image: url('permissions.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context_pinTab,
|
|
||||||
#context_unpinTab,
|
|
||||||
#context_pinSelectedTabs,
|
|
||||||
#context_unpinSelectedTabs,
|
|
||||||
.customize-context-moveToPanel,
|
|
||||||
#context_zen-replace-pinned-url-with-current {
|
|
||||||
--menu-image: url('pin.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context_zen-add-essential {
|
|
||||||
--menu-image: url('essential-add.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context_zen-remove-essential {
|
|
||||||
--menu-image: url('essential-remove.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.customize-context-removeFromToolbar {
|
|
||||||
--menu-image: url('unpin.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#zen-sidebar-web-panel-pinned[pinned='true'] {
|
|
||||||
list-style-image: url('pin.svg') !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#zen-sidebar-web-panel-pinned {
|
|
||||||
list-style-image: url('unpin.svg') !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
#context_duplicateTab,
|
|
||||||
#context_duplicateTabs {
|
|
||||||
--menu-image: url('duplicate-tab.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#zen-context-menu-compact-mode {
|
|
||||||
--menu-image: url('sidebar.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context_bookmarkTab,
|
|
||||||
#context_bookmarkSelectedTabs,
|
|
||||||
#toggle_PersonalToolbar,
|
|
||||||
#context-bookmarklink,
|
|
||||||
#toolbar-context-bookmarkSelectedTab,
|
|
||||||
#toolbar-context-bookmarkSelectedTabs {
|
|
||||||
--menu-image: url('bookmark-hollow.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
menuitem[id='placesContext_show_bookmark:info'],
|
|
||||||
menuitem[id='placesContext_show_folder:info'],
|
|
||||||
menuitem[id='placesContext_show:info'] {
|
|
||||||
--menu-image: url('edit.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
menuitem[id='placesContext_showAllBookmarks'],
|
|
||||||
#BMB_bookmarksShowAllTop,
|
|
||||||
#BMB_bookmarksShowAll,
|
|
||||||
.customize-context-manageExtension,
|
|
||||||
.unified-extensions-context-menu-manage-extension {
|
|
||||||
--menu-image: url('manage.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#BMB_viewBookmarksSidebar {
|
|
||||||
--menu-image: url('chrome://browser/skin/sidebars.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#BMB_searchBookmarks {
|
|
||||||
--menu-image: url('search-page.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#appMenuRecentlyClosedTabs {
|
#appMenuRecentlyClosedTabs {
|
||||||
list-style-image: url('container-tab.svg') !important;
|
list-style-image: url('container-tab.svg') !important;
|
||||||
}
|
}
|
||||||
|
@ -1020,57 +648,12 @@ menuitem[id='placesContext_showAllBookmarks'],
|
||||||
list-style-image: url('manage.svg') !important;
|
list-style-image: url('manage.svg') !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
menuitem[id='placesContext_new:bookmark'],
|
|
||||||
menuitem[id='placesContext_new:folder'],
|
|
||||||
menuitem[id='placesContext_new:separator'] {
|
|
||||||
--menu-image: url('plus.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context-savelinktopocket,
|
|
||||||
#context-pocket {
|
|
||||||
--menu-image: url('pocket-outline.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context_moveTabOptions {
|
|
||||||
--menu-image: url('move-tab.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.share-tab-url-item {
|
|
||||||
--menu-image: url('share.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context_reopenInContainer {
|
|
||||||
--menu-image: url('container-tab.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context_closeTab {
|
|
||||||
--menu-image: url('close.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context_closeTabOptions {
|
|
||||||
--menu-image: url('close-all.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#context_unloadTab,
|
|
||||||
#context_zenTabActions {
|
|
||||||
--menu-image: url('close-all.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.customize-context-reportExtension,
|
|
||||||
.unified-extensions-context-menu-report-extension {
|
|
||||||
--menu-image: url('report.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
/* FIX header icons for the app menu sub menus (eg. fx account, history...) */
|
/* FIX header icons for the app menu sub menus (eg. fx account, history...) */
|
||||||
.panel-header > h1 {
|
.panel-header > h1 {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
margin-left: 8px !important;
|
margin-left: 8px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wordmark::after {
|
|
||||||
content: 'Plus' !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* header icons for the app menu sub menus (eg. fx account, history...) */
|
/* header icons for the app menu sub menus (eg. fx account, history...) */
|
||||||
.panel-header > h1 > span::before {
|
.panel-header > h1 > span::before {
|
||||||
content: '';
|
content: '';
|
||||||
|
@ -1115,67 +698,10 @@ menuitem[id='placesContext_new:separator'] {
|
||||||
--fp-enabled: 1;
|
--fp-enabled: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media not (-moz-platform: linux) {
|
|
||||||
.unified-extensions-context-menu-pin-to-toolbar {
|
|
||||||
--menu-image: url('pin.svg');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.unified-extensions-context-menu-move-widget-down {
|
|
||||||
--menu-image: url('arrow-down.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
.unified-extensions-context-menu-move-widget-up {
|
|
||||||
--menu-image: url('arrow-up.svg');
|
|
||||||
}
|
|
||||||
|
|
||||||
#alltabs-button {
|
#alltabs-button {
|
||||||
list-style-image: url('chrome://browser/skin/tabs.svg') !important;
|
list-style-image: url('chrome://browser/skin/tabs.svg') !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
:not(:not(menubar) > menu, #ContentSelectDropdown)
|
|
||||||
> menupopup
|
|
||||||
> menuitem:not(
|
|
||||||
.menuitem-iconic,
|
|
||||||
[type='checkbox'],
|
|
||||||
[type='radio'],
|
|
||||||
.in-menulist,
|
|
||||||
.in-menulist menuitem,
|
|
||||||
.unified-nav-current
|
|
||||||
),
|
|
||||||
:not(:not(menubar) > menu, #ContentSelectDropdown)
|
|
||||||
> menupopup
|
|
||||||
> menu:not(
|
|
||||||
.menu-iconic,
|
|
||||||
[type='checkbox'],
|
|
||||||
[type='radio'],
|
|
||||||
.in-menulist,
|
|
||||||
.in-menulist menu,
|
|
||||||
.unified-nav-current
|
|
||||||
),
|
|
||||||
:not(:not(menubar) > menu, #ContentSelectDropdown) > menupopup > menucaption {
|
|
||||||
padding-inline-start: calc(
|
|
||||||
var(--zen-contextmenu-menuitem-padding-inline) + var(--zen-contextmenu-menuicon-margin-inline) /
|
|
||||||
2
|
|
||||||
) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
menupopup > menuitem:is([type='checkbox']) .menu-iconic-left {
|
|
||||||
--menu-image: none !important;
|
|
||||||
margin-inline-start: 4px;
|
|
||||||
|
|
||||||
@media not (-moz-platform: windows) {
|
|
||||||
margin-inline-end: 0;
|
|
||||||
padding-inline-end: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (-moz-platform: windows) {
|
|
||||||
menupopup > menuitem[checked='true'] {
|
|
||||||
padding-inline-start: 6px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#toolbar-context-toggle-vertical-tabs,
|
#toolbar-context-toggle-vertical-tabs,
|
||||||
#toolbar-context-customize-sidebar,
|
#toolbar-context-customize-sidebar,
|
||||||
#sidebarRevampSeparator {
|
#sidebarRevampSeparator {
|
||||||
|
|
3
src/zen/@types/lib.gecko.darwin.d.ts
vendored
3
src/zen/@types/lib.gecko.darwin.d.ts
vendored
|
@ -1,3 +1,6 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
/**
|
/**
|
||||||
* NOTE: Do not modify this file by hand.
|
* NOTE: Do not modify this file by hand.
|
||||||
* Content was generated from source XPCOM .idl files.
|
* Content was generated from source XPCOM .idl files.
|
||||||
|
|
3
src/zen/@types/lib.gecko.dom.d.ts
vendored
3
src/zen/@types/lib.gecko.dom.d.ts
vendored
|
@ -1,3 +1,6 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
/**
|
/**
|
||||||
* NOTE: Do not modify this file by hand.
|
* NOTE: Do not modify this file by hand.
|
||||||
* Content was generated from source .webidl files.
|
* Content was generated from source .webidl files.
|
||||||
|
|
3
src/zen/@types/lib.gecko.glean.d.ts
vendored
3
src/zen/@types/lib.gecko.glean.d.ts
vendored
|
@ -1,3 +1,6 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
/**
|
/**
|
||||||
* NOTE: Do not modify this file by hand.
|
* NOTE: Do not modify this file by hand.
|
||||||
* Content was generated from source metrics.yaml files.
|
* Content was generated from source metrics.yaml files.
|
||||||
|
|
3
src/zen/@types/lib.gecko.linux.d.ts
vendored
3
src/zen/@types/lib.gecko.linux.d.ts
vendored
|
@ -1,3 +1,6 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
/**
|
/**
|
||||||
* NOTE: Do not modify this file by hand.
|
* NOTE: Do not modify this file by hand.
|
||||||
* Content was generated from source XPCOM .idl files.
|
* Content was generated from source XPCOM .idl files.
|
||||||
|
|
3
src/zen/@types/lib.gecko.modules.d.ts
vendored
3
src/zen/@types/lib.gecko.modules.d.ts
vendored
|
@ -1,3 +1,6 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
/**
|
/**
|
||||||
* NOTE: Do not modify this file by hand.
|
* NOTE: Do not modify this file by hand.
|
||||||
* Content was generated by running "mach ts paths".
|
* Content was generated by running "mach ts paths".
|
||||||
|
|
3
src/zen/@types/lib.gecko.nsresult.d.ts
vendored
3
src/zen/@types/lib.gecko.nsresult.d.ts
vendored
|
@ -1,3 +1,6 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
/**
|
/**
|
||||||
* NOTE: Do not modify this file by hand.
|
* NOTE: Do not modify this file by hand.
|
||||||
* Content was generated from xpc.msg and error_list.json.
|
* Content was generated from xpc.msg and error_list.json.
|
||||||
|
|
3
src/zen/@types/lib.gecko.services.d.ts
vendored
3
src/zen/@types/lib.gecko.services.d.ts
vendored
|
@ -1,3 +1,6 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
/**
|
/**
|
||||||
* NOTE: Do not modify this file by hand.
|
* NOTE: Do not modify this file by hand.
|
||||||
* Content was generated from services.json.
|
* Content was generated from services.json.
|
||||||
|
|
3
src/zen/@types/lib.gecko.tweaks.d.ts
vendored
3
src/zen/@types/lib.gecko.tweaks.d.ts
vendored
|
@ -1,3 +1,6 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
/**
|
/**
|
||||||
* Gecko generic/specialized adjustments for xpcom and webidl types.
|
* Gecko generic/specialized adjustments for xpcom and webidl types.
|
||||||
*/
|
*/
|
||||||
|
|
3
src/zen/@types/lib.gecko.win32.d.ts
vendored
3
src/zen/@types/lib.gecko.win32.d.ts
vendored
|
@ -1,3 +1,6 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
/**
|
/**
|
||||||
* NOTE: Do not modify this file by hand.
|
* NOTE: Do not modify this file by hand.
|
||||||
* Content was generated from source XPCOM .idl files.
|
* Content was generated from source XPCOM .idl files.
|
||||||
|
|
3
src/zen/@types/lib.gecko.xpcom.d.ts
vendored
3
src/zen/@types/lib.gecko.xpcom.d.ts
vendored
|
@ -1,3 +1,6 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
/**
|
/**
|
||||||
* NOTE: Do not modify this file by hand.
|
* NOTE: Do not modify this file by hand.
|
||||||
* Content was generated from source XPCOM .idl files.
|
* Content was generated from source XPCOM .idl files.
|
||||||
|
|
3
src/zen/@types/lib.gecko.xpidl.d.ts
vendored
3
src/zen/@types/lib.gecko.xpidl.d.ts
vendored
|
@ -1,3 +1,6 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
/**
|
/**
|
||||||
* Gecko XPIDL base types.
|
* Gecko XPIDL base types.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,16 +1,32 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
// Utility to register JSWindowActors
|
// Utility to register JSWindowActors
|
||||||
|
|
||||||
var gZenActorsManager = {
|
var gZenActorsManager = {
|
||||||
_actors: new Set(),
|
_actors: new Set(),
|
||||||
|
_lazy: {},
|
||||||
|
|
||||||
addJSWindowActor(...args) {
|
init() {
|
||||||
if (this._actors.has(args[0])) {
|
ChromeUtils.defineESModuleGetters(this._lazy, {
|
||||||
|
ActorManagerParent: 'resource://gre/modules/ActorManagerParent.sys.mjs',
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
addJSWindowActor(name, data) {
|
||||||
|
if (!this._lazy.ActorManagerParent) {
|
||||||
|
this.init();
|
||||||
|
}
|
||||||
|
if (this._actors.has(name)) {
|
||||||
// Actor already registered, nothing to do
|
// Actor already registered, nothing to do
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const decl = {};
|
||||||
|
decl[name] = data;
|
||||||
try {
|
try {
|
||||||
ChromeUtils.registerWindowActor(...args);
|
this._lazy.ActorManagerParent.addJSWindowActors(decl);
|
||||||
this._actors.add(args[0]);
|
this._actors.add(name);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(`Failed to register JSWindowActor: ${e}`);
|
console.warn(`Failed to register JSWindowActor: ${e}`);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
var gZenOperatingSystemCommonUtils = {
|
var gZenOperatingSystemCommonUtils = {
|
||||||
kZenOSToSmallName: {
|
kZenOSToSmallName: {
|
||||||
WINNT: 'windows',
|
WINNT: 'windows',
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import { AppConstants } from 'resource://gre/modules/AppConstants.sys.mjs';
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
export var ZenCustomizableUI = new (class {
|
export var ZenCustomizableUI = new (class {
|
||||||
constructor() {}
|
constructor() {}
|
||||||
|
|
File diff suppressed because one or more lines are too long
50
src/zen/common/ZenSessionStore.mjs
Normal file
50
src/zen/common/ZenSessionStore.mjs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
{
|
||||||
|
class ZenSessionStore extends ZenPreloadedFeature {
|
||||||
|
init() {
|
||||||
|
this.#waitAndCleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
promiseInitialized = new Promise((resolve) => {
|
||||||
|
this._resolveInitialized = resolve;
|
||||||
|
});
|
||||||
|
|
||||||
|
restoreInitialTabData(tab, tabData) {
|
||||||
|
if (tabData.zenWorkspace) {
|
||||||
|
tab.setAttribute('zen-workspace-id', tabData.zenWorkspace);
|
||||||
|
}
|
||||||
|
if (tabData.zenPinnedId) {
|
||||||
|
tab.setAttribute('zen-pin-id', tabData.zenPinnedId);
|
||||||
|
}
|
||||||
|
if (tabData.zenIsEmpty) {
|
||||||
|
tab.setAttribute('zen-empty-tab', 'true');
|
||||||
|
}
|
||||||
|
if (tabData.zenHasStaticLabel) {
|
||||||
|
tab.setAttribute('zen-has-static-label', 'true');
|
||||||
|
}
|
||||||
|
if (tabData.zenEssential) {
|
||||||
|
tab.setAttribute('zen-essential', 'true');
|
||||||
|
}
|
||||||
|
if (tabData.zenDefaultUserContextId) {
|
||||||
|
tab.setAttribute('zenDefaultUserContextId', 'true');
|
||||||
|
}
|
||||||
|
if (tabData.zenPinnedEntry) {
|
||||||
|
tab.setAttribute('zen-pinned-entry', tabData.zenPinnedEntry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async #waitAndCleanup() {
|
||||||
|
await SessionStore.promiseAllWindowsRestored;
|
||||||
|
await SessionStore.promiseInitialized;
|
||||||
|
this.#cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
#cleanup() {
|
||||||
|
this._resolveInitialized();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.gZenSessionStore = new ZenSessionStore();
|
||||||
|
}
|
|
@ -1,3 +1,6 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
{
|
{
|
||||||
var ZenStartup = {
|
var ZenStartup = {
|
||||||
init() {
|
init() {
|
||||||
|
|
|
@ -1,9 +1,14 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
var gZenUIManager = {
|
var gZenUIManager = {
|
||||||
_popupTrackingElements: [],
|
_popupTrackingElements: [],
|
||||||
_hoverPausedForExpand: false,
|
_hoverPausedForExpand: false,
|
||||||
_hasLoadedDOM: false,
|
_hasLoadedDOM: false,
|
||||||
testingEnabled: Services.prefs.getBoolPref('zen.testing.enabled', false),
|
testingEnabled: Services.prefs.getBoolPref('zen.testing.enabled', false),
|
||||||
|
|
||||||
|
_lastClickPosition: null,
|
||||||
|
|
||||||
_toastTimeouts: [],
|
_toastTimeouts: [],
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
|
@ -31,6 +36,8 @@ var gZenUIManager = {
|
||||||
|
|
||||||
gURLBar._zenTrimURL = this.urlbarTrim.bind(this);
|
gURLBar._zenTrimURL = this.urlbarTrim.bind(this);
|
||||||
|
|
||||||
|
document.addEventListener('mousedown', this.handleMouseDown.bind(this), true);
|
||||||
|
|
||||||
ChromeUtils.defineLazyGetter(this, 'motion', () => {
|
ChromeUtils.defineLazyGetter(this, 'motion', () => {
|
||||||
return ChromeUtils.importESModule('chrome://browser/content/zen-vendor/motion.min.mjs', {
|
return ChromeUtils.importESModule('chrome://browser/content/zen-vendor/motion.min.mjs', {
|
||||||
global: 'current',
|
global: 'current',
|
||||||
|
@ -62,6 +69,13 @@ var gZenUIManager = {
|
||||||
gZenMediaController.init();
|
gZenMediaController.init();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
handleMouseDown(event) {
|
||||||
|
this._lastClickPosition = {
|
||||||
|
clientX: event.clientX,
|
||||||
|
clientY: event.clientY,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
updateTabsToolbar() {
|
updateTabsToolbar() {
|
||||||
const kUrlbarHeight = 440;
|
const kUrlbarHeight = 440;
|
||||||
gURLBar.textbox.style.setProperty(
|
gURLBar.textbox.style.setProperty(
|
||||||
|
@ -70,7 +84,6 @@ var gZenUIManager = {
|
||||||
);
|
);
|
||||||
gZenVerticalTabsManager.actualWindowButtons.removeAttribute('zen-has-hover');
|
gZenVerticalTabsManager.actualWindowButtons.removeAttribute('zen-has-hover');
|
||||||
gZenVerticalTabsManager.recalculateURLBarHeight();
|
gZenVerticalTabsManager.recalculateURLBarHeight();
|
||||||
setTimeout(gURLBar.formatValue.bind(gURLBar), 350);
|
|
||||||
if (!this._preventToolbarRebuild) {
|
if (!this._preventToolbarRebuild) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
gZenWorkspaces.updateTabsContainers();
|
gZenWorkspaces.updateTabsContainers();
|
||||||
|
@ -989,43 +1002,54 @@ var gZenVerticalTabsManager = {
|
||||||
async renameTabKeydown(event) {
|
async renameTabKeydown(event) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
if (event.key === 'Enter') {
|
if (event.key === 'Enter') {
|
||||||
let label = this._tabEdited.querySelector('.tab-label-container-editing');
|
const isTab = !!event.target.closest('.tabbrowser-tab');
|
||||||
let input = this._tabEdited.querySelector('#tab-label-input');
|
let label = isTab
|
||||||
|
? this._tabEdited.querySelector('.tab-label-container-editing')
|
||||||
|
: this._tabEdited;
|
||||||
|
let input = document.getElementById('tab-label-input');
|
||||||
let newName = input.value.trim();
|
let newName = input.value.trim();
|
||||||
|
|
||||||
// Check if name is blank, reset if so
|
document.documentElement.removeAttribute('zen-renaming-tab');
|
||||||
// Always remove, so we can always rename and if it's empty,
|
input.remove();
|
||||||
// it will reset to the original name anyway
|
if (!isTab) {
|
||||||
this._tabEdited.removeAttribute('zen-has-static-label');
|
await this._tabEdited.onRenameFinished(newName);
|
||||||
if (newName) {
|
|
||||||
gBrowser._setTabLabel(this._tabEdited, newName);
|
|
||||||
this._tabEdited.setAttribute('zen-has-static-label', 'true');
|
|
||||||
gZenUIManager.showToast('zen-tabs-renamed');
|
|
||||||
} else {
|
} else {
|
||||||
gBrowser.setTabTitle(this._tabEdited);
|
// Check if name is blank, reset if so
|
||||||
}
|
// Always remove, so we can always rename and if it's empty,
|
||||||
if (this._tabEdited.getAttribute('zen-pin-id')) {
|
// it will reset to the original name anyway
|
||||||
// Update pin title in storage
|
this._tabEdited.removeAttribute('zen-has-static-label');
|
||||||
await gZenPinnedTabManager.updatePinTitle(
|
if (newName) {
|
||||||
|
gBrowser._setTabLabel(this._tabEdited, newName);
|
||||||
|
this._tabEdited.setAttribute('zen-has-static-label', 'true');
|
||||||
|
gZenUIManager.showToast('zen-tabs-renamed');
|
||||||
|
} else {
|
||||||
|
gBrowser.setTabTitle(this._tabEdited);
|
||||||
|
}
|
||||||
|
if (this._tabEdited.getAttribute('zen-pin-id')) {
|
||||||
|
// Update pin title in storage
|
||||||
|
await gZenPinnedTabManager.updatePinTitle(
|
||||||
|
this._tabEdited,
|
||||||
|
this._tabEdited.label,
|
||||||
|
!!newName
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maybe add some confetti here?!?
|
||||||
|
gZenUIManager.motion.animate(
|
||||||
this._tabEdited,
|
this._tabEdited,
|
||||||
this._tabEdited.label,
|
{
|
||||||
!!newName
|
scale: [1, 0.98, 1],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
duration: 0.25,
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
document.documentElement.removeAttribute('zen-renaming-tab');
|
|
||||||
|
|
||||||
// Maybe add some confetti here?!?
|
const editorContainer = this._tabEdited.querySelector('.tab-editor-container');
|
||||||
gZenUIManager.motion.animate(
|
if (editorContainer) {
|
||||||
this._tabEdited,
|
editorContainer.remove();
|
||||||
{
|
}
|
||||||
scale: [1, 0.98, 1],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
duration: 0.25,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
this._tabEdited.querySelector('.tab-editor-container').remove();
|
|
||||||
label.classList.remove('tab-label-container-editing');
|
label.classList.remove('tab-label-container-editing');
|
||||||
|
|
||||||
this._tabEdited = null;
|
this._tabEdited = null;
|
||||||
|
@ -1035,34 +1059,40 @@ var gZenVerticalTabsManager = {
|
||||||
},
|
},
|
||||||
|
|
||||||
renameTabStart(event) {
|
renameTabStart(event) {
|
||||||
|
const isTab = !!event.target.closest('.tabbrowser-tab');
|
||||||
if (
|
if (
|
||||||
this._tabEdited ||
|
this._tabEdited ||
|
||||||
!Services.prefs.getBoolPref('zen.tabs.rename-tabs') ||
|
((!Services.prefs.getBoolPref('zen.tabs.rename-tabs') ||
|
||||||
Services.prefs.getBoolPref('browser.tabs.closeTabByDblclick') ||
|
Services.prefs.getBoolPref('browser.tabs.closeTabByDblclick')) &&
|
||||||
|
isTab) ||
|
||||||
!gZenVerticalTabsManager._prefsSidebarExpanded
|
!gZenVerticalTabsManager._prefsSidebarExpanded
|
||||||
)
|
)
|
||||||
return;
|
return;
|
||||||
this._tabEdited = event.target.closest('.tabbrowser-tab');
|
this._tabEdited = event.target.closest('.tabbrowser-tab');
|
||||||
if (
|
if (
|
||||||
!this._tabEdited ||
|
!this._tabEdited ||
|
||||||
!this._tabEdited.pinned ||
|
((!this._tabEdited.pinned || this._tabEdited.hasAttribute('zen-essential')) && isTab)
|
||||||
this._tabEdited.hasAttribute('zen-essential')
|
|
||||||
) {
|
) {
|
||||||
this._tabEdited = null;
|
this._tabEdited = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
event.stopPropagation();
|
||||||
document.documentElement.setAttribute('zen-renaming-tab', 'true');
|
document.documentElement.setAttribute('zen-renaming-tab', 'true');
|
||||||
const label = this._tabEdited.querySelector('.tab-label-container');
|
const label = isTab ? this._tabEdited.querySelector('.tab-label-container') : this._tabEdited;
|
||||||
label.classList.add('tab-label-container-editing');
|
label.classList.add('tab-label-container-editing');
|
||||||
|
|
||||||
const container = window.MozXULElement.parseXULToFragment(`
|
if (isTab) {
|
||||||
<vbox class="tab-label-container tab-editor-container" flex="1" align="start" pack="center"></vbox>
|
const container = window.MozXULElement.parseXULToFragment(`
|
||||||
`);
|
<vbox class="tab-label-container tab-editor-container" flex="1" align="start" pack="center"></vbox>
|
||||||
label.after(container);
|
`);
|
||||||
const containerHtml = this._tabEdited.querySelector('.tab-editor-container');
|
label.after(container);
|
||||||
|
}
|
||||||
|
const containerHtml = isTab
|
||||||
|
? this._tabEdited.querySelector('.tab-editor-container')
|
||||||
|
: this._tabEdited.parentNode;
|
||||||
const input = document.createElement('input');
|
const input = document.createElement('input');
|
||||||
input.id = 'tab-label-input';
|
input.id = 'tab-label-input';
|
||||||
input.value = this._tabEdited.label;
|
input.value = isTab ? this._tabEdited.label : this._tabEdited.textContent;
|
||||||
input.addEventListener('keydown', this.renameTabKeydown.bind(this));
|
input.addEventListener('keydown', this.renameTabKeydown.bind(this));
|
||||||
|
|
||||||
containerHtml.appendChild(input);
|
containerHtml.appendChild(input);
|
||||||
|
@ -1077,8 +1107,16 @@ var gZenVerticalTabsManager = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
document.documentElement.removeAttribute('zen-renaming-tab');
|
document.documentElement.removeAttribute('zen-renaming-tab');
|
||||||
this._tabEdited.querySelector('.tab-editor-container').remove();
|
const editorContainer = this._tabEdited.querySelector('.tab-editor-container');
|
||||||
const label = this._tabEdited.querySelector('.tab-label-container-editing');
|
let input = document.getElementById('tab-label-input');
|
||||||
|
input.remove();
|
||||||
|
if (editorContainer) {
|
||||||
|
editorContainer.remove();
|
||||||
|
}
|
||||||
|
const isTab = !!this._tabEdited.closest('.tabbrowser-tab');
|
||||||
|
const label = isTab
|
||||||
|
? this._tabEdited.querySelector('.tab-label-container-editing')
|
||||||
|
: this._tabEdited;
|
||||||
label.classList.remove('tab-label-container-editing');
|
label.classList.remove('tab-label-container-editing');
|
||||||
|
|
||||||
this._tabEdited = null;
|
this._tabEdited = null;
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
const lazy = {};
|
const lazy = {};
|
||||||
|
|
||||||
ChromeUtils.defineESModuleGetters(lazy, {
|
ChromeUtils.defineESModuleGetters(lazy, {
|
||||||
|
@ -87,6 +90,9 @@ class ZenUIMigration {
|
||||||
}
|
}
|
||||||
|
|
||||||
_migrateV4(win) {
|
_migrateV4(win) {
|
||||||
|
if (AppConstants.platform === 'linux') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
Services.prefs.setBoolPref(
|
Services.prefs.setBoolPref(
|
||||||
'browser.tabs.unloadOnLowMemory',
|
'browser.tabs.unloadOnLowMemory',
|
||||||
Services.prefs.getBoolPref('zen.tab-unloader.enabled', true)
|
Services.prefs.getBoolPref('zen.tab-unloader.enabled', true)
|
||||||
|
|
|
@ -78,16 +78,6 @@
|
||||||
transition: background-color var(--inactive-window-transition);
|
transition: background-color var(--inactive-window-transition);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (not (-moz-windows-mica)) and -moz-pref('zen.view.grey-out-inactive-windows') {
|
|
||||||
transition: color var(--inactive-window-transition);
|
|
||||||
:root:not([zen-welcome-stage]) &:-moz-window-inactive {
|
|
||||||
color: var(--toolbox-textcolor-inactive);
|
|
||||||
&::before {
|
|
||||||
background-color: var(--toolbox-bgcolor-inactive);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#zen-browser-grain {
|
#zen-browser-grain {
|
||||||
content: '';
|
content: '';
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
@ -131,9 +121,7 @@
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (-moz-windows-accent-color-in-titlebar) and (-moz-windows-mica) {
|
@media -moz-pref('zen.view.grey-out-inactive-windows') {
|
||||||
background-color: ActiveCaption;
|
|
||||||
color: CaptionText;
|
|
||||||
transition: background-color var(--inactive-window-transition);
|
transition: background-color var(--inactive-window-transition);
|
||||||
&:-moz-window-inactive {
|
&:-moz-window-inactive {
|
||||||
background-color: InactiveCaption;
|
background-color: InactiveCaption;
|
||||||
|
@ -264,6 +252,7 @@
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity 0.1s ease-in-out;
|
transition: opacity 0.1s ease-in-out;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
transform: translateX(-50%);
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover::before {
|
&:hover::before {
|
||||||
|
|
|
@ -35,17 +35,13 @@
|
||||||
--uc-permission-item-margin-block: 4px;
|
--uc-permission-item-margin-block: 4px;
|
||||||
--uc-permission-item-padding-inline: 16px;
|
--uc-permission-item-padding-inline: 16px;
|
||||||
--zen-panel-separator-width: 1px;
|
--zen-panel-separator-width: 1px;
|
||||||
|
|
||||||
--zen-contextmenu-menuitem-padding-inline: 10px;
|
|
||||||
--zen-contextmenu-menuicon-margin-inline: 12px;
|
|
||||||
--zen-contextmenu-menuitem-margin: 0px 2px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
menupopup,
|
menupopup,
|
||||||
panel {
|
panel {
|
||||||
--panel-background: var(--arrowpanel-background);
|
--panel-background: var(--arrowpanel-background);
|
||||||
--panel-border-radius: var(--zen-native-inner-radius);
|
--panel-border-radius: var(--zen-native-inner-radius);
|
||||||
--menuitem-padding: 6px 5px !important;
|
--menuitem-padding: 6px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* split-view popup */
|
/* split-view popup */
|
||||||
|
@ -247,11 +243,6 @@ panel {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
menupopup::part(content),
|
|
||||||
panel::part(content) {
|
|
||||||
border: var(--zen-appcontent-border);
|
|
||||||
}
|
|
||||||
|
|
||||||
menupopup,
|
menupopup,
|
||||||
panel {
|
panel {
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
const lazyCompactMode = {};
|
const lazyCompactMode = {};
|
||||||
|
|
||||||
XPCOMUtils.defineLazyPreferenceGetter(
|
XPCOMUtils.defineLazyPreferenceGetter(
|
||||||
|
@ -88,10 +91,6 @@ var gZenCompactModeManager = {
|
||||||
this.preference === value ||
|
this.preference === value ||
|
||||||
document.documentElement.hasAttribute('zen-compact-animating')
|
document.documentElement.hasAttribute('zen-compact-animating')
|
||||||
) {
|
) {
|
||||||
if (typeof this._wasInCompactMode !== 'undefined') {
|
|
||||||
// We wont do anything with it anyway, so we remove it
|
|
||||||
delete this._wasInCompactMode;
|
|
||||||
}
|
|
||||||
// We dont want the user to be able to spam the button
|
// We dont want the user to be able to spam the button
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
{
|
{
|
||||||
const { Downloads } = ChromeUtils.importESModule('resource://gre/modules/Downloads.sys.mjs');
|
const { Downloads } = ChromeUtils.importESModule('resource://gre/modules/Downloads.sys.mjs');
|
||||||
|
|
||||||
|
@ -11,24 +14,10 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
class ZenDownloadAnimation extends ZenDOMOperatedFeature {
|
class ZenDownloadAnimation extends ZenDOMOperatedFeature {
|
||||||
#lastClickPosition = null;
|
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
this.#setupClickListener();
|
|
||||||
await this.#setupDownloadListeners();
|
await this.#setupDownloadListeners();
|
||||||
}
|
}
|
||||||
|
|
||||||
#setupClickListener() {
|
|
||||||
document.addEventListener('mousedown', this.#handleClick.bind(this), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
#handleClick(event) {
|
|
||||||
this.#lastClickPosition = {
|
|
||||||
clientX: event.clientX,
|
|
||||||
clientY: event.clientY,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
async #setupDownloadListeners() {
|
async #setupDownloadListeners() {
|
||||||
try {
|
try {
|
||||||
const list = await Downloads.getList(Downloads.ALL);
|
const list = await Downloads.getList(Downloads.ALL);
|
||||||
|
@ -50,14 +39,14 @@
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.#lastClickPosition) {
|
if (!gZenUIManager._lastClickPosition) {
|
||||||
console.warn(
|
console.warn(
|
||||||
`[${ZenDownloadAnimation.name}] No recent click position available for animation`
|
`[${ZenDownloadAnimation.name}] No recent click position available for animation`
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.#animateDownload(this.#lastClickPosition);
|
this.#animateDownload(gZenUIManager._lastClickPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
#animateDownload(startPosition) {
|
#animateDownload(startPosition) {
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
{
|
{
|
||||||
class ZenFolders {
|
class ZenFolders {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
{
|
{
|
||||||
class ZenGlanceManager extends ZenDOMOperatedFeature {
|
class ZenGlanceManager extends ZenDOMOperatedFeature {
|
||||||
_animating = false;
|
_animating = false;
|
||||||
|
@ -151,8 +154,8 @@
|
||||||
this.animatingOpen = true;
|
this.animatingOpen = true;
|
||||||
this._animating = true;
|
this._animating = true;
|
||||||
|
|
||||||
const initialX = data.x;
|
const initialX = data.clientX;
|
||||||
const initialY = data.y;
|
const initialY = data.clientY;
|
||||||
const initialWidth = data.width;
|
const initialWidth = data.width;
|
||||||
const initialHeight = data.height;
|
const initialHeight = data.height;
|
||||||
|
|
||||||
|
@ -594,8 +597,10 @@
|
||||||
this.openGlance(
|
this.openGlance(
|
||||||
{
|
{
|
||||||
url: undefined,
|
url: undefined,
|
||||||
x: browserRect.width / 2,
|
...(gZenUIManager._lastClickPosition || {
|
||||||
y: browserRect.height / 2,
|
clientX: browserRect.width / 2,
|
||||||
|
clientY: browserRect.height / 2,
|
||||||
|
}),
|
||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
},
|
},
|
||||||
|
@ -636,7 +641,12 @@
|
||||||
.classList.remove('zen-glance-background');
|
.classList.remove('zen-glance-background');
|
||||||
this.#currentParentTab._visuallySelected = false;
|
this.#currentParentTab._visuallySelected = false;
|
||||||
this.hideSidebarButtons();
|
this.hideSidebarButtons();
|
||||||
|
if (forSplit) {
|
||||||
|
this.finishOpeningGlance();
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (gReduceMotion || forSplit) {
|
if (gReduceMotion || forSplit) {
|
||||||
|
gZenViewSplitter.deactivateCurrentSplitView();
|
||||||
this.finishOpeningGlance();
|
this.finishOpeningGlance();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -651,6 +661,7 @@
|
||||||
type: 'spring',
|
type: 'spring',
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
gZenViewSplitter.deactivateCurrentSplitView();
|
||||||
this.finishOpeningGlance();
|
this.finishOpeningGlance();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -675,8 +686,8 @@
|
||||||
const rect = event.target.getBoundingClientRect();
|
const rect = event.target.getBoundingClientRect();
|
||||||
const data = {
|
const data = {
|
||||||
url: event.target._placesNode.uri,
|
url: event.target._placesNode.uri,
|
||||||
x: rect.left,
|
clientX: rect.left,
|
||||||
y: rect.top,
|
clientY: rect.top,
|
||||||
width: rect.width,
|
width: rect.width,
|
||||||
height: rect.height,
|
height: rect.height,
|
||||||
};
|
};
|
||||||
|
@ -705,8 +716,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
getTabOrGlanceParent(tab) {
|
getTabOrGlanceParent(tab) {
|
||||||
if (tab?.hasAttribute('glance-id')) {
|
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) {
|
if (parentTab) {
|
||||||
return parentTab;
|
return parentTab;
|
||||||
}
|
}
|
||||||
|
@ -734,29 +745,54 @@
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onSearchSelectCommand(where) {
|
||||||
|
if (where !== 'tab') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const currentTab = gBrowser.selectedTab;
|
||||||
|
const parentTab = currentTab.owner;
|
||||||
|
if (!parentTab) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Open a new glance if the current tab is a glance tab
|
||||||
|
const browserRect = gBrowser.tabbox.getBoundingClientRect();
|
||||||
|
this.openGlance(
|
||||||
|
{
|
||||||
|
url: undefined,
|
||||||
|
...(gZenUIManager._lastClickPosition || {
|
||||||
|
clientX: browserRect.width / 2,
|
||||||
|
clientY: browserRect.height / 2,
|
||||||
|
}),
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
},
|
||||||
|
currentTab,
|
||||||
|
parentTab
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.gZenGlanceManager = new ZenGlanceManager();
|
window.gZenGlanceManager = new ZenGlanceManager();
|
||||||
|
|
||||||
function registerWindowActors() {
|
function registerWindowActors() {
|
||||||
if (Services.prefs.getBoolPref('zen.glance.enabled', true)) {
|
gZenActorsManager.addJSWindowActor('ZenGlance', {
|
||||||
gZenActorsManager.addJSWindowActor('ZenGlance', {
|
parent: {
|
||||||
parent: {
|
esModuleURI: 'resource:///actors/ZenGlanceParent.sys.mjs',
|
||||||
esModuleURI: 'chrome://browser/content/zen-components/actors/ZenGlanceParent.sys.mjs',
|
},
|
||||||
},
|
child: {
|
||||||
child: {
|
esModuleURI: 'resource:///actors/ZenGlanceChild.sys.mjs',
|
||||||
esModuleURI: 'chrome://browser/content/zen-components/actors/ZenGlanceChild.sys.mjs',
|
events: {
|
||||||
events: {
|
DOMContentLoaded: {},
|
||||||
DOMContentLoaded: {},
|
keydown: {
|
||||||
keydown: {
|
capture: true,
|
||||||
capture: true,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
allFrames: true,
|
},
|
||||||
matches: ['*://*/*'],
|
allFrames: true,
|
||||||
});
|
matches: ['*://*/*'],
|
||||||
}
|
enablePreference: 'zen.glance.enabled',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
registerWindowActors();
|
registerWindowActors();
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
export class ZenGlanceChild extends JSWindowActorChild {
|
export class ZenGlanceChild extends JSWindowActorChild {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
@ -71,8 +74,8 @@ export class ZenGlanceChild extends JSWindowActorChild {
|
||||||
const rect = target.getBoundingClientRect();
|
const rect = target.getBoundingClientRect();
|
||||||
this.sendAsyncMessage('ZenGlance:OpenGlance', {
|
this.sendAsyncMessage('ZenGlance:OpenGlance', {
|
||||||
url,
|
url,
|
||||||
x: rect.left,
|
clientX: rect.left,
|
||||||
y: rect.top,
|
clientY: rect.top,
|
||||||
width: rect.width,
|
width: rect.width,
|
||||||
height: rect.height,
|
height: rect.height,
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
export class ZenGlanceParent extends JSWindowActorParent {
|
export class ZenGlanceParent extends JSWindowActorParent {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
{
|
{
|
||||||
const lazy = {};
|
const lazy = {};
|
||||||
XPCOMUtils.defineLazyPreferenceGetter(
|
XPCOMUtils.defineLazyPreferenceGetter(
|
||||||
|
|
735
src/zen/mods/ZenMods.mjs
Normal file
735
src/zen/mods/ZenMods.mjs
Normal file
|
@ -0,0 +1,735 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
{
|
||||||
|
class ZenMods extends ZenPreloadedFeature {
|
||||||
|
// private properties start
|
||||||
|
#kZenStylesheetModHeader = '/* Zen Mods - Generated by ZenMods.';
|
||||||
|
#kZenStylesheetModHeaderBody = `* DO NOT EDIT THIS FILE DIRECTLY!
|
||||||
|
* Your changes will be overwritten.
|
||||||
|
* Instead, go to the preferences and edit the mods there.
|
||||||
|
*/
|
||||||
|
`;
|
||||||
|
#kZenStylesheetModFooter = `
|
||||||
|
/* End of Zen Mods */
|
||||||
|
`;
|
||||||
|
#getCurrentDateTime = () =>
|
||||||
|
new Intl.DateTimeFormat('en-US', {
|
||||||
|
dateStyle: 'full',
|
||||||
|
timeStyle: 'full',
|
||||||
|
}).format(new Date().getTime());
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
console.log('[ZenMods]: Initializing ZenMods module');
|
||||||
|
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stylesheet service
|
||||||
|
#sss = null;
|
||||||
|
|
||||||
|
get #stylesheetService() {
|
||||||
|
if (!this.#sss) {
|
||||||
|
this.#sss = Cc['@mozilla.org/content/style-sheet-service;1'].getService(
|
||||||
|
Ci.nsIStyleSheetService
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return this.#sss;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ssu = null;
|
||||||
|
|
||||||
|
get #styleSheetUri() {
|
||||||
|
if (!this.#ssu) {
|
||||||
|
this.#ssu = Services.io.newFileURI(new FileUtils.File(this.#styleSheetPath));
|
||||||
|
}
|
||||||
|
return this.#ssu;
|
||||||
|
}
|
||||||
|
|
||||||
|
get #styleSheetPath() {
|
||||||
|
return PathUtils.join(PathUtils.profileDir, 'chrome', 'zen-themes.css');
|
||||||
|
}
|
||||||
|
|
||||||
|
async #handleDisableMods() {
|
||||||
|
if (Services.prefs.getBoolPref('zen.themes.disable-all', false)) {
|
||||||
|
console.log('[ZenMods]: Disabling mods module.');
|
||||||
|
|
||||||
|
await this.#removeStylesheet();
|
||||||
|
} else {
|
||||||
|
console.log('[ZenMods]: Enabling mods module.');
|
||||||
|
|
||||||
|
await this.#rebuildModsStylesheet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#getStylesheetURIForMod(mod) {
|
||||||
|
return Services.io.newFileURI(
|
||||||
|
new FileUtils.File(PathUtils.join(this.getModFolder(mod.id), 'chrome.css'))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async #insertStylesheet() {
|
||||||
|
if (await IOUtils.exists(this.#styleSheetPath)) {
|
||||||
|
await this.#stylesheetService.loadAndRegisterSheet(
|
||||||
|
this.#styleSheetUri,
|
||||||
|
this.#stylesheetService.AGENT_SHEET
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!this.#stylesheetService.sheetRegistered(
|
||||||
|
this.#styleSheetUri,
|
||||||
|
this.#stylesheetService.AGENT_SHEET
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
console.error(`[ZenMods]: Failed to register stylesheet at ${this.#styleSheetUri.spec}.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async #removeStylesheet() {
|
||||||
|
await this.#stylesheetService.unregisterSheet(
|
||||||
|
this.#styleSheetUri,
|
||||||
|
this.#stylesheetService.AGENT_SHEET
|
||||||
|
);
|
||||||
|
const rv = this.#stylesheetService.sheetRegistered(
|
||||||
|
this.#styleSheetUri,
|
||||||
|
this.#stylesheetService.AGENT_SHEET
|
||||||
|
);
|
||||||
|
await IOUtils.remove(this.#styleSheetPath, { ignoreAbsent: true });
|
||||||
|
|
||||||
|
if (rv || (await IOUtils.exists(this.#styleSheetPath))) {
|
||||||
|
console.error(`[ZenMods]: Failed to unregister stylesheet at ${this.#styleSheetUri.spec}.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async #rebuildModsStylesheet() {
|
||||||
|
await this.#removeStylesheet();
|
||||||
|
|
||||||
|
const mods = await this.#getEnabledMods();
|
||||||
|
|
||||||
|
await this.#writeStylesheet(mods);
|
||||||
|
|
||||||
|
const modsWithPreferences = await Promise.all(
|
||||||
|
mods.map(async (mod) => {
|
||||||
|
const preferences = await this.getModPreferences(mod);
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: mod.name,
|
||||||
|
enabled: mod.enabled,
|
||||||
|
preferences,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
this.#setDefaults(modsWithPreferences);
|
||||||
|
this.#writeToDom(modsWithPreferences);
|
||||||
|
|
||||||
|
await this.#insertStylesheet();
|
||||||
|
}
|
||||||
|
|
||||||
|
async #getEnabledMods() {
|
||||||
|
const modsObject = await this.getMods();
|
||||||
|
const mods = Object.values(modsObject).filter(
|
||||||
|
(mod) => mod.enabled === undefined || mod.enabled
|
||||||
|
);
|
||||||
|
|
||||||
|
const modList = mods.map(({ name }) => name).join(', ');
|
||||||
|
|
||||||
|
const message =
|
||||||
|
modList !== ''
|
||||||
|
? `[ZenMods]: Loading enabled Zen mods: ${modList}.`
|
||||||
|
: '[ZenMods]: No enabled Zen mods.';
|
||||||
|
|
||||||
|
console.log(message);
|
||||||
|
|
||||||
|
return mods;
|
||||||
|
}
|
||||||
|
|
||||||
|
#setDefaults(modsWithPreferences) {
|
||||||
|
for (const { preferences, enabled } of modsWithPreferences) {
|
||||||
|
if (enabled !== undefined && !enabled) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const { type, property, defaultValue } of preferences) {
|
||||||
|
if (defaultValue === undefined) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'checkbox') {
|
||||||
|
const value = Services.prefs.getBoolPref(property, false);
|
||||||
|
if (typeof defaultValue !== 'boolean') {
|
||||||
|
console.warn(
|
||||||
|
'[ZenMods]: Warning, invalid data type received for expected type boolean, skipping.'
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!value) {
|
||||||
|
Services.prefs.setBoolPref(property, defaultValue);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const value = Services.prefs.getStringPref(property, 'zen-property-no-saved');
|
||||||
|
|
||||||
|
if (typeof defaultValue !== 'string' && typeof defaultValue !== 'number') {
|
||||||
|
console.warn(
|
||||||
|
`[ZenMods]: Warning, invalid data type received (${typeof defaultValue}), skipping.`
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (value === 'zen-property-no-saved') {
|
||||||
|
Services.prefs.setStringPref(property, defaultValue.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#writeToDom(modsWithPreferences) {
|
||||||
|
for (const browser of ZenMultiWindowFeature.browsers) {
|
||||||
|
for (const { enabled, preferences, name } of modsWithPreferences) {
|
||||||
|
const sanitizedName = this.sanitizeModName(name);
|
||||||
|
|
||||||
|
if (enabled !== undefined && !enabled) {
|
||||||
|
const element = browser.document.getElementById(sanitizedName);
|
||||||
|
|
||||||
|
if (element) {
|
||||||
|
element.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const { property } of preferences.filter(({ type }) => type !== 'checkbox')) {
|
||||||
|
const sanitizedProperty = property?.replaceAll(/\./g, '-');
|
||||||
|
|
||||||
|
browser.document
|
||||||
|
.querySelector(':root')
|
||||||
|
.style.removeProperty(`--${sanitizedProperty}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const { property, type } of preferences) {
|
||||||
|
const value = Services.prefs.getStringPref(property, '');
|
||||||
|
const sanitizedProperty = property?.replaceAll(/\./g, '-');
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case 'dropdown': {
|
||||||
|
if (value !== '') {
|
||||||
|
let element = browser.document.getElementById(sanitizedName);
|
||||||
|
|
||||||
|
if (!element) {
|
||||||
|
element = browser.document.createElement('div');
|
||||||
|
|
||||||
|
element.style.display = 'none';
|
||||||
|
element.setAttribute('id', sanitizedName);
|
||||||
|
|
||||||
|
browser.document.body.appendChild(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
element.setAttribute(sanitizedProperty, value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'string': {
|
||||||
|
if (value === '') {
|
||||||
|
browser.document
|
||||||
|
.querySelector(':root')
|
||||||
|
.style.removeProperty(`--${sanitizedProperty}`);
|
||||||
|
} else {
|
||||||
|
browser.document
|
||||||
|
.querySelector(':root')
|
||||||
|
.style.setProperty(`--${sanitizedProperty}`, value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async #writeStylesheet(modList = []) {
|
||||||
|
const mods = [];
|
||||||
|
|
||||||
|
for (let mod of modList) {
|
||||||
|
mod._chromeURL = this.#getStylesheetURIForMod(mod).spec;
|
||||||
|
mods.push(mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
let content = this.#kZenStylesheetModHeader;
|
||||||
|
content += `\n* FILE GENERATED AT: ${this.#getCurrentDateTime()}\n`;
|
||||||
|
content += this.#kZenStylesheetModHeaderBody;
|
||||||
|
|
||||||
|
for (let mod of mods) {
|
||||||
|
if (mod.enabled !== undefined && !mod.enabled) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
content += `\n/* Name: ${mod.name} */\n`;
|
||||||
|
content += `/* Description: ${mod.description} */\n`;
|
||||||
|
content += `/* Author: @${mod.author} */\n`;
|
||||||
|
|
||||||
|
if (mod._readmeURL) {
|
||||||
|
content += `/* Readme: ${mod.readme} */\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
content += `@import url("${mod._chromeURL}");\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
content += this.#kZenStylesheetModFooter;
|
||||||
|
|
||||||
|
const buffer = new TextEncoder().encode(content);
|
||||||
|
|
||||||
|
await IOUtils.write(this.#styleSheetPath, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
#compareVersions(version1, version2) {
|
||||||
|
let result = false;
|
||||||
|
|
||||||
|
if (typeof version1 !== 'object') {
|
||||||
|
version1 = version1.toString().split('.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof version2 !== 'object') {
|
||||||
|
version2 = version2.toString().split('.');
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < Math.max(version1.length, version2.length); i++) {
|
||||||
|
if (version1[i] == undefined) {
|
||||||
|
version1[i] = 0;
|
||||||
|
}
|
||||||
|
if (version2[i] == undefined) {
|
||||||
|
version2[i] = 0;
|
||||||
|
}
|
||||||
|
if (Number(version1[i]) < Number(version2[i])) {
|
||||||
|
result = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (version1[i] != version2[i]) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
#composeModApiUrl(modId) {
|
||||||
|
// keeping theme here as it would require changes to CI to change the name
|
||||||
|
return `https://zen-browser.github.io/theme-store/themes/${modId}/theme.json`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async #downloadUrlToFile(url, path, isStyleSheet = false, maxRetries = 3, retryDelayMs = 500) {
|
||||||
|
let attempt = 0;
|
||||||
|
|
||||||
|
while (attempt < maxRetries) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(url);
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status} for url: ${url}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await response.text();
|
||||||
|
|
||||||
|
let content = data;
|
||||||
|
|
||||||
|
if (isStyleSheet) {
|
||||||
|
content = '@-moz-document url-prefix("chrome:") {\n';
|
||||||
|
|
||||||
|
for (const line of data.split('\n')) {
|
||||||
|
content += ` ${line}\n`;
|
||||||
|
}
|
||||||
|
|
||||||
|
content += '}';
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert the data into a Uint8Array
|
||||||
|
const buffer = new TextEncoder().encode(content);
|
||||||
|
await IOUtils.write(path, buffer);
|
||||||
|
|
||||||
|
return; // to exit the loop
|
||||||
|
} catch (e) {
|
||||||
|
attempt++;
|
||||||
|
if (attempt >= maxRetries) {
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// private properties end
|
||||||
|
|
||||||
|
// public properties start
|
||||||
|
|
||||||
|
throttle(mainFunction, delay) {
|
||||||
|
let timerFlag = null;
|
||||||
|
|
||||||
|
return (...args) => {
|
||||||
|
if (timerFlag === null) {
|
||||||
|
mainFunction(...args);
|
||||||
|
timerFlag = setTimeout(() => {
|
||||||
|
timerFlag = null;
|
||||||
|
}, delay);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
debounce(mainFunction, wait) {
|
||||||
|
let timerFlag;
|
||||||
|
|
||||||
|
return (...args) => {
|
||||||
|
clearTimeout(timerFlag);
|
||||||
|
timerFlag = setTimeout(() => {
|
||||||
|
mainFunction(...args);
|
||||||
|
}, wait);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
sanitizeModName(name) {
|
||||||
|
// Do not change to "mod-" for backwards compatibility
|
||||||
|
return `theme-${name?.replaceAll(/\s/g, '-')?.replaceAll(/[^A-Za-z_-]+/g, '')}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
get updatePref() {
|
||||||
|
return 'zen.themes.updated-value-observer';
|
||||||
|
}
|
||||||
|
|
||||||
|
get modsRootPath() {
|
||||||
|
return PathUtils.join(PathUtils.profileDir, 'chrome', 'zen-themes');
|
||||||
|
}
|
||||||
|
|
||||||
|
get modsDataFile() {
|
||||||
|
return PathUtils.join(PathUtils.profileDir, 'zen-themes.json');
|
||||||
|
}
|
||||||
|
|
||||||
|
getModFolder(modId) {
|
||||||
|
return PathUtils.join(this.modsRootPath, modId);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getMods() {
|
||||||
|
if (!(await IOUtils.exists(this.modsDataFile))) {
|
||||||
|
await IOUtils.writeJSON(this.modsDataFile, {});
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
let mods = {};
|
||||||
|
|
||||||
|
try {
|
||||||
|
mods = await IOUtils.readJSON(this.modsDataFile);
|
||||||
|
|
||||||
|
if (mods === null || typeof mods !== 'object') {
|
||||||
|
throw new Error('Mods data file is invalid');
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// If we have a corrupted file, reset it
|
||||||
|
await IOUtils.writeJSON(this.modsDataFile, {});
|
||||||
|
|
||||||
|
Services.wm
|
||||||
|
.getMostRecentWindow('navigator:browser')
|
||||||
|
.gZenUIManager.showToast('zen-themes-corrupted', {
|
||||||
|
timeout: 8000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return mods;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getModPreferences(mod) {
|
||||||
|
const modPath = PathUtils.join(this.modsRootPath, mod.id, 'preferences.json');
|
||||||
|
|
||||||
|
if (!(await IOUtils.exists(modPath)) || !mod.preferences) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const preferences = await IOUtils.readJSON(modPath);
|
||||||
|
|
||||||
|
return preferences.filter(({ disabledOn = [] }) => {
|
||||||
|
return !disabledOn.includes(gZenOperatingSystemCommonUtils.currentOperatingSystem);
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`[ZenMods]: Error reading mod preferences for ${mod.name}:`, e);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
try {
|
||||||
|
await SessionStore.promiseInitialized;
|
||||||
|
|
||||||
|
if (
|
||||||
|
Services.prefs.getBoolPref('zen.themes.disable-all', false) ||
|
||||||
|
Services.appinfo.inSafeMode
|
||||||
|
) {
|
||||||
|
console.log('[ZenMods]: Mods disabled by user or in safe mode.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.getMods(); // Check for any errors in the themes data file
|
||||||
|
const mods = await this.#getEnabledMods();
|
||||||
|
|
||||||
|
const modsWithPreferences = await Promise.all(
|
||||||
|
mods.map(async (mod) => {
|
||||||
|
const preferences = await this.getModPreferences(mod);
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: mod.name,
|
||||||
|
enabled: mod.enabled,
|
||||||
|
preferences,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
this.#writeToDom(modsWithPreferences);
|
||||||
|
|
||||||
|
await this.#insertStylesheet();
|
||||||
|
|
||||||
|
this.#setNewMilestoneIfNeeded();
|
||||||
|
if (this.#shouldAutoUpdate()) {
|
||||||
|
requestIdleCallback(
|
||||||
|
() => {
|
||||||
|
if (!window.closed) {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
this.checkForModsUpdates();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ timeout: 1000 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[ZenMods]: Error loading Zen Mods:', e);
|
||||||
|
}
|
||||||
|
|
||||||
|
Services.prefs.addObserver(this.updatePref, this.#rebuildModsStylesheet.bind(this), false);
|
||||||
|
Services.prefs.addObserver(
|
||||||
|
'zen.themes.disable-all',
|
||||||
|
this.#handleDisableMods.bind(this),
|
||||||
|
false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#setNewMilestoneIfNeeded() {
|
||||||
|
const previousMilestone = Services.prefs.getStringPref('zen.mods.milestone', '');
|
||||||
|
if (previousMilestone != 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 nowSec = Math.floor(Date.now() / 1000);
|
||||||
|
const daysSinceUpdate = (nowSec - lastUpdatedSec) / (60 * 60 * 24);
|
||||||
|
|
||||||
|
return (
|
||||||
|
(Services.prefs.getBoolPref('zen.mods.auto-update', true) &&
|
||||||
|
daysSinceUpdate >= daysBeforeUpdate) ||
|
||||||
|
lastUpdatedSec < 0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async checkForModsUpdates() {
|
||||||
|
const mods = await this.getMods();
|
||||||
|
|
||||||
|
const updates = await Promise.all(
|
||||||
|
Object.values(mods).map(async (currentMod) => {
|
||||||
|
try {
|
||||||
|
const possibleNewModVersion = await this.requestMod(currentMod.id);
|
||||||
|
|
||||||
|
if (!possibleNewModVersion) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!this.#compareVersions(
|
||||||
|
possibleNewModVersion.version,
|
||||||
|
currentMod.version ?? '0.0.0'
|
||||||
|
) &&
|
||||||
|
possibleNewModVersion.version != currentMod.version
|
||||||
|
) {
|
||||||
|
console.log(
|
||||||
|
`[ZenMods]: Mod update found for mod ${currentMod.name} (${currentMod.id}), current: ${currentMod.version}, new: ${possibleNewModVersion.version}`
|
||||||
|
);
|
||||||
|
|
||||||
|
possibleNewModVersion.enabled = currentMod.enabled;
|
||||||
|
|
||||||
|
await this.removeMod(currentMod.id, false);
|
||||||
|
|
||||||
|
mods[currentMod.id] = possibleNewModVersion;
|
||||||
|
|
||||||
|
return possibleNewModVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[ZenMods]: Error checking for mod updates', e);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
await this.updateMods(mods);
|
||||||
|
Services.prefs.setIntPref('zen.mods.last-update', Math.floor(Date.now() / 1000));
|
||||||
|
return updates.filter((update) => {
|
||||||
|
return update !== null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async removeMod(modId, triggerUpdate = true) {
|
||||||
|
const modPath = this.getModFolder(modId);
|
||||||
|
|
||||||
|
console.log(`[ZenMods]: Removing mod ${modPath}`);
|
||||||
|
|
||||||
|
await IOUtils.remove(modPath, { recursive: true, ignoreAbsent: true });
|
||||||
|
|
||||||
|
const mods = await this.getMods();
|
||||||
|
|
||||||
|
delete mods[modId];
|
||||||
|
|
||||||
|
await IOUtils.writeJSON(this.modsDataFile, mods);
|
||||||
|
|
||||||
|
if (triggerUpdate) {
|
||||||
|
this.triggerModsUpdate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async enableMod(modId) {
|
||||||
|
const mods = await this.getMods();
|
||||||
|
const mod = mods[modId];
|
||||||
|
|
||||||
|
console.log(`[ZenMods]: Enabling mod ${mod.name}`);
|
||||||
|
|
||||||
|
mod.enabled = true;
|
||||||
|
|
||||||
|
await IOUtils.writeJSON(this.modsDataFile, mods);
|
||||||
|
}
|
||||||
|
|
||||||
|
async disableMod(modId) {
|
||||||
|
const mods = await this.getMods();
|
||||||
|
const mod = mods[modId];
|
||||||
|
|
||||||
|
console.log(`[ZenMods]: Disabling mod ${mod.name}`);
|
||||||
|
|
||||||
|
mod.enabled = false;
|
||||||
|
|
||||||
|
await IOUtils.writeJSON(this.modsDataFile, mods);
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateMods(mods = undefined) {
|
||||||
|
if (!mods) {
|
||||||
|
mods = await this.getMods();
|
||||||
|
}
|
||||||
|
|
||||||
|
await IOUtils.writeJSON(this.modsDataFile, mods);
|
||||||
|
await this.checkForModChanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
triggerModsUpdate() {
|
||||||
|
Services.prefs.setBoolPref(this.updatePref, !Services.prefs.getBoolPref(this.updatePref));
|
||||||
|
}
|
||||||
|
|
||||||
|
async installMod(mod) {
|
||||||
|
try {
|
||||||
|
const modPath = PathUtils.join(this.modsRootPath, mod.id);
|
||||||
|
await IOUtils.makeDirectory(modPath, { ignoreExisting: true });
|
||||||
|
|
||||||
|
await this.#downloadUrlToFile(mod.style, PathUtils.join(modPath, 'chrome.css'), true);
|
||||||
|
await this.#downloadUrlToFile(mod.readme, PathUtils.join(modPath, 'readme.md'));
|
||||||
|
|
||||||
|
if (mod.preferences) {
|
||||||
|
await this.#downloadUrlToFile(
|
||||||
|
mod.preferences,
|
||||||
|
PathUtils.join(modPath, 'preferences.json')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[ZenMods]: Error installing mod', mod.id, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async checkForModChanges() {
|
||||||
|
const mods = await this.getMods();
|
||||||
|
|
||||||
|
for (const [modId, mod] of Object.entries(mods)) {
|
||||||
|
try {
|
||||||
|
if (!mod) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(await IOUtils.exists(this.getModFolder(modId)))) {
|
||||||
|
await this.installMod(mod);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('[ZenMods]: Error checking for mod changes', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.triggerModsUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
async requestMod(modId) {
|
||||||
|
const url = this.#composeModApiUrl(modId);
|
||||||
|
|
||||||
|
console.debug(`[ZenMods]: Fetching mod ${modId} info from ${url}`);
|
||||||
|
|
||||||
|
const data = await fetch(url, {
|
||||||
|
mode: 'no-cors',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (data.ok) {
|
||||||
|
try {
|
||||||
|
const obj = await data.json();
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`[ZenMods]: Error parsing mod ${modId} info:`, e);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error(`[ZenMods]: Error fetching mod ${modId} info:`, data.status);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async isModInstalled(modId) {
|
||||||
|
const mods = await this.getMods();
|
||||||
|
return Boolean(mods?.[modId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// public properties end
|
||||||
|
}
|
||||||
|
|
||||||
|
window.gZenMods = new ZenMods();
|
||||||
|
|
||||||
|
gZenActorsManager.addJSWindowActor('ZenModsMarketplace', {
|
||||||
|
parent: {
|
||||||
|
esModuleURI: 'resource:///actors/ZenModsMarketplaceParent.sys.mjs',
|
||||||
|
},
|
||||||
|
child: {
|
||||||
|
esModuleURI: 'resource:///actors/ZenModsMarketplaceChild.sys.mjs',
|
||||||
|
events: {
|
||||||
|
DOMContentLoaded: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
matches: [
|
||||||
|
...Services.prefs.getStringPref('zen.injections.match-urls').split(','),
|
||||||
|
'about:preferences',
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
|
@ -1,138 +0,0 @@
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
var ZenThemesCommon = {
|
|
||||||
kZenColors: [
|
|
||||||
'#aac7ff',
|
|
||||||
'#74d7cb',
|
|
||||||
'#a0d490',
|
|
||||||
'#dec663',
|
|
||||||
'#ffb787',
|
|
||||||
'#dec1b1',
|
|
||||||
'#ffb1c0',
|
|
||||||
'#ddbfc3',
|
|
||||||
'#f6b0ea',
|
|
||||||
'#d4bbff',
|
|
||||||
],
|
|
||||||
|
|
||||||
get browsers() {
|
|
||||||
return Services.wm.getEnumerator('navigator:browser');
|
|
||||||
},
|
|
||||||
|
|
||||||
get currentBrowser() {
|
|
||||||
return Services.wm.getMostRecentWindow('navigator:browser');
|
|
||||||
},
|
|
||||||
|
|
||||||
get themesRootPath() {
|
|
||||||
return PathUtils.join(PathUtils.profileDir, 'chrome', 'zen-themes');
|
|
||||||
},
|
|
||||||
|
|
||||||
get themesDataFile() {
|
|
||||||
return PathUtils.join(PathUtils.profileDir, 'zen-themes.json');
|
|
||||||
},
|
|
||||||
|
|
||||||
getThemeFolder(themeId) {
|
|
||||||
return PathUtils.join(this.themesRootPath, themeId);
|
|
||||||
},
|
|
||||||
|
|
||||||
async getThemes() {
|
|
||||||
if (!(await IOUtils.exists(this.themesDataFile))) {
|
|
||||||
await IOUtils.writeJSON(this.themesDataFile, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
let themes = {};
|
|
||||||
try {
|
|
||||||
themes = await IOUtils.readJSON(this.themesDataFile);
|
|
||||||
if (themes === null || typeof themes !== 'object') {
|
|
||||||
throw new Error('Themes data file is null');
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// If we have a corrupted file, reset it
|
|
||||||
await IOUtils.writeJSON(this.themesDataFile, {});
|
|
||||||
|
|
||||||
Services.wm
|
|
||||||
.getMostRecentWindow('navigator:browser')
|
|
||||||
.gZenUIManager.showToast('zen-themes-corrupted', {
|
|
||||||
timeout: 8000,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return themes;
|
|
||||||
},
|
|
||||||
|
|
||||||
async getThemePreferences(theme) {
|
|
||||||
const themePath = PathUtils.join(this.themesRootPath, theme.id, 'preferences.json');
|
|
||||||
|
|
||||||
if (!(await IOUtils.exists(themePath)) || !theme.preferences) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const preferences = await IOUtils.readJSON(themePath);
|
|
||||||
|
|
||||||
// compat mode for old preferences, all of them can only be checkboxes
|
|
||||||
if (typeof preferences === 'object' && !Array.isArray(preferences)) {
|
|
||||||
console.warn(
|
|
||||||
`[ZenThemes]: Warning, ${theme.name} uses legacy preferences, please migrate them to the new preferences style, as legacy preferences might be removed at a future release. More information at: https://docs.zen-browser.app/themes-store/themes-marketplace-preferences`
|
|
||||||
);
|
|
||||||
const newThemePreferences = [];
|
|
||||||
|
|
||||||
for (let [entry, label] of Object.entries(preferences)) {
|
|
||||||
const [, negation = '', os = '', property] =
|
|
||||||
/(!?)(?:(macos|windows|linux):)?([A-Za-z0-9-_.]+)/g.exec(entry);
|
|
||||||
const isNegation = negation === '!';
|
|
||||||
|
|
||||||
if (
|
|
||||||
(isNegation && os === gZenOperatingSystemCommonUtils.currentOperatingSystem) ||
|
|
||||||
(os !== '' &&
|
|
||||||
os !== gZenOperatingSystemCommonUtils.currentOperatingSystem &&
|
|
||||||
!isNegation)
|
|
||||||
) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
newThemePreferences.push({
|
|
||||||
property,
|
|
||||||
label,
|
|
||||||
type: 'checkbox',
|
|
||||||
disabledOn: os !== '' ? [os] : [],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return newThemePreferences;
|
|
||||||
}
|
|
||||||
|
|
||||||
return preferences.filter(
|
|
||||||
({ disabledOn = [] }) =>
|
|
||||||
!disabledOn.includes(gZenOperatingSystemCommonUtils.currentOperatingSystem)
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(`[ZenThemes]: Error reading preferences for ${theme.name}:`, e);
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
throttle(mainFunction, delay) {
|
|
||||||
let timerFlag = null;
|
|
||||||
|
|
||||||
return (...args) => {
|
|
||||||
if (timerFlag === null) {
|
|
||||||
mainFunction(...args);
|
|
||||||
timerFlag = setTimeout(() => {
|
|
||||||
timerFlag = null;
|
|
||||||
}, delay);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
debounce(mainFunction, wait) {
|
|
||||||
let timerFlag;
|
|
||||||
|
|
||||||
return (...args) => {
|
|
||||||
clearTimeout(timerFlag);
|
|
||||||
timerFlag = setTimeout(() => {
|
|
||||||
mainFunction(...args);
|
|
||||||
}, wait);
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -1,335 +0,0 @@
|
||||||
const kZenStylesheetThemeHeader = '/* Zen Themes - Generated by ZenThemesImporter.';
|
|
||||||
const kZenStylesheetThemeHeaderBody = `* DO NOT EDIT THIS FILE DIRECTLY!
|
|
||||||
* Your changes will be overwritten.
|
|
||||||
* Instead, go to the preferences and edit the themes there.
|
|
||||||
*/
|
|
||||||
`;
|
|
||||||
const kenStylesheetFooter = `
|
|
||||||
/* End of Zen Themes */
|
|
||||||
`;
|
|
||||||
const getCurrentDateTime = () =>
|
|
||||||
new Intl.DateTimeFormat('en-US', {
|
|
||||||
dateStyle: 'full',
|
|
||||||
timeStyle: 'full',
|
|
||||||
}).format(new Date().getTime());
|
|
||||||
|
|
||||||
var gZenStylesheetManager = {
|
|
||||||
async writeStylesheet(path, themes) {
|
|
||||||
let content = kZenStylesheetThemeHeader;
|
|
||||||
content += `\n* FILE GENERATED AT: ${getCurrentDateTime()}\n`;
|
|
||||||
content += kZenStylesheetThemeHeaderBody;
|
|
||||||
|
|
||||||
for (let theme of themes) {
|
|
||||||
if (theme.enabled !== undefined && !theme.enabled) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
content += this.getThemeCSS(theme);
|
|
||||||
}
|
|
||||||
|
|
||||||
content += kenStylesheetFooter;
|
|
||||||
|
|
||||||
const buffer = new TextEncoder().encode(content);
|
|
||||||
|
|
||||||
await IOUtils.write(path, buffer);
|
|
||||||
},
|
|
||||||
|
|
||||||
getThemeCSS(theme) {
|
|
||||||
let css = '\n';
|
|
||||||
|
|
||||||
css += `/* Name: ${theme.name} */\n`;
|
|
||||||
css += `/* Description: ${theme.description} */\n`;
|
|
||||||
css += `/* Author: @${theme.author} */\n`;
|
|
||||||
|
|
||||||
if (theme._readmeURL) {
|
|
||||||
css += `/* Readme: ${theme.readme} */\n`;
|
|
||||||
}
|
|
||||||
|
|
||||||
css += `@import url("${theme._chromeURL}");\n`;
|
|
||||||
|
|
||||||
return css;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
var gZenThemesImporter = new (class {
|
|
||||||
constructor() {
|
|
||||||
try {
|
|
||||||
window.SessionStore.promiseInitialized.then(async () => {
|
|
||||||
if (
|
|
||||||
Services.prefs.getBoolPref('zen.themes.disable-all', false) ||
|
|
||||||
Services.appinfo.inSafeMode
|
|
||||||
) {
|
|
||||||
console.log('[ZenThemesImporter]: Disabling all themes.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await ZenThemesCommon.getThemes(); // Check for any errors in the themes data file
|
|
||||||
const themes = await this.getEnabledThemes();
|
|
||||||
|
|
||||||
const themesWithPreferences = await Promise.all(
|
|
||||||
themes.map(async (theme) => {
|
|
||||||
const preferences = await ZenThemesCommon.getThemePreferences(theme);
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: theme.name,
|
|
||||||
enabled: theme.enabled,
|
|
||||||
preferences,
|
|
||||||
};
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
this.writeToDom(themesWithPreferences);
|
|
||||||
|
|
||||||
await this.insertStylesheet();
|
|
||||||
});
|
|
||||||
console.info('[ZenThemesImporter]: Zen Themes imported');
|
|
||||||
} catch (e) {
|
|
||||||
console.error('[ZenThemesImporter]: Error importing Zen Themes: ', e);
|
|
||||||
}
|
|
||||||
|
|
||||||
Services.prefs.addObserver(
|
|
||||||
'zen.themes.updated-value-observer',
|
|
||||||
this.rebuildThemeStylesheet.bind(this),
|
|
||||||
false
|
|
||||||
);
|
|
||||||
Services.prefs.addObserver(
|
|
||||||
'zen.themes.disable-all',
|
|
||||||
this.handleDisableThemes.bind(this),
|
|
||||||
false
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
get sss() {
|
|
||||||
if (!this._sss) {
|
|
||||||
this._sss = Cc['@mozilla.org/content/style-sheet-service;1'].getService(
|
|
||||||
Ci.nsIStyleSheetService
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return this._sss;
|
|
||||||
}
|
|
||||||
|
|
||||||
get styleSheetPath() {
|
|
||||||
return PathUtils.join(PathUtils.profileDir, 'chrome', 'zen-themes.css');
|
|
||||||
}
|
|
||||||
|
|
||||||
async handleDisableThemes() {
|
|
||||||
if (Services.prefs.getBoolPref('zen.themes.disable-all', false)) {
|
|
||||||
console.log('[ZenThemesImporter]: Disabling themes module.');
|
|
||||||
|
|
||||||
await this.removeStylesheet();
|
|
||||||
} else {
|
|
||||||
console.log('[ZenThemesImporter]: Enabling themes module.');
|
|
||||||
|
|
||||||
await this.rebuildThemeStylesheet();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get styleSheetURI() {
|
|
||||||
if (!this._styleSheetURI) {
|
|
||||||
this._styleSheetURI = Services.io.newFileURI(new FileUtils.File(this.styleSheetPath));
|
|
||||||
}
|
|
||||||
return this._styleSheetURI;
|
|
||||||
}
|
|
||||||
|
|
||||||
getStylesheetURIForTheme(theme) {
|
|
||||||
return Services.io.newFileURI(
|
|
||||||
new FileUtils.File(PathUtils.join(ZenThemesCommon.getThemeFolder(theme.id), 'chrome.css'))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async insertStylesheet() {
|
|
||||||
if (await IOUtils.exists(this.styleSheetPath)) {
|
|
||||||
await this.sss.loadAndRegisterSheet(this.styleSheetURI, this.sss.AGENT_SHEET);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.sss.sheetRegistered(this.styleSheetURI, this.sss.AGENT_SHEET)) {
|
|
||||||
console.debug('[ZenThemesImporter]: Sheet successfully registered');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async removeStylesheet() {
|
|
||||||
await this.sss.unregisterSheet(this.styleSheetURI, this.sss.AGENT_SHEET);
|
|
||||||
const rv = this.sss.sheetRegistered(this.styleSheetURI, this.sss.AGENT_SHEET);
|
|
||||||
await IOUtils.remove(this.styleSheetPath, { ignoreAbsent: true });
|
|
||||||
|
|
||||||
if (!rv && !(await IOUtils.exists(this.styleSheetPath))) {
|
|
||||||
console.debug('[ZenThemesImporter]: Sheet successfully unregistered');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async rebuildThemeStylesheet() {
|
|
||||||
await this.removeStylesheet();
|
|
||||||
|
|
||||||
const themes = await this.getEnabledThemes();
|
|
||||||
|
|
||||||
await this.writeStylesheet(themes);
|
|
||||||
|
|
||||||
const themesWithPreferences = await Promise.all(
|
|
||||||
themes.map(async (theme) => {
|
|
||||||
const preferences = await ZenThemesCommon.getThemePreferences(theme);
|
|
||||||
|
|
||||||
return {
|
|
||||||
name: theme.name,
|
|
||||||
enabled: theme.enabled,
|
|
||||||
preferences,
|
|
||||||
};
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
this.setDefaults(themesWithPreferences);
|
|
||||||
this.writeToDom(themesWithPreferences);
|
|
||||||
|
|
||||||
await this.insertStylesheet();
|
|
||||||
}
|
|
||||||
|
|
||||||
async getEnabledThemes() {
|
|
||||||
const themeObject = await ZenThemesCommon.getThemes();
|
|
||||||
const themes = Object.values(themeObject).filter(
|
|
||||||
(theme) => theme.enabled === undefined || theme.enabled
|
|
||||||
);
|
|
||||||
|
|
||||||
const themeList = themes.map(({ name }) => name).join(', ');
|
|
||||||
|
|
||||||
const message =
|
|
||||||
themeList !== ''
|
|
||||||
? `[ZenThemesImporter]: Loading enabled Zen themes: ${themeList}.`
|
|
||||||
: '[ZenThemesImporter]: No enabled Zen themes.';
|
|
||||||
|
|
||||||
console.log(message);
|
|
||||||
|
|
||||||
return themes;
|
|
||||||
}
|
|
||||||
|
|
||||||
setDefaults(themesWithPreferences) {
|
|
||||||
for (const { preferences, enabled } of themesWithPreferences) {
|
|
||||||
if (enabled !== undefined && !enabled) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const { type, property, defaultValue } of preferences) {
|
|
||||||
if (defaultValue === undefined) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === 'checkbox') {
|
|
||||||
const value = Services.prefs.getBoolPref(property, false);
|
|
||||||
if (typeof defaultValue !== 'boolean') {
|
|
||||||
console.log(
|
|
||||||
`[ZenThemesImporter]: Warning, invalid data type received for expected type boolean, skipping.`
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!value) {
|
|
||||||
Services.prefs.setBoolPref(property, defaultValue);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const value = Services.prefs.getStringPref(property, 'zen-property-no-saved');
|
|
||||||
|
|
||||||
if (typeof defaultValue !== 'string' && typeof defaultValue !== 'number') {
|
|
||||||
console.log(
|
|
||||||
`[ZenThemesImporter]: Warning, invalid data type received (${typeof defaultValue}), skipping.`
|
|
||||||
);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value === 'zen-property-no-saved') {
|
|
||||||
Services.prefs.setStringPref(property, defaultValue.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
writeToDom(themesWithPreferences) {
|
|
||||||
for (const browser of ZenMultiWindowFeature.browsers) {
|
|
||||||
for (const { enabled, preferences, name } of themesWithPreferences) {
|
|
||||||
const sanitizedName = `theme-${name?.replaceAll(/\s/g, '-')?.replaceAll(/[^A-Za-z_-]+/g, '')}`;
|
|
||||||
|
|
||||||
if (enabled !== undefined && !enabled) {
|
|
||||||
const element = browser.document.getElementById(sanitizedName);
|
|
||||||
|
|
||||||
if (element) {
|
|
||||||
element.remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const { property } of preferences.filter(({ type }) => type !== 'checkbox')) {
|
|
||||||
const sanitizedProperty = property?.replaceAll(/\./g, '-');
|
|
||||||
|
|
||||||
browser.document.querySelector(':root').style.removeProperty(`--${sanitizedProperty}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const { property, type } of preferences) {
|
|
||||||
const value = Services.prefs.getStringPref(property, '');
|
|
||||||
const sanitizedProperty = property?.replaceAll(/\./g, '-');
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case 'dropdown': {
|
|
||||||
if (value !== '') {
|
|
||||||
let element = browser.document.getElementById(sanitizedName);
|
|
||||||
|
|
||||||
if (!element) {
|
|
||||||
element = browser.document.createElement('div');
|
|
||||||
|
|
||||||
element.style.display = 'none';
|
|
||||||
element.setAttribute('id', sanitizedName);
|
|
||||||
|
|
||||||
browser.document.body.appendChild(element);
|
|
||||||
}
|
|
||||||
|
|
||||||
element.setAttribute(sanitizedProperty, value);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'string': {
|
|
||||||
if (value === '') {
|
|
||||||
browser.document
|
|
||||||
.querySelector(':root')
|
|
||||||
.style.removeProperty(`--${sanitizedProperty}`);
|
|
||||||
} else {
|
|
||||||
browser.document
|
|
||||||
.querySelector(':root')
|
|
||||||
.style.setProperty(`--${sanitizedProperty}`, value);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async writeStylesheet(themeList = []) {
|
|
||||||
const themes = [];
|
|
||||||
|
|
||||||
for (let theme of themeList) {
|
|
||||||
theme._chromeURL = this.getStylesheetURIForTheme(theme).spec;
|
|
||||||
themes.push(theme);
|
|
||||||
}
|
|
||||||
|
|
||||||
await gZenStylesheetManager.writeStylesheet(this.styleSheetPath, themes);
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
gZenActorsManager.addJSWindowActor('ZenThemeMarketplace', {
|
|
||||||
parent: {
|
|
||||||
esModuleURI: 'chrome://browser/content/zen-components/actors/ZenThemeMarketplaceParent.sys.mjs',
|
|
||||||
},
|
|
||||||
child: {
|
|
||||||
esModuleURI: 'chrome://browser/content/zen-components/actors/ZenThemeMarketplaceChild.sys.mjs',
|
|
||||||
events: {
|
|
||||||
DOMContentLoaded: {},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
matches: [
|
|
||||||
...Services.prefs.getStringPref('zen.injections.match-urls').split(','),
|
|
||||||
'about:preferences',
|
|
||||||
],
|
|
||||||
});
|
|
142
src/zen/mods/actors/ZenModsMarketplaceChild.sys.mjs
Normal file
142
src/zen/mods/actors/ZenModsMarketplaceChild.sys.mjs
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
export class ZenModsMarketplaceChild extends JSWindowActorChild {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleEvent(event) {
|
||||||
|
if (event.type === 'DOMContentLoaded') {
|
||||||
|
const verifier = this.contentWindow.document.querySelector(
|
||||||
|
'meta[name="zen-content-verified"]'
|
||||||
|
);
|
||||||
|
|
||||||
|
if (verifier) {
|
||||||
|
verifier.setAttribute('content', 'verified');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.initiateModsMarketplace();
|
||||||
|
|
||||||
|
this.contentWindow.document.addEventListener(
|
||||||
|
'ZenCheckForModUpdates',
|
||||||
|
this.checkForModUpdates.bind(this)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function will be called from about:preferences
|
||||||
|
checkForModUpdates(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
this.sendAsyncMessage('ZenModsMarketplace:CheckForUpdates');
|
||||||
|
}
|
||||||
|
|
||||||
|
initiateModsMarketplace() {
|
||||||
|
this.contentWindow.setTimeout(() => {
|
||||||
|
this.addButtons();
|
||||||
|
this.injectMarketplaceAPI();
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
get actionButton() {
|
||||||
|
return this.contentWindow.document.getElementById('install-theme');
|
||||||
|
}
|
||||||
|
|
||||||
|
get actionButtonUninstall() {
|
||||||
|
return this.contentWindow.document.getElementById('install-theme-uninstall');
|
||||||
|
}
|
||||||
|
|
||||||
|
async isThemeInstalled(themeId) {
|
||||||
|
return await this.sendQuery('ZenModsMarketplace:IsModInstalled', { themeId });
|
||||||
|
}
|
||||||
|
|
||||||
|
async receiveMessage(message) {
|
||||||
|
switch (message.name) {
|
||||||
|
case 'ZenModsMarketplace:ModChanged': {
|
||||||
|
const modId = message.data.modId;
|
||||||
|
const actionButton = this.actionButton;
|
||||||
|
const actionButtonInstalled = this.actionButtonUninstall;
|
||||||
|
|
||||||
|
if (actionButton && actionButtonInstalled) {
|
||||||
|
actionButton.disabled = false;
|
||||||
|
actionButtonInstalled.disabled = false;
|
||||||
|
|
||||||
|
if (await this.isThemeInstalled(modId)) {
|
||||||
|
actionButton.classList.add('hidden');
|
||||||
|
actionButtonInstalled.classList.remove('hidden');
|
||||||
|
} else {
|
||||||
|
actionButton.classList.remove('hidden');
|
||||||
|
actionButtonInstalled.classList.add('hidden');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'ZenModsMarketplace:CheckForUpdatesFinished': {
|
||||||
|
const updates = message.data.updates;
|
||||||
|
|
||||||
|
this.contentWindow.document.dispatchEvent(
|
||||||
|
new CustomEvent('ZenModsMarketplace:CheckForUpdatesFinished', { detail: { updates } })
|
||||||
|
);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
injectMarketplaceAPI() {
|
||||||
|
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');
|
||||||
|
if (!actionButton || !actionButtonUninstall) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
errorMessage.classList.add('hidden');
|
||||||
|
|
||||||
|
const themeId = actionButton.getAttribute('zen-theme-id');
|
||||||
|
if (await this.isThemeInstalled(themeId)) {
|
||||||
|
actionButtonUninstall.classList.remove('hidden');
|
||||||
|
} else {
|
||||||
|
actionButton.classList.remove('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
actionButton.addEventListener('click', this.handleModInstallationEvent.bind(this));
|
||||||
|
actionButtonUninstall.addEventListener('click', this.handleModUninstallEvent.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleModUninstallEvent(event) {
|
||||||
|
const button = event.target;
|
||||||
|
button.disabled = true;
|
||||||
|
|
||||||
|
const modId = button.getAttribute('zen-theme-id');
|
||||||
|
|
||||||
|
this.sendAsyncMessage('ZenModsMarketplace:UninstallMod', { modId });
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleModInstallationEvent(event) {
|
||||||
|
// Object can be an event or a theme id
|
||||||
|
let modId;
|
||||||
|
|
||||||
|
if (event.target) {
|
||||||
|
const button = event.target;
|
||||||
|
button.disabled = true;
|
||||||
|
|
||||||
|
modId = button.getAttribute('zen-theme-id');
|
||||||
|
} else {
|
||||||
|
// Backwards compatibility is... Interesting
|
||||||
|
modId = event.themeId ?? event.modId ?? event.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sendAsyncMessage('ZenModsMarketplace:InstallMod', { modId });
|
||||||
|
}
|
||||||
|
}
|
65
src/zen/mods/actors/ZenModsMarketplaceParent.sys.mjs
Normal file
65
src/zen/mods/actors/ZenModsMarketplaceParent.sys.mjs
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
export class ZenModsMarketplaceParent extends JSWindowActorParent {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
get modsManager() {
|
||||||
|
return this.browsingContext.topChromeWindow.gZenMods;
|
||||||
|
}
|
||||||
|
|
||||||
|
async receiveMessage(message) {
|
||||||
|
switch (message.name) {
|
||||||
|
case 'ZenModsMarketplace:InstallMod': {
|
||||||
|
const modId = message.data.modId;
|
||||||
|
const mod = await this.modsManager.requestMod(modId);
|
||||||
|
|
||||||
|
console.log(`[ZenModsMarketplaceParent]: Installing mod ${mod.id}`);
|
||||||
|
|
||||||
|
mod.enabled = true;
|
||||||
|
|
||||||
|
const mods = await this.modsManager.getMods();
|
||||||
|
mods[mod.id] = mod;
|
||||||
|
|
||||||
|
await this.modsManager.updateMods(mods);
|
||||||
|
await this.updateChildProcesses(mod.id);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'ZenModsMarketplace:UninstallMod': {
|
||||||
|
const modId = message.data.modId;
|
||||||
|
console.log(`[ZenModsMarketplaceParent]: Uninstalling mod ${modId}`);
|
||||||
|
|
||||||
|
const mods = await this.modsManager.getMods();
|
||||||
|
|
||||||
|
delete mods[modId];
|
||||||
|
|
||||||
|
await this.modsManager.removeMod(modId);
|
||||||
|
await this.modsManager.updateMods(mods);
|
||||||
|
|
||||||
|
await this.updateChildProcesses(modId);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'ZenModsMarketplace:CheckForUpdates': {
|
||||||
|
const updates = await this.modsManager.checkForModsUpdates();
|
||||||
|
this.sendAsyncMessage('ZenModsMarketplace:CheckForUpdatesFinished', { updates });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'ZenModsMarketplace:IsModInstalled': {
|
||||||
|
const themeId = message.data.themeId;
|
||||||
|
const themes = await this.modsManager.getMods();
|
||||||
|
|
||||||
|
return Boolean(themes?.[themeId]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateChildProcesses(modId) {
|
||||||
|
this.sendAsyncMessage('ZenModsMarketplace:ModChanged', { modId });
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,200 +0,0 @@
|
||||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
||||||
|
|
||||||
export class ZenThemeMarketplaceChild extends JSWindowActorChild {
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
handleEvent(event) {
|
|
||||||
switch (event.type) {
|
|
||||||
case 'DOMContentLoaded':
|
|
||||||
this.initalizeZenAPI(event);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
initalizeZenAPI(event) {
|
|
||||||
const verifier = this.contentWindow.document.querySelector('meta[name="zen-content-verified"]');
|
|
||||||
|
|
||||||
if (verifier) {
|
|
||||||
verifier.setAttribute('content', 'verified');
|
|
||||||
}
|
|
||||||
|
|
||||||
const possibleRicePage = this.collectRiceMetadata();
|
|
||||||
|
|
||||||
if (possibleRicePage?.id) {
|
|
||||||
this.sendAsyncMessage('ZenThemeMarketplace:RicePage', possibleRicePage);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.initiateThemeMarketplace();
|
|
||||||
this.contentWindow.document.addEventListener(
|
|
||||||
'ZenCheckForThemeUpdates',
|
|
||||||
this.checkForThemeUpdates.bind(this)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
collectRiceMetadata() {
|
|
||||||
const meta = this.contentWindow.document.querySelector('meta[name="zen-rice-data"]');
|
|
||||||
if (meta) {
|
|
||||||
return {
|
|
||||||
id: meta.getAttribute('data-id'),
|
|
||||||
name: meta.getAttribute('data-name'),
|
|
||||||
author: meta.getAttribute('data-author'),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This function will be called from about:preferences
|
|
||||||
checkForThemeUpdates(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
this.sendAsyncMessage('ZenThemeMarketplace:CheckForUpdates');
|
|
||||||
}
|
|
||||||
|
|
||||||
initiateThemeMarketplace() {
|
|
||||||
this.contentWindow.setTimeout(() => {
|
|
||||||
this.addInstallButtons();
|
|
||||||
this.injectMarketplaceAPI();
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
get actionButton() {
|
|
||||||
return this.contentWindow.document.getElementById('install-theme');
|
|
||||||
}
|
|
||||||
|
|
||||||
get actionButtonUninstall() {
|
|
||||||
return this.contentWindow.document.getElementById('install-theme-uninstall');
|
|
||||||
}
|
|
||||||
|
|
||||||
async receiveMessage(message) {
|
|
||||||
switch (message.name) {
|
|
||||||
case 'ZenThemeMarketplace:ThemeChanged': {
|
|
||||||
const themeId = message.data.themeId;
|
|
||||||
const actionButton = this.actionButton;
|
|
||||||
const actionButtonInstalled = this.actionButtonUninstall;
|
|
||||||
|
|
||||||
if (actionButton && actionButtonInstalled) {
|
|
||||||
actionButton.disabled = false;
|
|
||||||
actionButtonInstalled.disabled = false;
|
|
||||||
|
|
||||||
if (await this.isThemeInstalled(themeId)) {
|
|
||||||
actionButton.classList.add('hidden');
|
|
||||||
actionButtonInstalled.classList.remove('hidden');
|
|
||||||
} else {
|
|
||||||
actionButton.classList.remove('hidden');
|
|
||||||
actionButtonInstalled.classList.add('hidden');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'ZenThemeMarketplace:CheckForUpdatesFinished': {
|
|
||||||
const updates = message.data.updates;
|
|
||||||
|
|
||||||
this.contentWindow.document.dispatchEvent(
|
|
||||||
new CustomEvent('ZenThemeMarketplace:CheckForUpdatesFinished', { detail: { updates } })
|
|
||||||
);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case 'ZenThemeMarketplace:GetThemeInfo': {
|
|
||||||
const themeId = message.data.themeId;
|
|
||||||
const theme = await this.getThemeInfo(themeId);
|
|
||||||
|
|
||||||
return theme;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
injectMarketplaceAPI() {
|
|
||||||
Cu.exportFunction(this.installTheme.bind(this), this.contentWindow, {
|
|
||||||
defineAs: 'ZenInstallTheme',
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async addInstallButtons() {
|
|
||||||
const actionButton = this.actionButton;
|
|
||||||
const actionButtonUninstall = this.actionButtonUninstall;
|
|
||||||
const errorMessage = this.contentWindow.document.getElementById('install-theme-error');
|
|
||||||
if (!actionButton || !actionButtonUninstall) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
errorMessage.classList.add('hidden');
|
|
||||||
|
|
||||||
const themeId = actionButton.getAttribute('zen-theme-id');
|
|
||||||
if (await this.isThemeInstalled(themeId)) {
|
|
||||||
actionButtonUninstall.classList.remove('hidden');
|
|
||||||
} else {
|
|
||||||
actionButton.classList.remove('hidden');
|
|
||||||
}
|
|
||||||
|
|
||||||
actionButton.addEventListener('click', this.installTheme.bind(this));
|
|
||||||
actionButtonUninstall.addEventListener('click', this.uninstallTheme.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
async isThemeInstalled(themeId) {
|
|
||||||
return await this.sendQuery('ZenThemeMarketplace:IsThemeInstalled', { themeId });
|
|
||||||
}
|
|
||||||
|
|
||||||
addTheme(theme) {
|
|
||||||
this.sendAsyncMessage('ZenThemeMarketplace:InstallTheme', { theme });
|
|
||||||
}
|
|
||||||
|
|
||||||
getThemeAPIUrl(themeId) {
|
|
||||||
return `https://zen-browser.github.io/theme-store/themes/${themeId}/theme.json`;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getThemeInfo(themeId) {
|
|
||||||
const url = this.getThemeAPIUrl(themeId);
|
|
||||||
const data = await fetch(url, {
|
|
||||||
mode: 'no-cors',
|
|
||||||
});
|
|
||||||
|
|
||||||
if (data.ok) {
|
|
||||||
try {
|
|
||||||
const obj = await data.json();
|
|
||||||
return obj;
|
|
||||||
} catch (e) {
|
|
||||||
console.error('ZenThemeMarketplace: Error parsing theme info: ', e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.error('ZenThemeMarketplace: Error fetching theme info: ', data.status);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
async uninstallTheme(event) {
|
|
||||||
const button = event.target;
|
|
||||||
button.disabled = true;
|
|
||||||
const themeId = button.getAttribute('zen-theme-id');
|
|
||||||
this.sendAsyncMessage('ZenThemeMarketplace:UninstallTheme', { themeId });
|
|
||||||
}
|
|
||||||
|
|
||||||
async installTheme(object) {
|
|
||||||
// Object can be an event or a theme id
|
|
||||||
let themeId;
|
|
||||||
if (object.target) {
|
|
||||||
const button = object.target;
|
|
||||||
button.disabled = true;
|
|
||||||
themeId = button.getAttribute('zen-theme-id');
|
|
||||||
} else {
|
|
||||||
themeId = object.themeId;
|
|
||||||
}
|
|
||||||
console.info('ZenThemeMarketplace: Installing theme with id: ', themeId);
|
|
||||||
|
|
||||||
const theme = await this.getThemeInfo(themeId);
|
|
||||||
if (!theme) {
|
|
||||||
console.error('ZenThemeMarketplace: Error fetching theme info');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.addTheme(theme);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,256 +0,0 @@
|
||||||
export class ZenThemeMarketplaceParent extends JSWindowActorParent {
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
async receiveMessage(message) {
|
|
||||||
switch (message.name) {
|
|
||||||
case 'ZenThemeMarketplace:InstallTheme': {
|
|
||||||
console.info('ZenThemeMarketplaceParent: Updating themes');
|
|
||||||
const theme = message.data.theme;
|
|
||||||
theme.enabled = true;
|
|
||||||
const themes = await this.getThemes();
|
|
||||||
themes[theme.id] = theme;
|
|
||||||
this.updateThemes(themes);
|
|
||||||
this.updateChildProcesses(theme.id);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'ZenThemeMarketplace:UninstallTheme': {
|
|
||||||
console.info('ZenThemeMarketplaceParent: Uninstalling theme');
|
|
||||||
const themeId = message.data.themeId;
|
|
||||||
const themes = await this.getThemes();
|
|
||||||
delete themes[themeId];
|
|
||||||
this.removeTheme(themeId);
|
|
||||||
this.updateThemes(themes);
|
|
||||||
this.updateChildProcesses(themeId);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'ZenThemeMarketplace:IsThemeInstalled': {
|
|
||||||
const themeId = message.data.themeId;
|
|
||||||
const themes = await this.getThemes();
|
|
||||||
|
|
||||||
return Boolean(themes?.[themeId]);
|
|
||||||
}
|
|
||||||
case 'ZenThemeMarketplace:CheckForUpdates': {
|
|
||||||
this.checkForThemeUpdates();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 'ZenThemeMarketplace:RicePage': {
|
|
||||||
this.openRicePage(this.browsingContext.topChromeWindow, message.data);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
openRicePage(window, data) {
|
|
||||||
window.gZenThemePicker.riceManager.openRicePage(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
compareVersions(version1, version2) {
|
|
||||||
let result = false;
|
|
||||||
|
|
||||||
if (typeof version1 !== 'object') {
|
|
||||||
version1 = version1.toString().split('.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof version2 !== 'object') {
|
|
||||||
version2 = version2.toString().split('.');
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < Math.max(version1.length, version2.length); i++) {
|
|
||||||
if (version1[i] == undefined) {
|
|
||||||
version1[i] = 0;
|
|
||||||
}
|
|
||||||
if (version2[i] == undefined) {
|
|
||||||
version2[i] = 0;
|
|
||||||
}
|
|
||||||
if (Number(version1[i]) < Number(version2[i])) {
|
|
||||||
result = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (version1[i] != version2[i]) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
async checkForThemeUpdates() {
|
|
||||||
console.info('ZenThemeMarketplaceParent: Checking for theme updates');
|
|
||||||
|
|
||||||
let updates = [];
|
|
||||||
const themes = await this.getThemes();
|
|
||||||
for (const theme of Object.values(themes)) {
|
|
||||||
try {
|
|
||||||
const themeInfo = await this.sendQuery('ZenThemeMarketplace:GetThemeInfo', {
|
|
||||||
themeId: theme.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!themeInfo) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
!this.compareVersions(themeInfo.version, theme.version || '0.0.0') &&
|
|
||||||
themeInfo.version != theme.version
|
|
||||||
) {
|
|
||||||
console.info(
|
|
||||||
'ZenThemeMarketplaceParent: Theme update found',
|
|
||||||
theme.id,
|
|
||||||
theme.version,
|
|
||||||
themeInfo.version
|
|
||||||
);
|
|
||||||
|
|
||||||
themeInfo.enabled = theme.enabled;
|
|
||||||
updates.push(themeInfo);
|
|
||||||
|
|
||||||
await this.removeTheme(theme.id, false);
|
|
||||||
themes[themeInfo.id] = themeInfo;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error('ZenThemeMarketplaceParent: Error checking for theme updates', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.updateThemes(themes);
|
|
||||||
|
|
||||||
this.sendAsyncMessage('ZenThemeMarketplace:CheckForUpdatesFinished', { updates });
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateChildProcesses(themeId) {
|
|
||||||
this.sendAsyncMessage('ZenThemeMarketplace:ThemeChanged', { themeId });
|
|
||||||
}
|
|
||||||
|
|
||||||
async getThemes() {
|
|
||||||
return await IOUtils.readJSON(this.themesDataFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
async updateThemes(themes = undefined) {
|
|
||||||
if (!themes) {
|
|
||||||
themes = await this.getThemes();
|
|
||||||
}
|
|
||||||
await IOUtils.writeJSON(this.themesDataFile, themes);
|
|
||||||
await this.checkForThemeChanges();
|
|
||||||
}
|
|
||||||
|
|
||||||
getStyleSheetFullContent(style = '') {
|
|
||||||
let stylesheet = '@-moz-document url-prefix("chrome:") {\n';
|
|
||||||
|
|
||||||
for (const line of style.split('\n')) {
|
|
||||||
stylesheet += ` ${line}\n`;
|
|
||||||
}
|
|
||||||
|
|
||||||
stylesheet += '}';
|
|
||||||
|
|
||||||
return stylesheet;
|
|
||||||
}
|
|
||||||
|
|
||||||
async downloadUrlToFile(url, path, isStyleSheet = false, maxRetries = 3, retryDelayMs = 500) {
|
|
||||||
let attempt = 0;
|
|
||||||
|
|
||||||
while (attempt < maxRetries) {
|
|
||||||
try {
|
|
||||||
const response = await fetch(url);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(
|
|
||||||
`ZenThemeMarketplaceParent: HTTP error! status: ${response.status} for url: ${url}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = await response.text();
|
|
||||||
const content = isStyleSheet ? this.getStyleSheetFullContent(data) : data;
|
|
||||||
// convert the data into a Uint8Array
|
|
||||||
const buffer = new TextEncoder().encode(content);
|
|
||||||
await IOUtils.write(path, buffer);
|
|
||||||
|
|
||||||
return;
|
|
||||||
} catch (e) {
|
|
||||||
attempt++;
|
|
||||||
if (attempt >= maxRetries) {
|
|
||||||
console.error('ZenThemeMarketplaceParent: Error downloading file after retries', url, e);
|
|
||||||
} else {
|
|
||||||
console.warn(
|
|
||||||
`ZenThemeMarketplaceParent: Download failed (attempt ${attempt} of ${maxRetries}), retrying in ${retryDelayMs}ms...`,
|
|
||||||
url,
|
|
||||||
e
|
|
||||||
);
|
|
||||||
await new Promise((res) => setTimeout(res, retryDelayMs));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async downloadThemeFileContents(theme) {
|
|
||||||
try {
|
|
||||||
const themePath = PathUtils.join(this.themesRootPath, theme.id);
|
|
||||||
await IOUtils.makeDirectory(themePath, { ignoreExisting: true });
|
|
||||||
|
|
||||||
await this.downloadUrlToFile(theme.style, PathUtils.join(themePath, 'chrome.css'), true);
|
|
||||||
await this.downloadUrlToFile(theme.readme, PathUtils.join(themePath, 'readme.md'));
|
|
||||||
|
|
||||||
if (theme.preferences) {
|
|
||||||
await this.downloadUrlToFile(
|
|
||||||
theme.preferences,
|
|
||||||
PathUtils.join(themePath, 'preferences.json')
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.log('ZenThemeMarketplaceParent: Error downloading theme file contents', theme.id, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get themesRootPath() {
|
|
||||||
return PathUtils.join(PathUtils.profileDir, 'chrome', 'zen-themes');
|
|
||||||
}
|
|
||||||
|
|
||||||
get themesDataFile() {
|
|
||||||
return PathUtils.join(PathUtils.profileDir, 'zen-themes.json');
|
|
||||||
}
|
|
||||||
|
|
||||||
triggerThemeUpdate() {
|
|
||||||
const pref = 'zen.themes.updated-value-observer';
|
|
||||||
Services.prefs.setBoolPref(pref, !Services.prefs.getBoolPref(pref));
|
|
||||||
}
|
|
||||||
|
|
||||||
async installTheme(theme) {
|
|
||||||
try {
|
|
||||||
await this.downloadThemeFileContents(theme);
|
|
||||||
} catch (e) {
|
|
||||||
console.error('ZenThemeMarketplaceParent: Error installing theme', theme.id, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async checkForThemeChanges() {
|
|
||||||
const themes = await this.getThemes();
|
|
||||||
|
|
||||||
const themeIds = Object.keys(themes);
|
|
||||||
|
|
||||||
for (const themeId of themeIds) {
|
|
||||||
try {
|
|
||||||
const theme = themes[themeId];
|
|
||||||
if (!theme) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const themePath = PathUtils.join(this.themesRootPath, themeId);
|
|
||||||
if (!(await IOUtils.exists(themePath))) {
|
|
||||||
await this.installTheme(theme);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error('ZenThemeMarketplaceParent: Error checking for theme changes', e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.triggerThemeUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
async removeTheme(themeId, triggerUpdate = true) {
|
|
||||||
const themePath = PathUtils.join(this.themesRootPath, themeId);
|
|
||||||
await IOUtils.remove(themePath, { recursive: true, ignoreAbsent: true });
|
|
||||||
|
|
||||||
if (triggerUpdate) {
|
|
||||||
this.triggerThemeUpdate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -3,8 +3,7 @@
|
||||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
|
||||||
FINAL_TARGET_FILES.actors += [
|
FINAL_TARGET_FILES.actors += [
|
||||||
"actors/ZenThemeMarketplaceChild.sys.mjs",
|
"actors/ZenModsMarketplaceChild.sys.mjs",
|
||||||
"actors/ZenThemeMarketplaceParent.sys.mjs",
|
"actors/ZenModsMarketplaceParent.sys.mjs",
|
||||||
]
|
]
|
||||||
|
|
|
@ -104,6 +104,7 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||||
);
|
);
|
||||||
|
|
||||||
window.addEventListener('TabClose', this.handleTabClose.bind(this));
|
window.addEventListener('TabClose', this.handleTabClose.bind(this));
|
||||||
|
window.addEventListener('TabBrowserDiscarded', this.handleTabBrowserDiscarded.bind(this));
|
||||||
window.addEventListener('TabSelect', this.onTabSelect.bind(this));
|
window.addEventListener('TabSelect', this.onTabSelect.bind(this));
|
||||||
this.initializeContextMenu();
|
this.initializeContextMenu();
|
||||||
this.insertIntoContextMenu();
|
this.insertIntoContextMenu();
|
||||||
|
@ -150,6 +151,22 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||||
this.removeTabFromGroup(tab, groupIndex, true);
|
this.removeTabFromGroup(tab, groupIndex, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Event} event - The event that triggered the tab browser discard.
|
||||||
|
* @description Handles the tab browser discard event.
|
||||||
|
*/
|
||||||
|
async handleTabBrowserDiscarded(event) {
|
||||||
|
const tab = event.target;
|
||||||
|
if (tab.group?.hasAttribute('split-view-group')) {
|
||||||
|
gBrowser.explicitUnloadTabs(tab.group.tabs);
|
||||||
|
for (const t of tab.group.tabs) {
|
||||||
|
if (t.glanceTab) {
|
||||||
|
gBrowser.explicitUnloadTabs([t.glanceTab]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Event} event - The event that triggered the tab select.
|
* @param {Event} event - The event that triggered the tab select.
|
||||||
* @description Handles the tab select event.
|
* @description Handles the tab select event.
|
||||||
|
@ -193,6 +210,10 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||||
const node = this.getSplitNodeFromTab(tab);
|
const node = this.getSplitNodeFromTab(tab);
|
||||||
const toUpdate = this.removeNode(node);
|
const toUpdate = this.removeNode(node);
|
||||||
this.applyGridLayout(toUpdate);
|
this.applyGridLayout(toUpdate);
|
||||||
|
// Select next tab if the removed tab was selected
|
||||||
|
if (gBrowser.selectedTab === tab) {
|
||||||
|
gBrowser.selectedTab = group.tabs[0];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -876,7 +897,9 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||||
tabCount: window.gBrowser.selectedTabs.length,
|
tabCount: window.gBrowser.selectedTabs.length,
|
||||||
});
|
});
|
||||||
document.getElementById('context_zenSplitTabs').setAttribute('data-l10n-args', tabCountInfo);
|
document.getElementById('context_zenSplitTabs').setAttribute('data-l10n-args', tabCountInfo);
|
||||||
document.getElementById('context_zenSplitTabs').disabled = !this.contextCanSplitTabs();
|
document
|
||||||
|
.getElementById('context_zenSplitTabs')
|
||||||
|
.setAttribute('disabled', !this.contextCanSplitTabs());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -929,8 +952,8 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||||
window.gContextMenu.contentData.docLocation ||
|
window.gContextMenu.contentData.docLocation ||
|
||||||
window.gContextMenu.target.ownerDocument.location.href;
|
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);
|
const newTab = this.openAndSwitchToTab(url, { inBackground: false });
|
||||||
this.splitTabs([currentTab, newTab]);
|
this.splitTabs([currentTab, newTab], undefined, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -976,7 +999,6 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||||
tab = tab.parentNode.closest('.tabbrowser-tab');
|
tab = tab.parentNode.closest('.tabbrowser-tab');
|
||||||
console.assert(tab, 'Tab not found for zen-glance-tab');
|
console.assert(tab, 'Tab not found for zen-glance-tab');
|
||||||
}
|
}
|
||||||
this.updateSplitViewButton(!tab?.splitView);
|
|
||||||
if (tab) {
|
if (tab) {
|
||||||
this.updateSplitView(tab);
|
this.updateSplitView(tab);
|
||||||
tab.linkedBrowser.docShellIsActive = true;
|
tab.linkedBrowser.docShellIsActive = true;
|
||||||
|
@ -1008,7 +1030,7 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||||
* Splits the given tabs.
|
* Splits the given tabs.
|
||||||
*
|
*
|
||||||
* @param {Tab[]} tabs - The tabs to split.
|
* @param {Tab[]} tabs - The tabs to split.
|
||||||
* @param {string} gridType - The type of grid layout.
|
* @param {string|undefined} gridType - The type of grid layout.
|
||||||
*/
|
*/
|
||||||
splitTabs(tabs, gridType, initialIndex = 0) {
|
splitTabs(tabs, gridType, initialIndex = 0) {
|
||||||
// TODO: Add support for splitting essential tabs
|
// TODO: Add support for splitting essential tabs
|
||||||
|
@ -1074,7 +1096,7 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||||
};
|
};
|
||||||
this._data.push(splitData);
|
this._data.push(splitData);
|
||||||
if (!this._sessionRestoring) {
|
if (!this._sessionRestoring) {
|
||||||
window.gBrowser.selectedTab = tabs[0];
|
window.gBrowser.selectedTab = tabs[initialIndex] ?? tabs[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add tabs to the split view group
|
// Add tabs to the split view group
|
||||||
|
@ -1110,7 +1132,6 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||||
|
|
||||||
if (oldView === newView) return;
|
if (oldView === newView) return;
|
||||||
if (newView < 0 && oldView >= 0) {
|
if (newView < 0 && oldView >= 0) {
|
||||||
this.updateSplitViewButton(true);
|
|
||||||
this.deactivateCurrentSplitView();
|
this.deactivateCurrentSplitView();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1122,6 +1143,7 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||||
* Deactivates the split view.
|
* Deactivates the split view.
|
||||||
*/
|
*/
|
||||||
deactivateCurrentSplitView() {
|
deactivateCurrentSplitView() {
|
||||||
|
if (this.currentView < 0) return;
|
||||||
this.setTabsDocShellState(this._data[this.currentView].tabs, false);
|
this.setTabsDocShellState(this._data[this.currentView].tabs, false);
|
||||||
for (const tab of this._data[this.currentView].tabs) {
|
for (const tab of this._data[this.currentView].tabs) {
|
||||||
const container = tab.linkedBrowser.closest('.browserSidebarContainer');
|
const container = tab.linkedBrowser.closest('.browserSidebarContainer');
|
||||||
|
@ -1129,9 +1151,9 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||||
}
|
}
|
||||||
this.removeSplitters();
|
this.removeSplitters();
|
||||||
this.tabBrowserPanel.removeAttribute('zen-split-view');
|
this.tabBrowserPanel.removeAttribute('zen-split-view');
|
||||||
this.updateSplitViewButton(true);
|
|
||||||
this.currentView = -1;
|
this.currentView = -1;
|
||||||
this.toggleWrapperDisplay(false);
|
this.toggleWrapperDisplay(false);
|
||||||
|
this.maybeDisableOpeningTabOnSplitView();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1153,7 +1175,6 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||||
|
|
||||||
this.tabBrowserPanel.setAttribute('zen-split-view', 'true');
|
this.tabBrowserPanel.setAttribute('zen-split-view', 'true');
|
||||||
|
|
||||||
this.updateSplitViewButton(false);
|
|
||||||
this.applyGridToTabs(splitData.tabs);
|
this.applyGridToTabs(splitData.tabs);
|
||||||
this.applyGridLayout(splitData.layoutTree);
|
this.applyGridLayout(splitData.layoutTree);
|
||||||
this.setTabsDocShellState(splitData.tabs, true);
|
this.setTabsDocShellState(splitData.tabs, true);
|
||||||
|
@ -1290,6 +1311,7 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
this.maybeDisableOpeningTabOnSplitView();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1480,20 +1502,6 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||||
container.style.inset = '';
|
container.style.inset = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the split view button visibility.
|
|
||||||
*
|
|
||||||
* @param {boolean} hidden - Indicates if the button should be hidden.
|
|
||||||
*/
|
|
||||||
updateSplitViewButton(hidden) {
|
|
||||||
const button = document.getElementById('zen-split-views-box');
|
|
||||||
if (hidden) {
|
|
||||||
button?.setAttribute('hidden', 'true');
|
|
||||||
} else {
|
|
||||||
button?.removeAttribute('hidden');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the UI of the panel.
|
* Updates the UI of the panel.
|
||||||
*
|
*
|
||||||
|
@ -1668,6 +1676,15 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||||
if (splitGroup && (!draggedTab.group || draggedTab.group !== splitGroup)) {
|
if (splitGroup && (!draggedTab.group || draggedTab.group !== splitGroup)) {
|
||||||
this._moveTabsToContainer([draggedTab], droppedOnTab);
|
this._moveTabsToContainer([draggedTab], droppedOnTab);
|
||||||
gBrowser.moveTabToGroup(draggedTab, splitGroup);
|
gBrowser.moveTabToGroup(draggedTab, splitGroup);
|
||||||
|
if (hoverSide === 'left' || hoverSide === 'top') {
|
||||||
|
try {
|
||||||
|
splitGroup.tabs[0].before(draggedTab);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(
|
||||||
|
`Failed to move tab ${draggedTab.id} before ${splitGroup.tabs[0].id}: ${e}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const droppedOnSplitNode = this.getSplitNodeFromTab(droppedOnTab);
|
const droppedOnSplitNode = this.getSplitNodeFromTab(droppedOnTab);
|
||||||
|
@ -1857,6 +1874,26 @@ class ZenViewSplitter extends ZenDOMOperatedFeature {
|
||||||
this.onLocationChange(gBrowser.selectedTab.linkedBrowser);
|
this.onLocationChange(gBrowser.selectedTab.linkedBrowser);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
maybeDisableOpeningTabOnSplitView() {
|
||||||
|
const shouldBeDisabled = !this.canOpenLinkInSplitView();
|
||||||
|
document
|
||||||
|
.getElementById('cmd_zenSplitViewLinkInNewTab')
|
||||||
|
.setAttribute('disabled', shouldBeDisabled);
|
||||||
|
document.getElementById('zen-glance-sidebar-split').setAttribute('disabled', shouldBeDisabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
canOpenLinkInSplitView() {
|
||||||
|
const currentView = this.currentView;
|
||||||
|
if (currentView < 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const group = this._data[currentView];
|
||||||
|
if (!group || group.tabs.length >= this.MAX_TABS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.gZenViewSplitter = new ZenViewSplitter();
|
window.gZenViewSplitter = new ZenViewSplitter();
|
||||||
|
|
|
@ -41,6 +41,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#zen-splitview-dropzone {
|
||||||
|
margin-top: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
#tabbrowser-tabpanels[zen-split-view='true']:not([zen-split-resizing]) > [zen-split='true'] {
|
#tabbrowser-tabpanels[zen-split-view='true']:not([zen-split-resizing]) > [zen-split='true'] {
|
||||||
transition: inset 0.09s ease-out !important;
|
transition: inset 0.09s ease-out !important;
|
||||||
& browser {
|
& browser {
|
||||||
|
@ -70,15 +74,8 @@
|
||||||
margin-right: calc(-1 * var(--zen-split-column-gap));
|
margin-right: calc(-1 * var(--zen-split-column-gap));
|
||||||
}
|
}
|
||||||
|
|
||||||
#tabbrowser-tabpanels:has(> [zen-split='true']),
|
:root:not([customizing]) #zen-splitview-overlay {
|
||||||
#zen-splitview-overlay {
|
margin-top: calc(var(--zen-split-column-gap) * -1);
|
||||||
:root:not([zen-compact-mode='true']):not([customizing]) & {
|
|
||||||
@media -moz-pref('zen.view.compact.hide-toolbar') {
|
|
||||||
& {
|
|
||||||
margin-top: calc(var(--zen-split-column-gap) * -1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#tabbrowser-tabpanels[zen-split-view] {
|
#tabbrowser-tabpanels[zen-split-view] {
|
||||||
|
@ -112,10 +109,6 @@
|
||||||
cursor: ns-resize;
|
cursor: ns-resize;
|
||||||
}
|
}
|
||||||
|
|
||||||
#zen-split-views-box:not([hidden='true']) {
|
|
||||||
display: flex !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.zen-view-splitter-header-container {
|
.zen-view-splitter-header-container {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: calc(var(--zen-split-column-gap) / -2);
|
top: calc(var(--zen-split-column-gap) / -2);
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
{
|
{
|
||||||
const lazy = {};
|
const lazy = {};
|
||||||
|
|
||||||
|
@ -49,8 +52,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
class ZenPinnedTabManager extends ZenDOMOperatedFeature {
|
class ZenPinnedTabManager extends ZenDOMOperatedFeature {
|
||||||
MAX_ESSENTIALS_TABS = 12;
|
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
if (!this.enabled) {
|
if (!this.enabled) {
|
||||||
return;
|
return;
|
||||||
|
@ -87,10 +88,14 @@
|
||||||
|
|
||||||
onTabIconChanged(tab, url = null) {
|
onTabIconChanged(tab, url = null) {
|
||||||
const iconUrl = url ?? tab.iconImage.src;
|
const iconUrl = url ?? tab.iconImage.src;
|
||||||
if (!iconUrl) {
|
if (!iconUrl && tab.hasAttribute('zen-pin-id')) {
|
||||||
try {
|
try {
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
try {
|
try {
|
||||||
|
await this.promisePinnedCacheInitialized;
|
||||||
|
const pin = this._pinsCache?.find(
|
||||||
|
(pin) => pin.uuid === tab.getAttribute('zen-pin-id')
|
||||||
|
);
|
||||||
let favicon = await PlacesUtils.favicons.getFaviconForPage(
|
let favicon = await PlacesUtils.favicons.getFaviconForPage(
|
||||||
Services.io.newURI(pin.url)
|
Services.io.newURI(pin.url)
|
||||||
);
|
);
|
||||||
|
@ -156,6 +161,9 @@
|
||||||
await gZenWorkspaces.promiseSectionsInitialized;
|
await gZenWorkspaces.promiseSectionsInitialized;
|
||||||
await this._initializePinsCache();
|
await this._initializePinsCache();
|
||||||
await this._initializePinnedTabs(init);
|
await this._initializePinnedTabs(init);
|
||||||
|
if (init) {
|
||||||
|
this._resolveInitializedPinnedCache();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async _initializePinsCache() {
|
async _initializePinsCache() {
|
||||||
|
@ -659,7 +667,7 @@
|
||||||
for (let i = 0; i < tabs.length; i++) {
|
for (let i = 0; i < tabs.length; i++) {
|
||||||
let tab = tabs[i];
|
let tab = tabs[i];
|
||||||
const section = gZenWorkspaces.getEssentialsSection(tab);
|
const section = gZenWorkspaces.getEssentialsSection(tab);
|
||||||
if (section.children.length >= this.MAX_ESSENTIALS_TABS) {
|
if (!this.canEssentialBeAdded(tab)) {
|
||||||
movedAll = false;
|
movedAll = false;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -791,7 +799,7 @@
|
||||||
document.getElementById('context_zen-add-essential').hidden =
|
document.getElementById('context_zen-add-essential').hidden =
|
||||||
contextTab.getAttribute('zen-essential') ||
|
contextTab.getAttribute('zen-essential') ||
|
||||||
!!contextTab.group ||
|
!!contextTab.group ||
|
||||||
gBrowser._numZenEssentials >= this.MAX_ESSENTIALS_TABS;
|
!this.canEssentialBeAdded(contextTab);
|
||||||
document.getElementById('context_zen-remove-essential').hidden =
|
document.getElementById('context_zen-remove-essential').hidden =
|
||||||
!contextTab.getAttribute('zen-essential');
|
!contextTab.getAttribute('zen-essential');
|
||||||
document.getElementById('context_unpinTab').hidden =
|
document.getElementById('context_unpinTab').hidden =
|
||||||
|
@ -943,7 +951,7 @@
|
||||||
} else {
|
} else {
|
||||||
tab.setAttribute('zen-pinned-changed', 'true');
|
tab.setAttribute('zen-pinned-changed', 'true');
|
||||||
}
|
}
|
||||||
tab.style.setProperty('--zen-original-tab-icon', `url(${pin.iconUrl})`);
|
tab.style.setProperty('--zen-original-tab-icon', `url(${pin.iconUrl.spec})`);
|
||||||
}
|
}
|
||||||
|
|
||||||
removeTabContainersDragoverClass() {
|
removeTabContainersDragoverClass() {
|
||||||
|
@ -1004,6 +1012,16 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
canEssentialBeAdded(tab) {
|
||||||
|
return (
|
||||||
|
!(
|
||||||
|
(tab.getAttribute('usercontextid') || 0) !=
|
||||||
|
gZenWorkspaces.getActiveWorkspaceFromCache().containerTabId &&
|
||||||
|
gZenWorkspaces.containerSpecificEssentials
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
applyDragoverClass(event, draggedTab) {
|
applyDragoverClass(event, draggedTab) {
|
||||||
if (!this.enabled) {
|
if (!this.enabled) {
|
||||||
return;
|
return;
|
||||||
|
@ -1037,10 +1055,7 @@
|
||||||
shouldAddDragOverElement = true;
|
shouldAddDragOverElement = true;
|
||||||
}
|
}
|
||||||
} else if (essentialTabsTarget) {
|
} else if (essentialTabsTarget) {
|
||||||
if (
|
if (!draggedTab.hasAttribute('zen-essential') && this.canEssentialBeAdded(draggedTab)) {
|
||||||
!draggedTab.hasAttribute('zen-essential') &&
|
|
||||||
gBrowser._numZenEssentials < this.MAX_ESSENTIALS_TABS
|
|
||||||
) {
|
|
||||||
shouldAddDragOverElement = true;
|
shouldAddDragOverElement = true;
|
||||||
isVertical = false;
|
isVertical = false;
|
||||||
}
|
}
|
||||||
|
@ -1117,4 +1132,8 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
window.gZenPinnedTabManager = new ZenPinnedTabManager();
|
window.gZenPinnedTabManager = new ZenPinnedTabManager();
|
||||||
|
|
||||||
|
gZenPinnedTabManager.promisePinnedCacheInitialized = new Promise((resolve) => {
|
||||||
|
gZenPinnedTabManager._resolveInitializedPinnedCache = resolve;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
var ZenPinnedTabsStorage = {
|
var ZenPinnedTabsStorage = {
|
||||||
async init() {
|
async init() {
|
||||||
await this._ensureTable();
|
await this._ensureTable();
|
||||||
|
@ -106,8 +109,8 @@ var ZenPinnedTabsStorage = {
|
||||||
`
|
`
|
||||||
INSERT OR REPLACE INTO zen_pins (
|
INSERT OR REPLACE INTO zen_pins (
|
||||||
uuid, title, url, container_id, workspace_uuid, position,
|
uuid, title, url, container_id, workspace_uuid, position,
|
||||||
is_essential, is_group, parent_uuid, created_at, updated_at,
|
is_essential, is_group, parent_uuid, edited_title, created_at,
|
||||||
edited_title
|
updated_at
|
||||||
) VALUES (
|
) VALUES (
|
||||||
:uuid, :title, :url, :container_id, :workspace_uuid, :position,
|
:uuid, :title, :url, :container_id, :workspace_uuid, :position,
|
||||||
:is_essential, :is_group, :parent_uuid, :edited_title,
|
:is_essential, :is_group, :parent_uuid, :edited_title,
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
|
/*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
height: var(--zen-toolbar-height);
|
height: var(--zen-toolbar-height);
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
|
||||||
|
|
|
@ -356,6 +356,7 @@
|
||||||
margin-block: 0 !important;
|
margin-block: 0 !important;
|
||||||
margin-inline: 0 !important;
|
margin-inline: 0 !important;
|
||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
|
border-radius: calc(var(--border-radius-medium) - 2px);
|
||||||
}
|
}
|
||||||
/* Adjust padding for content */
|
/* Adjust padding for content */
|
||||||
& .tab-content {
|
& .tab-content {
|
||||||
|
@ -374,6 +375,27 @@
|
||||||
width: 14px;
|
width: 14px;
|
||||||
height: 14px;
|
height: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
& .tab-audio-button {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[soundplaying]:not([muted]) .tab-icon-stack::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
width: 110%;
|
||||||
|
height: 110%;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
opacity: 1;
|
||||||
|
background: url('chrome://browser/content/zen-images/note-indicator.svg') no-repeat;
|
||||||
|
top: -70%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
z-index: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
transition: opacity 0.8s ease;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* --- Essentials Glance Tab Specifics (Floating) --- */
|
/* --- Essentials Glance Tab Specifics (Floating) --- */
|
||||||
|
@ -1239,7 +1261,7 @@
|
||||||
|
|
||||||
padding-bottom: var(--zen-toolbox-padding);
|
padding-bottom: var(--zen-toolbox-padding);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
gap: calc(var(--zen-toolbox-padding) - 2px);
|
gap: 4px;
|
||||||
transition:
|
transition:
|
||||||
max-height 0.3s ease-out,
|
max-height 0.3s ease-out,
|
||||||
grid-template-columns 0.3s ease-out;
|
grid-template-columns 0.3s ease-out;
|
||||||
|
|
|
@ -9,8 +9,8 @@ function openGlanceOnTab(callback, close = true) {
|
||||||
gZenGlanceManager
|
gZenGlanceManager
|
||||||
.openGlance({
|
.openGlance({
|
||||||
url: 'https://example.com',
|
url: 'https://example.com',
|
||||||
x: 0,
|
clientX: 0,
|
||||||
y: 0,
|
clientY: 0,
|
||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
BROWSER_CHROME_MANIFESTS += [
|
BROWSER_CHROME_MANIFESTS += [
|
||||||
"compact_mode/browser.toml",
|
"compact_mode/browser.toml",
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
FINAL_LIBRARY = "xul"
|
FINAL_LIBRARY = "xul"
|
||||||
SOURCES += [
|
SOURCES += [
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
XPIDL_SOURCES += [
|
XPIDL_SOURCES += [
|
||||||
"nsIZenCommonUtils.idl",
|
"nsIZenCommonUtils.idl",
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
FINAL_LIBRARY = "xul"
|
FINAL_LIBRARY = "xul"
|
||||||
SOURCES += [
|
SOURCES += [
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
DIRS += [
|
DIRS += [
|
||||||
"common",
|
"common",
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
{
|
{
|
||||||
class ZenWorkspace extends MozXULElement {
|
class ZenWorkspace extends MozXULElement {
|
||||||
static get markup() {
|
static get markup() {
|
||||||
|
@ -6,7 +9,7 @@
|
||||||
<hbox class="zen-current-workspace-indicator-icon"></hbox>
|
<hbox class="zen-current-workspace-indicator-icon"></hbox>
|
||||||
<hbox class="zen-current-workspace-indicator-name"></hbox>
|
<hbox class="zen-current-workspace-indicator-name"></hbox>
|
||||||
</vbox>
|
</vbox>
|
||||||
<arrowscrollbox orient="vertical" tabindex="-1" class="workspace-arrowscrollbox">
|
<arrowscrollbox orient="vertical" class="workspace-arrowscrollbox">
|
||||||
<vbox class="zen-workspace-tabs-section zen-workspace-pinned-tabs-section">
|
<vbox class="zen-workspace-tabs-section zen-workspace-pinned-tabs-section">
|
||||||
<html:div class="vertical-pinned-tabs-container-separator"></html:div>
|
<html:div class="vertical-pinned-tabs-container-separator"></html:div>
|
||||||
</vbox>
|
</vbox>
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
{
|
{
|
||||||
class ZenWorkspaceIcons extends MozXULElement {
|
class ZenWorkspaceIcons extends MozXULElement {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|
|
@ -42,11 +42,6 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||||
this._resolveInitialized = resolve;
|
this._resolveInitialized = resolve;
|
||||||
});
|
});
|
||||||
|
|
||||||
workspaceIndicatorXUL = `
|
|
||||||
<hbox class="zen-current-workspace-indicator-icon"></hbox>
|
|
||||||
<hbox class="zen-current-workspace-indicator-name"></hbox>
|
|
||||||
`;
|
|
||||||
|
|
||||||
async waitForPromises() {
|
async waitForPromises() {
|
||||||
if (this.privateWindowOrDisabled) {
|
if (this.privateWindowOrDisabled) {
|
||||||
return;
|
return;
|
||||||
|
@ -858,6 +853,9 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||||
console.error('gZenWorkspaces: Error initializing theme picker', e);
|
console.error('gZenWorkspaces: Error initializing theme picker', e);
|
||||||
}
|
}
|
||||||
this.onWindowResize();
|
this.onWindowResize();
|
||||||
|
if (window.gZenSessionStore) {
|
||||||
|
await gZenSessionStore.promiseInitialized;
|
||||||
|
}
|
||||||
await this._selectStartPage();
|
await this._selectStartPage();
|
||||||
this._fixTabPositions();
|
this._fixTabPositions();
|
||||||
this._resolveInitialized();
|
this._resolveInitialized();
|
||||||
|
@ -915,7 +913,7 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||||
) {
|
) {
|
||||||
this.log(`Found tab to select: ${this._tabToSelect}, ${tabs.length}`);
|
this.log(`Found tab to select: ${this._tabToSelect}, ${tabs.length}`);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
gBrowser.selectedTab = tabs[this._tabToSelect];
|
gBrowser.selectedTab = gZenGlanceManager.getTabOrGlanceParent(tabs[this._tabToSelect]);
|
||||||
this._removedByStartupPage = true;
|
this._removedByStartupPage = true;
|
||||||
gBrowser.removeTab(this._tabToRemoveForEmpty, {
|
gBrowser.removeTab(this._tabToRemoveForEmpty, {
|
||||||
skipSessionStore: true,
|
skipSessionStore: true,
|
||||||
|
@ -2325,7 +2323,7 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||||
existingTransform = container.style.transform;
|
existingTransform = container.style.transform;
|
||||||
}
|
}
|
||||||
if (shouldAnimate) {
|
if (shouldAnimate) {
|
||||||
container.style.transform = newTransform;
|
container.style.transform = existingTransform;
|
||||||
animations.push(
|
animations.push(
|
||||||
gZenUIManager.motion.animate(
|
gZenUIManager.motion.animate(
|
||||||
container,
|
container,
|
||||||
|
@ -2542,8 +2540,6 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
setTimeout(gURLBar.formatValue.bind(gURLBar), 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async _fixCtrlTabBehavior() {
|
async _fixCtrlTabBehavior() {
|
||||||
|
@ -2941,6 +2937,9 @@ var gZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||||
}
|
}
|
||||||
|
|
||||||
_initializeWorkspaceTabContextMenus() {
|
_initializeWorkspaceTabContextMenus() {
|
||||||
|
if (this.privateWindowOrDisabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
const menu = document.createXULElement('menu');
|
const menu = document.createXULElement('menu');
|
||||||
menu.setAttribute('id', 'context-zen-change-workspace-tab');
|
menu.setAttribute('id', 'context-zen-change-workspace-tab');
|
||||||
menu.setAttribute('data-l10n-id', 'context-zen-change-workspace-tab');
|
menu.setAttribute('data-l10n-id', 'context-zen-change-workspace-tab');
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
"binaryName": "zen",
|
"binaryName": "zen",
|
||||||
"version": {
|
"version": {
|
||||||
"product": "firefox",
|
"product": "firefox",
|
||||||
"version": "138.0.4",
|
"version": "139.0",
|
||||||
"candidate": "139.0"
|
"candidate": "139.0"
|
||||||
},
|
},
|
||||||
"buildOptions": {
|
"buildOptions": {
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
"brandShortName": "Zen",
|
"brandShortName": "Zen",
|
||||||
"brandFullName": "Zen Browser",
|
"brandFullName": "Zen Browser",
|
||||||
"release": {
|
"release": {
|
||||||
"displayVersion": "1.12.8b",
|
"displayVersion": "1.12.9b",
|
||||||
"github": {
|
"github": {
|
||||||
"repo": "zen-browser/desktop"
|
"repo": "zen-browser/desktop"
|
||||||
},
|
},
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue