no-bug: Add polish to finished boosts implementation (gh-13762)

Signed-off-by: mr. m <91018726+mr-cheffy@users.noreply.github.com>
This commit is contained in:
mr. m
2026-05-19 23:58:00 +02:00
committed by GitHub
parent ef259f58aa
commit 1b9408ecb0
18 changed files with 465 additions and 124 deletions

View File

@@ -28,6 +28,7 @@
"surfer": "surfer",
"test": "python3 scripts/run_tests.py",
"test:dbg": "python3 scripts/run_tests.py --jsdebugger --debug-on-failure",
"test:gtest": "cd engine && ./mach gtest",
"ffprefs": "cd tools/ffprefs && cargo run --bin ffprefs -- ../../",
"lc": "surfer license-check",
"lc:fix": "surfer license-check --fix",

View File

@@ -3,7 +3,7 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
- name: zen.boosts.enabled
value: "@IS_TWILIGHT@"
value: true
- name: zen.boosts.dissolve-on-zap
value: true

View File

@@ -924,7 +924,7 @@
position: relative;
list-style-image: url("paintbrush.svg");
list-style-image: url("boost.svg");
& .toolbarbutton-text {
display: none;
@@ -937,6 +937,8 @@
& image {
-moz-context-properties: fill, fill-opacity;
fill: currentColor;
width: 14px;
fill-opacity: 0.7;
}
}
@@ -1066,7 +1068,7 @@
}
#zen-boost-shuffle {
list-style-image: url("arrow-rotate-anticlockwise.svg");
list-style-image: url("dice.svg");
}
#zen-boost-css-picker {

View File

