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",
144 "ui.alertNotificationOrigin",
146 "ui.IMERawInputUnderlineStyle",
147 "ui.IMESelectedRawTextUnderlineStyle",
148 "ui.IMEConvertedTextUnderlineStyle",
149 "ui.IMESelectedConvertedTextUnderlineStyle",
150 "ui.SpellCheckerUnderlineStyle",
152 "ui.windowsThemeIdentifier",
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.GtkCSDHideTitlebarByDefault",
164 "ui.GtkCSDTransparentBackground",
165 "ui.GtkCSDMinimizeButton",
166 "ui.GtkCSDMaximizeButton",
167 "ui.GtkCSDCloseButton",
168 "ui.GtkCSDMinimizeButtonPosition",
169 "ui.GtkCSDMaximizeButtonPosition",
170 "ui.GtkCSDCloseButtonPosition",
171 "ui.GtkCSDReversedPlacement",
172 "ui.systemUsesDarkTheme",
173 "ui.prefersReducedMotion",
174 "ui.primaryPointerCapabilities",
175 "ui.allPointerCapabilities",
176 "ui.systemVerticalScrollbarWidth",
177 "ui.systemHorizontalScrollbarHeight",
178 "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
186 static const char sFloatPrefs
[][37] = {
187 "ui.IMEUnderlineRelativeSize",
188 "ui.SpellCheckerUnderlineRelativeSize",
189 "ui.caretAspectRatio",
190 "ui.textScaleFactor",
193 static_assert(ArrayLength(sFloatPrefs
) == size_t(LookAndFeel::FloatID::End
),
194 "Should have a pref for each float value");
196 // This array MUST be kept in the same order as the color list in
197 // specified/color.rs
198 static const char sColorPrefs
[][41] = {
199 "ui.windowBackground",
200 "ui.windowForeground",
201 "ui.widgetBackground",
202 "ui.widgetForeground",
203 "ui.widgetSelectBackground",
204 "ui.widgetSelectForeground",
205 "ui.widget3DHighlight",
209 "ui.textSelectBackground",
210 "ui.textSelectForeground",
211 "ui.textSelectBackgroundDisabled",
212 "ui.textSelectBackgroundAttention",
213 "ui.textHighlightBackground",
214 "ui.textHighlightForeground",
215 "ui.IMERawInputBackground",
216 "ui.IMERawInputForeground",
217 "ui.IMERawInputUnderline",
218 "ui.IMESelectedRawTextBackground",
219 "ui.IMESelectedRawTextForeground",
220 "ui.IMESelectedRawTextUnderline",
221 "ui.IMEConvertedTextBackground",
222 "ui.IMEConvertedTextForeground",
223 "ui.IMEConvertedTextUnderline",
224 "ui.IMESelectedConvertedTextBackground",
225 "ui.IMESelectedConvertedTextForeground",
226 "ui.IMESelectedConvertedTextUnderline",
227 "ui.SpellCheckerUnderline",
228 "ui.themedScrollbar",
229 "ui.themedScrollbarInactive",
230 "ui.themedScrollbarThumb",
231 "ui.themedScrollbarThumbHover",
232 "ui.themedScrollbarThumbActive",
233 "ui.themedScrollbarThumbInactive",
239 "ui.buttonhighlight",
249 "ui.inactivecaption",
250 "ui.inactivecaptiontext",
256 "ui.threeddarkshadow",
258 "ui.threedhighlight",
259 "ui.threedlightshadow",
264 "ui.-moz-buttondefault",
265 "ui.-moz-default-color",
266 "ui.-moz-default-background-color",
268 "ui.-moz-dialogtext",
269 "ui.-moz-dragtargetzone",
270 "ui.-moz-cellhighlight",
271 "ui.-moz_cellhighlighttext",
272 "ui.-moz-html-cellhighlight",
273 "ui.-moz-html-cellhighlighttext",
274 "ui.-moz-buttonhoverface",
275 "ui.-moz_buttonhovertext",
277 "ui.-moz_menuhovertext",
278 "ui.-moz_menubartext",
279 "ui.-moz_menubarhovertext",
280 "ui.-moz_eventreerow",
281 "ui.-moz_oddtreerow",
282 "ui.-moz-gtk-buttonactivetext",
283 "ui.-moz-mac-buttonactivetext",
284 "ui.-moz_mac_chrome_active",
285 "ui.-moz_mac_chrome_inactive",
286 "ui.-moz-mac-defaultbuttontext",
287 "ui.-moz-mac-focusring",
288 "ui.-moz-mac-menuselect",
289 "ui.-moz-mac-menushadow",
290 "ui.-moz-mac-menutextdisable",
291 "ui.-moz-mac-menutextselect",
292 "ui.-moz_mac_disabledtoolbartext",
293 "ui.-moz-mac-secondaryhighlight",
294 "ui.-moz-mac-vibrant-titlebar-light",
295 "ui.-moz-mac-vibrant-titlebar-dark",
296 "ui.-moz-mac-menupopup",
297 "ui.-moz-mac-menuitem",
298 "ui.-moz-mac-active-menuitem",
299 "ui.-moz-mac-source-list",
300 "ui.-moz-mac-source-list-selection",
301 "ui.-moz-mac-active-source-list-selection",
302 "ui.-moz-mac-tooltip",
303 "ui.-moz-accent-color",
304 "ui.-moz-accent-color-foreground",
305 "ui.-moz-win-mediatext",
306 "ui.-moz-win-communicationstext",
307 "ui.-moz-nativehyperlinktext",
308 "ui.-moz-nativevisitedhyperlinktext",
309 "ui.-moz-hyperlinktext",
310 "ui.-moz-activehyperlinktext",
311 "ui.-moz-visitedhyperlinktext",
312 "ui.-moz-comboboxtext",
314 "ui.-moz-colheadertext",
315 "ui.-moz-colheaderhovertext",
316 "ui.-moz-gtk-titlebar-text",
317 "ui.-moz-gtk-titlebar-inactive-text",
320 static_assert(ArrayLength(sColorPrefs
) == size_t(LookAndFeel::ColorID::End
),
321 "Should have a pref for each color value");
323 const char* nsXPLookAndFeel::GetColorPrefName(ColorID aId
) {
324 return sColorPrefs
[size_t(aId
)];
327 bool nsXPLookAndFeel::sInitialized
= false;
329 nsXPLookAndFeel
* nsXPLookAndFeel::sInstance
= nullptr;
330 bool nsXPLookAndFeel::sShutdown
= false;
333 nsXPLookAndFeel
* nsXPLookAndFeel::GetInstance() {
338 NS_ENSURE_TRUE(!sShutdown
, nullptr);
340 // If we're in a content process, then the parent process will have supplied
341 // us with an initial FullLookAndFeel object.
342 // We grab this data from the ContentChild,
343 // where it's been temporarily stashed, and initialize our new LookAndFeel
346 FullLookAndFeel
* lnf
= nullptr;
348 if (auto* cc
= mozilla::dom::ContentChild::GetSingleton()) {
349 lnf
= &cc
->BorrowLookAndFeelData();
353 sInstance
= new widget::RemoteLookAndFeel(std::move(*lnf
));
354 } else if (gfxPlatform::IsHeadless()) {
355 sInstance
= new widget::HeadlessLookAndFeel();
357 sInstance
= new nsLookAndFeel();
360 // This is only ever used once during initialization, and can be cleared now.
365 nsNativeBasicTheme::Init();
370 void nsXPLookAndFeel::Shutdown() {
379 // This keeps strings alive, so need to clear to make leak checking happy.
382 nsNativeBasicTheme::Shutdown();
385 static void IntPrefChanged() {
386 // Int prefs can't change our system colors or fonts.
387 LookAndFeel::NotifyChangedAllWindows(
388 widget::ThemeChangeKind::MediaQueriesOnly
);
391 static void FloatPrefChanged() {
392 // Float prefs can't change our system colors or fonts.
393 LookAndFeel::NotifyChangedAllWindows(
394 widget::ThemeChangeKind::MediaQueriesOnly
);
397 static void ColorPrefChanged() {
398 // Color prefs affect style, because they by definition change system colors.
399 LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind::Style
);
403 void nsXPLookAndFeel::OnPrefChanged(const char* aPref
, void* aClosure
) {
404 nsDependentCString
prefName(aPref
);
405 for (const char* pref
: sIntPrefs
) {
406 if (prefName
.Equals(pref
)) {
412 for (const char* pref
: sFloatPrefs
) {
413 if (prefName
.Equals(pref
)) {
419 for (const char* pref
: sColorPrefs
) {
420 if (prefName
.Equals(pref
)) {
427 static constexpr struct {
428 nsLiteralCString mName
;
429 widget::ThemeChangeKind mChangeKind
=
430 widget::ThemeChangeKind::MediaQueriesOnly
;
431 } kMediaQueryPrefs
[] = {
432 {"browser.display.windows.native_menus"_ns
},
433 {"browser.proton.enabled"_ns
},
434 {"browser.proton.places-tooltip.enabled"_ns
},
435 // This affects not only the media query, but also the native theme, so we
436 // need to re-layout.
437 {"browser.theme.toolbar-theme"_ns
, widget::ThemeChangeKind::AllBits
},
440 // Read values from the user's preferences.
441 // This is done once at startup, but since the user's preferences
442 // haven't actually been read yet at that time, we also have to
443 // set a callback to inform us of changes to each pref.
444 void nsXPLookAndFeel::Init() {
445 MOZ_RELEASE_ASSERT(NS_IsMainThread());
447 // Say we're already initialized, and take the chance that it might fail;
448 // protects against some other process writing to our static variables.
451 // XXX If we could reorganize the pref names, we should separate the branch
452 // for each types. Then, we could reduce the unnecessary loop from
453 // nsXPLookAndFeel::OnPrefChanged().
454 Preferences::RegisterPrefixCallback(OnPrefChanged
, "ui.");
455 // We really do just want the accessibility.tabfocus pref, not other prefs
456 // that start with that string.
457 Preferences::RegisterCallback(OnPrefChanged
, "accessibility.tabfocus");
459 for (auto& pref
: kMediaQueryPrefs
) {
460 Preferences::RegisterCallback(
461 [](const char*, void* aChangeKind
) {
463 widget::ThemeChangeKind(reinterpret_cast<uintptr_t>(aChangeKind
));
464 LookAndFeel::NotifyChangedAllWindows(changeKind
);
466 pref
.mName
, reinterpret_cast<void*>(uintptr_t(pref
.mChangeKind
)));
470 nsXPLookAndFeel::~nsXPLookAndFeel() {
471 NS_ASSERTION(sInstance
== this,
472 "This destroying instance isn't the singleton instance");
476 static bool IsSpecialColor(LookAndFeel::ColorID aID
, nscolor aColor
) {
477 using ColorID
= LookAndFeel::ColorID
;
480 case ColorID::TextSelectForeground
:
481 case ColorID::IMESelectedRawTextBackground
:
482 case ColorID::IMESelectedConvertedTextBackground
:
483 case ColorID::IMERawInputBackground
:
484 case ColorID::IMEConvertedTextBackground
:
485 case ColorID::IMESelectedRawTextForeground
:
486 case ColorID::IMESelectedConvertedTextForeground
:
487 case ColorID::IMERawInputForeground
:
488 case ColorID::IMEConvertedTextForeground
:
489 case ColorID::IMERawInputUnderline
:
490 case ColorID::IMEConvertedTextUnderline
:
491 case ColorID::IMESelectedRawTextUnderline
:
492 case ColorID::IMESelectedConvertedTextUnderline
:
493 case ColorID::SpellCheckerUnderline
:
494 return NS_IS_SELECTION_SPECIAL_COLOR(aColor
);
499 * In GetColor(), every color that is not a special color is color
500 * corrected. Use false to make other colors color corrected.
505 nscolor
nsXPLookAndFeel::GetStandinForNativeColor(ColorID aID
) {
506 // The stand-in colors are taken from the Windows 7 Aero theme
507 // except Mac-specific colors which are taken from Mac OS 10.7.
509 #define COLOR(name_, r, g, b) \
510 case ColorID::name_: \
511 return NS_RGB(r, g, b);
515 COLOR(Activeborder
, 0xB4, 0xB4, 0xB4)
516 COLOR(Activecaption
, 0x99, 0xB4, 0xD1)
517 COLOR(Appworkspace
, 0xAB, 0xAB, 0xAB)
518 COLOR(Background
, 0x00, 0x00, 0x00)
519 COLOR(Buttonface
, 0xF0, 0xF0, 0xF0)
520 COLOR(Buttonhighlight
, 0xFF, 0xFF, 0xFF)
521 COLOR(Buttonshadow
, 0xA0, 0xA0, 0xA0)
522 COLOR(Buttontext
, 0x00, 0x00, 0x00)
523 COLOR(Captiontext
, 0x00, 0x00, 0x00)
524 COLOR(Graytext
, 0x6D, 0x6D, 0x6D)
525 COLOR(Highlight
, 0x33, 0x99, 0xFF)
526 COLOR(Highlighttext
, 0xFF, 0xFF, 0xFF)
527 COLOR(Inactiveborder
, 0xF4, 0xF7, 0xFC)
528 COLOR(Inactivecaption
, 0xBF, 0xCD, 0xDB)
529 COLOR(Inactivecaptiontext
, 0x43, 0x4E, 0x54)
530 COLOR(Infobackground
, 0xFF, 0xFF, 0xE1)
531 COLOR(Infotext
, 0x00, 0x00, 0x00)
532 COLOR(Menu
, 0xF0, 0xF0, 0xF0)
533 COLOR(Menutext
, 0x00, 0x00, 0x00)
534 COLOR(Scrollbar
, 0xC8, 0xC8, 0xC8)
535 COLOR(Threeddarkshadow
, 0x69, 0x69, 0x69)
536 COLOR(Threedface
, 0xF0, 0xF0, 0xF0)
537 COLOR(Threedhighlight
, 0xFF, 0xFF, 0xFF)
538 COLOR(Threedlightshadow
, 0xE3, 0xE3, 0xE3)
539 COLOR(Threedshadow
, 0xA0, 0xA0, 0xA0)
540 COLOR(Window
, 0xFF, 0xFF, 0xFF)
541 COLOR(Windowframe
, 0x64, 0x64, 0x64)
542 COLOR(Windowtext
, 0x00, 0x00, 0x00)
543 COLOR(MozButtondefault
, 0x69, 0x69, 0x69)
544 COLOR(Field
, 0xFF, 0xFF, 0xFF)
545 COLOR(Fieldtext
, 0x00, 0x00, 0x00)
546 COLOR(MozDialog
, 0xF0, 0xF0, 0xF0)
547 COLOR(MozDialogtext
, 0x00, 0x00, 0x00)
548 COLOR(MozColheadertext
, 0x00, 0x00, 0x00)
549 COLOR(MozColheaderhovertext
, 0x00, 0x00, 0x00)
550 COLOR(MozDragtargetzone
, 0xFF, 0xFF, 0xFF)
551 COLOR(MozCellhighlight
, 0xF0, 0xF0, 0xF0)
552 COLOR(MozCellhighlighttext
, 0x00, 0x00, 0x00)
553 COLOR(MozHtmlCellhighlight
, 0x33, 0x99, 0xFF)
554 COLOR(MozHtmlCellhighlighttext
, 0xFF, 0xFF, 0xFF)
555 COLOR(MozButtonhoverface
, 0xF0, 0xF0, 0xF0)
556 COLOR(MozGtkButtonactivetext
, 0x00, 0x00, 0x00)
557 COLOR(MozButtonhovertext
, 0x00, 0x00, 0x00)
558 COLOR(MozMenuhover
, 0x33, 0x99, 0xFF)
559 COLOR(MozMenuhovertext
, 0x00, 0x00, 0x00)
560 COLOR(MozMenubartext
, 0x00, 0x00, 0x00)
561 COLOR(MozMenubarhovertext
, 0x00, 0x00, 0x00)
562 COLOR(MozOddtreerow
, 0xFF, 0xFF, 0xFF)
563 COLOR(MozMacChromeActive
, 0xB2, 0xB2, 0xB2)
564 COLOR(MozMacChromeInactive
, 0xE1, 0xE1, 0xE1)
565 COLOR(MozMacFocusring
, 0x60, 0x9D, 0xD7)
566 COLOR(MozMacMenuselect
, 0x38, 0x75, 0xD7)
567 COLOR(MozMacMenushadow
, 0xA3, 0xA3, 0xA3)
568 COLOR(MozMacMenutextdisable
, 0x88, 0x88, 0x88)
569 COLOR(MozMacMenutextselect
, 0xFF, 0xFF, 0xFF)
570 COLOR(MozMacDisabledtoolbartext
, 0x3F, 0x3F, 0x3F)
571 COLOR(MozMacSecondaryhighlight
, 0xD4, 0xD4, 0xD4)
572 COLOR(MozMacVibrantTitlebarLight
, 0xf7, 0xf7, 0xf7)
573 COLOR(MozMacVibrantTitlebarDark
, 0x28, 0x28, 0x28)
574 COLOR(MozMacMenupopup
, 0xe6, 0xe6, 0xe6)
575 COLOR(MozMacMenuitem
, 0xe6, 0xe6, 0xe6)
576 COLOR(MozMacActiveMenuitem
, 0x0a, 0x64, 0xdc)
577 COLOR(MozMacSourceList
, 0xf7, 0xf7, 0xf7)
578 COLOR(MozMacSourceListSelection
, 0xc8, 0xc8, 0xc8)
579 COLOR(MozMacActiveSourceListSelection
, 0x0a, 0x64, 0xdc)
580 COLOR(MozMacTooltip
, 0xf7, 0xf7, 0xf7)
581 // Seems to be the default color (hardcoded because of bug 1065998)
582 COLOR(MozWinMediatext
, 0xFF, 0xFF, 0xFF)
583 COLOR(MozWinCommunicationstext
, 0xFF, 0xFF, 0xFF)
584 COLOR(MozNativehyperlinktext
, 0x00, 0x66, 0xCC)
585 COLOR(MozNativevisitedhyperlinktext
, 0x55, 0x1A, 0x8B)
586 COLOR(MozComboboxtext
, 0x00, 0x00, 0x00)
587 COLOR(MozCombobox
, 0xFF, 0xFF, 0xFF)
591 return NS_RGB(0xFF, 0xFF, 0xFF);
594 // Uncomment the #define below if you want to debug system color use in a skin
595 // that uses them. When set, it will make all system color pairs that are
596 // appropriate for foreground/background pairing the same. This means if the
597 // skin is using system colors correctly you will not be able to see *any* text.
599 // #define DEBUG_SYSTEM_COLOR_USE
601 #ifdef DEBUG_SYSTEM_COLOR_USE
602 static nsresult
SystemColorUseDebuggingColor(LookAndFeel::ColorID aID
,
604 using ColorID
= LookAndFeel::ColorID
;
607 // css2 http://www.w3.org/TR/REC-CSS2/ui.html#system-colors
608 case ColorID::Activecaption
:
609 // active window caption background
610 case ColorID::Captiontext
:
611 // text in active window caption
612 aResult
= NS_RGB(0xff, 0x00, 0x00);
615 case ColorID::Highlight
:
616 // background of selected item
617 case ColorID::Highlighttext
:
618 // text of selected item
619 aResult
= NS_RGB(0xff, 0xff, 0x00);
622 case ColorID::Inactivecaption
:
623 // inactive window caption
624 case ColorID::Inactivecaptiontext
:
625 // text in inactive window caption
626 aResult
= NS_RGB(0x66, 0x66, 0x00);
629 case ColorID::Infobackground
:
630 // tooltip background color
631 case ColorID::Infotext
:
632 // tooltip text color
633 aResult
= NS_RGB(0x00, 0xff, 0x00);
638 case ColorID::Menutext
:
640 aResult
= NS_RGB(0x00, 0xff, 0xff);
643 case ColorID::Threedface
:
644 case ColorID::Buttonface
:
646 case ColorID::Buttontext
:
647 // text on push buttons
648 aResult
= NS_RGB(0x00, 0x66, 0x66);
651 case ColorID::Window
:
652 case ColorID::Windowtext
:
653 aResult
= NS_RGB(0x00, 0x00, 0xff);
656 // from the CSS3 working draft (not yet finalized)
657 // http://www.w3.org/tr/2000/wd-css3-userint-20000216.html#color
660 case ColorID::Fieldtext
:
661 aResult
= NS_RGB(0xff, 0x00, 0xff);
664 case ColorID::MozDialog
:
665 case ColorID::MozDialogtext
:
666 aResult
= NS_RGB(0x66, 0x00, 0x66);
670 return NS_ERROR_NOT_AVAILABLE
;
677 static nsresult
GetColorFromPref(LookAndFeel::ColorID aID
, nscolor
& aResult
) {
678 const char* prefName
= sColorPrefs
[size_t(aID
)];
679 nsAutoCString colorStr
;
680 MOZ_TRY(Preferences::GetCString(prefName
, colorStr
));
681 if (!ServoCSSParser::ComputeColor(nullptr, NS_RGB(0, 0, 0), colorStr
,
683 return NS_ERROR_FAILURE
;
688 // All these routines will return NS_OK if they have a value,
689 // in which case the nsLookAndFeel should use that value;
690 // otherwise we'll return NS_ERROR_NOT_AVAILABLE, in which case, the
691 // platform-specific nsLookAndFeel should use its own values instead.
692 nsresult
nsXPLookAndFeel::GetColorValue(ColorID aID
, ColorScheme aScheme
,
693 UseStandins aUseStandins
,
699 #ifdef DEBUG_SYSTEM_COLOR_USE
700 if (NS_SUCCEEDED(SystemColorUseDebuggingColor(aID
, aResult
))) {
705 if (aUseStandins
== UseStandins::Yes
) {
706 aResult
= GetStandinForNativeColor(aID
);
711 aScheme
== ColorScheme::Light
? sLightColorCache
: sDarkColorCache
;
712 if (const auto* cached
= cache
.Get(aID
)) {
713 if (cached
->isNothing()) {
714 return NS_ERROR_FAILURE
;
716 aResult
= cached
->value();
720 if (NS_SUCCEEDED(GetColorFromPref(aID
, aResult
))) {
721 cache
.Insert(aID
, Some(aResult
));
725 if (NS_SUCCEEDED(NativeGetColor(aID
, aScheme
, aResult
))) {
726 if (gfxPlatform::GetCMSMode() == CMSMode::All
&&
727 !IsSpecialColor(aID
, aResult
)) {
728 qcms_transform
* transform
= gfxPlatform::GetCMSInverseRGBTransform();
731 color
[0] = NS_GET_R(aResult
);
732 color
[1] = NS_GET_G(aResult
);
733 color
[2] = NS_GET_B(aResult
);
734 color
[3] = NS_GET_A(aResult
);
735 qcms_transform_data(transform
, color
, color
, 1);
736 aResult
= NS_RGBA(color
[0], color
[1], color
[2], color
[3]);
740 // NOTE: Servo holds a lock and the main thread is paused, so writing to the
741 // global cache here is fine.
742 cache
.Insert(aID
, Some(aResult
));
746 cache
.Insert(aID
, Nothing());
747 return NS_ERROR_FAILURE
;
750 nsresult
nsXPLookAndFeel::GetIntValue(IntID aID
, int32_t& aResult
) {
755 if (const auto* cached
= sIntCache
.Get(aID
)) {
756 if (cached
->isNothing()) {
757 return NS_ERROR_FAILURE
;
759 aResult
= cached
->value();
763 if (NS_SUCCEEDED(Preferences::GetInt(sIntPrefs
[size_t(aID
)], &aResult
))) {
764 sIntCache
.Insert(aID
, Some(aResult
));
768 if (NS_FAILED(NativeGetInt(aID
, aResult
))) {
769 sIntCache
.Insert(aID
, Nothing());
770 return NS_ERROR_FAILURE
;
773 sIntCache
.Insert(aID
, Some(aResult
));
777 nsresult
nsXPLookAndFeel::GetFloatValue(FloatID aID
, float& aResult
) {
782 if (const auto* cached
= sFloatCache
.Get(aID
)) {
783 if (cached
->isNothing()) {
784 return NS_ERROR_FAILURE
;
786 aResult
= cached
->value();
791 if (NS_SUCCEEDED(Preferences::GetInt(sFloatPrefs
[size_t(aID
)], &pref
))) {
792 aResult
= float(pref
) / 100.0f
;
793 sFloatCache
.Insert(aID
, Some(aResult
));
797 if (NS_FAILED(NativeGetFloat(aID
, aResult
))) {
798 sFloatCache
.Insert(aID
, Nothing());
799 return NS_ERROR_FAILURE
;
802 sFloatCache
.Insert(aID
, Some(aResult
));
806 bool nsXPLookAndFeel::LookAndFeelFontToStyle(const LookAndFeelFont
& aFont
,
808 gfxFontStyle
& aStyle
) {
809 if (!aFont
.haveFont()) {
812 aName
= aFont
.name();
813 aStyle
= gfxFontStyle();
814 aStyle
.size
= aFont
.size();
815 aStyle
.weight
= FontWeight(aFont
.weight());
817 aFont
.italic() ? FontSlantStyle::Italic() : FontSlantStyle::Normal();
818 aStyle
.systemFont
= true;
822 widget::LookAndFeelFont
nsXPLookAndFeel::StyleToLookAndFeelFont(
823 const nsAString
& aName
, const gfxFontStyle
& aStyle
) {
824 LookAndFeelFont font
;
825 font
.haveFont() = true;
827 font
.size() = aStyle
.size
;
828 font
.weight() = aStyle
.weight
.ToFloat();
829 font
.italic() = aStyle
.style
.IsItalic();
830 MOZ_ASSERT(aStyle
.style
.IsNormal() || aStyle
.style
.IsItalic(),
831 "Cannot handle oblique font style");
834 // Assert that all the remaining font style properties have their
836 gfxFontStyle candidate
= aStyle
;
837 gfxFontStyle defaults
{};
838 candidate
.size
= defaults
.size
;
839 candidate
.weight
= defaults
.weight
;
840 candidate
.style
= defaults
.style
;
841 MOZ_ASSERT(candidate
.Equals(defaults
),
842 "Some font style properties not supported");
848 bool nsXPLookAndFeel::GetFontValue(FontID aID
, nsString
& aName
,
849 gfxFontStyle
& aStyle
) {
850 if (const LookAndFeelFont
* cached
= sFontCache
.Get(aID
)) {
851 return LookAndFeelFontToStyle(*cached
, aName
, aStyle
);
853 LookAndFeelFont font
;
854 const bool haveFont
= NativeGetFont(aID
, aName
, aStyle
);
855 font
.haveFont() = haveFont
;
857 font
= StyleToLookAndFeelFont(aName
, aStyle
);
859 sFontCache
.Insert(aID
, std::move(font
));
863 void nsXPLookAndFeel::RefreshImpl() {
864 // Wipe out our caches.
865 sLightColorCache
.Clear();
866 sDarkColorCache
.Clear();
871 // Clear any cached FullLookAndFeel data, which is now invalid.
872 if (XRE_IsParentProcess()) {
873 widget::RemoteLookAndFeel::ClearCachedData();
877 static bool sRecordedLookAndFeelTelemetry
= false;
879 void nsXPLookAndFeel::RecordTelemetry() {
880 if (!XRE_IsParentProcess()) {
884 if (sRecordedLookAndFeelTelemetry
) {
888 sRecordedLookAndFeelTelemetry
= true;
891 Telemetry::ScalarSet(
892 Telemetry::ScalarID::WIDGET_DARK_MODE
,
893 NS_SUCCEEDED(GetIntValue(IntID::SystemUsesDarkTheme
, i
)) && i
!= 0);
895 RecordLookAndFeelSpecificTelemetry();
901 void LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind aKind
) {
902 if (nsCOMPtr
<nsIObserverService
> obs
= services::GetObserverService()) {
903 const char16_t kind
[] = {char16_t(aKind
), 0};
904 obs
->NotifyObservers(nullptr, "look-and-feel-changed", kind
);
908 static bool ShouldUseStandinsForNativeColorForNonNativeTheme(
909 const dom::Document
& aDoc
, LookAndFeel::ColorID aColor
) {
910 using ColorID
= LookAndFeel::ColorID
;
911 if (!aDoc
.ShouldAvoidNativeTheme()) {
915 // The native theme doesn't use system colors backgrounds etc, except when in
916 // high-contrast mode, so spoof some of the colors with stand-ins to prevent
919 case ColorID::Buttonface
:
920 case ColorID::Buttontext
:
921 case ColorID::MozButtonhoverface
:
922 case ColorID::MozButtonhovertext
:
923 case ColorID::MozGtkButtonactivetext
:
925 case ColorID::MozCombobox
:
926 case ColorID::MozComboboxtext
:
929 case ColorID::Fieldtext
:
931 case ColorID::Graytext
:
933 return !PreferenceSheet::PrefsFor(aDoc
)
934 .NonNativeThemeShouldUseSystemColors();
943 static bool ShouldRespectSystemColorSchemeForChromeDoc() {
945 // macOS follows the global toolbar theme, not the system theme.
946 // (If the global toolbar theme is set to System, then it *that* follows the
950 // GTK historically has behaved like this. Other platforms don't have support
951 // for light / dark color schemes yet so it doesn't matter for them.
956 static bool ShouldRespectGlobalToolbarThemeAppearanceForChromeDoc() {
958 // Need to be consistent with AppearanceOverride.mm on macOS, which respects
959 // the browser.theme.toolbar-theme pref.
960 // However, if widget.macos.support-dark-appearance is false, we need to
961 // pretend everything's Light and not follow the toolbar theme.
962 return StaticPrefs::widget_macos_support_dark_appearance();
963 #elif defined(MOZ_WIDGET_GTK)
964 return StaticPrefs::widget_gtk_follow_firefox_theme();
970 LookAndFeel::ColorScheme
LookAndFeel::ColorSchemeForChrome() {
971 if (ShouldRespectGlobalToolbarThemeAppearanceForChromeDoc()) {
972 switch (StaticPrefs::browser_theme_toolbar_theme()) {
974 return ColorScheme::Dark
;
976 return ColorScheme::Light
;
978 return SystemColorScheme();
983 if (ShouldRespectSystemColorSchemeForChromeDoc()) {
984 return SystemColorScheme();
986 return ColorScheme::Light
;
989 static LookAndFeel::ColorScheme
ColorSchemeForDocument(
990 const dom::Document
& aDoc
, bool aContentSupportsDark
) {
991 if (nsContentUtils::IsChromeDoc(&aDoc
)) {
992 return LookAndFeel::ColorSchemeForChrome();
994 #ifdef MOZ_WIDGET_GTK
995 if (StaticPrefs::widget_content_allow_gtk_dark_theme()) {
996 // If users manually tweak allow-gtk-dark-theme, allow content to use the
997 // system color scheme rather than forcing it to light.
998 return LookAndFeel::SystemColorScheme();
1001 return aContentSupportsDark
? LookAndFeel::SystemColorScheme()
1002 : LookAndFeel::ColorScheme::Light
;
1005 LookAndFeel::ColorScheme
LookAndFeel::ColorSchemeForStyle(
1006 const dom::Document
& aDoc
, const StyleColorSchemeFlags
& aFlags
) {
1007 StyleColorSchemeFlags
style(aFlags
);
1009 style
.bits
= aDoc
.GetColorSchemeBits();
1011 const bool supportsDark
= bool(style
& StyleColorSchemeFlags::DARK
);
1012 const bool supportsLight
= bool(style
& StyleColorSchemeFlags::LIGHT
);
1013 if (supportsDark
&& !supportsLight
) {
1014 return ColorScheme::Dark
;
1016 if (supportsLight
&& !supportsDark
) {
1017 return ColorScheme::Light
;
1019 return ColorSchemeForDocument(aDoc
, supportsDark
);
1022 LookAndFeel::ColorScheme
LookAndFeel::ColorSchemeForFrame(
1023 const nsIFrame
* aFrame
) {
1024 return ColorSchemeForStyle(*aFrame
->PresContext()->Document(),
1025 aFrame
->StyleUI()->mColorScheme
.bits
);
1029 Maybe
<nscolor
> LookAndFeel::GetColor(ColorID aId
, ColorScheme aScheme
,
1030 UseStandins aUseStandins
) {
1032 nsresult rv
= nsLookAndFeel::GetInstance()->GetColorValue(
1033 aId
, aScheme
, aUseStandins
, result
);
1034 if (NS_FAILED(rv
)) {
1037 return Some(result
);
1040 // Returns whether there is a CSS color name for this color.
1041 static bool ColorIsCSSAccessible(LookAndFeel::ColorID aId
) {
1042 using ColorID
= LookAndFeel::ColorID
;
1045 case ColorID::WindowBackground
:
1046 case ColorID::WindowForeground
:
1047 case ColorID::WidgetBackground
:
1048 case ColorID::WidgetForeground
:
1049 case ColorID::WidgetSelectBackground
:
1050 case ColorID::WidgetSelectForeground
:
1051 case ColorID::Widget3DHighlight
:
1052 case ColorID::Widget3DShadow
:
1053 case ColorID::TextBackground
:
1054 case ColorID::TextForeground
:
1055 case ColorID::TextSelectBackground
:
1056 case ColorID::TextSelectForeground
:
1057 case ColorID::TextSelectBackgroundDisabled
:
1058 case ColorID::TextSelectBackgroundAttention
:
1059 case ColorID::TextHighlightBackground
:
1060 case ColorID::TextHighlightForeground
:
1061 case ColorID::IMERawInputBackground
:
1062 case ColorID::IMERawInputForeground
:
1063 case ColorID::IMERawInputUnderline
:
1064 case ColorID::IMESelectedRawTextBackground
:
1065 case ColorID::IMESelectedRawTextForeground
:
1066 case ColorID::IMESelectedRawTextUnderline
:
1067 case ColorID::IMEConvertedTextBackground
:
1068 case ColorID::IMEConvertedTextForeground
:
1069 case ColorID::IMEConvertedTextUnderline
:
1070 case ColorID::IMESelectedConvertedTextBackground
:
1071 case ColorID::IMESelectedConvertedTextForeground
:
1072 case ColorID::IMESelectedConvertedTextUnderline
:
1073 case ColorID::SpellCheckerUnderline
:
1082 LookAndFeel::UseStandins
LookAndFeel::ShouldUseStandins(
1083 const dom::Document
& aDoc
, ColorID aId
) {
1084 if (ShouldUseStandinsForNativeColorForNonNativeTheme(aDoc
, aId
)) {
1085 return UseStandins::Yes
;
1087 if (nsContentUtils::UseStandinsForNativeColors() &&
1088 !nsContentUtils::IsChromeDoc(&aDoc
) && ColorIsCSSAccessible(aId
)) {
1089 return UseStandins::Yes
;
1091 if (aDoc
.IsStaticDocument() &&
1092 !PreferenceSheet::ContentPrefs().mUseDocumentColors
) {
1093 return UseStandins::Yes
;
1095 return UseStandins::No
;
1098 Maybe
<nscolor
> LookAndFeel::GetColor(ColorID aId
, const nsIFrame
* aFrame
) {
1099 const auto* doc
= aFrame
->PresContext()->Document();
1100 return GetColor(aId
, ColorSchemeForFrame(aFrame
),
1101 ShouldUseStandins(*doc
, aId
));
1105 nsresult
LookAndFeel::GetInt(IntID aID
, int32_t* aResult
) {
1106 return nsLookAndFeel::GetInstance()->GetIntValue(aID
, *aResult
);
1110 nsresult
LookAndFeel::GetFloat(FloatID aID
, float* aResult
) {
1111 return nsLookAndFeel::GetInstance()->GetFloatValue(aID
, *aResult
);
1115 bool LookAndFeel::GetFont(FontID aID
, nsString
& aName
, gfxFontStyle
& aStyle
) {
1116 return nsLookAndFeel::GetInstance()->GetFontValue(aID
, aName
, aStyle
);
1120 char16_t
LookAndFeel::GetPasswordCharacter() {
1121 return nsLookAndFeel::GetInstance()->GetPasswordCharacterImpl();
1125 bool LookAndFeel::GetEchoPassword() {
1126 if (StaticPrefs::editor_password_mask_delay() >= 0) {
1127 return StaticPrefs::editor_password_mask_delay() > 0;
1129 return nsLookAndFeel::GetInstance()->GetEchoPasswordImpl();
1133 uint32_t LookAndFeel::GetPasswordMaskDelay() {
1134 int32_t delay
= StaticPrefs::editor_password_mask_delay();
1136 return nsLookAndFeel::GetInstance()->GetPasswordMaskDelayImpl();
1141 void LookAndFeel::GetThemeInfo(nsACString
& aOut
) {
1142 nsLookAndFeel::GetInstance()->GetThemeInfo(aOut
);
1146 void LookAndFeel::Refresh() {
1147 nsLookAndFeel::GetInstance()->RefreshImpl();
1148 nsNativeBasicTheme::LookAndFeelChanged();
1152 void LookAndFeel::NativeInit() { nsLookAndFeel::GetInstance()->NativeInit(); }
1155 void LookAndFeel::SetData(widget::FullLookAndFeel
&& aTables
) {
1156 nsLookAndFeel::GetInstance()->SetDataImpl(std::move(aTables
));
1159 } // namespace mozilla