mirror of
https://github.com/zen-browser/desktop.git
synced 2026-06-28 05:30:27 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
94b2a4b67e | ||
|
|
cf98127a5e | ||
|
|
b7a8e79299 | ||
|
|
0048f21a52 |
@@ -34,8 +34,8 @@ Zen is a firefox-based browser with the aim of pushing your productivity to a ne
|
||||
|
||||
### Firefox Versions
|
||||
|
||||
- [`Release`](https://zen-browser.app/download) - Is currently built using Firefox version `152.0.3`! 🚀
|
||||
- [`Twilight`](https://zen-browser.app/download?twilight) - Is currently built using Firefox version `RC 152.0.3`!
|
||||
- [`Release`](https://zen-browser.app/download) - Is currently built using Firefox version `152.0.1`! 🚀
|
||||
- [`Twilight`](https://zen-browser.app/download?twilight) - Is currently built using Firefox version `RC 152.0.1`!
|
||||
|
||||
### Contributing
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
b223f3727a037a7b8a0f36f34bfe8c4622a7f387
|
||||
2960f4c1ce58d289d3b9ec885695f0017d2636ab
|
||||
@@ -19,19 +19,13 @@ tab-context-zen-add-essential-badge = { $num } / { $max }
|
||||
tab-context-zen-remove-essential =
|
||||
.label = Remove from Essentials
|
||||
.accesskey = R
|
||||
tab-context-zen-edit-pinned-page =
|
||||
tab-context-zen-replace-pinned-url-with-current =
|
||||
.label =
|
||||
{ $isEssential ->
|
||||
[true] Edit Essential URL
|
||||
*[false] Edit Pinned URL
|
||||
[true] Replace Essential URL with Current
|
||||
*[false] Replace Pinned URL with Current
|
||||
}
|
||||
.accesskey = P
|
||||
tab-context-zen-replace-pinned-url-with-current =
|
||||
.label = Replace with Current URL
|
||||
.accesskey = C
|
||||
tab-context-zen-edit-pinned-url =
|
||||
.label = Edit...
|
||||
.accesskey = E
|
||||
tab-context-zen-edit-title =
|
||||
.label = Change Label...
|
||||
tab-context-zen-edit-icon =
|
||||
@@ -61,10 +55,6 @@ zen-general-confirm =
|
||||
.label = Confirm
|
||||
|
||||
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-background-tab-opened-toast = New background tab opened!
|
||||
zen-workspace-renamed-toast = Workspace has been successfully renamed!
|
||||
|
||||
@@ -25,9 +25,3 @@ zen-space-routing-open-in = Open In
|
||||
zen-space-routing-url = URL
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -95,6 +95,9 @@
|
||||
- name: browser.search.widget.new
|
||||
value: true
|
||||
|
||||
- name: layout.css.corner-shape.enabled
|
||||
value: true
|
||||
|
||||
# Disabled from https://searchfox.org/firefox-main/rev/d6bfff43852356ca98af848b4705d37f8d41856f/modules/libpref/init/StaticPrefList.yaml#2008
|
||||
# Only enabled for windows, doesn't really fit inside Zen.
|
||||
- name: browser.startup.preXulSkeletonUI
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
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
|
||||
index 3ffd464b960a4299a7dd0cd87e4fc2f781b9d593..7a831c8ee2b73bb89bf8a82ac24958b55c16a5aa 100644
|
||||
index 3ffd464b960a4299a7dd0cd87e4fc2f781b9d593..ef9f42d1f0196902b4af31f4496891fcd6319831 100644
|
||||
--- a/browser/base/content/aboutDialog.xhtml
|
||||
+++ b/browser/base/content/aboutDialog.xhtml
|
||||
@@ -102,10 +102,6 @@
|
||||
@@ -39,8 +39,8 @@ index 3ffd464b960a4299a7dd0cd87e4fc2f781b9d593..7a831c8ee2b73bb89bf8a82ac24958b5
|
||||
<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/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="bottom-links-terms"/>
|
||||
+ <label is="text-link" class="bottom-link" href="https://www.zen-browser.app/privacy-policy/" 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="https://www.zen-browser.app/privacy-policy/" data-l10n-id="bottomLinks-privacy"/>
|
||||
</hbox>
|
||||
<description id="trademark" data-l10n-id="trademarkInfo"></description>
|
||||
</vbox>
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
<command id="cmd_zenToggleTabsOnRight" />
|
||||
|
||||
<command id="cmd_zenReplacePinnedUrlWithCurrent" />
|
||||
<command id="cmd_zenEditPinnedUrl" />
|
||||
<command id="cmd_contextZenAddToEssentials" />
|
||||
<command id="cmd_contextZenRemoveFromEssentials" />
|
||||
|
||||
|
||||
@@ -131,10 +131,10 @@
|
||||
</box>
|
||||
<html:input type="range" value="0.4" step="0.001" id="PanelUI-zen-gradient-generator-opacity"
|
||||
#ifdef XP_MACOSX
|
||||
max="0.9"
|
||||
max="0.8"
|
||||
min="0.30"
|
||||
#else
|
||||
max="0.9"
|
||||
max="0.8"
|
||||
min="0.25"
|
||||
#endif
|
||||
/>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js
|
||||
index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d83330c265dfc 100644
|
||||
index 08b5b56e069d038d72c87355920c4ce8a55ed805..555ffd4772d9d4903491fdff9f3682852f8a52bd 100644
|
||||
--- a/browser/components/tabbrowser/content/tabbrowser.js
|
||||
+++ b/browser/components/tabbrowser/content/tabbrowser.js
|
||||
@@ -511,6 +511,7 @@
|
||||
@@ -523,15 +523,12 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
|
||||
if (tabs.length > 1 || !tabs[0].selected) {
|
||||
this._updateTabsAfterInsert();
|
||||
@@ -4866,11 +5030,17 @@
|
||||
@@ -4866,11 +5030,14 @@
|
||||
if (ownerTab) {
|
||||
tab.owner = ownerTab;
|
||||
}
|
||||
+ if ((!tab.pinned && tabGroup?.isZenFolder && !Services.prefs.getBoolPref('zen.folders.owned-tabs-in-folder')) || (tabGroup && tabGroup.hasAttribute("split-view-group"))) {
|
||||
+ tabGroup = null;
|
||||
+ }
|
||||
+ if (openerTab?.hasAttribute("zen-glance-tab")) {
|
||||
+ openerTab = gZenGlanceManager.getTabOrGlanceParent(openerTab);
|
||||
+ }
|
||||
|
||||
// Ensure we have an index if one was not provided.
|
||||
@@ -542,7 +539,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
if (
|
||||
!bulkOrderedOpen &&
|
||||
((openerTab &&
|
||||
@@ -4882,7 +5052,7 @@
|
||||
@@ -4882,7 +5049,7 @@
|
||||
let lastRelatedTab =
|
||||
openerTab && this._lastRelatedTabMap.get(openerTab);
|
||||
let previousTab = lastRelatedTab || openerTab || this.selectedTab;
|
||||
@@ -551,7 +548,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
tabGroup = previousTab.group;
|
||||
}
|
||||
if (
|
||||
@@ -4898,7 +5068,7 @@
|
||||
@@ -4898,7 +5065,7 @@
|
||||
previousTab.splitview
|
||||
) + 1;
|
||||
} else if (previousTab.visible) {
|
||||
@@ -560,7 +557,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
} else if (previousTab == FirefoxViewHandler.tab) {
|
||||
elementIndex = 0;
|
||||
}
|
||||
@@ -4926,14 +5096,14 @@
|
||||
@@ -4926,14 +5093,14 @@
|
||||
}
|
||||
// Ensure index is within bounds.
|
||||
if (tab.pinned) {
|
||||
@@ -579,7 +576,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
|
||||
if (pinned && !itemAfter?.pinned) {
|
||||
itemAfter = null;
|
||||
@@ -4950,7 +5120,7 @@
|
||||
@@ -4950,7 +5117,7 @@
|
||||
|
||||
this.tabContainer._invalidateCachedTabs();
|
||||
|
||||
@@ -588,7 +585,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
if (
|
||||
(this.isTab(itemAfter) && itemAfter.group == tabGroup) ||
|
||||
this.isSplitViewWrapper(itemAfter)
|
||||
@@ -4981,7 +5151,11 @@
|
||||
@@ -4981,7 +5148,11 @@
|
||||
const tabContainer = pinned
|
||||
? this.tabContainer.pinnedTabsContainer
|
||||
: this.tabContainer;
|
||||
@@ -600,7 +597,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
}
|
||||
|
||||
if (tab.group?.collapsed) {
|
||||
@@ -4996,6 +5170,7 @@
|
||||
@@ -4996,6 +5167,7 @@
|
||||
if (pinned) {
|
||||
this._updateTabBarForPinnedTabs();
|
||||
}
|
||||
@@ -608,7 +605,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
|
||||
TabBarVisibility.update();
|
||||
}
|
||||
@@ -5544,6 +5719,7 @@
|
||||
@@ -5544,6 +5716,7 @@
|
||||
telemetrySource,
|
||||
} = {}
|
||||
) {
|
||||
@@ -616,7 +613,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
// When 'closeWindowWithLastTab' pref is enabled, closing all tabs
|
||||
// can be considered equivalent to closing the window.
|
||||
if (
|
||||
@@ -5633,6 +5809,7 @@
|
||||
@@ -5633,6 +5806,7 @@
|
||||
if (lastToClose) {
|
||||
this.removeTab(lastToClose, aParams);
|
||||
}
|
||||
@@ -624,7 +621,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
@@ -5678,6 +5855,14 @@
|
||||
@@ -5678,6 +5852,14 @@
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -639,7 +636,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
let isVisibleTab = aTab.visible;
|
||||
// We have to sample the tab width now, since _beginRemoveTab might
|
||||
// end up modifying the DOM in such a way that aTab gets a new
|
||||
@@ -5685,6 +5870,9 @@
|
||||
@@ -5685,6 +5867,9 @@
|
||||
// state).
|
||||
let tabWidth = window.windowUtils.getBoundsWithoutFlushing(aTab).width;
|
||||
let isLastTab = this.#isLastTabInWindow(aTab);
|
||||
@@ -649,7 +646,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
if (
|
||||
!this._beginRemoveTab(aTab, {
|
||||
closeWindowFastpath: true,
|
||||
@@ -5696,13 +5884,14 @@
|
||||
@@ -5696,13 +5881,14 @@
|
||||
telemetrySource,
|
||||
})
|
||||
) {
|
||||
@@ -665,7 +662,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
let lockTabSizing =
|
||||
!this.tabContainer.verticalMode &&
|
||||
!aTab.pinned &&
|
||||
@@ -5733,7 +5922,13 @@
|
||||
@@ -5733,7 +5919,13 @@
|
||||
// We're not animating, so we can cancel the animation stopwatch.
|
||||
Glean.browserTabclose.timeAnim.cancel(aTab._closeTimeAnimTimerId);
|
||||
aTab._closeTimeAnimTimerId = null;
|
||||
@@ -680,7 +677,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -5867,7 +6062,7 @@
|
||||
@@ -5867,7 +6059,7 @@
|
||||
closeWindowWithLastTab != null
|
||||
? closeWindowWithLastTab
|
||||
: !window.toolbar.visible ||
|
||||
@@ -689,7 +686,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
|
||||
if (closeWindow) {
|
||||
// We've already called beforeunload on all the relevant tabs if we get here,
|
||||
@@ -5891,6 +6086,7 @@
|
||||
@@ -5891,6 +6083,7 @@
|
||||
|
||||
newTab = true;
|
||||
}
|
||||
@@ -697,7 +694,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
aTab._endRemoveArgs = [closeWindow, newTab];
|
||||
|
||||
// swapBrowsersAndCloseOther will take care of closing the window without animation.
|
||||
@@ -5931,13 +6127,7 @@
|
||||
@@ -5931,13 +6124,7 @@
|
||||
aTab._mouseleave();
|
||||
|
||||
if (newTab) {
|
||||
@@ -712,7 +709,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
} else {
|
||||
TabBarVisibility.update();
|
||||
}
|
||||
@@ -6070,6 +6260,7 @@
|
||||
@@ -6070,6 +6257,7 @@
|
||||
this.tabs[i]._tPos = i;
|
||||
}
|
||||
|
||||
@@ -720,7 +717,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
if (!this._windowIsClosing) {
|
||||
// update tab close buttons state
|
||||
this.tabContainer._updateCloseButtons();
|
||||
@@ -6255,6 +6446,7 @@
|
||||
@@ -6255,6 +6443,7 @@
|
||||
memory_after: await getTotalMemoryUsage(),
|
||||
time_to_unload_in_ms: timeElapsed,
|
||||
});
|
||||
@@ -728,7 +725,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -6300,6 +6492,7 @@
|
||||
@@ -6300,6 +6489,7 @@
|
||||
}
|
||||
|
||||
let excludeTabs = new Set(aExcludeTabs);
|
||||
@@ -736,7 +733,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
|
||||
// If this tab has a successor, it should be selectable, since
|
||||
// hiding or closing a tab removes that tab as a successor.
|
||||
@@ -6312,15 +6505,22 @@
|
||||
@@ -6312,15 +6502,22 @@
|
||||
!excludeTabs.has(aTab.owner) &&
|
||||
Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose")
|
||||
) {
|
||||
@@ -761,7 +758,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
let tab = this.tabContainer.findNextTab(aTab, {
|
||||
direction: 1,
|
||||
filter: _tab => remainingTabs.includes(_tab),
|
||||
@@ -6334,7 +6534,7 @@
|
||||
@@ -6334,7 +6531,7 @@
|
||||
}
|
||||
|
||||
if (tab) {
|
||||
@@ -770,7 +767,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
}
|
||||
|
||||
// If no qualifying visible tab was found, see if there is a tab in
|
||||
@@ -6355,7 +6555,7 @@
|
||||
@@ -6355,7 +6552,7 @@
|
||||
});
|
||||
}
|
||||
|
||||
@@ -779,7 +776,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
}
|
||||
|
||||
_blurTab(aTab) {
|
||||
@@ -6366,7 +6566,7 @@
|
||||
@@ -6366,7 +6563,7 @@
|
||||
* @returns {boolean}
|
||||
* False if swapping isn't permitted, true otherwise.
|
||||
*/
|
||||
@@ -788,7 +785,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
// Do not allow transfering a private tab to a non-private window
|
||||
// and vice versa.
|
||||
if (
|
||||
@@ -6420,6 +6620,7 @@
|
||||
@@ -6420,6 +6617,7 @@
|
||||
// fire the beforeunload event in the process. Close the other
|
||||
// window if this was its last tab.
|
||||
if (
|
||||
@@ -796,7 +793,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
!remoteBrowser._beginRemoveTab(aOtherTab, {
|
||||
adoptedByTab: aOurTab,
|
||||
closeWindowWithLastTab: true,
|
||||
@@ -6431,7 +6632,7 @@
|
||||
@@ -6431,7 +6629,7 @@
|
||||
// If this is the last tab of the window, hide the window
|
||||
// immediately without animation before the docshell swap, to avoid
|
||||
// about:blank being painted.
|
||||
@@ -805,7 +802,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
if (closeWindow) {
|
||||
let win = aOtherTab.documentGlobal;
|
||||
win.windowUtils.suppressAnimation(true);
|
||||
@@ -6565,11 +6766,13 @@
|
||||
@@ -6565,11 +6763,13 @@
|
||||
}
|
||||
|
||||
// Finish tearing down the tab that's going away.
|
||||
@@ -819,7 +816,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
|
||||
this.setTabTitle(aOurTab);
|
||||
|
||||
@@ -6771,10 +6974,10 @@
|
||||
@@ -6771,10 +6971,10 @@
|
||||
SessionStore.deleteCustomTabValue(aTab, "hiddenBy");
|
||||
}
|
||||
|
||||
@@ -832,7 +829,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
aTab.selected ||
|
||||
aTab.closing ||
|
||||
// Tabs that are sharing the screen, microphone or camera cannot be hidden.
|
||||
@@ -6834,7 +7037,8 @@
|
||||
@@ -6834,7 +7034,8 @@
|
||||
* @param {object} [aOptions={}]
|
||||
* Key-value pairs that will be serialized into the features string.
|
||||
*/
|
||||
@@ -842,7 +839,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
if (this.tabs.length == 1) {
|
||||
return null;
|
||||
}
|
||||
@@ -6851,7 +7055,7 @@
|
||||
@@ -6851,7 +7052,7 @@
|
||||
// tell a new window to take the "dropped" tab
|
||||
let args = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
|
||||
args.appendElement(aTab.splitview ?? aTab);
|
||||
@@ -851,7 +848,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
private: PrivateBrowsingUtils.isWindowPrivate(window),
|
||||
features: Object.entries(aOptions)
|
||||
.map(([key, value]) => `${key}=${value}`)
|
||||
@@ -6859,6 +7063,8 @@
|
||||
@@ -6859,6 +7060,8 @@
|
||||
openerWindow: window,
|
||||
args,
|
||||
});
|
||||
@@ -860,7 +857,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -6971,7 +7177,7 @@
|
||||
@@ -6971,7 +7174,7 @@
|
||||
* `true` if element is a `<tab-group>`
|
||||
*/
|
||||
isTabGroup(element) {
|
||||
@@ -869,7 +866,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -7056,8 +7262,8 @@
|
||||
@@ -7056,8 +7259,8 @@
|
||||
}
|
||||
|
||||
// Don't allow mixing pinned and unpinned tabs.
|
||||
@@ -880,7 +877,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
} else {
|
||||
tabIndex = Math.max(tabIndex, this.pinnedTabCount);
|
||||
}
|
||||
@@ -7103,8 +7309,8 @@
|
||||
@@ -7103,8 +7306,8 @@
|
||||
this.#handleTabMove(
|
||||
element,
|
||||
() => {
|
||||
@@ -891,7 +888,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
neighbor = neighbor.group;
|
||||
}
|
||||
if (neighbor?.splitview) {
|
||||
@@ -7115,6 +7321,12 @@
|
||||
@@ -7115,6 +7318,12 @@
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -904,7 +901,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
|
||||
if (movingForwards && neighbor) {
|
||||
neighbor.after(element);
|
||||
@@ -7173,23 +7385,31 @@
|
||||
@@ -7173,23 +7382,31 @@
|
||||
#moveTabNextTo(element, targetElement, moveBefore = false, metricsContext) {
|
||||
if (this.isTabGroupLabel(targetElement)) {
|
||||
targetElement = targetElement.group;
|
||||
@@ -942,7 +939,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
} else if (!element.pinned && targetElement && targetElement.pinned) {
|
||||
// If the caller asks to move an unpinned element next to a pinned
|
||||
// tab, move the unpinned element to be the first unpinned element
|
||||
@@ -7202,12 +7422,35 @@
|
||||
@@ -7202,12 +7419,35 @@
|
||||
// move the tab group right before the first unpinned tab.
|
||||
// 4. Moving a tab group and the first unpinned tab is grouped:
|
||||
// move the tab group right before the first unpinned tab's tab group.
|
||||
@@ -979,7 +976,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
|
||||
// We want to include the splitview wrapper if it's the targetElement, but
|
||||
// not in the case where we want to reverse tabs within the same splitview.
|
||||
@@ -7216,6 +7459,7 @@
|
||||
@@ -7216,6 +7456,7 @@
|
||||
}
|
||||
|
||||
let getContainer = () =>
|
||||
@@ -987,7 +984,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
element.pinned
|
||||
? this.tabContainer.pinnedTabsContainer
|
||||
: this.tabContainer;
|
||||
@@ -7224,11 +7468,15 @@
|
||||
@@ -7224,11 +7465,15 @@
|
||||
element,
|
||||
() => {
|
||||
if (moveBefore) {
|
||||
@@ -1004,7 +1001,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
}
|
||||
},
|
||||
metricsContext
|
||||
@@ -7302,11 +7550,15 @@
|
||||
@@ -7302,11 +7547,15 @@
|
||||
* @param {TabMetricsContext} [metricsContext]
|
||||
*/
|
||||
moveTabToExistingGroup(aTab, aGroup, metricsContext) {
|
||||
@@ -1023,7 +1020,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
}
|
||||
if (aTab.group && aTab.group.id === aGroup.id) {
|
||||
return;
|
||||
@@ -7378,6 +7630,7 @@
|
||||
@@ -7378,6 +7627,7 @@
|
||||
|
||||
let state = {
|
||||
tabIndex: tab._tPos,
|
||||
@@ -1031,7 +1028,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
};
|
||||
if (tab.visible) {
|
||||
state.elementIndex = tab.elementIndex;
|
||||
@@ -7409,7 +7662,7 @@
|
||||
@@ -7409,7 +7659,7 @@
|
||||
let changedSplitView =
|
||||
previousTabState.splitViewId != currentTabState.splitViewId;
|
||||
|
||||
@@ -1040,7 +1037,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
tab.dispatchEvent(
|
||||
new CustomEvent("TabMove", {
|
||||
bubbles: true,
|
||||
@@ -7456,6 +7709,10 @@
|
||||
@@ -7456,6 +7706,10 @@
|
||||
|
||||
moveActionCallback();
|
||||
|
||||
@@ -1051,7 +1048,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
// Clear tabs cache after moving nodes because the order of tabs may have
|
||||
// changed.
|
||||
this.tabContainer._invalidateCachedTabs();
|
||||
@@ -7506,7 +7763,22 @@
|
||||
@@ -7506,7 +7760,22 @@
|
||||
* @returns {object}
|
||||
* The new tab in the current window, null if the tab couldn't be adopted.
|
||||
*/
|
||||
@@ -1075,7 +1072,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
// Swap the dropped tab with a new one we create and then close
|
||||
// it in the other window (making it seem to have moved between
|
||||
// windows). We also ensure that the tab we create to swap into has
|
||||
@@ -7549,6 +7821,8 @@
|
||||
@@ -7549,6 +7818,8 @@
|
||||
}
|
||||
params.skipLoad = true;
|
||||
let newTab = this.addWebTab("about:blank", params);
|
||||
@@ -1084,7 +1081,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
|
||||
aTab.container.tabDragAndDrop.finishAnimateTabMove();
|
||||
|
||||
@@ -8259,7 +8533,7 @@
|
||||
@@ -8259,7 +8530,7 @@
|
||||
// preventDefault(). It will still raise the window if appropriate.
|
||||
return;
|
||||
}
|
||||
@@ -1093,7 +1090,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
window.focus();
|
||||
aEvent.preventDefault();
|
||||
}
|
||||
@@ -8276,7 +8550,6 @@
|
||||
@@ -8276,7 +8547,6 @@
|
||||
|
||||
on_TabGroupCollapse(aEvent) {
|
||||
aEvent.target.tabs.forEach(tab => {
|
||||
@@ -1101,7 +1098,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
});
|
||||
}
|
||||
|
||||
@@ -8630,7 +8903,9 @@
|
||||
@@ -8630,7 +8900,9 @@
|
||||
|
||||
let filter = this._tabFilters.get(tab);
|
||||
if (filter) {
|
||||
@@ -1111,7 +1108,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
|
||||
let listener = this._tabListeners.get(tab);
|
||||
if (listener) {
|
||||
@@ -9435,6 +9710,7 @@
|
||||
@@ -9435,6 +9707,7 @@
|
||||
aWebProgress.isTopLevel
|
||||
) {
|
||||
this.mTab.setAttribute("busy", "true");
|
||||
@@ -1119,7 +1116,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
gBrowser._tabAttrModified(this.mTab, ["busy"]);
|
||||
this.mTab._notselectedsinceload = !this.mTab.selected;
|
||||
}
|
||||
@@ -9515,6 +9791,7 @@
|
||||
@@ -9515,6 +9788,7 @@
|
||||
// known defaults. Note we use the original URL since about:newtab
|
||||
// redirects to a prerendered page.
|
||||
const shouldRemoveFavicon =
|
||||
@@ -1127,7 +1124,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
!this.mBrowser.mIconURL &&
|
||||
!ignoreBlank &&
|
||||
!(originalLocation.spec in FAVICON_DEFAULTS);
|
||||
@@ -9689,13 +9966,6 @@
|
||||
@@ -9689,13 +9963,6 @@
|
||||
this.mBrowser.originalURI = aRequest.originalURI;
|
||||
}
|
||||
|
||||
@@ -1141,7 +1138,7 @@ index 08b5b56e069d038d72c87355920c4ce8a55ed805..87f41c8583c26f364530def5bd5d8333
|
||||
}
|
||||
|
||||
let userContextId = this.mBrowser.getAttribute("usercontextid") || 0;
|
||||
@@ -10587,7 +10857,8 @@ var TabContextMenu = {
|
||||
@@ -10587,7 +10854,8 @@ var TabContextMenu = {
|
||||
);
|
||||
contextUnpinSelectedTabs.hidden =
|
||||
!this.contextTab.pinned || !this.multiselected;
|
||||
|
||||
@@ -1,23 +1,8 @@
|
||||
diff --git a/browser/components/tabbrowser/content/tabs.js b/browser/components/tabbrowser/content/tabs.js
|
||||
index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..b9a1cfe3a4a5035d9b06b0b3826a97c52cfcb39e 100644
|
||||
index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..3036768b8911b4fbc28df7528f7189d9ea21b6f6 100644
|
||||
--- a/browser/components/tabbrowser/content/tabs.js
|
||||
+++ b/browser/components/tabbrowser/content/tabs.js
|
||||
@@ -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 @@
|
||||
@@ -220,7 +220,7 @@
|
||||
|
||||
this.tooltip = "tabbrowser-tab-tooltip";
|
||||
|
||||
@@ -26,7 +11,7 @@ index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..b9a1cfe3a4a5035d9b06b0b3826a97c5
|
||||
this.tabDragAndDrop.init();
|
||||
}
|
||||
|
||||
@@ -444,7 +448,7 @@
|
||||
@@ -444,7 +444,7 @@
|
||||
// and we're not hitting the scroll buttons.
|
||||
if (
|
||||
event.button != 0 ||
|
||||
@@ -35,7 +20,7 @@ index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..b9a1cfe3a4a5035d9b06b0b3826a97c5
|
||||
event.composedTarget.localName == "toolbarbutton"
|
||||
) {
|
||||
return;
|
||||
@@ -525,7 +529,6 @@
|
||||
@@ -525,7 +525,6 @@
|
||||
});
|
||||
}
|
||||
} else if (isTabGroupLabel(event.target)) {
|
||||
@@ -43,7 +28,7 @@ index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..b9a1cfe3a4a5035d9b06b0b3826a97c5
|
||||
} else if (
|
||||
event.originalTarget.closest("scrollbox") &&
|
||||
!Services.prefs.getBoolPref(
|
||||
@@ -561,6 +564,9 @@
|
||||
@@ -561,6 +560,9 @@
|
||||
}
|
||||
|
||||
on_keydown(event) {
|
||||
@@ -53,7 +38,7 @@ index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..b9a1cfe3a4a5035d9b06b0b3826a97c5
|
||||
let { altKey, shiftKey } = event;
|
||||
let [accel, nonAccel] =
|
||||
AppConstants.platform == "macosx"
|
||||
@@ -755,7 +761,6 @@
|
||||
@@ -755,7 +757,6 @@
|
||||
this._updateCloseButtons();
|
||||
|
||||
if (!this.#animatingGroups.size) {
|
||||
@@ -61,7 +46,7 @@ index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..b9a1cfe3a4a5035d9b06b0b3826a97c5
|
||||
}
|
||||
|
||||
document
|
||||
@@ -822,7 +827,7 @@
|
||||
@@ -822,7 +823,7 @@
|
||||
}
|
||||
|
||||
get newTabButton() {
|
||||
@@ -70,7 +55,7 @@ index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..b9a1cfe3a4a5035d9b06b0b3826a97c5
|
||||
}
|
||||
|
||||
get verticalMode() {
|
||||
@@ -838,6 +843,7 @@
|
||||
@@ -838,6 +839,7 @@
|
||||
}
|
||||
|
||||
get overflowing() {
|
||||
@@ -78,7 +63,7 @@ index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..b9a1cfe3a4a5035d9b06b0b3826a97c5
|
||||
return this.hasAttribute("overflow");
|
||||
}
|
||||
|
||||
@@ -851,29 +857,56 @@
|
||||
@@ -851,29 +853,56 @@
|
||||
if (pinnedChildren?.at(-1)?.id == "pinned-tabs-container-periphery") {
|
||||
pinnedChildren.pop();
|
||||
}
|
||||
@@ -108,7 +93,7 @@ index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..b9a1cfe3a4a5035d9b06b0b3826a97c5
|
||||
+ } else if (!isTab(tab)) {
|
||||
+ tabs.splice(i, 1);
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
+ };
|
||||
+ expandTabs(pinnedTabs);
|
||||
+ expandTabs(unpinnedChildren);
|
||||
@@ -129,7 +114,7 @@ index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..b9a1cfe3a4a5035d9b06b0b3826a97c5
|
||||
+ // remove the separator from the list
|
||||
+ allTabs.splice(i, 1);
|
||||
+ i--;
|
||||
}
|
||||
+ }
|
||||
+ i++;
|
||||
}
|
||||
-
|
||||
@@ -145,7 +130,7 @@ index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..b9a1cfe3a4a5035d9b06b0b3826a97c5
|
||||
}
|
||||
|
||||
get allSplitViews() {
|
||||
@@ -958,29 +991,28 @@
|
||||
@@ -958,29 +987,28 @@
|
||||
return this.#focusableItems;
|
||||
}
|
||||
|
||||
@@ -185,7 +170,7 @@ index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..b9a1cfe3a4a5035d9b06b0b3826a97c5
|
||||
this.#focusableItems = focusableItems;
|
||||
|
||||
return this.#focusableItems;
|
||||
@@ -993,6 +1025,7 @@
|
||||
@@ -993,6 +1021,7 @@
|
||||
* focusable (ex, we don't want the splitview container to be focusable, only its children).
|
||||
*/
|
||||
get dragAndDropElements() {
|
||||
@@ -193,7 +178,7 @@ index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..b9a1cfe3a4a5035d9b06b0b3826a97c5
|
||||
if (this.#dragAndDropElements) {
|
||||
return this.#dragAndDropElements;
|
||||
}
|
||||
@@ -1063,6 +1096,7 @@
|
||||
@@ -1063,6 +1092,7 @@
|
||||
_invalidateCachedTabs() {
|
||||
this.#allTabs = null;
|
||||
this._invalidateCachedVisibleTabs();
|
||||
@@ -201,7 +186,7 @@ index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..b9a1cfe3a4a5035d9b06b0b3826a97c5
|
||||
}
|
||||
|
||||
_invalidateCachedVisibleTabs() {
|
||||
@@ -1082,7 +1116,8 @@
|
||||
@@ -1082,7 +1112,8 @@
|
||||
|
||||
isContainerVerticalPinnedGrid(tab) {
|
||||
return (
|
||||
@@ -211,7 +196,7 @@ index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..b9a1cfe3a4a5035d9b06b0b3826a97c5
|
||||
this.verticalMode &&
|
||||
this.hasAttribute("expanded") &&
|
||||
!this.expandOnHover
|
||||
@@ -1176,7 +1211,7 @@
|
||||
@@ -1176,7 +1207,7 @@
|
||||
|
||||
if (node == null) {
|
||||
// We have a container for non-tab elements at the end of the scrollbox.
|
||||
@@ -220,7 +205,7 @@ index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..b9a1cfe3a4a5035d9b06b0b3826a97c5
|
||||
}
|
||||
|
||||
node.before(tab);
|
||||
@@ -1271,7 +1306,7 @@
|
||||
@@ -1271,7 +1302,7 @@
|
||||
// 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);
|
||||
// Attach the long click popup to all of them.
|
||||
@@ -229,7 +214,7 @@ index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..b9a1cfe3a4a5035d9b06b0b3826a97c5
|
||||
const newTab2 = this.newTabButton;
|
||||
const newTabVertical = document.getElementById(
|
||||
"vertical-tabs-newtab-button"
|
||||
@@ -1376,8 +1411,10 @@
|
||||
@@ -1376,8 +1407,10 @@
|
||||
*/
|
||||
_handleTabSelect(aInstant) {
|
||||
let selectedTab = this.selectedItem;
|
||||
@@ -240,7 +225,7 @@ index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..b9a1cfe3a4a5035d9b06b0b3826a97c5
|
||||
selectedTab._notselectedsinceload = false;
|
||||
}
|
||||
|
||||
@@ -1386,7 +1423,7 @@
|
||||
@@ -1386,7 +1419,7 @@
|
||||
* @param {boolean} [shouldScrollInstantly=false]
|
||||
*/
|
||||
#ensureTabIsVisible(tab, shouldScrollInstantly = false) {
|
||||
@@ -249,7 +234,7 @@ index 568f3a7cc7051ff8cb569f6bcb8018a5212f7072..b9a1cfe3a4a5035d9b06b0b3826a97c5
|
||||
if (arrowScrollbox?.overflowing) {
|
||||
arrowScrollbox.ensureElementIsVisible(tab, shouldScrollInstantly);
|
||||
}
|
||||
@@ -1513,7 +1550,7 @@
|
||||
@@ -1513,7 +1546,7 @@
|
||||
}
|
||||
|
||||
_notifyBackgroundTab(aTab) {
|
||||
|
||||
4114
src/external-patches/firefox/corner_shape_support/D296935.patch
Normal file
4114
src/external-patches/firefox/corner_shape_support/D296935.patch
Normal file
File diff suppressed because it is too large
Load Diff
1336
src/external-patches/firefox/corner_shape_support/D297660.patch
Normal file
1336
src/external-patches/firefox/corner_shape_support/D297660.patch
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,15 @@
|
||||
diff --git a/servo/components/style/values/generics/border.rs b/servo/components/style/values/generics/border.rs
|
||||
--- a/servo/components/style/values/generics/border.rs
|
||||
+++ b/servo/components/style/values/generics/border.rs
|
||||
@@ -296,9 +296,9 @@
|
||||
pub fn all(s: S) -> Self {
|
||||
Self {
|
||||
top_left: s.clone(),
|
||||
top_right: s.clone(),
|
||||
bottom_right: s.clone(),
|
||||
- bottom_left: s.clone(),
|
||||
+ bottom_left: s,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
diff --git a/gfx/wr/glsl-to-cxx/src/hir.rs b/gfx/wr/glsl-to-cxx/src/hir.rs
|
||||
--- a/gfx/wr/glsl-to-cxx/src/hir.rs
|
||||
+++ b/gfx/wr/glsl-to-cxx/src/hir.rs
|
||||
@@ -3531,10 +3531,11 @@
|
||||
None,
|
||||
Type::new(Float),
|
||||
vec![Type::new(Vec2)],
|
||||
);
|
||||
declare_function(state, "pow", None, Type::new(Vec3), vec![Type::new(Vec3)]);
|
||||
+ declare_function(state, "pow", None, Type::new(Vec2), vec![Type::new(Vec2)]);
|
||||
declare_function(state, "pow", None, Type::new(Float), vec![Type::new(Float)]);
|
||||
declare_function(state, "exp", None, Type::new(Float), vec![Type::new(Float)]);
|
||||
declare_function(state, "exp2", None, Type::new(Float), vec![Type::new(Float)]);
|
||||
declare_function(state, "log", None, Type::new(Float), vec![Type::new(Float)]);
|
||||
declare_function(state, "log2", None, Type::new(Float), vec![Type::new(Float)]);
|
||||
diff --git a/gfx/wr/swgl/src/glsl.h b/gfx/wr/swgl/src/glsl.h
|
||||
--- a/gfx/wr/swgl/src/glsl.h
|
||||
+++ b/gfx/wr/swgl/src/glsl.h
|
||||
@@ -800,10 +800,18 @@
|
||||
|
||||
Float pow(Float x, Float y) {
|
||||
return if_then_else((x == 0) | (x == 1), x, approx_pow2(approx_log2(x) * y));
|
||||
}
|
||||
|
||||
+vec2 pow(vec2 a, vec2 b) {
|
||||
+ return vec2(pow(a.x, b.x), pow(a.y, b.y));
|
||||
+}
|
||||
+
|
||||
+vec2_scalar pow(vec2_scalar a, vec2_scalar b) {
|
||||
+ return vec2_scalar(pow(a.x, b.x), pow(a.y, b.y));
|
||||
+}
|
||||
+
|
||||
#define exp __glsl_exp
|
||||
|
||||
SI float exp(float x) { return expf(x); }
|
||||
|
||||
Float exp(Float y) {
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
diff --git a/gfx/wr/swgl/src/glsl.h b/gfx/wr/swgl/src/glsl.h
|
||||
--- a/gfx/wr/swgl/src/glsl.h
|
||||
+++ b/gfx/wr/swgl/src/glsl.h
|
||||
@@ -1611,10 +1611,14 @@
|
||||
|
||||
vec3_scalar make_vec3(const vec2_scalar& v, float z) {
|
||||
return vec3_scalar{v.x, v.y, z};
|
||||
}
|
||||
|
||||
+vec3_scalar make_vec3(float x, const vec2_scalar& v) {
|
||||
+ return vec3_scalar{x, v.x, v.y};
|
||||
+}
|
||||
+
|
||||
vec3_scalar make_vec3(float x, float y, float z) {
|
||||
return vec3_scalar{x, y, z};
|
||||
}
|
||||
|
||||
vec3_scalar make_vec3(int32_t x, int32_t y, float z) {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
diff --git a/gfx/wr/swgl/src/glsl.h b/gfx/wr/swgl/src/glsl.h
|
||||
--- a/gfx/wr/swgl/src/glsl.h
|
||||
+++ b/gfx/wr/swgl/src/glsl.h
|
||||
@@ -1599,10 +1599,17 @@
|
||||
x += a.x;
|
||||
y += a.y;
|
||||
z += a.z;
|
||||
return *this;
|
||||
}
|
||||
+
|
||||
+ vec3& operator*=(Float a) {
|
||||
+ x *= a;
|
||||
+ y *= a;
|
||||
+ z *= a;
|
||||
+ return *this;
|
||||
+ }
|
||||
};
|
||||
|
||||
vec3_scalar force_scalar(const vec3& v) {
|
||||
return vec3_scalar{force_scalar(v.x), force_scalar(v.y), force_scalar(v.z)};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
diff --git a/gfx/wr/swgl/src/glsl.h b/gfx/wr/swgl/src/glsl.h
|
||||
--- a/gfx/wr/swgl/src/glsl.h
|
||||
+++ b/gfx/wr/swgl/src/glsl.h
|
||||
@@ -469,10 +469,15 @@
|
||||
vec2_scalar_ref& operator=(const vec2_scalar& a) {
|
||||
x = a.x;
|
||||
y = a.y;
|
||||
return *this;
|
||||
}
|
||||
+ vec2_scalar_ref& operator+=(vec2_scalar a) {
|
||||
+ x += a.x;
|
||||
+ y += a.y;
|
||||
+ return *this;
|
||||
+ }
|
||||
vec2_scalar_ref& operator*=(vec2_scalar a) {
|
||||
x *= a.x;
|
||||
y *= a.y;
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
diff --git a/gfx/wr/glsl-to-cxx/src/hir.rs b/gfx/wr/glsl-to-cxx/src/hir.rs
|
||||
--- a/gfx/wr/glsl-to-cxx/src/hir.rs
|
||||
+++ b/gfx/wr/glsl-to-cxx/src/hir.rs
|
||||
@@ -3537,10 +3537,12 @@
|
||||
declare_function(state, "pow", None, Type::new(Float), vec![Type::new(Float)]);
|
||||
declare_function(state, "exp", None, Type::new(Float), vec![Type::new(Float)]);
|
||||
declare_function(state, "exp2", None, Type::new(Float), vec![Type::new(Float)]);
|
||||
declare_function(state, "log", None, Type::new(Float), vec![Type::new(Float)]);
|
||||
declare_function(state, "log2", None, Type::new(Float), vec![Type::new(Float)]);
|
||||
+ declare_function(state, "isnan", None, Type::new(Bool), vec![Type::new(Float)]);
|
||||
+ declare_function(state, "isinf", None, Type::new(Bool), vec![Type::new(Float)]);
|
||||
for t in &[Float, Vec2] {
|
||||
// recip is non-standard
|
||||
declare_function(
|
||||
state,
|
||||
"recip",
|
||||
diff --git a/gfx/wr/swgl/src/glsl.h b/gfx/wr/swgl/src/glsl.h
|
||||
--- a/gfx/wr/swgl/src/glsl.h
|
||||
+++ b/gfx/wr/swgl/src/glsl.h
|
||||
@@ -205,10 +205,46 @@
|
||||
#else
|
||||
return (Float){sqrtf(v.x), sqrtf(v.y), sqrtf(v.z), sqrtf(v.w)};
|
||||
#endif
|
||||
}
|
||||
|
||||
+// NOTE: the Bool type is actually int under the hood,
|
||||
+// and is used for bitwise return value masking in the
|
||||
+// generated code ("ret_mask" variable).
|
||||
+//
|
||||
+// The ret_mask is initialized to -1 (0xffffffff), and
|
||||
+// is then subsequently masked with condition results.
|
||||
+//
|
||||
+// If we use the boolean result directly here (0 or 1),
|
||||
+// the bitwise AND ends up removing just one bit from
|
||||
+// the mask, and it doesn't work.
|
||||
+//
|
||||
+// Taking the negative transforms 1 (single bit) into -1
|
||||
+// (all bits 1), and correctly broadcasts the boolean
|
||||
+// result into all bits of the ret_mask.
|
||||
+//
|
||||
+// If the condition is false, 0 becomes -0 which is the
|
||||
+// same bit pattern for integers (all bits 0).
|
||||
+
|
||||
+SI Bool isnan(Float v) {
|
||||
+ return (Bool){
|
||||
+ -(fpclassify(v.x) == FP_NAN),
|
||||
+ -(fpclassify(v.y) == FP_NAN),
|
||||
+ -(fpclassify(v.z) == FP_NAN),
|
||||
+ -(fpclassify(v.w) == FP_NAN)
|
||||
+ };
|
||||
+}
|
||||
+
|
||||
+SI Bool isinf(Float v) {
|
||||
+ return (Bool){
|
||||
+ -(fpclassify(v.x) == FP_INFINITE),
|
||||
+ -(fpclassify(v.y) == FP_INFINITE),
|
||||
+ -(fpclassify(v.z) == FP_INFINITE),
|
||||
+ -(fpclassify(v.w) == FP_INFINITE)
|
||||
+ };
|
||||
+}
|
||||
+
|
||||
SI float recip(float x) {
|
||||
#if USE_SSE2
|
||||
return _mm_cvtss_f32(_mm_rcp_ss(_mm_set_ss(x)));
|
||||
#else
|
||||
return 1.0f / x;
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
diff --git a/gfx/wr/webrender/res/debug.glsl b/gfx/wr/webrender/res/debug.glsl
|
||||
new file mode 100644
|
||||
--- /dev/null
|
||||
+++ b/gfx/wr/webrender/res/debug.glsl
|
||||
@@ -0,0 +1,43 @@
|
||||
+/* 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/. */
|
||||
+
|
||||
+// Display a signed distance field as color
|
||||
+//
|
||||
+// It will be orange outside the shape (positive distance), and blue
|
||||
+// inside (negative distance).
|
||||
+//
|
||||
+// Color will cycle every 10px.
|
||||
+//
|
||||
+// The shape outline (distance = 0) is drawn as a thin white outline.
|
||||
+//
|
||||
+// NaNs and infinite values will be drawn as red and yellow respectively.
|
||||
+//
|
||||
+// Example usage:
|
||||
+//
|
||||
+// #include debug
|
||||
+//
|
||||
+// void main(void) {
|
||||
+// float d = compute_some_distance_in_pixels(...);
|
||||
+// oFragColor = debug_sdf(d);
|
||||
+// }
|
||||
+//
|
||||
+vec4 debug_sdf(float d) {
|
||||
+ if (isnan(d)) {
|
||||
+ return vec4(1.0, 0.0, 0.0, 1.0);
|
||||
+ }
|
||||
+ if (isinf(d)) {
|
||||
+ return vec4(1.0, 1.0, 0.0, 1.0);
|
||||
+ }
|
||||
+
|
||||
+ vec3 color = (d > 0.0) ? vec3(0.9, 0.6, 0.3) : vec3(0.6, 0.8, 1.0);
|
||||
+ color *= 1.0 - exp(-0.32 * abs(d));
|
||||
+ color *= fract(d / 10.0) * 0.4 + 0.6;
|
||||
+#ifdef SWGL
|
||||
+ // SWGL doesn't support smoothstep() for now
|
||||
+ color = mix(color, vec3(1.0), step(abs(d), 2.0));
|
||||
+#else
|
||||
+ color = mix(color, vec3(1.0), smoothstep(2.0, 0.0, abs(d)));
|
||||
+#endif
|
||||
+ return vec4(color, 1.0);
|
||||
+}
|
||||
|
||||
456
src/external-patches/firefox/corner_shape_support/D306778.patch
Normal file
456
src/external-patches/firefox/corner_shape_support/D306778.patch
Normal file
@@ -0,0 +1,456 @@
|
||||
diff --git a/gfx/wr/wrench/reftests/clip/clip-superellipse.png b/gfx/wr/wrench/reftests/clip/clip-superellipse.png
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..4470f8b707c255bc3e4eb026368945267ee895e6
|
||||
GIT binary patch
|
||||
literal 7953
|
||||
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
|
||||
G@_zvPNe@N<
|
||||
|
||||
literal 0
|
||||
Hc$@<O00001
|
||||
|
||||
diff --git a/gfx/wr/wrench/reftests/clip/clip-superellipse.yaml b/gfx/wr/wrench/reftests/clip/clip-superellipse.yaml
|
||||
new file mode 100644
|
||||
--- /dev/null
|
||||
+++ b/gfx/wr/wrench/reftests/clip/clip-superellipse.yaml
|
||||
@@ -0,0 +1,174 @@
|
||||
+---
|
||||
+root:
|
||||
+ items:
|
||||
+ - type: clip
|
||||
+ id: 100
|
||||
+ complex:
|
||||
+ - rect: [20, 20, 100, 100]
|
||||
+ radius:
|
||||
+ top-left: [32, 16]
|
||||
+ top-right: [32, 16]
|
||||
+ bottom-right: [32, 16]
|
||||
+ bottom-left: [32, 16]
|
||||
+ shape-top-left: 0.5
|
||||
+ shape-top-right: +Inf
|
||||
+ shape-bottom-left: -2.1
|
||||
+ shape-bottom-right: 0
|
||||
+ - type: clip-chain
|
||||
+ id: 200
|
||||
+ clips: [100]
|
||||
+ - type: rect
|
||||
+ bounds: [20, 20, 100, 100]
|
||||
+ color: red
|
||||
+ clip-chain: 200
|
||||
+
|
||||
+ - type: clip
|
||||
+ id: 101
|
||||
+ complex:
|
||||
+ - rect: [130, 20, 100, 100]
|
||||
+ radius:
|
||||
+ top-left: [32, 16]
|
||||
+ top-right: [32, 16]
|
||||
+ bottom-right: [32, 16]
|
||||
+ bottom-left: [32, 16]
|
||||
+ shape-top-left: 0.5
|
||||
+ shape-top-right: +Inf
|
||||
+ shape-bottom-left: -2.1
|
||||
+ shape-bottom-right: 0
|
||||
+ clip-mode: clip-out
|
||||
+ - type: clip-chain
|
||||
+ id: 201
|
||||
+ clips: [101]
|
||||
+ - type: rect
|
||||
+ bounds: [130, 20, 100, 100]
|
||||
+ color: green
|
||||
+ clip-chain: 201
|
||||
+
|
||||
+ - type: clip
|
||||
+ id: 102
|
||||
+ complex:
|
||||
+ - rect: [20, 130, 100, 100]
|
||||
+ radius:
|
||||
+ top-left: [16, 32]
|
||||
+ top-right: [16, 32]
|
||||
+ bottom-right: [16, 32]
|
||||
+ bottom-left: [16, 32]
|
||||
+ shape-top-left: 0.5
|
||||
+ shape-top-right: +Inf
|
||||
+ shape-bottom-left: -2.1
|
||||
+ shape-bottom-right: 0
|
||||
+ - type: clip-chain
|
||||
+ id: 202
|
||||
+ clips: [102]
|
||||
+ - type: rect
|
||||
+ bounds: [20, 130, 100, 100]
|
||||
+ color: red
|
||||
+ clip-chain: 202
|
||||
+
|
||||
+ - type: clip
|
||||
+ id: 103
|
||||
+ complex:
|
||||
+ - rect: [130, 130, 100, 100]
|
||||
+ radius:
|
||||
+ top-left: [16, 32]
|
||||
+ top-right: [16, 32]
|
||||
+ bottom-right: [16, 32]
|
||||
+ bottom-left: [16, 32]
|
||||
+ shape-top-left: 0.5
|
||||
+ shape-top-right: +Inf
|
||||
+ shape-bottom-left: -2.1
|
||||
+ shape-bottom-right: 0
|
||||
+ clip-mode: clip-out
|
||||
+ - type: clip-chain
|
||||
+ id: 203
|
||||
+ clips: [103]
|
||||
+ - type: rect
|
||||
+ bounds: [130, 130, 100, 100]
|
||||
+ color: green
|
||||
+ clip-chain: 203
|
||||
+
|
||||
+ - type: clip
|
||||
+ id: 104
|
||||
+ complex:
|
||||
+ - rect: [20, 240, 100, 100]
|
||||
+ radius:
|
||||
+ top-left: [128, 32]
|
||||
+ top-right: [128, 32]
|
||||
+ bottom-right: [128, 32]
|
||||
+ bottom-left: [128, 32]
|
||||
+ shape-top-left: 0.5
|
||||
+ shape-top-right: +Inf
|
||||
+ shape-bottom-left: -2.1
|
||||
+ shape-bottom-right: 0
|
||||
+ - type: clip-chain
|
||||
+ id: 204
|
||||
+ clips: [104]
|
||||
+ - type: rect
|
||||
+ bounds: [20, 240, 100, 100]
|
||||
+ color: red
|
||||
+ clip-chain: 204
|
||||
+
|
||||
+ - type: clip
|
||||
+ id: 105
|
||||
+ complex:
|
||||
+ - rect: [130, 240, 100, 100]
|
||||
+ radius:
|
||||
+ top-left: [128, 32]
|
||||
+ top-right: [128, 32]
|
||||
+ bottom-right: [128, 32]
|
||||
+ bottom-left: [128, 32]
|
||||
+ shape-top-left: 0.5
|
||||
+ shape-top-right: +Inf
|
||||
+ shape-bottom-left: -2.1
|
||||
+ shape-bottom-right: 0
|
||||
+ clip-mode: clip-out
|
||||
+ - type: clip-chain
|
||||
+ id: 205
|
||||
+ clips: [105]
|
||||
+ - type: rect
|
||||
+ bounds: [130, 240, 100, 100]
|
||||
+ color: green
|
||||
+ clip-chain: 205
|
||||
+
|
||||
+ - type: clip
|
||||
+ id: 106
|
||||
+ complex:
|
||||
+ - rect: [20, 350, 100, 100]
|
||||
+ radius:
|
||||
+ top-left: [32, 128]
|
||||
+ top-right: [32, 128]
|
||||
+ bottom-right: [32, 128]
|
||||
+ bottom-left: [32, 128]
|
||||
+ shape-top-left: 0.5
|
||||
+ shape-top-right: +Inf
|
||||
+ shape-bottom-left: -2.1
|
||||
+ shape-bottom-right: 0
|
||||
+ - type: clip-chain
|
||||
+ id: 206
|
||||
+ clips: [106]
|
||||
+ - type: rect
|
||||
+ bounds: [20, 350, 100, 100]
|
||||
+ color: red
|
||||
+ clip-chain: 206
|
||||
+
|
||||
+ - type: clip
|
||||
+ id: 107
|
||||
+ complex:
|
||||
+ - rect: [130, 350, 100, 100]
|
||||
+ radius:
|
||||
+ top-left: [32, 128]
|
||||
+ top-right: [32, 128]
|
||||
+ bottom-right: [32, 128]
|
||||
+ bottom-left: [32, 128]
|
||||
+ shape-top-left: 0.5
|
||||
+ shape-top-right: +Inf
|
||||
+ shape-bottom-left: -2.1
|
||||
+ shape-bottom-right: 0
|
||||
+ clip-mode: clip-out
|
||||
+ - type: clip-chain
|
||||
+ id: 207
|
||||
+ clips: [107]
|
||||
+ - type: rect
|
||||
+ bounds: [130, 350, 100, 100]
|
||||
+ color: green
|
||||
+ clip-chain: 207
|
||||
diff --git a/gfx/wr/wrench/reftests/clip/reftest.list b/gfx/wr/wrench/reftests/clip/reftest.list
|
||||
--- a/gfx/wr/wrench/reftests/clip/reftest.list
|
||||
+++ b/gfx/wr/wrench/reftests/clip/reftest.list
|
||||
@@ -1,8 +1,9 @@
|
||||
platform(linux,mac) == border-with-rounded-clip.yaml border-with-rounded-clip.png
|
||||
fuzzy-if(platform(swgl),1,4) == clip-mode.yaml clip-mode.png
|
||||
fuzzy-if(platform(swgl),1,80) == clip-ellipse.yaml clip-ellipse.png
|
||||
+fuzzy-if(platform(android),128,200) fuzzy-if(platform(swgl),1,350) == clip-superellipse.yaml clip-superellipse.png
|
||||
fuzzy(1,1000) platform(linux,mac) == clip-45-degree-rotation.yaml clip-45-degree-rotation-ref.png
|
||||
== clip-3d-transform.yaml clip-3d-transform-ref.yaml
|
||||
fuzzy(1,4) == clip-corner-overlap.yaml clip-corner-overlap-ref.yaml
|
||||
== custom-clip-chain-node-ancestors.yaml custom-clip-chain-node-ancestors-ref.yaml
|
||||
== fixed-position-clipping.yaml fixed-position-clipping-ref.yaml
|
||||
diff --git a/gfx/wr/wrench/reftests/mask/reftest.list b/gfx/wr/wrench/reftests/mask/reftest.list
|
||||
--- a/gfx/wr/wrench/reftests/mask/reftest.list
|
||||
+++ b/gfx/wr/wrench/reftests/mask/reftest.list
|
||||
@@ -14,5 +14,6 @@
|
||||
platform(linux,mac) == checkerboard.yaml checkerboard.png
|
||||
skip_on(android,device) fuzzy(2,1900) == checkerboard.yaml checkerboard-tiling.yaml # Fails on a Pixel2
|
||||
== missing-mask.yaml missing-mask-ref.yaml
|
||||
platform(linux) == scaled-filter-raster-root.yaml scaled-filter-raster-root.png
|
||||
platform(linux,mac) == mask-multiple-coord-systems.yaml mask-multiple-coord-systems.png
|
||||
+fuzzy(1,10) fuzzy-if(platform(swgl),5,100) == shaped-corners.yaml shaped-corners.png
|
||||
diff --git a/gfx/wr/wrench/reftests/mask/shaped-corners.png b/gfx/wr/wrench/reftests/mask/shaped-corners.png
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..0599a1f93a7d3564c6da950908fe495c98216f7c
|
||||
GIT binary patch
|
||||
literal 2026
|
||||
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
|
||||
TV>Bg#Z@5WtKp0!@_p|UHon4m#
|
||||
|
||||
literal 0
|
||||
Hc$@<O00001
|
||||
|
||||
diff --git a/gfx/wr/wrench/reftests/mask/shaped-corners.yaml b/gfx/wr/wrench/reftests/mask/shaped-corners.yaml
|
||||
new file mode 100644
|
||||
--- /dev/null
|
||||
+++ b/gfx/wr/wrench/reftests/mask/shaped-corners.yaml
|
||||
@@ -0,0 +1,12 @@
|
||||
+---
|
||||
+root:
|
||||
+ items:
|
||||
+ - type: clip
|
||||
+ id: 2
|
||||
+ complex:
|
||||
+ - rect: [10, 10, 200, 200]
|
||||
+ radius: [200, 200, 200, 200, 8.2, 0, -2.8, +Inf]
|
||||
+ - type: rect
|
||||
+ clip-chain: [2]
|
||||
+ bounds: [10, 10, 200, 200]
|
||||
+ color: blue
|
||||
diff --git a/gfx/wr/wrench/src/yaml_helper.rs b/gfx/wr/wrench/src/yaml_helper.rs
|
||||
--- a/gfx/wr/wrench/src/yaml_helper.rs
|
||||
+++ b/gfx/wr/wrench/src/yaml_helper.rs
|
||||
@@ -206,10 +206,12 @@
|
||||
impl YamlHelper for Yaml {
|
||||
fn as_f32(&self) -> Option<f32> {
|
||||
match *self {
|
||||
Yaml::Integer(iv) => Some(iv as f32),
|
||||
Yaml::Real(ref sv) => f32::from_str(sv.as_str()).ok(),
|
||||
+ Yaml::String(ref sv) if sv == "+Inf" => Some(f32::INFINITY),
|
||||
+ Yaml::String(ref sv) if sv == "-Inf" => Some(f32::NEG_INFINITY),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_force_f32(&self) -> Option<f32> {
|
||||
@@ -483,24 +485,48 @@
|
||||
shape_top_right: 1.0,
|
||||
shape_bottom_left: 1.0,
|
||||
shape_bottom_right: 1.0,
|
||||
})
|
||||
}
|
||||
+ Yaml::Array(ref array) if array.len() == 8 => {
|
||||
+ let top_left = array[0].as_border_radius_component();
|
||||
+ let top_right = array[1].as_border_radius_component();
|
||||
+ let bottom_left = array[2].as_border_radius_component();
|
||||
+ let bottom_right = array[3].as_border_radius_component();
|
||||
+ let shape_top_left = array[4].as_f32().unwrap();
|
||||
+ let shape_top_right = array[5].as_f32().unwrap();
|
||||
+ let shape_bottom_left = array[6].as_f32().unwrap();
|
||||
+ let shape_bottom_right = array[7].as_f32().unwrap();
|
||||
+ Some(BorderRadius {
|
||||
+ top_left,
|
||||
+ top_right,
|
||||
+ bottom_left,
|
||||
+ bottom_right,
|
||||
+ shape_top_left,
|
||||
+ shape_top_right,
|
||||
+ shape_bottom_left,
|
||||
+ shape_bottom_right,
|
||||
+ })
|
||||
+ }
|
||||
Yaml::Hash(_) => {
|
||||
let top_left = self["top-left"].as_border_radius_component();
|
||||
let top_right = self["top-right"].as_border_radius_component();
|
||||
let bottom_left = self["bottom-left"].as_border_radius_component();
|
||||
let bottom_right = self["bottom-right"].as_border_radius_component();
|
||||
+ let shape_top_left = self["shape-top-left"].as_f32().unwrap_or(1.0);
|
||||
+ let shape_top_right = self["shape-top-right"].as_f32().unwrap_or(1.0);
|
||||
+ let shape_bottom_left = self["shape-bottom-left"].as_f32().unwrap_or(1.0);
|
||||
+ let shape_bottom_right = self["shape-bottom-right"].as_f32().unwrap_or(1.0);
|
||||
Some(BorderRadius {
|
||||
top_left,
|
||||
top_right,
|
||||
bottom_left,
|
||||
bottom_right,
|
||||
- shape_top_left: 1.0,
|
||||
- shape_top_right: 1.0,
|
||||
- shape_bottom_left: 1.0,
|
||||
- shape_bottom_right: 1.0,
|
||||
+ shape_top_left,
|
||||
+ shape_top_right,
|
||||
+ shape_bottom_left,
|
||||
+ shape_bottom_right,
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
panic!("Invalid border radius specified: {:?}", self);
|
||||
}
|
||||
|
||||
592
src/external-patches/firefox/corner_shape_support/D306779.patch
Normal file
592
src/external-patches/firefox/corner_shape_support/D306779.patch
Normal file
@@ -0,0 +1,592 @@
|
||||
diff --git a/gfx/wr/webrender/res/border_shared.glsl b/gfx/wr/webrender/res/border_shared.glsl
|
||||
new file mode 100644
|
||||
--- /dev/null
|
||||
+++ b/gfx/wr/webrender/res/border_shared.glsl
|
||||
@@ -0,0 +1,47 @@
|
||||
+/* 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/. */
|
||||
+
|
||||
+#include gpu_buffer
|
||||
+
|
||||
+#define SEGMENT_TOP_LEFT 0
|
||||
+#define SEGMENT_TOP_RIGHT 1
|
||||
+#define SEGMENT_BOTTOM_RIGHT 2
|
||||
+#define SEGMENT_BOTTOM_LEFT 3
|
||||
+#define SEGMENT_LEFT 4
|
||||
+#define SEGMENT_TOP 5
|
||||
+#define SEGMENT_RIGHT 6
|
||||
+#define SEGMENT_BOTTOM 7
|
||||
+
|
||||
+#ifdef WR_VERTEX_SHADER
|
||||
+
|
||||
+PER_INSTANCE in vec2 aTaskOrigin;
|
||||
+PER_INSTANCE in int aFlags;
|
||||
+PER_INSTANCE in int aGpuDataAddress;
|
||||
+PER_INSTANCE in vec4 aClipParams1;
|
||||
+PER_INSTANCE in vec4 aClipParams2;
|
||||
+
|
||||
+struct BorderInstanceGpuData {
|
||||
+ vec4 rect;
|
||||
+ vec4 color0;
|
||||
+ vec4 color1;
|
||||
+ vec2 widths;
|
||||
+ vec2 radii;
|
||||
+ float shape;
|
||||
+};
|
||||
+
|
||||
+BorderInstanceGpuData fetch_gpu_data(int index) {
|
||||
+ BorderInstanceGpuData data;
|
||||
+
|
||||
+ vec4 texels[5] = fetch_from_gpu_buffer_5f(index);
|
||||
+ data.rect = texels[0];
|
||||
+ data.color0 = texels[1];
|
||||
+ data.color1 = texels[2];
|
||||
+ data.widths = texels[3].xy;
|
||||
+ data.radii = texels[3].zw;
|
||||
+ data.shape = texels[4].x;
|
||||
+
|
||||
+ return data;
|
||||
+}
|
||||
+
|
||||
+#endif
|
||||
diff --git a/gfx/wr/webrender/res/cs_border_segment.glsl b/gfx/wr/webrender/res/cs_border_segment.glsl
|
||||
--- a/gfx/wr/webrender/res/cs_border_segment.glsl
|
||||
+++ b/gfx/wr/webrender/res/cs_border_segment.glsl
|
||||
@@ -1,10 +1,10 @@
|
||||
/* 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/. */
|
||||
|
||||
-#include shared,rect,ellipse
|
||||
+#include shared,rect,border_shared,ellipse
|
||||
|
||||
// For edges, the colors are the same. For corners, these
|
||||
// are the colors of each edge making up the corner.
|
||||
flat varying mediump vec4 vColor00;
|
||||
flat varying mediump vec4 vColor01;
|
||||
@@ -44,19 +44,10 @@
|
||||
flat varying mediump vec4 vClipParams2;
|
||||
|
||||
// Local space position
|
||||
varying highp vec2 vPos;
|
||||
|
||||
-#define SEGMENT_TOP_LEFT 0
|
||||
-#define SEGMENT_TOP_RIGHT 1
|
||||
-#define SEGMENT_BOTTOM_RIGHT 2
|
||||
-#define SEGMENT_BOTTOM_LEFT 3
|
||||
-#define SEGMENT_LEFT 4
|
||||
-#define SEGMENT_TOP 5
|
||||
-#define SEGMENT_RIGHT 6
|
||||
-#define SEGMENT_BOTTOM 7
|
||||
-
|
||||
// Border styles as defined in webrender_api/types.rs
|
||||
#define BORDER_STYLE_NONE 0
|
||||
#define BORDER_STYLE_SOLID 1
|
||||
#define BORDER_STYLE_DOUBLE 2
|
||||
#define BORDER_STYLE_DOTTED 3
|
||||
@@ -72,20 +63,10 @@
|
||||
#define CLIP_DASH_EDGE 2
|
||||
#define CLIP_DOT 3
|
||||
|
||||
#ifdef WR_VERTEX_SHADER
|
||||
|
||||
-PER_INSTANCE in vec2 aTaskOrigin;
|
||||
-PER_INSTANCE in vec4 aRect;
|
||||
-PER_INSTANCE in vec4 aColor0;
|
||||
-PER_INSTANCE in vec4 aColor1;
|
||||
-PER_INSTANCE in int aFlags;
|
||||
-PER_INSTANCE in vec2 aWidths;
|
||||
-PER_INSTANCE in vec2 aRadii;
|
||||
-PER_INSTANCE in vec4 aClipParams1;
|
||||
-PER_INSTANCE in vec4 aClipParams2;
|
||||
-
|
||||
vec2 get_outer_corner_scale(int segment) {
|
||||
vec2 p;
|
||||
|
||||
switch (segment) {
|
||||
case SEGMENT_TOP_LEFT:
|
||||
@@ -153,16 +134,18 @@
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
+ BorderInstanceGpuData data = fetch_gpu_data(aGpuDataAddress);
|
||||
+
|
||||
int segment = aFlags & 0xff;
|
||||
int style0 = (aFlags >> 8) & 0xff;
|
||||
int style1 = (aFlags >> 16) & 0xff;
|
||||
int clip_mode = (aFlags >> 24) & 0x0f;
|
||||
|
||||
- vec2 size = aRect.zw - aRect.xy;
|
||||
+ vec2 size = data.rect.zw - data.rect.xy;
|
||||
vec2 outer_scale = get_outer_corner_scale(segment);
|
||||
vec2 outer = outer_scale * size;
|
||||
vec2 clip_sign = 1.0 - 2.0 * outer_scale;
|
||||
|
||||
// Set some flags used by the FS to determine the
|
||||
@@ -176,19 +159,19 @@
|
||||
edge_axis = ivec2(0, 1);
|
||||
edge_reference = outer;
|
||||
break;
|
||||
case SEGMENT_TOP_RIGHT:
|
||||
edge_axis = ivec2(1, 0);
|
||||
- edge_reference = vec2(outer.x - aWidths.x, outer.y);
|
||||
+ edge_reference = vec2(outer.x - data.widths.x, outer.y);
|
||||
break;
|
||||
case SEGMENT_BOTTOM_RIGHT:
|
||||
edge_axis = ivec2(0, 1);
|
||||
- edge_reference = outer - aWidths;
|
||||
+ edge_reference = outer - data.widths;
|
||||
break;
|
||||
case SEGMENT_BOTTOM_LEFT:
|
||||
edge_axis = ivec2(1, 0);
|
||||
- edge_reference = vec2(outer.x, outer.y - aWidths.y);
|
||||
+ edge_reference = vec2(outer.x, outer.y - data.widths.y);
|
||||
break;
|
||||
case SEGMENT_TOP:
|
||||
case SEGMENT_BOTTOM:
|
||||
edge_axis = ivec2(1, 1);
|
||||
break;
|
||||
@@ -199,23 +182,23 @@
|
||||
}
|
||||
|
||||
vSegmentClipMode = vec2(float(segment), float(clip_mode));
|
||||
vStyleEdgeAxis = vec4(float(style0), float(style1), float(edge_axis.x), float(edge_axis.y));
|
||||
|
||||
- vPartialWidths = vec4(aWidths / 3.0, aWidths / 2.0);
|
||||
+ vPartialWidths = vec4(data.widths / 3.0, data.widths / 2.0);
|
||||
vPos = size * aPosition.xy;
|
||||
|
||||
- vec4[2] color0 = get_colors_for_side(aColor0, style0);
|
||||
+ vec4[2] color0 = get_colors_for_side(data.color0, style0);
|
||||
vColor00 = color0[0];
|
||||
vColor01 = color0[1];
|
||||
- vec4[2] color1 = get_colors_for_side(aColor1, style1);
|
||||
+ vec4[2] color1 = get_colors_for_side(data.color1, style1);
|
||||
vColor10 = color1[0];
|
||||
vColor11 = color1[1];
|
||||
- vClipCenter_Sign = vec4(outer + clip_sign * aRadii, clip_sign);
|
||||
- vClipRadii = vec4(aRadii, max(aRadii - aWidths, 0.0));
|
||||
- vColorLine = vec4(outer, aWidths.y * -clip_sign.y, aWidths.x * clip_sign.x);
|
||||
- vEdgeReference = vec4(edge_reference, edge_reference + aWidths);
|
||||
+ vClipCenter_Sign = vec4(outer + clip_sign * data.radii, clip_sign);
|
||||
+ vClipRadii = vec4(data.radii, max(data.radii - data.widths, 0.0));
|
||||
+ vColorLine = vec4(outer, data.widths.y * -clip_sign.y, data.widths.x * clip_sign.x);
|
||||
+ vEdgeReference = vec4(edge_reference, edge_reference + data.widths);
|
||||
vClipParams1 = aClipParams1;
|
||||
vClipParams2 = aClipParams2;
|
||||
|
||||
// For the case of dot and dash clips, optimize the number of pixels that
|
||||
// are hit to just include the dot itself.
|
||||
@@ -234,17 +217,17 @@
|
||||
// This is a gross approximation which works out because dashes don't have
|
||||
// a strong curvature and we will overshoot by inflating the geometry by
|
||||
// this amount on each side (sqrt(2) * length(dash) would be enough and we
|
||||
// compute 2 * approx_length(dash)).
|
||||
float dash_length = length(aClipParams1.xy - aClipParams2.xy);
|
||||
- float width = max(aWidths.x, aWidths.y);
|
||||
+ float width = max(data.widths.x, data.widths.y);
|
||||
// expand by a small amout for AA just like we do for dots.
|
||||
vec2 r = vec2(max(dash_length, width)) + 2.0;
|
||||
vPos = clamp(vPos, center - r, center + r);
|
||||
}
|
||||
|
||||
- gl_Position = uTransform * vec4(aTaskOrigin + aRect.xy + vPos, 0.0, 1.0);
|
||||
+ gl_Position = uTransform * vec4(aTaskOrigin + data.rect.xy + vPos, 0.0, 1.0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WR_FRAGMENT_SHADER
|
||||
vec4 evaluate_color_for_style_in_corner(
|
||||
diff --git a/gfx/wr/webrender/res/cs_border_solid.glsl b/gfx/wr/webrender/res/cs_border_solid.glsl
|
||||
--- a/gfx/wr/webrender/res/cs_border_solid.glsl
|
||||
+++ b/gfx/wr/webrender/res/cs_border_solid.glsl
|
||||
@@ -1,10 +1,10 @@
|
||||
/* 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/. */
|
||||
|
||||
-#include shared,rect,ellipse
|
||||
+#include shared,rect,border_shared,ellipse
|
||||
|
||||
#define DONT_MIX 0
|
||||
#define MIX_AA 1
|
||||
#define MIX_NO_AA 2
|
||||
|
||||
@@ -37,27 +37,12 @@
|
||||
flat varying highp vec2 vVerticalClipRadii;
|
||||
|
||||
// Local space position
|
||||
varying highp vec2 vPos;
|
||||
|
||||
-#define SEGMENT_TOP_LEFT 0
|
||||
-#define SEGMENT_TOP_RIGHT 1
|
||||
-#define SEGMENT_BOTTOM_RIGHT 2
|
||||
-#define SEGMENT_BOTTOM_LEFT 3
|
||||
-
|
||||
#ifdef WR_VERTEX_SHADER
|
||||
|
||||
-PER_INSTANCE in vec2 aTaskOrigin;
|
||||
-PER_INSTANCE in vec4 aRect;
|
||||
-PER_INSTANCE in vec4 aColor0;
|
||||
-PER_INSTANCE in vec4 aColor1;
|
||||
-PER_INSTANCE in int aFlags;
|
||||
-PER_INSTANCE in vec2 aWidths;
|
||||
-PER_INSTANCE in vec2 aRadii;
|
||||
-PER_INSTANCE in vec4 aClipParams1;
|
||||
-PER_INSTANCE in vec4 aClipParams2;
|
||||
-
|
||||
vec2 get_outer_corner_scale(int segment) {
|
||||
vec2 p;
|
||||
|
||||
switch (segment) {
|
||||
case SEGMENT_TOP_LEFT:
|
||||
@@ -80,15 +65,17 @@
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
+ BorderInstanceGpuData data = fetch_gpu_data(aGpuDataAddress);
|
||||
+
|
||||
int segment = aFlags & 0xff;
|
||||
bool do_aa = ((aFlags >> 24) & 0xf0) != 0;
|
||||
|
||||
vec2 outer_scale = get_outer_corner_scale(segment);
|
||||
- vec2 size = aRect.zw - aRect.xy;
|
||||
+ vec2 size = data.rect.zw - data.rect.xy;
|
||||
vec2 outer = outer_scale * size;
|
||||
vec2 clip_sign = 1.0 - 2.0 * outer_scale;
|
||||
|
||||
int mix_colors;
|
||||
switch (segment) {
|
||||
@@ -105,15 +92,15 @@
|
||||
}
|
||||
|
||||
vMixColors.x = mix_colors;
|
||||
vPos = size * aPosition.xy;
|
||||
|
||||
- vColor0 = aColor0;
|
||||
- vColor1 = aColor1;
|
||||
- vClipCenter_Sign = vec4(outer + clip_sign * aRadii, clip_sign);
|
||||
- vClipRadii = vec4(aRadii, max(aRadii - aWidths, 0.0));
|
||||
- vColorLine = vec4(outer, aWidths.y * -clip_sign.y, aWidths.x * clip_sign.x);
|
||||
+ vColor0 = data.color0;
|
||||
+ vColor1 = data.color1;
|
||||
+ vClipCenter_Sign = vec4(outer + clip_sign * data.radii, clip_sign);
|
||||
+ vClipRadii = vec4(data.radii, max(data.radii - data.widths, 0.0));
|
||||
+ vColorLine = vec4(outer, data.widths.y * -clip_sign.y, data.widths.x * clip_sign.x);
|
||||
|
||||
vec2 horizontal_clip_sign = vec2(-clip_sign.x, clip_sign.y);
|
||||
vHorizontalClipCenter_Sign = vec4(aClipParams1.xy +
|
||||
horizontal_clip_sign * aClipParams1.zw,
|
||||
horizontal_clip_sign);
|
||||
@@ -123,11 +110,11 @@
|
||||
vVerticalClipCenter_Sign = vec4(aClipParams2.xy +
|
||||
vertical_clip_sign * aClipParams2.zw,
|
||||
vertical_clip_sign);
|
||||
vVerticalClipRadii = aClipParams2.zw;
|
||||
|
||||
- gl_Position = uTransform * vec4(aTaskOrigin + aRect.xy + vPos, 0.0, 1.0);
|
||||
+ gl_Position = uTransform * vec4(aTaskOrigin + data.rect.xy + vPos, 0.0, 1.0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WR_FRAGMENT_SHADER
|
||||
void main(void) {
|
||||
diff --git a/gfx/wr/webrender/src/border.rs b/gfx/wr/webrender/src/border.rs
|
||||
--- a/gfx/wr/webrender/src/border.rs
|
||||
+++ b/gfx/wr/webrender/src/border.rs
|
||||
@@ -6,13 +6,14 @@
|
||||
use api::{NormalBorder as ApiNormalBorder, RepeatMode};
|
||||
use api::units::*;
|
||||
use crate::clip::ClipNodeId;
|
||||
use crate::ellipse::Ellipse;
|
||||
use euclid::vec2;
|
||||
+use crate::renderer::GpuBufferBuilderF;
|
||||
use crate::scene_building::SceneBuilder;
|
||||
use crate::spatial_tree::SpatialNodeIndex;
|
||||
-use crate::gpu_types::{BorderInstance, BorderSegment, BrushFlags};
|
||||
+use crate::gpu_types::{BorderInstance, BorderInstanceGpuData, BorderSegment, BrushFlags};
|
||||
use crate::prim_store::{BorderSegmentInfo, BrushSegment, NinePatchDescriptor};
|
||||
use crate::prim_store::borders::NormalBorderPrim;
|
||||
use crate::util::{lerp, RectHelpers};
|
||||
use crate::internal_types::LayoutPrimitiveInfo;
|
||||
use crate::segment::EdgeMask;
|
||||
@@ -125,10 +126,11 @@
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
pub struct BorderSegmentCacheKey {
|
||||
pub size: LayoutSizeAu,
|
||||
pub radius: LayoutSizeAu,
|
||||
+ pub shape: u32,
|
||||
pub side0: BorderSideAu,
|
||||
pub side1: BorderSideAu,
|
||||
pub segment: BorderSegment,
|
||||
pub do_aa: bool,
|
||||
pub h_adjacent_corner_outer: LayoutPointAu,
|
||||
@@ -762,10 +764,11 @@
|
||||
),
|
||||
border.left,
|
||||
border.top,
|
||||
LayoutSize::new(widths.left, widths.top),
|
||||
border.radius.top_left,
|
||||
+ border.radius.shape_top_left,
|
||||
BorderSegment::TopLeft,
|
||||
EdgeMask::TOP | EdgeMask::LEFT,
|
||||
rect.top_right(),
|
||||
border.radius.top_right,
|
||||
rect.bottom_left(),
|
||||
@@ -789,10 +792,11 @@
|
||||
),
|
||||
border.top,
|
||||
border.right,
|
||||
LayoutSize::new(widths.right, widths.top),
|
||||
border.radius.top_right,
|
||||
+ border.radius.shape_top_right,
|
||||
BorderSegment::TopRight,
|
||||
EdgeMask::TOP | EdgeMask::RIGHT,
|
||||
rect.min,
|
||||
border.radius.top_left,
|
||||
rect.max,
|
||||
@@ -816,10 +820,11 @@
|
||||
),
|
||||
border.right,
|
||||
border.bottom,
|
||||
LayoutSize::new(widths.right, widths.bottom),
|
||||
border.radius.bottom_right,
|
||||
+ border.radius.shape_bottom_right,
|
||||
BorderSegment::BottomRight,
|
||||
EdgeMask::BOTTOM | EdgeMask::RIGHT,
|
||||
rect.bottom_left(),
|
||||
border.radius.bottom_left,
|
||||
rect.top_right(),
|
||||
@@ -843,10 +848,11 @@
|
||||
),
|
||||
border.bottom,
|
||||
border.left,
|
||||
LayoutSize::new(widths.left, widths.bottom),
|
||||
border.radius.bottom_left,
|
||||
+ border.radius.shape_bottom_left,
|
||||
BorderSegment::BottomLeft,
|
||||
EdgeMask::BOTTOM | EdgeMask::LEFT,
|
||||
rect.max,
|
||||
border.radius.bottom_right,
|
||||
rect.min,
|
||||
@@ -881,30 +887,37 @@
|
||||
color1: ColorF,
|
||||
segment: BorderSegment,
|
||||
instances: &mut Vec<BorderInstance>,
|
||||
widths: DeviceSize,
|
||||
radius: DeviceSize,
|
||||
+ shape: f32,
|
||||
do_aa: bool,
|
||||
h_adjacent_corner_outer: DevicePoint,
|
||||
h_adjacent_corner_radius: DeviceSize,
|
||||
v_adjacent_corner_outer: DevicePoint,
|
||||
v_adjacent_corner_radius: DeviceSize,
|
||||
+ gpu_buffer_builder: &mut GpuBufferBuilderF,
|
||||
) {
|
||||
let base_flags = (segment as i32) |
|
||||
((style0 as i32) << 8) |
|
||||
((style1 as i32) << 16) |
|
||||
((do_aa as i32) << 28);
|
||||
|
||||
- let base_instance = BorderInstance {
|
||||
- task_origin: DevicePoint::zero(),
|
||||
+ let instance_gpu_data = BorderInstanceGpuData {
|
||||
local_rect: task_rect,
|
||||
- flags: base_flags,
|
||||
color0: color0.premultiplied(),
|
||||
color1: color1.premultiplied(),
|
||||
widths,
|
||||
radius,
|
||||
+ shape
|
||||
+ };
|
||||
+
|
||||
+ let base_instance = BorderInstance {
|
||||
+ task_origin: DevicePoint::zero(),
|
||||
+ flags: base_flags,
|
||||
clip_params: [0.0; 8],
|
||||
+ gpu_data_address: instance_gpu_data.write(gpu_buffer_builder)
|
||||
};
|
||||
|
||||
match segment {
|
||||
BorderSegment::TopLeft |
|
||||
BorderSegment::TopRight |
|
||||
@@ -1018,10 +1031,11 @@
|
||||
non_overlapping_rect: LayoutRect,
|
||||
side0: BorderSide,
|
||||
side1: BorderSide,
|
||||
widths: LayoutSize,
|
||||
radius: LayoutSize,
|
||||
+ shape: f32,
|
||||
segment: BorderSegment,
|
||||
edge_flags: EdgeMask,
|
||||
h_adjacent_corner_outer: LayoutPoint,
|
||||
h_adjacent_corner_radius: LayoutSize,
|
||||
v_adjacent_corner_outer: LayoutPoint,
|
||||
@@ -1137,10 +1151,11 @@
|
||||
do_aa,
|
||||
side0: side0.into(),
|
||||
side1: side1.into(),
|
||||
segment,
|
||||
radius: radius.to_au(),
|
||||
+ shape: shape.to_bits(),
|
||||
size: widths.to_au(),
|
||||
h_adjacent_corner_outer: (h_corner_outer - image_rect.min).to_point().to_au(),
|
||||
h_adjacent_corner_radius: h_corner_radius.to_au(),
|
||||
v_adjacent_corner_outer: (v_corner_outer - image_rect.min).to_point().to_au(),
|
||||
v_adjacent_corner_radius: v_corner_radius.to_au(),
|
||||
@@ -1200,10 +1215,11 @@
|
||||
cache_key: BorderSegmentCacheKey {
|
||||
do_aa,
|
||||
side0: side.into(),
|
||||
side1: side.into(),
|
||||
radius: LayoutSizeAu::zero(),
|
||||
+ shape: 0,
|
||||
size: size.to_au(),
|
||||
segment,
|
||||
h_adjacent_corner_outer: LayoutPointAu::zero(),
|
||||
h_adjacent_corner_radius: LayoutSizeAu::zero(),
|
||||
v_adjacent_corner_outer: LayoutPointAu::zero(),
|
||||
@@ -1217,10 +1233,11 @@
|
||||
pub fn build_border_instances(
|
||||
cache_key: &BorderSegmentCacheKey,
|
||||
cache_size: DeviceIntSize,
|
||||
border: &ApiNormalBorder,
|
||||
scale: LayoutToDeviceScale,
|
||||
+ gpu_buffer_builder: &mut GpuBufferBuilderF,
|
||||
) -> Vec<BorderInstance> {
|
||||
let mut instances = Vec::new();
|
||||
|
||||
let (side0, side1, flip0, flip1) = match cache_key.segment {
|
||||
BorderSegment::Left => (&border.left, &border.left, false, false),
|
||||
@@ -1247,10 +1264,11 @@
|
||||
let color0 = side0.border_color(flip0);
|
||||
let color1 = side1.border_color(flip1);
|
||||
|
||||
let widths = (LayoutSize::from_au(cache_key.size) * scale).ceil();
|
||||
let radius = (LayoutSize::from_au(cache_key.radius) * scale).ceil();
|
||||
+ let shape = f32::from_bits(cache_key.shape);
|
||||
|
||||
let h_corner_outer = (LayoutPoint::from_au(cache_key.h_adjacent_corner_outer) * scale).round();
|
||||
let h_corner_radius = (LayoutSize::from_au(cache_key.h_adjacent_corner_radius) * scale).ceil();
|
||||
let v_corner_outer = (LayoutPoint::from_au(cache_key.v_adjacent_corner_outer) * scale).round();
|
||||
let v_corner_radius = (LayoutSize::from_au(cache_key.v_adjacent_corner_radius) * scale).ceil();
|
||||
@@ -1263,15 +1281,17 @@
|
||||
color1,
|
||||
cache_key.segment,
|
||||
&mut instances,
|
||||
widths,
|
||||
radius,
|
||||
+ shape,
|
||||
border.do_aa,
|
||||
h_corner_outer,
|
||||
h_corner_radius,
|
||||
v_corner_outer,
|
||||
v_corner_radius,
|
||||
+ gpu_buffer_builder,
|
||||
);
|
||||
|
||||
instances
|
||||
}
|
||||
|
||||
diff --git a/gfx/wr/webrender/src/gpu_types.rs b/gfx/wr/webrender/src/gpu_types.rs
|
||||
--- a/gfx/wr/webrender/src/gpu_types.rs
|
||||
+++ b/gfx/wr/webrender/src/gpu_types.rs
|
||||
@@ -192,22 +192,40 @@
|
||||
Top,
|
||||
Right,
|
||||
Bottom,
|
||||
}
|
||||
|
||||
+pub struct BorderInstanceGpuData {
|
||||
+ pub local_rect: DeviceRect,
|
||||
+ pub color0: PremultipliedColorF,
|
||||
+ pub color1: PremultipliedColorF,
|
||||
+ pub widths: DeviceSize,
|
||||
+ pub radius: DeviceSize,
|
||||
+ pub shape: f32,
|
||||
+}
|
||||
+
|
||||
+impl BorderInstanceGpuData {
|
||||
+ pub fn write(&self, gpu_buffer_builder: &mut GpuBufferBuilderF) -> GpuBufferAddress {
|
||||
+ let mut writer = gpu_buffer_builder.write_blocks(5);
|
||||
+ writer.push_one(self.local_rect);
|
||||
+ writer.push_one(self.color0);
|
||||
+ writer.push_one(self.color1);
|
||||
+ writer.push_one([self.widths.width, self.widths.height, self.radius.width, self.radius.height]);
|
||||
+ writer.push_one([self.shape, 0.0, 0.0, 0.0]);
|
||||
+
|
||||
+ writer.finish()
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
#[derive(Debug, Clone)]
|
||||
#[repr(C)]
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
pub struct BorderInstance {
|
||||
pub task_origin: DevicePoint,
|
||||
- pub local_rect: DeviceRect,
|
||||
- pub color0: PremultipliedColorF,
|
||||
- pub color1: PremultipliedColorF,
|
||||
pub flags: i32,
|
||||
- pub widths: DeviceSize,
|
||||
- pub radius: DeviceSize,
|
||||
+ pub gpu_data_address: GpuBufferAddress,
|
||||
pub clip_params: [f32; 8],
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
diff --git a/gfx/wr/webrender/src/prim_store/borders.rs b/gfx/wr/webrender/src/prim_store/borders.rs
|
||||
--- a/gfx/wr/webrender/src/prim_store/borders.rs
|
||||
+++ b/gfx/wr/webrender/src/prim_store/borders.rs
|
||||
@@ -230,19 +230,20 @@
|
||||
false, // TODO(gw): We don't calculate opacity for borders yet!
|
||||
RenderTaskParent::Surface,
|
||||
&mut frame_state.frame_gpu_data.f32,
|
||||
frame_state.rg_builder,
|
||||
&mut frame_state.surface_builder,
|
||||
- &mut |rg_builder, _| {
|
||||
+ &mut |rg_builder, gpu_buffer_builder| {
|
||||
rg_builder.add().init(RenderTask::new_dynamic(
|
||||
cache_size,
|
||||
RenderTaskKind::new_border_segment(
|
||||
build_border_instances(
|
||||
&segment.cache_key,
|
||||
cache_size,
|
||||
&self.border,
|
||||
scale,
|
||||
+ gpu_buffer_builder,
|
||||
)
|
||||
),
|
||||
))
|
||||
}
|
||||
);
|
||||
diff --git a/gfx/wr/webrender/src/renderer/vertex.rs b/gfx/wr/webrender/src/renderer/vertex.rs
|
||||
--- a/gfx/wr/webrender/src/renderer/vertex.rs
|
||||
+++ b/gfx/wr/webrender/src/renderer/vertex.rs
|
||||
@@ -63,16 +63,12 @@
|
||||
|
||||
pub const BORDER: VertexDescriptor = VertexDescriptor {
|
||||
vertex_attributes: &[VertexAttribute::quad_instance_vertex()],
|
||||
instance_attributes: &[
|
||||
VertexAttribute::f32x2("aTaskOrigin"),
|
||||
- VertexAttribute::f32x4("aRect"),
|
||||
- VertexAttribute::f32x4("aColor0"),
|
||||
- VertexAttribute::f32x4("aColor1"),
|
||||
VertexAttribute::i32("aFlags"),
|
||||
- VertexAttribute::f32x2("aWidths"),
|
||||
- VertexAttribute::f32x2("aRadii"),
|
||||
+ VertexAttribute::gpu_buffer_address("aGpuDataAddress"),
|
||||
VertexAttribute::f32x4("aClipParams1"),
|
||||
VertexAttribute::f32x4("aClipParams2"),
|
||||
],
|
||||
};
|
||||
|
||||
|
||||
@@ -2,6 +2,24 @@
|
||||
// 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/.
|
||||
[
|
||||
{
|
||||
"type": "phabricator",
|
||||
"ids": [
|
||||
"D296935",
|
||||
"D303334",
|
||||
"D297660",
|
||||
"D304517",
|
||||
|
||||
"D306773",
|
||||
"D306774",
|
||||
"D306775",
|
||||
"D306776",
|
||||
"D306777",
|
||||
"D306778",
|
||||
"D306779"
|
||||
],
|
||||
"name": "Corner shape support"
|
||||
},
|
||||
{
|
||||
"type": "phabricator",
|
||||
"id": "D299584",
|
||||
|
||||
@@ -16,4 +16,3 @@ category app-startup nsBrowserGlue @mozilla.org/browser/browserglue;1 applicatio
|
||||
#include common/Components.manifest
|
||||
#include sessionstore/SessionComponents.manifest
|
||||
#include live-folders/LiveFoldersComponents.manifest
|
||||
#include space-routing/SpaceRoutingComponents.manifest
|
||||
|
||||
@@ -6,29 +6,29 @@ import { nsZenDOMOperatedFeature } from "chrome://browser/content/zen-components
|
||||
|
||||
// prettier-ignore
|
||||
const SVG_ICONS = [
|
||||
"airplane.svg", "american-football.svg", "baseball.svg", "basket.svg",
|
||||
"bed.svg", "bell.svg", "bookmark.svg", "book.svg",
|
||||
"briefcase.svg", "brush.svg", "bug.svg", "build.svg",
|
||||
"cafe.svg", "call.svg", "card.svg", "chat.svg",
|
||||
"checkbox.svg", "circle.svg", "cloud.svg", "code.svg",
|
||||
"coins.svg", "construct.svg", "cutlery.svg", "egg.svg",
|
||||
"extension-puzzle.svg", "eye.svg", "fast-food.svg", "fish.svg",
|
||||
"flag.svg", "flame.svg", "flask.svg", "folder.svg",
|
||||
"game-controller.svg", "globe-1.svg", "globe.svg", "grid-2x2.svg",
|
||||
"grid-3x3.svg", "heart.svg", "ice-cream.svg", "image.svg",
|
||||
"inbox.svg", "key.svg", "layers.svg", "leaf.svg",
|
||||
"lightning.svg", "location.svg", "lock-closed.svg", "logo-rss.svg",
|
||||
"logo-usd.svg", "mail.svg", "map.svg", "megaphone.svg",
|
||||
"moon.svg", "music.svg", "navigate.svg", "nuclear.svg",
|
||||
"page.svg", "palette.svg", "paw.svg", "people.svg",
|
||||
"pizza.svg", "planet.svg", "present.svg", "rocket.svg",
|
||||
"school.svg", "shapes.svg", "shirt.svg", "skull.svg",
|
||||
"squares.svg", "square.svg", "star-1.svg", "star.svg",
|
||||
"stats-chart.svg", "sun.svg", "tada.svg", "terminal.svg",
|
||||
"ticket.svg", "time.svg", "trash.svg", "triangle.svg",
|
||||
"video.svg", "volume-high.svg", "wallet.svg", "warning.svg",
|
||||
"water.svg", "weight.svg",
|
||||
];
|
||||
"airplane.svg", "american-football.svg", "baseball.svg", "basket.svg",
|
||||
"bed.svg", "bell.svg", "bookmark.svg", "book.svg",
|
||||
"briefcase.svg", "brush.svg", "bug.svg", "build.svg",
|
||||
"cafe.svg", "call.svg", "card.svg", "chat.svg",
|
||||
"checkbox.svg", "circle.svg", "cloud.svg", "code.svg",
|
||||
"coins.svg", "construct.svg", "cutlery.svg", "egg.svg",
|
||||
"extension-puzzle.svg", "eye.svg", "fast-food.svg", "fish.svg",
|
||||
"flag.svg", "flame.svg", "flask.svg", "folder.svg",
|
||||
"game-controller.svg", "globe-1.svg", "globe.svg", "grid-2x2.svg",
|
||||
"grid-3x3.svg", "heart.svg", "ice-cream.svg", "image.svg",
|
||||
"inbox.svg", "key.svg", "layers.svg", "leaf.svg",
|
||||
"lightning.svg", "location.svg", "lock-closed.svg", "logo-rss.svg",
|
||||
"logo-usd.svg", "mail.svg", "map.svg", "megaphone.svg",
|
||||
"moon.svg", "music.svg", "navigate.svg", "nuclear.svg",
|
||||
"page.svg", "palette.svg", "paw.svg", "people.svg",
|
||||
"pizza.svg", "planet.svg", "present.svg", "rocket.svg",
|
||||
"school.svg", "shapes.svg", "shirt.svg", "skull.svg",
|
||||
"squares.svg", "square.svg", "star-1.svg", "star.svg",
|
||||
"stats-chart.svg", "sun.svg", "tada.svg", "terminal.svg",
|
||||
"ticket.svg", "time.svg", "trash.svg", "triangle.svg",
|
||||
"video.svg", "volume-high.svg", "wallet.svg", "warning.svg",
|
||||
"water.svg", "weight.svg",
|
||||
];
|
||||
|
||||
class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
|
||||
#panel;
|
||||
@@ -47,7 +47,6 @@ class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
|
||||
init() {
|
||||
this.#panel = document.getElementById("PanelUI-zen-emojis-picker");
|
||||
this.#panel.addEventListener("popupshowing", this);
|
||||
this.#panel.addEventListener("popupshown", this);
|
||||
this.#panel.addEventListener("popuphidden", this);
|
||||
this.#panel.addEventListener("command", this);
|
||||
this.searchInput.addEventListener("input", this);
|
||||
@@ -58,9 +57,6 @@ class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
|
||||
case "popupshowing":
|
||||
this.#onPopupShowing(event);
|
||||
break;
|
||||
case "popupshown":
|
||||
this.#onPopupShown(event);
|
||||
break;
|
||||
case "popuphidden":
|
||||
this.#onPopupHidden(event);
|
||||
break;
|
||||
@@ -107,20 +103,17 @@ class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
|
||||
return document.getElementById("PanelUI-zen-emojis-picker-search");
|
||||
}
|
||||
|
||||
#changePage(toSvg = false, { animate = true } = {}) {
|
||||
const pages = document.getElementById("PanelUI-zen-emojis-picker-pages");
|
||||
#changePage(toSvg = false) {
|
||||
const itemToScroll = toSvg
|
||||
? this.svgList
|
||||
: pages.querySelector('[emojis="true"]');
|
||||
if (animate) {
|
||||
itemToScroll.scrollIntoView({
|
||||
behavior: "smooth",
|
||||
block: "nearest",
|
||||
inline: "start",
|
||||
});
|
||||
} else {
|
||||
pages.scrollLeft = toSvg ? itemToScroll.offsetLeft : 0;
|
||||
}
|
||||
: document
|
||||
.getElementById("PanelUI-zen-emojis-picker-pages")
|
||||
.querySelector('[emojis="true"]');
|
||||
itemToScroll.scrollIntoView({
|
||||
behavior: "smooth",
|
||||
block: "nearest",
|
||||
inline: "start",
|
||||
});
|
||||
const button = document.getElementById(
|
||||
`PanelUI-zen-emojis-picker-change-${toSvg ? "svg" : "emojis"}`
|
||||
);
|
||||
@@ -187,6 +180,9 @@ class nsZenEmojiPicker extends nsZenDOMOperatedFeature {
|
||||
});
|
||||
emojiList.appendChild(item);
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.searchInput.focus();
|
||||
}, 500);
|
||||
}
|
||||
const svgList = this.svgList;
|
||||
for (const icon of SVG_ICONS) {
|
||||
@@ -203,23 +199,14 @@ 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) {
|
||||
if (event.target !== this.#panel) {
|
||||
return;
|
||||
}
|
||||
this.#clearEmojis();
|
||||
|
||||
this.#changePage(false);
|
||||
|
||||
const emojiList = this.emojiList;
|
||||
emojiList.innerHTML = "";
|
||||
|
||||
|
||||
@@ -185,7 +185,7 @@
|
||||
.toolbarbutton-1:not(#tabs-newtab-button),
|
||||
.urlbar-page-action,
|
||||
.identity-box-button {
|
||||
--tab-border-radius: 6px;
|
||||
--tab-border-radius: 8px;
|
||||
--toolbarbutton-border-radius: var(--tab-border-radius);
|
||||
--toolbarbutton-padding-inner: 6px;
|
||||
--toolbarbutton-padding-outer: 1px;
|
||||
|
||||
@@ -213,6 +213,8 @@
|
||||
--toolbarbutton-border-radius: 6px;
|
||||
--urlbar-margin-inline: 1px !important;
|
||||
|
||||
--zen-squircle-value: 1.4;
|
||||
|
||||
--tab-icon-overlay-stroke: light-dark(white, black) !important;
|
||||
--tab-close-button-padding: 4px !important;
|
||||
|
||||
@@ -343,3 +345,9 @@
|
||||
}
|
||||
|
||||
%include zen-buttons.css
|
||||
|
||||
@media (-moz-pref('layout.css.corner-shape.enabled')) {
|
||||
*:not(.no-squircles) {
|
||||
corner-shape: superellipse(var(--zen-squircle-value));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,13 @@ export class ZenSpaceRoutingNavigation extends ZenUIComponent {
|
||||
return;
|
||||
}
|
||||
|
||||
// The tab we spawn for a route must be allowed to load once without being
|
||||
// redirected again, regardless of when its workspace attribute lands.
|
||||
if (aBrowser._zenSkipNavRouteOnce) {
|
||||
aBrowser._zenSkipNavRouteOnce = false;
|
||||
return;
|
||||
}
|
||||
|
||||
let uri;
|
||||
try {
|
||||
uri = aRequest.QueryInterface(Ci.nsIChannel).URI;
|
||||
@@ -73,49 +80,16 @@ export class ZenSpaceRoutingNavigation extends ZenUIComponent {
|
||||
}
|
||||
|
||||
const currentWorkspaceId = tab.getAttribute("zen-workspace-id");
|
||||
const targetWorkspaceId =
|
||||
win.gZenSpaceRoutingManager.getRedirectTargetWorkspaceId(
|
||||
if (
|
||||
!win.gZenSpaceRoutingManager.shouldRedirectNavigation(
|
||||
uri.spec,
|
||||
currentWorkspaceId,
|
||||
win
|
||||
);
|
||||
if (!targetWorkspaceId) {
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// A brand-new tab whose very first real navigation this is (a
|
||||
// target="_blank" link, window.open(), or a freshly opened tab) is still
|
||||
// showing its initial about:blank document. There is nothing to preserve,
|
||||
// so rather than cancelling the load and spawning a duplicate tab - which
|
||||
// would leave this one behind empty - just move this very tab into the
|
||||
// destination space and let the in-flight load finish in place.
|
||||
const isInitialDocument =
|
||||
aBrowser.browsingContext?.currentWindowGlobal?.isInitialDocument ?? false;
|
||||
if (isInitialDocument) {
|
||||
const wasSelected = tab.selected;
|
||||
// Defer so we don't mutate the tab strip from inside a progress notification.
|
||||
win.setTimeout(() => {
|
||||
if (!tab.isConnected) {
|
||||
return;
|
||||
}
|
||||
gBrowser.selectedTab = tab.owner;
|
||||
win.gZenWorkspaces.moveTabToWorkspace(tab, targetWorkspaceId);
|
||||
if (wasSelected) {
|
||||
const targetWorkspace =
|
||||
win.gZenWorkspaces.getWorkspaceFromId(targetWorkspaceId);
|
||||
if (targetWorkspace) {
|
||||
win.gZenWorkspaces.lastSelectedWorkspaceTabs[targetWorkspaceId] =
|
||||
tab;
|
||||
win.gZenWorkspaces.changeWorkspace(targetWorkspace);
|
||||
}
|
||||
}
|
||||
}, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// An already-loaded page is navigating in place. Preserve it in its current
|
||||
// tab and re-open the destination in a new routed tab instead.
|
||||
//
|
||||
// Under Fission the parent-side aRequest is a RemoteWebProgress stand-in
|
||||
// whose cancel()/loadInfo throw NS_ERROR_NOT_IMPLEMENTED (the real channel
|
||||
// lives in the content process). Stop the in-place load through the browser,
|
||||
@@ -137,14 +111,13 @@ export class ZenSpaceRoutingNavigation extends ZenUIComponent {
|
||||
|
||||
// Defer so we don't mutate the tab strip from inside a progress notification.
|
||||
win.setTimeout(() => {
|
||||
gBrowser.addTab(urlToOpen, {
|
||||
const newTab = gBrowser.addTab(urlToOpen, {
|
||||
triggeringPrincipal: principal,
|
||||
ownerTab: tab.isConnected ? tab : null,
|
||||
// The user was actively navigating this tab, so follow the navigation
|
||||
// into the routed tab instead of opening it in the background (addTab
|
||||
// defaults inBackground to true).
|
||||
inBackground: false,
|
||||
});
|
||||
if (newTab?.linkedBrowser) {
|
||||
newTab.linkedBrowser._zenSkipNavRouteOnce = true;
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,9 +78,6 @@ document.addEventListener(
|
||||
case "cmd_zenReplacePinnedUrlWithCurrent":
|
||||
gZenPinnedTabManager.replacePinnedUrlWithCurrent();
|
||||
break;
|
||||
case "cmd_zenEditPinnedUrl":
|
||||
gZenPinnedTabManager.editPinnedUrl();
|
||||
break;
|
||||
case "cmd_contextZenAddToEssentials":
|
||||
gZenPinnedTabManager.addToEssentials();
|
||||
break;
|
||||
|
||||
@@ -723,11 +723,8 @@
|
||||
const { isNearLeftEdge, isNearRightEdge } =
|
||||
this.#shouldSwitchSpace(event);
|
||||
if (isNearLeftEdge || isNearRightEdge) {
|
||||
if (!this.#changeSpaceTimer && !this.#isOutOfWindow) {
|
||||
if (!this.#changeSpaceTimer) {
|
||||
this.#changeSpaceTimer = setTimeout(() => {
|
||||
if (this.#isOutOfWindow) {
|
||||
return;
|
||||
}
|
||||
this.clearDragOverVisuals();
|
||||
gZenWorkspaces
|
||||
.changeWorkspaceShortcut(
|
||||
@@ -959,10 +956,8 @@
|
||||
if (ownerGlobal?.gZenCompactModeManager) {
|
||||
// Sometimes, dragend doesn't always get called when dragging
|
||||
// to different windows, see gh-8643.
|
||||
requestAnimationFrame(() => {
|
||||
delete ownerGlobal.gZenCompactModeManager._isTabBeingDragged;
|
||||
ownerGlobal.gZenCompactModeManager._clearAllHoverStates();
|
||||
});
|
||||
delete ownerGlobal.gZenCompactModeManager._isTabBeingDragged;
|
||||
ownerGlobal.gZenCompactModeManager._clearAllHoverStates();
|
||||
}
|
||||
this.clearSpaceSwitchTimer();
|
||||
gZenFolders.highlightGroupOnDragOver(null);
|
||||
|
||||
@@ -53,10 +53,6 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
|
||||
#setupEventListeners() {
|
||||
window.addEventListener("TabClose", this.onTabClose.bind(this));
|
||||
window.addEventListener("TabSelect", this.onLocationChange.bind(this));
|
||||
window.addEventListener(
|
||||
"MozDOMFullscreen:Entered",
|
||||
this.onFullscreenEntered.bind(this)
|
||||
);
|
||||
|
||||
document
|
||||
.getElementById("tabbrowser-tabpanels")
|
||||
@@ -1418,23 +1414,6 @@ 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
|
||||
*
|
||||
|
||||
@@ -41,11 +41,11 @@
|
||||
|
||||
&:hover {
|
||||
background: light-dark(rgb(41, 41, 41), rgb(204, 204, 204));
|
||||
scale: 1.05;
|
||||
scale: 1.02;
|
||||
}
|
||||
|
||||
&:hover:active {
|
||||
scale: 0.95;
|
||||
scale: 0.98;
|
||||
}
|
||||
|
||||
& label {
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
<html:template id="zen-glance-sidebar-template">
|
||||
<vbox class="zen-glance-sidebar-container">
|
||||
<toolbarbutton class="zen-glance-sidebar-close toolbarbutton-1" command="cmd_zenGlanceClose" data-l10n-id="zen-general-confirm" />
|
||||
<toolbarbutton class="zen-glance-sidebar-open toolbarbutton-1" command="cmd_zenGlanceExpand" />
|
||||
<toolbarbutton class="zen-glance-sidebar-split toolbarbutton-1" command="cmd_zenGlanceSplit" />
|
||||
<toolbarbutton class="no-squircles zen-glance-sidebar-close toolbarbutton-1" command="cmd_zenGlanceClose" data-l10n-id="zen-general-confirm" />
|
||||
<toolbarbutton class="no-squircles zen-glance-sidebar-open toolbarbutton-1" command="cmd_zenGlanceExpand" />
|
||||
<toolbarbutton class="no-squircles zen-glance-sidebar-split toolbarbutton-1" command="cmd_zenGlanceSplit" />
|
||||
</vbox>
|
||||
</html:template>
|
||||
|
||||
@@ -1232,38 +1232,19 @@ class nsZenWindowSync {
|
||||
activeIndex = Math.min(activeIndex, entries.length - 1);
|
||||
activeIndex = Math.max(activeIndex, 0);
|
||||
let entryToUse = (entries[activeIndex] || entries[0]) ?? null;
|
||||
this.#setPinnedInitialState(
|
||||
aTab,
|
||||
{ url: entryToUse?.url, title: entryToUse?.title },
|
||||
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 => {
|
||||
const targetTab = this.getItemFromWindow(win, aTab.id);
|
||||
if (targetTab) {
|
||||
targetTab._zenPinnedInitialState = initialState;
|
||||
}
|
||||
const initialState = {
|
||||
entry: {
|
||||
url: entryToUse?.url,
|
||||
title: entryToUse?.title,
|
||||
},
|
||||
image,
|
||||
};
|
||||
this.#runOnAllWindows(null, win => {
|
||||
const targetTab = this.getItemFromWindow(win, aTab.id);
|
||||
if (targetTab) {
|
||||
targetTab._zenPinnedInitialState = initialState;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +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/.
|
||||
|
||||
category browser-window-delayed-startup resource:///modules/zen/spacerouting/ZenSpaceRoutingManager.sys.mjs gZenSpaceRoutingManager.onDelayedBrowserStartup
|
||||
@@ -18,58 +18,6 @@ class nsZenSpaceRoutingManager {
|
||||
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
|
||||
* This method can be used to stop the tab from being created.
|
||||
@@ -104,7 +52,7 @@ class nsZenSpaceRoutingManager {
|
||||
break;
|
||||
default: {
|
||||
const targetWorkspace =
|
||||
win.gZenWorkspaces.getWorkspaceFromId(targetRoute);
|
||||
win?.gZenWorkspaces?.getWorkspaceFromId(targetRoute);
|
||||
|
||||
if (targetWorkspace) {
|
||||
userContextId = targetWorkspace.containerTabId;
|
||||
@@ -159,26 +107,8 @@ class nsZenSpaceRoutingManager {
|
||||
* @returns {boolean} True when the navigation should open in a new routed tab
|
||||
*/
|
||||
shouldRedirectNavigation(uriString, currentWorkspaceId, win) {
|
||||
return !!this.getRedirectTargetWorkspaceId(
|
||||
uriString,
|
||||
currentWorkspaceId,
|
||||
win
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the destination space for an in-place top-level navigation, or
|
||||
* null when the navigation should be left alone (no rule, the destination is
|
||||
* "most-recent-space", the tab already lives there, or the space is gone).
|
||||
*
|
||||
* @param {string} uriString - The destination URI
|
||||
* @param {string|null} currentWorkspaceId - The zen-workspace-id of the navigating tab
|
||||
* @param {Window} win - The owning browser window
|
||||
* @returns {string|null} The target workspace id, or null to leave the navigation in place
|
||||
*/
|
||||
getRedirectTargetWorkspaceId(uriString, currentWorkspaceId, win) {
|
||||
if (!win?.gZenWorkspaces?.workspaceEnabled) {
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
const targetRoute = this.routeUri(uriString, { fromExternal: false });
|
||||
@@ -188,13 +118,11 @@ class nsZenSpaceRoutingManager {
|
||||
targetRoute === "most-recent-space" ||
|
||||
targetRoute === currentWorkspaceId
|
||||
) {
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only redirect when the destination space actually exists.
|
||||
return win.gZenWorkspaces.getWorkspaceFromId(targetRoute)
|
||||
? targetRoute
|
||||
: null;
|
||||
return !!win.gZenWorkspaces.getWorkspaceFromId(targetRoute);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -475,37 +403,6 @@ class nsZenSpaceRoutingManager {
|
||||
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
|
||||
* routes is stripped of empty routes
|
||||
|
||||
@@ -1507,26 +1507,18 @@ class nsZenWorkspaces {
|
||||
}
|
||||
|
||||
if (container) {
|
||||
const newtabPlacement = Services.prefs.getBoolPref(
|
||||
"zen.view.show-newtab-button-top",
|
||||
false
|
||||
);
|
||||
const insertElement = newtabPlacement
|
||||
? container.firstChild
|
||||
: container.lastChild;
|
||||
|
||||
if (tab.group?.hasAttribute("split-view-group")) {
|
||||
gBrowser.zenHandleTabMove(tab.group, () => {
|
||||
for (const subTab of tab.group.tabs) {
|
||||
subTab.setAttribute("zen-workspace-id", workspaceID);
|
||||
}
|
||||
container.insertBefore(tab.group, insertElement);
|
||||
container.insertBefore(tab.group, container.lastChild);
|
||||
});
|
||||
continue;
|
||||
}
|
||||
gBrowser.zenHandleTabMove(tab, () => {
|
||||
tab.setAttribute("zen-workspace-id", workspaceID);
|
||||
container.insertBefore(tab, insertElement);
|
||||
container.insertBefore(tab, container.lastChild);
|
||||
});
|
||||
}
|
||||
// also change glance tab if it's the same tab
|
||||
@@ -2298,12 +2290,12 @@ class nsZenWorkspaces {
|
||||
}
|
||||
|
||||
onBeforeTabSelect(aTab) {
|
||||
if (this.#inChangingWorkspace || !aTab) {
|
||||
if (this.#inChangingWorkspace) {
|
||||
// Just in case, Let's not do these checks while we are
|
||||
// in the middle of changing workspace,
|
||||
return false;
|
||||
}
|
||||
const tabSpace = aTab.getAttribute("zen-workspace-id");
|
||||
const tabSpace = aTab?.getAttribute("zen-workspace-id");
|
||||
if (
|
||||
tabSpace &&
|
||||
tabSpace !== this.activeWorkspace &&
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
& toolbarbutton {
|
||||
margin: 0;
|
||||
max-width: 28px;
|
||||
border-radius: var(--toolbarbutton-border-radius);
|
||||
height: 28px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
@@ -1240,17 +1240,13 @@ class nsZenViewSplitter extends nsZenDOMOperatedFeature {
|
||||
*/
|
||||
contextSplitTabs(otherTabHint = null) {
|
||||
let tabs;
|
||||
let currentTab = gZenGlanceManager.getTabOrGlanceParent(
|
||||
TabContextMenu.contextTab || gBrowser.selectedTab
|
||||
);
|
||||
let currentTab = TabContextMenu.contextTab || gBrowser.selectedTab;
|
||||
if (currentTab.multiselected) {
|
||||
tabs = gBrowser.selectedTabs;
|
||||
} else if (!currentTab.selected && !currentTab.splitView) {
|
||||
tabs = [
|
||||
currentTab,
|
||||
...gBrowser.selectedTabs.filter(
|
||||
t => t !== currentTab && !t.hasAttribute("zen-glance-tab")
|
||||
),
|
||||
...gBrowser.selectedTabs.filter(t => t !== currentTab),
|
||||
];
|
||||
} else {
|
||||
tabs = [currentTab];
|
||||
|
||||
@@ -246,66 +246,6 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
|
||||
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() {
|
||||
let cmdClose = document.getElementById("cmd_close");
|
||||
|
||||
@@ -605,20 +545,11 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
|
||||
}
|
||||
const elements = window.MozXULElement.parseXULToFragment(`
|
||||
<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"
|
||||
data-lazy-l10n-id="tab-context-zen-replace-pinned-url-with-current"
|
||||
data-l10n-args="{"isEssential":""}"
|
||||
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-replace-pinned-url-with-current"
|
||||
data-lazy-l10n-id="tab-context-zen-replace-pinned-url-with-current"
|
||||
data-l10n-args="{"isEssential":""}"
|
||||
hidden="true"
|
||||
command="cmd_zenReplacePinnedUrlWithCurrent"/>
|
||||
<menuitem id="context_zen-reset-pinned-tab"
|
||||
data-lazy-l10n-id="tab-context-zen-reset-pinned-tab"
|
||||
data-l10n-args="{"isEssential":""}"
|
||||
@@ -688,24 +619,15 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
|
||||
const zenResetPinnedTab = document.getElementById(
|
||||
"context_zen-reset-pinned-tab"
|
||||
);
|
||||
const zenEditPinnedPage = document.getElementById(
|
||||
"context_zen-edit-pinned-page"
|
||||
);
|
||||
const zenReplacePinnedUrl = document.getElementById(
|
||||
"context_zen-replace-pinned-url-with-current"
|
||||
);
|
||||
[zenResetPinnedTab, zenEditPinnedPage].forEach(element => {
|
||||
[zenResetPinnedTab, zenReplacePinnedUrl].forEach(element => {
|
||||
if (element) {
|
||||
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;
|
||||
document.l10n
|
||||
.formatValue("tab-context-zen-add-essential-badge", {
|
||||
@@ -935,7 +857,7 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
|
||||
) {
|
||||
return;
|
||||
}
|
||||
// Remove # from the URL
|
||||
// Remove # and ? from the URL
|
||||
const pinUrl = tab._zenPinnedInitialState.entry.url.split("#")[0];
|
||||
const currentUrl = location.split("#")[0];
|
||||
// Add an indicator that the pin has been changed
|
||||
@@ -975,14 +897,10 @@ class nsZenPinnedTabManager extends nsZenDOMOperatedFeature {
|
||||
} else {
|
||||
tab.setAttribute("zen-pinned-changed", "true");
|
||||
}
|
||||
if (tab._zenPinnedInitialState.image) {
|
||||
tab.style.setProperty(
|
||||
"--zen-original-tab-icon",
|
||||
`url(${tab._zenPinnedInitialState.image})`
|
||||
);
|
||||
} else {
|
||||
tab.style.removeProperty("--zen-original-tab-icon");
|
||||
}
|
||||
tab.style.setProperty(
|
||||
"--zen-original-tab-icon",
|
||||
`url(${tab._zenPinnedInitialState.image})`
|
||||
);
|
||||
}
|
||||
|
||||
removeTabContainersDragoverClass(hideIndicator = true) {
|
||||
|
||||
@@ -235,7 +235,7 @@
|
||||
}
|
||||
|
||||
@media (-moz-platform: macos) {
|
||||
--border-radius-medium: 12px;
|
||||
--border-radius-medium: 14px;
|
||||
--tab-border-radius: 8px;
|
||||
}
|
||||
|
||||
@@ -1232,6 +1232,8 @@
|
||||
background: var(--zen-essential-tab-selected-bg);
|
||||
margin: var(--zen-essential-bg-margin);
|
||||
border-radius: calc(var(--border-radius-medium) - var(--zen-essential-bg-margin));
|
||||
/* stylelint-disable-next-line property-no-unknown */
|
||||
corner-shape: var(--zen-squircle-value);
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 0;
|
||||
|
||||
@@ -13,8 +13,6 @@ prefs = ["zen.workspaces.separate-essentials=false"]
|
||||
|
||||
["browser_pinned_created.js"]
|
||||
|
||||
["browser_pinned_edit_url.js"]
|
||||
|
||||
["browser_pinned_nounload_reset.js"]
|
||||
|
||||
["browser_pinned_reset_button.js"]
|
||||
|
||||
@@ -1,384 +0,0 @@
|
||||
/* 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",
|
||||
"version": {
|
||||
"product": "firefox",
|
||||
"version": "152.0.3",
|
||||
"candidate": "152.0.3",
|
||||
"version": "152.0.1",
|
||||
"candidate": "152.0.1",
|
||||
"candidateBuild": 1
|
||||
},
|
||||
"buildOptions": {
|
||||
@@ -20,7 +20,7 @@
|
||||
"brandShortName": "Zen",
|
||||
"brandFullName": "Zen Browser",
|
||||
"release": {
|
||||
"displayVersion": "1.21.4b",
|
||||
"displayVersion": "1.21.3b",
|
||||
"github": {
|
||||
"repo": "zen-browser/desktop"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user