@@ -13,13 +13,13 @@
* skin/classic/browser/zen-icons/autoplay-media-fill.svg (../shared/zen-icons/nucleo/autoplay-media-fill.svg)
* skin/classic/browser/zen-icons/autoplay-media.svg (../shared/zen-icons/nucleo/autoplay-media.svg)
* skin/classic/browser/zen-icons/back.svg (../shared/zen-icons/nucleo/back.svg)
* skin/classic/browser/zen-icons/blocked-element.svg (../shared/zen-icons/nucleo/blocked-element.svg)
* skin/classic/browser/zen-icons/block.svg (../shared/zen-icons/nucleo/block.svg)
* skin/classic/browser/zen-icons/bolt.svg (../shared/zen-icons/nucleo/bolt.svg)
* skin/classic/browser/zen-icons/bookmark-hollow.svg (../shared/zen-icons/nucleo/bookmark-hollow.svg)
* skin/classic/browser/zen-icons/bookmark-star-on-tray.svg (../shared/zen-icons/nucleo/bookmark-star-on-tray.svg)
* skin/classic/browser/zen-icons/bookmark.svg (../shared/zen-icons/nucleo/bookmark.svg)
* skin/classic/browser/zen-icons/boost.svg (../shared/zen-icons/nucleo/boost.svg)
* skin/classic/browser/zen-icons/blocked-element.svg (../shared/zen-icons/nucleo/blocked-element.svg)
* skin/classic/browser/zen-icons/brackets-curly.svg (../shared/zen-icons/nucleo/brackets-curly.svg)
* skin/classic/browser/zen-icons/camera-blocked.svg (../shared/zen-icons/nucleo/camera-blocked.svg)
* skin/classic/browser/zen-icons/camera-fill.svg (../shared/zen-icons/nucleo/camera-fill.svg)
@@ -37,6 +37,7 @@
* skin/classic/browser/zen-icons/desktop-notification-fill.svg (../shared/zen-icons/nucleo/desktop-notification-fill.svg)
* skin/classic/browser/zen-icons/desktop-notification.svg (../shared/zen-icons/nucleo/desktop-notification.svg)
* skin/classic/browser/zen-icons/developer.svg (../shared/zen-icons/nucleo/developer.svg)
* skin/classic/browser/zen-icons/dice.svg (../shared/zen-icons/nucleo/dice.svg)
* skin/classic/browser/zen-icons/downloads.svg (../shared/zen-icons/nucleo/downloads.svg)
* skin/classic/browser/zen-icons/drag-indicator.svg (../shared/zen-icons/nucleo/drag-indicator.svg)
* skin/classic/browser/zen-icons/duplicate-tab.svg (../shared/zen-icons/nucleo/duplicate-tab.svg)
@@ -102,26 +103,26 @@
* skin/classic/browser/zen-icons/popup-fill.svg (../shared/zen-icons/nucleo/popup-fill.svg)
* skin/classic/browser/zen-icons/popup.svg (../shared/zen-icons/nucleo/popup.svg)
* skin/classic/browser/zen-icons/print.svg (../shared/zen-icons/nucleo/print.svg)
* skin/classic/browser/zen-icons/private-window.svg (../shared/zen-icons/nucleo/private-window.svg)
* skin/classic/browser/zen-icons/privateBrowsing.svg (../shared/zen-icons/nucleo/privateBrowsing.svg)
* skin/classic/browser/zen-icons/private-window.svg (../shared/zen-icons/nucleo/private-window.svg)
* skin/classic/browser/zen-icons/reader-mode.svg (../shared/zen-icons/nucleo/reader-mode.svg)
* skin/classic/browser/zen-icons/reload.svg (../shared/zen-icons/nucleo/reload.svg)
* skin/classic/browser/zen-icons/save.svg (../shared/zen-icons/nucleo/save.svg)
* skin/classic/browser/zen-icons/screen-blocked.svg (../shared/zen-icons/nucleo/screen-blocked.svg)
* skin/classic/browser/zen-icons/screen.svg (../shared/zen-icons/nucleo/screen.svg)
* skin/classic/browser/zen-icons/screenshot.svg (../shared/zen-icons/nucleo/screenshot.svg)
* skin/classic/browser/zen-icons/screen.svg (../shared/zen-icons/nucleo/screen.svg)
* skin/classic/browser/zen-icons/search-glass.svg (../shared/zen-icons/nucleo/search-glass.svg)
* skin/classic/browser/zen-icons/search-page.svg (../shared/zen-icons/nucleo/search-page.svg)
* skin/classic/browser/zen-icons/security-broken.svg (../shared/zen-icons/nucleo/security-broken.svg)
* skin/classic/browser/zen-icons/security-warning.svg (../shared/zen-icons/nucleo/security-warning.svg)
* skin/classic/browser/zen-icons/security.svg (../shared/zen-icons/nucleo/security.svg)
* skin/classic/browser/zen-icons/security-warning.svg (../shared/zen-icons/nucleo/security-warning.svg)
* skin/classic/browser/zen-icons/send-to-device.svg (../shared/zen-icons/nucleo/send-to-device.svg)
* skin/classic/browser/zen-icons/settings-fill.svg (../shared/zen-icons/nucleo/settings-fill.svg)
* skin/classic/browser/zen-icons/settings.svg (../shared/zen-icons/nucleo/settings.svg)
* skin/classic/browser/zen-icons/share.svg (../shared/zen-icons/nucleo/share.svg)
* skin/classic/browser/zen-icons/sidebar-right.svg (../shared/zen-icons/nucleo/sidebar-right.svg)
* skin/classic/browser/zen-icons/sidebar.svg (../shared/zen-icons/nucleo/sidebar.svg)
* skin/classic/browser/zen-icons/sidebars-right.svg (../shared/zen-icons/nucleo/sidebars-right.svg)
* skin/classic/browser/zen-icons/sidebar.svg (../shared/zen-icons/nucleo/sidebar.svg)
* skin/classic/browser/zen-icons/sliders.svg (../shared/zen-icons/nucleo/sliders.svg)
* skin/classic/browser/zen-icons/sparkles.svg (../shared/zen-icons/nucleo/sparkles.svg)
* skin/classic/browser/zen-icons/spell-check.svg (../shared/zen-icons/nucleo/spell-check.svg)
@@ -161,13 +162,13 @@
* skin/classic/browser/zen-icons/autoplay-media-fill.svg (../shared/zen-icons/nucleo/autoplay-media-fill.svg)
* skin/classic/browser/zen-icons/autoplay-media.svg (../shared/zen-icons/nucleo/autoplay-media.svg)
* skin/classic/browser/zen-icons/back.svg (../shared/zen-icons/nucleo/back.svg)
* skin/classic/browser/zen-icons/blocked-element.svg (../shared/zen-icons/nucleo/blocked-element.svg)
* skin/classic/browser/zen-icons/block.svg (../shared/zen-icons/nucleo/block.svg)
* skin/classic/browser/zen-icons/bolt.svg (../shared/zen-icons/nucleo/bolt.svg)
* skin/classic/browser/zen-icons/bookmark-hollow.svg (../shared/zen-icons/nucleo/bookmark-hollow.svg)
* skin/classic/browser/zen-icons/bookmark-star-on-tray.svg (../shared/zen-icons/nucleo/bookmark-star-on-tray.svg)
* skin/classic/browser/zen-icons/bookmark.svg (../shared/zen-icons/nucleo/bookmark.svg)
* skin/classic/browser/zen-icons/boost.svg (../shared/zen-icons/nucleo/boost.svg)
* skin/classic/browser/zen-icons/blocked-element.svg (../shared/zen-icons/nucleo/blocked-element.svg)
* skin/classic/browser/zen-icons/brackets-curly.svg (../shared/zen-icons/nucleo/brackets-curly.svg)
* skin/classic/browser/zen-icons/camera-blocked.svg (../shared/zen-icons/nucleo/camera-blocked.svg)
* skin/classic/browser/zen-icons/camera-fill.svg (../shared/zen-icons/nucleo/camera-fill.svg)
@@ -185,6 +186,7 @@
* skin/classic/browser/zen-icons/desktop-notification-fill.svg (../shared/zen-icons/nucleo/desktop-notification-fill.svg)
* skin/classic/browser/zen-icons/desktop-notification.svg (../shared/zen-icons/nucleo/desktop-notification.svg)
* skin/classic/browser/zen-icons/developer.svg (../shared/zen-icons/nucleo/developer.svg)
* skin/classic/browser/zen-icons/dice.svg (../shared/zen-icons/nucleo/dice.svg)
* skin/classic/browser/zen-icons/downloads.svg (../shared/zen-icons/nucleo/downloads.svg)
* skin/classic/browser/zen-icons/drag-indicator.svg (../shared/zen-icons/nucleo/drag-indicator.svg)
* skin/classic/browser/zen-icons/duplicate-tab.svg (../shared/zen-icons/nucleo/duplicate-tab.svg)
@@ -250,26 +252,26 @@
* skin/classic/browser/zen-icons/popup-fill.svg (../shared/zen-icons/nucleo/popup-fill.svg)
* skin/classic/browser/zen-icons/popup.svg (../shared/zen-icons/nucleo/popup.svg)
* skin/classic/browser/zen-icons/print.svg (../shared/zen-icons/nucleo/print.svg)
* skin/classic/browser/zen-icons/private-window.svg (../shared/zen-icons/nucleo/private-window.svg)
* skin/classic/browser/zen-icons/privateBrowsing.svg (../shared/zen-icons/nucleo/privateBrowsing.svg)
* skin/classic/browser/zen-icons/private-window.svg (../shared/zen-icons/nucleo/private-window.svg)
* skin/classic/browser/zen-icons/reader-mode.svg (../shared/zen-icons/nucleo/reader-mode.svg)
* skin/classic/browser/zen-icons/reload.svg (../shared/zen-icons/nucleo/reload.svg)
* skin/classic/browser/zen-icons/save.svg (../shared/zen-icons/nucleo/save.svg)
* skin/classic/browser/zen-icons/screen-blocked.svg (../shared/zen-icons/nucleo/screen-blocked.svg)
* skin/classic/browser/zen-icons/screen.svg (../shared/zen-icons/nucleo/screen.svg)
* skin/classic/browser/zen-icons/screenshot.svg (../shared/zen-icons/nucleo/screenshot.svg)
* skin/classic/browser/zen-icons/screen.svg (../shared/zen-icons/nucleo/screen.svg)
* skin/classic/browser/zen-icons/search-glass.svg (../shared/zen-icons/nucleo/search-glass.svg)
* skin/classic/browser/zen-icons/search-page.svg (../shared/zen-icons/nucleo/search-page.svg)
* skin/classic/browser/zen-icons/security-broken.svg (../shared/zen-icons/nucleo/security-broken.svg)
* skin/classic/browser/zen-icons/security-warning.svg (../shared/zen-icons/nucleo/security-warning.svg)
* skin/classic/browser/zen-icons/security.svg (../shared/zen-icons/nucleo/security.svg)
* skin/classic/browser/zen-icons/security-warning.svg (../shared/zen-icons/nucleo/security-warning.svg)
* skin/classic/browser/zen-icons/send-to-device.svg (../shared/zen-icons/nucleo/send-to-device.svg)
* skin/classic/browser/zen-icons/settings-fill.svg (../shared/zen-icons/nucleo/settings-fill.svg)
* skin/classic/browser/zen-icons/settings.svg (../shared/zen-icons/nucleo/settings.svg)
* skin/classic/browser/zen-icons/share.svg (../shared/zen-icons/nucleo/share.svg)
* skin/classic/browser/zen-icons/sidebar-right.svg (../shared/zen-icons/nucleo/sidebar-right.svg)
* skin/classic/browser/zen-icons/sidebar.svg (../shared/zen-icons/nucleo/sidebar.svg)
* skin/classic/browser/zen-icons/sidebars-right.svg (../shared/zen-icons/nucleo/sidebars-right.svg)
* skin/classic/browser/zen-icons/sidebar.svg (../shared/zen-icons/nucleo/sidebar.svg)
* skin/classic/browser/zen-icons/sliders.svg (../shared/zen-icons/nucleo/sliders.svg)
* skin/classic/browser/zen-icons/sparkles.svg (../shared/zen-icons/nucleo/sparkles.svg)
* skin/classic/browser/zen-icons/spell-check.svg (../shared/zen-icons/nucleo/spell-check.svg)
@@ -309,13 +311,13 @@
* skin/classic/browser/zen-icons/autoplay-media-fill.svg (../shared/zen-icons/nucleo/autoplay-media-fill.svg)
* skin/classic/browser/zen-icons/autoplay-media.svg (../shared/zen-icons/nucleo/autoplay-media.svg)
* skin/classic/browser/zen-icons/back.svg (../shared/zen-icons/nucleo/back.svg)
* skin/classic/browser/zen-icons/blocked-element.svg (../shared/zen-icons/nucleo/blocked-element.svg)
* skin/classic/browser/zen-icons/block.svg (../shared/zen-icons/nucleo/block.svg)
* skin/classic/browser/zen-icons/bolt.svg (../shared/zen-icons/nucleo/bolt.svg)
* skin/classic/browser/zen-icons/bookmark-hollow.svg (../shared/zen-icons/nucleo/bookmark-hollow.svg)
* skin/classic/browser/zen-icons/bookmark-star-on-tray.svg (../shared/zen-icons/nucleo/bookmark-star-on-tray.svg)
* skin/classic/browser/zen-icons/bookmark.svg (../shared/zen-icons/nucleo/bookmark.svg)
* skin/classic/browser/zen-icons/boost.svg (../shared/zen-icons/nucleo/boost.svg)
* skin/classic/browser/zen-icons/blocked-element.svg (../shared/zen-icons/nucleo/blocked-element.svg)
* skin/classic/browser/zen-icons/brackets-curly.svg (../shared/zen-icons/nucleo/brackets-curly.svg)
* skin/classic/browser/zen-icons/camera-blocked.svg (../shared/zen-icons/nucleo/camera-blocked.svg)
* skin/classic/browser/zen-icons/camera-fill.svg (../shared/zen-icons/nucleo/camera-fill.svg)
@@ -333,6 +335,7 @@
* skin/classic/browser/zen-icons/desktop-notification-fill.svg (../shared/zen-icons/nucleo/desktop-notification-fill.svg)
* skin/classic/browser/zen-icons/desktop-notification.svg (../shared/zen-icons/nucleo/desktop-notification.svg)
* skin/classic/browser/zen-icons/developer.svg (../shared/zen-icons/nucleo/developer.svg)
* skin/classic/browser/zen-icons/dice.svg (../shared/zen-icons/nucleo/dice.svg)
* skin/classic/browser/zen-icons/downloads.svg (../shared/zen-icons/nucleo/downloads.svg)
* skin/classic/browser/zen-icons/drag-indicator.svg (../shared/zen-icons/nucleo/drag-indicator.svg)
* skin/classic/browser/zen-icons/duplicate-tab.svg (../shared/zen-icons/nucleo/duplicate-tab.svg)
@@ -398,26 +401,26 @@
* skin/classic/browser/zen-icons/popup-fill.svg (../shared/zen-icons/nucleo/popup-fill.svg)
* skin/classic/browser/zen-icons/popup.svg (../shared/zen-icons/nucleo/popup.svg)
* skin/classic/browser/zen-icons/print.svg (../shared/zen-icons/nucleo/print.svg)
* skin/classic/browser/zen-icons/private-window.svg (../shared/zen-icons/nucleo/private-window.svg)
* skin/classic/browser/zen-icons/privateBrowsing.svg (../shared/zen-icons/nucleo/privateBrowsing.svg)
* skin/classic/browser/zen-icons/private-window.svg (../shared/zen-icons/nucleo/private-window.svg)
* skin/classic/browser/zen-icons/reader-mode.svg (../shared/zen-icons/nucleo/reader-mode.svg)
* skin/classic/browser/zen-icons/reload.svg (../shared/zen-icons/nucleo/reload.svg)
* skin/classic/browser/zen-icons/save.svg (../shared/zen-icons/nucleo/save.svg)
* skin/classic/browser/zen-icons/screen-blocked.svg (../shared/zen-icons/nucleo/screen-blocked.svg)
* skin/classic/browser/zen-icons/screen.svg (../shared/zen-icons/nucleo/screen.svg)
* skin/classic/browser/zen-icons/screenshot.svg (../shared/zen-icons/nucleo/screenshot.svg)
* skin/classic/browser/zen-icons/screen.svg (../shared/zen-icons/nucleo/screen.svg)
* skin/classic/browser/zen-icons/search-glass.svg (../shared/zen-icons/nucleo/search-glass.svg)
* skin/classic/browser/zen-icons/search-page.svg (../shared/zen-icons/nucleo/search-page.svg)
* skin/classic/browser/zen-icons/security-broken.svg (../shared/zen-icons/nucleo/security-broken.svg)
* skin/classic/browser/zen-icons/security-warning.svg (../shared/zen-icons/nucleo/security-warning.svg)
* skin/classic/browser/zen-icons/security.svg (../shared/zen-icons/nucleo/security.svg)
* skin/classic/browser/zen-icons/security-warning.svg (../shared/zen-icons/nucleo/security-warning.svg)
* skin/classic/browser/zen-icons/send-to-device.svg (../shared/zen-icons/nucleo/send-to-device.svg)
* skin/classic/browser/zen-icons/settings-fill.svg (../shared/zen-icons/nucleo/settings-fill.svg)
* skin/classic/browser/zen-icons/settings.svg (../shared/zen-icons/nucleo/settings.svg)
* skin/classic/browser/zen-icons/share.svg (../shared/zen-icons/nucleo/share.svg)
* skin/classic/browser/zen-icons/sidebar-right.svg (../shared/zen-icons/nucleo/sidebar-right.svg)
* skin/classic/browser/zen-icons/sidebar.svg (../shared/zen-icons/nucleo/sidebar.svg)
* skin/classic/browser/zen-icons/sidebars-right.svg (../shared/zen-icons/nucleo/sidebars-right.svg)
* skin/classic/browser/zen-icons/sidebar.svg (../shared/zen-icons/nucleo/sidebar.svg)
* skin/classic/browser/zen-icons/sliders.svg (../shared/zen-icons/nucleo/sliders.svg)
* skin/classic/browser/zen-icons/sparkles.svg (../shared/zen-icons/nucleo/sparkles.svg)
* skin/classic/browser/zen-icons/spell-check.svg (../shared/zen-icons/nucleo/spell-check.svg)
@@ -453,8 +456,8 @@
* skin/classic/browser/zen-icons/selectable/basket.svg (../shared/zen-icons/common/selectable/basket.svg)
* skin/classic/browser/zen-icons/selectable/bed.svg (../shared/zen-icons/common/selectable/bed.svg)
* skin/classic/browser/zen-icons/selectable/bell.svg (../shared/zen-icons/common/selectable/bell.svg)
* skin/classic/browser/zen-icons/selectable/book.svg (../shared/zen-icons/common/selectable/book.svg)
* skin/classic/browser/zen-icons/selectable/bookmark.svg (../shared/zen-icons/common/selectable/bookmark.svg)
* skin/classic/browser/zen-icons/selectable/book.svg (../shared/zen-icons/common/selectable/book.svg)
* skin/classic/browser/zen-icons/selectable/briefcase.svg (../shared/zen-icons/common/selectable/briefcase.svg)
* skin/classic/browser/zen-icons/selectable/brush.svg (../shared/zen-icons/common/selectable/brush.svg)
* skin/classic/browser/zen-icons/selectable/bug.svg (../shared/zen-icons/common/selectable/bug.svg)
@@ -516,8 +519,8 @@
* skin/classic/browser/zen-icons/selectable/shapes.svg (../shared/zen-icons/common/selectable/shapes.svg)
* skin/classic/browser/zen-icons/selectable/shirt.svg (../shared/zen-icons/common/selectable/shirt.svg)
* skin/classic/browser/zen-icons/selectable/skull.svg (../shared/zen-icons/common/selectable/skull.svg)
* skin/classic/browser/zen-icons/selectable/square.svg (../shared/zen-icons/common/selectable/square.svg)
* skin/classic/browser/zen-icons/selectable/squares.svg (../shared/zen-icons/common/selectable/squares.svg)
* skin/classic/browser/zen-icons/selectable/square.svg (../shared/zen-icons/common/selectable/square.svg)
* skin/classic/browser/zen-icons/selectable/star-1.svg (../shared/zen-icons/common/selectable/star-1.svg)
* skin/classic/browser/zen-icons/selectable/star.svg (../shared/zen-icons/common/selectable/star.svg)
* skin/classic/browser/zen-icons/selectable/stats-chart.svg (../shared/zen-icons/common/selectable/stats-chart.svg)

