From 66c5931a29016cf3844e46f24a7ee64085bcf77b Mon Sep 17 00:00:00 2001 From: "mr. m" <91018726+mr-cheffy@users.noreply.github.com> Date: Sun, 30 Aug 2026 00:26:45 +0200 Subject: [PATCH] gh-15163: Sync upstream Firefox to version `155.0` RC (gh-15164) --- README.md | 2 +- build/firefox-cache/l10n-last-commit-hash | 2 +- src/-stylelintrc-js.patch | 6 +- .../CustomizableUI-sys-mjs.patch | 45 +- .../preferences/preferences-xhtml.patch | 2 +- .../tabbrowser/content/drag-and-drop-js.patch | 104 +- .../tabbrowser/content/tabbrowser-js.patch | 206 +- .../urlbar/UrlbarPrefs-sys-mjs.patch | 12 +- ...barProviderHeuristicFallback-sys-mjs.patch | 6 +- .../urlbar/UrlbarUtils-sys-mjs.patch | 39 +- .../content/UrlbarChildController-mjs.patch | 28 +- .../urlbar/content/UrlbarInput-mjs.patch | 110 +- .../urlbar/content/UrlbarShared-mjs.patch | 38 +- .../urlbar/content/UrlbarView-mjs.patch | 14 +- src/browser/themes/linux/browser-css.patch | 4 +- .../shared/tabbrowser/content-area-css.patch | 20 +- .../themes/shared/tabbrowser/tabs-css.patch | 42 +- .../themes/shared/urlbar-searchbar-css.patch | 24 +- ...low_backdrop_to_work_on_transparency.patch | 14 +- .../firefox/bug_2011236.patch | 4 +- .../firefox/css_corner_shape_rendering.patch | 3796 +++++------------ src/external-patches/manifest.json | 18 +- src/layout/style/StyleColor-cpp.patch | 14 +- src/layout/svg/SVGImageContext-h.patch | 20 +- .../moz-toggle/moz-toggle-tokens-css.patch | 4 +- src/widget/cocoa/nsCocoaWindow-h.patch | 10 +- ...pp.patch => nsDragSessionSource-cpp.patch} | 14 +- src/zen/common/modules/ZenUIManager.mjs | 2 +- src/zen/common/styles/zen-omnibox.css | 5 +- .../common/styles/zen-single-components.css | 2 + .../zen-tabs/vertical-tabs-topbuttons-fix.css | 1 + src/zen/tabs/zen-tabs/vertical-tabs.css | 4 +- src/zen/urlbar/ZenUBActionsProvider.sys.mjs | 27 +- src/zen/welcome/zen-welcome.css | 2 + surfer.json | 2 +- 35 files changed, 1416 insertions(+), 3227 deletions(-) rename src/widget/gtk/{nsDragService-cpp.patch => nsDragSessionSource-cpp.patch} (70%) diff --git a/README.md b/README.md index 434e58315..f391024f4 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ 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 `154.0.1`! -- [`Twilight`](https://zen-browser.app/download?twilight) - Is currently built using Firefox version `RC 154.0.1`! +- [`Twilight`](https://zen-browser.app/download?twilight) - Is currently built using Firefox version `RC 155.0`! ### Contributing diff --git a/build/firefox-cache/l10n-last-commit-hash b/build/firefox-cache/l10n-last-commit-hash index 2dbfec531..e668d9111 100644 --- a/build/firefox-cache/l10n-last-commit-hash +++ b/build/firefox-cache/l10n-last-commit-hash @@ -1 +1 @@ -bf4b57fd7fe94054457be7018e8e7beaf754c520 \ No newline at end of file +6b4abe0093721c1be89eb41b3fb060dd6336923d \ No newline at end of file diff --git a/src/-stylelintrc-js.patch b/src/-stylelintrc-js.patch index baadd6208..75a6e5999 100644 --- a/src/-stylelintrc-js.patch +++ b/src/-stylelintrc-js.patch @@ -1,5 +1,5 @@ diff --git a/.stylelintrc.js b/.stylelintrc.js -index b484bf6600a7e1b8ad2aed8c8b7fe5e84cc3d023..f89712e2c13dfa0bcb48e55cd3872c67f5c98dad 100644 +index b5f8245fa04ffb8fc82240261d63f893f4629f19..1b6ead24adbe69fcb5aeb95cfd3e7fa9662d89d8 100644 --- a/.stylelintrc.js +++ b/.stylelintrc.js @@ -67,7 +67,7 @@ module.exports = { @@ -11,10 +11,10 @@ index b484bf6600a7e1b8ad2aed8c8b7fe5e84cc3d023..f89712e2c13dfa0bcb48e55cd3872c67 { ignore: ["blockless-at-rules"], }, -@@ -280,7 +280,7 @@ module.exports = { - "media-query-no-invalid": null, +@@ -281,7 +281,7 @@ module.exports = { "stylelint-plugin-mozilla/media-query-no-invalid": true, "stylelint-plugin-mozilla/no-base-design-tokens": true, + "stylelint-plugin-mozilla/no-has-selector": true, - "stylelint-plugin-mozilla/use-design-tokens": true, + "stylelint-plugin-mozilla/use-design-tokens": false, }, diff --git a/src/browser/components/customizableui/CustomizableUI-sys-mjs.patch b/src/browser/components/customizableui/CustomizableUI-sys-mjs.patch index 15192e8c1..ba2b8eb35 100644 --- a/src/browser/components/customizableui/CustomizableUI-sys-mjs.patch +++ b/src/browser/components/customizableui/CustomizableUI-sys-mjs.patch @@ -1,5 +1,5 @@ diff --git a/browser/components/customizableui/CustomizableUI.sys.mjs b/browser/components/customizableui/CustomizableUI.sys.mjs -index 491161e48044f75167a8e8a0348779cfa22fc40e..867d51b8a6b74f841afc6d84dcdfd8f2681652e5 100644 +index 6d1885a54815d10b3a4e79f7e5acd61ab3abb159..97e07d85c69ec97baeab05aedd57bd8869254d59 100644 --- a/browser/components/customizableui/CustomizableUI.sys.mjs +++ b/browser/components/customizableui/CustomizableUI.sys.mjs @@ -13,6 +13,7 @@ ChromeUtils.defineESModuleGetters(lazy, { @@ -10,7 +10,7 @@ index 491161e48044f75167a8e8a0348779cfa22fc40e..867d51b8a6b74f841afc6d84dcdfd8f2 HomePage: "resource:///modules/HomePage.sys.mjs", PanelMultiView: "moz-src:///browser/components/customizableui/PanelMultiView.sys.mjs", -@@ -348,7 +349,7 @@ var CustomizableUIInternal = { +@@ -352,7 +353,7 @@ var CustomizableUIInternal = { { type: CustomizableUI.TYPE_PANEL, defaultPlacements: [], @@ -19,7 +19,7 @@ index 491161e48044f75167a8e8a0348779cfa22fc40e..867d51b8a6b74f841afc6d84dcdfd8f2 }, false ); -@@ -358,27 +359,23 @@ var CustomizableUIInternal = { +@@ -362,27 +363,23 @@ var CustomizableUIInternal = { "back-button", "forward-button", "stop-reload-button", @@ -50,17 +50,18 @@ index 491161e48044f75167a8e8a0348779cfa22fc40e..867d51b8a6b74f841afc6d84dcdfd8f2 defaultCollapsed: false, }, true -@@ -402,9 +399,6 @@ var CustomizableUIInternal = { +@@ -406,10 +403,6 @@ var CustomizableUIInternal = { type: CustomizableUI.TYPE_TOOLBAR, defaultPlacements: [ "tabbrowser-tabs", - "new-tab-button", +- "spring", - "alltabs-button", - "ai-window-toggle", ], verticalTabsDefaultPlacements: [], defaultCollapsed: null, -@@ -484,6 +478,7 @@ var CustomizableUIInternal = { +@@ -502,6 +495,7 @@ var CustomizableUIInternal = { CustomizableUI.AREA_NAVBAR, CustomizableUI.AREA_BOOKMARKS, CustomizableUI.AREA_TABSTRIP, @@ -68,7 +69,7 @@ index 491161e48044f75167a8e8a0348779cfa22fc40e..867d51b8a6b74f841afc6d84dcdfd8f2 ]); if (AppConstants.platform != "macosx") { toolbars.add(CustomizableUI.AREA_MENUBAR); -@@ -1285,6 +1280,9 @@ var CustomizableUIInternal = { +@@ -1334,6 +1328,9 @@ var CustomizableUIInternal = { placements = gPlacements.get(area); } @@ -78,7 +79,7 @@ index 491161e48044f75167a8e8a0348779cfa22fc40e..867d51b8a6b74f841afc6d84dcdfd8f2 // For toolbars that need it, mark as dirty. let defaultPlacements = areaProperties.get("defaultPlacements"); if ( -@@ -1793,7 +1791,6 @@ var CustomizableUIInternal = { +@@ -1842,7 +1839,6 @@ var CustomizableUIInternal = { lazy.log.info( "Widget " + aWidgetId + " not found, unable to remove from " + aArea ); @@ -86,7 +87,7 @@ index 491161e48044f75167a8e8a0348779cfa22fc40e..867d51b8a6b74f841afc6d84dcdfd8f2 } this.notifyDOMChange(widgetNode, null, container, true, () => { -@@ -1803,7 +1800,7 @@ var CustomizableUIInternal = { +@@ -1852,7 +1848,7 @@ var CustomizableUIInternal = { // We also need to remove the panel context menu if it's there: this.ensureButtonContextMenu(widgetNode); if (gPalette.has(aWidgetId) || this.isSpecialWidget(aWidgetId)) { @@ -95,7 +96,7 @@ index 491161e48044f75167a8e8a0348779cfa22fc40e..867d51b8a6b74f841afc6d84dcdfd8f2 } else { window.gNavToolbox.palette.appendChild(widgetNode); } -@@ -1971,16 +1968,16 @@ var CustomizableUIInternal = { +@@ -2020,16 +2016,16 @@ var CustomizableUIInternal = { elem.setAttribute("skipintoolbarset", "true"); } } @@ -115,7 +116,7 @@ index 491161e48044f75167a8e8a0348779cfa22fc40e..867d51b8a6b74f841afc6d84dcdfd8f2 // Handle initial state of vertical tabs. if (isVerticalTabs) { // Show the vertical tabs toolbar -@@ -2222,6 +2219,10 @@ var CustomizableUIInternal = { +@@ -2271,6 +2267,10 @@ var CustomizableUIInternal = { * The identifier string of the area that aNode is being inserted into. */ insertWidgetBefore(aNode, aNextNode, aContainer, aAreaId) { @@ -126,7 +127,7 @@ index 491161e48044f75167a8e8a0348779cfa22fc40e..867d51b8a6b74f841afc6d84dcdfd8f2 this.notifyDOMChange(aNode, aNextNode, aContainer, false, () => { this.setLocationAttributes(aNode, aAreaId); aContainer.insertBefore(aNode, aNextNode); -@@ -4592,7 +4593,7 @@ var CustomizableUIInternal = { +@@ -4647,7 +4647,7 @@ var CustomizableUIInternal = { * For all registered areas, builds those areas to reflect the current * placement state of all widgets. */ @@ -135,7 +136,7 @@ index 491161e48044f75167a8e8a0348779cfa22fc40e..867d51b8a6b74f841afc6d84dcdfd8f2 for (let [areaId, areaNodes] of gBuildAreas) { let placements = gPlacements.get(areaId); let isFirstChangedToolbar = true; -@@ -4603,7 +4604,7 @@ var CustomizableUIInternal = { +@@ -4658,7 +4658,7 @@ var CustomizableUIInternal = { if (area.get("type") == CustomizableUI.TYPE_TOOLBAR) { let defaultCollapsed = area.get("defaultCollapsed"); let win = areaNode.documentGlobal; @@ -144,7 +145,7 @@ index 491161e48044f75167a8e8a0348779cfa22fc40e..867d51b8a6b74f841afc6d84dcdfd8f2 win.setToolbarVisibility( areaNode, typeof defaultCollapsed == "string" -@@ -5899,6 +5900,7 @@ export var CustomizableUI = { +@@ -5971,6 +5971,7 @@ export var CustomizableUI = { unregisterArea(aName, aDestroyPlacements) { CustomizableUIInternal.unregisterArea(aName, aDestroyPlacements); }, @@ -152,7 +153,7 @@ index 491161e48044f75167a8e8a0348779cfa22fc40e..867d51b8a6b74f841afc6d84dcdfd8f2 /** * Add a widget to an area. * If the area to which you try to add is not known to CustomizableUI, -@@ -7869,7 +7871,9 @@ class OverflowableToolbar { +@@ -7946,7 +7947,9 @@ class OverflowableToolbar { ); if (webExtList && CustomizableUI.isWebExtensionWidget(child.id)) { @@ -162,7 +163,7 @@ index 491161e48044f75167a8e8a0348779cfa22fc40e..867d51b8a6b74f841afc6d84dcdfd8f2 webExtList.insertBefore(child, webExtList.firstElementChild); } else { child.setAttribute("cui-anchorid", this.#defaultListButton.id); -@@ -7929,7 +7933,7 @@ class OverflowableToolbar { +@@ -8006,7 +8009,7 @@ class OverflowableToolbar { ) { continue; } @@ -171,7 +172,7 @@ index 491161e48044f75167a8e8a0348779cfa22fc40e..867d51b8a6b74f841afc6d84dcdfd8f2 if (child != aExceptChild) { sum += getInlineSize(child); } -@@ -7953,11 +7957,11 @@ class OverflowableToolbar { +@@ -8030,11 +8033,11 @@ class OverflowableToolbar { parseFloat(style.paddingLeft) - parseFloat(style.paddingRight) - toolbarChildrenWidth; @@ -185,7 +186,7 @@ index 491161e48044f75167a8e8a0348779cfa22fc40e..867d51b8a6b74f841afc6d84dcdfd8f2 }); lazy.log.debug( -@@ -7972,7 +7976,39 @@ class OverflowableToolbar { +@@ -8049,7 +8052,39 @@ class OverflowableToolbar { Math.max(targetWidth, targetChildrenWidth) ); totalAvailWidth = Math.ceil(totalAvailWidth); @@ -226,7 +227,7 @@ index 491161e48044f75167a8e8a0348779cfa22fc40e..867d51b8a6b74f841afc6d84dcdfd8f2 return { isOverflowing, targetContentWidth, totalAvailWidth }; } -@@ -8033,7 +8069,11 @@ class OverflowableToolbar { +@@ -8110,7 +8145,11 @@ class OverflowableToolbar { return; } } @@ -239,7 +240,7 @@ index 491161e48044f75167a8e8a0348779cfa22fc40e..867d51b8a6b74f841afc6d84dcdfd8f2 lazy.log.debug( `Need ${minSize} but width is ${totalAvailWidth} so bailing` ); -@@ -8066,7 +8106,7 @@ class OverflowableToolbar { +@@ -8143,7 +8182,7 @@ class OverflowableToolbar { } } if (!inserted) { @@ -248,7 +249,7 @@ index 491161e48044f75167a8e8a0348779cfa22fc40e..867d51b8a6b74f841afc6d84dcdfd8f2 } child.removeAttribute("cui-anchorid"); child.removeAttribute("overflowedItem"); -@@ -8192,6 +8232,9 @@ class OverflowableToolbar { +@@ -8269,6 +8308,9 @@ class OverflowableToolbar { * if no such list exists. */ get #webExtList() { @@ -258,7 +259,7 @@ index 491161e48044f75167a8e8a0348779cfa22fc40e..867d51b8a6b74f841afc6d84dcdfd8f2 if (!this.#webExtListRef) { let targetID = this.#toolbar.getAttribute("addon-webext-overflowtarget"); if (!targetID) { -@@ -8203,6 +8246,9 @@ class OverflowableToolbar { +@@ -8280,6 +8322,9 @@ class OverflowableToolbar { let win = this.#toolbar.documentGlobal; let { panel } = win.gUnifiedExtensions; this.#webExtListRef = panel.querySelector(`#${targetID}`); @@ -268,7 +269,7 @@ index 491161e48044f75167a8e8a0348779cfa22fc40e..867d51b8a6b74f841afc6d84dcdfd8f2 } return this.#webExtListRef; } -@@ -8411,7 +8457,7 @@ class OverflowableToolbar { +@@ -8488,7 +8533,7 @@ class OverflowableToolbar { break; } case "mousedown": { diff --git a/src/browser/components/preferences/preferences-xhtml.patch b/src/browser/components/preferences/preferences-xhtml.patch index 2c2c134fb..354e6c5c5 100644 --- a/src/browser/components/preferences/preferences-xhtml.patch +++ b/src/browser/components/preferences/preferences-xhtml.patch @@ -27,7 +27,7 @@ index 1b8c38159314b6edee9e6f455beb2b63db1ad3f0..1308345db640dba6b2848b49ef7c1181 + + + + { -@@ -552,7 +566,7 @@ +@@ -580,7 +594,7 @@ if (tab.selected) { selectedTab = tab; indexForSelectedTab = newIndex; @@ -113,7 +113,7 @@ index 8bb1219962a1368f50b53c61041f135c68e35f31..8e72441d26883cbef7a352b00c0fe2b1 const droppedIntoPinnedArea = dropIndex < gBrowser.pinnedTabCount; const newSplitView = gBrowser.adoptSplitView(tab, { elementIndex: droppedIntoPinnedArea -@@ -577,7 +591,7 @@ +@@ -605,7 +619,7 @@ } } } @@ -122,7 +122,7 @@ index 8bb1219962a1368f50b53c61041f135c68e35f31..8e72441d26883cbef7a352b00c0fe2b1 const newTab = gBrowser.adoptTab(selectedTab, { elementIndex: indexForSelectedTab, selectTab: selectedTab == draggedTab, -@@ -611,10 +625,6 @@ +@@ -639,10 +653,6 @@ this._tabbrowserTabs.dragAndDropElements[ gBrowser.pinnedTabCount + unpinnedSplitViews.length - 1 ]; @@ -133,7 +133,7 @@ index 8bb1219962a1368f50b53c61041f135c68e35f31..8e72441d26883cbef7a352b00c0fe2b1 } } } else { -@@ -664,6 +674,7 @@ +@@ -703,6 +713,7 @@ let nextItem = this._tabbrowserTabs.dragAndDropElements[newIndex]; let tabGroup = isTab(nextItem) && nextItem.group; @@ -141,12 +141,15 @@ index 8bb1219962a1368f50b53c61041f135c68e35f31..8e72441d26883cbef7a352b00c0fe2b1 gBrowser.loadTabs(urls, { inBackground, replace, -@@ -705,7 +716,16 @@ +@@ -744,6 +755,22 @@ this._expandGroupOnDrop(draggedTab); } - this._resetTabsAfterDrop(draggedTab.ownerDocument); -- -+ if (!dt.mozUserCancelled && dt.dropEffect == "none" && !this._isCustomizing) { + this._resetTabsAfterDrop(draggedTab); ++ if ( ++ !dt.mozUserCancelled && ++ dt.dropEffect == "none" && ++ !this._tabbrowserTabs._isCustomizing ++ ) { + const moved = gZenViewSplitter.moveTabToSplitView(event, draggedTab); + if (moved) { + delete draggedTab._dragData; @@ -154,12 +157,14 @@ index 8bb1219962a1368f50b53c61041f135c68e35f31..8e72441d26883cbef7a352b00c0fe2b1 + } + } else if (dt.mozUserCancelled) { + gZenViewSplitter.onBrowserDragEndToSplit(event, true); -+ if (gZenViewSplitter._lastOpenedTab) gZenViewSplitter._lastOpenedTab._visuallySelected = false; ++ if (gZenViewSplitter._lastOpenedTab) { ++ gZenViewSplitter._lastOpenedTab._visuallySelected = false; ++ } + } + if ( dt.mozUserCancelled || - dt.dropEffect != "none" || -@@ -927,11 +947,10 @@ +@@ -1074,11 +1101,10 @@ return this.#getHorizontalScrollboxDragTarget(event, ignoreSides); } while (target) { @@ -175,7 +180,7 @@ index 8bb1219962a1368f50b53c61041f135c68e35f31..8e72441d26883cbef7a352b00c0fe2b1 break; } target = target.parentNode; -@@ -961,6 +980,9 @@ +@@ -1108,6 +1134,9 @@ return null; } } @@ -185,17 +190,36 @@ index 8bb1219962a1368f50b53c61041f135c68e35f31..8e72441d26883cbef7a352b00c0fe2b1 return target; } -@@ -1014,7 +1036,8 @@ - isTabGroupLabel(draggedTab) && - draggedTab._dragData?.expandGroupOnDrop - ) { -- draggedTab.group.collapsed = false; -+ draggedTab.group.collapsed = draggedTab.group.hasAttribute("has-active"); -+ gZenFolders.animateGroupMove(draggedTab.group, true); +@@ -1170,14 +1199,20 @@ + let releaseReservedSpace = () => + this.#releaseSpaceInScrolledContent(periphery); + if (group.collapsed) { +- // The group's tabs animate back to their full size, so hold on to the +- // space reserved for them until they get there. +- group.addEventListener( +- "TabGroupAnimationComplete", +- releaseReservedSpace, +- { once: true } +- ); +- group.collapsed = false; ++ const expand = !group.hasAttribute("has-active"); ++ if (expand) { ++ // The group's tabs animate back to their full size, so hold on to the ++ // space reserved for them until they get there. ++ group.addEventListener( ++ "TabGroupAnimationComplete", ++ releaseReservedSpace, ++ { once: true } ++ ); ++ } else { ++ releaseReservedSpace(); ++ } ++ group.collapsed = !expand; ++ gZenFolders.animateGroupMove(group, true); + } else { + releaseReservedSpace(); } - } - -@@ -1206,7 +1229,6 @@ +@@ -1362,7 +1397,6 @@ // using updateDragImage. On Linux, we can use a panel. if (platform == "win" || platform == "macosx") { captureListener = function () { @@ -203,7 +227,7 @@ index 8bb1219962a1368f50b53c61041f135c68e35f31..8e72441d26883cbef7a352b00c0fe2b1 }; } else { // Create a panel to use it in setDragImage -@@ -1244,7 +1266,6 @@ +@@ -1400,7 +1434,6 @@ ); dragImageOffset = dragImageOffset * scale; } @@ -211,7 +235,7 @@ index 8bb1219962a1368f50b53c61041f135c68e35f31..8e72441d26883cbef7a352b00c0fe2b1 // _dragData.offsetX/Y give the coordinates that the mouse should be // positioned relative to the corner of the new window created upon -@@ -1263,7 +1284,7 @@ +@@ -1419,7 +1452,7 @@ let dropEffect = this.getDropEffectForTabDrag(event); let isMovingInTabStrip = !fromTabList && dropEffect == "move"; let collapseTabGroupDuringDrag = @@ -220,7 +244,7 @@ index 8bb1219962a1368f50b53c61041f135c68e35f31..8e72441d26883cbef7a352b00c0fe2b1 tab._dragData = { offsetX: this._tabbrowserTabs.verticalMode -@@ -1273,7 +1294,7 @@ +@@ -1429,7 +1462,7 @@ ? event.screenY - window.screenY - tabOffset : event.screenY - window.screenY, scrollPos: @@ -229,7 +253,7 @@ index 8bb1219962a1368f50b53c61041f135c68e35f31..8e72441d26883cbef7a352b00c0fe2b1 ? this._tabbrowserTabs.pinnedTabsContainer.scrollPosition : this._tabbrowserTabs.arrowScrollbox.scrollPosition, screenX: event.screenX, -@@ -1320,6 +1341,7 @@ +@@ -1522,6 +1555,7 @@ if (tabStripItemElement.hasAttribute("dragtarget")) { return; } @@ -237,7 +261,7 @@ index 8bb1219962a1368f50b53c61041f135c68e35f31..8e72441d26883cbef7a352b00c0fe2b1 let isPinned = tab.pinned; let dragAndDropElements = this._tabbrowserTabs.dragAndDropElements; let isGrid = this._tabbrowserTabs.isContainerVerticalPinnedGrid(tab); -@@ -1686,23 +1708,6 @@ +@@ -1895,23 +1929,6 @@ // Slide the relevant tabs to their new position. // non-moving tabs adjust for RTL @@ -261,7 +285,7 @@ index 8bb1219962a1368f50b53c61041f135c68e35f31..8e72441d26883cbef7a352b00c0fe2b1 // moving tabs don't adjust for RTL for (let item of selectedElements) { if ( -@@ -1751,7 +1756,6 @@ +@@ -1960,7 +1977,6 @@ for (let item of this._tabbrowserTabs.dragAndDropElements) { delete item._moveTogetherSelectedTabsData; item = elementToMove(item); @@ -269,7 +293,7 @@ index 8bb1219962a1368f50b53c61041f135c68e35f31..8e72441d26883cbef7a352b00c0fe2b1 item.removeAttribute("multiselected-move-together"); } } -@@ -2598,7 +2602,6 @@ +@@ -2812,7 +2828,6 @@ tab.style.top = ""; tab.style.maxWidth = ""; tab.style.pointerEvents = ""; @@ -277,7 +301,7 @@ index 8bb1219962a1368f50b53c61041f135c68e35f31..8e72441d26883cbef7a352b00c0fe2b1 tab.removeAttribute("small-stack"); tab.removeAttribute("big-stack"); } -@@ -2607,11 +2610,9 @@ +@@ -2821,11 +2836,9 @@ )) { label.style.width = ""; label.style.maxWidth = ""; diff --git a/src/browser/components/tabbrowser/content/tabbrowser-js.patch b/src/browser/components/tabbrowser/content/tabbrowser-js.patch index 54ab76446..e5813e9ba 100644 --- a/src/browser/components/tabbrowser/content/tabbrowser-js.patch +++ b/src/browser/components/tabbrowser/content/tabbrowser-js.patch @@ -1,8 +1,8 @@ diff --git a/browser/components/tabbrowser/content/tabbrowser.js b/browser/components/tabbrowser/content/tabbrowser.js -index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da5504f9d8 100644 +index 43180487a2fac15b7a65fac94f233eeed9094990..ae1288974cae29da929f27b78ff8db542566eba2 100644 --- a/browser/components/tabbrowser/content/tabbrowser.js +++ b/browser/components/tabbrowser/content/tabbrowser.js -@@ -513,6 +513,7 @@ +@@ -514,6 +514,7 @@ * @type {MozBrowser[]} */ get splitViewBrowsers() { @@ -10,7 +10,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da const browsers = []; if (this.#activeSplitView) { for (const tab of this.#activeSplitView.tabs) { -@@ -611,15 +612,66 @@ +@@ -612,15 +613,66 @@ return this.tabContainer.visibleTabs; } @@ -79,7 +79,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da setSelectedTab(val, metricsContext = null) { if (this.selectedTab === val) { return; -@@ -632,6 +684,9 @@ +@@ -633,6 +685,9 @@ ) { return; } @@ -89,7 +89,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da // Update the tab this.tabbox.selectedTab = val; -@@ -708,6 +763,10 @@ +@@ -709,6 +764,10 @@ userContextId = parseInt(tabArgument.getAttribute("usercontextid"), 10); } @@ -100,7 +100,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da if (openWindowInfo) { userContextId = openWindowInfo.originAttributes.userContextId; } -@@ -809,6 +868,8 @@ +@@ -810,6 +869,8 @@ this.tabpanels.appendChild(panel); let tab = this.tabs[0]; @@ -109,7 +109,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da tab.linkedPanel = uniqueId; this._selectedTab = tab; this._selectedBrowser = browser; -@@ -1178,18 +1239,24 @@ +@@ -1179,18 +1240,24 @@ * The context for the operation for telemetry purposes, defaults to an unknown context. */ pinTab(aTab, { metricsContext = this.TabMetrics.UNKNOWN_CONTEXT } = {}) { @@ -135,7 +135,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da aTab.setAttribute("pinned", "true"); this._updateTabBarForPinnedTabs(); -@@ -1206,16 +1273,25 @@ +@@ -1207,16 +1274,25 @@ * The context for the operation for telemetry purposes, defaults to an unknown context. */ unpinTab(aTab, { metricsContext = this.TabMetrics.UNKNOWN_CONTEXT } = {}) { @@ -162,7 +162,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da }); aTab.style.marginInlineStart = ""; -@@ -1430,6 +1506,9 @@ +@@ -1432,6 +1508,9 @@ let LOCAL_PROTOCOLS = ["chrome:", "about:", "resource:", "data:"]; @@ -172,7 +172,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da if ( aIconURL && !LOCAL_PROTOCOLS.some(protocol => aIconURL.startsWith(protocol)) -@@ -1439,6 +1518,9 @@ +@@ -1441,6 +1520,9 @@ ); return; } @@ -182,7 +182,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da let browser = this.getBrowserForTab(aTab); browser.mIconURL = aIconURL; -@@ -1761,7 +1843,6 @@ +@@ -1763,7 +1845,6 @@ // Preview mode should not reset the owner if (!this.#previewMode && !oldTab.selected) { @@ -190,7 +190,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da } let lastRelatedTab = this.#lastRelatedTabMap.get(oldTab); -@@ -1852,6 +1933,7 @@ +@@ -1854,6 +1935,7 @@ if (!this.#previewMode) { newTab.recordTimeFromUnloadToReload(); newTab.updateLastAccessed(); @@ -198,7 +198,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da oldTab.updateLastAccessed(); // if this is the foreground window, update the last-seen timestamps. if (this.documentGlobal == BrowserWindowTracker.getTopWindow()) { -@@ -2066,6 +2148,9 @@ +@@ -2068,6 +2150,9 @@ } let activeEl = document.activeElement; @@ -208,7 +208,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da // If focus is on the old tab, move it to the new tab. if (activeEl == oldTab) { newTab.focus(); -@@ -2104,7 +2189,7 @@ +@@ -2106,7 +2191,7 @@ // Focus the location bar if it was previously focused for that tab. // In full screen mode, only bother making the location bar visible // if the tab is a blank one. @@ -217,7 +217,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da let selectURL = () => { if (this._asyncTabSwitching) { // Set _awaitingSetURI flag to suppress popup notification -@@ -2392,7 +2477,12 @@ +@@ -2394,7 +2479,12 @@ return this._setTabLabel(aTab, aLabel); } @@ -231,7 +231,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da if (!aLabel || (isURL && /^about:reader\?url=/.test(aLabel))) { return false; } -@@ -2518,7 +2608,7 @@ +@@ -2520,7 +2610,7 @@ newIndex = this.selectedTab._tPos + 1; } @@ -240,7 +240,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da if (this.isTabGroupLabel(targetTab)) { throw new Error( "Replacing a tab group label with a tab is not supported" -@@ -2793,6 +2883,7 @@ +@@ -2795,6 +2885,7 @@ uriIsAboutBlank, userContextId, skipLoad, @@ -248,7 +248,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da } = {}) { let b = document.createXULElement("browser"); // Use the JSM global to create the permanentKey, so that if the -@@ -2866,8 +2957,7 @@ +@@ -2868,8 +2959,7 @@ // we use a different attribute name for this? b.setAttribute("name", name); } @@ -258,7 +258,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da b.setAttribute("transparent", "true"); } -@@ -3022,7 +3112,7 @@ +@@ -3024,7 +3114,7 @@ let panel = this.getPanel(browser); let uniqueId = this._generateUniquePanelID(); @@ -267,7 +267,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da aTab.linkedPanel = uniqueId; // Inject the into the DOM if necessary. -@@ -3082,8 +3172,8 @@ +@@ -3084,8 +3174,8 @@ // If we transitioned from one browser to two browsers, we need to set // hasSiblings=false on both the existing browser and the new browser. if (this.tabs.length == 2) { @@ -278,7 +278,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da } else { aTab.linkedBrowser.browsingContext.hasSiblings = this.tabs.length > 1; } -@@ -3268,7 +3358,6 @@ +@@ -3270,7 +3360,6 @@ this.selectedTab = this.addTrustedTab(BROWSER_NEW_TAB_URL, { tabIndex: tab._tPos + 1, userContextId: tab.userContextId, @@ -286,7 +286,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da focusUrlBar: true, }); resolve(this.selectedBrowser); -@@ -3378,6 +3467,10 @@ +@@ -3380,6 +3469,10 @@ schemelessInput, hasValidUserGestureActivation = false, textDirectiveUserActivation = false, @@ -297,7 +297,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da } = {} ) { // all callers of addTab that pass a params object need to pass -@@ -3388,10 +3481,25 @@ +@@ -3390,10 +3483,25 @@ ); } @@ -323,7 +323,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da // If we're opening a foreground tab, set the owner by default. ownerTab ??= inBackground ? null : this.selectedTab; -@@ -3399,6 +3507,7 @@ +@@ -3401,6 +3509,7 @@ if (this.selectedTab.owner) { this.selectedTab.owner = null; } @@ -331,7 +331,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da // Find the tab that opened this one, if any. This is used for // determining positioning, and inherited attributes such as the -@@ -3451,6 +3560,22 @@ +@@ -3453,6 +3562,22 @@ noInitialLabel, skipBackgroundNotify, }); @@ -354,7 +354,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da if (insertTab) { // Insert the tab into the tab container in the correct position. this.#insertTabAtIndex(t, { -@@ -3459,6 +3584,7 @@ +@@ -3461,6 +3586,7 @@ ownerTab, openerTab, pinned, @@ -362,7 +362,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da bulkOrderedOpen, tabGroup: tabGroup ?? openerTab?.group, }); -@@ -3477,6 +3603,7 @@ +@@ -3479,6 +3605,7 @@ openWindowInfo, skipLoad, triggeringRemoteType, @@ -370,7 +370,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da })); if (focusUrlBar) { -@@ -3601,6 +3728,12 @@ +@@ -3603,6 +3730,12 @@ } } @@ -383,7 +383,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da // Additionally send pinned tab events if (pinned) { this.#notifyPinnedStatus(t); -@@ -3611,6 +3744,15 @@ +@@ -3613,6 +3746,15 @@ if (!inBackground) { this.selectedTab = t; } @@ -399,7 +399,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da return t; } -@@ -3834,6 +3976,7 @@ +@@ -3836,6 +3978,7 @@ insertBefore = null, isAdoptingGroup = false, metricsContext = this.TabMetrics.UNKNOWN_CONTEXT, @@ -407,7 +407,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da } = {} ) { if ( -@@ -3844,9 +3987,6 @@ +@@ -3846,9 +3989,6 @@ !this.isSplitViewWrapper(tabOrSplitView) ) ) { @@ -417,7 +417,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da } if (!color) { -@@ -3867,9 +4007,14 @@ +@@ -3869,9 +4009,14 @@ label, isAdoptingGroup ); @@ -434,7 +434,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da ); group.addTabs(tabsAndSplitViews, metricsContext); -@@ -3973,7 +4118,7 @@ +@@ -3975,7 +4120,7 @@ } this.#handleTabMove(tab, () => @@ -443,7 +443,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da ); } -@@ -4059,6 +4204,7 @@ +@@ -4061,6 +4206,7 @@ color: group.color, insertBefore: newTabs[0], isAdoptingGroup: true, @@ -451,7 +451,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da }); } -@@ -4269,6 +4415,7 @@ +@@ -4271,6 +4417,7 @@ openWindowInfo, skipLoad, triggeringRemoteType, @@ -459,7 +459,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da } ) { // If we don't have a preferred remote type (or it is `NOT_REMOTE`), and -@@ -4329,6 +4476,7 @@ +@@ -4331,6 +4478,7 @@ openWindowInfo, name, skipLoad, @@ -467,7 +467,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da }); } -@@ -4542,9 +4690,9 @@ +@@ -4544,9 +4692,9 @@ } // Add a new tab if needed. @@ -479,7 +479,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da let url = "about:blank"; if (tabData.entries?.length) { -@@ -4577,8 +4725,10 @@ +@@ -4579,8 +4727,10 @@ insertTab: false, skipLoad: true, preferredRemoteType, @@ -491,7 +491,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da if (select) { tabToSelect = tab; } -@@ -4600,7 +4750,8 @@ +@@ -4602,7 +4752,8 @@ this.pinTab(tab); // Then ensure all the tab open/pinning information is sent. this._fireTabOpen(tab, {}); @@ -501,7 +501,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da let { groupId } = tabData; const tabGroup = tabGroupWorkingData.get(groupId); // if a tab refers to a tab group we don't know, skip any group -@@ -4620,7 +4771,10 @@ +@@ -4622,7 +4773,10 @@ tabGroup.stateData.id, tabGroup.stateData.color, tabGroup.stateData.collapsed, @@ -513,7 +513,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da ); tabsFragment.appendChild(tabGroup.node); } -@@ -4675,9 +4829,21 @@ +@@ -4677,9 +4831,21 @@ // to remove the old selected tab. if (tabToSelect) { let leftoverTab = this.selectedTab; @@ -535,7 +535,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da if (tabs.length > 1 || !tabs[0].selected) { this._updateTabsAfterInsert(); -@@ -4882,11 +5048,17 @@ +@@ -4884,11 +5050,17 @@ if (ownerTab) { tab.owner = ownerTab; } @@ -554,7 +554,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da if ( !bulkOrderedOpen && ((openerTab && -@@ -4898,7 +5070,7 @@ +@@ -4900,7 +5072,7 @@ let lastRelatedTab = openerTab && this.#lastRelatedTabMap.get(openerTab); let previousTab = lastRelatedTab || openerTab || this.selectedTab; @@ -563,7 +563,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da tabGroup = previousTab.group; } if ( -@@ -4914,7 +5086,7 @@ +@@ -4916,7 +5088,7 @@ previousTab.splitview ) + 1; } else if (previousTab.visible) { @@ -572,7 +572,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da } else if (previousTab == FirefoxViewHandler.tab) { elementIndex = 0; } -@@ -4942,14 +5114,14 @@ +@@ -4944,14 +5116,14 @@ } // Ensure index is within bounds. if (tab.pinned) { @@ -591,7 +591,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da if (pinned && !itemAfter?.pinned) { itemAfter = null; -@@ -4966,7 +5138,7 @@ +@@ -4968,7 +5140,7 @@ this.tabContainer._invalidateCachedTabs(); @@ -600,7 +600,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da if ( (this.isTab(itemAfter) && itemAfter.group == tabGroup) || this.isSplitViewWrapper(itemAfter) -@@ -4997,7 +5169,11 @@ +@@ -4999,7 +5171,11 @@ const tabContainer = pinned ? this.tabContainer.pinnedTabsContainer : this.tabContainer; @@ -612,7 +612,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da } if (tab.group?.collapsed) { -@@ -5012,6 +5188,7 @@ +@@ -5014,6 +5190,7 @@ if (pinned) { this._updateTabBarForPinnedTabs(); } @@ -620,7 +620,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da TabBarVisibility.update(); } -@@ -5568,6 +5745,7 @@ +@@ -5577,6 +5754,7 @@ metricsContext, } = {} ) { @@ -628,7 +628,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da // When 'closeWindowWithLastTab' pref is enabled, closing all tabs // can be considered equivalent to closing the window. if ( -@@ -5678,6 +5856,7 @@ +@@ -5687,6 +5865,7 @@ closedTabCount -= 1; } } @@ -636,7 +636,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da if (closedTabCount > 0) { this.recordTabMetrics( -@@ -5730,6 +5909,14 @@ +@@ -5761,6 +5940,14 @@ return; } @@ -651,7 +651,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da 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 -@@ -5737,6 +5924,9 @@ +@@ -5768,6 +5955,9 @@ // state). let tabWidth = window.windowUtils.getBoundsWithoutFlushing(aTab).width; let isLastTab = this.#isLastTabInWindow(aTab); @@ -661,7 +661,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da if ( !this._beginRemoveTab(aTab, { closeWindowFastpath: true, -@@ -5747,13 +5937,14 @@ +@@ -5778,13 +5968,14 @@ metricsContext, }) ) { @@ -677,7 +677,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da let lockTabSizing = !this.tabContainer.verticalMode && !aTab.pinned && -@@ -5784,7 +5975,13 @@ +@@ -5815,7 +6006,13 @@ // We're not animating, so we can cancel the animation stopwatch. Glean.browserTabclose.timeAnim.cancel(aTab._closeTimeAnimTimerId); aTab._closeTimeAnimTimerId = null; @@ -692,7 +692,18 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da return; } -@@ -5827,7 +6024,7 @@ +@@ -5855,7 +6052,9 @@ + get #shouldCloseWindowWithLastTab() { + return ( + !window.toolbar.visible || +- Services.prefs.getBoolPref("browser.tabs.closeWindowWithLastTab") ++ (Services.prefs.getBoolPref("browser.tabs.closeWindowWithLastTab") && ++ !gZenWorkspaces._isClosingWindow && ++ !gZenWorkspaces._removedByStartupPage) + ); + } + +@@ -5871,7 +6070,7 @@ */ #isLastTabInWindow(tab) { for (const otherTab of this.tabs) { @@ -701,16 +712,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da return false; } } -@@ -5917,7 +6114,7 @@ - closeWindowWithLastTab != null - ? closeWindowWithLastTab - : !window.toolbar.visible || -- Services.prefs.getBoolPref("browser.tabs.closeWindowWithLastTab"); -+ Services.prefs.getBoolPref("browser.tabs.closeWindowWithLastTab") && !gZenWorkspaces._isClosingWindow && !gZenWorkspaces._removedByStartupPage; - - if (closeWindow) { - // We've already called beforeunload on all the relevant tabs if we get here, -@@ -5941,6 +6138,7 @@ +@@ -5982,6 +6181,7 @@ newTab = true; } @@ -718,8 +720,8 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da aTab._endRemoveArgs = [closeWindow, newTab]; // swapBrowsersAndCloseOther will take care of closing the window without animation. -@@ -5982,13 +6180,7 @@ - aTab._mouseleave(); +@@ -6029,13 +6229,7 @@ + } if (newTab) { - this.addTrustedTab(BROWSER_NEW_TAB_URL, { @@ -733,7 +735,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da } else { TabBarVisibility.update(); } -@@ -6128,6 +6320,7 @@ +@@ -6192,6 +6386,7 @@ this.tabs[i]._tPos = i; } @@ -741,7 +743,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da if (!this.#windowIsClosing) { // update tab close buttons state this.tabContainer._updateCloseButtons(); -@@ -6313,6 +6506,7 @@ +@@ -6377,6 +6572,7 @@ memory_after: await getTotalMemoryUsage(), time_to_unload_in_ms: timeElapsed, }); @@ -749,7 +751,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da } /** -@@ -6358,6 +6552,7 @@ +@@ -6422,6 +6618,7 @@ } let excludeTabs = new Set(aExcludeTabs); @@ -757,7 +759,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da // If this tab has a successor, it should be selectable, since // hiding or closing a tab removes that tab as a successor. -@@ -6370,13 +6565,13 @@ +@@ -6434,13 +6631,13 @@ !excludeTabs.has(aTab.owner) && Services.prefs.getBoolPref("browser.tabs.selectOwnerOnClose") ) { @@ -773,7 +775,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da ); if (Services.prefs.getBoolPref("browser.tabs.selectMRUOnClose", false)) { -@@ -6392,6 +6587,13 @@ +@@ -6456,6 +6653,13 @@ } } @@ -787,7 +789,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da let tab = this.tabContainer.findNextTab(aTab, { direction: 1, filter: _tab => remainingTabs.includes(_tab), -@@ -6405,7 +6607,7 @@ +@@ -6469,7 +6673,7 @@ } if (tab) { @@ -796,7 +798,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da } // If no qualifying visible tab was found, see if there is a tab in -@@ -6426,7 +6628,7 @@ +@@ -6490,7 +6694,7 @@ }); } @@ -805,7 +807,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da } _blurTab(aTab) { -@@ -6437,7 +6639,7 @@ +@@ -6501,7 +6705,7 @@ * @returns {boolean} * False if swapping isn't permitted, true otherwise. */ @@ -814,7 +816,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da // Do not allow transfering a private tab to a non-private window // and vice versa. if ( -@@ -6491,6 +6693,7 @@ +@@ -6555,6 +6759,7 @@ // fire the beforeunload event in the process. Close the other // window if this was its last tab. if ( @@ -822,7 +824,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da !remoteBrowser._beginRemoveTab(aOtherTab, { adoptedByTab: aOurTab, closeWindowWithLastTab: true, -@@ -6502,7 +6705,7 @@ +@@ -6566,7 +6771,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. @@ -831,7 +833,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da if (closeWindow) { let win = aOtherTab.documentGlobal; win.windowUtils.suppressAnimation(true); -@@ -6636,11 +6839,13 @@ +@@ -6700,11 +6905,13 @@ } // Finish tearing down the tab that's going away. @@ -845,7 +847,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da this.setTabTitle(aOurTab); -@@ -6860,10 +7065,10 @@ +@@ -6928,10 +7135,10 @@ SessionStore.deleteCustomTabValue(aTab, "hiddenBy"); } @@ -858,7 +860,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da aTab.selected || aTab.closing || // Tabs that are sharing the screen, microphone or camera cannot be hidden. -@@ -6923,7 +7128,8 @@ +@@ -6991,7 +7198,8 @@ * @param {object} [aOptions={}] * Key-value pairs that will be serialized into the features string. */ @@ -868,7 +870,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da if (this.tabs.length == 1) { return null; } -@@ -6940,7 +7146,7 @@ +@@ -7008,7 +7216,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); @@ -877,7 +879,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da private: PrivateBrowsingUtils.isWindowPrivate(window), features: Object.entries(aOptions) .map(([key, value]) => `${key}=${value}`) -@@ -6948,6 +7154,8 @@ +@@ -7016,6 +7224,8 @@ openerWindow: window, args, }); @@ -886,7 +888,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da } /** -@@ -7074,7 +7282,7 @@ +@@ -7142,7 +7352,7 @@ * `true` if element is a `` */ isTabGroup(element) { @@ -895,7 +897,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da } /** -@@ -7146,8 +7354,8 @@ +@@ -7214,8 +7424,8 @@ } // Don't allow mixing pinned and unpinned tabs. @@ -906,7 +908,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da } else { tabIndex = Math.max(tabIndex, this.pinnedTabCount); } -@@ -7193,8 +7401,8 @@ +@@ -7261,8 +7471,8 @@ this.#handleTabMove( element, () => { @@ -917,7 +919,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da neighbor = neighbor.group; } if (neighbor?.splitview) { -@@ -7205,6 +7413,12 @@ +@@ -7273,6 +7483,12 @@ return; } } @@ -930,7 +932,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da if (movingForwards && neighbor) { neighbor.after(element); -@@ -7278,23 +7492,31 @@ +@@ -7346,23 +7562,31 @@ ) { if (this.isTabGroupLabel(targetElement)) { targetElement = targetElement.group; @@ -968,7 +970,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da } 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 -@@ -7307,12 +7529,35 @@ +@@ -7375,12 +7599,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. @@ -1005,7 +1007,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da // 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. -@@ -7321,6 +7566,7 @@ +@@ -7389,6 +7636,7 @@ } let getContainer = () => @@ -1013,7 +1015,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da element.pinned ? this.tabContainer.pinnedTabsContainer : this.tabContainer; -@@ -7329,11 +7575,15 @@ +@@ -7397,11 +7645,15 @@ element, () => { if (moveBefore) { @@ -1030,7 +1032,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da } }, { metricsContext } -@@ -7411,11 +7661,15 @@ +@@ -7479,11 +7731,15 @@ * The context for the operation for telemetry purposes. */ moveTabToExistingGroup(aTab, aGroup, { metricsContext } = {}) { @@ -1049,7 +1051,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da } if (aTab.group && aTab.group.id === aGroup.id) { return; -@@ -7489,6 +7743,7 @@ +@@ -7557,6 +7813,7 @@ let state = { tabIndex: tab._tPos, @@ -1057,7 +1059,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da }; if (tab.visible) { state.elementIndex = tab.elementIndex; -@@ -7527,7 +7782,7 @@ +@@ -7595,7 +7852,7 @@ let changedSplitView = previousTabState.splitViewId != currentTabState.splitViewId; @@ -1066,7 +1068,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da tab.dispatchEvent( new CustomEvent("TabMove", { bubbles: true, -@@ -7579,6 +7834,10 @@ +@@ -7647,6 +7904,10 @@ moveActionCallback(); @@ -1077,7 +1079,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da // Clear tabs cache after moving nodes because the order of tabs may have // changed. this.tabContainer._invalidateCachedTabs(); -@@ -7626,7 +7885,22 @@ +@@ -7694,7 +7955,22 @@ * @returns {object} * The new tab in the current window, null if the tab couldn't be adopted. */ @@ -1101,7 +1103,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da // 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 -@@ -7669,6 +7943,8 @@ +@@ -7737,6 +8013,8 @@ } params.skipLoad = true; let newTab = this.addWebTab("about:blank", params); @@ -1110,7 +1112,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da aTab.container.tabDragAndDrop.finishAnimateTabMove(); -@@ -8448,7 +8724,7 @@ +@@ -8516,7 +8794,7 @@ // preventDefault(). It will still raise the window if appropriate. return; } @@ -1119,7 +1121,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da window.focus(); aEvent.preventDefault(); } -@@ -8465,7 +8741,6 @@ +@@ -8533,7 +8811,6 @@ on_TabGroupCollapse(aEvent) { aEvent.target.tabs.forEach(tab => { @@ -1127,7 +1129,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da }); } -@@ -8819,7 +9094,9 @@ +@@ -8887,7 +9164,9 @@ let filter = this.#tabFilters.get(tab); if (filter) { @@ -1137,7 +1139,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da let listener = this.#tabListeners.get(tab); if (listener) { -@@ -9604,6 +9881,7 @@ +@@ -9676,6 +9955,7 @@ aWebProgress.isTopLevel ) { this._tab.setAttribute("busy", "true"); @@ -1145,7 +1147,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da gBrowser._tabAttrModified(this._tab, ["busy"]); this._tab._notselectedsinceload = !this._tab.selected; } -@@ -9684,6 +9962,7 @@ +@@ -9756,6 +10036,7 @@ // known defaults. Note we use the original URL since about:newtab // redirects to a prerendered page. const shouldRemoveFavicon = @@ -1153,7 +1155,7 @@ index 39b5babee5942427a82a7a64ad69e1b16efebcc1..32beac13dd4215a16de3a783db6c32da !this._browser.mIconURL && !ignoreBlank && !(originalLocation.spec in FAVICON_DEFAULTS); -@@ -9858,13 +10137,6 @@ +@@ -9930,13 +10211,6 @@ this._browser.originalURI = aRequest.originalURI; } diff --git a/src/browser/components/urlbar/UrlbarPrefs-sys-mjs.patch b/src/browser/components/urlbar/UrlbarPrefs-sys-mjs.patch index 9644cca42..c99cdbb52 100644 --- a/src/browser/components/urlbar/UrlbarPrefs-sys-mjs.patch +++ b/src/browser/components/urlbar/UrlbarPrefs-sys-mjs.patch @@ -1,8 +1,8 @@ diff --git a/browser/components/urlbar/UrlbarPrefs.sys.mjs b/browser/components/urlbar/UrlbarPrefs.sys.mjs -index 89f0d44d462de3d7d0f581d6371606f475443822..0d45ce5f6455e9c7276c54f504906bade5b3d286 100644 +index e48f0295d803ba9eac5ab9d01ebb3b3dacbc9d4b..cb78b74230f63a47479404767b5ab5883278dfba 100644 --- a/browser/components/urlbar/UrlbarPrefs.sys.mjs +++ b/browser/components/urlbar/UrlbarPrefs.sys.mjs -@@ -163,6 +163,9 @@ const PREF_URLBAR_DEFAULTS = /** @type {PreferenceDefinition[]} */ ([ +@@ -161,6 +161,9 @@ const PREF_URLBAR_DEFAULTS = /** @type {PreferenceDefinition[]} */ ([ // Feature gate pref for flight status suggestions in the urlbar. ["flightStatus.featureGate", false], @@ -12,7 +12,7 @@ index 89f0d44d462de3d7d0f581d6371606f475443822..0d45ce5f6455e9c7276c54f504906bad // The minimum prefix length of a flight status keyword the user must type to // trigger the suggestion. 0 means the min length should be taken from Nimbus // or remote settings. -@@ -482,6 +485,7 @@ const PREF_URLBAR_DEFAULTS = /** @type {PreferenceDefinition[]} */ ([ +@@ -486,6 +489,7 @@ const PREF_URLBAR_DEFAULTS = /** @type {PreferenceDefinition[]} */ ([ ["shortcuts.tabs", true], ["shortcuts.history", true], ["shortcuts.actions", true], @@ -20,12 +20,12 @@ index 89f0d44d462de3d7d0f581d6371606f475443822..0d45ce5f6455e9c7276c54f504906bad // Boolean to determine if the providers defined in `exposureResults` // should be displayed in search results. This can be set by a -@@ -840,6 +844,8 @@ function makeDefaultResultGroups({ +@@ -854,6 +858,8 @@ function makeDefaultResultGroups({ */ let rootGroup = { children: [ -+ { children: [{ group: lazy.UrlbarUtils.RESULT_GROUP.ZEN_ACTION }] }, -+ { children: [{ group: lazy.UrlbarUtils.RESULT_GROUP.ZEN_WORKSPACE }] }, ++ { children: [{ group: lazy.UrlbarShared.RESULT_GROUP.ZEN_ACTION }] }, ++ { children: [{ group: lazy.UrlbarShared.RESULT_GROUP.ZEN_WORKSPACE }] }, // heuristic { maxResultCount: 1, diff --git a/src/browser/components/urlbar/UrlbarProviderHeuristicFallback-sys-mjs.patch b/src/browser/components/urlbar/UrlbarProviderHeuristicFallback-sys-mjs.patch index 82e3f9236..e79eef7e8 100644 --- a/src/browser/components/urlbar/UrlbarProviderHeuristicFallback-sys-mjs.patch +++ b/src/browser/components/urlbar/UrlbarProviderHeuristicFallback-sys-mjs.patch @@ -1,8 +1,8 @@ diff --git a/browser/components/urlbar/UrlbarProviderHeuristicFallback.sys.mjs b/browser/components/urlbar/UrlbarProviderHeuristicFallback.sys.mjs -index f611d8e44af518aa1adc7505c5021235f55b49de..0eca20cb869626dfa6ace02acc00cad84462d54f 100644 +index cfc232f038cd491db32e0c0ca35622360dd9dec5..4806a725ddefa9d0709020d537eae5464249a294 100644 --- a/browser/components/urlbar/UrlbarProviderHeuristicFallback.sys.mjs +++ b/browser/components/urlbar/UrlbarProviderHeuristicFallback.sys.mjs -@@ -78,22 +78,26 @@ export class UrlbarProviderHeuristicFallback extends UrlbarProvider { +@@ -77,22 +77,26 @@ export class UrlbarProviderHeuristicFallback extends UrlbarProvider { // Since we can't tell if this is a real URL and whether the user wants // to visit or search for it, we provide an alternative searchengine // match if the string looks like an alphanumeric origin or an e-mail. @@ -17,7 +17,7 @@ index f611d8e44af518aa1adc7505c5021235f55b49de..0eca20cb869626dfa6ace02acc00cad8 - lazy.UrlUtils.REGEXP_COMMON_EMAIL.test(str)) - ) { + let trimmedSearchString = queryContext.trimmedSearchString; -+ let [searchCandidate] = UrlbarUtils.stripPrefixAndTrim( ++ let [searchCandidate] = lazy.UrlbarShared.stripPrefixAndTrim( + trimmedSearchString, + { + trimSlash: true, diff --git a/src/browser/components/urlbar/UrlbarUtils-sys-mjs.patch b/src/browser/components/urlbar/UrlbarUtils-sys-mjs.patch index 630553ff9..b3833b83b 100644 --- a/src/browser/components/urlbar/UrlbarUtils-sys-mjs.patch +++ b/src/browser/components/urlbar/UrlbarUtils-sys-mjs.patch @@ -1,41 +1,16 @@ diff --git a/browser/components/urlbar/UrlbarUtils.sys.mjs b/browser/components/urlbar/UrlbarUtils.sys.mjs -index 78c76bde00b6b581184cee1cd0e1756e5f2da6a4..94c713470b1240729ef2350d090b0449f3801af4 100644 +index 178cd5e84fda74a28b2de59b671b3399cc28fac4..b03c6e06de4ee9a95dcb4d33d7ac9f0b053be4e1 100644 --- a/browser/components/urlbar/UrlbarUtils.sys.mjs +++ b/browser/components/urlbar/UrlbarUtils.sys.mjs -@@ -126,6 +126,8 @@ export var UrlbarUtils = { - SEMANTIC_HISTORY: "semanticHistory", - SUGGESTED_INDEX: "suggestedIndex", - TAIL_SUGGESTION: "tailSuggestion", -+ ZEN_ACTION: "zenAction", -+ ZEN_WORKSPACE: "zenWorkspace", - }), - - // Defines provider types. -@@ -274,6 +276,14 @@ export var UrlbarUtils = { - telemetryLabel: "actions", - uiLabel: "urlbar-searchmode-actions3", - }, -+ { -+ source: UrlbarShared.RESULT_SOURCE.WORKSPACES, -+ restrict: UrlbarShared.RESTRICT_TOKENS.WORKSPACE, -+ icon: "chrome://browser/skin/zen-icons/selectable/layers.svg", -+ pref: "shortcuts.workspaces", -+ telemetryLabel: "workspaces", -+ uiLabel: "urlbar-search-mode-workspaces", -+ }, - ]); - }, - -@@ -566,6 +576,12 @@ export var UrlbarUtils = { - return this.RESULT_GROUP.HEURISTIC_FALLBACK; +@@ -214,6 +214,11 @@ export var UrlbarUtils = { + return UrlbarShared.RESULT_GROUP.HEURISTIC_FALLBACK; case "UrlbarProviderHistoryUrlHeuristic": - return this.RESULT_GROUP.HEURISTIC_HISTORY_URL; + return UrlbarShared.RESULT_GROUP.HEURISTIC_HISTORY_URL; + case "ZenUrlbarProviderGlobalActions": + if (result.source == UrlbarShared.RESULT_SOURCE.WORKSPACES) { -+ return this.RESULT_GROUP.ZEN_WORKSPACE; -+ } else { -+ return this.RESULT_GROUP.ZEN_ACTION; ++ return UrlbarShared.RESULT_GROUP.ZEN_WORKSPACE; + } ++ return UrlbarShared.RESULT_GROUP.ZEN_ACTION; case "UrlbarProviderOmnibox": - return this.RESULT_GROUP.HEURISTIC_OMNIBOX; + return UrlbarShared.RESULT_GROUP.HEURISTIC_OMNIBOX; case "UrlbarProviderRestrictKeywordsAutofill": diff --git a/src/browser/components/urlbar/content/UrlbarChildController-mjs.patch b/src/browser/components/urlbar/content/UrlbarChildController-mjs.patch index a7751f7ca..cd89c299e 100644 --- a/src/browser/components/urlbar/content/UrlbarChildController-mjs.patch +++ b/src/browser/components/urlbar/content/UrlbarChildController-mjs.patch @@ -1,8 +1,8 @@ diff --git a/browser/components/urlbar/content/UrlbarChildController.mjs b/browser/components/urlbar/content/UrlbarChildController.mjs -index a8e9d3093d046612650a17970330a8279f55833f..063a3e8adf009d6c0ddc60430bc0e7485e812b8a 100644 +index a17a4de079b69d2818388b33a2c1746cc6148c73..4fee63a22919972af08716db2d2de1c7549ff100 100644 --- a/browser/components/urlbar/content/UrlbarChildController.mjs +++ b/browser/components/urlbar/content/UrlbarChildController.mjs -@@ -258,7 +258,6 @@ export class UrlbarChildController { +@@ -438,7 +438,6 @@ export class UrlbarChildController { const isMac = AppConstants.platform == "macosx"; // Handle readline/emacs-style navigation bindings on Mac. if ( @@ -10,7 +10,7 @@ index a8e9d3093d046612650a17970330a8279f55833f..063a3e8adf009d6c0ddc60430bc0e748 this.view.isOpen && event.ctrlKey && (event.key == "n" || event.key == "p") -@@ -450,6 +449,8 @@ export class UrlbarChildController { +@@ -630,6 +629,8 @@ export class UrlbarChildController { }); } event.preventDefault(); @@ -19,3 +19,25 @@ index a8e9d3093d046612650a17970330a8279f55833f..063a3e8adf009d6c0ddc60430bc0e748 } break; } +@@ -934,6 +935,10 @@ export class UrlbarChildController { + } + reuseEmpty = true; + } ++ if (this.#input.hasAttribute?.("zen-newtab")) { ++ where = "tab"; ++ reuseEmpty = true; ++ } + // The browser window exists only in chrome; a content-process input has no + // tab to reuse, so `gBrowser` is absent and the reuse is skipped. + if ( +@@ -943,6 +948,10 @@ export class UrlbarChildController { + ) { + where = "current"; + } ++ if (this.window.gBrowser?.selectedTab.hasAttribute("zen-empty-tab")) { ++ // Always open in a new tab if the current tab is our empty tab. ++ return "tab"; ++ } + return where; + } + diff --git a/src/browser/components/urlbar/content/UrlbarInput-mjs.patch b/src/browser/components/urlbar/content/UrlbarInput-mjs.patch index b6b5daebe..535cd52bf 100644 --- a/src/browser/components/urlbar/content/UrlbarInput-mjs.patch +++ b/src/browser/components/urlbar/content/UrlbarInput-mjs.patch @@ -1,8 +1,8 @@ diff --git a/browser/components/urlbar/content/UrlbarInput.mjs b/browser/components/urlbar/content/UrlbarInput.mjs -index e7e9495e56076cd112beafb8297dece4ae4cea58..95e4333866a5f0d41fbdbe23910c396406a247e9 100644 +index 319fb6870ec18d87b9f1263bd960edaa39b0447c..554cc90fb2307450ddc68033968cefd1a95e28b8 100644 --- a/browser/components/urlbar/content/UrlbarInput.mjs +++ b/browser/components/urlbar/content/UrlbarInput.mjs -@@ -101,6 +101,13 @@ const lazy = XPCOMUtils.declareLazy({ +@@ -88,6 +88,13 @@ const lazy = XPCOMUtils.declareLazy({ logger: () => UrlbarShared.getLogger({ prefix: "Input" }), }); @@ -16,7 +16,7 @@ index e7e9495e56076cd112beafb8297dece4ae4cea58..95e4333866a5f0d41fbdbe23910c3964 const UNLIMITED_MAX_RESULTS = 99; let getBoundsWithoutFlushing = element => -@@ -769,7 +776,16 @@ ${ +@@ -771,7 +778,16 @@ ${ // See _on_select(). HTMLInputElement.select() dispatches a "select" // event but does not set the primary selection. this._suppressPrimaryAdjustment = true; @@ -33,7 +33,7 @@ index e7e9495e56076cd112beafb8297dece4ae4cea58..95e4333866a5f0d41fbdbe23910c3964 this._suppressPrimaryAdjustment = false; } -@@ -843,6 +859,10 @@ ${ +@@ -846,6 +862,10 @@ ${ hideSearchTerms = false, isSameDocument = false, } = {}) { @@ -44,7 +44,7 @@ index e7e9495e56076cd112beafb8297dece4ae4cea58..95e4333866a5f0d41fbdbe23910c3964 if (!this.#isAddressbar) { throw new Error( "Cannot set URI for UrlbarInput that is not an address bar" -@@ -1137,7 +1157,16 @@ ${ +@@ -1143,7 +1163,16 @@ ${ this.searchModeSwitcher?.updateSearchIcon(); } @@ -61,7 +61,7 @@ index e7e9495e56076cd112beafb8297dece4ae4cea58..95e4333866a5f0d41fbdbe23910c3964 } /** -@@ -1626,7 +1655,11 @@ ${ +@@ -1726,7 +1755,11 @@ ${ openParams.avoidBrowserFocus = keepViewOpen; if (!this.#providesSearchMode(result) && !keepViewOpen) { @@ -74,7 +74,7 @@ index e7e9495e56076cd112beafb8297dece4ae4cea58..95e4333866a5f0d41fbdbe23910c3964 } if (isCanonized) { -@@ -2974,6 +3007,42 @@ ${ +@@ -3016,6 +3049,42 @@ ${ await this.#updateLayoutBreakoutDimensions(); } @@ -117,7 +117,7 @@ index e7e9495e56076cd112beafb8297dece4ae4cea58..95e4333866a5f0d41fbdbe23910c3964 startLayoutExtend() { if (!this.#allowBreakout || this.hasAttribute("breakout-extend")) { // Do not expand if the Urlbar does not support being expanded or it is -@@ -2988,6 +3057,13 @@ ${ +@@ -3030,6 +3099,13 @@ ${ this.toggleAttribute("breakout-extend", true); this.#updateTextboxPosition(); @@ -131,7 +131,7 @@ index e7e9495e56076cd112beafb8297dece4ae4cea58..95e4333866a5f0d41fbdbe23910c3964 // Enable the animation only after the first extend call to ensure it // doesn't run when opening a new window. if (!this.hasAttribute("breakout-extend-animate")) { -@@ -3011,6 +3087,29 @@ ${ +@@ -3053,6 +3129,29 @@ ${ return; } @@ -161,7 +161,7 @@ index e7e9495e56076cd112beafb8297dece4ae4cea58..95e4333866a5f0d41fbdbe23910c3964 this.toggleAttribute("breakout-extend", false); this.#updateTextboxPosition(); } -@@ -3049,7 +3148,7 @@ ${ +@@ -3091,7 +3190,7 @@ ${ forceUnifiedSearchButtonAvailable = false ) { let prevState = this.getAttribute("pageproxystate"); @@ -170,7 +170,7 @@ index e7e9495e56076cd112beafb8297dece4ae4cea58..95e4333866a5f0d41fbdbe23910c3964 this.setAttribute("pageproxystate", state); this._inputContainer.setAttribute("pageproxystate", state); this._identityBox?.setAttribute("pageproxystate", state); -@@ -3332,10 +3431,12 @@ ${ +@@ -3355,10 +3454,12 @@ ${ } this.style.top = px( @@ -183,7 +183,7 @@ index e7e9495e56076cd112beafb8297dece4ae4cea58..95e4333866a5f0d41fbdbe23910c3964 ); } -@@ -3394,9 +3495,10 @@ ${ +@@ -3417,9 +3518,10 @@ ${ return; } @@ -193,9 +193,9 @@ index e7e9495e56076cd112beafb8297dece4ae4cea58..95e4333866a5f0d41fbdbe23910c3964 - px(getBoundsWithoutFlushing(this.parentNode).height) + px(getBoundsWithoutFlushing(this.parentNode).height + 8) ); - this.style.setProperty( - "--urlbar-height", -@@ -3902,6 +4004,7 @@ ${ + + if (this.#breakoutBlockerCount) { +@@ -3929,6 +4031,7 @@ ${ } _toggleActionOverride(event) { @@ -203,47 +203,33 @@ index e7e9495e56076cd112beafb8297dece4ae4cea58..95e4333866a5f0d41fbdbe23910c3964 if ( event.keyCode == KeyEvent.DOM_VK_SHIFT || event.keyCode == KeyEvent.DOM_VK_ALT || -@@ -4014,8 +4117,8 @@ ${ +@@ -4027,9 +4130,10 @@ ${ if (!this.#isAddressbar) { return val; } -- let trimmedValue = lazy.UrlbarPrefs.get("trimURLs") +- let trimmedValue = UrlbarPrefs.get("trimURLs") - ? lazy.BrowserUIUtils.trimURL(val) -+ let trimmedValue = lazy.UrlbarPrefs.get("trimURLs") && this._zenTrimURL -+ ? this._zenTrimURL(val) - : val; +- : val; ++ let trimmedValue = ++ UrlbarPrefs.get("trimURLs") && this._zenTrimURL ++ ? this._zenTrimURL(val) ++ : val; // Only trim value if the directionality doesn't change to RTL and we're not // showing a strikeout https protocol. -@@ -4317,6 +4420,7 @@ ${ - browser = this.window.gBrowser.selectedBrowser, - keepViewOpen = false - ) { -+ openUILinkWhere = this.window.gZenUIManager.getOpenUILinkWhere(url, browser, openUILinkWhere); - if (this.#isAddressbar) { - this.#prepareAddressbarLoad( - url, -@@ -4430,6 +4534,10 @@ ${ - } - reuseEmpty = true; - } -+ if (this.hasAttribute("zen-newtab")) { -+ where = "tab"; -+ reuseEmpty = true; -+ } - if ( - where == "tab" && - reuseEmpty && -@@ -4437,6 +4545,9 @@ ${ - ) { - where = "current"; - } -+ if (this.window.gBrowser.selectedTab.hasAttribute("zen-empty-tab")) { -+ return "tab"; // Always open in a new tab if the current tab is "our empty tab". -+ } - return where; - } - -@@ -4691,6 +4802,7 @@ ${ + return this.controller.isTextDirectionRTL(trimmedValue) || +@@ -4235,6 +4339,11 @@ ${ + keepViewOpen = false, + browserId = null, + }) { ++ where = this.window.gZenUIManager.getOpenUILinkWhere( ++ url, ++ this.window.gBrowser.selectedBrowser, ++ where ++ ); + let userTypedValue; + if (this.#isAddressbar && where == "current") { + // Make sure URL is formatted properly (don't show punycode). +@@ -4576,6 +4685,7 @@ ${ this.setResultForCurrentValue(null); this.handleCommand(); this.controller.clearLastQueryContextCache(); @@ -251,7 +237,7 @@ index e7e9495e56076cd112beafb8297dece4ae4cea58..95e4333866a5f0d41fbdbe23910c3964 this._suppressStartQuery = false; }); -@@ -4698,7 +4810,6 @@ ${ +@@ -4583,7 +4693,6 @@ ${ contextMenu.addEventListener("popupshowing", () => { // Close the results pane when the input field contextual menu is open, // because paste and go doesn't want a result selection. @@ -259,7 +245,7 @@ index e7e9495e56076cd112beafb8297dece4ae4cea58..95e4333866a5f0d41fbdbe23910c3964 let controller = this.document.commandDispatcher.getControllerForCommand("cmd_paste"); -@@ -4979,7 +5090,11 @@ ${ +@@ -4850,7 +4959,11 @@ ${ if (!engineName && !source && !this.hasAttribute("searchmode")) { return; } @@ -272,7 +258,7 @@ index e7e9495e56076cd112beafb8297dece4ae4cea58..95e4333866a5f0d41fbdbe23910c3964 if (this._searchModeIndicatorTitle) { this._searchModeIndicatorTitle.textContent = ""; this._searchModeIndicatorTitle.removeAttribute("data-l10n-id"); -@@ -5297,6 +5412,7 @@ ${ +@@ -5149,6 +5262,7 @@ ${ this.document.l10n.setAttributes( this.inputField, @@ -280,20 +266,20 @@ index e7e9495e56076cd112beafb8297dece4ae4cea58..95e4333866a5f0d41fbdbe23910c3964 l10nId, l10nId == "urlbar-placeholder-with-name" ? { name: engineName } -@@ -5347,6 +5463,12 @@ ${ +@@ -5199,6 +5313,12 @@ ${ } lazy.logger.debug("Blur Event"); + if ( + this.document.commandDispatcher.focusedElement == this.inputField && -+ !lazy.UrlbarPrefs.get("closeOnWindowBlur") ++ !UrlbarPrefs.get("closeOnWindowBlur") + ) { + return; + } // We cannot count every blur events after a missed engagement as abandoment // because the user may have clicked on some view element that executes // a command causing a focus change. For example opening preferences from -@@ -5424,6 +5546,11 @@ ${ +@@ -5276,6 +5396,11 @@ ${ } _on_click(event) { @@ -305,7 +291,7 @@ index e7e9495e56076cd112beafb8297dece4ae4cea58..95e4333866a5f0d41fbdbe23910c3964 switch (event.target) { case this.inputField: case this._inputContainer: -@@ -5513,10 +5640,11 @@ ${ +@@ -5371,10 +5496,11 @@ ${ } if (untrim) { this.setValue(this._untrimmedValue); @@ -318,7 +304,7 @@ index e7e9495e56076cd112beafb8297dece4ae4cea58..95e4333866a5f0d41fbdbe23910c3964 this.view.autoOpen({ event }); } else { if (this._untrimOnFocusAfterKeydown) { -@@ -5556,9 +5684,16 @@ ${ +@@ -5414,9 +5540,16 @@ ${ } _on_mousedown(event) { @@ -336,7 +322,7 @@ index e7e9495e56076cd112beafb8297dece4ae4cea58..95e4333866a5f0d41fbdbe23910c3964 if ( event.composedTarget != this.inputField && event.composedTarget != this._inputContainer -@@ -5568,6 +5703,10 @@ ${ +@@ -5426,6 +5559,10 @@ ${ this.focusedViaMousedown = !this.focused; this.#preventClickSelectsAll = this.focused; @@ -347,7 +333,7 @@ index e7e9495e56076cd112beafb8297dece4ae4cea58..95e4333866a5f0d41fbdbe23910c3964 // Keep the focus status, since the attribute may be changed // upon calling this.focus(). -@@ -5605,7 +5744,7 @@ ${ +@@ -5463,7 +5600,7 @@ ${ // view open on tab switch, and the TabSelect event arrived earlier. // Also ignore mousedown on the urlbarView context menu: opening/closing the // view is already handled by the result opening flow. @@ -356,12 +342,12 @@ index e7e9495e56076cd112beafb8297dece4ae4cea58..95e4333866a5f0d41fbdbe23910c3964 break; } -@@ -5894,7 +6033,7 @@ ${ +@@ -5754,7 +5891,7 @@ ${ // When we are in actions search mode we can show more results so // increase the limit. let maxResults = - this.searchMode?.source != UrlbarShared.RESULT_SOURCE.ACTIONS + this.searchMode?.source != UrlbarShared.RESULT_SOURCE.ZEN_ACTIONS - ? lazy.UrlbarPrefs.get("maxRichResults") + ? UrlbarPrefs.get("maxRichResults") : UNLIMITED_MAX_RESULTS; let options = { diff --git a/src/browser/components/urlbar/content/UrlbarShared-mjs.patch b/src/browser/components/urlbar/content/UrlbarShared-mjs.patch index 6a676136c..a36136808 100644 --- a/src/browser/components/urlbar/content/UrlbarShared-mjs.patch +++ b/src/browser/components/urlbar/content/UrlbarShared-mjs.patch @@ -1,8 +1,8 @@ diff --git a/browser/components/urlbar/content/UrlbarShared.mjs b/browser/components/urlbar/content/UrlbarShared.mjs -index 5b45afb747bfcdbd06782186737669aa2c4c634d..f68bd009f70415b5fb513abdfe19b4427ceefbfc 100644 +index 4e67dad66ae006842abc6eed547e4a244320d57a..393b61b4d2543c73e60afd334a353f36119ae73e 100644 --- a/browser/components/urlbar/content/UrlbarShared.mjs +++ b/browser/components/urlbar/content/UrlbarShared.mjs -@@ -47,6 +47,7 @@ export const UrlbarShared = { +@@ -100,6 +100,7 @@ export const UrlbarShared = { // `looksLikeOrigin()` returned `LOOKS_LIKE_ORIGIN.OTHER` for this token. // It may or may not be an origin. POSSIBLE_ORIGIN_BUT_SEARCH_ALLOWED: 12, @@ -10,7 +10,7 @@ index 5b45afb747bfcdbd06782186737669aa2c4c634d..f68bd009f70415b5fb513abdfe19b442 }), /** -@@ -67,6 +68,7 @@ export const UrlbarShared = { +@@ -120,6 +121,7 @@ export const UrlbarShared = { TITLE: "#", URL: "$", ACTION: ">", @@ -18,7 +18,7 @@ index 5b45afb747bfcdbd06782186737669aa2c4c634d..f68bd009f70415b5fb513abdfe19b442 }), // Defines UrlbarResult types. -@@ -112,6 +114,8 @@ export const UrlbarShared = { +@@ -165,6 +167,8 @@ export const UrlbarShared = { OTHER_NETWORK: 6, ADDON: 7, ACTIONS: 8, @@ -26,9 +26,33 @@ index 5b45afb747bfcdbd06782186737669aa2c4c634d..f68bd009f70415b5fb513abdfe19b442 + WORKSPACES: 10, }), - /** -@@ -128,6 +132,7 @@ export const UrlbarShared = { - if (lazy.UrlbarPrefs.get("scotchBonnet.enableOverride")) { + // Results are categorized into groups to help the muxer compose them. See +@@ -200,6 +204,8 @@ export const UrlbarShared = { + SEMANTIC_HISTORY: "semanticHistory", + SUGGESTED_INDEX: "suggestedIndex", + TAIL_SUGGESTION: "tailSuggestion", ++ ZEN_ACTION: "zenAction", ++ ZEN_WORKSPACE: "zenWorkspace", + }), + + // Defines provider types. +@@ -385,6 +391,14 @@ export const UrlbarShared = { + telemetryLabel: "actions", + uiLabel: "urlbar-searchmode-actions3", + }, ++ { ++ source: this.RESULT_SOURCE.WORKSPACES, ++ restrict: this.RESTRICT_TOKENS.WORKSPACE, ++ icon: "chrome://browser/skin/zen-icons/selectable/layers.svg", ++ pref: "shortcuts.workspaces", ++ telemetryLabel: "workspaces", ++ uiLabel: "urlbar-search-mode-workspaces", ++ }, + ]); + }, + +@@ -402,6 +416,7 @@ export const UrlbarShared = { + if (UrlbarPrefs.get("scotchBonnet.enableOverride")) { keys.push(this.RESTRICT_TOKENS.ACTION); } + keys.push(this.RESTRICT_TOKENS.WORKSPACE); diff --git a/src/browser/components/urlbar/content/UrlbarView-mjs.patch b/src/browser/components/urlbar/content/UrlbarView-mjs.patch index 7225a807e..1ef7cf0a8 100644 --- a/src/browser/components/urlbar/content/UrlbarView-mjs.patch +++ b/src/browser/components/urlbar/content/UrlbarView-mjs.patch @@ -1,8 +1,8 @@ diff --git a/browser/components/urlbar/content/UrlbarView.mjs b/browser/components/urlbar/content/UrlbarView.mjs -index e465806c4ae4fc372d9aa2b71aa7f0e74c65d026..c0b3781901298bedee2736819a93edbf545507a7 100644 +index 9a2c787cfa4d70bd587d49e017075f905c90f626..1c3b341e5d520ec9541c4021aaedce98751b6993 100644 --- a/browser/components/urlbar/content/UrlbarView.mjs +++ b/browser/components/urlbar/content/UrlbarView.mjs -@@ -803,7 +803,7 @@ export class UrlbarView { +@@ -822,7 +822,7 @@ export class UrlbarView { !this.input.value || this.input.getAttribute("pageproxystate") == "valid" ) { @@ -11,7 +11,7 @@ index e465806c4ae4fc372d9aa2b71aa7f0e74c65d026..c0b3781901298bedee2736819a93edbf // Try to reuse the cached top-sites context. If it's not cached, then // there will be a gap of time between when the input is focused and // when the view opens that can be perceived as flicker. -@@ -941,10 +941,6 @@ export class UrlbarView { +@@ -963,10 +963,6 @@ export class UrlbarView { } // If search mode isn't active, close the view. @@ -22,7 +22,7 @@ index e465806c4ae4fc372d9aa2b71aa7f0e74c65d026..c0b3781901298bedee2736819a93edbf // Search mode is active. If the one-offs should be shown, make sure they // are enabled and show the view. -@@ -3203,6 +3199,8 @@ export class UrlbarView { +@@ -3306,6 +3302,8 @@ export class UrlbarView { if (row?.hasAttribute("row-selectable")) { row?.toggleAttribute("selected", true); } @@ -31,7 +31,7 @@ index e465806c4ae4fc372d9aa2b71aa7f0e74c65d026..c0b3781901298bedee2736819a93edbf if (element != row) { row?.toggleAttribute("descendant-selected", true); } -@@ -3715,7 +3713,7 @@ export class UrlbarView { +@@ -3972,7 +3970,7 @@ export class UrlbarView { } #enableOrDisableRowWrap() { @@ -40,13 +40,13 @@ index e465806c4ae4fc372d9aa2b71aa7f0e74c65d026..c0b3781901298bedee2736819a93edbf this.#rows.toggleAttribute("wrap", wrap); this.oneOffSearchButtons?.container.toggleAttribute("wrap", wrap); } -@@ -4144,6 +4142,12 @@ export class UrlbarView { +@@ -4387,6 +4385,12 @@ export class UrlbarView { } on_blur() { + if ( + this.document.commandDispatcher.focusedElement == this.input.inputField && -+ !lazy.UrlbarPrefs.get("closeOnWindowBlur") ++ !UrlbarPrefs.get("closeOnWindowBlur") + ) { + return; + } diff --git a/src/browser/themes/linux/browser-css.patch b/src/browser/themes/linux/browser-css.patch index 4331385b9..3079f7f20 100644 --- a/src/browser/themes/linux/browser-css.patch +++ b/src/browser/themes/linux/browser-css.patch @@ -1,11 +1,11 @@ diff --git a/browser/themes/linux/browser.css b/browser/themes/linux/browser.css -index e735ec80b56e1a2da4bc5bec29136ce60353277b..330ea62d220d9acd96a95a605888839388e97452 100644 +index f276db1d456703493ee713370691352985021708..619ee6724eec1e6a25adcb257af0d02eb7a29d34 100644 --- a/browser/themes/linux/browser.css +++ b/browser/themes/linux/browser.css @@ -14,7 +14,6 @@ @media (-moz-gtk-theme-family) { --tabs-navbar-separator-style: none; - @media (prefers-color-scheme: light) and (not -moz-pref("browser.nova.enabled")) { + @media (prefers-color-scheme: light) { - --urlbar-box-background-color: #fafafa; } } diff --git a/src/browser/themes/shared/tabbrowser/content-area-css.patch b/src/browser/themes/shared/tabbrowser/content-area-css.patch index 678dc33ec..d8db6cb91 100644 --- a/src/browser/themes/shared/tabbrowser/content-area-css.patch +++ b/src/browser/themes/shared/tabbrowser/content-area-css.patch @@ -1,16 +1,16 @@ diff --git a/browser/themes/shared/tabbrowser/content-area.css b/browser/themes/shared/tabbrowser/content-area.css -index 3acdec2b8e043fe27919cfa54e73f4d3c491bfc8..267a1087134464a8193a824a2d7f8ac15ff8ad60 100644 +index 2374ed38bd9361d6c7e1886117099c6761e403b8..72f1f52a13096f19ca4009037ad0cdc880775ff7 100644 --- a/browser/themes/shared/tabbrowser/content-area.css +++ b/browser/themes/shared/tabbrowser/content-area.css -@@ -157,7 +157,6 @@ - min-height: 0; +@@ -239,7 +239,6 @@ + min-width: 0; /* We want to be able to show the frame color behind the clipped radiused corner */ - background: var(--tabpanel-background-color); - @media -moz-pref("sidebar.revamp") { - outline: 0.01px solid var(--chrome-content-separator-color); -@@ -216,7 +215,6 @@ + display: grid; + /** +@@ -425,7 +424,6 @@ } browser:is([blank], [pendingpaint]) { @@ -18,7 +18,7 @@ index 3acdec2b8e043fe27919cfa54e73f4d3c491bfc8..267a1087134464a8193a824a2d7f8ac1 } /* Exclude browsers with smartwindow-content attribute which inherit -@@ -553,7 +551,7 @@ split-view-footer { +@@ -865,7 +863,7 @@ split-view-footer { .dialogStack { z-index: var(--browser-stack-z-index-dialog-stack); @@ -27,12 +27,12 @@ index 3acdec2b8e043fe27919cfa54e73f4d3c491bfc8..267a1087134464a8193a824a2d7f8ac1 inset: 0; /* --browser-with-dialog set on browser[tabDialogShowing], we want to position the overlay only on the top of the element so it doesn't overlap the DevTools toolbox */ -@@ -718,7 +716,7 @@ split-view-footer { +@@ -1034,7 +1032,7 @@ split-view-footer { .dialogOverlay[topmost="true"], #window-modal-dialog::backdrop { - background-color: var(--background-color-overlay); + background-color: light-dark(rgba(255, 255, 255, .3), rgba(0, 0, 0, .3)); - } + border-radius: var(--chrome-block-radius); - .dialogOverlay[hideContent="true"][topmost="true"] { + .browserSidebarContainer.responsive-mode & { diff --git a/src/browser/themes/shared/tabbrowser/tabs-css.patch b/src/browser/themes/shared/tabbrowser/tabs-css.patch index 28160af66..810751b0b 100644 --- a/src/browser/themes/shared/tabbrowser/tabs-css.patch +++ b/src/browser/themes/shared/tabbrowser/tabs-css.patch @@ -1,17 +1,17 @@ diff --git a/browser/themes/shared/tabbrowser/tabs.css b/browser/themes/shared/tabbrowser/tabs.css -index 0820fa040642ee05c2a24f5d2cfa58da5c926647..2d93abcce1729744e5963f1c6ad47f253804576e 100644 +index 61c0b8382c78f63ad9113af55a6236c4a29bc45b..0ab463c768d4fb3f45d22e753f408c46b61c0480 100644 --- a/browser/themes/shared/tabbrowser/tabs.css +++ b/browser/themes/shared/tabbrowser/tabs.css -@@ -45,7 +45,7 @@ +@@ -46,7 +46,7 @@ --tab-group-line-thickness: 2px; --tab-group-line-toolbar-border-distance: 1px; /* Collapsed tabs should be square, so set width to match the min height */ - --tab-collapsed-background-width: var(--tab-min-height); + --tab-collapsed-background-width: 48px; --tab-collapsed-width: calc(var(--tab-collapsed-background-width) + 2 * var(--tab-inner-inline-margin)); + --tab-collapsed-split-pill-offset: 0px; --tab-pinned-min-width-expanded: calc(var(--tab-pinned-expanded-background-width) + 2 * var(--tab-pinned-margin-inline-expanded)); - --tab-note-icon-end-margin: var(--dimension-4); -@@ -295,7 +295,6 @@ tab-split-view-wrapper[dragtarget] { +@@ -308,7 +308,6 @@ tab-split-view-wrapper[dragtarget] { } :root:not([uidensity="compact"], [sidebar-expand-on-hover]) &[pinned] { @@ -19,7 +19,7 @@ index 0820fa040642ee05c2a24f5d2cfa58da5c926647..2d93abcce1729744e5963f1c6ad47f25 } &:is([selected], [multiselected]) { -@@ -309,6 +308,7 @@ tab-split-view-wrapper[dragtarget] { +@@ -322,6 +321,7 @@ tab-split-view-wrapper[dragtarget] { border-radius: inherit; position: relative; overflow: hidden; @@ -27,7 +27,7 @@ index 0820fa040642ee05c2a24f5d2cfa58da5c926647..2d93abcce1729744e5963f1c6ad47f25 &::before { position: absolute; -@@ -506,10 +506,6 @@ tab-split-view-wrapper[dragtarget] { +@@ -519,10 +519,6 @@ tab-split-view-wrapper[dragtarget] { @media -moz-pref("browser.tabs.fadeOutUnloadedTabs") { &[pending] { @@ -38,7 +38,7 @@ index 0820fa040642ee05c2a24f5d2cfa58da5c926647..2d93abcce1729744e5963f1c6ad47f25 opacity: 0.5; /* Fade the favicon out */ transition-property: filter, opacity; -@@ -525,10 +521,6 @@ tab-split-view-wrapper[dragtarget] { +@@ -538,10 +534,6 @@ tab-split-view-wrapper[dragtarget] { @media -moz-pref("browser.tabs.fadeOutExplicitlyUnloadedTabs") { &[pending][discarded] { @@ -49,7 +49,7 @@ index 0820fa040642ee05c2a24f5d2cfa58da5c926647..2d93abcce1729744e5963f1c6ad47f25 opacity: 0.5; /* Fade the favicon out */ transition-property: filter, opacity; -@@ -602,7 +594,7 @@ tab-split-view-wrapper[dragtarget] { +@@ -615,7 +607,7 @@ tab-split-view-wrapper[dragtarget] { } #tabbrowser-tabs[orient="vertical"] & { @@ -58,7 +58,7 @@ index 0820fa040642ee05c2a24f5d2cfa58da5c926647..2d93abcce1729744e5963f1c6ad47f25 } &[crashed] { -@@ -610,7 +602,7 @@ tab-split-view-wrapper[dragtarget] { +@@ -623,7 +615,7 @@ tab-split-view-wrapper[dragtarget] { } #tabbrowser-tabs[orient="vertical"]:not([expanded]) &:not([crashed]), @@ -67,7 +67,7 @@ index 0820fa040642ee05c2a24f5d2cfa58da5c926647..2d93abcce1729744e5963f1c6ad47f25 &[soundplaying] { list-style-image: url("chrome://browser/skin/tabbrowser/tab-audio-playing-small.svg"); } -@@ -664,7 +656,7 @@ tab-split-view-wrapper[dragtarget] { +@@ -677,7 +669,7 @@ tab-split-view-wrapper[dragtarget] { } } @@ -76,7 +76,7 @@ index 0820fa040642ee05c2a24f5d2cfa58da5c926647..2d93abcce1729744e5963f1c6ad47f25 &[crashed] { display: revert; } -@@ -824,7 +816,7 @@ tab-split-view-wrapper[dragtarget] { +@@ -837,7 +829,7 @@ tab-split-view-wrapper[dragtarget] { has not been added to root. There are certain scenarios when that attribute is temporarily removed from root such as when toggling the sidebar to expand with the toolbar button. */ #tabbrowser-tabs[orient="horizontal"] &:not([pinned]):not([crashed]), @@ -85,7 +85,7 @@ index 0820fa040642ee05c2a24f5d2cfa58da5c926647..2d93abcce1729744e5963f1c6ad47f25 &:is([soundplaying], [muted], [activemedia-blocked]) { display: flex; } -@@ -1044,7 +1036,6 @@ tab-split-view-wrapper[dragtarget] { +@@ -1151,7 +1143,6 @@ tab-split-view-wrapper[dragtarget] { .tabbrowser-tab:is([image], [pinned]) > .tab-stack > .tab-content[attention]:not([selected]), .tabbrowser-tab > .tab-stack > .tab-content[pinned][titlechanged]:not([selected]), #tabbrowser-tabs[orient="vertical"] .tabbrowser-tab > .tab-stack > .tab-content[titlechanged]:not([selected]) { @@ -93,7 +93,7 @@ index 0820fa040642ee05c2a24f5d2cfa58da5c926647..2d93abcce1729744e5963f1c6ad47f25 background-position: center bottom 6.5px; background-size: 4px 4px; background-repeat: no-repeat; -@@ -1797,7 +1788,7 @@ tab-group { +@@ -1904,7 +1895,7 @@ tab-group { } #tabbrowser-tabs[orient="vertical"][expanded] { @@ -102,7 +102,7 @@ index 0820fa040642ee05c2a24f5d2cfa58da5c926647..2d93abcce1729744e5963f1c6ad47f25 &[movingtab][movingtab-addToGroup]:not([movingtab-group], [movingtab-ungroup]) .tabbrowser-tab:is(:active, [multiselected]) { margin-inline-start: var(--space-medium); } -@@ -2351,7 +2342,7 @@ tab-group { +@@ -2504,7 +2495,7 @@ tab-group { } } @@ -111,8 +111,8 @@ index 0820fa040642ee05c2a24f5d2cfa58da5c926647..2d93abcce1729744e5963f1c6ad47f25 #vertical-tabs-newtab-button { appearance: none; min-height: var(--tab-min-height); -@@ -2362,7 +2353,7 @@ tab-group { - margin-inline: var(--tab-inner-inline-margin); +@@ -2524,7 +2515,7 @@ tab-group { + } #tabbrowser-tabs[orient="vertical"]:not([expanded]) & > .toolbarbutton-text { - display: none; @@ -120,7 +120,7 @@ index 0820fa040642ee05c2a24f5d2cfa58da5c926647..2d93abcce1729744e5963f1c6ad47f25 } &:hover { -@@ -2386,7 +2377,7 @@ tab-group { +@@ -2548,7 +2539,7 @@ tab-group { * flex container. #tabs-newtab-button is a child of the arrowscrollbox where * we don't want a gap (between tabs), so we have to add some margin. */ @@ -129,7 +129,7 @@ index 0820fa040642ee05c2a24f5d2cfa58da5c926647..2d93abcce1729744e5963f1c6ad47f25 margin-block: var(--tab-margin-block); } -@@ -2576,7 +2567,6 @@ tab-group { +@@ -2740,7 +2731,6 @@ tab-group { &:not([expanded]) { .tabbrowser-tab[pinned] { @@ -137,7 +137,7 @@ index 0820fa040642ee05c2a24f5d2cfa58da5c926647..2d93abcce1729744e5963f1c6ad47f25 } .tab-background { -@@ -2615,8 +2605,8 @@ tab-group { +@@ -2779,8 +2769,8 @@ tab-group { display: block; position: absolute; inset: auto; @@ -148,7 +148,7 @@ index 0820fa040642ee05c2a24f5d2cfa58da5c926647..2d93abcce1729744e5963f1c6ad47f25 &:-moz-window-inactive { background-image: -@@ -2714,9 +2704,6 @@ tab-group { +@@ -2878,9 +2868,6 @@ tab-group { ~ #tabbrowser-tabs[orient="horizontal"]::before { display: flex; content: ""; @@ -158,7 +158,7 @@ index 0820fa040642ee05c2a24f5d2cfa58da5c926647..2d93abcce1729744e5963f1c6ad47f25 } } -@@ -2749,7 +2736,6 @@ toolbar:not(#TabsToolbar) #firefox-view-button { +@@ -2913,7 +2900,6 @@ toolbar:not(#TabsToolbar) #firefox-view-button { list-style-image: url(chrome://global/skin/icons/plus.svg); } diff --git a/src/browser/themes/shared/urlbar-searchbar-css.patch b/src/browser/themes/shared/urlbar-searchbar-css.patch index fdc1f148d..74b873f46 100644 --- a/src/browser/themes/shared/urlbar-searchbar-css.patch +++ b/src/browser/themes/shared/urlbar-searchbar-css.patch @@ -1,19 +1,19 @@ diff --git a/browser/themes/shared/urlbar-searchbar.css b/browser/themes/shared/urlbar-searchbar.css -index 22d624d718a289dd7b84ffc2f04dd35893dfbe95..4f57f7853f3043e3ad01d893aa928b9d2e6f3c16 100644 +index 149f123e26cd79e89432dfeb504ca7218ee04810..7e0e6d981d6c2aa53bc2a76d82d2078419659065 100644 --- a/browser/themes/shared/urlbar-searchbar.css +++ b/browser/themes/shared/urlbar-searchbar.css @@ -19,8 +19,8 @@ @media not -moz-pref("browser.nova.enabled") { - --urlbar-basic-component-size: calc(var(--urlbar-min-height) - 2 * (var(--urlbar-input-container-border-width) + var(--urlbar-input-container-padding))); -- --urlbar-icon-padding: calc((var(--urlbar-min-height) - 2px /* border */ - 2px /* padding */ - 16px /* icon */) / 2); + --urlbar-basic-component-size: calc(var(--urlbar-height) - 2 * (var(--urlbar-input-container-border-width) + var(--urlbar-input-container-padding))); +- --urlbar-icon-padding: calc((var(--urlbar-height) - 2px /* border */ - 2px /* padding */ - 16px /* icon */) / 2); - --urlbar-input-container-padding: round(up, 1px, env(hairline)); -+ --urlbar-icon-padding: calc((var(--urlbar-min-height) - 2px /* border */ - 14px /* icon */) / 2); ++ --urlbar-icon-padding: calc((var(--urlbar-height) - 2px /* border */ - 14px /* icon */) / 2); + --urlbar-input-container-padding: 2px; } @media (max-width: 770px) { --urlbar-container-min-width: 240px; -@@ -126,7 +126,7 @@ toolbar[inactive="true"] .urlbar, +@@ -127,7 +127,7 @@ toolbar[inactive="true"] .urlbar, .urlbar:not([usertyping]) > .urlbar-input-container > .urlbar-go-button, .urlbar:not(#searchbar-new, [focused]) > .urlbar-input-container > .urlbar-go-button, #urlbar-revert-button-container { @@ -22,7 +22,7 @@ index 22d624d718a289dd7b84ffc2f04dd35893dfbe95..4f57f7853f3043e3ad01d893aa928b9d } /* Document Picture-in-Picture API window */ -@@ -403,6 +403,10 @@ toolbar[inactive="true"] .urlbar, +@@ -405,6 +405,10 @@ toolbar[inactive="true"] .urlbar, .urlbar:not([focused])[textoverflow="left"][domaindir="rtl"] > .urlbar-input-container > .urlbar-input-box > & { mask-image: linear-gradient(to right, transparent var(--urlbar-scheme-size), black calc(var(--urlbar-scheme-size) + 3ch)); } @@ -33,16 +33,18 @@ index 22d624d718a289dd7b84ffc2f04dd35893dfbe95..4f57f7853f3043e3ad01d893aa928b9d } #urlbar-scheme { -@@ -492,10 +496,14 @@ toolbar[inactive="true"] .urlbar, +@@ -476,10 +480,15 @@ toolbar[inactive="true"] .urlbar, + } .urlbar[breakout][breakout-extend] { - height: auto; +- margin-left: calc(-1 * var(--urlbar-margin-inline)); + align-items: center; -+ :root:not([zen-single-toolbar='true']) & { - margin-left: calc(-1 * var(--urlbar-margin-inline)); -+ } width: calc(var(--urlbar-width) + 2 * var(--urlbar-margin-inline)); ++ :root:not([zen-single-toolbar="true"]) & { ++ margin-left: calc(-1 * var(--urlbar-margin-inline)); ++ } ++ > .urlbar-input-container { + align-items: center; height: var(--urlbar-container-height); diff --git a/src/external-patches/firefox/allow_backdrop_to_work_on_transparency.patch b/src/external-patches/firefox/allow_backdrop_to_work_on_transparency.patch index 20ebb535c..2c7e5bfe1 100644 --- a/src/external-patches/firefox/allow_backdrop_to_work_on_transparency.patch +++ b/src/external-patches/firefox/allow_backdrop_to_work_on_transparency.patch @@ -31,12 +31,13 @@ diff --git a/gfx/webrender_bindings/src/bindings.rs b/gfx/webrender_bindings/src // Ensure the WR profiler callbacks are hooked up to the Gecko profiler. set_profiler_hooks(Some(&PROFILER_HOOKS)); -@@ -2164,10 +2165,11 @@ +@@ -2164,11 +2165,12 @@ texture_cache_config, reject_software_rasterizer, low_quality_pinch_zoom, max_shared_surface_size, enable_dithering, + enable_shared_instance_buffer, + opaque_backdrop_fallback, ..Default::default() }; @@ -73,8 +74,9 @@ diff --git a/gfx/wr/webrender/src/renderer/init.rs b/gfx/wr/webrender/src/render + /// See explanation of `gfx.webrender.opaque-backdrop-fallback`. + pub opaque_backdrop_fallback: bool, } -@@ -277,9 +279,10 @@ +@@ -277,10 +279,11 @@ enable_instancing: true, + enable_shared_instance_buffer: false, reject_software_rasterizer: false, low_quality_pinch_zoom: false, max_shared_surface_size: 2048, @@ -84,12 +86,13 @@ diff --git a/gfx/wr/webrender/src/renderer/init.rs b/gfx/wr/webrender/src/render } } -@@ -802,10 +805,11 @@ +@@ -802,11 +805,12 @@ allocated_native_surfaces: FastHashSet::default(), debug_overlay_state: DebugOverlayState::new(), buffer_damage_tracker: BufferDamageTracker::default(), max_primitive_instance_count, enable_instancing: options.enable_instancing, + use_shared_instance_buffer, + opaque_backdrop_fallback: options.opaque_backdrop_fallback, consecutive_oom_frames: 0, target_frame_publish_id: None, @@ -99,11 +102,14 @@ diff --git a/gfx/wr/webrender/src/renderer/init.rs b/gfx/wr/webrender/src/render diff --git a/gfx/wr/webrender/src/renderer/mod.rs b/gfx/wr/webrender/src/renderer/mod.rs --- a/gfx/wr/webrender/src/renderer/mod.rs +++ b/gfx/wr/webrender/src/renderer/mod.rs -@@ -867,10 +867,12 @@ +@@ -867,13 +867,15 @@ buffer_damage_tracker: BufferDamageTracker, max_primitive_instance_count: usize, enable_instancing: bool, + /// If true, stream instance data into large buffers shared by multiple + /// draws, rather than allocating a new instance buffer for each draw. + use_shared_instance_buffer: bool, + opaque_backdrop_fallback: bool, + diff --git a/src/external-patches/firefox/bug_2011236.patch b/src/external-patches/firefox/bug_2011236.patch index a8764a3d4..1a46a9c03 100644 --- a/src/external-patches/firefox/bug_2011236.patch +++ b/src/external-patches/firefox/bug_2011236.patch @@ -137,7 +137,7 @@ diff --git a/docshell/shistory/SessionHistoryEntry.cpp b/docshell/shistory/Sessi : mURI(aLoadState->URI()), mOriginalURI(aLoadState->OriginalURI()), mResultPrincipalURI(aLoadState->ResultPrincipalURI()), -@@ -59,9 +67,10 @@ +@@ -59,11 +67,12 @@ mLoadReplace(aLoadState->LoadReplace()), mHasUserActivation(aLoadState->HasValidUserGestureActivation()), mSharedState(SharedState::Create( @@ -148,6 +148,8 @@ diff --git a/docshell/shistory/SessionHistoryEntry.cpp b/docshell/shistory/Sessi + aLoadState->PolicyContainer()), /* FIXME Is this correct? */ aLoadState->TypeHint())) { + MOZ_DIAGNOSTIC_ASSERT(!mURI->SchemeIs("javascript")); + // Pull the upload stream off of the channel instead of the load state, as @@ -124,11 +133,12 @@ loadInfo->GetPrincipalToInherit( diff --git a/src/external-patches/firefox/css_corner_shape_rendering.patch b/src/external-patches/firefox/css_corner_shape_rendering.patch index b4e70bbe1..3d26a5fbf 100644 --- a/src/external-patches/firefox/css_corner_shape_rendering.patch +++ b/src/external-patches/firefox/css_corner_shape_rendering.patch @@ -1,351 +1,51 @@ -diff --git a/gfx/webrender_bindings/WebRenderAPI.cpp b/gfx/webrender_bindings/WebRenderAPI.cpp -index 4a72a0eecf..31092b3c05 100644 ---- a/gfx/webrender_bindings/WebRenderAPI.cpp -+++ b/gfx/webrender_bindings/WebRenderAPI.cpp -@@ -1448,11 +1448,12 @@ void DisplayListBuilder::PushRoundedRect(const wr::LayoutRect& aBounds, - wr::LayoutSideOffsets widths = {v, h, v, h}; - wr::BorderRadius radii = {{h, v}, {h, v}, {h, v}, {h, v}, - 1.0f, 1.0f, 1.0f, 1.0f}; -+ wr::LayoutSideOffsets inset = EmptyLayoutSideOffsets(); - - // Anti-aliased borders are required for rounded borders. - wr_dp_push_border(mWrState, aBounds, aClip, aIsBackfaceVisible, - &mCurrentSpaceAndClipChain, wr::AntialiasBorder::Yes, -- widths, side, side, side, side, radii); -+ widths, side, side, side, side, radii, inset); - } - - void DisplayListBuilder::PushHitTest( -@@ -1668,20 +1669,18 @@ void DisplayListBuilder::PushIFrame(const LayoutDeviceRect& aDevPxBounds, - aIgnoreMissingPipeline); - } - --void DisplayListBuilder::PushBorder(const wr::LayoutRect& aBounds, -- const wr::LayoutRect& aClip, -- bool aIsBackfaceVisible, -- const wr::LayoutSideOffsets& aWidths, -- const Range& aSides, -- const wr::BorderRadius& aRadius, -- wr::AntialiasBorder aAntialias) { -+void DisplayListBuilder::PushBorder( -+ const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip, -+ bool aIsBackfaceVisible, const wr::LayoutSideOffsets& aWidths, -+ const Range& aSides, const wr::BorderRadius& aRadius, -+ const wr::LayoutSideOffsets& aInset, wr::AntialiasBorder aAntialias) { - MOZ_ASSERT(aSides.length() == 4); - if (aSides.length() != 4) { - return; - } - wr_dp_push_border(mWrState, aBounds, aClip, aIsBackfaceVisible, - &mCurrentSpaceAndClipChain, aAntialias, aWidths, aSides[0], -- aSides[1], aSides[2], aSides[3], aRadius); -+ aSides[1], aSides[2], aSides[3], aRadius, aInset); - } - - void DisplayListBuilder::PushBorderImage(const wr::LayoutRect& aBounds, -diff --git a/gfx/webrender_bindings/WebRenderAPI.h b/gfx/webrender_bindings/WebRenderAPI.h -index 30d4da889a..f464c2a626 100644 ---- a/gfx/webrender_bindings/WebRenderAPI.h -+++ b/gfx/webrender_bindings/WebRenderAPI.h -@@ -753,11 +753,13 @@ class DisplayListBuilder final { - - // XXX WrBorderSides are passed with Range. - // It is just to bypass compiler bug. See Bug 1357734. -- void PushBorder(const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip, -- bool aIsBackfaceVisible, const wr::LayoutSideOffsets& aWidths, -- const Range& aSides, -- const wr::BorderRadius& aRadius, -- wr::AntialiasBorder = wr::AntialiasBorder::Yes); -+ void PushBorder( -+ const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip, -+ bool aIsBackfaceVisible, const wr::LayoutSideOffsets& aWidths, -+ const Range& aSides, -+ const wr::BorderRadius& aRadius, -+ const wr::LayoutSideOffsets& aInset = EmptyLayoutSideOffsets(), -+ wr::AntialiasBorder = wr::AntialiasBorder::Yes); - - void PushBorderImage(const wr::LayoutRect& aBounds, - const wr::LayoutRect& aClip, bool aIsBackfaceVisible, -diff --git a/gfx/webrender_bindings/WebRenderTypes.h b/gfx/webrender_bindings/WebRenderTypes.h -index 5799ed46cf..db7dc93faa 100644 ---- a/gfx/webrender_bindings/WebRenderTypes.h -+++ b/gfx/webrender_bindings/WebRenderTypes.h -@@ -485,40 +485,6 @@ static inline wr::BorderRadius ToBorderRadius(const nsRectCornerRadii& aRadii, - return br; - } - --static inline wr::ComplexClipRegion ToComplexClipRegion( -- const nsRect& aRect, const nsRectCornerRadii& aRadii, -- int32_t aAppUnitsPerDevPixel) { -- wr::ComplexClipRegion ret; -- ret.rect = -- ToLayoutRect(LayoutDeviceRect::FromAppUnits(aRect, aAppUnitsPerDevPixel)); -- ret.radii = ToBorderRadius(aRadii, aAppUnitsPerDevPixel); -- ret.mode = ClipMode::Clip; -- return ret; --} -- --static inline wr::ComplexClipRegion ToComplexClipRegion( -- const gfx::RoundedRect& rect) { -- wr::ComplexClipRegion ret; -- ret.rect = ToLayoutRect(rect.rect); -- ret.radii = ToBorderRadius(rect.corners); -- ret.mode = wr::ClipMode::Clip; -- return ret; --} -- --static inline wr::ComplexClipRegion SimpleRadii(const wr::LayoutRect& aRect, -- float aRadii) { -- wr::ComplexClipRegion ret; -- wr::LayoutSize radii{aRadii, aRadii}; -- ret.rect = aRect; -- ret.radii = EmptyBorderRadius(); -- ret.radii.top_left = radii; -- ret.radii.top_right = radii; -- ret.radii.bottom_left = radii; -- ret.radii.bottom_right = radii; -- ret.mode = wr::ClipMode::Clip; -- return ret; --} -- - static inline wr::DeviceIntSideOffsets ToDeviceIntSideOffsets(int32_t top, - int32_t right, - int32_t bottom, -@@ -548,6 +514,18 @@ static inline wr::LayoutSideOffsets ToLayoutSideOffsets( - aMargin.left); - } - -+static inline wr::LayoutSideOffsets ToLayoutSideOffsets( -+ const nsMargin& aMargin, int32_t aAppUnitsPerDevPixel) { -+ return ToLayoutSideOffsets(aMargin.top / float(aAppUnitsPerDevPixel), -+ aMargin.right / float(aAppUnitsPerDevPixel), -+ aMargin.bottom / float(aAppUnitsPerDevPixel), -+ aMargin.left / float(aAppUnitsPerDevPixel)); -+} -+ -+static inline wr::LayoutSideOffsets EmptyLayoutSideOffsets() { -+ return ToLayoutSideOffsets(0.0f, 0.0f, 0.0f, 0.0f); -+} -+ - static inline wr::LayoutSideOffsets ToBorderWidths(float top, float right, - float bottom, float left) { - return ToLayoutSideOffsets(top, right, bottom, left); -@@ -559,6 +537,55 @@ static inline wr::LayoutSideOffsets ToBorderWidths(const gfx::Margin& aMargin) { - - wr::RepeatMode ToRepeatMode(StyleBorderImageRepeatKeyword); - -+static inline wr::ComplexClipRegion SimpleRadii(const wr::LayoutRect& aRect, -+ float aRadii) { -+ wr::ComplexClipRegion ret; -+ wr::LayoutSize radii{aRadii, aRadii}; -+ ret.rect = aRect; -+ ret.radii = EmptyBorderRadius(); -+ ret.radii.top_left = radii; -+ ret.radii.top_right = radii; -+ ret.radii.bottom_left = radii; -+ ret.radii.bottom_right = radii; -+ ret.inset = EmptyLayoutSideOffsets(); -+ ret.mode = wr::ClipMode::Clip; -+ return ret; -+} -+ -+static inline wr::ComplexClipRegion ToComplexClipRegion( -+ const gfx::RoundedRect& rect) { -+ wr::ComplexClipRegion ret; -+ ret.rect = ToLayoutRect(rect.rect); -+ ret.radii = ToBorderRadius(rect.corners); -+ ret.inset = EmptyLayoutSideOffsets(); -+ ret.mode = wr::ClipMode::Clip; -+ return ret; -+} -+ -+static inline wr::ComplexClipRegion ToComplexClipRegion( -+ const nsRect& aRect, const nsRectCornerRadii& aRadii, -+ int32_t aAppUnitsPerDevPixel) { -+ wr::ComplexClipRegion ret; -+ ret.rect = -+ ToLayoutRect(LayoutDeviceRect::FromAppUnits(aRect, aAppUnitsPerDevPixel)); -+ ret.radii = ToBorderRadius(aRadii, aAppUnitsPerDevPixel); -+ ret.inset = EmptyLayoutSideOffsets(); -+ ret.mode = ClipMode::Clip; -+ return ret; -+} -+ -+static inline wr::ComplexClipRegion ToComplexClipRegion( -+ const nsRect& aRect, const nsRectCornerRadii& aRadii, -+ const nsMargin& aInset, int32_t aAppUnitsPerDevPixel) { -+ wr::ComplexClipRegion ret; -+ ret.rect = -+ ToLayoutRect(LayoutDeviceRect::FromAppUnits(aRect, aAppUnitsPerDevPixel)); -+ ret.radii = ToBorderRadius(aRadii, aAppUnitsPerDevPixel); -+ ret.inset = ToLayoutSideOffsets(aInset, aAppUnitsPerDevPixel); -+ ret.mode = ClipMode::Clip; -+ return ret; -+} -+ - template - static inline wr::WrTransformProperty ToWrTransformProperty( - uint64_t id, const gfx::Matrix4x4Typed& transform) { -diff --git a/gfx/webrender_bindings/src/bindings.rs b/gfx/webrender_bindings/src/bindings.rs -index f76367a1f9..5f0b02097c 100644 ---- a/gfx/webrender_bindings/src/bindings.rs -+++ b/gfx/webrender_bindings/src/bindings.rs -@@ -4037,6 +4037,7 @@ pub extern "C" fn wr_dp_push_border( - bottom: BorderSide, - left: BorderSide, - radius: BorderRadius, -+ inset: LayoutSideOffsets, - ) { - debug_assert!(unsafe { is_in_main_thread() }); - -@@ -4046,6 +4047,7 @@ pub extern "C" fn wr_dp_push_border( - top, - bottom, - radius, -+ inset, - do_aa: do_aa == AntialiasBorder::Yes, - }); - -diff --git a/gfx/wr/examples/animation.rs b/gfx/wr/examples/animation.rs -index 11fcfe647c..55f688bc52 100644 ---- a/gfx/wr/examples/animation.rs -+++ b/gfx/wr/examples/animation.rs -@@ -85,6 +85,7 @@ impl App { - let complex_clip = ComplexClipRegion { - rect: clip_bounds, - radii: BorderRadius::uniform(30.0), -+ inset: LayoutSideOffsets::zero(), - mode: ClipMode::Clip, - }; - let clip_id = builder.define_clip_rounded_rect( -diff --git a/gfx/wr/examples/basic.rs b/gfx/wr/examples/basic.rs -index df2f322a43..1dfbef8d53 100644 ---- a/gfx/wr/examples/basic.rs -+++ b/gfx/wr/examples/basic.rs -@@ -52,6 +52,7 @@ impl Example for App { - let complex = ComplexClipRegion::new( - (50, 50).to(150, 150), - BorderRadius::uniform(20.0), -+ LayoutSideOffsets::zero(), - ClipMode::Clip - ); - let clip_id = builder.define_clip_rounded_rect( -@@ -88,6 +89,7 @@ impl Example for App { - bottom: border_side, - left: border_side, - radius: BorderRadius::uniform(20.0), -+ inset: LayoutSideOffsets::zero(), - do_aa: true, - }); - -diff --git a/gfx/wr/swgl/src/glsl.h b/gfx/wr/swgl/src/glsl.h -index b9e7982948..533d891aaa 100644 ---- a/gfx/wr/swgl/src/glsl.h -+++ b/gfx/wr/swgl/src/glsl.h -@@ -645,6 +645,9 @@ SI vec2 min(vec2 a, Float b) { return vec2(min(a.x, b), min(a.y, b)); } - SI vec2_scalar min(vec2_scalar a, vec2_scalar b) { - return vec2_scalar{min(a.x, b.x), min(a.y, b.y)}; - } -+SI vec2_scalar min(vec2_scalar a, float b) { -+ return vec2_scalar{min(a.x, b), min(a.y, b)}; -+} - - SI vec2 if_then_else(I32 c, vec2 t, vec2 e) { - return vec2(if_then_else(c, t.x, e.x), if_then_else(c, t.y, e.y)); -@@ -1743,6 +1746,9 @@ SI vec3 min(vec3 a, Float b) { - SI vec3_scalar min(vec3_scalar a, vec3_scalar b) { - return vec3_scalar{min(a.x, b.x), min(a.y, b.y), min(a.z, b.z)}; - } -+SI vec3_scalar min(vec3_scalar a, float b) { -+ return vec3_scalar{min(a.x, b), min(a.y, b), min(a.z, b)}; -+} - - SI vec3 max(vec3 a, vec3 b) { - return vec3(max(a.x, b.x), max(a.y, b.y), max(a.z, b.z)); -@@ -1753,6 +1759,9 @@ SI vec3 max(vec3 a, Float b) { - SI vec3_scalar max(vec3_scalar a, vec3_scalar b) { - return vec3_scalar{max(a.x, b.x), max(a.y, b.y), max(a.z, b.z)}; - } -+SI vec3_scalar max(vec3_scalar a, float b) { -+ return vec3_scalar{max(a.x, b), max(a.y, b), max(a.z, b)}; -+} - - vec3 pow(vec3 x, vec3 y) { - return vec3(pow(x.x, y.x), pow(x.y, y.y), pow(x.z, y.z)); -@@ -2278,6 +2287,32 @@ vec4_scalar step(vec4_scalar edge, vec4_scalar x) { - step(edge.w, x.w)); - } - -+SI vec4 min(vec4 a, vec4 b) { -+ return vec4(min(a.x, b.x), min(a.y, b.y), min(a.z, b.z), min(a.w, b.w)); -+} -+SI vec4 min(vec4 a, Float b) { -+ return vec4(min(a.x, b), min(a.y, b), min(a.z, b), min(a.w, b)); -+} -+SI vec4_scalar min(vec4_scalar a, vec4_scalar b) { -+ return vec4_scalar{min(a.x, b.x), min(a.y, b.y), min(a.z, b.z), min(a.w, b.w)}; -+} -+SI vec4_scalar min(vec4_scalar a, float b) { -+ return vec4_scalar{min(a.x, b), min(a.y, b), min(a.z, b), min(a.w, b)}; -+} -+ -+SI vec4 max(vec4 a, vec4 b) { -+ return vec4(max(a.x, b.x), max(a.y, b.y), max(a.z, b.z), max(a.w, b.w)); -+} -+SI vec4 max(vec4 a, Float b) { -+ return vec4(max(a.x, b), max(a.y, b), max(a.z, b), max(a.w, b)); -+} -+SI vec4_scalar max(vec4_scalar a, vec4_scalar b) { -+ return vec4_scalar{max(a.x, b.x), max(a.y, b.y), max(a.z, b.z), max(a.w, b.w)}; -+} -+SI vec4_scalar max(vec4_scalar a, float b) { -+ return vec4_scalar{max(a.x, b), max(a.y, b), max(a.z, b), max(a.w, b)}; -+} -+ - template - auto lessThanEqual(T x, T y) -> decltype(x <= y) { - return x <= y; diff --git a/gfx/wr/webrender/res/border_shared.glsl b/gfx/wr/webrender/res/border_shared.glsl -index 5981e53bde..313b51bcb0 100644 +index f385449fa7..76eba024bd 100644 --- a/gfx/wr/webrender/res/border_shared.glsl +++ b/gfx/wr/webrender/res/border_shared.glsl -@@ -21,6 +21,8 @@ PER_INSTANCE in int aGpuDataAddress; +@@ -21,31 +21,39 @@ PER_INSTANCE in int aGpuDataAddress; PER_INSTANCE in vec4 aClipParams1; PER_INSTANCE in vec4 aClipParams2; -+#endif -+ ++#endif ++ struct BorderInstanceGpuData { vec4 rect; vec4 color0; -@@ -28,20 +30,22 @@ struct BorderInstanceGpuData { + vec4 color1; vec2 widths; vec2 radii; ++#ifdef WR_FEATURE_SUPERELLIPSE float shape; -+ vec2 shape_offset; -+ vec2 inset; + vec2 shape_offset; + vec2 inset; ++#endif }; BorderInstanceGpuData fetch_gpu_data(int index) { BorderInstanceGpuData data; -- vec4 texels[5] = fetch_from_gpu_buffer_5f(index); -+ vec4 texels[6] = fetch_from_gpu_buffer_6f(index); ++#ifdef WR_FEATURE_SUPERELLIPSE + vec4 texels[6] = fetch_from_gpu_buffer_6f(index); ++#else ++ vec4 texels[4] = fetch_from_gpu_buffer_4f(index); ++#endif data.rect = texels[0]; data.color0 = texels[1]; data.color1 = texels[2]; data.widths = texels[3].xy; data.radii = texels[3].zw; ++#ifdef WR_FEATURE_SUPERELLIPSE data.shape = texels[4].x; -+ data.shape_offset = texels[4].yz; -+ data.inset = texels[5].xy; + data.shape_offset = texels[4].yz; + data.inset = texels[5].xy; ++#endif return data; } - -#endif diff --git a/gfx/wr/webrender/res/cs_border_segment.glsl b/gfx/wr/webrender/res/cs_border_segment.glsl -index 066f63f62a..f9998badd6 100644 +index 6a94117405..51c9318b31 100644 --- a/gfx/wr/webrender/res/cs_border_segment.glsl +++ b/gfx/wr/webrender/res/cs_border_segment.glsl @@ -15,10 +15,10 @@ flat varying mediump vec4 vColor11; @@ -361,15 +61,17 @@ index 066f63f62a..f9998badd6 100644 // x, y: styles, z, w: edge axes // We cast these to/from floats rather than using an ivec (and bitshifting) // due to a driver bug on Adreno 3xx. See bug 1730458. -@@ -32,6 +32,7 @@ flat varying highp vec4 vClipCenter_Sign; +@@ -32,6 +32,9 @@ flat varying highp vec4 vClipCenter_Sign; // An outer and inner elliptical radii for border // corner clipping. flat varying highp vec4 vClipRadii; ++#ifdef WR_FEATURE_SUPERELLIPSE +flat varying highp vec4 vClipOffsets; ++#endif // Reference point for determine edge clip lines. flat varying highp vec4 vEdgeReference; -@@ -181,7 +182,7 @@ void main(void) { +@@ -181,7 +184,7 @@ void main(void) { break; } @@ -378,19 +80,25 @@ index 066f63f62a..f9998badd6 100644 vStyleEdgeAxis = vec4(float(style0), float(style1), float(edge_axis.x), float(edge_axis.y)); vPartialWidths = vec4(data.widths / 3.0, data.widths / 2.0); -@@ -193,13 +194,23 @@ void main(void) { +@@ -193,13 +196,31 @@ void main(void) { vec4[2] color1 = get_colors_for_side(data.color1, style1); vColor10 = color1[0]; vColor11 = color1[1]; -- vClipCenter_Sign = vec4(outer + clip_sign * data.radii, clip_sign); ++#ifdef WR_FEATURE_SUPERELLIPSE + vClipCenter_Sign = vec4(outer + clip_sign * (data.radii + data.shape_offset), clip_sign); ++#else + vClipCenter_Sign = vec4(outer + clip_sign * data.radii, clip_sign); ++#endif vClipRadii = vec4(data.radii, max(data.radii - data.widths, 0.0)); ++#ifdef WR_FEATURE_SUPERELLIPSE + vClipOffsets = vec4(0.0); ++#endif 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; ++#ifdef WR_FEATURE_SUPERELLIPSE + if (data.shape != 1.0) + { + vec2 reference_radii = (data.radii == vec2(0.0)) ? vec2(0.0) : data.radii + data.inset; @@ -399,11 +107,12 @@ index 066f63f62a..f9998badd6 100644 + vClipOffsets = vec4(contour1.xy, contour2.xy); + vClipRadii = vec4(contour1.zw, contour2.zw); + } ++#endif + // For the case of dot and dash clips, optimize the number of pixels that // are hit to just include the dot itself. if (clip_mode == CLIP_DOT) { -@@ -231,6 +242,7 @@ void main(void) { +@@ -231,6 +252,7 @@ void main(void) { #ifdef WR_FRAGMENT_SHADER vec4 evaluate_color_for_style_in_corner( @@ -411,7 +120,7 @@ index 066f63f62a..f9998badd6 100644 vec2 clip_relative_pos, int style, vec4 color0, -@@ -246,24 +258,61 @@ vec4 evaluate_color_for_style_in_corner( +@@ -246,24 +268,69 @@ vec4 evaluate_color_for_style_in_corner( // also 0.67 of the radii. Use these to form a // SDF subtraction which will clip out the inside // third of the rounded edge. @@ -425,7 +134,9 @@ index 066f63f62a..f9998badd6 100644 - ); + float d_radii_a; + float d_radii_b; ++#ifdef WR_FEATURE_SUPERELLIPSE + if (data.shape == 1.0) { ++#endif + d_radii_a = distance_to_ellipse( + clip_relative_pos, + clip_radii.xy - vPartialWidths.xy @@ -434,6 +145,7 @@ index 066f63f62a..f9998badd6 100644 + clip_relative_pos, + clip_radii.xy - 2.0 * vPartialWidths.xy + ); ++#ifdef WR_FEATURE_SUPERELLIPSE + } else { + d_radii_a = distance_to_superellipse( + clip_relative_pos - mix(vClipOffsets.xy, vClipOffsets.zw, 1.0 / 3.0), @@ -453,6 +165,7 @@ index 066f63f62a..f9998badd6 100644 + vec2 included_region_b = data.radii - 2.0 * vPartialWidths.xy - clip_relative_pos; + d_radii_b = max(d_radii_b, -min(included_region_b.x, included_region_b.y)); + } ++#endif + float d = min(-d_radii_a, d_radii_b); + @@ -466,11 +179,14 @@ index 066f63f62a..f9998badd6 100644 - clip_radii.xy - vPartialWidths.zw - ); + float d; ++#ifdef WR_FEATURE_SUPERELLIPSE + if (data.shape == 1.0) { ++#endif + d = distance_to_ellipse( + clip_relative_pos, + clip_radii.xy - vPartialWidths.zw + ); ++#ifdef WR_FEATURE_SUPERELLIPSE + } else { + d = distance_to_superellipse( + clip_relative_pos - mix(vClipOffsets.xy, vClipOffsets.zw, 0.5), @@ -482,10 +198,11 @@ index 066f63f62a..f9998badd6 100644 + vec2 included_region = data.radii - vPartialWidths.zw - clip_relative_pos; + d = max(d, -min(included_region.x, included_region.y)); + } ++#endif float alpha = distance_aa(aa_range, d); float swizzled_factor; switch (segment) { -@@ -328,11 +377,14 @@ void main(void) { +@@ -328,11 +395,14 @@ void main(void) { float aa_range = compute_aa_range(vPos); vec4 color0, color1; @@ -502,7 +219,7 @@ index 066f63f62a..f9998badd6 100644 float mix_factor = 0.0; if (edge_axis.x != edge_axis.y) { float d_line = distance_to_line(vColorLine.xy, vColorLine.zw, vPos); -@@ -382,12 +434,27 @@ void main(void) { +@@ -382,12 +452,31 @@ void main(void) { } if (in_clip_region) { @@ -511,9 +228,12 @@ index 066f63f62a..f9998badd6 100644 + float d_radii_a; + float d_radii_b; + ++#ifdef WR_FEATURE_SUPERELLIPSE + if (data.shape == 1.0) { ++#endif + d_radii_a = distance_to_ellipse(clip_relative_pos, vClipRadii.xy); + d_radii_b = distance_to_ellipse(clip_relative_pos, vClipRadii.zw); ++#ifdef WR_FEATURE_SUPERELLIPSE + } else { + clip_relative_pos = abs(clip_relative_pos) - data.shape_offset; + d_radii_a = distance_to_superellipse(clip_relative_pos - vClipOffsets.xy, vClipRadii.xy, data.shape); @@ -523,6 +243,7 @@ index 066f63f62a..f9998badd6 100644 + vec2 included_region = data.radii - data.widths - clip_relative_pos; + d_radii_b = max(d_radii_b, -min(included_region.x, included_region.y)); + } ++#endif + float d_radii = max(d_radii_a, -d_radii_b); d = max(d, d_radii); @@ -533,7 +254,7 @@ index 066f63f62a..f9998badd6 100644 clip_relative_pos, style.x, vColor00, -@@ -398,6 +465,7 @@ void main(void) { +@@ -398,6 +487,7 @@ void main(void) { aa_range ); color1 = evaluate_color_for_style_in_corner( @@ -542,7 +263,7 @@ index 066f63f62a..f9998badd6 100644 style.y, vColor10, diff --git a/gfx/wr/webrender/res/cs_border_solid.glsl b/gfx/wr/webrender/res/cs_border_solid.glsl -index 90584346a4..a3ff0e887e 100644 +index 90584346a4..05d314571e 100644 --- a/gfx/wr/webrender/res/cs_border_solid.glsl +++ b/gfx/wr/webrender/res/cs_border_solid.glsl @@ -8,6 +8,10 @@ @@ -556,32 +277,44 @@ index 90584346a4..a3ff0e887e 100644 // For edges, the colors are the same. For corners, these // are the colors of each edge making up the corner. flat varying mediump vec4 vColor0; -@@ -29,6 +33,7 @@ flat varying highp vec4 vClipCenter_Sign; +@@ -29,6 +33,9 @@ flat varying highp vec4 vClipCenter_Sign; // An outer and inner elliptical radii for border // corner clipping. flat varying highp vec4 vClipRadii; ++#ifdef WR_FEATURE_SUPERELLIPSE +flat varying highp vec4 vClipOffsets; ++#endif // Position, scale, and radii of horizontally and vertically adjacent corner clips. flat varying highp vec4 vHorizontalClipCenter_Sign; -@@ -68,6 +73,7 @@ vec2 get_outer_corner_scale(int segment) { +@@ -68,9 +75,10 @@ vec2 get_outer_corner_scale(int segment) { void main(void) { BorderInstanceGpuData data = fetch_gpu_data(aGpuDataAddress); + vGpuDataAddress.x = aGpuDataAddress; int segment = aFlags & 0xff; - bool do_aa = ((aFlags >> 24) & 0xf0) != 0; -@@ -96,10 +102,20 @@ void main(void) { +- bool do_aa = ((aFlags >> 24) & 0xf0) != 0; ++ bool do_aa = ((aFlags >> 28) & 0x1) != 0; + + vec2 outer_scale = get_outer_corner_scale(segment); + vec2 size = data.rect.zw - data.rect.xy; +@@ -96,10 +104,28 @@ void main(void) { vColor0 = data.color0; vColor1 = data.color1; -- vClipCenter_Sign = vec4(outer + clip_sign * data.radii, clip_sign); ++#ifdef WR_FEATURE_SUPERELLIPSE + vClipCenter_Sign = vec4(outer + clip_sign * (data.radii + data.shape_offset), clip_sign); ++#else + vClipCenter_Sign = vec4(outer + clip_sign * data.radii, clip_sign); ++#endif vClipRadii = vec4(data.radii, max(data.radii - data.widths, 0.0)); ++#ifdef WR_FEATURE_SUPERELLIPSE + vClipOffsets = vec4(0.0); ++#endif vColorLine = vec4(outer, data.widths.y * -clip_sign.y, data.widths.x * clip_sign.x); ++#ifdef WR_FEATURE_SUPERELLIPSE + if (data.shape != 1.0) + { + vec2 reference_radii = (data.radii == vec2(0.0)) ? vec2(0.0) : data.radii + data.inset; @@ -590,11 +323,12 @@ index 90584346a4..a3ff0e887e 100644 + vClipOffsets = vec4(contour1.xy, contour2.xy); + vClipRadii = vec4(contour1.zw, contour2.zw); + } ++#endif + vec2 horizontal_clip_sign = vec2(-clip_sign.x, clip_sign.y); vHorizontalClipCenter_Sign = vec4(aClipParams1.xy + horizontal_clip_sign * aClipParams1.zw, -@@ -118,6 +134,8 @@ void main(void) { +@@ -118,6 +144,8 @@ void main(void) { #ifdef WR_FRAGMENT_SHADER void main(void) { @@ -603,7 +337,7 @@ index 90584346a4..a3ff0e887e 100644 float aa_range = compute_aa_range(vPos); bool do_aa = vMixColors.x != MIX_NO_AA; -@@ -137,8 +155,22 @@ void main(void) { +@@ -137,8 +165,26 @@ void main(void) { float d = -1.0; if (in_clip_region) { @@ -612,9 +346,12 @@ index 90584346a4..a3ff0e887e 100644 + float d_radii_a; + float d_radii_b; + ++#ifdef WR_FEATURE_SUPERELLIPSE + if (data.shape == 1.0) { ++#endif + d_radii_a = distance_to_ellipse(clip_relative_pos, vClipRadii.xy); + d_radii_b = distance_to_ellipse(clip_relative_pos, vClipRadii.zw); ++#ifdef WR_FEATURE_SUPERELLIPSE + } else { + clip_relative_pos = abs(clip_relative_pos) - data.shape_offset; + d_radii_a = distance_to_superellipse(clip_relative_pos - vClipOffsets.xy, vClipRadii.xy, data.shape); @@ -624,28 +361,19 @@ index 90584346a4..a3ff0e887e 100644 + vec2 included_region = data.radii - data.widths - clip_relative_pos; + d_radii_b = max(d_radii_b, -min(included_region.x, included_region.y)); + } ++#endif + d = max(d_radii_a, -d_radii_b); } diff --git a/gfx/wr/webrender/res/ellipse.glsl b/gfx/wr/webrender/res/ellipse.glsl -index 4c33057214..7ce1bcacb4 100644 +index cb7d5d89af..7ce1bcacb4 100644 --- a/gfx/wr/webrender/res/ellipse.glsl +++ b/gfx/wr/webrender/res/ellipse.glsl -@@ -17,6 +17,184 @@ vec2 inverse_radii(vec2 radii) { - return 1.0 / max(radii, 1.0e-3); - } +@@ -27,9 +27,27 @@ float compute_superellipse_half_corner(float shape) { + float n = exp2(shape); + float convex_half_corner = pow(0.5, 1.0 / n); -+// Spec: https://drafts.csswg.org/css-borders/#normalized-superellipse-half-corner -+// -+// Contrary to the spec, the symmetric case (shape < 0) is handled in -+// compute_contoured_superellipse() instead of in this function. -+float compute_superellipse_half_corner(float shape) { -+ shape = min(1.0, abs(shape)); -+ -+ float n = exp2(shape); -+ float convex_half_corner = pow(0.5, 1.0 / n); -+ + // Ensure the curve tends towards 0.75 (which will produce axis-aligned + // normals in compute_superellipse_normals()) when shape goes to 1 (round). + // This makes the curve continuous from subellipses (shape < 1) to @@ -656,9 +384,9 @@ index 4c33057214..7ce1bcacb4 100644 + float falloff = shape * shape; // quadratic falloff + convex_half_corner += delta * falloff; + -+ return convex_half_corner; -+} -+ + return convex_half_corner; + } + +// Super cheap approximation of the function above: the delta with quadratic +// falloff actually makes it almost locally linear for shape values between +// zero and one. @@ -667,94 +395,15 @@ index 4c33057214..7ce1bcacb4 100644 + return 0.25 * shape + 0.5; +} + -+#if 0 -+ -+// Alternate method to find the normals of a superellipse -+// at the start and end points or the curve. -+// -+// Using implicit differentiation [1, 2], we find that for the superellipse -+// defined with this implicit formula: -+// -+// S(x, y) = (x/a)^n + (y/a)^n - 1 = 0 -+// -+// where (a, b) are the radii and n is the order, -+// we find that the gradient at the point (x0, y0) is: -+// -+// dy/dx = -b^n * x0^(n-1) / a^n * y0^(n-1) -+// -+// Introducing an intermediate variable q, this simplifies to: -+// -+// let q = [(x0 / a)^(n-1); (y0 / b)^(n-1)] -+// -+// dy/dx = -b*qx / a*qy -+// -+// And, symmetrically: -+// -+// dx/dy = -a*qy / b*qx -+// -+// Using both derivatives is useful for numerical stabillty: as the two -+// normals (at start and end) tend towards orthogonality when the exponent -+// approches 2 (= circle), one of them is likely to go towards infinity. -+// -+// The exact start and end of the curve are discontinuities, so we pick -+// points that are technically not on the curve but very close to start -+// and end points, at a distance of 5% of the radii: -+// -+// Start gradient evaluated at: (0.05*a, b) -+// End gradient evaluated at: (a, 0.05*b) -+// -+// Using ratios of the radii simplifies the computation further: -+// -+// let x0 = 0.05*a -+// let y0 = b -+// -+// qx = (x0 / a)^(n-1) -+// = (0.05*a / a)^(n-1) -+// = 0.05^(n-1) -+// -+// qy = (b / b)^(n-1) -+// = 1 -+// -+// The same simplification applies symmetrically for the dx/dy calculation -+// at the end point. -+// -+// In the final form, we can compute a single q value (instead of a vector), -+// and derive the gradients from this and the radii ratios (see code below): -+// -+// let q = 0.05^(n-1) -+// dy/dx = -q * b / a -+// dx/dy = -q * a / b -+// -+// The tangents are then defined from the gradients: -+// let t1 = (1, dy/dx) -+// let t2 = (dx/dy, 1) -+// -+// And the normals are obtained by a simple 90° rotation of the tangents: -+// let n1 = (t1.y, -t1.x) = (dy/dx, -1) -+// let n2 = (-t2.y, t2.x) = (-1, dx/dy) -+// -+// Returns start and end normals in (x, y) and (z, w) respectively -+// -+// [1] https://www.kristakingmath.com/blog/equation-of-the-tangent-line-with-implicit-differentiation -+// [2] https://www.khanacademy.org/math/ap-calculus-bc/bc-differentiation-2-new/bc-3-2/v/implicit-differentiation-1 -+// -+// Note: the points where we compute the gradients are technically not -+// on the curve. In practice, we found that it is not an issue unless the -+// curve is concave (n < 1). As the spec defines this case to be implemented -+// as a geometrical symmetry from the convex case (n > 1), it is never encountered -+// in this implementation. -+// -+// For reference, here is the full form that reprojects the point on the curve, -+// given a fixed x0: -+// -+// qx = (x0 / a)^(n-1) # Same as before -+// qy = (1 - x0 / a * qx)^((n-1) / n) # Project the y coord on the curve -+// -+// Which yields: -+// -+// dy/dx = -b / a * qx * (1 - x0 / a * qx)^((1-n) / n) # Careful, exponent flipped -+// -+vec4 compute_superellipse_normals(vec2 radii, float shape) { + #if 0 + + // Alternate method to find the normals of a superellipse +@@ -118,8 +136,15 @@ float compute_superellipse_half_corner(float shape) { + // dy/dx = -b / a * qx * (1 - x0 / a * qx)^((1-n) / n) # Careful, exponent flipped + // + vec4 compute_superellipse_normals(vec2 radii, float shape) { +- float n = exp2(abs(shape)); +- float q = pow(0.05, n - 1.0); + shape = min(1.0, abs(shape)); + + float n = exp2(shape); @@ -764,1660 +413,960 @@ index 4c33057214..7ce1bcacb4 100644 + // of 1 and above, the normals should always be axis-aligned) + float damping_factor = (1.0 - shape * shape); + float q = pow(0.05 * damping_factor, n - 1.0); -+ -+ // x: dy/dx at (0.05 * radii.x, radii.y) -+ // y: dx/dy at (radii.x, 0.05 * radii.y) -+ vec2 grad = -q * radii.yx / max(radii.xy, 0.1); -+ -+ vec2 n1 = normalize(vec2(grad.x, -1.0)); -+ vec2 n2 = normalize(vec2(-1.0, grad.y)); -+ -+ return vec4(n1, n2); -+} -+ -+#else -+ -+// Spec: https://drafts.csswg.org/css-borders/#contour-path -+// -+// Returns start and end normals in (x, y) and (z, w) respectively -+vec4 compute_superellipse_normals(vec2 radii, float shape) { + + // x: dy/dx at (0.05 * radii.x, radii.y) + // y: dx/dy at (radii.x, 0.05 * radii.y) +@@ -137,8 +162,7 @@ vec4 compute_superellipse_normals(vec2 radii, float shape) { + // + // Returns start and end normals in (x, y) and (z, w) respectively + vec4 compute_superellipse_normals(vec2 radii, float shape) { +- +- float convex_half_corner = compute_superellipse_half_corner(shape); + float convex_half_corner = compute_superellipse_half_corner_approx(shape); -+ vec2 tangent = vec2(0.5 - 2.0 * convex_half_corner, 2.0 * convex_half_corner - 1.5); -+ -+ // Avoid dividing by zero -+ radii = max(radii, 0.1); -+ -+ vec2 n1 = normalize(radii.yx * tangent.yx); -+ vec2 n2 = normalize(radii.yx * tangent.xy); -+ -+ return vec4(n1, n2); -+} -+ -+#endif -+ -+// Spec: https://drafts.csswg.org/css-borders/#contour-path -+// -+// Returns an offset (x,y), and new radii (zw) -+vec4 compute_contoured_superellipse(vec2 radii, float shape, vec2 inset) { -+ vec4 normals = compute_superellipse_normals(radii, shape); -+ -+ // Flip x/y for symmetry -+ if (shape < 0.0) { -+ normals = normals.zwxy; -+ } -+ -+ // Compute delta by extruding inset along the normals -+ vec4 delta = normals * inset.yyxx; -+ -+ vec2 offset = vec2(delta.x, delta.w); -+ vec2 contourRadii = max(radii + vec2(delta.z, delta.y) - offset, 0.1); -+ return vec4(offset, contourRadii); -+} -+ - #ifdef WR_FRAGMENT_SHADER + vec2 tangent = vec2(0.5 - 2.0 * convex_half_corner, 2.0 * convex_half_corner - 1.5); - // One iteration of Newton's method on the 2D equation of an ellipse: -@@ -134,8 +312,7 @@ float distance_to_superellipse_approx(vec2 p, vec2 inv_radii, float k) { - // +x,+y projection so the line distance matches geometry inside - // the box (where p has negative components). - if (k == 0.0) { -- vec2 pp = max(p, vec2(0.0)); -- float g = dot(pp, inv_radii) - 1.0; -+ float g = dot(p, inv_radii) - 1.0; - return g * inversesqrt(dot(inv_radii, inv_radii)); - } - -@@ -159,6 +336,21 @@ float distance_to_superellipse_approx(vec2 p, vec2 inv_radii, float k) { - // Divide by radii: normalize the position to [0, 1] - vec2 q = p * inv_radii; - -+ // If position is outside the shaped region, extend along the curve tangents -+ if (any(lessThanEqual(q, vec2(0.0)))) { -+ vec2 radii = 1.0 / inv_radii; -+ -+ vec4 normals = compute_superellipse_normals(radii, k); -+ -+ float d1 = dot(p, normals.xy) - normals.y * radii.y; -+ float d2 = dot(p, normals.zw) - normals.z * radii.x; -+ if (k >= 0.0) { -+ return max(-d1, -d2); -+ } else { -+ return min(d1, d2); -+ } -+ } -+ - // Compute the superellipse function - vec2 qn = pow(q, vec2(n)); - qn = clamp(qn, 0.0, 1.0e3); // Clamp to avoid numerical overflow -@@ -183,9 +375,21 @@ float distance_to_superellipse_approx(vec2 p, vec2 inv_radii, float k) { - } - - float distance_to_superellipse(vec2 p, vec2 radii, float k) { -+ if (any(lessThan(radii, vec2(1.0)))) { -+ return -1.0; -+ } -+ - return distance_to_superellipse_approx(p, inverse_radii(radii), k); - } - -+float distance_to_shaped_corner(vec2 pos, vec2 inv_radii, float shape) { -+ if (shape == 1.0) { -+ return distance_to_ellipse_approx(pos, inv_radii, 1.0); -+ } else { -+ return distance_to_superellipse_approx(pos, inv_radii, shape); -+ } -+} -+ - // Same as distance_to_rounded_rect but with per-corner shape values. - float distance_to_shaped_rect( - vec2 pos, -@@ -194,56 +398,34 @@ float distance_to_shaped_rect( - vec4 center_radius_br, - vec4 center_radius_bl, - vec4 rect_bounds, -- vec4 corner_shapes -+ vec4 corner_shapes, -+ vec4 clamped_inset // always negative (i.e outset-only) - ) { -- vec2 corner_p = vec2(1.0e-6); -- vec2 corner_inv = vec2(1.0); -- float corner_k = 1.0; -- -- float in_corner = 0.0; -+ float d = signed_distance_rect(pos, rect_bounds.xy, rect_bounds.zw); - - vec2 p_tl = center_radius_tl.xy - pos; - vec2 p_tr = (center_radius_tr.xy - pos) * vec2(-1.0, 1.0); - vec2 p_br = pos - center_radius_br.xy; - vec2 p_bl = (center_radius_bl.xy - pos) * vec2(1.0, -1.0); - -- if (p_tl.x >= 0.0 && p_tl.y >= 0.0) { -- corner_p = p_tl; -- corner_inv = center_radius_tl.zw; -- corner_k = corner_shapes.x; -- in_corner = 1.0; -- } -- if (p_tr.x >= 0.0 && p_tr.y >= 0.0) { -- corner_p = p_tr; -- corner_inv = center_radius_tr.zw; -- corner_k = corner_shapes.y; -- in_corner = 1.0; -- } -- if (p_br.x >= 0.0 && p_br.y >= 0.0) { -- corner_p = p_br; -- corner_inv = center_radius_br.zw; -- corner_k = corner_shapes.z; -- in_corner = 1.0; -- } -- if (p_bl.x >= 0.0 && p_bl.y >= 0.0) { -- corner_p = p_bl; -- corner_inv = center_radius_bl.zw; -- corner_k = corner_shapes.w; -- in_corner = 1.0; -- } -- -- float d_corner; -- if (in_corner == 0.0) { -- // Not in any corner. let the rect SDF decide. Use a strongly -- // negative value so max() defers to the rect. -- d_corner = -1.0e6; -- } else if (corner_k == 1.0) { -- d_corner = distance_to_ellipse_approx(corner_p, corner_inv, 1.0); -- } else { -- d_corner = distance_to_superellipse_approx(corner_p, corner_inv, corner_k); -+ vec2 i_tl = (corner_shapes.x == 1.0) ? vec2(0.0) : clamped_inset.xw; -+ vec2 i_tr = (corner_shapes.y == 1.0) ? vec2(0.0) : clamped_inset.xy; -+ vec2 i_br = (corner_shapes.z == 1.0) ? vec2(0.0) : clamped_inset.zy; -+ vec2 i_bl = (corner_shapes.w == 1.0) ? vec2(0.0) : clamped_inset.zw; -+ -+ if (p_tl.x >= i_tl.x && p_tl.y >= i_tl.y) { -+ d = max(d, distance_to_shaped_corner(p_tl, center_radius_tl.zw, corner_shapes.x)); -+ } -+ if (p_tr.x >= i_tr.x && p_tr.y >= i_tr.y) { -+ d = max(d, distance_to_shaped_corner(p_tr, center_radius_tr.zw, corner_shapes.y)); -+ } -+ if (p_br.x >= i_br.x && p_br.y >= i_br.y) { -+ d = max(d, distance_to_shaped_corner(p_br, center_radius_br.zw, corner_shapes.z)); -+ } -+ if (p_bl.x >= i_bl.x && p_bl.y >= i_bl.y) { -+ d = max(d, distance_to_shaped_corner(p_bl, center_radius_bl.zw, corner_shapes.w)); - } - -- return max(d_corner, -- signed_distance_rect(pos, rect_bounds.xy, rect_bounds.zw)); -+ return d; - } - #endif -diff --git a/gfx/wr/webrender/res/gpu_buffer.glsl b/gfx/wr/webrender/res/gpu_buffer.glsl -index b6f55a2c02..c649ab1807 100644 ---- a/gfx/wr/webrender/res/gpu_buffer.glsl -+++ b/gfx/wr/webrender/res/gpu_buffer.glsl -@@ -44,6 +44,17 @@ vec4[5] fetch_from_gpu_buffer_5f_direct(ivec2 uv) { - ); - } - -+vec4[6] fetch_from_gpu_buffer_6f_direct(ivec2 uv) { -+ return vec4[6]( -+ TEXEL_FETCH(sGpuBufferF, uv, 0, ivec2(0, 0)), -+ TEXEL_FETCH(sGpuBufferF, uv, 0, ivec2(1, 0)), -+ TEXEL_FETCH(sGpuBufferF, uv, 0, ivec2(2, 0)), -+ TEXEL_FETCH(sGpuBufferF, uv, 0, ivec2(3, 0)), -+ TEXEL_FETCH(sGpuBufferF, uv, 0, ivec2(4, 0)), -+ TEXEL_FETCH(sGpuBufferF, uv, 0, ivec2(5, 0)) -+ ); -+} -+ - vec4 fetch_from_gpu_buffer_1f(HIGHP_FS_ADDRESS int address) { - ivec2 uv = get_gpu_buffer_uv(address); - return texelFetch(sGpuBufferF, uv, 0); -@@ -69,6 +80,11 @@ vec4[5] fetch_from_gpu_buffer_5f(HIGHP_FS_ADDRESS int address) { - return fetch_from_gpu_buffer_5f_direct(uv); - } - -+vec4[6] fetch_from_gpu_buffer_6f(HIGHP_FS_ADDRESS int address) { -+ ivec2 uv = get_gpu_buffer_uv(address); -+ return fetch_from_gpu_buffer_6f_direct(uv); -+} -+ - vec4 fetch_from_gpu_buffer_1f_direct(ivec2 uv) { - return texelFetch(sGpuBufferF, uv, 0); - } + // Avoid dividing by zero diff --git a/gfx/wr/webrender/res/ps_quad_box_shadow.glsl b/gfx/wr/webrender/res/ps_quad_box_shadow.glsl -index 3fb233cb17..baf1d92e7c 100644 +index 1153b21193..73514b7c4e 100644 --- a/gfx/wr/webrender/res/ps_quad_box_shadow.glsl +++ b/gfx/wr/webrender/res/ps_quad_box_shadow.glsl -@@ -181,7 +181,8 @@ vec4 pattern_fragment(vec4 base_color) { - vec4(c_br, elem_shape.z == 1.0 ? inverse_radii_squared(r_br) : inverse_radii(r_br)), - vec4(c_bl, elem_shape.w == 1.0 ? inverse_radii_squared(r_bl) : inverse_radii(r_bl)), - elem_bounds, -- elem_shape -+ elem_shape, -+ vec4(0.0) +@@ -51,7 +51,9 @@ flat varying highp vec4 vElemCenter_Radius_TR; + flat varying highp vec4 vElemCenter_Radius_BR; + flat varying highp vec4 vElemCenter_Radius_BL; + ++#ifdef WR_FEATURE_SUPERELLIPSE + flat varying highp vec4 vElemShape; ++#endif + + #ifdef WR_VERTEX_SHADER + +@@ -62,7 +64,9 @@ void pattern_vertex(PrimitiveInfo info) { + vec4 data3 = fetch_from_gpu_buffer_1f(info.pattern_input.x + 3); + vec4 data4 = fetch_from_gpu_buffer_1f(info.pattern_input.x + 4); + vec4 data5 = fetch_from_gpu_buffer_1f(info.pattern_input.x + 5); ++#ifdef WR_FEATURE_SUPERELLIPSE + vec4 data6 = fetch_from_gpu_buffer_1f(info.pattern_input.x + 6); ++#endif + + vec2 alloc_size = data0.xy; + vec2 dest_rect_size = data0.zw; +@@ -86,7 +90,7 @@ void pattern_vertex(PrimitiveInfo info) { + // allocation is rounded up from content_device_size, so this edge always lies + // inside the entry; using it keeps the mapping stable to sub-texel precision as + // the blur animates (bug 2002194). +- vec2 content_device_size = data6.xy; ++ vec2 content_device_size = data5.xy; + vec2 texture_size = vec2(TEX_SIZE(sColor0)); + vec2 uv_p0 = info.segment.uv_rect.p0; + vec2 uv_p1 = uv_p0 + content_device_size; +@@ -112,7 +116,9 @@ void pattern_vertex(PrimitiveInfo info) { + vElemCenter_Radius_BR = vec4(elem_p1 - r_br, r_br); + vElemCenter_Radius_BL = vec4(elem_p0.x + r_bl.x, elem_p1.y - r_bl.y, r_bl); + +- vElemShape = data5; ++#ifdef WR_FEATURE_SUPERELLIPSE ++ vElemShape = data6; ++#endif + } + + #endif +@@ -172,10 +178,11 @@ vec4 pattern_fragment(vec4 base_color) { + // Reconstruct the element rect bounds from the TL and BR corner data. + vec4 elem_bounds = vec4(c_tl - r_tl, c_br + r_br); + +- vec4 elem_shape = vElemShape; +- + float elem_dist; ++#ifdef WR_FEATURE_SUPERELLIPSE ++ vec4 elem_shape = vElemShape; + if (elem_shape == vec4(1.0)) { ++#endif + elem_dist = distance_to_rounded_rect( + local_pos, + elem_plane_tl, vec4(c_tl, inverse_radii_squared(r_tl)), +@@ -184,6 +191,7 @@ vec4 pattern_fragment(vec4 base_color) { + elem_plane_bl, vec4(c_bl, inverse_radii_squared(r_bl)), + elem_bounds + ); ++#ifdef WR_FEATURE_SUPERELLIPSE + } else { + elem_dist = distance_to_shaped_rect( + local_pos, +@@ -196,6 +204,7 @@ vec4 pattern_fragment(vec4 base_color) { + vec4(0.0) ); } ++#endif + // Outset (inset=0): dist < 0 = inside element → should be clipped out → use -elem_dist. + // Inset (inset=1): dist < 0 = inside element → should be kept → use elem_dist. diff --git a/gfx/wr/webrender/res/ps_quad_mask.glsl b/gfx/wr/webrender/res/ps_quad_mask.glsl -index 47e79f7c4d..e065fff4f7 100644 +index 6949a343a1..351f300fbc 100644 --- a/gfx/wr/webrender/res/ps_quad_mask.glsl +++ b/gfx/wr/webrender/res/ps_quad_mask.glsl -@@ -22,6 +22,7 @@ flat varying highp vec4 vClipPlane_B; +@@ -21,8 +21,10 @@ flat varying highp vec4 vClipPlane_A; + flat varying highp vec4 vClipPlane_B; flat varying highp vec4 vClipPlane_C; ++#ifdef WR_FEATURE_SUPERELLIPSE flat varying highp vec4 vClipShape; -+flat varying highp vec4 vClipClampedInset; + flat varying highp vec4 vClipClampedInset; ++#endif #endif flat varying highp vec2 vClipMode; -@@ -42,6 +43,7 @@ struct Clip { +@@ -42,8 +44,10 @@ struct Clip { + vec4 radii_top; vec4 radii_bottom; ++#ifdef WR_FEATURE_SUPERELLIPSE vec4 shape; -+ vec4 inset; + vec4 inset; ++#endif #endif float mode; int space; -@@ -58,23 +60,30 @@ Clip fetch_clip(int index) { +@@ -60,18 +64,25 @@ Clip fetch_clip(int index) { clip.radii = texels[1]; clip.mode = texels[2].x; #else -- vec4 texels[5] = fetch_from_gpu_buffer_5f(index); -+ vec4 texels[6] = fetch_from_gpu_buffer_6f(index); ++#ifdef WR_FEATURE_SUPERELLIPSE + vec4 texels[6] = fetch_from_gpu_buffer_6f(index); ++#else ++ vec4 texels[4] = fetch_from_gpu_buffer_4f(index); ++#endif clip.rect = RectWithEndpoint(texels[0].xy, texels[0].zw); clip.radii_top = texels[1]; clip.radii_bottom = texels[2]; clip.mode = texels[3].x; ++#ifdef WR_FEATURE_SUPERELLIPSE clip.shape = texels[4]; -+ clip.inset = texels[5]; + clip.inset = texels[5]; ++#endif #endif return clip; } --vec4 precalc_corner(vec2 center, vec2 radii, float k) { -+vec4 precalc_corner(vec2 center, vec2 radii, vec2 inset, vec2 clip_sign, float k) { ++#ifdef WR_FEATURE_SUPERELLIPSE + vec4 precalc_corner(vec2 center, vec2 radii, vec2 inset, vec2 clip_sign, float k) { if (k == 1.0) { // round/ellipse corner, precalc the ellipse parameters - return vec4(center, inverse_radii_squared(radii)); - } else { - // superellipse, precalc the superellipse parameters -+ if (k < 1.0) { -+ vec2 reference_radii = (radii == vec2(0.0)) ? vec2(0.0) : radii + inset; -+ vec4 offset_radii = compute_contoured_superellipse(reference_radii, k, inset); -+ center += offset_radii.xy * clip_sign; -+ radii = offset_radii.zw; -+ } +@@ -87,6 +98,7 @@ vec4 precalc_corner(vec2 center, vec2 radii, vec2 inset, vec2 clip_sign, float k return vec4(center, inverse_radii(radii)); } } -@@ -116,20 +125,28 @@ void pattern_vertex(PrimitiveInfo prim_info) { ++#endif + void pattern_vertex(PrimitiveInfo prim_info) { + +@@ -123,6 +135,7 @@ void pattern_vertex(PrimitiveInfo prim_info) { + vec2 r_br = clip.radii_bottom.zw; + vec2 r_bl = clip.radii_bottom.xy; + ++#ifdef WR_FEATURE_SUPERELLIPSE vClipCenter_Radius_TL = precalc_corner(clip.rect.p0 + r_tl, r_tl, -+ clip.inset.wx, -+ vec2(-1.0, -1.0), - clip.shape.x); - - vClipCenter_Radius_TR = precalc_corner(vec2(clip.rect.p1.x - r_tr.x, - clip.rect.p0.y + r_tr.y), - r_tr, -+ clip.inset.yx, -+ vec2(1.0, -1.0), - clip.shape.y); - - vClipCenter_Radius_BR = precalc_corner(clip.rect.p1 - r_br, - r_br, -+ clip.inset.yz, -+ vec2(1.0, 1.0), - clip.shape.z); - - vClipCenter_Radius_BL = precalc_corner(vec2(clip.rect.p0.x + r_bl.x, - clip.rect.p1.y - r_bl.y), - r_bl, -+ clip.inset.wz, -+ vec2(-1.0, 1.0), + clip.inset.wx, +@@ -148,6 +161,21 @@ void pattern_vertex(PrimitiveInfo prim_info) { + clip.inset.wz, + vec2(-1.0, 1.0), clip.shape.w); ++#else ++ vClipCenter_Radius_TL = vec4(clip.rect.p0 + r_tl, ++ inverse_radii_squared(r_tl)); ++ ++ vClipCenter_Radius_TR = vec4(clip.rect.p1.x - r_tr.x, ++ clip.rect.p0.y + r_tr.y, ++ inverse_radii_squared(r_tr)); ++ ++ vClipCenter_Radius_BR = vec4(clip.rect.p1 - r_br, ++ inverse_radii_squared(r_br)); ++ ++ vClipCenter_Radius_BL = vec4(clip.rect.p0.x + r_bl.x, ++ clip.rect.p1.y - r_bl.y, ++ inverse_radii_squared(r_bl)); ++#endif // We need to know the half-spaces of the corners separate from the center -@@ -156,6 +173,7 @@ void pattern_vertex(PrimitiveInfo prim_info) { + // and radius. We compute a point that falls on the diagonal (which is just +@@ -172,9 +200,11 @@ void pattern_vertex(PrimitiveInfo prim_info) { + vClipPlane_B = vec4(tr.y, tr.z, br.x, br.y); vClipPlane_C = vec4(br.z, bl.x, bl.y, bl.z); ++#ifdef WR_FEATURE_SUPERELLIPSE vClipShape = clip.shape; -+ vClipClampedInset = min(clip.inset, 0.0); + vClipClampedInset = min(clip.inset, 0.0); #endif ++#endif } -@@ -210,7 +228,8 @@ vec4 pattern_fragment(vec4 _base_color) { - vClipCenter_Radius_BR, + #endif +@@ -207,7 +237,9 @@ vec4 pattern_fragment(vec4 _base_color) { + vec3 plane_bl = vec3(vClipPlane_C.y, vClipPlane_C.z, vClipPlane_C.w); + + float dist; ++#ifdef WR_FEATURE_SUPERELLIPSE + if (vClipShape == vec4(1.0)) { ++#endif + dist = distance_to_rounded_rect( + clip_local_pos, + plane_tl, +@@ -220,6 +252,7 @@ vec4 pattern_fragment(vec4 _base_color) { vClipCenter_Radius_BL, - vTransformBounds, -- vClipShape -+ vClipShape, -+ vClipClampedInset + vTransformBounds + ); ++#ifdef WR_FEATURE_SUPERELLIPSE + } else { + dist = distance_to_shaped_rect( + clip_local_pos, +@@ -232,6 +265,7 @@ vec4 pattern_fragment(vec4 _base_color) { + vClipClampedInset ); } ++#endif #endif + + // Compute AA for the given dist and range. +@@ -286,8 +320,26 @@ void swgl_drawSpanR8() { + vec4 clip_dist = + mix(clip_rect, clip_rect.zwxy, lessThan(local_step, vec2(0.0)).xyxy) + - local_pos0.xyxy; ++ ++ // Half-width, in local units, of the band around an edge over which ++ // distance_aa() produces partial coverage. ++ float aa_band = 0.5 * recip(aa_range); ++ // An axis the span does not step along has the same distance to its two ++ // edges for every pixel of the span, so the inside/outside test below ++ // cannot resolve an edge that runs parallel to the span and coverage ends ++ // up quantized to whole spans. Grow the rect by the AA band so that a span ++ // landing inside the band counts as inside, which leaves the corner ++ // selection below unchanged, and remember that the span is only partially ++ // covered so the opaque run can be dropped further down. ++ // `inset` is the distance from the span to the nearer of an axis' two ++ // edges, positive when the span is between them. ++ vec2 inset = min(-clip_dist.xy, clip_dist.zw); ++ bvec2 flat_axis = equal(local_step, vec2(0.0)); ++ bvec2 partial = lessThan(inset, vec2(aa_band)); ++ bool aa_span = (flat_axis.x && partial.x) || (flat_axis.y && partial.y); ++ + clip_dist = +- mix(1.0e6 * step(0.0, clip_dist), ++ mix(1.0e6 * step(0.0, clip_dist - vec4(aa_band, aa_band, -aa_band, -aa_band)), + clip_dist * recip(local_step).xyxy, + notEqual(local_step, vec2(0.0)).xyxy); + +@@ -342,9 +394,9 @@ void swgl_drawSpanR8() { + #else + // The span fast path only handles elliptical corners. For superellipse + // shapes, bail out and let SWGL fall back to the fragment shader. +- if (vClipShape != vec4(1.0)) { ++ #ifdef WR_FEATURE_SUPERELLIPSE + return; +- } ++ #endif + + // Unpack the corner half-spaces packed into vClipPlane_A/B/C. + vec3 plane_tl = vec3(vClipPlane_A.x, vClipPlane_A.y, vClipPlane_A.z); +@@ -372,6 +424,12 @@ void swgl_drawSpanR8() { + aa_start -= aa_margin; + aa_end += aa_margin; + ++ if (aa_span) { ++ // No pixel of the span is fully covered, so collapse the opaque run and ++ // let the anti-aliased runs on either side of it cover the whole span. ++ opaque_end = min(opaque_end, opaque_start); ++ } ++ + ivec4 steps = ivec4(clamp( + swgl_SpanLength - + swgl_StepSize * +diff --git a/gfx/wr/webrender/src/batch.rs b/gfx/wr/webrender/src/batch.rs +index e137afd807..eb5de7a9fb 100644 +--- a/gfx/wr/webrender/src/batch.rs ++++ b/gfx/wr/webrender/src/batch.rs +@@ -1225,9 +1225,11 @@ impl BatchBuilder { + #[cfg_attr(feature = "replay", derive(Deserialize))] + pub struct ClipMaskInstanceList { + pub mask_instances_fast: FrameVec, ++ pub mask_instances_superellipse: FrameVec, + pub mask_instances_slow: FrameVec, + + pub mask_instances_fast_with_scissor: FastHashMap>, ++ pub mask_instances_superellipse_with_scissor: FastHashMap>, + pub mask_instances_slow_with_scissor: FastHashMap>, + + pub image_mask_instances: FastHashMap>, +@@ -1238,8 +1240,10 @@ impl ClipMaskInstanceList { + pub fn new(memory: &FrameMemory) -> Self { + ClipMaskInstanceList { + mask_instances_fast: memory.new_vec(), ++ mask_instances_superellipse: memory.new_vec(), + mask_instances_slow: memory.new_vec(), + mask_instances_fast_with_scissor: FastHashMap::default(), ++ mask_instances_superellipse_with_scissor: FastHashMap::default(), + mask_instances_slow_with_scissor: FastHashMap::default(), + image_mask_instances: FastHashMap::default(), + image_mask_instances_with_scissor: FastHashMap::default(), +@@ -1251,16 +1255,20 @@ impl ClipMaskInstanceList { + // a new member is added. + let ClipMaskInstanceList { + mask_instances_fast, ++ mask_instances_superellipse, + mask_instances_slow, + mask_instances_fast_with_scissor, ++ mask_instances_superellipse_with_scissor, + mask_instances_slow_with_scissor, + image_mask_instances, + image_mask_instances_with_scissor, + } = self; + + mask_instances_fast.is_empty() ++ && mask_instances_superellipse.is_empty() + && mask_instances_slow.is_empty() + && mask_instances_fast_with_scissor.is_empty() ++ && mask_instances_superellipse_with_scissor.is_empty() + && mask_instances_slow_with_scissor.is_empty() + && image_mask_instances.is_empty() + && image_mask_instances_with_scissor.is_empty() diff --git a/gfx/wr/webrender/src/border.rs b/gfx/wr/webrender/src/border.rs -index dc456e85c3..2a591da08f 100644 +index eb9381f7d1..b6158fde38 100644 --- a/gfx/wr/webrender/src/border.rs +++ b/gfx/wr/webrender/src/border.rs -@@ -55,6 +55,8 @@ pub struct BorderSegmentCacheKey { - pub size: LayoutSizeAu, - pub radius: LayoutSizeAu, - pub shape: u32, -+ pub shape_offset: LayoutSizeAu, -+ pub inset: LayoutSizeAu, - pub side0: BorderSideAu, - pub side1: BorderSideAu, - pub segment: BorderSegment, -@@ -560,22 +562,79 @@ pub fn create_border_segments( - widths.left - overlap.width / 2.0, - ); +@@ -857,10 +857,13 @@ fn add_segment( + v_adjacent_corner_radius: DeviceSize, + gpu_buffer_builder: &mut GpuBufferBuilderF, + ) { ++ let superellipse = shape != 1.0; ++ + let base_flags = (segment as i32) | + ((style0 as i32) << 8) | + ((style1 as i32) << 16) | +- ((do_aa as i32) << 28); ++ ((do_aa as i32) << 28) | ++ ((superellipse as i32) << 29); -+ let inset_tl = LayoutSize::new(border.inset.left, border.inset.top); -+ let inset_tr = LayoutSize::new(border.inset.right, border.inset.top); -+ let inset_br = LayoutSize::new(border.inset.right, border.inset.bottom); -+ let inset_bl = LayoutSize::new(border.inset.left, border.inset.bottom); -+ -+ let max_shape_offsets = LayoutSideOffsets::new( -+ rect.width() - border.radius.top_left.width - border.radius.top_right.width, -+ rect.height() - border.radius.top_right.height - border.radius.bottom_right.height, -+ rect.width() - border.radius.bottom_left.width - border.radius.bottom_right.width, -+ rect.height() - border.radius.top_left.height - border.radius.bottom_left.height, -+ ); -+ -+ let shape_offset_tl = if border.radius.shape_top_left < 1.0 { -+ LayoutSize::new( -+ non_overlapping_widths.top + inset_tl.height.max(0.0), -+ non_overlapping_widths.left + inset_tl.width.max(0.0), -+ ).min(LayoutSize::new( -+ max_shape_offsets.top, -+ max_shape_offsets.left, -+ )) -+ } else { -+ LayoutSize::zero() -+ }; -+ let shape_offset_tr = if border.radius.shape_top_right < 1.0 { -+ LayoutSize::new( -+ non_overlapping_widths.top + inset_tr.height.max(0.0), -+ non_overlapping_widths.right + inset_tr.width.max(0.0), -+ ).min(LayoutSize::new( -+ max_shape_offsets.top, -+ max_shape_offsets.right, -+ )) -+ } else { -+ LayoutSize::zero() -+ }; -+ let shape_offset_br = if border.radius.shape_bottom_right < 1.0 { -+ LayoutSize::new( -+ non_overlapping_widths.bottom + inset_br.height.max(0.0), -+ non_overlapping_widths.right + inset_br.width.max(0.0), -+ ).min(LayoutSize::new( -+ max_shape_offsets.bottom, -+ max_shape_offsets.right, -+ )) -+ } else { -+ LayoutSize::zero() -+ }; -+ let shape_offset_bl = if border.radius.shape_bottom_left < 1.0 { -+ LayoutSize::new( -+ non_overlapping_widths.bottom + inset_bl.height.max(0.0), -+ non_overlapping_widths.left + inset_bl.width.max(0.0), -+ ).min(LayoutSize::new( -+ max_shape_offsets.bottom, -+ max_shape_offsets.left, -+ )) -+ } else { -+ LayoutSize::zero() -+ }; -+ - let local_size_tl = LayoutSize::new( - border.radius.top_left.width.max(widths.left), - border.radius.top_left.height.max(widths.top), -- ); -+ ) + shape_offset_tl; - let local_size_tr = LayoutSize::new( - border.radius.top_right.width.max(widths.right), - border.radius.top_right.height.max(widths.top), -- ); -+ ) + shape_offset_tr; - let local_size_br = LayoutSize::new( - border.radius.bottom_right.width.max(widths.right), - border.radius.bottom_right.height.max(widths.bottom), -- ); -+ ) + shape_offset_br; - let local_size_bl = LayoutSize::new( - border.radius.bottom_left.width.max(widths.left), - border.radius.bottom_left.height.max(widths.bottom), -- ); -+ ) + shape_offset_bl; - - let top_edge_info = get_edge_info( - border.top.style, -@@ -678,6 +737,8 @@ pub fn create_border_segments( - LayoutSize::new(widths.left, widths.top), - border.radius.top_left, - border.radius.shape_top_left, -+ shape_offset_tl, -+ inset_tl, - BorderSegment::TopLeft, - EdgeMask::TOP | EdgeMask::LEFT, - rect.top_right(), -@@ -705,6 +766,8 @@ pub fn create_border_segments( - LayoutSize::new(widths.right, widths.top), - border.radius.top_right, - border.radius.shape_top_right, -+ shape_offset_tr, -+ inset_tr, - BorderSegment::TopRight, - EdgeMask::TOP | EdgeMask::RIGHT, - rect.min, -@@ -732,6 +795,8 @@ pub fn create_border_segments( - LayoutSize::new(widths.right, widths.bottom), - border.radius.bottom_right, - border.radius.shape_bottom_right, -+ shape_offset_br, -+ inset_br, - BorderSegment::BottomRight, - EdgeMask::BOTTOM | EdgeMask::RIGHT, - rect.bottom_left(), -@@ -759,6 +824,8 @@ pub fn create_border_segments( - LayoutSize::new(widths.left, widths.bottom), - border.radius.bottom_left, - border.radius.shape_bottom_left, -+ shape_offset_bl, -+ inset_bl, - BorderSegment::BottomLeft, - EdgeMask::BOTTOM | EdgeMask::LEFT, - rect.max, -@@ -781,6 +848,8 @@ fn add_segment( - widths: DeviceSize, - radius: DeviceSize, - shape: f32, -+ shape_offset: DeviceSize, -+ inset: DeviceSize, - do_aa: bool, - h_adjacent_corner_outer: DevicePoint, - h_adjacent_corner_radius: DeviceSize, -@@ -799,7 +868,9 @@ fn add_segment( - color1: color1.premultiplied(), - widths, - radius, -- shape -+ shape, -+ shape_offset, -+ inset, + let instance_gpu_data = BorderInstanceGpuData { + local_rect: task_rect, +@@ -877,7 +880,7 @@ fn add_segment( + task_origin: DevicePoint::zero(), + flags: base_flags, + clip_params: [0.0; 8], +- gpu_data_address: instance_gpu_data.write(gpu_buffer_builder) ++ gpu_data_address: instance_gpu_data.write(superellipse, gpu_buffer_builder) }; - let base_instance = BorderInstance { -@@ -925,6 +996,8 @@ fn add_corner_segment( - widths: LayoutSize, - radius: LayoutSize, - shape: f32, -+ shape_offset: LayoutSize, -+ inset: LayoutSize, - segment: BorderSegment, - edge_flags: EdgeMask, - h_adjacent_corner_outer: LayoutPoint, -@@ -1035,6 +1108,8 @@ fn add_corner_segment( - segment, - radius: radius.to_au(), - shape: shape.to_bits(), -+ shape_offset: shape_offset.to_au(), -+ inset: inset.to_au(), - 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(), -@@ -1105,7 +1180,9 @@ fn add_edge_segment( - side0: side.into(), - side1: side.into(), - radius: LayoutSizeAu::zero(), -- shape: 0, -+ shape: 1.0f32.to_bits(), -+ shape_offset: LayoutSizeAu::zero(), -+ inset: LayoutSizeAu::zero(), - size: size.to_au(), - segment, - h_adjacent_corner_outer: LayoutPointAu::zero(), -@@ -1156,12 +1233,20 @@ pub fn build_border_instances( - 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 shape_offset = (LayoutSize::from_au(cache_key.shape_offset) * scale).ceil(); -+ let inset = (LayoutSize::from_au(cache_key.inset) * scale).ceil(); - - 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(); - -+ let shape_offset = if shape < 1.0 { -+ radius.max(widths) + shape_offset -+ } else { -+ DeviceSize::zero() -+ }; -+ - add_segment( - DeviceRect::from_size(cache_size.to_f32()), - style0, -@@ -1173,6 +1258,8 @@ pub fn build_border_instances( - widths, - radius, - shape, -+ shape_offset, -+ inset, - border.do_aa, - h_corner_outer, - h_corner_radius, -diff --git a/gfx/wr/webrender/src/box_shadow.rs b/gfx/wr/webrender/src/box_shadow.rs -index 2e83acb366..27e9f9b9e1 100644 ---- a/gfx/wr/webrender/src/box_shadow.rs -+++ b/gfx/wr/webrender/src/box_shadow.rs -@@ -121,6 +121,7 @@ pub struct BoxShadowCacheKey { - pub shape_bottom_left: u32, - pub shape_bottom_right: u32, - pub device_pixel_scale: Au, -+ pub spread_amount: u32, - } - - impl<'a> SceneBuilder<'a> { -diff --git a/gfx/wr/webrender/src/clip.rs b/gfx/wr/webrender/src/clip.rs -index 11e279eab9..d26d421b5d 100644 ---- a/gfx/wr/webrender/src/clip.rs -+++ b/gfx/wr/webrender/src/clip.rs -@@ -879,7 +879,7 @@ impl ClipTreeBuilder { - - match clip_info.key.kind { - ClipItemKeyKind::Rectangle(ClipMode::Clip) => {} -- ClipItemKeyKind::RoundedRectangle(_, ClipMode::Clip) => { -+ ClipItemKeyKind::RoundedRectangle(_, _, ClipMode::Clip) => { - if !spatial_tree.is_root_coord_system(clip_entry.spatial_node_index) { - return false; - } -@@ -1123,9 +1123,10 @@ impl From for ClipNode { - ClipItemKeyKind::Rectangle(mode) => { - ClipItemKind::Rectangle { mode } - } -- ClipItemKeyKind::RoundedRectangle(radius, mode) => { -+ ClipItemKeyKind::RoundedRectangle(radius, inset, mode) => { - ClipItemKind::RoundedRectangle { - radius: radius.into(), -+ inset: LayoutSideOffsets::from_au(inset), - mode, - } - } -@@ -1651,10 +1652,10 @@ impl ClipStore { - // Normal Clip rects are already handled by the clip-chain pic_coverage_rect, - // no need to do anything here - ClipItemKind::Rectangle { mode: ClipMode::Clip, .. } => {} -- ClipItemKind::RoundedRectangle { mode: ClipMode::Clip, radius } => { -+ ClipItemKind::RoundedRectangle { mode: ClipMode::Clip, radius, inset } => { - // Get an inner rect for the rounded-rect clip - let radius = clamped_radius(&radius, clip_instance.clip_rect.size()); -- let local_inner_rect = match extract_inner_rect_safe(&clip_instance.clip_rect, &radius) { -+ let local_inner_rect = match extract_inner_rect_safe(&clip_instance.clip_rect, &radius, &inset) { - Some(rect) => rect, - None => return None, - }; -@@ -1881,7 +1882,7 @@ impl Default for ClipStore { - #[cfg_attr(feature = "replay", derive(Deserialize))] - pub enum ClipItemKeyKind { - Rectangle(ClipMode), -- RoundedRectangle(BorderRadiusAu, ClipMode), -+ RoundedRectangle(BorderRadiusAu, LayoutSideOffsetsAu, ClipMode), - ImageMask(ImageKey, Option), - } - -@@ -1890,12 +1891,13 @@ impl ClipItemKeyKind { - ClipItemKeyKind::Rectangle(mode) - } - -- pub fn rounded_rect(radii: BorderRadius, mode: ClipMode) -> Self { -+ pub fn rounded_rect(radii: BorderRadius, inset: LayoutSideOffsets, mode: ClipMode) -> Self { - if radii.is_zero() { - ClipItemKeyKind::rectangle(mode) - } else { - ClipItemKeyKind::RoundedRectangle( - radii.into(), -+ inset.to_au(), - mode, - ) - } -@@ -1953,6 +1955,7 @@ pub enum ClipItemKind { - }, - RoundedRectangle { - radius: BorderRadius, -+ inset: LayoutSideOffsets, - mode: ClipMode, - }, - Image { -@@ -2024,9 +2027,9 @@ impl ClipItemKind { - ClipItemKind::Rectangle { mode } => { - (clip_rect, Some(clip_rect), mode) - } -- ClipItemKind::RoundedRectangle { ref radius, mode } => { -+ ClipItemKind::RoundedRectangle { ref radius, ref inset, mode } => { - let clamped = clamped_radius(radius, clip_rect.size()); -- let inner_clip_rect = extract_inner_rect_safe(&clip_rect, &clamped); -+ let inner_clip_rect = extract_inner_rect_safe(&clip_rect, &clamped, &inset); - (clip_rect, inner_clip_rect, mode) - } - ClipItemKind::Image { .. } => { -@@ -2104,7 +2107,7 @@ impl ClipItemKind { - } - } - } -- ClipItemKind::RoundedRectangle { ref radius, mode: ClipMode::Clip } => { -+ ClipItemKind::RoundedRectangle { ref radius, inset: _, mode: ClipMode::Clip } => { - let rect = clip_rect; - let radius = clamped_radius(radius, rect.size()); - // TODO(gw): Consider caching this in the ClipNode -@@ -2122,7 +2125,7 @@ impl ClipItemKind { - } - } - } -- ClipItemKind::RoundedRectangle { ref radius, mode: ClipMode::ClipOut } => { -+ ClipItemKind::RoundedRectangle { ref radius, inset: _, mode: ClipMode::ClipOut } => { - let rect = clip_rect; - let radius = clamped_radius(radius, rect.size()); - // TODO(gw): Consider caching this in the ClipNode -diff --git a/gfx/wr/webrender/src/composite.rs b/gfx/wr/webrender/src/composite.rs -index a69214fb4a..ca899dea4b 100644 ---- a/gfx/wr/webrender/src/composite.rs -+++ b/gfx/wr/webrender/src/composite.rs -@@ -8,7 +8,7 @@ use api::ColorDepth; - use crate::image_source::resolve_image; - use crate::picture::ResolvedSurfaceTexture; - use crate::renderer::GpuBufferBuilderF; --use euclid::Box2D; -+use euclid::{Box2D, SideOffsets2D}; - use crate::gpu_types::{ZBufferId, ZBufferIdGenerator}; - use crate::internal_types::{FrameAllocator, FrameMemory, FrameVec, TextureSource}; - use crate::invalidation::compare::ImageDependency; -@@ -854,6 +854,7 @@ impl CompositeState { - let inner_rect = match extract_inner_rect_safe( - &clip.rect, - &clip.radius, -+ &SideOffsets2D::::zero() - ) { - Some(rect) => rect, - None => return, + match segment { diff --git a/gfx/wr/webrender/src/gpu_types.rs b/gfx/wr/webrender/src/gpu_types.rs -index 5e949621c2..6303bb749b 100644 +index a53058b470..6373884471 100644 --- a/gfx/wr/webrender/src/gpu_types.rs +++ b/gfx/wr/webrender/src/gpu_types.rs -@@ -192,16 +192,19 @@ pub struct BorderInstanceGpuData { - pub widths: DeviceSize, - pub radius: DeviceSize, - pub shape: f32, -+ pub shape_offset: DeviceSize, -+ pub inset: DeviceSize, +@@ -194,14 +194,17 @@ pub struct BorderInstanceGpuData { } impl BorderInstanceGpuData { - pub fn write(&self, gpu_buffer_builder: &mut GpuBufferBuilderF) -> GpuBufferAddress { -- let mut writer = gpu_buffer_builder.write_blocks(5); -+ let mut writer = gpu_buffer_builder.write_blocks(6); +- pub fn write(&self, gpu_buffer_builder: &mut GpuBufferBuilderF) -> GpuBufferAddress { +- let mut writer = gpu_buffer_builder.write_blocks(6); ++ pub fn write(&self, superellipse: bool, gpu_buffer_builder: &mut GpuBufferBuilderF) -> GpuBufferAddress { ++ let block_count = if superellipse { 6 } else { 4 }; ++ let mut writer = gpu_buffer_builder.write_blocks(block_count); 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.push_one([self.shape, self.shape_offset.width, self.shape_offset.height, 0.0]); -+ writer.push_one([self.inset.width, self.inset.height, 0.0, 0.0]); +- writer.push_one([self.shape, self.shape_offset.width, self.shape_offset.height, 0.0]); +- writer.push_one([self.inset.width, self.inset.height, 0.0, 0.0]); ++ if superellipse { ++ writer.push_one([self.shape, self.shape_offset.width, self.shape_offset.height, 0.0]); ++ writer.push_one([self.inset.width, self.inset.height, 0.0, 0.0]); ++ } writer.finish() } -diff --git a/gfx/wr/webrender/src/hit_test.rs b/gfx/wr/webrender/src/hit_test.rs -index 625bd74eae..81456200d6 100644 ---- a/gfx/wr/webrender/src/hit_test.rs -+++ b/gfx/wr/webrender/src/hit_test.rs -@@ -87,7 +87,8 @@ impl HitTestClipNode { - ClipItemKeyKind::Rectangle(mode) => { - HitTestRegion::Rectangle(clip_rect, mode) - } -- ClipItemKeyKind::RoundedRectangle(radius, mode) => { -+ ClipItemKeyKind::RoundedRectangle(radius, _, mode) => { -+ // TODO(wsmind): implement hit-testing for corner-shape - HitTestRegion::RoundedRectangle(clip_rect, radius.into(), mode) - } - ClipItemKeyKind::ImageMask(_, polygon_handle) => { -diff --git a/gfx/wr/webrender/src/prepare.rs b/gfx/wr/webrender/src/prepare.rs -index b21fa8d7a5..bb2993d303 100644 ---- a/gfx/wr/webrender/src/prepare.rs -+++ b/gfx/wr/webrender/src/prepare.rs -@@ -434,15 +434,22 @@ fn prepare_prim_for_render( - - let blur_region = (BLUR_SAMPLE_SCALE * blur_radius).ceil(); - -- let max_corner_width = shadow_radius.top_left.width -+ let mut max_corner_width = shadow_radius.top_left.width - .max(shadow_radius.bottom_left.width) - .max(shadow_radius.top_right.width) - .max(shadow_radius.bottom_right.width); -- let max_corner_height = shadow_radius.top_left.height -+ let mut max_corner_height = shadow_radius.top_left.height - .max(shadow_radius.bottom_left.height) - .max(shadow_radius.top_right.height) - .max(shadow_radius.bottom_right.height); - -+ if shadow_data.clip_mode == BoxShadowClipMode::Inset && !shadow_radius.shapes_all_round() { -+ // Add one extra pixel to avoid stretching the antialiasing tail -+ // in extreme cases (e.g corner-shape: notch with a blur radius of 1px). -+ max_corner_width -= shadow_data.spread_amount - 1.0; -+ max_corner_height -= shadow_data.spread_amount - 1.0; -+ } +diff --git a/gfx/wr/webrender/src/pattern/box_shadow.rs b/gfx/wr/webrender/src/pattern/box_shadow.rs +index 1cee87e980..6d0cb1797d 100644 +--- a/gfx/wr/webrender/src/pattern/box_shadow.rs ++++ b/gfx/wr/webrender/src/pattern/box_shadow.rs +@@ -43,7 +43,10 @@ impl PatternBuilder for BoxShadowPatternData { + _ctx: &PatternBuilderContext, + state: &mut PatternBuilderState, + ) -> Pattern { +- let mut writer = state.frame_gpu_data.f32.write_blocks(7); ++ let superellipse = !self.element_radius.shapes_all_round(); + - let used_corner_width = max_corner_width.max(blur_region); - let used_corner_height = max_corner_height.max(blur_region); ++ let block_count = if superellipse { 7 } else { 6 }; ++ let mut writer = state.frame_gpu_data.f32.write_blocks(block_count); + writer.push_one([ + self.shadow_rect_alloc_size.width, + self.shadow_rect_alloc_size.height, +@@ -74,22 +77,24 @@ impl PatternBuilder for BoxShadowPatternData { + self.element_radius.bottom_left.width, + self.element_radius.bottom_left.height, + ]); +- writer.push_one([ +- self.element_radius.shape_top_left, +- self.element_radius.shape_top_right, +- self.element_radius.shape_bottom_right, +- self.element_radius.shape_bottom_left, +- ]); + writer.push_one([ + self.content_device_size.width, + self.content_device_size.height, + 0.0, + 0.0, + ]); ++ if superellipse { ++ writer.push_one([ ++ self.element_radius.shape_top_left, ++ self.element_radius.shape_top_right, ++ self.element_radius.shape_bottom_right, ++ self.element_radius.shape_bottom_left, ++ ]); ++ } + let addr = writer.finish(); -@@ -514,6 +521,14 @@ fn prepare_prim_for_render( - content_scale.0, - ); + Pattern { +- kind: PatternKind::BoxShadow, ++ kind: if superellipse { PatternKind::BoxShadowSuperellipse } else { PatternKind::BoxShadow }, + shader_input: PatternShaderInput(addr.as_int(), 0), + texture_input: PatternTextureInput::new(self.render_task), + base_color: self.color, +diff --git a/gfx/wr/webrender/src/pattern/mod.rs b/gfx/wr/webrender/src/pattern/mod.rs +index e3c1d9c771..8fb3d66f74 100644 +--- a/gfx/wr/webrender/src/pattern/mod.rs ++++ b/gfx/wr/webrender/src/pattern/mod.rs +@@ -31,32 +31,33 @@ pub enum PatternKind { -+ // We only need the spread amount (-inset) in the cache key when using other -+ // corner shapes than 'round' -+ let cache_key_spread_amount = if !shadow_radius.shapes_all_round() { -+ shadow_data.spread_amount.to_bits() -+ } else { -+ 0 -+ }; -+ - let bs_cache_key = BoxShadowCacheKey { - blur_radius_dp: Au::from_f32_px(blur_std_dev), - clip_mode: shadow_data.clip_mode, -@@ -527,6 +542,7 @@ fn prepare_prim_for_render( - shape_bottom_right: shadow_radius.shape_bottom_right.to_bits(), - shape_bottom_left: shadow_radius.shape_bottom_left.to_bits(), - device_pixel_scale: Au::from_f32_px(content_scale.0), -+ spread_amount: cache_key_spread_amount, - }; + Mask = 3, + BoxShadow = 4, ++ BoxShadowSuperellipse = 5, + // Variants of ColorOrTexture that use a non-default sampler type + // (samplerExternalOES / __samplerExternal2DY2YEXT / sampler2DRect). The + // quad shader is compiled in matching per-kind variants; see + // ps_quad_textured.glsl. +- TextureExternal = 5, +- TextureExternalBT709 = 6, +- TextureRect = 7, ++ TextureExternal = 6, ++ TextureExternalBT709 = 7, ++ TextureRect = 8, + // Samples up to three planes (sColor0/1/2) and converts from YUV to RGB. + // Like ColorOrTexture, the YUV pattern comes in sampler-type-specific + // variants so that the planes are sampled with the matching sColor + // declaration; see ps_quad_yuv.glsl. `Yuv` is the default (TEXTURE_2D). +- Yuv = 8, +- YuvTextureExternal = 9, +- YuvTextureExternalBT709 = 10, +- YuvTextureRect = 11, ++ Yuv = 9, ++ YuvTextureExternal = 10, ++ YuvTextureExternalBT709 = 11, ++ YuvTextureRect = 12, + // Samples a captured backdrop texture using a (bilerp) 4-corner uv quad. +- Backdrop = 12, ++ Backdrop = 13, + // Applies a filter to a sampled source texture; see ps_quad_blend.glsl. +- Blend = 13, ++ Blend = 14, + // Composites a picture over its backdrop with a software mix-blend-mode; + // samples two textures (backdrop + source). See ps_quad_mix_blend.glsl. +- MixBlend = 14, ++ MixBlend = 15, + // When adding patterns, don't forget to update the NUM_PATTERNS constant. + } - // The shadow shape is offset by blur_region within the alloc task (local pixels). -@@ -538,6 +554,8 @@ fn prepare_prim_for_render( - ); - let device_pixel_scale_for_task = DevicePixelScale::new(content_scale.0); +-pub const NUM_PATTERNS: u32 = 15; ++pub const NUM_PATTERNS: u32 = 16; -+ let shadow_inset = LayoutSideOffsets::new_all_same(-shadow_data.spread_amount); -+ - let task_id = frame_state.resource_cache.request_render_task( - Some(RenderTaskCacheKey { - origin: DeviceIntPoint::zero(), -@@ -555,6 +573,7 @@ fn prepare_prim_for_render( - RenderTaskKind::new_rounded_rect_mask( - minimal_shadow_rect, - shadow_radius, -+ shadow_inset, - ClipMode::Clip, - device_pixel_scale_for_task, - ), -@@ -2323,20 +2342,20 @@ fn write_brush_segment_description( - continue; - } - -- let (local_clip_rect, radius, mode) = match clip_node.item.kind { -- ClipItemKind::RoundedRectangle { radius, mode } => { -+ let (local_clip_rect, radius, inset, mode) = match clip_node.item.kind { -+ ClipItemKind::RoundedRectangle { radius, inset, mode } => { - let radius = clamped_radius(&radius, clip_instance.clip_rect.size()); -- (clip_instance.clip_rect, Some(radius), mode) -+ (clip_instance.clip_rect, Some(radius), Some(inset), mode) - } - ClipItemKind::Rectangle { mode } => { -- (clip_instance.clip_rect, None, mode) -+ (clip_instance.clip_rect, None, None, mode) - } - ClipItemKind::Image { .. } => { - panic!("bug: masks not supported on old segment path"); - } - }; - -- segment_builder.push_clip_rect(local_clip_rect, radius, mode); -+ segment_builder.push_clip_rect(local_clip_rect, radius, inset, mode); - } - - true -diff --git a/gfx/wr/webrender/src/prim_store/borders.rs b/gfx/wr/webrender/src/prim_store/borders.rs -index 9e93764c31..f7cb517e4a 100644 ---- a/gfx/wr/webrender/src/prim_store/borders.rs -+++ b/gfx/wr/webrender/src/prim_store/borders.rs -@@ -541,9 +541,9 @@ fn test_struct_sizes() { - // test expectations and move on. - // (b) You made a structure larger. This is not necessarily a problem, but should only - // be done with care, and after checking if talos performance regresses badly. -- assert_eq!(mem::size_of::(), 100, "NormalBorderPrim size changed"); -- assert_eq!(mem::size_of::(), 152, "NormalBorderTemplate size changed"); -- assert_eq!(mem::size_of::(), 104, "NormalBorderKey size changed"); -+ assert_eq!(mem::size_of::(), 116, "NormalBorderPrim size changed"); -+ assert_eq!(mem::size_of::(), 168, "NormalBorderTemplate size changed"); -+ assert_eq!(mem::size_of::(), 120, "NormalBorderKey size changed"); - assert_eq!(mem::size_of::(), 68, "ImageBorder size changed"); - assert_eq!(mem::size_of::(), 72, "ImageBorderTemplate size changed"); - assert_eq!(mem::size_of::(), 72, "ImageBorderKey size changed"); + impl PatternKind { + pub fn from_u32(val: u32) -> Self { diff --git a/gfx/wr/webrender/src/quad.rs b/gfx/wr/webrender/src/quad.rs -index e07ec25550..fabe4ed46d 100644 +index 1a5d92bf6d..185f6e3ee6 100644 --- a/gfx/wr/webrender/src/quad.rs +++ b/gfx/wr/webrender/src/quad.rs -@@ -3,7 +3,7 @@ - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - - use api::{BorderRadius, ClipMode, ColorF, units::*}; --use euclid::{Scale, point2}; -+use euclid::{Scale, SideOffsets2D, Size2D, point2}; - - use crate::ItemUid; - use crate::border::NinePatchDescriptorExt; -@@ -1207,7 +1207,7 @@ fn prepare_tiles( - let rect = transform.map_rect(&clip_instance.clip_rect); - scratch.retained.quad_tile_classifier.add_clip_rect(rect, mode, applied_as_local_clip); - } -- ClipItemKind::RoundedRectangle { mode: ClipMode::Clip, ref radius } => { -+ ClipItemKind::RoundedRectangle { mode: ClipMode::Clip, ref radius, ref inset } => { - // For rounded-rects with Clip mode, we need a mask for each corner, - // and to add the clip rect itself (to cull tiles outside that rect) - -@@ -1222,31 +1222,54 @@ fn prepare_tiles( - let r_br = transform.map_size(&radius.bottom_right).abs(); - let r_bl = transform.map_size(&radius.bottom_left).abs(); - -+ let inset: SideOffsets2D = transform.map_side_offsets(&inset); -+ -+ let i_tl = if radius.shape_top_left < 1.0 { -+ Size2D::new(inset.top, inset.left) -+ } else { -+ Size2D::zero() -+ }; -+ let i_tr = if radius.shape_top_right < 1.0 { -+ Size2D::new(inset.top, inset.right) -+ } else { -+ Size2D::zero() -+ }; -+ let i_br = if radius.shape_bottom_right < 1.0 { -+ Size2D::new(inset.bottom, inset.right) -+ } else { -+ Size2D::zero() -+ }; -+ let i_bl = if radius.shape_bottom_left < 1.0 { -+ Size2D::new(inset.bottom, inset.left) -+ } else { -+ Size2D::zero() -+ }; -+ - // Construct the mask regions for each corner - let c_tl = DeviceRect::from_origin_and_size( - clip_device_rect.min, -- r_tl, -+ r_tl + i_tl, - ); - let c_tr = DeviceRect::from_origin_and_size( - DevicePoint::new( - clip_device_rect.max.x - r_tr.width, - clip_device_rect.min.y, - ), -- r_tr, -+ r_tr + i_tr, - ); - let c_br = DeviceRect::from_origin_and_size( - DevicePoint::new( - clip_device_rect.max.x - r_br.width, - clip_device_rect.max.y - r_br.height, - ), -- r_br, -+ r_br + i_br, - ); - let c_bl = DeviceRect::from_origin_and_size( - DevicePoint::new( - clip_device_rect.min.x, - clip_device_rect.max.y - r_bl.height, - ), -- r_bl, -+ r_bl + i_bl, - ); - - scratch.retained.quad_tile_classifier.add_clip_rect(clip_device_rect, ClipMode::Clip, applied_as_local_clip); -@@ -1255,11 +1278,11 @@ fn prepare_tiles( - scratch.retained.quad_tile_classifier.add_mask_region(c_br); - scratch.retained.quad_tile_classifier.add_mask_region(c_bl); - } -- ClipItemKind::RoundedRectangle { mode: ClipMode::ClipOut, ref radius } => { -+ ClipItemKind::RoundedRectangle { mode: ClipMode::ClipOut, ref radius , ref inset } => { - let radius = clamped_radius(radius, clip_instance.clip_rect.size()); - // Try to find an inner rect within the clip-out rounded rect that we can - // use to cull inner tiles. If we can't, the entire rect needs to be masked -- match extract_inner_rect_k(&clip_instance.clip_rect, &radius, 0.5) { -+ match extract_inner_rect_k(&clip_instance.clip_rect, &radius, &inset, 0.5) { - Some(ref inner_rect) => { - let rect = transform.map_rect(inner_rect); - scratch.retained.quad_tile_classifier.add_clip_rect(rect, ClipMode::ClipOut, false); -@@ -1419,7 +1442,7 @@ fn get_prim_render_strategy( - let clip_instance = clip_store.get_instance_from_range(&clip_chain.clips_range, 0); - let clip_node = &interned_clips[clip_instance.handle]; - -- if let ClipItemKind::RoundedRectangle { ref radius, mode: ClipMode::Clip, .. } = clip_node.item.kind { -+ if let ClipItemKind::RoundedRectangle { ref radius, ref inset, mode: ClipMode::Clip } = clip_node.item.kind { - let size = clip_instance.clip_rect.size(); - let radius = clamped_radius(radius, size); - let max_corner_width = radius.top_left.width -@@ -1430,9 +1453,28 @@ fn get_prim_render_strategy( - .max(radius.bottom_left.height) - .max(radius.top_right.height) - .max(radius.bottom_right.height); -+ -+ let mut max_inset_width = 0.0f32; -+ let mut max_inset_height = 0.0f32; -+ if radius.shape_top_left < 1.0 { -+ max_inset_width = max_inset_width.max(inset.top); -+ max_inset_height = max_inset_height.max(inset.left); -+ } -+ if radius.shape_top_right < 1.0 { -+ max_inset_width = max_inset_width.max(inset.top); -+ max_inset_height = max_inset_height.max(inset.right); -+ } -+ if radius.shape_bottom_right < 1.0 { -+ max_inset_width = max_inset_width.max(inset.bottom); -+ max_inset_height = max_inset_height.max(inset.right); -+ } -+ if radius.shape_bottom_left < 1.0 { -+ max_inset_width = max_inset_width.max(inset.bottom); -+ max_inset_height = max_inset_height.max(inset.left); -+ } - -- if max_corner_width <= 0.5 * size.width && -- max_corner_height <= 0.5 * size.height { -+ if (max_corner_width + max_inset_width) <= 0.5 * size.width && -+ (max_corner_height + max_inset_height) <= 0.5 * size.height { - - let clip_prim_coords_match = spatial_tree.is_matching_coord_system( - prim_spatial_node_index, -@@ -1449,7 +1491,7 @@ fn get_prim_render_strategy( - - if let Some(clip_rect) = map_clip_to_prim.map(&clip_instance.clip_rect) { - let radius = map_clip_to_prim.map_vector( -- LayoutVector2D::new(max_corner_width, max_corner_height) -+ LayoutVector2D::new(max_corner_width + max_inset_width, max_corner_height + max_inset_height) - ); - return QuadRenderStrategy::NinePatch { - radius, -@@ -1776,6 +1818,7 @@ pub fn write_rounded_rect_clip_blocks( - gpu_buffer: &mut GpuBufferBuilderF, - clip_rect: LayoutRect, +@@ -1809,7 +1809,7 @@ pub fn write_rounded_rect_clip_blocks( radius: &BorderRadius, -+ inset: LayoutSideOffsets, + inset: LayoutSideOffsets, mode: ClipMode, - ) -> (GpuBufferAddress, bool) { +-) -> (GpuBufferAddress, bool) { ++) -> (GpuBufferAddress, bool, bool) { let radius = clamped_radius(radius, clip_rect.size()); -@@ -1793,7 +1836,7 @@ pub fn write_rounded_rect_clip_blocks( - (writer.finish(), true) + if radius.can_use_fast_path_in(&clip_rect) { +@@ -1823,9 +1823,11 @@ pub fn write_rounded_rect_clip_blocks( + ]); + writer.push_one([mode as i32 as f32, 0.0, 0.0, 0.0]); + +- (writer.finish(), true) ++ (writer.finish(), true, false) } else { -- let mut writer = gpu_buffer.write_blocks(5); -+ let mut writer = gpu_buffer.write_blocks(6); +- let mut writer = gpu_buffer.write_blocks(6); ++ let superellipse = !radius.shapes_all_round(); ++ let block_count = if superellipse { 6 } else { 4 }; ++ let mut writer = gpu_buffer.write_blocks(block_count); writer.push_one(clip_rect); writer.push_one([ radius.top_left.width, -@@ -1814,6 +1857,7 @@ pub fn write_rounded_rect_clip_blocks( - radius.shape_bottom_right, - radius.shape_bottom_left, +@@ -1840,15 +1842,17 @@ pub fn write_rounded_rect_clip_blocks( + radius.bottom_right.height, ]); -+ writer.push_one(inset); + writer.push_one([mode as i32 as f32, 0.0, 0.0, 0.0]); +- writer.push_one([ +- radius.shape_top_left, +- radius.shape_top_right, +- radius.shape_bottom_right, +- radius.shape_bottom_left, +- ]); +- writer.push_one(inset); ++ if superellipse { ++ writer.push_one([ ++ radius.shape_top_left, ++ radius.shape_top_right, ++ radius.shape_bottom_right, ++ radius.shape_bottom_left, ++ ]); ++ writer.push_one(inset); ++ } - (writer.finish(), false) +- (writer.finish(), false) ++ (writer.finish(), false, superellipse) } -@@ -1835,11 +1879,12 @@ pub fn prepare_clip_task( + } + +@@ -1867,7 +1871,7 @@ pub fn prepare_clip_task( + rg_builder: &mut RenderTaskGraphBuilder, sub_tasks: &mut SubTaskRange, ) { - let (clip_address, fast_path) = match clip_item.kind { -- ClipItemKind::RoundedRectangle { radius, mode } => { -+ ClipItemKind::RoundedRectangle { radius, inset, mode } => { +- let (clip_address, fast_path) = match clip_item.kind { ++ let (clip_address, fast_path, superellipse) = match clip_item.kind { + ClipItemKind::RoundedRectangle { radius, inset, mode } => { write_rounded_rect_clip_blocks( gpu_buffer, - clip_instance.clip_rect, - &radius, -+ inset, - mode, - ) +@@ -1884,7 +1888,7 @@ pub fn prepare_clip_task( + writer.push_one([mode as i32 as f32, 0.0, 0.0, 0.0]); + let clip_address = writer.finish(); + +- (clip_address, true) ++ (clip_address, true, false) } + ClipItemKind::Image { .. } => { + let transform_id = transforms.gpu.get_id_with_post_scale( +@@ -2035,6 +2039,7 @@ pub fn prepare_clip_task( + clip_space, + needs_scissor_rect, + rounded_rect_fast_path: fast_path, ++ rounded_rect_superellipse: superellipse, + }), + ); + } diff --git a/gfx/wr/webrender/src/render_target.rs b/gfx/wr/webrender/src/render_target.rs -index 6213555e12..cd6bd0c4cc 100644 +index d393c99a72..21853655a9 100644 --- a/gfx/wr/webrender/src/render_target.rs +++ b/gfx/wr/webrender/src/render_target.rs -@@ -451,6 +451,7 @@ impl RenderTarget { +@@ -35,6 +35,7 @@ use crate::spatial_tree::SpatialNodeIndex; + + const STYLE_SOLID: i32 = ((BorderStyle::Solid as i32) << 8) | ((BorderStyle::Solid as i32) << 16); + const STYLE_MASK: i32 = 0x00FF_FF00; ++const SUPERELLIPSE_MASK: i32 = 1 << 29; + + /// A tag used to identify the output format of a `RenderTarget`. + #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +@@ -165,6 +166,8 @@ pub struct RenderTarget { + + pub border_segments_complex: FrameVec, + pub border_segments_solid: FrameVec, ++ pub border_segments_complex_superellipse: FrameVec, ++ pub border_segments_solid_superellipse: FrameVec, + pub line_decorations: FrameVec, + + // Clearing render targets has a fair amount of special cases. +@@ -225,6 +228,8 @@ impl RenderTarget { + clip_masks: ClipMaskInstanceList::new(memory), + border_segments_complex: memory.new_vec(), + border_segments_solid: memory.new_vec(), ++ border_segments_complex_superellipse: memory.new_vec(), ++ border_segments_solid_superellipse: memory.new_vec(), + clears: memory.new_vec(), + line_decorations: memory.new_vec(), + } +@@ -443,7 +448,7 @@ impl RenderTarget { + + let device_rect = DeviceRect::from_size(target_rect.size().to_f32()); + +- let (clip_address, fast_path) = quad::write_rounded_rect_clip_blocks( ++ let (clip_address, fast_path, superellipse) = quad::write_rounded_rect_clip_blocks( &mut gpu_buffer_builder.f32, region_task.clip_rect, ®ion_task.radius, -+ region_task.inset, - region_task.mode, - ); +@@ -497,6 +502,8 @@ impl RenderTarget { + if fast_path { + self.clip_masks.mask_instances_fast.push(instance); ++ } else if superellipse { ++ self.clip_masks.mask_instances_superellipse.push(instance); + } else { + self.clip_masks.mask_instances_slow.push(instance); + } +@@ -547,10 +554,19 @@ impl RenderTarget { + // TODO(gw): It may be better to store the task origin in + // the render task data instead of per instance. + instance.task_origin = task_origin; ++ let superellipse = (instance.flags & SUPERELLIPSE_MASK) != 0; + if instance.flags & STYLE_MASK == STYLE_SOLID { +- self.border_segments_solid.push(instance); ++ if superellipse { ++ self.border_segments_solid_superellipse.push(instance); ++ } else { ++ self.border_segments_solid.push(instance); ++ } + } else { +- self.border_segments_complex.push(instance); ++ if superellipse { ++ self.border_segments_complex_superellipse.push(instance); ++ } else { ++ self.border_segments_complex.push(instance); ++ } + } + } + } +@@ -927,6 +943,11 @@ fn add_rect_clip_task_to_batch( + .entry(*target_rect) + .or_insert_with(|| memory.new_vec()) + .push(instance); ++ } else if task.rounded_rect_superellipse { ++ results.mask_instances_superellipse_with_scissor ++ .entry(*target_rect) ++ .or_insert_with(|| memory.new_vec()) ++ .push(instance); + } else { + results.mask_instances_slow_with_scissor + .entry(*target_rect) +@@ -936,6 +957,8 @@ fn add_rect_clip_task_to_batch( + } else { + if task.rounded_rect_fast_path { + results.mask_instances_fast.push(instance); ++ } else if task.rounded_rect_superellipse { ++ results.mask_instances_superellipse.push(instance); + } else { + results.mask_instances_slow.push(instance); + } diff --git a/gfx/wr/webrender/src/render_task.rs b/gfx/wr/webrender/src/render_task.rs -index 4759884e34..d97d6fa071 100644 +index 4501e2de8b..b4e1478192 100644 --- a/gfx/wr/webrender/src/render_task.rs +++ b/gfx/wr/webrender/src/render_task.rs -@@ -170,6 +170,7 @@ pub struct ImageRequestTask { - pub struct ClipRegionTask { - pub clip_rect: LayoutRect, - pub radius: BorderRadius, -+ pub inset: LayoutSideOffsets, - pub mode: ClipMode, - pub device_pixel_scale: DevicePixelScale, - } -@@ -550,12 +551,14 @@ impl RenderTaskKind { - pub fn new_rounded_rect_mask( - clip_rect: LayoutRect, - radius: BorderRadius, -+ inset: LayoutSideOffsets, - mode: ClipMode, - device_pixel_scale: DevicePixelScale, - ) -> Self { - RenderTaskKind::ClipRegion(ClipRegionTask { - clip_rect, - radius, -+ inset, - mode, - device_pixel_scale, - }) -diff --git a/gfx/wr/webrender/src/renderer/composite.rs b/gfx/wr/webrender/src/renderer/composite.rs -index bc59b72b37..276d7ad78c 100644 ---- a/gfx/wr/webrender/src/renderer/composite.rs -+++ b/gfx/wr/webrender/src/renderer/composite.rs -@@ -806,6 +806,7 @@ impl Renderer { - segment_builder.push_clip_rect( - clip.rect.cast_unit(), - Some(clip.radius), -+ None, - ClipMode::Clip, - ); - segment_builder.build(|segment| { -@@ -1060,6 +1061,7 @@ impl Renderer { - segment_builder.push_clip_rect( - clip.rect.cast_unit(), - Some(clip.radius), -+ None, - ClipMode::Clip, - ); - segment_builder.build(|segment| { -diff --git a/gfx/wr/webrender/src/renderer/gpu_buffer.rs b/gfx/wr/webrender/src/renderer/gpu_buffer.rs -index e20752544d..4cbedc3b4f 100644 ---- a/gfx/wr/webrender/src/renderer/gpu_buffer.rs -+++ b/gfx/wr/webrender/src/renderer/gpu_buffer.rs -@@ -13,7 +13,7 @@ use crate::gpu_types::UvRectKind; - use crate::internal_types::{FrameId, FrameMemory, FrameVec, TextureSource, TextureSourceExternal}; - use crate::renderer::MAX_VERTEX_TEXTURE_WIDTH; - use crate::util::ScaleOffset; --use api::units::{DeviceIntRect, DeviceIntSize, DevicePoint, DeviceRect, LayoutRect, PictureRect}; -+use api::units::{DeviceIntRect, DeviceIntSize, DevicePoint, DeviceRect, LayoutRect, LayoutSideOffsets, PictureRect}; - use api::{PremultipliedColorF, ImageFormat}; - use crate::device::Texel; - use crate::render_task::{RenderTaskLocation, StaticRenderTaskSurface}; -@@ -196,6 +196,19 @@ impl Into for crate::quad::LayoutOrDeviceRect { - } +@@ -2242,6 +2242,7 @@ pub struct RectangleClipSubTask { + pub quad_flags: QuadFlags, + pub needs_scissor_rect: bool, + pub rounded_rect_fast_path: bool, ++ pub rounded_rect_superellipse: bool, } -+impl Into for LayoutSideOffsets { -+ fn into(self) -> GpuBufferBlockF { -+ GpuBufferBlockF { -+ data: [ -+ self.top, -+ self.right, -+ self.bottom, -+ self.left, -+ ], -+ } -+ } -+} -+ - impl Into for ScaleOffset { - fn into(self) -> GpuBufferBlockF { - GpuBufferBlockF { -diff --git a/gfx/wr/webrender/src/scene_building.rs b/gfx/wr/webrender/src/scene_building.rs -index cb6f4c3ac6..71f058b90e 100644 ---- a/gfx/wr/webrender/src/scene_building.rs -+++ b/gfx/wr/webrender/src/scene_building.rs -@@ -2710,6 +2710,7 @@ impl<'a> SceneBuilder<'a> { - let item = ClipItemKey { - kind: ClipItemKeyKind::rounded_rect( - clip.radii, -+ clip.inset, - clip.mode, - ), - }; -diff --git a/gfx/wr/webrender/src/segment.rs b/gfx/wr/webrender/src/segment.rs -index dda042f81f..b2e5d3e7c2 100644 ---- a/gfx/wr/webrender/src/segment.rs -+++ b/gfx/wr/webrender/src/segment.rs -@@ -263,8 +263,8 @@ impl SegmentBuilder { - self.inner_rect = inner_rect; - self.bounding_rect = Some(local_rect); - -- self.push_clip_rect(local_rect, None, ClipMode::Clip); -- self.push_clip_rect(local_clip_rect, None, ClipMode::Clip); -+ self.push_clip_rect(local_rect, None, None, ClipMode::Clip); -+ self.push_clip_rect(local_clip_rect, None, None, ClipMode::Clip); - - // This must be set after the push_clip_rect calls above, since we - // want to skip segment building if those are the only clips. -@@ -366,6 +366,7 @@ impl SegmentBuilder { - &mut self, - rect: LayoutRect, - radius: Option, -+ inset: Option, - mode: ClipMode, - ) { - self.has_interesting_clips = true; -@@ -383,7 +384,7 @@ impl SegmentBuilder { - Some(radius) => { - // For a rounded rect, try to create a nine-patch where there - // is a clip item for each corner, inner and edge region. -- match extract_inner_rect_safe(&rect, &radius) { -+ match extract_inner_rect_safe(&rect, &radius, &inset.unwrap_or_default()) { - Some(inner) => { - let p0 = rect.min; - let p1 = inner.min; -@@ -709,7 +710,8 @@ fn emit_segment_if_needed( - #[cfg(test)] - mod test { - use api::{BorderRadius, ClipMode}; -- use api::units::{LayoutPoint, LayoutRect}; -+ use api::units::{LayoutPoint, LayoutRect, LayoutSideOffsets, LayoutSize}; -+ - use super::{Segment, SegmentBuilder, EdgeMask}; - use std::cmp; - -@@ -768,7 +770,7 @@ mod test { - local_rect: LayoutRect, - inner_rect: Option, - local_clip_rect: LayoutRect, -- clips: &[(LayoutRect, Option, ClipMode)], -+ clips: &[(LayoutRect, Option, Option, ClipMode)], - expected_segments: &mut [Segment] - ) { - let mut sb = SegmentBuilder::new(); -@@ -777,11 +779,11 @@ mod test { - inner_rect, - local_clip_rect, - ); -- sb.push_clip_rect(local_rect, None, ClipMode::Clip); -- sb.push_clip_rect(local_clip_rect, None, ClipMode::Clip); -+ sb.push_clip_rect(local_rect, None, None, ClipMode::Clip); -+ sb.push_clip_rect(local_clip_rect, None, None, ClipMode::Clip); - let mut segments = Vec::new(); -- for &(rect, radius, mode) in clips { -- sb.push_clip_rect(rect, radius, mode); -+ for &(rect, radius, inset, mode) in clips { -+ sb.push_clip_rect(rect, radius, inset, mode); - } - sb.build(|segment| { - segments.push(Segment { -@@ -926,8 +928,8 @@ mod test { - None, - rect(-1000.0, -1000.0, 1000.0, 1000.0), - &[ -- (rect(20.0, 20.0, 40.0, 40.0), None, ClipMode::Clip), -- (rect(40.0, 20.0, 60.0, 40.0), None, ClipMode::Clip), -+ (rect(20.0, 20.0, 40.0, 40.0), None, None, ClipMode::Clip), -+ (rect(40.0, 20.0, 60.0, 40.0), None, None, ClipMode::Clip), - ], - &mut [ - ], -@@ -941,7 +943,7 @@ mod test { - None, - rect(-1000.0, -1000.0, 1000.0, 1000.0), - &[ -- (rect(20.0, 20.0, 60.0, 60.0), Some(BorderRadius::uniform(10.0)), ClipMode::Clip), -+ (rect(20.0, 20.0, 60.0, 60.0), Some(BorderRadius::uniform(10.0)), None, ClipMode::Clip), - ], - &mut [ - // corners -@@ -962,6 +964,45 @@ mod test { - ); - } - -+ #[test] -+ fn segment_rounded_clip_inset() { -+ let radius = BorderRadius { -+ top_left: LayoutSize::new(10.0, 10.0), -+ top_right: LayoutSize::new(10.0, 10.0), -+ bottom_left: LayoutSize::new(10.0, 10.0), -+ bottom_right: LayoutSize::new(10.0, 10.0), -+ shape_top_left: 2.0, -+ shape_top_right: 1.0, -+ shape_bottom_left: 0.0, -+ shape_bottom_right: -1.0, -+ }; -+ let inset = LayoutSideOffsets::new_all_same(5.0); -+ seg_test( -+ rect(0.0, 0.0, 100.0, 100.0), -+ None, -+ rect(-1000.0, -1000.0, 1000.0, 1000.0), -+ &[ -+ (rect(20.0, 20.0, 60.0, 60.0), Some(radius), Some(inset), ClipMode::Clip), -+ ], -+ &mut [ -+ // corners -+ seg(20.0, 20.0, 35.0, 30.0, true, Some(EdgeMask::LEFT | EdgeMask::TOP)), -+ seg(20.0, 45.0, 35.0, 60.0, true, Some(EdgeMask::LEFT | EdgeMask::BOTTOM)), -+ seg(45.0, 20.0, 60.0, 30.0, true, Some(EdgeMask::RIGHT | EdgeMask::TOP)), -+ seg(45.0, 45.0, 60.0, 60.0, true, Some(EdgeMask::RIGHT | EdgeMask::BOTTOM)), -+ -+ // inner -+ seg(35.0, 30.0, 45.0, 45.0, false, None), -+ -+ // edges -+ seg(35.0, 20.0, 45.0, 30.0, false, Some(EdgeMask::TOP)), -+ seg(35.0, 45.0, 45.0, 60.0, false, Some(EdgeMask::BOTTOM)), -+ seg(20.0, 30.0, 35.0, 45.0, false, Some(EdgeMask::LEFT)), -+ seg(45.0, 30.0, 60.0, 45.0, false, Some(EdgeMask::RIGHT)), -+ ], -+ ); -+ } -+ - #[test] - fn segment_clip_out() { - seg_test( -@@ -969,7 +1010,7 @@ mod test { - None, - rect(-1000.0, -1000.0, 2000.0, 2000.0), - &[ -- (rect(20.0, 20.0, 60.0, 60.0), None, ClipMode::ClipOut), -+ (rect(20.0, 20.0, 60.0, 60.0), None, None, ClipMode::ClipOut), - ], - &mut [ - seg(0.0, 0.0, 20.0, 20.0, false, Some(EdgeMask::TOP | EdgeMask::LEFT)), -@@ -993,7 +1034,7 @@ mod test { - None, - rect(-1000.0, -1000.0, 2000.0, 2000.0), - &[ -- (rect(20.0, 20.0, 60.0, 60.0), Some(BorderRadius::uniform(10.0)), ClipMode::ClipOut), -+ (rect(20.0, 20.0, 60.0, 60.0), Some(BorderRadius::uniform(10.0)), None, ClipMode::ClipOut), - ], - &mut [ - // top row -@@ -1036,8 +1077,8 @@ mod test { - None, - rect(-1000.0, -1000.0, 2000.0, 2000.0), - &[ -- (rect(20.0, 20.0, 60.0, 60.0), None, ClipMode::Clip), -- (rect(50.0, 50.0, 80.0, 80.0), None, ClipMode::ClipOut), -+ (rect(20.0, 20.0, 60.0, 60.0), None, None, ClipMode::Clip), -+ (rect(50.0, 50.0, 80.0, 80.0), None, None, ClipMode::ClipOut), - ], - &mut [ - seg(20.0, 20.0, 50.0, 50.0, false, Some(EdgeMask::LEFT | EdgeMask::TOP)), -@@ -1054,8 +1095,8 @@ mod test { - None, - rect(0.0, 0.0, 100.0, 100.0), - &[ -- (rect(0.0, 0.0, 10.0, 10.0), None, ClipMode::ClipOut), -- (rect(0.0, 0.0, 100.0, 100.0), Some(BorderRadius::uniform(10.0)), ClipMode::Clip), -+ (rect(0.0, 0.0, 10.0, 10.0), None, None, ClipMode::ClipOut), -+ (rect(0.0, 0.0, 100.0, 100.0), Some(BorderRadius::uniform(10.0)), None, ClipMode::Clip), - ], - &mut [ - // corners -@@ -1082,8 +1123,8 @@ mod test { - None, - rect(0.0, 0.0, 100.0, 100.0), - &[ -- (rect(10.0, 10.0, 90.0, 90.0), None, ClipMode::Clip), -- (rect(0.0, 0.0, 100.0, 100.0), Some(BorderRadius::uniform(10.0)), ClipMode::Clip), -+ (rect(10.0, 10.0, 90.0, 90.0), None, None, ClipMode::Clip), -+ (rect(0.0, 0.0, 100.0, 100.0), Some(BorderRadius::uniform(10.0)), None, ClipMode::Clip), - ], - &mut [ - seg(10.0, 10.0, 90.0, 90.0, false, -@@ -1104,8 +1145,8 @@ mod test { - None, - rect(0.0, 0.0, 100.0, 100.0), - &[ -- (rect(10.0, 10.0, 90.0, 90.0), None, ClipMode::Clip), -- (rect(10.0, 10.0, 90.0, 90.0), None, ClipMode::ClipOut), -+ (rect(10.0, 10.0, 90.0, 90.0), None, None, ClipMode::Clip), -+ (rect(10.0, 10.0, 90.0, 90.0), None, None, ClipMode::ClipOut), - ], - &mut [ - ], -@@ -1119,7 +1160,7 @@ mod test { - None, - rect(0.0, 0.0, 100.0, 100.0), - &[ -- (rect(0.0, 0.0, 100.0, 90.0), None, ClipMode::ClipOut), -+ (rect(0.0, 0.0, 100.0, 90.0), None, None, ClipMode::ClipOut), - ], - &mut [ - seg(0.0, 90.0, 100.0, 100.0, false, Some( -@@ -1224,7 +1265,7 @@ mod test { - Some(rect(20.0, 40.0, 60.0, 80.0)), - rect(0.0, 0.0, 100.0, 100.0), - &[ -- (rect(0.0, 0.0, 100.0, 90.0), None, ClipMode::ClipOut), -+ (rect(0.0, 0.0, 100.0, 90.0), None, None, ClipMode::ClipOut), - ], - &mut [ - seg_region( -@@ -1262,7 +1303,7 @@ mod test { - Some(rect(20.0, 20.0, 80.0, 80.0)), - rect(0.0, 0.0, 100.0, 100.0), - &[ -- (rect(20.0, 20.0, 100.0, 100.0), None, ClipMode::ClipOut), -+ (rect(20.0, 20.0, 100.0, 100.0), None, None, ClipMode::ClipOut), - ], - &mut [ - seg_region( -@@ -1315,7 +1356,7 @@ mod test { - Some(rect(20.0, 20.0, 80.0, 80.0)), - rect(0.0, 0.0, 100.0, 100.0), - &[ -- (rect(10.0, 10.0, 30.0, 30.0), None, ClipMode::Clip), -+ (rect(10.0, 10.0, 30.0, 30.0), None, None, ClipMode::Clip), - ], - &mut [ - seg_region( -diff --git a/gfx/wr/webrender/src/tile_cache/mod.rs b/gfx/wr/webrender/src/tile_cache/mod.rs -index 3d08ff4dc3..927a8d465c 100644 ---- a/gfx/wr/webrender/src/tile_cache/mod.rs -+++ b/gfx/wr/webrender/src/tile_cache/mod.rs -@@ -1141,7 +1141,7 @@ impl TileCacheInstance { - .get_instance_from_range(&clip_chain.clips_range, i); - let clip_node = &frame_state.data_stores.clip[clip_instance.handle]; - -- if let ClipItemKind::RoundedRectangle { radius, mode } = clip_node.item.kind { -+ if let ClipItemKind::RoundedRectangle { radius, inset: _, mode } = clip_node.item.kind { - assert_eq!(mode, ClipMode::Clip); - - let radius = clamped_radius(&radius, clip_instance.clip_rect.size()); -diff --git a/gfx/wr/webrender/src/tile_cache/slice_builder.rs b/gfx/wr/webrender/src/tile_cache/slice_builder.rs -index 595377d04d..4f29370636 100644 ---- a/gfx/wr/webrender/src/tile_cache/slice_builder.rs -+++ b/gfx/wr/webrender/src/tile_cache/slice_builder.rs -@@ -569,11 +569,11 @@ fn create_tile_cache( - match clip_node_data.key.kind { - ClipItemKeyKind::ImageMask(..) | - ClipItemKeyKind::Rectangle(ClipMode::ClipOut) | -- ClipItemKeyKind::RoundedRectangle(_, ClipMode::ClipOut) => { -+ ClipItemKeyKind::RoundedRectangle(_, _, ClipMode::ClipOut) => { - // Has an image-mask or clip-out clip, we can't handle this as a shared clip - false - } -- ClipItemKeyKind::RoundedRectangle(radius, ClipMode::Clip) => { -+ ClipItemKeyKind::RoundedRectangle(radius, _, ClipMode::Clip) => { - // The shader and CoreAnimation rely on certain constraints such - // as uniform radii to be able to apply the clip during compositing. - let br = clamped_radius(&BorderRadius::from(radius), node.unsnapped_clip_rect.size()); -@@ -611,7 +611,7 @@ fn create_tile_cache( - let can_combine = match (accumulated_rounded_rect, clip_node_data.key.kind) { - ( - Some((acc_rect, acc_radius)), -- ClipItemKeyKind::RoundedRectangle(radius, ClipMode::Clip), -+ ClipItemKeyKind::RoundedRectangle(radius, _, ClipMode::Clip), - ) => { - let radius = clamped_radius(&BorderRadius::from(radius), node.unsnapped_clip_rect.size()); - intersect_rounded_rects( -@@ -632,7 +632,7 @@ fn create_tile_cache( - // Can't combine, drop children and keep only this clip. - shared_clip_node_id = current_node_id; - rounded_rect_count = 1; -- if let ClipItemKeyKind::RoundedRectangle(radius, ClipMode::Clip) = clip_node_data.key.kind { -+ if let ClipItemKeyKind::RoundedRectangle(radius, _, ClipMode::Clip) = clip_node_data.key.kind { - let radius = clamped_radius(&BorderRadius::from(radius), node.unsnapped_clip_rect.size()); - accumulated_rounded_rect = Some((node.unsnapped_clip_rect, radius)); - } -diff --git a/gfx/wr/webrender/src/util.rs b/gfx/wr/webrender/src/util.rs -index 811549d1cb..b2da95259e 100644 ---- a/gfx/wr/webrender/src/util.rs -+++ b/gfx/wr/webrender/src/util.rs -@@ -4,7 +4,7 @@ - - use api::BorderRadius; - use api::units::*; --use euclid::{Point2D, Rect, Box2D, Size2D, Vector2D, point2, point3}; -+use euclid::{Point2D, Rect, Box2D, Size2D, SideOffsets2D, Vector2D, point2, point3}; - use euclid::{default, Transform2D, Transform3D, Scale, approxeq::ApproxEq}; - use plane_split::{Clipper, Polygon}; - use std::{i32, f32, fmt, ptr}; -@@ -309,6 +309,15 @@ impl ScaleOffset { - ) - } - -+ pub fn map_side_offsets(&self, side_offsets: &SideOffsets2D) -> SideOffsets2D { -+ SideOffsets2D::new( -+ side_offsets.top * self.scale.y.abs(), -+ side_offsets.right * self.scale.x.abs(), -+ side_offsets.bottom * self.scale.y.abs(), -+ side_offsets.left * self.scale.x.abs(), -+ ) -+ } -+ - pub fn unmap_vector(&self, vector: &Vector2D) -> Vector2D { - Vector2D::new( - vector.x / self.scale.x, -@@ -682,17 +691,49 @@ pub fn pack_as_float(value: u32) -> f32 { - fn extract_inner_rect_impl( - rect: &Box2D, - radii: &BorderRadius, -+ inset: &SideOffsets2D, - k: f32, - ) -> Option> { - // `k` defines how much border is taken into account - // We enforce the offsets to be rounded to pixel boundaries - // by `ceil`-ing and `floor`-ing them - -- let xl = (k * radii.top_left.width.max(radii.bottom_left.width)).ceil(); -- let xr = (rect.width() - k * radii.top_right.width.max(radii.bottom_right.width)).floor(); -- let yt = (k * radii.top_left.height.max(radii.top_right.height)).ceil(); -+ // In case of a corner shape below "round" (superellipse parameter < 1), -+ // we need to add the inset to the radii for correctness. This will slightly -+ // overestimate the corner area for sub-ellipses (-1 < parameter < 1), but -+ // keeps the computation cheap. -+ -+ let mut top_left_width = radii.top_left.width; -+ let mut top_left_height = radii.top_left.height; -+ let mut top_right_width = radii.top_right.width; -+ let mut top_right_height = radii.top_right.height; -+ let mut bottom_left_width = radii.bottom_left.width; -+ let mut bottom_left_height = radii.bottom_left.height; -+ let mut bottom_right_width = radii.bottom_right.width; -+ let mut bottom_right_height = radii.bottom_right.height; -+ -+ if radii.shape_top_left < 1.0 { -+ top_left_width += inset.top; -+ top_left_height += inset.left; -+ } -+ if radii.shape_top_right < 1.0 { -+ top_right_width += inset.top; -+ top_right_height += inset.right; -+ } -+ if radii.shape_bottom_left < 1.0 { -+ bottom_left_width += inset.bottom; -+ bottom_left_height += inset.left; -+ } -+ if radii.shape_bottom_right < 1.0 { -+ bottom_right_width += inset.bottom; -+ bottom_right_height += inset.right; -+ } -+ -+ let xl = (k * top_left_width.max(bottom_left_width)).ceil(); -+ let xr = (rect.width() - k * top_right_width.max(bottom_right_width)).floor(); -+ let yt = (k * top_left_height.max(top_right_height)).ceil(); - let yb = -- (rect.height() - k * radii.bottom_left.height.max(radii.bottom_right.height)).floor(); -+ (rect.height() - k * bottom_left_height.max(bottom_right_height)).floor(); - - if xl <= xr && yt <= yb { - Some(Box2D::from_origin_and_size( -@@ -709,10 +750,11 @@ fn extract_inner_rect_impl( - pub fn extract_inner_rect_safe( - rect: &Box2D, - radii: &BorderRadius, -+ inset: &SideOffsets2D, - ) -> Option> { - // value of `k==1.0` is used for extraction of the corner rectangles - // see `SEGMENT_CORNER_*` in `clip_shared.glsl` -- extract_inner_rect_impl(rect, radii, 1.0) -+ extract_inner_rect_impl(rect, radii, inset, 1.0) - } - - /// Return an aligned rectangle that is inside the clip region and doesn't intersect -@@ -721,9 +763,18 @@ pub fn extract_inner_rect_safe( - pub fn extract_inner_rect_k( - rect: &Box2D, - radii: &BorderRadius, -+ inset: &SideOffsets2D, - k: f32, - ) -> Option> { -- extract_inner_rect_impl(rect, radii, k) -+ // When using corner shape, corners can go inside the shape and create -+ // clipping issues, we need the 'safe' (k == 1.0) version in that case. -+ // This could be refined by computing the superellipse half corners but -+ // would make the calculation a bit more expensive. -+ if radii.shapes_all_round() { -+ extract_inner_rect_impl(rect, radii, inset, k) -+ } else { -+ extract_inner_rect_impl(rect, radii, inset, 1.0) -+ } - } - - #[cfg(test)] -diff --git a/gfx/wr/webrender_api/src/display_item.rs b/gfx/wr/webrender_api/src/display_item.rs -index cc8b80a049..32b0f625f7 100644 ---- a/gfx/wr/webrender_api/src/display_item.rs -+++ b/gfx/wr/webrender_api/src/display_item.rs -@@ -418,6 +418,17 @@ pub struct NormalBorder { - pub top: BorderSide, - pub bottom: BorderSide, - pub radius: BorderRadius, -+ -+ /// Accumulated inset from the reference shape, in case of inflation/deflation. -+ /// The inset is expected to be already baked in rect and radii, but is necessary -+ /// for the correct computation of corner shapes. -+ /// -+ /// As borders have widths, this value should be calculated for the *outer* edge -+ /// of the border. For instance, a regular shape with a border will have an inset -+ /// of zero, but an outline of 5px, of which the outer edge is 5px away from the -+ /// original border-box, will have an inset of -5px. -+ pub inset: LayoutSideOffsets, -+ - /// Whether to apply anti-aliasing on the border corners. - /// - /// Note that for this to be `false` and work, this requires the borders to -@@ -2124,6 +2135,12 @@ pub struct ComplexClipRegion { - pub rect: LayoutRect, - /// Border radii of this rectangle. - pub radii: BorderRadius, -+ /// Accumulated inset from the reference shape, in case of inflation/deflation. -+ /// The inset is expected to be already baked in rect and radii, but is necessary -+ /// for the correct computation of corner shapes. -+ /// Example: a shape with a border of 10px should have a 10px inset applied on its -+ /// content mask. -+ pub inset: LayoutSideOffsets, - /// Whether we are clipping inside or outside - /// the region. - pub mode: ClipMode, -@@ -2214,9 +2231,10 @@ impl ComplexClipRegion { - pub fn new( - rect: LayoutRect, - radii: BorderRadius, -+ inset: LayoutSideOffsets, - mode: ClipMode, - ) -> Self { -- ComplexClipRegion { rect, radii, mode } -+ ComplexClipRegion { rect, radii, inset, mode } - } - } - -diff --git a/gfx/wr/webrender_api/src/display_list.rs b/gfx/wr/webrender_api/src/display_list.rs -index b676317c1f..c131db0eeb 100644 ---- a/gfx/wr/webrender_api/src/display_list.rs -+++ b/gfx/wr/webrender_api/src/display_list.rs -@@ -1526,8 +1526,10 @@ impl DisplayListBuilder { - .inflate(spread_amount, spread_amount); - let spatial_id = common.spatial_id; - -+ let shadow_inset = LayoutSideOffsets::new_all_same(-spread_amount); -+ - let mut clips: Vec = Vec::with_capacity(2); -- let (final_prim_rect, clip_radius) = match clip_mode { -+ let (final_prim_rect, clip_radius, clip_inset) = match clip_mode { - BoxShadowClipMode::Outset => { - if shadow_rect.is_empty() { - return; -@@ -1538,12 +1540,13 @@ impl DisplayListBuilder { - ComplexClipRegion { - rect: box_bounds, - radii: border_radius, -+ inset: LayoutSideOffsets::zero(), - mode: ClipMode::ClipOut, - }, - spread_radius, - )); - -- (shadow_rect, shadow_radius) -+ (shadow_rect, shadow_radius, shadow_inset) + /// An clip applied to a render task using the multiply blend mode on top of +diff --git a/gfx/wr/webrender/src/renderer/mod.rs b/gfx/wr/webrender/src/renderer/mod.rs +index 024e1b63c4..d177263096 100644 +--- a/gfx/wr/webrender/src/renderer/mod.rs ++++ b/gfx/wr/webrender/src/renderer/mod.rs +@@ -253,6 +253,7 @@ impl BatchKind { + BatchKind::Quad(PatternKind::Gradient) => GPU_TAG_GRADIENT, + BatchKind::Quad(PatternKind::Repeat) => GPU_TAG_REPEAT, + BatchKind::Quad(PatternKind::BoxShadow) => GPU_TAG_PRIMITIVE, ++ BatchKind::Quad(PatternKind::BoxShadowSuperellipse) => GPU_TAG_PRIMITIVE, + BatchKind::Quad(PatternKind::Yuv) => GPU_TAG_BRUSH_YUV_IMAGE, + BatchKind::Quad(PatternKind::YuvTextureExternal) => GPU_TAG_BRUSH_YUV_IMAGE, + BatchKind::Quad(PatternKind::YuvTextureExternalBT709) => GPU_TAG_BRUSH_YUV_IMAGE, +@@ -2452,6 +2453,50 @@ impl Renderer { + self.device.disable_scissor(); } - BoxShadowClipMode::Inset => { - if !shadow_rect.is_empty() { -@@ -1552,13 +1555,14 @@ impl DisplayListBuilder { - ComplexClipRegion { - rect: shadow_rect, - radii: shadow_radius, -+ inset: shadow_inset, - mode: ClipMode::ClipOut, - }, - spread_radius, - )); - } -- (box_bounds, border_radius) -+ (box_bounds, border_radius, LayoutSideOffsets::zero()) ++ if !masks.mask_instances_superellipse.is_empty() { ++ self.shaders.borrow_mut().ps_mask_superellipse().bind( ++ &mut self.device, ++ projection, ++ None, ++ &mut self.renderer_errors, ++ &mut self.profile, ++ &mut self.command_log, ++ ); ++ ++ self.draw_instanced_batch( ++ &masks.mask_instances_superellipse, ++ VertexArrayKind::Mask, ++ &BatchTextures::empty(), ++ stats, ++ ); ++ } ++ ++ if !masks.mask_instances_superellipse_with_scissor.is_empty() { ++ self.shaders.borrow_mut().ps_mask_superellipse().bind( ++ &mut self.device, ++ projection, ++ None, ++ &mut self.renderer_errors, ++ &mut self.profile, ++ &mut self.command_log, ++ ); ++ ++ self.device.enable_scissor(); ++ ++ for (scissor_rect, instances) in &masks.mask_instances_superellipse_with_scissor { ++ self.device.set_scissor_rect(draw_target.to_framebuffer_rect(*scissor_rect)); ++ ++ self.draw_instanced_batch( ++ instances, ++ VertexArrayKind::Mask, ++ &BatchTextures::empty(), ++ stats, ++ ); ++ } ++ ++ self.device.disable_scissor(); ++ } ++ + if !masks.image_mask_instances.is_empty() { + self.shaders.borrow_mut().ps_quad_textured().bind( + &mut self.device, +@@ -3351,7 +3396,9 @@ impl Renderer { + + // Draw any borders for this target. + if !target.border_segments_solid.is_empty() || +- !target.border_segments_complex.is_empty() ++ !target.border_segments_complex.is_empty() || ++ !target.border_segments_solid_superellipse.is_empty() || ++ !target.border_segments_complex_superellipse.is_empty() + { + let _timer = self.gpu_profiler.start_timer(GPU_TAG_CACHE_BORDER); + +@@ -3394,6 +3441,42 @@ impl Renderer { + ); } - }; -@@ -1568,6 +1572,7 @@ impl DisplayListBuilder { - ComplexClipRegion { - rect: final_prim_rect, - radii: clip_radius, -+ inset: clip_inset, - mode: ClipMode::Clip, - }, - 0.0, -diff --git a/gfx/wr/webrender_api/src/key_types.rs b/gfx/wr/webrender_api/src/key_types.rs -index c170551247..0b1be8f29f 100644 ---- a/gfx/wr/webrender_api/src/key_types.rs -+++ b/gfx/wr/webrender_api/src/key_types.rs -@@ -13,7 +13,7 @@ - use crate::serde::{Serialize, Deserialize}; - use crate::{ColorU, BorderRadius, BorderSide, BorderStyle, NormalBorder, RepeatMode, GradientStop, PrimitiveFlags}; - use crate::{FillRule, GlyphIndex, POLYGON_CLIP_VERTEX_MAX}; --use crate::units::{LayoutVector2D, WorldVector2D, LayoutPoint, PicturePoint, WorldPoint}; -+use crate::units::{LayoutPoint, LayoutSideOffsetsAu, LayoutVector2D, PicturePoint, WorldPoint, WorldVector2D}; - use crate::units::{LayoutSize, LayoutSizeAu, LayoutPointAu, AuHelpers, LayoutSideOffsets, DeviceIntSideOffsets}; - use euclid::{Size2D, SideOffsets2D}; - use peek_poke::PeekPoke; -@@ -198,6 +198,7 @@ pub struct NormalBorderAu { - pub top: BorderSideAu, - pub bottom: BorderSideAu, - pub radius: BorderRadiusAu, -+ pub inset: LayoutSideOffsetsAu, - /// Whether to apply anti-aliasing on the border corners. - /// - /// Note that for this to be `false` and work, this requires the borders to -@@ -225,6 +226,7 @@ impl From for NormalBorderAu { - top: border.top.into(), - bottom: border.bottom.into(), - radius: border.radius.into(), -+ inset: border.inset.to_au(), - do_aa: border.do_aa, ++ if !target.border_segments_solid_superellipse.is_empty() { ++ self.shaders.borrow_mut().cs_border_solid_superellipse().bind( ++ &mut self.device, ++ &projection, ++ None, ++ &mut self.renderer_errors, ++ &mut self.profile, ++ &mut self.command_log, ++ ); ++ ++ self.draw_instanced_batch( ++ &target.border_segments_solid_superellipse, ++ VertexArrayKind::Border, ++ &BatchTextures::empty(), ++ stats, ++ ); ++ } ++ ++ if !target.border_segments_complex_superellipse.is_empty() { ++ self.shaders.borrow_mut().cs_border_segment_superellipse().bind( ++ &mut self.device, ++ &projection, ++ None, ++ &mut self.renderer_errors, ++ &mut self.profile, ++ &mut self.command_log, ++ ); ++ ++ self.draw_instanced_batch( ++ &target.border_segments_complex_superellipse, ++ VertexArrayKind::Border, ++ &BatchTextures::empty(), ++ stats, ++ ); ++ } ++ + self.set_blend(false, FramebufferKind::Other); } + +diff --git a/gfx/wr/webrender/src/renderer/shade.rs b/gfx/wr/webrender/src/renderer/shade.rs +index 0a88c76bf8..575275d3b4 100644 +--- a/gfx/wr/webrender/src/renderer/shade.rs ++++ b/gfx/wr/webrender/src/renderer/shade.rs +@@ -67,6 +67,7 @@ pub const IMAGE_BUFFER_KINDS: [ImageBufferKind; 4] = [ + const DITHERING_FEATURE: &str = "DITHERING"; + const DUAL_SOURCE_FEATURE: &str = "DUAL_SOURCE_BLENDING"; + const FAST_PATH_FEATURE: &str = "FAST_PATH"; ++const SUPERELLIPSE_FEATURE: &str = "SUPERELLIPSE"; + + pub(crate) enum ShaderKind { + Primitive, +@@ -414,6 +415,8 @@ pub struct Shaders { + cs_blur_rgba8: ShaderHandle, + cs_border_segment: ShaderHandle, + cs_border_solid: ShaderHandle, ++ cs_border_segment_superellipse: ShaderHandle, ++ cs_border_solid_superellipse: ShaderHandle, + cs_scale: Vec>, + cs_line_decoration: ShaderHandle, + cs_svg_filter_node: ShaderHandle, +@@ -439,6 +442,7 @@ pub struct Shaders { + ps_quad_repeat: ShaderHandle, + ps_quad_gradient: ShaderHandle, + ps_quad_box_shadow: ShaderHandle, ++ ps_quad_box_shadow_superellipse: ShaderHandle, + // ps_quad_yuv, like ps_quad_textured, comes in sampler-type-specific + // variants so the YUV planes are sampled with the matching sColor + // declaration. The variant is selected via PatternKind. +@@ -451,6 +455,7 @@ pub struct Shaders { + ps_quad_mix_blend: ShaderHandle, + ps_mask: ShaderHandle, + ps_mask_fast: ShaderHandle, ++ ps_mask_superellipse: ShaderHandle, + ps_clear: ShaderHandle, + ps_copy: ShaderHandle, + +@@ -516,6 +521,13 @@ impl Shaders { + &shader_list, + )?; + ++ let ps_mask_superellipse = loader.create_shader( ++ ShaderKind::Cache(VertexArrayKind::Mask), ++ "ps_quad_mask", ++ &[SUPERELLIPSE_FEATURE], ++ &shader_list, ++ )?; ++ + let mut cs_scale = Vec::new(); + let scale_shader_num = IMAGE_BUFFER_KINDS.len(); + // PrimitiveShader is not clonable. Use push() to initialize the vec. +@@ -644,6 +656,13 @@ impl Shaders { + &shader_list, + )?; + ++ let ps_quad_box_shadow_superellipse = loader.create_shader( ++ ShaderKind::Primitive, ++ "ps_quad_box_shadow", ++ &[SUPERELLIPSE_FEATURE], ++ &shader_list, ++ )?; ++ + let ps_quad_yuv = loader.create_shader( + ShaderKind::Primitive, + "ps_quad_yuv", +@@ -759,6 +778,20 @@ impl Shaders { + &shader_list, + )?; + ++ let cs_border_segment_superellipse = loader.create_shader( ++ ShaderKind::Cache(VertexArrayKind::Border), ++ "cs_border_segment", ++ &[SUPERELLIPSE_FEATURE], ++ &shader_list, ++ )?; ++ ++ let cs_border_solid_superellipse = loader.create_shader( ++ ShaderKind::Cache(VertexArrayKind::Border), ++ "cs_border_solid", ++ &[SUPERELLIPSE_FEATURE], ++ &shader_list, ++ )?; ++ + let composite = CompositorShaders::new(device, gl_type, &mut loader)?; + + Ok(Shaders { +@@ -766,8 +799,10 @@ impl Shaders { + + cs_blur_rgba8, + cs_border_segment, +- cs_line_decoration, + cs_border_solid, ++ cs_border_segment_superellipse, ++ cs_border_solid_superellipse, ++ cs_line_decoration, + cs_scale, + cs_svg_filter_node, + ps_text_run, +@@ -779,6 +814,7 @@ impl Shaders { + ps_quad_repeat, + ps_quad_gradient, + ps_quad_box_shadow, ++ ps_quad_box_shadow_superellipse, + ps_quad_yuv, + ps_quad_yuv_external, + ps_quad_yuv_external_bt709, +@@ -788,6 +824,7 @@ impl Shaders { + ps_quad_mix_blend, + ps_mask, + ps_mask_fast, ++ ps_mask_superellipse, + ps_split_composite, + ps_clear, + ps_copy, +@@ -859,6 +896,7 @@ impl Shaders { + PatternKind::Gradient => self.ps_quad_gradient, + PatternKind::Repeat => self.ps_quad_repeat, + PatternKind::BoxShadow => self.ps_quad_box_shadow, ++ PatternKind::BoxShadowSuperellipse => self.ps_quad_box_shadow_superellipse, + PatternKind::Yuv => self.ps_quad_yuv, + PatternKind::YuvTextureExternal => self.ps_quad_yuv_external + .expect("bug: ps_quad_yuv TEXTURE_EXTERNAL variant not loaded"), +@@ -915,6 +953,9 @@ impl Shaders { + BatchKind::Quad(PatternKind::BoxShadow) => { + self.ps_quad_box_shadow + } ++ BatchKind::Quad(PatternKind::BoxShadowSuperellipse) => { ++ self.ps_quad_box_shadow_superellipse ++ } + BatchKind::Quad(PatternKind::Yuv) => { + self.ps_quad_yuv + } +@@ -958,6 +999,8 @@ impl Shaders { + pub fn cs_blur_rgba8(&mut self) -> &mut LazilyCompiledShader { self.loader.get(self.cs_blur_rgba8) } + pub fn cs_border_segment(&mut self) -> &mut LazilyCompiledShader { self.loader.get(self.cs_border_segment) } + pub fn cs_border_solid(&mut self) -> &mut LazilyCompiledShader { self.loader.get(self.cs_border_solid) } ++ pub fn cs_border_segment_superellipse(&mut self) -> &mut LazilyCompiledShader { self.loader.get(self.cs_border_segment_superellipse) } ++ pub fn cs_border_solid_superellipse(&mut self) -> &mut LazilyCompiledShader { self.loader.get(self.cs_border_solid_superellipse) } + pub fn cs_line_decoration(&mut self) -> &mut LazilyCompiledShader { self.loader.get(self.cs_line_decoration) } + pub fn cs_svg_filter_node(&mut self) -> &mut LazilyCompiledShader { self.loader.get(self.cs_svg_filter_node) } + pub fn ps_quad_textured(&mut self) -> &mut LazilyCompiledShader { +@@ -965,6 +1008,7 @@ impl Shaders { } -@@ -238,6 +240,7 @@ impl From for NormalBorder { - top: border.top.into(), - bottom: border.bottom.into(), - radius: border.radius.into(), -+ inset: LayoutSideOffsets::from_au(border.inset), - do_aa: border.do_aa, - } + pub fn ps_mask(&mut self) -> &mut LazilyCompiledShader { self.loader.get(self.ps_mask) } + pub fn ps_mask_fast(&mut self) -> &mut LazilyCompiledShader { self.loader.get(self.ps_mask_fast) } ++ pub fn ps_mask_superellipse(&mut self) -> &mut LazilyCompiledShader { self.loader.get(self.ps_mask_superellipse) } + pub fn ps_clear(&mut self) -> &mut LazilyCompiledShader { self.loader.get(self.ps_clear) } + pub fn ps_copy(&mut self) -> &mut LazilyCompiledShader { self.loader.get(self.ps_copy) } + +diff --git a/gfx/wr/webrender_build/src/shader_features.rs b/gfx/wr/webrender_build/src/shader_features.rs +index 87a60097a6..873f413557 100644 +--- a/gfx/wr/webrender_build/src/shader_features.rs ++++ b/gfx/wr/webrender_build/src/shader_features.rs +@@ -67,17 +67,22 @@ pub fn get_shader_features(flags: ShaderFeatureFlags) -> ShaderFeatures { + // Cache shaders + shaders.insert("cs_blur", vec!["ALPHA_TARGET".to_string(), "COLOR_TARGET".to_string()]); + +- shaders.insert("ps_quad_mask", vec![String::new(), "FAST_PATH".to_string()]); ++ shaders.insert("ps_quad_mask", vec![String::new(), "FAST_PATH".to_string(), "SUPERELLIPSE".to_string()]); + + for name in &[ + "cs_line_decoration", +- "cs_border_segment", +- "cs_border_solid", + "cs_svg_filter_node", + ] { + shaders.insert(name, vec![String::new()]); } -diff --git a/gfx/wr/wrench/reftests/border/border-shape-2.png b/gfx/wr/wrench/reftests/border/border-shape-2.png -new file mode 100644 -index 0000000000..e69de29bb2 + ++ for name in &[ ++ "cs_border_segment", ++ "cs_border_solid", ++ ] { ++ shaders.insert(name, vec![String::new(), "SUPERELLIPSE".to_string()]); ++ } ++ + let mut base_prim_features = FeatureList::new(); + + let mut texture_types = vec!["TEXTURE_2D"]; +@@ -168,7 +173,7 @@ pub fn get_shader_features(flags: ShaderFeatureFlags) -> ShaderFeatures { + + shaders.insert("ps_quad_repeat", vec![base_prim_features.finish()]); + +- shaders.insert("ps_quad_box_shadow", vec![base_prim_features.finish()]); ++ shaders.insert("ps_quad_box_shadow", vec![base_prim_features.finish(), "SUPERELLIPSE".to_string()]); + + // Like ps_quad_textured, ps_quad_yuv needs per-texture-kind variants so that + // the YUV planes are sampled with the correct sampler type. diff --git a/gfx/wr/wrench/reftests/border/border-shape-2.yaml b/gfx/wr/wrench/reftests/border/border-shape-2.yaml new file mode 100644 index 0000000000..44f12d74f3 @@ -2475,9 +1424,6 @@ index 0000000000..44f12d74f3 + style: double + color: white + radius: [ 16, 16, 16, 16, 2.0, -2.0, 2.0, 2.0 ] -diff --git a/gfx/wr/wrench/reftests/border/border-shape-3.png b/gfx/wr/wrench/reftests/border/border-shape-3.png -new file mode 100644 -index 0000000000..e69de29bb2 diff --git a/gfx/wr/wrench/reftests/border/border-shape-3.yaml b/gfx/wr/wrench/reftests/border/border-shape-3.yaml new file mode 100644 index 0000000000..5085c083dc @@ -2500,9 +1446,6 @@ index 0000000000..5085c083dc + style: solid + color: white + radius: [ 60, 30, [100, 50], [100, 50], 0.2, 0.0, -Inf, -0.7 ] -diff --git a/gfx/wr/wrench/reftests/border/border-shape-4.png b/gfx/wr/wrench/reftests/border/border-shape-4.png -new file mode 100644 -index 0000000000..e69de29bb2 diff --git a/gfx/wr/wrench/reftests/border/border-shape-4.yaml b/gfx/wr/wrench/reftests/border/border-shape-4.yaml new file mode 100644 index 0000000000..27c9b2fd9d @@ -2534,9 +1477,6 @@ index 0000000000..27c9b2fd9d + style: solid + color: [[0, 1, 0, 0.2], [1, 0, 0, 0.3], [0, 0, 1, 0.2], [1, 1, 0, 0.3]] + radius: [ 0, 30, 60, 0, 0.0, 0.0, 0.0, 0.0 ] -diff --git a/gfx/wr/wrench/reftests/border/border-shape-inset.png b/gfx/wr/wrench/reftests/border/border-shape-inset.png -new file mode 100644 -index 0000000000..e69de29bb2 diff --git a/gfx/wr/wrench/reftests/border/border-shape-inset.yaml b/gfx/wr/wrench/reftests/border/border-shape-inset.yaml new file mode 100644 index 0000000000..4b6e51ee3e @@ -2585,9 +1525,6 @@ index 0000000000..4b6e51ee3e + shape-bottom-right: -0.7, + } + inset: [ 20, 24, 12, 4.8 ] -diff --git a/gfx/wr/wrench/reftests/border/border-shape.png b/gfx/wr/wrench/reftests/border/border-shape.png -new file mode 100644 -index 0000000000..e69de29bb2 diff --git a/gfx/wr/wrench/reftests/border/border-shape.yaml b/gfx/wr/wrench/reftests/border/border-shape.yaml new file mode 100644 index 0000000000..c0ac275110 @@ -3014,10 +1951,10 @@ index 0000000000..c0ac275110 + color: [ red, green, blue, black ] + radius: [50, -2.3] diff --git a/gfx/wr/wrench/reftests/border/reftest.list b/gfx/wr/wrench/reftests/border/reftest.list -index 12fbb462d6..bbb793ebb1 100644 +index 98550ba96a..8813005879 100644 --- a/gfx/wr/wrench/reftests/border/reftest.list +++ b/gfx/wr/wrench/reftests/border/reftest.list -@@ -37,3 +37,8 @@ platform(linux,mac) == border-dashed-dotted-caching.yaml border-dashed-dotted-ca +@@ -39,3 +39,8 @@ scale(*) != small-inset-outset.yaml small-inset-outset-notref.yaml fuzzy(1,90) == no-aa.yaml green-square.yaml fuzzy(1,20) skip_on(android,device) == border-double-1px.yaml border-double-1px-ref.yaml # Fails on Pixel2 == max-scale.yaml max-scale-ref.yaml @@ -3026,729 +1963,32 @@ index 12fbb462d6..bbb793ebb1 100644 +platform(linux,mac) == border-shape-3.yaml border-shape-3.png +platform(linux,mac) == border-shape-4.yaml border-shape-4.png +platform(linux,mac) == border-shape-inset.yaml border-shape-inset.png -diff --git a/gfx/wr/wrench/reftests/boxshadow/box-shadow-spread-shape.png b/gfx/wr/wrench/reftests/boxshadow/box-shadow-spread-shape.png -new file mode 100644 -index 0000000000..e69de29bb2 -diff --git a/gfx/wr/wrench/reftests/boxshadow/box-shadow-spread-shape.yaml b/gfx/wr/wrench/reftests/boxshadow/box-shadow-spread-shape.yaml -new file mode 100644 -index 0000000000..a2c25ca419 ---- /dev/null -+++ b/gfx/wr/wrench/reftests/boxshadow/box-shadow-spread-shape.yaml -@@ -0,0 +1,128 @@ -+# Test box shadows with inset and outset superellipses. -+# -+# Some tests are disabled as they don't render correctly (yet). -+--- -+root: -+ items: -+ - type: stacking-context -+ bounds: [0, 0, 1000, 1000] -+ items: -+ - type: box-shadow -+ bounds: [ 20, 20, 80, 80 ] -+ color: blue -+ clip-mode: inset -+ spread-radius: 10 -+ border-radius: [ 0, 20, 20, 20, 1.0, -0.7, -Inf, 0.0 ] -+ -+ - type: box-shadow -+ bounds: [ 120, 20, 80, 80 ] -+ color: blue -+ clip-mode: inset -+ spread-radius: 10 -+ border-radius: [ [10, 40], [40, 10], 25, 25, 0.4, -0.7, -Inf, 0.0 ] -+ -+ # - type: box-shadow -+ # bounds: [ 220, 20, 80, 80 ] -+ # color: blue -+ # clip-mode: inset -+ # spread-radius: 10 -+ # border-radius: [ 0, 10, 10, 10, 1.0, -0.7, -Inf, 0.0 ] -+ -+ # - type: box-shadow -+ # bounds: [ 320, 20, 80, 80 ] -+ # color: blue -+ # clip-mode: inset -+ # spread-radius: 10 -+ # border-radius: [ 4, 4, 4, 4, 1.0, -0.7, -Inf, 0.0 ] -+ -+ - type: box-shadow -+ bounds: [ 30, 130, 60, 60 ] -+ color: red -+ clip-mode: outset -+ spread-radius: 10 -+ border-radius: [ 0, 20, 20, 20, 1.0, -0.7, -Inf, 0.0 ] -+ -+ - type: box-shadow -+ bounds: [ 130, 130, 60, 60 ] -+ color: red -+ clip-mode: outset -+ spread-radius: 10 -+ border-radius: [ [10, 40], [40, 10], 25, 25, 0.4, -0.7, -Inf, 0.0 ] -+ -+ - type: box-shadow -+ bounds: [ 230, 130, 60, 60 ] -+ color: red -+ clip-mode: outset -+ spread-radius: 10 -+ border-radius: [ 0, 10, 10, 10, 1.0, -0.7, -Inf, 0.0 ] -+ -+ - type: box-shadow -+ bounds: [ 330, 130, 60, 60 ] -+ color: red -+ clip-mode: outset -+ spread-radius: 10 -+ border-radius: [ 4, 4, 4, 4, 1.0, -0.7, -Inf, 0.0 ] -+ -+ - type: box-shadow -+ bounds: [ 20, 220, 80, 80 ] -+ color: blue -+ clip-mode: inset -+ spread-radius: 10 -+ blur-radius: 1 -+ border-radius: [ 0, 20, 20, 20, 1.0, -0.7, -Inf, 0.0 ] -+ -+ - type: box-shadow -+ bounds: [ 120, 220, 80, 80 ] -+ color: blue -+ clip-mode: inset -+ spread-radius: 10 -+ blur-radius: 1 -+ border-radius: [ 25, 25, 25, 25, 1.0, -0.7, -Inf, 0.0 ] -+ -+ # - type: box-shadow -+ # bounds: [ 220, 220, 80, 80 ] -+ # color: blue -+ # clip-mode: inset -+ # spread-radius: 10 -+ # blur-radius: 1 -+ # border-radius: [ 0, 10, 10, 10, 1.0, -0.7, -Inf, 0.0 ] -+ -+ # - type: box-shadow -+ # bounds: [ 320, 220, 80, 80 ] -+ # color: blue -+ # clip-mode: inset -+ # spread-radius: 10 -+ # blur-radius: 1 -+ # border-radius: [ 4, 4, 4, 4, 1.0, -0.7, -Inf, 0.0 ] -+ -+ - type: box-shadow -+ bounds: [ 30, 330, 60, 60 ] -+ color: red -+ clip-mode: outset -+ spread-radius: 10 -+ blur-radius: 1 -+ border-radius: [ 0, 20, 20, 20, 1.0, -0.7, -Inf, 0.0 ] -+ -+ - type: box-shadow -+ bounds: [ 130, 330, 60, 60 ] -+ color: red -+ clip-mode: outset -+ spread-radius: 10 -+ blur-radius: 1 -+ border-radius: [ 25, 25, 25, 25, 1.0, -0.7, -Inf, 0.0 ] -+ -+ - type: box-shadow -+ bounds: [ 230, 330, 60, 60 ] -+ color: red -+ clip-mode: outset -+ spread-radius: 10 -+ blur-radius: 1 -+ border-radius: [ 0, 10, 10, 10, 1.0, -0.7, -Inf, 0.0 ] -+ -+ - type: box-shadow -+ bounds: [ 330, 330, 60, 60 ] -+ color: red -+ clip-mode: outset -+ spread-radius: 10 -+ blur-radius: 1 -+ border-radius: [ 4, 4, 4, 4, 1.0, -0.7, -Inf, 0.0 ] -diff --git a/gfx/wr/wrench/reftests/boxshadow/reftest.list b/gfx/wr/wrench/reftests/boxshadow/reftest.list -index 19ac776670..f9e48b54e1 100644 ---- a/gfx/wr/wrench/reftests/boxshadow/reftest.list -+++ b/gfx/wr/wrench/reftests/boxshadow/reftest.list -@@ -18,6 +18,7 @@ fuzzy(1,8) == box-shadow-large-blur-radius.yaml box-shadow-large-blur-radius-ref - fuzzy(1,6388) == rounding.yaml rounding-ref.yaml - platform(linux,mac) == box-shadow-border-radii.yaml box-shadow-border-radii.png - platform(linux,mac) == box-shadow-corner-shape.yaml box-shadow-corner-shape.png -+platform(linux,mac) == box-shadow-spread-shape.yaml box-shadow-spread-shape.png - skip_on(android) fuzzy-if(platform(swgl),9,34) == box-shadow-spread.yaml box-shadow-spread.png # Too wide for Android - == box-shadow-spread-radii.yaml box-shadow-spread-radii-ref.yaml - == invalid.yaml invalid-ref.yaml -diff --git a/gfx/wr/wrench/reftests/mask/reftest.list b/gfx/wr/wrench/reftests/mask/reftest.list -index b3e837f887..0e4792f2c9 100644 ---- a/gfx/wr/wrench/reftests/mask/reftest.list -+++ b/gfx/wr/wrench/reftests/mask/reftest.list -@@ -17,3 +17,5 @@ skip_on(android,device) fuzzy(2,1900) == checkerboard.yaml checkerboard-tiling.y - 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 -+platform(linux,mac) == shaped-corners-inset.yaml shaped-corners-inset.png -+platform(linux,mac) == shaped-corners-outset.yaml shaped-corners-outset.png -diff --git a/gfx/wr/wrench/reftests/mask/shaped-corners-inset.png b/gfx/wr/wrench/reftests/mask/shaped-corners-inset.png -new file mode 100644 -index 0000000000..e69de29bb2 -diff --git a/gfx/wr/wrench/reftests/mask/shaped-corners-inset.yaml b/gfx/wr/wrench/reftests/mask/shaped-corners-inset.yaml -new file mode 100644 -index 0000000000..0ac5e071e9 ---- /dev/null -+++ b/gfx/wr/wrench/reftests/mask/shaped-corners-inset.yaml -@@ -0,0 +1,22 @@ -+--- -+root: -+ items: -+ - type: clip -+ id: 2 -+ complex: -+ - rect: [10, 10, 200, 200] -+ radius: [100, 100, 100, 100, 8.2, 0, -2.8, +Inf] -+ - type: rect -+ clip-chain: [2] -+ bounds: [10, 10, 200, 200] -+ color: blue -+ - type: clip -+ id: 4 -+ complex: -+ - rect: [30, 30, 160, 160] -+ radius: [80, 80, 80, 80, 8.2, 0, -2.8, +Inf] -+ inset: [20, 20, 20, 20] -+ - type: rect -+ clip-chain: [4] -+ bounds: [30, 30, 160, 160] -+ color: cyan -diff --git a/gfx/wr/wrench/reftests/mask/shaped-corners-outset.png b/gfx/wr/wrench/reftests/mask/shaped-corners-outset.png -new file mode 100644 -index 0000000000..e69de29bb2 -diff --git a/gfx/wr/wrench/reftests/mask/shaped-corners-outset.yaml b/gfx/wr/wrench/reftests/mask/shaped-corners-outset.yaml -new file mode 100644 -index 0000000000..0173a3be60 ---- /dev/null -+++ b/gfx/wr/wrench/reftests/mask/shaped-corners-outset.yaml -@@ -0,0 +1,22 @@ -+--- -+root: -+ items: -+ - type: clip -+ id: 2 -+ complex: -+ - rect: [10, 10, 200, 200] -+ radius: [100, 100, 100, 100, 8.2, 0, -2.8, +Inf] -+ inset: [-20, -20, -20, -20] -+ - type: rect -+ clip-chain: [2] -+ bounds: [10, 10, 200, 200] -+ color: cyan -+ - type: clip -+ id: 4 -+ complex: -+ - rect: [30, 30, 160, 160] -+ radius: [80, 80, 80, 80, 8.2, 0, -2.8, +Inf] -+ - type: rect -+ clip-chain: [4] -+ bounds: [30, 30, 160, 160] -+ color: blue -diff --git a/gfx/wr/wrench/reftests/mask/shaped-corners.yaml b/gfx/wr/wrench/reftests/mask/shaped-corners.yaml -index 9e84cea937..7e96191039 100644 ---- a/gfx/wr/wrench/reftests/mask/shaped-corners.yaml -+++ b/gfx/wr/wrench/reftests/mask/shaped-corners.yaml -@@ -5,7 +5,7 @@ root: - id: 2 - complex: - - rect: [10, 10, 200, 200] -- radius: [200, 200, 200, 200, 8.2, 0, -2.8, +Inf] -+ radius: [100, 100, 100, 100, 8.2, 0, -2.8, +Inf] - - type: rect - clip-chain: [2] - bounds: [10, 10, 200, 200] -diff --git a/gfx/wr/wrench/src/rawtest.rs b/gfx/wr/wrench/src/rawtest.rs -index 9420b1a6a2..a09c5418dd 100644 ---- a/gfx/wr/wrench/src/rawtest.rs -+++ b/gfx/wr/wrench/src/rawtest.rs -@@ -1359,6 +1359,7 @@ impl<'a> RawtestHarness<'a> { - ComplexClipRegion::new( - *rect, - BorderRadius::uniform_size(LayoutSize::new(radius, radius)), -+ LayoutSideOffsets::zero(), - ClipMode::Clip - ) - }; -diff --git a/gfx/wr/wrench/src/yaml_frame_reader.rs b/gfx/wr/wrench/src/yaml_frame_reader.rs -index f6b638667e..fe128b87e1 100644 ---- a/gfx/wr/wrench/src/yaml_frame_reader.rs -+++ b/gfx/wr/wrench/src/yaml_frame_reader.rs -@@ -1100,6 +1100,9 @@ impl YamlFrameReader { - let radius = item["radius"] - .as_border_radius() - .unwrap_or_else(BorderRadius::zero); -+ let inset = item["inset"] -+ .as_side_offsets() -+ .unwrap_or_else(LayoutSideOffsets::zero); +diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.cpp +index 9aed0b1841..221e296ade 100644 +--- a/layout/painting/nsDisplayList.cpp ++++ b/layout/painting/nsDisplayList.cpp +@@ -4601,8 +4601,8 @@ bool nsDisplayBoxShadowOuter::CreateWebRenderCommands( + nscoord spread = shadow.spread.ToAppUnits(); + float spreadRadius = float(spread) / float(appUnitsPerDevPixel); - let colors = broadcast(&colors, 4); - let styles = broadcast(&styles, 4); -@@ -1127,6 +1130,7 @@ impl YamlFrameReader { - bottom, - right, - radius, -+ inset, - do_aa, - })) - } -diff --git a/gfx/wr/wrench/src/yaml_helper.rs b/gfx/wr/wrench/src/yaml_helper.rs -index 757e53f5ba..dbe57b63ec 100644 ---- a/gfx/wr/wrench/src/yaml_helper.rs -+++ b/gfx/wr/wrench/src/yaml_helper.rs -@@ -20,6 +20,7 @@ pub trait YamlHelper { - fn as_pipeline_id(&self) -> Option; - fn as_rect(&self) -> Option; - fn as_size(&self) -> Option; -+ fn as_side_offsets(&self) -> Option; - fn as_point(&self) -> Option; - fn as_vector(&self) -> Option; - fn as_matrix4d(&self) -> Option; -@@ -290,6 +291,18 @@ impl YamlHelper for Yaml { - None - } +- wr::BorderRadius borderRadius{}; +- wr::BorderRadius shadowRadius{}; ++ wr::BorderRadius borderRadius = wr::EmptyBorderRadius(); ++ wr::BorderRadius shadowRadius = wr::EmptyBorderRadius(); + if (hasBorderRadius) { + borderRadius = wr::ToBorderRadius(borderRadii); + if (spreadRadius) { +@@ -4722,8 +4722,8 @@ void nsDisplayBoxShadowInner::CreateInsetBoxShadowWebRenderCommands( + nscoord spread = shadow.spread.ToAppUnits(); + float spreadRadius = spread / float(appUnitsPerDevPixel); -+ fn as_side_offsets(&self) -> Option { -+ self.as_vec_f32().and_then(|v| match v.as_slice() { -+ &[top, right, bottom, left] => Some(LayoutSideOffsets::new( -+ top, -+ right, -+ bottom, -+ left -+ )), -+ _ => None, -+ }) -+ } -+ - fn as_point(&self) -> Option { - if self.is_badvalue() { - return None; -@@ -1020,10 +1033,13 @@ impl YamlHelper for Yaml { - let radius = self["radius"] - .as_border_radius() - .unwrap_or_else(BorderRadius::zero); -+ let inset = self["inset"] -+ .as_side_offsets() -+ .unwrap_or_else(LayoutSideOffsets::zero); - let mode = self["clip-mode"] - .as_clip_mode() - .unwrap_or(ClipMode::Clip); -- ComplexClipRegion::new(rect, radius, mode) -+ ComplexClipRegion::new(rect, radius, inset, mode) - } - - fn as_sticky_offset_bounds(&self) -> StickyOffsetBounds { -diff --git a/layout/forms/nsFieldSetFrame.cpp b/layout/forms/nsFieldSetFrame.cpp -index 94cf571c63..c04a5586c7 100644 ---- a/layout/forms/nsFieldSetFrame.cpp -+++ b/layout/forms/nsFieldSetFrame.cpp -@@ -175,6 +175,7 @@ bool nsDisplayFieldSetBorder::CreateWebRenderCommands( - LayoutDeviceRect::FromAppUnits(legendRect, appUnitsPerDevPixel)); - region.mode = wr::ClipMode::ClipOut; - region.radii = wr::EmptyBorderRadius(); -+ region.inset = wr::EmptyLayoutSideOffsets(); - - std::array clips = { - aBuilder.DefineRectClip(Nothing(), layoutRect), -diff --git a/layout/generic/nsHTMLCanvasFrame.cpp b/layout/generic/nsHTMLCanvasFrame.cpp -index ef576417f4..79e26bb9e9 100644 ---- a/layout/generic/nsHTMLCanvasFrame.cpp -+++ b/layout/generic/nsHTMLCanvasFrame.cpp -@@ -446,13 +446,14 @@ void nsHTMLCanvasFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, - if (!clipAxes.isEmpty()) { - nsRect clipRect; - nsRectCornerRadii radii; -+ nsMargin inset; - bool haveRadii = -- ComputeOverflowClipRectRelativeToSelf(clipAxes, clipRect, radii); -+ ComputeOverflowClipRectRelativeToSelf(clipAxes, clipRect, radii, inset); - if (haveRadii || - nsStyleUtil::ObjectPropsMightCauseOverflow(StylePosition())) { - clipState.ClipContainingBlockDescendants( - clipRect + aBuilder->ToReferenceFrame(this), -- haveRadii ? &radii : nullptr); -+ haveRadii ? &radii : nullptr, haveRadii ? &inset : nullptr); - } - } - -diff --git a/layout/generic/nsIFrame.cpp b/layout/generic/nsIFrame.cpp -index 4cf6fc45bc..929b92cc0b 100644 ---- a/layout/generic/nsIFrame.cpp -+++ b/layout/generic/nsIFrame.cpp -@@ -2758,11 +2758,12 @@ static void ApplyOverflowClipping( - DisplayListClipState::AutoClipMultiple& aClipState) { - nsRect clipRect; - nsRectCornerRadii radii; -- bool haveRadii = -- aFrame->ComputeOverflowClipRectRelativeToSelf(aClipAxes, clipRect, radii); -+ nsMargin inset; -+ bool haveRadii = aFrame->ComputeOverflowClipRectRelativeToSelf( -+ aClipAxes, clipRect, radii, inset); - aClipState.ClipContainingBlockDescendantsExtra( - clipRect + aBuilder->ToReferenceFrame(aFrame), -- haveRadii ? &radii : nullptr); -+ haveRadii ? &radii : nullptr, haveRadii ? &inset : nullptr); - } - - static Sides ToSkipSides(PhysicalAxes aClipAxes) { -@@ -2780,7 +2781,7 @@ static Sides ToSkipSides(PhysicalAxes aClipAxes) { - - bool nsIFrame::ComputeOverflowClipRectRelativeToSelf( - const PhysicalAxes aClipAxes, nsRect& aOutRect, -- nsRectCornerRadii& aOutRadii) const { -+ nsRectCornerRadii& aOutRadii, nsMargin& aOutInset) const { - // Only 'clip' is handled here (and 'hidden' for table frames, and any - // non-'visible' value for blocks in a paginated context). - // We allow 'clip' to apply to any kind of frame. This is required by -@@ -2789,6 +2790,7 @@ bool nsIFrame::ComputeOverflowClipRectRelativeToSelf( - MOZ_ASSERT(ShouldApplyOverflowClipping(StyleDisplay()) == aClipAxes); - auto boxMargin = OverflowClipMargin(aClipAxes, /* aAllowNegative = */ true); - boxMargin.ApplySkipSides(GetSkipSides() | ToSkipSides(aClipAxes)); -+ aOutInset = -boxMargin; - - aOutRect = nsRect(nsPoint(), GetSize()); - aOutRect.Inflate(boxMargin); -diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h -index 6adcd089bd..c21e362a3d 100644 ---- a/layout/generic/nsIFrame.h -+++ b/layout/generic/nsIFrame.h -@@ -3268,7 +3268,7 @@ class nsIFrame : public nsQueryFrame { - */ - bool ComputeOverflowClipRectRelativeToSelf( - const mozilla::PhysicalAxes aClipAxes, nsRect& aOutRect, -- nsRectCornerRadii& aOutRadii) const; -+ nsRectCornerRadii& aOutRadii, nsMargin& aOutInset) const; - - // Returns the applicable overflow-clip-margin values relative to our - // border-box. If aAllowNegative is false, prevents us from returning margins -diff --git a/layout/generic/nsImageFrame.cpp b/layout/generic/nsImageFrame.cpp -index c370ac05df..6bd849615e 100644 ---- a/layout/generic/nsImageFrame.cpp -+++ b/layout/generic/nsImageFrame.cpp -@@ -2652,11 +2652,12 @@ void nsImageFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, - if (!clipAxes.isEmpty()) { - nsRect clipRect; - nsRectCornerRadii radii; -+ nsMargin inset; - bool haveRadii = -- ComputeOverflowClipRectRelativeToSelf(clipAxes, clipRect, radii); -+ ComputeOverflowClipRectRelativeToSelf(clipAxes, clipRect, radii, inset); - clipState.ClipContainingBlockDescendants( - clipRect + aBuilder->ToReferenceFrame(this), -- haveRadii ? &radii : nullptr); -+ haveRadii ? &radii : nullptr, haveRadii ? &inset : nullptr); - } - - if (!mComputedSize.IsEmpty()) { -diff --git a/layout/generic/nsVideoFrame.cpp b/layout/generic/nsVideoFrame.cpp -index 32c65b7e56..a06864a72d 100644 ---- a/layout/generic/nsVideoFrame.cpp -+++ b/layout/generic/nsVideoFrame.cpp -@@ -726,8 +726,9 @@ void nsVideoFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, - if (!clipAxes.isEmpty()) { - nsRect clipRect; - nsRectCornerRadii radii; -+ nsMargin inset; - const bool haveRadii = -- ComputeOverflowClipRectRelativeToSelf(clipAxes, clipRect, radii); -+ ComputeOverflowClipRectRelativeToSelf(clipAxes, clipRect, radii, inset); - // NOTE: If we're displaying a poster image (instead of video data), we can - // trust the nsImageFrame to constrain its drawing to its content rect - // (which happens to be the same as our content rect). -@@ -737,7 +738,7 @@ void nsVideoFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, - if (haveRadii || canOverflowWithoutRadii) { - clipState.ClipContainingBlockDescendants( - clipRect + aBuilder->ToReferenceFrame(this), -- haveRadii ? &radii : nullptr); -+ haveRadii ? &radii : nullptr, haveRadii ? &inset : nullptr); - } - } - -diff --git a/layout/painting/DisplayItemClip.cpp b/layout/painting/DisplayItemClip.cpp -index b182e257b7..f0b911b62c 100644 ---- a/layout/painting/DisplayItemClip.cpp -+++ b/layout/painting/DisplayItemClip.cpp -@@ -23,12 +23,14 @@ namespace mozilla { - void DisplayItemClip::SetTo(const nsRect& aRect) { SetTo(aRect, nullptr); } - - void DisplayItemClip::SetTo(const nsRect& aRect, -- const nsRectCornerRadii* aRadii) { -+ const nsRectCornerRadii* aRadii, -+ const nsMargin* aInset) { - mHaveClipRect = true; - mClipRect = aRect; - if (aRadii) { - mRoundedClipRects.Clear(); -- mRoundedClipRects.AppendElement(RoundedRect{aRect, *aRadii}); -+ mRoundedClipRects.AppendElement( -+ RoundedRect{aRect, *aRadii, aInset ? *aInset : nsMargin()}); - } else { - mRoundedClipRects.Clear(); - } -@@ -480,8 +482,9 @@ void DisplayItemClip::ToComplexClipRegions( - int32_t aAppUnitsPerDevPixel, - nsTArray& aOutArray) const { - for (const auto& clipRect : mRoundedClipRects) { -- aOutArray.AppendElement(wr::ToComplexClipRegion( -- clipRect.mRect, clipRect.mRadii, aAppUnitsPerDevPixel)); -+ aOutArray.AppendElement( -+ wr::ToComplexClipRegion(clipRect.mRect, clipRect.mRadii, -+ clipRect.mInset, aAppUnitsPerDevPixel)); - } - } - -diff --git a/layout/painting/DisplayItemClip.h b/layout/painting/DisplayItemClip.h -index 9e9b394fc2..a068f0098c 100644 ---- a/layout/painting/DisplayItemClip.h -+++ b/layout/painting/DisplayItemClip.h -@@ -6,6 +6,7 @@ - #define DISPLAYITEMCLIP_H_ - - #include "mozilla/AlreadyAddRefed.h" -+#include "nsMargin.h" - #include "nsRect.h" - #include "nsTArray.h" - -@@ -45,6 +46,13 @@ class DisplayItemClip { - // Indices into mRadii are the HalfCorner values in gfx/2d/Types.h - nsRectCornerRadii mRadii; - -+ // This is here to keep track of how much the rounded rect was -+ // inflated or shrunk relative to its reference frame. It is -+ // necessary for the correct computation of contoured superellipses -+ // used by CSS corner-shape (see bug 2058091). -+ // Spec: https://drafts.csswg.org/css-borders/#contour-path -+ nsMargin mInset; -+ - RoundedRect operator+(const nsPoint& aOffset) const { - RoundedRect r = *this; - r.mRect += aOffset; -@@ -68,7 +76,8 @@ class DisplayItemClip { - DisplayItemClip() : mHaveClipRect(false) {} - - void SetTo(const nsRect& aRect); -- void SetTo(const nsRect& aRect, const nsRectCornerRadii* aRadii); -+ void SetTo(const nsRect& aRect, const nsRectCornerRadii* aRadii, -+ const nsMargin* aInset = nullptr); - void SetTo(const nsRect& aRect, const nsRect& aRoundedRect, - const nsRectCornerRadii* aRadii); - void IntersectWith(const DisplayItemClip& aOther); -diff --git a/layout/painting/DisplayListClipState.cpp b/layout/painting/DisplayListClipState.cpp -index ff3b8b42d5..27b5da6f79 100644 ---- a/layout/painting/DisplayListClipState.cpp -+++ b/layout/painting/DisplayListClipState.cpp -@@ -66,9 +66,10 @@ static void ApplyClip(nsDisplayListBuilder* aBuilder, - - void DisplayListClipState::ClipContainingBlockDescendants( - nsDisplayListBuilder* aBuilder, const nsRect& aRect, -- const nsRectCornerRadii* aRadii, DisplayItemClipChain& aClipChainOnStack) { -+ const nsRectCornerRadii* aRadii, const nsMargin* aInset, -+ DisplayItemClipChain& aClipChainOnStack) { - if (aRadii) { -- aClipChainOnStack.mClip.SetTo(aRect, aRadii); -+ aClipChainOnStack.mClip.SetTo(aRect, aRadii, aInset); - } else { - aClipChainOnStack.mClip.SetTo(aRect); - } -@@ -142,7 +143,7 @@ void DisplayListClipState::ClipContainingBlockDescendantsToContentBox( - // If we have a border-radius, we have to clip our content to that - // radius. - ClipContainingBlockDescendants(aBuilder, clipRect, -- hasBorderRadius ? &radii : nullptr, -+ hasBorderRadius ? &radii : nullptr, nullptr, - aClipChainOnStack); - } - -diff --git a/layout/painting/DisplayListClipState.h b/layout/painting/DisplayListClipState.h -index 5e1a8930b5..0e77f993a5 100644 ---- a/layout/painting/DisplayListClipState.h -+++ b/layout/painting/DisplayListClipState.h -@@ -76,6 +76,7 @@ class DisplayListClipState { - void ClipContainingBlockDescendants(nsDisplayListBuilder* aBuilder, - const nsRect& aRect, - const nsRectCornerRadii* aRadii, -+ const nsMargin* aInset, - DisplayItemClipChain& aClipChainOnStack); - - void ClipToDisplayPort(nsDisplayListBuilder* aBuilder, const nsRect& aRect, -@@ -165,14 +166,16 @@ class DisplayListClipState::AutoSaveRestore { - * mClipContainingBlockDescendants and sets mClipContainingBlockDescendants to - * the result, stored in aClipOnStack. - */ -- void ClipContainingBlockDescendants( -- const nsRect& aRect, const nsRectCornerRadii* aRadii = nullptr) { -+ void ClipContainingBlockDescendants(const nsRect& aRect, -+ const nsRectCornerRadii* aRadii = nullptr, -+ const nsMargin* aInset = nullptr) { - NS_ASSERTION(!mRestored, "Already restored!"); - NS_ASSERTION(!mClipUsed, "mClip already used"); - #ifdef DEBUG - mClipUsed = true; - #endif -- mState.ClipContainingBlockDescendants(mBuilder, aRect, aRadii, mClipChain); -+ mState.ClipContainingBlockDescendants(mBuilder, aRect, aRadii, aInset, -+ mClipChain); - } - - void ClipToDisplayPort(const nsRect& aRect) { -@@ -287,13 +290,14 @@ class DisplayListClipState::AutoClipMultiple : public AutoSaveRestore { - * the result, stored in aClipOnStack. - */ - void ClipContainingBlockDescendantsExtra(const nsRect& aRect, -- const nsRectCornerRadii* aRadii) { -+ const nsRectCornerRadii* aRadii, -+ const nsMargin* aInset) { - NS_ASSERTION(!mRestored, "Already restored!"); - NS_ASSERTION(!mExtraClipUsed, "mExtraClip already used"); - #ifdef DEBUG - mExtraClipUsed = true; - #endif -- mState.ClipContainingBlockDescendants(mBuilder, aRect, aRadii, -+ mState.ClipContainingBlockDescendants(mBuilder, aRect, aRadii, aInset, - mExtraClipChain); - } - -diff --git a/layout/painting/nsCSSRendering.cpp b/layout/painting/nsCSSRendering.cpp -index a1d5268ef6..523c1b0694 100644 ---- a/layout/painting/nsCSSRendering.cpp -+++ b/layout/painting/nsCSSRendering.cpp -@@ -798,7 +798,8 @@ static nsCSSBorderRenderer ConstructBorderRenderer( - - return nsCSSBorderRenderer( - aPresContext, aDrawTarget, dirtyRect, joinedBorderAreaPx, borderStyles, -- borderWidths, bgRadii, borderColors, !aForFrame->BackfaceIsHidden(), -+ borderWidths, bgRadii, /* aInset = */ Margin(), borderColors, -+ !aForFrame->BackfaceIsHidden(), - *aNeedsClip ? Some(NSRectToRect(aBorderArea, oneDevPixel)) : Nothing()); - } - -@@ -984,6 +985,8 @@ nsCSSRendering::CreateBorderRendererForNonThemedOutline( - Float(width) / oneDevPixel, Float(width) / oneDevPixel, - Float(width) / oneDevPixel, Float(width) / oneDevPixel); - -+ Margin outlineInset; -+ - // convert the radii - nsRectCornerRadii twipsRadii; - -@@ -999,6 +1002,11 @@ nsCSSRendering::CreateBorderRendererForNonThemedOutline( - outlineWidths.right + devPxOffset.Width(), - outlineWidths.bottom + devPxOffset.Height(), - outlineWidths.left + devPxOffset.Width()); -+ -+ // Pass down the computed inset for the correct rendering of -+ // corner-shape contoured superellipses. -+ outlineInset = -widths; -+ - nsCSSBorderRenderer::ComputeOuterRadii(innerRadii, widths, &outlineRadii); - } - -@@ -1014,9 +1022,10 @@ nsCSSRendering::CreateBorderRendererForNonThemedOutline( - - Rect dirtyRect = NSRectToRect(aDirtyRect, oneDevPixel); - -- return Some(nsCSSBorderRenderer( -- aPresContext, aDrawTarget, dirtyRect, oRect, outlineStyles, outlineWidths, -- outlineRadii, outlineColors, !aForFrame->BackfaceIsHidden(), Nothing())); -+ return Some(nsCSSBorderRenderer(aPresContext, aDrawTarget, dirtyRect, oRect, -+ outlineStyles, outlineWidths, outlineRadii, -+ outlineInset, outlineColors, -+ !aForFrame->BackfaceIsHidden(), Nothing())); - } - - void nsCSSRendering::PaintNonThemedOutline(nsPresContext* aPresContext, -@@ -1064,8 +1073,9 @@ nsCSSBorderRenderer nsCSSRendering::GetBorderRendererForFocus( - // to a ComputedStyle and can use the same logic that PaintBorder - // and PaintOutline do.) - return nsCSSBorderRenderer(pc, aDrawTarget, focusRect, focusRect, focusStyles, -- focusWidths, focusRadii, focusColors, -- !aForFrame->BackfaceIsHidden(), Nothing()); -+ focusWidths, focusRadii, /* aInset = */ Margin(), -+ focusColors, !aForFrame->BackfaceIsHidden(), -+ Nothing()); - } - - // Thebes Border Rendering Code End -diff --git a/layout/painting/nsCSSRenderingBorders.cpp b/layout/painting/nsCSSRenderingBorders.cpp -index fd2524fd78..45a37eff1c 100644 ---- a/layout/painting/nsCSSRenderingBorders.cpp -+++ b/layout/painting/nsCSSRenderingBorders.cpp -@@ -142,14 +142,16 @@ nsCSSBorderRenderer::nsCSSBorderRenderer( - nsPresContext* aPresContext, DrawTarget* aDrawTarget, - const Rect& aDirtyRect, Rect& aOuterRect, - const StyleBorderStyle* aBorderStyles, const Margin& aBorderWidths, -- RectCornerRadii& aBorderRadii, const nscolor* aBorderColors, -- bool aBackfaceIsVisible, const Maybe& aClipRect) -+ RectCornerRadii& aBorderRadii, const Margin& aBorderInset, -+ const nscolor* aBorderColors, bool aBackfaceIsVisible, -+ const Maybe& aClipRect) - : mPresContext(aPresContext), - mDrawTarget(aDrawTarget), - mDirtyRect(aDirtyRect), - mOuterRect(aOuterRect), - mBorderWidths(aBorderWidths), - mBorderRadii(aBorderRadii), -+ mBorderInset(aBorderInset), - mBackfaceIsVisible(aBackfaceIsVisible), - mLocalClip(aClipRect) { - PodCopy(mBorderStyles, aBorderStyles, 4); -@@ -3292,7 +3294,9 @@ void nsCSSBorderRenderer::CreateWebRenderCommands( - wr::ToBorderSide(ToDeviceColor(mBorderColors[i]), mBorderStyles[i]); - } - -+ wr::LayoutSideOffsets borderWidths = wr::ToBorderWidths(mBorderWidths); - wr::BorderRadius borderRadius = wr::ToBorderRadius(mBorderRadii); -+ wr::LayoutSideOffsets borderInset = wr::ToLayoutSideOffsets(mBorderInset); - - if (mLocalClip) { - LayoutDeviceRect localClip = -@@ -3301,8 +3305,8 @@ void nsCSSBorderRenderer::CreateWebRenderCommands( - } - - Range wrsides(side, 4); -- aBuilder.PushBorder(roundedRect, clipRect, mBackfaceIsVisible, -- wr::ToBorderWidths(mBorderWidths), wrsides, borderRadius); -+ aBuilder.PushBorder(roundedRect, clipRect, mBackfaceIsVisible, borderWidths, -+ wrsides, borderRadius, borderInset); - } - - /* static */ -diff --git a/layout/painting/nsCSSRenderingBorders.h b/layout/painting/nsCSSRenderingBorders.h -index 15157edd6e..c219b65f89 100644 ---- a/layout/painting/nsCSSRenderingBorders.h -+++ b/layout/painting/nsCSSRenderingBorders.h -@@ -93,7 +93,7 @@ class nsCSSBorderRenderer final { - const Rect& aDirtyRect, Rect& aOuterRect, - const mozilla::StyleBorderStyle* aBorderStyles, - const Margin& aBorderWidths, -- RectCornerRadii& aBorderRadii, -+ RectCornerRadii& aBorderRadii, const Margin& aBorderInset, - const nscolor* aBorderColors, bool aBackfaceIsVisible, - const mozilla::Maybe& aClipRect); - -@@ -137,6 +137,7 @@ class nsCSSBorderRenderer final { - mozilla::StyleBorderStyle mBorderStyles[4]; - Margin mBorderWidths; - RectCornerRadii mBorderRadii; -+ Margin mBorderInset; - - // the colors for 'border-top-color' et. al. - nscolor mBorderColors[4]; -diff --git a/layout/tables/nsTableFrame.cpp b/layout/tables/nsTableFrame.cpp -index 28e555c345..83c9b9ce2b 100644 ---- a/layout/tables/nsTableFrame.cpp -+++ b/layout/tables/nsTableFrame.cpp -@@ -6458,7 +6458,7 @@ static void AdjustAndPushBevel(wr::DisplayListBuilder& aBuilder, - // be able bevel to sides of the same color without bleeding in the middle. - aBuilder.PushBorder(bevelRect, bevelRect, aBackfaceIsVisible, borderWidths, - wrsides, wr::EmptyBorderRadius(), -- wr::AntialiasBorder::No); -+ wr::EmptyLayoutSideOffsets(), wr::AntialiasBorder::No); - } - - static void CreateWRCommandsForBeveledBorder( +- wr::BorderRadius borderRadius{}; +- wr::BorderRadius shadowRadius{}; ++ wr::BorderRadius borderRadius = wr::EmptyBorderRadius(); ++ wr::BorderRadius shadowRadius = wr::EmptyBorderRadius(); + if (hasBorderRadius) { + borderRadius = wr::ToBorderRadius(innerRadii); + if (spreadRadius) { diff --git a/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-bevel.html.ini b/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-bevel.html.ini index 48f1f399ff..64de30993d 100644 --- a/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-bevel.html.ini @@ -3777,30 +2017,30 @@ index e69fb19cc6..9cd5ad23a6 100644 + expected: + if useDrawSnapshot: FAIL diff --git a/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-scoop.html.ini b/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-scoop.html.ini -new file mode 100644 -index 0000000000..bebaeb9706 ---- /dev/null +index b8df36dfff..bebaeb9706 100644 +--- a/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-scoop.html.ini +++ b/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-scoop.html.ini -@@ -0,0 +1,3 @@ -+[corner-shape-scoop.html] +@@ -1,2 +1,3 @@ + [corner-shape-scoop.html] +- expected: FAIL + expected: + if useDrawSnapshot: FAIL diff --git a/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-square-notch-mixed.html.ini b/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-square-notch-mixed.html.ini -new file mode 100644 -index 0000000000..e829d53044 ---- /dev/null +index d33e0df628..e829d53044 100644 +--- a/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-square-notch-mixed.html.ini +++ b/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-square-notch-mixed.html.ini -@@ -0,0 +1,3 @@ -+[corner-shape-square-notch-mixed.html] +@@ -1,2 +1,3 @@ + [corner-shape-square-notch-mixed.html] +- expected: FAIL + expected: + if useDrawSnapshot: FAIL diff --git a/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-square-notch-superellipse.html.ini b/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-square-notch-superellipse.html.ini -new file mode 100644 -index 0000000000..12d5bac673 ---- /dev/null +index d66f06741d..12d5bac673 100644 +--- a/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-square-notch-superellipse.html.ini +++ b/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-square-notch-superellipse.html.ini -@@ -0,0 +1,3 @@ -+[corner-shape-square-notch-superellipse.html] +@@ -1,2 +1,3 @@ + [corner-shape-square-notch-superellipse.html] +- expected: FAIL + expected: + if useDrawSnapshot: FAIL diff --git a/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-square.html.ini b/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-square.html.ini @@ -3812,15 +2052,6 @@ index e9b1a330d2..5aca5449b0 100644 - expected: FAIL + expected: + if useDrawSnapshot: FAIL -diff --git a/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-squircle.html.ini b/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-squircle.html.ini -new file mode 100644 -index 0000000000..79a10bee60 ---- /dev/null -+++ b/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-squircle.html.ini -@@ -0,0 +1,3 @@ -+[corner-shape-squircle.html] -+ expected: -+ if useDrawSnapshot: FAIL diff --git a/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-superellipse-bevel.html.ini b/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-superellipse-bevel.html.ini index 1a106e3e3c..f175e3fb97 100644 --- a/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-superellipse-bevel.html.ini @@ -3830,24 +2061,6 @@ index 1a106e3e3c..f175e3fb97 100644 - expected: FAIL + expected: + if useDrawSnapshot: FAIL -diff --git a/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-superellipse-concave.html.ini b/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-superellipse-concave.html.ini -new file mode 100644 -index 0000000000..8e27df458c ---- /dev/null -+++ b/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-superellipse-concave.html.ini -@@ -0,0 +1,3 @@ -+[corner-shape-superellipse-concave.html] -+ expected: -+ if useDrawSnapshot: FAIL -diff --git a/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-superellipse-convex.html.ini b/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-superellipse-convex.html.ini -new file mode 100644 -index 0000000000..3a2ad3c584 ---- /dev/null -+++ b/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-superellipse-convex.html.ini -@@ -0,0 +1,3 @@ -+[corner-shape-superellipse-convex.html] -+ expected: -+ if useDrawSnapshot: FAIL diff --git a/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-superellipse-negative-100.html.ini b/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-superellipse-negative-100.html.ini index df13a251a0..6a32def3ac 100644 --- a/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-superellipse-negative-100.html.ini @@ -3867,35 +2080,19 @@ index c6a7827768..4c2ad71472 100644 + expected: + if useDrawSnapshot: FAIL diff --git a/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-superellipse-scoop.html.ini b/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-superellipse-scoop.html.ini -new file mode 100644 -index 0000000000..fc98f5a2f2 ---- /dev/null +index f3e23852d0..fc98f5a2f2 100644 +--- a/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-superellipse-scoop.html.ini +++ b/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-superellipse-scoop.html.ini -@@ -0,0 +1,3 @@ -+[corner-shape-superellipse-scoop.html] -+ expected: -+ if useDrawSnapshot: FAIL -diff --git a/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-superellipse-squircle.html.ini b/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-superellipse-squircle.html.ini -new file mode 100644 -index 0000000000..0ee4512987 ---- /dev/null -+++ b/testing/web-platform/meta/css/css-borders/corner-shape/corner-shape-superellipse-squircle.html.ini -@@ -0,0 +1,3 @@ -+[corner-shape-superellipse-squircle.html] -+ expected: -+ if useDrawSnapshot: FAIL -diff --git a/testing/web-platform/meta/css/css-borders/corner-shape/render-corner-shape.html.ini b/testing/web-platform/meta/css/css-borders/corner-shape/render-corner-shape.html.ini -index 43ad75fc1a..53522770ee 100644 ---- a/testing/web-platform/meta/css/css-borders/corner-shape/render-corner-shape.html.ini -+++ b/testing/web-platform/meta/css/css-borders/corner-shape/render-corner-shape.html.ini -@@ -1,12 +1,14 @@ - [render-corner-shape.html?border-radius=50&corner-shape=0&shadow-spread=10] +@@ -1,2 +1,3 @@ + [corner-shape-superellipse-scoop.html] - expected: FAIL + expected: + if useDrawSnapshot: FAIL - - [render-corner-shape.html?border-radius=40&corner-shape=3&shadow-spread=10] - expected: +diff --git a/testing/web-platform/meta/css/css-borders/corner-shape/render-corner-shape.html.ini b/testing/web-platform/meta/css/css-borders/corner-shape/render-corner-shape.html.ini +index 84c9fe0339..6c121825bb 100644 +--- a/testing/web-platform/meta/css/css-borders/corner-shape/render-corner-shape.html.ini ++++ b/testing/web-platform/meta/css/css-borders/corner-shape/render-corner-shape.html.ini +@@ -7,7 +7,8 @@ if useDrawSnapshot: FAIL [render-corner-shape.html?border-top-left-radius=50%&corner-shape=0.7&border-left-width=30&border-top-width=30] @@ -3905,49 +2102,6 @@ index 43ad75fc1a..53522770ee 100644 [render-corner-shape.html?corner-shape=-2&border-radius=20%&border-width=20] expected: -@@ -33,7 +35,8 @@ - if useDrawSnapshot: FAIL - - [render-corner-shape.html?border-top-right-radius=100&corner-top-right-shape=0&shadow-spread=30] -- expected: FAIL -+ expected: -+ if useDrawSnapshot: FAIL - - [render-corner-shape.html?corner-top-left-shape=scoop&border-radius=40] - expected: -@@ -52,24 +55,28 @@ - if useDrawSnapshot: FAIL - - [render-corner-shape.html?corner-top-left-shape=0&corner-top-right-shape=notch&corner-bottom-right-shape=2&corner-bottom-left-shape=-1&border-width=20&shadow-spread=20&border-radius=50] -- expected: FAIL -+ expected: -+ if useDrawSnapshot: FAIL - - [render-corner-shape.html?corner-shape=-7&border-radius=20%&border-width=20] - expected: - if useDrawSnapshot: FAIL - - [render-corner-shape.html?border-radius=5&corner-top-left-shape=0.5&corner-bottom-right-shape=-0.5&shadow-spread=10] -- expected: FAIL -+ expected: -+ if useDrawSnapshot: FAIL - - [render-corner-shape.html?corner-top-left-shape=-4&border-radius=40] - expected: - if useDrawSnapshot: FAIL - - [render-corner-shape.html?border-radius=40&corner-shape=-infinity&shadow-spread=10] -- expected: FAIL -+ expected: -+ if useDrawSnapshot: FAIL - - [render-corner-shape.html?border-radius=40&corner-shape=-1.5&shadow-spread=10] -- expected: FAIL -+ expected: -+ if useDrawSnapshot: FAIL - - [render-corner-shape.html?corner-shape=-2&border-top-left-radius=40%&border-width=20] - expected: diff --git a/widget/ThemeDrawing.cpp b/widget/ThemeDrawing.cpp index c2550d9c2d..ec7aa4bb1c 100644 --- a/widget/ThemeDrawing.cpp diff --git a/src/external-patches/manifest.json b/src/external-patches/manifest.json index a0ae01584..9fb9de527 100644 --- a/src/external-patches/manifest.json +++ b/src/external-patches/manifest.json @@ -44,13 +44,7 @@ { "type": "phabricator", "id": "D320897", - "name": "Bug 2011236", - "replaces": { - // The upstream patch context includes a MOZ_DIAGNOSTIC_ASSERT that - // doesn't exist yet in our Firefox version. - "@@ -59,11 +67,12 @@": "@@ -59,9 +67,10 @@", - "aLoadState->TypeHint())) {\n MOZ_DIAGNOSTIC_ASSERT(!mURI->SchemeIs(\"javascript\"));\n \n // Pull": "aLoadState->TypeHint())) {\n // Pull" - } + "name": "Bug 2011236" }, { "type": "phabricator", @@ -59,16 +53,6 @@ }, { "type": "local", - // CSS corner-shape rendering stack from bug 2047627 (borders), - // bug 2048908 (box shadows) and bug 2058091 (inset), squashed into a - // single patch: surfer's revert-then-apply on re-import only works for - // independently revertible patches, so a stack of separate files fails. - // Combined revisions, in stack order: D313684, D314332-D314337, - // D316301-D316304, D317056-D317058. The earlier revisions of these - // bugs (D306773-D306779, D308243, D311523) already landed in the - // current Firefox version and are omitted. D314332, D314336, D316301, - // D316303, D316304, D317057 and D317058 are hand-rebased onto - // Firefox 154 (the upstream diffs target a newer mozilla-central). "path": "firefox/css_corner_shape_rendering.patch" } ] diff --git a/src/layout/style/StyleColor-cpp.patch b/src/layout/style/StyleColor-cpp.patch index b95dbad1a..abc872dc3 100644 --- a/src/layout/style/StyleColor-cpp.patch +++ b/src/layout/style/StyleColor-cpp.patch @@ -1,16 +1,16 @@ diff --git a/layout/style/StyleColor.cpp b/layout/style/StyleColor.cpp -index bbee32af30f35525730a10d0b242c0e8a8fe4695..a590fff105b30bc96fa7a0d8a8d3b1d5aa997e08 100644 +index f922f7fdacc7e1a14beb8da77b48aef3f6ae0b2b..47d19242baaf2a1f25ee07c6e210c839cc7b5089 100644 --- a/layout/style/StyleColor.cpp +++ b/layout/style/StyleColor.cpp -@@ -9,6 +9,7 @@ - #include "mozilla/dom/BindingDeclarations.h" +@@ -8,6 +8,7 @@ + #include "mozilla/StyleColorInlines.h" #include "nsIFrame.h" #include "nsStyleStruct.h" +#include "mozilla/nsZenBoostsBackend.h" + #include "nsStyleUtil.h" namespace mozilla { - -@@ -96,23 +97,24 @@ StyleAbsoluteColor StyleColor::ResolveColor( +@@ -98,23 +99,24 @@ StyleAbsoluteColor StyleColor::ResolveColor( template <> nscolor StyleColor::CalcColor(nscolor aColor) const { @@ -40,7 +40,7 @@ index bbee32af30f35525730a10d0b242c0e8a8fe4695..a590fff105b30bc96fa7a0d8a8d3b1d5 } StyleAbsoluteColor StyleAbsoluteColor::ToColorSpace( -@@ -120,7 +122,7 @@ StyleAbsoluteColor StyleAbsoluteColor::ToColorSpace( +@@ -122,7 +124,7 @@ StyleAbsoluteColor StyleAbsoluteColor::ToColorSpace( return Servo_ConvertColorSpace(this, aColorSpace); } @@ -49,7 +49,7 @@ index bbee32af30f35525730a10d0b242c0e8a8fe4695..a590fff105b30bc96fa7a0d8a8d3b1d5 constexpr StyleColorSpace DEST_COLOR_SPACE = StyleColorSpace::Srgb; constexpr float MIN = 0.0f; -@@ -162,11 +164,13 @@ nscolor StyleAbsoluteColor::ToColor() const { +@@ -164,11 +166,13 @@ nscolor StyleAbsoluteColor::ToColor() const { } } diff --git a/src/layout/svg/SVGImageContext-h.patch b/src/layout/svg/SVGImageContext-h.patch index 591d42ada..fd225672e 100644 --- a/src/layout/svg/SVGImageContext-h.patch +++ b/src/layout/svg/SVGImageContext-h.patch @@ -1,5 +1,5 @@ diff --git a/layout/svg/SVGImageContext.h b/layout/svg/SVGImageContext.h -index b1f811989697966e30859610f4fc07579d0385ab..f6f4cd5a45fc49b416111d980fab4e612788903e 100644 +index ad833c7c2ee6a4c4984efdb0d59e3576c2d1bc19..36864a34badc0cbfe44d6295798622fd6178f9e3 100644 --- a/layout/svg/SVGImageContext.h +++ b/layout/svg/SVGImageContext.h @@ -6,6 +6,7 @@ @@ -44,7 +44,7 @@ index b1f811989697966e30859610f4fc07579d0385ab..f6f4cd5a45fc49b416111d980fab4e61 const Maybe& GetPreserveAspectRatio() const { return mPreserveAspectRatio; } -@@ -116,7 +137,11 @@ class SVGImageContext { +@@ -110,7 +131,11 @@ class SVGImageContext { return contextPaintIsEqual && mViewportSize == aOther.mViewportSize && mPreserveAspectRatio == aOther.mPreserveAspectRatio && mColorScheme == aOther.mColorScheme && @@ -57,17 +57,17 @@ index b1f811989697966e30859610f4fc07579d0385ab..f6f4cd5a45fc49b416111d980fab4e61 } bool operator!=(const SVGImageContext&) const = default; -@@ -129,7 +154,8 @@ class SVGImageContext { - return HashGeneric(hash, mViewportSize.map(HashSize).valueOr(0), - mPreserveAspectRatio.map(HashPAR).valueOr(0), - mColorScheme.map(HashColorScheme).valueOr(0), -- HashLinkParameters(mLinkParameters)); -+ HashLinkParameters(mLinkParameters), mZenBoostsAccent, -+ mZenBoostsComplementaryRotation, mZenBoostsInverted); +@@ -123,7 +148,8 @@ class SVGImageContext { + return AddToHash(hash, mViewportSize.map(HashSize).valueOr(0), + mPreserveAspectRatio.map(HashPAR).valueOr(0), + mColorScheme.map(HashColorScheme).valueOr(0), +- HashLinkParameters(mLinkParameters)); ++ HashLinkParameters(mLinkParameters), mZenBoostsAccent, ++ mZenBoostsComplementaryRotation, mZenBoostsInverted); } private: -@@ -167,6 +193,9 @@ class SVGImageContext { +@@ -156,6 +182,9 @@ class SVGImageContext { Maybe mPreserveAspectRatio; Maybe mColorScheme; StyleLinkParameters mLinkParameters; diff --git a/src/toolkit/content/widgets/moz-toggle/moz-toggle-tokens-css.patch b/src/toolkit/content/widgets/moz-toggle/moz-toggle-tokens-css.patch index 584d7759c..4d6a572f3 100644 --- a/src/toolkit/content/widgets/moz-toggle/moz-toggle-tokens-css.patch +++ b/src/toolkit/content/widgets/moz-toggle/moz-toggle-tokens-css.patch @@ -1,5 +1,5 @@ diff --git a/toolkit/content/widgets/moz-toggle/moz-toggle.tokens.css b/toolkit/content/widgets/moz-toggle/moz-toggle.tokens.css -index 6247d7a3923a6fedd4ec9911aba309ce565cd498..744e670eccc936dde664aa07df3e87bdcafd5e48 100644 +index c0976c54418a28046802443931042bd285aefbde..2adb18d25edbb8aac4e396fcfc64a4a664607836 100644 --- a/toolkit/content/widgets/moz-toggle/moz-toggle.tokens.css +++ b/toolkit/content/widgets/moz-toggle/moz-toggle.tokens.css @@ -13,8 +13,8 @@ @@ -11,5 +11,5 @@ index 6247d7a3923a6fedd4ec9911aba309ce565cd498..744e670eccc936dde664aa07df3e87bd + --toggle-background-color-pressed: var(--zen-primary-color); + --toggle-background-color-pressed-hover: var(--zen-colors-hover-bg); --toggle-background-color-pressed-active: var(--color-accent-primary-active); - --toggle-border-color: var(--border-color-interactive); + --toggle-border-color: light-dark(var(--color-gray-70), var(--border-color-interactive)); --toggle-border-color-hover: var(--toggle-border-color); diff --git a/src/widget/cocoa/nsCocoaWindow-h.patch b/src/widget/cocoa/nsCocoaWindow-h.patch index 77f4033c0..c082c028c 100644 --- a/src/widget/cocoa/nsCocoaWindow-h.patch +++ b/src/widget/cocoa/nsCocoaWindow-h.patch @@ -1,8 +1,8 @@ diff --git a/widget/cocoa/nsCocoaWindow.h b/widget/cocoa/nsCocoaWindow.h -index 312011c1c2ac41e78affecbdef25d72766a7c882..e5865c7d6259f0a3df48edf7a26b1b75a23cdb3a 100644 +index 22a2a7ef3a5e9257c8bb00ba6a55e8d39d2abbd9..6d8353fd11921585f7101f5717bdb3dd8f2a5eae 100644 --- a/widget/cocoa/nsCocoaWindow.h +++ b/widget/cocoa/nsCocoaWindow.h -@@ -462,6 +462,10 @@ class nsCocoaWindow final : public nsIWidget { +@@ -461,6 +461,10 @@ class nsCocoaWindow final : public nsIWidget { void SetHideTitlebarSeparator(bool) override; bool IsMacTitlebarDirectionRTL() override; void SetCustomTitlebar(bool) override; @@ -13,9 +13,9 @@ index 312011c1c2ac41e78affecbdef25d72766a7c882..e5865c7d6259f0a3df48edf7a26b1b75 void UpdateThemeGeometries( const nsTArray& aThemeGeometries) override; void LockAspectRatio(bool aShouldLock) override; -@@ -672,6 +676,7 @@ class nsCocoaWindow final : public nsIWidget { - bool mAspectRatioLocked = false; - bool mIsAlert = false; // True if this is an non-native alert window. +@@ -674,6 +678,7 @@ class nsCocoaWindow final : public nsIWidget { + // show (e.g. a window created by detaching a tab from a fullscreen window). + bool mIsInitialFullscreenSuppressed = false; bool mWasShown = false; + bool mIsBrowserWindow = false; diff --git a/src/widget/gtk/nsDragService-cpp.patch b/src/widget/gtk/nsDragSessionSource-cpp.patch similarity index 70% rename from src/widget/gtk/nsDragService-cpp.patch rename to src/widget/gtk/nsDragSessionSource-cpp.patch index 85e9760e3..eb8b1a0c7 100644 --- a/src/widget/gtk/nsDragService-cpp.patch +++ b/src/widget/gtk/nsDragSessionSource-cpp.patch @@ -1,16 +1,16 @@ -diff --git a/widget/gtk/nsDragService.cpp b/widget/gtk/nsDragService.cpp -index f760a6bb887b321376761431886c771f14fd03af..d52110ba52a95ef7c7bfb915e120b8dd636c9448 100644 ---- a/widget/gtk/nsDragService.cpp -+++ b/widget/gtk/nsDragService.cpp -@@ -23,6 +23,7 @@ +diff --git a/widget/gtk/nsDragSessionSource.cpp b/widget/gtk/nsDragSessionSource.cpp +index a5f67d700f8c80d513f08cc25405b57cf28a2a9a..58186470bcbb7b1eda34ee20a19e29d540ab052a 100644 +--- a/widget/gtk/nsDragSessionSource.cpp ++++ b/widget/gtk/nsDragSessionSource.cpp +@@ -13,6 +13,7 @@ + #include "mozilla/Services.h" #include "mozilla/StaticPrefs_widget.h" - #include "mozilla/WidgetUtils.h" #include "mozilla/WidgetUtilsGtk.h" +#include "mozilla/nsZenDragAndDrop.h" #include "nsAppShell.h" #include "nsArrayUtils.h" #include "nsCRT.h" -@@ -2304,11 +2305,15 @@ bool nsDragSession::SetAlphaPixmap(SourceSurface* aSurface, +@@ -1196,11 +1197,15 @@ bool nsDragSessionSource::SetAlphaPixmap(SourceSurface* aSurface, cairo_image_surface_get_stride(surf), SurfaceFormat::B8G8R8A8); if (!dt) return false; diff --git a/src/zen/common/modules/ZenUIManager.mjs b/src/zen/common/modules/ZenUIManager.mjs index 405361ccf..977a7d8df 100644 --- a/src/zen/common/modules/ZenUIManager.mjs +++ b/src/zen/common/modules/ZenUIManager.mjs @@ -1282,7 +1282,7 @@ window.gZenVerticalTabsManager = { if (!this._hasSetSingleToolbar) { height = AppConstants.platform == "macosx" ? 34 : 32; } else if (!gURLBar.hasAttribute("breakout-extend")) { - height = 38; + height = 36; } if (typeof height !== "undefined") { gURLBar.style.setProperty("--urlbar-height", `${height}px`); diff --git a/src/zen/common/styles/zen-omnibox.css b/src/zen/common/styles/zen-omnibox.css index a93bb97de..98659ef8d 100644 --- a/src/zen/common/styles/zen-omnibox.css +++ b/src/zen/common/styles/zen-omnibox.css @@ -20,9 +20,9 @@ --urlbarview-separator-color: light-dark(hsl(0, 0%, 80%), hsl(0, 0%, 20%)); --urlbarview-background-color-hover: var(--toolbarbutton-hover-background); border-radius: calc(var(--toolbarbutton-border-radius) - 2px); - height: var(--urlbar-height); --urlbarView-results-padding: 10px !important; --urlbar-container-height: 48px !important; + --urlbar-input-container-border: none; --urlbar-margin-inline: 5px; --urlbar-container-padding: 4px; @@ -326,6 +326,7 @@ order: 2; } + /* stylelint-disable-next-line stylelint-plugin-mozilla/no-has-selector */ #notification-popup-box:not([open]):not(:has(> [showing="true"])) { margin-inline-start: calc(-10px - 2 * var(--urlbar-icon-padding)); opacity: 0; @@ -487,6 +488,7 @@ translate: -50% 0%; left: 50% !important; + /* stylelint-disable-next-line stylelint-plugin-mozilla/no-has-selector */ #urlbar-container:has(&) { border-radius: var(--border-radius-medium); background: var(--toolbarbutton-hover-background); @@ -633,6 +635,7 @@ #urlbar-label-box, #urlbar-search-mode-indicator { + --urlbar-basic-component-size: 18px; background-color: color-mix(in srgb, var(--zen-primary-color) 50%, light-dark(rgba(0, 0, 0, 0.5), rgba(255, 255, 255, 0.2)) 50%) !important; color: white; padding: 4px 8px; diff --git a/src/zen/common/styles/zen-single-components.css b/src/zen/common/styles/zen-single-components.css index 36daaf52e..a93faa7f7 100644 --- a/src/zen/common/styles/zen-single-components.css +++ b/src/zen/common/styles/zen-single-components.css @@ -502,11 +502,13 @@ opacity 0.12s ease-in-out; } + /* stylelint-disable-next-line stylelint-plugin-mozilla/no-has-selector */ .permission-popup-permission-item:hover &::before, .permission-popup-boost-item:hover:not(:has(.zen-permission-popup-boost-editor-button:hover)) &::before { transform: scale(1.05); } + /* stylelint-disable-next-line stylelint-plugin-mozilla/no-has-selector */ .permission-popup-permission-item:active:hover &::before, .permission-popup-boost-item:active:hover:not(:has(.zen-permission-popup-boost-editor-button:hover)) &::before { transform: scale(0.95); diff --git a/src/zen/tabs/zen-tabs/vertical-tabs-topbuttons-fix.css b/src/zen/tabs/zen-tabs/vertical-tabs-topbuttons-fix.css index aec12142c..142babda4 100644 --- a/src/zen/tabs/zen-tabs/vertical-tabs-topbuttons-fix.css +++ b/src/zen/tabs/zen-tabs/vertical-tabs-topbuttons-fix.css @@ -4,6 +4,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/* stylelint-disable-next-line stylelint-plugin-mozilla/no-has-selector */ &:not([zen-compact-animating]) #zen-sidebar-top-buttons:not(:has(#zen-sidebar-top-buttons-customization-target > *:not(#zen-sidebar-top-buttons-separator), .titlebar-buttonbox-container)) { max-height: 0 !important; diff --git a/src/zen/tabs/zen-tabs/vertical-tabs.css b/src/zen/tabs/zen-tabs/vertical-tabs.css index 02be56bfe..671373fe1 100644 --- a/src/zen/tabs/zen-tabs/vertical-tabs.css +++ b/src/zen/tabs/zen-tabs/vertical-tabs.css @@ -100,9 +100,10 @@ width: -moz-available !important; } + /* stylelint-disable-next-line stylelint-plugin-mozilla/no-has-selector */ & #urlbar-container[breakout="true"]:has(#urlbar[zen-floating-urlbar="true"]) { --urlbar-container-height: 36px !important; - --urlbar-height: 38px !important; + --urlbar-height: 36px !important; } & #nav-bar { @@ -319,6 +320,7 @@ display: none; } + /* stylelint-disable-next-line stylelint-plugin-mozilla/no-has-selector */ :root:not([zen-renaming-tab="true"]) #tabbrowser-tabs:not([movingtab]) &:active:not(:has(.tab-content > image:active)) { diff --git a/src/zen/urlbar/ZenUBActionsProvider.sys.mjs b/src/zen/urlbar/ZenUBActionsProvider.sys.mjs index cdec35c28..cc98ee276 100644 --- a/src/zen/urlbar/ZenUBActionsProvider.sys.mjs +++ b/src/zen/urlbar/ZenUBActionsProvider.sys.mjs @@ -3,10 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; -import { - UrlbarProvider, - UrlbarUtils, -} from "moz-src:///browser/components/urlbar/UrlbarUtils.sys.mjs"; +import { UrlbarProvider } from "moz-src:///browser/components/urlbar/UrlbarUtils.sys.mjs"; import { UrlbarShared } from "chrome://browser/content/urlbar/UrlbarShared.mjs"; import { globalActions } from "resource:///modules/ZenUBGlobalActions.sys.mjs"; import { zenUrlbarResultsLearner } from "./ZenUBResultsLearner.sys.mjs"; @@ -41,10 +38,10 @@ XPCOMUtils.defineLazyPreferenceGetter( /** * A convenience function that takes a payload annotated with - * UrlbarUtils.HIGHLIGHT enums and returns the payload and the payload's + * UrlbarShared.HIGHLIGHT enums and returns the payload and the payload's * highlights. Use this function when the highlighting required by your * payload is based on simple substring matching, as done by - * UrlbarUtils.getTokenMatches(). Pass the return values as the `payload` and + * UrlbarShared.getTokenMatches(). Pass the return values as the `payload` and * `payloadHighlights` params of the UrlbarResult constructor. * `payloadHighlights` is optional. If omitted, payload will not be * highlighted. @@ -64,7 +61,7 @@ XPCOMUtils.defineLazyPreferenceGetter( * payloadPropertyValue may be a string or an array of strings. If * it's a string, then the payloadHighlights in the return value will * be an array of match highlights as described in - * UrlbarUtils.getTokenMatches(). If it's an array, then + * UrlbarShared.getTokenMatches(). If it's an array, then * payloadHighlights will be an array of arrays of match highlights, * one element per element in payloadPropertyValue. * @returns {{ payload: object, payloadHighlights: object }} @@ -93,14 +90,14 @@ function payloadAndSimpleHighlights(tokens, payloadInfo) { ) { // If there's no title, show the domain as the title. Not all valid URLs // have a domain. - highlightTypes.title = UrlbarUtils.HIGHLIGHT.TYPED; + highlightTypes.title = UrlbarShared.HIGHLIGHT.TYPED; try { payload.title = new URL(payload.url).URI.displayHostPort; } catch (e) {} } if (payload.url) { // For display purposes we need to unescape the url. - payload.displayUrl = UrlbarUtils.prepareUrlForDisplay(payload.url); + payload.displayUrl = UrlbarShared.prepareUrlForDisplay(payload.url); highlightTypes.displayUrl = highlightTypes.url; } // For performance reasons limit excessive string lengths, to reduce the @@ -109,7 +106,7 @@ function payloadAndSimpleHighlights(tokens, payloadInfo) { for (let prop of ["displayUrl", "title", "suggestion"]) { let value = payload[prop]; if (typeof value == "string") { - payload[prop] = value.substring(0, UrlbarUtils.MAX_TEXT_LENGTH); + payload[prop] = value.substring(0, UrlbarShared.MAX_TEXT_LENGTH); } } let payloadHighlights = {}; @@ -118,9 +115,9 @@ function payloadAndSimpleHighlights(tokens, payloadInfo) { let value = payload[name]; let highlights = Array.isArray(value) ? value.map(subval => - UrlbarUtils.getTokenMatches(tokens, subval, highlightType) + UrlbarShared.getTokenMatches(tokens, subval, highlightType) ) - : UrlbarUtils.getTokenMatches(tokens, value || "", highlightType); + : UrlbarShared.getTokenMatches(tokens, value || "", highlightType); if (highlights.length) { payloadHighlights[name] = highlights; } @@ -140,10 +137,10 @@ export class ZenUrlbarProviderGlobalActions extends UrlbarProvider { } /** - * @returns {Values} + * @returns {Values} */ get type() { - return UrlbarUtils.PROVIDER_TYPE.HEURISTIC; + return UrlbarShared.PROVIDER_TYPE.HEURISTIC; } /** @@ -161,7 +158,7 @@ export class ZenUrlbarProviderGlobalActions extends UrlbarProvider { UrlbarShared.RESULT_SOURCE.ZEN_ACTIONS || (lazy.enabledPref && queryContext.searchString && - queryContext.searchString.length < UrlbarUtils.MAX_TEXT_LENGTH && + queryContext.searchString.length < UrlbarShared.MAX_TEXT_LENGTH && queryContext.searchString.length > 2 && !lazy.UrlUtils.REGEXP_LIKE_PROTOCOL.test(queryContext.searchString)) ); diff --git a/src/zen/welcome/zen-welcome.css b/src/zen/welcome/zen-welcome.css index 54d2c7f10..0f7307366 100644 --- a/src/zen/welcome/zen-welcome.css +++ b/src/zen/welcome/zen-welcome.css @@ -178,6 +178,7 @@ box-shadow: var(--zen-big-shadow); } + /* stylelint-disable-next-line stylelint-plugin-mozilla/no-has-selector */ &:has(:checked) { box-shadow: var(--zen-big-shadow); border: 2px solid var(--zen-primary-color); @@ -197,6 +198,7 @@ } /* 3 or more engines */ + /* stylelint-disable-next-line stylelint-plugin-mozilla/no-has-selector */ &[select-engine="true"]:has(label:nth-of-type(3)) > label { height: 50%; } diff --git a/surfer.json b/surfer.json index a1a87a88a..c0e204c51 100644 --- a/surfer.json +++ b/surfer.json @@ -6,7 +6,7 @@ "version": { "product": "firefox", "version": "154.0.1", - "candidate": "154.0.1", + "candidate": "155.0", "candidateBuild": 1 }, "buildOptions": {