mirror of
https://github.com/zen-browser/desktop.git
synced 2026-09-23 12:53:55 +00:00
Compare commits
30 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4c92731b2d | ||
|
|
be04ad6d50 | ||
|
|
f0f21cdade | ||
|
|
e68b5e5c26 | ||
|
|
7545e5a870 | ||
|
|
b3467c0ff2 | ||
|
|
303b6e39ed | ||
|
|
7debd15013 | ||
|
|
450aeeccd3 | ||
|
|
8645a5b9c8 | ||
|
|
031b5d8d0a | ||
|
|
2804ba8350 | ||
|
|
5e5926e708 | ||
|
|
a65f96c3b8 | ||
|
|
4b4a659c7b | ||
|
|
5ff06e4274 | ||
|
|
f31f99e507 | ||
|
|
2ee1275f28 | ||
|
|
45b9cfe37f | ||
|
|
f800d550cb | ||
|
|
04e7db5d3a | ||
|
|
644bf48b6c | ||
|
|
d2acc61a88 | ||
|
|
f84278317f | ||
|
|
4980f3ce18 | ||
|
|
5362ddbccd | ||
|
|
6c32eaad68 | ||
|
|
610e7bfbae | ||
|
|
b43785de65 | ||
|
|
787f9c5e02 |
@@ -1 +1 @@
|
||||
1.94.1
|
||||
1.95.0
|
||||
@@ -34,8 +34,8 @@ Zen is a firefox-based browser with the aim of pushing your productivity to a ne
|
||||
|
||||
### Firefox Versions
|
||||
|
||||
- [`Release`](https://zen-browser.app/download) - Is currently built using Firefox version `155.0.1`!
|
||||
- [`Twilight`](https://zen-browser.app/download?twilight) - Is currently built using Firefox version `RC 155.0.1`!
|
||||
- [`Release`](https://zen-browser.app/download) - Is currently built using Firefox version `156.0.1`!
|
||||
- [`Twilight`](https://zen-browser.app/download?twilight) - Is currently built using Firefox version `RC 156.0.1`!
|
||||
|
||||
### Contributing
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
b6d45eac914fd4886c9b6e5f1447f613771228ff
|
||||
782b15a7c771795a323e9403340c029e7c8d7629
|
||||
@@ -147,9 +147,9 @@ function SignAndPackage($name) {
|
||||
$env:ZEN_SETUP_EXE_PATH="$PWD\windsign-temp\windows-x64-obj-$name\browser\installer\windows\instgen\setup.exe"
|
||||
|
||||
if ($name -eq "arm64") {
|
||||
$env:WIN32_REDIST_DIR="$PWD\win-cross\vs2026\VC\Redist\MSVC\14.50.35710\arm64\Microsoft.VC145.CRT"
|
||||
$env:WIN32_REDIST_DIR="$PWD\win-cross\vs2026\VC\Redist\MSVC\14.51.36231\arm64\Microsoft.VC145.CRT"
|
||||
} else {
|
||||
$env:WIN32_REDIST_DIR="$PWD\win-cross\vs2026\VC\Redist\MSVC\14.50.35710\x64\Microsoft.VC145.CRT"
|
||||
$env:WIN32_REDIST_DIR="$PWD\win-cross\vs2026\VC\Redist\MSVC\14.51.36231\x64\Microsoft.VC145.CRT"
|
||||
}
|
||||
|
||||
$env:MAR="..\\build\\windows\\mar.exe"
|
||||
|
||||
@@ -15,9 +15,9 @@ if test "$ZEN_CROSS_COMPILING"; then
|
||||
CROSS_COMPILE=1
|
||||
|
||||
if test "$SURFER_COMPAT" = "aarch64"; then
|
||||
export WIN32_REDIST_DIR="$WINSYSROOT/VC/Redist/MSVC/14.50.35710/arm64/Microsoft.VC145.CRT"
|
||||
export WIN32_REDIST_DIR="$WINSYSROOT/VC/Redist/MSVC/14.51.36231/arm64/Microsoft.VC145.CRT"
|
||||
else
|
||||
export WIN32_REDIST_DIR="$WINSYSROOT/VC/Redist/MSVC/14.50.35710/x64/Microsoft.VC145.CRT"
|
||||
export WIN32_REDIST_DIR="$WINSYSROOT/VC/Redist/MSVC/14.51.36231/x64/Microsoft.VC145.CRT"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
zen-action-toggle-compact-mode = Toggle Compact Mode
|
||||
zen-action-open-theme-picker = Open Theme Picker
|
||||
zen-action-new-split-view = New Split View
|
||||
zen-action-unsplit-view = Expand current view
|
||||
zen-action-new-space = New Space
|
||||
zen-action-new-folder = New Folder
|
||||
zen-action-copy-current-url = Copy Current URL
|
||||
zen-action-settings = Settings
|
||||
@@ -18,6 +20,9 @@ zen-action-new-boost = New Boost
|
||||
zen-action-next-space = Next Space
|
||||
zen-action-previous-space = Previous Space
|
||||
zen-action-close-tab = Close Tab
|
||||
zen-action-reopen-closed-tab = Reopen Closed Tab
|
||||
zen-action-duplicate-tab = Duplicate Current Tab
|
||||
zen-action-reset-pinned-tab = Reset Pinned Tab
|
||||
zen-action-reload-tab = Reload Tab
|
||||
zen-action-reload-tab-without-cache = Reload Tab Without Cache
|
||||
zen-action-next-tab = Next Tab
|
||||
@@ -34,3 +39,4 @@ zen-action-switch-to-dark-mode = Switch to Dark Mode
|
||||
zen-action-print = Print
|
||||
zen-action-focus-on = Focus on
|
||||
zen-action-extension = Extension
|
||||
zen-action-open = Open
|
||||
|
||||
112
locales/en-US/browser/browser/zen-library.ftl
Normal file
112
locales/en-US/browser/browser/zen-library.ftl
Normal file
@@ -0,0 +1,112 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
zen-library-button =
|
||||
.label = Library
|
||||
.tooltiptext = Library
|
||||
|
||||
library-footer-close-button =
|
||||
.tooltiptext = Close library
|
||||
library-footer-donate-button =
|
||||
.tooltiptext = Donate to Zen
|
||||
|
||||
library-history-section-title = History
|
||||
library-downloads-section-title = Downloads
|
||||
library-boosts-section-title = Boosts
|
||||
library-media-section-title = Media
|
||||
library-spaces-section-title = Spaces
|
||||
|
||||
library-filter-button = Filter
|
||||
library-filter-done = Done
|
||||
|
||||
library-history-filter-title = Filter History…
|
||||
library-history-filter-when = When was it visited?
|
||||
library-filter-today = Today
|
||||
library-filter-week = This Week
|
||||
library-filter-month = This Month
|
||||
library-history-filter-sort = Sort by
|
||||
library-history-sort-date = By Date
|
||||
library-history-sort-site = By Site
|
||||
library-history-sort-most-visited = By Most Visited
|
||||
library-history-sort-last-visited = By Last Visited
|
||||
library-history-site-other = Other
|
||||
library-history-search-placeholder =
|
||||
.placeholder = Search History…
|
||||
library-history-empty = No history found
|
||||
library-media-empty = No media found
|
||||
library-media-search-placeholder =
|
||||
.placeholder = Search Media…
|
||||
library-media-loading = Looking for media…
|
||||
library-media-opt-in-title = Your Media in the Library
|
||||
library-media-opt-in-body = Quickly grab screenshots, images and videos from your computer by showing them in the Zen Library.
|
||||
library-media-opt-in-button = Show Media
|
||||
library-media-opt-in-note = Media never leaves your computer and is not viewable by anyone but you.
|
||||
library-media-filter-title = Filter Media…
|
||||
library-media-filter-folders = Where to look
|
||||
library-media-open-in = Open in { $app }
|
||||
library-media-close =
|
||||
.title = Close
|
||||
library-media-previous =
|
||||
.title = Previous
|
||||
library-media-next =
|
||||
.title = Next
|
||||
library-media-more =
|
||||
.title = More options
|
||||
library-media-menu-open =
|
||||
.label = Open
|
||||
library-media-menu-copy =
|
||||
.label = Copy
|
||||
library-history-forget-button =
|
||||
.title = Remove from history
|
||||
library-history-reopen-button =
|
||||
.title = Reopen page
|
||||
|
||||
library-downloads-empty = No downloads found
|
||||
library-downloads-more-button =
|
||||
.title = More options
|
||||
library-downloads-menu-open =
|
||||
.label = Open { $name }
|
||||
library-downloads-menu-copy =
|
||||
.label = Copy { $name }
|
||||
library-downloads-menu-hide =
|
||||
.label = Hide from Zen
|
||||
library-downloads-menu-trash =
|
||||
.label = Move to Trash
|
||||
library-downloads-opening-in = { PLATFORM() ->
|
||||
[macos] Opening in Finder…
|
||||
[windows] Opening in File Explorer…
|
||||
*[other] Opening in file manager…
|
||||
}
|
||||
library-downloads-cancel-button =
|
||||
.title = Cancel download
|
||||
library-downloads-filter-title = Filter Downloads…
|
||||
library-downloads-filter-type = File type
|
||||
library-downloads-filter-when = When was it downloaded?
|
||||
library-downloads-type-images = Images
|
||||
library-downloads-type-video = Video
|
||||
library-downloads-type-audio = Audio
|
||||
library-downloads-type-documents = Documents
|
||||
library-downloads-type-archives = Archives
|
||||
library-downloads-type-apps = Apps
|
||||
library-downloads-retry-button =
|
||||
.title = Retry download
|
||||
|
||||
library-spaces-theme-button =
|
||||
.title = Change theme
|
||||
library-spaces-move-button =
|
||||
.title = Drag to reorder
|
||||
library-spaces-actions-button =
|
||||
.tooltiptext = More options
|
||||
library-spaces-icon-button =
|
||||
.title = Change icon
|
||||
library-spaces-name-button =
|
||||
.title = Rename space
|
||||
|
||||
library-boosts-search-placeholder =
|
||||
.placeholder = Search Boosts…
|
||||
library-boosts-empty = No boosts yet
|
||||
library-boosts-toggle =
|
||||
.aria-label = Enable boost
|
||||
library-boosts-menu-edit =
|
||||
.label = Edit Boost
|
||||
15
prefs/zen/library.yaml
Normal file
15
prefs/zen/library.yaml
Normal file
@@ -0,0 +1,15 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
- name: zen.library.enabled
|
||||
value: "@IS_TWILIGHT@"
|
||||
|
||||
- name: zen.library.last-tab
|
||||
value: "history"
|
||||
|
||||
- name: zen.library.media.enabled
|
||||
value: false
|
||||
|
||||
- name: zen.library.media.folders
|
||||
value: "downloads,documents,pictures"
|
||||
@@ -20,8 +20,6 @@
|
||||
- name: zen.theme.acrylic-elements
|
||||
value: "@IS_TWILIGHT@"
|
||||
|
||||
- name: zen.theme.squircle-browser-view
|
||||
value: "@IS_TWILIGHT@"
|
||||
|
||||
- name: zen.theme.disable-lightweight
|
||||
value: true
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
diff --git a/.stylelintignore b/.stylelintignore
|
||||
index 185490999507b8a5032977237af50f5e61c71df1..77c1635d90c6480af4ebd3c6e3ec49780eaca571 100644
|
||||
index 185490999507b8a5032977237af50f5e61c71df1..4b1b1dc76d316f38f60c90e6cbd74d9569800d04 100644
|
||||
--- a/.stylelintignore
|
||||
+++ b/.stylelintignore
|
||||
@@ -106,3 +106,19 @@ build/pgo/blueprint/**/*.css
|
||||
@@ -106,3 +106,20 @@ build/pgo/blueprint/**/*.css
|
||||
# under our control or we don't want to modify at this point:
|
||||
testing/web-platform/mozilla/
|
||||
testing/web-platform/tests/
|
||||
@@ -20,5 +20,6 @@ index 185490999507b8a5032977237af50f5e61c71df1..77c1635d90c6480af4ebd3c6e3ec4978
|
||||
+zen/split-view/zen-split-view.css
|
||||
+zen/spaces/zen-workspaces.css
|
||||
+zen/common/styles/zen-toolbar.css
|
||||
+zen/common/styles/zen-single-components.css
|
||||
+
|
||||
+*.inc
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/base/content/navigator-toolbox.inc.xhtml b/browser/base/content/navigator-toolbox.inc.xhtml
|
||||
index dd2f391096968564086911c532f696dc7ffb8ae1..c968e5f0ebb5f0151fb124bafe10d8d5361e060f 100644
|
||||
index a76bb7aae7636c07989a4acb35281777d3103505..d35aa0457307e95ad00464861fcee2827325193e 100644
|
||||
--- a/browser/base/content/navigator-toolbox.inc.xhtml
|
||||
+++ b/browser/base/content/navigator-toolbox.inc.xhtml
|
||||
@@ -2,7 +2,7 @@
|
||||
@@ -26,7 +26,7 @@ index dd2f391096968564086911c532f696dc7ffb8ae1..c968e5f0ebb5f0151fb124bafe10d8d5
|
||||
<toolbar id="TabsToolbar"
|
||||
class="browser-toolbar browser-titlebar"
|
||||
fullscreentoolbar="true"
|
||||
@@ -54,6 +58,8 @@
|
||||
@@ -56,6 +60,8 @@
|
||||
<html:sidebar-pins-promo id="drag-to-pin-promo-card"></html:sidebar-pins-promo>
|
||||
<arrowscrollbox id="pinned-tabs-container" orient="horizontal" clicktoscroll=""></arrowscrollbox>
|
||||
<splitter orient="vertical" id="vertical-pinned-tabs-splitter" resizebefore="sibling" resizeafter="none" hidden="true"/>
|
||||
@@ -35,7 +35,7 @@ index dd2f391096968564086911c532f696dc7ffb8ae1..c968e5f0ebb5f0151fb124bafe10d8d5
|
||||
<hbox class="tab-drop-indicator" hidden="true"/>
|
||||
<arrowscrollbox id="tabbrowser-arrowscrollbox" orient="horizontal" flex="1" clicktoscroll="" scrolledtostart="" scrolledtoend="">
|
||||
<tab is="tabbrowser-tab" class="tabbrowser-tab" selected="" visuallyselected="" fadein=""/>
|
||||
@@ -73,6 +79,7 @@
|
||||
@@ -75,6 +81,7 @@
|
||||
tooltip="dynamic-shortcut-tooltip"
|
||||
data-l10n-id="tabs-toolbar-new-tab"/>
|
||||
<html:span id="tabbrowser-tab-a11y-desc" hidden="true"/>
|
||||
@@ -43,7 +43,7 @@ index dd2f391096968564086911c532f696dc7ffb8ae1..c968e5f0ebb5f0151fb124bafe10d8d5
|
||||
</tabs>
|
||||
|
||||
<toolbarbutton id="new-tab-button"
|
||||
@@ -99,9 +106,10 @@
|
||||
@@ -102,9 +109,10 @@
|
||||
|
||||
<toolbarbutton class="content-analysis-indicator toolbarbutton-1 content-analysis-indicator-icon"/>
|
||||
|
||||
@@ -54,4 +54,12 @@ index dd2f391096968564086911c532f696dc7ffb8ae1..c968e5f0ebb5f0151fb124bafe10d8d5
|
||||
+</hbox>
|
||||
|
||||
<toolbar id="nav-bar"
|
||||
class="browser-toolbar chromeclass-location"
|
||||
class="browser-toolbar"
|
||||
@@ -180,7 +188,6 @@
|
||||
<toolbartabstop/>
|
||||
<html:moz-urlbar id="urlbar"
|
||||
class="urlbar"
|
||||
- popover="manual"
|
||||
pageproxystate="invalid"
|
||||
unifiedsearchbutton-available=""
|
||||
sap-name="urlbar">
|
||||
|
||||
@@ -22,3 +22,4 @@
|
||||
#include ../../../zen/live-folders/jar.inc.mn
|
||||
#include ../../../zen/space-routing/jar.inc.mn
|
||||
#include ../../../zen/share/jar.inc.mn
|
||||
#include ../../../zen/library/jar.inc.mn
|
||||
|
||||
@@ -72,4 +72,6 @@
|
||||
<command id="cmd_zenNewLiveFolder" />
|
||||
|
||||
<command id="cmd_zenDuplicateTab" />
|
||||
|
||||
<command id="cmd_zenToggleLibrary" />
|
||||
</commandset>
|
||||
|
||||
@@ -13,4 +13,5 @@
|
||||
<link rel="localization" href="browser/zen-live-folders.ftl"/>
|
||||
<link rel="localization" href="browser/zen-space-routing.ftl"/>
|
||||
<link rel="localization" href="browser/zen-share.ftl"/>
|
||||
<link rel="localization" href="browser/zen-library.ftl"/>
|
||||
</linkset>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/components/customizableui/CustomizableUI.sys.mjs b/browser/components/customizableui/CustomizableUI.sys.mjs
|
||||
index 6d1885a54815d10b3a4e79f7e5acd61ab3abb159..3d6e092e7ed126cdee8c32229df2cfa13303d3a3 100644
|
||||
index 875f8b03d979353ef44a57c15ef1ae09d33ab730..3d6e092e7ed126cdee8c32229df2cfa13303d3a3 100644
|
||||
--- a/browser/components/customizableui/CustomizableUI.sys.mjs
|
||||
+++ b/browser/components/customizableui/CustomizableUI.sys.mjs
|
||||
@@ -13,6 +13,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
|
||||
@@ -19,7 +19,7 @@ index 6d1885a54815d10b3a4e79f7e5acd61ab3abb159..3d6e092e7ed126cdee8c32229df2cfa1
|
||||
},
|
||||
false
|
||||
);
|
||||
@@ -362,27 +363,23 @@ var CustomizableUIInternal = {
|
||||
@@ -362,20 +363,15 @@ var CustomizableUIInternal = {
|
||||
"back-button",
|
||||
"forward-button",
|
||||
"stop-reload-button",
|
||||
@@ -41,27 +41,29 @@ index 6d1885a54815d10b3a4e79f7e5acd61ab3abb159..3d6e092e7ed126cdee8c32229df2cfa1
|
||||
this.registerArea(
|
||||
CustomizableUI.AREA_NAVBAR,
|
||||
{
|
||||
type: CustomizableUI.TYPE_TOOLBAR,
|
||||
@@ -383,9 +379,6 @@ var CustomizableUIInternal = {
|
||||
overflowable: true,
|
||||
defaultPlacements: navbarPlacements,
|
||||
- verticalTabsDefaultPlacements: ["alltabs-button", "ai-window-toggle"],
|
||||
+ verticalTabsDefaultPlacements: [
|
||||
+ ],
|
||||
verticalTabsDefaultPlacements: [
|
||||
- "alltabs-button",
|
||||
- "smartwindow-group-tabs-button",
|
||||
- "ai-window-toggle",
|
||||
],
|
||||
defaultCollapsed: false,
|
||||
},
|
||||
true
|
||||
@@ -406,10 +403,6 @@ var CustomizableUIInternal = {
|
||||
@@ -410,11 +403,6 @@ var CustomizableUIInternal = {
|
||||
type: CustomizableUI.TYPE_TOOLBAR,
|
||||
defaultPlacements: [
|
||||
"tabbrowser-tabs",
|
||||
- "new-tab-button",
|
||||
- "spring",
|
||||
- "alltabs-button",
|
||||
- "smartwindow-group-tabs-button",
|
||||
- "ai-window-toggle",
|
||||
],
|
||||
verticalTabsDefaultPlacements: [],
|
||||
defaultCollapsed: null,
|
||||
@@ -502,6 +495,7 @@ var CustomizableUIInternal = {
|
||||
@@ -507,6 +495,7 @@ var CustomizableUIInternal = {
|
||||
CustomizableUI.AREA_NAVBAR,
|
||||
CustomizableUI.AREA_BOOKMARKS,
|
||||
CustomizableUI.AREA_TABSTRIP,
|
||||
@@ -69,7 +71,7 @@ index 6d1885a54815d10b3a4e79f7e5acd61ab3abb159..3d6e092e7ed126cdee8c32229df2cfa1
|
||||
]);
|
||||
if (AppConstants.platform != "macosx") {
|
||||
toolbars.add(CustomizableUI.AREA_MENUBAR);
|
||||
@@ -1334,6 +1328,9 @@ var CustomizableUIInternal = {
|
||||
@@ -1339,6 +1328,9 @@ var CustomizableUIInternal = {
|
||||
placements = gPlacements.get(area);
|
||||
}
|
||||
|
||||
@@ -79,7 +81,7 @@ index 6d1885a54815d10b3a4e79f7e5acd61ab3abb159..3d6e092e7ed126cdee8c32229df2cfa1
|
||||
// For toolbars that need it, mark as dirty.
|
||||
let defaultPlacements = areaProperties.get("defaultPlacements");
|
||||
if (
|
||||
@@ -1842,7 +1839,6 @@ var CustomizableUIInternal = {
|
||||
@@ -1847,7 +1839,6 @@ var CustomizableUIInternal = {
|
||||
lazy.log.info(
|
||||
"Widget " + aWidgetId + " not found, unable to remove from " + aArea
|
||||
);
|
||||
@@ -87,7 +89,7 @@ index 6d1885a54815d10b3a4e79f7e5acd61ab3abb159..3d6e092e7ed126cdee8c32229df2cfa1
|
||||
}
|
||||
|
||||
this.notifyDOMChange(widgetNode, null, container, true, () => {
|
||||
@@ -1852,7 +1848,7 @@ var CustomizableUIInternal = {
|
||||
@@ -1857,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)) {
|
||||
@@ -96,7 +98,7 @@ index 6d1885a54815d10b3a4e79f7e5acd61ab3abb159..3d6e092e7ed126cdee8c32229df2cfa1
|
||||
} else {
|
||||
window.gNavToolbox.palette.appendChild(widgetNode);
|
||||
}
|
||||
@@ -2020,16 +2016,16 @@ var CustomizableUIInternal = {
|
||||
@@ -2025,16 +2016,16 @@ var CustomizableUIInternal = {
|
||||
elem.setAttribute("skipintoolbarset", "true");
|
||||
}
|
||||
}
|
||||
@@ -116,7 +118,7 @@ index 6d1885a54815d10b3a4e79f7e5acd61ab3abb159..3d6e092e7ed126cdee8c32229df2cfa1
|
||||
// Handle initial state of vertical tabs.
|
||||
if (isVerticalTabs) {
|
||||
// Show the vertical tabs toolbar
|
||||
@@ -2271,6 +2267,10 @@ var CustomizableUIInternal = {
|
||||
@@ -2276,6 +2267,10 @@ var CustomizableUIInternal = {
|
||||
* The identifier string of the area that aNode is being inserted into.
|
||||
*/
|
||||
insertWidgetBefore(aNode, aNextNode, aContainer, aAreaId) {
|
||||
@@ -127,7 +129,7 @@ index 6d1885a54815d10b3a4e79f7e5acd61ab3abb159..3d6e092e7ed126cdee8c32229df2cfa1
|
||||
this.notifyDOMChange(aNode, aNextNode, aContainer, false, () => {
|
||||
this.setLocationAttributes(aNode, aAreaId);
|
||||
aContainer.insertBefore(aNode, aNextNode);
|
||||
@@ -4647,7 +4647,7 @@ var CustomizableUIInternal = {
|
||||
@@ -4652,7 +4647,7 @@ var CustomizableUIInternal = {
|
||||
* For all registered areas, builds those areas to reflect the current
|
||||
* placement state of all widgets.
|
||||
*/
|
||||
@@ -136,7 +138,7 @@ index 6d1885a54815d10b3a4e79f7e5acd61ab3abb159..3d6e092e7ed126cdee8c32229df2cfa1
|
||||
for (let [areaId, areaNodes] of gBuildAreas) {
|
||||
let placements = gPlacements.get(areaId);
|
||||
let isFirstChangedToolbar = true;
|
||||
@@ -4658,7 +4658,7 @@ var CustomizableUIInternal = {
|
||||
@@ -4663,7 +4658,7 @@ var CustomizableUIInternal = {
|
||||
if (area.get("type") == CustomizableUI.TYPE_TOOLBAR) {
|
||||
let defaultCollapsed = area.get("defaultCollapsed");
|
||||
let win = areaNode.documentGlobal;
|
||||
@@ -145,7 +147,7 @@ index 6d1885a54815d10b3a4e79f7e5acd61ab3abb159..3d6e092e7ed126cdee8c32229df2cfa1
|
||||
win.setToolbarVisibility(
|
||||
areaNode,
|
||||
typeof defaultCollapsed == "string"
|
||||
@@ -5971,6 +5971,7 @@ export var CustomizableUI = {
|
||||
@@ -5976,6 +5971,7 @@ export var CustomizableUI = {
|
||||
unregisterArea(aName, aDestroyPlacements) {
|
||||
CustomizableUIInternal.unregisterArea(aName, aDestroyPlacements);
|
||||
},
|
||||
@@ -153,7 +155,7 @@ index 6d1885a54815d10b3a4e79f7e5acd61ab3abb159..3d6e092e7ed126cdee8c32229df2cfa1
|
||||
/**
|
||||
* Add a widget to an area.
|
||||
* If the area to which you try to add is not known to CustomizableUI,
|
||||
@@ -7946,7 +7947,9 @@ class OverflowableToolbar {
|
||||
@@ -7951,7 +7947,9 @@ class OverflowableToolbar {
|
||||
);
|
||||
|
||||
if (webExtList && CustomizableUI.isWebExtensionWidget(child.id)) {
|
||||
@@ -163,7 +165,7 @@ index 6d1885a54815d10b3a4e79f7e5acd61ab3abb159..3d6e092e7ed126cdee8c32229df2cfa1
|
||||
webExtList.insertBefore(child, webExtList.firstElementChild);
|
||||
} else {
|
||||
child.setAttribute("cui-anchorid", this.#defaultListButton.id);
|
||||
@@ -7995,6 +7998,7 @@ class OverflowableToolbar {
|
||||
@@ -8000,6 +7998,7 @@ class OverflowableToolbar {
|
||||
return aElement.getBoundingClientRect().width;
|
||||
}
|
||||
|
||||
@@ -171,7 +173,7 @@ index 6d1885a54815d10b3a4e79f7e5acd61ab3abb159..3d6e092e7ed126cdee8c32229df2cfa1
|
||||
function sumChildrenInlineSize(aParent, aExceptChild = null) {
|
||||
let sum = 0;
|
||||
for (let child of aParent.children) {
|
||||
@@ -8006,7 +8010,10 @@ class OverflowableToolbar {
|
||||
@@ -8011,7 +8010,10 @@ class OverflowableToolbar {
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
@@ -183,7 +185,7 @@ index 6d1885a54815d10b3a4e79f7e5acd61ab3abb159..3d6e092e7ed126cdee8c32229df2cfa1
|
||||
if (child != aExceptChild) {
|
||||
sum += getInlineSize(child);
|
||||
}
|
||||
@@ -8030,11 +8037,11 @@ class OverflowableToolbar {
|
||||
@@ -8035,11 +8037,11 @@ class OverflowableToolbar {
|
||||
parseFloat(style.paddingLeft) -
|
||||
parseFloat(style.paddingRight) -
|
||||
toolbarChildrenWidth;
|
||||
@@ -197,7 +199,7 @@ index 6d1885a54815d10b3a4e79f7e5acd61ab3abb159..3d6e092e7ed126cdee8c32229df2cfa1
|
||||
});
|
||||
|
||||
lazy.log.debug(
|
||||
@@ -8049,7 +8056,39 @@ class OverflowableToolbar {
|
||||
@@ -8054,7 +8056,39 @@ class OverflowableToolbar {
|
||||
Math.max(targetWidth, targetChildrenWidth)
|
||||
);
|
||||
totalAvailWidth = Math.ceil(totalAvailWidth);
|
||||
@@ -238,7 +240,7 @@ index 6d1885a54815d10b3a4e79f7e5acd61ab3abb159..3d6e092e7ed126cdee8c32229df2cfa1
|
||||
return { isOverflowing, targetContentWidth, totalAvailWidth };
|
||||
}
|
||||
|
||||
@@ -8110,7 +8149,11 @@ class OverflowableToolbar {
|
||||
@@ -8115,7 +8149,11 @@ class OverflowableToolbar {
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -251,7 +253,7 @@ index 6d1885a54815d10b3a4e79f7e5acd61ab3abb159..3d6e092e7ed126cdee8c32229df2cfa1
|
||||
lazy.log.debug(
|
||||
`Need ${minSize} but width is ${totalAvailWidth} so bailing`
|
||||
);
|
||||
@@ -8143,7 +8186,7 @@ class OverflowableToolbar {
|
||||
@@ -8148,7 +8186,7 @@ class OverflowableToolbar {
|
||||
}
|
||||
}
|
||||
if (!inserted) {
|
||||
@@ -260,7 +262,7 @@ index 6d1885a54815d10b3a4e79f7e5acd61ab3abb159..3d6e092e7ed126cdee8c32229df2cfa1
|
||||
}
|
||||
child.removeAttribute("cui-anchorid");
|
||||
child.removeAttribute("overflowedItem");
|
||||
@@ -8269,6 +8312,9 @@ class OverflowableToolbar {
|
||||
@@ -8274,6 +8312,9 @@ class OverflowableToolbar {
|
||||
* if no such list exists.
|
||||
*/
|
||||
get #webExtList() {
|
||||
@@ -270,7 +272,7 @@ index 6d1885a54815d10b3a4e79f7e5acd61ab3abb159..3d6e092e7ed126cdee8c32229df2cfa1
|
||||
if (!this.#webExtListRef) {
|
||||
let targetID = this.#toolbar.getAttribute("addon-webext-overflowtarget");
|
||||
if (!targetID) {
|
||||
@@ -8280,6 +8326,9 @@ class OverflowableToolbar {
|
||||
@@ -8285,6 +8326,9 @@ class OverflowableToolbar {
|
||||
let win = this.#toolbar.documentGlobal;
|
||||
let { panel } = win.gUnifiedExtensions;
|
||||
this.#webExtListRef = panel.querySelector(`#${targetID}`);
|
||||
@@ -280,7 +282,7 @@ index 6d1885a54815d10b3a4e79f7e5acd61ab3abb159..3d6e092e7ed126cdee8c32229df2cfa1
|
||||
}
|
||||
return this.#webExtListRef;
|
||||
}
|
||||
@@ -8488,7 +8537,7 @@ class OverflowableToolbar {
|
||||
@@ -8493,7 +8537,7 @@ class OverflowableToolbar {
|
||||
break;
|
||||
}
|
||||
case "mousedown": {
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
diff --git a/browser/components/sessionstore/SessionFile.sys.mjs b/browser/components/sessionstore/SessionFile.sys.mjs
|
||||
index 5580838acd72bf0e1189d367984859529d89f5b4..e5fa6076a9cc6ada016ccc4e129cad455010a522 100644
|
||||
index 370e5c105c3d9bee05c2d9d7b63d0ee45b8331e1..1c37c19794ebe863a25f780e9a74d63526b929d7 100644
|
||||
--- a/browser/components/sessionstore/SessionFile.sys.mjs
|
||||
+++ b/browser/components/sessionstore/SessionFile.sys.mjs
|
||||
@@ -22,6 +22,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
|
||||
RunState: "resource:///modules/sessionstore/RunState.sys.mjs",
|
||||
SessionStore: "resource:///modules/sessionstore/SessionStore.sys.mjs",
|
||||
SessionWriter: "resource:///modules/sessionstore/SessionWriter.sys.mjs",
|
||||
@@ -25,6 +25,7 @@ const lazy = XPCOMUtils.declareLazy({
|
||||
"moz-src:///browser/components/sessionstore/SessionStore.sys.mjs",
|
||||
SessionWriter:
|
||||
"moz-src:///browser/components/sessionstore/SessionWriter.sys.mjs",
|
||||
+ ZenSessionStore: "resource:///modules/zen/ZenSessionManager.sys.mjs",
|
||||
});
|
||||
|
||||
const PREF_UPGRADE_BACKUP = "browser.sessionstore.upgradeBackup.latestBuildID";
|
||||
@@ -381,7 +382,7 @@ var SessionFileInternal = {
|
||||
@@ -384,7 +385,7 @@ var SessionFileInternal = {
|
||||
this._readOrigin = result.origin;
|
||||
|
||||
result.noFilesFound = noFilesFound;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
diff --git a/browser/components/sessionstore/SessionSaver.sys.mjs b/browser/components/sessionstore/SessionSaver.sys.mjs
|
||||
index 9141793550f7c7ff6aa63d4c85bf571b4499e2d0..6906fd9be7ae6ca4316133e0d6552b797c54a7ec 100644
|
||||
index 1f26bc5fdb83f55bdf0b9b7d01fafe1cd37f77f4..5916f741d0fa04c392dcdd10f843e595fe5e058c 100644
|
||||
--- a/browser/components/sessionstore/SessionSaver.sys.mjs
|
||||
+++ b/browser/components/sessionstore/SessionSaver.sys.mjs
|
||||
@@ -20,6 +20,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
|
||||
SessionFile: "resource:///modules/sessionstore/SessionFile.sys.mjs",
|
||||
SessionStore: "resource:///modules/sessionstore/SessionStore.sys.mjs",
|
||||
sessionStoreLogger: "resource:///modules/sessionstore/SessionLogger.sys.mjs",
|
||||
@@ -20,6 +20,7 @@ const lazy = XPCOMUtils.declareLazy({
|
||||
"moz-src:///browser/components/sessionstore/SessionStore.sys.mjs",
|
||||
sessionStoreLogger:
|
||||
"moz-src:///browser/components/sessionstore/SessionLogger.sys.mjs",
|
||||
+ ZenSessionStore: "resource:///modules/zen/ZenSessionManager.sys.mjs",
|
||||
});
|
||||
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
diff --git a/browser/components/sessionstore/SessionStartup.sys.mjs b/browser/components/sessionstore/SessionStartup.sys.mjs
|
||||
index 86600ffb5178599ab23270a964064ca657a3283f..70d7b5c7d69d3700475d663b595052264c67d351 100644
|
||||
index 919d4536c5462603346684bbe26794ae9279b517..5a8fae0708e4a9a82c5e85f8a473ebaeafebe71f 100644
|
||||
--- a/browser/components/sessionstore/SessionStartup.sys.mjs
|
||||
+++ b/browser/components/sessionstore/SessionStartup.sys.mjs
|
||||
@@ -40,6 +40,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
|
||||
StartupPerformance:
|
||||
"resource:///modules/sessionstore/StartupPerformance.sys.mjs",
|
||||
sessionStoreLogger: "resource:///modules/sessionstore/SessionLogger.sys.mjs",
|
||||
@@ -41,6 +41,7 @@ const lazy = XPCOMUtils.declareLazy({
|
||||
"moz-src:///browser/components/sessionstore/StartupPerformance.sys.mjs",
|
||||
sessionStoreLogger:
|
||||
"moz-src:///browser/components/sessionstore/SessionLogger.sys.mjs",
|
||||
+ ZenSessionStore: "resource:///modules/zen/ZenSessionManager.sys.mjs",
|
||||
});
|
||||
|
||||
const STATE_RUNNING_STR = "running";
|
||||
@@ -85,7 +86,7 @@ export var SessionStartup = {
|
||||
@@ -86,7 +87,7 @@ export var SessionStartup = {
|
||||
}
|
||||
|
||||
// do not need to initialize anything in auto-started private browsing sessions
|
||||
@@ -19,7 +19,7 @@ index 86600ffb5178599ab23270a964064ca657a3283f..70d7b5c7d69d3700475d663b59505226
|
||||
this._initialized = true;
|
||||
gOnceInitializedDeferred.resolve();
|
||||
return;
|
||||
@@ -179,6 +180,8 @@ export var SessionStartup = {
|
||||
@@ -180,6 +181,8 @@ export var SessionStartup = {
|
||||
this._initialState = parsed;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ index 86600ffb5178599ab23270a964064ca657a3283f..70d7b5c7d69d3700475d663b59505226
|
||||
if (this._initialState == null) {
|
||||
// No valid session found.
|
||||
this._sessionType = this.NO_SESSION;
|
||||
@@ -276,6 +279,8 @@ export var SessionStartup = {
|
||||
@@ -277,6 +280,8 @@ export var SessionStartup = {
|
||||
`Previous shutdown ok? ${this._previousSessionCrashed}, reason: ${previousSessionCrashedReason}`
|
||||
);
|
||||
|
||||
@@ -37,7 +37,7 @@ index 86600ffb5178599ab23270a964064ca657a3283f..70d7b5c7d69d3700475d663b59505226
|
||||
Services.obs.addObserver(this, "sessionstore-windows-restored", true);
|
||||
|
||||
if (this.sessionType == this.NO_SESSION) {
|
||||
@@ -336,12 +341,7 @@ export var SessionStartup = {
|
||||
@@ -337,12 +342,7 @@ export var SessionStartup = {
|
||||
isAutomaticRestoreEnabled() {
|
||||
if (this._resumeSessionEnabled === null) {
|
||||
this._resumeSessionEnabled =
|
||||
@@ -51,7 +51,7 @@ index 86600ffb5178599ab23270a964064ca657a3283f..70d7b5c7d69d3700475d663b59505226
|
||||
}
|
||||
|
||||
return this._resumeSessionEnabled;
|
||||
@@ -354,8 +354,7 @@ export var SessionStartup = {
|
||||
@@ -355,8 +355,7 @@ export var SessionStartup = {
|
||||
*/
|
||||
willRestore() {
|
||||
return (
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
diff --git a/browser/components/sessionstore/SessionStore.sys.mjs b/browser/components/sessionstore/SessionStore.sys.mjs
|
||||
index 76a984f6e21409941ea038218d161e67f2e0c23d..52408568a33a89c0e3d0d1514152ded2eaa1afed 100644
|
||||
index 52fc803050cd257d6de52b1e4a92f3211cbaba88..a9841832ddce540dc0f9b14c29e5eadac7510935 100644
|
||||
--- a/browser/components/sessionstore/SessionStore.sys.mjs
|
||||
+++ b/browser/components/sessionstore/SessionStore.sys.mjs
|
||||
@@ -126,6 +126,11 @@ const TAB_EVENTS = [
|
||||
@@ -111,6 +111,11 @@ const TAB_EVENTS = [
|
||||
"TabSplitViewActivate",
|
||||
"SplitViewRemoved",
|
||||
"SplitViewCreated",
|
||||
@@ -14,16 +14,39 @@ index 76a984f6e21409941ea038218d161e67f2e0c23d..52408568a33a89c0e3d0d1514152ded2
|
||||
];
|
||||
|
||||
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
|
||||
@@ -198,6 +203,8 @@ ChromeUtils.defineESModuleGetters(lazy, {
|
||||
TabStateCache: "resource:///modules/sessionstore/TabStateCache.sys.mjs",
|
||||
TabStateFlusher: "resource:///modules/sessionstore/TabStateFlusher.sys.mjs",
|
||||
@@ -185,6 +190,8 @@ const lazy = XPCOMUtils.declareLazy({
|
||||
TabStateFlusher:
|
||||
"moz-src:///browser/components/sessionstore/TabStateFlusher.sys.mjs",
|
||||
setTimeout: "resource://gre/modules/Timer.sys.mjs",
|
||||
+ ZenSessionStore: "resource:///modules/zen/ZenSessionManager.sys.mjs",
|
||||
+ ZenWindowSync: "resource:///modules/zen/ZenWindowSync.sys.mjs",
|
||||
});
|
||||
blankURI: () => Services.io.newURI("about:blank"),
|
||||
gRestoreWindowsToVirtualDesktop: {
|
||||
pref: "browser.sessionstore.restore_windows_to_virtual_desktop",
|
||||
@@ -348,6 +355,22 @@ class _SessionStore {
|
||||
return this.#shouldRestoreLastSession;
|
||||
}
|
||||
|
||||
ChromeUtils.defineLazyGetter(lazy, "blankURI", () => {
|
||||
@@ -1314,10 +1321,7 @@ var SessionStoreInternal = {
|
||||
+ get _windows() {
|
||||
+ return this.#windows;
|
||||
+ }
|
||||
+
|
||||
+ get _browserWindows() {
|
||||
+ return this.#browserWindows;
|
||||
+ }
|
||||
+
|
||||
+ set _deferredInitialState(aState) {
|
||||
+ this.#deferredInitialState = aState;
|
||||
+ }
|
||||
+
|
||||
+ initializeWindow(aWindow, aInitialState) {
|
||||
+ this.#initializeWindow(aWindow, aInitialState);
|
||||
+ }
|
||||
+
|
||||
// whether we will potentially be restoring the session
|
||||
// more than once without Firefox restarting in between
|
||||
#restoreWithoutRestart = false;
|
||||
@@ -515,10 +538,7 @@ class _SessionStore {
|
||||
*/
|
||||
get willAutoRestore() {
|
||||
return (
|
||||
@@ -33,9 +56,9 @@ index 76a984f6e21409941ea038218d161e67f2e0c23d..52408568a33a89c0e3d0d1514152ded2
|
||||
- BROWSER_STARTUP_RESUME_SESSION)
|
||||
+ true
|
||||
);
|
||||
},
|
||||
}
|
||||
|
||||
@@ -2005,6 +2009,11 @@ var SessionStoreInternal = {
|
||||
@@ -1215,6 +1235,11 @@ class _SessionStore {
|
||||
case "TabPinned":
|
||||
case "TabUnpinned":
|
||||
case "SwapDocShells":
|
||||
@@ -44,47 +67,47 @@ index 76a984f6e21409941ea038218d161e67f2e0c23d..52408568a33a89c0e3d0d1514152ded2
|
||||
+ case "TabMove":
|
||||
+ case "TabGroupMoved":
|
||||
+ case "ZenWorkspaceDataChanged":
|
||||
this.saveStateDelayed(win);
|
||||
this.#saveStateDelayed(win);
|
||||
break;
|
||||
case "TabGroupCreate":
|
||||
@@ -2114,6 +2123,10 @@ var SessionStoreInternal = {
|
||||
this._windows[aWindow.__SSi].isTaskbarTab = true;
|
||||
@@ -1341,6 +1366,10 @@ class _SessionStore {
|
||||
this.#windows[aWindow.__SSi].isTaskbarTab = true;
|
||||
}
|
||||
|
||||
+ if (aWindow.document.documentElement.hasAttribute("zen-unsynced-window")) {
|
||||
+ this._windows[aWindow.__SSi].isZenUnsynced = true;
|
||||
+ this.#windows[aWindow.__SSi].isZenUnsynced = true;
|
||||
+ }
|
||||
+
|
||||
if (lazy.AIWindow.isAIWindowActiveAndEnabled(aWindow)) {
|
||||
this._windows[aWindow.__SSi].isAIWindow = true;
|
||||
this.#windows[aWindow.__SSi].isAIWindow = true;
|
||||
}
|
||||
@@ -2150,7 +2163,7 @@ var SessionStoreInternal = {
|
||||
let isTaskbarTab = this._windows[aWindow.__SSi].isTaskbarTab;
|
||||
@@ -1377,7 +1406,7 @@ class _SessionStore {
|
||||
let isTaskbarTab = this.#windows[aWindow.__SSi].isTaskbarTab;
|
||||
// A regular window is not a private window, taskbar tab window, or popup window
|
||||
let isRegularWindow =
|
||||
- !isPrivateWindow && !isTaskbarTab && aWindow.toolbar.visible;
|
||||
+ !isPrivateWindow && !isTaskbarTab && aWindow.toolbar.visible && !this._windows[aWindow.__SSi].isZenUnsynced;
|
||||
+ !isPrivateWindow && !isTaskbarTab && aWindow.toolbar.visible && !this.#windows[aWindow.__SSi].isZenUnsynced;
|
||||
|
||||
// perform additional initialization when the first window is loading
|
||||
if (lazy.RunState.isStopped) {
|
||||
@@ -2162,7 +2175,7 @@ var SessionStoreInternal = {
|
||||
@@ -1389,7 +1418,7 @@ class _SessionStore {
|
||||
// to disk to NOW() to enforce a full interval before the next write.
|
||||
lazy.SessionSaver.updateLastSaveTime();
|
||||
|
||||
- if (isPrivateWindow || isTaskbarTab) {
|
||||
+ if (isPrivateWindow || isTaskbarTab || aWindow.document.documentElement.hasAttribute("zen-unsynced-window")) {
|
||||
this._log.debug(
|
||||
this.#log.debug(
|
||||
"initializeWindow, the window is private or a web app. Saving SessionStartup.state for possibly restoring later"
|
||||
);
|
||||
@@ -2206,6 +2219,7 @@ var SessionStoreInternal = {
|
||||
@@ -1433,6 +1462,7 @@ class _SessionStore {
|
||||
null,
|
||||
"sessionstore-one-or-no-tab-restored"
|
||||
);
|
||||
+ lazy.ZenSessionStore.onNewEmptySession(aWindow);
|
||||
this._deferredAllWindowsRestored.resolve();
|
||||
this.#deferredAllWindowsRestored.resolve();
|
||||
}
|
||||
// this window was opened by _openWindowWithState
|
||||
@@ -2255,7 +2269,6 @@ var SessionStoreInternal = {
|
||||
// this window was opened by #openWindowWithState
|
||||
@@ -1482,7 +1512,6 @@ class _SessionStore {
|
||||
if (closedWindowState) {
|
||||
let newWindowState;
|
||||
if (
|
||||
@@ -92,25 +115,25 @@ index 76a984f6e21409941ea038218d161e67f2e0c23d..52408568a33a89c0e3d0d1514152ded2
|
||||
!lazy.SessionStartup.willRestore()
|
||||
) {
|
||||
// We want to split the window up into pinned tabs and unpinned tabs.
|
||||
@@ -2291,6 +2304,7 @@ var SessionStoreInternal = {
|
||||
@@ -1518,6 +1547,7 @@ class _SessionStore {
|
||||
}
|
||||
|
||||
if (newWindowState) {
|
||||
+ lazy.ZenSessionStore.onRestoringClosedWindow(newWindowState);
|
||||
// Ensure that the window state isn't hidden
|
||||
this._restoreCount = 1;
|
||||
this.#restoreCount = 1;
|
||||
let state = { windows: [newWindowState] };
|
||||
@@ -2327,6 +2341,9 @@ var SessionStoreInternal = {
|
||||
@@ -1554,6 +1584,9 @@ class _SessionStore {
|
||||
});
|
||||
this._shouldRestoreLastSession = false;
|
||||
this.#shouldRestoreLastSession = false;
|
||||
}
|
||||
+ else if (!aInitialState && isRegularWindow) {
|
||||
+ lazy.ZenSessionStore.restoreNewWindow(aWindow, this);
|
||||
+ }
|
||||
|
||||
if (this._restoreLastWindow && aWindow.toolbar.visible) {
|
||||
if (this.#restoreLastWindow && aWindow.toolbar.visible) {
|
||||
// always reset (if not a popup window)
|
||||
@@ -2477,7 +2494,7 @@ var SessionStoreInternal = {
|
||||
@@ -1704,7 +1737,7 @@ class _SessionStore {
|
||||
|
||||
var tabbrowser = aWindow.gBrowser;
|
||||
|
||||
@@ -119,56 +142,56 @@ index 76a984f6e21409941ea038218d161e67f2e0c23d..52408568a33a89c0e3d0d1514152ded2
|
||||
|
||||
TAB_EVENTS.forEach(function (aEvent) {
|
||||
tabbrowser.tabContainer.removeEventListener(aEvent, this, true);
|
||||
@@ -2528,7 +2545,7 @@ var SessionStoreInternal = {
|
||||
@@ -1755,7 +1788,7 @@ class _SessionStore {
|
||||
|
||||
let isLastRegularWindow =
|
||||
Object.values(this._windows).filter(
|
||||
Object.values(this.#windows).filter(
|
||||
- wData => !wData.isPrivate && !wData.isTaskbarTab
|
||||
+ wData => !wData.isPrivate && !wData.isTaskbarTab && !wData.isZenUnsynced
|
||||
).length == 1;
|
||||
this._log.debug(
|
||||
this.#log.debug(
|
||||
`onClose, closing window isLastRegularWindow? ${isLastRegularWindow}`
|
||||
@@ -2585,8 +2602,8 @@ var SessionStoreInternal = {
|
||||
@@ -1812,8 +1845,8 @@ class _SessionStore {
|
||||
// 2) Flush the window.
|
||||
// 3) When the flush is complete, revisit our decision to store the window
|
||||
// in _closedWindows, and add/remove as necessary.
|
||||
// in #closedWindows, and add/remove as necessary.
|
||||
- if (!winData.isPrivate && !winData.isTaskbarTab) {
|
||||
- this.maybeSaveClosedWindow(winData, isLastWindow);
|
||||
- this.#maybeSaveClosedWindow(winData, isLastWindow);
|
||||
+ if (!winData.isPrivate && !winData.isTaskbarTab && !winData.isZenUnsynced) {
|
||||
+ this.maybeSaveClosedWindow(winData, isLastWindow, isLastRegularWindow);
|
||||
+ this.#maybeSaveClosedWindow(winData, isLastWindow, isLastRegularWindow);
|
||||
}
|
||||
|
||||
completionPromise = lazy.TabStateFlusher.flushWindow(aWindow).then(() => {
|
||||
@@ -2606,8 +2623,9 @@ var SessionStoreInternal = {
|
||||
@@ -1833,8 +1866,9 @@ class _SessionStore {
|
||||
|
||||
// Save non-private windows if they have at
|
||||
// least one saveable tab or are the last window.
|
||||
- if (!winData.isPrivate && !winData.isTaskbarTab) {
|
||||
- this.maybeSaveClosedWindow(winData, isLastWindow);
|
||||
- this.#maybeSaveClosedWindow(winData, isLastWindow);
|
||||
+ lazy.ZenWindowSync.on_WindowCloseAndBrowserFlushed(browsers);
|
||||
+ if (!winData.isPrivate && !winData.isTaskbarTab && !winData.isZenUnsynced) {
|
||||
+ this.maybeSaveClosedWindow(winData, isLastWindow, isLastRegularWindow);
|
||||
+ this.#maybeSaveClosedWindow(winData, isLastWindow, isLastRegularWindow);
|
||||
|
||||
if (!isLastWindow && winData.closedId > -1) {
|
||||
this._addClosedAction(
|
||||
@@ -2683,7 +2701,7 @@ var SessionStoreInternal = {
|
||||
this.#addClosedAction(
|
||||
@@ -1910,7 +1944,7 @@ class _SessionStore {
|
||||
* to call this method again asynchronously (for example, after
|
||||
* a window flush).
|
||||
*/
|
||||
- maybeSaveClosedWindow(winData, isLastWindow) {
|
||||
+ maybeSaveClosedWindow(winData, isLastWindow, isLastRegularWindow = false) {
|
||||
- #maybeSaveClosedWindow(winData, isLastWindow) {
|
||||
+ #maybeSaveClosedWindow(winData, isLastWindow, isLastRegularWindow = false) {
|
||||
// Make sure SessionStore is still running, and make sure that we
|
||||
// haven't chosen to forget this window.
|
||||
if (
|
||||
@@ -2700,6 +2718,7 @@ var SessionStoreInternal = {
|
||||
// _closedWindows from a previous call to this function.
|
||||
let winIndex = this._closedWindows.indexOf(winData);
|
||||
@@ -1927,6 +1961,7 @@ class _SessionStore {
|
||||
// #closedWindows from a previous call to this function.
|
||||
let winIndex = this.#closedWindows.indexOf(winData);
|
||||
let alreadyStored = winIndex != -1;
|
||||
+ lazy.ZenSessionStore.maybeSaveClosedWindow(winData, isLastRegularWindow);
|
||||
// If sidebar command is truthy, i.e. sidebar is open, store sidebar settings
|
||||
let shouldStore = hasSaveableTabs || isLastWindow;
|
||||
|
||||
@@ -3522,7 +3541,7 @@ var SessionStoreInternal = {
|
||||
@@ -2744,7 +2779,7 @@ class _SessionStore {
|
||||
if (!isPrivateWindow && tabState.isPrivate) {
|
||||
return;
|
||||
}
|
||||
@@ -177,7 +200,7 @@ index 76a984f6e21409941ea038218d161e67f2e0c23d..52408568a33a89c0e3d0d1514152ded2
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -4260,7 +4279,15 @@ var SessionStoreInternal = {
|
||||
@@ -3511,7 +3546,15 @@ class _SessionStore {
|
||||
1,
|
||||
Math.min(tabState.index, tabState.entries.length)
|
||||
);
|
||||
@@ -194,7 +217,7 @@ index 76a984f6e21409941ea038218d161e67f2e0c23d..52408568a33a89c0e3d0d1514152ded2
|
||||
|
||||
if (inBackground === false) {
|
||||
aWindow.gBrowser.selectedTab = newTab;
|
||||
@@ -4694,6 +4721,8 @@ var SessionStoreInternal = {
|
||||
@@ -4036,6 +4079,8 @@ class _SessionStore {
|
||||
// Append the tab if we're opening into a different window,
|
||||
tabIndex: aSource == aTargetWindow ? pos : Infinity,
|
||||
pinned: state.pinned,
|
||||
@@ -203,7 +226,7 @@ index 76a984f6e21409941ea038218d161e67f2e0c23d..52408568a33a89c0e3d0d1514152ded2
|
||||
userContextId: state.userContextId,
|
||||
skipLoad: true,
|
||||
preferredRemoteType,
|
||||
@@ -5191,9 +5220,10 @@ var SessionStoreInternal = {
|
||||
@@ -4608,9 +4653,10 @@ class _SessionStore {
|
||||
if (activePageData.title && activePageData.title != activePageData.url) {
|
||||
win.gBrowser.setInitialTabTitle(tab, activePageData.title, {
|
||||
isContentTitle: true,
|
||||
@@ -215,7 +238,7 @@ index 76a984f6e21409941ea038218d161e67f2e0c23d..52408568a33a89c0e3d0d1514152ded2
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5561,7 +5591,7 @@ var SessionStoreInternal = {
|
||||
@@ -4982,7 +5028,7 @@ class _SessionStore {
|
||||
|
||||
for (let i = tabbrowser.pinnedTabCount; i < tabbrowser.tabs.length; i++) {
|
||||
let tab = tabbrowser.tabs[i];
|
||||
@@ -224,30 +247,30 @@ index 76a984f6e21409941ea038218d161e67f2e0c23d..52408568a33a89c0e3d0d1514152ded2
|
||||
removableTabs.push(tab);
|
||||
}
|
||||
}
|
||||
@@ -5674,7 +5704,7 @@ var SessionStoreInternal = {
|
||||
@@ -5095,7 +5141,7 @@ class _SessionStore {
|
||||
|
||||
// collect the data for all windows
|
||||
for (ix in this._windows) {
|
||||
- if (this._windows[ix]._restoring || this._windows[ix].isTaskbarTab) {
|
||||
+ if (this._windows[ix]._restoring || this._windows[ix].isTaskbarTab && !this._windows[ix].isZenUnsynced) {
|
||||
// window data is still in _statesToRestore
|
||||
for (ix in this.#windows) {
|
||||
- if (this.#windows[ix]._restoring || this.#windows[ix].isTaskbarTab) {
|
||||
+ if (this.#windows[ix]._restoring || this.#windows[ix].isTaskbarTab && !this.#windows[ix].isZenUnsynced) {
|
||||
// window data is still in #statesToRestore
|
||||
continue;
|
||||
}
|
||||
@@ -5818,11 +5848,12 @@ var SessionStoreInternal = {
|
||||
@@ -5239,11 +5285,12 @@ class _SessionStore {
|
||||
}
|
||||
|
||||
let tabbrowser = aWindow.gBrowser;
|
||||
- let tabs = tabbrowser.tabs;
|
||||
+ let tabs = aWindow.gZenWorkspaces.allStoredTabs;
|
||||
/** @type {WindowStateData} */
|
||||
let winData = this._windows[aWindow.__SSi];
|
||||
let winData = this.#windows[aWindow.__SSi];
|
||||
let tabsData = (winData.tabs = []);
|
||||
|
||||
+ winData.splitViewData = aWindow.gZenViewSplitter?.storeDataForSessionStore();
|
||||
// update the internal state data for this window
|
||||
for (let tab of tabs) {
|
||||
if (tab == aWindow.FirefoxViewHandler.tab) {
|
||||
@@ -5833,6 +5864,9 @@ var SessionStoreInternal = {
|
||||
@@ -5254,6 +5301,9 @@ class _SessionStore {
|
||||
tabsData.push(tabData);
|
||||
}
|
||||
|
||||
@@ -257,7 +280,7 @@ index 76a984f6e21409941ea038218d161e67f2e0c23d..52408568a33a89c0e3d0d1514152ded2
|
||||
// update tab group state for this window
|
||||
winData.groups = [];
|
||||
for (let tabGroup of aWindow.gBrowser.tabGroups) {
|
||||
@@ -5849,7 +5883,7 @@ var SessionStoreInternal = {
|
||||
@@ -5270,7 +5320,7 @@ class _SessionStore {
|
||||
// a window is closed, point to the first item in the tab strip instead (it will never be the Firefox View tab,
|
||||
// since it's only inserted into the tab strip after it's selected).
|
||||
if (aWindow.FirefoxViewHandler.tab?.selected) {
|
||||
@@ -266,7 +289,7 @@ index 76a984f6e21409941ea038218d161e67f2e0c23d..52408568a33a89c0e3d0d1514152ded2
|
||||
winData.title = tabbrowser.tabs[0].label;
|
||||
}
|
||||
winData.selected = selectedIndex;
|
||||
@@ -6051,8 +6085,8 @@ var SessionStoreInternal = {
|
||||
@@ -5472,8 +5522,8 @@ class _SessionStore {
|
||||
// selectTab represents.
|
||||
let selectTab = 0;
|
||||
if (overwriteTabs) {
|
||||
@@ -277,7 +300,7 @@ index 76a984f6e21409941ea038218d161e67f2e0c23d..52408568a33a89c0e3d0d1514152ded2
|
||||
selectTab = Math.min(selectTab, winData.tabs.length);
|
||||
}
|
||||
|
||||
@@ -6074,6 +6108,7 @@ var SessionStoreInternal = {
|
||||
@@ -5495,6 +5545,7 @@ class _SessionStore {
|
||||
if (overwriteTabs) {
|
||||
for (let i = tabbrowser.browsers.length - 1; i >= 0; i--) {
|
||||
if (!tabbrowser.tabs[i].selected) {
|
||||
@@ -285,7 +308,7 @@ index 76a984f6e21409941ea038218d161e67f2e0c23d..52408568a33a89c0e3d0d1514152ded2
|
||||
tabbrowser.removeTab(tabbrowser.tabs[i]);
|
||||
}
|
||||
}
|
||||
@@ -6108,6 +6143,12 @@ var SessionStoreInternal = {
|
||||
@@ -5529,6 +5580,12 @@ class _SessionStore {
|
||||
savedTabGroup => !openTabGroupIdsInWindow.has(savedTabGroup.id)
|
||||
);
|
||||
}
|
||||
@@ -298,7 +321,7 @@ index 76a984f6e21409941ea038218d161e67f2e0c23d..52408568a33a89c0e3d0d1514152ded2
|
||||
|
||||
// Move the originally open tabs to the end.
|
||||
if (initialTabs) {
|
||||
@@ -6674,6 +6715,25 @@ var SessionStoreInternal = {
|
||||
@@ -6098,6 +6155,25 @@ class _SessionStore {
|
||||
|
||||
// Most of tabData has been restored, now continue with restoring
|
||||
// attributes that may trigger external events.
|
||||
@@ -324,24 +347,25 @@ index 76a984f6e21409941ea038218d161e67f2e0c23d..52408568a33a89c0e3d0d1514152ded2
|
||||
|
||||
if (tabData.pinned) {
|
||||
tabbrowser.pinTab(tab);
|
||||
@@ -6859,6 +6919,9 @@ var SessionStoreInternal = {
|
||||
@@ -6272,6 +6348,9 @@ class _SessionStore {
|
||||
aWindow.gURLBar.readOnly = false;
|
||||
}
|
||||
}
|
||||
+ if (aWinData.isZenUnsynced) {
|
||||
+ this._windows[aWindow.__SSi].isZenUnsynced = true;
|
||||
+ this.#windows[aWindow.__SSi].isZenUnsynced = true;
|
||||
+ }
|
||||
|
||||
let promiseParts = Promise.withResolvers();
|
||||
aWindow.setTimeout(() => {
|
||||
@@ -7356,5 +7419,6 @@ var SessionStoreInternal = {
|
||||
const wasMinimized = aWindow.windowState == aWindow.STATE_MINIMIZED;
|
||||
@@ -6774,6 +6853,7 @@ class _SessionStore {
|
||||
*/
|
||||
_isCmdLineEmpty: function ssi_isCmdLineEmpty(aWindow, aState) {
|
||||
#isCmdLineEmpty(aWindow, aState) {
|
||||
var pinnedOnly =
|
||||
+ !Services.prefs.getBoolPref("zen.workspaces.continue-where-left-off") &&
|
||||
aState.windows &&
|
||||
aState.windows.every(win => win.tabs.every(tab => tab.pinned));
|
||||
@@ -7654,7 +7718,7 @@ var SessionStoreInternal = {
|
||||
|
||||
@@ -7116,7 +7196,7 @@ class _SessionStore {
|
||||
|
||||
let groupsToSave = new Map();
|
||||
for (let tIndex = 0; tIndex < window.tabs.length; ) {
|
||||
@@ -350,7 +374,7 @@ index 76a984f6e21409941ea038218d161e67f2e0c23d..52408568a33a89c0e3d0d1514152ded2
|
||||
// Adjust window.selected
|
||||
if (tIndex + 1 < window.selected) {
|
||||
window.selected -= 1;
|
||||
@@ -7669,7 +7733,7 @@ var SessionStoreInternal = {
|
||||
@@ -7131,7 +7211,7 @@ class _SessionStore {
|
||||
);
|
||||
// We don't want to increment tIndex here.
|
||||
continue;
|
||||
@@ -359,15 +383,15 @@ index 76a984f6e21409941ea038218d161e67f2e0c23d..52408568a33a89c0e3d0d1514152ded2
|
||||
// Convert any open groups into saved groups.
|
||||
let groupStateToSave = window.groups.find(
|
||||
groupState => groupState.id == window.tabs[tIndex].groupId
|
||||
@@ -8233,7 +8297,6 @@ var SessionStoreInternal = {
|
||||
@@ -7691,7 +7771,6 @@ class _SessionStore {
|
||||
timer.initWithCallback(
|
||||
function () {
|
||||
() => {
|
||||
if (beats <= 0) {
|
||||
- this._log.debug(`looseTimer of ${delay} timed out`);
|
||||
- this.#log.debug(`looseTimer of ${delay} timed out`);
|
||||
Glean.sessionRestore.shutdownFlushAllOutcomes.timed_out.add(1);
|
||||
deferred.resolve();
|
||||
}
|
||||
@@ -8711,6 +8774,7 @@ var SessionStoreInternal = {
|
||||
@@ -8174,6 +8253,7 @@ class _SessionStore {
|
||||
if (
|
||||
!savedTabGroupState.tabs.length ||
|
||||
this.getSavedTabGroup(savedTabGroupState.id)
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
diff --git a/browser/components/sessionstore/TabState.sys.mjs b/browser/components/sessionstore/TabState.sys.mjs
|
||||
index a33deeb5b587d6a950960a1a4781176e4e730ad7..7927471d389f2626e4cc1b0e7a6eab6446dca432 100644
|
||||
index bb7bd8c7ca88f776188fce64e0c85beb5ceb27a5..bdec77ba26172fe1b41369b1d45d5a6f9d52ae25 100644
|
||||
--- a/browser/components/sessionstore/TabState.sys.mjs
|
||||
+++ b/browser/components/sessionstore/TabState.sys.mjs
|
||||
@@ -8,6 +8,7 @@ ChromeUtils.defineESModuleGetters(lazy, {
|
||||
PrivacyFilter: "resource://gre/modules/sessionstore/PrivacyFilter.sys.mjs",
|
||||
TabAttributes: "resource:///modules/sessionstore/TabAttributes.sys.mjs",
|
||||
TabStateCache: "resource:///modules/sessionstore/TabStateCache.sys.mjs",
|
||||
@@ -10,6 +10,7 @@ const lazy = XPCOMUtils.declareLazy({
|
||||
"moz-src:///browser/components/sessionstore/TabAttributes.sys.mjs",
|
||||
TabStateCache:
|
||||
"moz-src:///browser/components/sessionstore/TabStateCache.sys.mjs",
|
||||
+ UrlbarShared: "chrome://browser/content/urlbar/UrlbarShared.mjs",
|
||||
sessionStoreLogger: "resource:///modules/sessionstore/SessionLogger.sys.mjs",
|
||||
sessionStoreLogger:
|
||||
"moz-src:///browser/components/sessionstore/SessionLogger.sys.mjs",
|
||||
});
|
||||
|
||||
@@ -103,10 +104,28 @@ class _TabState {
|
||||
@@ -106,10 +107,28 @@ class _TabState {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ index a33deeb5b587d6a950960a1a4781176e4e730ad7..7927471d389f2626e4cc1b0e7a6eab64
|
||||
|
||||
tabData.userContextId = tab.userContextId || 0;
|
||||
|
||||
@@ -132,6 +151,10 @@ class _TabState {
|
||||
@@ -135,6 +154,10 @@ class _TabState {
|
||||
tabData.image = tabbrowser.getIcon(tab);
|
||||
}
|
||||
|
||||
|
||||
1211
src/browser/components/tabbrowser/Tabbrowser-sys-mjs.patch
Normal file
1211
src/browser/components/tabbrowser/Tabbrowser-sys-mjs.patch
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,8 @@
|
||||
diff --git a/browser/components/tabbrowser/content/drag-and-drop.js b/browser/components/tabbrowser/content/drag-and-drop.js
|
||||
index 70b37f93c4e8ff4ad9a2452681e468d23914c1b5..dbf2d9c83dafbe0e0b4e6caf3835edc8edf9bcf4 100644
|
||||
index f9623f82499e516050d523068e70ebaa4fe9a66a..95bcc078a86b4ec4b35f5916b7e4a5e305afba9e 100644
|
||||
--- a/browser/components/tabbrowser/content/drag-and-drop.js
|
||||
+++ b/browser/components/tabbrowser/content/drag-and-drop.js
|
||||
@@ -35,6 +35,9 @@
|
||||
@@ -42,6 +42,9 @@
|
||||
* @returns {MozTabbrowserTab|vbox}
|
||||
*/
|
||||
const elementToMove = element => {
|
||||
@@ -12,7 +12,7 @@ index 70b37f93c4e8ff4ad9a2452681e468d23914c1b5..dbf2d9c83dafbe0e0b4e6caf3835edc8
|
||||
if (isTab(element) || isSplitViewWrapper(element)) {
|
||||
return element;
|
||||
}
|
||||
@@ -128,6 +131,9 @@
|
||||
@@ -140,6 +143,9 @@
|
||||
}
|
||||
|
||||
let draggedTab = event.dataTransfer.mozGetDataAt(TAB_DROP_TYPE, 0);
|
||||
@@ -22,15 +22,15 @@ index 70b37f93c4e8ff4ad9a2452681e468d23914c1b5..dbf2d9c83dafbe0e0b4e6caf3835edc8
|
||||
if (
|
||||
(dropEffect == "move" || dropEffect == "copy") &&
|
||||
document == draggedTab.ownerDocument &&
|
||||
@@ -148,6 +154,7 @@
|
||||
!draggedTab.group.collapsed
|
||||
@@ -161,6 +167,7 @@
|
||||
) {
|
||||
draggedTab.group.collapsedByDrag = true;
|
||||
draggedTab.group.collapsed = true;
|
||||
+ gZenFolders.animateGroupMove(draggedTab.group);
|
||||
}
|
||||
|
||||
if (dropEffect == "move") {
|
||||
@@ -155,10 +162,6 @@
|
||||
@@ -168,10 +175,6 @@
|
||||
|
||||
// Pinned tabs in expanded vertical mode are on a grid format and require
|
||||
// different logic to drag and drop.
|
||||
@@ -41,7 +41,7 @@ index 70b37f93c4e8ff4ad9a2452681e468d23914c1b5..dbf2d9c83dafbe0e0b4e6caf3835edc8
|
||||
this._animateTabMove(event);
|
||||
return;
|
||||
}
|
||||
@@ -297,6 +300,10 @@
|
||||
@@ -310,6 +313,10 @@
|
||||
|
||||
this._tabDropIndicator.hidden = true;
|
||||
event.stopPropagation();
|
||||
@@ -52,7 +52,7 @@ index 70b37f93c4e8ff4ad9a2452681e468d23914c1b5..dbf2d9c83dafbe0e0b4e6caf3835edc8
|
||||
if (draggedTab && dropEffect == "copy") {
|
||||
let duplicatedDraggedTab;
|
||||
let duplicatedTabs = [];
|
||||
@@ -324,12 +331,16 @@
|
||||
@@ -337,12 +344,16 @@
|
||||
let translateOffsetY = oldTranslateY % tabHeight;
|
||||
let newTranslateX = oldTranslateX - translateOffsetX;
|
||||
let newTranslateY = oldTranslateY - translateOffsetY;
|
||||
@@ -73,7 +73,7 @@ index 70b37f93c4e8ff4ad9a2452681e468d23914c1b5..dbf2d9c83dafbe0e0b4e6caf3835edc8
|
||||
|
||||
if (this._tabbrowserTabs.isContainerVerticalPinnedGrid(draggedTab)) {
|
||||
// Update both translate axis for pinned vertical expanded tabs
|
||||
@@ -404,11 +415,13 @@
|
||||
@@ -417,11 +428,13 @@
|
||||
this._dragToPinPromoCard,
|
||||
];
|
||||
let shouldPin =
|
||||
@@ -87,7 +87,7 @@ index 70b37f93c4e8ff4ad9a2452681e468d23914c1b5..dbf2d9c83dafbe0e0b4e6caf3835edc8
|
||||
isTab(draggedTab) &&
|
||||
draggedTab.pinned &&
|
||||
this._tabbrowserTabs.arrowScrollbox.contains(event.target);
|
||||
@@ -484,6 +497,7 @@
|
||||
@@ -492,6 +505,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ index 70b37f93c4e8ff4ad9a2452681e468d23914c1b5..dbf2d9c83dafbe0e0b4e6caf3835edc8
|
||||
if (shouldTranslate) {
|
||||
this.#dropAnimationEndTime = Date.now() + DROP_ANIMATION_GRACE_MS;
|
||||
let translationPromises = [];
|
||||
@@ -496,7 +510,7 @@
|
||||
@@ -504,7 +518,7 @@
|
||||
item.removeAttribute("tabdrop-samewindow");
|
||||
resolve();
|
||||
};
|
||||
@@ -104,7 +104,7 @@ index 70b37f93c4e8ff4ad9a2452681e468d23914c1b5..dbf2d9c83dafbe0e0b4e6caf3835edc8
|
||||
postTransitionCleanup();
|
||||
} else {
|
||||
let onTransitionEnd = transitionendEvent => {
|
||||
@@ -580,7 +594,7 @@
|
||||
@@ -595,7 +609,7 @@
|
||||
if (tab.selected) {
|
||||
selectedTab = tab;
|
||||
indexForSelectedTab = newIndex;
|
||||
@@ -113,7 +113,7 @@ index 70b37f93c4e8ff4ad9a2452681e468d23914c1b5..dbf2d9c83dafbe0e0b4e6caf3835edc8
|
||||
const droppedIntoPinnedArea = dropIndex < gBrowser.pinnedTabCount;
|
||||
const newSplitView = gBrowser.adoptSplitView(tab, {
|
||||
elementIndex: droppedIntoPinnedArea
|
||||
@@ -605,7 +619,7 @@
|
||||
@@ -620,7 +634,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -122,7 +122,7 @@ index 70b37f93c4e8ff4ad9a2452681e468d23914c1b5..dbf2d9c83dafbe0e0b4e6caf3835edc8
|
||||
const newTab = gBrowser.adoptTab(selectedTab, {
|
||||
elementIndex: indexForSelectedTab,
|
||||
selectTab: selectedTab == draggedTab,
|
||||
@@ -639,10 +653,6 @@
|
||||
@@ -654,10 +668,6 @@
|
||||
this._tabbrowserTabs.dragAndDropElements[
|
||||
gBrowser.pinnedTabCount + unpinnedSplitViews.length - 1
|
||||
];
|
||||
@@ -133,7 +133,7 @@ index 70b37f93c4e8ff4ad9a2452681e468d23914c1b5..dbf2d9c83dafbe0e0b4e6caf3835edc8
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -703,6 +713,7 @@
|
||||
@@ -717,6 +727,7 @@
|
||||
|
||||
let nextItem = this._tabbrowserTabs.dragAndDropElements[newIndex];
|
||||
let tabGroup = isTab(nextItem) && nextItem.group;
|
||||
@@ -141,7 +141,7 @@ index 70b37f93c4e8ff4ad9a2452681e468d23914c1b5..dbf2d9c83dafbe0e0b4e6caf3835edc8
|
||||
gBrowser.loadTabs(urls, {
|
||||
inBackground,
|
||||
replace,
|
||||
@@ -744,6 +755,22 @@
|
||||
@@ -758,6 +769,22 @@
|
||||
this._expandGroupOnDrop(draggedTab);
|
||||
}
|
||||
this._resetTabsAfterDrop(draggedTab);
|
||||
@@ -164,7 +164,7 @@ index 70b37f93c4e8ff4ad9a2452681e468d23914c1b5..dbf2d9c83dafbe0e0b4e6caf3835edc8
|
||||
|
||||
if (
|
||||
dt.mozUserCancelled ||
|
||||
@@ -1074,11 +1101,10 @@
|
||||
@@ -1088,11 +1115,10 @@
|
||||
return this.#getHorizontalScrollboxDragTarget(event, ignoreSides);
|
||||
}
|
||||
while (target) {
|
||||
@@ -180,7 +180,7 @@ index 70b37f93c4e8ff4ad9a2452681e468d23914c1b5..dbf2d9c83dafbe0e0b4e6caf3835edc8
|
||||
break;
|
||||
}
|
||||
target = target.parentNode;
|
||||
@@ -1108,6 +1134,9 @@
|
||||
@@ -1122,6 +1148,9 @@
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -190,36 +190,43 @@ index 70b37f93c4e8ff4ad9a2452681e468d23914c1b5..dbf2d9c83dafbe0e0b4e6caf3835edc8
|
||||
return target;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -1362,7 +1397,6 @@
|
||||
@@ -1188,17 +1217,24 @@
|
||||
let periphery = draggedTab.ownerDocument.getElementById(
|
||||
"tabbrowser-arrowscrollbox-periphery"
|
||||
);
|
||||
- // 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",
|
||||
- () => {
|
||||
- group.collapsedByDrag = false;
|
||||
- this.#releaseSpaceInScrolledContent(periphery);
|
||||
- },
|
||||
- { 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",
|
||||
+ () => {
|
||||
+ group.collapsedByDrag = false;
|
||||
+ this.#releaseSpaceInScrolledContent(periphery);
|
||||
+ },
|
||||
+ { once: true }
|
||||
+ );
|
||||
+ } else {
|
||||
+ group.collapsedByDrag = false;
|
||||
+ this.#releaseSpaceInScrolledContent(periphery);
|
||||
+ }
|
||||
+ group.collapsed = !expand;
|
||||
+ gZenFolders.animateGroupMove(group, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1380,7 +1416,6 @@
|
||||
// using updateDragImage. On Linux, we can use a panel.
|
||||
if (platform == "win" || platform == "macosx") {
|
||||
captureListener = function () {
|
||||
@@ -227,7 +234,7 @@ index 70b37f93c4e8ff4ad9a2452681e468d23914c1b5..dbf2d9c83dafbe0e0b4e6caf3835edc8
|
||||
};
|
||||
} else {
|
||||
// Create a panel to use it in setDragImage
|
||||
@@ -1400,7 +1434,6 @@
|
||||
@@ -1418,7 +1453,6 @@
|
||||
);
|
||||
dragImageOffset = dragImageOffset * scale;
|
||||
}
|
||||
@@ -235,7 +242,7 @@ index 70b37f93c4e8ff4ad9a2452681e468d23914c1b5..dbf2d9c83dafbe0e0b4e6caf3835edc8
|
||||
|
||||
// _dragData.offsetX/Y give the coordinates that the mouse should be
|
||||
// positioned relative to the corner of the new window created upon
|
||||
@@ -1419,7 +1452,7 @@
|
||||
@@ -1437,7 +1471,7 @@
|
||||
let dropEffect = this.getDropEffectForTabDrag(event);
|
||||
let isMovingInTabStrip = !fromTabList && dropEffect == "move";
|
||||
let collapseTabGroupDuringDrag =
|
||||
@@ -244,7 +251,7 @@ index 70b37f93c4e8ff4ad9a2452681e468d23914c1b5..dbf2d9c83dafbe0e0b4e6caf3835edc8
|
||||
|
||||
tab._dragData = {
|
||||
offsetX: this._tabbrowserTabs.verticalMode
|
||||
@@ -1429,7 +1462,7 @@
|
||||
@@ -1447,7 +1481,7 @@
|
||||
? event.screenY - window.screenY - tabOffset
|
||||
: event.screenY - window.screenY,
|
||||
scrollPos:
|
||||
@@ -253,7 +260,7 @@ index 70b37f93c4e8ff4ad9a2452681e468d23914c1b5..dbf2d9c83dafbe0e0b4e6caf3835edc8
|
||||
? this._tabbrowserTabs.pinnedTabsContainer.scrollPosition
|
||||
: this._tabbrowserTabs.arrowScrollbox.scrollPosition,
|
||||
screenX: event.screenX,
|
||||
@@ -1522,6 +1555,7 @@
|
||||
@@ -1540,6 +1574,7 @@
|
||||
if (tabStripItemElement.hasAttribute("dragtarget")) {
|
||||
return;
|
||||
}
|
||||
@@ -261,7 +268,7 @@ index 70b37f93c4e8ff4ad9a2452681e468d23914c1b5..dbf2d9c83dafbe0e0b4e6caf3835edc8
|
||||
let isPinned = tab.pinned;
|
||||
let dragAndDropElements = this._tabbrowserTabs.dragAndDropElements;
|
||||
let isGrid = this._tabbrowserTabs.isContainerVerticalPinnedGrid(tab);
|
||||
@@ -1895,23 +1929,6 @@
|
||||
@@ -1913,23 +1948,6 @@
|
||||
|
||||
// Slide the relevant tabs to their new position.
|
||||
// non-moving tabs adjust for RTL
|
||||
@@ -285,7 +292,7 @@ index 70b37f93c4e8ff4ad9a2452681e468d23914c1b5..dbf2d9c83dafbe0e0b4e6caf3835edc8
|
||||
// moving tabs don't adjust for RTL
|
||||
for (let item of selectedElements) {
|
||||
if (
|
||||
@@ -1960,7 +1977,6 @@
|
||||
@@ -1978,7 +1996,6 @@
|
||||
for (let item of this._tabbrowserTabs.dragAndDropElements) {
|
||||
delete item._moveTogetherSelectedTabsData;
|
||||
item = elementToMove(item);
|
||||
@@ -293,7 +300,7 @@ index 70b37f93c4e8ff4ad9a2452681e468d23914c1b5..dbf2d9c83dafbe0e0b4e6caf3835edc8
|
||||
item.removeAttribute("multiselected-move-together");
|
||||
}
|
||||
}
|
||||
@@ -2812,7 +2828,6 @@
|
||||
@@ -2830,7 +2847,6 @@
|
||||
tab.style.top = "";
|
||||
tab.style.maxWidth = "";
|
||||
tab.style.pointerEvents = "";
|
||||
@@ -301,7 +308,7 @@ index 70b37f93c4e8ff4ad9a2452681e468d23914c1b5..dbf2d9c83dafbe0e0b4e6caf3835edc8
|
||||
tab.removeAttribute("small-stack");
|
||||
tab.removeAttribute("big-stack");
|
||||
}
|
||||
@@ -2821,11 +2836,9 @@
|
||||
@@ -2839,11 +2855,9 @@
|
||||
)) {
|
||||
label.style.width = "";
|
||||
label.style.maxWidth = "";
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,15 +1,15 @@
|
||||
diff --git a/browser/components/urlbar/UrlbarProviderHeuristicFallback.sys.mjs b/browser/components/urlbar/UrlbarProviderHeuristicFallback.sys.mjs
|
||||
index cfc232f038cd491db32e0c0ca35622360dd9dec5..4806a725ddefa9d0709020d537eae5464249a294 100644
|
||||
index e32b02941a6019703212cede4660a24b88845120..5cb363ed20d49c1ae8abedecc3b08540406b7a1b 100644
|
||||
--- a/browser/components/urlbar/UrlbarProviderHeuristicFallback.sys.mjs
|
||||
+++ b/browser/components/urlbar/UrlbarProviderHeuristicFallback.sys.mjs
|
||||
@@ -77,22 +77,26 @@ export class UrlbarProviderHeuristicFallback extends UrlbarProvider {
|
||||
@@ -76,22 +76,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.
|
||||
- let str = queryContext.searchString;
|
||||
- if (!URL.canParse(str)) {
|
||||
- if (
|
||||
- lazy.UrlbarPrefs.get("keyword.enabled") &&
|
||||
- queryContext.keywordEnabled &&
|
||||
- (lazy.UrlUtils.looksLikeOrigin(str, {
|
||||
- noIp: true,
|
||||
- noPort: true,
|
||||
@@ -24,7 +24,7 @@ index cfc232f038cd491db32e0c0ca35622360dd9dec5..4806a725ddefa9d0709020d537eae546
|
||||
+ }
|
||||
+ );
|
||||
+ if (
|
||||
+ lazy.UrlbarPrefs.get("keyword.enabled") &&
|
||||
+ queryContext.keywordEnabled &&
|
||||
+ (lazy.UrlUtils.looksLikeUrl(searchCandidate) ||
|
||||
+ lazy.UrlUtils.looksLikeOrigin(searchCandidate, {
|
||||
+ allowPartialNumericalTLDs: true,
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
diff --git a/browser/components/urlbar/UrlbarUtils.sys.mjs b/browser/components/urlbar/UrlbarUtils.sys.mjs
|
||||
index 178cd5e84fda74a28b2de59b671b3399cc28fac4..b03c6e06de4ee9a95dcb4d33d7ac9f0b053be4e1 100644
|
||||
--- a/browser/components/urlbar/UrlbarUtils.sys.mjs
|
||||
+++ b/browser/components/urlbar/UrlbarUtils.sys.mjs
|
||||
@@ -214,6 +214,11 @@ export var UrlbarUtils = {
|
||||
return UrlbarShared.RESULT_GROUP.HEURISTIC_FALLBACK;
|
||||
case "UrlbarProviderHistoryUrlHeuristic":
|
||||
return UrlbarShared.RESULT_GROUP.HEURISTIC_HISTORY_URL;
|
||||
+ case "ZenUrlbarProviderGlobalActions":
|
||||
+ if (result.source == UrlbarShared.RESULT_SOURCE.WORKSPACES) {
|
||||
+ return UrlbarShared.RESULT_GROUP.ZEN_WORKSPACE;
|
||||
+ }
|
||||
+ return UrlbarShared.RESULT_GROUP.ZEN_ACTION;
|
||||
case "UrlbarProviderOmnibox":
|
||||
return UrlbarShared.RESULT_GROUP.HEURISTIC_OMNIBOX;
|
||||
case "UrlbarProviderRestrictKeywordsAutofill":
|
||||
@@ -1,16 +1,16 @@
|
||||
diff --git a/browser/components/urlbar/content/UrlbarChildController.mjs b/browser/components/urlbar/content/UrlbarChildController.mjs
|
||||
index a17a4de079b69d2818388b33a2c1746cc6148c73..4fee63a22919972af08716db2d2de1c7549ff100 100644
|
||||
index 52ed36deac35d3de9f7f537313159fb4debe98a6..3bbc612a2e10eedafdabdaa8e17cf36831a6a27c 100644
|
||||
--- a/browser/components/urlbar/content/UrlbarChildController.mjs
|
||||
+++ b/browser/components/urlbar/content/UrlbarChildController.mjs
|
||||
@@ -438,7 +438,6 @@ export class UrlbarChildController {
|
||||
const isMac = AppConstants.platform == "macosx";
|
||||
@@ -446,7 +446,6 @@ export class UrlbarChildController {
|
||||
const isMac = UrlbarContentUtils.getPlatform() == "macosx";
|
||||
// Handle readline/emacs-style navigation bindings on Mac.
|
||||
if (
|
||||
- isMac &&
|
||||
this.view.isOpen &&
|
||||
event.ctrlKey &&
|
||||
(event.key == "n" || event.key == "p")
|
||||
@@ -630,6 +629,8 @@ export class UrlbarChildController {
|
||||
@@ -644,6 +643,8 @@ export class UrlbarChildController {
|
||||
});
|
||||
}
|
||||
event.preventDefault();
|
||||
@@ -19,7 +19,7 @@ index a17a4de079b69d2818388b33a2c1746cc6148c73..4fee63a22919972af08716db2d2de1c7
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -934,6 +935,10 @@ export class UrlbarChildController {
|
||||
@@ -905,6 +906,10 @@ export class UrlbarChildController {
|
||||
}
|
||||
reuseEmpty = true;
|
||||
}
|
||||
@@ -30,7 +30,7 @@ index a17a4de079b69d2818388b33a2c1746cc6148c73..4fee63a22919972af08716db2d2de1c7
|
||||
// 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 {
|
||||
@@ -914,6 +919,10 @@ export class UrlbarChildController {
|
||||
) {
|
||||
where = "current";
|
||||
}
|
||||
|
||||
@@ -1,22 +1,21 @@
|
||||
diff --git a/browser/components/urlbar/content/UrlbarInput.mjs b/browser/components/urlbar/content/UrlbarInput.mjs
|
||||
index 319fb6870ec18d87b9f1263bd960edaa39b0447c..1746a052c304e7988447fdb869ad2e36d7d74e8e 100644
|
||||
--- a/browser/components/urlbar/content/UrlbarInput.mjs
|
||||
+++ b/browser/components/urlbar/content/UrlbarInput.mjs
|
||||
@@ -88,6 +88,13 @@ const lazy = XPCOMUtils.declareLazy({
|
||||
logger: () => UrlbarShared.getLogger({ prefix: "Input" }),
|
||||
});
|
||||
diff --git a/browser/components/urlbar/content/UrlbarInputBase.mjs b/browser/components/urlbar/content/UrlbarInputBase.mjs
|
||||
index 8be35b58c5369e123ca69426e852e3849bf75513..778d8aecce4da6748ecbf8b86a38c2da6e01bec5 100644
|
||||
--- a/browser/components/urlbar/content/UrlbarInputBase.mjs
|
||||
+++ b/browser/components/urlbar/content/UrlbarInputBase.mjs
|
||||
@@ -83,6 +83,12 @@ if (lazy) {
|
||||
Ci.nsIURLQueryStringStripper,
|
||||
],
|
||||
});
|
||||
+ XPCOMUtils.defineLazyPreferenceGetter(
|
||||
+ lazy,
|
||||
+ "ZEN_URLBAR_BEHAVIOR",
|
||||
+ "zen.urlbar.behavior",
|
||||
+ 'default'
|
||||
+ );
|
||||
}
|
||||
|
||||
+XPCOMUtils.defineLazyPreferenceGetter(
|
||||
+ lazy,
|
||||
+ "ZEN_URLBAR_BEHAVIOR",
|
||||
+ "zen.urlbar.behavior",
|
||||
+ 'default'
|
||||
+);
|
||||
+
|
||||
const UNLIMITED_MAX_RESULTS = 99;
|
||||
|
||||
let getBoundsWithoutFlushing = element =>
|
||||
@@ -771,7 +778,16 @@ ${
|
||||
const logger = () => UrlbarShared.getLogger({ prefix: "Input" });
|
||||
@@ -917,7 +923,16 @@ ${
|
||||
// See _on_select(). HTMLInputElement.select() dispatches a "select"
|
||||
// event but does not set the primary selection.
|
||||
this._suppressPrimaryAdjustment = true;
|
||||
@@ -33,7 +32,7 @@ index 319fb6870ec18d87b9f1263bd960edaa39b0447c..1746a052c304e7988447fdb869ad2e36
|
||||
this._suppressPrimaryAdjustment = false;
|
||||
}
|
||||
|
||||
@@ -846,6 +862,10 @@ ${
|
||||
@@ -992,6 +1007,10 @@ ${
|
||||
hideSearchTerms = false,
|
||||
isSameDocument = false,
|
||||
} = {}) {
|
||||
@@ -44,7 +43,7 @@ index 319fb6870ec18d87b9f1263bd960edaa39b0447c..1746a052c304e7988447fdb869ad2e36
|
||||
if (!this.#isAddressbar) {
|
||||
throw new Error(
|
||||
"Cannot set URI for UrlbarInput that is not an address bar"
|
||||
@@ -1143,7 +1163,16 @@ ${
|
||||
@@ -1289,7 +1308,16 @@ ${
|
||||
this.searchModeSwitcher?.updateSearchIcon();
|
||||
}
|
||||
|
||||
@@ -61,7 +60,7 @@ index 319fb6870ec18d87b9f1263bd960edaa39b0447c..1746a052c304e7988447fdb869ad2e36
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1255,6 +1284,9 @@ ${
|
||||
@@ -1401,6 +1429,9 @@ ${
|
||||
where,
|
||||
query: searchString,
|
||||
});
|
||||
@@ -71,7 +70,7 @@ index 319fb6870ec18d87b9f1263bd960edaa39b0447c..1746a052c304e7988447fdb869ad2e36
|
||||
this.controller.openSERP(
|
||||
engine.id,
|
||||
searchString,
|
||||
@@ -1726,7 +1758,11 @@ ${
|
||||
@@ -1884,7 +1915,11 @@ ${
|
||||
openParams.avoidBrowserFocus = keepViewOpen;
|
||||
|
||||
if (!this.#providesSearchMode(result) && !keepViewOpen) {
|
||||
@@ -84,12 +83,12 @@ index 319fb6870ec18d87b9f1263bd960edaa39b0447c..1746a052c304e7988447fdb869ad2e36
|
||||
}
|
||||
|
||||
if (isCanonized) {
|
||||
@@ -3016,6 +3052,42 @@ ${
|
||||
@@ -3181,6 +3216,42 @@ ${
|
||||
await this.#updateLayoutBreakoutDimensions();
|
||||
}
|
||||
|
||||
+ zenFormatURLValue() {
|
||||
+ return this.#lazy.valueFormatter._formatURL();
|
||||
+ return this.#getValueFormatter().update();
|
||||
+ }
|
||||
+
|
||||
+ get zenUrlbarBehavior() {
|
||||
@@ -127,8 +126,11 @@ index 319fb6870ec18d87b9f1263bd960edaa39b0447c..1746a052c304e7988447fdb869ad2e36
|
||||
startLayoutExtend() {
|
||||
if (!this.#allowBreakout || this.hasAttribute("breakout-extend")) {
|
||||
// Do not expand if the Urlbar does not support being expanded or it is
|
||||
@@ -3030,6 +3102,13 @@ ${
|
||||
@@ -3193,8 +3264,16 @@ ${
|
||||
}
|
||||
|
||||
this.toggleAttribute("breakout-extend", true);
|
||||
+ this.#showBreakoutPopover();
|
||||
this.#updateTextboxPosition();
|
||||
|
||||
+ this.window.gZenUIManager.onUrlbarOpen();
|
||||
@@ -141,7 +143,7 @@ index 319fb6870ec18d87b9f1263bd960edaa39b0447c..1746a052c304e7988447fdb869ad2e36
|
||||
// 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")) {
|
||||
@@ -3053,6 +3132,29 @@ ${
|
||||
@@ -3218,7 +3297,33 @@ ${
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -169,9 +171,13 @@ index 319fb6870ec18d87b9f1263bd960edaa39b0447c..1746a052c304e7988447fdb869ad2e36
|
||||
+ this.removeAttribute("zen-floating-urlbar");
|
||||
+
|
||||
this.toggleAttribute("breakout-extend", false);
|
||||
+ if (this.#isAddressbar) {
|
||||
+ this.#hideBreakoutPopover();
|
||||
+ }
|
||||
this.#updateTextboxPosition();
|
||||
}
|
||||
@@ -3091,7 +3193,7 @@ ${
|
||||
|
||||
@@ -3256,7 +3361,7 @@ ${
|
||||
forceUnifiedSearchButtonAvailable = false
|
||||
) {
|
||||
let prevState = this.getAttribute("pageproxystate");
|
||||
@@ -180,20 +186,51 @@ index 319fb6870ec18d87b9f1263bd960edaa39b0447c..1746a052c304e7988447fdb869ad2e36
|
||||
this.setAttribute("pageproxystate", state);
|
||||
this._inputContainer.setAttribute("pageproxystate", state);
|
||||
this._identityBox?.setAttribute("pageproxystate", state);
|
||||
@@ -3355,10 +3457,12 @@ ${
|
||||
@@ -3533,7 +3638,11 @@ ${
|
||||
return;
|
||||
}
|
||||
|
||||
this.style.top = px(
|
||||
- this.style.top = px(getUntransformedTop(this.parentNode));
|
||||
+ this.style.top = px(
|
||||
+ this.window.gZenVerticalTabsManager._hasSetSingleToolbar ?
|
||||
this.parentNode.getBoxQuads({
|
||||
ignoreTransforms: true,
|
||||
flush: false,
|
||||
})[0].p1.y
|
||||
+ : (AppConstants.platform == "macosx" ? -2 : -5)
|
||||
);
|
||||
+ getUntransformedTop(this.parentNode)
|
||||
+ : (UrlbarContentUtils.getPlatform() == "macosx" ? -2 : -5)
|
||||
+ );
|
||||
}
|
||||
|
||||
@@ -3417,9 +3521,10 @@ ${
|
||||
#updateTextboxPositionNextFrame() {
|
||||
@@ -3553,12 +3662,29 @@ ${
|
||||
this.removeAttribute("breakout");
|
||||
this.parentNode.removeAttribute("breakout");
|
||||
this.style.top = "";
|
||||
+ this.#hideBreakoutPopover();
|
||||
+ this._layoutBreakoutUpdateKey = {};
|
||||
+ }
|
||||
+
|
||||
+ #showBreakoutPopover() {
|
||||
+ if (this.#isAddressbar) {
|
||||
+ this.setAttribute("popover", "manual");
|
||||
+ }
|
||||
+ if (!this.matches(":popover-open")) {
|
||||
+ this.showPopover();
|
||||
+ }
|
||||
+ this.#fixAddressbarSearchbarOrder();
|
||||
+ }
|
||||
+
|
||||
+ #hideBreakoutPopover() {
|
||||
try {
|
||||
this.hidePopover();
|
||||
} catch (ex) {
|
||||
// No big deal if not a popover already.
|
||||
}
|
||||
- this._layoutBreakoutUpdateKey = {};
|
||||
+ if (this.#isAddressbar) {
|
||||
+ this.removeAttribute("popover");
|
||||
+ }
|
||||
}
|
||||
|
||||
incrementBreakoutBlockerCount() {
|
||||
@@ -3591,9 +3717,10 @@ ${
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -205,7 +242,19 @@ index 319fb6870ec18d87b9f1263bd960edaa39b0447c..1746a052c304e7988447fdb869ad2e36
|
||||
);
|
||||
|
||||
if (this.#breakoutBlockerCount) {
|
||||
@@ -3929,6 +4034,7 @@ ${
|
||||
@@ -3602,8 +3729,9 @@ ${
|
||||
|
||||
this.setAttribute("breakout", "true");
|
||||
this.parentNode.setAttribute("breakout", "true");
|
||||
- this.showPopover();
|
||||
- this.#fixAddressbarSearchbarOrder();
|
||||
+ if (!this.#isAddressbar || this.hasAttribute("breakout-extend")) {
|
||||
+ this.#showBreakoutPopover();
|
||||
+ }
|
||||
this.#updateTextboxPosition();
|
||||
|
||||
resolve();
|
||||
@@ -4108,6 +4236,7 @@ ${
|
||||
}
|
||||
|
||||
_toggleActionOverride(event) {
|
||||
@@ -213,7 +262,7 @@ index 319fb6870ec18d87b9f1263bd960edaa39b0447c..1746a052c304e7988447fdb869ad2e36
|
||||
if (
|
||||
event.keyCode == KeyEvent.DOM_VK_SHIFT ||
|
||||
event.keyCode == KeyEvent.DOM_VK_ALT ||
|
||||
@@ -4027,9 +4133,10 @@ ${
|
||||
@@ -4206,9 +4335,10 @@ ${
|
||||
if (!this.#isAddressbar) {
|
||||
return val;
|
||||
}
|
||||
@@ -226,36 +275,36 @@ index 319fb6870ec18d87b9f1263bd960edaa39b0447c..1746a052c304e7988447fdb869ad2e36
|
||||
+ : val;
|
||||
// Only trim value if the directionality doesn't change to RTL and we're not
|
||||
// showing a strikeout https protocol.
|
||||
return this.controller.isTextDirectionRTL(trimmedValue) ||
|
||||
@@ -4235,6 +4342,11 @@ ${
|
||||
return UrlbarContentUtils.isTextDirectionRTL(trimmedValue, window) ||
|
||||
@@ -4410,6 +4540,11 @@ ${
|
||||
keepViewOpen = false,
|
||||
browserId = null,
|
||||
}) {
|
||||
+ where = this.window.gZenUIManager.getOpenUILinkWhere(
|
||||
+ url,
|
||||
+ loadRequest.urlLoad?.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 +4688,7 @@ ${
|
||||
this.setResultForCurrentValue(null);
|
||||
this.handleCommand();
|
||||
this.controller.clearLastQueryContextCache();
|
||||
+ this.view.close();
|
||||
let keyDownEnterDeferred;
|
||||
if (
|
||||
this._keyDownEnterDeferred &&
|
||||
@@ -4731,6 +4866,7 @@ ${
|
||||
this.setResultForCurrentValue(null);
|
||||
this.handleCommand();
|
||||
this.controller.clearLastQueryContextCache();
|
||||
+ this.view.close();
|
||||
|
||||
this._suppressStartQuery = false;
|
||||
});
|
||||
@@ -4583,7 +4696,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.
|
||||
- this.view.close();
|
||||
this._suppressStartQuery = false;
|
||||
});
|
||||
@@ -4742,7 +4878,6 @@ ${
|
||||
// Close the results pane, because paste and go doesn't want a result
|
||||
// selection. This has to happen before the menu opens: ending
|
||||
// breakout-extend once it's open keeps it from showing (bug 2037468).
|
||||
- this.view.close();
|
||||
|
||||
let controller =
|
||||
this.document.commandDispatcher.getControllerForCommand("cmd_paste");
|
||||
@@ -4850,7 +4962,11 @@ ${
|
||||
let controller =
|
||||
this.document.commandDispatcher.getControllerForCommand("cmd_paste");
|
||||
@@ -5025,7 +5160,11 @@ ${
|
||||
if (!engineName && !source && !this.hasAttribute("searchmode")) {
|
||||
return;
|
||||
}
|
||||
@@ -268,7 +317,7 @@ index 319fb6870ec18d87b9f1263bd960edaa39b0447c..1746a052c304e7988447fdb869ad2e36
|
||||
if (this._searchModeIndicatorTitle) {
|
||||
this._searchModeIndicatorTitle.textContent = "";
|
||||
this._searchModeIndicatorTitle.removeAttribute("data-l10n-id");
|
||||
@@ -5149,6 +5265,7 @@ ${
|
||||
@@ -5335,6 +5474,7 @@ ${
|
||||
|
||||
this.document.l10n.setAttributes(
|
||||
this.inputField,
|
||||
@@ -276,10 +325,10 @@ index 319fb6870ec18d87b9f1263bd960edaa39b0447c..1746a052c304e7988447fdb869ad2e36
|
||||
l10nId,
|
||||
l10nId == "urlbar-placeholder-with-name"
|
||||
? { name: engineName }
|
||||
@@ -5199,6 +5316,12 @@ ${
|
||||
@@ -5385,6 +5525,12 @@ ${
|
||||
}
|
||||
|
||||
lazy.logger.debug("Blur Event");
|
||||
logger().debug("Blur Event");
|
||||
+ if (
|
||||
+ this.document.commandDispatcher.focusedElement == this.inputField &&
|
||||
+ !UrlbarPrefs.get("closeOnWindowBlur")
|
||||
@@ -289,7 +338,7 @@ index 319fb6870ec18d87b9f1263bd960edaa39b0447c..1746a052c304e7988447fdb869ad2e36
|
||||
// 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
|
||||
@@ -5276,6 +5399,11 @@ ${
|
||||
@@ -5464,6 +5610,11 @@ ${
|
||||
}
|
||||
|
||||
_on_click(event) {
|
||||
@@ -301,7 +350,7 @@ index 319fb6870ec18d87b9f1263bd960edaa39b0447c..1746a052c304e7988447fdb869ad2e36
|
||||
switch (event.target) {
|
||||
case this.inputField:
|
||||
case this._inputContainer:
|
||||
@@ -5371,10 +5499,11 @@ ${
|
||||
@@ -5558,10 +5709,11 @@ ${
|
||||
}
|
||||
if (untrim) {
|
||||
this.setValue(this._untrimmedValue);
|
||||
@@ -314,7 +363,7 @@ index 319fb6870ec18d87b9f1263bd960edaa39b0447c..1746a052c304e7988447fdb869ad2e36
|
||||
this.view.autoOpen({ event });
|
||||
} else {
|
||||
if (this._untrimOnFocusAfterKeydown) {
|
||||
@@ -5414,9 +5543,16 @@ ${
|
||||
@@ -5605,9 +5757,16 @@ ${
|
||||
}
|
||||
|
||||
_on_mousedown(event) {
|
||||
@@ -332,7 +381,7 @@ index 319fb6870ec18d87b9f1263bd960edaa39b0447c..1746a052c304e7988447fdb869ad2e36
|
||||
if (
|
||||
event.composedTarget != this.inputField &&
|
||||
event.composedTarget != this._inputContainer
|
||||
@@ -5426,6 +5562,10 @@ ${
|
||||
@@ -5617,6 +5776,10 @@ ${
|
||||
|
||||
this.focusedViaMousedown = !this.focused;
|
||||
this.#preventClickSelectsAll = this.focused;
|
||||
@@ -343,16 +392,16 @@ index 319fb6870ec18d87b9f1263bd960edaa39b0447c..1746a052c304e7988447fdb869ad2e36
|
||||
|
||||
// Keep the focus status, since the attribute may be changed
|
||||
// upon calling this.focus().
|
||||
@@ -5463,7 +5603,7 @@ ${
|
||||
@@ -5654,7 +5817,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.
|
||||
- if (event.target.closest?.("tab, #urlbarView-context-menu")) {
|
||||
+ if (event.target.closest?.("tab, #urlbarView-context-menu") || event.target.closest?.("#tabs-newtab-button")) {
|
||||
+ if (event.target.closest?.("tab, #urlbarView-context-menu") || event.target.closest?.("menupopup") || event.target.closest?.("#tabs-newtab-button")) {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -5754,7 +5894,7 @@ ${
|
||||
@@ -5948,7 +6111,7 @@ ${
|
||||
// When we are in actions search mode we can show more results so
|
||||
// increase the limit.
|
||||
let maxResults =
|
||||
@@ -1,8 +1,8 @@
|
||||
diff --git a/browser/components/urlbar/content/UrlbarShared.mjs b/browser/components/urlbar/content/UrlbarShared.mjs
|
||||
index 4e67dad66ae006842abc6eed547e4a244320d57a..393b61b4d2543c73e60afd334a353f36119ae73e 100644
|
||||
index 0ac5330223c84fbb98880a5c2d83ae58a8f3079e..ff488cf5a2c8b7857a8e145e40aaf861cfa655ec 100644
|
||||
--- a/browser/components/urlbar/content/UrlbarShared.mjs
|
||||
+++ b/browser/components/urlbar/content/UrlbarShared.mjs
|
||||
@@ -100,6 +100,7 @@ export const UrlbarShared = {
|
||||
@@ -158,6 +158,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 4e67dad66ae006842abc6eed547e4a244320d57a..393b61b4d2543c73e60afd334a353f36
|
||||
}),
|
||||
|
||||
/**
|
||||
@@ -120,6 +121,7 @@ export const UrlbarShared = {
|
||||
@@ -178,6 +179,7 @@ export const UrlbarShared = {
|
||||
TITLE: "#",
|
||||
URL: "$",
|
||||
ACTION: ">",
|
||||
@@ -18,7 +18,7 @@ index 4e67dad66ae006842abc6eed547e4a244320d57a..393b61b4d2543c73e60afd334a353f36
|
||||
}),
|
||||
|
||||
// Defines UrlbarResult types.
|
||||
@@ -165,6 +167,8 @@ export const UrlbarShared = {
|
||||
@@ -223,6 +225,8 @@ export const UrlbarShared = {
|
||||
OTHER_NETWORK: 6,
|
||||
ADDON: 7,
|
||||
ACTIONS: 8,
|
||||
@@ -27,7 +27,7 @@ index 4e67dad66ae006842abc6eed547e4a244320d57a..393b61b4d2543c73e60afd334a353f36
|
||||
}),
|
||||
|
||||
// Results are categorized into groups to help the muxer compose them. See
|
||||
@@ -200,6 +204,8 @@ export const UrlbarShared = {
|
||||
@@ -258,6 +262,8 @@ export const UrlbarShared = {
|
||||
SEMANTIC_HISTORY: "semanticHistory",
|
||||
SUGGESTED_INDEX: "suggestedIndex",
|
||||
TAIL_SUGGESTION: "tailSuggestion",
|
||||
@@ -36,7 +36,7 @@ index 4e67dad66ae006842abc6eed547e4a244320d57a..393b61b4d2543c73e60afd334a353f36
|
||||
}),
|
||||
|
||||
// Defines provider types.
|
||||
@@ -385,6 +391,14 @@ export const UrlbarShared = {
|
||||
@@ -443,6 +449,14 @@ export const UrlbarShared = {
|
||||
telemetryLabel: "actions",
|
||||
uiLabel: "urlbar-searchmode-actions3",
|
||||
},
|
||||
@@ -51,7 +51,7 @@ index 4e67dad66ae006842abc6eed547e4a244320d57a..393b61b4d2543c73e60afd334a353f36
|
||||
]);
|
||||
},
|
||||
|
||||
@@ -402,6 +416,7 @@ export const UrlbarShared = {
|
||||
@@ -460,6 +474,7 @@ export const UrlbarShared = {
|
||||
if (UrlbarPrefs.get("scotchBonnet.enableOverride")) {
|
||||
keys.push(this.RESTRICT_TOKENS.ACTION);
|
||||
}
|
||||
@@ -59,3 +59,23 @@ index 4e67dad66ae006842abc6eed547e4a244320d57a..393b61b4d2543c73e60afd334a353f36
|
||||
return new Set(keys);
|
||||
},
|
||||
|
||||
@@ -1032,6 +1047,11 @@ export const UrlbarShared = {
|
||||
return UrlbarShared.RESULT_GROUP.HEURISTIC_FALLBACK;
|
||||
case "UrlbarProviderHistoryUrlHeuristic":
|
||||
return UrlbarShared.RESULT_GROUP.HEURISTIC_HISTORY_URL;
|
||||
+ case "ZenUrlbarProviderGlobalActions":
|
||||
+ if (result.source == UrlbarShared.RESULT_SOURCE.WORKSPACES) {
|
||||
+ return UrlbarShared.RESULT_GROUP.ZEN_WORKSPACE;
|
||||
+ }
|
||||
+ return UrlbarShared.RESULT_GROUP.ZEN_ACTION;
|
||||
case "UrlbarProviderOmnibox":
|
||||
return UrlbarShared.RESULT_GROUP.HEURISTIC_OMNIBOX;
|
||||
case "UrlbarProviderRestrictKeywordsAutofill":
|
||||
@@ -1060,6 +1080,7 @@ export const UrlbarShared = {
|
||||
case "UrlbarProviderAboutPages":
|
||||
return UrlbarShared.RESULT_GROUP.ABOUT_PAGES;
|
||||
case "UrlbarProviderInputHistory":
|
||||
+ case "ZenUrlbarProviderSidebar":
|
||||
return UrlbarShared.RESULT_GROUP.INPUT_HISTORY;
|
||||
case "UrlbarProviderQuickSuggest":
|
||||
return UrlbarShared.RESULT_GROUP.GENERAL_PARENT;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
diff --git a/browser/modules/ExtensionsUI.sys.mjs b/browser/modules/ExtensionsUI.sys.mjs
|
||||
index 95a4b6e40bd568c3ea5003b648b0acbf96bbb725..6ab8a658721aa6470d482dc958e4b3ac78f49f44 100644
|
||||
index 73ef024602cb4554a024de4ddef83f492adc33ef..959c3403a48a3e4294ad5d2f72bad0e5d4fbfd75 100644
|
||||
--- a/browser/modules/ExtensionsUI.sys.mjs
|
||||
+++ b/browser/modules/ExtensionsUI.sys.mjs
|
||||
@@ -502,7 +502,7 @@ export var ExtensionsUI = {
|
||||
eventCallback,
|
||||
removeOnDismissal: true,
|
||||
@@ -507,7 +507,7 @@ export var ExtensionsUI = {
|
||||
// right after the panel opens can't accidentally grant permissions).
|
||||
autofocus: true,
|
||||
popupOptions: {
|
||||
- position: "bottomright topright",
|
||||
+ position: window.gZenUIManager.panelUIPosition(),
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
diff --git a/browser/themes/shared/tabbrowser/content-area.css b/browser/themes/shared/tabbrowser/content-area.css
|
||||
index 2374ed38bd9361d6c7e1886117099c6761e403b8..72f1f52a13096f19ca4009037ad0cdc880775ff7 100644
|
||||
index 86400b643c8b2af4207d226fbd6d91e5e74548b9..3044e4ab87b7ebcfaa2ac624680da1cc5653c861 100644
|
||||
--- a/browser/themes/shared/tabbrowser/content-area.css
|
||||
+++ b/browser/themes/shared/tabbrowser/content-area.css
|
||||
@@ -239,7 +239,6 @@
|
||||
@@ -166,7 +166,6 @@
|
||||
min-width: 0;
|
||||
|
||||
/* We want to be able to show the frame color behind the clipped radiused corner */
|
||||
@@ -10,7 +10,7 @@ index 2374ed38bd9361d6c7e1886117099c6761e403b8..72f1f52a13096f19ca4009037ad0cdc8
|
||||
|
||||
display: grid;
|
||||
/**
|
||||
@@ -425,7 +424,6 @@
|
||||
@@ -355,7 +354,6 @@
|
||||
}
|
||||
|
||||
browser:is([blank], [pendingpaint]) {
|
||||
@@ -18,21 +18,21 @@ index 2374ed38bd9361d6c7e1886117099c6761e403b8..72f1f52a13096f19ca4009037ad0cdc8
|
||||
}
|
||||
|
||||
/* Exclude browsers with smartwindow-content attribute which inherit
|
||||
@@ -865,7 +863,7 @@ split-view-footer {
|
||||
@@ -735,7 +733,7 @@ split-view-footer {
|
||||
|
||||
.dialogStack {
|
||||
z-index: var(--browser-stack-z-index-dialog-stack);
|
||||
- position: absolute;
|
||||
+ position: fixed;
|
||||
inset: 0;
|
||||
|
||||
/* --browser-with-dialog set on browser[tabDialogShowing], we want to position the overlay
|
||||
only on the top of the <browser> element so it doesn't overlap the DevTools toolbox */
|
||||
@@ -1034,7 +1032,7 @@ split-view-footer {
|
||||
@@ -909,7 +907,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);
|
||||
}
|
||||
|
||||
.browserSidebarContainer.responsive-mode & {
|
||||
.dialogOverlay[hideContent="true"][topmost="true"] {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/browser/themes/shared/toolbarbuttons.css b/browser/themes/shared/toolbarbuttons.css
|
||||
index 94b1a2a841d8fd47a491616d52e5bf544f0c0bf9..9e98266b98c6b66fed26fec12fa3391fc5fcc79b 100644
|
||||
index bbf906c7bf1d6f0d71744953aeeb750c5181639a..dd116e22c38dc376c9c6b34dfa569bf4661a8971 100644
|
||||
--- a/browser/themes/shared/toolbarbuttons.css
|
||||
+++ b/browser/themes/shared/toolbarbuttons.css
|
||||
@@ -13,20 +13,6 @@
|
||||
@@ -23,7 +23,7 @@ index 94b1a2a841d8fd47a491616d52e5bf544f0c0bf9..9e98266b98c6b66fed26fec12fa3391f
|
||||
&:where([uidensity="compact"]) {
|
||||
--toolbarbutton-padding-inner: var(--toolbarbutton-padding-inner-compact);
|
||||
--bookmark-block-padding: 1px;
|
||||
@@ -123,9 +109,7 @@
|
||||
@@ -119,9 +105,7 @@
|
||||
#TabsToolbar {
|
||||
/* Override the inner padding to ensure the dimensions match the tabs, but also making sure
|
||||
different types of buttons (combined-buttons-dropmarker or text) still look correct. */
|
||||
@@ -33,18 +33,16 @@ index 94b1a2a841d8fd47a491616d52e5bf544f0c0bf9..9e98266b98c6b66fed26fec12fa3391f
|
||||
}
|
||||
|
||||
/* Primary toolbar buttons */
|
||||
@@ -205,8 +189,8 @@ toolbar .toolbarbutton-1 {
|
||||
@@ -174,7 +158,7 @@
|
||||
|
||||
> .toolbarbutton-icon {
|
||||
/* horizontal padding + actual icon width */
|
||||
- width: calc(2 * var(--toolbarbutton-padding-inner) + 16px);
|
||||
- height: calc(2 * var(--toolbarbutton-padding-inner) + 16px);
|
||||
+ width: calc(2 * var(--toolbarbutton-padding-inner) + var(--zen-toolbar-button-size, 16px));
|
||||
+ height: calc(2 * var(--toolbarbutton-padding-inner) + var(--zen-toolbar-button-size, 16px));
|
||||
}
|
||||
toolbar .toolbarbutton-1 {
|
||||
/* horizontal padding + actual icon width */
|
||||
- --toolbarbutton-icon-size: calc(2 * var(--toolbarbutton-padding-inner) + 16px);
|
||||
+ --toolbarbutton-icon-size: calc(2 * var(--toolbarbutton-padding-inner) + var(--zen-toolbar-button-size, 16px));
|
||||
|
||||
> .toolbarbutton-text {
|
||||
@@ -216,7 +200,7 @@ toolbar .toolbarbutton-1 {
|
||||
appearance: none;
|
||||
margin: 0;
|
||||
@@ -214,7 +198,7 @@ toolbar .toolbarbutton-1 {
|
||||
* height as the button icons and the same vertical padding, but as a minimum,
|
||||
* because otherwise an increase in text sizes would break things.
|
||||
*/
|
||||
@@ -62,7 +60,7 @@ index 94b1a2a841d8fd47a491616d52e5bf544f0c0bf9..9e98266b98c6b66fed26fec12fa3391f
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -536,7 +520,7 @@ toolbarbutton.bookmark-item:not(.subviewbutton) {
|
||||
@@ -427,7 +411,7 @@ toolbarbutton.bookmark-item:not(.subviewbutton) {
|
||||
*/
|
||||
align-items: stretch;
|
||||
> .toolbarbutton-icon {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
diff --git a/browser/themes/shared/urlbar-searchbar.css b/browser/themes/shared/urlbar-searchbar.css
|
||||
index 149f123e26cd79e89432dfeb504ca7218ee04810..7e0e6d981d6c2aa53bc2a76d82d2078419659065 100644
|
||||
--- a/browser/themes/shared/urlbar-searchbar.css
|
||||
+++ b/browser/themes/shared/urlbar-searchbar.css
|
||||
@@ -19,8 +19,8 @@
|
||||
diff --git a/browser/themes/shared/urlbar.css b/browser/themes/shared/urlbar.css
|
||||
index 06183c82e2869290a5fb476c47fb7db92bbef57f..c64cde1eef50d3457fb1c540d95fe3b8aa087294 100644
|
||||
--- a/browser/themes/shared/urlbar.css
|
||||
+++ b/browser/themes/shared/urlbar.css
|
||||
@@ -24,8 +24,8 @@
|
||||
|
||||
@media not -moz-pref("browser.nova.enabled") {
|
||||
--urlbar-basic-component-size: calc(var(--urlbar-height) - 2 * (var(--urlbar-input-container-border-width) + var(--urlbar-input-container-padding)));
|
||||
@@ -13,7 +13,7 @@ index 149f123e26cd79e89432dfeb504ca7218ee04810..7e0e6d981d6c2aa53bc2a76d82d20784
|
||||
}
|
||||
@media (max-width: 770px) {
|
||||
--urlbar-container-min-width: 240px;
|
||||
@@ -127,7 +127,7 @@ toolbar[inactive="true"] .urlbar,
|
||||
@@ -143,7 +143,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 149f123e26cd79e89432dfeb504ca7218ee04810..7e0e6d981d6c2aa53bc2a76d82d20784
|
||||
}
|
||||
|
||||
/* Document Picture-in-Picture API window */
|
||||
@@ -405,6 +405,10 @@ toolbar[inactive="true"] .urlbar,
|
||||
@@ -434,6 +434,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,7 +33,7 @@ index 149f123e26cd79e89432dfeb504ca7218ee04810..7e0e6d981d6c2aa53bc2a76d82d20784
|
||||
}
|
||||
|
||||
#urlbar-scheme {
|
||||
@@ -476,10 +480,15 @@ toolbar[inactive="true"] .urlbar,
|
||||
@@ -505,10 +509,15 @@ toolbar[inactive="true"] .urlbar,
|
||||
}
|
||||
|
||||
.urlbar[breakout][breakout-extend] {
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,37 @@
|
||||
#filter dumbComments emptyLines substitution
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="context-fill" fill-opacity="context-fill-opacity" class="zen-library-icon" viewBox="4 4 24 24"><style>@property --zen-triangle-anim { syntax: '<number>'; initial-value: 1; inherits: false; }
|
||||
@property --zen-circle-anim { syntax: '<number>'; initial-value: 1; inherits: false; }
|
||||
@keyframes StarBackward {
|
||||
0% { transform: rotate(120deg); }
|
||||
100% { transform: rotate(0deg); }
|
||||
}
|
||||
@keyframes TriangleBackward {
|
||||
0% { --zen-triangle-anim: 1; }
|
||||
100% { --zen-triangle-anim: 0; }
|
||||
}
|
||||
@keyframes CircleBackward {
|
||||
0% { --zen-circle-anim: 1; }
|
||||
100% { --zen-circle-anim: 0; }
|
||||
}
|
||||
@keyframes GlowBackward {
|
||||
0% { opacity: 0.4; }
|
||||
100% { opacity: 0; }
|
||||
}
|
||||
@keyframes GlowElementsBackward {
|
||||
0% { opacity: 0.5; }
|
||||
100% { opacity: 0; }
|
||||
}
|
||||
.zen-library-star { transform-origin: 10px 10.2px; animation: StarBackward 0.4s cubic-bezier(0.4, 0, 0.2, 1) forwards; }
|
||||
.zen-library-triangle-group { transform-origin: 17px 9.2px; animation: TriangleBackward 0.4s cubic-bezier(0.4, 0, 0.2, 1) forwards; transform: translate(calc(var(--zen-triangle-anim) * 2px), 0px) rotate(calc(var(--zen-triangle-anim) * 365deg)) scale(max(1 - var(--zen-triangle-anim) / 0.65, (var(--zen-triangle-anim) - 0.65) / 0.35)); }
|
||||
.zen-library-circle { transform-origin: 0px 0px; animation: CircleBackward 0.4s cubic-bezier(0.4, 0, 0.2, 1) forwards; transform: translate(calc(var(--zen-circle-anim) * 1px), calc(var(--zen-circle-anim) * -1.5px)) scale(max(0, max((var(--zen-circle-anim) - 0.75) / 0.25, min(1, 1 - (var(--zen-circle-anim) - 0.3) / 0.35)))); }
|
||||
.zen-library-glow { filter: blur(2px); animation: GlowBackward 0.4s cubic-bezier(0.4, 0, 0.2, 1) forwards; }
|
||||
.zen-library-glow-element { filter: blur(2px); animation: GlowElementsBackward 0.4s cubic-bezier(0.4, 0, 0.2, 1) forwards; }
|
||||
.zen-library-glow-bg, .zen-library-glow-element { fill: transparent !important; stroke: none !important; }
|
||||
.zen-library-glow-rays { stroke: transparent !important; fill: none !important; }
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.zen-library-glow-bg, .zen-library-glow-element { fill: white !important; }
|
||||
.zen-library-glow-rays { stroke: white !important; }
|
||||
}</style><defs><mask id="zen-library-box-mask"><rect width="40" height="40" x="-10" y="-10" fill="#fff"/><rect width="40" height="40" x="-10" y="11.635" fill="#000"/></mask></defs><g fill-rule="evenodd"><g mask="url(#zen-library-box-mask)"><path d="M7.773 11.635c.363.228.467.44.445.659l-.182 1.788a.579.579 0 0 0 .946.503l1.38-1.153a.58.58 0 0 1 .604-.085l1.644.725a.578.578 0 0 0 .77-.745l-.679-1.692a.58.58 0 0 1 .115-.577l1.198-1.34a.578.578 0 0 0-.47-.962l-1.794.121a.58.58 0 0 1-.539-.286l-.904-1.553a.578.578 0 0 0-1.06.15l-.439 1.743a.58.58 0 0 1-.438.424l-1.757.38a.578.578 0 0 0-.186 1.055z" class="zen-library-star zen-library-glow-element"/><path d="M7.773 11.635c.363.228.467.44.445.659l-.182 1.788a.579.579 0 0 0 .946.503l1.38-1.153a.58.58 0 0 1 .604-.085l1.644.725a.578.578 0 0 0 .77-.745l-.679-1.692a.58.58 0 0 1 .115-.577l1.198-1.34a.578.578 0 0 0-.47-.962l-1.794.121a.58.58 0 0 1-.539-.286l-.904-1.553a.578.578 0 0 0-1.06.15l-.439 1.743a.58.58 0 0 1-.438.424l-1.757.38a.578.578 0 0 0-.186 1.055z" class="zen-library-star"/></g><g transform="translate(14, 6.2)"><circle r="1.4" class="zen-library-circle zen-library-glow-element"/><circle r="1" class="zen-library-circle"/></g><g class="zen-library-triangle-group"><g transform="translate(17, 9.2)"><path d="M-1.1 1.8c-.3.2-.6 0-.6-.3v-3c0-.3.3-.5.6-.3L1.4-.4c.3.2.3.6 0 .8Z" class="zen-library-triangle zen-library-glow-element"/><path d="M-1.1 1.8c-.3.2-.6 0-.6-.3v-3c0-.3.3-.5.6-.3L1.4-.4c.3.2.3.6 0 .8Z" class="zen-library-triangle"/></g></g><g class="zen-library-glow"><path d="m6.6 6.6 1.44 6.1h9.92l1.44-6.1Z" class="zen-library-glow-bg"/><path d="m6.1 6.6 1.94 6.1h9.92l1.94-6.1" class="zen-library-glow-rays"/></g><path d="M7.218 13.287c0-.456.37-.826.826-.826h9.913c.457 0 .826.37.826.826v4.131a3.304 3.304 0 0 1-3.304 3.304h-4.956a3.305 3.305 0 0 1-3.305-3.304zm3.391 1.239c0-.456.37-.826.826-.826h3.304a.826.826 0 1 1 0 1.652h-3.304a.826.826 0 0 1-.826-.826" class="zen-library-box"/></g></svg>
|
||||
@@ -143,6 +143,7 @@
|
||||
* skin/classic/browser/zen-icons/tracking-protection.svg (../shared/zen-icons/nucleo/tracking-protection.svg)
|
||||
* skin/classic/browser/zen-icons/translations.svg (../shared/zen-icons/nucleo/translations.svg)
|
||||
* skin/classic/browser/zen-icons/trash.svg (../shared/zen-icons/nucleo/trash.svg)
|
||||
* skin/classic/browser/zen-icons/u-turn-to-left.svg (../shared/zen-icons/nucleo/u-turn-to-left.svg)
|
||||
* skin/classic/browser/zen-icons/unpin.svg (../shared/zen-icons/nucleo/unpin.svg)
|
||||
* skin/classic/browser/zen-icons/video-blocked-fill.svg (../shared/zen-icons/nucleo/video-blocked-fill.svg)
|
||||
* skin/classic/browser/zen-icons/video-fill.svg (../shared/zen-icons/nucleo/video-fill.svg)
|
||||
@@ -294,6 +295,7 @@
|
||||
* skin/classic/browser/zen-icons/tracking-protection.svg (../shared/zen-icons/nucleo/tracking-protection.svg)
|
||||
* skin/classic/browser/zen-icons/translations.svg (../shared/zen-icons/nucleo/translations.svg)
|
||||
* skin/classic/browser/zen-icons/trash.svg (../shared/zen-icons/nucleo/trash.svg)
|
||||
* skin/classic/browser/zen-icons/u-turn-to-left.svg (../shared/zen-icons/nucleo/u-turn-to-left.svg)
|
||||
* skin/classic/browser/zen-icons/unpin.svg (../shared/zen-icons/nucleo/unpin.svg)
|
||||
* skin/classic/browser/zen-icons/video-blocked-fill.svg (../shared/zen-icons/nucleo/video-blocked-fill.svg)
|
||||
* skin/classic/browser/zen-icons/video-fill.svg (../shared/zen-icons/nucleo/video-fill.svg)
|
||||
@@ -445,6 +447,7 @@
|
||||
* skin/classic/browser/zen-icons/tracking-protection.svg (../shared/zen-icons/nucleo/tracking-protection.svg)
|
||||
* skin/classic/browser/zen-icons/translations.svg (../shared/zen-icons/nucleo/translations.svg)
|
||||
* skin/classic/browser/zen-icons/trash.svg (../shared/zen-icons/nucleo/trash.svg)
|
||||
* skin/classic/browser/zen-icons/u-turn-to-left.svg (../shared/zen-icons/nucleo/u-turn-to-left.svg)
|
||||
* skin/classic/browser/zen-icons/unpin.svg (../shared/zen-icons/nucleo/unpin.svg)
|
||||
* skin/classic/browser/zen-icons/video-blocked-fill.svg (../shared/zen-icons/nucleo/video-blocked-fill.svg)
|
||||
* skin/classic/browser/zen-icons/video-fill.svg (../shared/zen-icons/nucleo/video-fill.svg)
|
||||
@@ -543,4 +546,11 @@
|
||||
* skin/classic/browser/zen-icons/selectable/warning.svg (../shared/zen-icons/common/selectable/warning.svg)
|
||||
* skin/classic/browser/zen-icons/selectable/water.svg (../shared/zen-icons/common/selectable/water.svg)
|
||||
* skin/classic/browser/zen-icons/selectable/weight.svg (../shared/zen-icons/common/selectable/weight.svg)
|
||||
* skin/classic/browser/zen-icons/library/library-boosts-sprite.svg (../shared/zen-icons/common/library/library-boosts-sprite.svg)
|
||||
* skin/classic/browser/zen-icons/library/library-downloads-sprite.svg (../shared/zen-icons/common/library/library-downloads-sprite.svg)
|
||||
* skin/classic/browser/zen-icons/library/library-history-sprite.svg (../shared/zen-icons/common/library/library-history-sprite.svg)
|
||||
* skin/classic/browser/zen-icons/library/library-media-sprite.svg (../shared/zen-icons/common/library/library-media-sprite.svg)
|
||||
* skin/classic/browser/zen-icons/library/library-spaces-sprite.svg (../shared/zen-icons/common/library/library-spaces-sprite.svg)
|
||||
* skin/classic/browser/zen-icons/library/library-sprite.svg (../shared/zen-icons/common/library/library-sprite.svg)
|
||||
* skin/classic/browser/zen-icons/library/library.svg (../shared/zen-icons/common/library/library.svg)
|
||||
skin/classic/browser/zen-icons/icons.css (../shared/zen-icons/icons.css)
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
#filter dumbComments emptyLines substitution
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If applicable, you may obtain one at
|
||||
# http://mozilla.org/MPL/2.0/.
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="context-fill" fill-opacity="context-fill-opacity" viewBox="0 0 18 18"><path d="M5.22 2.22a.75.75 0 1 1 1.06 1.06L4.06 5.5H12a4.5 4.5 0 0 1 0 9H8.25a.75.75 0 0 1 0-1.5H12a3 3 0 0 0 0-6H4.06l2.22 2.22a.75.75 0 0 1-1.06 1.06l-3.5-3.5a.75.75 0 0 1-.165-.812l.004-.01a1 1 0 0 1 .056-.105 1 1 0 0 1 .105-.133z"/></svg>
|
||||
@@ -60,6 +60,13 @@ do_common_icons() {
|
||||
echo "Working on $filename"
|
||||
echo "* skin/classic/browser/zen-icons/selectable/$filename (../shared/zen-icons/common/selectable/$filename) " >> jar.inc.mn
|
||||
done
|
||||
for filename in common/library/*.svg; do
|
||||
# remove the os/ prefix
|
||||
add_header_to_file $filename
|
||||
filename=$(basename $filename)
|
||||
echo "Working on $filename"
|
||||
echo "* skin/classic/browser/zen-icons/library/$filename (../shared/zen-icons/common/library/$filename) " >> jar.inc.mn
|
||||
done
|
||||
}
|
||||
|
||||
do_icons nucleo WIN # TODO: use windows icons
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/build/moz.configure/lto-pgo.configure b/build/moz.configure/lto-pgo.configure
|
||||
index df2b0bf0081edfdc2c7bccaefc780d2b0c6555ff..53a3b55de7650799e3730372e72ad7b4bb2eea03 100644
|
||||
index c72af5ed5f88fc80c810ef626e57a8b991dee514..53a3b55de7650799e3730372e72ad7b4bb2eea03 100644
|
||||
--- a/build/moz.configure/lto-pgo.configure
|
||||
+++ b/build/moz.configure/lto-pgo.configure
|
||||
@@ -234,6 +234,7 @@ def moz_pgo_rust(pgo, profile_use, profile_generate, c_compiler):
|
||||
@@ -14,7 +14,7 @@ index df2b0bf0081edfdc2c7bccaefc780d2b0c6555ff..53a3b55de7650799e3730372e72ad7b4
|
||||
help="Indicate that ld64 is free of symbol aliasing bugs",
|
||||
)
|
||||
|
||||
-imply_option("MOZ_LD64_KNOWN_GOOD", moz_automation)
|
||||
-imply_option("MOZ_LD64_KNOWN_GOOD", True, when=moz_automation)
|
||||
|
||||
use_fat_lto = cxx_compiler.try_link(
|
||||
ldflags=depends(stlport_libs)(
|
||||
|
||||
@@ -1,173 +0,0 @@
|
||||
diff --git a/gfx/webrender_bindings/WebRenderAPI.cpp b/gfx/webrender_bindings/WebRenderAPI.cpp
|
||||
--- a/gfx/webrender_bindings/WebRenderAPI.cpp
|
||||
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
|
||||
@@ -298,11 +298,13 @@
|
||||
panic_on_gl_error, picTileWidth, picTileHeight,
|
||||
gfx::gfxVars::WebRenderRequiresHardwareDriver(),
|
||||
StaticPrefs::gfx_webrender_low_quality_pinch_zoom_AtStartup(),
|
||||
StaticPrefs::gfx_webrender_max_shared_surface_size_AtStartup(),
|
||||
StaticPrefs::gfx_webrender_enable_subpixel_aa_AtStartup(),
|
||||
- compositor->ShouldUseLayerCompositor())) {
|
||||
+ compositor->ShouldUseLayerCompositor(),
|
||||
+ StaticPrefs::
|
||||
+ gfx_webrender_opaque_backdrop_fallback_AtStartup())) {
|
||||
// wr_window_new puts a message into gfxCriticalNote if it returns
|
||||
// false
|
||||
MOZ_ASSERT(errorMessage);
|
||||
error.AssignASCII(errorMessage);
|
||||
wr_api_free_error_msg(errorMessage);
|
||||
diff --git a/gfx/webrender_bindings/src/bindings.rs b/gfx/webrender_bindings/src/bindings.rs
|
||||
--- a/gfx/webrender_bindings/src/bindings.rs
|
||||
+++ b/gfx/webrender_bindings/src/bindings.rs
|
||||
@@ -1998,10 +1998,11 @@
|
||||
reject_software_rasterizer: bool,
|
||||
low_quality_pinch_zoom: bool,
|
||||
max_shared_surface_size: i32,
|
||||
enable_subpixel_aa: bool,
|
||||
use_layer_compositor: bool,
|
||||
+ opaque_backdrop_fallback: bool,
|
||||
) -> bool {
|
||||
assert!(unsafe { is_in_render_thread() });
|
||||
|
||||
// Ensure the WR profiler callbacks are hooked up to the Gecko profiler.
|
||||
set_profiler_hooks(Some(&PROFILER_HOOKS));
|
||||
@@ -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()
|
||||
};
|
||||
|
||||
let window_size = DeviceIntSize::new(window_width, window_height);
|
||||
let notifier = Box::new(CppNotifier { window_id });
|
||||
diff --git a/gfx/wr/webrender/src/device/gl.rs b/gfx/wr/webrender/src/device/gl.rs
|
||||
--- a/gfx/wr/webrender/src/device/gl.rs
|
||||
+++ b/gfx/wr/webrender/src/device/gl.rs
|
||||
@@ -3982,10 +3982,14 @@
|
||||
|
||||
pub fn disable_color_write(&self) {
|
||||
self.gl.color_mask(false, false, false, false);
|
||||
}
|
||||
|
||||
+ pub fn set_color_mask(&self, r: bool, g: bool, b: bool, a: bool) {
|
||||
+ self.gl.color_mask(r, g, b, a);
|
||||
+ }
|
||||
+
|
||||
pub fn set_blend(&mut self, enable: bool) {
|
||||
if enable {
|
||||
self.gl.enable(gl::BLEND);
|
||||
} else {
|
||||
self.gl.disable(gl::BLEND);
|
||||
diff --git a/gfx/wr/webrender/src/renderer/init.rs b/gfx/wr/webrender/src/renderer/init.rs
|
||||
--- a/gfx/wr/webrender/src/renderer/init.rs
|
||||
+++ b/gfx/wr/webrender/src/renderer/init.rs
|
||||
@@ -204,6 +204,8 @@
|
||||
pub low_quality_pinch_zoom: bool,
|
||||
pub max_shared_surface_size: i32,
|
||||
/// If true, open a debug socket to listen for remote debugger.
|
||||
/// Relies on `debugger` cargo feature being enabled.
|
||||
pub enable_debugger: bool,
|
||||
+ /// See explanation of `gfx.webrender.opaque-backdrop-fallback`.
|
||||
+ pub opaque_backdrop_fallback: bool,
|
||||
}
|
||||
@@ -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,
|
||||
enable_debugger: true,
|
||||
+ opaque_backdrop_fallback: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
pending_result_msg: None,
|
||||
layer_compositor_frame_state_in_prev_frame: None,
|
||||
external_composite_debug_items: Vec::new(),
|
||||
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,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,
|
||||
+
|
||||
/// Count consecutive oom frames to detectif we are stuck unable to render
|
||||
/// in a loop.
|
||||
consecutive_oom_frames: u32,
|
||||
|
||||
/// update() defers processing of ResultMsg, if frame_publish_id of
|
||||
@@ -2787,18 +2789,29 @@
|
||||
let read_target = ReadTarget::from_texture(cache_texture);
|
||||
|
||||
// Should always be drawing to picture cache tiles or off-screen surface!
|
||||
debug_assert!(!draw_target.is_default());
|
||||
let device_to_framebuffer = Scale::new(1i32);
|
||||
+ let dest_fb_rect = dest * device_to_framebuffer;
|
||||
|
||||
self.device.blit_render_target(
|
||||
read_target,
|
||||
src * device_to_framebuffer,
|
||||
draw_target,
|
||||
- dest * device_to_framebuffer,
|
||||
+ dest_fb_rect,
|
||||
TextureFilter::Linear,
|
||||
);
|
||||
+
|
||||
+ if self.opaque_backdrop_fallback {
|
||||
+ self.device.set_color_mask(false, false, false, true);
|
||||
+ self.device.clear_target(
|
||||
+ Some([0.0, 0.0, 0.0, 1.0]),
|
||||
+ None,
|
||||
+ Some(dest_fb_rect),
|
||||
+ );
|
||||
+ self.device.set_color_mask(true, true, true, true);
|
||||
+ }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_picture_cache_target(
|
||||
diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml
|
||||
--- a/modules/libpref/init/StaticPrefList.yaml
|
||||
+++ b/modules/libpref/init/StaticPrefList.yaml
|
||||
@@ -8439,10 +8439,17 @@
|
||||
#else
|
||||
value: false
|
||||
#endif
|
||||
mirror: once
|
||||
|
||||
+# Make backdrop-filter treat its captured backdrop as if it had been
|
||||
+# composited over an opaque-black background. (See bug 2036640)
|
||||
+- name: gfx.webrender.opaque-backdrop-fallback
|
||||
+ type: bool
|
||||
+ value: true
|
||||
+ mirror: once
|
||||
+
|
||||
# Disable wait of GPU execution completion
|
||||
- name: gfx.webrender.wait-gpu-finished.disabled
|
||||
type: bool
|
||||
value: false
|
||||
mirror: once
|
||||
|
||||
@@ -0,0 +1,886 @@
|
||||
diff --git a/gfx/wr/webrender/src/command_buffer.rs b/gfx/wr/webrender/src/command_buffer.rs
|
||||
index 9369cea..8c70d29 100644
|
||||
--- a/gfx/wr/webrender/src/command_buffer.rs
|
||||
+++ b/gfx/wr/webrender/src/command_buffer.rs
|
||||
@@ -476,6 +476,9 @@ pub struct CommandBufferBuilder {
|
||||
|
||||
/// List of render tasks that depend on the task that will be created for this builder.
|
||||
pub extra_dependencies: Vec<RenderTaskId>,
|
||||
+
|
||||
+ /// If true, backdrop-filters on this (tiled) surface also read back the slices below it.
|
||||
+ pub backdrop_reads_lower_slices: bool,
|
||||
}
|
||||
|
||||
impl CommandBufferBuilder {
|
||||
@@ -485,12 +488,14 @@ impl CommandBufferBuilder {
|
||||
establishes_sub_graph: false,
|
||||
resolve_source: None,
|
||||
extra_dependencies: Vec::new(),
|
||||
+ backdrop_reads_lower_slices: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a tiled command buffer builder.
|
||||
pub fn new_tiled(
|
||||
tiles: FastHashMap<TileKey, SurfaceTileDescriptor>,
|
||||
+ backdrop_reads_lower_slices: bool,
|
||||
) -> Self {
|
||||
CommandBufferBuilder {
|
||||
kind: CommandBufferBuilderKind::Tiled {
|
||||
@@ -499,6 +504,7 @@ impl CommandBufferBuilder {
|
||||
establishes_sub_graph: false,
|
||||
resolve_source: None,
|
||||
extra_dependencies: Vec::new(),
|
||||
+ backdrop_reads_lower_slices,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -518,6 +524,7 @@ impl CommandBufferBuilder {
|
||||
establishes_sub_graph,
|
||||
resolve_source: None,
|
||||
extra_dependencies: Vec::new(),
|
||||
+ backdrop_reads_lower_slices: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
diff --git a/gfx/wr/webrender/src/frame_builder.rs b/gfx/wr/webrender/src/frame_builder.rs
|
||||
index 068182d..1442528 100644
|
||||
--- a/gfx/wr/webrender/src/frame_builder.rs
|
||||
+++ b/gfx/wr/webrender/src/frame_builder.rs
|
||||
@@ -17,7 +17,7 @@ use crate::gpu_types::{PrimitiveHeaders, ZBufferIdGenerator};
|
||||
use crate::gpu_types::QuadSegment;
|
||||
use crate::internal_types::{FastHashMap, PlaneSplitter, FrameStamp};
|
||||
use crate::invalidation::DirtyRegion;
|
||||
-use crate::tile_cache::{SliceId, TileCacheInstance};
|
||||
+use crate::tile_cache::{SliceId, TileCacheInstance, update_cross_slice_backdrops};
|
||||
use crate::picture::PictureInstance;
|
||||
use crate::picture::ResolvedSurfaceTexture;
|
||||
use crate::picture::{RasterConfig, PictureScratch};
|
||||
@@ -487,6 +487,25 @@ impl FrameBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
+ if render_picture_cache_slices {
|
||||
+ let slice_ids: Vec<SliceId> = scene.tile_cache_pictures
|
||||
+ .iter()
|
||||
+ .filter_map(|pic_index| {
|
||||
+ match scene.prim_store.pictures[pic_index.0 as usize].raster_config {
|
||||
+ Some(RasterConfig { composite_mode: PictureCompositeMode::TileCache { slice_id }, .. }) => Some(slice_id),
|
||||
+ _ => None,
|
||||
+ }
|
||||
+ })
|
||||
+ .collect();
|
||||
+
|
||||
+ update_cross_slice_backdrops(
|
||||
+ &slice_ids,
|
||||
+ tile_caches,
|
||||
+ &visibility_context,
|
||||
+ &composite_state.compositor_kind,
|
||||
+ );
|
||||
+ }
|
||||
+
|
||||
scratch.primitive.frame.assert_draws_resolved();
|
||||
|
||||
profile.end_time(profiler::FRAME_VISIBILITY_TIME);
|
||||
diff --git a/gfx/wr/webrender/src/picture.rs b/gfx/wr/webrender/src/picture.rs
|
||||
index 9b100ee..8f476e5 100644
|
||||
--- a/gfx/wr/webrender/src/picture.rs
|
||||
+++ b/gfx/wr/webrender/src/picture.rs
|
||||
@@ -140,7 +140,8 @@ use std::ops::Range;
|
||||
use crate::picture_textures::PictureCacheTextureHandle;
|
||||
use crate::util::{MaxRect, Recycler, ScaleOffset};
|
||||
use crate::tile_cache::{SliceDebugInfo, TileDebugInfo, DirtyTileDebugInfo, CompositorClipDebugInfo};
|
||||
-use crate::tile_cache::{SliceId, TileCacheInstance, TileSurface, NativeSurface};
|
||||
+use crate::tile_cache::{SliceId, Tile, TileCacheInstance, TileSurface, NativeSurface};
|
||||
+use crate::render_target::{LowerSliceSource, LowerSliceSourceKind};
|
||||
use crate::tile_cache::{BackdropKind, BackdropSurface};
|
||||
use crate::tile_cache::{TileKey, SubSliceIndex};
|
||||
use crate::invalidation::InvalidationReason;
|
||||
@@ -1660,6 +1661,32 @@ fn prepare_tiled_picture_surface(
|
||||
let device_pixel_scale = frame_state
|
||||
.surfaces[surface_index.0]
|
||||
.device_pixel_scale;
|
||||
+
|
||||
+ let pic_to_root = if tile_cache.feeds_cross_slice_backdrop {
|
||||
+ frame_context.spatial_tree
|
||||
+ .get_relative_transform(tile_cache.spatial_node_index, frame_context.root_spatial_node_index)
|
||||
+ .as_2d_scale_offset()
|
||||
+ } else {
|
||||
+ None
|
||||
+ };
|
||||
+ let rounded_clip = tile_cache.compositor_clip.map(|clip_index| {
|
||||
+ let clip = frame_state.composite_state.get_compositor_clip(clip_index);
|
||||
+ (clip.rect.cast_unit(), clip.radius)
|
||||
+ });
|
||||
+ let local_clip_rect = tile_cache.local_clip_rect;
|
||||
+ let lower_slice_source = move |tile: &Tile, kind: LowerSliceSourceKind, rect: PictureRect, sub_slice_index: usize| {
|
||||
+ let pic_to_root = pic_to_root?;
|
||||
+ let clip_rect = local_clip_rect
|
||||
+ .intersection(&tile.cached_surface.current_descriptor.local_valid_rect)?;
|
||||
+
|
||||
+ Some(LowerSliceSource {
|
||||
+ kind,
|
||||
+ rect: pic_to_root.map_rect(&rect),
|
||||
+ clip_rect: pic_to_root.map_rect(&clip_rect),
|
||||
+ rounded_clip,
|
||||
+ sub_slice_index,
|
||||
+ })
|
||||
+ };
|
||||
let mut at_least_one_tile_visible = false;
|
||||
|
||||
// Get the overall device space rect of the picture cache. Used to clip
|
||||
@@ -1784,6 +1811,19 @@ fn prepare_tiled_picture_surface(
|
||||
|
||||
at_least_one_tile_visible = true;
|
||||
|
||||
+ if tile.feeds_cross_slice_backdrop {
|
||||
+ if let Some(TileSurface::Color { color }) = tile.surface {
|
||||
+ if let Some(source) = lower_slice_source(
|
||||
+ tile,
|
||||
+ LowerSliceSourceKind::Color(color),
|
||||
+ tile.cached_surface.current_descriptor.local_valid_rect,
|
||||
+ sub_slice_index,
|
||||
+ ) {
|
||||
+ frame_state.surface_builder.lower_slice_sources.push(source);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
if let TileSurface::Texture { descriptor, .. } = tile.surface.as_mut().unwrap() {
|
||||
match descriptor {
|
||||
SurfaceTextureDescriptor::TextureCache { ref handle, .. } => {
|
||||
@@ -1972,7 +2012,8 @@ fn prepare_tiled_picture_surface(
|
||||
// TODO(gw): As a performance optimization, we could skip the resolve picture
|
||||
// if the dirty rect is the same as the resolve rect (probably quite
|
||||
// common for effects that scroll underneath a backdrop-filter, for example).
|
||||
- let use_tile_composite = !tile.cached_surface.sub_graphs.is_empty();
|
||||
+ let use_tile_composite = !tile.cached_surface.sub_graphs.is_empty() ||
|
||||
+ tile.feeds_cross_slice_backdrop;
|
||||
|
||||
if use_tile_composite {
|
||||
let mut local_content_rect = tile.cached_surface.local_dirty_rect;
|
||||
@@ -2056,12 +2097,24 @@ fn prepare_tiled_picture_surface(
|
||||
),
|
||||
);
|
||||
|
||||
+ let lower_slice_source = if tile.feeds_cross_slice_backdrop {
|
||||
+ lower_slice_source(
|
||||
+ tile,
|
||||
+ LowerSliceSourceKind::Task(render_task_id),
|
||||
+ (content_device_rect.to_f32() / device_pixel_scale).cast_unit(),
|
||||
+ sub_slice_index,
|
||||
+ )
|
||||
+ } else {
|
||||
+ None
|
||||
+ };
|
||||
+
|
||||
surface_render_tasks.insert(
|
||||
tile_key,
|
||||
SurfaceTileDescriptor {
|
||||
current_task_id: render_task_id,
|
||||
composite_task_id: Some(composite_task_id),
|
||||
dirty_rect: tile.cached_surface.local_dirty_rect,
|
||||
+ lower_slice_source,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
@@ -2097,6 +2150,7 @@ fn prepare_tiled_picture_surface(
|
||||
current_task_id: render_task_id,
|
||||
composite_task_id: None,
|
||||
dirty_rect: tile.cached_surface.local_dirty_rect,
|
||||
+ lower_slice_source: None,
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -2262,7 +2316,10 @@ fn prepare_tiled_picture_surface(
|
||||
);
|
||||
}
|
||||
|
||||
- let descriptor = SurfaceDescriptor::new_tiled(surface_render_tasks);
|
||||
+ let descriptor = SurfaceDescriptor::new_tiled(
|
||||
+ surface_render_tasks,
|
||||
+ tile_cache.backdrop_reads_lower_slices,
|
||||
+ );
|
||||
|
||||
frame_state.surface_builder.push_surface(
|
||||
surface_index,
|
||||
diff --git a/gfx/wr/webrender/src/render_target.rs b/gfx/wr/webrender/src/render_target.rs
|
||||
index 2185365..d3ca780 100644
|
||||
--- a/gfx/wr/webrender/src/render_target.rs
|
||||
+++ b/gfx/wr/webrender/src/render_target.rs
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
|
||||
use api::units::*;
|
||||
-use api::{ColorF, LineOrientation, BorderStyle};
|
||||
+use api::{BorderRadius, ColorF, LineOrientation, BorderStyle};
|
||||
use crate::batch::{AlphaBatchBuilder, AlphaBatchContainer, BatchTextures, TextureSet};
|
||||
use crate::batch::{BatchBuilder, INVALID_SEGMENT_INDEX, ClipMaskInstanceList};
|
||||
use crate::render_task::{SubTask, RectangleClipSubTask, ImageClipSubTask};
|
||||
@@ -631,6 +631,33 @@ pub struct ResolveOp {
|
||||
/// back the region the backdrop actually covers rather than one offset by
|
||||
/// the difference between the two raster roots.
|
||||
pub dest_to_src_raster: ScaleOffset,
|
||||
+ /// Content of the picture cache slices below the backdrop root, drawn bottom-up
|
||||
+ /// under the content of the src tasks.
|
||||
+ pub lower_slice_sources: Vec<LowerSliceSource>,
|
||||
+ /// Maps a rect from the dest surface's raster space into the root coordinate system.
|
||||
+ pub dest_raster_to_root: ScaleOffset,
|
||||
+}
|
||||
+
|
||||
+#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
+#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
+#[derive(Debug, PartialEq, Clone)]
|
||||
+pub enum LowerSliceSourceKind {
|
||||
+ Task(RenderTaskId),
|
||||
+ Color(ColorF),
|
||||
+}
|
||||
+
|
||||
+/// Content of a picture cache tile that a backdrop-filter in a slice above it reads
|
||||
+/// back.
|
||||
+#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
+#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
+#[derive(Debug, PartialEq, Clone)]
|
||||
+pub struct LowerSliceSource {
|
||||
+ pub kind: LowerSliceSourceKind,
|
||||
+ /// The rect covered by the task's content, or by the color.
|
||||
+ pub rect: LayoutRect,
|
||||
+ pub clip_rect: LayoutRect,
|
||||
+ pub rounded_clip: Option<(LayoutRect, BorderRadius)>,
|
||||
+ pub sub_slice_index: usize,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
diff --git a/gfx/wr/webrender/src/renderer/composite.rs b/gfx/wr/webrender/src/renderer/composite.rs
|
||||
index 032a0e1..dd91394 100644
|
||||
--- a/gfx/wr/webrender/src/renderer/composite.rs
|
||||
+++ b/gfx/wr/webrender/src/renderer/composite.rs
|
||||
@@ -6,6 +6,7 @@ use api::units::*;
|
||||
use api::{ColorF, ImageBufferKind, ImageRendering, PremultipliedColorF};
|
||||
use crate::batch::BatchTextures;
|
||||
use crate::composite::{
|
||||
+ CompositorClip,
|
||||
CompositeState, CompositeSurfaceFormat, CompositeTileSurface, CompositorConfig,
|
||||
CompositorInputLayer, CompositorSurfaceUsage, CompositorSurfaceTransform,
|
||||
CompositeRoundedCorner, NativeTileId, ResolvedExternalSurface,
|
||||
@@ -20,7 +21,11 @@ use crate::debug_item::DebugItem;
|
||||
use crate::segment::EdgeMask;
|
||||
use crate::device::DrawTarget;
|
||||
use crate::gpu_types::{CompositeInstance, ZBufferId};
|
||||
-use crate::internal_types::{FastHashMap, TextureSource};
|
||||
+use crate::internal_types::{FastHashMap, Swizzle, TextureSource};
|
||||
+use crate::render_target::{LowerSliceSourceKind, ResolveOp};
|
||||
+use crate::render_task::RenderTaskKind;
|
||||
+use crate::render_task_graph::RenderTaskGraph;
|
||||
+use api::BorderRadius;
|
||||
use crate::picture::ResolvedSurfaceTexture;
|
||||
use super::RenderResults;
|
||||
use crate::profiler::{self};
|
||||
@@ -435,6 +440,134 @@ impl Renderer {
|
||||
}
|
||||
}
|
||||
|
||||
+ pub(super) fn draw_resolve_with_lower_slices(
|
||||
+ &mut self,
|
||||
+ resolve_op: &ResolveOp,
|
||||
+ render_tasks: &RenderTaskGraph,
|
||||
+ draw_target: DrawTarget,
|
||||
+ projection: &default::Transform3D<f32>,
|
||||
+ src_copies: &[(TextureSource, DeviceIntRect, DeviceIntRect)],
|
||||
+ stats: &mut RendererStats,
|
||||
+ ) {
|
||||
+ let dest_task = &render_tasks[resolve_op.dest_task_id];
|
||||
+ let dest_info = match dest_task.kind {
|
||||
+ RenderTaskKind::Picture(ref info) => info,
|
||||
+ _ => panic!("bug: not a picture"),
|
||||
+ };
|
||||
+ let dest_task_rect = DeviceRect::from_origin_and_size(
|
||||
+ dest_task.get_target_rect().min.to_f32(),
|
||||
+ dest_info.content_size.to_f32(),
|
||||
+ );
|
||||
+ let dest_offset = dest_task_rect.min.to_vector() - dest_info.content_origin.to_vector();
|
||||
+ let root_to_dest = |rect: &LayoutRect| -> DeviceRect {
|
||||
+ let raster: WorldRect = resolve_op.dest_raster_to_root.unmap_rect(rect);
|
||||
+ let device: DeviceRect = (raster * dest_info.device_pixel_scale).cast_unit();
|
||||
+ device.translate(dest_offset)
|
||||
+ };
|
||||
+ let radius_scale = dest_info.device_pixel_scale.0 / resolve_op.dest_raster_to_root.scale.x;
|
||||
+
|
||||
+ let mut draws = Vec::new();
|
||||
+
|
||||
+ for source in &resolve_op.lower_slice_sources {
|
||||
+ let clip_rect = match root_to_dest(&source.clip_rect).intersection(&dest_task_rect) {
|
||||
+ Some(clip_rect) => clip_rect,
|
||||
+ None => continue,
|
||||
+ };
|
||||
+ let rect = root_to_dest(&source.rect);
|
||||
+ let clip = source.rounded_clip.map(|(clip_rect, radius)| {
|
||||
+ CompositorClip {
|
||||
+ rect: root_to_dest(&clip_rect),
|
||||
+ radius: BorderRadius {
|
||||
+ top_left: radius.top_left * radius_scale,
|
||||
+ top_right: radius.top_right * radius_scale,
|
||||
+ bottom_left: radius.bottom_left * radius_scale,
|
||||
+ bottom_right: radius.bottom_right * radius_scale,
|
||||
+ ..radius
|
||||
+ },
|
||||
+ }
|
||||
+ });
|
||||
+
|
||||
+ match source.kind {
|
||||
+ LowerSliceSourceKind::Task(task_id) => {
|
||||
+ let task = &render_tasks[task_id];
|
||||
+ let texture = TextureSource::TextureCache(task.get_target_texture(), Swizzle::default());
|
||||
+ let uv_rect = task.get_target_rect().to_f32();
|
||||
+ let instance = CompositeInstance::new_rgb(
|
||||
+ rect,
|
||||
+ clip_rect,
|
||||
+ PremultipliedColorF::WHITE,
|
||||
+ TexelRect::new(uv_rect.min.x, uv_rect.min.y, uv_rect.max.x, uv_rect.max.y),
|
||||
+ false,
|
||||
+ (false, false),
|
||||
+ clip.as_ref(),
|
||||
+ );
|
||||
+ let texture_size = self.texture_resolver.get_texture_size(&texture).to_f32();
|
||||
+ draws.push((instance, texture, Some(texture_size)));
|
||||
+ }
|
||||
+ LowerSliceSourceKind::Color(color) => {
|
||||
+ let instance = CompositeInstance::new(
|
||||
+ rect,
|
||||
+ clip_rect,
|
||||
+ color.premultiplied(),
|
||||
+ (false, false),
|
||||
+ clip.as_ref(),
|
||||
+ );
|
||||
+ draws.push((instance, TextureSource::Dummy, None));
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ for (texture, src, dest) in src_copies {
|
||||
+ let dest = dest.to_f32();
|
||||
+ let src = src.to_f32();
|
||||
+ let instance = CompositeInstance::new_rgb(
|
||||
+ dest,
|
||||
+ dest,
|
||||
+ PremultipliedColorF::WHITE,
|
||||
+ TexelRect::new(src.min.x, src.min.y, src.max.x, src.max.y),
|
||||
+ false,
|
||||
+ (false, false),
|
||||
+ None,
|
||||
+ );
|
||||
+ let texture_size = self.texture_resolver.get_texture_size(texture).to_f32();
|
||||
+ draws.push((instance, *texture, Some(texture_size)));
|
||||
+ }
|
||||
+
|
||||
+ self.device.bind_draw_target(draw_target);
|
||||
+ self.device.disable_depth();
|
||||
+ self.device.disable_depth_write();
|
||||
+ self.set_blend(true, FramebufferKind::Other);
|
||||
+ self.set_blend_mode_premultiplied_alpha(FramebufferKind::Other);
|
||||
+
|
||||
+ for (instance, texture, texture_size) in draws {
|
||||
+ let features = instance.get_rgb_features();
|
||||
+
|
||||
+ self.shaders
|
||||
+ .borrow_mut()
|
||||
+ .get_composite_shader(
|
||||
+ CompositeSurfaceFormat::Rgba,
|
||||
+ texture.image_buffer_kind(),
|
||||
+ features,
|
||||
+ ).bind(
|
||||
+ &mut self.device,
|
||||
+ projection,
|
||||
+ texture_size,
|
||||
+ &mut self.renderer_errors,
|
||||
+ &mut self.profile,
|
||||
+ &mut self.command_log,
|
||||
+ );
|
||||
+
|
||||
+ self.draw_instanced_batch(
|
||||
+ &[instance],
|
||||
+ VertexArrayKind::Composite,
|
||||
+ &BatchTextures::composite_rgb(texture),
|
||||
+ stats,
|
||||
+ );
|
||||
+ }
|
||||
+
|
||||
+ self.set_blend(false, FramebufferKind::Other);
|
||||
+ }
|
||||
+
|
||||
// Composite tiles in a swapchain. When using LayerCompositor, we may
|
||||
// split the compositing in to multiple swapchains.
|
||||
fn composite_pass(
|
||||
diff --git a/gfx/wr/webrender/src/renderer/mod.rs b/gfx/wr/webrender/src/renderer/mod.rs
|
||||
index d177263..a45ec70 100644
|
||||
--- a/gfx/wr/webrender/src/renderer/mod.rs
|
||||
+++ b/gfx/wr/webrender/src/renderer/mod.rs
|
||||
@@ -2288,6 +2288,8 @@ impl Renderer {
|
||||
resolve_ops: &[ResolveOp],
|
||||
render_tasks: &RenderTaskGraph,
|
||||
draw_target: DrawTarget,
|
||||
+ projection: &default::Transform3D<f32>,
|
||||
+ stats: &mut RendererStats,
|
||||
) {
|
||||
if resolve_ops.is_empty() {
|
||||
return;
|
||||
@@ -2300,6 +2302,8 @@ impl Renderer {
|
||||
resolve_op,
|
||||
render_tasks,
|
||||
draw_target,
|
||||
+ projection,
|
||||
+ stats,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2729,7 +2733,12 @@ impl Renderer {
|
||||
resolve_op: &ResolveOp,
|
||||
render_tasks: &RenderTaskGraph,
|
||||
draw_target: DrawTarget,
|
||||
+ projection: &default::Transform3D<f32>,
|
||||
+ stats: &mut RendererStats,
|
||||
) {
|
||||
+ let has_lower_slices = !resolve_op.lower_slice_sources.is_empty();
|
||||
+ let mut src_copies = Vec::new();
|
||||
+
|
||||
for src_task_id in &resolve_op.src_task_ids {
|
||||
let src_task = &render_tasks[*src_task_id];
|
||||
let src_info = match src_task.kind {
|
||||
@@ -2818,6 +2827,12 @@ impl Renderer {
|
||||
src_task.get_target_texture(),
|
||||
Swizzle::default(),
|
||||
);
|
||||
+
|
||||
+ if has_lower_slices {
|
||||
+ src_copies.push((texture_source, src, dest));
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
let (cache_texture, _) = self.texture_resolver
|
||||
.resolve(&texture_source).expect("bug: no source texture");
|
||||
|
||||
@@ -2836,6 +2851,17 @@ impl Renderer {
|
||||
);
|
||||
}
|
||||
}
|
||||
+
|
||||
+ if has_lower_slices {
|
||||
+ self.draw_resolve_with_lower_slices(
|
||||
+ resolve_op,
|
||||
+ render_tasks,
|
||||
+ draw_target,
|
||||
+ projection,
|
||||
+ &src_copies,
|
||||
+ stats,
|
||||
+ );
|
||||
+ }
|
||||
}
|
||||
|
||||
fn draw_picture_cache_target(
|
||||
@@ -3385,6 +3411,8 @@ impl Renderer {
|
||||
&target.resolve_ops,
|
||||
render_tasks,
|
||||
draw_target,
|
||||
+ &projection,
|
||||
+ stats,
|
||||
);
|
||||
|
||||
// Handle any blits from the texture cache to this target.
|
||||
diff --git a/gfx/wr/webrender/src/surface.rs b/gfx/wr/webrender/src/surface.rs
|
||||
index 20536bb..f8a8709 100644
|
||||
--- a/gfx/wr/webrender/src/surface.rs
|
||||
+++ b/gfx/wr/webrender/src/surface.rs
|
||||
@@ -14,7 +14,7 @@ use crate::picture_composite_mode::PictureCompositeMode;
|
||||
use crate::tile_cache::{TileKey, SubSliceIndex, MAX_COMPOSITOR_SURFACES};
|
||||
use crate::prim_store::PictureIndex;
|
||||
use crate::render_task_graph::{RenderTaskId, RenderTaskGraphBuilder};
|
||||
-use crate::render_target::ResolveOp;
|
||||
+use crate::render_target::{LowerSliceSource, LowerSliceSourceKind, ResolveOp};
|
||||
use crate::render_task::{RenderTask, RenderTaskKind, RenderTaskLocation};
|
||||
use crate::space::SpaceMapper;
|
||||
use crate::spatial_tree::{CoordinateSpaceMapping, SpatialTree, SpatialNodeIndex};
|
||||
@@ -380,6 +380,8 @@ pub struct SurfaceTileDescriptor {
|
||||
pub composite_task_id: Option<RenderTaskId>,
|
||||
/// Dirty rect for this tile
|
||||
pub dirty_rect: PictureRect,
|
||||
+ /// Set if this tile's content is read back by a backdrop-filter in a slice above.
|
||||
+ pub lower_slice_source: Option<LowerSliceSource>,
|
||||
}
|
||||
|
||||
// Details of how a surface is rendered
|
||||
@@ -387,6 +389,7 @@ pub enum SurfaceDescriptorKind {
|
||||
// Picture cache tiles
|
||||
Tiled {
|
||||
tiles: FastHashMap<TileKey, SurfaceTileDescriptor>,
|
||||
+ backdrop_reads_lower_slices: bool,
|
||||
},
|
||||
// A single surface (e.g. for an opacity filter)
|
||||
Simple {
|
||||
@@ -410,10 +413,12 @@ impl SurfaceDescriptor {
|
||||
// Create a picture cache tiled surface
|
||||
pub fn new_tiled(
|
||||
tiles: FastHashMap<TileKey, SurfaceTileDescriptor>,
|
||||
+ backdrop_reads_lower_slices: bool,
|
||||
) -> Self {
|
||||
SurfaceDescriptor {
|
||||
kind: SurfaceDescriptorKind::Tiled {
|
||||
tiles,
|
||||
+ backdrop_reads_lower_slices,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -526,6 +531,9 @@ pub struct SurfaceBuilder {
|
||||
// A map of the output render tasks from any sub-graphs that haven't
|
||||
// been consumed by BackdropRender prims yet
|
||||
pub sub_graph_output_map: FastHashMap<PictureIndex, RenderTaskId>,
|
||||
+ // Content of the picture cache slices built so far that backdrop-filters in
|
||||
+ // slices above them read back, ordered bottom-up
|
||||
+ pub lower_slice_sources: Vec<LowerSliceSource>,
|
||||
}
|
||||
|
||||
impl SurfaceBuilder {
|
||||
@@ -534,6 +542,7 @@ impl SurfaceBuilder {
|
||||
current_cmd_buffers: CommandBufferTargets::new(),
|
||||
builder_stack: Vec::new(),
|
||||
sub_graph_output_map: FastHashMap::default(),
|
||||
+ lower_slice_sources: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -574,9 +583,10 @@ impl SurfaceBuilder {
|
||||
|
||||
let builder = if let Some(descriptor) = descriptor {
|
||||
match descriptor.kind {
|
||||
- SurfaceDescriptorKind::Tiled { tiles } => {
|
||||
+ SurfaceDescriptorKind::Tiled { tiles, backdrop_reads_lower_slices } => {
|
||||
CommandBufferBuilder::new_tiled(
|
||||
tiles,
|
||||
+ backdrop_reads_lower_slices,
|
||||
)
|
||||
}
|
||||
SurfaceDescriptorKind::Simple { render_task_id, dirty_rect, .. } => {
|
||||
@@ -700,6 +710,7 @@ impl SurfaceBuilder {
|
||||
// Get info about the resolve operation to copy from parent surface or tiles to the picture cache task
|
||||
if let Some(resolve_task_id) = builder.resolve_source {
|
||||
let mut src_task_ids = Vec::new();
|
||||
+ let reads_lower_slices = self.builder_stack.last().unwrap().backdrop_reads_lower_slices;
|
||||
|
||||
// Make the output of the sub-graph a dependency of the new replacement tile task
|
||||
let _old = self.sub_graph_output_map.insert(
|
||||
@@ -869,6 +880,27 @@ impl SurfaceBuilder {
|
||||
&src_task_ids,
|
||||
);
|
||||
|
||||
+ let mut lower_slice_sources = Vec::new();
|
||||
+ let mut dest_raster_to_root = ScaleOffset::identity();
|
||||
+
|
||||
+ if reads_lower_slices && !self.lower_slice_sources.is_empty() {
|
||||
+ let dest_raster = raster_spatial_node(rg_builder, resolve_task_id);
|
||||
+ let to_root = spatial_tree
|
||||
+ .get_relative_transform(dest_raster, spatial_tree.root_reference_frame_index())
|
||||
+ .as_2d_scale_offset();
|
||||
+
|
||||
+ if let Some(to_root) = to_root {
|
||||
+ dest_raster_to_root = to_root;
|
||||
+ lower_slice_sources = self.lower_slice_sources.clone();
|
||||
+
|
||||
+ for source in &lower_slice_sources {
|
||||
+ if let LowerSliceSourceKind::Task(task_id) = source.kind {
|
||||
+ rg_builder.add_dependency(resolve_task_id, task_id);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
let dest_task = rg_builder.get_task_mut(resolve_task_id);
|
||||
|
||||
match dest_task.kind {
|
||||
@@ -878,6 +910,8 @@ impl SurfaceBuilder {
|
||||
src_task_ids,
|
||||
dest_task_id: resolve_task_id,
|
||||
dest_to_src_raster,
|
||||
+ lower_slice_sources,
|
||||
+ dest_raster_to_root,
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
@@ -917,7 +951,16 @@ impl SurfaceBuilder {
|
||||
} else {
|
||||
match builder.kind {
|
||||
CommandBufferBuilderKind::Tiled { ref tiles } => {
|
||||
+ let first_source = self.lower_slice_sources.len();
|
||||
+
|
||||
for (_, descriptor) in tiles {
|
||||
+ if let Some(ref source) = descriptor.lower_slice_source {
|
||||
+ self.lower_slice_sources.push(LowerSliceSource {
|
||||
+ kind: LowerSliceSourceKind::Task(descriptor.current_task_id),
|
||||
+ ..source.clone()
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
if let Some(composite_task_id) = descriptor.composite_task_id {
|
||||
rg_builder.add_dependency(
|
||||
composite_task_id,
|
||||
@@ -933,6 +976,8 @@ impl SurfaceBuilder {
|
||||
}
|
||||
}
|
||||
}
|
||||
+
|
||||
+ self.lower_slice_sources[first_source ..].sort_by_key(|source| source.sub_slice_index);
|
||||
}
|
||||
CommandBufferBuilderKind::Simple { render_task_id: child_task_id, root_task_id: child_root_task_id, .. } => {
|
||||
match self.builder_stack.last().unwrap().kind {
|
||||
diff --git a/gfx/wr/webrender/src/tile_cache/mod.rs b/gfx/wr/webrender/src/tile_cache/mod.rs
|
||||
index 731be26..d20e6ac 100644
|
||||
--- a/gfx/wr/webrender/src/tile_cache/mod.rs
|
||||
+++ b/gfx/wr/webrender/src/tile_cache/mod.rs
|
||||
@@ -198,6 +198,8 @@ pub struct TileCacheParams {
|
||||
// This is only a suggestion - the tile cache will clamp this as a reasonable number
|
||||
// and only promote a limited number of surfaces.
|
||||
pub yuv_image_surface_count: usize,
|
||||
+ // If true, backdrop-filters in this slice also read back content from the slices below it.
|
||||
+ pub backdrop_reads_lower_slices: bool,
|
||||
}
|
||||
|
||||
/// The backing surface for this tile.
|
||||
@@ -250,6 +252,8 @@ pub struct Tile {
|
||||
pub cached_surface: CachedSurface,
|
||||
/// Raster-space rect for this tile, cached to avoid recomputing per primitive.
|
||||
pub local_raster_rect: RasterRect,
|
||||
+ /// If true, this tile is read back this frame by a slice above.
|
||||
+ pub feeds_cross_slice_backdrop: bool,
|
||||
}
|
||||
|
||||
impl Tile {
|
||||
@@ -269,6 +273,7 @@ impl Tile {
|
||||
z_id: ZBufferId::invalid(),
|
||||
cached_surface: CachedSurface::new(),
|
||||
local_raster_rect: RasterRect::zero(),
|
||||
+ feeds_cross_slice_backdrop: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -828,6 +833,15 @@ pub struct TileCacheInstance {
|
||||
pub yuv_images_remaining: usize,
|
||||
/// Persistent cache for computing and storing raster-space primitive corners.
|
||||
corners_cache: CornersCache,
|
||||
+ /// If true, backdrop-filters in this slice also read back content from the slices below it.
|
||||
+ pub backdrop_reads_lower_slices: bool,
|
||||
+ /// The picture-space coverage rects of the backdrop-filters found in this slice this frame.
|
||||
+ backdrop_render_rects: Vec<PictureRect>,
|
||||
+ /// The picture to root transform of this slice in the previous frame, used to detect
|
||||
+ /// movement relative to backdrop-filters in slices above.
|
||||
+ prev_pic_to_root: Option<ScaleOffset>,
|
||||
+ /// If true, some tiles of this slice are read back by a backdrop-filter in a slice above.
|
||||
+ pub feeds_cross_slice_backdrop: bool,
|
||||
}
|
||||
|
||||
impl TileCacheInstance {
|
||||
@@ -893,6 +907,10 @@ impl TileCacheInstance {
|
||||
yuv_images_count: params.yuv_image_surface_count,
|
||||
yuv_images_remaining: 0,
|
||||
corners_cache: CornersCache::new(),
|
||||
+ backdrop_reads_lower_slices: params.backdrop_reads_lower_slices,
|
||||
+ backdrop_render_rects: Vec::new(),
|
||||
+ prev_pic_to_root: None,
|
||||
+ feeds_cross_slice_backdrop: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -970,6 +988,7 @@ impl TileCacheInstance {
|
||||
self.background_color = params.background_color;
|
||||
self.shared_clip_leaf_id = params.shared_clip_leaf_id;
|
||||
self.shared_clip_node_id = params.shared_clip_node_id;
|
||||
+ self.backdrop_reads_lower_slices = params.backdrop_reads_lower_slices;
|
||||
|
||||
// Since the slice flags may have changed, ensure we re-evaluate the
|
||||
// appropriate tile size for this cache next update.
|
||||
@@ -1040,6 +1059,7 @@ impl TileCacheInstance {
|
||||
self.local_rect = pic_rect;
|
||||
self.local_clip_rect = PictureRect::max_rect();
|
||||
self.deferred_dirty_tests.clear();
|
||||
+ self.backdrop_render_rects.clear();
|
||||
self.mix_blend_pic_rects.clear();
|
||||
self.underlays.clear();
|
||||
self.overlay_region = PictureRect::zero();
|
||||
@@ -2667,6 +2687,8 @@ impl TileCacheInstance {
|
||||
}
|
||||
}
|
||||
|
||||
+ self.backdrop_render_rects.push(pic_coverage_rect);
|
||||
+
|
||||
// For backdrop-filter, we need to check if any of the dirty rects
|
||||
// in tiles that are affected by the filter primitive are dirty.
|
||||
self.deferred_dirty_tests.push(DeferredDirtyTest {
|
||||
@@ -3264,6 +3286,159 @@ impl TileCacheInstance {
|
||||
}
|
||||
|
||||
|
||||
+fn slice_is_dirty_in_rect(tile_cache: &TileCacheInstance, rect: &PictureRect) -> bool {
|
||||
+ tile_cache.sub_slices.iter().any(|sub_slice| {
|
||||
+ sub_slice.tiles.values().any(|tile| {
|
||||
+ tile.is_visible &&
|
||||
+ !tile.cached_surface.is_valid &&
|
||||
+ tile.cached_surface.local_dirty_rect.intersects(rect)
|
||||
+ })
|
||||
+ })
|
||||
+}
|
||||
+
|
||||
+fn invalidate_slice_rect(
|
||||
+ tile_cache: &mut TileCacheInstance,
|
||||
+ rect: &PictureRect,
|
||||
+ supports_dirty_rects: bool,
|
||||
+ feeds_cross_slice_backdrop: bool,
|
||||
+) {
|
||||
+ let mut rects = vec![*rect];
|
||||
+
|
||||
+ rects.extend(
|
||||
+ tile_cache.backdrop_render_rects
|
||||
+ .iter()
|
||||
+ .filter(|backdrop_rect| backdrop_rect.intersects(rect))
|
||||
+ .cloned()
|
||||
+ );
|
||||
+
|
||||
+ for sub_slice in &mut tile_cache.sub_slices {
|
||||
+ for tile in sub_slice.tiles.values_mut() {
|
||||
+ if !tile.is_visible {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ for rect in &rects {
|
||||
+ if !tile.cached_surface.local_rect.intersects(rect) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ tile.invalidate(Some(*rect), InvalidationReason::SurfaceContentChanged);
|
||||
+ if !supports_dirty_rects {
|
||||
+ tile.cached_surface.local_dirty_rect = tile.cached_surface.local_rect;
|
||||
+ }
|
||||
+ tile.feeds_cross_slice_backdrop |= feeds_cross_slice_backdrop;
|
||||
+ tile_cache.feeds_cross_slice_backdrop |= feeds_cross_slice_backdrop;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+pub fn update_cross_slice_backdrops(
|
||||
+ slice_ids: &[SliceId],
|
||||
+ tile_caches: &mut FastHashMap<SliceId, Box<TileCacheInstance>>,
|
||||
+ frame_context: &FrameVisibilityContext,
|
||||
+ compositor_kind: &CompositorKind,
|
||||
+) {
|
||||
+ let supports_dirty_rects = match compositor_kind {
|
||||
+ CompositorKind::Draw { .. } | CompositorKind::Layer { .. } => {
|
||||
+ frame_context.config.gpu_supports_render_target_partial_update
|
||||
+ }
|
||||
+ CompositorKind::Native { capabilities, .. } => {
|
||||
+ capabilities.max_update_rects > 0
|
||||
+ }
|
||||
+ };
|
||||
+
|
||||
+ let any_backdrop = slice_ids.iter().any(|slice_id| {
|
||||
+ let tile_cache = &tile_caches[slice_id];
|
||||
+ tile_cache.backdrop_reads_lower_slices && !tile_cache.backdrop_render_rects.is_empty()
|
||||
+ });
|
||||
+
|
||||
+ let mut pic_to_root = Vec::with_capacity(slice_ids.len());
|
||||
+ let mut moved = Vec::with_capacity(slice_ids.len());
|
||||
+
|
||||
+ for slice_id in slice_ids {
|
||||
+ let tile_cache = tile_caches.get_mut(slice_id).expect("bug: non-existent tile cache");
|
||||
+
|
||||
+ if tile_cache.feeds_cross_slice_backdrop {
|
||||
+ tile_cache.feeds_cross_slice_backdrop = false;
|
||||
+ for sub_slice in &mut tile_cache.sub_slices {
|
||||
+ for tile in sub_slice.tiles.values_mut() {
|
||||
+ tile.feeds_cross_slice_backdrop = false;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if !any_backdrop {
|
||||
+ tile_cache.prev_pic_to_root = None;
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ let transform = frame_context.spatial_tree
|
||||
+ .get_relative_transform(tile_cache.spatial_node_index, frame_context.root_spatial_node_index)
|
||||
+ .as_2d_scale_offset();
|
||||
+
|
||||
+ moved.push(transform != tile_cache.prev_pic_to_root);
|
||||
+ tile_cache.prev_pic_to_root = transform;
|
||||
+ pic_to_root.push(transform);
|
||||
+ }
|
||||
+
|
||||
+ if !any_backdrop {
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ for top in 1 .. slice_ids.len() {
|
||||
+ let top_transform = match pic_to_root[top] {
|
||||
+ Some(transform) => transform,
|
||||
+ None => continue,
|
||||
+ };
|
||||
+
|
||||
+ let backdrop_rects = {
|
||||
+ let tile_cache = &tile_caches[&slice_ids[top]];
|
||||
+ if !tile_cache.backdrop_reads_lower_slices {
|
||||
+ continue;
|
||||
+ }
|
||||
+ tile_cache.backdrop_render_rects.clone()
|
||||
+ };
|
||||
+
|
||||
+ for backdrop_rect in backdrop_rects {
|
||||
+ let root_rect: LayoutRect = top_transform.map_rect(&backdrop_rect);
|
||||
+
|
||||
+ let lower_rects: Vec<(usize, PictureRect)> = (0 .. top)
|
||||
+ .filter_map(|lower| {
|
||||
+ let rect: PictureRect = pic_to_root[lower]?.unmap_rect(&root_rect);
|
||||
+ Some((lower, rect))
|
||||
+ })
|
||||
+ .collect();
|
||||
+
|
||||
+ let needs_update = moved[top] ||
|
||||
+ slice_is_dirty_in_rect(&tile_caches[&slice_ids[top]], &backdrop_rect) ||
|
||||
+ lower_rects.iter().any(|(lower, rect)| {
|
||||
+ moved[*lower] || slice_is_dirty_in_rect(&tile_caches[&slice_ids[*lower]], rect)
|
||||
+ });
|
||||
+
|
||||
+ if !needs_update {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ invalidate_slice_rect(
|
||||
+ tile_caches.get_mut(&slice_ids[top]).unwrap(),
|
||||
+ &backdrop_rect,
|
||||
+ supports_dirty_rects,
|
||||
+ false,
|
||||
+ );
|
||||
+
|
||||
+ for (lower, rect) in lower_rects {
|
||||
+ invalidate_slice_rect(
|
||||
+ tile_caches.get_mut(&slice_ids[lower]).unwrap(),
|
||||
+ &rect,
|
||||
+ supports_dirty_rects,
|
||||
+ true,
|
||||
+ );
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
/// A SubSlice represents a potentially overlapping set of tiles within a picture cache. Most
|
||||
/// picture cache instances will have only a single sub-slice. The exception to this is when
|
||||
/// a picture cache has compositor surfaces, in which case sub slices are used to interleave
|
||||
diff --git a/gfx/wr/webrender/src/tile_cache/slice_builder.rs b/gfx/wr/webrender/src/tile_cache/slice_builder.rs
|
||||
index f81edc9..d8759bc 100644
|
||||
--- a/gfx/wr/webrender/src/tile_cache/slice_builder.rs
|
||||
+++ b/gfx/wr/webrender/src/tile_cache/slice_builder.rs
|
||||
@@ -688,6 +688,7 @@ fn create_tile_cache(
|
||||
virtual_surface_size: frame_builder_config.compositor_kind.get_virtual_surface_size(),
|
||||
image_surface_count: prim_list.image_surface_count,
|
||||
yuv_image_surface_count: prim_list.yuv_image_surface_count,
|
||||
+ backdrop_reads_lower_slices: iframe_clip.is_none() && slice > 0,
|
||||
});
|
||||
|
||||
let pic_index = prim_store.pictures.alloc().init(PictureInstance::new_image(
|
||||
@@ -1,168 +0,0 @@
|
||||
diff --git a/browser/components/sessionstore/test/browser.toml b/browser/components/sessionstore/test/browser.toml
|
||||
--- a/browser/components/sessionstore/test/browser.toml
|
||||
+++ b/browser/components/sessionstore/test/browser.toml
|
||||
@@ -14,10 +14,12 @@
|
||||
"browser_frame_history_c.html",
|
||||
"browser_frame_history_c1.html",
|
||||
"browser_frame_history_c2.html",
|
||||
"browser_formdata_format_sample.html",
|
||||
"browser_sessionHistory_slow.sjs",
|
||||
+ "browser_policy_container_sample.html",
|
||||
+ "browser_policy_container_sample_frame.html",
|
||||
"browser_scrollPositions_sample.html",
|
||||
"browser_scrollPositions_sample2.html",
|
||||
"browser_scrollPositions_sample_frameset.html",
|
||||
"browser_scrollPositions_readerModeArticle.html",
|
||||
"browser_sessionStorage.html",
|
||||
@@ -226,10 +228,12 @@
|
||||
["browser_pinned_tabs.js"]
|
||||
skip-if = [
|
||||
"ccov", # Bug 1625525
|
||||
]
|
||||
|
||||
+["browser_policy_container_not_stored.js"]
|
||||
+
|
||||
["browser_privatetabs.js"]
|
||||
|
||||
["browser_purge_domaindata.js"]
|
||||
|
||||
["browser_purge_shistory.js"]
|
||||
diff --git a/browser/components/sessionstore/test/browser_policy_container_not_stored.js b/browser/components/sessionstore/test/browser_policy_container_not_stored.js
|
||||
new file mode 100644
|
||||
--- /dev/null
|
||||
+++ b/browser/components/sessionstore/test/browser_policy_container_not_stored.js
|
||||
@@ -0,0 +1,51 @@
|
||||
+/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
+
|
||||
+"use strict";
|
||||
+
|
||||
+/**
|
||||
+ * A session history entry only needs to retain the policy container for loads
|
||||
+ * that inherit their policies (about:blank, about:srcdoc, blob:, data:, ...).
|
||||
+ * Anything fetched over the network gets its policies from the response again,
|
||||
+ * so storing them would only bloat the session store (bug 2011236).
|
||||
+ */
|
||||
+
|
||||
+const TEST_ROOT = getRootDirectory(gTestPath).replace(
|
||||
+ "chrome://mochitests/content",
|
||||
+ "https://example.com"
|
||||
+);
|
||||
+const PARENT_URL = TEST_ROOT + "browser_policy_container_sample.html";
|
||||
+const FRAME_URL = TEST_ROOT + "browser_policy_container_sample_frame.html";
|
||||
+
|
||||
+add_task(async function test_policy_container_only_for_inheriting_loads() {
|
||||
+ const tab = BrowserTestUtils.addTab(gBrowser, PARENT_URL);
|
||||
+ gBrowser.selectedTab = tab;
|
||||
+ await promiseBrowserLoaded(tab.linkedBrowser, true, PARENT_URL);
|
||||
+ await TabStateFlusher.flush(tab.linkedBrowser);
|
||||
+
|
||||
+ const state = JSON.parse(ss.getTabState(tab));
|
||||
+ const topEntry = state.entries.at(-1);
|
||||
+ is(topEntry.url, PARENT_URL, "collected the parent entry");
|
||||
+
|
||||
+ const children = topEntry.children ?? [];
|
||||
+ const networkFrame = children.find(child => child.url === FRAME_URL);
|
||||
+ const blankFrame = children.find(child => child.url === "about:blank");
|
||||
+
|
||||
+ ok(networkFrame, "collected the network-scheme subframe entry");
|
||||
+ ok(blankFrame, "collected the about:blank subframe entry");
|
||||
+
|
||||
+ // The parent has a CSP, so both subframes inherit a policy container onto
|
||||
+ // their load state. Only the one that cannot recover it from a response
|
||||
+ // should keep it in session history.
|
||||
+ ok(
|
||||
+ !("policyContainer" in networkFrame),
|
||||
+ "https subframe entry does not store a policyContainer"
|
||||
+ );
|
||||
+ ok(
|
||||
+ "policyContainer" in blankFrame,
|
||||
+ "about:blank subframe entry still stores its inherited policyContainer"
|
||||
+ );
|
||||
+
|
||||
+ await BrowserTestUtils.removeTab(tab);
|
||||
+});
|
||||
diff --git a/browser/components/sessionstore/test/browser_policy_container_sample.html b/browser/components/sessionstore/test/browser_policy_container_sample.html
|
||||
new file mode 100644
|
||||
--- /dev/null
|
||||
+++ b/browser/components/sessionstore/test/browser_policy_container_sample.html
|
||||
@@ -0,0 +1,12 @@
|
||||
+<!DOCTYPE html>
|
||||
+<html>
|
||||
+<head>
|
||||
+ <meta charset="utf-8">
|
||||
+ <meta http-equiv="Content-Security-Policy" content="default-src * data: blob: 'unsafe-inline'">
|
||||
+ <title>policyContainer session store sample</title>
|
||||
+</head>
|
||||
+<body>
|
||||
+ <iframe id="network" src="browser_policy_container_sample_frame.html"></iframe>
|
||||
+ <iframe id="blank" src="about:blank"></iframe>
|
||||
+</body>
|
||||
+</html>
|
||||
diff --git a/browser/components/sessionstore/test/browser_policy_container_sample_frame.html b/browser/components/sessionstore/test/browser_policy_container_sample_frame.html
|
||||
new file mode 100644
|
||||
--- /dev/null
|
||||
+++ b/browser/components/sessionstore/test/browser_policy_container_sample_frame.html
|
||||
@@ -0,0 +1,10 @@
|
||||
+<!DOCTYPE html>
|
||||
+<html>
|
||||
+<head>
|
||||
+ <meta charset="utf-8">
|
||||
+ <title>policyContainer session store sample frame</title>
|
||||
+</head>
|
||||
+<body>
|
||||
+ frame
|
||||
+</body>
|
||||
+</html>
|
||||
diff --git a/docshell/shistory/SessionHistoryEntry.cpp b/docshell/shistory/SessionHistoryEntry.cpp
|
||||
--- a/docshell/shistory/SessionHistoryEntry.cpp
|
||||
+++ b/docshell/shistory/SessionHistoryEntry.cpp
|
||||
@@ -41,10 +41,18 @@
|
||||
extern mozilla::LazyLogModule gSHLog;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
+// Only store policy container for loads that can't carry it themselves
|
||||
+// (about:blank, about:srcdoc, blob:, data:, ...)
|
||||
+// bug 1867137, bug 2011236
|
||||
+static nsIPolicyContainer* PolicyContainerToStore(
|
||||
+ nsIURI* aURI, nsIPolicyContainer* aPolicyContainer) {
|
||||
+ return CSP_ShouldURIInheritCSP(aURI) ? aPolicyContainer : nullptr;
|
||||
+}
|
||||
+
|
||||
SessionHistoryInfo::SessionHistoryInfo(nsDocShellLoadState* aLoadState,
|
||||
nsIChannel* aChannel)
|
||||
: mURI(aLoadState->URI()),
|
||||
mOriginalURI(aLoadState->OriginalURI()),
|
||||
mResultPrincipalURI(aLoadState->ResultPrincipalURI()),
|
||||
@@ -59,11 +67,12 @@
|
||||
mLoadReplace(aLoadState->LoadReplace()),
|
||||
mHasUserActivation(aLoadState->HasValidUserGestureActivation()),
|
||||
mSharedState(SharedState::Create(
|
||||
aLoadState->TriggeringPrincipal(), aLoadState->PrincipalToInherit(),
|
||||
aLoadState->PartitionedPrincipalToInherit(),
|
||||
- aLoadState->PolicyContainer(),
|
||||
+ PolicyContainerToStore(aLoadState->URI(),
|
||||
+ 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(
|
||||
getter_AddRefs(mSharedState.Get()->mPrincipalToInherit));
|
||||
|
||||
mSharedState.Get()->mPartitionedPrincipalToInherit =
|
||||
aPartitionedPrincipalToInherit;
|
||||
- mSharedState.Get()->mPolicyContainer = aPolicyContainer;
|
||||
+ mSharedState.Get()->mPolicyContainer =
|
||||
+ PolicyContainerToStore(mURI, aPolicyContainer);
|
||||
aChannel->GetContentType(mSharedState.Get()->mContentType);
|
||||
aChannel->GetOriginalURI(getter_AddRefs(mOriginalURI));
|
||||
|
||||
uint32_t loadFlags;
|
||||
aChannel->GetLoadFlags(&loadFlags);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,558 +0,0 @@
|
||||
diff --git a/gfx/wr/webrender/res/ps_quad_mask.glsl b/gfx/wr/webrender/res/ps_quad_mask.glsl
|
||||
--- a/gfx/wr/webrender/res/ps_quad_mask.glsl
|
||||
+++ b/gfx/wr/webrender/res/ps_quad_mask.glsl
|
||||
@@ -19,12 +19,14 @@
|
||||
// We pack 4 vec3 clip planes into 3 vec4 to save a varying slot.
|
||||
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;
|
||||
+#endif
|
||||
|
||||
#endif
|
||||
flat varying highp vec2 vClipMode;
|
||||
|
||||
#ifdef WR_VERTEX_SHADER
|
||||
@@ -40,12 +42,14 @@
|
||||
vec4 radii;
|
||||
#else
|
||||
vec4 radii_top;
|
||||
vec4 radii_bottom;
|
||||
|
||||
+#ifdef WR_FEATURE_SUPERELLIPSE
|
||||
vec4 shape;
|
||||
vec4 inset;
|
||||
+#endif
|
||||
#endif
|
||||
float mode;
|
||||
int space;
|
||||
};
|
||||
|
||||
@@ -58,22 +62,29 @@
|
||||
vec4 texels[3] = fetch_from_gpu_buffer_3f(index);
|
||||
clip.rect = RectWithEndpoint(texels[0].xy, texels[0].zw);
|
||||
clip.radii = texels[1];
|
||||
clip.mode = texels[2].x;
|
||||
#else
|
||||
+#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];
|
||||
+#endif
|
||||
#endif
|
||||
|
||||
return clip;
|
||||
}
|
||||
|
||||
+#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 {
|
||||
@@ -85,10 +96,11 @@
|
||||
radii = offset_radii.zw;
|
||||
}
|
||||
return vec4(center, inverse_radii(radii));
|
||||
}
|
||||
}
|
||||
+#endif
|
||||
|
||||
void pattern_vertex(PrimitiveInfo prim_info) {
|
||||
|
||||
Clip clip = fetch_clip(aClipData.y);
|
||||
Transform clip_transform = fetch_transform(aClipData.x);
|
||||
@@ -121,10 +133,11 @@
|
||||
vec2 r_tl = clip.radii_top.xy;
|
||||
vec2 r_tr = clip.radii_top.zw;
|
||||
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);
|
||||
@@ -146,10 +159,25 @@
|
||||
clip.rect.p1.y - r_bl.y),
|
||||
r_bl,
|
||||
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
|
||||
// and radius. We compute a point that falls on the diagonal (which is just
|
||||
// an inner vertex pushed out along one axis, but not on both) to get the
|
||||
// plane offset of the half-space. We also compute the direction vector of
|
||||
@@ -170,13 +198,15 @@
|
||||
|
||||
vClipPlane_A = vec4(tl.x, tl.y, tl.z, tr.x);
|
||||
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);
|
||||
#endif
|
||||
+#endif
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WR_FRAGMENT_SHADER
|
||||
@@ -205,11 +235,13 @@
|
||||
vec3 plane_tr = vec3(vClipPlane_A.w, vClipPlane_B.x, vClipPlane_B.y);
|
||||
vec3 plane_br = vec3(vClipPlane_B.z, vClipPlane_B.w, vClipPlane_C.x);
|
||||
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,
|
||||
vClipCenter_Radius_TL,
|
||||
plane_tr,
|
||||
@@ -218,10 +250,11 @@
|
||||
vClipCenter_Radius_BR,
|
||||
plane_bl,
|
||||
vClipCenter_Radius_BL,
|
||||
vTransformBounds
|
||||
);
|
||||
+#ifdef WR_FEATURE_SUPERELLIPSE
|
||||
} else {
|
||||
dist = distance_to_shaped_rect(
|
||||
clip_local_pos,
|
||||
vClipCenter_Radius_TL,
|
||||
vClipCenter_Radius_TR,
|
||||
@@ -230,10 +263,11 @@
|
||||
vTransformBounds,
|
||||
vClipShape,
|
||||
vClipClampedInset
|
||||
);
|
||||
}
|
||||
+#endif
|
||||
#endif
|
||||
|
||||
// Compute AA for the given dist and range.
|
||||
float alpha = distance_aa(aa_range, dist);
|
||||
|
||||
@@ -340,13 +374,13 @@
|
||||
#define AA_RECT(local_pos) \
|
||||
sd_round_box(local_pos, v_clip_size, v_clip_radii)
|
||||
#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);
|
||||
vec3 plane_tr = vec3(vClipPlane_A.w, vClipPlane_B.x, vClipPlane_B.y);
|
||||
vec3 plane_br = vec3(vClipPlane_B.z, vClipPlane_B.w, vClipPlane_C.x);
|
||||
diff --git a/gfx/wr/webrender/src/batch.rs b/gfx/wr/webrender/src/batch.rs
|
||||
--- a/gfx/wr/webrender/src/batch.rs
|
||||
+++ b/gfx/wr/webrender/src/batch.rs
|
||||
@@ -1224,25 +1224,29 @@
|
||||
/// A list of clip instances to be drawn into a target.
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
pub struct ClipMaskInstanceList {
|
||||
pub mask_instances_fast: FrameVec<MaskInstance>,
|
||||
+ pub mask_instances_superellipse: FrameVec<MaskInstance>,
|
||||
pub mask_instances_slow: FrameVec<MaskInstance>,
|
||||
|
||||
pub mask_instances_fast_with_scissor: FastHashMap<DeviceIntRect, FrameVec<MaskInstance>>,
|
||||
+ pub mask_instances_superellipse_with_scissor: FastHashMap<DeviceIntRect, FrameVec<MaskInstance>>,
|
||||
pub mask_instances_slow_with_scissor: FastHashMap<DeviceIntRect, FrameVec<MaskInstance>>,
|
||||
|
||||
pub image_mask_instances: FastHashMap<TextureSource, FrameVec<PrimitiveInstanceData>>,
|
||||
pub image_mask_instances_with_scissor: FastHashMap<(DeviceIntRect, TextureSource), FrameVec<PrimitiveInstanceData>>,
|
||||
}
|
||||
|
||||
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(),
|
||||
}
|
||||
}
|
||||
@@ -1250,20 +1254,24 @@
|
||||
pub fn is_empty(&self) -> bool {
|
||||
// Destructure self to make sure we don't forget to update this method if
|
||||
// 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/quad.rs b/gfx/wr/webrender/src/quad.rs
|
||||
--- a/gfx/wr/webrender/src/quad.rs
|
||||
+++ b/gfx/wr/webrender/src/quad.rs
|
||||
@@ -1807,11 +1807,11 @@
|
||||
gpu_buffer: &mut GpuBufferBuilderF,
|
||||
clip_rect: LayoutRect,
|
||||
radius: &BorderRadius,
|
||||
inset: LayoutSideOffsets,
|
||||
mode: ClipMode,
|
||||
-) -> (GpuBufferAddress, bool) {
|
||||
+) -> (GpuBufferAddress, bool, bool) {
|
||||
let radius = clamped_radius(radius, clip_rect.size());
|
||||
|
||||
if radius.can_use_fast_path_in(&clip_rect) {
|
||||
let mut writer = gpu_buffer.write_blocks(3);
|
||||
writer.push_one(clip_rect);
|
||||
@@ -1821,13 +1821,15 @@
|
||||
radius.bottom_left.width,
|
||||
radius.top_left.width,
|
||||
]);
|
||||
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(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,
|
||||
radius.top_left.height,
|
||||
radius.top_right.width,
|
||||
@@ -1838,19 +1840,21 @@
|
||||
radius.bottom_left.height,
|
||||
radius.bottom_right.width,
|
||||
radius.bottom_right.height,
|
||||
]);
|
||||
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, superellipse)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn prepare_clip_task(
|
||||
clip_instance: &ClipNodeInstance,
|
||||
@@ -1865,11 +1869,11 @@
|
||||
gpu_buffer: &mut GpuBufferBuilderF,
|
||||
transforms: &mut TransformPalette,
|
||||
rg_builder: &mut RenderTaskGraphBuilder,
|
||||
sub_tasks: &mut SubTaskRange,
|
||||
) {
|
||||
- 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,
|
||||
@@ -1882,11 +1886,11 @@
|
||||
writer.push_one(clip_instance.clip_rect);
|
||||
writer.push_one([0.0, 0.0, 0.0, 0.0]);
|
||||
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(
|
||||
clip_instance.spatial_node_index,
|
||||
raster_spatial_node_index,
|
||||
@@ -2033,10 +2037,11 @@
|
||||
clip_transform_id,
|
||||
quad_flags,
|
||||
clip_space,
|
||||
needs_scissor_rect,
|
||||
rounded_rect_fast_path: fast_path,
|
||||
+ rounded_rect_superellipse: superellipse,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
fn create_quad_primitive(
|
||||
diff --git a/gfx/wr/webrender/src/render_target.rs b/gfx/wr/webrender/src/render_target.rs
|
||||
--- a/gfx/wr/webrender/src/render_target.rs
|
||||
+++ b/gfx/wr/webrender/src/render_target.rs
|
||||
@@ -441,11 +441,11 @@
|
||||
// so the target must be initialized to one.
|
||||
self.clears.push((target_rect, ColorF::WHITE));
|
||||
|
||||
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,
|
||||
@@ -495,10 +495,12 @@
|
||||
unused: 0,
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -925,19 +927,26 @@
|
||||
if task.rounded_rect_fast_path {
|
||||
results.mask_instances_fast_with_scissor
|
||||
.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)
|
||||
.or_insert_with(|| memory.new_vec())
|
||||
.push(instance);
|
||||
}
|
||||
} 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
|
||||
--- a/gfx/wr/webrender/src/render_task.rs
|
||||
+++ b/gfx/wr/webrender/src/render_task.rs
|
||||
@@ -2240,10 +2240,11 @@
|
||||
/// Transform from the quad primitive's space to the clip's local space.
|
||||
pub clip_transform_id: GpuTransformId,
|
||||
pub quad_flags: QuadFlags,
|
||||
pub needs_scissor_rect: bool,
|
||||
pub rounded_rect_fast_path: bool,
|
||||
+ pub rounded_rect_superellipse: bool,
|
||||
}
|
||||
|
||||
/// An clip applied to a render task using the multiply blend mode on top of
|
||||
/// the content being clipped.
|
||||
#[derive(Debug)]
|
||||
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
|
||||
@@ -2450,10 +2450,54 @@
|
||||
}
|
||||
|
||||
self.device.disable_scissor();
|
||||
}
|
||||
|
||||
+ 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,
|
||||
projection,
|
||||
None,
|
||||
diff --git a/gfx/wr/webrender/src/renderer/shade.rs b/gfx/wr/webrender/src/renderer/shade.rs
|
||||
--- a/gfx/wr/webrender/src/renderer/shade.rs
|
||||
+++ b/gfx/wr/webrender/src/renderer/shade.rs
|
||||
@@ -65,10 +65,11 @@
|
||||
];
|
||||
|
||||
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,
|
||||
Cache(VertexArrayKind),
|
||||
Text,
|
||||
@@ -449,10 +450,11 @@
|
||||
ps_quad_backdrop: ShaderHandle,
|
||||
ps_quad_blend: ShaderHandle,
|
||||
ps_quad_mix_blend: ShaderHandle,
|
||||
ps_mask: ShaderHandle,
|
||||
ps_mask_fast: ShaderHandle,
|
||||
+ ps_mask_superellipse: ShaderHandle,
|
||||
ps_clear: ShaderHandle,
|
||||
ps_copy: ShaderHandle,
|
||||
|
||||
composite: CompositorShaders,
|
||||
}
|
||||
@@ -514,10 +516,17 @@
|
||||
"ps_quad_mask",
|
||||
&[FAST_PATH_FEATURE],
|
||||
&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.
|
||||
for _ in 0 .. scale_shader_num {
|
||||
cs_scale.push(None);
|
||||
@@ -786,10 +795,11 @@
|
||||
ps_quad_backdrop,
|
||||
ps_quad_blend,
|
||||
ps_quad_mix_blend,
|
||||
ps_mask,
|
||||
ps_mask_fast,
|
||||
+ ps_mask_superellipse,
|
||||
ps_split_composite,
|
||||
ps_clear,
|
||||
ps_copy,
|
||||
composite,
|
||||
})
|
||||
@@ -963,10 +973,11 @@
|
||||
pub fn ps_quad_textured(&mut self) -> &mut LazilyCompiledShader {
|
||||
self.loader.get(self.ps_quad_textured)
|
||||
}
|
||||
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) }
|
||||
|
||||
pub fn deinit(self, device: &mut Device) {
|
||||
self.loader.deinit(device);
|
||||
diff --git a/gfx/wr/webrender_build/src/shader_features.rs b/gfx/wr/webrender_build/src/shader_features.rs
|
||||
--- a/gfx/wr/webrender_build/src/shader_features.rs
|
||||
+++ b/gfx/wr/webrender_build/src/shader_features.rs
|
||||
@@ -65,11 +65,11 @@
|
||||
let mut shaders = ShaderFeatures::new();
|
||||
|
||||
// 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",
|
||||
|
||||
@@ -1,307 +0,0 @@
|
||||
diff --git a/gfx/wr/webrender/res/ps_quad_box_shadow.glsl b/gfx/wr/webrender/res/ps_quad_box_shadow.glsl
|
||||
--- a/gfx/wr/webrender/res/ps_quad_box_shadow.glsl
|
||||
+++ b/gfx/wr/webrender/res/ps_quad_box_shadow.glsl
|
||||
@@ -49,22 +49,26 @@
|
||||
flat varying highp vec4 vElemCenter_Radius_TL;
|
||||
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
|
||||
|
||||
void pattern_vertex(PrimitiveInfo info) {
|
||||
vec4 data0 = fetch_from_gpu_buffer_1f(info.pattern_input.x);
|
||||
vec4 data1 = fetch_from_gpu_buffer_1f(info.pattern_input.x + 1);
|
||||
vec4 data2 = fetch_from_gpu_buffer_1f(info.pattern_input.x + 2);
|
||||
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;
|
||||
vec2 dest_rect_off = data1.xy;
|
||||
v_uv_scale_inset = vec4(vec2(1.0) / alloc_size, data1.z, 0.0);
|
||||
@@ -84,11 +88,11 @@
|
||||
// Map nine-patch UV=1.0 to the true content edge (uv_p0 + content_device_size)
|
||||
// rather than the rounded atlas entry edge (info.segment.uv_rect.p1). The atlas
|
||||
// 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;
|
||||
v_uv_rect = vec4(uv_p0, uv_p1) / texture_size.xyxy;
|
||||
v_uv_bounds = vec4(
|
||||
@@ -110,11 +114,13 @@
|
||||
vElemCenter_Radius_TL = vec4(elem_p0 + r_tl, r_tl);
|
||||
vElemCenter_Radius_TR = vec4(elem_p1.x - r_tr.x, elem_p0.y + r_tr.y, r_tr);
|
||||
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
|
||||
|
||||
#ifdef WR_FRAGMENT_SHADER
|
||||
@@ -170,22 +176,24 @@
|
||||
vec3 elem_plane_bl = vec3(n_bl, dot(n_bl, vec2(c_bl.x, c_bl.y + r_bl.y)));
|
||||
|
||||
// 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)),
|
||||
elem_plane_tr, vec4(c_tr, inverse_radii_squared(r_tr)),
|
||||
elem_plane_br, vec4(c_br, inverse_radii_squared(r_br)),
|
||||
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,
|
||||
vec4(c_tl, elem_shape.x == 1.0 ? inverse_radii_squared(r_tl) : inverse_radii(r_tl)),
|
||||
vec4(c_tr, elem_shape.y == 1.0 ? inverse_radii_squared(r_tr) : inverse_radii(r_tr)),
|
||||
@@ -194,10 +202,11 @@
|
||||
elem_bounds,
|
||||
elem_shape,
|
||||
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.
|
||||
float element_clip = distance_aa(aa_range, mix(-elem_dist, elem_dist, inset));
|
||||
|
||||
diff --git a/gfx/wr/webrender/src/pattern/box_shadow.rs b/gfx/wr/webrender/src/pattern/box_shadow.rs
|
||||
--- a/gfx/wr/webrender/src/pattern/box_shadow.rs
|
||||
+++ b/gfx/wr/webrender/src/pattern/box_shadow.rs
|
||||
@@ -41,11 +41,14 @@
|
||||
_sub_rect: Option<DeviceRect>,
|
||||
_offset: LayoutVector2D,
|
||||
_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 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,
|
||||
self.dest_rect_size.width,
|
||||
self.dest_rect_size.height,
|
||||
@@ -72,26 +75,28 @@
|
||||
self.element_radius.bottom_right.width,
|
||||
self.element_radius.bottom_right.height,
|
||||
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();
|
||||
|
||||
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,
|
||||
is_opaque: false,
|
||||
blend_mode: BlendMode::PremultipliedAlpha,
|
||||
diff --git a/gfx/wr/webrender/src/pattern/mod.rs b/gfx/wr/webrender/src/pattern/mod.rs
|
||||
--- a/gfx/wr/webrender/src/pattern/mod.rs
|
||||
+++ b/gfx/wr/webrender/src/pattern/mod.rs
|
||||
@@ -29,36 +29,37 @@
|
||||
Gradient = 1,
|
||||
Repeat = 2,
|
||||
|
||||
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.
|
||||
}
|
||||
|
||||
-pub const NUM_PATTERNS: u32 = 15;
|
||||
+pub const NUM_PATTERNS: u32 = 16;
|
||||
|
||||
impl PatternKind {
|
||||
pub fn from_u32(val: u32) -> Self {
|
||||
assert!(val < NUM_PATTERNS);
|
||||
unsafe { std::mem::transmute(val) }
|
||||
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
|
||||
@@ -251,10 +251,11 @@
|
||||
BatchKind::Quad(PatternKind::TextureExternalBT709) => GPU_TAG_PRIMITIVE,
|
||||
BatchKind::Quad(PatternKind::TextureRect) => GPU_TAG_PRIMITIVE,
|
||||
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,
|
||||
BatchKind::Quad(PatternKind::YuvTextureRect) => GPU_TAG_BRUSH_YUV_IMAGE,
|
||||
BatchKind::Quad(PatternKind::Backdrop) => GPU_TAG_PRIMITIVE,
|
||||
diff --git a/gfx/wr/webrender/src/renderer/shade.rs b/gfx/wr/webrender/src/renderer/shade.rs
|
||||
--- a/gfx/wr/webrender/src/renderer/shade.rs
|
||||
+++ b/gfx/wr/webrender/src/renderer/shade.rs
|
||||
@@ -438,10 +438,11 @@
|
||||
ps_quad_textured_external_bt709: Option<ShaderHandle>,
|
||||
ps_quad_textured_rect: Option<ShaderHandle>,
|
||||
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.
|
||||
ps_quad_yuv: ShaderHandle,
|
||||
ps_quad_yuv_external: Option<ShaderHandle>,
|
||||
@@ -651,10 +652,17 @@
|
||||
"ps_quad_box_shadow",
|
||||
&[],
|
||||
&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",
|
||||
&["TEXTURE_2D"],
|
||||
&shader_list,
|
||||
@@ -786,10 +794,11 @@
|
||||
ps_quad_textured_external_bt709,
|
||||
ps_quad_textured_rect,
|
||||
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,
|
||||
ps_quad_yuv_rect,
|
||||
ps_quad_backdrop,
|
||||
@@ -867,10 +876,11 @@
|
||||
PatternKind::TextureRect => self.ps_quad_textured_rect
|
||||
.expect("bug: ps_quad_textured TEXTURE_RECT variant not loaded"),
|
||||
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"),
|
||||
PatternKind::YuvTextureExternalBT709 => self.ps_quad_yuv_external_bt709
|
||||
.expect("bug: ps_quad_yuv TEXTURE_EXTERNAL_BT709 variant not loaded"),
|
||||
@@ -923,10 +933,13 @@
|
||||
self.ps_quad_repeat
|
||||
}
|
||||
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
|
||||
}
|
||||
BatchKind::Quad(PatternKind::YuvTextureExternal) => {
|
||||
self.ps_quad_yuv_external
|
||||
diff --git a/gfx/wr/webrender_build/src/shader_features.rs b/gfx/wr/webrender_build/src/shader_features.rs
|
||||
--- a/gfx/wr/webrender_build/src/shader_features.rs
|
||||
+++ b/gfx/wr/webrender_build/src/shader_features.rs
|
||||
@@ -166,11 +166,11 @@
|
||||
}
|
||||
shaders.insert("ps_quad_textured", ps_quad_textured_features);
|
||||
|
||||
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.
|
||||
let mut ps_quad_yuv_features: Vec<String> = vec!["TEXTURE_2D".to_string()];
|
||||
if flags.contains(ShaderFeatureFlags::GL) {
|
||||
|
||||
@@ -1,560 +0,0 @@
|
||||
diff --git a/gfx/wr/webrender/res/border_shared.glsl b/gfx/wr/webrender/res/border_shared.glsl
|
||||
--- a/gfx/wr/webrender/res/border_shared.glsl
|
||||
+++ b/gfx/wr/webrender/res/border_shared.glsl
|
||||
@@ -27,25 +27,33 @@
|
||||
vec4 rect;
|
||||
vec4 color0;
|
||||
vec4 color1;
|
||||
vec2 widths;
|
||||
vec2 radii;
|
||||
+#ifdef WR_FEATURE_SUPERELLIPSE
|
||||
float shape;
|
||||
vec2 shape_offset;
|
||||
vec2 inset;
|
||||
+#endif
|
||||
};
|
||||
|
||||
BorderInstanceGpuData fetch_gpu_data(int index) {
|
||||
BorderInstanceGpuData data;
|
||||
|
||||
+#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;
|
||||
+#endif
|
||||
|
||||
return data;
|
||||
}
|
||||
diff --git a/gfx/wr/webrender/res/cs_border_segment.glsl b/gfx/wr/webrender/res/cs_border_segment.glsl
|
||||
--- a/gfx/wr/webrender/res/cs_border_segment.glsl
|
||||
+++ b/gfx/wr/webrender/res/cs_border_segment.glsl
|
||||
@@ -30,11 +30,13 @@
|
||||
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;
|
||||
|
||||
// Stores widths/2 and widths/3 to save doing this in FS.
|
||||
@@ -192,26 +194,34 @@
|
||||
vColor00 = color0[0];
|
||||
vColor01 = color0[1];
|
||||
vec4[2] color1 = get_colors_for_side(data.color1, style1);
|
||||
vColor10 = color1[0];
|
||||
vColor11 = color1[1];
|
||||
+#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;
|
||||
vec4 contour1 = compute_contoured_superellipse(reference_radii, data.shape, data.inset);
|
||||
vec4 contour2 = compute_contoured_superellipse(reference_radii, data.shape, data.inset + data.widths);
|
||||
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) {
|
||||
float radius = aClipParams1.z;
|
||||
@@ -258,19 +268,22 @@
|
||||
// also 0.67 of the radii. Use these to form a
|
||||
// SDF subtraction which will clip out the inside
|
||||
// third of the rounded edge.
|
||||
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
|
||||
);
|
||||
d_radii_b = distance_to_ellipse(
|
||||
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),
|
||||
mix(clip_radii.xy, clip_radii.zw, 1.0 / 3.0),
|
||||
data.shape
|
||||
@@ -286,24 +299,28 @@
|
||||
d_radii_a = max(d_radii_a, -min(included_region_a.x, included_region_a.y));
|
||||
|
||||
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);
|
||||
|
||||
color0 *= distance_aa(aa_range, d);
|
||||
break;
|
||||
}
|
||||
case BORDER_STYLE_GROOVE:
|
||||
case BORDER_STYLE_RIDGE: {
|
||||
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),
|
||||
mix(clip_radii.xy, clip_radii.zw, 0.5),
|
||||
data.shape
|
||||
@@ -311,10 +328,11 @@
|
||||
|
||||
// Clamp to the middle of the adjacent borders
|
||||
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) {
|
||||
case SEGMENT_TOP_LEFT: swizzled_factor = 0.0; break;
|
||||
case SEGMENT_TOP_RIGHT: swizzled_factor = mix_factor; break;
|
||||
@@ -435,22 +453,26 @@
|
||||
|
||||
if (in_clip_region) {
|
||||
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);
|
||||
d_radii_b = distance_to_superellipse(clip_relative_pos - vClipOffsets.zw, vClipRadii.zw, data.shape);
|
||||
|
||||
// exclude the straight border part from the subtracted region
|
||||
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);
|
||||
|
||||
color0 = evaluate_color_for_style_in_corner(
|
||||
diff --git a/gfx/wr/webrender/res/cs_border_solid.glsl b/gfx/wr/webrender/res/cs_border_solid.glsl
|
||||
--- a/gfx/wr/webrender/res/cs_border_solid.glsl
|
||||
+++ b/gfx/wr/webrender/res/cs_border_solid.glsl
|
||||
@@ -31,11 +31,13 @@
|
||||
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;
|
||||
flat varying highp vec2 vHorizontalClipRadii;
|
||||
flat varying highp vec4 vVerticalClipCenter_Sign;
|
||||
@@ -74,11 +76,11 @@
|
||||
void main(void) {
|
||||
BorderInstanceGpuData data = fetch_gpu_data(aGpuDataAddress);
|
||||
vGpuDataAddress.x = aGpuDataAddress;
|
||||
|
||||
int segment = aFlags & 0xff;
|
||||
- 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;
|
||||
vec2 outer = outer_scale * size;
|
||||
vec2 clip_sign = 1.0 - 2.0 * outer_scale;
|
||||
@@ -100,23 +102,31 @@
|
||||
vMixColors.x = mix_colors;
|
||||
vPos = size * aPosition.xy;
|
||||
|
||||
vColor0 = data.color0;
|
||||
vColor1 = data.color1;
|
||||
+#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;
|
||||
vec4 contour1 = compute_contoured_superellipse(reference_radii, data.shape, data.inset);
|
||||
vec4 contour2 = compute_contoured_superellipse(reference_radii, data.shape, data.inset + data.widths);
|
||||
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,
|
||||
horizontal_clip_sign);
|
||||
@@ -156,22 +166,26 @@
|
||||
float d = -1.0;
|
||||
if (in_clip_region) {
|
||||
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);
|
||||
d_radii_b = distance_to_superellipse(clip_relative_pos - vClipOffsets.zw, vClipRadii.zw, data.shape);
|
||||
|
||||
// exclude the straight border part from the subtracted region
|
||||
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);
|
||||
}
|
||||
|
||||
// And again for horizontally-adjacent corner
|
||||
diff --git a/gfx/wr/webrender/src/border.rs b/gfx/wr/webrender/src/border.rs
|
||||
--- a/gfx/wr/webrender/src/border.rs
|
||||
+++ b/gfx/wr/webrender/src/border.rs
|
||||
@@ -855,14 +855,17 @@
|
||||
h_adjacent_corner_radius: DeviceSize,
|
||||
v_adjacent_corner_outer: DevicePoint,
|
||||
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 instance_gpu_data = BorderInstanceGpuData {
|
||||
local_rect: task_rect,
|
||||
color0: color0.premultiplied(),
|
||||
color1: color1.premultiplied(),
|
||||
@@ -875,11 +878,11 @@
|
||||
|
||||
let base_instance = BorderInstance {
|
||||
task_origin: DevicePoint::zero(),
|
||||
flags: base_flags,
|
||||
clip_params: [0.0; 8],
|
||||
- gpu_data_address: instance_gpu_data.write(gpu_buffer_builder)
|
||||
+ gpu_data_address: instance_gpu_data.write(superellipse, gpu_buffer_builder)
|
||||
};
|
||||
|
||||
match segment {
|
||||
BorderSegment::TopLeft |
|
||||
BorderSegment::TopRight |
|
||||
diff --git a/gfx/wr/webrender/src/gpu_types.rs b/gfx/wr/webrender/src/gpu_types.rs
|
||||
--- a/gfx/wr/webrender/src/gpu_types.rs
|
||||
+++ b/gfx/wr/webrender/src/gpu_types.rs
|
||||
@@ -192,18 +192,21 @@
|
||||
pub shape_offset: DeviceSize,
|
||||
pub inset: DeviceSize,
|
||||
}
|
||||
|
||||
impl BorderInstanceGpuData {
|
||||
- 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, 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/render_target.rs b/gfx/wr/webrender/src/render_target.rs
|
||||
--- a/gfx/wr/webrender/src/render_target.rs
|
||||
+++ b/gfx/wr/webrender/src/render_target.rs
|
||||
@@ -33,10 +33,11 @@
|
||||
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)]
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
@@ -163,10 +164,12 @@
|
||||
|
||||
pub clip_masks: ClipMaskInstanceList,
|
||||
|
||||
pub border_segments_complex: FrameVec<BorderInstance>,
|
||||
pub border_segments_solid: FrameVec<BorderInstance>,
|
||||
+ pub border_segments_complex_superellipse: FrameVec<BorderInstance>,
|
||||
+ pub border_segments_solid_superellipse: FrameVec<BorderInstance>,
|
||||
pub line_decorations: FrameVec<LineDecorationJob>,
|
||||
|
||||
// Clearing render targets has a fair amount of special cases.
|
||||
// The general rules are:
|
||||
// - Depth (for at least the used potion of the target) is always cleared if it
|
||||
@@ -223,10 +226,12 @@
|
||||
prim_instances: std::array::from_fn(|_| FastHashMap::default()),
|
||||
prim_instances_with_scissor: FastHashMap::default(),
|
||||
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(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -547,14 +552,23 @@
|
||||
let instances = task_info.instances.clone();
|
||||
for mut instance in instances {
|
||||
// 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);
|
||||
+ }
|
||||
}
|
||||
}
|
||||
}
|
||||
RenderTaskKind::Image(..) |
|
||||
RenderTaskKind::Cached(..) |
|
||||
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
|
||||
@@ -3394,11 +3394,13 @@
|
||||
draw_target,
|
||||
);
|
||||
|
||||
// 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);
|
||||
|
||||
self.set_blend(true, FramebufferKind::Other);
|
||||
self.set_blend_mode_premultiplied_alpha(FramebufferKind::Other);
|
||||
@@ -3437,10 +3439,46 @@
|
||||
&BatchTextures::empty(),
|
||||
stats,
|
||||
);
|
||||
}
|
||||
|
||||
+ 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);
|
||||
}
|
||||
|
||||
// Draw any line decorations for this target.
|
||||
if !target.line_decorations.is_empty() {
|
||||
diff --git a/gfx/wr/webrender/src/renderer/shade.rs b/gfx/wr/webrender/src/renderer/shade.rs
|
||||
--- a/gfx/wr/webrender/src/renderer/shade.rs
|
||||
+++ b/gfx/wr/webrender/src/renderer/shade.rs
|
||||
@@ -413,10 +413,12 @@
|
||||
// draw intermediate results to cache targets. The results
|
||||
// of these shaders are then used by the primitive 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<Option<ShaderHandle>>,
|
||||
cs_line_decoration: ShaderHandle,
|
||||
cs_svg_filter_node: ShaderHandle,
|
||||
|
||||
// The are "primitive shaders". These shaders draw and blend
|
||||
@@ -774,19 +776,35 @@
|
||||
"cs_border_solid",
|
||||
&[],
|
||||
&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 {
|
||||
loader,
|
||||
|
||||
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,
|
||||
ps_text_run_dual_source,
|
||||
ps_quad_textured,
|
||||
@@ -979,10 +997,12 @@
|
||||
}
|
||||
|
||||
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 {
|
||||
self.loader.get(self.ps_quad_textured)
|
||||
}
|
||||
diff --git a/gfx/wr/webrender_build/src/shader_features.rs b/gfx/wr/webrender_build/src/shader_features.rs
|
||||
--- a/gfx/wr/webrender_build/src/shader_features.rs
|
||||
+++ b/gfx/wr/webrender_build/src/shader_features.rs
|
||||
@@ -69,17 +69,22 @@
|
||||
|
||||
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()]);
|
||||
}
|
||||
|
||||
+ 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"];
|
||||
if flags.contains(ShaderFeatureFlags::GL) {
|
||||
texture_types.push("TEXTURE_RECT");
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
diff --git a/devtools/client/responsive/test/browser/browser.toml b/devtools/client/responsive/test/browser/browser.toml
|
||||
--- a/devtools/client/responsive/test/browser/browser.toml
|
||||
+++ b/devtools/client/responsive/test/browser/browser.toml
|
||||
@@ -275,7 +275,10 @@
|
||||
skip-if = [
|
||||
"os == 'win' && os_version == '11.26100' && arch == 'x86_64' && asan", # Bug 2016837
|
||||
]
|
||||
|
||||
["browser_window_sizing.js"]
|
||||
+skip-if = [
|
||||
+ "os == 'win' && opt && standalone", # Bug 2063169
|
||||
+]
|
||||
|
||||
["browser_zoom.js"]
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
diff --git a/layout/painting/nsDisplayList.cpp b/layout/painting/nsDisplayList.cpp
|
||||
--- a/layout/painting/nsDisplayList.cpp
|
||||
+++ b/layout/painting/nsDisplayList.cpp
|
||||
@@ -4606,12 +4606,12 @@
|
||||
wr::LayoutRect deviceClipRect = wr::ToLayoutRect(clipRect);
|
||||
|
||||
nscoord spread = shadow.spread.ToAppUnits();
|
||||
float spreadRadius = float(spread) / float(appUnitsPerDevPixel);
|
||||
|
||||
- wr::BorderRadius borderRadius{};
|
||||
- wr::BorderRadius shadowRadius{};
|
||||
+ wr::BorderRadius borderRadius = wr::EmptyBorderRadius();
|
||||
+ wr::BorderRadius shadowRadius = wr::EmptyBorderRadius();
|
||||
if (hasBorderRadius) {
|
||||
borderRadius = wr::ToBorderRadius(borderRadii);
|
||||
if (spreadRadius) {
|
||||
auto shadowRadii = borderRadii;
|
||||
shadowRadii.AdjustOutwards(
|
||||
@@ -4727,12 +4727,12 @@
|
||||
float(shadow.base.blur.ToAppUnits()) / float(appUnitsPerDevPixel);
|
||||
|
||||
nscoord spread = shadow.spread.ToAppUnits();
|
||||
float spreadRadius = spread / float(appUnitsPerDevPixel);
|
||||
|
||||
- wr::BorderRadius borderRadius{};
|
||||
- wr::BorderRadius shadowRadius{};
|
||||
+ wr::BorderRadius borderRadius = wr::EmptyBorderRadius();
|
||||
+ wr::BorderRadius shadowRadius = wr::EmptyBorderRadius();
|
||||
if (hasBorderRadius) {
|
||||
borderRadius = wr::ToBorderRadius(innerRadii);
|
||||
if (spreadRadius) {
|
||||
RectCornerRadii shadowRadii = innerRadii;
|
||||
shadowRadii.AdjustInwards(
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
diff --git a/gfx/wr/webrender/res/ps_quad_mask.glsl b/gfx/wr/webrender/res/ps_quad_mask.glsl
|
||||
--- a/gfx/wr/webrender/res/ps_quad_mask.glsl
|
||||
+++ b/gfx/wr/webrender/res/ps_quad_mask.glsl
|
||||
@@ -318,12 +318,30 @@
|
||||
#endif
|
||||
|
||||
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);
|
||||
|
||||
float opaque_start = max(clip_dist.x, clip_dist.y);
|
||||
float opaque_end = min(clip_dist.z, clip_dist.w);
|
||||
@@ -404,10 +422,16 @@
|
||||
|
||||
aa_margin = max(aa_margin - max(aa_start - aa_end, 0.0), 0.0);
|
||||
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 *
|
||||
vec4(floor(aa_start), ceil(opaque_start), floor(opaque_end), ceil(aa_end)),
|
||||
0.0, swgl_SpanLength));
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
diff --git a/toolkit/modules/subprocess/subprocess_shared_unix.js b/toolkit/modules/subprocess/subprocess_shared_unix.js
|
||||
--- a/toolkit/modules/subprocess/subprocess_shared_unix.js
|
||||
+++ b/toolkit/modules/subprocess/subprocess_shared_unix.js
|
||||
@@ -52,16 +52,17 @@
|
||||
|
||||
close: [ctypes.default_abi, ctypes.int, ctypes.int /* fildes */],
|
||||
|
||||
dup: [ctypes.default_abi, ctypes.int, ctypes.int],
|
||||
|
||||
+ // Variadic arguments use a different calling convention on Apple silicon.
|
||||
@@ -57,11 +57,11 @@
|
||||
fcntl: [
|
||||
ctypes.default_abi,
|
||||
ctypes.int,
|
||||
@@ -64,79 +58,62 @@ diff --git a/toolkit/modules/subprocess/test/xpcshell/test_subprocess_pipe_flags
|
||||
new file mode 100644
|
||||
--- /dev/null
|
||||
+++ b/toolkit/modules/subprocess/test/xpcshell/test_subprocess_pipe_flags.js
|
||||
@@ -0,0 +1,73 @@
|
||||
@@ -0,0 +1,56 @@
|
||||
+/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
+
|
||||
+"use strict";
|
||||
+
|
||||
+// fcntl commands for reading the flags set by the subprocess modules. The
|
||||
+// values are the same on Linux and macOS but are not exposed by
|
||||
+// ChromeUtils.getLibcConstants().
|
||||
+const F_GETFD = 1;
|
||||
+const F_GETFL = 3;
|
||||
+
|
||||
+add_task(async function test_subprocess_pipe_flags() {
|
||||
+ const { Subprocess, getSubprocessImplForTest } = ChromeUtils.importESModule(
|
||||
+ const { getSubprocessImplForTest } = ChromeUtils.importESModule(
|
||||
+ "resource://gre/modules/Subprocess.sys.mjs"
|
||||
+ );
|
||||
+ const { ctypes } = ChromeUtils.importESModule(
|
||||
+ "resource://gre/modules/ctypes.sys.mjs"
|
||||
+ const { libc } = ChromeUtils.importESModule(
|
||||
+ "resource://gre/modules/subprocess/subprocess_unix.sys.mjs"
|
||||
+ );
|
||||
+ const constants = ChromeUtils.getLibcConstants();
|
||||
+ const library = ctypes.open("a.out");
|
||||
+ const fcntl = library.declare(
|
||||
+ "fcntl",
|
||||
+ ctypes.default_abi,
|
||||
+ ctypes.int,
|
||||
+ ctypes.int,
|
||||
+ ctypes.int,
|
||||
+ "..."
|
||||
+ const { FD_CLOEXEC, O_NONBLOCK } = ChromeUtils.getLibcConstants();
|
||||
+
|
||||
+ const worker = getSubprocessImplForTest().Process.getWorker();
|
||||
+ equal(
|
||||
+ libc.fcntl(worker.signalFd, F_GETFD),
|
||||
+ FD_CLOEXEC,
|
||||
+ "The main-thread signal pipe has exactly FD_CLOEXEC set"
|
||||
+ );
|
||||
+ // Darwin's fcntl commands for reading descriptor and file status flags.
|
||||
+ const F_GETFD = 1;
|
||||
+ const F_GETFL = 3;
|
||||
+ let process;
|
||||
+
|
||||
+ try {
|
||||
+ const worker = getSubprocessImplForTest().Process.getWorker();
|
||||
+ Assert.equal(
|
||||
+ fcntl(worker.signalFd, F_GETFD),
|
||||
+ constants.FD_CLOEXEC,
|
||||
+ "The main-thread signal pipe has exactly FD_CLOEXEC set"
|
||||
+ const proc = await Subprocess.call({
|
||||
+ command: await Subprocess.pathSearch("cat"),
|
||||
+ stderr: "pipe",
|
||||
+ });
|
||||
+ const fds = await worker.call("getFds", [proc.id]);
|
||||
+ ok(
|
||||
+ fds.every(Number.isInteger),
|
||||
+ "The worker owns the stdin, stdout and stderr pipes"
|
||||
+ );
|
||||
+ for (const fd of fds) {
|
||||
+ equal(
|
||||
+ libc.fcntl(fd, F_GETFD),
|
||||
+ FD_CLOEXEC,
|
||||
+ "Worker pipes have exactly FD_CLOEXEC set"
|
||||
+ );
|
||||
+
|
||||
+ process = await Subprocess.call({
|
||||
+ command: "/bin/cat",
|
||||
+ stderr: "pipe",
|
||||
+ disclaim: true,
|
||||
+ });
|
||||
+ const fds = await worker.call("getFds", [process.id]);
|
||||
+ // Only inspect the descriptors while the worker owns them and cat is alive.
|
||||
+ // Exact flags also rule out an unintended FD_CLOFORK on macOS.
|
||||
+ for (const fd of fds) {
|
||||
+ Assert.equal(
|
||||
+ fcntl(fd, F_GETFD),
|
||||
+ constants.FD_CLOEXEC,
|
||||
+ "Worker pipes have exactly FD_CLOEXEC set"
|
||||
+ );
|
||||
+ Assert.equal(
|
||||
+ fcntl(fd, F_GETFL) & constants.O_NONBLOCK,
|
||||
+ constants.O_NONBLOCK,
|
||||
+ "Worker pipes are nonblocking"
|
||||
+ );
|
||||
+ }
|
||||
+
|
||||
+ const output = process.stdout.readString(5);
|
||||
+ await process.stdin.write("hello");
|
||||
+ Assert.equal(await output, "hello", "The subprocess pipes transfer data");
|
||||
+ await process.stdin.close();
|
||||
+ Assert.equal(
|
||||
+ (await process.wait()).exitCode,
|
||||
+ 0,
|
||||
+ "The subprocess exits cleanly"
|
||||
+ equal(
|
||||
+ libc.fcntl(fd, F_GETFL) & O_NONBLOCK,
|
||||
+ O_NONBLOCK,
|
||||
+ "Worker pipes are nonblocking"
|
||||
+ );
|
||||
+ } finally {
|
||||
+ if (process && process.exitCode === null) {
|
||||
+ await process.kill();
|
||||
+ }
|
||||
+ library.close();
|
||||
+ }
|
||||
+
|
||||
+ const output = proc.stdout.readString(5);
|
||||
+ await proc.stdin.write("hello");
|
||||
+ equal(await output, "hello", "The subprocess pipes transfer data");
|
||||
+ await proc.stdin.close();
|
||||
+ equal((await proc.wait()).exitCode, 0, "The subprocess exits cleanly");
|
||||
+});
|
||||
diff --git a/toolkit/modules/subprocess/test/xpcshell/xpcshell.toml b/toolkit/modules/subprocess/test/xpcshell/xpcshell.toml
|
||||
--- a/toolkit/modules/subprocess/test/xpcshell/xpcshell.toml
|
||||
@@ -148,7 +125,7 @@ diff --git a/toolkit/modules/subprocess/test/xpcshell/xpcshell.toml b/toolkit/mo
|
||||
requesttimeoutfactor = 2 # Slow on Windows
|
||||
|
||||
+["test_subprocess_pipe_flags.js"]
|
||||
+run-if = ["os == 'mac'"]
|
||||
+skip-if = ["os == 'win'"]
|
||||
+
|
||||
["test_subprocess_polling.js"]
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
diff --git a/toolkit/themes/shared/global-shared.css b/toolkit/themes/shared/global-shared.css
|
||||
--- a/toolkit/themes/shared/global-shared.css
|
||||
+++ b/toolkit/themes/shared/global-shared.css
|
||||
@@ -80,11 +80,10 @@
|
||||
@@ -80,10 +80,9 @@
|
||||
background-color: transparent;
|
||||
--panel-background: transparent;
|
||||
--panel-box-shadow: none;
|
||||
--panel-border-color: transparent;
|
||||
--panel-box-shadow-margin: 0px;
|
||||
|
||||
@@ -13,10 +13,9 @@
|
||||
"name": "Issue 15123"
|
||||
},
|
||||
{
|
||||
"type": "local",
|
||||
// TODO: Convert into https://phabricator.services.mozilla.com/D298079
|
||||
// once it gets accepted.
|
||||
"path": "firefox/allow_backdrop_to_work_on_transparency.patch"
|
||||
"type": "phabricator",
|
||||
"id": "D327914",
|
||||
"name": "Backdrop filter reads web content"
|
||||
},
|
||||
{
|
||||
"type": "local",
|
||||
@@ -46,27 +45,11 @@
|
||||
" jsonLengthHint,": " lengthHint: jsonLengthHint,"
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "phabricator",
|
||||
"id": "D320897",
|
||||
"name": "Bug 2011236"
|
||||
},
|
||||
{
|
||||
"type": "phabricator",
|
||||
"id": "D321446",
|
||||
"name": "Issue 14710"
|
||||
},
|
||||
{
|
||||
"type": "local",
|
||||
// Sorts before bug_2063169/ on purpose: D319869 builds on the border
|
||||
// shader hunks in here, and surfer applies patches in path order.
|
||||
"path": "firefox/bug_2047627_corner_shape_borders.patch"
|
||||
},
|
||||
{
|
||||
"type": "phabricator",
|
||||
"ids": ["D319577", "D319796", "D319869", "D320211", "D320568", "D321282"],
|
||||
"name": "Bug 2063169"
|
||||
},
|
||||
{
|
||||
"type": "local",
|
||||
"path": "firefox/override_cert_checks_temp.patch"
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
diff --git a/layout/painting/nsImageRenderer.cpp b/layout/painting/nsImageRenderer.cpp
|
||||
index 6e464b48c90174b8954aad8a79bae30a7a30cbc9..9ea9a4963ceae679f092e014ac24a0d24fb106ce 100644
|
||||
index 7ddc4f7e259272e7b905f44aff7316c1ba540c22..277a92e5d10e6a04b12373854ffe6791d562215e 100644
|
||||
--- a/layout/painting/nsImageRenderer.cpp
|
||||
+++ b/layout/painting/nsImageRenderer.cpp
|
||||
@@ -90,7 +90,7 @@ static already_AddRefed<imgIContainer> GetSymbolicIconImage(nsAtom* aName,
|
||||
@@ -95,7 +95,7 @@ static already_AddRefed<imgIContainer> GetSymbolicIconImage(nsAtom* aName,
|
||||
if (NS_WARN_IF(!XRE_IsParentProcess())) {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -11,7 +11,7 @@ index 6e464b48c90174b8954aad8a79bae30a7a30cbc9..9ea9a4963ceae679f092e014ac24a0d2
|
||||
auto key = std::make_tuple(aName, aScale, fg);
|
||||
auto* cache = aFrame->GetOrCreateDeletableProperty(SymbolicImageCacheProp());
|
||||
auto lookup = cache->Lookup(key);
|
||||
@@ -575,7 +575,7 @@ ImgDrawResult nsImageRenderer::Draw(nsPresContext* aPresContext,
|
||||
@@ -580,7 +580,7 @@ ImgDrawResult nsImageRenderer::Draw(nsPresContext* aPresContext,
|
||||
}
|
||||
case StyleImage::Tag::Gradient: {
|
||||
nsCSSGradientRenderer renderer = nsCSSGradientRenderer::Create(
|
||||
@@ -20,7 +20,7 @@ index 6e464b48c90174b8954aad8a79bae30a7a30cbc9..9ea9a4963ceae679f092e014ac24a0d2
|
||||
|
||||
renderer.Paint(*ctx, aDest, aFill, aRepeatSize, aSrc, aDirtyRect,
|
||||
aOpacity);
|
||||
@@ -652,7 +652,7 @@ ImgDrawResult nsImageRenderer::BuildWebRenderDisplayItems(
|
||||
@@ -657,7 +657,7 @@ ImgDrawResult nsImageRenderer::BuildWebRenderDisplayItems(
|
||||
switch (mType) {
|
||||
case StyleImage::Tag::Gradient: {
|
||||
nsCSSGradientRenderer renderer = nsCSSGradientRenderer::Create(
|
||||
@@ -29,16 +29,16 @@ index 6e464b48c90174b8954aad8a79bae30a7a30cbc9..9ea9a4963ceae679f092e014ac24a0d2
|
||||
|
||||
renderer.BuildWebRenderDisplayItems(aBuilder, aSc, aDest, aFill,
|
||||
aRepeatSize, aSrc,
|
||||
@@ -678,6 +678,8 @@ ImgDrawResult nsImageRenderer::BuildWebRenderDisplayItems(
|
||||
nsPresContext::AppUnitsToIntCSSPixels(aDest.height)};
|
||||
@@ -679,6 +679,8 @@ ImgDrawResult nsImageRenderer::BuildWebRenderDisplayItems(
|
||||
}
|
||||
|
||||
SVGImageContext svgContext(Some(destCSSSize));
|
||||
SVGImageContext svgContext(Some(CSSSize::FromAppUnits(aDest.Size())));
|
||||
+ SVGImageContext::MaybeStoreZenBoosts(svgContext,
|
||||
+ *mForFrame->PresContext());
|
||||
Maybe<ImageIntRegion> region;
|
||||
|
||||
const int32_t appUnitsPerDevPixel = aPresContext->AppUnitsPerDevPixel();
|
||||
@@ -1092,7 +1094,7 @@ ImgDrawResult nsImageRenderer::DrawShapeImage(nsPresContext* aPresContext,
|
||||
@@ -1098,7 +1100,7 @@ ImgDrawResult nsImageRenderer::DrawShapeImage(nsPresContext* aPresContext,
|
||||
|
||||
if (mImage->IsGradient()) {
|
||||
nsCSSGradientRenderer renderer = nsCSSGradientRenderer::Create(
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
diff --git a/layout/svg/SVGGradientFrame.cpp b/layout/svg/SVGGradientFrame.cpp
|
||||
index b0d38b0fab01797328af0b1f1f48af8fb5eebe4d..953feffdc96143dc2d8f8e57f8bb4a6aeb33f0c5 100644
|
||||
index eebcced86ec1e9010b761d5df4a92684dde44009..4dc018a73a979f50ac7842e4a3f3fcf9d0071b56 100644
|
||||
--- a/layout/svg/SVGGradientFrame.cpp
|
||||
+++ b/layout/svg/SVGGradientFrame.cpp
|
||||
@@ -229,8 +229,9 @@ class MOZ_STACK_CLASS SVGColorStopInterpolator
|
||||
@@ -228,8 +228,9 @@ class MOZ_STACK_CLASS SVGColorStopInterpolator
|
||||
SVGColorStopInterpolator(
|
||||
gfxPattern* aGradient, const nsTArray<ColorStop>& aStops,
|
||||
const StyleColorInterpolationMethod& aStyleColorInterpolationMethod,
|
||||
@@ -14,8 +14,8 @@ index b0d38b0fab01797328af0b1f1f48af8fb5eebe4d..953feffdc96143dc2d8f8e57f8bb4a6a
|
||||
mGradient(aGradient) {}
|
||||
|
||||
void CreateStop(float aPosition, DeviceColor aColor) {
|
||||
@@ -270,7 +271,8 @@ already_AddRefed<gfxPattern> SVGGradientFrame::GetPaintServerPattern(
|
||||
if (nStops == 1 || GradientVectorLengthIsZero()) {
|
||||
@@ -269,7 +270,8 @@ already_AddRefed<gfxPattern> SVGGradientFrame::GetPaintServerPattern(
|
||||
if (nStops == 1 || GradientVectorLengthIsZero(gradientUnits)) {
|
||||
// The gradient paints a single colour, using the stop-color of the last
|
||||
// gradient step if there are more than one.
|
||||
- return MakeAndAddRef<gfxPattern>(ToDeviceColor(stops.LastElement().mColor));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/layout/svg/SVGImageContext.h b/layout/svg/SVGImageContext.h
|
||||
index ad833c7c2ee6a4c4984efdb0d59e3576c2d1bc19..36864a34badc0cbfe44d6295798622fd6178f9e3 100644
|
||||
index 63f3c4926cfdb8b12e9e4a38016fc66f50cf3129..5ba3f2c07f521905a086ee6eea87975d5a3441ec 100644
|
||||
--- a/layout/svg/SVGImageContext.h
|
||||
+++ b/layout/svg/SVGImageContext.h
|
||||
@@ -6,6 +6,7 @@
|
||||
@@ -7,10 +7,10 @@ index ad833c7c2ee6a4c4984efdb0d59e3576c2d1bc19..36864a34badc0cbfe44d6295798622fd
|
||||
|
||||
#include "Units.h"
|
||||
+#include "nsColor.h"
|
||||
#include "mozilla/Casting.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/SVGContextPaint.h"
|
||||
#include "mozilla/SVGPreserveAspectRatio.h"
|
||||
@@ -63,6 +64,11 @@ class SVGImageContext {
|
||||
@@ -64,6 +65,11 @@ class SVGImageContext {
|
||||
nsISVGPaintContext* aPaintContext,
|
||||
imgIContainer* aImgContainer);
|
||||
|
||||
@@ -19,10 +19,10 @@ index ad833c7c2ee6a4c4984efdb0d59e3576c2d1bc19..36864a34badc0cbfe44d6295798622fd
|
||||
+ static void MaybeStoreZenBoosts(SVGImageContext& aContext,
|
||||
+ const nsPresContext& aPresContext);
|
||||
+
|
||||
const Maybe<CSSIntSize>& GetViewportSize() const { return mViewportSize; }
|
||||
const Maybe<CSSSize>& GetViewportSize() const { return mViewportSize; }
|
||||
|
||||
void SetViewportSize(const Maybe<CSSIntSize>& aSize) {
|
||||
@@ -75,6 +81,21 @@ class SVGImageContext {
|
||||
void SetViewportSize(const Maybe<CSSSize>& aSize) { mViewportSize = aSize; }
|
||||
@@ -74,6 +80,21 @@ class SVGImageContext {
|
||||
mColorScheme = aScheme;
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ index ad833c7c2ee6a4c4984efdb0d59e3576c2d1bc19..36864a34badc0cbfe44d6295798622fd
|
||||
const Maybe<SVGPreserveAspectRatio>& GetPreserveAspectRatio() const {
|
||||
return mPreserveAspectRatio;
|
||||
}
|
||||
@@ -110,7 +131,11 @@ class SVGImageContext {
|
||||
@@ -109,7 +130,11 @@ class SVGImageContext {
|
||||
return contextPaintIsEqual && mViewportSize == aOther.mViewportSize &&
|
||||
mPreserveAspectRatio == aOther.mPreserveAspectRatio &&
|
||||
mColorScheme == aOther.mColorScheme &&
|
||||
@@ -56,8 +56,8 @@ index ad833c7c2ee6a4c4984efdb0d59e3576c2d1bc19..36864a34badc0cbfe44d6295798622fd
|
||||
+ mZenBoostsInverted == aOther.mZenBoostsInverted;
|
||||
}
|
||||
|
||||
bool operator!=(const SVGImageContext&) const = default;
|
||||
@@ -123,7 +148,8 @@ class SVGImageContext {
|
||||
PLDHashNumber Hash() const {
|
||||
@@ -120,7 +145,8 @@ class SVGImageContext {
|
||||
return AddToHash(hash, mViewportSize.map(HashSize).valueOr(0),
|
||||
mPreserveAspectRatio.map(HashPAR).valueOr(0),
|
||||
mColorScheme.map(HashColorScheme).valueOr(0),
|
||||
@@ -67,7 +67,7 @@ index ad833c7c2ee6a4c4984efdb0d59e3576c2d1bc19..36864a34badc0cbfe44d6295798622fd
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -156,6 +182,9 @@ class SVGImageContext {
|
||||
@@ -154,6 +180,9 @@ class SVGImageContext {
|
||||
Maybe<SVGPreserveAspectRatio> mPreserveAspectRatio;
|
||||
Maybe<ColorScheme> mColorScheme;
|
||||
StyleLinkParameters mLinkParameters;
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
diff --git a/toolkit/content/widgets/panel-list/panel-list.mjs b/toolkit/content/widgets/panel-list/panel-list.mjs
|
||||
index 68ad2fafc8b31dfff541cf8381d9b9533e376d1c..fa62fec6bfb33338091fc06d3736fb76fd5c47c4 100644
|
||||
--- a/toolkit/content/widgets/panel-list/panel-list.mjs
|
||||
+++ b/toolkit/content/widgets/panel-list/panel-list.mjs
|
||||
@@ -223,7 +223,12 @@ export class PanelList extends HTMLElement {
|
||||
this.setAttribute("showing", "true");
|
||||
// Tell the host element to hide any overflow in case the panel extends off
|
||||
// the page before the alignment is set.
|
||||
- hostElement.style.overflow = "hidden";
|
||||
+ const hostHidesOverflow = !getComputedStyle(hostElement)
|
||||
+ .overflow.split(" ")
|
||||
+ .includes("visible");
|
||||
+ if (!hostHidesOverflow) {
|
||||
+ hostElement.style.overflow = "hidden";
|
||||
+ }
|
||||
|
||||
// Wait for a layout flush, then find the bounds.
|
||||
let {
|
||||
@@ -344,7 +349,9 @@ export class PanelList extends HTMLElement {
|
||||
// Set the alignments and show the panel.
|
||||
this.setAttribute("align", align);
|
||||
this.setAttribute("valign", valign);
|
||||
- hostElement.style.overflow = "";
|
||||
+ if (!hostHidesOverflow) {
|
||||
+ hostElement.style.overflow = "";
|
||||
+ }
|
||||
// Decide positioning based on where this panel will be rendered
|
||||
const offsetParentIsBody =
|
||||
this.supportsPopover() ||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/toolkit/themes/shared/design-system/dist/tokens-shared.css b/toolkit/themes/shared/design-system/dist/tokens-shared.css
|
||||
index 63c641f560d4c0ce06b40a652ca99d1c48cd6941..39c81e773a442870763396e68fb57bd336e3625a 100644
|
||||
index b0f8fa146de335c53e73d46853f45dda9b15cc73..1b7592c0e9ca12fd9d1f8a555633fd39c2efb537 100644
|
||||
--- a/toolkit/themes/shared/design-system/dist/tokens-shared.css
|
||||
+++ b/toolkit/themes/shared/design-system/dist/tokens-shared.css
|
||||
@@ -7,6 +7,8 @@
|
||||
@@ -8,9 +8,9 @@ index 63c641f560d4c0ce06b40a652ca99d1c48cd6941..39c81e773a442870763396e68fb57bd3
|
||||
|
||||
+@import url("chrome://browser/content/zen-styles/zen-theme.css");
|
||||
+
|
||||
@layer tokens-foundation {
|
||||
:root,
|
||||
:host(.anonymous-content-host) {
|
||||
:root,
|
||||
:host(.anonymous-content-host) {
|
||||
@layer tokens-foundation {
|
||||
@@ -28,10 +30,10 @@
|
||||
--border-color-selected: var(--color-accent-primary);
|
||||
--border-color-transparent: transparent;
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
diff --git a/widget/cocoa/nsCocoaWindow.mm b/widget/cocoa/nsCocoaWindow.mm
|
||||
index 2efd02e4a92433fd29bbfdb707aa81cfec45031e..3b763c3927f07e529c826f8e4f62bc80a324b247 100644
|
||||
index 887e4c052d5dd2b73d4c2059463529fb4b09dcab..36da18e19018b1b216576c299f938e75093546d3 100644
|
||||
--- a/widget/cocoa/nsCocoaWindow.mm
|
||||
+++ b/widget/cocoa/nsCocoaWindow.mm
|
||||
@@ -86,6 +86,7 @@
|
||||
@@ -86,6 +86,8 @@
|
||||
#include "mozilla/StaticPrefs_gfx.h"
|
||||
#include "mozilla/StaticPrefs_ui.h"
|
||||
#include "mozilla/StaticPrefs_widget.h"
|
||||
+#include "mozilla/StaticPrefs_zen.h"
|
||||
+#include "mozilla/nsZenDragAndDrop.h"
|
||||
#include "mozilla/WritingModes.h"
|
||||
#include "mozilla/dom/Document.h"
|
||||
#include "mozilla/layers/CompositorBridgeChild.h"
|
||||
@@ -1332,6 +1333,110 @@ - (NSRect)_opaqueRectForWindowMoveWhenInTitlebar {
|
||||
@@ -1332,6 +1334,110 @@ - (NSRect)_opaqueRectForWindowMoveWhenInTitlebar {
|
||||
}
|
||||
@end
|
||||
|
||||
@@ -121,7 +122,28 @@ index 2efd02e4a92433fd29bbfdb707aa81cfec45031e..3b763c3927f07e529c826f8e4f62bc80
|
||||
void nsCocoaWindow::UpdateWindowDraggingRegion(
|
||||
const LayoutDeviceIntRegion& aRegion) {
|
||||
// mChildView returns YES from mouseDownCanMoveWindow, so we need to put
|
||||
@@ -4919,6 +5024,10 @@ - (BOOL)wantsBestResolutionOpenGLSurface {
|
||||
@@ -3858,7 +3964,9 @@ - (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender {
|
||||
// NSDraggingDestination
|
||||
- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)sender {
|
||||
MOZ_LOG(sCocoaLog, LogLevel::Info, ("ChildView draggingUpdated: entered\n"));
|
||||
- return [self doDragAction:eDragOver sender:sender];
|
||||
+ NSDragOperation operation = [self doDragAction:eDragOver sender:sender];
|
||||
+ zen::PrepareDropLanding(sender);
|
||||
+ return operation;
|
||||
}
|
||||
|
||||
// NSDraggingDestination
|
||||
@@ -3874,6 +3982,9 @@ - (void)draggingExited:(id<NSDraggingInfo>)sender {
|
||||
- (BOOL)performDragOperation:(id<NSDraggingInfo>)sender {
|
||||
nsAutoRetainCocoaObject kungFuDeathGrip(self);
|
||||
BOOL handled = [self doDragAction:eDrop sender:sender] != NSDragOperationNone;
|
||||
+ if (handled) {
|
||||
+ zen::LandDrop(sender);
|
||||
+ }
|
||||
NS_IF_RELEASE(mDragService);
|
||||
return handled;
|
||||
}
|
||||
@@ -4919,6 +5030,10 @@ - (BOOL)wantsBestResolutionOpenGLSurface {
|
||||
nsCocoaWindow::~nsCocoaWindow() {
|
||||
NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
diff --git a/widget/cocoa/nsDragService.mm b/widget/cocoa/nsDragService.mm
|
||||
index 340a9ad81c5cb23bb7e5f927d11d5cffdde7ce70..32fc707ccfa56fa8c307860868d092c4a9050419 100644
|
||||
index 340a9ad81c5cb23bb7e5f927d11d5cffdde7ce70..ba597ba4373ba3ace7455ce0859f2339349ffc06 100644
|
||||
--- a/widget/cocoa/nsDragService.mm
|
||||
+++ b/widget/cocoa/nsDragService.mm
|
||||
@@ -10,6 +10,7 @@
|
||||
@@ -45,3 +45,112 @@ index 340a9ad81c5cb23bb7e5f927d11d5cffdde7ce70..32fc707ccfa56fa8c307860868d092c4
|
||||
#endif
|
||||
src += 4;
|
||||
dest += 4;
|
||||
@@ -249,12 +254,44 @@
|
||||
[[[NSDraggingItem alloc] initWithPasteboardWriter:pbItem] autorelease];
|
||||
[dragItem setDraggingFrame:localDragRect contents:image];
|
||||
|
||||
+ NSMutableArray* dragItems = [NSMutableArray arrayWithObject:dragItem];
|
||||
+ auto zenDragAndDrop = zen::nsZenDragAndDrop::GetZenDragAndDropInstance();
|
||||
+ const nsTArray<nsCOMPtr<nsINode>> noExtraImages;
|
||||
+ @try {
|
||||
+ for (const auto& extraImageNode :
|
||||
+ zenDragAndDrop ? zenDragAndDrop->DragImages() : noExtraImages) {
|
||||
+ nsCOMPtr<nsINode> mainImage = std::move(mImage);
|
||||
+ mImage = extraImageNode;
|
||||
+ NSPoint extraPoint;
|
||||
+ NSImage* extraImage = ConstructDragImage(mSourceNode, aRegion, &extraPoint);
|
||||
+ mImage = std::move(mainImage);
|
||||
+ if (!extraImage) {
|
||||
+ continue;
|
||||
+ }
|
||||
+ NSRect extraRect = extraImage.alignmentRect;
|
||||
+ extraRect.origin.x = extraPoint.x;
|
||||
+ extraRect.origin.y = extraPoint.y - extraRect.size.height;
|
||||
+ NSPasteboardItem* extraPbItem = [[NSPasteboardItem new] autorelease];
|
||||
+ [extraPbItem setData:[NSData data]
|
||||
+ forType:[UTIHelper stringFromPboardType:kMozWildcardPboardType]];
|
||||
+ NSDraggingItem* extraItem = [[[NSDraggingItem alloc]
|
||||
+ initWithPasteboardWriter:extraPbItem] autorelease];
|
||||
+ [extraItem setDraggingFrame:extraRect contents:extraImage];
|
||||
+ [dragItems addObject:extraItem];
|
||||
+ }
|
||||
+ } @catch (NSException* e) {
|
||||
+ dragItems = [NSMutableArray arrayWithObject:dragItem];
|
||||
+ }
|
||||
+
|
||||
OpenDragPopup();
|
||||
|
||||
mNSDraggingSession = [mNativeDragView
|
||||
- beginDraggingSessionWithItems:[NSArray arrayWithObject:dragItem]
|
||||
+ beginDraggingSessionWithItems:dragItems
|
||||
event:mNativeDragEvent
|
||||
source:mNativeDragView];
|
||||
+ if (dragItems.count > 1) {
|
||||
+ mNSDraggingSession.draggingFormation = NSDraggingFormationStack;
|
||||
+ }
|
||||
|
||||
mNSDraggingSession.animatesToStartingPositionsOnCancelOrFail =
|
||||
!mDataTransfer || mDataTransfer->MozShowFailAnimation();
|
||||
@@ -455,29 +492,44 @@
|
||||
}
|
||||
|
||||
if (pc) {
|
||||
+ nsPoint pt = LayoutDevicePixel::ToAppUnits(
|
||||
+ devPoint, pc->DeviceContext()->AppUnitsPerDevPixel());
|
||||
+ CSSIntPoint screenPoint =
|
||||
+ CSSIntPoint(nsPresContext::AppUnitsToIntCSSPixels(pt.x),
|
||||
+ nsPresContext::AppUnitsToIntCSSPixels(pt.y));
|
||||
+
|
||||
+ NSMutableArray* images = [NSMutableArray array];
|
||||
+ NSMutableArray* rects = [NSMutableArray array];
|
||||
+ auto render = [&]() {
|
||||
+ LayoutDeviceIntRect newRect;
|
||||
+ NSImage* image =
|
||||
+ ConstructDragImage(mSourceNode, Nothing(), screenPoint, &newRect);
|
||||
+ [images addObject:image ? (id)image : (id)[NSNull null]];
|
||||
+ [rects addObject:[NSValue valueWithRect:
|
||||
+ image ? nsCocoaUtils::GeckoRectToCocoaRectDevPix(
|
||||
+ newRect, scaleFactor)
|
||||
+ : NSZeroRect]];
|
||||
+ };
|
||||
+ render();
|
||||
+ if (auto zenDragAndDrop = zen::nsZenDragAndDrop::GetZenDragAndDropInstance()) {
|
||||
+ for (const auto& extraImageNode : zenDragAndDrop->DragImages()) {
|
||||
+ nsCOMPtr<nsINode> mainImage = std::move(mImage);
|
||||
+ mImage = extraImageNode;
|
||||
+ render();
|
||||
+ mImage = std::move(mainImage);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
void (^changeImageBlock)(NSDraggingItem*, NSInteger, BOOL*) = ^(
|
||||
NSDraggingItem* draggingItem, NSInteger idx, BOOL* stop) {
|
||||
- // We never add more than one item right now, but check just in case.
|
||||
- if (idx > 0) {
|
||||
+ if ((NSUInteger)idx >= images.count || images[idx] == [NSNull null]) {
|
||||
return;
|
||||
}
|
||||
-
|
||||
- nsPoint pt = LayoutDevicePixel::ToAppUnits(
|
||||
- devPoint, pc->DeviceContext()->AppUnitsPerDevPixel());
|
||||
- CSSIntPoint screenPoint =
|
||||
- CSSIntPoint(nsPresContext::AppUnitsToIntCSSPixels(pt.x),
|
||||
- nsPresContext::AppUnitsToIntCSSPixels(pt.y));
|
||||
-
|
||||
- // Create a new image; if one isn't returned don't change the current
|
||||
- // one.
|
||||
- LayoutDeviceIntRect newRect;
|
||||
- NSImage* image =
|
||||
- ConstructDragImage(mSourceNode, Nothing(), screenPoint, &newRect);
|
||||
- if (image) {
|
||||
- NSRect draggingRect =
|
||||
- nsCocoaUtils::GeckoRectToCocoaRectDevPix(newRect, scaleFactor);
|
||||
- [draggingItem setDraggingFrame:draggingRect contents:image];
|
||||
+ NSRect frame = [rects[idx] rectValue];
|
||||
+ if (idx > 0) {
|
||||
+ frame.origin = draggingItem.draggingFrame.origin;
|
||||
}
|
||||
+ [draggingItem setDraggingFrame:frame contents:images[idx]];
|
||||
};
|
||||
|
||||
[mNSDraggingSession
|
||||
|
||||
@@ -69,7 +69,9 @@ export class nsZenBoostEditor {
|
||||
* @returns {ZenBoostsParent} Boost JSActor parent
|
||||
*/
|
||||
get zenBoostsParent() {
|
||||
const linkedBrowser = this.openerWindow.gBrowser.selectedTab.linkedBrowser;
|
||||
const linkedBrowser =
|
||||
this.editorWindow.browser ??
|
||||
this.openerWindow.gBrowser.selectedTab.linkedBrowser;
|
||||
const actor =
|
||||
linkedBrowser.browsingContext.currentWindowGlobal.getActor("ZenBoosts");
|
||||
return actor;
|
||||
|
||||
@@ -606,9 +606,12 @@ class nsZenBoostsManager {
|
||||
* @param {Window} parentWindow - The parent browser window
|
||||
* @param {Boost} boost - The boost which will be edited
|
||||
* @param {nsIURI} domainUri - The boost which will be edited
|
||||
* @param {object} [options]
|
||||
* @param {Element} [options.browser] - Browser showing the site when it is
|
||||
* not the selected tab, e.g. a detached glance
|
||||
* @returns {Window|null} The instanced editor window
|
||||
*/
|
||||
openBoostWindow(parentWindow, boost, domainUri) {
|
||||
openBoostWindow(parentWindow, boost, domainUri, { browser = null } = {}) {
|
||||
if (!this.canBoostSite(domainUri)) {
|
||||
console.error(
|
||||
"[ZenBoostsManager] Cannot open editor for boost with invalid domain."
|
||||
@@ -648,38 +651,51 @@ class nsZenBoostsManager {
|
||||
null
|
||||
);
|
||||
|
||||
// Close the editor if the tab is switched
|
||||
const onTabSelect = editor.close.bind(editor);
|
||||
parentWindow.gBrowser.tabContainer.addEventListener(
|
||||
"TabSelect",
|
||||
onTabSelect,
|
||||
{
|
||||
once: true,
|
||||
}
|
||||
);
|
||||
|
||||
const progressListener = {
|
||||
QueryInterface: ChromeUtils.generateQI([
|
||||
"nsIWebProgressListener",
|
||||
"nsISupportsWeakReference",
|
||||
]),
|
||||
onLocationChange: webProgress => {
|
||||
if (webProgress.isTopLevel) {
|
||||
editor.close();
|
||||
}
|
||||
},
|
||||
};
|
||||
const onTabSelect = editor.close.bind(editor);
|
||||
if (browser) {
|
||||
browser.addProgressListener(progressListener);
|
||||
editor.addEventListener(
|
||||
"unload",
|
||||
() => {
|
||||
browser.removeProgressListener(progressListener);
|
||||
editor.browser = null;
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
} else {
|
||||
// Close the editor if the tab is switched or navigates.
|
||||
parentWindow.gBrowser.tabContainer.addEventListener(
|
||||
"TabSelect",
|
||||
onTabSelect,
|
||||
{ once: true }
|
||||
);
|
||||
parentWindow.gBrowser.addProgressListener(progressListener);
|
||||
editor.addEventListener(
|
||||
"unload",
|
||||
() => {
|
||||
parentWindow.gBrowser.tabContainer.removeEventListener(
|
||||
"TabSelect",
|
||||
onTabSelect
|
||||
);
|
||||
parentWindow.gBrowser.removeProgressListener(progressListener);
|
||||
editor.browser = null;
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
}
|
||||
|
||||
parentWindow.gBrowser.addProgressListener(progressListener);
|
||||
|
||||
editor.addEventListener(
|
||||
"unload",
|
||||
() => {
|
||||
parentWindow.gBrowser.tabContainer.removeEventListener(
|
||||
"TabSelect",
|
||||
onTabSelect
|
||||
);
|
||||
parentWindow.gBrowser.removeProgressListener(progressListener);
|
||||
},
|
||||
{ once: true }
|
||||
);
|
||||
|
||||
editor.browser = browser;
|
||||
// Give the domain
|
||||
editor.domain = domain;
|
||||
editor.openerWindow = parentWindow;
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
content/browser/zen-styles/zen-browser-ui.css (../../zen/common/styles/zen-browser-ui.css)
|
||||
content/browser/zen-styles/zen-animations.css (../../zen/common/styles/zen-animations.css)
|
||||
content/browser/zen-styles/zen-panel-ui.css (../../zen/common/styles/zen-panel-ui.css)
|
||||
content/browser/zen-styles/zen-single-components.css (../../zen/common/styles/zen-single-components.css)
|
||||
* content/browser/zen-styles/zen-single-components.css (../../zen/common/styles/zen-single-components.css)
|
||||
content/browser/zen-styles/zen-sidebar.css (../../zen/common/styles/zen-sidebar.css)
|
||||
* content/browser/zen-styles/zen-toolbar.css (../../zen/common/styles/zen-toolbar.css)
|
||||
content/browser/zen-styles/zen-browser-container.css (../../zen/common/styles/zen-browser-container.css)
|
||||
@@ -31,6 +31,7 @@
|
||||
content/browser/zen-styles/zen-popup.css (../../zen/common/styles/zen-popup.css)
|
||||
content/browser/zen-styles/zen-branding.css (../../zen/common/styles/zen-branding.css)
|
||||
content/browser/zen-styles/zen-sidebar-notification.css (../../zen/common/styles/zen-sidebar-notification.css)
|
||||
* content/browser/zen-styles/zen-backdrop-filters.svg (../../zen/common/styles/zen-backdrop-filters.svg)
|
||||
|
||||
content/browser/zen-styles/zen-panels/bookmarks.css (../../zen/common/styles/zen-panels/bookmarks.css)
|
||||
content/browser/zen-styles/zen-panels/print.css (../../zen/common/styles/zen-panels/print.css)
|
||||
|
||||
@@ -146,6 +146,20 @@ window.gZenUIManager = {
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Shakes an element from side to side to catch the user's eye.
|
||||
*
|
||||
* @param {Element} element
|
||||
* @param {number} delay Milliseconds to wait before shaking.
|
||||
*/
|
||||
shakeElement(element, delay = 0) {
|
||||
return this.elementAnimate(
|
||||
element,
|
||||
{ x: [0, -12, 8, -4, 2, 0] },
|
||||
{ duration: 600, delay, easing: "ease-out" }
|
||||
);
|
||||
},
|
||||
|
||||
_addNewCustomizableButtonsIfNeeded() {
|
||||
const kPref = "zen.ui.migration.compact-mode-button-added";
|
||||
let navbarPlacements = CustomizableUI.getWidgetIdsInArea(
|
||||
@@ -990,14 +1004,7 @@ window.gZenVerticalTabsManager = {
|
||||
});
|
||||
|
||||
ChromeUtils.defineLazyGetter(this, "hidesTabsToolbar", () => {
|
||||
return (
|
||||
document.documentElement
|
||||
.getAttribute("chromehidden")
|
||||
?.includes("toolbar") ||
|
||||
document.documentElement
|
||||
.getAttribute("chromehidden")
|
||||
?.includes("menubar")
|
||||
);
|
||||
return document.documentElement.hasAttribute("popup-window");
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyPreferenceGetter(
|
||||
@@ -1411,18 +1418,37 @@ window.gZenVerticalTabsManager = {
|
||||
|
||||
if (isSingleToolbar) {
|
||||
this._navbarParent = navBar.parentElement;
|
||||
let elements = document.querySelectorAll(
|
||||
'#nav-bar-customization-target > :is([cui-areatype="toolbar"], .chromeclass-toolbar-additional):not(#urlbar-container):not(toolbarspring)'
|
||||
let elements = Array.from(
|
||||
document.querySelectorAll(
|
||||
'#nav-bar-customization-target > :is([cui-areatype="toolbar"], .chromeclass-toolbar-additional):not(#urlbar-container):not(toolbarspring)'
|
||||
)
|
||||
);
|
||||
elements = Array.from(elements).reverse();
|
||||
let normalButtons = [];
|
||||
let extensionButtons = [];
|
||||
for (const element of elements) {
|
||||
if (
|
||||
element.hasAttribute("data-extensionid") &&
|
||||
Services.prefs.getBoolPref("zen.view.overflow-webext-toolbar", true)
|
||||
) {
|
||||
extensionButtons.push(element);
|
||||
} else {
|
||||
normalButtons.push(element);
|
||||
}
|
||||
}
|
||||
// Add separator if it doesn't exist
|
||||
if (!this._hasSetSingleToolbar) {
|
||||
buttonsTarget.append(this._topButtonsSeparatorElement);
|
||||
}
|
||||
this._hasSetSingleToolbar = true;
|
||||
for (const button of elements) {
|
||||
for (const button of normalButtons.reverse()) {
|
||||
this.appendCustomizableItem(this._topButtonsSeparatorElement, button);
|
||||
}
|
||||
for (const extension of extensionButtons) {
|
||||
this.appendCustomizableItem(
|
||||
this._topButtonsSeparatorElement,
|
||||
extension
|
||||
);
|
||||
}
|
||||
buttonsTarget.prepend(
|
||||
document.getElementById("unified-extensions-button")
|
||||
);
|
||||
@@ -1583,13 +1609,7 @@ window.gZenVerticalTabsManager = {
|
||||
},
|
||||
|
||||
rebuildURLBarMenus() {
|
||||
if (document.getElementById("paste-and-go")) {
|
||||
return;
|
||||
}
|
||||
gURLBar._initCopyCutController();
|
||||
gURLBar._initPasteAndGo();
|
||||
gURLBar._initStripOnShare();
|
||||
gURLBar._updatePlaceholderFromDefaultEngine();
|
||||
gURLBar.updatePlaceholder();
|
||||
},
|
||||
|
||||
rebuildAreas() {
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
% file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
color-scheme: dark;
|
||||
--zen-backdrop-underlay: url("chrome://browser/content/zen-styles/zen-backdrop-filters.svg#underlay-dark");
|
||||
--zen-urlbar-outline-offset: -2px;
|
||||
--zen-urlbar-filter: blur(40px) saturate(110%) brightness(25%) contrast(100%);
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
% file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
color-scheme: light;
|
||||
--zen-backdrop-underlay: url("chrome://browser/content/zen-styles/zen-backdrop-filters.svg#underlay-light");
|
||||
--zen-urlbar-outline-offset: 0px;
|
||||
--zen-urlbar-filter: blur(40px) saturate(110%) brightness(75%) contrast(100%);
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
.tab-group-overflow-count,
|
||||
.tab-group-editor-swatch,
|
||||
.tab-preview-container-indicator,
|
||||
.tab-preview-thumbnail-container > canvas,
|
||||
#tabbrowser-tabs[orient='vertical']:not([expanded]) .tab-close-button,
|
||||
.all-tabs-item > toolbarbutton.all-tabs-container-indicator::before,
|
||||
.urlbarView-button,
|
||||
@@ -25,9 +26,12 @@
|
||||
[fxastatus='signedin'] #fxa-avatar-image,
|
||||
#profile-icon-image,
|
||||
.appMenu-update-banner-pulse-ring,
|
||||
.urlbarView-favicon,
|
||||
:is(#appMenu-zoomReduce-button2, #appMenu-zoomEnlarge-button2) > .toolbarbutton-icon,
|
||||
.color-swatches .userContext-icon,
|
||||
.reusable-relay-masks > button,
|
||||
.radio-check {
|
||||
.radio-check,
|
||||
#zen-loading-progress-bar,
|
||||
#statuspanel-label {
|
||||
corner-shape: round;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,38 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
@keyframes library-badge-download {
|
||||
0% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
20% {
|
||||
transform: translateY(12px);
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes zen-library-sprite-hover {
|
||||
from {
|
||||
background-position-x: 0%;
|
||||
}
|
||||
|
||||
to {
|
||||
background-position-x: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes zen-library-sprite-unhover {
|
||||
from {
|
||||
background-position-x: 100%;
|
||||
}
|
||||
|
||||
to {
|
||||
background-position-x: 0%;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes zen-back-and-forth-text {
|
||||
0%,
|
||||
10% {
|
||||
|
||||
20
src/zen/common/styles/zen-backdrop-filters.svg
Normal file
20
src/zen/common/styles/zen-backdrop-filters.svg
Normal file
@@ -0,0 +1,20 @@
|
||||
#filter dumbComments emptyLines substitution
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<filter id="underlay-dark" x="0" y="0" width="100%" height="100%" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-color="black" />
|
||||
<feMerge>
|
||||
<feMergeNode />
|
||||
<feMergeNode in="SourceGraphic" />
|
||||
</feMerge>
|
||||
</filter>
|
||||
<filter id="underlay-light" x="0" y="0" width="100%" height="100%" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-color="white" />
|
||||
<feMerge>
|
||||
<feMergeNode />
|
||||
<feMergeNode in="SourceGraphic" />
|
||||
</feMerge>
|
||||
</filter>
|
||||
</svg>
|
||||
@@ -3,19 +3,15 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
:root:not([inDOMFullscreen="true"]):not([chromehidden~="location"]):not([chromehidden~="toolbar"]) {
|
||||
:root:not([inDOMFullscreen="true"]):not([popup-window]) {
|
||||
& #tabbrowser-tabbox #tabbrowser-tabpanels .browserSidebarContainer {
|
||||
--zen-transparent-browser-background: light-dark(rgba(255, 255, 255, 0.6), rgba(255, 255, 255, 0.1));
|
||||
overflow: clip;
|
||||
|
||||
:root:not([zen-no-padding="true"]) &:not(.zen-glance-overlay) {
|
||||
border-radius: var(--zen-native-inner-radius);
|
||||
box-shadow: var(--zen-big-shadow);
|
||||
@media not (-moz-pref("zen.theme.squircle-browser-view")) {
|
||||
/* stylelint-disable-next-line property-no-unknown */
|
||||
corner-shape: round;
|
||||
border-radius: calc(var(--zen-native-inner-radius) / var(--zen-squircle-value));
|
||||
}
|
||||
/* stylelint-disable-next-line property-no-unknown */
|
||||
corner-shape: round;
|
||||
border-radius: calc(var(--zen-native-inner-radius) / var(--zen-squircle-value));
|
||||
}
|
||||
|
||||
& browser[type="content"] {
|
||||
@@ -34,18 +30,6 @@
|
||||
color-scheme: env(-moz-content-preferred-color-scheme);
|
||||
}
|
||||
}
|
||||
|
||||
/* stylelint-disable-next-line media-query-no-invalid */
|
||||
@media -moz-pref("zen.theme.acrylic-elements") {
|
||||
& browser[type="content"] {
|
||||
/* For the rendering engine to apply layering optimizations. This
|
||||
* is a hacky solution, but it works for now. Note that this makes
|
||||
* zen REALLY slow, so lets not enable it by default until we have
|
||||
* a better solution. */
|
||||
translate: 0;
|
||||
backface-visibility: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* stylelint-disable-next-line media-query-no-invalid */
|
||||
|
||||
@@ -20,7 +20,7 @@ body,
|
||||
position: inherit;
|
||||
}
|
||||
|
||||
:root:is([inDOMFullscreen="true"], [chromehidden~="location"], [chromehidden~="toolbar"]) {
|
||||
:root:is([inDOMFullscreen="true"], [popup-window]) {
|
||||
#navigator-toolbox,
|
||||
#zen-sidebar-splitter {
|
||||
visibility: collapse;
|
||||
@@ -61,7 +61,7 @@ body,
|
||||
background: var(--zen-main-browser-background);
|
||||
opacity: var(--zen-background-opacity);
|
||||
transition: 0s;
|
||||
background-blend-mode: screen;
|
||||
background-blend-mode: lighten;
|
||||
}
|
||||
|
||||
&:is(.zen-toolbar-background) {
|
||||
@@ -74,7 +74,7 @@ body,
|
||||
background: var(--zen-main-browser-background-old);
|
||||
opacity: calc(1 - var(--zen-background-opacity));
|
||||
transition: 0s;
|
||||
background-blend-mode: screen;
|
||||
background-blend-mode: lighten;
|
||||
}
|
||||
|
||||
&:is(.zen-toolbar-background) {
|
||||
@@ -100,14 +100,14 @@ body,
|
||||
display: flex;
|
||||
opacity: var(--zen-grainy-background-opacity, 0);
|
||||
|
||||
:root:not([swipe-gesture="true"]) & {
|
||||
&:not([swipe-gesture="true"]) {
|
||||
/* note: Keep in sync with kGlobalAnimationDuration */
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:root[swipe-gesture] #tabbrowser-arrowscrollbox {
|
||||
#tabbrowser-arrowscrollbox[swipe-gesture] {
|
||||
/* See gh-13703 for rationale. */
|
||||
pointer-events: none;
|
||||
}
|
||||
@@ -152,7 +152,7 @@ body,
|
||||
}
|
||||
|
||||
& > * {
|
||||
z-index: 1;
|
||||
z-index: var(--zen-main-app-child-z-index, 1);
|
||||
}
|
||||
|
||||
/* stylelint-disable-next-line media-query-no-invalid */
|
||||
@@ -170,7 +170,7 @@ body,
|
||||
min-width: 1px;
|
||||
}
|
||||
|
||||
:root:not([inDOMFullscreen="true"]):not([chromehidden~="location"]):not([chromehidden~="toolbar"]) {
|
||||
:root:not([inDOMFullscreen="true"]):not([popup-window]) {
|
||||
& #zen-tabbox-wrapper {
|
||||
margin: var(--zen-element-separation);
|
||||
margin-top: 0;
|
||||
|
||||
94
src/zen/common/styles/zen-library-media-preview.css
Normal file
94
src/zen/common/styles/zen-library-media-preview.css
Normal file
@@ -0,0 +1,94 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
.zen-library-media-preview {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
--zen-main-app-child-z-index: 3;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.zen-library-media-preview-backdrop {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: light-dark(rgba(255, 255, 255, 0.55), rgba(0, 0, 0, 0.45));
|
||||
}
|
||||
|
||||
.zen-library-media-preview-panel {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: min(78vw, 1100px);
|
||||
border-radius: 18px;
|
||||
overflow: hidden;
|
||||
background: light-dark(rgb(245, 245, 247), rgb(28, 30, 32));
|
||||
box-shadow: 0 24px 60px rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
|
||||
.zen-library-media-preview-header {
|
||||
--zen-toolbar-button-size: 15px;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 10px 12px;
|
||||
flex: none;
|
||||
color: light-dark(rgb(28, 30, 32), rgb(233, 234, 237));
|
||||
}
|
||||
|
||||
.zen-library-media-preview-name {
|
||||
margin-inline-start: 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.zen-library-media-preview-date {
|
||||
font-size: 13px;
|
||||
opacity: 0.6;
|
||||
margin-inline-end: auto;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.zen-library-media-preview .toolbarbutton-1 > .toolbarbutton-icon {
|
||||
box-sizing: border-box;
|
||||
-moz-context-properties: fill;
|
||||
fill: var(--toolbarbutton-icon-fill);
|
||||
}
|
||||
|
||||
.zen-library-media-preview-open {
|
||||
& > .toolbarbutton-text {
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 5px 10px;
|
||||
border-radius: var(--toolbarbutton-border-radius);
|
||||
background: color-mix(in srgb, currentColor 12%, transparent);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&:hover > .toolbarbutton-text {
|
||||
background: color-mix(in srgb, currentColor 20%, transparent);
|
||||
}
|
||||
}
|
||||
|
||||
.zen-library-media-preview-stage {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: min(64vh, 620px);
|
||||
padding: 16px;
|
||||
background: light-dark(rgba(0, 0, 0, 0.05), rgba(0, 0, 0, 0.28));
|
||||
border-top: 1px solid light-dark(rgba(0, 0, 0, 0.08), rgba(255, 255, 255, 0.07));
|
||||
}
|
||||
|
||||
.zen-library-media-preview-media {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
border-radius: 8px;
|
||||
object-fit: contain;
|
||||
}
|
||||
313
src/zen/common/styles/zen-library-widget.css
Normal file
313
src/zen/common/styles/zen-library-widget.css
Normal file
@@ -0,0 +1,313 @@
|
||||
|
||||
@property --zen-library-progress {
|
||||
syntax: "<number>";
|
||||
inherits: true;
|
||||
initial-value: 0;
|
||||
}
|
||||
|
||||
#zen-sidebar-foot-buttons,
|
||||
#tabbrowser-tabs {
|
||||
--zen-library-open-duration: 0.3s;
|
||||
--zen-library-open-easing: cubic-bezier(0.22, 1, 0.36, 1);
|
||||
--zen-library-entry-easing: linear(0, 0.34 12%, 0.7 24%, 0.93 38%, 1.03 55%, 1);
|
||||
transition: --zen-library-progress var(--zen-library-open-duration) var(--zen-library-open-easing);
|
||||
|
||||
&[zen-library-stack-open] {
|
||||
--zen-library-progress: 1;
|
||||
}
|
||||
}
|
||||
|
||||
#zen-library-button {
|
||||
position: relative;
|
||||
|
||||
--zen-library-sprite-size: 18px;
|
||||
&::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: var(--zen-library-sprite-size);
|
||||
height: var(--zen-library-sprite-size);
|
||||
background-image: url("chrome://browser/skin/zen-icons/library/library-sprite.svg");
|
||||
scale: 1.08;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
&::before {
|
||||
animation: zen-library-sprite-unhover 0.3s steps(36, jump-none);
|
||||
}
|
||||
&:hover::before {
|
||||
animation: zen-library-sprite-hover 0.35s steps(36, jump-none) forwards;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#zen-library-button > .toolbarbutton-badge-stack {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: none !important;
|
||||
pointer-events: none;
|
||||
|
||||
&[downloading] {
|
||||
animation: 0.4s library-badge-download ease;
|
||||
}
|
||||
|
||||
/* The badge flies from its spot on the button to the newest entry's badge
|
||||
* along an arc, the target being set as the list opens. */
|
||||
& #library-button-badge {
|
||||
position: absolute;
|
||||
margin: 0 !important;
|
||||
transform-origin: left top;
|
||||
|
||||
--zen-library-badge-x: 14px;
|
||||
--zen-library-badge-y: -8px;
|
||||
--zen-library-badge-to-x: var(--zen-library-badge-x);
|
||||
--zen-library-badge-to-y: var(--zen-library-badge-y);
|
||||
--zen-library-badge-mid-x: calc(
|
||||
(var(--zen-library-badge-x) + var(--zen-library-badge-to-x)) / 2 +
|
||||
abs(var(--zen-library-badge-to-y) - var(--zen-library-badge-y)) / 2
|
||||
);
|
||||
--zen-library-badge-mid-y: calc(
|
||||
(var(--zen-library-badge-y) + var(--zen-library-badge-to-y)) / 2 -
|
||||
abs(var(--zen-library-badge-to-x) - var(--zen-library-badge-x)) / 2
|
||||
);
|
||||
--zen-library-left: calc(1 - var(--zen-library-progress));
|
||||
left: calc(
|
||||
var(--zen-library-left) * var(--zen-library-left) * var(--zen-library-badge-x) +
|
||||
2 * var(--zen-library-left) * var(--zen-library-progress) * var(--zen-library-badge-mid-x) +
|
||||
var(--zen-library-progress) * var(--zen-library-progress) * var(--zen-library-badge-to-x)
|
||||
);
|
||||
top: calc(
|
||||
var(--zen-library-left) * var(--zen-library-left) * var(--zen-library-badge-y) +
|
||||
2 * var(--zen-library-left) * var(--zen-library-progress) * var(--zen-library-badge-mid-y) +
|
||||
var(--zen-library-progress) * var(--zen-library-progress) * var(--zen-library-badge-to-y)
|
||||
);
|
||||
|
||||
&:not([downloading]) {
|
||||
transform: scale(calc((var(--zen-library-progress) * 0.5 + 0.5) * 100%));
|
||||
}
|
||||
|
||||
&[downloading] {
|
||||
transform: scale(calc((var(--zen-library-progress) * 0.2 + 0.8) * 100%));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#library-button-badge,
|
||||
#zen-sidebar-foot-buttons[zen-library-badge] #zen-library-download-list > :last-child .zen-library-download-badge {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#zen-sidebar-foot-buttons[zen-library-badge] {
|
||||
& #library-button-badge {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
&[zen-library-stack-open] {
|
||||
& #library-button-badge {
|
||||
visibility: hidden;
|
||||
transition: visibility 0s var(--zen-library-open-duration);
|
||||
}
|
||||
|
||||
& #zen-library-download-list > :last-child .zen-library-download-badge {
|
||||
visibility: visible;
|
||||
transition: visibility 0s var(--zen-library-open-duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.zen-library-download-badge {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.zen-library-download-badge:not([downloading]) {
|
||||
background-image: var(--download-image);
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: var(--border-radius-xsmall);
|
||||
background-size: cover;
|
||||
scale: 2;
|
||||
|
||||
& .zen-library-download-progress {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.zen-library-download-badge[downloading] {
|
||||
will-change: transform;
|
||||
align-self: start;
|
||||
transform: translateX(-2px);
|
||||
|
||||
--border-color: color-mix(in srgb, var(--zen-primary-color), light-dark(rgba(0,0,0,.8), white) 70%);
|
||||
|
||||
background-color: color-mix(in srgb, var(--zen-primary-color), light-dark(white, rgba(0,0,0,.5)) 80%);
|
||||
border: 3px solid transparent;
|
||||
box-shadow: 0 0 6px 2px rgba(0,0,0,.1);
|
||||
color: var(--border-color);
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
border-radius: 100%;
|
||||
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.zen-library-download-badge[downloading]::after {
|
||||
background-image: url("chrome://browser/skin/zen-icons/forward.svg");
|
||||
rotate: 90deg;
|
||||
|
||||
position: absolute;
|
||||
content: '';
|
||||
background-size: contain;
|
||||
inset: 2px;
|
||||
fill: currentColor;
|
||||
-moz-context-properties: fill;
|
||||
}
|
||||
|
||||
.zen-library-download-progress {
|
||||
position: absolute;
|
||||
inset: 1px;
|
||||
border-radius: 50%;
|
||||
|
||||
--badge-size: 25px;
|
||||
--border-width: 4px;
|
||||
--size: calc(var(--badge-size) + 2 * var(--border-width));
|
||||
margin: calc(-1 * var(--border-width));
|
||||
|
||||
--thickness: 3px;
|
||||
--progress: var(--zen-accent-button-background);
|
||||
|
||||
background:
|
||||
conic-gradient(
|
||||
from 0deg,
|
||||
var(--progress) calc(var(--value) * 1%),
|
||||
transparent 0
|
||||
)
|
||||
border-box,
|
||||
var(--border-color) border-box;
|
||||
|
||||
mask: radial-gradient(
|
||||
circle closest-side,
|
||||
transparent calc(100% - var(--thickness)),
|
||||
black calc(100% - var(--thickness))
|
||||
);
|
||||
}
|
||||
|
||||
#zen-sidebar-foot-buttons:is([zen-library-stack-open], [zen-library-stack-closing]) {
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
#zen-library-download-list {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
|
||||
visibility: hidden;
|
||||
transition: visibility 0s var(--zen-library-open-duration);
|
||||
|
||||
#zen-sidebar-foot-buttons[zen-library-stack-open] & {
|
||||
visibility: visible;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0;
|
||||
padding-top: 10px;
|
||||
gap: 4px;
|
||||
|
||||
.zen-library-download-list-download {
|
||||
container-type: inline-size;
|
||||
-moz-window-dragging: no-drag;
|
||||
|
||||
height: 52px;
|
||||
border-radius: 16px;
|
||||
display: flex;
|
||||
position: relative;
|
||||
padding: 0 4px;
|
||||
|
||||
--zen-library-progress: 0;
|
||||
--zen-library-entry-index: 0;
|
||||
--zen-library-entry-delay: calc(var(--zen-library-entry-index) * 12ms);
|
||||
--zen-library-entry-settle: 0s;
|
||||
transition:
|
||||
--zen-library-progress 0.22s var(--zen-library-entry-easing) var(--zen-library-entry-delay),
|
||||
pointer-events 0s var(--zen-library-entry-settle) allow-discrete;
|
||||
|
||||
pointer-events: none;
|
||||
|
||||
&:nth-last-child(2) {
|
||||
--zen-library-entry-index: 1;
|
||||
}
|
||||
&:nth-last-child(3) {
|
||||
--zen-library-entry-index: 2;
|
||||
}
|
||||
&:nth-last-child(4) {
|
||||
--zen-library-entry-index: 3;
|
||||
}
|
||||
|
||||
#zen-sidebar-foot-buttons[zen-library-stack-open] & {
|
||||
--zen-library-progress: 1;
|
||||
--zen-library-entry-settle: calc(0.22s + var(--zen-library-entry-delay));
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
transform: translateY(calc((1 - var(--zen-library-progress)) * 25px));
|
||||
opacity: var(--zen-library-progress);
|
||||
|
||||
&:hover {
|
||||
background: var(--zen-toolbar-element-bg);
|
||||
}
|
||||
|
||||
& .zen-library-download-list-title-container {
|
||||
@container (max-width: 50px) {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
margin: auto 0;
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
|
||||
& .zen-library-download-list-title {
|
||||
font-size: 11pt;
|
||||
opacity: 0.9;
|
||||
|
||||
text-overflow: ellipsis;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
& .zen-library-download-list-subtitle {
|
||||
font-size: 9pt;
|
||||
opacity: 0.6;
|
||||
|
||||
text-overflow: ellipsis;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
& .zen-library-download-badge {
|
||||
margin: auto 20px auto 8px;
|
||||
@container (max-width: 50px) {
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
|
||||
& .zen-library-download-action {
|
||||
display: none;
|
||||
margin: auto 4px auto 0;
|
||||
|
||||
@container (max-width: 50px) {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
&[downloading] .zen-library-download-action {
|
||||
display: flex;
|
||||
list-style-image: url("chrome://browser/skin/zen-icons/close.svg");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
}
|
||||
|
||||
.urlbar {
|
||||
--urlbarview-separator-color: light-dark(hsl(0, 0%, 80%), hsl(0, 0%, 20%));
|
||||
--urlbarview-separator-color: light-dark(rgb(233, 233, 233), rgb(46 46 47));
|
||||
--urlbarview-background-color-hover: var(--toolbarbutton-background-color-hover);
|
||||
border-radius: calc(var(--toolbarbutton-border-radius) - 2px);
|
||||
--urlbarView-results-padding: 10px !important;
|
||||
@@ -27,6 +27,9 @@
|
||||
--urlbar-margin-inline: 5px;
|
||||
--urlbar-container-padding: 4px;
|
||||
|
||||
--urlbar-icon-border-radius: 8px !important;
|
||||
--urlbar-inner-border-radius: 8px !important;
|
||||
|
||||
&[breakout-extend] {
|
||||
--urlbar-container-padding: 0px;
|
||||
}
|
||||
@@ -164,11 +167,12 @@
|
||||
}
|
||||
|
||||
#navigator-toolbox[zen-has-implicit-hover="true"] &,
|
||||
&[open],
|
||||
&:is([open], [showing]),
|
||||
#urlbar[has-popup-open="true"] &,
|
||||
:root[zen-has-empty-tab="true"] & {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
transition: opacity 0.15s !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -273,29 +277,31 @@
|
||||
}
|
||||
|
||||
& .urlbar-background {
|
||||
--zen-urlbar-background-base: light-dark(#fbfbfb, color-mix(in srgb, hsl(0, 0%, 6.7%), var(--zen-colors-primary) 30%));
|
||||
--zen-urlbar-background-base: light-dark(rgb(255, 255, 255), rgb(24 24 24));
|
||||
/* stylelint-disable-next-line media-query-no-invalid */
|
||||
@media -moz-pref("zen.theme.acrylic-elements") and (not (prefers-reduced-transparency: reduce)) {
|
||||
--zen-urlbar-background-transparent: light-dark(
|
||||
color-mix(in srgb, white 80%, transparent),
|
||||
color-mix(in srgb, var(--zen-urlbar-background-base) 65%, transparent)
|
||||
);
|
||||
&:not(:-moz-window-inactive) {
|
||||
--zen-urlbar-background-transparent: light-dark(
|
||||
rgba(255, 255, 255, .93),
|
||||
rgba(20, 20, 20, .93)
|
||||
);
|
||||
}
|
||||
}
|
||||
background-color: var(--zen-urlbar-background-transparent, var(--zen-urlbar-background-base)) !important;
|
||||
box-shadow: 0 30px 140px -15px light-dark(rgba(0, 0, 0, 0.8), rgba(0, 0, 0, 0.6)) !important;
|
||||
backdrop-filter: none !important;
|
||||
outline: 0.5px solid light-dark(rgba(0, 0, 0, 0.2), rgba(255, 255, 255, 0.2)) !important;
|
||||
outline: 0.5px solid light-dark(rgba(0, 0, 0, 0.2), rgb(75, 75, 75)) !important;
|
||||
outline-offset: var(--zen-urlbar-outline-offset) !important;
|
||||
|
||||
/* stylelint-disable-next-line media-query-no-invalid */
|
||||
@media -moz-pref("zen.theme.acrylic-elements") {
|
||||
backdrop-filter: var(--zen-urlbar-filter) !important;
|
||||
backdrop-filter: var(--zen-backdrop-underlay) blur(40px) !important;
|
||||
}
|
||||
}
|
||||
|
||||
&,
|
||||
.urlbar-background {
|
||||
border-radius: 12px !important;
|
||||
border-radius: 15px !important;
|
||||
}
|
||||
|
||||
&[breakout-extend][animate-searchmode="true"]::before {
|
||||
@@ -315,9 +321,6 @@
|
||||
}
|
||||
|
||||
:root[zen-single-toolbar="true"] {
|
||||
--urlbar-icon-border-radius: 8px !important;
|
||||
--urlbar-inner-border-radius: 6px !important;
|
||||
|
||||
#identity-permission-box > *:not(#zen-site-data-icon-button) {
|
||||
visibility: collapse;
|
||||
}
|
||||
@@ -509,7 +512,7 @@
|
||||
#urlbar[open][zen-floating-urlbar="true"] #identity-icon-box,
|
||||
:root[zen-single-toolbar="true"] #urlbar[breakout-extend] #identity-icon-box {
|
||||
--urlbar-box-hover-bgcolor: transparent;
|
||||
margin-inline: 2px 8px;
|
||||
margin-inline: 2px 4px;
|
||||
transition: none;
|
||||
}
|
||||
|
||||
@@ -535,8 +538,8 @@
|
||||
.urlbarView-favicon {
|
||||
margin-left: 0 !important;
|
||||
margin-right: 12px !important;
|
||||
padding: 6px !important;
|
||||
border-radius: 3px !important;
|
||||
padding: 4px !important;
|
||||
border-radius: 4px !important;
|
||||
}
|
||||
|
||||
.urlbarView-userContext {
|
||||
@@ -558,22 +561,24 @@
|
||||
}
|
||||
|
||||
.urlbarView-prettyName,
|
||||
.urlbarView-tabGroup,
|
||||
.urlbarView-shortcutContent {
|
||||
border-radius: 4px;
|
||||
border-radius: 4px !important;
|
||||
font-weight: 600;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
margin-top: auto !important;
|
||||
margin-bottom: auto !important;
|
||||
}
|
||||
|
||||
.urlbarView-prettyName {
|
||||
padding: 4px 6px;
|
||||
background-color: color-mix(in srgb, var(--zen-branding-bg-reverse), transparent 90%);
|
||||
margin-left: 6px;
|
||||
.urlbarView-prettyName,
|
||||
.urlbarView-tabGroup {
|
||||
padding: 4px 6px !important;
|
||||
background-color: color-mix(in srgb, var(--zen-branding-bg-reverse), transparent 90%) !important;
|
||||
margin-left: 6px !important;
|
||||
font-size: 12px;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
display: flex;
|
||||
color: color-mix(in srgb, var(--zen-primary-color), currentColor 95%);
|
||||
color: color-mix(in srgb, var(--zen-primary-color), currentColor 95%) !important;
|
||||
|
||||
& img {
|
||||
-moz-context-properties: fill;
|
||||
@@ -649,18 +654,19 @@
|
||||
|
||||
.urlbarView-row {
|
||||
--urlbarView-row-padding-inline: 8px;
|
||||
--urlbarview-row-padding-block: 10px;
|
||||
--urlbarview-row-padding-block: 12px;
|
||||
--urlbarView-row-gutter: 0px;
|
||||
|
||||
color: light-dark(rgba(0, 0, 0, 0.7), rgba(255, 255, 255, 0.7)) !important;
|
||||
|
||||
&:hover {
|
||||
&,
|
||||
& .urlbarView-favicon {
|
||||
background-color: color-mix(in srgb, var(--zen-branding-bg-reverse) 5%, transparent 95%) !important;
|
||||
}
|
||||
&:hover,
|
||||
&:hover .urlbarView-favicon,
|
||||
.urlbarView-action-favicon {
|
||||
background-color: color-mix(in srgb, var(--zen-branding-bg-reverse) 5%, transparent 95%) !important;
|
||||
}
|
||||
|
||||
&[selected] {
|
||||
--zen-selected-bg: color-mix(in srgb, var(--zen-primary-color) 50%, light-dark(rgba(0, 0, 0, 0.5), rgba(255, 255, 255, 0.1)));
|
||||
--zen-selected-bg: color-mix(in srgb, var(--zen-primary-color) 50%, light-dark(rgba(0, 0, 0, 0.3), rgba(255, 255, 255, 0.1)));
|
||||
--zen-selected-color: color-mix(in srgb, var(--zen-selected-bg), black 30%);
|
||||
background-color: var(--zen-selected-bg) !important;
|
||||
|
||||
@@ -684,14 +690,15 @@
|
||||
background-color: rgba(255, 255, 255, 0.9) !important;
|
||||
}
|
||||
|
||||
& .urlbarView-prettyName {
|
||||
background-color: color-mix(in srgb, var(--zen-branding-bg-reverse), transparent 80%);
|
||||
& .urlbarView-prettyName,
|
||||
& .urlbarView-tabGroup {
|
||||
background-color: color-mix(in srgb, var(--zen-branding-bg-reverse), transparent 80%) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#urlbar-results {
|
||||
max-height: 252px;
|
||||
max-height: 244px;
|
||||
overflow-y: auto;
|
||||
scrollbar-width: none;
|
||||
margin-block-start: calc(var(--urlbarView-results-padding) - 2px);
|
||||
|
||||
@@ -32,6 +32,7 @@ panel[type="arrow"]:not(#feature-callout) {
|
||||
|
||||
@media (-moz-platform: linux) or ((-moz-platform: windows) and (not (-moz-windows-mica-popups))) {
|
||||
&::part(content) {
|
||||
/* stylelint-disable-next-line property-no-unknown */
|
||||
corner-shape: round;
|
||||
border-radius: calc(var(--panel-border-radius) / var(--zen-squircle-value));
|
||||
}
|
||||
|
||||
@@ -411,3 +411,7 @@ menuseparator {
|
||||
#window-modal-dialog {
|
||||
inset: 0;
|
||||
}
|
||||
|
||||
.tab-preview-thumbnail-container > canvas {
|
||||
border-radius: calc(var(--border-radius-medium) / var(--zen-squircle-value));
|
||||
}
|
||||
|
||||
@@ -800,3 +800,16 @@
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
#tabbrowser-tabs:is([zen-library-stack-open], [zen-library-stack-closing]) {
|
||||
--gradient-pos: calc(100% - var(--zen-library-stack-height, 0px) + 20px);
|
||||
mask-image: linear-gradient(
|
||||
to bottom,
|
||||
black
|
||||
calc(var(--gradient-pos) - 50px),
|
||||
color-mix(transparent calc(var(--zen-library-progress) * 100%), black)
|
||||
var(--gradient-pos));
|
||||
}
|
||||
|
||||
%include zen-library-widget.css
|
||||
%include zen-library-media-preview.css
|
||||
|
||||
@@ -303,7 +303,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
#main-window[windowtype="navigator:browser"]:not([chromehidden~='toolbar']) {
|
||||
#main-window[windowtype="navigator:browser"]:not([popup-window]) {
|
||||
min-height: 495px !important;
|
||||
|
||||
@media (-moz-windows-mica) or (-moz-platform: macos) or ((-moz-platform: linux) and
|
||||
|
||||
@@ -4,17 +4,25 @@
|
||||
|
||||
import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
|
||||
|
||||
const lazy = {};
|
||||
ChromeUtils.defineESModuleGetters(lazy, {
|
||||
ZenLibraryWidget: "moz-src:///zen/library/ZenLibraryWidget.sys.mjs",
|
||||
});
|
||||
|
||||
export const ZenCustomizableUI = new (class {
|
||||
constructor() {}
|
||||
|
||||
TYPE_TOOLBAR = "toolbar";
|
||||
defaultSidebarIcons = [
|
||||
"downloads-button",
|
||||
Services.prefs.getBoolPref("zen.library.enabled")
|
||||
? "zen-library-button"
|
||||
: "downloads-button",
|
||||
"zen-workspaces-button",
|
||||
"zen-create-new-button",
|
||||
];
|
||||
|
||||
startup(CustomizableUIInternal) {
|
||||
CustomizableUIInternal.createBuiltinWidget(lazy.ZenLibraryWidget);
|
||||
CustomizableUIInternal.registerArea(
|
||||
"zen-sidebar-top-buttons",
|
||||
{
|
||||
|
||||
@@ -7,7 +7,10 @@ import { AppConstants } from "resource://gre/modules/AppConstants.sys.mjs";
|
||||
const lazy = {};
|
||||
|
||||
ChromeUtils.defineESModuleGetters(lazy, {
|
||||
SessionStore: "resource:///modules/sessionstore/SessionStore.sys.mjs",
|
||||
CustomizableUI:
|
||||
"moz-src:///browser/components/customizableui/CustomizableUI.sys.mjs",
|
||||
SessionStore:
|
||||
"moz-src:///browser/components/sessionstore/SessionStore.sys.mjs",
|
||||
});
|
||||
|
||||
class nsZenUIMigration {
|
||||
@@ -22,6 +25,7 @@ class nsZenUIMigration {
|
||||
console.error("ZenUIMigration: Error during migration", e);
|
||||
}
|
||||
}
|
||||
this.#migrateLibraryButton();
|
||||
this.clearVariables();
|
||||
if (this.shouldRestart) {
|
||||
Services.startup.quit(
|
||||
@@ -50,6 +54,41 @@ class nsZenUIMigration {
|
||||
this._migrationVersion = this.MIGRATION_VERSION;
|
||||
}
|
||||
|
||||
#migrateLibraryButton() {
|
||||
const donePref = "zen.library.migrated-downloads-button";
|
||||
if (
|
||||
!Services.prefs.getBoolPref("zen.library.enabled", false) ||
|
||||
Services.prefs.getBoolPref(donePref, false)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
// A toolbar's saved placements are only there once a window has it.
|
||||
const footButtons = "zen-sidebar-foot-buttons";
|
||||
const listener = {
|
||||
onAreaNodeRegistered: area => {
|
||||
if (area !== footButtons) {
|
||||
return;
|
||||
}
|
||||
lazy.CustomizableUI.removeListener(listener);
|
||||
Services.prefs.setBoolPref(donePref, true);
|
||||
const downloads =
|
||||
lazy.CustomizableUI.getPlacementOfWidget("downloads-button");
|
||||
if (
|
||||
downloads?.area === footButtons &&
|
||||
!lazy.CustomizableUI.getPlacementOfWidget("zen-library-button")
|
||||
) {
|
||||
lazy.CustomizableUI.addWidgetToArea(
|
||||
"zen-library-button",
|
||||
footButtons,
|
||||
downloads.position
|
||||
);
|
||||
lazy.CustomizableUI.removeWidgetFromArea("downloads-button");
|
||||
}
|
||||
},
|
||||
};
|
||||
lazy.CustomizableUI.addListener(listener);
|
||||
}
|
||||
|
||||
_migrateV1() {
|
||||
// If there's an userChrome.css or userContent.css existing, we set
|
||||
// 'toolkit.legacyUserProfileCustomizations.stylesheets' back to true
|
||||
|
||||
@@ -160,12 +160,20 @@ document.addEventListener(
|
||||
}
|
||||
case "cmd_zenDuplicateTab": {
|
||||
const selectedTabs = gBrowser.selectedTabs;
|
||||
let insertAt = selectedTabs.at(-1)._tPos + 1;
|
||||
let insertAt = selectedTabs.at(-1).index + 1;
|
||||
for (const tab of selectedTabs) {
|
||||
gBrowser.duplicateTab(tab, true, { tabIndex: insertAt++ });
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "cmd_zenToggleLibrary": {
|
||||
const { ZenLibrary } = ChromeUtils.importESModule(
|
||||
"moz-src:///zen/library/ZenLibrary.mjs",
|
||||
{ global: "current" }
|
||||
);
|
||||
ZenLibrary.toggle();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
gZenGlanceManager.handleMainCommandSet(event);
|
||||
if (event.target.id.startsWith("cmd_zenWorkspaceSwitch")) {
|
||||
|
||||
@@ -109,7 +109,7 @@
|
||||
if (AppConstants.platform == "macosx") {
|
||||
const targetRadius = window.matchMedia("(-moz-mac-tahoe-theme)")
|
||||
.matches
|
||||
? 12
|
||||
? 11
|
||||
: 10;
|
||||
document.documentElement.style.setProperty(
|
||||
"--zen-border-radius",
|
||||
|
||||
@@ -61,6 +61,12 @@ ChromeUtils.defineLazyGetter(lazy, "mainAppWrapper", () =>
|
||||
document.getElementById("zen-main-app-wrapper")
|
||||
);
|
||||
|
||||
ChromeUtils.defineESModuleGetters(
|
||||
lazy,
|
||||
{ ZenLibrary: "moz-src:///zen/library/ZenLibrary.mjs" },
|
||||
{ global: "current" }
|
||||
);
|
||||
|
||||
window.gZenCompactModeManager = {
|
||||
_flashTimeouts: {},
|
||||
_eventListeners: [],
|
||||
@@ -165,12 +171,13 @@ window.gZenCompactModeManager = {
|
||||
},
|
||||
|
||||
get shouldBeCompact() {
|
||||
return !document.documentElement
|
||||
.getAttribute("chromehidden")
|
||||
?.includes("toolbar");
|
||||
return !document.documentElement.hasAttribute("popup-window");
|
||||
},
|
||||
|
||||
set preference(value) {
|
||||
if (lazy.ZenLibrary.isLibrarySlightlyOpen) {
|
||||
return;
|
||||
}
|
||||
if (!this.shouldBeCompact) {
|
||||
value = false;
|
||||
}
|
||||
@@ -543,7 +550,7 @@ window.gZenCompactModeManager = {
|
||||
ease: "easeIn",
|
||||
type: "spring",
|
||||
bounce: 0,
|
||||
duration: 0.12,
|
||||
duration: 0.1,
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
@@ -599,7 +606,7 @@ window.gZenCompactModeManager = {
|
||||
ease: "easeOut",
|
||||
type: "spring",
|
||||
bounce: 0,
|
||||
duration: 0.12,
|
||||
duration: 0.1,
|
||||
}
|
||||
)
|
||||
.then(() => {
|
||||
@@ -864,7 +871,6 @@ window.gZenCompactModeManager = {
|
||||
"supress-primary-adjustment"
|
||||
) === "true" &&
|
||||
gZenVerticalTabsManager._hasSetSingleToolbar) ||
|
||||
this._hasHoveredUrlbar ||
|
||||
this._ignoreNextHover ||
|
||||
(event.type === "dragleave" &&
|
||||
event.explicitOriginalTarget !== target &&
|
||||
@@ -877,7 +883,10 @@ window.gZenCompactModeManager = {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.hoverableElements[i].keepHoverDuration) {
|
||||
if (
|
||||
this.hoverableElements[i].keepHoverDuration &&
|
||||
!this._hasHoveredUrlbar
|
||||
) {
|
||||
this.flashElement(
|
||||
target,
|
||||
this.hoverableElements[i].keepHoverDuration,
|
||||
|
||||
@@ -14,8 +14,10 @@
|
||||
background: light-dark(#e9e9e9, #131313);
|
||||
box-shadow: var(--zen-big-shadow);
|
||||
@media -moz-pref('zen.theme.acrylic-elements') {
|
||||
background: transparent;
|
||||
backdrop-filter: blur(42px) saturate(110%) brightness(0.25) contrast(100%) !important;
|
||||
&:not(:-moz-window-inactive) {
|
||||
background: transparent;
|
||||
backdrop-filter: blur(42px) saturate(110%) brightness(0.25) contrast(100%) !important;
|
||||
}
|
||||
}
|
||||
|
||||
&::before,
|
||||
|
||||
@@ -148,9 +148,11 @@ class nsZenDownloadAnimationElement extends HTMLElement {
|
||||
}
|
||||
|
||||
#determineEndPosition() {
|
||||
const downloadsButton = document.getElementById("downloads-button");
|
||||
const isDownloadButtonVisible =
|
||||
downloadsButton && this.#isElementVisible(downloadsButton);
|
||||
const downloadsButton = [
|
||||
document.getElementById("zen-library-button"),
|
||||
document.getElementById("downloads-button"),
|
||||
].find(button => button && this.#isElementVisible(button));
|
||||
const isDownloadButtonVisible = !!downloadsButton;
|
||||
|
||||
let endPosition = { clientX: 0, clientY: 0 };
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
width: 50%;
|
||||
height: 50%;
|
||||
background-color: var(--zen-primary-color);
|
||||
mask: url("chrome://browser/content/zen-images/downloads/archive.svg") no-repeat center;
|
||||
mask: url("chrome://browser/skin/zen-icons/library/library.svg") no-repeat center;
|
||||
mask-size: contain;
|
||||
display: block;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
* - <tab-group> label element wrapper (.tab-group-label-container)
|
||||
*
|
||||
* When working with tab strip items, if you need logical information, you
|
||||
* can get it directly, e.g. `element.elementIndex` or `element._tPos`. If
|
||||
* can get it directly, e.g. `element.elementIndex` or `element._index`. If
|
||||
* you need spatial information like position or dimensions, then you should
|
||||
* call this function. For example, `elementToMove(element).getBoundingClientRect()`
|
||||
* or `elementToMove(element).style.top`.
|
||||
@@ -69,6 +69,7 @@
|
||||
#changeSpaceTimer = null;
|
||||
#isAnimatingTabMove = false;
|
||||
#firstHapticFeedbackPlayed = false;
|
||||
#dragImageEssential = false;
|
||||
|
||||
#dragOverSplit = {};
|
||||
|
||||
@@ -136,6 +137,7 @@
|
||||
}
|
||||
|
||||
startTabDrag(event, tab, ...args) {
|
||||
const options = args[0] ?? {};
|
||||
this.ZenDragAndDropService.onDragStart(1);
|
||||
this.#isOutOfWindow = false;
|
||||
gZenCompactModeManager._isTabBeingDragged = true;
|
||||
@@ -144,83 +146,112 @@
|
||||
if (isTabGroupLabel(tab)) {
|
||||
tab = tab.group;
|
||||
}
|
||||
const draggingTabs = tab.multiselected ? gBrowser.selectedTabs : [tab];
|
||||
const shown = options.dragImageSource ?? tab;
|
||||
let draggingTabs;
|
||||
if (options.dragImageSource) {
|
||||
draggingTabs = [shown];
|
||||
} else {
|
||||
draggingTabs = tab.multiselected ? gBrowser.selectedTabs : [tab];
|
||||
}
|
||||
const { offsetX, offsetY } = this.#getDragImageOffset(
|
||||
event,
|
||||
tab,
|
||||
shown,
|
||||
draggingTabs
|
||||
);
|
||||
const dragImage = this.#createDragImageForTabs(draggingTabs);
|
||||
const dragImage = this._createDragImageForTabs(draggingTabs);
|
||||
this.originalDragImageArgs = [dragImage, offsetX, offsetY];
|
||||
this.#dragImageEssential = tab.hasAttribute("zen-essential");
|
||||
dt.setDragImage(...this.originalDragImageArgs);
|
||||
if (tab.hasAttribute("zen-essential")) {
|
||||
tab.style.visibility = "hidden";
|
||||
}
|
||||
this._dropLandingArmed =
|
||||
options.armLanding ??
|
||||
(!args[0]?.fromTabList &&
|
||||
this.getDropEffectForTabDrag(event) == "move");
|
||||
this.ZenDragAndDropService.armDropLanding(this._dropLandingArmed);
|
||||
}
|
||||
|
||||
#createDragImageForTabs(movingTabs) {
|
||||
_createDragImageForTabs(movingTabs) {
|
||||
const periphery = gZenWorkspaces.activeWorkspaceElement.querySelector(
|
||||
"#tabbrowser-arrowscrollbox-periphery"
|
||||
);
|
||||
const tabRect = window.windowUtils.getBoundsWithoutFlushing(
|
||||
movingTabs[0]
|
||||
);
|
||||
const native = AppConstants.platform == "macosx";
|
||||
const stacked = native ? 1 : Math.min(movingTabs.length, 3);
|
||||
const wrapper = this.#createDragImageWrapper(periphery, tabRect, stacked);
|
||||
for (let i = 0; i < stacked; i++) {
|
||||
this.#appendDragImageClone(wrapper, movingTabs[i], tabRect, i);
|
||||
}
|
||||
this._dragImageTabs = native ? [...movingTabs] : [movingTabs[0]];
|
||||
this._extraDragImages = [];
|
||||
if (native) {
|
||||
for (const tab of movingTabs.slice(1)) {
|
||||
const extra = this.#createDragImageWrapper(periphery, tabRect, 1);
|
||||
this.#appendDragImageClone(extra, tab, tabRect, 0);
|
||||
this.ZenDragAndDropService.addDragImage(extra);
|
||||
this._extraDragImages.push(extra);
|
||||
}
|
||||
} else {
|
||||
this.#maybeCreateDragImageDot(movingTabs, wrapper);
|
||||
}
|
||||
this._tempDragImageParent = wrapper;
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
#createDragImageWrapper(periphery, tabRect, count) {
|
||||
const wrapper = document.createElement("div");
|
||||
let movingTabsCount = Math.min(movingTabs.length, 3);
|
||||
wrapper.style.width = tabRect.width + "px";
|
||||
wrapper.style.height = tabRect.height * movingTabsCount + "px";
|
||||
wrapper.style.height = tabRect.height * count + "px";
|
||||
wrapper.style.position = "fixed";
|
||||
wrapper.style.top = "-9999px";
|
||||
periphery.appendChild(wrapper);
|
||||
for (let i = 0; i < movingTabsCount; i++) {
|
||||
const tab = movingTabs[i];
|
||||
const tabClone = tab.cloneNode(true);
|
||||
if (tab.hasAttribute("zen-essential")) {
|
||||
const rect = tab.getBoundingClientRect();
|
||||
tabClone.style.minWidth = tabClone.style.maxWidth = `${rect.width}px`;
|
||||
tabClone.style.minHeight =
|
||||
tabClone.style.maxHeight = `${rect.height}px`;
|
||||
if (tabClone.hasAttribute("visuallyselected")) {
|
||||
tabClone.style.transform = "translate(-50%, -50%)";
|
||||
}
|
||||
} else if (AppConstants.platform == "win" && !tab.isZenFolder) {
|
||||
// On windows, the shell applies its own translucency to the drag
|
||||
// image which we can't control, so force some extra contrast for
|
||||
// the tab to be more visible. This is a hacky workaround.
|
||||
tabClone.style.colorScheme = "light";
|
||||
tabClone.style.setProperty("--tab-selected-textcolor", "black");
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
#appendDragImageClone(wrapper, tab, tabRect, i) {
|
||||
const tabClone = tab.cloneNode(true);
|
||||
if (tab.hasAttribute("zen-essential")) {
|
||||
const rect = tab.getBoundingClientRect();
|
||||
tabClone.style.minWidth = tabClone.style.maxWidth = `${rect.width}px`;
|
||||
tabClone.style.minHeight =
|
||||
tabClone.style.maxHeight = `${rect.height}px`;
|
||||
if (tabClone.hasAttribute("visuallyselected")) {
|
||||
tabClone.style.transform = "translate(-50%, -50%)";
|
||||
}
|
||||
if (i > 0) {
|
||||
tabClone.style.transform = `translate(${i * 4}px, -${i * (tabRect.height - 4)}px)`;
|
||||
tabClone.style.opacity = "0.2";
|
||||
tabClone.style.zIndex = `${-i}`;
|
||||
}
|
||||
tabClone.setAttribute("drag-image", "true");
|
||||
wrapper.appendChild(tabClone);
|
||||
if (isTab(tabClone)) {
|
||||
// We need to limit the label content so the drag image doesn't grow too big.
|
||||
const label = tabClone.textLabel;
|
||||
const tabLabelParentWidth =
|
||||
label.parentElement.getBoundingClientRect().width;
|
||||
label.textContent = label.textContent.slice(
|
||||
0,
|
||||
Math.floor(tabLabelParentWidth / 6)
|
||||
);
|
||||
} else if (
|
||||
gBrowser.isTabGroup(tabClone) &&
|
||||
tabClone.hasAttribute("split-view-group")
|
||||
) {
|
||||
let tabs = tab.tabs;
|
||||
for (let j = 0; j < tabs.length; j++) {
|
||||
const tabInGroup = tabs[j];
|
||||
const tabInGroupClone = tabInGroup.cloneNode(true);
|
||||
tabClone.appendChild(tabInGroupClone);
|
||||
}
|
||||
} else if (AppConstants.platform == "win" && !tab.isZenFolder) {
|
||||
// On windows, the shell applies its own translucency to the drag
|
||||
// image which we can't control, so force some extra contrast for
|
||||
// the tab to be more visible. This is a hacky workaround.
|
||||
tabClone.style.colorScheme = "light";
|
||||
tabClone.style.setProperty("--tab-selected-textcolor", "black");
|
||||
}
|
||||
if (i > 0) {
|
||||
tabClone.style.transform = `translate(${i * 4}px, -${i * (tabRect.height - 4)}px)`;
|
||||
tabClone.style.opacity = "0.2";
|
||||
tabClone.style.zIndex = `${-i}`;
|
||||
}
|
||||
tabClone.setAttribute("drag-image", "true");
|
||||
wrapper.appendChild(tabClone);
|
||||
if (isTab(tabClone)) {
|
||||
// We need to limit the label content so the drag image doesn't grow too big.
|
||||
const label = tabClone.textLabel;
|
||||
const tabLabelParentWidth =
|
||||
label.parentElement.getBoundingClientRect().width;
|
||||
label.textContent = label.textContent.slice(
|
||||
0,
|
||||
Math.floor(tabLabelParentWidth / 6)
|
||||
);
|
||||
} else if (
|
||||
gBrowser.isTabGroup(tabClone) &&
|
||||
tabClone.hasAttribute("split-view-group")
|
||||
) {
|
||||
for (const tabInGroup of tab.tabs) {
|
||||
tabClone.appendChild(tabInGroup.cloneNode(true));
|
||||
}
|
||||
}
|
||||
this.#maybeCreateDragImageDot(movingTabs, wrapper);
|
||||
this._tempDragImageParent = wrapper;
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
#maybeCreateDragImageDot(movingTabs, wrapper) {
|
||||
@@ -255,7 +286,10 @@
|
||||
return;
|
||||
}
|
||||
return this.#animateVerticalPinnedGridDragOver(event);
|
||||
} else if (this._fakeEssentialTab) {
|
||||
} else if (
|
||||
this.#dragImageEssential &&
|
||||
!event.target.closest("zen-essentials-promo")
|
||||
) {
|
||||
this.#makeDragImageNonEssential(event);
|
||||
}
|
||||
let dragData = draggedTab._dragData;
|
||||
@@ -557,7 +591,7 @@
|
||||
dropElementSize
|
||||
);
|
||||
|
||||
moveOverThreshold = gBrowser._tabGroupsEnabled
|
||||
moveOverThreshold = gBrowser.tabGroupsEnabled
|
||||
? Services.prefs.getIntPref(
|
||||
"browser.tabs.dragDrop.moveOverThresholdPercent"
|
||||
) / 100
|
||||
@@ -650,7 +684,7 @@
|
||||
return;
|
||||
}
|
||||
this.#handle_sidebarDragOver(event);
|
||||
this.#handle_tabDragOverToSplit(event);
|
||||
this._handle_tabDragOverToSplit(event);
|
||||
}
|
||||
|
||||
#shouldSwitchSpace(event) {
|
||||
@@ -696,16 +730,30 @@
|
||||
|
||||
#onSpaceChanged(spaceChanged, dt) {
|
||||
if (AppConstants.platform !== "macosx") {
|
||||
// See the hack in #createDragImageForTabs for more details which
|
||||
// See the hack in _createDragImageForTabs for more details which
|
||||
// explains why we need to do this on non-macOS platforms.
|
||||
return;
|
||||
}
|
||||
let tabs = this.originalDragImageArgs[0].children;
|
||||
this._recolorDragImage(spaceChanged);
|
||||
this._refreshDragImage(dt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recolours the current drag image's tab clones for a workspace's theme,
|
||||
* so the image reads as the space it is over.
|
||||
*
|
||||
* @param {object} workspace
|
||||
*/
|
||||
_recolorDragImage(workspace) {
|
||||
const wrapper = this.originalDragImageArgs?.[0];
|
||||
if (!wrapper || !workspace) {
|
||||
return;
|
||||
}
|
||||
const { isDarkMode, isExplicitMode } =
|
||||
gZenThemePicker.getGradientForWorkspace(spaceChanged, {
|
||||
gZenThemePicker.getGradientForWorkspace(workspace, {
|
||||
getGradient: false,
|
||||
});
|
||||
for (let tab of tabs) {
|
||||
for (const tab of wrapper.querySelectorAll("tab")) {
|
||||
if (isExplicitMode) {
|
||||
tab.style.colorScheme = isDarkMode ? "dark" : "light";
|
||||
tab.style.setProperty(
|
||||
@@ -717,9 +765,21 @@
|
||||
tab.style.removeProperty("--tab-selected-textcolor");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-hands the drag image to the drag session once its new styles have
|
||||
* been laid out.
|
||||
*
|
||||
* @param {DataTransfer} dt
|
||||
*/
|
||||
_refreshDragImage(dt) {
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(() => {
|
||||
dt.updateDragImage(...this.originalDragImageArgs);
|
||||
// The drag may have ended over the two frames, clearing the image.
|
||||
if (this.originalDragImageArgs.length) {
|
||||
dt.updateDragImage(...this.originalDragImageArgs);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -777,7 +837,60 @@
|
||||
});
|
||||
}
|
||||
|
||||
#handle_tabDragOverToSplit(event) {
|
||||
/**
|
||||
* The element a drop on `element` acts on.
|
||||
*
|
||||
* @param {Element} element
|
||||
* @returns {Element}
|
||||
*/
|
||||
_targetForDrop(element) {
|
||||
return element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a drop lands before (vs after) an element, from how far down its
|
||||
* box the pointer sits and the move-over threshold pref.
|
||||
*
|
||||
* @param {DragEvent} event
|
||||
* @param {DOMRect} rect - The element's bounds
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_dropsBefore(event, rect) {
|
||||
const threshold =
|
||||
Services.prefs.getIntPref(
|
||||
"browser.tabs.dragDrop.moveOverThresholdPercent"
|
||||
) / 100;
|
||||
return (event.clientY - rect.top) / rect.height <= threshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lays the shared drop indicator across an element, horizontally.
|
||||
*
|
||||
* @param {object} options
|
||||
* @param {Element} [options.parent] - Reparent the indicator here first
|
||||
* @param {number} options.left - Inset from the left, in px
|
||||
* @param {number} options.width - Indicator width, in px
|
||||
* @param {number} options.top - Top offset, in px
|
||||
* @returns {Element} The indicator
|
||||
*/
|
||||
_placeDropIndicator({ parent = null, left, width, top }) {
|
||||
const indicator = gZenPinnedTabManager.dragIndicator;
|
||||
if (parent && indicator.parentNode !== parent) {
|
||||
parent.appendChild(indicator);
|
||||
}
|
||||
indicator.setAttribute("orientation", "horizontal");
|
||||
indicator.style.setProperty("--indicator-left", `${left}px`);
|
||||
indicator.style.setProperty("--indicator-width", `${width}px`);
|
||||
indicator.style.top = `${Math.round(top)}px`;
|
||||
indicator.style.removeProperty("left");
|
||||
return indicator;
|
||||
}
|
||||
|
||||
get _splitDropReady() {
|
||||
return !!this.#dragOverSplit.canDrop;
|
||||
}
|
||||
|
||||
_handle_tabDragOverToSplit(event) {
|
||||
if (!this._dndSplitEnabled) {
|
||||
return;
|
||||
}
|
||||
@@ -806,7 +919,7 @@
|
||||
}
|
||||
|
||||
if (
|
||||
movingTabsSet.has(dropElement) ||
|
||||
movingTabsSet.has(this._targetForDrop(dropElement)) ||
|
||||
!isTab(draggedTab) ||
|
||||
draggedTab?.group?.hasAttribute("split-view-group") ||
|
||||
draggedTab.hasAttribute("zen-live-folder-item-id") ||
|
||||
@@ -882,6 +995,8 @@
|
||||
|
||||
this.#dragOverSplit.fakeTab = element;
|
||||
this.#dragOverSplit.canDrop = true;
|
||||
// A drop into the split takes the drag image away at once.
|
||||
this.ZenDragAndDropService.armDropLanding(false);
|
||||
}
|
||||
|
||||
_clearDragOverSplit() {
|
||||
@@ -889,6 +1004,9 @@
|
||||
clearTimeout(this.#dragOverSplit.timer);
|
||||
}
|
||||
this.#dragOverSplit.fakeTab?.remove();
|
||||
if (this.#dragOverSplit.canDrop && this._dropLandingArmed) {
|
||||
this.ZenDragAndDropService.armDropLanding(true);
|
||||
}
|
||||
|
||||
this.#dragOverSplit.timer = null;
|
||||
this.#dragOverSplit.fakeTab = null;
|
||||
@@ -902,9 +1020,54 @@
|
||||
}
|
||||
this.#isOutOfWindow = false;
|
||||
const dt = event.dataTransfer;
|
||||
for (const extra of this._extraDragImages ?? []) {
|
||||
this.#restoreExtraDragImage(extra);
|
||||
}
|
||||
dt.updateDragImage(...this.originalDragImageArgs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Out of the window, every dragged tab becomes the window. Each further
|
||||
* drag item gets a copy of the preview the drag image shows.
|
||||
*
|
||||
* @param {Element} extra - The image of a further tab
|
||||
* @param {HTMLCanvasElement} canvas - The window preview
|
||||
*/
|
||||
#previewExtraDragImage(extra, canvas) {
|
||||
if (extra._tabContents) {
|
||||
return;
|
||||
}
|
||||
extra._tabContents = {
|
||||
nodes: [...extra.childNodes],
|
||||
width: extra.style.width,
|
||||
height: extra.style.height,
|
||||
};
|
||||
const copy = document.createElementNS(
|
||||
"http://www.w3.org/1999/xhtml",
|
||||
"canvas"
|
||||
);
|
||||
copy.width = canvas.width;
|
||||
copy.height = canvas.height;
|
||||
copy.getContext("2d").drawImage(canvas, 0, 0);
|
||||
copy.style.width = copy.style.height = "100%";
|
||||
copy.style.borderRadius = canvas.style.borderRadius;
|
||||
copy.style.border = canvas.style.border;
|
||||
extra.style.width = this._browserDragImageWrapper.style.width;
|
||||
extra.style.height = this._browserDragImageWrapper.style.height;
|
||||
extra.replaceChildren(copy);
|
||||
}
|
||||
|
||||
#restoreExtraDragImage(extra) {
|
||||
const contents = extra._tabContents;
|
||||
if (!contents) {
|
||||
return;
|
||||
}
|
||||
delete extra._tabContents;
|
||||
extra.style.width = contents.width;
|
||||
extra.style.height = contents.height;
|
||||
extra.replaceChildren(...contents.nodes);
|
||||
}
|
||||
|
||||
handle_windowDragLeave(event) {
|
||||
// If relatedTarget exists, then we are still in the window
|
||||
if (
|
||||
@@ -930,8 +1093,6 @@
|
||||
this.clearDragOverVisuals();
|
||||
this.clearSpaceSwitchTimer();
|
||||
const dt = event.dataTransfer;
|
||||
let dragData = draggedTab._dragData;
|
||||
let movingTabs = dragData.movingTabs;
|
||||
if (!this._browserDragImageWrapper) {
|
||||
const wrappingDiv = document.createXULElement("vbox");
|
||||
canvas.style.borderRadius = "8px";
|
||||
@@ -939,11 +1100,13 @@
|
||||
wrappingDiv.style.width = 200 + "px";
|
||||
wrappingDiv.style.height = 130 + "px";
|
||||
wrappingDiv.style.position = "relative";
|
||||
this.#maybeCreateDragImageDot(movingTabs, wrappingDiv);
|
||||
wrappingDiv.appendChild(canvas);
|
||||
this._browserDragImageWrapper = wrappingDiv;
|
||||
document.documentElement.appendChild(wrappingDiv);
|
||||
}
|
||||
for (const extra of this._extraDragImages ?? []) {
|
||||
this.#previewExtraDragImage(extra, canvas);
|
||||
}
|
||||
dt.updateDragImage(
|
||||
this._browserDragImageWrapper,
|
||||
this.originalDragImageArgs[1],
|
||||
@@ -970,11 +1133,82 @@
|
||||
}
|
||||
this.clearSpaceSwitchTimer();
|
||||
gZenFolders.highlightGroupOnDragOver(null);
|
||||
const toSplit = !!this.#dragOverSplit.canDrop;
|
||||
const placesBefore = new Map(
|
||||
this.#draggedElements(event).map(element => [
|
||||
element,
|
||||
this._placeOf(element),
|
||||
])
|
||||
);
|
||||
super.handle_drop(event);
|
||||
this.#maybeClearVerticalPinnedGridDragOver();
|
||||
this.#handle_dropSwitchSpace(event);
|
||||
this.#handle_dropCreateSplit(event);
|
||||
this._handle_dropCreateSplit(event);
|
||||
this._clearDragOverSplit();
|
||||
if (!toSplit) {
|
||||
this.#landDragImage(event, placesBefore);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {DragEvent} event
|
||||
* @returns {Element[]} The elements of the dragged tabs, in the order of
|
||||
* their drag images, when they are this window's
|
||||
*/
|
||||
#draggedElements(event) {
|
||||
const draggedTab = event.dataTransfer.mozGetDataAt(TAB_DROP_TYPE, 0);
|
||||
if (draggedTab?.documentGlobal !== window) {
|
||||
return [];
|
||||
}
|
||||
return (this._dragImageTabs ?? [draggedTab]).map(elementToMove);
|
||||
}
|
||||
|
||||
_placeOf(element) {
|
||||
return `${element.screenX},${element.screenY}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Has the drag image land on the dropped tab rather than vanish. The OS
|
||||
* slides it onto the tab's new place and ends the drag once it is there,
|
||||
* which is when the tab shows again. Only macOS does so.
|
||||
*
|
||||
* @param {DragEvent} event - The drop
|
||||
* @param {Map<Element, string>} placesBefore - Where each dragged element
|
||||
* was before the drop.
|
||||
*/
|
||||
#landDragImage(event, placesBefore) {
|
||||
// The drop is done, so its indicator goes before the image lands.
|
||||
this.clearDragOverVisuals();
|
||||
if (gReduceMotion) {
|
||||
return;
|
||||
}
|
||||
this._landDragImageOnElements(this.#draggedElements(event), placesBefore);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marks where the drag image lands, one item per element in order, and
|
||||
* keeps those elements out of sight until the drag ends.
|
||||
*
|
||||
* @param {Element[]} elements - Where the dropped items land
|
||||
* @param {Map<Element, string>} [placesBefore] - Where each element was
|
||||
* before the drop; one still there stays in sight under the image
|
||||
*/
|
||||
_landDragImageOnElements(elements, placesBefore = null) {
|
||||
const landing = [];
|
||||
for (const element of elements) {
|
||||
const { width, height } = element.getBoundingClientRect();
|
||||
this.ZenDragAndDropService.addDropLandingRect(
|
||||
Math.round(element.screenX),
|
||||
Math.round(element.screenY),
|
||||
Math.round(width),
|
||||
Math.round(height)
|
||||
);
|
||||
if (placesBefore?.get(element) !== this._placeOf(element)) {
|
||||
element.style.visibility = "hidden";
|
||||
landing.push(element);
|
||||
}
|
||||
}
|
||||
this._landingElements = landing;
|
||||
}
|
||||
|
||||
#handle_dropSwitchSpace(event) {
|
||||
@@ -1003,9 +1237,16 @@
|
||||
gZenWorkspaces.updateTabsContainers();
|
||||
}
|
||||
|
||||
#handle_dropCreateSplit(event) {
|
||||
/**
|
||||
* @param {DragEvent} event
|
||||
* @param {object} [options]
|
||||
* @param {boolean} [options.activate] - Whether to select the created split
|
||||
* @returns {boolean} Whether the drop split the dragged tab with the tab
|
||||
* it was held over
|
||||
*/
|
||||
_handle_dropCreateSplit(event, { activate = true } = {}) {
|
||||
if (!this.#dragOverSplit.canDrop) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
const dragData = this.#dragOverSplit.data;
|
||||
@@ -1013,11 +1254,11 @@
|
||||
const draggedTab = dt.mozGetDataAt(TAB_DROP_TYPE, 0);
|
||||
|
||||
if (!dragData || !draggedTab) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
this._dontAnimateTabMove = true;
|
||||
const droppedOnTab = dragData.dropElement;
|
||||
const droppedOnTab = this._targetForDrop(dragData.dropElement);
|
||||
const dropSide = dragData.dropSide;
|
||||
|
||||
// Clear any visuals and timer
|
||||
@@ -1027,10 +1268,13 @@
|
||||
gZenViewSplitter.splitTabs(
|
||||
isLeft ? [draggedTab, droppedOnTab] : [droppedOnTab, draggedTab],
|
||||
"vsep",
|
||||
isLeft ? 0 : 1
|
||||
isLeft ? 0 : 1,
|
||||
{ activate }
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line complexity
|
||||
handle_drop_transition(dropElement, draggedTab, movingTabs, dropBefore) {
|
||||
if (
|
||||
dropElement?.hasAttribute("zen-empty-tab") &&
|
||||
@@ -1056,6 +1300,7 @@
|
||||
try {
|
||||
if (
|
||||
this.#isAnimatingTabMove ||
|
||||
this.#dragOverSplit.canDrop ||
|
||||
!gZenStartup.isReady ||
|
||||
gReduceMotion ||
|
||||
!dropElement ||
|
||||
@@ -1175,6 +1420,10 @@
|
||||
let ownerGlobal = draggedTab?.documentGlobal;
|
||||
draggedTab.style.visibility = "";
|
||||
let thisFromGlobal = ownerGlobal?.gBrowser.tabContainer.tabDragAndDrop;
|
||||
for (const element of thisFromGlobal._landingElements ?? []) {
|
||||
element.style.visibility = "";
|
||||
}
|
||||
delete thisFromGlobal._landingElements;
|
||||
let currentEssenialContainer =
|
||||
ownerGlobal.gZenWorkspaces.getCurrentEssentialsContainer();
|
||||
if (currentEssenialContainer?.essentialsPromo) {
|
||||
@@ -1194,6 +1443,7 @@
|
||||
thisFromGlobal._clearDragOverSplit();
|
||||
this.#maybeClearVerticalPinnedGridDragOver();
|
||||
thisFromGlobal.originalDragImageArgs = [];
|
||||
thisFromGlobal._dropLandingArmed = false;
|
||||
this.#firstHapticFeedbackPlayed = false;
|
||||
window.removeEventListener(
|
||||
"dragenter",
|
||||
@@ -1211,6 +1461,11 @@
|
||||
thisFromGlobal._tempDragImageParent.remove();
|
||||
delete thisFromGlobal._tempDragImageParent;
|
||||
}
|
||||
for (const extra of thisFromGlobal._extraDragImages ?? []) {
|
||||
extra.remove();
|
||||
}
|
||||
delete thisFromGlobal._extraDragImages;
|
||||
delete thisFromGlobal._dragImageTabs;
|
||||
delete ownerGlobal.gZenCompactModeManager._isTabBeingDragged;
|
||||
if (dt.dropEffect !== "move") {
|
||||
ownerGlobal.gZenCompactModeManager._clearAllHoverStates();
|
||||
@@ -1404,33 +1659,17 @@
|
||||
this.#dragShiftableItems.at(-1)
|
||||
);
|
||||
}
|
||||
const indicator = gZenPinnedTabManager.dragIndicator;
|
||||
let top = 0;
|
||||
threshold =
|
||||
Services.prefs.getIntPref(
|
||||
"browser.tabs.dragDrop.moveOverThresholdPercent"
|
||||
) / 100;
|
||||
if (overlapPercent > threshold || showIndicatorUnderNewTabButton) {
|
||||
top = Math.round(rect.top + rect.height) + "px";
|
||||
dropBefore = false;
|
||||
} else {
|
||||
top = Math.round(rect.top) + "px";
|
||||
dropBefore = true;
|
||||
}
|
||||
if (indicator.style.top !== top) {
|
||||
dropBefore =
|
||||
!showIndicatorUnderNewTabButton && this._dropsBefore(event, rect);
|
||||
const top = Math.round(dropBefore ? rect.top : rect.top + rect.height);
|
||||
if (gZenPinnedTabManager.dragIndicator.style.top !== `${top}px`) {
|
||||
shouldPlayHapticFeedback = true;
|
||||
}
|
||||
indicator.setAttribute("orientation", "horizontal");
|
||||
indicator.style.setProperty(
|
||||
"--indicator-left",
|
||||
rect.left + separation / 2 + "px"
|
||||
);
|
||||
indicator.style.setProperty(
|
||||
"--indicator-width",
|
||||
rect.width - separation + "px"
|
||||
);
|
||||
indicator.style.top = top;
|
||||
indicator.style.removeProperty("left");
|
||||
this._placeDropIndicator({
|
||||
left: rect.left + separation / 2,
|
||||
width: rect.width - separation,
|
||||
top,
|
||||
});
|
||||
this.#removeDragOverBackground();
|
||||
if (!isTab(dropElement) && dropElement?.parentElement?.isZenFolder) {
|
||||
dropElement = dropElement.parentElement;
|
||||
@@ -1494,6 +1733,7 @@
|
||||
switch (essentialsPromoStatus) {
|
||||
case "shown":
|
||||
case "created":
|
||||
this.#makeDragImageEssential(event);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1781,53 +2021,131 @@
|
||||
#makeDragImageEssential(event) {
|
||||
const dt = event.dataTransfer;
|
||||
const draggedTab = event.dataTransfer.mozGetDataAt(TAB_DROP_TYPE, 0);
|
||||
if (draggedTab.hasAttribute("zen-essential")) {
|
||||
if (this.#dragImageEssential) {
|
||||
return;
|
||||
}
|
||||
this.#dragImageEssential = true;
|
||||
const dragData = draggedTab._dragData;
|
||||
const [wrapper] = this.originalDragImageArgs;
|
||||
const tab = wrapper.firstElementChild;
|
||||
tab.setAttribute("zen-essential", "true");
|
||||
tab.setAttribute("pinned", "true");
|
||||
tab.setAttribute("selected", "true");
|
||||
const draggedTabRect = window.windowUtils.getBoundsWithoutFlushing(
|
||||
this._fakeEssentialTab
|
||||
const wasEssential = draggedTab.hasAttribute("zen-essential");
|
||||
const draggedTabRect = this.#essentialTileRect(event, draggedTab);
|
||||
for (const { wrapper, tab } of this.#dragImageClones(draggedTab)) {
|
||||
tab.setAttribute("zen-essential", "true");
|
||||
tab.setAttribute("pinned", "true");
|
||||
tab.setAttribute("selected", "true");
|
||||
tab.style.minWidth =
|
||||
tab.style.maxWidth =
|
||||
wrapper.style.width =
|
||||
draggedTabRect.width + "px";
|
||||
tab.style.minHeight =
|
||||
tab.style.maxHeight =
|
||||
wrapper.style.height =
|
||||
draggedTabRect.height + "px";
|
||||
if (wasEssential) {
|
||||
// Back to how the drag began.
|
||||
tab.style.transform = "";
|
||||
continue;
|
||||
}
|
||||
const offsetY = dragData.offsetY;
|
||||
const offsetX = dragData.offsetX;
|
||||
// Apply a transform translate to the tab in order to center it within the drag image
|
||||
tab.style.transform = `translate(${(54 - offsetX) / 2}px, ${(50 - offsetY) / 2}px)`;
|
||||
gZenPinnedTabManager.setEssentialTabIcon(tab);
|
||||
}
|
||||
if (wasEssential) {
|
||||
dt.updateDragImage(...this.originalDragImageArgs);
|
||||
} else {
|
||||
dt.updateDragImage(this.originalDragImageArgs[0], -16, -16);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Every drag image with the tab it shows. The drag image itself and,
|
||||
* on macOS, one for each further dragged tab.
|
||||
*
|
||||
* @param {Element} draggedTab
|
||||
* @returns {{wrapper: Element, tab: Element, source: Element}[]} Each
|
||||
* image's wrapper, the clone in it, and the tab it was cloned from
|
||||
*/
|
||||
#dragImageClones(draggedTab) {
|
||||
const wrappers = [
|
||||
this.originalDragImageArgs[0],
|
||||
...(this._extraDragImages ?? []),
|
||||
];
|
||||
const sources = this._dragImageTabs ?? [draggedTab];
|
||||
return wrappers.map((wrapper, i) => ({
|
||||
wrapper,
|
||||
tab: wrapper.firstElementChild,
|
||||
source: sources[i] ?? draggedTab,
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* The size the dragged tab takes as an essential. Its own when it is
|
||||
* one, the tile made for it in the grid, or, with only the promo shown,
|
||||
* a tile as the grid would lay it out.
|
||||
*
|
||||
* @param {DragEvent} event
|
||||
* @param {Element} draggedTab
|
||||
* @returns {{width: number, height: number}}
|
||||
*/
|
||||
#essentialTileRect(event, draggedTab) {
|
||||
const bounds = element =>
|
||||
window.windowUtils.getBoundsWithoutFlushing(element);
|
||||
if (draggedTab.hasAttribute("zen-essential")) {
|
||||
return bounds(draggedTab);
|
||||
}
|
||||
if (this._fakeEssentialTab?.isConnected) {
|
||||
return bounds(this._fakeEssentialTab);
|
||||
}
|
||||
const container =
|
||||
event.target.closest(".zen-essentials-container") ??
|
||||
gZenWorkspaces.getCurrentEssentialsContainer();
|
||||
const height = parseFloat(
|
||||
getComputedStyle(container).getPropertyValue("--tab-min-height")
|
||||
);
|
||||
tab.style.minWidth =
|
||||
tab.style.maxWidth =
|
||||
wrapper.style.width =
|
||||
draggedTabRect.width + "px";
|
||||
tab.style.minHeight =
|
||||
tab.style.maxHeight =
|
||||
wrapper.style.height =
|
||||
draggedTabRect.height + "px";
|
||||
const offsetY = dragData.offsetY;
|
||||
const offsetX = dragData.offsetX;
|
||||
// Apply a transform translate to the tab in order to center it within the drag image
|
||||
tab.style.transform = `translate(${(54 - offsetX) / 2}px, ${(50 - offsetY) / 2}px)`;
|
||||
gZenPinnedTabManager.setEssentialTabIcon(tab);
|
||||
dt.updateDragImage(wrapper, -16, -16);
|
||||
return {
|
||||
width: Math.max(bounds(container).width * 0.237, height + 4),
|
||||
height,
|
||||
};
|
||||
}
|
||||
|
||||
#makeDragImageNonEssential(event) {
|
||||
const dt = event.dataTransfer;
|
||||
const draggedTab = event.dataTransfer.mozGetDataAt(TAB_DROP_TYPE, 0);
|
||||
if (draggedTab.hasAttribute("zen-essential")) {
|
||||
if (!this.#dragImageEssential) {
|
||||
return;
|
||||
}
|
||||
const wrapper = this.originalDragImageArgs[0];
|
||||
const tab = wrapper.firstElementChild;
|
||||
tab.style.setProperty("transition", "none", "important");
|
||||
tab.removeAttribute("zen-essential");
|
||||
tab.removeAttribute("pinned");
|
||||
tab.style.minWidth = tab.style.maxWidth = "";
|
||||
tab.style.minHeight = tab.style.maxHeight = "";
|
||||
tab.style.transform = "";
|
||||
const rect = window.windowUtils.getBoundsWithoutFlushing(draggedTab);
|
||||
wrapper.style.width = rect.width + "px";
|
||||
wrapper.style.height = rect.height + "px";
|
||||
this.#dragImageEssential = false;
|
||||
// An essential takes the size of a row in the strip, as any tab there.
|
||||
let model = draggedTab;
|
||||
if (draggedTab.hasAttribute("zen-essential")) {
|
||||
model =
|
||||
this._tabbrowserTabs.ariaFocusableItems.find(
|
||||
item => isTab(item) && !item.hasAttribute("zen-essential")
|
||||
) ?? draggedTab;
|
||||
}
|
||||
const rect = window.windowUtils.getBoundsWithoutFlushing(model);
|
||||
const clones = this.#dragImageClones(draggedTab);
|
||||
for (const { wrapper, tab, source } of clones) {
|
||||
tab.style.setProperty("transition", "none", "important");
|
||||
tab.removeAttribute("zen-essential");
|
||||
tab.removeAttribute("pinned");
|
||||
tab.style.minWidth = tab.style.maxWidth = "";
|
||||
tab.style.minHeight = tab.style.maxHeight = "";
|
||||
tab.style.transform = "";
|
||||
wrapper.style.width = rect.width + "px";
|
||||
wrapper.style.height = rect.height + "px";
|
||||
if (source.hasAttribute("zen-essential") && tab.textLabel) {
|
||||
tab.textLabel.textContent = source.label.slice(
|
||||
0,
|
||||
Math.floor(rect.width / 6)
|
||||
);
|
||||
}
|
||||
}
|
||||
setTimeout(() => {
|
||||
tab.style.transition = "";
|
||||
for (const { tab } of clones) {
|
||||
tab.style.transition = "";
|
||||
}
|
||||
dt.updateDragImage(...this.originalDragImageArgs);
|
||||
}, 50);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,10 @@ SOURCES += [
|
||||
]
|
||||
|
||||
if CONFIG["MOZ_WIDGET_TOOLKIT"] == "cocoa":
|
||||
SOURCES += ["nsZenNativeWindowMove.mm"]
|
||||
SOURCES += [
|
||||
"nsZenDropLandingCocoa.mm",
|
||||
"nsZenNativeWindowMove.mm",
|
||||
]
|
||||
elif CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk":
|
||||
CXXFLAGS += CONFIG["MOZ_GTK3_CFLAGS"]
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface mozIDOMWindowProxy;
|
||||
webidl Node;
|
||||
|
||||
/**
|
||||
* @brief Interface for Zen's drag and drop functionality.
|
||||
@@ -33,4 +34,24 @@ interface nsIZenDragAndDrop : nsISupports {
|
||||
* @param window The chrome window to move.
|
||||
*/
|
||||
void beginNativeWindowMove(in mozIDOMWindowProxy window);
|
||||
|
||||
/**
|
||||
* @brief Slide the drag image onto the drop instead of removing it.
|
||||
* macOS only, the drag ends once it lands.
|
||||
* @param arm Whether this drag's drops land the image.
|
||||
*/
|
||||
void armDropLanding(in boolean arm);
|
||||
|
||||
/**
|
||||
* @brief Drag an extra image along as its own item, one per further tab.
|
||||
* macOS stacks and counts them. Add before the drag starts.
|
||||
* @param image The element to render.
|
||||
*/
|
||||
void addDragImage(in Node image);
|
||||
|
||||
/**
|
||||
* @brief Where a drag item lands, in screen CSS pixels. One call per
|
||||
* item in order, from within the drop event.
|
||||
*/
|
||||
void addDropLandingRect(in long x, in long y, in long width, in long height);
|
||||
};
|
||||
|
||||
@@ -91,9 +91,37 @@ nsZenDragAndDrop::OnDragStart(float opacity) {
|
||||
NS_IMETHODIMP
|
||||
nsZenDragAndDrop::OnDragEnd() {
|
||||
mDragImageOpacity = kZenDefaultDragImageOpacity;
|
||||
mDropLandingArmed = false;
|
||||
mDragImages.Clear();
|
||||
mDropLandingRects.Clear();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsZenDragAndDrop::ArmDropLanding(bool aArm) {
|
||||
mDropLandingArmed = aArm;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsZenDragAndDrop::AddDragImage(nsINode* aImage) {
|
||||
NS_ENSURE_ARG_POINTER(aImage);
|
||||
mDragImages.AppendElement(aImage);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsZenDragAndDrop::AddDropLandingRect(int32_t aX, int32_t aY, int32_t aWidth,
|
||||
int32_t aHeight) {
|
||||
mDropLandingRects.AppendElement(
|
||||
mozilla::DesktopIntRect(aX, aY, aWidth, aHeight));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsTArray<mozilla::DesktopIntRect> nsZenDragAndDrop::TakeDropLandingRects() {
|
||||
return std::move(mDropLandingRects);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsZenDragAndDrop::BeginNativeWindowMove(mozIDOMWindowProxy* aWindow) {
|
||||
NS_ENSURE_ARG_POINTER(aWindow);
|
||||
|
||||
@@ -7,6 +7,13 @@
|
||||
|
||||
#include "nsIZenDragAndDrop.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "Units.h"
|
||||
#include "nsINode.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
#if defined(XP_MACOSX) && defined(__OBJC__)
|
||||
@protocol NSDraggingInfo;
|
||||
#endif
|
||||
|
||||
#define ZEN_DND_MANAGER_CONTRACTID "@mozilla.org/zen/drag-and-drop;1"
|
||||
|
||||
@@ -26,6 +33,15 @@ class nsZenDragAndDrop final : public nsIZenDragAndDrop {
|
||||
public:
|
||||
explicit nsZenDragAndDrop();
|
||||
auto GetDragImageOpacity() const { return mDragImageOpacity; }
|
||||
auto DropLandingArmed() const { return mDropLandingArmed; }
|
||||
const auto& DragImages() const { return mDragImages; }
|
||||
|
||||
/**
|
||||
* @brief Take the rects the drag items are to land on after the drop
|
||||
* being handled, as the drop added them, in screen CSS pixels and item
|
||||
* order. Empty when the drop added none.
|
||||
*/
|
||||
nsTArray<mozilla::DesktopIntRect> TakeDropLandingRects();
|
||||
|
||||
/**
|
||||
* @brief Get the singleton instance of nsZenDragAndDrop. There may be
|
||||
@@ -39,8 +55,25 @@ class nsZenDragAndDrop final : public nsIZenDragAndDrop {
|
||||
private:
|
||||
~nsZenDragAndDrop() = default;
|
||||
float mDragImageOpacity{};
|
||||
bool mDropLandingArmed{};
|
||||
nsTArray<nsCOMPtr<nsINode>> mDragImages;
|
||||
nsTArray<mozilla::DesktopIntRect> mDropLandingRects;
|
||||
};
|
||||
|
||||
#if defined(XP_MACOSX) && defined(__OBJC__)
|
||||
/**
|
||||
* @brief From the drag destination view, once a dragover is handled, ask
|
||||
* the OS to land the drag image on the drop, if the drag wants that.
|
||||
*/
|
||||
void PrepareDropLanding(id<NSDraggingInfo> aInfo);
|
||||
|
||||
/**
|
||||
* @brief From the drag destination view, once a drop is handled, send the
|
||||
* drag image to the rect the drop set, if it set one.
|
||||
*/
|
||||
void LandDrop(id<NSDraggingInfo> aInfo);
|
||||
#endif
|
||||
|
||||
} // namespace zen
|
||||
|
||||
#endif
|
||||
|
||||
54
src/zen/drag-and-drop/nsZenDropLandingCocoa.mm
Normal file
54
src/zen/drag-and-drop/nsZenDropLandingCocoa.mm
Normal file
@@ -0,0 +1,54 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#include "nsCocoaUtils.h"
|
||||
#include "nsObjCExceptions.h"
|
||||
#include "nsZenDragAndDrop.h"
|
||||
|
||||
namespace zen {
|
||||
|
||||
void PrepareDropLanding(id<NSDraggingInfo> aInfo) {
|
||||
NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
|
||||
|
||||
auto zen = nsZenDragAndDrop::GetZenDragAndDropInstance();
|
||||
aInfo.animatesToDestination = zen && zen->DropLandingArmed();
|
||||
|
||||
NS_OBJC_END_TRY_IGNORE_BLOCK;
|
||||
}
|
||||
|
||||
void LandDrop(id<NSDraggingInfo> aInfo) {
|
||||
NS_OBJC_BEGIN_TRY_IGNORE_BLOCK;
|
||||
|
||||
auto zen = nsZenDragAndDrop::GetZenDragAndDropInstance();
|
||||
if (!zen) {
|
||||
return;
|
||||
}
|
||||
NSMutableArray* targets = [NSMutableArray array];
|
||||
for (const auto& landing : zen->TakeDropLandingRects()) {
|
||||
[targets
|
||||
addObject:[NSValue valueWithRect:nsCocoaUtils::GeckoRectToCocoaRect(
|
||||
landing)]];
|
||||
}
|
||||
if (!targets.count) {
|
||||
return;
|
||||
}
|
||||
[aInfo enumerateDraggingItemsWithOptions:NSDraggingItemEnumerationConcurrent
|
||||
forView:nil
|
||||
classes:@[ [NSPasteboardItem class] ]
|
||||
searchOptions:@{}
|
||||
usingBlock:^(NSDraggingItem* item, NSInteger idx, BOOL* stop) {
|
||||
NSRect target =
|
||||
[targets[MIN((NSUInteger)idx, targets.count - 1)] rectValue];
|
||||
NSRect frame = item.draggingFrame;
|
||||
frame.origin.x = target.origin.x;
|
||||
frame.origin.y = NSMaxY(target) - frame.size.height;
|
||||
item.draggingFrame = frame;
|
||||
}];
|
||||
|
||||
NS_OBJC_END_TRY_IGNORE_BLOCK;
|
||||
}
|
||||
|
||||
} // namespace zen
|
||||
@@ -100,7 +100,10 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
|
||||
const folderActionsMenu = document.getElementById("zenFolderActions");
|
||||
folderActionsMenu.addEventListener("popupshowing", event => {
|
||||
const target = event.explicitOriginalTarget;
|
||||
const target =
|
||||
event.target === folderActionsMenu
|
||||
? (folderActionsMenu.triggerNode ?? event.explicitOriginalTarget)
|
||||
: event.explicitOriginalTarget;
|
||||
let folder;
|
||||
if (gBrowser.isTabGroupLabel(target)) {
|
||||
folder = target.group;
|
||||
@@ -327,7 +330,7 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
if (isActiveFolder) {
|
||||
for (const folder of group.activeGroups) {
|
||||
folder.activeTabs = [...new Set([...folder.activeTabs, tab])].sort(
|
||||
(a, b) => a._tPos > b._tPos
|
||||
(a, b) => a.index - b.index
|
||||
);
|
||||
this.setFolderIndentation([tab], folder, /* for collapse = */ true);
|
||||
}
|
||||
@@ -378,7 +381,7 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
if (isActiveFolder && isSplitView) {
|
||||
parentFolder.activeTabs = [
|
||||
...new Set([...parentFolder.activeTabs, ...folder.tabs]),
|
||||
].sort((a, b) => a._tPos > b._tPos);
|
||||
].sort((a, b) => a.index - b.index);
|
||||
}
|
||||
parentFolder.collapsed = isActiveFolder;
|
||||
}
|
||||
@@ -404,13 +407,18 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
return;
|
||||
}
|
||||
|
||||
const collapsedRoot = group.rootMostCollapsedFolder;
|
||||
let collapsedRoot = group.rootMostCollapsedFolder;
|
||||
if (!collapsedRoot) {
|
||||
return;
|
||||
}
|
||||
|
||||
collapsedRoot.setAttribute("has-active", "true");
|
||||
await this.animateSelect(collapsedRoot);
|
||||
// If the folder got expanded instead, the tab may still be
|
||||
// inside of another collapsed subfolder.
|
||||
while (collapsedRoot) {
|
||||
collapsedRoot.setAttribute("has-active", "true");
|
||||
const expanded = await this.animateSelect(collapsedRoot);
|
||||
collapsedRoot = expanded ? group.rootMostCollapsedFolder : null;
|
||||
}
|
||||
gBrowser.tabContainer._invalidateCachedTabs();
|
||||
}
|
||||
|
||||
@@ -1505,10 +1513,90 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
#createAnimation(items, targetState, opts, callback = () => {}) {
|
||||
items = Array.isArray(items) ? items : [items];
|
||||
return items.map(item =>
|
||||
gZenUIManager.motion.animate(item, targetState, opts).then(callback)
|
||||
this.#animateItem(item, targetState, opts).then(
|
||||
finished => finished && callback()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#itemAnimations = new WeakMap();
|
||||
|
||||
/**
|
||||
* Animates an element to the given target state. An array value is a
|
||||
* [from, to] pair, "auto" endpoints are resolved by measuring and an
|
||||
* empty string animates back to the element's natural value. The final
|
||||
* values are left applied as inline styles. A running animation on the
|
||||
* same element is replaced, continuing from its current values.
|
||||
*
|
||||
* @param {Element} item - The element to animate.
|
||||
* @param {object} targetState - Property to value (or [from, to]) map.
|
||||
* @param {object} opts - The animation options.
|
||||
* @param {number} opts.duration - The duration in seconds.
|
||||
* @param {string} opts.ease - The easing name.
|
||||
* @returns {Promise<boolean>} Resolves when the animation has finished,
|
||||
* false if it got replaced by another one before that.
|
||||
*/
|
||||
async #animateItem(item, targetState, { duration = 0.18, ease } = {}) {
|
||||
const computed = window.getComputedStyle(item);
|
||||
const toCssValue = (prop, value) =>
|
||||
typeof value === "number" && prop !== "opacity"
|
||||
? `${value}px`
|
||||
: String(value);
|
||||
const measure = prop => {
|
||||
const value = computed[prop];
|
||||
if (value === "auto") {
|
||||
return `${item.getBoundingClientRect()[prop] ?? 0}px`;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
const from = {};
|
||||
const to = {};
|
||||
const finalStyles = new Map();
|
||||
for (const [prop, value] of Object.entries(targetState)) {
|
||||
const start = Array.isArray(value) ? value[0] : undefined;
|
||||
from[prop] =
|
||||
start === undefined || start === "auto"
|
||||
? measure(prop)
|
||||
: toCssValue(prop, start);
|
||||
}
|
||||
// The start values are taken, the natural values need to be measured
|
||||
// without the previous animation applying.
|
||||
this.#itemAnimations.get(item)?.cancel();
|
||||
for (const [prop, value] of Object.entries(targetState)) {
|
||||
let end = Array.isArray(value) ? value[1] : value;
|
||||
const cssProp = prop.replace(/[A-Z]/g, c => `-${c.toLowerCase()}`);
|
||||
if (end === "" || end === "auto") {
|
||||
// Resolve the natural value by clearing any inline override.
|
||||
item.style.removeProperty(cssProp);
|
||||
end = measure(prop);
|
||||
finalStyles.set(cssProp, null);
|
||||
} else {
|
||||
end = toCssValue(prop, end);
|
||||
finalStyles.set(cssProp, end);
|
||||
}
|
||||
to[prop] = end;
|
||||
}
|
||||
const animation = item.animate([from, to], {
|
||||
duration: duration * 1000,
|
||||
easing: ease === "easeInOut" ? "ease-in-out" : "ease",
|
||||
});
|
||||
this.#itemAnimations.set(item, animation);
|
||||
try {
|
||||
await animation.finished;
|
||||
} catch (e) {
|
||||
// The animation was replaced, leave the element to the new one.
|
||||
return false;
|
||||
}
|
||||
for (const [cssProp, value] of finalStyles) {
|
||||
if (value === null) {
|
||||
item.style.removeProperty(cssProp);
|
||||
} else {
|
||||
item.style.setProperty(cssProp, value);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#calculateHeightShift(tabsContainer, selectedTabs) {
|
||||
let heightShift = 0;
|
||||
if (selectedTabs.length) {
|
||||
@@ -1526,7 +1614,11 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
}
|
||||
|
||||
get #folderAnimationDuration() {
|
||||
return this._dontAnimateFolder ? 0 : 0.12;
|
||||
return this._dontAnimateFolder ? 0 : 0.18;
|
||||
}
|
||||
|
||||
get #folderRevealDuration() {
|
||||
return this._dontAnimateFolder ? 0 : 0.22;
|
||||
}
|
||||
|
||||
async animateCollapse(group) {
|
||||
@@ -1546,6 +1638,9 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
splitViewIds,
|
||||
activeFoldersIds,
|
||||
});
|
||||
if (selectedTabs.length) {
|
||||
tabsContainer.removeAttribute("hidden");
|
||||
}
|
||||
const collapsedHeight = this.#calculateHeightShift(
|
||||
tabsContainer,
|
||||
selectedTabs
|
||||
@@ -1682,49 +1777,53 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
}
|
||||
}
|
||||
|
||||
const afterMarginTop = () => {
|
||||
tabsContainer.style.overflowY = "";
|
||||
if (group.hasAttribute("has-active")) {
|
||||
const activeTabs = group.activeTabs;
|
||||
const folders = new Map();
|
||||
group.removeAttribute("has-active");
|
||||
for (let tab of activeTabs) {
|
||||
const tabGroup = tab?.group?.hasAttribute("split-view-group")
|
||||
? tab?.group?.group
|
||||
: tab?.group;
|
||||
if (!folders.has(tabGroup?.id)) {
|
||||
folders.set(tabGroup?.id, tabGroup?.activeGroups?.at(-1));
|
||||
}
|
||||
let activeGroup = folders.get(tabGroup?.id);
|
||||
if (activeGroup) {
|
||||
this.setFolderIndentation(
|
||||
[tab],
|
||||
activeGroup,
|
||||
/* for collapse = */ true
|
||||
);
|
||||
// Update the active state and indentation right away instead of when
|
||||
// the animation ends, it would otherwise be lost if the folder gets
|
||||
// collapsed again before that.
|
||||
if (group.hasAttribute("has-active")) {
|
||||
const activeTabs = group.activeTabs;
|
||||
group.removeAttribute("has-active");
|
||||
for (const tab of activeTabs) {
|
||||
const tabGroup = tab?.group?.hasAttribute("split-view-group")
|
||||
? tab?.group?.group
|
||||
: tab?.group;
|
||||
const activeGroup = tabGroup?.activeGroups?.at(-1);
|
||||
if (activeGroup) {
|
||||
this.setFolderIndentation(
|
||||
[tab],
|
||||
activeGroup,
|
||||
/* for collapse = */ true
|
||||
);
|
||||
} else {
|
||||
// Since the folder is now expanded, we should remove active attribute
|
||||
// to the tab that was previously visible
|
||||
tab.removeAttribute("folder-active");
|
||||
if (tab.group?.hasAttribute("split-view-group")) {
|
||||
tab.group.style.removeProperty("--zen-folder-indent");
|
||||
} else {
|
||||
// Since the folder is now expanded, we should remove active attribute
|
||||
// to the tab that was previously visible
|
||||
tab.removeAttribute("folder-active");
|
||||
if (tab.group?.hasAttribute("split-view-group")) {
|
||||
tab.group.style.removeProperty("--zen-folder-indent");
|
||||
} else {
|
||||
tab.style.removeProperty("--zen-folder-indent");
|
||||
}
|
||||
tab.style.removeProperty("--zen-folder-indent");
|
||||
}
|
||||
}
|
||||
folders.clear();
|
||||
}
|
||||
// Folder has been expanded and has no active tabs
|
||||
group.activeTabs = [];
|
||||
}
|
||||
// Folder has been expanded and has no active tabs
|
||||
group.activeTabs = [];
|
||||
|
||||
const afterMarginTop = () => {
|
||||
tabsContainer.style.overflowY = "";
|
||||
};
|
||||
|
||||
let duration = this.#folderAnimationDuration;
|
||||
|
||||
// Items of collapsed subfolders must stay hidden, don't animate them twice.
|
||||
const itemsToReveal = itemsToShow.filter(
|
||||
item => !itemsToHide.includes(item)
|
||||
);
|
||||
|
||||
animations.push(
|
||||
...this.#createAnimation(
|
||||
itemsToShow,
|
||||
{ opacity: "", height: "" },
|
||||
itemsToReveal,
|
||||
{ opacity: "", height: "", minHeight: "" },
|
||||
{ duration, ease: "easeInOut" }
|
||||
),
|
||||
...this.#createAnimation(
|
||||
@@ -1746,10 +1845,6 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
this.#animationCount += 1;
|
||||
await Promise.all(animations);
|
||||
this.#animationCount -= 1;
|
||||
|
||||
// Cleanup
|
||||
this.styleCleanup(itemsToShow);
|
||||
this.styleCleanup(itemsToHide);
|
||||
}
|
||||
|
||||
async animateUnloadAll(group) {
|
||||
@@ -1764,6 +1859,9 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
|
||||
// Set correct margin-top after animation
|
||||
const afterAnimate = () => {
|
||||
if (folder.hasAttribute("has-active")) {
|
||||
return;
|
||||
}
|
||||
groupStart.style.removeProperty("margin-top");
|
||||
this.styleCleanup(groupItems);
|
||||
// Trigger the recalculation so that zen returns
|
||||
@@ -1785,7 +1883,7 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
{
|
||||
marginTop: -(collapsedHeight + 4),
|
||||
},
|
||||
{ duration: 0.12, ease: "easeInOut" },
|
||||
{ duration: this.#folderAnimationDuration, ease: "easeInOut" },
|
||||
afterAnimate
|
||||
)
|
||||
);
|
||||
@@ -1821,6 +1919,9 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
|
||||
// Set correct margin-top after animation
|
||||
const afterAnimate = () => {
|
||||
if (folder.hasAttribute("has-active")) {
|
||||
return;
|
||||
}
|
||||
groupStart.style.removeProperty("margin-top");
|
||||
this.styleCleanup(groupItems);
|
||||
// Trigger the recalculation so that zen returns
|
||||
@@ -1845,7 +1946,7 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
{
|
||||
marginTop: -(collapsedHeight + 4),
|
||||
},
|
||||
{ duration: 0.12, ease: "easeInOut" },
|
||||
{ duration: this.#folderAnimationDuration, ease: "easeInOut" },
|
||||
afterAnimate
|
||||
),
|
||||
];
|
||||
@@ -1870,7 +1971,7 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
height: 0,
|
||||
},
|
||||
{
|
||||
duration: 0.12,
|
||||
duration: this.#folderAnimationDuration,
|
||||
ease: "easeInOut",
|
||||
}
|
||||
);
|
||||
@@ -1888,9 +1989,16 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
gBrowser.tabContainer._invalidateCachedTabs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the selected tabs of a collapsed folder.
|
||||
*
|
||||
* @param {MozTabbrowserTabGroup} group - The collapsed folder.
|
||||
* @returns {Promise<boolean>} True if the folder got expanded instead,
|
||||
* since all of its tabs are active.
|
||||
*/
|
||||
async animateSelect(group) {
|
||||
if (!group?.isZenFolder) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
this.cancelPopupTimer();
|
||||
@@ -1899,12 +2007,48 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
const selectedTabs = [];
|
||||
const splitViewIds = new Set();
|
||||
const itemsToHide = [];
|
||||
const newlyActive = new Set();
|
||||
|
||||
const groupItems = this.#collectGroupItems(group, {
|
||||
selectedTabs,
|
||||
splitViewIds,
|
||||
});
|
||||
|
||||
if (group.collapsed && selectedTabs.length) {
|
||||
const active = new Set([...(group.activeTabs ?? []), ...selectedTabs]);
|
||||
// Tabs inside of collapsed subfolders would stay hidden if the folder
|
||||
// gets expanded, so they don't need to be active.
|
||||
const isInsideCollapsedSubfolder = tab => {
|
||||
let folder = tab.group?.hasAttribute("split-view-group")
|
||||
? tab.group.group
|
||||
: tab.group;
|
||||
while (folder && folder !== group) {
|
||||
if (folder.collapsed) {
|
||||
return true;
|
||||
}
|
||||
folder = folder.group;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const tabs = group.tabs.filter(
|
||||
tab =>
|
||||
!tab.hasAttribute("zen-empty-tab") && !isInsideCollapsedSubfolder(tab)
|
||||
);
|
||||
if (
|
||||
tabs.length &&
|
||||
tabs.every(
|
||||
tab =>
|
||||
active.has(tab) ||
|
||||
tab.selected ||
|
||||
tab.multiselected ||
|
||||
tab.hasAttribute("folder-active")
|
||||
)
|
||||
) {
|
||||
group.collapsed = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (const tab of selectedTabs) {
|
||||
let currentGroup = tab?.group?.hasAttribute("split-view-group")
|
||||
? tab.group.group
|
||||
@@ -1916,10 +2060,15 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
if (activeTabs.length) {
|
||||
if (currentGroup.collapsed) {
|
||||
if (currentGroup.hasAttribute("has-active")) {
|
||||
for (const activeTab of activeTabs) {
|
||||
if (!currentGroup.activeTabs.includes(activeTab)) {
|
||||
newlyActive.add(activeTab);
|
||||
}
|
||||
}
|
||||
// It is important to keep the sequence of elements as in the DOM
|
||||
currentGroup.activeTabs = [
|
||||
...new Set([...currentGroup.activeTabs, ...activeTabs]),
|
||||
].sort((a, b) => a._tPos > b._tPos);
|
||||
].sort((a, b) => a.index - b.index);
|
||||
} else {
|
||||
currentGroup.setAttribute("has-active", "true");
|
||||
currentGroup.activeTabs = activeTabs;
|
||||
@@ -1944,7 +2093,7 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
{
|
||||
marginTop: 0,
|
||||
},
|
||||
{ duration: 0.12, ease: "easeInOut" },
|
||||
{ duration: this.#folderAnimationDuration, ease: "easeInOut" },
|
||||
afterMarginTop
|
||||
)
|
||||
);
|
||||
@@ -1963,13 +2112,14 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
}
|
||||
|
||||
const itemsToShow = [];
|
||||
const itemsToReveal = [];
|
||||
if (selectedTabs.length) {
|
||||
for (let i = 0; i < groupItems.length; i++) {
|
||||
const { item, splitViewId } = groupItems[i];
|
||||
|
||||
let itemVisible = item.visible;
|
||||
if (itemVisible) {
|
||||
itemsToShow.push(item);
|
||||
(newlyActive.has(item) ? itemsToReveal : itemsToShow).push(item);
|
||||
}
|
||||
|
||||
// Skip selected items
|
||||
@@ -2003,18 +2153,48 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
height: "",
|
||||
},
|
||||
{
|
||||
duration: 0.12,
|
||||
duration: this.#folderAnimationDuration,
|
||||
ease: "easeInOut",
|
||||
}
|
||||
),
|
||||
...itemsToReveal.flatMap(item => {
|
||||
const state = {
|
||||
opacity: [0, ""],
|
||||
height: [0, ""],
|
||||
minHeight: [0, ""],
|
||||
};
|
||||
const folder = item.group?.hasAttribute("split-view-group")
|
||||
? item.group.group
|
||||
: item.group;
|
||||
const slidesFromFolder = !(folder?.activeTabs ?? []).some(
|
||||
active =>
|
||||
!newlyActive.has(active) &&
|
||||
active.compareDocumentPosition(item) &
|
||||
Node.DOCUMENT_POSITION_FOLLOWING
|
||||
);
|
||||
if (slidesFromFolder) {
|
||||
const height =
|
||||
item.getBoundingClientRect().height ||
|
||||
(folder?.activeTabs ?? []).reduce(
|
||||
(found, active) => found || active.getBoundingClientRect().height,
|
||||
0
|
||||
);
|
||||
state.transform = [`translateY(-${height}px)`, ""];
|
||||
}
|
||||
return this.#createAnimation(item, state, {
|
||||
duration: this.#folderRevealDuration,
|
||||
ease: "easeInOut",
|
||||
});
|
||||
}),
|
||||
...this.#createAnimation(
|
||||
itemsToHide,
|
||||
{
|
||||
opacity: 0,
|
||||
height: 0,
|
||||
minHeight: 0,
|
||||
},
|
||||
{
|
||||
duration: 0.12,
|
||||
duration: this.#folderAnimationDuration,
|
||||
ease: "easeInOut",
|
||||
}
|
||||
)
|
||||
@@ -2024,11 +2204,12 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
await Promise.all(animations);
|
||||
this.#animationCount -= 1;
|
||||
if (this.#animationCount) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
this.styleCleanup(selectedTabs);
|
||||
return false;
|
||||
}
|
||||
|
||||
animateGroupMove(group, expand = false) {
|
||||
@@ -2047,7 +2228,32 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
{
|
||||
marginTop: expand ? 0 : -(heightContainer + 4),
|
||||
},
|
||||
{ duration: 0.12, ease: "easeInOut" }
|
||||
{ duration: this.#folderAnimationDuration, ease: "easeInOut" }
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches to the folder's space, expands its parents (root first) and the
|
||||
* folder itself, then jiggles it so it's easy to spot.
|
||||
*
|
||||
* @param {MozTabbrowserTabGroup} folder The folder to reveal.
|
||||
*/
|
||||
async revealFolder(folder) {
|
||||
const workspaceId = folder.getAttribute("zen-workspace-id");
|
||||
if (workspaceId && workspaceId != gZenWorkspaces.activeWorkspace) {
|
||||
await gZenWorkspaces.changeWorkspaceWithID(workspaceId);
|
||||
}
|
||||
let collapsedRoot = folder.rootMostCollapsedFolder;
|
||||
const wasCollapsed = !!collapsedRoot;
|
||||
while (collapsedRoot) {
|
||||
collapsedRoot.collapsed = false;
|
||||
collapsedRoot = folder.rootMostCollapsedFolder;
|
||||
}
|
||||
const label = folder.labelElement.parentElement;
|
||||
label.scrollIntoView({ block: "nearest", behavior: "smooth" });
|
||||
await gZenUIManager.shakeElement(
|
||||
label,
|
||||
wasCollapsed ? this.#folderRevealDuration * 1000 : 0
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2055,6 +2261,7 @@ class nsZenFolders extends nsZenDOMOperatedFeature {
|
||||
items.forEach(item => {
|
||||
item.style.removeProperty("opacity");
|
||||
item.style.removeProperty("height");
|
||||
item.style.removeProperty("min-height");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,10 @@
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
:root {
|
||||
:root,
|
||||
/* Add zen-folder so that accent color changes done to each
|
||||
* specific space is applied. */
|
||||
zen-folder {
|
||||
--zen-folder-behind-bgcolor: light-dark(color-mix(in srgb, var(--zen-primary-color) 60%, gray), color-mix(in srgb, var(--zen-primary-color) 60%, #c1c1c1));
|
||||
--zen-folder-front-bgcolor: light-dark(color-mix(in srgb, var(--zen-primary-color), white 70%), color-mix(in srgb, var(--zen-primary-color), black 40%));
|
||||
--zen-folder-stroke: light-dark(color-mix(in srgb, var(--zen-primary-color) 50%, black), color-mix(in srgb, var(--zen-colors-primary) 15%, #ebebeb));
|
||||
@@ -193,15 +196,8 @@ zen-folder {
|
||||
}
|
||||
}
|
||||
|
||||
:root[zen-sidebar-expanded] &[has-active] > .tab-group-label-container {
|
||||
& .tab-reset-button {
|
||||
display: flex;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
&:hover .tab-reset-button {
|
||||
opacity: 1;
|
||||
}
|
||||
:root[zen-sidebar-expanded] &[has-active] > .tab-group-label-container:hover .tab-reset-button {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -63,6 +63,146 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
|
||||
.addEventListener("click", this.onOverlayClick.bind(this));
|
||||
}
|
||||
|
||||
#detached = null;
|
||||
|
||||
get detachedBrowser() {
|
||||
return this.#detached?.browser ?? null;
|
||||
}
|
||||
|
||||
#createDetachedHost() {
|
||||
const host = window.MozXULElement.parseXULToFragment(`
|
||||
<html:div id="zen-glance-detached">
|
||||
<html:div class="zen-glance-detached-backdrop"/>
|
||||
<html:div class="browserContainer">
|
||||
<html:div class="browserStack"/>
|
||||
</html:div>
|
||||
</html:div>
|
||||
`).firstElementChild;
|
||||
host
|
||||
.querySelector(".zen-glance-detached-backdrop")
|
||||
.addEventListener("click", () => this.closeDetachedGlance());
|
||||
document.getElementById("zen-main-app-wrapper").appendChild(host);
|
||||
return host;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a detached glance.
|
||||
*
|
||||
* @param {object} data - The URL, its triggering principal, the point the
|
||||
* glance animates out of as clientX/clientY in window coordinates, and
|
||||
* optionally the userContextId to load in
|
||||
* @returns {Promise<{browser: Element, closed: Promise<string>}|null>}
|
||||
* Once open, the glance's browser and a promise for its closing, which
|
||||
*/
|
||||
async openDetachedGlance(data) {
|
||||
if (this.#detached || !this.#isGlanceLoadAllowed(data)) {
|
||||
return null;
|
||||
}
|
||||
const host = this.#createDetachedHost();
|
||||
const wrapper = host.querySelector(".browserContainer");
|
||||
const browser = document.createXULElement("browser");
|
||||
browser.setAttribute("type", "content");
|
||||
browser.setAttribute("remote", "true");
|
||||
browser.setAttribute("maychangeremoteness", "true");
|
||||
browser.setAttribute("disableglobalhistory", "true");
|
||||
browser.setAttribute("messagemanagergroup", "browsers");
|
||||
browser.setAttribute("nodefaultsrc", "true");
|
||||
if (data.userContextId) {
|
||||
browser.setAttribute("usercontextid", data.userContextId);
|
||||
}
|
||||
host.querySelector(".browserStack").appendChild(browser);
|
||||
wrapper.appendChild(this.#createNewOverlayButtons());
|
||||
for (const selector of [
|
||||
".zen-glance-sidebar-open",
|
||||
".zen-glance-sidebar-split",
|
||||
]) {
|
||||
wrapper.querySelector(selector)?.remove();
|
||||
}
|
||||
|
||||
const hostRect = host.getBoundingClientRect();
|
||||
const startPoint = {
|
||||
clientX: data.clientX - hostRect.left,
|
||||
clientY: data.clientY - hostRect.top,
|
||||
width: 0,
|
||||
height: 0,
|
||||
};
|
||||
let resolveClosed;
|
||||
const whenClosed = new Promise(resolve => {
|
||||
resolveClosed = resolve;
|
||||
});
|
||||
this.#detached = {
|
||||
host,
|
||||
wrapper,
|
||||
browser,
|
||||
startPoint,
|
||||
closing: false,
|
||||
resolveClosed,
|
||||
};
|
||||
|
||||
browser.loadURI(Services.io.newURI(data.url), {
|
||||
triggeringPrincipal: data.triggeringPrincipal,
|
||||
});
|
||||
wrapper.setAttribute("animate", true);
|
||||
await gZenUIManager.elementAnimate(
|
||||
wrapper,
|
||||
this.#createGlanceArcSequence(startPoint, "opening", null, hostRect),
|
||||
{
|
||||
duration: gZenUIManager.testingEnabled
|
||||
? 0
|
||||
: this.#GLANCE_ANIMATION_DURATION,
|
||||
easing: "ease-in-out",
|
||||
}
|
||||
);
|
||||
wrapper.removeAttribute("animate");
|
||||
wrapper.setAttribute("has-finished-animation", true);
|
||||
return { browser, closed: whenClosed };
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} [options]
|
||||
* @param {boolean} [options.skipPermitUnload] - Close even if the page
|
||||
* would rather not unload
|
||||
*/
|
||||
async closeDetachedGlance({ skipPermitUnload = false } = {}) {
|
||||
const detached = this.#detached;
|
||||
if (!detached || detached.closing) {
|
||||
return;
|
||||
}
|
||||
if (!skipPermitUnload && !detached.browser.permitUnload().permitUnload) {
|
||||
return;
|
||||
}
|
||||
detached.closing = true;
|
||||
const { host, wrapper, browser } = detached;
|
||||
detached.resolveClosed();
|
||||
this.#animateSidebarButtons(
|
||||
wrapper.querySelector(".zen-glance-sidebar-container")
|
||||
);
|
||||
host.setAttribute("fade-out", true);
|
||||
wrapper.removeAttribute("has-finished-animation");
|
||||
wrapper.setAttribute("animate", true);
|
||||
try {
|
||||
await gZenUIManager.elementAnimate(
|
||||
wrapper,
|
||||
this.#createGlanceArcSequence(
|
||||
detached.startPoint,
|
||||
"closing",
|
||||
null,
|
||||
host.getBoundingClientRect()
|
||||
),
|
||||
{
|
||||
duration: gZenUIManager.testingEnabled
|
||||
? 0
|
||||
: this.#GLANCE_ANIMATION_DURATION,
|
||||
easing: "ease-out",
|
||||
}
|
||||
);
|
||||
} finally {
|
||||
browser.destroy();
|
||||
host.remove();
|
||||
this.#detached = null;
|
||||
}
|
||||
}
|
||||
|
||||
#setupPreferences() {
|
||||
XPCOMUtils.defineLazyPreferenceGetter(
|
||||
this._lazyPref,
|
||||
@@ -102,7 +242,10 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
|
||||
handleMainCommandSet(event) {
|
||||
const command = event.target;
|
||||
const commandHandlers = {
|
||||
cmd_zenGlanceClose: () => this.closeGlance({ onTabClose: true }),
|
||||
cmd_zenGlanceClose: () =>
|
||||
this.#detached
|
||||
? this.closeDetachedGlance()
|
||||
: this.closeGlance({ onTabClose: true }),
|
||||
cmd_zenGlanceExpand: () => this.fullyOpenGlance(),
|
||||
cmd_zenGlanceSplit: () => this.splitGlance(),
|
||||
};
|
||||
@@ -617,9 +760,16 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
|
||||
* @param {object} data - Glance data with position and dimensions
|
||||
* @param {string} direction - 'opening' or 'closing'
|
||||
* @param {Element|null} imageDataElement - The image data element for preview (optional)
|
||||
* @param {DOMRect|null} referenceRect - Bounds the glance opens within;
|
||||
* defaults to the tab panels
|
||||
* @returns {object} Animation sequence object
|
||||
*/
|
||||
#createGlanceArcSequence(data, direction, imageDataElement = null) {
|
||||
#createGlanceArcSequence(
|
||||
data,
|
||||
direction,
|
||||
imageDataElement = null,
|
||||
referenceRect = null
|
||||
) {
|
||||
let { clientX, clientY, width, height } = data;
|
||||
if (imageDataElement?.parentElement) {
|
||||
// Since we are animating scale transforms on the wrapper, we need to
|
||||
@@ -649,9 +799,9 @@ class nsZenGlanceManager extends nsZenDOMOperatedFeature {
|
||||
// Calculate start and end positions based on direction
|
||||
let startPosition, endPosition;
|
||||
|
||||
const tabPanelsRect = window.windowUtils.getBoundsWithoutFlushing(
|
||||
gBrowser.tabpanels
|
||||
);
|
||||
const tabPanelsRect =
|
||||
referenceRect ??
|
||||
window.windowUtils.getBoundsWithoutFlushing(gBrowser.tabpanels);
|
||||
|
||||
const widthPercent = 0.8;
|
||||
if (direction === "opening") {
|
||||
|
||||
@@ -103,16 +103,14 @@
|
||||
}
|
||||
|
||||
.browserSidebarContainer.zen-glance-background,
|
||||
.browserSidebarContainer.zen-glance-overlay .browserContainer:not([fade-out="true"]) {
|
||||
border-radius: var(--zen-native-inner-radius);
|
||||
@media not (-moz-pref("zen.theme.squircle-browser-view")) {
|
||||
/* stylelint-disable-next-line property-no-unknown */
|
||||
corner-shape: round;
|
||||
border-radius: calc(var(--zen-native-inner-radius) / var(--zen-squircle-value));
|
||||
}
|
||||
:is(.browserSidebarContainer.zen-glance-overlay, #zen-glance-detached)
|
||||
.browserContainer:not([fade-out="true"]) {
|
||||
/* stylelint-disable-next-line property-no-unknown */
|
||||
corner-shape: round;
|
||||
border-radius: calc(var(--zen-native-inner-radius) / var(--zen-squircle-value));
|
||||
}
|
||||
|
||||
.browserSidebarContainer.zen-glance-overlay {
|
||||
:is(.browserSidebarContainer.zen-glance-overlay, #zen-glance-detached) {
|
||||
box-shadow: none !important;
|
||||
visibility: inherit;
|
||||
z-index: 1;
|
||||
@@ -146,14 +144,11 @@
|
||||
}
|
||||
|
||||
& .browserStack {
|
||||
border-radius: var(--zen-native-inner-radius);
|
||||
overflow: hidden;
|
||||
box-shadow: var(--zen-big-shadow);
|
||||
@media not (-moz-pref("zen.theme.squircle-browser-view")) {
|
||||
/* stylelint-disable-next-line property-no-unknown */
|
||||
corner-shape: round;
|
||||
border-radius: calc(var(--zen-native-inner-radius) / var(--zen-squircle-value));
|
||||
}
|
||||
/* stylelint-disable-next-line property-no-unknown */
|
||||
corner-shape: round;
|
||||
border-radius: calc(var(--zen-native-inner-radius) / var(--zen-squircle-value));
|
||||
}
|
||||
|
||||
& browser {
|
||||
@@ -198,3 +193,15 @@
|
||||
max-height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
#zen-main-app-wrapper > #zen-glance-detached {
|
||||
position: fixed;
|
||||
inset: calc(var(--zen-element-separation) / 2);
|
||||
z-index: 100;
|
||||
display: flex;
|
||||
|
||||
& .zen-glance-detached-backdrop {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
}
|
||||
}
|
||||
|
||||
705
src/zen/library/ZenLibrary.mjs
Normal file
705
src/zen/library/ZenLibrary.mjs
Normal file
@@ -0,0 +1,705 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import {
|
||||
html,
|
||||
nothing,
|
||||
repeat,
|
||||
} from "chrome://global/content/vendor/lit.all.mjs";
|
||||
import { MozLitElement } from "chrome://global/content/lit-utils.mjs";
|
||||
|
||||
const { ZenLibraryWidget } = ChromeUtils.importESModule(
|
||||
"moz-src:///zen/library/ZenLibraryWidget.sys.mjs"
|
||||
);
|
||||
|
||||
let lazy = {};
|
||||
|
||||
ChromeUtils.defineESModuleGetters(
|
||||
lazy,
|
||||
{
|
||||
ZenLibraryHistorySection:
|
||||
"moz-src:///zen/library/sections/ZenLibraryHistorySection.mjs",
|
||||
ZenLibraryDownloadsSection:
|
||||
"moz-src:///zen/library/sections/ZenLibraryDownloadsSection.mjs",
|
||||
ZenLibraryBoostsSection:
|
||||
"moz-src:///zen/library/sections/ZenLibraryBoostsSection.mjs",
|
||||
ZenLibraryMediaSection:
|
||||
"moz-src:///zen/library/sections/ZenLibraryMediaSection.mjs",
|
||||
ZenLibrarySpacesSection:
|
||||
"moz-src:///zen/library/sections/ZenLibrarySpacesSection.mjs",
|
||||
},
|
||||
{ global: "current" }
|
||||
);
|
||||
|
||||
const LAST_TAB_PREF = "zen.library.last-tab";
|
||||
const CLEANUP_DELAY_MS = 30000;
|
||||
|
||||
ChromeUtils.defineLazyGetter(lazy, "appContentWrapper", function () {
|
||||
return document.getElementById("zen-appcontent-wrapper");
|
||||
});
|
||||
|
||||
export class ZenLibrary extends MozLitElement {
|
||||
static instance = null;
|
||||
#contentMounted = true;
|
||||
#mounted = new Set();
|
||||
#progress = 0;
|
||||
|
||||
#springControls = null;
|
||||
|
||||
#toolboxWidth = 0;
|
||||
|
||||
#originalButtonsNextSibling = null;
|
||||
|
||||
get #hasAdoptedButtons() {
|
||||
return this.#originalButtonsNextSibling !== null;
|
||||
}
|
||||
|
||||
#canSwipe = false;
|
||||
#isOpen = false;
|
||||
|
||||
#wrapperGestureControl = null;
|
||||
#gestureControl = null;
|
||||
|
||||
#resizeObserver = new ResizeObserver(() => {
|
||||
this.openProgress = this.#progress;
|
||||
});
|
||||
|
||||
static queries = {
|
||||
_content: "#zen-library-content",
|
||||
_header: "#zen-library-header",
|
||||
_footer: "#zen-library-footer",
|
||||
};
|
||||
|
||||
static properties = {
|
||||
_activeTab: { type: String },
|
||||
};
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.zenLibrarySections = {
|
||||
media: lazy.ZenLibraryMediaSection,
|
||||
downloads: lazy.ZenLibraryDownloadsSection,
|
||||
boosts: lazy.ZenLibraryBoostsSection,
|
||||
...(!window.gZenWorkspaces.privateWindowOrDisabled
|
||||
? {
|
||||
spaces: lazy.ZenLibrarySpacesSection,
|
||||
}
|
||||
: {}),
|
||||
history: lazy.ZenLibraryHistorySection,
|
||||
};
|
||||
const lastTab = Services.prefs.getStringPref(LAST_TAB_PREF, "history");
|
||||
this.activeTab = lastTab in this.zenLibrarySections ? lastTab : "history";
|
||||
this.#mounted.add(this.activeTab);
|
||||
this.#hijackFirefoxCommands();
|
||||
}
|
||||
|
||||
static get isLibraryOpen() {
|
||||
const lib = this.getInstance();
|
||||
return lib.#isOpen;
|
||||
}
|
||||
|
||||
static get isLibrarySlightlyOpen() {
|
||||
const lib = this.getInstance(/* createIfMissing = */ false);
|
||||
if (!lib) {
|
||||
return false;
|
||||
}
|
||||
// Due to calculation inaccuracies assume
|
||||
// that openProgress never goes back to 0
|
||||
return lib.openProgress > 0.001;
|
||||
}
|
||||
|
||||
set isHidden(value) {
|
||||
this.requestUpdate();
|
||||
this.hidden = value;
|
||||
}
|
||||
|
||||
set activeTab(value) {
|
||||
if (this._activeTab === value) {
|
||||
return;
|
||||
}
|
||||
this._activeTab = value;
|
||||
this.#mounted.add(value);
|
||||
Services.prefs.setStringPref(LAST_TAB_PREF, value);
|
||||
}
|
||||
|
||||
get activeTab() {
|
||||
return this._activeTab;
|
||||
}
|
||||
|
||||
get activeSection() {
|
||||
return this.zenLibrarySections[this.activeTab];
|
||||
}
|
||||
|
||||
set openProgress(value) {
|
||||
const p = value;
|
||||
const stealWindowButtonsPastPoint = 0.6;
|
||||
const wasOpen = this.#progress > 0.001;
|
||||
this.#progress = p;
|
||||
const isPastWindowButtonSwitchPoint = p > stealWindowButtonsPastPoint;
|
||||
const isOpen = p > 0.001;
|
||||
|
||||
if (this.#stylesLoaded) {
|
||||
let libraryWidth =
|
||||
window.windowUtils.getBoundsWithoutFlushing(this).width;
|
||||
const compactModeOffsetDirection = this.#libraryOnRight
|
||||
? -this.#toolboxWidth + ZenThemeModifier.elementSeparation
|
||||
: this.#toolboxWidth - ZenThemeModifier.elementSeparation;
|
||||
const compactModeOffset = this.#isCompactMode
|
||||
? compactModeOffsetDirection
|
||||
: 0;
|
||||
|
||||
const leftAligned = this.#libraryOnRight ? -1 : 1;
|
||||
let webOffset =
|
||||
leftAligned * (libraryWidth - this.#toolboxWidth) + compactModeOffset;
|
||||
|
||||
this.style.setProperty(
|
||||
"transform",
|
||||
`translateX(calc(${leftAligned} * -100% * (1 - ${value})))`
|
||||
);
|
||||
lazy.appContentWrapper?.style.setProperty(
|
||||
"transform",
|
||||
`translateX(${value * webOffset}px)`
|
||||
);
|
||||
|
||||
const toolboxProgress = Math.min(1, value * 1.5);
|
||||
if (this.#isCompactMode) {
|
||||
if (this.#libraryOnRight) {
|
||||
gNavToolbox.style.setProperty(
|
||||
"transform",
|
||||
`translateX(calc(100% * ${toolboxProgress}))`
|
||||
);
|
||||
} else {
|
||||
gNavToolbox.style.setProperty(
|
||||
"transform",
|
||||
`translateX(calc(-100% * ${toolboxProgress}))`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
const toolboxScale = 1 - toolboxProgress * 0.04;
|
||||
const toolboxOpacity = 1 - toolboxProgress;
|
||||
gNavToolbox?.style.setProperty("transform", `scale(${toolboxScale})`);
|
||||
gNavToolbox?.style.setProperty("opacity", `${toolboxOpacity}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (isOpen && !wasOpen) {
|
||||
this.#init();
|
||||
} else if (!isOpen && wasOpen) {
|
||||
this.#cleanup();
|
||||
}
|
||||
|
||||
if (isPastWindowButtonSwitchPoint && this.#coversWindowButtons) {
|
||||
this.#adoptWindowButtons();
|
||||
} else if (!isPastWindowButtonSwitchPoint) {
|
||||
this.#restoreWindowButtons();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the window buttons sit in the sidebar column the library covers.
|
||||
*/
|
||||
get #coversWindowButtons() {
|
||||
if (!gZenVerticalTabsManager.isWindowsStyledButtons) {
|
||||
return !this.#libraryOnRight;
|
||||
}
|
||||
return this.#libraryOnRight && !this.#isCompactMode;
|
||||
}
|
||||
|
||||
get openProgress() {
|
||||
return this.#progress;
|
||||
}
|
||||
|
||||
#hijackFirefoxCommands() {
|
||||
document
|
||||
.getElementById("Browser:ShowAllHistory")
|
||||
.addEventListener("command", event => {
|
||||
event.stopPropagation();
|
||||
event.stopImmediatePropagation();
|
||||
|
||||
ZenLibrary.toggle("history");
|
||||
});
|
||||
}
|
||||
|
||||
#adoptWindowButtons() {
|
||||
if (this.#hasAdoptedButtons) {
|
||||
return;
|
||||
}
|
||||
|
||||
const realButtons = gZenVerticalTabsManager.actualWindowButtons;
|
||||
if (!this.#originalButtonsNextSibling) {
|
||||
this.#originalButtonsNextSibling = {
|
||||
isNext: realButtons.nextSibling,
|
||||
sibling: realButtons.nextSibling || realButtons.previousSibling,
|
||||
clone: realButtons.cloneNode(true),
|
||||
};
|
||||
|
||||
this.#originalButtonsNextSibling.clone.classList.add(
|
||||
"zen-library-window-buttons-clone"
|
||||
);
|
||||
if (this.#originalButtonsNextSibling.isNext) {
|
||||
this.#originalButtonsNextSibling.sibling.before(
|
||||
this.#originalButtonsNextSibling.clone
|
||||
);
|
||||
} else {
|
||||
this.#originalButtonsNextSibling.sibling.after(
|
||||
this.#originalButtonsNextSibling.clone
|
||||
);
|
||||
}
|
||||
|
||||
this._header.appendChild(realButtons);
|
||||
}
|
||||
}
|
||||
|
||||
#restoreWindowButtons() {
|
||||
if (!this.#hasAdoptedButtons) {
|
||||
return;
|
||||
}
|
||||
|
||||
const realButtons = gZenVerticalTabsManager.actualWindowButtons;
|
||||
if (this.#originalButtonsNextSibling) {
|
||||
this.#originalButtonsNextSibling.clone.remove();
|
||||
if (this.#originalButtonsNextSibling.isNext) {
|
||||
this.#originalButtonsNextSibling.sibling.before(realButtons);
|
||||
} else {
|
||||
this.#originalButtonsNextSibling.sibling.after(realButtons);
|
||||
}
|
||||
this.#originalButtonsNextSibling = null;
|
||||
}
|
||||
}
|
||||
|
||||
#stylesLoaded = null;
|
||||
|
||||
#whenStylesLoaded() {
|
||||
this.#stylesLoaded ??= this.updateComplete.then(() => {
|
||||
const link = this.querySelector("link[rel='stylesheet']");
|
||||
if (!link || link.sheet) {
|
||||
return undefined;
|
||||
}
|
||||
return new Promise(resolve => {
|
||||
link.addEventListener("load", resolve, { once: true });
|
||||
link.addEventListener("error", resolve, { once: true });
|
||||
});
|
||||
});
|
||||
return this.#stylesLoaded;
|
||||
}
|
||||
|
||||
#cleanupTimer = null;
|
||||
#idleCleanup = null;
|
||||
|
||||
#scheduleIdleCleanup() {
|
||||
this.#cancelIdleCleanup();
|
||||
this.#cleanupTimer = window.setTimeout(() => {
|
||||
this.#cleanupTimer = null;
|
||||
this.#idleCleanup = window.requestIdleCallback(() => {
|
||||
this.#idleCleanup = null;
|
||||
this.#stylesLoaded = null;
|
||||
|
||||
this.#contentMounted = false;
|
||||
this.#mounted = new Set([this.activeTab]);
|
||||
this.requestUpdate();
|
||||
});
|
||||
}, CLEANUP_DELAY_MS);
|
||||
}
|
||||
|
||||
#cancelIdleCleanup() {
|
||||
if (this.#cleanupTimer) {
|
||||
window.clearTimeout(this.#cleanupTimer);
|
||||
this.#cleanupTimer = null;
|
||||
}
|
||||
if (this.#idleCleanup) {
|
||||
window.cancelIdleCallback(this.#idleCleanup);
|
||||
this.#idleCleanup = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens or closes the library. With a tab id, opens the library on that
|
||||
* tab, switches to it if already open on another, or closes if it is
|
||||
* already the open one. Without one, plainly toggles open and closed.
|
||||
*
|
||||
* @param {string?} [tab] - A section id to open on
|
||||
*/
|
||||
static toggle(tab = undefined) {
|
||||
if (!Services.prefs.getBoolPref("zen.library.enabled")) {
|
||||
return;
|
||||
}
|
||||
|
||||
const lib = this.getInstance();
|
||||
if (tab && tab in lib.zenLibrarySections) {
|
||||
if (lib.#isOpen && lib.activeTab === tab) {
|
||||
this.animateProgress(0);
|
||||
} else if (lib.#isOpen) {
|
||||
lib.activeTab = tab;
|
||||
} else {
|
||||
lib.activeTab = tab;
|
||||
this.animateProgress(1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
this.animateProgress(lib.#isOpen ? 0 : 1);
|
||||
}
|
||||
|
||||
static async animateProgress(target) {
|
||||
const lib = this.getInstance();
|
||||
lib.#cancelIdleCleanup();
|
||||
await lib.#whenStylesLoaded();
|
||||
lib.style.visibility = "";
|
||||
await window.promiseDocumentFlushed(() => {});
|
||||
|
||||
if (lib.#springControls) {
|
||||
lib.#springControls.stop();
|
||||
lib.#springControls = null;
|
||||
}
|
||||
|
||||
if (target === 1) {
|
||||
lib.#onOpenLibrary();
|
||||
lib.#isOpen = true;
|
||||
} else if (target === 0) {
|
||||
lib.#isOpen = false;
|
||||
lib.#canSwipe = false;
|
||||
}
|
||||
|
||||
lib.setAttribute("transitioning", "true");
|
||||
lib.#springControls = gZenUIManager.motion.animate(
|
||||
lib.openProgress,
|
||||
target,
|
||||
{
|
||||
type: "spring",
|
||||
stiffness: 720,
|
||||
damping: 47,
|
||||
mass: 1.2,
|
||||
onUpdate: latest => {
|
||||
lib.openProgress = latest;
|
||||
},
|
||||
onComplete: () => {
|
||||
lib.openProgress = target;
|
||||
lib.#springControls = null;
|
||||
lib.removeAttribute("transitioning");
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
static async startSwipe() {
|
||||
const lib = this.getInstance();
|
||||
lib.#cancelIdleCleanup();
|
||||
lib.#canSwipe = true;
|
||||
await lib.#whenStylesLoaded();
|
||||
lib.style.visibility = "";
|
||||
await window.promiseDocumentFlushed(() => {});
|
||||
|
||||
lib.#onOpenLibrary();
|
||||
|
||||
if (lib.#springControls) {
|
||||
lib.#springControls.stop();
|
||||
lib.#springControls = null;
|
||||
}
|
||||
|
||||
lib.style.pointerEvents = "none";
|
||||
}
|
||||
|
||||
static stopSwipe(direction) {
|
||||
const lib = this.getInstance();
|
||||
lib.style.pointerEvents = "";
|
||||
lib.#canSwipe = false;
|
||||
|
||||
if (lib.#libraryOnRight) {
|
||||
direction = direction * -1;
|
||||
}
|
||||
|
||||
if (direction) {
|
||||
const target = Math.max(-direction, 0);
|
||||
this.animateProgress(target);
|
||||
}
|
||||
|
||||
return lib.#isOpen;
|
||||
}
|
||||
|
||||
static swipeProgress(target) {
|
||||
const lib = this.getInstance();
|
||||
if (!lib.#canSwipe) {
|
||||
return;
|
||||
}
|
||||
|
||||
lib.openProgress = target;
|
||||
}
|
||||
|
||||
#attachWrapperToSwipe() {
|
||||
if (!this.#wrapperGestureControl) {
|
||||
const appWrapper = document.getElementById("zen-main-app-wrapper");
|
||||
this.#wrapperGestureControl =
|
||||
window.gZenWorkspaces._swipeManager?.attachWorkspaceSwipeGestures(
|
||||
appWrapper
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#detachWrapperOfSwipe() {
|
||||
if (this.#wrapperGestureControl) {
|
||||
const appWrapper = document.getElementById("zen-main-app-wrapper");
|
||||
window.gZenWorkspaces._swipeManager?.detachWorkspaceSwipeGestures(
|
||||
appWrapper,
|
||||
this.#wrapperGestureControl
|
||||
);
|
||||
this.#wrapperGestureControl = null;
|
||||
}
|
||||
}
|
||||
|
||||
#onOpenLibrary() {
|
||||
this.#cancelIdleCleanup();
|
||||
if (!this.#contentMounted) {
|
||||
this.#contentMounted = true;
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
gURLBar.view.close();
|
||||
// Get the width from the css property,
|
||||
// getBoundsWithoutFlushing will fail as it takes the
|
||||
// toolbox transformation during the animation into account
|
||||
this.#toolboxWidth = parseFloat(
|
||||
gNavToolbox.style
|
||||
.getPropertyValue("--actual-zen-sidebar-width")
|
||||
.replace("/\D/g", "")
|
||||
);
|
||||
if (document.documentElement.hasAttribute("zen-sidebar-expanded")) {
|
||||
const splitterWidth = window.windowUtils.getBoundsWithoutFlushing(
|
||||
document.getElementById("zen-sidebar-splitter")
|
||||
).width;
|
||||
this.#toolboxWidth += splitterWidth;
|
||||
}
|
||||
}
|
||||
|
||||
static getInstance(createIfMissing = true) {
|
||||
if (!this.instance && createIfMissing) {
|
||||
this.instance = new ZenLibrary();
|
||||
this.instance.style.visibility = "collapse";
|
||||
const mountAfter = document.getElementById("navigator-toolbox");
|
||||
mountAfter.after(this.instance);
|
||||
}
|
||||
return this.instance;
|
||||
}
|
||||
|
||||
get #libraryOnRight() {
|
||||
return gZenVerticalTabsManager._prefsRightSide;
|
||||
}
|
||||
|
||||
static get libraryOnRight() {
|
||||
const lib = this.getInstance();
|
||||
return lib.#libraryOnRight;
|
||||
}
|
||||
|
||||
createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
|
||||
#init() {
|
||||
this.#cancelIdleCleanup();
|
||||
if (!this.#contentMounted) {
|
||||
this.#contentMounted = true;
|
||||
this.requestUpdate();
|
||||
}
|
||||
this.setAttribute("open", "true");
|
||||
document
|
||||
.getElementById("zen-sidebar-splitter")
|
||||
.setAttribute("zen-library-open", "true");
|
||||
document.addEventListener("keydown", this, true);
|
||||
window.addEventListener("TabOpen", this);
|
||||
|
||||
this.#attachWrapperToSwipe();
|
||||
this.#gestureControl =
|
||||
window.gZenWorkspaces._swipeManager.attachWorkspaceSwipeGestures(this);
|
||||
this.#resizeObserver.observe(this);
|
||||
ZenLibraryWidget.attachLibrary(this);
|
||||
this.isHidden = false;
|
||||
}
|
||||
|
||||
#cleanup() {
|
||||
this.removeAttribute("open");
|
||||
this.#mounted = new Set([this.activeTab]);
|
||||
this.requestUpdate();
|
||||
document
|
||||
.getElementById("zen-sidebar-splitter")
|
||||
.removeAttribute("zen-library-open");
|
||||
|
||||
if (this.#springControls) {
|
||||
this.#springControls.stop();
|
||||
this.#springControls = null;
|
||||
}
|
||||
this.removeAttribute("transitioning");
|
||||
|
||||
this.#detachWrapperOfSwipe();
|
||||
if (this.#gestureControl) {
|
||||
window.gZenWorkspaces._swipeManager.detachWorkspaceSwipeGestures(
|
||||
this,
|
||||
this.#gestureControl
|
||||
);
|
||||
}
|
||||
|
||||
this.#restoreWindowButtons();
|
||||
ZenLibraryWidget.detachLibrary(this);
|
||||
this.#resizeObserver.disconnect();
|
||||
document.removeEventListener("keydown", this, true);
|
||||
window.removeEventListener("TabOpen", this);
|
||||
this.isHidden = true;
|
||||
this.#scheduleIdleCleanup();
|
||||
}
|
||||
|
||||
get #isCompactMode() {
|
||||
return (
|
||||
window.gZenCompactModeManager.preference &&
|
||||
(Services.prefs.getBoolPref("zen.view.compact.hide-tabbar") ||
|
||||
Services.prefs.getBoolPref("zen.view.use-single-toolbar"))
|
||||
);
|
||||
}
|
||||
|
||||
handleEvent(e) {
|
||||
switch (e.type) {
|
||||
case "TabOpen":
|
||||
this.onTabOpen();
|
||||
break;
|
||||
case "keydown":
|
||||
this.onKeyDown(e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
onTabOpen() {
|
||||
if (this.#isOpen) {
|
||||
ZenLibrary.animateProgress(0);
|
||||
}
|
||||
}
|
||||
|
||||
onKeyDown(e) {
|
||||
if (!this.hasAttribute("open")) {
|
||||
return;
|
||||
}
|
||||
if (e.key === "Escape") {
|
||||
ZenLibrary.animateProgress(0);
|
||||
}
|
||||
}
|
||||
|
||||
firstUpdated() {
|
||||
if (super.firstUpdated) {
|
||||
super.firstUpdated();
|
||||
}
|
||||
this.#buildFooterButtons();
|
||||
}
|
||||
|
||||
#buildFooterButtons() {
|
||||
const footer = this.querySelector("#zen-library-footer");
|
||||
|
||||
const buttons = [
|
||||
{
|
||||
image: "chrome://browser/skin/zen-icons/back.svg",
|
||||
l10nId: "library-footer-close-button",
|
||||
command: () => ZenLibrary.animateProgress(0),
|
||||
},
|
||||
{
|
||||
image: "chrome://browser/skin/zen-icons/heart-circle-fill.svg",
|
||||
l10nId: "library-footer-donate-button",
|
||||
command: () => {
|
||||
window.openTrustedLinkIn("https://www.zen-browser.app/donate", "tab");
|
||||
ZenLibrary.animateProgress(0);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
for (const { image, l10nId, command } of buttons) {
|
||||
const button = document.createXULElement("toolbarbutton");
|
||||
button.className = "toolbarbutton-1";
|
||||
button.setAttribute("image", image);
|
||||
button.setAttribute("data-l10n-id", l10nId);
|
||||
button.addEventListener("command", command);
|
||||
footer.appendChild(button);
|
||||
}
|
||||
}
|
||||
|
||||
updated(changedProperties) {
|
||||
super.updated?.(changedProperties);
|
||||
this.#updateMountedSections();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the section being looked at and puts the others out of sight. A
|
||||
* section is told which it is, so one that reaches outside itself, such as
|
||||
* spaces setting the library's width, only does so while it is on show.
|
||||
*/
|
||||
#updateMountedSections() {
|
||||
for (const section of this._content?.children ?? []) {
|
||||
const id = section.dataset?.section;
|
||||
if (!id) {
|
||||
continue;
|
||||
}
|
||||
const showing = id === this.activeTab;
|
||||
const wasShowing = section.hasAttribute("showing");
|
||||
section.hidden = !showing;
|
||||
section.toggleAttribute("showing", showing);
|
||||
if (showing !== wasShowing) {
|
||||
(showing ? section.onShown : section.onHidden)?.call(section);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="chrome://browser/content/zen-styles/zen-library.css"
|
||||
/>
|
||||
<hbox id="zen-library-panel">
|
||||
<vbox id="zen-library-side">
|
||||
<vbox id="zen-library-header"></vbox>
|
||||
<vbox id="zen-library-sidebar-tabs">
|
||||
${Object.values(this.zenLibrarySections).map(
|
||||
Section => html`
|
||||
<vbox
|
||||
class="zen-library-tab"
|
||||
?active=${this.activeTab === Section.id}
|
||||
data-section=${Section.id}
|
||||
@click=${event => {
|
||||
if (this.activeTab !== Section.id) {
|
||||
this.activeTab = Section.id;
|
||||
const previousTab =
|
||||
event.currentTarget.parentNode.querySelector(
|
||||
`.zen-library-tab[animate="true"]`
|
||||
);
|
||||
if (previousTab) {
|
||||
previousTab.removeAttribute("animate");
|
||||
}
|
||||
event.currentTarget.setAttribute("animate", "true");
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div class="zen-library-tab-icon">
|
||||
<div class="zen-library-tab-icon-image"></div>
|
||||
</div>
|
||||
<label data-l10n-id=${Section.label}></label>
|
||||
</vbox>
|
||||
`
|
||||
)}
|
||||
</vbox>
|
||||
<toolbar
|
||||
id="zen-library-footer"
|
||||
class="chromeclass-location"
|
||||
mode="icons"
|
||||
fullscreentoolbar="true"
|
||||
></toolbar>
|
||||
</vbox>
|
||||
<vbox id="zen-library-content">
|
||||
${
|
||||
this.#contentMounted
|
||||
? repeat(
|
||||
[...this.#mounted],
|
||||
id => id,
|
||||
id => this.zenLibrarySections[id].render(this)
|
||||
)
|
||||
: nothing
|
||||
}
|
||||
</vbox>
|
||||
</hbox>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("zen-library", ZenLibrary);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user