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_editor.h"
26 #include "mozilla/StaticPrefs_findbar.h"
27 #include "mozilla/StaticPrefs_ui.h"
28 #include "mozilla/StaticPrefs_widget.h"
29 #include "mozilla/dom/Document.h"
30 #include "mozilla/PreferenceSheet.h"
31 #include "mozilla/gfx/2D.h"
32 #include "mozilla/widget/WidgetMessageUtils.h"
33 #include "mozilla/Telemetry.h"
34 #include "mozilla/TelemetryScalarEnums.h"
36 #include "gfxPlatform.h"
45 using namespace mozilla
;
47 using IntID
= mozilla::LookAndFeel::IntID
;
48 using FloatID
= mozilla::LookAndFeel::FloatID
;
49 using ColorID
= mozilla::LookAndFeel::ColorID
;
50 using FontID
= mozilla::LookAndFeel::FontID
;
52 template <typename Index
, typename Value
, Index kEnd
>
53 class EnumeratedCache
{
54 static constexpr uint32_t ChunkFor(Index aIndex
) {
55 return uint32_t(aIndex
) >> 5; // >> 5 is the same as / 32.
57 static constexpr uint32_t BitFor(Index aIndex
) {
58 return 1u << (uint32_t(aIndex
) & 31);
60 static constexpr uint32_t kChunks
= ChunkFor(kEnd
) + 1;
62 mozilla::EnumeratedArray
<Index
, kEnd
, Value
> mEntries
;
63 uint32_t mValidity
[kChunks
] = {0};
66 constexpr EnumeratedCache() = default;
68 bool IsValid(Index aIndex
) const {
69 return mValidity
[ChunkFor(aIndex
)] & BitFor(aIndex
);
72 const Value
* Get(Index aIndex
) const {
73 return IsValid(aIndex
) ? &mEntries
[aIndex
] : nullptr;
76 void Insert(Index aIndex
, Value aValue
) {
77 mValidity
[ChunkFor(aIndex
)] |= BitFor(aIndex
);
78 mEntries
[aIndex
] = aValue
;
81 void Remove(Index aIndex
) {
82 mValidity
[ChunkFor(aIndex
)] &= ~BitFor(aIndex
);
83 mEntries
[aIndex
] = Value();
87 for (auto& chunk
: mValidity
) {
90 for (auto& entry
: mEntries
) {
96 static EnumeratedCache
<ColorID
, Maybe
<nscolor
>, ColorID::End
> sLightColorCache
;
97 static EnumeratedCache
<ColorID
, Maybe
<nscolor
>, ColorID::End
> sDarkColorCache
;
98 static EnumeratedCache
<FloatID
, Maybe
<float>, FloatID::End
> sFloatCache
;
99 static EnumeratedCache
<IntID
, Maybe
<int32_t>, IntID::End
> sIntCache
;
100 static EnumeratedCache
<FontID
, widget::LookAndFeelFont
, FontID::End
> sFontCache
;
102 // To make one of these prefs toggleable from a reftest add a user
103 // pref in testing/profiles/reftest/user.js. For example, to make
104 // ui.useAccessibilityTheme toggleable, add:
106 // user_pref("ui.useAccessibilityTheme", 0);
108 // This needs to be of the same length and in the same order as
109 // LookAndFeel::IntID values.
110 static const char sIntPrefs
[][42] = {
113 "ui.caretVisibleWithSelection",
114 "ui.selectTextfieldsOnKeyFocus",
116 "ui.menusCanOverlapOSBar",
117 "ui.useOverlayScrollbars",
118 "ui.allowOverlayScrollbarsOverlap",
119 "ui.showHideScrollbars",
120 "ui.skipNavigatingDisabledMenuItem",
123 "ui.useAccessibilityTheme",
124 "ui.scrollArrowStyle",
125 "ui.scrollSliderStyle",
126 "ui.scrollButtonLeftMouseButtonAction",
127 "ui.scrollButtonMiddleMouseButtonAction",
128 "ui.scrollButtonRightMouseButtonAction",
131 "ui.treeLazyScrollDelay",
132 "ui.treeScrollDelay",
133 "ui.treeScrollLinesMax",
134 "accessibility.tabfocus", // Weird one...
135 "ui.chosenMenuItemsShouldBlink",
136 "ui.windowsAccentColorInTitlebar",
137 "ui.windowsDefaultTheme",
141 "ui.macGraphiteTheme",
143 "ui.alertNotificationOrigin",
145 "ui.IMERawInputUnderlineStyle",
146 "ui.IMESelectedRawTextUnderlineStyle",
147 "ui.IMEConvertedTextUnderlineStyle",
148 "ui.IMESelectedConvertedTextUnderlineStyle",
149 "ui.SpellCheckerUnderlineStyle",
151 "ui.windowsThemeIdentifier",
152 "ui.operatingSystemVersionIdentifier",
153 "ui.scrollbarButtonAutoRepeatBehavior",
155 "ui.swipeAnimationEnabled",
156 "ui.scrollbarDisplayOnMouseMove",
157 "ui.scrollbarFadeBeginDelay",
158 "ui.scrollbarFadeDuration",
159 "ui.contextMenuOffsetVertical",
160 "ui.contextMenuOffsetHorizontal",
161 "ui.GtkCSDAvailable",
162 "ui.GtkCSDHideTitlebarByDefault",
163 "ui.GtkCSDTransparentBackground",
164 "ui.GtkCSDMinimizeButton",
165 "ui.GtkCSDMaximizeButton",
166 "ui.GtkCSDCloseButton",
167 "ui.GtkCSDMinimizeButtonPosition",
168 "ui.GtkCSDMaximizeButtonPosition",
169 "ui.GtkCSDCloseButtonPosition",
170 "ui.GtkCSDReversedPlacement",
171 "ui.systemUsesDarkTheme",
172 "ui.prefersReducedMotion",
173 "ui.primaryPointerCapabilities",
174 "ui.allPointerCapabilities",
175 "ui.systemVerticalScrollbarWidth",
176 "ui.systemHorizontalScrollbarHeight",
179 static_assert(ArrayLength(sIntPrefs
) == size_t(LookAndFeel::IntID::End
),
180 "Should have a pref for each int value");
182 // This array MUST be kept in the same order as the float id list in
184 static const char sFloatPrefs
[][37] = {
185 "ui.IMEUnderlineRelativeSize",
186 "ui.SpellCheckerUnderlineRelativeSize",
187 "ui.caretAspectRatio",
188 "ui.textScaleFactor",
191 static_assert(ArrayLength(sFloatPrefs
) == size_t(LookAndFeel::FloatID::End
),
192 "Should have a pref for each float value");
194 // This array MUST be kept in the same order as the color list in
195 // specified/color.rs
196 static const char sColorPrefs
[][41] = {
197 "ui.windowBackground",
198 "ui.windowForeground",
199 "ui.widgetBackground",
200 "ui.widgetForeground",
201 "ui.widgetSelectBackground",
202 "ui.widgetSelectForeground",
203 "ui.widget3DHighlight",
207 "ui.textSelectBackground",
208 "ui.textSelectForeground",
209 "ui.textSelectForegroundCustom",
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-gtk-info-bar-text",
313 "ui.-moz-colheadertext",
314 "ui.-moz-colheaderhovertext"};
316 static_assert(ArrayLength(sColorPrefs
) == size_t(LookAndFeel::ColorID::End
),
317 "Should have a pref for each color value");
319 bool nsXPLookAndFeel::sInitialized
= false;
321 nsXPLookAndFeel
* nsXPLookAndFeel::sInstance
= nullptr;
322 bool nsXPLookAndFeel::sShutdown
= false;
325 nsXPLookAndFeel
* nsXPLookAndFeel::GetInstance() {
330 NS_ENSURE_TRUE(!sShutdown
, nullptr);
332 // If we're in a content process, then the parent process will have supplied
333 // us with an initial FullLookAndFeel object.
334 // We grab this data from the ContentChild,
335 // where it's been temporarily stashed, and initialize our new LookAndFeel
338 FullLookAndFeel
* lnf
= nullptr;
340 if (auto* cc
= mozilla::dom::ContentChild::GetSingleton()) {
341 lnf
= &cc
->BorrowLookAndFeelData();
345 sInstance
= new widget::RemoteLookAndFeel(std::move(*lnf
));
346 } else if (gfxPlatform::IsHeadless()) {
347 sInstance
= new widget::HeadlessLookAndFeel();
349 sInstance
= new nsLookAndFeel();
352 // This is only ever used once during initialization, and can be cleared now.
357 nsNativeBasicTheme::Init();
362 void nsXPLookAndFeel::Shutdown() {
371 // This keeps strings alive, so need to clear to make leak checking happy.
374 nsNativeBasicTheme::Shutdown();
377 static void IntPrefChanged() {
378 // Int prefs can't change our system colors or fonts.
379 LookAndFeel::NotifyChangedAllWindows(
380 widget::ThemeChangeKind::MediaQueriesOnly
);
383 static void FloatPrefChanged() {
384 // Float prefs can't change our system colors or fonts.
385 LookAndFeel::NotifyChangedAllWindows(
386 widget::ThemeChangeKind::MediaQueriesOnly
);
389 static void ColorPrefChanged() {
390 // Color prefs affect style, because they by definition change system colors.
391 LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind::Style
);
395 void nsXPLookAndFeel::OnPrefChanged(const char* aPref
, void* aClosure
) {
396 nsDependentCString
prefName(aPref
);
397 for (const char* pref
: sIntPrefs
) {
398 if (prefName
.Equals(pref
)) {
404 for (const char* pref
: sFloatPrefs
) {
405 if (prefName
.Equals(pref
)) {
411 for (const char* pref
: sColorPrefs
) {
412 if (prefName
.Equals(pref
)) {
419 static constexpr nsLiteralCString kBoolMediaQueryPrefs
[] = {
420 "browser.proton.enabled"_ns
,
421 "browser.proton.contextmenus.enabled"_ns
,
422 "browser.proton.modals.enabled"_ns
,
423 "browser.proton.doorhangers.enabled"_ns
,
424 "browser.proton.places-tooltip.enabled"_ns
,
427 // Read values from the user's preferences.
428 // This is done once at startup, but since the user's preferences
429 // haven't actually been read yet at that time, we also have to
430 // set a callback to inform us of changes to each pref.
431 void nsXPLookAndFeel::Init() {
432 MOZ_RELEASE_ASSERT(NS_IsMainThread());
434 // Say we're already initialized, and take the chance that it might fail;
435 // protects against some other process writing to our static variables.
438 // XXX If we could reorganize the pref names, we should separate the branch
439 // for each types. Then, we could reduce the unnecessary loop from
440 // nsXPLookAndFeel::OnPrefChanged().
441 Preferences::RegisterPrefixCallback(OnPrefChanged
, "ui.");
442 // We really do just want the accessibility.tabfocus pref, not other prefs
443 // that start with that string.
444 Preferences::RegisterCallback(OnPrefChanged
, "accessibility.tabfocus");
446 for (auto& pref
: kBoolMediaQueryPrefs
) {
447 Preferences::RegisterCallback(
448 [](const char*, void*) {
449 LookAndFeel::NotifyChangedAllWindows(
450 widget::ThemeChangeKind::MediaQueriesOnly
);
456 nsXPLookAndFeel::~nsXPLookAndFeel() {
457 NS_ASSERTION(sInstance
== this,
458 "This destroying instance isn't the singleton instance");
462 static bool IsSpecialColor(LookAndFeel::ColorID aID
, nscolor aColor
) {
463 using ColorID
= LookAndFeel::ColorID
;
466 case ColorID::TextSelectForeground
:
467 return aColor
== NS_DONT_CHANGE_COLOR
;
468 case ColorID::IMESelectedRawTextBackground
:
469 case ColorID::IMESelectedConvertedTextBackground
:
470 case ColorID::IMERawInputBackground
:
471 case ColorID::IMEConvertedTextBackground
:
472 case ColorID::IMESelectedRawTextForeground
:
473 case ColorID::IMESelectedConvertedTextForeground
:
474 case ColorID::IMERawInputForeground
:
475 case ColorID::IMEConvertedTextForeground
:
476 case ColorID::IMERawInputUnderline
:
477 case ColorID::IMEConvertedTextUnderline
:
478 case ColorID::IMESelectedRawTextUnderline
:
479 case ColorID::IMESelectedConvertedTextUnderline
:
480 case ColorID::SpellCheckerUnderline
:
481 return NS_IS_SELECTION_SPECIAL_COLOR(aColor
);
486 * In GetColor(), every color that is not a special color is color
487 * corrected. Use false to make other colors color corrected.
492 nscolor
nsXPLookAndFeel::GetStandinForNativeColor(ColorID aID
) {
493 // The stand-in colors are taken from the Windows 7 Aero theme
494 // except Mac-specific colors which are taken from Mac OS 10.7.
496 #define COLOR(name_, r, g, b) \
497 case ColorID::name_: \
498 return NS_RGB(r, g, b);
502 COLOR(Activeborder
, 0xB4, 0xB4, 0xB4)
503 COLOR(Activecaption
, 0x99, 0xB4, 0xD1)
504 COLOR(Appworkspace
, 0xAB, 0xAB, 0xAB)
505 COLOR(Background
, 0x00, 0x00, 0x00)
506 COLOR(Buttonface
, 0xF0, 0xF0, 0xF0)
507 COLOR(Buttonhighlight
, 0xFF, 0xFF, 0xFF)
508 COLOR(Buttonshadow
, 0xA0, 0xA0, 0xA0)
509 COLOR(Buttontext
, 0x00, 0x00, 0x00)
510 COLOR(Captiontext
, 0x00, 0x00, 0x00)
511 COLOR(Graytext
, 0x6D, 0x6D, 0x6D)
512 COLOR(Highlight
, 0x33, 0x99, 0xFF)
513 COLOR(Highlighttext
, 0xFF, 0xFF, 0xFF)
514 COLOR(Inactiveborder
, 0xF4, 0xF7, 0xFC)
515 COLOR(Inactivecaption
, 0xBF, 0xCD, 0xDB)
516 COLOR(Inactivecaptiontext
, 0x43, 0x4E, 0x54)
517 COLOR(Infobackground
, 0xFF, 0xFF, 0xE1)
518 COLOR(Infotext
, 0x00, 0x00, 0x00)
519 COLOR(Menu
, 0xF0, 0xF0, 0xF0)
520 COLOR(Menutext
, 0x00, 0x00, 0x00)
521 COLOR(Scrollbar
, 0xC8, 0xC8, 0xC8)
522 COLOR(Threeddarkshadow
, 0x69, 0x69, 0x69)
523 COLOR(Threedface
, 0xF0, 0xF0, 0xF0)
524 COLOR(Threedhighlight
, 0xFF, 0xFF, 0xFF)
525 COLOR(Threedlightshadow
, 0xE3, 0xE3, 0xE3)
526 COLOR(Threedshadow
, 0xA0, 0xA0, 0xA0)
527 COLOR(Window
, 0xFF, 0xFF, 0xFF)
528 COLOR(Windowframe
, 0x64, 0x64, 0x64)
529 COLOR(Windowtext
, 0x00, 0x00, 0x00)
530 COLOR(MozButtondefault
, 0x69, 0x69, 0x69)
531 COLOR(Field
, 0xFF, 0xFF, 0xFF)
532 COLOR(Fieldtext
, 0x00, 0x00, 0x00)
533 COLOR(MozDialog
, 0xF0, 0xF0, 0xF0)
534 COLOR(MozDialogtext
, 0x00, 0x00, 0x00)
535 COLOR(MozColheadertext
, 0x00, 0x00, 0x00)
536 COLOR(MozColheaderhovertext
, 0x00, 0x00, 0x00)
537 COLOR(MozDragtargetzone
, 0xFF, 0xFF, 0xFF)
538 COLOR(MozCellhighlight
, 0xF0, 0xF0, 0xF0)
539 COLOR(MozCellhighlighttext
, 0x00, 0x00, 0x00)
540 COLOR(MozHtmlCellhighlight
, 0x33, 0x99, 0xFF)
541 COLOR(MozHtmlCellhighlighttext
, 0xFF, 0xFF, 0xFF)
542 COLOR(MozButtonhoverface
, 0xF0, 0xF0, 0xF0)
543 COLOR(MozGtkButtonactivetext
, 0x00, 0x00, 0x00)
544 COLOR(MozButtonhovertext
, 0x00, 0x00, 0x00)
545 COLOR(MozMenuhover
, 0x33, 0x99, 0xFF)
546 COLOR(MozMenuhovertext
, 0x00, 0x00, 0x00)
547 COLOR(MozMenubartext
, 0x00, 0x00, 0x00)
548 COLOR(MozMenubarhovertext
, 0x00, 0x00, 0x00)
549 COLOR(MozOddtreerow
, 0xFF, 0xFF, 0xFF)
550 COLOR(MozMacChromeActive
, 0xB2, 0xB2, 0xB2)
551 COLOR(MozMacChromeInactive
, 0xE1, 0xE1, 0xE1)
552 COLOR(MozMacFocusring
, 0x60, 0x9D, 0xD7)
553 COLOR(MozMacMenuselect
, 0x38, 0x75, 0xD7)
554 COLOR(MozMacMenushadow
, 0xA3, 0xA3, 0xA3)
555 COLOR(MozMacMenutextdisable
, 0x88, 0x88, 0x88)
556 COLOR(MozMacMenutextselect
, 0xFF, 0xFF, 0xFF)
557 COLOR(MozMacDisabledtoolbartext
, 0x3F, 0x3F, 0x3F)
558 COLOR(MozMacSecondaryhighlight
, 0xD4, 0xD4, 0xD4)
559 COLOR(MozMacVibrantTitlebarLight
, 0xf7, 0xf7, 0xf7)
560 COLOR(MozMacVibrantTitlebarDark
, 0x28, 0x28, 0x28)
561 COLOR(MozMacMenupopup
, 0xe6, 0xe6, 0xe6)
562 COLOR(MozMacMenuitem
, 0xe6, 0xe6, 0xe6)
563 COLOR(MozMacActiveMenuitem
, 0x0a, 0x64, 0xdc)
564 COLOR(MozMacSourceList
, 0xf7, 0xf7, 0xf7)
565 COLOR(MozMacSourceListSelection
, 0xc8, 0xc8, 0xc8)
566 COLOR(MozMacActiveSourceListSelection
, 0x0a, 0x64, 0xdc)
567 COLOR(MozMacTooltip
, 0xf7, 0xf7, 0xf7)
568 // Seems to be the default color (hardcoded because of bug 1065998)
569 COLOR(MozWinMediatext
, 0xFF, 0xFF, 0xFF)
570 COLOR(MozWinCommunicationstext
, 0xFF, 0xFF, 0xFF)
571 COLOR(MozNativehyperlinktext
, 0x00, 0x66, 0xCC)
572 COLOR(MozComboboxtext
, 0x00, 0x00, 0x00)
573 COLOR(MozCombobox
, 0xFF, 0xFF, 0xFF)
577 return NS_RGB(0xFF, 0xFF, 0xFF);
580 // Uncomment the #define below if you want to debug system color use in a skin
581 // that uses them. When set, it will make all system color pairs that are
582 // appropriate for foreground/background pairing the same. This means if the
583 // skin is using system colors correctly you will not be able to see *any* text.
585 // #define DEBUG_SYSTEM_COLOR_USE
587 #ifdef DEBUG_SYSTEM_COLOR_USE
588 static nsresult
SystemColorUseDebuggingColor(LookAndFeel::ColorID aID
,
590 using ColorID
= LookAndFeel::ColorID
;
593 // css2 http://www.w3.org/TR/REC-CSS2/ui.html#system-colors
594 case ColorID::Activecaption
:
595 // active window caption background
596 case ColorID::Captiontext
:
597 // text in active window caption
598 aResult
= NS_RGB(0xff, 0x00, 0x00);
601 case ColorID::Highlight
:
602 // background of selected item
603 case ColorID::Highlighttext
:
604 // text of selected item
605 aResult
= NS_RGB(0xff, 0xff, 0x00);
608 case ColorID::Inactivecaption
:
609 // inactive window caption
610 case ColorID::Inactivecaptiontext
:
611 // text in inactive window caption
612 aResult
= NS_RGB(0x66, 0x66, 0x00);
615 case ColorID::Infobackground
:
616 // tooltip background color
617 case ColorID::Infotext
:
618 // tooltip text color
619 aResult
= NS_RGB(0x00, 0xff, 0x00);
624 case ColorID::Menutext
:
626 aResult
= NS_RGB(0x00, 0xff, 0xff);
629 case ColorID::Threedface
:
630 case ColorID::Buttonface
:
632 case ColorID::Buttontext
:
633 // text on push buttons
634 aResult
= NS_RGB(0x00, 0x66, 0x66);
637 case ColorID::Window
:
638 case ColorID::Windowtext
:
639 aResult
= NS_RGB(0x00, 0x00, 0xff);
642 // from the CSS3 working draft (not yet finalized)
643 // http://www.w3.org/tr/2000/wd-css3-userint-20000216.html#color
646 case ColorID::Fieldtext
:
647 aResult
= NS_RGB(0xff, 0x00, 0xff);
650 case ColorID::MozDialog
:
651 case ColorID::MozDialogtext
:
652 aResult
= NS_RGB(0x66, 0x00, 0x66);
656 return NS_ERROR_NOT_AVAILABLE
;
663 static nsresult
GetColorFromPref(LookAndFeel::ColorID aID
, nscolor
& aResult
) {
664 const char* prefName
= sColorPrefs
[size_t(aID
)];
665 nsAutoCString colorStr
;
666 MOZ_TRY(Preferences::GetCString(prefName
, colorStr
));
667 if (!ServoCSSParser::ComputeColor(nullptr, NS_RGB(0, 0, 0), colorStr
,
669 return NS_ERROR_FAILURE
;
674 // All these routines will return NS_OK if they have a value,
675 // in which case the nsLookAndFeel should use that value;
676 // otherwise we'll return NS_ERROR_NOT_AVAILABLE, in which case, the
677 // platform-specific nsLookAndFeel should use its own values instead.
678 nsresult
nsXPLookAndFeel::GetColorValue(ColorID aID
, ColorScheme aScheme
,
679 UseStandins aUseStandins
,
685 #ifdef DEBUG_SYSTEM_COLOR_USE
686 if (NS_SUCCEEDED(SystemColorUseDebuggingColor(aID
, aResult
))) {
691 if (aUseStandins
== UseStandins::Yes
) {
692 aResult
= GetStandinForNativeColor(aID
);
697 aScheme
== ColorScheme::Light
? sLightColorCache
: sDarkColorCache
;
698 if (const auto* cached
= cache
.Get(aID
)) {
699 if (cached
->isNothing()) {
700 return NS_ERROR_FAILURE
;
702 aResult
= cached
->value();
706 if (NS_SUCCEEDED(GetColorFromPref(aID
, aResult
))) {
707 cache
.Insert(aID
, Some(aResult
));
711 if (NS_SUCCEEDED(NativeGetColor(aID
, aScheme
, aResult
))) {
712 if (gfxPlatform::GetCMSMode() == CMSMode::All
&&
713 !IsSpecialColor(aID
, aResult
)) {
714 qcms_transform
* transform
= gfxPlatform::GetCMSInverseRGBTransform();
717 color
[0] = NS_GET_R(aResult
);
718 color
[1] = NS_GET_G(aResult
);
719 color
[2] = NS_GET_B(aResult
);
720 color
[3] = NS_GET_A(aResult
);
721 qcms_transform_data(transform
, color
, color
, 1);
722 aResult
= NS_RGBA(color
[0], color
[1], color
[2], color
[3]);
726 // NOTE: Servo holds a lock and the main thread is paused, so writing to the
727 // global cache here is fine.
728 cache
.Insert(aID
, Some(aResult
));
732 cache
.Insert(aID
, Nothing());
733 return NS_ERROR_FAILURE
;
736 nsresult
nsXPLookAndFeel::GetIntValue(IntID aID
, int32_t& aResult
) {
741 if (const auto* cached
= sIntCache
.Get(aID
)) {
742 if (cached
->isNothing()) {
743 return NS_ERROR_FAILURE
;
745 aResult
= cached
->value();
749 if (NS_SUCCEEDED(Preferences::GetInt(sIntPrefs
[size_t(aID
)], &aResult
))) {
750 sIntCache
.Insert(aID
, Some(aResult
));
754 if (NS_FAILED(NativeGetInt(aID
, aResult
))) {
755 sIntCache
.Insert(aID
, Nothing());
756 return NS_ERROR_FAILURE
;
759 sIntCache
.Insert(aID
, Some(aResult
));
763 nsresult
nsXPLookAndFeel::GetFloatValue(FloatID aID
, float& aResult
) {
768 if (const auto* cached
= sFloatCache
.Get(aID
)) {
769 if (cached
->isNothing()) {
770 return NS_ERROR_FAILURE
;
772 aResult
= cached
->value();
777 if (NS_SUCCEEDED(Preferences::GetInt(sFloatPrefs
[size_t(aID
)], &pref
))) {
778 aResult
= float(pref
) / 100.0f
;
779 sFloatCache
.Insert(aID
, Some(aResult
));
783 if (NS_FAILED(NativeGetFloat(aID
, aResult
))) {
784 sFloatCache
.Insert(aID
, Nothing());
785 return NS_ERROR_FAILURE
;
788 sFloatCache
.Insert(aID
, Some(aResult
));
792 bool nsXPLookAndFeel::LookAndFeelFontToStyle(const LookAndFeelFont
& aFont
,
794 gfxFontStyle
& aStyle
) {
795 if (!aFont
.haveFont()) {
798 aName
= aFont
.name();
799 aStyle
= gfxFontStyle();
800 aStyle
.size
= aFont
.size();
801 aStyle
.weight
= FontWeight(aFont
.weight());
803 aFont
.italic() ? FontSlantStyle::Italic() : FontSlantStyle::Normal();
804 aStyle
.systemFont
= true;
808 widget::LookAndFeelFont
nsXPLookAndFeel::StyleToLookAndFeelFont(
809 const nsAString
& aName
, const gfxFontStyle
& aStyle
) {
810 LookAndFeelFont font
;
811 font
.haveFont() = true;
813 font
.size() = aStyle
.size
;
814 font
.weight() = aStyle
.weight
.ToFloat();
815 font
.italic() = aStyle
.style
.IsItalic();
816 MOZ_ASSERT(aStyle
.style
.IsNormal() || aStyle
.style
.IsItalic(),
817 "Cannot handle oblique font style");
820 // Assert that all the remaining font style properties have their
822 gfxFontStyle candidate
= aStyle
;
823 gfxFontStyle defaults
{};
824 candidate
.size
= defaults
.size
;
825 candidate
.weight
= defaults
.weight
;
826 candidate
.style
= defaults
.style
;
827 MOZ_ASSERT(candidate
.Equals(defaults
),
828 "Some font style properties not supported");
834 bool nsXPLookAndFeel::GetFontValue(FontID aID
, nsString
& aName
,
835 gfxFontStyle
& aStyle
) {
836 if (const LookAndFeelFont
* cached
= sFontCache
.Get(aID
)) {
837 return LookAndFeelFontToStyle(*cached
, aName
, aStyle
);
839 LookAndFeelFont font
;
840 const bool haveFont
= NativeGetFont(aID
, aName
, aStyle
);
841 font
.haveFont() = haveFont
;
843 font
= StyleToLookAndFeelFont(aName
, aStyle
);
845 sFontCache
.Insert(aID
, std::move(font
));
849 void nsXPLookAndFeel::RefreshImpl() {
850 // Wipe out our caches.
851 sLightColorCache
.Clear();
852 sDarkColorCache
.Clear();
857 // Clear any cached FullLookAndFeel data, which is now invalid.
858 if (XRE_IsParentProcess()) {
859 widget::RemoteLookAndFeel::ClearCachedData();
863 static bool sRecordedLookAndFeelTelemetry
= false;
865 void nsXPLookAndFeel::RecordTelemetry() {
866 if (!XRE_IsParentProcess()) {
870 if (sRecordedLookAndFeelTelemetry
) {
874 sRecordedLookAndFeelTelemetry
= true;
877 Telemetry::ScalarSet(
878 Telemetry::ScalarID::WIDGET_DARK_MODE
,
879 NS_SUCCEEDED(GetIntValue(IntID::SystemUsesDarkTheme
, i
)) && i
!= 0);
881 RecordLookAndFeelSpecificTelemetry();
887 void LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind aKind
) {
888 if (nsCOMPtr
<nsIObserverService
> obs
= services::GetObserverService()) {
889 obs
->NotifyObservers(nullptr, "look-and-feel-changed",
890 reinterpret_cast<char16_t
*>(uintptr_t(aKind
)));
894 static bool ShouldUseStandinsForNativeColorForNonNativeTheme(
895 const dom::Document
& aDoc
, LookAndFeel::ColorID aColor
) {
896 using ColorID
= LookAndFeel::ColorID
;
897 if (!aDoc
.ShouldAvoidNativeTheme()) {
901 // The native theme doesn't use system colors backgrounds etc, except when in
902 // high-contrast mode, so spoof some of the colors with stand-ins to prevent
905 case ColorID::Buttonface
:
906 case ColorID::Buttontext
:
907 case ColorID::MozButtonhoverface
:
908 case ColorID::MozButtonhovertext
:
909 case ColorID::MozGtkButtonactivetext
:
911 case ColorID::MozCombobox
:
912 case ColorID::MozComboboxtext
:
915 case ColorID::Fieldtext
:
917 case ColorID::Graytext
:
919 return !PreferenceSheet::PrefsFor(aDoc
)
920 .NonNativeThemeShouldUseSystemColors();
929 LookAndFeel::ColorScheme
LookAndFeel::ColorSchemeForDocument(
930 const dom::Document
& aDoc
) {
932 if (nsContentUtils::IsChromeDoc(&aDoc
) &&
933 StaticPrefs::widget_macos_respect_system_appearance()) {
934 const auto* doc
= &aDoc
;
935 while (const auto* parent
= doc
->GetInProcessParentDocument()) {
938 switch (doc
->ThreadSafeGetDocumentLWTheme()) {
939 case dom::Document::Doc_Theme_None
:
940 return LookAndFeel::SystemColorScheme();
941 case dom::Document::Doc_Theme_Dark
:
942 return LookAndFeel::ColorScheme::Light
;
943 case dom::Document::Doc_Theme_Bright
:
944 // NOTE(emilio): This looks backwards, but it's actually correct.
945 // Doc_Theme_Bright means that the theme has bright _text_ (and thus
946 // dark background). Tricky!
947 return LookAndFeel::ColorScheme::Dark
;
953 return LookAndFeel::ColorScheme::Light
;
957 Maybe
<nscolor
> LookAndFeel::GetColor(ColorID aId
, ColorScheme aScheme
,
958 UseStandins aUseStandins
) {
960 nsresult rv
= nsLookAndFeel::GetInstance()->GetColorValue(
961 aId
, aScheme
, aUseStandins
, result
);
968 // Returns whether there is a CSS color name for this color.
969 static bool ColorIsCSSAccessible(LookAndFeel::ColorID aId
) {
970 using ColorID
= LookAndFeel::ColorID
;
973 case ColorID::WindowBackground
:
974 case ColorID::WindowForeground
:
975 case ColorID::WidgetBackground
:
976 case ColorID::WidgetForeground
:
977 case ColorID::WidgetSelectBackground
:
978 case ColorID::WidgetSelectForeground
:
979 case ColorID::Widget3DHighlight
:
980 case ColorID::Widget3DShadow
:
981 case ColorID::TextBackground
:
982 case ColorID::TextForeground
:
983 case ColorID::TextSelectBackground
:
984 case ColorID::TextSelectForeground
:
985 case ColorID::TextSelectBackgroundDisabled
:
986 case ColorID::TextSelectBackgroundAttention
:
987 case ColorID::TextHighlightBackground
:
988 case ColorID::TextHighlightForeground
:
989 case ColorID::IMERawInputBackground
:
990 case ColorID::IMERawInputForeground
:
991 case ColorID::IMERawInputUnderline
:
992 case ColorID::IMESelectedRawTextBackground
:
993 case ColorID::IMESelectedRawTextForeground
:
994 case ColorID::IMESelectedRawTextUnderline
:
995 case ColorID::IMEConvertedTextBackground
:
996 case ColorID::IMEConvertedTextForeground
:
997 case ColorID::IMEConvertedTextUnderline
:
998 case ColorID::IMESelectedConvertedTextBackground
:
999 case ColorID::IMESelectedConvertedTextForeground
:
1000 case ColorID::IMESelectedConvertedTextUnderline
:
1001 case ColorID::SpellCheckerUnderline
:
1010 Maybe
<nscolor
> LookAndFeel::GetColor(ColorID aId
, const dom::Document
& aDoc
) {
1011 const bool useStandins
=
1012 ShouldUseStandinsForNativeColorForNonNativeTheme(aDoc
, aId
) ||
1013 (nsContentUtils::UseStandinsForNativeColors() &&
1014 !nsContentUtils::IsChromeDoc(&aDoc
) && ColorIsCSSAccessible(aId
));
1015 return GetColor(aId
, ColorSchemeForDocument(aDoc
), UseStandins(useStandins
));
1018 Maybe
<nscolor
> LookAndFeel::GetColor(ColorID aId
, const nsIFrame
* aFrame
) {
1019 return GetColor(aId
, *aFrame
->PresContext()->Document());
1023 nsresult
LookAndFeel::GetInt(IntID aID
, int32_t* aResult
) {
1024 return nsLookAndFeel::GetInstance()->GetIntValue(aID
, *aResult
);
1028 nsresult
LookAndFeel::GetFloat(FloatID aID
, float* aResult
) {
1029 return nsLookAndFeel::GetInstance()->GetFloatValue(aID
, *aResult
);
1033 bool LookAndFeel::GetFont(FontID aID
, nsString
& aName
, gfxFontStyle
& aStyle
) {
1034 return nsLookAndFeel::GetInstance()->GetFontValue(aID
, aName
, aStyle
);
1038 char16_t
LookAndFeel::GetPasswordCharacter() {
1039 return nsLookAndFeel::GetInstance()->GetPasswordCharacterImpl();
1043 bool LookAndFeel::GetEchoPassword() {
1044 if (StaticPrefs::editor_password_mask_delay() >= 0) {
1045 return StaticPrefs::editor_password_mask_delay() > 0;
1047 return nsLookAndFeel::GetInstance()->GetEchoPasswordImpl();
1051 uint32_t LookAndFeel::GetPasswordMaskDelay() {
1052 int32_t delay
= StaticPrefs::editor_password_mask_delay();
1054 return nsLookAndFeel::GetInstance()->GetPasswordMaskDelayImpl();
1060 void LookAndFeel::Refresh() {
1061 nsLookAndFeel::GetInstance()->RefreshImpl();
1062 nsNativeBasicTheme::LookAndFeelChanged();
1066 void LookAndFeel::NativeInit() { nsLookAndFeel::GetInstance()->NativeInit(); }
1069 void LookAndFeel::SetData(widget::FullLookAndFeel
&& aTables
) {
1070 nsLookAndFeel::GetInstance()->SetDataImpl(std::move(aTables
));
1073 } // namespace mozilla