mirror of
https://github.com/zen-browser/desktop.git
synced 2026-06-23 19:29:39 +00:00
Compare commits
11 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f9ae52c63 | ||
|
|
3c3322058e | ||
|
|
31bb9d606d | ||
|
|
b6cb8338e3 | ||
|
|
183c583841 | ||
|
|
40080a25dc | ||
|
|
733061fbe3 | ||
|
|
61e631902c | ||
|
|
0cd67f882a | ||
|
|
bc6e4676f4 | ||
|
|
e331f07265 |
@@ -34,8 +34,8 @@ Zen is a firefox-based browser with the aim of pushing your productivity to a ne
|
|||||||
|
|
||||||
### Firefox Versions
|
### Firefox Versions
|
||||||
|
|
||||||
- [`Release`](https://zen-browser.app/download) - Is currently built using Firefox version `152.0`! 🚀
|
- [`Release`](https://zen-browser.app/download) - Is currently built using Firefox version `152.0.2`! 🚀
|
||||||
- [`Twilight`](https://zen-browser.app/download?twilight) - Is currently built using Firefox version `RC 152.0`!
|
- [`Twilight`](https://zen-browser.app/download?twilight) - Is currently built using Firefox version `RC 152.0.2`!
|
||||||
|
|
||||||
### Contributing
|
### Contributing
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
01604ce201d4de3c6d4b930d271ce4fe05e8d0c8
|
16659f37ea7611e9444cacbbdb33831d4dbbe0db
|
||||||
@@ -19,13 +19,19 @@ tab-context-zen-add-essential-badge = { $num } / { $max }
|
|||||||
tab-context-zen-remove-essential =
|
tab-context-zen-remove-essential =
|
||||||
.label = Remove from Essentials
|
.label = Remove from Essentials
|
||||||
.accesskey = R
|
.accesskey = R
|
||||||
tab-context-zen-replace-pinned-url-with-current =
|
tab-context-zen-edit-pinned-page =
|
||||||
.label =
|
.label =
|
||||||
{ $isEssential ->
|
{ $isEssential ->
|
||||||
[true] Replace Essential URL with Current
|
[true] Edit Essential URL
|
||||||
*[false] Replace Pinned URL with Current
|
*[false] Edit Pinned URL
|
||||||
}
|
}
|
||||||
|
.accesskey = P
|
||||||
|
tab-context-zen-replace-pinned-url-with-current =
|
||||||
|
.label = Replace with Current URL
|
||||||
.accesskey = C
|
.accesskey = C
|
||||||
|
tab-context-zen-edit-pinned-url =
|
||||||
|
.label = Edit...
|
||||||
|
.accesskey = E
|
||||||
tab-context-zen-edit-title =
|
tab-context-zen-edit-title =
|
||||||
.label = Change Label...
|
.label = Change Label...
|
||||||
tab-context-zen-edit-icon =
|
tab-context-zen-edit-icon =
|
||||||
@@ -55,6 +61,10 @@ zen-general-confirm =
|
|||||||
.label = Confirm
|
.label = Confirm
|
||||||
|
|
||||||
zen-pinned-tab-replaced = Pinned tab URL has been replaced with the current URL!
|
zen-pinned-tab-replaced = Pinned tab URL has been replaced with the current URL!
|
||||||
|
zen-pinned-tab-url-edited = Pinned tab URL has been updated!
|
||||||
|
zen-pinned-tab-url-invalid = That doesn't look like a valid URL.
|
||||||
|
zen-pinned-tab-edit-url-title = Edit Pinned URL
|
||||||
|
zen-pinned-tab-edit-url-label = Enter the URL this pinned tab should point to:
|
||||||
zen-tabs-renamed = Tab has been successfully renamed!
|
zen-tabs-renamed = Tab has been successfully renamed!
|
||||||
zen-background-tab-opened-toast = New background tab opened!
|
zen-background-tab-opened-toast = New background tab opened!
|
||||||
zen-workspace-renamed-toast = Workspace has been successfully renamed!
|
zen-workspace-renamed-toast = Workspace has been successfully renamed!
|
||||||
|
|||||||
@@ -25,3 +25,9 @@ zen-space-routing-open-in = Open In
|
|||||||
zen-space-routing-url = URL
|
zen-space-routing-url = URL
|
||||||
|
|
||||||
zen-space-routing-tab-routed-toast = New tab opened in { $targetWorkspace }
|
zen-space-routing-tab-routed-toast = New tab opened in { $targetWorkspace }
|
||||||
|
tab-context-zen-add-domain-to-sr =
|
||||||
|
.label =
|
||||||
|
{ $tabCount ->
|
||||||
|
[one] Add Route for Domain
|
||||||
|
*[other] Add Route for Domains
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,10 +7,6 @@
|
|||||||
value: true
|
value: true
|
||||||
condition: "defined(XP_WIN)"
|
condition: "defined(XP_WIN)"
|
||||||
|
|
||||||
- name: widget.windows.mica.popups
|
|
||||||
value: true
|
|
||||||
condition: "defined(XP_WIN)"
|
|
||||||
|
|
||||||
# 1 = DWMSBT_MAINWINDOW
|
# 1 = DWMSBT_MAINWINDOW
|
||||||
# 2 = DWMSBT_TRANSIENTWINDOW (default, also used for popups)
|
# 2 = DWMSBT_TRANSIENTWINDOW (default, also used for popups)
|
||||||
# 3 = DWMSBT_TABBEDWINDOW
|
# 3 = DWMSBT_TABBEDWINDOW
|
||||||
|
|||||||
18
src/browser/base/content/aboutDialog-css.patch
Normal file
18
src/browser/base/content/aboutDialog-css.patch
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
diff --git a/browser/base/content/aboutDialog.css b/browser/base/content/aboutDialog.css
|
||||||
|
index 017125bc2510e5f5e317a5e78c40d6aa9ded76ca..d343d8c62a2251e3c3a33ae8f2ab9c4c68218c22 100644
|
||||||
|
--- a/browser/base/content/aboutDialog.css
|
||||||
|
+++ b/browser/base/content/aboutDialog.css
|
||||||
|
@@ -135,6 +135,13 @@
|
||||||
|
margin: 0 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
+#trademark {
|
||||||
|
+ font-size: xx-small;
|
||||||
|
+ text-align: center;
|
||||||
|
+ margin-block: 10px;
|
||||||
|
+ color: var(--text-color-deemphasized);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
#currentChannel {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
@@ -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 3ffd464b960a4299a7dd0cd87e4fc2f781b9d593..ef9f42d1f0196902b4af31f4496891fcd6319831 100644
|
index 3ffd464b960a4299a7dd0cd87e4fc2f781b9d593..7a831c8ee2b73bb89bf8a82ac24958b55c16a5aa 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
|
||||||
@@ -102,10 +102,6 @@
|
@@ -102,10 +102,6 @@
|
||||||
@@ -39,8 +39,8 @@ index 3ffd464b960a4299a7dd0cd87e4fc2f781b9d593..ef9f42d1f0196902b4af31f4496891fc
|
|||||||
<label is="text-link" class="bottom-link" useoriginprincipal="true" href="about:license" data-l10n-id="bottomLinks-license"/>
|
<label is="text-link" class="bottom-link" useoriginprincipal="true" href="about:license" data-l10n-id="bottomLinks-license"/>
|
||||||
- <label is="text-link" class="bottom-link" href="https://www.mozilla.org/about/legal/terms/firefox/" data-l10n-id="bottom-links-terms"/>
|
- <label is="text-link" class="bottom-link" href="https://www.mozilla.org/about/legal/terms/firefox/" data-l10n-id="bottom-links-terms"/>
|
||||||
- <label is="text-link" class="bottom-link" href="https://www.mozilla.org/privacy/firefox/?utm_source=firefox-browser&utm_medium=firefox-desktop&utm_campaign=about-dialog" data-l10n-id="bottom-links-privacy"/>
|
- <label is="text-link" class="bottom-link" href="https://www.mozilla.org/privacy/firefox/?utm_source=firefox-browser&utm_medium=firefox-desktop&utm_campaign=about-dialog" data-l10n-id="bottom-links-privacy"/>
|
||||||
+ <label is="text-link" class="bottom-link" href="about:rights" data-l10n-id="bottomLinks-rights"/>
|
+ <label is="text-link" class="bottom-link" href="about:rights" data-l10n-id="bottom-links-terms"/>
|
||||||
+ <label is="text-link" class="bottom-link" href="https://www.zen-browser.app/privacy-policy/" data-l10n-id="bottomLinks-privacy"/>
|
+ <label is="text-link" class="bottom-link" href="https://www.zen-browser.app/privacy-policy/" data-l10n-id="bottom-links-privacy"/>
|
||||||
</hbox>
|
</hbox>
|
||||||
<description id="trademark" data-l10n-id="trademarkInfo"></description>
|
<description id="trademark" data-l10n-id="trademarkInfo"></description>
|
||||||
</vbox>
|
</vbox>
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
<command id="cmd_zenToggleTabsOnRight" />
|
<command id="cmd_zenToggleTabsOnRight" />
|
||||||
|
|
||||||
<command id="cmd_zenReplacePinnedUrlWithCurrent" />
|
<command id="cmd_zenReplacePinnedUrlWithCurrent" />
|
||||||
|
<command id="cmd_zenEditPinnedUrl" />
|
||||||
<command id="cmd_contextZenAddToEssentials" />
|
<command id="cmd_contextZenAddToEssentials" />
|
||||||
<command id="cmd_contextZenRemoveFromEssentials" />
|
<command id="cmd_contextZenRemoveFromEssentials" />
|
||||||
|
|
||||||
|
|||||||
13
src/browser/components/nova/NovaPrefs-sys-mjs.patch
Normal file
13
src/browser/components/nova/NovaPrefs-sys-mjs.patch
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
diff --git a/browser/components/nova/NovaPrefs.sys.mjs b/browser/components/nova/NovaPrefs.sys.mjs
|
||||||
|
index 3d22c881c481643fcffbc581523905a1847a7d41..453dd4d9c43d7483c037a993afbf2b854533497c 100644
|
||||||
|
--- a/browser/components/nova/NovaPrefs.sys.mjs
|
||||||
|
+++ b/browser/components/nova/NovaPrefs.sys.mjs
|
||||||
|
@@ -18,7 +18,7 @@ const PLATFORM_PREFS = (() => {
|
||||||
|
})();
|
||||||
|
|
||||||
|
function applyNovaPlatformDefaults() {
|
||||||
|
- const on = Services.prefs.getBoolPref("browser.nova.enabled", false);
|
||||||
|
+ const on = true;
|
||||||
|
const defaults = Services.prefs.getDefaultBranch("");
|
||||||
|
for (const pref of PLATFORM_PREFS) {
|
||||||
|
defaults.setBoolPref(pref, on);
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
diff --git a/browser/components/preferences/preferences.js b/browser/components/preferences/preferences.js
|
diff --git a/browser/components/preferences/preferences.js b/browser/components/preferences/preferences.js
|
||||||
index 57add34d876fb885275f1147209c6fbeee367a7c..5f4616d5f7d3d077326246e843775f58c293ee48 100644
|
index 57add34d876fb885275f1147209c6fbeee367a7c..be0ab43b299317c0022a5e719f47a070c1574714 100644
|
||||||
--- a/browser/components/preferences/preferences.js
|
--- a/browser/components/preferences/preferences.js
|
||||||
+++ b/browser/components/preferences/preferences.js
|
+++ b/browser/components/preferences/preferences.js
|
||||||
@@ -132,6 +132,7 @@ ChromeUtils.defineLazyGetter(this, "gSubDialog", function () {
|
@@ -132,6 +132,7 @@ ChromeUtils.defineLazyGetter(this, "gSubDialog", function () {
|
||||||
@@ -39,9 +39,12 @@ index 57add34d876fb885275f1147209c6fbeee367a7c..5f4616d5f7d3d077326246e843775f58
|
|||||||
|
|
||||||
// Restore the cached Firefox Labs nav button visibility so it shows
|
// Restore the cached Firefox Labs nav button visibility so it shows
|
||||||
// immediately when recipes are expected to be available, before
|
// immediately when recipes are expected to be available, before
|
||||||
@@ -653,7 +660,7 @@ async function gotoPref(
|
@@ -651,9 +658,9 @@ async function gotoPref(
|
||||||
|
let redesignEnabled = srdSectionPrefs.all;
|
||||||
|
let categories = document.getElementById("categories");
|
||||||
const kDefaultCategoryInternalName = redesignEnabled
|
const kDefaultCategoryInternalName = redesignEnabled
|
||||||
? "paneSync"
|
- ? "paneSync"
|
||||||
|
+ ? "paneTabsBrowsing"
|
||||||
: "paneGeneral";
|
: "paneGeneral";
|
||||||
- const kDefaultCategory = redesignEnabled ? "sync" : "general";
|
- const kDefaultCategory = redesignEnabled ? "sync" : "general";
|
||||||
+ const kDefaultCategory = redesignEnabled ? "tabsBrowsing" : "general";
|
+ const kDefaultCategory = redesignEnabled ? "tabsBrowsing" : "general";
|
||||||
|
|||||||
@@ -1,8 +1,23 @@
|
|||||||
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 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..3036768b8911b4fbc28df7528f7189d9ea21b6f6 100644
|
index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..b9a1cfe3a4a5035d9b06b0b3826a97c52cfcb39e 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
|
||||||
@@ -220,7 +220,7 @@
|
@@ -197,8 +197,12 @@
|
||||||
|
XPCOMUtils.defineLazyPreferenceGetter(
|
||||||
|
this,
|
||||||
|
"_sidebarPositionStart",
|
||||||
|
- "sidebar.position_start",
|
||||||
|
- true
|
||||||
|
+ "zen.tabs.vertical.right-side",
|
||||||
|
+ true,
|
||||||
|
+ null,
|
||||||
|
+ newValue => {
|
||||||
|
+ return !newValue;
|
||||||
|
+ }
|
||||||
|
);
|
||||||
|
|
||||||
|
if (gMultiProcessBrowser) {
|
||||||
|
@@ -220,7 +224,7 @@
|
||||||
|
|
||||||
this.tooltip = "tabbrowser-tab-tooltip";
|
this.tooltip = "tabbrowser-tab-tooltip";
|
||||||
|
|
||||||
@@ -11,7 +26,7 @@ index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..3036768b8911b4fbc28df7528f7189d9
|
|||||||
this.tabDragAndDrop.init();
|
this.tabDragAndDrop.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -444,7 +444,7 @@
|
@@ -444,7 +448,7 @@
|
||||||
// and we're not hitting the scroll buttons.
|
// and we're not hitting the scroll buttons.
|
||||||
if (
|
if (
|
||||||
event.button != 0 ||
|
event.button != 0 ||
|
||||||
@@ -20,7 +35,7 @@ index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..3036768b8911b4fbc28df7528f7189d9
|
|||||||
event.composedTarget.localName == "toolbarbutton"
|
event.composedTarget.localName == "toolbarbutton"
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
@@ -525,7 +525,6 @@
|
@@ -525,7 +529,6 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else if (isTabGroupLabel(event.target)) {
|
} else if (isTabGroupLabel(event.target)) {
|
||||||
@@ -28,7 +43,7 @@ index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..3036768b8911b4fbc28df7528f7189d9
|
|||||||
} else if (
|
} else if (
|
||||||
event.originalTarget.closest("scrollbox") &&
|
event.originalTarget.closest("scrollbox") &&
|
||||||
!Services.prefs.getBoolPref(
|
!Services.prefs.getBoolPref(
|
||||||
@@ -561,6 +560,9 @@
|
@@ -561,6 +564,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
on_keydown(event) {
|
on_keydown(event) {
|
||||||
@@ -38,7 +53,7 @@ index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..3036768b8911b4fbc28df7528f7189d9
|
|||||||
let { altKey, shiftKey } = event;
|
let { altKey, shiftKey } = event;
|
||||||
let [accel, nonAccel] =
|
let [accel, nonAccel] =
|
||||||
AppConstants.platform == "macosx"
|
AppConstants.platform == "macosx"
|
||||||
@@ -755,7 +757,6 @@
|
@@ -755,7 +761,6 @@
|
||||||
this._updateCloseButtons();
|
this._updateCloseButtons();
|
||||||
|
|
||||||
if (!this.#animatingGroups.size) {
|
if (!this.#animatingGroups.size) {
|
||||||
@@ -46,7 +61,7 @@ index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..3036768b8911b4fbc28df7528f7189d9
|
|||||||
}
|
}
|
||||||
|
|
||||||
document
|
document
|
||||||
@@ -822,7 +823,7 @@
|
@@ -822,7 +827,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
get newTabButton() {
|
get newTabButton() {
|
||||||
@@ -55,7 +70,7 @@ index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..3036768b8911b4fbc28df7528f7189d9
|
|||||||
}
|
}
|
||||||
|
|
||||||
get verticalMode() {
|
get verticalMode() {
|
||||||
@@ -838,6 +839,7 @@
|
@@ -838,6 +843,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
get overflowing() {
|
get overflowing() {
|
||||||
@@ -63,7 +78,7 @@ index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..3036768b8911b4fbc28df7528f7189d9
|
|||||||
return this.hasAttribute("overflow");
|
return this.hasAttribute("overflow");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -851,29 +853,56 @@
|
@@ -851,29 +857,56 @@
|
||||||
if (pinnedChildren?.at(-1)?.id == "pinned-tabs-container-periphery") {
|
if (pinnedChildren?.at(-1)?.id == "pinned-tabs-container-periphery") {
|
||||||
pinnedChildren.pop();
|
pinnedChildren.pop();
|
||||||
}
|
}
|
||||||
@@ -93,7 +108,7 @@ index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..3036768b8911b4fbc28df7528f7189d9
|
|||||||
+ } else if (!isTab(tab)) {
|
+ } else if (!isTab(tab)) {
|
||||||
+ tabs.splice(i, 1);
|
+ tabs.splice(i, 1);
|
||||||
+ }
|
+ }
|
||||||
}
|
+ }
|
||||||
+ };
|
+ };
|
||||||
+ expandTabs(pinnedTabs);
|
+ expandTabs(pinnedTabs);
|
||||||
+ expandTabs(unpinnedChildren);
|
+ expandTabs(unpinnedChildren);
|
||||||
@@ -114,7 +129,7 @@ index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..3036768b8911b4fbc28df7528f7189d9
|
|||||||
+ // remove the separator from the list
|
+ // remove the separator from the list
|
||||||
+ allTabs.splice(i, 1);
|
+ allTabs.splice(i, 1);
|
||||||
+ i--;
|
+ i--;
|
||||||
+ }
|
}
|
||||||
+ i++;
|
+ i++;
|
||||||
}
|
}
|
||||||
-
|
-
|
||||||
@@ -130,7 +145,7 @@ index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..3036768b8911b4fbc28df7528f7189d9
|
|||||||
}
|
}
|
||||||
|
|
||||||
get allSplitViews() {
|
get allSplitViews() {
|
||||||
@@ -958,29 +987,28 @@
|
@@ -958,29 +991,28 @@
|
||||||
return this.#focusableItems;
|
return this.#focusableItems;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,7 +185,7 @@ index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..3036768b8911b4fbc28df7528f7189d9
|
|||||||
this.#focusableItems = focusableItems;
|
this.#focusableItems = focusableItems;
|
||||||
|
|
||||||
return this.#focusableItems;
|
return this.#focusableItems;
|
||||||
@@ -993,6 +1021,7 @@
|
@@ -993,6 +1025,7 @@
|
||||||
* focusable (ex, we don't want the splitview container to be focusable, only its children).
|
* focusable (ex, we don't want the splitview container to be focusable, only its children).
|
||||||
*/
|
*/
|
||||||
get dragAndDropElements() {
|
get dragAndDropElements() {
|
||||||
@@ -178,7 +193,7 @@ index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..3036768b8911b4fbc28df7528f7189d9
|
|||||||
if (this.#dragAndDropElements) {
|
if (this.#dragAndDropElements) {
|
||||||
return this.#dragAndDropElements;
|
return this.#dragAndDropElements;
|
||||||
}
|
}
|
||||||
@@ -1063,6 +1092,7 @@
|
@@ -1063,6 +1096,7 @@
|
||||||
_invalidateCachedTabs() {
|
_invalidateCachedTabs() {
|
||||||
this.#allTabs = null;
|
this.#allTabs = null;
|
||||||
this._invalidateCachedVisibleTabs();
|
this._invalidateCachedVisibleTabs();
|
||||||
@@ -186,7 +201,7 @@ index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..3036768b8911b4fbc28df7528f7189d9
|
|||||||
}
|
}
|
||||||
|
|
||||||
_invalidateCachedVisibleTabs() {
|
_invalidateCachedVisibleTabs() {
|
||||||
@@ -1082,7 +1112,8 @@
|
@@ -1082,7 +1116,8 @@
|
||||||
|
|
||||||
isContainerVerticalPinnedGrid(tab) {
|
isContainerVerticalPinnedGrid(tab) {
|
||||||
return (
|
return (
|
||||||
@@ -196,7 +211,7 @@ index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..3036768b8911b4fbc28df7528f7189d9
|
|||||||
this.verticalMode &&
|
this.verticalMode &&
|
||||||
this.hasAttribute("expanded") &&
|
this.hasAttribute("expanded") &&
|
||||||
!this.expandOnHover
|
!this.expandOnHover
|
||||||
@@ -1176,7 +1207,7 @@
|
@@ -1176,7 +1211,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.
|
||||||
@@ -205,7 +220,7 @@ index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..3036768b8911b4fbc28df7528f7189d9
|
|||||||
}
|
}
|
||||||
|
|
||||||
node.before(tab);
|
node.before(tab);
|
||||||
@@ -1271,7 +1302,7 @@
|
@@ -1271,7 +1306,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.
|
||||||
@@ -214,7 +229,7 @@ index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..3036768b8911b4fbc28df7528f7189d9
|
|||||||
const newTab2 = this.newTabButton;
|
const newTab2 = this.newTabButton;
|
||||||
const newTabVertical = document.getElementById(
|
const newTabVertical = document.getElementById(
|
||||||
"vertical-tabs-newtab-button"
|
"vertical-tabs-newtab-button"
|
||||||
@@ -1376,8 +1407,10 @@
|
@@ -1376,8 +1411,10 @@
|
||||||
*/
|
*/
|
||||||
_handleTabSelect(aInstant) {
|
_handleTabSelect(aInstant) {
|
||||||
let selectedTab = this.selectedItem;
|
let selectedTab = this.selectedItem;
|
||||||
@@ -225,7 +240,7 @@ index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..3036768b8911b4fbc28df7528f7189d9
|
|||||||
selectedTab._notselectedsinceload = false;
|
selectedTab._notselectedsinceload = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1386,7 +1419,7 @@
|
@@ -1386,7 +1423,7 @@
|
||||||
* @param {boolean} [shouldScrollInstantly=false]
|
* @param {boolean} [shouldScrollInstantly=false]
|
||||||
*/
|
*/
|
||||||
#ensureTabIsVisible(tab, shouldScrollInstantly = false) {
|
#ensureTabIsVisible(tab, shouldScrollInstantly = false) {
|
||||||
@@ -234,7 +249,7 @@ index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..3036768b8911b4fbc28df7528f7189d9
|
|||||||
if (arrowScrollbox?.overflowing) {
|
if (arrowScrollbox?.overflowing) {
|
||||||
arrowScrollbox.ensureElementIsVisible(tab, shouldScrollInstantly);
|
arrowScrollbox.ensureElementIsVisible(tab, shouldScrollInstantly);
|
||||||
}
|
}
|
||||||
@@ -1513,7 +1546,7 @@
|
@@ -1513,7 +1550,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
_notifyBackgroundTab(aTab) {
|
_notifyBackgroundTab(aTab) {
|
||||||
|
|||||||
@@ -16,3 +16,4 @@ category app-startup nsBrowserGlue @mozilla.org/browser/browserglue;1 applicatio
|
|||||||
#include common/Components.manifest
|
#include common/Components.manifest
|
||||||
#include sessionstore/SessionComponents.manifest
|
#include sessionstore/SessionComponents.manifest
|
||||||
#include live-folders/LiveFoldersComponents.manifest
|
#include live-folders/LiveFoldersComponents.manifest
|
||||||
|
#include space-routing/SpaceRoutingComponents.manifest
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
|
|||||||
init() {
|
init() {
|
||||||
this.#panel = document.getElementById("PanelUI-zen-emojis-picker");
|
this.#panel = document.getElementById("PanelUI-zen-emojis-picker");
|
||||||
this.#panel.addEventListener("popupshowing", this);
|
this.#panel.addEventListener("popupshowing", this);
|
||||||
|
this.#panel.addEventListener("popupshown", this);
|
||||||
this.#panel.addEventListener("popuphidden", this);
|
this.#panel.addEventListener("popuphidden", this);
|
||||||
this.#panel.addEventListener("command", this);
|
this.#panel.addEventListener("command", this);
|
||||||
this.searchInput.addEventListener("input", this);
|
this.searchInput.addEventListener("input", this);
|
||||||
@@ -57,6 +58,9 @@ class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
|
|||||||
case "popupshowing":
|
case "popupshowing":
|
||||||
this.#onPopupShowing(event);
|
this.#onPopupShowing(event);
|
||||||
break;
|
break;
|
||||||
|
case "popupshown":
|
||||||
|
this.#onPopupShown(event);
|
||||||
|
break;
|
||||||
case "popuphidden":
|
case "popuphidden":
|
||||||
this.#onPopupHidden(event);
|
this.#onPopupHidden(event);
|
||||||
break;
|
break;
|
||||||
@@ -103,17 +107,20 @@ class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
|
|||||||
return document.getElementById("PanelUI-zen-emojis-picker-search");
|
return document.getElementById("PanelUI-zen-emojis-picker-search");
|
||||||
}
|
}
|
||||||
|
|
||||||
#changePage(toSvg = false) {
|
#changePage(toSvg = false, { animate = true } = {}) {
|
||||||
|
const pages = document.getElementById("PanelUI-zen-emojis-picker-pages");
|
||||||
const itemToScroll = toSvg
|
const itemToScroll = toSvg
|
||||||
? this.svgList
|
? this.svgList
|
||||||
: document
|
: pages.querySelector('[emojis="true"]');
|
||||||
.getElementById("PanelUI-zen-emojis-picker-pages")
|
if (animate) {
|
||||||
.querySelector('[emojis="true"]');
|
|
||||||
itemToScroll.scrollIntoView({
|
itemToScroll.scrollIntoView({
|
||||||
behavior: "smooth",
|
behavior: "smooth",
|
||||||
block: "nearest",
|
block: "nearest",
|
||||||
inline: "start",
|
inline: "start",
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
pages.scrollLeft = toSvg ? itemToScroll.offsetLeft : 0;
|
||||||
|
}
|
||||||
const button = document.getElementById(
|
const button = document.getElementById(
|
||||||
`PanelUI-zen-emojis-picker-change-${toSvg ? "svg" : "emojis"}`
|
`PanelUI-zen-emojis-picker-change-${toSvg ? "svg" : "emojis"}`
|
||||||
);
|
);
|
||||||
@@ -180,9 +187,6 @@ class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
|
|||||||
});
|
});
|
||||||
emojiList.appendChild(item);
|
emojiList.appendChild(item);
|
||||||
}
|
}
|
||||||
setTimeout(() => {
|
|
||||||
this.searchInput.focus();
|
|
||||||
}, 500);
|
|
||||||
}
|
}
|
||||||
const svgList = this.svgList;
|
const svgList = this.svgList;
|
||||||
for (const icon of SVG_ICONS) {
|
for (const icon of SVG_ICONS) {
|
||||||
@@ -199,14 +203,23 @@ class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#onPopupShown(event) {
|
||||||
|
if (event.target !== this.#panel) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const allowEmojis = !this.#panel.hasAttribute("only-svg-icons");
|
||||||
|
if (allowEmojis) {
|
||||||
|
this.searchInput.focus({ preventScroll: true });
|
||||||
|
}
|
||||||
|
this.#changePage(false, { animate: false });
|
||||||
|
}
|
||||||
|
|
||||||
#onPopupHidden(event) {
|
#onPopupHidden(event) {
|
||||||
if (event.target !== this.#panel) {
|
if (event.target !== this.#panel) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.#clearEmojis();
|
this.#clearEmojis();
|
||||||
|
|
||||||
this.#changePage(false);
|
|
||||||
|
|
||||||
const emojiList = this.emojiList;
|
const emojiList = this.emojiList;
|
||||||
emojiList.innerHTML = "";
|
emojiList.innerHTML = "";
|
||||||
|
|
||||||
|
|||||||
@@ -78,6 +78,9 @@ document.addEventListener(
|
|||||||
case "cmd_zenReplacePinnedUrlWithCurrent":
|
case "cmd_zenReplacePinnedUrlWithCurrent":
|
||||||
gZenPinnedTabManager.replacePinnedUrlWithCurrent();
|
gZenPinnedTabManager.replacePinnedUrlWithCurrent();
|
||||||
break;
|
break;
|
||||||
|
case "cmd_zenEditPinnedUrl":
|
||||||
|
gZenPinnedTabManager.editPinnedUrl();
|
||||||
|
break;
|
||||||
case "cmd_contextZenAddToEssentials":
|
case "cmd_contextZenAddToEssentials":
|
||||||
gZenPinnedTabManager.addToEssentials();
|
gZenPinnedTabManager.addToEssentials();
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -723,8 +723,11 @@
|
|||||||
const { isNearLeftEdge, isNearRightEdge } =
|
const { isNearLeftEdge, isNearRightEdge } =
|
||||||
this.#shouldSwitchSpace(event);
|
this.#shouldSwitchSpace(event);
|
||||||
if (isNearLeftEdge || isNearRightEdge) {
|
if (isNearLeftEdge || isNearRightEdge) {
|
||||||
if (!this.#changeSpaceTimer) {
|
if (!this.#changeSpaceTimer && !this.#isOutOfWindow) {
|
||||||
this.#changeSpaceTimer = setTimeout(() => {
|
this.#changeSpaceTimer = setTimeout(() => {
|
||||||
|
if (this.#isOutOfWindow) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.clearDragOverVisuals();
|
this.clearDragOverVisuals();
|
||||||
gZenWorkspaces
|
gZenWorkspaces
|
||||||
.changeWorkspaceShortcut(
|
.changeWorkspaceShortcut(
|
||||||
@@ -956,8 +959,10 @@
|
|||||||
if (ownerGlobal?.gZenCompactModeManager) {
|
if (ownerGlobal?.gZenCompactModeManager) {
|
||||||
// Sometimes, dragend doesn't always get called when dragging
|
// Sometimes, dragend doesn't always get called when dragging
|
||||||
// to different windows, see gh-8643.
|
// to different windows, see gh-8643.
|
||||||
|
requestAnimationFrame(() => {
|
||||||
delete ownerGlobal.gZenCompactModeManager._isTabBeingDragged;
|
delete ownerGlobal.gZenCompactModeManager._isTabBeingDragged;
|
||||||
ownerGlobal.gZenCompactModeManager._clearAllHoverStates();
|
ownerGlobal.gZenCompactModeManager._clearAllHoverStates();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
this.clearSpaceSwitchTimer();
|
this.clearSpaceSwitchTimer();
|
||||||
gZenFolders.highlightGroupOnDragOver(null);
|
gZenFolders.highlightGroupOnDragOver(null);
|
||||||
|
|||||||
@@ -53,6 +53,10 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
|
|||||||
#setupEventListeners() {
|
#setupEventListeners() {
|
||||||
window.addEventListener("TabClose", this.onTabClose.bind(this));
|
window.addEventListener("TabClose", this.onTabClose.bind(this));
|
||||||
window.addEventListener("TabSelect", this.onLocationChange.bind(this));
|
window.addEventListener("TabSelect", this.onLocationChange.bind(this));
|
||||||
|
window.addEventListener(
|
||||||
|
"MozDOMFullscreen:Entered",
|
||||||
|
this.onFullscreenEntered.bind(this)
|
||||||
|
);
|
||||||
|
|
||||||
document
|
document
|
||||||
.getElementById("tabbrowser-tabpanels")
|
.getElementById("tabbrowser-tabpanels")
|
||||||
@@ -1414,6 +1418,23 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle DOM Fullscreen request while inside glance
|
||||||
|
*
|
||||||
|
* @param {Event} event - The MozDOMFullscreen:Entered event
|
||||||
|
*/
|
||||||
|
onFullscreenEntered(event) {
|
||||||
|
const browser = this.#currentBrowser;
|
||||||
|
|
||||||
|
if (!browser) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.target === browser) {
|
||||||
|
this.fullyOpenGlance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manage tab close for glance tabs
|
* Manage tab close for glance tabs
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1232,20 +1232,39 @@ class nsZenWindowSync {
|
|||||||
activeIndex = Math.min(activeIndex, entries.length - 1);
|
activeIndex = Math.min(activeIndex, entries.length - 1);
|
||||||
activeIndex = Math.max(activeIndex, 0);
|
activeIndex = Math.max(activeIndex, 0);
|
||||||
let entryToUse = (entries[activeIndex] || entries[0]) ?? null;
|
let entryToUse = (entries[activeIndex] || entries[0]) ?? null;
|
||||||
const initialState = {
|
this.#setPinnedInitialState(
|
||||||
entry: {
|
aTab,
|
||||||
url: entryToUse?.url,
|
{ url: entryToUse?.url, title: entryToUse?.title },
|
||||||
title: entryToUse?.title,
|
image
|
||||||
},
|
);
|
||||||
image,
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the canonical pinned URL for a tab across all windows. Used to let the
|
||||||
|
* user edit a pinned tab's URL directly.
|
||||||
|
*
|
||||||
|
* @param {object} aTab - The tab to set the pinned URL for.
|
||||||
|
* @param {string} aUrl - The URL to store as the canonical pinned URL.
|
||||||
|
* @param {string} [aImage] - Optional Icon to store.
|
||||||
|
*/
|
||||||
|
setPinnedUrl(aTab, aUrl, aImage) {
|
||||||
|
this.log(`Setting pinned url for tab ${aTab.id}`);
|
||||||
|
this.#setPinnedInitialState(
|
||||||
|
aTab,
|
||||||
|
{ url: aUrl, title: aTab.zenStaticLabel },
|
||||||
|
aImage
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#setPinnedInitialState(aTab, aEntry, aImage) {
|
||||||
|
const initialState = { entry: aEntry, image: aImage };
|
||||||
this.#runOnAllWindows(null, win => {
|
this.#runOnAllWindows(null, win => {
|
||||||
const targetTab = this.getItemFromWindow(win, aTab.id);
|
const targetTab = this.getItemFromWindow(win, aTab.id);
|
||||||
if (targetTab) {
|
if (targetTab) {
|
||||||
targetTab._zenPinnedInitialState = initialState;
|
targetTab._zenPinnedInitialState = initialState;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
5
src/zen/space-routing/SpaceRoutingComponents.manifest
Normal file
5
src/zen/space-routing/SpaceRoutingComponents.manifest
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# 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/.
|
||||||
|
|
||||||
|
category browser-window-delayed-startup resource:///modules/zen/spacerouting/ZenSpaceRoutingManager.sys.mjs gZenSpaceRoutingManager.onDelayedBrowserStartup
|
||||||
@@ -18,6 +18,58 @@ class nsZenSpaceRoutingManager {
|
|||||||
this.#readFromDisk();
|
this.#readFromDisk();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto invoked for every window on delayed startup
|
||||||
|
*
|
||||||
|
* @param {nsIDOMWindow} window - The browser window that just started up
|
||||||
|
*/
|
||||||
|
onDelayedBrowserStartup(window) {
|
||||||
|
const element = window.MozXULElement.parseXULToFragment(`
|
||||||
|
<menuseparator/>
|
||||||
|
<menuitem id="context_zen-add-domain-to-routing"
|
||||||
|
data-lazy-l10n-id="tab-context-zen-add-domain-to-sr"
|
||||||
|
data-l10n-args='{"tabCount": 1}'/>
|
||||||
|
`);
|
||||||
|
window.document.getElementById("context_undoCloseTab").after(element);
|
||||||
|
|
||||||
|
window.document
|
||||||
|
.getElementById("context_zen-add-domain-to-routing")
|
||||||
|
.addEventListener("command", this.#onAddSelectedToRouting.bind(this));
|
||||||
|
window.document
|
||||||
|
.getElementById("tabContextMenu")
|
||||||
|
.addEventListener(
|
||||||
|
"popupshowing",
|
||||||
|
this.#updateTabCloseCountState.bind(this)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the "context_zen-add-domain-to-routing" command
|
||||||
|
* to reflect the number of selected tabs, when applicable.
|
||||||
|
*
|
||||||
|
* @param {Event} event - The event param
|
||||||
|
*/
|
||||||
|
#updateTabCloseCountState(event) {
|
||||||
|
const window = event.target.documentGlobal;
|
||||||
|
window.document.l10n.setArgs(
|
||||||
|
window.document.getElementById("context_zen-add-domain-to-routing"),
|
||||||
|
{ tabCount: window.gBrowser.selectedTabs.length }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback for whenever the menuitem command is ran
|
||||||
|
*
|
||||||
|
* @param {Event} event - The event parameter
|
||||||
|
*/
|
||||||
|
#onAddSelectedToRouting(event) {
|
||||||
|
const window = event.target.documentGlobal;
|
||||||
|
const tabs = window.TabContextMenu.contextTab.multiselected
|
||||||
|
? window.gBrowser.selectedTabs
|
||||||
|
: [window.TabContextMenu.contextTab];
|
||||||
|
this.addRouteForSelected(tabs, window);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback that will be executed from tabbrowser.js
|
* Callback that will be executed from tabbrowser.js
|
||||||
* This method can be used to stop the tab from being created.
|
* This method can be used to stop the tab from being created.
|
||||||
@@ -403,6 +455,37 @@ class nsZenSpaceRoutingManager {
|
|||||||
this.#file.data.defaultRouteExternal = routeType;
|
this.#file.data.defaultRouteExternal = routeType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new route for all given tabs
|
||||||
|
*
|
||||||
|
* @param {Array<object>} selectedTabs - The tabs that should be routed
|
||||||
|
* @param {Window} parentWindow - The window from which this is being executed
|
||||||
|
*/
|
||||||
|
addRouteForSelected(selectedTabs, parentWindow) {
|
||||||
|
const newRoute = this.createNewRoute();
|
||||||
|
let routeReference = "";
|
||||||
|
|
||||||
|
if (selectedTabs.length == 1) {
|
||||||
|
newRoute.matchType = "contains";
|
||||||
|
routeReference = selectedTabs[0].linkedBrowser.currentURI.host;
|
||||||
|
} else {
|
||||||
|
newRoute.matchType = "regex";
|
||||||
|
routeReference = "(";
|
||||||
|
for (let i = 0; i < selectedTabs.length; i++) {
|
||||||
|
const domain = selectedTabs[i].linkedBrowser.currentURI.host;
|
||||||
|
routeReference += domain.replaceAll(".", "\.");
|
||||||
|
if (i != selectedTabs.length - 1) {
|
||||||
|
routeReference += "|";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
routeReference += ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
newRoute.reference = routeReference;
|
||||||
|
this.updateRoute(newRoute);
|
||||||
|
this.openSpaceRoutingDialog(parentWindow);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves all routes. The list of
|
* Saves all routes. The list of
|
||||||
* routes is stripped of empty routes
|
* routes is stripped of empty routes
|
||||||
|
|||||||
@@ -1506,19 +1506,27 @@ class nsZenWorkspaces {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const newtabPlacement = Services.prefs.getBoolPref(
|
||||||
|
"zen.view.show-newtab-button-top",
|
||||||
|
false
|
||||||
|
);
|
||||||
|
const insertElement = newtabPlacement
|
||||||
|
? container.firstChild
|
||||||
|
: container.lastChild;
|
||||||
|
|
||||||
if (container) {
|
if (container) {
|
||||||
if (tab.group?.hasAttribute("split-view-group")) {
|
if (tab.group?.hasAttribute("split-view-group")) {
|
||||||
gBrowser.zenHandleTabMove(tab.group, () => {
|
gBrowser.zenHandleTabMove(tab.group, () => {
|
||||||
for (const subTab of tab.group.tabs) {
|
for (const subTab of tab.group.tabs) {
|
||||||
subTab.setAttribute("zen-workspace-id", workspaceID);
|
subTab.setAttribute("zen-workspace-id", workspaceID);
|
||||||
}
|
}
|
||||||
container.insertBefore(tab.group, container.lastChild);
|
container.insertBefore(tab.group, insertElement);
|
||||||
});
|
});
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
gBrowser.zenHandleTabMove(tab, () => {
|
gBrowser.zenHandleTabMove(tab, () => {
|
||||||
tab.setAttribute("zen-workspace-id", workspaceID);
|
tab.setAttribute("zen-workspace-id", workspaceID);
|
||||||
container.insertBefore(tab, container.lastChild);
|
container.insertBefore(tab, insertElement);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// also change glance tab if it's the same tab
|
// also change glance tab if it's the same tab
|
||||||
|
|||||||
@@ -246,6 +246,66 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
|
|||||||
gZenUIManager.showToast("zen-pinned-tab-replaced");
|
gZenUIManager.showToast("zen-pinned-tab-replaced");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async editPinnedUrl(tab = undefined) {
|
||||||
|
tab ??= TabContextMenu.contextTab;
|
||||||
|
if (!tab || !tab.pinned) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialUrl =
|
||||||
|
tab._zenPinnedInitialState?.entry?.url ||
|
||||||
|
tab.linkedBrowser?.currentURI?.spec;
|
||||||
|
const [title, label] = await document.l10n.formatValues([
|
||||||
|
{ id: "zen-pinned-tab-edit-url-title" },
|
||||||
|
{ id: "zen-pinned-tab-edit-url-label" },
|
||||||
|
]);
|
||||||
|
const result = { value: initialUrl ?? "" };
|
||||||
|
const confirmed = Services.prompt.prompt(
|
||||||
|
window,
|
||||||
|
title,
|
||||||
|
label,
|
||||||
|
result,
|
||||||
|
null,
|
||||||
|
{ value: false }
|
||||||
|
);
|
||||||
|
if (!confirmed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let uri;
|
||||||
|
try {
|
||||||
|
uri = Services.uriFixup.getFixupURIInfo(
|
||||||
|
result.value.trim(),
|
||||||
|
Ci.nsIURIFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS
|
||||||
|
).preferredURI;
|
||||||
|
} catch (_) {}
|
||||||
|
if (!uri) {
|
||||||
|
gZenUIManager.showToast("zen-pinned-tab-url-invalid");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const url = uri.spec;
|
||||||
|
|
||||||
|
// Skip when the value wasn't actually changed from what was prefilled.
|
||||||
|
if (!url || url === initialUrl) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const image = tab.zenStaticIcon || (await this.#getCachedFavicon(uri));
|
||||||
|
window.gZenWindowSync.setPinnedUrl(tab, url, image);
|
||||||
|
this.#resetTabToStoredState(tab);
|
||||||
|
gZenUIManager.showToast("zen-pinned-tab-url-edited");
|
||||||
|
}
|
||||||
|
|
||||||
|
async #getCachedFavicon(uri) {
|
||||||
|
try {
|
||||||
|
const favicon = await PlacesUtils.favicons.getFaviconForPage(uri);
|
||||||
|
return favicon?.dataURI?.spec;
|
||||||
|
} catch (ex) {
|
||||||
|
console.error("Failed to get favicon for edited pinned url:", ex);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_initClosePinnedTabShortcut() {
|
_initClosePinnedTabShortcut() {
|
||||||
let cmdClose = document.getElementById("cmd_close");
|
let cmdClose = document.getElementById("cmd_close");
|
||||||
|
|
||||||
@@ -545,11 +605,20 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
|
|||||||
}
|
}
|
||||||
const elements = window.MozXULElement.parseXULToFragment(`
|
const elements = window.MozXULElement.parseXULToFragment(`
|
||||||
<menuseparator id="context_zen-pinned-tab-separator" hidden="true"/>
|
<menuseparator id="context_zen-pinned-tab-separator" hidden="true"/>
|
||||||
|
<menu id="context_zen-edit-pinned-page"
|
||||||
|
data-lazy-l10n-id="tab-context-zen-edit-pinned-page"
|
||||||
|
data-l10n-args="{"isEssential":""}"
|
||||||
|
hidden="true">
|
||||||
|
<menupopup>
|
||||||
<menuitem id="context_zen-replace-pinned-url-with-current"
|
<menuitem id="context_zen-replace-pinned-url-with-current"
|
||||||
data-lazy-l10n-id="tab-context-zen-replace-pinned-url-with-current"
|
data-lazy-l10n-id="tab-context-zen-replace-pinned-url-with-current"
|
||||||
data-l10n-args="{"isEssential":""}"
|
data-l10n-args="{"isEssential":""}"
|
||||||
hidden="true"
|
|
||||||
command="cmd_zenReplacePinnedUrlWithCurrent"/>
|
command="cmd_zenReplacePinnedUrlWithCurrent"/>
|
||||||
|
<menuitem id="context_zen-edit-pinned-url"
|
||||||
|
data-lazy-l10n-id="tab-context-zen-edit-pinned-url"
|
||||||
|
command="cmd_zenEditPinnedUrl"/>
|
||||||
|
</menupopup>
|
||||||
|
</menu>
|
||||||
<menuitem id="context_zen-reset-pinned-tab"
|
<menuitem id="context_zen-reset-pinned-tab"
|
||||||
data-lazy-l10n-id="tab-context-zen-reset-pinned-tab"
|
data-lazy-l10n-id="tab-context-zen-reset-pinned-tab"
|
||||||
data-l10n-args="{"isEssential":""}"
|
data-l10n-args="{"isEssential":""}"
|
||||||
@@ -619,15 +688,24 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
|
|||||||
const zenResetPinnedTab = document.getElementById(
|
const zenResetPinnedTab = document.getElementById(
|
||||||
"context_zen-reset-pinned-tab"
|
"context_zen-reset-pinned-tab"
|
||||||
);
|
);
|
||||||
|
const zenEditPinnedPage = document.getElementById(
|
||||||
|
"context_zen-edit-pinned-page"
|
||||||
|
);
|
||||||
const zenReplacePinnedUrl = document.getElementById(
|
const zenReplacePinnedUrl = document.getElementById(
|
||||||
"context_zen-replace-pinned-url-with-current"
|
"context_zen-replace-pinned-url-with-current"
|
||||||
);
|
);
|
||||||
[zenResetPinnedTab, zenReplacePinnedUrl].forEach(element => {
|
[zenResetPinnedTab, zenEditPinnedPage].forEach(element => {
|
||||||
if (element) {
|
if (element) {
|
||||||
element.hidden = !isVisible;
|
element.hidden = !isVisible;
|
||||||
document.l10n.setArgs(element, { isEssential });
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
[zenResetPinnedTab, zenEditPinnedPage, zenReplacePinnedUrl].forEach(
|
||||||
|
element => {
|
||||||
|
if (element) {
|
||||||
|
document.l10n.setArgs(element, { isEssential });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
zenAddEssential.hidden = isEssential || !!contextTab.group;
|
zenAddEssential.hidden = isEssential || !!contextTab.group;
|
||||||
document.l10n
|
document.l10n
|
||||||
.formatValue("tab-context-zen-add-essential-badge", {
|
.formatValue("tab-context-zen-add-essential-badge", {
|
||||||
@@ -857,7 +935,7 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
|
|||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Remove # and ? from the URL
|
// Remove # from the URL
|
||||||
const pinUrl = tab._zenPinnedInitialState.entry.url.split("#")[0];
|
const pinUrl = tab._zenPinnedInitialState.entry.url.split("#")[0];
|
||||||
const currentUrl = location.split("#")[0];
|
const currentUrl = location.split("#")[0];
|
||||||
// Add an indicator that the pin has been changed
|
// Add an indicator that the pin has been changed
|
||||||
@@ -897,10 +975,14 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
|
|||||||
} else {
|
} else {
|
||||||
tab.setAttribute("zen-pinned-changed", "true");
|
tab.setAttribute("zen-pinned-changed", "true");
|
||||||
}
|
}
|
||||||
|
if (tab._zenPinnedInitialState.image) {
|
||||||
tab.style.setProperty(
|
tab.style.setProperty(
|
||||||
"--zen-original-tab-icon",
|
"--zen-original-tab-icon",
|
||||||
`url(${tab._zenPinnedInitialState.image})`
|
`url(${tab._zenPinnedInitialState.image})`
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
tab.style.removeProperty("--zen-original-tab-icon");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
removeTabContainersDragoverClass(hideIndicator = true) {
|
removeTabContainersDragoverClass(hideIndicator = true) {
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ prefs = ["zen.workspaces.separate-essentials=false"]
|
|||||||
|
|
||||||
["browser_pinned_created.js"]
|
["browser_pinned_created.js"]
|
||||||
|
|
||||||
|
["browser_pinned_edit_url.js"]
|
||||||
|
|
||||||
["browser_pinned_nounload_reset.js"]
|
["browser_pinned_nounload_reset.js"]
|
||||||
|
|
||||||
["browser_pinned_reset_button.js"]
|
["browser_pinned_reset_button.js"]
|
||||||
|
|||||||
384
src/zen/tests/pinned/browser_pinned_edit_url.js
Normal file
384
src/zen/tests/pinned/browser_pinned_edit_url.js
Normal file
@@ -0,0 +1,384 @@
|
|||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
https://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
async function pinTab(url) {
|
||||||
|
const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);
|
||||||
|
gBrowser.pinTab(tab);
|
||||||
|
await gBrowser.TabStateFlusher.flush(tab.linkedBrowser);
|
||||||
|
// eslint-disable-next-line mozilla/no-arbitrary-setTimeout
|
||||||
|
await new Promise(r => setTimeout(r, 500));
|
||||||
|
return tab;
|
||||||
|
}
|
||||||
|
|
||||||
|
// XPCOM service methods can't be stubbed in place (non-configurable), so we
|
||||||
|
// swap the whole service object out for a mock and restore it afterwards.
|
||||||
|
function mockPrompt(value) {
|
||||||
|
const original = Services.prompt;
|
||||||
|
Services.prompt = {
|
||||||
|
QueryInterface: ChromeUtils.generateQI([Ci.nsIPromptService]),
|
||||||
|
prompt(win, title, label, result) {
|
||||||
|
if (value === null) {
|
||||||
|
return false; // user cancelled
|
||||||
|
}
|
||||||
|
result.value = value;
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return () => {
|
||||||
|
Services.prompt = original;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function mockFavicons(faviconSpec) {
|
||||||
|
const original = PlacesUtils.favicons;
|
||||||
|
const mock = {
|
||||||
|
callCount: 0,
|
||||||
|
defaultFavicon: { spec: "data:image/png;base64,DEFAULT" },
|
||||||
|
getFaviconForPage() {
|
||||||
|
mock.callCount++;
|
||||||
|
return Promise.resolve(
|
||||||
|
faviconSpec ? { dataURI: { spec: faviconSpec } } : null
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
PlacesUtils.favicons = mock;
|
||||||
|
return {
|
||||||
|
mock,
|
||||||
|
restore: () => {
|
||||||
|
PlacesUtils.favicons = original;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
add_task(async function test_EditPinnedUrl_SurvivesRebuild() {
|
||||||
|
// Pinned tab at url1 (loaded), then select a different tab (unfocus it).
|
||||||
|
const tab = await pinTab("https://example.com/1");
|
||||||
|
const other = await BrowserTestUtils.openNewForegroundTab(
|
||||||
|
gBrowser,
|
||||||
|
"https://example.com/other"
|
||||||
|
);
|
||||||
|
|
||||||
|
const editedUrl = "https://example.com/edited";
|
||||||
|
const restorePrompt = mockPrompt(editedUrl);
|
||||||
|
const favicons = mockFavicons("data:image/png;base64,iVBORw0KGgo=");
|
||||||
|
try {
|
||||||
|
await gZenPinnedTabManager.editPinnedUrl(tab);
|
||||||
|
|
||||||
|
// Close + re-open rebuilds the tab: the in-memory _zenPinnedInitialState is
|
||||||
|
// gone and gets reconstructed from the persisted session via
|
||||||
|
// setPinnedTabState (exactly what #onSessionStoreInitialized does).
|
||||||
|
delete tab._zenPinnedInitialState;
|
||||||
|
await window.gZenWindowSync.setPinnedTabState(tab);
|
||||||
|
|
||||||
|
Assert.equal(
|
||||||
|
tab._zenPinnedInitialState.entry.url,
|
||||||
|
editedUrl,
|
||||||
|
"After the tab is rebuilt, the pinned URL should still be the edited one"
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
restorePrompt();
|
||||||
|
favicons.restore();
|
||||||
|
await BrowserTestUtils.removeTab(other);
|
||||||
|
await BrowserTestUtils.removeTab(tab);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(async function test_EditPinnedUrl_ActiveTabNavigates() {
|
||||||
|
// Editing the active (focused) pinned tab applies the new URL immediately:
|
||||||
|
// the live tab navigates to it (matching Arc's behavior).
|
||||||
|
const tab = await pinTab("https://example.com/1");
|
||||||
|
Assert.equal(gBrowser.selectedTab, tab, "the pinned tab should be active");
|
||||||
|
|
||||||
|
const editedUrl = "https://example.com/edited";
|
||||||
|
const restorePrompt = mockPrompt(editedUrl);
|
||||||
|
const favicons = mockFavicons("data:image/png;base64,iVBORw0KGgo=");
|
||||||
|
try {
|
||||||
|
await gZenPinnedTabManager.editPinnedUrl(tab);
|
||||||
|
await BrowserTestUtils.waitForCondition(
|
||||||
|
() => tab.linkedBrowser.currentURI.spec === editedUrl,
|
||||||
|
"the active pinned tab to navigate to the edited URL"
|
||||||
|
);
|
||||||
|
|
||||||
|
Assert.equal(
|
||||||
|
tab.linkedBrowser.currentURI.spec,
|
||||||
|
editedUrl,
|
||||||
|
"Editing the active pinned tab should navigate it to the new URL"
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
restorePrompt();
|
||||||
|
favicons.restore();
|
||||||
|
await BrowserTestUtils.removeTab(tab);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(
|
||||||
|
async function test_EditPinnedUrl_FaviconLookupErrorLeavesImageEmpty() {
|
||||||
|
const tab = await pinTab("https://example.com/1");
|
||||||
|
const restorePrompt = mockPrompt("https://example.org/edited");
|
||||||
|
const favicons = mockFavicons(null);
|
||||||
|
// Simulate a Places DB failure so #getCachedFavicon hits its catch branch.
|
||||||
|
favicons.mock.getFaviconForPage = () =>
|
||||||
|
Promise.reject(new Error("simulated favicon DB failure"));
|
||||||
|
|
||||||
|
try {
|
||||||
|
await gZenPinnedTabManager.editPinnedUrl(tab);
|
||||||
|
|
||||||
|
Assert.equal(
|
||||||
|
tab._zenPinnedInitialState.entry.url,
|
||||||
|
"https://example.org/edited",
|
||||||
|
"The URL should still be updated when the favicon lookup fails"
|
||||||
|
);
|
||||||
|
ok(
|
||||||
|
!tab._zenPinnedInitialState.image,
|
||||||
|
"The image should be left empty (populated by the next navigation)"
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
restorePrompt();
|
||||||
|
favicons.restore();
|
||||||
|
await BrowserTestUtils.removeTab(tab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
add_task(async function test_EditPinnedUrl_UpdatesUrlAndFavicon() {
|
||||||
|
const tab = await pinTab("https://example.com/1");
|
||||||
|
const faviconSpec = "data:image/png;base64,iVBORw0KGgo=";
|
||||||
|
const restorePrompt = mockPrompt("https://example.org/edited");
|
||||||
|
const favicons = mockFavicons(faviconSpec);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await gZenPinnedTabManager.editPinnedUrl(tab);
|
||||||
|
|
||||||
|
Assert.equal(
|
||||||
|
tab._zenPinnedInitialState.entry.url,
|
||||||
|
"https://example.org/edited",
|
||||||
|
"The pinned URL should be updated to the edited value"
|
||||||
|
);
|
||||||
|
Assert.equal(
|
||||||
|
tab._zenPinnedInitialState.image,
|
||||||
|
faviconSpec,
|
||||||
|
"The stored icon should be the cached favicon for the new URL"
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
restorePrompt();
|
||||||
|
favicons.restore();
|
||||||
|
await BrowserTestUtils.removeTab(tab);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(async function test_EditPinnedUrl_NoCachedFaviconLeavesImageEmpty() {
|
||||||
|
const tab = await pinTab("https://example.com/1");
|
||||||
|
const restorePrompt = mockPrompt("https://example.org/edited");
|
||||||
|
const favicons = mockFavicons(null); // no cached favicon for the new URL
|
||||||
|
|
||||||
|
try {
|
||||||
|
await gZenPinnedTabManager.editPinnedUrl(tab);
|
||||||
|
|
||||||
|
ok(
|
||||||
|
!tab._zenPinnedInitialState.image,
|
||||||
|
"Without a cached favicon the image is left empty, not the default; the " +
|
||||||
|
"next navigation captures the real icon"
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
restorePrompt();
|
||||||
|
favicons.restore();
|
||||||
|
await BrowserTestUtils.removeTab(tab);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(async function test_EditPinnedUrl_ClearsStaleTitle() {
|
||||||
|
const tab = await pinTab("https://example.com/1");
|
||||||
|
const restorePrompt = mockPrompt("https://example.org/edited");
|
||||||
|
const favicons = mockFavicons("data:image/png;base64,iVBORw0KGgo=");
|
||||||
|
|
||||||
|
try {
|
||||||
|
await gZenPinnedTabManager.editPinnedUrl(tab);
|
||||||
|
|
||||||
|
ok(
|
||||||
|
!tab._zenPinnedInitialState.entry.title,
|
||||||
|
"The previous title is cleared so the new page's title is used on load"
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
restorePrompt();
|
||||||
|
favicons.restore();
|
||||||
|
await BrowserTestUtils.removeTab(tab);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(async function test_EditPinnedUrl_KeepsCustomLabel() {
|
||||||
|
const tab = await pinTab("https://example.com/1");
|
||||||
|
tab.zenStaticLabel = "My Pinned Tab";
|
||||||
|
const restorePrompt = mockPrompt("https://example.org/edited");
|
||||||
|
const favicons = mockFavicons("data:image/png;base64,iVBORw0KGgo=");
|
||||||
|
|
||||||
|
try {
|
||||||
|
await gZenPinnedTabManager.editPinnedUrl(tab);
|
||||||
|
|
||||||
|
Assert.equal(
|
||||||
|
tab._zenPinnedInitialState.entry.title,
|
||||||
|
"My Pinned Tab",
|
||||||
|
"An explicit custom label is preserved across a URL edit"
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
restorePrompt();
|
||||||
|
favicons.restore();
|
||||||
|
delete tab.zenStaticLabel;
|
||||||
|
await BrowserTestUtils.removeTab(tab);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(async function test_EditPinnedUrl_KeepsCustomIcon() {
|
||||||
|
const tab = await pinTab("https://example.com/1");
|
||||||
|
const customIcon = "data:image/svg+xml,custom-icon";
|
||||||
|
tab.zenStaticIcon = customIcon;
|
||||||
|
const restorePrompt = mockPrompt("https://example.org/edited");
|
||||||
|
const favicons = mockFavicons("data:image/png;base64,iVBORw0KGgo=");
|
||||||
|
|
||||||
|
try {
|
||||||
|
await gZenPinnedTabManager.editPinnedUrl(tab);
|
||||||
|
|
||||||
|
Assert.equal(
|
||||||
|
tab._zenPinnedInitialState.entry.url,
|
||||||
|
"https://example.org/edited",
|
||||||
|
"The pinned URL should still be updated when a custom icon is set"
|
||||||
|
);
|
||||||
|
Assert.equal(
|
||||||
|
tab._zenPinnedInitialState.image,
|
||||||
|
customIcon,
|
||||||
|
"A user-set custom icon should be preserved, not overridden by a favicon"
|
||||||
|
);
|
||||||
|
Assert.equal(
|
||||||
|
favicons.mock.callCount,
|
||||||
|
0,
|
||||||
|
"Favicon lookup should be skipped when a custom icon is set"
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
restorePrompt();
|
||||||
|
favicons.restore();
|
||||||
|
delete tab.zenStaticIcon;
|
||||||
|
await BrowserTestUtils.removeTab(tab);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(async function test_EditPinnedUrl_InvalidUrlKeepsState() {
|
||||||
|
const tab = await pinTab("https://example.com/1");
|
||||||
|
const originalUrl = tab._zenPinnedInitialState.entry.url;
|
||||||
|
const restorePrompt = mockPrompt(" "); // whitespace only -> not a valid URL
|
||||||
|
const favicons = mockFavicons("data:image/png;base64,iVBORw0KGgo=");
|
||||||
|
|
||||||
|
try {
|
||||||
|
await gZenPinnedTabManager.editPinnedUrl(tab);
|
||||||
|
|
||||||
|
Assert.equal(
|
||||||
|
tab._zenPinnedInitialState.entry.url,
|
||||||
|
originalUrl,
|
||||||
|
"The pinned URL should be unchanged for invalid input"
|
||||||
|
);
|
||||||
|
ok(
|
||||||
|
!tab.hasAttribute("zen-pinned-changed"),
|
||||||
|
"The tab should not be marked as changed for invalid input"
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
restorePrompt();
|
||||||
|
favicons.restore();
|
||||||
|
await BrowserTestUtils.removeTab(tab);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(async function test_EditPinnedUrl_CancelKeepsState() {
|
||||||
|
const tab = await pinTab("https://example.com/1");
|
||||||
|
const originalUrl = tab._zenPinnedInitialState.entry.url;
|
||||||
|
const restorePrompt = mockPrompt(null); // user cancels the dialog
|
||||||
|
|
||||||
|
try {
|
||||||
|
await gZenPinnedTabManager.editPinnedUrl(tab);
|
||||||
|
|
||||||
|
Assert.equal(
|
||||||
|
tab._zenPinnedInitialState.entry.url,
|
||||||
|
originalUrl,
|
||||||
|
"The pinned URL should be unchanged when the dialog is cancelled"
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
restorePrompt();
|
||||||
|
await BrowserTestUtils.removeTab(tab);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(async function test_EditPinnedUrl_FixesSchemeTypo() {
|
||||||
|
const tab = await pinTab("https://example.com/1");
|
||||||
|
const restorePrompt = mockPrompt("htps://example.org/typo");
|
||||||
|
const favicons = mockFavicons("data:image/png;base64,iVBORw0KGgo=");
|
||||||
|
|
||||||
|
try {
|
||||||
|
await gZenPinnedTabManager.editPinnedUrl(tab);
|
||||||
|
|
||||||
|
Assert.equal(
|
||||||
|
tab._zenPinnedInitialState.entry.url,
|
||||||
|
"https://example.org/typo",
|
||||||
|
"A mistyped scheme (htps://) should be auto-fixed to https://"
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
restorePrompt();
|
||||||
|
favicons.restore();
|
||||||
|
await BrowserTestUtils.removeTab(tab);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(async function test_EditPinnedUrl_AddsMissingScheme() {
|
||||||
|
const tab = await pinTab("https://example.com/1");
|
||||||
|
const restorePrompt = mockPrompt("example.org/no-scheme");
|
||||||
|
const favicons = mockFavicons("data:image/png;base64,iVBORw0KGgo=");
|
||||||
|
|
||||||
|
try {
|
||||||
|
await gZenPinnedTabManager.editPinnedUrl(tab);
|
||||||
|
|
||||||
|
const stored = tab._zenPinnedInitialState.entry.url;
|
||||||
|
ok(
|
||||||
|
/^https?:\/\//.test(stored),
|
||||||
|
`A scheme should be prepended when omitted (got "${stored}")`
|
||||||
|
);
|
||||||
|
ok(
|
||||||
|
stored.endsWith("example.org/no-scheme"),
|
||||||
|
`Host and path should be preserved (got "${stored}")`
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
restorePrompt();
|
||||||
|
favicons.restore();
|
||||||
|
await BrowserTestUtils.removeTab(tab);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(async function test_EditPinnedUrl_PrefillsWithStoredUrl() {
|
||||||
|
const tab = await pinTab("https://example.com/1");
|
||||||
|
// The stored pinned URL differs from the live browser URL (e.g. it was pinned
|
||||||
|
// as http but the server redirected the tab to https).
|
||||||
|
tab._zenPinnedInitialState = {
|
||||||
|
entry: { url: "http://example.com/pinned" },
|
||||||
|
image: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
let prefilled;
|
||||||
|
const originalPrompt = Services.prompt;
|
||||||
|
Services.prompt = {
|
||||||
|
QueryInterface: ChromeUtils.generateQI([Ci.nsIPromptService]),
|
||||||
|
prompt(win, title, label, result) {
|
||||||
|
prefilled = result.value;
|
||||||
|
return false; // cancel, we only care about the prefilled value
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await gZenPinnedTabManager.editPinnedUrl(tab);
|
||||||
|
|
||||||
|
Assert.equal(
|
||||||
|
prefilled,
|
||||||
|
"http://example.com/pinned",
|
||||||
|
"The edit dialog should prefill with the stored pinned URL, not the live browser URL"
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
Services.prompt = originalPrompt;
|
||||||
|
await BrowserTestUtils.removeTab(tab);
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -5,8 +5,8 @@
|
|||||||
"binaryName": "zen",
|
"binaryName": "zen",
|
||||||
"version": {
|
"version": {
|
||||||
"product": "firefox",
|
"product": "firefox",
|
||||||
"version": "152.0",
|
"version": "152.0.2",
|
||||||
"candidate": "152.0",
|
"candidate": "152.0.2",
|
||||||
"candidateBuild": 1
|
"candidateBuild": 1
|
||||||
},
|
},
|
||||||
"buildOptions": {
|
"buildOptions": {
|
||||||
@@ -20,7 +20,7 @@
|
|||||||
"brandShortName": "Zen",
|
"brandShortName": "Zen",
|
||||||
"brandFullName": "Zen Browser",
|
"brandFullName": "Zen Browser",
|
||||||
"release": {
|
"release": {
|
||||||
"displayVersion": "1.21.2b",
|
"displayVersion": "1.21.4b",
|
||||||
"github": {
|
"github": {
|
||||||
"repo": "zen-browser/desktop"
|
"repo": "zen-browser/desktop"
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user