View File

@@ -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 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="18" height="18" fill="context-fill" fill-opacity="context-fill-opacity" viewBox="0 0 18 18"><path d="M5 13.5a1 1 0 1 0 0-2 1 1 0 0 0 0 2M9.5 9a1 1 0 1 0 0-2 1 1 0 0 0 0 2m-2.25 2.25a1 1 0 1 0 0-2 1 1 0 0 0 0 2M1 13.75v-7c.033-.705.349-1.488.805-1.945.456-.456 1.239-.773 1.945-.805h7c.706.033 1.488.349 1.945.805.456.456.773 1.239.805 1.945v7c-.033.705-.349 1.488-.805 1.945-.456.456-1.239.773-1.945.805h-7c-.706-.033-1.488-.349-1.945-.805-.456-.456-.773-1.239-.805-1.945m1.866.884c.267.267.485.399.884.366h7c.399.033.617-.099.884-.366s.399-.485.366-.884v-7c.033-.399-.099-.617-.366-.884s-.485-.399-.884-.366h-7c-.399-.033-.617.099-.884.366s-.399.485-.366.884v7c-.033.399.099.617.366.884m12.274-2.737a.75.75 0 0 0 .852-.632l.978-6.581c.071-.703-.127-1.524-.511-2.042-.384-.519-1.112-.946-1.805-1.082L7.73.53C7.191.469 6.557.562 6.11.783s-.897.66-1.167 1.092a.75.75 0 1 0 1.299.749c.129-.258.275-.37.531-.497.258-.127.437-.178.735-.114l6.924 1.03c.399.026.595.188.82.492s.323.538.232.928l-.978 6.581a.75.75 0 0 0 .632.852z"/></svg>

