1 /* -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/ArrayUtils.h"
10 #include "nsXPLookAndFeel.h"
11 #include "nsLookAndFeel.h"
12 #include "HeadlessLookAndFeel.h"
13 #include "RemoteLookAndFeel.h"
14 #include "nsContentUtils.h"
18 #include "nsIXULRuntime.h"
20 #include "SurfaceCacheUtils.h"
21 #include "mozilla/dom/ContentParent.h"
22 #include "mozilla/dom/ContentChild.h"
23 #include "mozilla/Preferences.h"
24 #include "mozilla/Services.h"
25 #include "mozilla/ServoStyleSet.h"
26 #include "mozilla/ServoCSSParser.h"
27 #include "mozilla/StaticPrefs_browser.h"
28 #include "mozilla/StaticPrefs_editor.h"
29 #include "mozilla/StaticPrefs_layout.h"
30 #include "mozilla/StaticPrefs_ui.h"
31 #include "mozilla/StaticPrefs_widget.h"
32 #include "mozilla/dom/Document.h"
33 #include "mozilla/PreferenceSheet.h"
34 #include "mozilla/gfx/2D.h"
35 #include "mozilla/widget/WidgetMessageUtils.h"
36 #include "mozilla/RelativeLuminanceUtils.h"
37 #include "mozilla/Telemetry.h"
38 #include "mozilla/TelemetryScalarEnums.h"
40 #include "gfxPlatform.h"
47 using namespace mozilla
;
49 using IntID
= mozilla::LookAndFeel::IntID
;
50 using FloatID
= mozilla::LookAndFeel::FloatID
;
51 using ColorID
= mozilla::LookAndFeel::ColorID
;
52 using FontID
= mozilla::LookAndFeel::FontID
;
54 template <typename Index
, typename Value
, Index kEnd
>
55 class EnumeratedCache
{
56 mozilla::EnumeratedArray
<Index
, kEnd
, Value
> mEntries
;
57 std::bitset
<size_t(kEnd
)> mValidity
;
60 constexpr EnumeratedCache() = default;
62 bool IsValid(Index aIndex
) const { return mValidity
[size_t(aIndex
)]; }
64 const Value
* Get(Index aIndex
) const {
65 return IsValid(aIndex
) ? &mEntries
[aIndex
] : nullptr;
68 void Insert(Index aIndex
, Value aValue
) {
69 mValidity
[size_t(aIndex
)] = true;
70 mEntries
[aIndex
] = aValue
;
73 void Remove(Index aIndex
) {
74 mValidity
[size_t(aIndex
)] = false;
75 mEntries
[aIndex
] = Value();
80 for (auto& entry
: mEntries
) {
86 using ColorCache
= EnumeratedCache
<ColorID
, Maybe
<nscolor
>, ColorID::End
>;
89 using UseStandins
= LookAndFeel::UseStandins
;
91 ColorCache mCaches
[2][2];
93 constexpr ColorCaches() = default;
95 ColorCache
& Get(ColorScheme aScheme
, UseStandins aUseStandins
) {
96 return mCaches
[aScheme
== ColorScheme::Dark
]
97 [aUseStandins
== UseStandins::Yes
];
101 for (auto& c
: mCaches
) {
102 for (auto& cache
: c
) {
109 static ColorCaches sColorCaches
;
111 static EnumeratedCache
<FloatID
, Maybe
<float>, FloatID::End
> sFloatCache
;
112 static EnumeratedCache
<IntID
, Maybe
<int32_t>, IntID::End
> sIntCache
;
113 static EnumeratedCache
<FontID
, widget::LookAndFeelFont
, FontID::End
> sFontCache
;
115 // To make one of these prefs toggleable from a reftest add a user
116 // pref in testing/profiles/reftest/user.js. For example, to make
117 // ui.useAccessibilityTheme toggleable, add:
119 // user_pref("ui.useAccessibilityTheme", 0);
121 // This needs to be of the same length and in the same order as
122 // LookAndFeel::IntID values.
123 static const char sIntPrefs
[][45] = {
125 "ui.caretBlinkCount",
127 "ui.caretVisibleWithSelection",
128 "ui.selectTextfieldsOnKeyFocus",
130 "ui.menusCanOverlapOSBar",
131 "ui.useOverlayScrollbars",
132 "ui.allowOverlayScrollbarsOverlap",
133 "ui.skipNavigatingDisabledMenuItem",
136 "ui.useAccessibilityTheme",
137 "ui.scrollArrowStyle",
138 "ui.scrollButtonLeftMouseButtonAction",
139 "ui.scrollButtonMiddleMouseButtonAction",
140 "ui.scrollButtonRightMouseButtonAction",
143 "ui.treeLazyScrollDelay",
144 "ui.treeScrollDelay",
145 "ui.treeScrollLinesMax",
146 "accessibility.tabfocus", // Weird one...
147 "ui.chosenMenuItemsShouldBlink",
148 "ui.windowsAccentColorInTitlebar",
149 "ui.windowsDefaultTheme",
153 "ui.macGraphiteTheme",
156 "ui.alertNotificationOrigin",
158 "ui.IMERawInputUnderlineStyle",
159 "ui.IMESelectedRawTextUnderlineStyle",
160 "ui.IMEConvertedTextUnderlineStyle",
161 "ui.IMESelectedConvertedTextUnderlineStyle",
162 "ui.SpellCheckerUnderlineStyle",
164 "ui.scrollbarButtonAutoRepeatBehavior",
166 "ui.swipeAnimationEnabled",
167 "ui.scrollbarDisplayOnMouseMove",
168 "ui.scrollbarFadeBeginDelay",
169 "ui.scrollbarFadeDuration",
170 "ui.contextMenuOffsetVertical",
171 "ui.contextMenuOffsetHorizontal",
172 "ui.GtkCSDAvailable",
173 "ui.GtkCSDMinimizeButton",
174 "ui.GtkCSDMaximizeButton",
175 "ui.GtkCSDCloseButton",
176 "ui.GtkCSDMinimizeButtonPosition",
177 "ui.GtkCSDMaximizeButtonPosition",
178 "ui.GtkCSDCloseButtonPosition",
179 "ui.GtkCSDReversedPlacement",
180 "ui.systemUsesDarkTheme",
181 "ui.prefersReducedMotion",
182 "ui.primaryPointerCapabilities",
183 "ui.allPointerCapabilities",
184 "ui.systemVerticalScrollbarWidth",
185 "ui.systemHorizontalScrollbarHeight",
186 "ui.touchDeviceSupportPresent",
190 "ui.videoDynamicRange",
191 "ui.panelAnimations",
194 static_assert(ArrayLength(sIntPrefs
) == size_t(LookAndFeel::IntID::End
),
195 "Should have a pref for each int value");
197 // This array MUST be kept in the same order as the float id list in
200 static const char sFloatPrefs
[][37] = {
201 "ui.IMEUnderlineRelativeSize",
202 "ui.SpellCheckerUnderlineRelativeSize",
203 "ui.caretAspectRatio",
204 "ui.textScaleFactor",
209 static_assert(ArrayLength(sFloatPrefs
) == size_t(LookAndFeel::FloatID::End
),
210 "Should have a pref for each float value");
212 // This array MUST be kept in the same order as the color list in
213 // specified/color.rs
214 static const char sColorPrefs
[][41] = {
220 "ui.buttonhighlight",
226 "ui.-moz-disabledfield",
230 "ui.-moz-comboboxtext",
236 "ui.inactivecaption",
237 "ui.inactivecaptiontext",
243 "ui.threeddarkshadow",
245 "ui.threedhighlight",
246 "ui.threedlightshadow",
251 "ui.-moz-buttondefault",
252 "ui.-moz-default-color",
253 "ui.-moz-default-background-color",
255 "ui.-moz-dialogtext",
256 "ui.-moz-dragtargetzone",
257 "ui.-moz-cellhighlight",
258 "ui.-moz_cellhighlighttext",
260 "ui.selecteditemtext",
261 "ui.-moz-buttonhoverface",
262 "ui.-moz_buttonhovertext",
264 "ui.-moz_menuhoverdisabled",
265 "ui.-moz_menuhovertext",
266 "ui.-moz_menubartext",
267 "ui.-moz_menubarhovertext",
268 "ui.-moz_eventreerow",
269 "ui.-moz_oddtreerow",
270 "ui.-moz-buttonactivetext",
271 "ui.-moz-buttonactiveface",
272 "ui.-moz-buttondisabledface",
273 "ui.-moz_mac_chrome_active",
274 "ui.-moz_mac_chrome_inactive",
275 "ui.-moz-mac-defaultbuttontext",
276 "ui.-moz-mac-focusring",
277 "ui.-moz-mac-menuselect",
278 "ui.-moz-mac-menushadow",
279 "ui.-moz-mac-menutextdisable",
280 "ui.-moz-mac-menutextselect",
281 "ui.-moz_mac_disabledtoolbartext",
282 "ui.-moz-mac-secondaryhighlight",
283 "ui.-moz-mac-menupopup",
284 "ui.-moz-mac-menuitem",
285 "ui.-moz-mac-active-menuitem",
286 "ui.-moz-mac-source-list",
287 "ui.-moz-mac-source-list-selection",
288 "ui.-moz-mac-active-source-list-selection",
289 "ui.-moz-mac-tooltip",
291 "ui.accentcolortext",
292 "ui.-moz-autofill-background",
293 "ui.-moz-win-mediatext",
294 "ui.-moz-win-communicationstext",
295 "ui.-moz-nativehyperlinktext",
296 "ui.-moz-nativevisitedhyperlinktext",
297 "ui.-moz-hyperlinktext",
298 "ui.-moz-activehyperlinktext",
299 "ui.-moz-visitedhyperlinktext",
300 "ui.-moz-colheadertext",
301 "ui.-moz-colheaderhovertext",
302 "ui.textSelectDisabledBackground",
303 "ui.textSelectAttentionBackground",
304 "ui.textSelectAttentionForeground",
305 "ui.textHighlightBackground",
306 "ui.textHighlightForeground",
307 "ui.IMERawInputBackground",
308 "ui.IMERawInputForeground",
309 "ui.IMERawInputUnderline",
310 "ui.IMESelectedRawTextBackground",
311 "ui.IMESelectedRawTextForeground",
312 "ui.IMESelectedRawTextUnderline",
313 "ui.IMEConvertedTextBackground",
314 "ui.IMEConvertedTextForeground",
315 "ui.IMEConvertedTextUnderline",
316 "ui.IMESelectedConvertedTextBackground",
317 "ui.IMESelectedConvertedTextForeground",
318 "ui.IMESelectedConvertedTextUnderline",
319 "ui.SpellCheckerUnderline",
320 "ui.themedScrollbar",
321 "ui.themedScrollbarInactive",
322 "ui.themedScrollbarThumb",
323 "ui.themedScrollbarThumbHover",
324 "ui.themedScrollbarThumbActive",
325 "ui.themedScrollbarThumbInactive",
328 static_assert(ArrayLength(sColorPrefs
) == size_t(LookAndFeel::ColorID::End
),
329 "Should have a pref for each color value");
331 // This array MUST be kept in the same order as the SystemFont enum.
332 static const char sFontPrefs
[][41] = {
336 "ui.font.message-box",
337 "ui.font.small-caption",
338 "ui.font.status-bar",
339 "ui.font.-moz-pull-down-menu",
340 "ui.font.-moz-button",
342 "ui.font.-moz-field",
345 static_assert(ArrayLength(sFontPrefs
) == size_t(LookAndFeel::FontID::End
),
346 "Should have a pref for each font value");
348 const char* nsXPLookAndFeel::GetColorPrefName(ColorID aId
) {
349 return sColorPrefs
[size_t(aId
)];
352 bool nsXPLookAndFeel::sInitialized
= false;
354 nsXPLookAndFeel
* nsXPLookAndFeel::sInstance
= nullptr;
355 bool nsXPLookAndFeel::sShutdown
= false;
357 auto LookAndFeel::SystemZoomSettings() -> ZoomSettings
{
358 ZoomSettings settings
;
359 switch (StaticPrefs::browser_display_os_zoom_behavior()) {
364 settings
.mFullZoom
= GetTextScaleFactor();
367 settings
.mTextZoom
= GetTextScaleFactor();
374 nsXPLookAndFeel
* nsXPLookAndFeel::GetInstance() {
379 NS_ENSURE_TRUE(!sShutdown
, nullptr);
381 // If we're in a content process, then the parent process will have supplied
382 // us with an initial FullLookAndFeel object.
383 // We grab this data from the ContentChild,
384 // where it's been temporarily stashed, and initialize our new LookAndFeel
387 FullLookAndFeel
* lnf
= nullptr;
389 if (auto* cc
= mozilla::dom::ContentChild::GetSingleton()) {
390 lnf
= &cc
->BorrowLookAndFeelData();
394 sInstance
= new widget::RemoteLookAndFeel(std::move(*lnf
));
395 } else if (gfxPlatform::IsHeadless()) {
396 sInstance
= new widget::HeadlessLookAndFeel();
398 sInstance
= new nsLookAndFeel();
401 // This is only ever used once during initialization, and can be cleared now.
406 widget::Theme::Init();
411 void nsXPLookAndFeel::Shutdown() {
420 // This keeps strings alive, so need to clear to make leak checking happy.
423 widget::Theme::Shutdown();
426 static void IntPrefChanged(const nsACString
& aPref
) {
427 // Most Int prefs can't change our system colors or fonts, but
428 // ui.systemUsesDarkTheme can, since it affects the effective color-scheme
429 // (affecting system colors).
430 auto changeKind
= aPref
.EqualsLiteral("ui.systemUsesDarkTheme")
431 ? widget::ThemeChangeKind::Style
432 : widget::ThemeChangeKind::MediaQueriesOnly
;
433 LookAndFeel::NotifyChangedAllWindows(changeKind
);
436 static void FloatPrefChanged(const nsACString
& aPref
) {
437 // Most float prefs can't change our system colors or fonts, but
438 // textScaleFactor affects layout.
439 auto changeKind
= aPref
.EqualsLiteral("ui.textScaleFactor")
440 ? widget::ThemeChangeKind::StyleAndLayout
441 : widget::ThemeChangeKind::MediaQueriesOnly
;
442 LookAndFeel::NotifyChangedAllWindows(changeKind
);
445 static void ColorPrefChanged() {
446 // Color prefs affect style, because they by definition change system colors.
447 LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind::Style
);
450 static void FontPrefChanged() {
451 // Color prefs affect style, because they by definition change system fonts.
452 LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind::Style
);
456 void nsXPLookAndFeel::OnPrefChanged(const char* aPref
, void* aClosure
) {
457 nsDependentCString
prefName(aPref
);
458 for (const char* pref
: sIntPrefs
) {
459 if (prefName
.Equals(pref
)) {
460 IntPrefChanged(prefName
);
465 for (const char* pref
: sFloatPrefs
) {
466 if (prefName
.Equals(pref
)) {
467 FloatPrefChanged(prefName
);
472 for (const char* pref
: sColorPrefs
) {
473 // We use StringBeginsWith to handle .dark prefs too.
474 if (StringBeginsWith(prefName
, nsDependentCString(pref
))) {
480 for (const char* pref
: sFontPrefs
) {
481 if (StringBeginsWith(prefName
, nsDependentCString(pref
))) {
488 bool LookAndFeel::WindowsNonNativeMenusEnabled() {
489 switch (StaticPrefs::browser_display_windows_non_native_menus()) {
496 return IsWin10OrLater();
503 static constexpr struct {
504 nsLiteralCString mName
;
505 widget::ThemeChangeKind mChangeKind
=
506 widget::ThemeChangeKind::MediaQueriesOnly
;
507 } kMediaQueryPrefs
[] = {
508 {"browser.display.windows.non_native_menus"_ns
},
509 // Affects whether standins are used for the accent color.
510 {"widget.non-native-theme.use-theme-accent"_ns
,
511 widget::ThemeChangeKind::Style
},
512 // These two affect system colors on Windows.
513 {"widget.windows.uwp-system-colors.enabled"_ns
,
514 widget::ThemeChangeKind::Style
},
515 // These two affect system colors on Windows.
516 {"widget.windows.uwp-system-colors.highlight-accent"_ns
,
517 widget::ThemeChangeKind::Style
},
519 {"layout.css.prefers-color-scheme.content-override"_ns
,
520 widget::ThemeChangeKind::Style
},
521 // Affects media queries and scrollbar sizes, so gotta relayout.
522 {"widget.gtk.overlay-scrollbars.enabled"_ns
,
523 widget::ThemeChangeKind::StyleAndLayout
},
524 // Affects zoom settings which includes text and full zoom.
525 {"browser.display.os-zoom-behavior"_ns
,
526 widget::ThemeChangeKind::StyleAndLayout
},
527 // This affects not only the media query, but also the native theme, so we
528 // need to re-layout.
529 {"browser.theme.toolbar-theme"_ns
, widget::ThemeChangeKind::AllBits
},
530 {"browser.theme.content-theme"_ns
},
531 {"layout.css.moz-box-flexbox-emulation.enabled"_ns
},
532 {"mathml.legacy_maction_and_semantics_implementations.disabled"_ns
},
533 {"mathml.ms_lquote_rquote_attributes.disabled"_ns
},
536 // Read values from the user's preferences.
537 // This is done once at startup, but since the user's preferences
538 // haven't actually been read yet at that time, we also have to
539 // set a callback to inform us of changes to each pref.
540 void nsXPLookAndFeel::Init() {
541 MOZ_RELEASE_ASSERT(NS_IsMainThread());
543 // Say we're already initialized, and take the chance that it might fail;
544 // protects against some other process writing to our static variables.
547 RecomputeColorSchemes();
549 // XXX If we could reorganize the pref names, we should separate the branch
550 // for each types. Then, we could reduce the unnecessary loop from
551 // nsXPLookAndFeel::OnPrefChanged().
552 Preferences::RegisterPrefixCallback(OnPrefChanged
, "ui.");
553 // We really do just want the accessibility.tabfocus pref, not other prefs
554 // that start with that string.
555 Preferences::RegisterCallback(OnPrefChanged
, "accessibility.tabfocus");
557 for (auto& pref
: kMediaQueryPrefs
) {
558 Preferences::RegisterCallback(
559 [](const char*, void* aChangeKind
) {
561 widget::ThemeChangeKind(reinterpret_cast<uintptr_t>(aChangeKind
));
562 LookAndFeel::NotifyChangedAllWindows(changeKind
);
564 pref
.mName
, reinterpret_cast<void*>(uintptr_t(pref
.mChangeKind
)));
568 nsXPLookAndFeel::~nsXPLookAndFeel() {
569 NS_ASSERTION(sInstance
== this,
570 "This destroying instance isn't the singleton instance");
574 static bool IsSpecialColor(LookAndFeel::ColorID aID
, nscolor aColor
) {
575 using ColorID
= LookAndFeel::ColorID
;
577 if (aColor
== NS_SAME_AS_FOREGROUND_COLOR
) {
582 case ColorID::IMESelectedRawTextBackground
:
583 case ColorID::IMESelectedConvertedTextBackground
:
584 case ColorID::IMERawInputBackground
:
585 case ColorID::IMEConvertedTextBackground
:
586 case ColorID::IMESelectedRawTextForeground
:
587 case ColorID::IMESelectedConvertedTextForeground
:
588 case ColorID::IMERawInputForeground
:
589 case ColorID::IMEConvertedTextForeground
:
590 case ColorID::IMERawInputUnderline
:
591 case ColorID::IMEConvertedTextUnderline
:
592 case ColorID::IMESelectedRawTextUnderline
:
593 case ColorID::IMESelectedConvertedTextUnderline
:
594 case ColorID::SpellCheckerUnderline
:
595 return NS_IS_SELECTION_SPECIAL_COLOR(aColor
);
600 * In GetColor(), every color that is not a special color is color
601 * corrected. Use false to make other colors color corrected.
606 nscolor
nsXPLookAndFeel::GetStandinForNativeColor(ColorID aID
,
607 ColorScheme aScheme
) {
608 if (aScheme
== ColorScheme::Dark
) {
609 if (auto color
= GenericDarkColor(aID
)) {
614 // The stand-in colors are taken from what the non-native theme needs (for
615 // field/button colors), the Windows 7 Aero theme except Mac-specific colors
616 // which are taken from Mac OS 10.7.
618 #define COLOR(name_, r, g, b) \
619 case ColorID::name_: \
620 return NS_RGB(r, g, b);
622 #define COLORA(name_, r, g, b, a) \
623 case ColorID::name_: \
624 return NS_RGBA(r, g, b, a);
627 // These are here for the purposes of headless mode.
628 case ColorID::IMESelectedRawTextBackground
:
629 case ColorID::IMESelectedConvertedTextBackground
:
630 case ColorID::IMERawInputBackground
:
631 case ColorID::IMEConvertedTextBackground
:
632 return NS_TRANSPARENT
;
633 case ColorID::IMESelectedRawTextForeground
:
634 case ColorID::IMESelectedConvertedTextForeground
:
635 case ColorID::IMERawInputForeground
:
636 case ColorID::IMEConvertedTextForeground
:
637 return NS_SAME_AS_FOREGROUND_COLOR
;
638 case ColorID::IMERawInputUnderline
:
639 case ColorID::IMEConvertedTextUnderline
:
640 return NS_40PERCENT_FOREGROUND_COLOR
;
641 case ColorID::Accentcolor
:
642 return widget::sDefaultAccent
.ToABGR();
643 case ColorID::Accentcolortext
:
644 return widget::sDefaultAccentText
.ToABGR();
645 COLOR(SpellCheckerUnderline
, 0xff, 0x00, 0x00)
646 COLOR(TextSelectDisabledBackground
, 0xaa, 0xaa, 0xaa)
649 COLOR(Activeborder
, 0xB4, 0xB4, 0xB4)
650 COLOR(Activecaption
, 0x99, 0xB4, 0xD1)
651 COLOR(Appworkspace
, 0xAB, 0xAB, 0xAB)
652 COLOR(Background
, 0x00, 0x00, 0x00)
653 COLOR(Buttonhighlight
, 0xFF, 0xFF, 0xFF)
654 COLOR(Buttonshadow
, 0xA0, 0xA0, 0xA0)
656 // Buttons and comboboxes should be kept in sync since they are drawn with
657 // the same colors by the non-native theme.
658 COLOR(Buttonface
, 0xe9, 0xe9, 0xed)
659 COLORA(MozButtondisabledface
, 0xe9, 0xe9, 0xed, 128)
661 COLOR(MozCombobox
, 0xe9, 0xe9, 0xed)
663 COLOR(Buttontext
, 0x00, 0x00, 0x00)
664 COLOR(MozComboboxtext
, 0x00, 0x00, 0x00)
666 COLOR(Captiontext
, 0x00, 0x00, 0x00)
667 COLOR(Graytext
, 0x6D, 0x6D, 0x6D)
668 COLOR(Highlight
, 0x33, 0x99, 0xFF)
669 COLOR(Highlighttext
, 0xFF, 0xFF, 0xFF)
670 COLOR(Inactiveborder
, 0xF4, 0xF7, 0xFC)
671 COLOR(Inactivecaption
, 0xBF, 0xCD, 0xDB)
672 COLOR(Inactivecaptiontext
, 0x43, 0x4E, 0x54)
673 COLOR(Infobackground
, 0xFF, 0xFF, 0xE1)
674 COLOR(Infotext
, 0x00, 0x00, 0x00)
675 COLOR(Menu
, 0xF0, 0xF0, 0xF0)
676 COLOR(Menutext
, 0x00, 0x00, 0x00)
677 COLOR(Scrollbar
, 0xC8, 0xC8, 0xC8)
678 COLOR(Threeddarkshadow
, 0x69, 0x69, 0x69)
679 COLOR(Threedface
, 0xF0, 0xF0, 0xF0)
680 COLOR(Threedhighlight
, 0xFF, 0xFF, 0xFF)
681 COLOR(Threedlightshadow
, 0xE3, 0xE3, 0xE3)
682 COLOR(Threedshadow
, 0xA0, 0xA0, 0xA0)
683 COLOR(Buttonborder
, 0xE3, 0xE3, 0xE3)
684 COLOR(Mark
, 0xFF, 0xFF, 0x00)
685 COLOR(Marktext
, 0x00, 0x00, 0x00)
686 COLOR(Window
, 0xFF, 0xFF, 0xFF)
687 COLOR(Windowframe
, 0x64, 0x64, 0x64)
688 COLOR(Windowtext
, 0x00, 0x00, 0x00)
689 COLOR(MozButtondefault
, 0x69, 0x69, 0x69)
690 COLOR(Field
, 0xFF, 0xFF, 0xFF)
691 COLORA(MozDisabledfield
, 0xFF, 0xFF, 0xFF, 128)
692 COLOR(Fieldtext
, 0x00, 0x00, 0x00)
693 COLOR(MozDialog
, 0xF0, 0xF0, 0xF0)
694 COLOR(MozDialogtext
, 0x00, 0x00, 0x00)
695 COLOR(MozColheadertext
, 0x00, 0x00, 0x00)
696 COLOR(MozColheaderhovertext
, 0x00, 0x00, 0x00)
697 COLOR(MozDragtargetzone
, 0xFF, 0xFF, 0xFF)
698 COLOR(MozCellhighlight
, 0xF0, 0xF0, 0xF0)
699 COLOR(MozCellhighlighttext
, 0x00, 0x00, 0x00)
700 COLOR(Selecteditem
, 0x33, 0x99, 0xFF)
701 COLOR(Selecteditemtext
, 0xFF, 0xFF, 0xFF)
702 COLOR(MozButtonhoverface
, 0xd0, 0xd0, 0xd7)
703 COLOR(MozButtonhovertext
, 0x00, 0x00, 0x00)
704 COLOR(MozButtonactiveface
, 0xb1, 0xb1, 0xb9)
705 COLOR(MozButtonactivetext
, 0x00, 0x00, 0x00)
706 COLOR(MozMenuhover
, 0x33, 0x99, 0xFF)
707 COLOR(MozMenuhovertext
, 0x00, 0x00, 0x00)
708 COLOR(MozMenubartext
, 0x00, 0x00, 0x00)
709 COLOR(MozMenubarhovertext
, 0x00, 0x00, 0x00)
710 COLOR(MozMenuhoverdisabled
, 0xF0, 0xF0, 0xF0)
711 COLOR(MozEventreerow
, 0xFF, 0xFF, 0xFF)
712 COLOR(MozOddtreerow
, 0xFF, 0xFF, 0xFF)
713 COLOR(MozMacChromeActive
, 0xB2, 0xB2, 0xB2)
714 COLOR(MozMacChromeInactive
, 0xE1, 0xE1, 0xE1)
715 COLOR(MozMacFocusring
, 0x60, 0x9D, 0xD7)
716 COLOR(MozMacMenuselect
, 0x38, 0x75, 0xD7)
717 COLOR(MozMacMenushadow
, 0xA3, 0xA3, 0xA3)
718 COLOR(MozMacMenutextdisable
, 0x88, 0x88, 0x88)
719 COLOR(MozMacMenutextselect
, 0xFF, 0xFF, 0xFF)
720 COLOR(MozMacDisabledtoolbartext
, 0x3F, 0x3F, 0x3F)
721 COLOR(MozMacSecondaryhighlight
, 0xD4, 0xD4, 0xD4)
722 COLOR(MozMacMenupopup
, 0xe6, 0xe6, 0xe6)
723 COLOR(MozMacMenuitem
, 0xe6, 0xe6, 0xe6)
724 COLOR(MozMacActiveMenuitem
, 0x0a, 0x64, 0xdc)
725 COLOR(MozMacSourceList
, 0xf7, 0xf7, 0xf7)
726 COLOR(MozMacSourceListSelection
, 0xc8, 0xc8, 0xc8)
727 COLOR(MozMacActiveSourceListSelection
, 0x0a, 0x64, 0xdc)
728 COLOR(MozMacTooltip
, 0xf7, 0xf7, 0xf7)
729 // Seems to be the default color (hardcoded because of bug 1065998)
730 COLOR(MozWinMediatext
, 0xFF, 0xFF, 0xFF)
731 COLOR(MozWinCommunicationstext
, 0xFF, 0xFF, 0xFF)
732 COLOR(MozNativehyperlinktext
, 0x00, 0x66, 0xCC)
733 COLOR(MozNativevisitedhyperlinktext
, 0x55, 0x1A, 0x8B)
737 return NS_RGB(0xFF, 0xFF, 0xFF);
743 // Taken from in-content/common.inc.css's dark theme.
744 Maybe
<nscolor
> nsXPLookAndFeel::GenericDarkColor(ColorID aID
) {
745 nscolor color
= NS_RGB(0, 0, 0);
746 static constexpr nscolor kWindowBackground
= NS_RGB(28, 27, 34);
747 static constexpr nscolor kWindowText
= NS_RGB(251, 251, 254);
749 case ColorID::Window
: // --in-content-page-background
750 case ColorID::Background
:
751 color
= kWindowBackground
;
755 color
= NS_RGB(0x2b, 0x2a, 0x33);
758 case ColorID::MozMenuhovertext
:
759 case ColorID::MozMenubarhovertext
:
760 case ColorID::Menutext
:
761 color
= NS_RGB(0xfb, 0xfb, 0xfe);
764 case ColorID::MozMenuhover
:
765 color
= NS_RGB(0x52, 0x52, 0x5e);
768 case ColorID::MozMenuhoverdisabled
:
769 color
= NS_RGB(0x3a, 0x39, 0x44);
772 case ColorID::MozOddtreerow
:
773 case ColorID::MozDialog
: // --in-content-box-background
774 color
= NS_RGB(35, 34, 43);
776 case ColorID::Windowtext
: // --in-content-page-color
777 case ColorID::MozDialogtext
:
778 case ColorID::Fieldtext
:
779 case ColorID::Buttontext
: // --in-content-button-text-color (via
780 // --in-content-page-color)
781 case ColorID::MozComboboxtext
:
782 case ColorID::MozButtonhovertext
:
783 case ColorID::MozButtonactivetext
:
786 case ColorID::Buttonshadow
:
787 case ColorID::Threedshadow
:
788 case ColorID::Threedlightshadow
:
789 case ColorID::Buttonborder
: // --in-content-box-border-color computed
790 // with kWindowText above
791 // kWindowBackground.
792 case ColorID::Graytext
: // opacity: 0.4 of kWindowText blended over the
793 // "Window" background color, which happens to be
795 color
= NS_ComposeColors(kWindowBackground
, NS_RGBA(251, 251, 254, 102));
797 case ColorID::MozCellhighlight
:
798 case ColorID::Selecteditem
: // --in-content-primary-button-background /
799 // --in-content-item-selected
800 color
= NS_RGB(0, 221, 255);
803 case ColorID::Buttonface
: // --in-content-button-background
804 case ColorID::Threedface
:
805 case ColorID::MozCombobox
:
806 case ColorID::MozCellhighlighttext
:
807 case ColorID::Selecteditemtext
: // --in-content-primary-button-text-color /
808 // --in-content-item-selected-text
809 color
= NS_RGB(43, 42, 51);
811 case ColorID::Threeddarkshadow
: // Same as Threedlightshadow but with the
813 case ColorID::MozDisabledfield
: // opacity: 0.4 of the face above blended
814 // over the "Window" background color.
815 case ColorID::MozButtondisabledface
:
816 color
= NS_ComposeColors(kWindowBackground
, NS_RGBA(43, 42, 51, 102));
818 case ColorID::MozButtonhoverface
: // --in-content-button-background-hover
819 color
= NS_RGB(82, 82, 94);
821 case ColorID::MozButtonactiveface
: // --in-content-button-background-active
822 color
= NS_RGB(91, 91, 102);
824 case ColorID::Highlight
:
825 color
= NS_RGBA(0, 221, 255, 78);
827 case ColorID::Highlighttext
:
828 color
= NS_SAME_AS_FOREGROUND_COLOR
;
830 case ColorID::MozNativehyperlinktext
:
831 // If you change this color, you probably also want to change the default
832 // value of browser.anchor_color.dark.
833 color
= NS_RGB(0x8c, 0x8c, 0xff);
835 case ColorID::MozNativevisitedhyperlinktext
:
836 // If you change this color, you probably also want to change the default
837 // value of browser.visited_color.dark.
838 color
= NS_RGB(0xff, 0xad, 0xff);
840 case ColorID::SpellCheckerUnderline
:
841 // This is the default for active links in dark mode as well
842 // (browser.active_color.dark). See bug 1755564 for some analysis and
843 // other options too.
844 color
= NS_RGB(0xff, 0x66, 0x66);
852 // Uncomment the #define below if you want to debug system color use in a skin
853 // that uses them. When set, it will make all system color pairs that are
854 // appropriate for foreground/background pairing the same. This means if the
855 // skin is using system colors correctly you will not be able to see *any* text.
857 // #define DEBUG_SYSTEM_COLOR_USE
859 #ifdef DEBUG_SYSTEM_COLOR_USE
860 static nsresult
SystemColorUseDebuggingColor(LookAndFeel::ColorID aID
,
862 using ColorID
= LookAndFeel::ColorID
;
865 // css2 http://www.w3.org/TR/REC-CSS2/ui.html#system-colors
866 case ColorID::Activecaption
:
867 // active window caption background
868 case ColorID::Captiontext
:
869 // text in active window caption
870 aResult
= NS_RGB(0xff, 0x00, 0x00);
873 case ColorID::Highlight
:
874 // background of selected item
875 case ColorID::Highlighttext
:
876 // text of selected item
877 aResult
= NS_RGB(0xff, 0xff, 0x00);
880 case ColorID::Inactivecaption
:
881 // inactive window caption
882 case ColorID::Inactivecaptiontext
:
883 // text in inactive window caption
884 aResult
= NS_RGB(0x66, 0x66, 0x00);
887 case ColorID::Infobackground
:
888 // tooltip background color
889 case ColorID::Infotext
:
890 // tooltip text color
891 aResult
= NS_RGB(0x00, 0xff, 0x00);
896 case ColorID::Menutext
:
898 aResult
= NS_RGB(0x00, 0xff, 0xff);
901 case ColorID::Threedface
:
902 case ColorID::Buttonface
:
904 case ColorID::Buttontext
:
905 // text on push buttons
906 aResult
= NS_RGB(0x00, 0x66, 0x66);
909 case ColorID::Window
:
910 case ColorID::Windowtext
:
911 aResult
= NS_RGB(0x00, 0x00, 0xff);
914 // from the CSS3 working draft (not yet finalized)
915 // http://www.w3.org/tr/2000/wd-css3-userint-20000216.html#color
918 case ColorID::Fieldtext
:
919 aResult
= NS_RGB(0xff, 0x00, 0xff);
922 case ColorID::MozDialog
:
923 case ColorID::MozDialogtext
:
924 aResult
= NS_RGB(0x66, 0x00, 0x66);
928 return NS_ERROR_NOT_AVAILABLE
;
935 static nsresult
GetPrefColor(const char* aPref
, nscolor
& aResult
) {
936 nsAutoCString colorStr
;
937 MOZ_TRY(Preferences::GetCString(aPref
, colorStr
));
938 if (!ServoCSSParser::ComputeColor(nullptr, NS_RGB(0, 0, 0), colorStr
,
940 return NS_ERROR_FAILURE
;
945 static nsresult
GetColorFromPref(LookAndFeel::ColorID aID
, ColorScheme aScheme
,
947 const char* prefName
= sColorPrefs
[size_t(aID
)];
948 if (aScheme
== ColorScheme::Dark
) {
949 nsAutoCString
darkPrefName(prefName
);
950 darkPrefName
.Append(".dark");
951 if (NS_SUCCEEDED(GetPrefColor(darkPrefName
.get(), aResult
))) {
955 return GetPrefColor(prefName
, aResult
);
958 // All these routines will return NS_OK if they have a value,
959 // in which case the nsLookAndFeel should use that value;
960 // otherwise we'll return NS_ERROR_NOT_AVAILABLE, in which case, the
961 // platform-specific nsLookAndFeel should use its own values instead.
962 nsresult
nsXPLookAndFeel::GetColorValue(ColorID aID
, ColorScheme aScheme
,
963 UseStandins aUseStandins
,
969 #ifdef DEBUG_SYSTEM_COLOR_USE
970 if (NS_SUCCEEDED(SystemColorUseDebuggingColor(aID
, aResult
))) {
975 auto& cache
= sColorCaches
.Get(aScheme
, aUseStandins
);
976 if (const auto* cached
= cache
.Get(aID
)) {
977 if (cached
->isNothing()) {
978 return NS_ERROR_FAILURE
;
980 aResult
= cached
->value();
984 // NOTE: Servo holds a lock and the main thread is paused, so writing to the
985 // global cache here is fine.
986 auto result
= GetUncachedColor(aID
, aScheme
, aUseStandins
);
987 cache
.Insert(aID
, result
);
989 return NS_ERROR_FAILURE
;
995 Maybe
<nscolor
> nsXPLookAndFeel::GetUncachedColor(ColorID aID
,
997 UseStandins aUseStandins
) {
998 if (aUseStandins
== UseStandins::Yes
) {
999 return Some(GetStandinForNativeColor(aID
, aScheme
));
1002 if (NS_SUCCEEDED(GetColorFromPref(aID
, aScheme
, r
))) {
1005 if (NS_SUCCEEDED(NativeGetColor(aID
, aScheme
, r
))) {
1006 if (gfxPlatform::GetCMSMode() == CMSMode::All
&& !IsSpecialColor(aID
, r
)) {
1007 qcms_transform
* transform
= gfxPlatform::GetCMSInverseRGBTransform();
1010 color
[0] = NS_GET_R(r
);
1011 color
[1] = NS_GET_G(r
);
1012 color
[2] = NS_GET_B(r
);
1013 color
[3] = NS_GET_A(r
);
1014 qcms_transform_data(transform
, color
, color
, 1);
1015 r
= NS_RGBA(color
[0], color
[1], color
[2], color
[3]);
1024 nsresult
nsXPLookAndFeel::GetIntValue(IntID aID
, int32_t& aResult
) {
1025 if (!sInitialized
) {
1029 if (const auto* cached
= sIntCache
.Get(aID
)) {
1030 if (cached
->isNothing()) {
1031 return NS_ERROR_FAILURE
;
1033 aResult
= cached
->value();
1037 if (NS_SUCCEEDED(Preferences::GetInt(sIntPrefs
[size_t(aID
)], &aResult
))) {
1038 sIntCache
.Insert(aID
, Some(aResult
));
1042 if (NS_FAILED(NativeGetInt(aID
, aResult
))) {
1043 sIntCache
.Insert(aID
, Nothing());
1044 return NS_ERROR_FAILURE
;
1047 sIntCache
.Insert(aID
, Some(aResult
));
1051 nsresult
nsXPLookAndFeel::GetFloatValue(FloatID aID
, float& aResult
) {
1052 if (!sInitialized
) {
1056 if (const auto* cached
= sFloatCache
.Get(aID
)) {
1057 if (cached
->isNothing()) {
1058 return NS_ERROR_FAILURE
;
1060 aResult
= cached
->value();
1065 if (NS_SUCCEEDED(Preferences::GetInt(sFloatPrefs
[size_t(aID
)], &pref
))) {
1066 aResult
= float(pref
) / 100.0f
;
1067 sFloatCache
.Insert(aID
, Some(aResult
));
1071 if (NS_FAILED(NativeGetFloat(aID
, aResult
))) {
1072 sFloatCache
.Insert(aID
, Nothing());
1073 return NS_ERROR_FAILURE
;
1076 sFloatCache
.Insert(aID
, Some(aResult
));
1080 bool nsXPLookAndFeel::LookAndFeelFontToStyle(const LookAndFeelFont
& aFont
,
1082 gfxFontStyle
& aStyle
) {
1083 if (!aFont
.haveFont()) {
1086 aName
= aFont
.name();
1087 aStyle
= gfxFontStyle();
1088 aStyle
.size
= aFont
.size();
1089 aStyle
.weight
= FontWeight::FromInt(aFont
.weight());
1091 aFont
.italic() ? FontSlantStyle::ITALIC
: FontSlantStyle::NORMAL
;
1092 aStyle
.systemFont
= true;
1096 widget::LookAndFeelFont
nsXPLookAndFeel::StyleToLookAndFeelFont(
1097 const nsAString
& aName
, const gfxFontStyle
& aStyle
) {
1098 LookAndFeelFont font
;
1099 font
.haveFont() = true;
1100 font
.name() = aName
;
1101 font
.size() = aStyle
.size
;
1102 font
.weight() = aStyle
.weight
.ToFloat();
1103 font
.italic() = aStyle
.style
.IsItalic();
1104 MOZ_ASSERT(aStyle
.style
.IsNormal() || aStyle
.style
.IsItalic(),
1105 "Cannot handle oblique font style");
1108 // Assert that all the remaining font style properties have their
1110 gfxFontStyle candidate
= aStyle
;
1111 gfxFontStyle defaults
{};
1112 candidate
.size
= defaults
.size
;
1113 candidate
.weight
= defaults
.weight
;
1114 candidate
.style
= defaults
.style
;
1115 MOZ_ASSERT(candidate
.Equals(defaults
),
1116 "Some font style properties not supported");
1122 bool nsXPLookAndFeel::GetFontValue(FontID aID
, nsString
& aName
,
1123 gfxFontStyle
& aStyle
) {
1124 if (const LookAndFeelFont
* cached
= sFontCache
.Get(aID
)) {
1125 return LookAndFeelFontToStyle(*cached
, aName
, aStyle
);
1128 LookAndFeelFont font
;
1129 auto GetFontsFromPrefs
= [&]() -> bool {
1130 nsDependentCString
pref(sFontPrefs
[size_t(aID
)]);
1131 if (NS_FAILED(Preferences::GetString(pref
.get(), aName
))) {
1134 font
.haveFont() = true;
1135 font
.name() = aName
;
1136 font
.size() = Preferences::GetFloat(nsAutoCString(pref
+ ".size"_ns
).get());
1137 // This is written this way rather than using the fallback so that an empty
1138 // pref (such like the one about:config creates) doesn't cause system fonts
1139 // to have zero-size.
1140 if (font
.size() < 1.0f
) {
1141 font
.size() = StyleFONT_MEDIUM_PX
;
1143 font
.weight() = Preferences::GetFloat(
1144 nsAutoCString(pref
+ ".weight"_ns
).get(), FontWeight::NORMAL
.ToFloat());
1146 Preferences::GetBool(nsAutoCString(pref
+ ".italic"_ns
).get());
1150 if (GetFontsFromPrefs()) {
1151 LookAndFeelFontToStyle(font
, aName
, aStyle
);
1152 } else if (NativeGetFont(aID
, aName
, aStyle
)) {
1153 font
= StyleToLookAndFeelFont(aName
, aStyle
);
1155 MOZ_ASSERT(!font
.haveFont());
1157 bool success
= font
.haveFont();
1158 sFontCache
.Insert(aID
, std::move(font
));
1162 void nsXPLookAndFeel::RefreshImpl() {
1163 // Wipe out our caches.
1164 sColorCaches
.Clear();
1166 sFloatCache
.Clear();
1168 RecomputeColorSchemes();
1170 // Clear any cached FullLookAndFeel data, which is now invalid.
1171 if (XRE_IsParentProcess()) {
1172 widget::RemoteLookAndFeel::ClearCachedData();
1176 static bool sRecordedLookAndFeelTelemetry
= false;
1178 void nsXPLookAndFeel::RecordTelemetry() {
1179 if (!XRE_IsParentProcess()) {
1183 if (sRecordedLookAndFeelTelemetry
) {
1187 sRecordedLookAndFeelTelemetry
= true;
1190 Telemetry::ScalarSet(
1191 Telemetry::ScalarID::WIDGET_DARK_MODE
,
1192 NS_SUCCEEDED(GetIntValue(IntID::SystemUsesDarkTheme
, i
)) && i
!= 0);
1194 RecordLookAndFeelSpecificTelemetry();
1199 static widget::ThemeChangeKind sGlobalThemeChangeKind
{0};
1201 void LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind aKind
) {
1202 sGlobalThemeChanged
= true;
1203 sGlobalThemeChangeKind
|= aKind
;
1205 if (nsCOMPtr
<nsIObserverService
> obs
= services::GetObserverService()) {
1206 const char16_t kind
[] = {char16_t(aKind
), 0};
1207 obs
->NotifyObservers(nullptr, "internal-look-and-feel-changed", kind
);
1211 void LookAndFeel::DoHandleGlobalThemeChange() {
1212 MOZ_ASSERT(sGlobalThemeChanged
);
1213 sGlobalThemeChanged
= false;
1214 auto kind
= std::exchange(sGlobalThemeChangeKind
, widget::ThemeChangeKind(0));
1216 // Tell the theme that it changed, so it can flush any handles to stale theme
1219 // We can use the *DoNotUseDirectly functions directly here, because we want
1220 // to notify all possible themes in a given process (but just once).
1221 if (XRE_IsParentProcess() ||
1222 !StaticPrefs::widget_non_native_theme_enabled()) {
1223 if (nsCOMPtr
<nsITheme
> theme
= do_GetNativeThemeDoNotUseDirectly()) {
1224 theme
->ThemeChanged();
1227 if (nsCOMPtr
<nsITheme
> theme
= do_GetBasicNativeThemeDoNotUseDirectly()) {
1228 theme
->ThemeChanged();
1231 // Clear all cached LookAndFeel colors.
1232 LookAndFeel::Refresh();
1234 // Reset default background and foreground colors for the document since they
1235 // may be using system colors.
1236 PreferenceSheet::Refresh();
1238 // Vector images (SVG) may be using theme colors so we discard all cached
1239 // surfaces. (We could add a vector image only version of DiscardAll, but
1240 // in bug 940625 we decided theme changes are rare enough not to bother.)
1241 image::SurfaceCacheUtils::DiscardAll();
1243 if (XRE_IsParentProcess()) {
1244 dom::ContentParent::BroadcastThemeUpdate(kind
);
1247 nsContentUtils::AddScriptRunner(
1248 NS_NewRunnableFunction("HandleGlobalThemeChange", [] {
1249 if (nsCOMPtr
<nsIObserverService
> obs
= services::GetObserverService()) {
1250 obs
->NotifyObservers(nullptr, "look-and-feel-changed", nullptr);
1255 #define BIT_FOR(_c) (1ull << size_t(ColorID::_c))
1257 // We want to use a non-native color scheme for the non-native theme (except in
1258 // high-contrast mode), so spoof some of the colors with stand-ins to prevent
1259 // lack of contrast.
1260 static constexpr std::bitset
<size_t(ColorID::End
)> sNonNativeThemeStandinColors
{
1261 // Used by default button styles.
1262 BIT_FOR(Buttonface
) | BIT_FOR(Buttontext
) | BIT_FOR(MozButtonhoverface
) |
1263 BIT_FOR(MozButtonhovertext
) | BIT_FOR(MozButtonactiveface
) |
1264 BIT_FOR(MozButtonactivetext
) | BIT_FOR(MozButtondisabledface
) |
1265 BIT_FOR(Buttonborder
) |
1266 // Used by select elements.
1267 BIT_FOR(MozCombobox
) | BIT_FOR(MozComboboxtext
) |
1268 BIT_FOR(Threedlightshadow
) |
1269 // For symmetry with the above.
1270 BIT_FOR(Threeddarkshadow
) |
1271 // Used by fieldset borders.
1272 BIT_FOR(Threedface
) |
1273 // Used by input / textarea.
1274 BIT_FOR(Field
) | BIT_FOR(Fieldtext
) |
1275 // Used by disabled form controls.
1276 BIT_FOR(MozDisabledfield
) | BIT_FOR(Graytext
) |
1277 // Some pages expect these to return windows-like colors, see bug 1773795.
1278 // Also, per spec these should match Canvas/CanvasText, see
1279 // https://drafts.csswg.org/css-color-4/#window
1280 BIT_FOR(Window
) | BIT_FOR(Windowtext
)};
1283 static bool ShouldUseStandinsForNativeColorForNonNativeTheme(
1284 const dom::Document
& aDoc
, LookAndFeel::ColorID aColor
,
1285 const PreferenceSheet::Prefs
& aPrefs
) {
1286 const bool shouldUseStandinsForColor
= [&] {
1287 if (sNonNativeThemeStandinColors
[size_t(aColor
)]) {
1290 // There are platforms where we want the content-exposed accent color to be
1291 // the windows blue rather than the system accent color, for now.
1292 return !StaticPrefs::widget_non_native_theme_use_theme_accent() &&
1293 (aColor
== LookAndFeel::ColorID::Accentcolor
||
1294 aColor
== LookAndFeel::ColorID::Accentcolortext
);
1297 return shouldUseStandinsForColor
&& aDoc
.ShouldAvoidNativeTheme() &&
1298 !aPrefs
.NonNativeThemeShouldBeHighContrast();
1301 ColorScheme
LookAndFeel::sChromeColorScheme
;
1302 ColorScheme
LookAndFeel::sContentColorScheme
;
1303 bool LookAndFeel::sColorSchemeInitialized
;
1304 bool LookAndFeel::sGlobalThemeChanged
;
1306 bool LookAndFeel::IsDarkColor(nscolor aColor
) {
1307 // Given https://www.w3.org/TR/WCAG20/#contrast-ratiodef, this is the
1308 // threshold that tells us whether contrast is better against white or black.
1310 // Contrast ratio against black is: (L + 0.05) / 0.05
1311 // Contrast ratio against white is: 1.05 / (L + 0.05)
1313 // So the intersection is:
1315 // (L + 0.05) / 0.05 = 1.05 / (L + 0.05)
1317 // And the solution to that equation is:
1319 // sqrt(1.05 * 0.05) - 0.05
1321 // So we consider a color dark if the contrast is below this threshold, and
1322 // it's at least half-opaque.
1323 constexpr float kThreshold
= 0.179129;
1324 return NS_GET_A(aColor
) > 127 &&
1325 RelativeLuminanceUtils::Compute(aColor
) < kThreshold
;
1328 auto LookAndFeel::ColorSchemeSettingForChrome() -> ChromeColorSchemeSetting
{
1329 switch (StaticPrefs::browser_theme_toolbar_theme()) {
1331 return ChromeColorSchemeSetting::Dark
;
1333 return ChromeColorSchemeSetting::Light
;
1335 return ChromeColorSchemeSetting::System
;
1339 ColorScheme
LookAndFeel::ThemeDerivedColorSchemeForContent() {
1340 switch (StaticPrefs::browser_theme_content_theme()) {
1342 return ColorScheme::Dark
;
1344 return ColorScheme::Light
;
1346 return SystemColorScheme();
1350 void LookAndFeel::RecomputeColorSchemes() {
1351 sColorSchemeInitialized
= true;
1353 sChromeColorScheme
= [] {
1354 switch (ColorSchemeSettingForChrome()) {
1355 case ChromeColorSchemeSetting::Light
:
1356 return ColorScheme::Light
;
1357 case ChromeColorSchemeSetting::Dark
:
1358 return ColorScheme::Dark
;
1359 case ChromeColorSchemeSetting::System
:
1362 return SystemColorScheme();
1365 sContentColorScheme
= [] {
1366 switch (StaticPrefs::layout_css_prefers_color_scheme_content_override()) {
1368 return ColorScheme::Dark
;
1370 return ColorScheme::Light
;
1372 return ThemeDerivedColorSchemeForContent();
1377 ColorScheme
LookAndFeel::ColorSchemeForStyle(
1378 const dom::Document
& aDoc
, const StyleColorSchemeFlags
& aFlags
,
1379 ColorSchemeMode aMode
) {
1380 using Choice
= PreferenceSheet::Prefs::ColorSchemeChoice
;
1382 const auto& prefs
= PreferenceSheet::PrefsFor(aDoc
);
1383 switch (prefs
.mColorSchemeChoice
) {
1384 case Choice::Standard
:
1386 case Choice::UserPreferred
:
1387 return aDoc
.PreferredColorScheme();
1389 return ColorScheme::Light
;
1391 return ColorScheme::Dark
;
1394 StyleColorSchemeFlags
style(aFlags
);
1396 style
.bits
= aDoc
.GetColorSchemeBits();
1398 const bool supportsDark
= bool(style
& StyleColorSchemeFlags::DARK
);
1399 const bool supportsLight
= bool(style
& StyleColorSchemeFlags::LIGHT
);
1400 if (supportsLight
&& supportsDark
) {
1401 // Both color-schemes are explicitly supported, use the preferred one.
1402 return aDoc
.PreferredColorScheme();
1404 if (supportsDark
|| supportsLight
) {
1405 // One color-scheme is explicitly supported and one isn't, so use the one
1406 // the content supports.
1407 return supportsDark
? ColorScheme::Dark
: ColorScheme::Light
;
1409 // No value specified. Chrome docs always supports both, so use the preferred
1411 if (aMode
== ColorSchemeMode::Preferred
||
1412 nsContentUtils::IsChromeDoc(&aDoc
)) {
1413 return aDoc
.PreferredColorScheme();
1415 // Default content to light.
1416 return ColorScheme::Light
;
1419 LookAndFeel::ColorScheme
LookAndFeel::ColorSchemeForFrame(
1420 const nsIFrame
* aFrame
, ColorSchemeMode aMode
) {
1421 return ColorSchemeForStyle(*aFrame
->PresContext()->Document(),
1422 aFrame
->StyleUI()->mColorScheme
.bits
, aMode
);
1426 Maybe
<nscolor
> LookAndFeel::GetColor(ColorID aId
, ColorScheme aScheme
,
1427 UseStandins aUseStandins
) {
1429 nsresult rv
= nsLookAndFeel::GetInstance()->GetColorValue(
1430 aId
, aScheme
, aUseStandins
, result
);
1431 if (NS_FAILED(rv
)) {
1434 return Some(result
);
1437 // Returns whether there is a CSS color name for this color.
1438 static bool ColorIsCSSAccessible(LookAndFeel::ColorID aId
) {
1439 using ColorID
= LookAndFeel::ColorID
;
1442 case ColorID::TextSelectDisabledBackground
:
1443 case ColorID::TextSelectAttentionBackground
:
1444 case ColorID::TextSelectAttentionForeground
:
1445 case ColorID::TextHighlightBackground
:
1446 case ColorID::TextHighlightForeground
:
1447 case ColorID::ThemedScrollbar
:
1448 case ColorID::ThemedScrollbarInactive
:
1449 case ColorID::ThemedScrollbarThumb
:
1450 case ColorID::ThemedScrollbarThumbActive
:
1451 case ColorID::ThemedScrollbarThumbInactive
:
1452 case ColorID::ThemedScrollbarThumbHover
:
1453 case ColorID::IMERawInputBackground
:
1454 case ColorID::IMERawInputForeground
:
1455 case ColorID::IMERawInputUnderline
:
1456 case ColorID::IMESelectedRawTextBackground
:
1457 case ColorID::IMESelectedRawTextForeground
:
1458 case ColorID::IMESelectedRawTextUnderline
:
1459 case ColorID::IMEConvertedTextBackground
:
1460 case ColorID::IMEConvertedTextForeground
:
1461 case ColorID::IMEConvertedTextUnderline
:
1462 case ColorID::IMESelectedConvertedTextBackground
:
1463 case ColorID::IMESelectedConvertedTextForeground
:
1464 case ColorID::IMESelectedConvertedTextUnderline
:
1465 case ColorID::SpellCheckerUnderline
:
1474 LookAndFeel::UseStandins
LookAndFeel::ShouldUseStandins(
1475 const dom::Document
& aDoc
, ColorID aId
) {
1476 const auto& prefs
= PreferenceSheet::PrefsFor(aDoc
);
1477 if (ShouldUseStandinsForNativeColorForNonNativeTheme(aDoc
, aId
, prefs
)) {
1478 return UseStandins::Yes
;
1480 if (prefs
.mUseStandins
&& ColorIsCSSAccessible(aId
)) {
1481 return UseStandins::Yes
;
1483 return UseStandins::No
;
1486 Maybe
<nscolor
> LookAndFeel::GetColor(ColorID aId
, const nsIFrame
* aFrame
) {
1487 const auto* doc
= aFrame
->PresContext()->Document();
1488 return GetColor(aId
, ColorSchemeForFrame(aFrame
),
1489 ShouldUseStandins(*doc
, aId
));
1493 nsresult
LookAndFeel::GetInt(IntID aID
, int32_t* aResult
) {
1494 return nsLookAndFeel::GetInstance()->GetIntValue(aID
, *aResult
);
1498 nsresult
LookAndFeel::GetFloat(FloatID aID
, float* aResult
) {
1499 return nsLookAndFeel::GetInstance()->GetFloatValue(aID
, *aResult
);
1503 bool LookAndFeel::GetFont(FontID aID
, nsString
& aName
, gfxFontStyle
& aStyle
) {
1504 return nsLookAndFeel::GetInstance()->GetFontValue(aID
, aName
, aStyle
);
1508 char16_t
LookAndFeel::GetPasswordCharacter() {
1509 return nsLookAndFeel::GetInstance()->GetPasswordCharacterImpl();
1513 bool LookAndFeel::GetEchoPassword() {
1514 if (StaticPrefs::editor_password_mask_delay() >= 0) {
1515 return StaticPrefs::editor_password_mask_delay() > 0;
1517 return nsLookAndFeel::GetInstance()->GetEchoPasswordImpl();
1521 uint32_t LookAndFeel::GetPasswordMaskDelay() {
1522 int32_t delay
= StaticPrefs::editor_password_mask_delay();
1524 return nsLookAndFeel::GetInstance()->GetPasswordMaskDelayImpl();
1529 bool LookAndFeel::DrawInTitlebar() {
1530 switch (StaticPrefs::browser_tabs_inTitlebar()) {
1538 return nsLookAndFeel::GetInstance()->GetDefaultDrawInTitlebar();
1541 void LookAndFeel::GetThemeInfo(nsACString
& aOut
) {
1542 nsLookAndFeel::GetInstance()->GetThemeInfo(aOut
);
1546 void LookAndFeel::Refresh() {
1547 nsLookAndFeel::GetInstance()->RefreshImpl();
1548 widget::Theme::LookAndFeelChanged();
1552 void LookAndFeel::NativeInit() { nsLookAndFeel::GetInstance()->NativeInit(); }
1555 void LookAndFeel::SetData(widget::FullLookAndFeel
&& aTables
) {
1556 nsLookAndFeel::GetInstance()->SetDataImpl(std::move(aTables
));
1559 } // namespace mozilla