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"
19 #include "nsNativeBasicTheme.h"
20 #include "mozilla/dom/ContentChild.h"
21 #include "mozilla/Preferences.h"
22 #include "mozilla/Services.h"
23 #include "mozilla/ServoStyleSet.h"
24 #include "mozilla/ServoCSSParser.h"
25 #include "mozilla/StaticPrefs_browser.h"
26 #include "mozilla/StaticPrefs_editor.h"
27 #include "mozilla/StaticPrefs_findbar.h"
28 #include "mozilla/StaticPrefs_ui.h"
29 #include "mozilla/StaticPrefs_widget.h"
30 #include "mozilla/dom/Document.h"
31 #include "mozilla/PreferenceSheet.h"
32 #include "mozilla/gfx/2D.h"
33 #include "mozilla/widget/WidgetMessageUtils.h"
34 #include "mozilla/Telemetry.h"
35 #include "mozilla/TelemetryScalarEnums.h"
37 #include "gfxPlatform.h"
46 using namespace mozilla
;
48 using IntID
= mozilla::LookAndFeel::IntID
;
49 using FloatID
= mozilla::LookAndFeel::FloatID
;
50 using ColorID
= mozilla::LookAndFeel::ColorID
;
51 using FontID
= mozilla::LookAndFeel::FontID
;
53 template <typename Index
, typename Value
, Index kEnd
>
54 class EnumeratedCache
{
55 static constexpr uint32_t ChunkFor(Index aIndex
) {
56 return uint32_t(aIndex
) >> 5; // >> 5 is the same as / 32.
58 static constexpr uint32_t BitFor(Index aIndex
) {
59 return 1u << (uint32_t(aIndex
) & 31);
61 static constexpr uint32_t kChunks
= ChunkFor(kEnd
) + 1;
63 mozilla::EnumeratedArray
<Index
, kEnd
, Value
> mEntries
;
64 uint32_t mValidity
[kChunks
] = {0};
67 constexpr EnumeratedCache() = default;
69 bool IsValid(Index aIndex
) const {
70 return mValidity
[ChunkFor(aIndex
)] & BitFor(aIndex
);
73 const Value
* Get(Index aIndex
) const {
74 return IsValid(aIndex
) ? &mEntries
[aIndex
] : nullptr;
77 void Insert(Index aIndex
, Value aValue
) {
78 mValidity
[ChunkFor(aIndex
)] |= BitFor(aIndex
);
79 mEntries
[aIndex
] = aValue
;
82 void Remove(Index aIndex
) {
83 mValidity
[ChunkFor(aIndex
)] &= ~BitFor(aIndex
);
84 mEntries
[aIndex
] = Value();
88 for (auto& chunk
: mValidity
) {
91 for (auto& entry
: mEntries
) {
97 static EnumeratedCache
<ColorID
, Maybe
<nscolor
>, ColorID::End
> sLightColorCache
;
98 static EnumeratedCache
<ColorID
, Maybe
<nscolor
>, ColorID::End
> sDarkColorCache
;
99 static EnumeratedCache
<FloatID
, Maybe
<float>, FloatID::End
> sFloatCache
;
100 static EnumeratedCache
<IntID
, Maybe
<int32_t>, IntID::End
> sIntCache
;
101 static EnumeratedCache
<FontID
, widget::LookAndFeelFont
, FontID::End
> sFontCache
;
103 // To make one of these prefs toggleable from a reftest add a user
104 // pref in testing/profiles/reftest/user.js. For example, to make
105 // ui.useAccessibilityTheme toggleable, add:
107 // user_pref("ui.useAccessibilityTheme", 0);
109 // This needs to be of the same length and in the same order as
110 // LookAndFeel::IntID values.
111 static const char sIntPrefs
[][43] = {
113 "ui.caretBlinkCount",
115 "ui.caretVisibleWithSelection",
116 "ui.selectTextfieldsOnKeyFocus",
118 "ui.menusCanOverlapOSBar",
119 "ui.useOverlayScrollbars",
120 "ui.allowOverlayScrollbarsOverlap",
121 "ui.skipNavigatingDisabledMenuItem",
124 "ui.useAccessibilityTheme",
125 "ui.scrollArrowStyle",
126 "ui.scrollSliderStyle",
127 "ui.scrollButtonLeftMouseButtonAction",
128 "ui.scrollButtonMiddleMouseButtonAction",
129 "ui.scrollButtonRightMouseButtonAction",
132 "ui.treeLazyScrollDelay",
133 "ui.treeScrollDelay",
134 "ui.treeScrollLinesMax",
135 "accessibility.tabfocus", // Weird one...
136 "ui.chosenMenuItemsShouldBlink",
137 "ui.windowsAccentColorInTitlebar",
138 "ui.windowsDefaultTheme",
142 "ui.macGraphiteTheme",
145 "ui.alertNotificationOrigin",
147 "ui.IMERawInputUnderlineStyle",
148 "ui.IMESelectedRawTextUnderlineStyle",
149 "ui.IMEConvertedTextUnderlineStyle",
150 "ui.IMESelectedConvertedTextUnderlineStyle",
151 "ui.SpellCheckerUnderlineStyle",
153 "ui.operatingSystemVersionIdentifier",
154 "ui.scrollbarButtonAutoRepeatBehavior",
156 "ui.swipeAnimationEnabled",
157 "ui.scrollbarDisplayOnMouseMove",
158 "ui.scrollbarFadeBeginDelay",
159 "ui.scrollbarFadeDuration",
160 "ui.contextMenuOffsetVertical",
161 "ui.contextMenuOffsetHorizontal",
162 "ui.GtkCSDAvailable",
163 "ui.GtkCSDMinimizeButton",
164 "ui.GtkCSDMaximizeButton",
165 "ui.GtkCSDCloseButton",
166 "ui.GtkCSDMinimizeButtonPosition",
167 "ui.GtkCSDMaximizeButtonPosition",
168 "ui.GtkCSDCloseButtonPosition",
169 "ui.GtkCSDReversedPlacement",
170 "ui.systemUsesDarkTheme",
171 "ui.prefersReducedMotion",
172 "ui.primaryPointerCapabilities",
173 "ui.allPointerCapabilities",
174 "ui.systemVerticalScrollbarWidth",
175 "ui.systemHorizontalScrollbarHeight",
176 "ui.touchDeviceSupportPresent",
181 static_assert(ArrayLength(sIntPrefs
) == size_t(LookAndFeel::IntID::End
),
182 "Should have a pref for each int value");
184 // This array MUST be kept in the same order as the float id list in
187 static const char sFloatPrefs
[][37] = {
188 "ui.IMEUnderlineRelativeSize",
189 "ui.SpellCheckerUnderlineRelativeSize",
190 "ui.caretAspectRatio",
191 "ui.textScaleFactor",
196 static_assert(ArrayLength(sFloatPrefs
) == size_t(LookAndFeel::FloatID::End
),
197 "Should have a pref for each float value");
199 // This array MUST be kept in the same order as the color list in
200 // specified/color.rs
201 static const char sColorPrefs
[][41] = {
202 "ui.textSelectDisabledBackground",
203 "ui.textSelectAttentionBackground",
204 "ui.textSelectAttentionForeground",
205 "ui.textHighlightBackground",
206 "ui.textHighlightForeground",
207 "ui.IMERawInputBackground",
208 "ui.IMERawInputForeground",
209 "ui.IMERawInputUnderline",
210 "ui.IMESelectedRawTextBackground",
211 "ui.IMESelectedRawTextForeground",
212 "ui.IMESelectedRawTextUnderline",
213 "ui.IMEConvertedTextBackground",
214 "ui.IMEConvertedTextForeground",
215 "ui.IMEConvertedTextUnderline",
216 "ui.IMESelectedConvertedTextBackground",
217 "ui.IMESelectedConvertedTextForeground",
218 "ui.IMESelectedConvertedTextUnderline",
219 "ui.SpellCheckerUnderline",
220 "ui.themedScrollbar",
221 "ui.themedScrollbarInactive",
222 "ui.themedScrollbarThumb",
223 "ui.themedScrollbarThumbHover",
224 "ui.themedScrollbarThumbActive",
225 "ui.themedScrollbarThumbInactive",
231 "ui.buttonhighlight",
236 "ui.-moz-disabledfield",
242 "ui.inactivecaption",
243 "ui.inactivecaptiontext",
249 "ui.threeddarkshadow",
251 "ui.threedhighlight",
252 "ui.threedlightshadow",
257 "ui.-moz-buttondefault",
258 "ui.-moz-default-color",
259 "ui.-moz-default-background-color",
261 "ui.-moz-dialogtext",
262 "ui.-moz-dragtargetzone",
263 "ui.-moz-cellhighlight",
264 "ui.-moz_cellhighlighttext",
266 "ui.selecteditemtext",
267 "ui.-moz-buttonhoverface",
268 "ui.-moz_buttonhovertext",
270 "ui.-moz_menuhovertext",
271 "ui.-moz_menubartext",
272 "ui.-moz_menubarhovertext",
273 "ui.-moz_eventreerow",
274 "ui.-moz_oddtreerow",
275 "ui.-moz-buttonactivetext",
276 "ui.-moz-buttonactiveface",
277 "ui.-moz-buttondisabledface",
278 "ui.-moz_mac_chrome_active",
279 "ui.-moz_mac_chrome_inactive",
280 "ui.-moz-mac-defaultbuttontext",
281 "ui.-moz-mac-focusring",
282 "ui.-moz-mac-menuselect",
283 "ui.-moz-mac-menushadow",
284 "ui.-moz-mac-menutextdisable",
285 "ui.-moz-mac-menutextselect",
286 "ui.-moz_mac_disabledtoolbartext",
287 "ui.-moz-mac-secondaryhighlight",
288 "ui.-moz-mac-vibrant-titlebar-light",
289 "ui.-moz-mac-vibrant-titlebar-dark",
290 "ui.-moz-mac-menupopup",
291 "ui.-moz-mac-menuitem",
292 "ui.-moz-mac-active-menuitem",
293 "ui.-moz-mac-source-list",
294 "ui.-moz-mac-source-list-selection",
295 "ui.-moz-mac-active-source-list-selection",
296 "ui.-moz-mac-tooltip",
297 "ui.-moz-accent-color",
298 "ui.-moz-accent-color-foreground",
299 "ui.-moz-autofill-background",
300 "ui.-moz-win-mediatext",
301 "ui.-moz-win-communicationstext",
302 "ui.-moz-nativehyperlinktext",
303 "ui.-moz-nativevisitedhyperlinktext",
304 "ui.-moz-hyperlinktext",
305 "ui.-moz-activehyperlinktext",
306 "ui.-moz-visitedhyperlinktext",
307 "ui.-moz-comboboxtext",
309 "ui.-moz-colheadertext",
310 "ui.-moz-colheaderhovertext",
313 static_assert(ArrayLength(sColorPrefs
) == size_t(LookAndFeel::ColorID::End
),
314 "Should have a pref for each color value");
316 const char* nsXPLookAndFeel::GetColorPrefName(ColorID aId
) {
317 return sColorPrefs
[size_t(aId
)];
320 bool nsXPLookAndFeel::sInitialized
= false;
322 nsXPLookAndFeel
* nsXPLookAndFeel::sInstance
= nullptr;
323 bool nsXPLookAndFeel::sShutdown
= false;
326 nsXPLookAndFeel
* nsXPLookAndFeel::GetInstance() {
331 NS_ENSURE_TRUE(!sShutdown
, nullptr);
333 // If we're in a content process, then the parent process will have supplied
334 // us with an initial FullLookAndFeel object.
335 // We grab this data from the ContentChild,
336 // where it's been temporarily stashed, and initialize our new LookAndFeel
339 FullLookAndFeel
* lnf
= nullptr;
341 if (auto* cc
= mozilla::dom::ContentChild::GetSingleton()) {
342 lnf
= &cc
->BorrowLookAndFeelData();
346 sInstance
= new widget::RemoteLookAndFeel(std::move(*lnf
));
347 } else if (gfxPlatform::IsHeadless()) {
348 sInstance
= new widget::HeadlessLookAndFeel();
350 sInstance
= new nsLookAndFeel();
353 // This is only ever used once during initialization, and can be cleared now.
358 nsNativeBasicTheme::Init();
363 void nsXPLookAndFeel::Shutdown() {
372 // This keeps strings alive, so need to clear to make leak checking happy.
375 nsNativeBasicTheme::Shutdown();
378 static void IntPrefChanged() {
379 // Int prefs can't change our system colors or fonts.
380 LookAndFeel::NotifyChangedAllWindows(
381 widget::ThemeChangeKind::MediaQueriesOnly
);
384 static void FloatPrefChanged() {
385 // Float prefs can't change our system colors or fonts.
386 LookAndFeel::NotifyChangedAllWindows(
387 widget::ThemeChangeKind::MediaQueriesOnly
);
390 static void ColorPrefChanged() {
391 // Color prefs affect style, because they by definition change system colors.
392 LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind::Style
);
396 void nsXPLookAndFeel::OnPrefChanged(const char* aPref
, void* aClosure
) {
397 nsDependentCString
prefName(aPref
);
398 for (const char* pref
: sIntPrefs
) {
399 if (prefName
.Equals(pref
)) {
405 for (const char* pref
: sFloatPrefs
) {
406 if (prefName
.Equals(pref
)) {
412 for (const char* pref
: sColorPrefs
) {
413 // We use StringBeginsWith to handle .dark prefs too.
414 if (StringBeginsWith(prefName
, nsDependentCString(pref
))) {
421 static constexpr struct {
422 nsLiteralCString mName
;
423 widget::ThemeChangeKind mChangeKind
=
424 widget::ThemeChangeKind::MediaQueriesOnly
;
425 } kMediaQueryPrefs
[] = {
426 {"browser.display.windows.native_menus"_ns
},
427 {"browser.proton.enabled"_ns
},
428 {"browser.proton.places-tooltip.enabled"_ns
},
429 {"layout.css.prefers-color-scheme.content-override"_ns
},
430 // This affects not only the media query, but also the native theme, so we
431 // need to re-layout.
432 {"browser.theme.toolbar-theme"_ns
, widget::ThemeChangeKind::AllBits
},
433 {"browser.theme.content-theme"_ns
},
434 {"layout.css.color-scheme.content-override"_ns
},
437 // Read values from the user's preferences.
438 // This is done once at startup, but since the user's preferences
439 // haven't actually been read yet at that time, we also have to
440 // set a callback to inform us of changes to each pref.
441 void nsXPLookAndFeel::Init() {
442 MOZ_RELEASE_ASSERT(NS_IsMainThread());
444 // Say we're already initialized, and take the chance that it might fail;
445 // protects against some other process writing to our static variables.
448 RecomputeColorSchemes();
450 // XXX If we could reorganize the pref names, we should separate the branch
451 // for each types. Then, we could reduce the unnecessary loop from
452 // nsXPLookAndFeel::OnPrefChanged().
453 Preferences::RegisterPrefixCallback(OnPrefChanged
, "ui.");
454 // We really do just want the accessibility.tabfocus pref, not other prefs
455 // that start with that string.
456 Preferences::RegisterCallback(OnPrefChanged
, "accessibility.tabfocus");
458 for (auto& pref
: kMediaQueryPrefs
) {
459 Preferences::RegisterCallback(
460 [](const char*, void* aChangeKind
) {
462 widget::ThemeChangeKind(reinterpret_cast<uintptr_t>(aChangeKind
));
463 LookAndFeel::NotifyChangedAllWindows(changeKind
);
465 pref
.mName
, reinterpret_cast<void*>(uintptr_t(pref
.mChangeKind
)));
469 nsXPLookAndFeel::~nsXPLookAndFeel() {
470 NS_ASSERTION(sInstance
== this,
471 "This destroying instance isn't the singleton instance");
475 static bool IsSpecialColor(LookAndFeel::ColorID aID
, nscolor aColor
) {
476 using ColorID
= LookAndFeel::ColorID
;
478 if (aColor
== NS_SAME_AS_FOREGROUND_COLOR
) {
483 case ColorID::IMESelectedRawTextBackground
:
484 case ColorID::IMESelectedConvertedTextBackground
:
485 case ColorID::IMERawInputBackground
:
486 case ColorID::IMEConvertedTextBackground
:
487 case ColorID::IMESelectedRawTextForeground
:
488 case ColorID::IMESelectedConvertedTextForeground
:
489 case ColorID::IMERawInputForeground
:
490 case ColorID::IMEConvertedTextForeground
:
491 case ColorID::IMERawInputUnderline
:
492 case ColorID::IMEConvertedTextUnderline
:
493 case ColorID::IMESelectedRawTextUnderline
:
494 case ColorID::IMESelectedConvertedTextUnderline
:
495 case ColorID::SpellCheckerUnderline
:
496 return NS_IS_SELECTION_SPECIAL_COLOR(aColor
);
501 * In GetColor(), every color that is not a special color is color
502 * corrected. Use false to make other colors color corrected.
507 nscolor
nsXPLookAndFeel::GetStandinForNativeColor(ColorID aID
,
508 ColorScheme aScheme
) {
509 if (aScheme
== ColorScheme::Dark
) {
510 if (auto color
= GenericDarkColor(aID
)) {
515 // The stand-in colors are taken from what the non-native theme needs (for
516 // field/button colors), the Windows 7 Aero theme except Mac-specific colors
517 // which are taken from Mac OS 10.7.
519 #define COLOR(name_, r, g, b) \
520 case ColorID::name_: \
521 return NS_RGB(r, g, b);
523 #define COLORA(name_, r, g, b, a) \
524 case ColorID::name_: \
525 return NS_RGBA(r, g, b, a);
529 COLOR(Activeborder
, 0xB4, 0xB4, 0xB4)
530 COLOR(Activecaption
, 0x99, 0xB4, 0xD1)
531 COLOR(Appworkspace
, 0xAB, 0xAB, 0xAB)
532 COLOR(Background
, 0x00, 0x00, 0x00)
533 COLOR(Buttonhighlight
, 0xFF, 0xFF, 0xFF)
534 COLOR(Buttonshadow
, 0xA0, 0xA0, 0xA0)
536 // Buttons and comboboxes should be kept in sync since they are drawn with
537 // the same colors by the non-native theme.
538 COLOR(Buttonface
, 0xe9, 0xe9, 0xed)
539 COLORA(MozButtondisabledface
, 0xe9, 0xe9, 0xed, 128)
541 COLOR(MozCombobox
, 0xe9, 0xe9, 0xed)
543 COLOR(Buttontext
, 0x00, 0x00, 0x00)
544 COLOR(MozComboboxtext
, 0x00, 0x00, 0x00)
546 COLOR(Captiontext
, 0x00, 0x00, 0x00)
547 COLOR(Graytext
, 0x6D, 0x6D, 0x6D)
548 COLOR(Highlight
, 0x33, 0x99, 0xFF)
549 COLOR(Highlighttext
, 0xFF, 0xFF, 0xFF)
550 COLOR(Inactiveborder
, 0xF4, 0xF7, 0xFC)
551 COLOR(Inactivecaption
, 0xBF, 0xCD, 0xDB)
552 COLOR(Inactivecaptiontext
, 0x43, 0x4E, 0x54)
553 COLOR(Infobackground
, 0xFF, 0xFF, 0xE1)
554 COLOR(Infotext
, 0x00, 0x00, 0x00)
555 COLOR(Menu
, 0xF0, 0xF0, 0xF0)
556 COLOR(Menutext
, 0x00, 0x00, 0x00)
557 COLOR(Scrollbar
, 0xC8, 0xC8, 0xC8)
558 COLOR(Threeddarkshadow
, 0x69, 0x69, 0x69)
559 COLOR(Threedface
, 0xF0, 0xF0, 0xF0)
560 COLOR(Threedhighlight
, 0xFF, 0xFF, 0xFF)
561 COLOR(Threedlightshadow
, 0xE3, 0xE3, 0xE3)
562 COLOR(Threedshadow
, 0xA0, 0xA0, 0xA0)
563 COLOR(Window
, 0xFF, 0xFF, 0xFF)
564 COLOR(Windowframe
, 0x64, 0x64, 0x64)
565 COLOR(Windowtext
, 0x00, 0x00, 0x00)
566 COLOR(MozButtondefault
, 0x69, 0x69, 0x69)
567 COLOR(Field
, 0xFF, 0xFF, 0xFF)
568 COLORA(MozDisabledfield
, 0xFF, 0xFF, 0xFF, 128)
569 COLOR(Fieldtext
, 0x00, 0x00, 0x00)
570 COLOR(MozDialog
, 0xF0, 0xF0, 0xF0)
571 COLOR(MozDialogtext
, 0x00, 0x00, 0x00)
572 COLOR(MozColheadertext
, 0x00, 0x00, 0x00)
573 COLOR(MozColheaderhovertext
, 0x00, 0x00, 0x00)
574 COLOR(MozDragtargetzone
, 0xFF, 0xFF, 0xFF)
575 COLOR(MozCellhighlight
, 0xF0, 0xF0, 0xF0)
576 COLOR(MozCellhighlighttext
, 0x00, 0x00, 0x00)
577 COLOR(Selecteditem
, 0x33, 0x99, 0xFF)
578 COLOR(Selecteditemtext
, 0xFF, 0xFF, 0xFF)
579 COLOR(MozButtonhoverface
, 0xd0, 0xd0, 0xd7)
580 COLOR(MozButtonhovertext
, 0x00, 0x00, 0x00)
581 COLOR(MozButtonactiveface
, 0xb1, 0xb1, 0xb9)
582 COLOR(MozButtonactivetext
, 0x00, 0x00, 0x00)
583 COLOR(MozMenuhover
, 0x33, 0x99, 0xFF)
584 COLOR(MozMenuhovertext
, 0x00, 0x00, 0x00)
585 COLOR(MozMenubartext
, 0x00, 0x00, 0x00)
586 COLOR(MozMenubarhovertext
, 0x00, 0x00, 0x00)
587 COLOR(MozOddtreerow
, 0xFF, 0xFF, 0xFF)
588 COLOR(MozMacChromeActive
, 0xB2, 0xB2, 0xB2)
589 COLOR(MozMacChromeInactive
, 0xE1, 0xE1, 0xE1)
590 COLOR(MozMacFocusring
, 0x60, 0x9D, 0xD7)
591 COLOR(MozMacMenuselect
, 0x38, 0x75, 0xD7)
592 COLOR(MozMacMenushadow
, 0xA3, 0xA3, 0xA3)
593 COLOR(MozMacMenutextdisable
, 0x88, 0x88, 0x88)
594 COLOR(MozMacMenutextselect
, 0xFF, 0xFF, 0xFF)
595 COLOR(MozMacDisabledtoolbartext
, 0x3F, 0x3F, 0x3F)
596 COLOR(MozMacSecondaryhighlight
, 0xD4, 0xD4, 0xD4)
597 COLOR(MozMacVibrantTitlebarLight
, 0xf7, 0xf7, 0xf7)
598 COLOR(MozMacVibrantTitlebarDark
, 0x28, 0x28, 0x28)
599 COLOR(MozMacMenupopup
, 0xe6, 0xe6, 0xe6)
600 COLOR(MozMacMenuitem
, 0xe6, 0xe6, 0xe6)
601 COLOR(MozMacActiveMenuitem
, 0x0a, 0x64, 0xdc)
602 COLOR(MozMacSourceList
, 0xf7, 0xf7, 0xf7)
603 COLOR(MozMacSourceListSelection
, 0xc8, 0xc8, 0xc8)
604 COLOR(MozMacActiveSourceListSelection
, 0x0a, 0x64, 0xdc)
605 COLOR(MozMacTooltip
, 0xf7, 0xf7, 0xf7)
606 // Seems to be the default color (hardcoded because of bug 1065998)
607 COLOR(MozWinMediatext
, 0xFF, 0xFF, 0xFF)
608 COLOR(MozWinCommunicationstext
, 0xFF, 0xFF, 0xFF)
609 COLOR(MozNativehyperlinktext
, 0x00, 0x66, 0xCC)
610 COLOR(MozNativevisitedhyperlinktext
, 0x55, 0x1A, 0x8B)
614 return NS_RGB(0xFF, 0xFF, 0xFF);
620 // Taken from in-content/common.inc.css's dark theme.
621 Maybe
<nscolor
> nsXPLookAndFeel::GenericDarkColor(ColorID aID
) {
622 nscolor color
= NS_RGB(0, 0, 0);
623 static constexpr nscolor kWindowBackground
= NS_RGB(28, 27, 34);
624 static constexpr nscolor kWindowText
= NS_RGB(251, 251, 254);
626 case ColorID::Window
: // --in-content-page-background
627 case ColorID::Background
:
629 color
= kWindowBackground
;
631 case ColorID::MozOddtreerow
:
632 case ColorID::MozDialog
: // --in-content-box-background
633 color
= NS_RGB(35, 34, 43);
635 case ColorID::Windowtext
: // --in-content-page-color
636 case ColorID::Menutext
:
637 case ColorID::MozDialogtext
:
638 case ColorID::Fieldtext
:
639 case ColorID::Buttontext
: // --in-content-button-text-color (via
640 // --in-content-page-color)
641 case ColorID::MozComboboxtext
:
642 case ColorID::MozButtonhovertext
:
643 case ColorID::MozButtonactivetext
:
646 case ColorID::Threedlightshadow
: // --in-content-box-border-color computed
647 // with kWindowText above
648 // kWindowBackground.
649 case ColorID::Graytext
: // opacity: 0.4 of kWindowText blended over the
650 // "Window" background color, which happens to be
652 color
= NS_ComposeColors(kWindowBackground
, NS_RGBA(251, 251, 254, 102));
654 case ColorID::MozCellhighlight
:
655 case ColorID::Selecteditem
: // --in-content-primary-button-background /
656 // --in-content-item-selected
657 color
= NS_RGB(0, 221, 255);
660 case ColorID::Buttonface
: // --in-content-button-background
661 case ColorID::MozCombobox
:
662 case ColorID::MozCellhighlighttext
:
663 case ColorID::Selecteditemtext
: // --in-content-primary-button-text-color /
664 // --in-content-item-selected-text
665 color
= NS_RGB(43, 42, 51);
667 case ColorID::Threeddarkshadow
: // Same as Threedlightshadow but with the
669 case ColorID::MozDisabledfield
: // opacity: 0.4 of the face above blended
670 // over the "Window" background color.
671 case ColorID::MozButtondisabledface
:
672 color
= NS_ComposeColors(kWindowBackground
, NS_RGBA(43, 42, 51, 102));
674 case ColorID::MozButtonhoverface
: // --in-content-button-background-hover
675 color
= NS_RGB(82, 82, 94);
677 case ColorID::MozButtonactiveface
: // --in-content-button-background-active
678 color
= NS_RGB(91, 91, 102);
680 case ColorID::Highlight
:
681 color
= NS_RGBA(0, 221, 255, 153);
683 case ColorID::Highlighttext
:
684 color
= NS_SAME_AS_FOREGROUND_COLOR
;
686 case ColorID::MozNativehyperlinktext
:
687 // If you change this color, you probably also want to change the default
688 // value of browser.anchor_color.dark.
689 color
= NS_RGB(0x8c, 0x8c, 0xff);
691 case ColorID::MozNativevisitedhyperlinktext
:
692 // If you change this color, you probably also want to change the default
693 // value of browser.visited_color.dark.
694 color
= NS_RGB(0xff, 0xad, 0xff);
703 // Uncomment the #define below if you want to debug system color use in a skin
704 // that uses them. When set, it will make all system color pairs that are
705 // appropriate for foreground/background pairing the same. This means if the
706 // skin is using system colors correctly you will not be able to see *any* text.
708 // #define DEBUG_SYSTEM_COLOR_USE
710 #ifdef DEBUG_SYSTEM_COLOR_USE
711 static nsresult
SystemColorUseDebuggingColor(LookAndFeel::ColorID aID
,
713 using ColorID
= LookAndFeel::ColorID
;
716 // css2 http://www.w3.org/TR/REC-CSS2/ui.html#system-colors
717 case ColorID::Activecaption
:
718 // active window caption background
719 case ColorID::Captiontext
:
720 // text in active window caption
721 aResult
= NS_RGB(0xff, 0x00, 0x00);
724 case ColorID::Highlight
:
725 // background of selected item
726 case ColorID::Highlighttext
:
727 // text of selected item
728 aResult
= NS_RGB(0xff, 0xff, 0x00);
731 case ColorID::Inactivecaption
:
732 // inactive window caption
733 case ColorID::Inactivecaptiontext
:
734 // text in inactive window caption
735 aResult
= NS_RGB(0x66, 0x66, 0x00);
738 case ColorID::Infobackground
:
739 // tooltip background color
740 case ColorID::Infotext
:
741 // tooltip text color
742 aResult
= NS_RGB(0x00, 0xff, 0x00);
747 case ColorID::Menutext
:
749 aResult
= NS_RGB(0x00, 0xff, 0xff);
752 case ColorID::Threedface
:
753 case ColorID::Buttonface
:
755 case ColorID::Buttontext
:
756 // text on push buttons
757 aResult
= NS_RGB(0x00, 0x66, 0x66);
760 case ColorID::Window
:
761 case ColorID::Windowtext
:
762 aResult
= NS_RGB(0x00, 0x00, 0xff);
765 // from the CSS3 working draft (not yet finalized)
766 // http://www.w3.org/tr/2000/wd-css3-userint-20000216.html#color
769 case ColorID::Fieldtext
:
770 aResult
= NS_RGB(0xff, 0x00, 0xff);
773 case ColorID::MozDialog
:
774 case ColorID::MozDialogtext
:
775 aResult
= NS_RGB(0x66, 0x00, 0x66);
779 return NS_ERROR_NOT_AVAILABLE
;
786 static nsresult
GetPrefColor(const char* aPref
, nscolor
& aResult
) {
787 nsAutoCString colorStr
;
788 MOZ_TRY(Preferences::GetCString(aPref
, colorStr
));
789 if (!ServoCSSParser::ComputeColor(nullptr, NS_RGB(0, 0, 0), colorStr
,
791 return NS_ERROR_FAILURE
;
796 static nsresult
GetColorFromPref(LookAndFeel::ColorID aID
, ColorScheme aScheme
,
798 const char* prefName
= sColorPrefs
[size_t(aID
)];
799 if (aScheme
== ColorScheme::Dark
) {
800 nsAutoCString
darkPrefName(prefName
);
801 darkPrefName
.Append(".dark");
802 if (NS_SUCCEEDED(GetPrefColor(darkPrefName
.get(), aResult
))) {
806 return GetPrefColor(prefName
, aResult
);
809 // All these routines will return NS_OK if they have a value,
810 // in which case the nsLookAndFeel should use that value;
811 // otherwise we'll return NS_ERROR_NOT_AVAILABLE, in which case, the
812 // platform-specific nsLookAndFeel should use its own values instead.
813 nsresult
nsXPLookAndFeel::GetColorValue(ColorID aID
, ColorScheme aScheme
,
814 UseStandins aUseStandins
,
820 #ifdef DEBUG_SYSTEM_COLOR_USE
821 if (NS_SUCCEEDED(SystemColorUseDebuggingColor(aID
, aResult
))) {
826 if (aUseStandins
== UseStandins::Yes
) {
827 aResult
= GetStandinForNativeColor(aID
, aScheme
);
832 aScheme
== ColorScheme::Light
? sLightColorCache
: sDarkColorCache
;
833 if (const auto* cached
= cache
.Get(aID
)) {
834 if (cached
->isNothing()) {
835 return NS_ERROR_FAILURE
;
837 aResult
= cached
->value();
841 if (NS_SUCCEEDED(GetColorFromPref(aID
, aScheme
, aResult
))) {
842 cache
.Insert(aID
, Some(aResult
));
846 if (NS_SUCCEEDED(NativeGetColor(aID
, aScheme
, aResult
))) {
847 if (gfxPlatform::GetCMSMode() == CMSMode::All
&&
848 !IsSpecialColor(aID
, aResult
)) {
849 qcms_transform
* transform
= gfxPlatform::GetCMSInverseRGBTransform();
852 color
[0] = NS_GET_R(aResult
);
853 color
[1] = NS_GET_G(aResult
);
854 color
[2] = NS_GET_B(aResult
);
855 color
[3] = NS_GET_A(aResult
);
856 qcms_transform_data(transform
, color
, color
, 1);
857 aResult
= NS_RGBA(color
[0], color
[1], color
[2], color
[3]);
861 // NOTE: Servo holds a lock and the main thread is paused, so writing to the
862 // global cache here is fine.
863 cache
.Insert(aID
, Some(aResult
));
867 cache
.Insert(aID
, Nothing());
868 return NS_ERROR_FAILURE
;
871 nsresult
nsXPLookAndFeel::GetIntValue(IntID aID
, int32_t& aResult
) {
876 if (const auto* cached
= sIntCache
.Get(aID
)) {
877 if (cached
->isNothing()) {
878 return NS_ERROR_FAILURE
;
880 aResult
= cached
->value();
884 if (NS_SUCCEEDED(Preferences::GetInt(sIntPrefs
[size_t(aID
)], &aResult
))) {
885 sIntCache
.Insert(aID
, Some(aResult
));
889 if (NS_FAILED(NativeGetInt(aID
, aResult
))) {
890 sIntCache
.Insert(aID
, Nothing());
891 return NS_ERROR_FAILURE
;
894 sIntCache
.Insert(aID
, Some(aResult
));
898 nsresult
nsXPLookAndFeel::GetFloatValue(FloatID aID
, float& aResult
) {
903 if (const auto* cached
= sFloatCache
.Get(aID
)) {
904 if (cached
->isNothing()) {
905 return NS_ERROR_FAILURE
;
907 aResult
= cached
->value();
912 if (NS_SUCCEEDED(Preferences::GetInt(sFloatPrefs
[size_t(aID
)], &pref
))) {
913 aResult
= float(pref
) / 100.0f
;
914 sFloatCache
.Insert(aID
, Some(aResult
));
918 if (NS_FAILED(NativeGetFloat(aID
, aResult
))) {
919 sFloatCache
.Insert(aID
, Nothing());
920 return NS_ERROR_FAILURE
;
923 sFloatCache
.Insert(aID
, Some(aResult
));
927 bool nsXPLookAndFeel::LookAndFeelFontToStyle(const LookAndFeelFont
& aFont
,
929 gfxFontStyle
& aStyle
) {
930 if (!aFont
.haveFont()) {
933 aName
= aFont
.name();
934 aStyle
= gfxFontStyle();
935 aStyle
.size
= aFont
.size();
936 aStyle
.weight
= FontWeight(aFont
.weight());
938 aFont
.italic() ? FontSlantStyle::Italic() : FontSlantStyle::Normal();
939 aStyle
.systemFont
= true;
943 widget::LookAndFeelFont
nsXPLookAndFeel::StyleToLookAndFeelFont(
944 const nsAString
& aName
, const gfxFontStyle
& aStyle
) {
945 LookAndFeelFont font
;
946 font
.haveFont() = true;
948 font
.size() = aStyle
.size
;
949 font
.weight() = aStyle
.weight
.ToFloat();
950 font
.italic() = aStyle
.style
.IsItalic();
951 MOZ_ASSERT(aStyle
.style
.IsNormal() || aStyle
.style
.IsItalic(),
952 "Cannot handle oblique font style");
955 // Assert that all the remaining font style properties have their
957 gfxFontStyle candidate
= aStyle
;
958 gfxFontStyle defaults
{};
959 candidate
.size
= defaults
.size
;
960 candidate
.weight
= defaults
.weight
;
961 candidate
.style
= defaults
.style
;
962 MOZ_ASSERT(candidate
.Equals(defaults
),
963 "Some font style properties not supported");
969 bool nsXPLookAndFeel::GetFontValue(FontID aID
, nsString
& aName
,
970 gfxFontStyle
& aStyle
) {
971 if (const LookAndFeelFont
* cached
= sFontCache
.Get(aID
)) {
972 return LookAndFeelFontToStyle(*cached
, aName
, aStyle
);
974 LookAndFeelFont font
;
975 const bool haveFont
= NativeGetFont(aID
, aName
, aStyle
);
976 font
.haveFont() = haveFont
;
978 font
= StyleToLookAndFeelFont(aName
, aStyle
);
980 sFontCache
.Insert(aID
, std::move(font
));
984 void nsXPLookAndFeel::RefreshImpl() {
985 // Wipe out our caches.
986 sLightColorCache
.Clear();
987 sDarkColorCache
.Clear();
991 RecomputeColorSchemes();
993 // Clear any cached FullLookAndFeel data, which is now invalid.
994 if (XRE_IsParentProcess()) {
995 widget::RemoteLookAndFeel::ClearCachedData();
999 static bool sRecordedLookAndFeelTelemetry
= false;
1001 void nsXPLookAndFeel::RecordTelemetry() {
1002 if (!XRE_IsParentProcess()) {
1006 if (sRecordedLookAndFeelTelemetry
) {
1010 sRecordedLookAndFeelTelemetry
= true;
1013 Telemetry::ScalarSet(
1014 Telemetry::ScalarID::WIDGET_DARK_MODE
,
1015 NS_SUCCEEDED(GetIntValue(IntID::SystemUsesDarkTheme
, i
)) && i
!= 0);
1017 RecordLookAndFeelSpecificTelemetry();
1023 void LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind aKind
) {
1024 if (nsCOMPtr
<nsIObserverService
> obs
= services::GetObserverService()) {
1025 const char16_t kind
[] = {char16_t(aKind
), 0};
1026 obs
->NotifyObservers(nullptr, "internal-look-and-feel-changed", kind
);
1030 static bool ShouldUseStandinsForNativeColorForNonNativeTheme(
1031 const dom::Document
& aDoc
, LookAndFeel::ColorID aColor
) {
1032 using ColorID
= LookAndFeel::ColorID
;
1033 if (!aDoc
.ShouldAvoidNativeTheme()) {
1037 // The native theme doesn't use native system colors backgrounds etc, except
1038 // when in high-contrast mode, so spoof some of the colors with stand-ins to
1039 // prevent lack of contrast.
1041 case ColorID::Buttonface
:
1042 case ColorID::Buttontext
:
1043 case ColorID::MozButtonhoverface
:
1044 case ColorID::MozButtonhovertext
:
1045 case ColorID::MozButtonactiveface
:
1046 case ColorID::MozButtonactivetext
:
1047 case ColorID::MozButtondisabledface
:
1049 case ColorID::Threedlightshadow
:
1050 case ColorID::Threeddarkshadow
:
1051 case ColorID::Threedface
:
1053 case ColorID::MozCombobox
:
1054 case ColorID::MozComboboxtext
:
1056 case ColorID::Field
:
1057 case ColorID::MozDisabledfield
:
1058 case ColorID::Fieldtext
:
1060 case ColorID::Graytext
:
1062 return !PreferenceSheet::PrefsFor(aDoc
)
1063 .NonNativeThemeShouldBeHighContrast();
1072 ColorScheme
LookAndFeel::sChromeColorScheme
;
1073 ColorScheme
LookAndFeel::sContentColorScheme
;
1075 auto LookAndFeel::ColorSchemeSettingForChrome() -> ChromeColorSchemeSetting
{
1076 switch (StaticPrefs::browser_theme_toolbar_theme()) {
1078 return ChromeColorSchemeSetting::Dark
;
1080 return ChromeColorSchemeSetting::Light
;
1082 return ChromeColorSchemeSetting::System
;
1086 void LookAndFeel::RecomputeColorSchemes() {
1087 sChromeColorScheme
= [] {
1088 switch (ColorSchemeSettingForChrome()) {
1089 case ChromeColorSchemeSetting::Light
:
1090 return ColorScheme::Light
;
1091 case ChromeColorSchemeSetting::Dark
:
1092 return ColorScheme::Dark
;
1093 case ChromeColorSchemeSetting::System
:
1096 return SystemColorScheme();
1099 sContentColorScheme
= [] {
1100 switch (StaticPrefs::layout_css_prefers_color_scheme_content_override()) {
1102 return ColorScheme::Dark
;
1104 return ColorScheme::Light
;
1106 return SystemColorScheme();
1108 break; // Use the browser theme.
1111 switch (StaticPrefs::browser_theme_content_theme()) {
1113 return ColorScheme::Dark
;
1115 return ColorScheme::Light
;
1117 return ColorSchemeForChrome();
1122 ColorScheme
LookAndFeel::ColorSchemeForStyle(
1123 const dom::Document
& aDoc
, const StyleColorSchemeFlags
& aFlags
) {
1124 StyleColorSchemeFlags
style(aFlags
);
1126 style
.bits
= aDoc
.GetColorSchemeBits();
1128 const bool supportsDark
= bool(style
& StyleColorSchemeFlags::DARK
);
1129 const bool supportsLight
= bool(style
& StyleColorSchemeFlags::LIGHT
);
1130 if (supportsLight
&& supportsDark
) {
1131 // Both color-schemes are explicitly supported, use the preferred one.
1132 return aDoc
.PreferredColorScheme();
1134 if (supportsDark
|| supportsLight
) {
1135 // One color-scheme is explicitly supported and one isn't, so use the one
1136 // the content supports.
1137 return supportsDark
? ColorScheme::Dark
: ColorScheme::Light
;
1139 // No value specified. Chrome docs always supports both, so use the preferred
1141 if (nsContentUtils::IsChromeDoc(&aDoc
)) {
1142 return aDoc
.PreferredColorScheme();
1144 // As an special-case, use the system color-scheme if allow-gtk-dark-theme is
1147 // TODO(emilio): Once we ship the color-scheme property and meta tag I think
1148 // this can go. The use case for this is sidebars and such (bug 1721359),
1149 // which will be able to just use <meta name=color-scheme value="light dark">
1150 // to state that they support light and dark color schemes (taking the
1151 // PreferredColorScheme codepath above).
1152 #ifdef MOZ_WIDGET_GTK
1153 if (StaticPrefs::widget_content_allow_gtk_dark_theme()) {
1154 return SystemColorScheme();
1157 // Default content to light.
1158 return ColorScheme::Light
;
1161 LookAndFeel::ColorScheme
LookAndFeel::ColorSchemeForFrame(
1162 const nsIFrame
* aFrame
) {
1163 return ColorSchemeForStyle(*aFrame
->PresContext()->Document(),
1164 aFrame
->StyleUI()->mColorScheme
.bits
);
1168 Maybe
<nscolor
> LookAndFeel::GetColor(ColorID aId
, ColorScheme aScheme
,
1169 UseStandins aUseStandins
) {
1171 nsresult rv
= nsLookAndFeel::GetInstance()->GetColorValue(
1172 aId
, aScheme
, aUseStandins
, result
);
1173 if (NS_FAILED(rv
)) {
1176 return Some(result
);
1179 // Returns whether there is a CSS color name for this color.
1180 static bool ColorIsCSSAccessible(LookAndFeel::ColorID aId
) {
1181 using ColorID
= LookAndFeel::ColorID
;
1184 case ColorID::TextSelectDisabledBackground
:
1185 case ColorID::TextSelectAttentionBackground
:
1186 case ColorID::TextSelectAttentionForeground
:
1187 case ColorID::TextHighlightBackground
:
1188 case ColorID::TextHighlightForeground
:
1189 case ColorID::ThemedScrollbar
:
1190 case ColorID::ThemedScrollbarInactive
:
1191 case ColorID::ThemedScrollbarThumb
:
1192 case ColorID::ThemedScrollbarThumbActive
:
1193 case ColorID::ThemedScrollbarThumbInactive
:
1194 case ColorID::ThemedScrollbarThumbHover
:
1195 case ColorID::IMERawInputBackground
:
1196 case ColorID::IMERawInputForeground
:
1197 case ColorID::IMERawInputUnderline
:
1198 case ColorID::IMESelectedRawTextBackground
:
1199 case ColorID::IMESelectedRawTextForeground
:
1200 case ColorID::IMESelectedRawTextUnderline
:
1201 case ColorID::IMEConvertedTextBackground
:
1202 case ColorID::IMEConvertedTextForeground
:
1203 case ColorID::IMEConvertedTextUnderline
:
1204 case ColorID::IMESelectedConvertedTextBackground
:
1205 case ColorID::IMESelectedConvertedTextForeground
:
1206 case ColorID::IMESelectedConvertedTextUnderline
:
1207 case ColorID::SpellCheckerUnderline
:
1216 LookAndFeel::UseStandins
LookAndFeel::ShouldUseStandins(
1217 const dom::Document
& aDoc
, ColorID aId
) {
1218 if (ShouldUseStandinsForNativeColorForNonNativeTheme(aDoc
, aId
)) {
1219 return UseStandins::Yes
;
1221 if (nsContentUtils::UseStandinsForNativeColors() &&
1222 ColorIsCSSAccessible(aId
) && !nsContentUtils::IsChromeDoc(&aDoc
)) {
1223 return UseStandins::Yes
;
1225 if (aDoc
.IsStaticDocument() &&
1226 !PreferenceSheet::ContentPrefs().mUseDocumentColors
) {
1227 return UseStandins::Yes
;
1229 return UseStandins::No
;
1232 Maybe
<nscolor
> LookAndFeel::GetColor(ColorID aId
, const nsIFrame
* aFrame
) {
1233 const auto* doc
= aFrame
->PresContext()->Document();
1234 return GetColor(aId
, ColorSchemeForFrame(aFrame
),
1235 ShouldUseStandins(*doc
, aId
));
1239 nsresult
LookAndFeel::GetInt(IntID aID
, int32_t* aResult
) {
1240 return nsLookAndFeel::GetInstance()->GetIntValue(aID
, *aResult
);
1244 nsresult
LookAndFeel::GetFloat(FloatID aID
, float* aResult
) {
1245 return nsLookAndFeel::GetInstance()->GetFloatValue(aID
, *aResult
);
1249 bool LookAndFeel::GetFont(FontID aID
, nsString
& aName
, gfxFontStyle
& aStyle
) {
1250 return nsLookAndFeel::GetInstance()->GetFontValue(aID
, aName
, aStyle
);
1254 char16_t
LookAndFeel::GetPasswordCharacter() {
1255 return nsLookAndFeel::GetInstance()->GetPasswordCharacterImpl();
1259 bool LookAndFeel::GetEchoPassword() {
1260 if (StaticPrefs::editor_password_mask_delay() >= 0) {
1261 return StaticPrefs::editor_password_mask_delay() > 0;
1263 return nsLookAndFeel::GetInstance()->GetEchoPasswordImpl();
1267 uint32_t LookAndFeel::GetPasswordMaskDelay() {
1268 int32_t delay
= StaticPrefs::editor_password_mask_delay();
1270 return nsLookAndFeel::GetInstance()->GetPasswordMaskDelayImpl();
1275 bool LookAndFeel::DrawInTitlebar() {
1276 switch (StaticPrefs::browser_tabs_inTitlebar()) {
1284 return nsLookAndFeel::GetInstance()->GetDefaultDrawInTitlebar();
1287 void LookAndFeel::GetThemeInfo(nsACString
& aOut
) {
1288 nsLookAndFeel::GetInstance()->GetThemeInfo(aOut
);
1292 void LookAndFeel::Refresh() {
1293 nsLookAndFeel::GetInstance()->RefreshImpl();
1294 nsNativeBasicTheme::LookAndFeelChanged();
1298 void LookAndFeel::NativeInit() { nsLookAndFeel::GetInstance()->NativeInit(); }
1301 void LookAndFeel::SetData(widget::FullLookAndFeel
&& aTables
) {
1302 nsLookAndFeel::GetInstance()->SetDataImpl(std::move(aTables
));
1305 } // namespace mozilla