View File

@@ -1,5 +1,5 @@
diff --git a/gfx/layers/AnimationInfo.cpp b/gfx/layers/AnimationInfo.cpp
index 1d330056bd7a4e89aac5e5296a3c164fb42b5c42..ef112715580b6bb7238e8f37bbe3133e187685dc 100644
index 1d330056bd7a4e89aac5e5296a3c164fb42b5c42..38bfbcfcaf0c791ee817aafbd24b1cad67974e62 100644
--- a/gfx/layers/AnimationInfo.cpp
+++ b/gfx/layers/AnimationInfo.cpp
@@ -14,6 +14,7 @@
@@ -10,12 +10,22 @@ index 1d330056bd7a4e89aac5e5296a3c164fb42b5c42..ef112715580b6bb7238e8f37bbe3133e
#include "nsIContent.h"
#include "nsLayoutUtils.h"
#include "nsRefreshDriver.h"
@@ -343,7 +344,7 @@ static void SetAnimatable(NonCustomCSSPropertyId aProperty,
@@ -343,7 +344,17 @@ static void SetAnimatable(NonCustomCSSPropertyId aProperty,
// resolve currentColor at this moment.
nscolor foreground =
aFrame->Style()->GetVisitedDependentColor(&nsStyleText::mColor);
- aAnimatable = aAnimationValue.GetColor(foreground);
+ aAnimatable = zen::nsZenBoostsBackend::FilterColorFromPresContext(aAnimationValue.GetColor(foreground), aFrame->PresContext());
+ nscolor resolved = aAnimationValue.GetColor(foreground);
+ // |foreground| is already boost-resolved through
+ // StyleAbsoluteColor::ToColor, so a currentColor keyframe is already
+ // filtered; only absolute keyframe colors still need the boost applied
+ // here, exactly once, so the composited transition endpoint matches the
+ // resting/static paint and doesn't snap when the transition ends.
+ aAnimatable =
+ aAnimationValue.IsCurrentColor()
+ ? resolved
+ : zen::nsZenBoostsBackend::FilterColorFromPresContext(
+ resolved, aFrame->PresContext());
break;
}
case eCSSProperty_opacity:

View File

@@ -1,5 +1,5 @@
diff --git a/layout/style/StyleColor.cpp b/layout/style/StyleColor.cpp
index 95c7ae6abea5032bef0466e8d59d212374d7a4d0..8dbfbb846b786d51af288989163aacfae12e787c 100644
index 95c7ae6abea5032bef0466e8d59d212374d7a4d0..3b2118e224141f5151a31ac663dfbe17864ef182 100644
--- a/layout/style/StyleColor.cpp
+++ b/layout/style/StyleColor.cpp
@@ -8,6 +8,7 @@
@@ -10,25 +10,7 @@ index 95c7ae6abea5032bef0466e8d59d212374d7a4d0..8dbfbb846b786d51af288989163aacfa
namespace mozilla {
@@ -21,6 +22,8 @@ bool StyleColor::MaybeTransparent() const {
template <>
StyleAbsoluteColor StyleColor::ResolveColor(
const StyleAbsoluteColor& aForegroundColor) const {
+ auto ResolveColorInner = [this,
+ &aForegroundColor]() -> StyleAbsoluteColor {
if (IsAbsolute()) {
return AsAbsolute();
}
@@ -30,6 +33,8 @@ StyleAbsoluteColor StyleColor::ResolveColor(
}
return Servo_ResolveColor(this, &aForegroundColor);
+ };
+ return zen::nsZenBoostsBackend::ResolveStyleColor(ResolveColorInner());
}
template <>
@@ -68,10 +73,11 @@ nscolor StyleAbsoluteColor::ToColor() const {
@@ -68,10 +69,11 @@ nscolor StyleAbsoluteColor::ToColor() const {
auto green = std::clamp(srgb.components._1, 0.0f, 1.0f);
auto blue = std::clamp(srgb.components._2, 0.0f, 1.0f);

View File

@@ -539,6 +539,12 @@ class nsZenBoostsManager {
const directoryPath = this.#cssPath;
const savePath = PathUtils.join(directoryPath, fileName);
if (!css || css.trim() === "") {
if (await IOUtils.exists(savePath)) {
await IOUtils.remove(savePath);
}
return;
}
await IOUtils.makeDirectory(directoryPath, { createAncestors: true });
await IOUtils.writeUTF8(savePath, css);
}

View File

@@ -26,6 +26,7 @@ export class ZapOverlay {
#dissolvePoolSize = 5;
#dissolveEffectPool = [];
#currentDissolveIndex = 0;
#onZapDoneClick = null;
/**
* @param {*} document Webpage document
@@ -77,10 +78,8 @@ export class ZapOverlay {
*/
#initializeElements() {
this.zapDoneButton = this.getElementById("zap-done");
this.zapDoneButton.addEventListener(
"click",
this.#disableZapMode.bind(this)
);
this.#onZapDoneClick = this.#disableZapMode.bind(this);
this.zapDoneButton.addEventListener("click", this.#onZapDoneClick);
this.#updateZappedList();
}
@@ -355,6 +354,12 @@ export class ZapOverlay {
dissolve.tearDown();
});
if (this.zapDoneButton && this.#onZapDoneClick) {
this.zapDoneButton.removeEventListener("click", this.#onZapDoneClick);
}
this.#onZapDoneClick = null;
this.zapDoneButton = null;
if (this.#content) {
try {
this.document.removeAnonymousContent(this.#content);

View File

@@ -322,13 +322,18 @@ export class ZenBoostsChild extends JSWindowActorChild {
return p;
}
get #hostWithoutPort() {
const host = this.browsingContext.topWindow?.location.host;
return host?.split(":")[0];
}
/**
* Aquires the boost data for this website
*
* @returns {object} Boost data for the current website
*/
getWebsiteBoost() {
const domain = this.browsingContext.topWindow?.location?.host;
const domain = this.#hostWithoutPort;
if (!domain) {
return null;
}
@@ -362,6 +367,7 @@ export class ZenBoostsChild extends JSWindowActorChild {
this.#loadStyleSheet(boost.styleSheet);
}
browsingContext.fullZoom = boostData.sizeOverride;
browsingContext.isZenBoostsInverted = boostData.smartInvert;
if (boostData.enableColorBoost) {
let primaryColor;
@@ -484,7 +490,7 @@ export class ZenBoostsChild extends JSWindowActorChild {
}
addZapSelector(selector) {
const domain = this.browsingContext.topWindow?.location?.host;
const domain = this.#hostWithoutPort;
this.sendQuery("ZenBoost:ZapSelector", {
action: "add",
selector,
@@ -493,7 +499,7 @@ export class ZenBoostsChild extends JSWindowActorChild {
}
removeZapSelector(selector) {
const domain = this.browsingContext.topWindow?.location?.host;
const domain = this.#hostWithoutPort;
this.sendQuery("ZenBoost:ZapSelector", {
action: "remove",
selector,

View File

@@ -0,0 +1,121 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "gtest/gtest.h"
#include "mozilla/nsZenBoostsBackend.h"
using zen::detail::FilterColorChannel;
using zen::detail::InvertColorChannel;
using zen::detail::PrecomputeAccent;
using zen::detail::RotateAccent;
namespace {
// A spread of representative input colors (opaque unless noted).
const nscolor kColors[] = {
NS_RGBA(255, 0, 0, 255), // pure red
NS_RGBA(0, 255, 0, 255), // pure green
NS_RGBA(0, 0, 255, 255), // pure blue
NS_RGBA(0, 0, 0, 255), // black
NS_RGBA(255, 255, 255, 255), // white
NS_RGBA(128, 128, 128, 255), // mid gray
NS_RGBA(18, 52, 86, 200), // arbitrary, semi-transparent
NS_RGBA(240, 17, 99, 1), // near-min alpha
};
// The accent stores the contrast/strength in its alpha byte
// (NS_GET_CONTRAST == NS_GET_A). 0 means "no tint".
zen::nsZenAccentOklab MakeAccent(uint8_t r, uint8_t g, uint8_t b,
uint8_t contrast) {
return PrecomputeAccent(NS_RGBA(r, g, b, contrast));
}
} // namespace
// The headline invariant: filtering must never change opacity. The whole
// pipeline overloads the alpha byte for contrast on the *accent*, but a
// filtered *content* color must keep its original alpha.
TEST(ZenBoostsColorFilter, PreservesAlpha)
{
const zen::nsZenAccentOklab accent = MakeAccent(80, 120, 200, 180);
const zen::nsZenAccentOklab complementary = RotateAccent(accent, 180.0f);
for (nscolor c : kColors) {
const nscolor out = FilterColorChannel(c, accent, complementary);
EXPECT_EQ(NS_GET_A(out), NS_GET_A(c)) << "alpha changed for input " << c;
}
}
// Fully transparent colors are invisible; the filter must pass them through
// untouched (and must not interpret their zero alpha as contrast).
TEST(ZenBoostsColorFilter, TransparentPassthrough)
{
const zen::nsZenAccentOklab accent = MakeAccent(80, 120, 200, 180);
const zen::nsZenAccentOklab complementary = RotateAccent(accent, 90.0f);
const nscolor transparent = NS_RGBA(255, 0, 0, 0);
EXPECT_EQ(FilterColorChannel(transparent, accent, complementary),
transparent);
EXPECT_EQ(InvertColorChannel(transparent), transparent);
}
// Same inputs must always yield the same output (no hidden global state in
// the math itself; the production cache lives outside these primitives).
TEST(ZenBoostsColorFilter, Deterministic)
{
const zen::nsZenAccentOklab accent = MakeAccent(33, 200, 90, 200);
const zen::nsZenAccentOklab complementary = RotateAccent(accent, 200.0f);
for (nscolor c : kColors) {
const nscolor a = FilterColorChannel(c, accent, complementary);
const nscolor b = FilterColorChannel(c, accent, complementary);
EXPECT_EQ(a, b);
EXPECT_EQ(InvertColorChannel(c), InvertColorChannel(c));
}
}
// A zero-contrast accent means "no boost strength": the color must come back
// essentially unchanged (allow +/-1 per channel for sRGB<->Oklab rounding).
TEST(ZenBoostsColorFilter, ZeroContrastIsNearIdentity)
{
const zen::nsZenAccentOklab accent = MakeAccent(200, 50, 50, 0);
const zen::nsZenAccentOklab complementary = RotateAccent(accent, 180.0f);
for (nscolor c : kColors) {
if (NS_GET_A(c) == 0) {
continue;
}
const nscolor out = FilterColorChannel(c, accent, complementary);
EXPECT_NEAR(NS_GET_R(out), NS_GET_R(c), 1);
EXPECT_NEAR(NS_GET_G(out), NS_GET_G(c), 1);
EXPECT_NEAR(NS_GET_B(out), NS_GET_B(c), 1);
EXPECT_EQ(NS_GET_A(out), NS_GET_A(c));
}
}
// Guards against a regression that turns the filter into a no-op: a strong
// accent applied to a neutral gray must actually move the color.
TEST(ZenBoostsColorFilter, StrongAccentActuallyTints)
{
const zen::nsZenAccentOklab accent = MakeAccent(20, 130, 240, 255);
const zen::nsZenAccentOklab complementary = RotateAccent(accent, 30.0f);
const nscolor gray = NS_RGBA(128, 128, 128, 255);
const nscolor out = FilterColorChannel(gray, accent, complementary);
const bool moved = NS_GET_R(out) != NS_GET_R(gray) ||
NS_GET_G(out) != NS_GET_G(gray) ||
NS_GET_B(out) != NS_GET_B(gray);
EXPECT_TRUE(moved) << "a full-strength accent should tint mid gray";
EXPECT_EQ(NS_GET_A(out), NS_GET_A(gray));
}
// Inversion must also preserve opacity.
TEST(ZenBoostsColorFilter, InvertPreservesAlpha)
{
for (nscolor c : kColors) {
EXPECT_EQ(NS_GET_A(InvertColorChannel(c)), NS_GET_A(c));
}
}

View File

@@ -0,0 +1,9 @@
# 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/.
UNIFIED_SOURCES += [
"TestZenBoostsColorFilter.cpp",
]
FINAL_LIBRARY = "xul-gtest"

View File

@@ -26,3 +26,7 @@ SOURCES += [
]
FINAL_LIBRARY = "xul"
TEST_DIRS += [
"gtest",
]

View File

@@ -43,7 +43,7 @@ static void RefreshBoostCacheIfMatchesCurrent(BrowsingContext* aChanged) {
if (!backend) {
return;
}
auto current = backend->GetCurrentBrowsingContext();
RefPtr<BrowsingContext> current = backend->GetCurrentBrowsingContext();
if (!current || current->Top() != aChanged) {
return;
}

View File

@@ -4,7 +4,9 @@
#include <cmath>
#include <algorithm>
#include <array>
#include <cstdint>
#include <cstring>
#include "nsZenBoostsBackend.h"
@@ -48,10 +50,6 @@ namespace zen {
NS_IMPL_ISUPPORTS0(nsZenBoostsBackend)
nsZenAccentOklab nsZenBoostsBackend::mCachedAccent{0};
nsZenAccentOklab nsZenBoostsBackend::mCachedComplementary{0};
float nsZenBoostsBackend::mCachedComplementaryRotationDeg = 0.0f;
namespace {
/**
@@ -83,17 +81,43 @@ static inline float linearToSrgb(float c) {
static inline float fastCbrt(float x) {
if (x == 0.0f) return 0.0f;
float a = std::abs(x);
union {
float f;
uint32_t i;
} u = {a};
u.i = u.i / 3 + 0x2a504a2e;
float y = u.f;
// Bit-level initial guess. Use memcpy rather than a union to avoid the
// undefined behaviour of type-punning through a union member in C++.
uint32_t i;
std::memcpy(&i, &a, sizeof(i));
i = i / 3 + 0x2a504a2e;
float y;
std::memcpy(&y, &i, sizeof(y));
y = (2.0f * y + a / (y * y)) * (1.0f / 3.0f);
y = (2.0f * y + a / (y * y)) * (1.0f / 3.0f);
return x < 0.0f ? -y : y;
}
/**
* @brief sRGB(0..255) -> linear lookup table. The filter only ever feeds
* integer 8-bit channels through srgbToLinear, so the 256 possible results
* are precomputed once instead of calling std::pow three times per color on
* the per-color hot path. Built lazily on first use; the function-local
* static makes initialization thread-safe.
*/
static inline const std::array<float, 256>& SrgbLinearTable() {
static const std::array<float, 256> kTable = [] {
std::array<float, 256> table{};
for (int i = 0; i < 256; ++i) {
table[i] = srgbToLinear(i * (1.0f / 255.0f));
}
return table;
}();
return kTable;
}
/**
* @brief Linearizes an 8-bit sRGB channel via the precomputed table.
*/
static inline float srgbToLinear8(uint8_t aChannel) {
return SrgbLinearTable()[aChannel];
}
/**
* @brief Precomputes the Oklab values for a given accent color. This allows us
* to efficiently apply the accent color as a filter to other colors without
@@ -107,13 +131,9 @@ ZEN_HOT_FUNCTION
inline static auto zenPrecomputeAccent(nscolor aAccentColor) {
constexpr float inv255 = 1.0f / 255.0f;
const float r = NS_GET_R(aAccentColor) * inv255;
const float g = NS_GET_G(aAccentColor) * inv255;
const float b = NS_GET_B(aAccentColor) * inv255;
const float lr = srgbToLinear(r);
const float lg = srgbToLinear(g);
const float lb = srgbToLinear(b);
const float lr = srgbToLinear8(NS_GET_R(aAccentColor));
const float lg = srgbToLinear8(NS_GET_G(aAccentColor));
const float lb = srgbToLinear8(NS_GET_B(aAccentColor));
const float l_ =
fastCbrt(0.4122214708f * lr + 0.5363325363f * lg + 0.0514459929f * lb);
@@ -156,6 +176,45 @@ inline static nsZenAccentOklab zenRotateAccent(const nsZenAccentOklab& aBase,
};
}
/**
* @brief Small round-robin cache of precomputed accents. Painting several
* boosted tabs with different accents interleaved would otherwise recompute
* the Oklab base accent (with cbrt) and the rotated complementary accent on
* every single color. Keyed by the accent nscolor and the complementary hue
* rotation. Main-thread only (same threading assumption as the per-color
* paint path it serves).
*/
struct AccentCacheEntry {
nscolor accentNS = 0;
float rotationDeg = 0.0f;
bool valid = false;
nsZenAccentOklab accent{};
nsZenAccentOklab complementary{};
};
static constexpr size_t kAccentCacheSize = 4;
static AccentCacheEntry sAccentCache[kAccentCacheSize];
static size_t sAccentCacheNext = 0;
ZEN_HOT_FUNCTION
static const AccentCacheEntry& GetCachedAccent(nscolor aAccentNS,
float aRotationDeg) {
for (const auto& entry : sAccentCache) {
if (entry.valid && entry.accentNS == aAccentNS &&
entry.rotationDeg == aRotationDeg) {
return entry;
}
}
AccentCacheEntry& slot = sAccentCache[sAccentCacheNext];
sAccentCacheNext = (sAccentCacheNext + 1) % kAccentCacheSize;
slot.accentNS = aAccentNS;
slot.rotationDeg = aRotationDeg;
slot.accent = zenPrecomputeAccent(aAccentNS);
slot.complementary = zenRotateAccent(slot.accent, aRotationDeg);
slot.valid = true;
return slot;
}
/**
* @brief Applies a duotone color filter to transform an original color toward
* one of two accent colors. The original color's perceived lightness decides
@@ -183,10 +242,10 @@ inline static nsZenAccentOklab zenRotateAccent(const nsZenAccentOklab& aBase,
constexpr float inv255 = 1.0f / 255.0f;
const float blendFactor = contrast * inv255;
// sRGB -> linear
const float lr = srgbToLinear(NS_GET_R(aOriginalColor) * inv255);
const float lg = srgbToLinear(NS_GET_G(aOriginalColor) * inv255);
const float lb = srgbToLinear(NS_GET_B(aOriginalColor) * inv255);
// sRGB -> linear (8-bit channels via the precomputed table)
const float lr = srgbToLinear8(NS_GET_R(aOriginalColor));
const float lg = srgbToLinear8(NS_GET_G(aOriginalColor));
const float lb = srgbToLinear8(NS_GET_B(aOriginalColor));
// Linear RGB -> LMS -> cube root -> Oklab (fused)
const float l_ =
@@ -347,6 +406,32 @@ inline static void GetZenBoostsDataFromBrowsingContext(
} // namespace
namespace detail {
// Thin forwarders that give unit tests access to the pure color math without
// pulling in the singleton / BrowsingContext. They are defined here, after the
// anonymous namespace, so they can reach those file-local implementations.
nsZenAccentOklab PrecomputeAccent(nscolor aAccentColor) {
return zenPrecomputeAccent(aAccentColor);
}
nsZenAccentOklab RotateAccent(const nsZenAccentOklab& aBase,
float aRotationDeg) {
return zenRotateAccent(aBase, aRotationDeg);
}
nscolor FilterColorChannel(nscolor aOriginalColor,
const nsZenAccentOklab& aAccent,
const nsZenAccentOklab& aComplementary) {
return zenFilterColorChannel(aOriginalColor, aAccent, aComplementary);
}
nscolor InvertColorChannel(nscolor aColor) {
return zenInvertColorChannel(aColor);
}
} // namespace detail
static mozilla::StaticRefPtr<nsZenBoostsBackend> sZenBoostsBackend;
auto nsZenBoostsBackend::GetInstance() -> nsZenBoostsBackend* {
@@ -376,18 +461,25 @@ auto nsZenBoostsBackend::onPresShellEntered(mozilla::dom::Document* aDocument)
if (!browsingContext) {
return;
}
mCurrentBrowsingContext = browsingContext;
mCurrentBrowsingContextId = browsingContext->Id();
RefreshCachedBoostState();
}
already_AddRefed<mozilla::dom::BrowsingContext>
nsZenBoostsBackend::GetCurrentBrowsingContext() const {
return mozilla::dom::BrowsingContext::Get(mCurrentBrowsingContextId);
}
auto nsZenBoostsBackend::RefreshCachedBoostState() -> void {
if (!mCurrentBrowsingContext) {
RefPtr<mozilla::dom::BrowsingContext> current =
mozilla::dom::BrowsingContext::Get(mCurrentBrowsingContextId);
if (!current) {
mCachedCurrentAccent = 0;
mCachedCurrentComplementaryRotation = 0.0f;
mCachedCurrentInverted = false;
return;
}
auto top = mCurrentBrowsingContext->Top();
auto top = current->Top();
mCachedCurrentAccent = top->ZenBoostsData();
mCachedCurrentComplementaryRotation = top->ZenBoostsComplementaryRotation();
mCachedCurrentInverted = top->IsZenBoostsInverted();
@@ -397,33 +489,27 @@ auto nsZenBoostsBackend::RefreshCachedBoostState() -> void {
nsZenBoostsBackend::FilterColorFromPresContext(nscolor aColor,
nsPresContext* aPresContext)
-> nscolor {
if (NS_GET_A(aColor) == 0) {
// Skip processing fully transparent colors since they won't be visible and
// we want to avoid unnecessary computations. This also prevents issues with
// using the alpha channel for contrast information in the accent color.
return aColor;
}
ZenBoostData accentNS = 0;
float complementaryRotation = 0.0f;
bool invertColors = false;
GetZenBoostsDataFromBrowsingContext(&accentNS, &complementaryRotation,
&invertColors, aPresContext);
if (accentNS) {
if (mCachedAccent.accentNS != accentNS) {
mCachedAccent = zenPrecomputeAccent(accentNS);
// Trigger a recompute of the complementary accent since
// it depends on the base accent.
mCachedComplementary.accentNS = 0;
}
// Derive the complementary accent by rotating the base accent's hue by the
// boost's complementary rotation. Cached so the per-color hot path only
// recomputes it when the base accent or rotation changes.
if (mCachedComplementary.accentNS != accentNS ||
mCachedComplementaryRotationDeg != complementaryRotation) {
mCachedComplementary =
zenRotateAccent(mCachedAccent, complementaryRotation);
mCachedComplementaryRotationDeg = complementaryRotation;
}
// Apply a filter-like tint:
// Resolve (and cache) the base + complementary accent for this accent and
// complementary rotation. Apply a filter-like tint:
// - Preserve the original color's perceived luminance
// - Map hue/chroma toward the base or complementary accent depending on
// the original color's lightness
// - Keep the original alpha
aColor = zenFilterColorChannel(aColor, mCachedAccent, mCachedComplementary);
const AccentCacheEntry& cached =
GetCachedAccent(accentNS, complementaryRotation);
aColor = zenFilterColorChannel(aColor, cached.accent, cached.complementary);
}
if (invertColors) {
aColor = zenInvertColorChannel(aColor);
@@ -431,20 +517,8 @@ nsZenBoostsBackend::FilterColorFromPresContext(nscolor aColor,
return aColor;
}
[[nodiscard]] ZEN_HOT_FUNCTION auto nsZenBoostsBackend::ResolveStyleColor(
mozilla::StyleAbsoluteColor aColor) -> mozilla::StyleAbsoluteColor {
const auto resultColor = FilterColorFromPresContext(aColor.ToColor());
return mozilla::StyleAbsoluteColor::FromColor(resultColor);
}
[[nodiscard]] ZEN_HOT_FUNCTION auto nsZenBoostsBackend::ResolveStyleColor(
nscolor aColor) -> nscolor {
if (NS_GET_A(aColor) == 0) {
// Skip processing fully transparent colors since they won't be visible and
// we want to avoid unnecessary computations. This also prevents issues with
// using the alpha channel for contrast information in the accent color.
return aColor;
}
return FilterColorFromPresContext(aColor);
}

View File

@@ -10,6 +10,13 @@
#include "nsPresContext.h"
#include "mozilla/RefPtr.h"
#include "mozilla/AlreadyAddRefed.h"
#include <cstdint>
namespace mozilla::dom {
class BrowsingContext;
}
#define ZEN_BOOSTS_BACKEND_CONTRACTID "@mozilla.org/zen/boosts-backend;1"
@@ -23,6 +30,19 @@ struct nsZenAccentOklab {
float contrastFactor;
};
namespace detail {
// Pure color-math primitives, exposed for unit testing. These have no
// dependency on the singleton, the BrowsingContext, or the process type, so
// they can be exercised directly from gtest.
nsZenAccentOklab PrecomputeAccent(nscolor aAccentColor);
nsZenAccentOklab RotateAccent(const nsZenAccentOklab& aBase,
float aRotationDeg);
nscolor FilterColorChannel(nscolor aOriginalColor,
const nsZenAccentOklab& aAccent,
const nsZenAccentOklab& aComplementary);
nscolor InvertColorChannel(nscolor aColor);
} // namespace detail
class nsZenBoostsBackend final : public nsISupports {
public:
NS_DECL_ISUPPORTS
@@ -36,17 +56,15 @@ class nsZenBoostsBackend final : public nsISupports {
bool mCurrentFrameIsAnonymousContent = false;
/**
* @brief Resolve a StyleAbsoluteColor to take into account Zen boosts.
* @brief Resolve a color to take into account Zen boosts. This is the single
* place style colors are filtered; it is reached for every style color via
* StyleAbsoluteColor::ToColor. Do not add a second StyleColor::ResolveColor
* filter on top of this or colors get filtered multiple times (which also
* makes resting colors disagree with composited transition endpoints).
* @param aColor The color to resolve.
* @return The resolved color with Zen boost filters applied, or the original
* color if no boost is active.
* @see StyleColor::ResolveColor for reference.
*/
static auto ResolveStyleColor(mozilla::StyleAbsoluteColor aColor)
-> mozilla::StyleAbsoluteColor;
/**
* @see ResolveStyleColor for reference.
* @see StyleAbsoluteColor::ToColor for reference.
*/
static auto ResolveStyleColor(nscolor aColor) -> nscolor;
@@ -73,10 +91,13 @@ class nsZenBoostsBackend final : public nsISupports {
*/
auto RefreshCachedBoostState() -> void;
[[nodiscard]]
inline auto GetCurrentBrowsingContext() const {
return mCurrentBrowsingContext;
}
/**
* Resolves the current top BrowsingContext from its stored id. May return
* null if it has since been discarded. Not on the per-color hot path; the
* hot path uses the mCachedCurrent* fields instead.
*/
[[nodiscard]] already_AddRefed<mozilla::dom::BrowsingContext>
GetCurrentBrowsingContext() const;
/**
* Cached boost data for the current top BrowsingContext, refreshed on
@@ -95,15 +116,12 @@ class nsZenBoostsBackend final : public nsISupports {
~nsZenBoostsBackend() = default;
/**
* The presshell of the current document being rendered.
* Id of the top BrowsingContext of the current document being rendered.
* Stored as an id rather than a strong RefPtr so the process-wide singleton
* does not keep a navigated-away BrowsingContext (and its subtree) alive
* until the next presshell entry.
*/
RefPtr<mozilla::dom::BrowsingContext> mCurrentBrowsingContext;
static nsZenAccentOklab mCachedAccent;
// Base accent with its Oklab hue rotated by mCachedComplementaryRotationDeg,
// recomputed only when the base accent or rotation changes.
static nsZenAccentOklab mCachedComplementary;
static float mCachedComplementaryRotationDeg;
uint64_t mCurrentBrowsingContextId = 0;
public:
/**

View File

@@ -8,5 +8,6 @@ support-files = [
]
["browser_boost_selector_basic.js"]
["browser_boost_selector_escaping.js"]
["browser_boost_selector_invalid.js"]
["browser_boost_selector_nthchild.js"]

View File

@@ -0,0 +1,94 @@
/* Any copyright is dedicated to the Public Domain.
https://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Covers code paths the basic/invalid/nthchild tests don't:
// - getIdentification() running ids/classes through CSS.escape()
// - the ancestor-disambiguation while-loop in traverse(), which only runs
// when the exact path still matches more than one element.
add_task(async function test_getSelectionPath_escapesSpecialChars() {
const doc = document.implementation.createHTMLDocument("TestEscape");
const container = doc.createElement("div");
// Characters that are invalid in a CSS selector unless escaped.
container.id = "with.dot:and#hash";
const target = doc.createElement("span");
target.className = "foo:bar baz.qux";
target.textContent = "target";
container.appendChild(target);
doc.body.appendChild(container);
const component = new SelectorComponent(doc, null, [], () => {});
const path = component.getSelectionPath(doc, 0, target);
ok(path, "A path should be generated for an element with special chars");
// The unescaped raw strings must not leak into the selector verbatim.
ok(
!path.includes("with.dot:and#hash"),
"Raw unescaped id must not appear in the selector"
);
// The generated selector must be valid and resolve back to the target.
let matched;
try {
matched = doc.querySelectorAll(path);
} catch (e) {
ok(false, `Generated selector should be parseable, got: ${e}`);
return;
}
ok(
Array.from(matched).includes(target),
"Escaped selector must still match the original element"
);
Assert.equal(
matched.length,
1,
"Selector should uniquely identify the element"
);
});
add_task(async function test_getSelectionPath_disambiguatesAncestors() {
const doc = document.implementation.createHTMLDocument("TestAncestors");
// Two structurally identical subtrees. The leaf elements carry no id/class,
// so disambiguation must climb ancestors until the path is unique. The two
// wrappers differ only by id, forcing the ancestor-walk loop in traverse().
const makeBranch = wrapperId => {
const wrapper = doc.createElement("section");
wrapper.id = wrapperId;
const mid = doc.createElement("div");
const leaf = doc.createElement("span");
leaf.textContent = "leaf";
mid.appendChild(leaf);
wrapper.appendChild(mid);
doc.body.appendChild(wrapper);
return leaf;
};
const leafA = makeBranch("branch-a");
const leafB = makeBranch("branch-b");
const component = new SelectorComponent(doc, null, [], () => {});
for (const [leaf, label] of [
[leafA, "branch-a"],
[leafB, "branch-b"],
]) {
const path = component.getSelectionPath(doc, 0, leaf);
ok(path, `Path generated for the leaf under ${label}`);
const matched = doc.querySelectorAll(path);
Assert.equal(
matched.length,
1,
`Selector for the ${label} leaf must be unique despite an identical sibling subtree`
);
ok(
matched[0] === leaf,
`Selector must resolve to the correct ${label} leaf, not the other branch`
);
}
});