Bug 1855360 - Fix the skip-if syntax. a=bustage-fix
[gecko.git] / widget / nsXPLookAndFeel.cpp
blob1b6b7adff58e77aecdc2fcf4f70d2bb19cc37325
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"
8 #include "mozilla/LookAndFeel.h"
9 #include "nscore.h"
11 #include "nsXPLookAndFeel.h"
12 #include "nsLookAndFeel.h"
13 #include "HeadlessLookAndFeel.h"
14 #include "RemoteLookAndFeel.h"
15 #include "nsContentUtils.h"
16 #include "nsCRT.h"
17 #include "nsFont.h"
18 #include "nsIFrame.h"
19 #include "nsIXULRuntime.h"
20 #include "nsLayoutUtils.h"
21 #include "Theme.h"
22 #include "SurfaceCacheUtils.h"
23 #include "mozilla/dom/ContentParent.h"
24 #include "mozilla/dom/ContentChild.h"
25 #include "mozilla/Preferences.h"
26 #include "mozilla/Services.h"
27 #include "mozilla/ServoStyleSet.h"
28 #include "mozilla/ServoCSSParser.h"
29 #include "mozilla/StaticPrefs_browser.h"
30 #include "mozilla/StaticPrefs_editor.h"
31 #include "mozilla/StaticPrefs_layout.h"
32 #include "mozilla/StaticPrefs_ui.h"
33 #include "mozilla/StaticPrefs_widget.h"
34 #include "mozilla/dom/Document.h"
35 #include "mozilla/PreferenceSheet.h"
36 #include "mozilla/gfx/2D.h"
37 #include "mozilla/widget/WidgetMessageUtils.h"
38 #include "mozilla/dom/KeyboardEventBinding.h"
39 #include "mozilla/RelativeLuminanceUtils.h"
40 #include "mozilla/Telemetry.h"
41 #include "mozilla/TelemetryScalarEnums.h"
42 #include "mozilla/Try.h"
44 #include "gfxPlatform.h"
45 #include "gfxFont.h"
47 #include "qcms.h"
49 #include <bitset>
51 using namespace mozilla;
53 using IntID = mozilla::LookAndFeel::IntID;
54 using FloatID = mozilla::LookAndFeel::FloatID;
55 using ColorID = mozilla::LookAndFeel::ColorID;
56 using FontID = mozilla::LookAndFeel::FontID;
58 template <typename Index, typename Value, Index kEnd>
59 class EnumeratedCache {
60 mozilla::EnumeratedArray<Index, kEnd, Value> mEntries;
61 std::bitset<size_t(kEnd)> mValidity;
63 public:
64 constexpr EnumeratedCache() = default;
66 bool IsValid(Index aIndex) const { return mValidity[size_t(aIndex)]; }
68 const Value* Get(Index aIndex) const {
69 return IsValid(aIndex) ? &mEntries[aIndex] : nullptr;
72 void Insert(Index aIndex, Value aValue) {
73 mValidity[size_t(aIndex)] = true;
74 mEntries[aIndex] = aValue;
77 void Remove(Index aIndex) {
78 mValidity[size_t(aIndex)] = false;
79 mEntries[aIndex] = Value();
82 void Clear() {
83 mValidity.reset();
84 for (auto& entry : mEntries) {
85 entry = Value();
90 using ColorCache = EnumeratedCache<ColorID, Maybe<nscolor>, ColorID::End>;
92 struct ColorCaches {
93 using UseStandins = LookAndFeel::UseStandins;
95 ColorCache mCaches[2][2];
97 constexpr ColorCaches() = default;
99 ColorCache& Get(ColorScheme aScheme, UseStandins aUseStandins) {
100 return mCaches[aScheme == ColorScheme::Dark]
101 [aUseStandins == UseStandins::Yes];
104 void Clear() {
105 for (auto& c : mCaches) {
106 for (auto& cache : c) {
107 cache.Clear();
113 static ColorCaches sColorCaches;
115 static EnumeratedCache<FloatID, Maybe<float>, FloatID::End> sFloatCache;
116 static EnumeratedCache<IntID, Maybe<int32_t>, IntID::End> sIntCache;
117 static EnumeratedCache<FontID, widget::LookAndFeelFont, FontID::End> sFontCache;
119 // To make one of these prefs toggleable from a reftest add a user
120 // pref in testing/profiles/reftest/user.js. For example, to make
121 // ui.useAccessibilityTheme toggleable, add:
123 // user_pref("ui.useAccessibilityTheme", 0);
125 // This needs to be of the same length and in the same order as
126 // LookAndFeel::IntID values.
127 static const char sIntPrefs[][45] = {
128 "ui.caretBlinkTime",
129 "ui.caretBlinkCount",
130 "ui.caretWidth",
131 "ui.caretVisibleWithSelection",
132 "ui.selectTextfieldsOnKeyFocus",
133 "ui.submenuDelay",
134 "ui.menusCanOverlapOSBar",
135 "ui.useOverlayScrollbars",
136 "ui.allowOverlayScrollbarsOverlap",
137 "ui.skipNavigatingDisabledMenuItem",
138 "ui.dragThresholdX",
139 "ui.dragThresholdY",
140 "ui.useAccessibilityTheme",
141 "ui.scrollArrowStyle",
142 "ui.scrollButtonLeftMouseButtonAction",
143 "ui.scrollButtonMiddleMouseButtonAction",
144 "ui.scrollButtonRightMouseButtonAction",
145 "ui.treeOpenDelay",
146 "ui.treeCloseDelay",
147 "ui.treeLazyScrollDelay",
148 "ui.treeScrollDelay",
149 "ui.treeScrollLinesMax",
150 "accessibility.tabfocus", // Weird one...
151 "ui.chosenMenuItemsShouldBlink",
152 "ui.windowsAccentColorInTitlebar",
153 "ui.macBigSurTheme",
154 "ui.macRTL",
155 "ui.alertNotificationOrigin",
156 "ui.scrollToClick",
157 "ui.IMERawInputUnderlineStyle",
158 "ui.IMESelectedRawTextUnderlineStyle",
159 "ui.IMEConvertedTextUnderlineStyle",
160 "ui.IMESelectedConvertedTextUnderlineStyle",
161 "ui.SpellCheckerUnderlineStyle",
162 "ui.menuBarDrag",
163 "ui.scrollbarButtonAutoRepeatBehavior",
164 "ui.tooltipDelay",
165 "ui.swipeAnimationEnabled",
166 "ui.scrollbarDisplayOnMouseMove",
167 "ui.scrollbarFadeBeginDelay",
168 "ui.scrollbarFadeDuration",
169 "ui.contextMenuOffsetVertical",
170 "ui.contextMenuOffsetHorizontal",
171 "ui.GtkCSDAvailable",
172 "ui.GtkCSDMinimizeButton",
173 "ui.GtkCSDMaximizeButton",
174 "ui.GtkCSDCloseButton",
175 "ui.GtkCSDMinimizeButtonPosition",
176 "ui.GtkCSDMaximizeButtonPosition",
177 "ui.GtkCSDCloseButtonPosition",
178 "ui.GtkCSDReversedPlacement",
179 "ui.systemUsesDarkTheme",
180 "ui.prefersReducedMotion",
181 "ui.prefersReducedTransparency",
182 "ui.invertedColors",
183 "ui.primaryPointerCapabilities",
184 "ui.allPointerCapabilities",
185 "ui.systemScrollbarSize",
186 "ui.touchDeviceSupportPresent",
187 "ui.titlebarRadius",
188 "ui.dynamicRange",
189 "ui.videoDynamicRange",
190 "ui.panelAnimations",
191 "ui.hideCursorWhileTyping",
192 "ui.gtkThemeFamily",
195 static_assert(ArrayLength(sIntPrefs) == size_t(LookAndFeel::IntID::End),
196 "Should have a pref for each int value");
198 // This array MUST be kept in the same order as the float id list in
199 // LookAndFeel.h
200 // clang-format off
201 static const char sFloatPrefs[][37] = {
202 "ui.IMEUnderlineRelativeSize",
203 "ui.SpellCheckerUnderlineRelativeSize",
204 "ui.caretAspectRatio",
205 "ui.textScaleFactor",
206 "ui.cursorScale",
208 // clang-format on
210 static_assert(ArrayLength(sFloatPrefs) == size_t(LookAndFeel::FloatID::End),
211 "Should have a pref for each float value");
213 // This array MUST be kept in the same order as the color list in
214 // specified/color.rs
215 static const char sColorPrefs[][41] = {
216 "ui.activeborder",
217 "ui.activecaption",
218 "ui.appworkspace",
219 "ui.background",
220 "ui.buttonface",
221 "ui.buttonhighlight",
222 "ui.buttonshadow",
223 "ui.buttontext",
224 "ui.buttonborder",
225 "ui.captiontext",
226 "ui.-moz-field",
227 "ui.-moz-disabledfield",
228 "ui.-moz-fieldtext",
229 "ui.mark",
230 "ui.marktext",
231 "ui.-moz-comboboxtext",
232 "ui.-moz-combobox",
233 "ui.graytext",
234 "ui.highlight",
235 "ui.highlighttext",
236 "ui.inactiveborder",
237 "ui.inactivecaption",
238 "ui.inactivecaptiontext",
239 "ui.infobackground",
240 "ui.infotext",
241 "ui.menu",
242 "ui.menutext",
243 "ui.scrollbar",
244 "ui.threeddarkshadow",
245 "ui.threedface",
246 "ui.threedhighlight",
247 "ui.threedlightshadow",
248 "ui.threedshadow",
249 "ui.window",
250 "ui.windowframe",
251 "ui.windowtext",
252 "ui.-moz-default-color",
253 "ui.-moz-default-background-color",
254 "ui.-moz-dialog",
255 "ui.-moz-dialogtext",
256 "ui.-moz-cellhighlight",
257 "ui.-moz_cellhighlighttext",
258 "ui.selecteditem",
259 "ui.selecteditemtext",
260 "ui.-moz-buttonhoverface",
261 "ui.-moz_buttonhovertext",
262 "ui.-moz_menuhover",
263 "ui.-moz_menuhoverdisabled",
264 "ui.-moz_menuhovertext",
265 "ui.-moz_menubarhovertext",
266 "ui.-moz_eventreerow",
267 "ui.-moz_oddtreerow",
268 "ui.-moz-buttonactivetext",
269 "ui.-moz-buttonactiveface",
270 "ui.-moz-buttondisabledface",
271 "ui.-moz-headerbar",
272 "ui.-moz-headerbartext",
273 "ui.-moz-headerbarinactive",
274 "ui.-moz-headerbarinactivetext",
275 "ui.-moz-mac-defaultbuttontext",
276 "ui.-moz-mac-focusring",
277 "ui.-moz_mac_disabledtoolbartext",
278 "ui.-moz-sidebar",
279 "ui.-moz-sidebartext",
280 "ui.-moz-sidebarborder",
281 "ui.accentcolor",
282 "ui.accentcolortext",
283 "ui.-moz-autofill-background",
284 "ui.-moz-nativehyperlinktext",
285 "ui.-moz-nativevisitedhyperlinktext",
286 "ui.-moz-hyperlinktext",
287 "ui.-moz-activehyperlinktext",
288 "ui.-moz-visitedhyperlinktext",
289 "ui.-moz-colheader",
290 "ui.-moz-colheadertext",
291 "ui.-moz-colheaderhover",
292 "ui.-moz-colheaderhovertext",
293 "ui.-moz-colheaderactive",
294 "ui.-moz-colheaderactivetext",
295 "ui.textSelectDisabledBackground",
296 "ui.textSelectAttentionBackground",
297 "ui.textSelectAttentionForeground",
298 "ui.textHighlightBackground",
299 "ui.textHighlightForeground",
300 "ui.IMERawInputBackground",
301 "ui.IMERawInputForeground",
302 "ui.IMERawInputUnderline",
303 "ui.IMESelectedRawTextBackground",
304 "ui.IMESelectedRawTextForeground",
305 "ui.IMESelectedRawTextUnderline",
306 "ui.IMEConvertedTextBackground",
307 "ui.IMEConvertedTextForeground",
308 "ui.IMEConvertedTextUnderline",
309 "ui.IMESelectedConvertedTextBackground",
310 "ui.IMESelectedConvertedTextForeground",
311 "ui.IMESelectedConvertedTextUnderline",
312 "ui.SpellCheckerUnderline",
313 "ui.themedScrollbar",
314 "ui.themedScrollbarInactive",
315 "ui.themedScrollbarThumb",
316 "ui.themedScrollbarThumbHover",
317 "ui.themedScrollbarThumbActive",
318 "ui.themedScrollbarThumbInactive",
321 static_assert(ArrayLength(sColorPrefs) == size_t(LookAndFeel::ColorID::End),
322 "Should have a pref for each color value");
324 // This array MUST be kept in the same order as the SystemFont enum.
325 static const char sFontPrefs[][41] = {
326 "ui.font.caption",
327 "ui.font.icon",
328 "ui.font.menu",
329 "ui.font.message-box",
330 "ui.font.small-caption",
331 "ui.font.status-bar",
332 "ui.font.-moz-pull-down-menu",
333 "ui.font.-moz-button",
334 "ui.font.-moz-list",
335 "ui.font.-moz-field",
338 static_assert(ArrayLength(sFontPrefs) == size_t(LookAndFeel::FontID::End),
339 "Should have a pref for each font value");
341 const char* nsXPLookAndFeel::GetColorPrefName(ColorID aId) {
342 return sColorPrefs[size_t(aId)];
345 bool nsXPLookAndFeel::sInitialized = false;
347 nsXPLookAndFeel* nsXPLookAndFeel::sInstance = nullptr;
348 bool nsXPLookAndFeel::sShutdown = false;
350 auto LookAndFeel::SystemZoomSettings() -> ZoomSettings {
351 ZoomSettings settings;
352 switch (StaticPrefs::browser_display_os_zoom_behavior()) {
353 case 0:
354 default:
355 break;
356 case 1:
357 settings.mFullZoom = GetTextScaleFactor();
358 break;
359 case 2:
360 settings.mTextZoom = GetTextScaleFactor();
361 break;
363 return settings;
366 // static
367 nsXPLookAndFeel* nsXPLookAndFeel::GetInstance() {
368 if (sInstance) {
369 return sInstance;
372 NS_ENSURE_TRUE(!sShutdown, nullptr);
374 // If we're in a content process, then the parent process will have supplied
375 // us with an initial FullLookAndFeel object.
376 // We grab this data from the ContentChild,
377 // where it's been temporarily stashed, and initialize our new LookAndFeel
378 // object with it.
380 FullLookAndFeel* lnf = nullptr;
382 if (auto* cc = mozilla::dom::ContentChild::GetSingleton()) {
383 lnf = &cc->BorrowLookAndFeelData();
386 if (lnf) {
387 sInstance = new widget::RemoteLookAndFeel(std::move(*lnf));
388 } else if (gfxPlatform::IsHeadless()) {
389 sInstance = new widget::HeadlessLookAndFeel();
390 } else {
391 sInstance = new nsLookAndFeel();
394 // This is only ever used once during initialization, and can be cleared now.
395 if (lnf) {
396 *lnf = {};
399 widget::Theme::Init();
400 return sInstance;
403 // static
404 void nsXPLookAndFeel::Shutdown() {
405 if (sShutdown) {
406 return;
409 sShutdown = true;
410 delete sInstance;
411 sInstance = nullptr;
413 // This keeps strings alive, so need to clear to make leak checking happy.
414 sFontCache.Clear();
416 widget::Theme::Shutdown();
419 static void IntPrefChanged(const nsACString& aPref) {
420 // Most Int prefs can't change our system colors or fonts, but
421 // ui.systemUsesDarkTheme can, since it affects the effective color-scheme
422 // (affecting system colors).
423 auto changeKind = aPref.EqualsLiteral("ui.systemUsesDarkTheme")
424 ? widget::ThemeChangeKind::Style
425 : widget::ThemeChangeKind::MediaQueriesOnly;
426 LookAndFeel::NotifyChangedAllWindows(changeKind);
429 static void FloatPrefChanged(const nsACString& aPref) {
430 // Most float prefs can't change our system colors or fonts, but
431 // textScaleFactor affects layout.
432 auto changeKind = aPref.EqualsLiteral("ui.textScaleFactor")
433 ? widget::ThemeChangeKind::StyleAndLayout
434 : widget::ThemeChangeKind::MediaQueriesOnly;
435 LookAndFeel::NotifyChangedAllWindows(changeKind);
438 static void ColorPrefChanged() {
439 // Color prefs affect style, because they by definition change system colors.
440 LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind::Style);
443 static void FontPrefChanged() {
444 // Color prefs affect style, because they by definition change system fonts.
445 LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind::Style);
448 // static
449 void nsXPLookAndFeel::OnPrefChanged(const char* aPref, void* aClosure) {
450 nsDependentCString prefName(aPref);
451 for (const char* pref : sIntPrefs) {
452 if (prefName.Equals(pref)) {
453 IntPrefChanged(prefName);
454 return;
458 for (const char* pref : sFloatPrefs) {
459 if (prefName.Equals(pref)) {
460 FloatPrefChanged(prefName);
461 return;
465 for (const char* pref : sColorPrefs) {
466 // We use StringBeginsWith to handle .dark prefs too.
467 if (StringBeginsWith(prefName, nsDependentCString(pref))) {
468 ColorPrefChanged();
469 return;
473 for (const char* pref : sFontPrefs) {
474 if (StringBeginsWith(prefName, nsDependentCString(pref))) {
475 FontPrefChanged();
476 return;
481 static constexpr struct {
482 nsLiteralCString mName;
483 widget::ThemeChangeKind mChangeKind =
484 widget::ThemeChangeKind::MediaQueriesOnly;
485 } kMediaQueryPrefs[] = {
486 // Affects whether standins are used for the accent color.
487 {"widget.non-native-theme.use-theme-accent"_ns,
488 widget::ThemeChangeKind::Style},
489 // These three affect system colors on Windows.
490 {"widget.windows.uwp-system-colors.enabled"_ns,
491 widget::ThemeChangeKind::Style},
492 {"widget.windows.uwp-system-colors.highlight-accent"_ns,
493 widget::ThemeChangeKind::Style},
494 // Affects env().
495 {"layout.css.prefers-color-scheme.content-override"_ns,
496 widget::ThemeChangeKind::Style},
497 // Affects media queries and scrollbar sizes, so gotta relayout.
498 {"widget.gtk.overlay-scrollbars.enabled"_ns,
499 widget::ThemeChangeKind::StyleAndLayout},
500 // Affects zoom settings which includes text and full zoom.
501 {"browser.display.os-zoom-behavior"_ns,
502 widget::ThemeChangeKind::StyleAndLayout},
503 // This affects system colors on Linux.
504 {"widget.gtk.libadwaita-colors.enabled"_ns, widget::ThemeChangeKind::Style},
505 // This affects not only the media query, but also the native theme, so we
506 // need to re-layout.
507 {"browser.theme.toolbar-theme"_ns, widget::ThemeChangeKind::AllBits},
508 {"browser.theme.content-theme"_ns},
511 // Read values from the user's preferences.
512 // This is done once at startup, but since the user's preferences
513 // haven't actually been read yet at that time, we also have to
514 // set a callback to inform us of changes to each pref.
515 void nsXPLookAndFeel::Init() {
516 MOZ_RELEASE_ASSERT(NS_IsMainThread());
518 // Say we're already initialized, and take the chance that it might fail;
519 // protects against some other process writing to our static variables.
520 sInitialized = true;
522 if (XRE_IsParentProcess()) {
523 nsLayoutUtils::RecomputeSmoothScrollDefault();
526 // XXX If we could reorganize the pref names, we should separate the branch
527 // for each types. Then, we could reduce the unnecessary loop from
528 // nsXPLookAndFeel::OnPrefChanged().
529 Preferences::RegisterPrefixCallback(OnPrefChanged, "ui.");
530 // We really do just want the accessibility.tabfocus pref, not other prefs
531 // that start with that string.
532 Preferences::RegisterCallback(OnPrefChanged, "accessibility.tabfocus");
534 for (const auto& pref : kMediaQueryPrefs) {
535 Preferences::RegisterCallback(
536 [](const char*, void* aChangeKind) {
537 auto changeKind =
538 widget::ThemeChangeKind(reinterpret_cast<uintptr_t>(aChangeKind));
539 LookAndFeel::NotifyChangedAllWindows(changeKind);
541 pref.mName, reinterpret_cast<void*>(uintptr_t(pref.mChangeKind)));
545 nsXPLookAndFeel::~nsXPLookAndFeel() {
546 NS_ASSERTION(sInstance == this,
547 "This destroying instance isn't the singleton instance");
548 sInstance = nullptr;
551 static bool IsSpecialColor(LookAndFeel::ColorID aID, nscolor aColor) {
552 using ColorID = LookAndFeel::ColorID;
554 if (aColor == NS_SAME_AS_FOREGROUND_COLOR) {
555 return true;
558 switch (aID) {
559 case ColorID::IMESelectedRawTextBackground:
560 case ColorID::IMESelectedConvertedTextBackground:
561 case ColorID::IMERawInputBackground:
562 case ColorID::IMEConvertedTextBackground:
563 case ColorID::IMESelectedRawTextForeground:
564 case ColorID::IMESelectedConvertedTextForeground:
565 case ColorID::IMERawInputForeground:
566 case ColorID::IMEConvertedTextForeground:
567 case ColorID::IMERawInputUnderline:
568 case ColorID::IMEConvertedTextUnderline:
569 case ColorID::IMESelectedRawTextUnderline:
570 case ColorID::IMESelectedConvertedTextUnderline:
571 case ColorID::SpellCheckerUnderline:
572 return NS_IS_SELECTION_SPECIAL_COLOR(aColor);
573 default:
574 break;
577 * In GetColor(), every color that is not a special color is color
578 * corrected. Use false to make other colors color corrected.
580 return false;
583 nscolor nsXPLookAndFeel::GetStandinForNativeColor(ColorID aID,
584 ColorScheme aScheme) {
585 if (aScheme == ColorScheme::Dark) {
586 if (auto color = GenericDarkColor(aID)) {
587 return *color;
591 // The stand-in colors are taken from what the non-native theme needs (for
592 // field/button colors), the Windows 7 Aero theme except Mac-specific colors
593 // which are taken from Mac OS 10.7.
595 #define COLOR(name_, r, g, b) \
596 case ColorID::name_: \
597 return NS_RGB(r, g, b);
599 #define COLORA(name_, r, g, b, a) \
600 case ColorID::name_: \
601 return NS_RGBA(r, g, b, a);
603 switch (aID) {
604 // These are here for the purposes of headless mode.
605 case ColorID::IMESelectedRawTextBackground:
606 case ColorID::IMESelectedConvertedTextBackground:
607 case ColorID::IMERawInputBackground:
608 case ColorID::IMEConvertedTextBackground:
609 return NS_TRANSPARENT;
610 case ColorID::IMESelectedRawTextForeground:
611 case ColorID::IMESelectedConvertedTextForeground:
612 case ColorID::IMERawInputForeground:
613 case ColorID::IMEConvertedTextForeground:
614 return NS_SAME_AS_FOREGROUND_COLOR;
615 case ColorID::IMERawInputUnderline:
616 case ColorID::IMEConvertedTextUnderline:
617 return NS_40PERCENT_FOREGROUND_COLOR;
618 case ColorID::Accentcolor:
619 return widget::sDefaultAccent.ToABGR();
620 case ColorID::Accentcolortext:
621 return widget::sDefaultAccentText.ToABGR();
622 COLOR(SpellCheckerUnderline, 0xff, 0x00, 0x00)
623 COLOR(TextSelectDisabledBackground, 0xaa, 0xaa, 0xaa)
625 // Titlebar colors
626 COLOR(Activeborder, 0xB4, 0xB4, 0xB4)
627 COLOR(Inactiveborder, 0xB4, 0xB4, 0xB4)
628 COLOR(Activecaption, 0xF0, 0xF0, 0xF4)
629 COLOR(Inactivecaption, 0xF0, 0xF0, 0xF4)
630 COLOR(Captiontext, 0x00, 0x00, 0x00)
631 COLOR(Inactivecaptiontext, 0x00, 0x00, 0x00)
633 // CSS 2 colors:
634 COLOR(Appworkspace, 0xAB, 0xAB, 0xAB)
635 COLOR(Background, 0x00, 0x00, 0x00)
636 COLOR(Buttonhighlight, 0xFF, 0xFF, 0xFF)
637 COLOR(Buttonshadow, 0xA0, 0xA0, 0xA0)
639 // Buttons and comboboxes should be kept in sync since they are drawn with
640 // the same colors by the non-native theme.
641 COLOR(Buttonface, 0xe9, 0xe9, 0xed)
642 COLORA(MozButtondisabledface, 0xe9, 0xe9, 0xed, 128)
644 COLOR(MozCombobox, 0xe9, 0xe9, 0xed)
646 COLOR(Buttontext, 0x00, 0x00, 0x00)
647 COLOR(MozComboboxtext, 0x00, 0x00, 0x00)
649 COLOR(Graytext, 0x6D, 0x6D, 0x6D)
650 COLOR(Highlight, 0x33, 0x99, 0xFF)
651 COLOR(Highlighttext, 0xFF, 0xFF, 0xFF)
652 COLOR(Infobackground, 0xFF, 0xFF, 0xE1)
653 COLOR(Infotext, 0x00, 0x00, 0x00)
654 COLOR(Menu, 0xF0, 0xF0, 0xF0)
655 COLOR(Menutext, 0x00, 0x00, 0x00)
656 COLOR(Scrollbar, 0xC8, 0xC8, 0xC8)
657 COLOR(Threeddarkshadow, 0x69, 0x69, 0x69)
658 COLOR(Threedface, 0xF0, 0xF0, 0xF0)
659 COLOR(Threedhighlight, 0xFF, 0xFF, 0xFF)
660 COLOR(Threedlightshadow, 0xE3, 0xE3, 0xE3)
661 COLOR(Threedshadow, 0xA0, 0xA0, 0xA0)
662 COLOR(Buttonborder, 0xE3, 0xE3, 0xE3)
663 COLOR(Mark, 0xFF, 0xFF, 0x00)
664 COLOR(Marktext, 0x00, 0x00, 0x00)
665 COLOR(Window, 0xFF, 0xFF, 0xFF)
666 COLOR(Windowframe, 0x64, 0x64, 0x64)
667 COLOR(Windowtext, 0x00, 0x00, 0x00)
668 COLOR(Field, 0xFF, 0xFF, 0xFF)
669 COLORA(MozDisabledfield, 0xFF, 0xFF, 0xFF, 128)
670 COLOR(Fieldtext, 0x00, 0x00, 0x00)
671 COLOR(MozDialog, 0xF0, 0xF0, 0xF0)
672 COLOR(MozDialogtext, 0x00, 0x00, 0x00)
673 COLOR(MozColheadertext, 0x00, 0x00, 0x00)
674 COLOR(MozColheaderhovertext, 0x00, 0x00, 0x00)
675 COLOR(MozCellhighlight, 0xF0, 0xF0, 0xF0)
676 COLOR(MozCellhighlighttext, 0x00, 0x00, 0x00)
677 COLOR(Selecteditem, 0x33, 0x99, 0xFF)
678 COLOR(Selecteditemtext, 0xFF, 0xFF, 0xFF)
679 COLOR(MozButtonhoverface, 0xd0, 0xd0, 0xd7)
680 COLOR(MozButtonhovertext, 0x00, 0x00, 0x00)
681 COLOR(MozButtonactiveface, 0xb1, 0xb1, 0xb9)
682 COLOR(MozButtonactivetext, 0x00, 0x00, 0x00)
683 COLOR(MozMenuhover, 0x33, 0x99, 0xFF)
684 COLOR(MozMenuhovertext, 0x00, 0x00, 0x00)
685 COLOR(MozMenubarhovertext, 0x00, 0x00, 0x00)
686 COLOR(MozMenuhoverdisabled, 0xF0, 0xF0, 0xF0)
687 COLOR(MozEventreerow, 0xFF, 0xFF, 0xFF)
688 COLOR(MozOddtreerow, 0xFF, 0xFF, 0xFF)
689 COLOR(MozMacFocusring, 0x60, 0x9D, 0xD7)
690 COLOR(MozMacDisabledtoolbartext, 0x3F, 0x3F, 0x3F)
691 // Seems to be the default color (hardcoded because of bug 1065998)
692 COLOR(MozNativehyperlinktext, 0x00, 0x66, 0xCC)
693 COLOR(MozNativevisitedhyperlinktext, 0x55, 0x1A, 0x8B)
694 default:
695 break;
697 return NS_RGB(0xFF, 0xFF, 0xFF);
700 #undef COLOR
701 #undef COLORA
703 // Taken from in-content/common.inc.css's dark theme.
704 Maybe<nscolor> nsXPLookAndFeel::GenericDarkColor(ColorID aID) {
705 nscolor color = NS_RGB(0, 0, 0);
706 static constexpr nscolor kWindowBackground = NS_RGB(28, 27, 34);
707 static constexpr nscolor kWindowText = NS_RGB(251, 251, 254);
708 switch (aID) {
709 case ColorID::Window: // --in-content-page-background
710 case ColorID::Background:
711 color = kWindowBackground;
712 break;
714 case ColorID::Menu:
715 color = NS_RGB(0x2b, 0x2a, 0x33);
716 break;
718 case ColorID::MozMenuhovertext:
719 case ColorID::MozMenubarhovertext:
720 case ColorID::Menutext:
721 color = NS_RGB(0xfb, 0xfb, 0xfe);
722 break;
724 case ColorID::MozMenuhover:
725 color = NS_RGB(0x52, 0x52, 0x5e);
726 break;
728 case ColorID::MozMenuhoverdisabled:
729 color = NS_RGB(0x3a, 0x39, 0x44);
730 break;
732 case ColorID::MozOddtreerow:
733 case ColorID::MozDialog: // --in-content-box-background
734 color = NS_RGB(35, 34, 43);
735 break;
736 case ColorID::Windowtext: // --in-content-page-color
737 case ColorID::MozDialogtext:
738 case ColorID::MozSidebartext:
739 case ColorID::Fieldtext:
740 case ColorID::Buttontext: // --in-content-button-text-color (via
741 // --in-content-page-color)
742 case ColorID::MozComboboxtext:
743 case ColorID::MozButtonhovertext:
744 case ColorID::MozButtonactivetext:
745 case ColorID::MozHeaderbartext:
746 case ColorID::MozHeaderbarinactivetext:
747 case ColorID::Captiontext:
748 case ColorID::Inactivecaptiontext: // TODO(emilio): Maybe make
749 // Inactivecaptiontext Graytext?
750 case ColorID::MozColheadertext:
751 case ColorID::MozColheaderhovertext:
752 case ColorID::MozColheaderactivetext:
753 color = kWindowText;
754 break;
755 case ColorID::Buttonshadow:
756 case ColorID::Threedshadow:
757 case ColorID::MozSidebarborder:
758 case ColorID::Threedlightshadow:
759 case ColorID::Buttonborder: // --in-content-box-border-color computed
760 // with kWindowText above
761 // kWindowBackground.
762 case ColorID::Graytext: // opacity: 0.4 of kWindowText blended over the
763 // "Window" background color, which happens to be
764 // the same :-)
765 color = NS_ComposeColors(kWindowBackground, NS_RGBA(251, 251, 254, 102));
766 break;
767 case ColorID::MozCellhighlight:
768 case ColorID::Selecteditem: // --in-content-primary-button-background /
769 // --in-content-item-selected
770 color = NS_RGB(0, 221, 255);
771 break;
772 case ColorID::MozSidebar:
773 case ColorID::Field:
774 case ColorID::Buttonface: // --in-content-button-background
775 case ColorID::MozColheader:
776 case ColorID::Threedface:
777 case ColorID::MozCombobox:
778 case ColorID::MozCellhighlighttext:
779 case ColorID::Selecteditemtext: // --in-content-primary-button-text-color /
780 // --in-content-item-selected-text
781 color = NS_RGB(43, 42, 51);
782 break;
783 case ColorID::Threeddarkshadow: // Same as Threedlightshadow but with the
784 // background.
785 case ColorID::MozDisabledfield: // opacity: 0.4 of the face above blended
786 // over the "Window" background color.
787 case ColorID::MozButtondisabledface:
788 color = NS_ComposeColors(kWindowBackground, NS_RGBA(43, 42, 51, 102));
789 break;
790 case ColorID::MozButtonhoverface: // --in-content-button-background-hover
791 case ColorID::MozColheaderhover:
792 color = NS_RGB(82, 82, 94);
793 break;
794 case ColorID::MozButtonactiveface: // --in-content-button-background-active
795 case ColorID::MozColheaderactive:
796 color = NS_RGB(91, 91, 102);
797 break;
798 case ColorID::Highlight:
799 color = NS_RGBA(0, 221, 255, 78);
800 break;
801 case ColorID::Highlighttext:
802 color = NS_SAME_AS_FOREGROUND_COLOR;
803 break;
804 case ColorID::MozNativehyperlinktext:
805 // If you change this color, you probably also want to change the default
806 // value of browser.anchor_color.dark.
807 color = NS_RGB(0x8c, 0x8c, 0xff);
808 break;
809 case ColorID::MozNativevisitedhyperlinktext:
810 // If you change this color, you probably also want to change the default
811 // value of browser.visited_color.dark.
812 color = NS_RGB(0xff, 0xad, 0xff);
813 break;
814 case ColorID::SpellCheckerUnderline:
815 // This is the default for active links in dark mode as well
816 // (browser.active_color.dark). See bug 1755564 for some analysis and
817 // other options too.
818 color = NS_RGB(0xff, 0x66, 0x66);
819 break;
820 case ColorID::Activeborder:
821 case ColorID::Inactiveborder:
822 color = NS_RGB(57, 57, 57);
823 break;
824 case ColorID::MozHeaderbar:
825 case ColorID::MozHeaderbarinactive:
826 case ColorID::Activecaption:
827 case ColorID::Inactivecaption:
828 color = NS_RGB(28, 27, 34);
829 break;
830 default:
831 return Nothing();
833 return Some(color);
836 // Uncomment the #define below if you want to debug system color use in a skin
837 // that uses them. When set, it will make all system color pairs that are
838 // appropriate for foreground/background pairing the same. This means if the
839 // skin is using system colors correctly you will not be able to see *any* text.
841 // #define DEBUG_SYSTEM_COLOR_USE
843 #ifdef DEBUG_SYSTEM_COLOR_USE
844 static nsresult SystemColorUseDebuggingColor(LookAndFeel::ColorID aID,
845 nscolor& aResult) {
846 using ColorID = LookAndFeel::ColorID;
848 switch (aID) {
849 // css2 http://www.w3.org/TR/REC-CSS2/ui.html#system-colors
850 case ColorID::Activecaption:
851 // active window caption background
852 case ColorID::Captiontext:
853 // text in active window caption
854 aResult = NS_RGB(0xff, 0x00, 0x00);
855 break;
857 case ColorID::Highlight:
858 // background of selected item
859 case ColorID::Highlighttext:
860 // text of selected item
861 aResult = NS_RGB(0xff, 0xff, 0x00);
862 break;
864 case ColorID::Inactivecaption:
865 // inactive window caption
866 case ColorID::Inactivecaptiontext:
867 // text in inactive window caption
868 aResult = NS_RGB(0x66, 0x66, 0x00);
869 break;
871 case ColorID::Infobackground:
872 // tooltip background color
873 case ColorID::Infotext:
874 // tooltip text color
875 aResult = NS_RGB(0x00, 0xff, 0x00);
876 break;
878 case ColorID::Menu:
879 // menu background
880 case ColorID::Menutext:
881 // menu text
882 aResult = NS_RGB(0x00, 0xff, 0xff);
883 break;
885 case ColorID::Threedface:
886 case ColorID::Buttonface:
887 // 3-D face color
888 case ColorID::Buttontext:
889 // text on push buttons
890 aResult = NS_RGB(0x00, 0x66, 0x66);
891 break;
893 case ColorID::Window:
894 case ColorID::Windowtext:
895 aResult = NS_RGB(0x00, 0x00, 0xff);
896 break;
898 // from the CSS3 working draft (not yet finalized)
899 // http://www.w3.org/tr/2000/wd-css3-userint-20000216.html#color
901 case ColorID::Field:
902 case ColorID::Fieldtext:
903 aResult = NS_RGB(0xff, 0x00, 0xff);
904 break;
906 case ColorID::MozDialog:
907 case ColorID::MozDialogtext:
908 aResult = NS_RGB(0x66, 0x00, 0x66);
909 break;
911 default:
912 return NS_ERROR_NOT_AVAILABLE;
915 return NS_OK;
917 #endif
919 static nsresult GetPrefColor(const char* aPref, nscolor& aResult) {
920 nsAutoCString colorStr;
921 MOZ_TRY(Preferences::GetCString(aPref, colorStr));
922 if (!ServoCSSParser::ComputeColor(nullptr, NS_RGB(0, 0, 0), colorStr,
923 &aResult)) {
924 return NS_ERROR_FAILURE;
926 return NS_OK;
929 static nsresult GetColorFromPref(LookAndFeel::ColorID aID, ColorScheme aScheme,
930 nscolor& aResult) {
931 const char* prefName = sColorPrefs[size_t(aID)];
932 if (aScheme == ColorScheme::Dark) {
933 nsAutoCString darkPrefName(prefName);
934 darkPrefName.Append(".dark");
935 if (NS_SUCCEEDED(GetPrefColor(darkPrefName.get(), aResult))) {
936 return NS_OK;
939 return GetPrefColor(prefName, aResult);
942 // All these routines will return NS_OK if they have a value,
943 // in which case the nsLookAndFeel should use that value;
944 // otherwise we'll return NS_ERROR_NOT_AVAILABLE, in which case, the
945 // platform-specific nsLookAndFeel should use its own values instead.
946 nsresult nsXPLookAndFeel::GetColorValue(ColorID aID, ColorScheme aScheme,
947 UseStandins aUseStandins,
948 nscolor& aResult) {
949 if (!sInitialized) {
950 Init();
953 #ifdef DEBUG_SYSTEM_COLOR_USE
954 if (NS_SUCCEEDED(SystemColorUseDebuggingColor(aID, aResult))) {
955 return NS_OK;
957 #endif
959 auto& cache = sColorCaches.Get(aScheme, aUseStandins);
960 if (const auto* cached = cache.Get(aID)) {
961 if (cached->isNothing()) {
962 return NS_ERROR_FAILURE;
964 aResult = cached->value();
965 return NS_OK;
968 // NOTE: Servo holds a lock and the main thread is paused, so writing to the
969 // global cache here is fine.
970 auto result = GetUncachedColor(aID, aScheme, aUseStandins);
971 cache.Insert(aID, result);
972 if (!result) {
973 return NS_ERROR_FAILURE;
975 aResult = *result;
976 return NS_OK;
979 Maybe<nscolor> nsXPLookAndFeel::GetUncachedColor(ColorID aID,
980 ColorScheme aScheme,
981 UseStandins aUseStandins) {
982 if (aUseStandins == UseStandins::Yes) {
983 return Some(GetStandinForNativeColor(aID, aScheme));
985 nscolor r;
986 if (NS_SUCCEEDED(GetColorFromPref(aID, aScheme, r))) {
987 return Some(r);
989 if (NS_SUCCEEDED(NativeGetColor(aID, aScheme, r))) {
990 if (gfxPlatform::GetCMSMode() == CMSMode::All && !IsSpecialColor(aID, r)) {
991 qcms_transform* transform = gfxPlatform::GetCMSInverseRGBTransform();
992 if (transform) {
993 uint8_t color[4];
994 color[0] = NS_GET_R(r);
995 color[1] = NS_GET_G(r);
996 color[2] = NS_GET_B(r);
997 color[3] = NS_GET_A(r);
998 qcms_transform_data(transform, color, color, 1);
999 r = NS_RGBA(color[0], color[1], color[2], color[3]);
1003 return Some(r);
1005 return Nothing();
1008 nsresult nsXPLookAndFeel::GetIntValue(IntID aID, int32_t& aResult) {
1009 if (!sInitialized) {
1010 Init();
1013 if (const auto* cached = sIntCache.Get(aID)) {
1014 if (cached->isNothing()) {
1015 return NS_ERROR_FAILURE;
1017 aResult = cached->value();
1018 return NS_OK;
1021 if (NS_SUCCEEDED(Preferences::GetInt(sIntPrefs[size_t(aID)], &aResult))) {
1022 sIntCache.Insert(aID, Some(aResult));
1023 return NS_OK;
1026 if (NS_FAILED(NativeGetInt(aID, aResult))) {
1027 sIntCache.Insert(aID, Nothing());
1028 return NS_ERROR_FAILURE;
1031 sIntCache.Insert(aID, Some(aResult));
1032 return NS_OK;
1035 nsresult nsXPLookAndFeel::GetFloatValue(FloatID aID, float& aResult) {
1036 if (!sInitialized) {
1037 Init();
1040 if (const auto* cached = sFloatCache.Get(aID)) {
1041 if (cached->isNothing()) {
1042 return NS_ERROR_FAILURE;
1044 aResult = cached->value();
1045 return NS_OK;
1048 int32_t pref = 0;
1049 if (NS_SUCCEEDED(Preferences::GetInt(sFloatPrefs[size_t(aID)], &pref))) {
1050 aResult = float(pref) / 100.0f;
1051 sFloatCache.Insert(aID, Some(aResult));
1052 return NS_OK;
1055 if (NS_FAILED(NativeGetFloat(aID, aResult))) {
1056 sFloatCache.Insert(aID, Nothing());
1057 return NS_ERROR_FAILURE;
1060 sFloatCache.Insert(aID, Some(aResult));
1061 return NS_OK;
1064 bool nsXPLookAndFeel::LookAndFeelFontToStyle(const LookAndFeelFont& aFont,
1065 nsString& aName,
1066 gfxFontStyle& aStyle) {
1067 if (!aFont.haveFont()) {
1068 return false;
1070 aName = aFont.name();
1071 aStyle = gfxFontStyle();
1072 aStyle.size = aFont.size();
1073 aStyle.weight = FontWeight::FromInt(aFont.weight());
1074 aStyle.style =
1075 aFont.italic() ? FontSlantStyle::ITALIC : FontSlantStyle::NORMAL;
1076 aStyle.systemFont = true;
1077 return true;
1080 widget::LookAndFeelFont nsXPLookAndFeel::StyleToLookAndFeelFont(
1081 const nsAString& aName, const gfxFontStyle& aStyle) {
1082 LookAndFeelFont font;
1083 font.haveFont() = true;
1084 font.name() = aName;
1085 font.size() = aStyle.size;
1086 font.weight() = aStyle.weight.ToFloat();
1087 font.italic() = aStyle.style.IsItalic();
1088 MOZ_ASSERT(aStyle.style.IsNormal() || aStyle.style.IsItalic(),
1089 "Cannot handle oblique font style");
1090 #ifdef DEBUG
1092 // Assert that all the remaining font style properties have their
1093 // default values.
1094 gfxFontStyle candidate = aStyle;
1095 gfxFontStyle defaults{};
1096 candidate.size = defaults.size;
1097 candidate.weight = defaults.weight;
1098 candidate.style = defaults.style;
1099 MOZ_ASSERT(candidate.Equals(defaults),
1100 "Some font style properties not supported");
1102 #endif
1103 return font;
1106 bool nsXPLookAndFeel::GetFontValue(FontID aID, nsString& aName,
1107 gfxFontStyle& aStyle) {
1108 if (const LookAndFeelFont* cached = sFontCache.Get(aID)) {
1109 return LookAndFeelFontToStyle(*cached, aName, aStyle);
1112 LookAndFeelFont font;
1113 auto GetFontsFromPrefs = [&]() -> bool {
1114 nsDependentCString pref(sFontPrefs[size_t(aID)]);
1115 if (NS_FAILED(Preferences::GetString(pref.get(), aName))) {
1116 return false;
1118 font.haveFont() = true;
1119 font.name() = aName;
1120 font.size() = Preferences::GetFloat(nsAutoCString(pref + ".size"_ns).get());
1121 // This is written this way rather than using the fallback so that an empty
1122 // pref (such like the one about:config creates) doesn't cause system fonts
1123 // to have zero-size.
1124 if (font.size() < 1.0f) {
1125 font.size() = StyleFONT_MEDIUM_PX;
1127 font.weight() = Preferences::GetFloat(
1128 nsAutoCString(pref + ".weight"_ns).get(), FontWeight::NORMAL.ToFloat());
1129 font.italic() =
1130 Preferences::GetBool(nsAutoCString(pref + ".italic"_ns).get());
1131 return true;
1134 if (GetFontsFromPrefs()) {
1135 LookAndFeelFontToStyle(font, aName, aStyle);
1136 } else if (NativeGetFont(aID, aName, aStyle)) {
1137 font = StyleToLookAndFeelFont(aName, aStyle);
1138 } else {
1139 MOZ_ASSERT(!font.haveFont());
1141 bool success = font.haveFont();
1142 sFontCache.Insert(aID, std::move(font));
1143 return success;
1146 void nsXPLookAndFeel::RefreshImpl() {
1147 // Wipe out our caches.
1148 sColorCaches.Clear();
1149 sFontCache.Clear();
1150 sFloatCache.Clear();
1151 sIntCache.Clear();
1153 if (XRE_IsParentProcess()) {
1154 nsLayoutUtils::RecomputeSmoothScrollDefault();
1155 // Clear any cached FullLookAndFeel data, which is now invalid.
1156 widget::RemoteLookAndFeel::ClearCachedData();
1160 static bool sRecordedLookAndFeelTelemetry = false;
1162 void nsXPLookAndFeel::RecordTelemetry() {
1163 if (!XRE_IsParentProcess()) {
1164 return;
1167 if (sRecordedLookAndFeelTelemetry) {
1168 return;
1171 sRecordedLookAndFeelTelemetry = true;
1173 int32_t i;
1174 Telemetry::ScalarSet(
1175 Telemetry::ScalarID::WIDGET_DARK_MODE,
1176 NS_SUCCEEDED(GetIntValue(IntID::SystemUsesDarkTheme, i)) && i != 0);
1178 RecordLookAndFeelSpecificTelemetry();
1181 namespace mozilla {
1183 bool LookAndFeel::sGlobalThemeChanged;
1184 static widget::ThemeChangeKind sGlobalThemeChangeKind{0};
1186 void LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind aKind) {
1187 sGlobalThemeChanged = true;
1188 sGlobalThemeChangeKind |= aKind;
1190 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
1191 const char16_t kind[] = {char16_t(aKind), 0};
1192 obs->NotifyObservers(nullptr, "internal-look-and-feel-changed", kind);
1196 void LookAndFeel::DoHandleGlobalThemeChange() {
1197 MOZ_ASSERT(sGlobalThemeChanged);
1198 sGlobalThemeChanged = false;
1199 auto kind = std::exchange(sGlobalThemeChangeKind, widget::ThemeChangeKind(0));
1201 // Tell the theme that it changed, so it can flush any handles to stale theme
1202 // data.
1204 // We can use the *DoNotUseDirectly functions directly here, because we want
1205 // to notify all possible themes in a given process (but just once).
1206 if (XRE_IsParentProcess() ||
1207 !StaticPrefs::widget_non_native_theme_enabled()) {
1208 if (nsCOMPtr<nsITheme> theme = do_GetNativeThemeDoNotUseDirectly()) {
1209 theme->ThemeChanged();
1212 if (nsCOMPtr<nsITheme> theme = do_GetBasicNativeThemeDoNotUseDirectly()) {
1213 theme->ThemeChanged();
1216 // Clear all cached LookAndFeel colors.
1217 LookAndFeel::Refresh();
1219 // Reset default background and foreground colors for the document since they
1220 // may be using system colors, color scheme, etc.
1221 PreferenceSheet::Refresh();
1223 // Vector images (SVG) may be using theme colors so we discard all cached
1224 // surfaces. (We could add a vector image only version of DiscardAll, but
1225 // in bug 940625 we decided theme changes are rare enough not to bother.)
1226 image::SurfaceCacheUtils::DiscardAll();
1228 if (XRE_IsParentProcess()) {
1229 dom::ContentParent::BroadcastThemeUpdate(kind);
1232 nsContentUtils::AddScriptRunner(
1233 NS_NewRunnableFunction("HandleGlobalThemeChange", [] {
1234 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
1235 obs->NotifyObservers(nullptr, "look-and-feel-changed", nullptr);
1237 }));
1240 #define BIT_FOR(_c) (1ull << size_t(ColorID::_c))
1242 // We want to use a non-native color scheme for the non-native theme (except in
1243 // high-contrast mode), so spoof some of the colors with stand-ins to prevent
1244 // lack of contrast.
1245 static constexpr std::bitset<size_t(ColorID::End)> sNonNativeThemeStandinColors{
1246 // Used by default button styles.
1247 BIT_FOR(Buttonface) | BIT_FOR(Buttontext) | BIT_FOR(MozButtonhoverface) |
1248 BIT_FOR(MozButtonhovertext) | BIT_FOR(MozButtonactiveface) |
1249 BIT_FOR(MozButtonactivetext) | BIT_FOR(MozButtondisabledface) |
1250 BIT_FOR(Buttonborder) |
1251 // Used by select elements.
1252 BIT_FOR(MozCombobox) | BIT_FOR(MozComboboxtext) |
1253 BIT_FOR(Threedlightshadow) |
1254 // For symmetry with the above.
1255 BIT_FOR(Threeddarkshadow) |
1256 // Used by fieldset borders.
1257 BIT_FOR(Threedface) |
1258 // Used by input / textarea.
1259 BIT_FOR(Field) | BIT_FOR(Fieldtext) |
1260 // Used by disabled form controls.
1261 BIT_FOR(MozDisabledfield) | BIT_FOR(Graytext) |
1262 // Some pages expect these to return windows-like colors, see bug 1773795.
1263 // Also, per spec these should match Canvas/CanvasText, see
1264 // https://drafts.csswg.org/css-color-4/#window
1265 BIT_FOR(Window) | BIT_FOR(Windowtext)};
1266 #undef BIT_FOR
1268 static bool ShouldUseStandinsForNativeColorForNonNativeTheme(
1269 const dom::Document& aDoc, LookAndFeel::ColorID aColor,
1270 const PreferenceSheet::Prefs& aPrefs) {
1271 const bool shouldUseStandinsForColor = [&] {
1272 if (sNonNativeThemeStandinColors[size_t(aColor)]) {
1273 return true;
1275 // There are platforms where we want the content-exposed accent color to be
1276 // the windows blue rather than the system accent color, for now.
1277 return !StaticPrefs::widget_non_native_theme_use_theme_accent() &&
1278 (aColor == LookAndFeel::ColorID::Accentcolor ||
1279 aColor == LookAndFeel::ColorID::Accentcolortext);
1280 }();
1282 return shouldUseStandinsForColor && aDoc.ShouldAvoidNativeTheme() &&
1283 !aPrefs.NonNativeThemeShouldBeHighContrast();
1286 bool LookAndFeel::IsDarkColor(nscolor aColor) {
1287 // Given https://www.w3.org/TR/WCAG20/#contrast-ratiodef, this is the
1288 // threshold that tells us whether contrast is better against white or black.
1290 // Contrast ratio against black is: (L + 0.05) / 0.05
1291 // Contrast ratio against white is: 1.05 / (L + 0.05)
1293 // So the intersection is:
1295 // (L + 0.05) / 0.05 = 1.05 / (L + 0.05)
1297 // And the solution to that equation is:
1299 // sqrt(1.05 * 0.05) - 0.05
1301 // So we consider a color dark if the contrast is below this threshold, and
1302 // it's at least half-opaque.
1303 constexpr float kThreshold = 0.179129;
1304 return NS_GET_A(aColor) > 127 &&
1305 RelativeLuminanceUtils::Compute(aColor) < kThreshold;
1308 ColorScheme LookAndFeel::ColorSchemeForStyle(
1309 const dom::Document& aDoc, const StyleColorSchemeFlags& aFlags,
1310 ColorSchemeMode aMode) {
1311 const auto& prefs = PreferenceSheet::PrefsFor(aDoc);
1312 StyleColorSchemeFlags style(aFlags);
1313 if (!style) {
1314 style._0 = aDoc.GetColorSchemeBits();
1316 const bool supportsDark = bool(style & StyleColorSchemeFlags::DARK);
1317 const bool supportsLight = bool(style & StyleColorSchemeFlags::LIGHT);
1318 if (supportsLight && supportsDark) {
1319 // Both color-schemes are explicitly supported, use the preferred one.
1320 return aDoc.PreferredColorScheme();
1322 if (supportsDark || supportsLight) {
1323 // One color-scheme is explicitly supported and one isn't, so use the one
1324 // the content supports.
1325 return supportsDark ? ColorScheme::Dark : ColorScheme::Light;
1327 // No value specified. Chrome docs, and forced-colors mode always supports
1328 // both, so use the preferred color-scheme.
1329 if (aMode == ColorSchemeMode::Preferred || aDoc.ChromeRulesEnabled() ||
1330 !prefs.mUseDocumentColors) {
1331 return aDoc.PreferredColorScheme();
1333 // Otherwise default content to light.
1334 return ColorScheme::Light;
1337 LookAndFeel::ColorScheme LookAndFeel::ColorSchemeForFrame(
1338 const nsIFrame* aFrame, ColorSchemeMode aMode) {
1339 return ColorSchemeForStyle(*aFrame->PresContext()->Document(),
1340 aFrame->StyleUI()->mColorScheme.bits, aMode);
1343 // static
1344 Maybe<nscolor> LookAndFeel::GetColor(ColorID aId, ColorScheme aScheme,
1345 UseStandins aUseStandins) {
1346 nscolor result;
1347 nsresult rv = nsLookAndFeel::GetInstance()->GetColorValue(
1348 aId, aScheme, aUseStandins, result);
1349 if (NS_FAILED(rv)) {
1350 return Nothing();
1352 return Some(result);
1355 // Returns whether there is a CSS color name for this color.
1356 static bool ColorIsCSSAccessible(LookAndFeel::ColorID aId) {
1357 using ColorID = LookAndFeel::ColorID;
1359 switch (aId) {
1360 case ColorID::TextSelectDisabledBackground:
1361 case ColorID::TextSelectAttentionBackground:
1362 case ColorID::TextSelectAttentionForeground:
1363 case ColorID::TextHighlightBackground:
1364 case ColorID::TextHighlightForeground:
1365 case ColorID::ThemedScrollbar:
1366 case ColorID::ThemedScrollbarInactive:
1367 case ColorID::ThemedScrollbarThumb:
1368 case ColorID::ThemedScrollbarThumbActive:
1369 case ColorID::ThemedScrollbarThumbInactive:
1370 case ColorID::ThemedScrollbarThumbHover:
1371 case ColorID::IMERawInputBackground:
1372 case ColorID::IMERawInputForeground:
1373 case ColorID::IMERawInputUnderline:
1374 case ColorID::IMESelectedRawTextBackground:
1375 case ColorID::IMESelectedRawTextForeground:
1376 case ColorID::IMESelectedRawTextUnderline:
1377 case ColorID::IMEConvertedTextBackground:
1378 case ColorID::IMEConvertedTextForeground:
1379 case ColorID::IMEConvertedTextUnderline:
1380 case ColorID::IMESelectedConvertedTextBackground:
1381 case ColorID::IMESelectedConvertedTextForeground:
1382 case ColorID::IMESelectedConvertedTextUnderline:
1383 case ColorID::SpellCheckerUnderline:
1384 return false;
1385 default:
1386 break;
1389 return true;
1392 LookAndFeel::UseStandins LookAndFeel::ShouldUseStandins(
1393 const dom::Document& aDoc, ColorID aId) {
1394 const auto& prefs = PreferenceSheet::PrefsFor(aDoc);
1395 if (ShouldUseStandinsForNativeColorForNonNativeTheme(aDoc, aId, prefs)) {
1396 return UseStandins::Yes;
1398 if (prefs.mUseStandins && ColorIsCSSAccessible(aId)) {
1399 return UseStandins::Yes;
1401 return UseStandins::No;
1404 Maybe<nscolor> LookAndFeel::GetColor(ColorID aId, const nsIFrame* aFrame) {
1405 const auto* doc = aFrame->PresContext()->Document();
1406 return GetColor(aId, ColorSchemeForFrame(aFrame),
1407 ShouldUseStandins(*doc, aId));
1410 // static
1411 nsresult LookAndFeel::GetInt(IntID aID, int32_t* aResult) {
1412 return nsLookAndFeel::GetInstance()->GetIntValue(aID, *aResult);
1415 // static
1416 nsresult LookAndFeel::GetFloat(FloatID aID, float* aResult) {
1417 return nsLookAndFeel::GetInstance()->GetFloatValue(aID, *aResult);
1420 // static
1421 bool LookAndFeel::GetFont(FontID aID, nsString& aName, gfxFontStyle& aStyle) {
1422 return nsLookAndFeel::GetInstance()->GetFontValue(aID, aName, aStyle);
1425 // static
1426 char16_t LookAndFeel::GetPasswordCharacter() {
1427 return nsLookAndFeel::GetInstance()->GetPasswordCharacterImpl();
1430 // static
1431 bool LookAndFeel::GetEchoPassword() {
1432 if (StaticPrefs::editor_password_mask_delay() >= 0) {
1433 return StaticPrefs::editor_password_mask_delay() > 0;
1435 return nsLookAndFeel::GetInstance()->GetEchoPasswordImpl();
1438 // static
1439 uint32_t LookAndFeel::GetPasswordMaskDelay() {
1440 int32_t delay = StaticPrefs::editor_password_mask_delay();
1441 if (delay < 0) {
1442 return nsLookAndFeel::GetInstance()->GetPasswordMaskDelayImpl();
1444 return delay;
1447 bool LookAndFeel::DrawInTitlebar() {
1448 switch (StaticPrefs::browser_tabs_inTitlebar()) {
1449 case 0:
1450 return false;
1451 case 1:
1452 return true;
1453 default:
1454 break;
1456 return nsLookAndFeel::GetInstance()->GetDefaultDrawInTitlebar();
1459 void LookAndFeel::GetThemeInfo(nsACString& aOut) {
1460 nsLookAndFeel::GetInstance()->GetThemeInfo(aOut);
1463 uint32_t LookAndFeel::GetMenuAccessKey() {
1464 return StaticPrefs::ui_key_menuAccessKey();
1467 Modifiers LookAndFeel::GetMenuAccessKeyModifiers() {
1468 switch (GetMenuAccessKey()) {
1469 case dom::KeyboardEvent_Binding::DOM_VK_SHIFT:
1470 return MODIFIER_SHIFT;
1471 case dom::KeyboardEvent_Binding::DOM_VK_CONTROL:
1472 return MODIFIER_CONTROL;
1473 case dom::KeyboardEvent_Binding::DOM_VK_ALT:
1474 return MODIFIER_ALT;
1475 case dom::KeyboardEvent_Binding::DOM_VK_META:
1476 case dom::KeyboardEvent_Binding::DOM_VK_WIN:
1477 return MODIFIER_META;
1478 default:
1479 return 0;
1483 // static
1484 void LookAndFeel::Refresh() {
1485 nsLookAndFeel::GetInstance()->RefreshImpl();
1486 widget::Theme::LookAndFeelChanged();
1489 // static
1490 void LookAndFeel::NativeInit() { nsLookAndFeel::GetInstance()->NativeInit(); }
1492 // static
1493 void LookAndFeel::SetData(widget::FullLookAndFeel&& aTables) {
1494 nsLookAndFeel::GetInstance()->SetDataImpl(std::move(aTables));
1497 } // namespace mozilla