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
[][42] = {
114 "ui.caretVisibleWithSelection",
115 "ui.selectTextfieldsOnKeyFocus",
117 "ui.menusCanOverlapOSBar",
118 "ui.useOverlayScrollbars",
119 "ui.allowOverlayScrollbarsOverlap",
120 "ui.showHideScrollbars",
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",
180 static_assert(ArrayLength(sIntPrefs
) == size_t(LookAndFeel::IntID::End
),
181 "Should have a pref for each int value");
183 // This array MUST be kept in the same order as the float id list in
185 static const char sFloatPrefs
[][37] = {
186 "ui.IMEUnderlineRelativeSize",
187 "ui.SpellCheckerUnderlineRelativeSize",
188 "ui.caretAspectRatio",
189 "ui.textScaleFactor",
192 static_assert(ArrayLength(sFloatPrefs
) == size_t(LookAndFeel::FloatID::End
),
193 "Should have a pref for each float value");
195 // This array MUST be kept in the same order as the color list in
196 // specified/color.rs
197 static const char sColorPrefs
[][41] = {
198 "ui.windowBackground",
199 "ui.windowForeground",
200 "ui.widgetBackground",
201 "ui.widgetForeground",
202 "ui.widgetSelectBackground",
203 "ui.widgetSelectForeground",
204 "ui.widget3DHighlight",
208 "ui.textSelectBackground",
209 "ui.textSelectForeground",
210 "ui.textSelectBackgroundDisabled",
211 "ui.textSelectBackgroundAttention",
212 "ui.textHighlightBackground",
213 "ui.textHighlightForeground",
214 "ui.IMERawInputBackground",
215 "ui.IMERawInputForeground",
216 "ui.IMERawInputUnderline",
217 "ui.IMESelectedRawTextBackground",
218 "ui.IMESelectedRawTextForeground",
219 "ui.IMESelectedRawTextUnderline",
220 "ui.IMEConvertedTextBackground",
221 "ui.IMEConvertedTextForeground",
222 "ui.IMEConvertedTextUnderline",
223 "ui.IMESelectedConvertedTextBackground",
224 "ui.IMESelectedConvertedTextForeground",
225 "ui.IMESelectedConvertedTextUnderline",
226 "ui.SpellCheckerUnderline",
227 "ui.themedScrollbar",
228 "ui.themedScrollbarInactive",
229 "ui.themedScrollbarThumb",
230 "ui.themedScrollbarThumbHover",
231 "ui.themedScrollbarThumbActive",
232 "ui.themedScrollbarThumbInactive",
238 "ui.buttonhighlight",
248 "ui.inactivecaption",
249 "ui.inactivecaptiontext",
255 "ui.threeddarkshadow",
257 "ui.threedhighlight",
258 "ui.threedlightshadow",
263 "ui.-moz-buttondefault",
264 "ui.-moz-default-color",
265 "ui.-moz-default-background-color",
267 "ui.-moz-dialogtext",
268 "ui.-moz-dragtargetzone",
269 "ui.-moz-cellhighlight",
270 "ui.-moz_cellhighlighttext",
271 "ui.-moz-html-cellhighlight",
272 "ui.-moz-html-cellhighlighttext",
273 "ui.-moz-buttonhoverface",
274 "ui.-moz_buttonhovertext",
276 "ui.-moz_menuhovertext",
277 "ui.-moz_menubartext",
278 "ui.-moz_menubarhovertext",
279 "ui.-moz_eventreerow",
280 "ui.-moz_oddtreerow",
281 "ui.-moz-gtk-buttonactivetext",
282 "ui.-moz-mac-buttonactivetext",
283 "ui.-moz_mac_chrome_active",
284 "ui.-moz_mac_chrome_inactive",
285 "ui.-moz-mac-defaultbuttontext",
286 "ui.-moz-mac-focusring",
287 "ui.-moz-mac-menuselect",
288 "ui.-moz-mac-menushadow",
289 "ui.-moz-mac-menutextdisable",
290 "ui.-moz-mac-menutextselect",
291 "ui.-moz_mac_disabledtoolbartext",
292 "ui.-moz-mac-secondaryhighlight",
293 "ui.-moz-mac-vibrant-titlebar-light",
294 "ui.-moz-mac-vibrant-titlebar-dark",
295 "ui.-moz-mac-menupopup",
296 "ui.-moz-mac-menuitem",
297 "ui.-moz-mac-active-menuitem",
298 "ui.-moz-mac-source-list",
299 "ui.-moz-mac-source-list-selection",
300 "ui.-moz-mac-active-source-list-selection",
301 "ui.-moz-mac-tooltip",
302 "ui.-moz-accent-color",
303 "ui.-moz-accent-color-foreground",
304 "ui.-moz-win-mediatext",
305 "ui.-moz-win-communicationstext",
306 "ui.-moz-nativehyperlinktext",
307 "ui.-moz-hyperlinktext",
308 "ui.-moz-activehyperlinktext",
309 "ui.-moz-visitedhyperlinktext",
310 "ui.-moz-comboboxtext",
312 "ui.-moz-colheadertext",
313 "ui.-moz-colheaderhovertext",
314 "ui.-moz-gtk-titlebar-text",
315 "ui.-moz-gtk-titlebar-inactive-text",
318 static_assert(ArrayLength(sColorPrefs
) == size_t(LookAndFeel::ColorID::End
),
319 "Should have a pref for each color value");
321 const char* nsXPLookAndFeel::GetColorPrefName(ColorID aId
) {
322 return sColorPrefs
[size_t(aId
)];
325 bool nsXPLookAndFeel::sInitialized
= false;
327 nsXPLookAndFeel
* nsXPLookAndFeel::sInstance
= nullptr;
328 bool nsXPLookAndFeel::sShutdown
= false;
331 nsXPLookAndFeel
* nsXPLookAndFeel::GetInstance() {
336 NS_ENSURE_TRUE(!sShutdown
, nullptr);
338 // If we're in a content process, then the parent process will have supplied
339 // us with an initial FullLookAndFeel object.
340 // We grab this data from the ContentChild,
341 // where it's been temporarily stashed, and initialize our new LookAndFeel
344 FullLookAndFeel
* lnf
= nullptr;
346 if (auto* cc
= mozilla::dom::ContentChild::GetSingleton()) {
347 lnf
= &cc
->BorrowLookAndFeelData();
351 sInstance
= new widget::RemoteLookAndFeel(std::move(*lnf
));
352 } else if (gfxPlatform::IsHeadless()) {
353 sInstance
= new widget::HeadlessLookAndFeel();
355 sInstance
= new nsLookAndFeel();
358 // This is only ever used once during initialization, and can be cleared now.
363 nsNativeBasicTheme::Init();
368 void nsXPLookAndFeel::Shutdown() {
377 // This keeps strings alive, so need to clear to make leak checking happy.
380 nsNativeBasicTheme::Shutdown();
383 static void IntPrefChanged() {
384 // Int prefs can't change our system colors or fonts.
385 LookAndFeel::NotifyChangedAllWindows(
386 widget::ThemeChangeKind::MediaQueriesOnly
);
389 static void FloatPrefChanged() {
390 // Float prefs can't change our system colors or fonts.
391 LookAndFeel::NotifyChangedAllWindows(
392 widget::ThemeChangeKind::MediaQueriesOnly
);
395 static void ColorPrefChanged() {
396 // Color prefs affect style, because they by definition change system colors.
397 LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind::Style
);
401 void nsXPLookAndFeel::OnPrefChanged(const char* aPref
, void* aClosure
) {
402 nsDependentCString
prefName(aPref
);
403 for (const char* pref
: sIntPrefs
) {
404 if (prefName
.Equals(pref
)) {
410 for (const char* pref
: sFloatPrefs
) {
411 if (prefName
.Equals(pref
)) {
417 for (const char* pref
: sColorPrefs
) {
418 if (prefName
.Equals(pref
)) {
425 static constexpr nsLiteralCString kMediaQueryPrefs
[] = {
426 "browser.display.windows.native_menus"_ns
,
427 "browser.proton.enabled"_ns
,
428 "browser.proton.places-tooltip.enabled"_ns
,
429 "browser.theme.toolbar-theme"_ns
,
432 // Read values from the user's preferences.
433 // This is done once at startup, but since the user's preferences
434 // haven't actually been read yet at that time, we also have to
435 // set a callback to inform us of changes to each pref.
436 void nsXPLookAndFeel::Init() {
437 MOZ_RELEASE_ASSERT(NS_IsMainThread());
439 // Say we're already initialized, and take the chance that it might fail;
440 // protects against some other process writing to our static variables.
443 // XXX If we could reorganize the pref names, we should separate the branch
444 // for each types. Then, we could reduce the unnecessary loop from
445 // nsXPLookAndFeel::OnPrefChanged().
446 Preferences::RegisterPrefixCallback(OnPrefChanged
, "ui.");
447 // We really do just want the accessibility.tabfocus pref, not other prefs
448 // that start with that string.
449 Preferences::RegisterCallback(OnPrefChanged
, "accessibility.tabfocus");
451 for (auto& pref
: kMediaQueryPrefs
) {
452 Preferences::RegisterCallback(
453 [](const char*, void*) {
454 LookAndFeel::NotifyChangedAllWindows(
455 widget::ThemeChangeKind::MediaQueriesOnly
);
461 nsXPLookAndFeel::~nsXPLookAndFeel() {
462 NS_ASSERTION(sInstance
== this,
463 "This destroying instance isn't the singleton instance");
467 static bool IsSpecialColor(LookAndFeel::ColorID aID
, nscolor aColor
) {
468 using ColorID
= LookAndFeel::ColorID
;
471 case ColorID::TextSelectForeground
:
472 case ColorID::IMESelectedRawTextBackground
:
473 case ColorID::IMESelectedConvertedTextBackground
:
474 case ColorID::IMERawInputBackground
:
475 case ColorID::IMEConvertedTextBackground
:
476 case ColorID::IMESelectedRawTextForeground
:
477 case ColorID::IMESelectedConvertedTextForeground
:
478 case ColorID::IMERawInputForeground
:
479 case ColorID::IMEConvertedTextForeground
:
480 case ColorID::IMERawInputUnderline
:
481 case ColorID::IMEConvertedTextUnderline
:
482 case ColorID::IMESelectedRawTextUnderline
:
483 case ColorID::IMESelectedConvertedTextUnderline
:
484 case ColorID::SpellCheckerUnderline
:
485 return NS_IS_SELECTION_SPECIAL_COLOR(aColor
);
490 * In GetColor(), every color that is not a special color is color
491 * corrected. Use false to make other colors color corrected.
496 nscolor
nsXPLookAndFeel::GetStandinForNativeColor(ColorID aID
) {
497 // The stand-in colors are taken from the Windows 7 Aero theme
498 // except Mac-specific colors which are taken from Mac OS 10.7.
500 #define COLOR(name_, r, g, b) \
501 case ColorID::name_: \
502 return NS_RGB(r, g, b);
506 COLOR(Activeborder
, 0xB4, 0xB4, 0xB4)
507 COLOR(Activecaption
, 0x99, 0xB4, 0xD1)
508 COLOR(Appworkspace
, 0xAB, 0xAB, 0xAB)
509 COLOR(Background
, 0x00, 0x00, 0x00)
510 COLOR(Buttonface
, 0xF0, 0xF0, 0xF0)
511 COLOR(Buttonhighlight
, 0xFF, 0xFF, 0xFF)
512 COLOR(Buttonshadow
, 0xA0, 0xA0, 0xA0)
513 COLOR(Buttontext
, 0x00, 0x00, 0x00)
514 COLOR(Captiontext
, 0x00, 0x00, 0x00)
515 COLOR(Graytext
, 0x6D, 0x6D, 0x6D)
516 COLOR(Highlight
, 0x33, 0x99, 0xFF)
517 COLOR(Highlighttext
, 0xFF, 0xFF, 0xFF)
518 COLOR(Inactiveborder
, 0xF4, 0xF7, 0xFC)
519 COLOR(Inactivecaption
, 0xBF, 0xCD, 0xDB)
520 COLOR(Inactivecaptiontext
, 0x43, 0x4E, 0x54)
521 COLOR(Infobackground
, 0xFF, 0xFF, 0xE1)
522 COLOR(Infotext
, 0x00, 0x00, 0x00)
523 COLOR(Menu
, 0xF0, 0xF0, 0xF0)
524 COLOR(Menutext
, 0x00, 0x00, 0x00)
525 COLOR(Scrollbar
, 0xC8, 0xC8, 0xC8)
526 COLOR(Threeddarkshadow
, 0x69, 0x69, 0x69)
527 COLOR(Threedface
, 0xF0, 0xF0, 0xF0)
528 COLOR(Threedhighlight
, 0xFF, 0xFF, 0xFF)
529 COLOR(Threedlightshadow
, 0xE3, 0xE3, 0xE3)
530 COLOR(Threedshadow
, 0xA0, 0xA0, 0xA0)
531 COLOR(Window
, 0xFF, 0xFF, 0xFF)
532 COLOR(Windowframe
, 0x64, 0x64, 0x64)
533 COLOR(Windowtext
, 0x00, 0x00, 0x00)
534 COLOR(MozButtondefault
, 0x69, 0x69, 0x69)
535 COLOR(Field
, 0xFF, 0xFF, 0xFF)
536 COLOR(Fieldtext
, 0x00, 0x00, 0x00)
537 COLOR(MozDialog
, 0xF0, 0xF0, 0xF0)
538 COLOR(MozDialogtext
, 0x00, 0x00, 0x00)
539 COLOR(MozColheadertext
, 0x00, 0x00, 0x00)
540 COLOR(MozColheaderhovertext
, 0x00, 0x00, 0x00)
541 COLOR(MozDragtargetzone
, 0xFF, 0xFF, 0xFF)
542 COLOR(MozCellhighlight
, 0xF0, 0xF0, 0xF0)
543 COLOR(MozCellhighlighttext
, 0x00, 0x00, 0x00)
544 COLOR(MozHtmlCellhighlight
, 0x33, 0x99, 0xFF)
545 COLOR(MozHtmlCellhighlighttext
, 0xFF, 0xFF, 0xFF)
546 COLOR(MozButtonhoverface
, 0xF0, 0xF0, 0xF0)
547 COLOR(MozGtkButtonactivetext
, 0x00, 0x00, 0x00)
548 COLOR(MozButtonhovertext
, 0x00, 0x00, 0x00)
549 COLOR(MozMenuhover
, 0x33, 0x99, 0xFF)
550 COLOR(MozMenuhovertext
, 0x00, 0x00, 0x00)
551 COLOR(MozMenubartext
, 0x00, 0x00, 0x00)
552 COLOR(MozMenubarhovertext
, 0x00, 0x00, 0x00)
553 COLOR(MozOddtreerow
, 0xFF, 0xFF, 0xFF)
554 COLOR(MozMacChromeActive
, 0xB2, 0xB2, 0xB2)
555 COLOR(MozMacChromeInactive
, 0xE1, 0xE1, 0xE1)
556 COLOR(MozMacFocusring
, 0x60, 0x9D, 0xD7)
557 COLOR(MozMacMenuselect
, 0x38, 0x75, 0xD7)
558 COLOR(MozMacMenushadow
, 0xA3, 0xA3, 0xA3)
559 COLOR(MozMacMenutextdisable
, 0x88, 0x88, 0x88)
560 COLOR(MozMacMenutextselect
, 0xFF, 0xFF, 0xFF)
561 COLOR(MozMacDisabledtoolbartext
, 0x3F, 0x3F, 0x3F)
562 COLOR(MozMacSecondaryhighlight
, 0xD4, 0xD4, 0xD4)
563 COLOR(MozMacVibrantTitlebarLight
, 0xf7, 0xf7, 0xf7)
564 COLOR(MozMacVibrantTitlebarDark
, 0x28, 0x28, 0x28)
565 COLOR(MozMacMenupopup
, 0xe6, 0xe6, 0xe6)
566 COLOR(MozMacMenuitem
, 0xe6, 0xe6, 0xe6)
567 COLOR(MozMacActiveMenuitem
, 0x0a, 0x64, 0xdc)
568 COLOR(MozMacSourceList
, 0xf7, 0xf7, 0xf7)
569 COLOR(MozMacSourceListSelection
, 0xc8, 0xc8, 0xc8)
570 COLOR(MozMacActiveSourceListSelection
, 0x0a, 0x64, 0xdc)
571 COLOR(MozMacTooltip
, 0xf7, 0xf7, 0xf7)
572 // Seems to be the default color (hardcoded because of bug 1065998)
573 COLOR(MozWinMediatext
, 0xFF, 0xFF, 0xFF)
574 COLOR(MozWinCommunicationstext
, 0xFF, 0xFF, 0xFF)
575 COLOR(MozNativehyperlinktext
, 0x00, 0x66, 0xCC)
576 COLOR(MozComboboxtext
, 0x00, 0x00, 0x00)
577 COLOR(MozCombobox
, 0xFF, 0xFF, 0xFF)
581 return NS_RGB(0xFF, 0xFF, 0xFF);
584 // Uncomment the #define below if you want to debug system color use in a skin
585 // that uses them. When set, it will make all system color pairs that are
586 // appropriate for foreground/background pairing the same. This means if the
587 // skin is using system colors correctly you will not be able to see *any* text.
589 // #define DEBUG_SYSTEM_COLOR_USE
591 #ifdef DEBUG_SYSTEM_COLOR_USE
592 static nsresult
SystemColorUseDebuggingColor(LookAndFeel::ColorID aID
,
594 using ColorID
= LookAndFeel::ColorID
;
597 // css2 http://www.w3.org/TR/REC-CSS2/ui.html#system-colors
598 case ColorID::Activecaption
:
599 // active window caption background
600 case ColorID::Captiontext
:
601 // text in active window caption
602 aResult
= NS_RGB(0xff, 0x00, 0x00);
605 case ColorID::Highlight
:
606 // background of selected item
607 case ColorID::Highlighttext
:
608 // text of selected item
609 aResult
= NS_RGB(0xff, 0xff, 0x00);
612 case ColorID::Inactivecaption
:
613 // inactive window caption
614 case ColorID::Inactivecaptiontext
:
615 // text in inactive window caption
616 aResult
= NS_RGB(0x66, 0x66, 0x00);
619 case ColorID::Infobackground
:
620 // tooltip background color
621 case ColorID::Infotext
:
622 // tooltip text color
623 aResult
= NS_RGB(0x00, 0xff, 0x00);
628 case ColorID::Menutext
:
630 aResult
= NS_RGB(0x00, 0xff, 0xff);
633 case ColorID::Threedface
:
634 case ColorID::Buttonface
:
636 case ColorID::Buttontext
:
637 // text on push buttons
638 aResult
= NS_RGB(0x00, 0x66, 0x66);
641 case ColorID::Window
:
642 case ColorID::Windowtext
:
643 aResult
= NS_RGB(0x00, 0x00, 0xff);
646 // from the CSS3 working draft (not yet finalized)
647 // http://www.w3.org/tr/2000/wd-css3-userint-20000216.html#color
650 case ColorID::Fieldtext
:
651 aResult
= NS_RGB(0xff, 0x00, 0xff);
654 case ColorID::MozDialog
:
655 case ColorID::MozDialogtext
:
656 aResult
= NS_RGB(0x66, 0x00, 0x66);
660 return NS_ERROR_NOT_AVAILABLE
;
667 static nsresult
GetColorFromPref(LookAndFeel::ColorID aID
, nscolor
& aResult
) {
668 const char* prefName
= sColorPrefs
[size_t(aID
)];
669 nsAutoCString colorStr
;
670 MOZ_TRY(Preferences::GetCString(prefName
, colorStr
));
671 if (!ServoCSSParser::ComputeColor(nullptr, NS_RGB(0, 0, 0), colorStr
,
673 return NS_ERROR_FAILURE
;
678 // All these routines will return NS_OK if they have a value,
679 // in which case the nsLookAndFeel should use that value;
680 // otherwise we'll return NS_ERROR_NOT_AVAILABLE, in which case, the
681 // platform-specific nsLookAndFeel should use its own values instead.
682 nsresult
nsXPLookAndFeel::GetColorValue(ColorID aID
, ColorScheme aScheme
,
683 UseStandins aUseStandins
,
689 #ifdef DEBUG_SYSTEM_COLOR_USE
690 if (NS_SUCCEEDED(SystemColorUseDebuggingColor(aID
, aResult
))) {
695 if (aUseStandins
== UseStandins::Yes
) {
696 aResult
= GetStandinForNativeColor(aID
);
701 aScheme
== ColorScheme::Light
? sLightColorCache
: sDarkColorCache
;
702 if (const auto* cached
= cache
.Get(aID
)) {
703 if (cached
->isNothing()) {
704 return NS_ERROR_FAILURE
;
706 aResult
= cached
->value();
710 if (NS_SUCCEEDED(GetColorFromPref(aID
, aResult
))) {
711 cache
.Insert(aID
, Some(aResult
));
715 if (NS_SUCCEEDED(NativeGetColor(aID
, aScheme
, aResult
))) {
716 if (gfxPlatform::GetCMSMode() == CMSMode::All
&&
717 !IsSpecialColor(aID
, aResult
)) {
718 qcms_transform
* transform
= gfxPlatform::GetCMSInverseRGBTransform();
721 color
[0] = NS_GET_R(aResult
);
722 color
[1] = NS_GET_G(aResult
);
723 color
[2] = NS_GET_B(aResult
);
724 color
[3] = NS_GET_A(aResult
);
725 qcms_transform_data(transform
, color
, color
, 1);
726 aResult
= NS_RGBA(color
[0], color
[1], color
[2], color
[3]);
730 // NOTE: Servo holds a lock and the main thread is paused, so writing to the
731 // global cache here is fine.
732 cache
.Insert(aID
, Some(aResult
));
736 cache
.Insert(aID
, Nothing());
737 return NS_ERROR_FAILURE
;
740 nsresult
nsXPLookAndFeel::GetIntValue(IntID aID
, int32_t& aResult
) {
745 if (const auto* cached
= sIntCache
.Get(aID
)) {
746 if (cached
->isNothing()) {
747 return NS_ERROR_FAILURE
;
749 aResult
= cached
->value();
753 if (NS_SUCCEEDED(Preferences::GetInt(sIntPrefs
[size_t(aID
)], &aResult
))) {
754 sIntCache
.Insert(aID
, Some(aResult
));
758 if (NS_FAILED(NativeGetInt(aID
, aResult
))) {
759 sIntCache
.Insert(aID
, Nothing());
760 return NS_ERROR_FAILURE
;
763 sIntCache
.Insert(aID
, Some(aResult
));
767 nsresult
nsXPLookAndFeel::GetFloatValue(FloatID aID
, float& aResult
) {
772 if (const auto* cached
= sFloatCache
.Get(aID
)) {
773 if (cached
->isNothing()) {
774 return NS_ERROR_FAILURE
;
776 aResult
= cached
->value();
781 if (NS_SUCCEEDED(Preferences::GetInt(sFloatPrefs
[size_t(aID
)], &pref
))) {
782 aResult
= float(pref
) / 100.0f
;
783 sFloatCache
.Insert(aID
, Some(aResult
));
787 if (NS_FAILED(NativeGetFloat(aID
, aResult
))) {
788 sFloatCache
.Insert(aID
, Nothing());
789 return NS_ERROR_FAILURE
;
792 sFloatCache
.Insert(aID
, Some(aResult
));
796 bool nsXPLookAndFeel::LookAndFeelFontToStyle(const LookAndFeelFont
& aFont
,
798 gfxFontStyle
& aStyle
) {
799 if (!aFont
.haveFont()) {
802 aName
= aFont
.name();
803 aStyle
= gfxFontStyle();
804 aStyle
.size
= aFont
.size();
805 aStyle
.weight
= FontWeight(aFont
.weight());
807 aFont
.italic() ? FontSlantStyle::Italic() : FontSlantStyle::Normal();
808 aStyle
.systemFont
= true;
812 widget::LookAndFeelFont
nsXPLookAndFeel::StyleToLookAndFeelFont(
813 const nsAString
& aName
, const gfxFontStyle
& aStyle
) {
814 LookAndFeelFont font
;
815 font
.haveFont() = true;
817 font
.size() = aStyle
.size
;
818 font
.weight() = aStyle
.weight
.ToFloat();
819 font
.italic() = aStyle
.style
.IsItalic();
820 MOZ_ASSERT(aStyle
.style
.IsNormal() || aStyle
.style
.IsItalic(),
821 "Cannot handle oblique font style");
824 // Assert that all the remaining font style properties have their
826 gfxFontStyle candidate
= aStyle
;
827 gfxFontStyle defaults
{};
828 candidate
.size
= defaults
.size
;
829 candidate
.weight
= defaults
.weight
;
830 candidate
.style
= defaults
.style
;
831 MOZ_ASSERT(candidate
.Equals(defaults
),
832 "Some font style properties not supported");
838 bool nsXPLookAndFeel::GetFontValue(FontID aID
, nsString
& aName
,
839 gfxFontStyle
& aStyle
) {
840 if (const LookAndFeelFont
* cached
= sFontCache
.Get(aID
)) {
841 return LookAndFeelFontToStyle(*cached
, aName
, aStyle
);
843 LookAndFeelFont font
;
844 const bool haveFont
= NativeGetFont(aID
, aName
, aStyle
);
845 font
.haveFont() = haveFont
;
847 font
= StyleToLookAndFeelFont(aName
, aStyle
);
849 sFontCache
.Insert(aID
, std::move(font
));
853 void nsXPLookAndFeel::RefreshImpl() {
854 // Wipe out our caches.
855 sLightColorCache
.Clear();
856 sDarkColorCache
.Clear();
861 // Clear any cached FullLookAndFeel data, which is now invalid.
862 if (XRE_IsParentProcess()) {
863 widget::RemoteLookAndFeel::ClearCachedData();
867 static bool sRecordedLookAndFeelTelemetry
= false;
869 void nsXPLookAndFeel::RecordTelemetry() {
870 if (!XRE_IsParentProcess()) {
874 if (sRecordedLookAndFeelTelemetry
) {
878 sRecordedLookAndFeelTelemetry
= true;
881 Telemetry::ScalarSet(
882 Telemetry::ScalarID::WIDGET_DARK_MODE
,
883 NS_SUCCEEDED(GetIntValue(IntID::SystemUsesDarkTheme
, i
)) && i
!= 0);
885 RecordLookAndFeelSpecificTelemetry();
891 void LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind aKind
) {
892 if (nsCOMPtr
<nsIObserverService
> obs
= services::GetObserverService()) {
893 const char16_t kind
[] = {char16_t(aKind
), 0};
894 obs
->NotifyObservers(nullptr, "look-and-feel-changed", kind
);
898 static bool ShouldUseStandinsForNativeColorForNonNativeTheme(
899 const dom::Document
& aDoc
, LookAndFeel::ColorID aColor
) {
900 using ColorID
= LookAndFeel::ColorID
;
901 if (!aDoc
.ShouldAvoidNativeTheme()) {
905 // The native theme doesn't use system colors backgrounds etc, except when in
906 // high-contrast mode, so spoof some of the colors with stand-ins to prevent
909 case ColorID::Buttonface
:
910 case ColorID::Buttontext
:
911 case ColorID::MozButtonhoverface
:
912 case ColorID::MozButtonhovertext
:
913 case ColorID::MozGtkButtonactivetext
:
915 case ColorID::MozCombobox
:
916 case ColorID::MozComboboxtext
:
919 case ColorID::Fieldtext
:
921 case ColorID::Graytext
:
923 return !PreferenceSheet::PrefsFor(aDoc
)
924 .NonNativeThemeShouldUseSystemColors();
933 static bool ShouldRespectSystemColorSchemeForChromeDoc() {
935 // macOS follows the global toolbar theme, not the system theme.
936 // (If the global toolbar theme is set to System, then it *that* follows the
940 // GTK historically has behaved like this. Other platforms don't have support
941 // for light / dark color schemes yet so it doesn't matter for them.
946 static bool ShouldRespectGlobalToolbarThemeAppearanceForChromeDoc() {
948 // Need to be consistent with AppearanceOverride.mm on macOS, which respects
949 // the browser.theme.toolbar-theme pref.
950 // However, if widget.macos.support-dark-appearance is false, we need to
951 // pretend everything's Light and not follow the toolbar theme.
952 return StaticPrefs::widget_macos_support_dark_appearance();
953 #elif defined(MOZ_WIDGET_GTK)
954 return StaticPrefs::widget_gtk_follow_firefox_theme();
960 LookAndFeel::ColorScheme
LookAndFeel::ColorSchemeForDocument(
961 const dom::Document
& aDoc
) {
962 if (nsContentUtils::IsChromeDoc(&aDoc
)) {
963 if (ShouldRespectGlobalToolbarThemeAppearanceForChromeDoc()) {
964 switch (StaticPrefs::browser_theme_toolbar_theme()) {
966 return ColorScheme::Dark
;
968 return ColorScheme::Light
;
970 return SystemColorScheme();
975 if (ShouldRespectSystemColorSchemeForChromeDoc()) {
976 return SystemColorScheme();
979 return LookAndFeel::ColorScheme::Light
;
983 Maybe
<nscolor
> LookAndFeel::GetColor(ColorID aId
, ColorScheme aScheme
,
984 UseStandins aUseStandins
) {
986 nsresult rv
= nsLookAndFeel::GetInstance()->GetColorValue(
987 aId
, aScheme
, aUseStandins
, result
);
994 // Returns whether there is a CSS color name for this color.
995 static bool ColorIsCSSAccessible(LookAndFeel::ColorID aId
) {
996 using ColorID
= LookAndFeel::ColorID
;
999 case ColorID::WindowBackground
:
1000 case ColorID::WindowForeground
:
1001 case ColorID::WidgetBackground
:
1002 case ColorID::WidgetForeground
:
1003 case ColorID::WidgetSelectBackground
:
1004 case ColorID::WidgetSelectForeground
:
1005 case ColorID::Widget3DHighlight
:
1006 case ColorID::Widget3DShadow
:
1007 case ColorID::TextBackground
:
1008 case ColorID::TextForeground
:
1009 case ColorID::TextSelectBackground
:
1010 case ColorID::TextSelectForeground
:
1011 case ColorID::TextSelectBackgroundDisabled
:
1012 case ColorID::TextSelectBackgroundAttention
:
1013 case ColorID::TextHighlightBackground
:
1014 case ColorID::TextHighlightForeground
:
1015 case ColorID::IMERawInputBackground
:
1016 case ColorID::IMERawInputForeground
:
1017 case ColorID::IMERawInputUnderline
:
1018 case ColorID::IMESelectedRawTextBackground
:
1019 case ColorID::IMESelectedRawTextForeground
:
1020 case ColorID::IMESelectedRawTextUnderline
:
1021 case ColorID::IMEConvertedTextBackground
:
1022 case ColorID::IMEConvertedTextForeground
:
1023 case ColorID::IMEConvertedTextUnderline
:
1024 case ColorID::IMESelectedConvertedTextBackground
:
1025 case ColorID::IMESelectedConvertedTextForeground
:
1026 case ColorID::IMESelectedConvertedTextUnderline
:
1027 case ColorID::SpellCheckerUnderline
:
1036 LookAndFeel::UseStandins
LookAndFeel::ShouldUseStandins(
1037 const dom::Document
& aDoc
, ColorID aId
) {
1039 ShouldUseStandinsForNativeColorForNonNativeTheme(aDoc
, aId
) ||
1040 (nsContentUtils::UseStandinsForNativeColors() &&
1041 !nsContentUtils::IsChromeDoc(&aDoc
) && ColorIsCSSAccessible(aId
)));
1044 Maybe
<nscolor
> LookAndFeel::GetColor(ColorID aId
, const dom::Document
& aDoc
) {
1045 return GetColor(aId
, ColorSchemeForDocument(aDoc
),
1046 ShouldUseStandins(aDoc
, aId
));
1049 Maybe
<nscolor
> LookAndFeel::GetColor(ColorID aId
, const nsIFrame
* aFrame
) {
1050 return GetColor(aId
, *aFrame
->PresContext()->Document());
1054 nsresult
LookAndFeel::GetInt(IntID aID
, int32_t* aResult
) {
1055 return nsLookAndFeel::GetInstance()->GetIntValue(aID
, *aResult
);
1059 nsresult
LookAndFeel::GetFloat(FloatID aID
, float* aResult
) {
1060 return nsLookAndFeel::GetInstance()->GetFloatValue(aID
, *aResult
);
1064 bool LookAndFeel::GetFont(FontID aID
, nsString
& aName
, gfxFontStyle
& aStyle
) {
1065 return nsLookAndFeel::GetInstance()->GetFontValue(aID
, aName
, aStyle
);
1069 char16_t
LookAndFeel::GetPasswordCharacter() {
1070 return nsLookAndFeel::GetInstance()->GetPasswordCharacterImpl();
1074 bool LookAndFeel::GetEchoPassword() {
1075 if (StaticPrefs::editor_password_mask_delay() >= 0) {
1076 return StaticPrefs::editor_password_mask_delay() > 0;
1078 return nsLookAndFeel::GetInstance()->GetEchoPasswordImpl();
1082 uint32_t LookAndFeel::GetPasswordMaskDelay() {
1083 int32_t delay
= StaticPrefs::editor_password_mask_delay();
1085 return nsLookAndFeel::GetInstance()->GetPasswordMaskDelayImpl();
1091 void LookAndFeel::Refresh() {
1092 nsLookAndFeel::GetInstance()->RefreshImpl();
1093 nsNativeBasicTheme::LookAndFeelChanged();
1097 void LookAndFeel::NativeInit() { nsLookAndFeel::GetInstance()->NativeInit(); }
1100 void LookAndFeel::SetData(widget::FullLookAndFeel
&& aTables
) {
1101 nsLookAndFeel::GetInstance()->SetDataImpl(std::move(aTables
));
1104 } // namespace mozilla