Bug 1861709 replace AudioCallbackDriver::ThreadRunning() assertions that mean to...
[gecko.git] / widget / nsXPLookAndFeel.cpp
blob86ec2eb8f8bb2eff61c0ebb68c8bf21e56532805
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",
194 static_assert(ArrayLength(sIntPrefs) == size_t(LookAndFeel::IntID::End),
195 "Should have a pref for each int value");
197 // This array MUST be kept in the same order as the float id list in
198 // LookAndFeel.h
199 // clang-format off
200 static const char sFloatPrefs[][37] = {
201 "ui.IMEUnderlineRelativeSize",
202 "ui.SpellCheckerUnderlineRelativeSize",
203 "ui.caretAspectRatio",
204 "ui.textScaleFactor",
205 "ui.cursorScale",
207 // clang-format on
209 static_assert(ArrayLength(sFloatPrefs) == size_t(LookAndFeel::FloatID::End),
210 "Should have a pref for each float value");
212 // This array MUST be kept in the same order as the color list in
213 // specified/color.rs
214 static const char sColorPrefs[][41] = {
215 "ui.activeborder",
216 "ui.activecaption",
217 "ui.appworkspace",
218 "ui.background",
219 "ui.buttonface",
220 "ui.buttonhighlight",
221 "ui.buttonshadow",
222 "ui.buttontext",
223 "ui.buttonborder",
224 "ui.captiontext",
225 "ui.-moz-field",
226 "ui.-moz-disabledfield",
227 "ui.-moz-fieldtext",
228 "ui.mark",
229 "ui.marktext",
230 "ui.-moz-comboboxtext",
231 "ui.-moz-combobox",
232 "ui.graytext",
233 "ui.highlight",
234 "ui.highlighttext",
235 "ui.inactiveborder",
236 "ui.inactivecaption",
237 "ui.inactivecaptiontext",
238 "ui.infobackground",
239 "ui.infotext",
240 "ui.menu",
241 "ui.menutext",
242 "ui.scrollbar",
243 "ui.threeddarkshadow",
244 "ui.threedface",
245 "ui.threedhighlight",
246 "ui.threedlightshadow",
247 "ui.threedshadow",
248 "ui.window",
249 "ui.windowframe",
250 "ui.windowtext",
251 "ui.-moz-default-color",
252 "ui.-moz-default-background-color",
253 "ui.-moz-dialog",
254 "ui.-moz-dialogtext",
255 "ui.-moz-cellhighlight",
256 "ui.-moz_cellhighlighttext",
257 "ui.selecteditem",
258 "ui.selecteditemtext",
259 "ui.-moz-buttonhoverface",
260 "ui.-moz_buttonhovertext",
261 "ui.-moz_menuhover",
262 "ui.-moz_menuhoverdisabled",
263 "ui.-moz_menuhovertext",
264 "ui.-moz_menubarhovertext",
265 "ui.-moz_eventreerow",
266 "ui.-moz_oddtreerow",
267 "ui.-moz-buttonactivetext",
268 "ui.-moz-buttonactiveface",
269 "ui.-moz-buttondisabledface",
270 "ui.-moz-headerbar",
271 "ui.-moz-headerbartext",
272 "ui.-moz-headerbarinactive",
273 "ui.-moz-headerbarinactivetext",
274 "ui.-moz-mac-defaultbuttontext",
275 "ui.-moz-mac-focusring",
276 "ui.-moz_mac_disabledtoolbartext",
277 "ui.-moz-mac-menupopup",
278 "ui.-moz-mac-menuitem",
279 "ui.-moz-mac-active-menuitem",
280 "ui.-moz-mac-source-list",
281 "ui.-moz-mac-source-list-selection",
282 "ui.-moz-mac-active-source-list-selection",
283 "ui.-moz-mac-tooltip",
284 "ui.accentcolor",
285 "ui.accentcolortext",
286 "ui.-moz-autofill-background",
287 "ui.-moz-nativehyperlinktext",
288 "ui.-moz-nativevisitedhyperlinktext",
289 "ui.-moz-hyperlinktext",
290 "ui.-moz-activehyperlinktext",
291 "ui.-moz-visitedhyperlinktext",
292 "ui.-moz-colheadertext",
293 "ui.-moz-colheaderhovertext",
294 "ui.textSelectDisabledBackground",
295 "ui.textSelectAttentionBackground",
296 "ui.textSelectAttentionForeground",
297 "ui.textHighlightBackground",
298 "ui.textHighlightForeground",
299 "ui.IMERawInputBackground",
300 "ui.IMERawInputForeground",
301 "ui.IMERawInputUnderline",
302 "ui.IMESelectedRawTextBackground",
303 "ui.IMESelectedRawTextForeground",
304 "ui.IMESelectedRawTextUnderline",
305 "ui.IMEConvertedTextBackground",
306 "ui.IMEConvertedTextForeground",
307 "ui.IMEConvertedTextUnderline",
308 "ui.IMESelectedConvertedTextBackground",
309 "ui.IMESelectedConvertedTextForeground",
310 "ui.IMESelectedConvertedTextUnderline",
311 "ui.SpellCheckerUnderline",
312 "ui.themedScrollbar",
313 "ui.themedScrollbarInactive",
314 "ui.themedScrollbarThumb",
315 "ui.themedScrollbarThumbHover",
316 "ui.themedScrollbarThumbActive",
317 "ui.themedScrollbarThumbInactive",
320 static_assert(ArrayLength(sColorPrefs) == size_t(LookAndFeel::ColorID::End),
321 "Should have a pref for each color value");
323 // This array MUST be kept in the same order as the SystemFont enum.
324 static const char sFontPrefs[][41] = {
325 "ui.font.caption",
326 "ui.font.icon",
327 "ui.font.menu",
328 "ui.font.message-box",
329 "ui.font.small-caption",
330 "ui.font.status-bar",
331 "ui.font.-moz-pull-down-menu",
332 "ui.font.-moz-button",
333 "ui.font.-moz-list",
334 "ui.font.-moz-field",
337 static_assert(ArrayLength(sFontPrefs) == size_t(LookAndFeel::FontID::End),
338 "Should have a pref for each font value");
340 const char* nsXPLookAndFeel::GetColorPrefName(ColorID aId) {
341 return sColorPrefs[size_t(aId)];
344 bool nsXPLookAndFeel::sInitialized = false;
346 nsXPLookAndFeel* nsXPLookAndFeel::sInstance = nullptr;
347 bool nsXPLookAndFeel::sShutdown = false;
349 auto LookAndFeel::SystemZoomSettings() -> ZoomSettings {
350 ZoomSettings settings;
351 switch (StaticPrefs::browser_display_os_zoom_behavior()) {
352 case 0:
353 default:
354 break;
355 case 1:
356 settings.mFullZoom = GetTextScaleFactor();
357 break;
358 case 2:
359 settings.mTextZoom = GetTextScaleFactor();
360 break;
362 return settings;
365 // static
366 nsXPLookAndFeel* nsXPLookAndFeel::GetInstance() {
367 if (sInstance) {
368 return sInstance;
371 NS_ENSURE_TRUE(!sShutdown, nullptr);
373 // If we're in a content process, then the parent process will have supplied
374 // us with an initial FullLookAndFeel object.
375 // We grab this data from the ContentChild,
376 // where it's been temporarily stashed, and initialize our new LookAndFeel
377 // object with it.
379 FullLookAndFeel* lnf = nullptr;
381 if (auto* cc = mozilla::dom::ContentChild::GetSingleton()) {
382 lnf = &cc->BorrowLookAndFeelData();
385 if (lnf) {
386 sInstance = new widget::RemoteLookAndFeel(std::move(*lnf));
387 } else if (gfxPlatform::IsHeadless()) {
388 sInstance = new widget::HeadlessLookAndFeel();
389 } else {
390 sInstance = new nsLookAndFeel();
393 // This is only ever used once during initialization, and can be cleared now.
394 if (lnf) {
395 *lnf = {};
398 widget::Theme::Init();
399 return sInstance;
402 // static
403 void nsXPLookAndFeel::Shutdown() {
404 if (sShutdown) {
405 return;
408 sShutdown = true;
409 delete sInstance;
410 sInstance = nullptr;
412 // This keeps strings alive, so need to clear to make leak checking happy.
413 sFontCache.Clear();
415 widget::Theme::Shutdown();
418 static void IntPrefChanged(const nsACString& aPref) {
419 // Most Int prefs can't change our system colors or fonts, but
420 // ui.systemUsesDarkTheme can, since it affects the effective color-scheme
421 // (affecting system colors).
422 auto changeKind = aPref.EqualsLiteral("ui.systemUsesDarkTheme")
423 ? widget::ThemeChangeKind::Style
424 : widget::ThemeChangeKind::MediaQueriesOnly;
425 LookAndFeel::NotifyChangedAllWindows(changeKind);
428 static void FloatPrefChanged(const nsACString& aPref) {
429 // Most float prefs can't change our system colors or fonts, but
430 // textScaleFactor affects layout.
431 auto changeKind = aPref.EqualsLiteral("ui.textScaleFactor")
432 ? widget::ThemeChangeKind::StyleAndLayout
433 : widget::ThemeChangeKind::MediaQueriesOnly;
434 LookAndFeel::NotifyChangedAllWindows(changeKind);
437 static void ColorPrefChanged() {
438 // Color prefs affect style, because they by definition change system colors.
439 LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind::Style);
442 static void FontPrefChanged() {
443 // Color prefs affect style, because they by definition change system fonts.
444 LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind::Style);
447 // static
448 void nsXPLookAndFeel::OnPrefChanged(const char* aPref, void* aClosure) {
449 nsDependentCString prefName(aPref);
450 for (const char* pref : sIntPrefs) {
451 if (prefName.Equals(pref)) {
452 IntPrefChanged(prefName);
453 return;
457 for (const char* pref : sFloatPrefs) {
458 if (prefName.Equals(pref)) {
459 FloatPrefChanged(prefName);
460 return;
464 for (const char* pref : sColorPrefs) {
465 // We use StringBeginsWith to handle .dark prefs too.
466 if (StringBeginsWith(prefName, nsDependentCString(pref))) {
467 ColorPrefChanged();
468 return;
472 for (const char* pref : sFontPrefs) {
473 if (StringBeginsWith(prefName, nsDependentCString(pref))) {
474 FontPrefChanged();
475 return;
480 static constexpr struct {
481 nsLiteralCString mName;
482 widget::ThemeChangeKind mChangeKind =
483 widget::ThemeChangeKind::MediaQueriesOnly;
484 } kMediaQueryPrefs[] = {
485 // Affects whether standins are used for the accent color.
486 {"widget.non-native-theme.use-theme-accent"_ns,
487 widget::ThemeChangeKind::Style},
488 // These three affect system colors on Windows.
489 {"widget.windows.uwp-system-colors.enabled"_ns,
490 widget::ThemeChangeKind::Style},
491 {"widget.windows.uwp-system-colors.highlight-accent"_ns,
492 widget::ThemeChangeKind::Style},
493 // Affects env().
494 {"layout.css.prefers-color-scheme.content-override"_ns,
495 widget::ThemeChangeKind::Style},
496 // Affects media queries and scrollbar sizes, so gotta relayout.
497 {"widget.gtk.overlay-scrollbars.enabled"_ns,
498 widget::ThemeChangeKind::StyleAndLayout},
499 // Affects zoom settings which includes text and full zoom.
500 {"browser.display.os-zoom-behavior"_ns,
501 widget::ThemeChangeKind::StyleAndLayout},
502 // This affects not only the media query, but also the native theme, so we
503 // need to re-layout.
504 {"browser.theme.toolbar-theme"_ns, widget::ThemeChangeKind::AllBits},
505 {"browser.theme.content-theme"_ns},
508 // Read values from the user's preferences.
509 // This is done once at startup, but since the user's preferences
510 // haven't actually been read yet at that time, we also have to
511 // set a callback to inform us of changes to each pref.
512 void nsXPLookAndFeel::Init() {
513 MOZ_RELEASE_ASSERT(NS_IsMainThread());
515 // Say we're already initialized, and take the chance that it might fail;
516 // protects against some other process writing to our static variables.
517 sInitialized = true;
519 RecomputeColorSchemes();
521 if (XRE_IsParentProcess()) {
522 nsLayoutUtils::RecomputeSmoothScrollDefault();
525 // XXX If we could reorganize the pref names, we should separate the branch
526 // for each types. Then, we could reduce the unnecessary loop from
527 // nsXPLookAndFeel::OnPrefChanged().
528 Preferences::RegisterPrefixCallback(OnPrefChanged, "ui.");
529 // We really do just want the accessibility.tabfocus pref, not other prefs
530 // that start with that string.
531 Preferences::RegisterCallback(OnPrefChanged, "accessibility.tabfocus");
533 for (const auto& pref : kMediaQueryPrefs) {
534 Preferences::RegisterCallback(
535 [](const char*, void* aChangeKind) {
536 auto changeKind =
537 widget::ThemeChangeKind(reinterpret_cast<uintptr_t>(aChangeKind));
538 LookAndFeel::NotifyChangedAllWindows(changeKind);
540 pref.mName, reinterpret_cast<void*>(uintptr_t(pref.mChangeKind)));
544 nsXPLookAndFeel::~nsXPLookAndFeel() {
545 NS_ASSERTION(sInstance == this,
546 "This destroying instance isn't the singleton instance");
547 sInstance = nullptr;
550 static bool IsSpecialColor(LookAndFeel::ColorID aID, nscolor aColor) {
551 using ColorID = LookAndFeel::ColorID;
553 if (aColor == NS_SAME_AS_FOREGROUND_COLOR) {
554 return true;
557 switch (aID) {
558 case ColorID::IMESelectedRawTextBackground:
559 case ColorID::IMESelectedConvertedTextBackground:
560 case ColorID::IMERawInputBackground:
561 case ColorID::IMEConvertedTextBackground:
562 case ColorID::IMESelectedRawTextForeground:
563 case ColorID::IMESelectedConvertedTextForeground:
564 case ColorID::IMERawInputForeground:
565 case ColorID::IMEConvertedTextForeground:
566 case ColorID::IMERawInputUnderline:
567 case ColorID::IMEConvertedTextUnderline:
568 case ColorID::IMESelectedRawTextUnderline:
569 case ColorID::IMESelectedConvertedTextUnderline:
570 case ColorID::SpellCheckerUnderline:
571 return NS_IS_SELECTION_SPECIAL_COLOR(aColor);
572 default:
573 break;
576 * In GetColor(), every color that is not a special color is color
577 * corrected. Use false to make other colors color corrected.
579 return false;
582 nscolor nsXPLookAndFeel::GetStandinForNativeColor(ColorID aID,
583 ColorScheme aScheme) {
584 if (aScheme == ColorScheme::Dark) {
585 if (auto color = GenericDarkColor(aID)) {
586 return *color;
590 // The stand-in colors are taken from what the non-native theme needs (for
591 // field/button colors), the Windows 7 Aero theme except Mac-specific colors
592 // which are taken from Mac OS 10.7.
594 #define COLOR(name_, r, g, b) \
595 case ColorID::name_: \
596 return NS_RGB(r, g, b);
598 #define COLORA(name_, r, g, b, a) \
599 case ColorID::name_: \
600 return NS_RGBA(r, g, b, a);
602 switch (aID) {
603 // These are here for the purposes of headless mode.
604 case ColorID::IMESelectedRawTextBackground:
605 case ColorID::IMESelectedConvertedTextBackground:
606 case ColorID::IMERawInputBackground:
607 case ColorID::IMEConvertedTextBackground:
608 return NS_TRANSPARENT;
609 case ColorID::IMESelectedRawTextForeground:
610 case ColorID::IMESelectedConvertedTextForeground:
611 case ColorID::IMERawInputForeground:
612 case ColorID::IMEConvertedTextForeground:
613 return NS_SAME_AS_FOREGROUND_COLOR;
614 case ColorID::IMERawInputUnderline:
615 case ColorID::IMEConvertedTextUnderline:
616 return NS_40PERCENT_FOREGROUND_COLOR;
617 case ColorID::Accentcolor:
618 return widget::sDefaultAccent.ToABGR();
619 case ColorID::Accentcolortext:
620 return widget::sDefaultAccentText.ToABGR();
621 COLOR(SpellCheckerUnderline, 0xff, 0x00, 0x00)
622 COLOR(TextSelectDisabledBackground, 0xaa, 0xaa, 0xaa)
624 // Titlebar colors
625 COLOR(Activeborder, 0xB4, 0xB4, 0xB4)
626 COLOR(Inactiveborder, 0xB4, 0xB4, 0xB4)
627 COLOR(Activecaption, 0xF0, 0xF0, 0xF4)
628 COLOR(Inactivecaption, 0xF0, 0xF0, 0xF4)
629 COLOR(Captiontext, 0x00, 0x00, 0x00)
630 COLOR(Inactivecaptiontext, 0x00, 0x00, 0x00)
632 // CSS 2 colors:
633 COLOR(Appworkspace, 0xAB, 0xAB, 0xAB)
634 COLOR(Background, 0x00, 0x00, 0x00)
635 COLOR(Buttonhighlight, 0xFF, 0xFF, 0xFF)
636 COLOR(Buttonshadow, 0xA0, 0xA0, 0xA0)
638 // Buttons and comboboxes should be kept in sync since they are drawn with
639 // the same colors by the non-native theme.
640 COLOR(Buttonface, 0xe9, 0xe9, 0xed)
641 COLORA(MozButtondisabledface, 0xe9, 0xe9, 0xed, 128)
643 COLOR(MozCombobox, 0xe9, 0xe9, 0xed)
645 COLOR(Buttontext, 0x00, 0x00, 0x00)
646 COLOR(MozComboboxtext, 0x00, 0x00, 0x00)
648 COLOR(Graytext, 0x6D, 0x6D, 0x6D)
649 COLOR(Highlight, 0x33, 0x99, 0xFF)
650 COLOR(Highlighttext, 0xFF, 0xFF, 0xFF)
651 COLOR(Infobackground, 0xFF, 0xFF, 0xE1)
652 COLOR(Infotext, 0x00, 0x00, 0x00)
653 COLOR(Menu, 0xF0, 0xF0, 0xF0)
654 COLOR(Menutext, 0x00, 0x00, 0x00)
655 COLOR(Scrollbar, 0xC8, 0xC8, 0xC8)
656 COLOR(Threeddarkshadow, 0x69, 0x69, 0x69)
657 COLOR(Threedface, 0xF0, 0xF0, 0xF0)
658 COLOR(Threedhighlight, 0xFF, 0xFF, 0xFF)
659 COLOR(Threedlightshadow, 0xE3, 0xE3, 0xE3)
660 COLOR(Threedshadow, 0xA0, 0xA0, 0xA0)
661 COLOR(Buttonborder, 0xE3, 0xE3, 0xE3)
662 COLOR(Mark, 0xFF, 0xFF, 0x00)
663 COLOR(Marktext, 0x00, 0x00, 0x00)
664 COLOR(Window, 0xFF, 0xFF, 0xFF)
665 COLOR(Windowframe, 0x64, 0x64, 0x64)
666 COLOR(Windowtext, 0x00, 0x00, 0x00)
667 COLOR(Field, 0xFF, 0xFF, 0xFF)
668 COLORA(MozDisabledfield, 0xFF, 0xFF, 0xFF, 128)
669 COLOR(Fieldtext, 0x00, 0x00, 0x00)
670 COLOR(MozDialog, 0xF0, 0xF0, 0xF0)
671 COLOR(MozDialogtext, 0x00, 0x00, 0x00)
672 COLOR(MozColheadertext, 0x00, 0x00, 0x00)
673 COLOR(MozColheaderhovertext, 0x00, 0x00, 0x00)
674 COLOR(MozCellhighlight, 0xF0, 0xF0, 0xF0)
675 COLOR(MozCellhighlighttext, 0x00, 0x00, 0x00)
676 COLOR(Selecteditem, 0x33, 0x99, 0xFF)
677 COLOR(Selecteditemtext, 0xFF, 0xFF, 0xFF)
678 COLOR(MozButtonhoverface, 0xd0, 0xd0, 0xd7)
679 COLOR(MozButtonhovertext, 0x00, 0x00, 0x00)
680 COLOR(MozButtonactiveface, 0xb1, 0xb1, 0xb9)
681 COLOR(MozButtonactivetext, 0x00, 0x00, 0x00)
682 COLOR(MozMenuhover, 0x33, 0x99, 0xFF)
683 COLOR(MozMenuhovertext, 0x00, 0x00, 0x00)
684 COLOR(MozMenubarhovertext, 0x00, 0x00, 0x00)
685 COLOR(MozMenuhoverdisabled, 0xF0, 0xF0, 0xF0)
686 COLOR(MozEventreerow, 0xFF, 0xFF, 0xFF)
687 COLOR(MozOddtreerow, 0xFF, 0xFF, 0xFF)
688 COLOR(MozMacFocusring, 0x60, 0x9D, 0xD7)
689 COLOR(MozMacDisabledtoolbartext, 0x3F, 0x3F, 0x3F)
690 COLOR(MozMacMenupopup, 0xe6, 0xe6, 0xe6)
691 COLOR(MozMacMenuitem, 0xe6, 0xe6, 0xe6)
692 COLOR(MozMacActiveMenuitem, 0x0a, 0x64, 0xdc)
693 COLOR(MozMacSourceList, 0xf7, 0xf7, 0xf7)
694 COLOR(MozMacSourceListSelection, 0xc8, 0xc8, 0xc8)
695 COLOR(MozMacActiveSourceListSelection, 0x0a, 0x64, 0xdc)
696 COLOR(MozMacTooltip, 0xf7, 0xf7, 0xf7)
697 // Seems to be the default color (hardcoded because of bug 1065998)
698 COLOR(MozNativehyperlinktext, 0x00, 0x66, 0xCC)
699 COLOR(MozNativevisitedhyperlinktext, 0x55, 0x1A, 0x8B)
700 default:
701 break;
703 return NS_RGB(0xFF, 0xFF, 0xFF);
706 #undef COLOR
707 #undef COLORA
709 // Taken from in-content/common.inc.css's dark theme.
710 Maybe<nscolor> nsXPLookAndFeel::GenericDarkColor(ColorID aID) {
711 nscolor color = NS_RGB(0, 0, 0);
712 static constexpr nscolor kWindowBackground = NS_RGB(28, 27, 34);
713 static constexpr nscolor kWindowText = NS_RGB(251, 251, 254);
714 switch (aID) {
715 case ColorID::Window: // --in-content-page-background
716 case ColorID::Background:
717 color = kWindowBackground;
718 break;
720 case ColorID::Menu:
721 color = NS_RGB(0x2b, 0x2a, 0x33);
722 break;
724 case ColorID::MozMenuhovertext:
725 case ColorID::MozMenubarhovertext:
726 case ColorID::Menutext:
727 color = NS_RGB(0xfb, 0xfb, 0xfe);
728 break;
730 case ColorID::MozMenuhover:
731 color = NS_RGB(0x52, 0x52, 0x5e);
732 break;
734 case ColorID::MozMenuhoverdisabled:
735 color = NS_RGB(0x3a, 0x39, 0x44);
736 break;
738 case ColorID::MozOddtreerow:
739 case ColorID::MozDialog: // --in-content-box-background
740 color = NS_RGB(35, 34, 43);
741 break;
742 case ColorID::Windowtext: // --in-content-page-color
743 case ColorID::MozDialogtext:
744 case ColorID::Fieldtext:
745 case ColorID::Buttontext: // --in-content-button-text-color (via
746 // --in-content-page-color)
747 case ColorID::MozComboboxtext:
748 case ColorID::MozButtonhovertext:
749 case ColorID::MozButtonactivetext:
750 case ColorID::MozHeaderbartext:
751 case ColorID::MozHeaderbarinactivetext:
752 case ColorID::Captiontext:
753 case ColorID::Inactivecaptiontext: // TODO(emilio): Maybe make
754 // Inactivecaptiontext Graytext?
755 color = kWindowText;
756 break;
757 case ColorID::Buttonshadow:
758 case ColorID::Threedshadow:
759 case ColorID::Threedlightshadow:
760 case ColorID::Buttonborder: // --in-content-box-border-color computed
761 // with kWindowText above
762 // kWindowBackground.
763 case ColorID::Graytext: // opacity: 0.4 of kWindowText blended over the
764 // "Window" background color, which happens to be
765 // the same :-)
766 color = NS_ComposeColors(kWindowBackground, NS_RGBA(251, 251, 254, 102));
767 break;
768 case ColorID::MozCellhighlight:
769 case ColorID::Selecteditem: // --in-content-primary-button-background /
770 // --in-content-item-selected
771 color = NS_RGB(0, 221, 255);
772 break;
773 case ColorID::Field:
774 case ColorID::Buttonface: // --in-content-button-background
775 case ColorID::Threedface:
776 case ColorID::MozCombobox:
777 case ColorID::MozCellhighlighttext:
778 case ColorID::Selecteditemtext: // --in-content-primary-button-text-color /
779 // --in-content-item-selected-text
780 color = NS_RGB(43, 42, 51);
781 break;
782 case ColorID::Threeddarkshadow: // Same as Threedlightshadow but with the
783 // background.
784 case ColorID::MozDisabledfield: // opacity: 0.4 of the face above blended
785 // over the "Window" background color.
786 case ColorID::MozButtondisabledface:
787 color = NS_ComposeColors(kWindowBackground, NS_RGBA(43, 42, 51, 102));
788 break;
789 case ColorID::MozButtonhoverface: // --in-content-button-background-hover
790 color = NS_RGB(82, 82, 94);
791 break;
792 case ColorID::MozButtonactiveface: // --in-content-button-background-active
793 color = NS_RGB(91, 91, 102);
794 break;
795 case ColorID::Highlight:
796 color = NS_RGBA(0, 221, 255, 78);
797 break;
798 case ColorID::Highlighttext:
799 color = NS_SAME_AS_FOREGROUND_COLOR;
800 break;
801 case ColorID::MozNativehyperlinktext:
802 // If you change this color, you probably also want to change the default
803 // value of browser.anchor_color.dark.
804 color = NS_RGB(0x8c, 0x8c, 0xff);
805 break;
806 case ColorID::MozNativevisitedhyperlinktext:
807 // If you change this color, you probably also want to change the default
808 // value of browser.visited_color.dark.
809 color = NS_RGB(0xff, 0xad, 0xff);
810 break;
811 case ColorID::SpellCheckerUnderline:
812 // This is the default for active links in dark mode as well
813 // (browser.active_color.dark). See bug 1755564 for some analysis and
814 // other options too.
815 color = NS_RGB(0xff, 0x66, 0x66);
816 break;
817 case ColorID::Activeborder:
818 case ColorID::Inactiveborder:
819 color = NS_RGB(57, 57, 57);
820 break;
821 case ColorID::MozHeaderbar:
822 case ColorID::MozHeaderbarinactive:
823 case ColorID::Activecaption:
824 case ColorID::Inactivecaption:
825 color = NS_RGB(28, 27, 34);
826 break;
827 default:
828 return Nothing();
830 return Some(color);
833 // Uncomment the #define below if you want to debug system color use in a skin
834 // that uses them. When set, it will make all system color pairs that are
835 // appropriate for foreground/background pairing the same. This means if the
836 // skin is using system colors correctly you will not be able to see *any* text.
838 // #define DEBUG_SYSTEM_COLOR_USE
840 #ifdef DEBUG_SYSTEM_COLOR_USE
841 static nsresult SystemColorUseDebuggingColor(LookAndFeel::ColorID aID,
842 nscolor& aResult) {
843 using ColorID = LookAndFeel::ColorID;
845 switch (aID) {
846 // css2 http://www.w3.org/TR/REC-CSS2/ui.html#system-colors
847 case ColorID::Activecaption:
848 // active window caption background
849 case ColorID::Captiontext:
850 // text in active window caption
851 aResult = NS_RGB(0xff, 0x00, 0x00);
852 break;
854 case ColorID::Highlight:
855 // background of selected item
856 case ColorID::Highlighttext:
857 // text of selected item
858 aResult = NS_RGB(0xff, 0xff, 0x00);
859 break;
861 case ColorID::Inactivecaption:
862 // inactive window caption
863 case ColorID::Inactivecaptiontext:
864 // text in inactive window caption
865 aResult = NS_RGB(0x66, 0x66, 0x00);
866 break;
868 case ColorID::Infobackground:
869 // tooltip background color
870 case ColorID::Infotext:
871 // tooltip text color
872 aResult = NS_RGB(0x00, 0xff, 0x00);
873 break;
875 case ColorID::Menu:
876 // menu background
877 case ColorID::Menutext:
878 // menu text
879 aResult = NS_RGB(0x00, 0xff, 0xff);
880 break;
882 case ColorID::Threedface:
883 case ColorID::Buttonface:
884 // 3-D face color
885 case ColorID::Buttontext:
886 // text on push buttons
887 aResult = NS_RGB(0x00, 0x66, 0x66);
888 break;
890 case ColorID::Window:
891 case ColorID::Windowtext:
892 aResult = NS_RGB(0x00, 0x00, 0xff);
893 break;
895 // from the CSS3 working draft (not yet finalized)
896 // http://www.w3.org/tr/2000/wd-css3-userint-20000216.html#color
898 case ColorID::Field:
899 case ColorID::Fieldtext:
900 aResult = NS_RGB(0xff, 0x00, 0xff);
901 break;
903 case ColorID::MozDialog:
904 case ColorID::MozDialogtext:
905 aResult = NS_RGB(0x66, 0x00, 0x66);
906 break;
908 default:
909 return NS_ERROR_NOT_AVAILABLE;
912 return NS_OK;
914 #endif
916 static nsresult GetPrefColor(const char* aPref, nscolor& aResult) {
917 nsAutoCString colorStr;
918 MOZ_TRY(Preferences::GetCString(aPref, colorStr));
919 if (!ServoCSSParser::ComputeColor(nullptr, NS_RGB(0, 0, 0), colorStr,
920 &aResult)) {
921 return NS_ERROR_FAILURE;
923 return NS_OK;
926 static nsresult GetColorFromPref(LookAndFeel::ColorID aID, ColorScheme aScheme,
927 nscolor& aResult) {
928 const char* prefName = sColorPrefs[size_t(aID)];
929 if (aScheme == ColorScheme::Dark) {
930 nsAutoCString darkPrefName(prefName);
931 darkPrefName.Append(".dark");
932 if (NS_SUCCEEDED(GetPrefColor(darkPrefName.get(), aResult))) {
933 return NS_OK;
936 return GetPrefColor(prefName, aResult);
939 // All these routines will return NS_OK if they have a value,
940 // in which case the nsLookAndFeel should use that value;
941 // otherwise we'll return NS_ERROR_NOT_AVAILABLE, in which case, the
942 // platform-specific nsLookAndFeel should use its own values instead.
943 nsresult nsXPLookAndFeel::GetColorValue(ColorID aID, ColorScheme aScheme,
944 UseStandins aUseStandins,
945 nscolor& aResult) {
946 if (!sInitialized) {
947 Init();
950 #ifdef DEBUG_SYSTEM_COLOR_USE
951 if (NS_SUCCEEDED(SystemColorUseDebuggingColor(aID, aResult))) {
952 return NS_OK;
954 #endif
956 auto& cache = sColorCaches.Get(aScheme, aUseStandins);
957 if (const auto* cached = cache.Get(aID)) {
958 if (cached->isNothing()) {
959 return NS_ERROR_FAILURE;
961 aResult = cached->value();
962 return NS_OK;
965 // NOTE: Servo holds a lock and the main thread is paused, so writing to the
966 // global cache here is fine.
967 auto result = GetUncachedColor(aID, aScheme, aUseStandins);
968 cache.Insert(aID, result);
969 if (!result) {
970 return NS_ERROR_FAILURE;
972 aResult = *result;
973 return NS_OK;
976 Maybe<nscolor> nsXPLookAndFeel::GetUncachedColor(ColorID aID,
977 ColorScheme aScheme,
978 UseStandins aUseStandins) {
979 if (aUseStandins == UseStandins::Yes) {
980 return Some(GetStandinForNativeColor(aID, aScheme));
982 nscolor r;
983 if (NS_SUCCEEDED(GetColorFromPref(aID, aScheme, r))) {
984 return Some(r);
986 if (NS_SUCCEEDED(NativeGetColor(aID, aScheme, r))) {
987 if (gfxPlatform::GetCMSMode() == CMSMode::All && !IsSpecialColor(aID, r)) {
988 qcms_transform* transform = gfxPlatform::GetCMSInverseRGBTransform();
989 if (transform) {
990 uint8_t color[4];
991 color[0] = NS_GET_R(r);
992 color[1] = NS_GET_G(r);
993 color[2] = NS_GET_B(r);
994 color[3] = NS_GET_A(r);
995 qcms_transform_data(transform, color, color, 1);
996 r = NS_RGBA(color[0], color[1], color[2], color[3]);
1000 return Some(r);
1002 return Nothing();
1005 nsresult nsXPLookAndFeel::GetIntValue(IntID aID, int32_t& aResult) {
1006 if (!sInitialized) {
1007 Init();
1010 if (const auto* cached = sIntCache.Get(aID)) {
1011 if (cached->isNothing()) {
1012 return NS_ERROR_FAILURE;
1014 aResult = cached->value();
1015 return NS_OK;
1018 if (NS_SUCCEEDED(Preferences::GetInt(sIntPrefs[size_t(aID)], &aResult))) {
1019 sIntCache.Insert(aID, Some(aResult));
1020 return NS_OK;
1023 if (NS_FAILED(NativeGetInt(aID, aResult))) {
1024 sIntCache.Insert(aID, Nothing());
1025 return NS_ERROR_FAILURE;
1028 sIntCache.Insert(aID, Some(aResult));
1029 return NS_OK;
1032 nsresult nsXPLookAndFeel::GetFloatValue(FloatID aID, float& aResult) {
1033 if (!sInitialized) {
1034 Init();
1037 if (const auto* cached = sFloatCache.Get(aID)) {
1038 if (cached->isNothing()) {
1039 return NS_ERROR_FAILURE;
1041 aResult = cached->value();
1042 return NS_OK;
1045 int32_t pref = 0;
1046 if (NS_SUCCEEDED(Preferences::GetInt(sFloatPrefs[size_t(aID)], &pref))) {
1047 aResult = float(pref) / 100.0f;
1048 sFloatCache.Insert(aID, Some(aResult));
1049 return NS_OK;
1052 if (NS_FAILED(NativeGetFloat(aID, aResult))) {
1053 sFloatCache.Insert(aID, Nothing());
1054 return NS_ERROR_FAILURE;
1057 sFloatCache.Insert(aID, Some(aResult));
1058 return NS_OK;
1061 bool nsXPLookAndFeel::LookAndFeelFontToStyle(const LookAndFeelFont& aFont,
1062 nsString& aName,
1063 gfxFontStyle& aStyle) {
1064 if (!aFont.haveFont()) {
1065 return false;
1067 aName = aFont.name();
1068 aStyle = gfxFontStyle();
1069 aStyle.size = aFont.size();
1070 aStyle.weight = FontWeight::FromInt(aFont.weight());
1071 aStyle.style =
1072 aFont.italic() ? FontSlantStyle::ITALIC : FontSlantStyle::NORMAL;
1073 aStyle.systemFont = true;
1074 return true;
1077 widget::LookAndFeelFont nsXPLookAndFeel::StyleToLookAndFeelFont(
1078 const nsAString& aName, const gfxFontStyle& aStyle) {
1079 LookAndFeelFont font;
1080 font.haveFont() = true;
1081 font.name() = aName;
1082 font.size() = aStyle.size;
1083 font.weight() = aStyle.weight.ToFloat();
1084 font.italic() = aStyle.style.IsItalic();
1085 MOZ_ASSERT(aStyle.style.IsNormal() || aStyle.style.IsItalic(),
1086 "Cannot handle oblique font style");
1087 #ifdef DEBUG
1089 // Assert that all the remaining font style properties have their
1090 // default values.
1091 gfxFontStyle candidate = aStyle;
1092 gfxFontStyle defaults{};
1093 candidate.size = defaults.size;
1094 candidate.weight = defaults.weight;
1095 candidate.style = defaults.style;
1096 MOZ_ASSERT(candidate.Equals(defaults),
1097 "Some font style properties not supported");
1099 #endif
1100 return font;
1103 bool nsXPLookAndFeel::GetFontValue(FontID aID, nsString& aName,
1104 gfxFontStyle& aStyle) {
1105 if (const LookAndFeelFont* cached = sFontCache.Get(aID)) {
1106 return LookAndFeelFontToStyle(*cached, aName, aStyle);
1109 LookAndFeelFont font;
1110 auto GetFontsFromPrefs = [&]() -> bool {
1111 nsDependentCString pref(sFontPrefs[size_t(aID)]);
1112 if (NS_FAILED(Preferences::GetString(pref.get(), aName))) {
1113 return false;
1115 font.haveFont() = true;
1116 font.name() = aName;
1117 font.size() = Preferences::GetFloat(nsAutoCString(pref + ".size"_ns).get());
1118 // This is written this way rather than using the fallback so that an empty
1119 // pref (such like the one about:config creates) doesn't cause system fonts
1120 // to have zero-size.
1121 if (font.size() < 1.0f) {
1122 font.size() = StyleFONT_MEDIUM_PX;
1124 font.weight() = Preferences::GetFloat(
1125 nsAutoCString(pref + ".weight"_ns).get(), FontWeight::NORMAL.ToFloat());
1126 font.italic() =
1127 Preferences::GetBool(nsAutoCString(pref + ".italic"_ns).get());
1128 return true;
1131 if (GetFontsFromPrefs()) {
1132 LookAndFeelFontToStyle(font, aName, aStyle);
1133 } else if (NativeGetFont(aID, aName, aStyle)) {
1134 font = StyleToLookAndFeelFont(aName, aStyle);
1135 } else {
1136 MOZ_ASSERT(!font.haveFont());
1138 bool success = font.haveFont();
1139 sFontCache.Insert(aID, std::move(font));
1140 return success;
1143 void nsXPLookAndFeel::RefreshImpl() {
1144 // Wipe out our caches.
1145 sColorCaches.Clear();
1146 sFontCache.Clear();
1147 sFloatCache.Clear();
1148 sIntCache.Clear();
1149 RecomputeColorSchemes();
1151 if (XRE_IsParentProcess()) {
1152 nsLayoutUtils::RecomputeSmoothScrollDefault();
1153 // Clear any cached FullLookAndFeel data, which is now invalid.
1154 widget::RemoteLookAndFeel::ClearCachedData();
1158 static bool sRecordedLookAndFeelTelemetry = false;
1160 void nsXPLookAndFeel::RecordTelemetry() {
1161 if (!XRE_IsParentProcess()) {
1162 return;
1165 if (sRecordedLookAndFeelTelemetry) {
1166 return;
1169 sRecordedLookAndFeelTelemetry = true;
1171 int32_t i;
1172 Telemetry::ScalarSet(
1173 Telemetry::ScalarID::WIDGET_DARK_MODE,
1174 NS_SUCCEEDED(GetIntValue(IntID::SystemUsesDarkTheme, i)) && i != 0);
1176 RecordLookAndFeelSpecificTelemetry();
1179 namespace mozilla {
1181 static widget::ThemeChangeKind sGlobalThemeChangeKind{0};
1183 void LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind aKind) {
1184 sGlobalThemeChanged = true;
1185 sGlobalThemeChangeKind |= aKind;
1187 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
1188 const char16_t kind[] = {char16_t(aKind), 0};
1189 obs->NotifyObservers(nullptr, "internal-look-and-feel-changed", kind);
1193 void LookAndFeel::DoHandleGlobalThemeChange() {
1194 MOZ_ASSERT(sGlobalThemeChanged);
1195 sGlobalThemeChanged = false;
1196 auto kind = std::exchange(sGlobalThemeChangeKind, widget::ThemeChangeKind(0));
1198 // Tell the theme that it changed, so it can flush any handles to stale theme
1199 // data.
1201 // We can use the *DoNotUseDirectly functions directly here, because we want
1202 // to notify all possible themes in a given process (but just once).
1203 if (XRE_IsParentProcess() ||
1204 !StaticPrefs::widget_non_native_theme_enabled()) {
1205 if (nsCOMPtr<nsITheme> theme = do_GetNativeThemeDoNotUseDirectly()) {
1206 theme->ThemeChanged();
1209 if (nsCOMPtr<nsITheme> theme = do_GetBasicNativeThemeDoNotUseDirectly()) {
1210 theme->ThemeChanged();
1213 // Clear all cached LookAndFeel colors.
1214 LookAndFeel::Refresh();
1216 // Reset default background and foreground colors for the document since they
1217 // may be using system colors.
1218 PreferenceSheet::Refresh();
1220 // Vector images (SVG) may be using theme colors so we discard all cached
1221 // surfaces. (We could add a vector image only version of DiscardAll, but
1222 // in bug 940625 we decided theme changes are rare enough not to bother.)
1223 image::SurfaceCacheUtils::DiscardAll();
1225 if (XRE_IsParentProcess()) {
1226 dom::ContentParent::BroadcastThemeUpdate(kind);
1229 nsContentUtils::AddScriptRunner(
1230 NS_NewRunnableFunction("HandleGlobalThemeChange", [] {
1231 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
1232 obs->NotifyObservers(nullptr, "look-and-feel-changed", nullptr);
1234 }));
1237 #define BIT_FOR(_c) (1ull << size_t(ColorID::_c))
1239 // We want to use a non-native color scheme for the non-native theme (except in
1240 // high-contrast mode), so spoof some of the colors with stand-ins to prevent
1241 // lack of contrast.
1242 static constexpr std::bitset<size_t(ColorID::End)> sNonNativeThemeStandinColors{
1243 // Used by default button styles.
1244 BIT_FOR(Buttonface) | BIT_FOR(Buttontext) | BIT_FOR(MozButtonhoverface) |
1245 BIT_FOR(MozButtonhovertext) | BIT_FOR(MozButtonactiveface) |
1246 BIT_FOR(MozButtonactivetext) | BIT_FOR(MozButtondisabledface) |
1247 BIT_FOR(Buttonborder) |
1248 // Used by select elements.
1249 BIT_FOR(MozCombobox) | BIT_FOR(MozComboboxtext) |
1250 BIT_FOR(Threedlightshadow) |
1251 // For symmetry with the above.
1252 BIT_FOR(Threeddarkshadow) |
1253 // Used by fieldset borders.
1254 BIT_FOR(Threedface) |
1255 // Used by input / textarea.
1256 BIT_FOR(Field) | BIT_FOR(Fieldtext) |
1257 // Used by disabled form controls.
1258 BIT_FOR(MozDisabledfield) | BIT_FOR(Graytext) |
1259 // Some pages expect these to return windows-like colors, see bug 1773795.
1260 // Also, per spec these should match Canvas/CanvasText, see
1261 // https://drafts.csswg.org/css-color-4/#window
1262 BIT_FOR(Window) | BIT_FOR(Windowtext)};
1263 #undef BIT_FOR
1265 static bool ShouldUseStandinsForNativeColorForNonNativeTheme(
1266 const dom::Document& aDoc, LookAndFeel::ColorID aColor,
1267 const PreferenceSheet::Prefs& aPrefs) {
1268 const bool shouldUseStandinsForColor = [&] {
1269 if (sNonNativeThemeStandinColors[size_t(aColor)]) {
1270 return true;
1272 // There are platforms where we want the content-exposed accent color to be
1273 // the windows blue rather than the system accent color, for now.
1274 return !StaticPrefs::widget_non_native_theme_use_theme_accent() &&
1275 (aColor == LookAndFeel::ColorID::Accentcolor ||
1276 aColor == LookAndFeel::ColorID::Accentcolortext);
1277 }();
1279 return shouldUseStandinsForColor && aDoc.ShouldAvoidNativeTheme() &&
1280 !aPrefs.NonNativeThemeShouldBeHighContrast();
1283 ColorScheme LookAndFeel::sChromeColorScheme;
1284 ColorScheme LookAndFeel::sContentColorScheme;
1285 bool LookAndFeel::sColorSchemeInitialized;
1286 bool LookAndFeel::sGlobalThemeChanged;
1288 bool LookAndFeel::IsDarkColor(nscolor aColor) {
1289 // Given https://www.w3.org/TR/WCAG20/#contrast-ratiodef, this is the
1290 // threshold that tells us whether contrast is better against white or black.
1292 // Contrast ratio against black is: (L + 0.05) / 0.05
1293 // Contrast ratio against white is: 1.05 / (L + 0.05)
1295 // So the intersection is:
1297 // (L + 0.05) / 0.05 = 1.05 / (L + 0.05)
1299 // And the solution to that equation is:
1301 // sqrt(1.05 * 0.05) - 0.05
1303 // So we consider a color dark if the contrast is below this threshold, and
1304 // it's at least half-opaque.
1305 constexpr float kThreshold = 0.179129;
1306 return NS_GET_A(aColor) > 127 &&
1307 RelativeLuminanceUtils::Compute(aColor) < kThreshold;
1310 auto LookAndFeel::ColorSchemeSettingForChrome() -> ChromeColorSchemeSetting {
1311 switch (StaticPrefs::browser_theme_toolbar_theme()) {
1312 case 0: // Dark
1313 return ChromeColorSchemeSetting::Dark;
1314 case 1: // Light
1315 return ChromeColorSchemeSetting::Light;
1316 default:
1317 return ChromeColorSchemeSetting::System;
1321 ColorScheme LookAndFeel::ThemeDerivedColorSchemeForContent() {
1322 switch (StaticPrefs::browser_theme_content_theme()) {
1323 case 0: // Dark
1324 return ColorScheme::Dark;
1325 case 1: // Light
1326 return ColorScheme::Light;
1327 default:
1328 return SystemColorScheme();
1332 void LookAndFeel::RecomputeColorSchemes() {
1333 sColorSchemeInitialized = true;
1335 sChromeColorScheme = [] {
1336 switch (ColorSchemeSettingForChrome()) {
1337 case ChromeColorSchemeSetting::Light:
1338 return ColorScheme::Light;
1339 case ChromeColorSchemeSetting::Dark:
1340 return ColorScheme::Dark;
1341 case ChromeColorSchemeSetting::System:
1342 break;
1344 return SystemColorScheme();
1345 }();
1347 sContentColorScheme = [] {
1348 switch (StaticPrefs::layout_css_prefers_color_scheme_content_override()) {
1349 case 0:
1350 return ColorScheme::Dark;
1351 case 1:
1352 return ColorScheme::Light;
1353 default:
1354 return ThemeDerivedColorSchemeForContent();
1356 }();
1359 ColorScheme LookAndFeel::ColorSchemeForStyle(
1360 const dom::Document& aDoc, const StyleColorSchemeFlags& aFlags,
1361 ColorSchemeMode aMode) {
1362 using Choice = PreferenceSheet::Prefs::ColorSchemeChoice;
1364 const auto& prefs = PreferenceSheet::PrefsFor(aDoc);
1365 switch (prefs.mColorSchemeChoice) {
1366 case Choice::Standard:
1367 break;
1368 case Choice::UserPreferred:
1369 return aDoc.PreferredColorScheme();
1370 case Choice::Light:
1371 return ColorScheme::Light;
1372 case Choice::Dark:
1373 return ColorScheme::Dark;
1376 StyleColorSchemeFlags style(aFlags);
1377 if (!style) {
1378 style._0 = aDoc.GetColorSchemeBits();
1380 const bool supportsDark = bool(style & StyleColorSchemeFlags::DARK);
1381 const bool supportsLight = bool(style & StyleColorSchemeFlags::LIGHT);
1382 if (supportsLight && supportsDark) {
1383 // Both color-schemes are explicitly supported, use the preferred one.
1384 return aDoc.PreferredColorScheme();
1386 if (supportsDark || supportsLight) {
1387 // One color-scheme is explicitly supported and one isn't, so use the one
1388 // the content supports.
1389 return supportsDark ? ColorScheme::Dark : ColorScheme::Light;
1391 // No value specified. Chrome docs always supports both, so use the preferred
1392 // color-scheme.
1393 if (aMode == ColorSchemeMode::Preferred || aDoc.ChromeRulesEnabled()) {
1394 return aDoc.PreferredColorScheme();
1396 // Default content to light.
1397 return ColorScheme::Light;
1400 LookAndFeel::ColorScheme LookAndFeel::ColorSchemeForFrame(
1401 const nsIFrame* aFrame, ColorSchemeMode aMode) {
1402 return ColorSchemeForStyle(*aFrame->PresContext()->Document(),
1403 aFrame->StyleUI()->mColorScheme.bits, aMode);
1406 // static
1407 Maybe<nscolor> LookAndFeel::GetColor(ColorID aId, ColorScheme aScheme,
1408 UseStandins aUseStandins) {
1409 nscolor result;
1410 nsresult rv = nsLookAndFeel::GetInstance()->GetColorValue(
1411 aId, aScheme, aUseStandins, result);
1412 if (NS_FAILED(rv)) {
1413 return Nothing();
1415 return Some(result);
1418 // Returns whether there is a CSS color name for this color.
1419 static bool ColorIsCSSAccessible(LookAndFeel::ColorID aId) {
1420 using ColorID = LookAndFeel::ColorID;
1422 switch (aId) {
1423 case ColorID::TextSelectDisabledBackground:
1424 case ColorID::TextSelectAttentionBackground:
1425 case ColorID::TextSelectAttentionForeground:
1426 case ColorID::TextHighlightBackground:
1427 case ColorID::TextHighlightForeground:
1428 case ColorID::ThemedScrollbar:
1429 case ColorID::ThemedScrollbarInactive:
1430 case ColorID::ThemedScrollbarThumb:
1431 case ColorID::ThemedScrollbarThumbActive:
1432 case ColorID::ThemedScrollbarThumbInactive:
1433 case ColorID::ThemedScrollbarThumbHover:
1434 case ColorID::IMERawInputBackground:
1435 case ColorID::IMERawInputForeground:
1436 case ColorID::IMERawInputUnderline:
1437 case ColorID::IMESelectedRawTextBackground:
1438 case ColorID::IMESelectedRawTextForeground:
1439 case ColorID::IMESelectedRawTextUnderline:
1440 case ColorID::IMEConvertedTextBackground:
1441 case ColorID::IMEConvertedTextForeground:
1442 case ColorID::IMEConvertedTextUnderline:
1443 case ColorID::IMESelectedConvertedTextBackground:
1444 case ColorID::IMESelectedConvertedTextForeground:
1445 case ColorID::IMESelectedConvertedTextUnderline:
1446 case ColorID::SpellCheckerUnderline:
1447 return false;
1448 default:
1449 break;
1452 return true;
1455 LookAndFeel::UseStandins LookAndFeel::ShouldUseStandins(
1456 const dom::Document& aDoc, ColorID aId) {
1457 const auto& prefs = PreferenceSheet::PrefsFor(aDoc);
1458 if (ShouldUseStandinsForNativeColorForNonNativeTheme(aDoc, aId, prefs)) {
1459 return UseStandins::Yes;
1461 if (prefs.mUseStandins && ColorIsCSSAccessible(aId)) {
1462 return UseStandins::Yes;
1464 return UseStandins::No;
1467 Maybe<nscolor> LookAndFeel::GetColor(ColorID aId, const nsIFrame* aFrame) {
1468 const auto* doc = aFrame->PresContext()->Document();
1469 return GetColor(aId, ColorSchemeForFrame(aFrame),
1470 ShouldUseStandins(*doc, aId));
1473 // static
1474 nsresult LookAndFeel::GetInt(IntID aID, int32_t* aResult) {
1475 return nsLookAndFeel::GetInstance()->GetIntValue(aID, *aResult);
1478 // static
1479 nsresult LookAndFeel::GetFloat(FloatID aID, float* aResult) {
1480 return nsLookAndFeel::GetInstance()->GetFloatValue(aID, *aResult);
1483 // static
1484 bool LookAndFeel::GetFont(FontID aID, nsString& aName, gfxFontStyle& aStyle) {
1485 return nsLookAndFeel::GetInstance()->GetFontValue(aID, aName, aStyle);
1488 // static
1489 char16_t LookAndFeel::GetPasswordCharacter() {
1490 return nsLookAndFeel::GetInstance()->GetPasswordCharacterImpl();
1493 // static
1494 bool LookAndFeel::GetEchoPassword() {
1495 if (StaticPrefs::editor_password_mask_delay() >= 0) {
1496 return StaticPrefs::editor_password_mask_delay() > 0;
1498 return nsLookAndFeel::GetInstance()->GetEchoPasswordImpl();
1501 // static
1502 uint32_t LookAndFeel::GetPasswordMaskDelay() {
1503 int32_t delay = StaticPrefs::editor_password_mask_delay();
1504 if (delay < 0) {
1505 return nsLookAndFeel::GetInstance()->GetPasswordMaskDelayImpl();
1507 return delay;
1510 bool LookAndFeel::DrawInTitlebar() {
1511 switch (StaticPrefs::browser_tabs_inTitlebar()) {
1512 case 0:
1513 return false;
1514 case 1:
1515 return true;
1516 default:
1517 break;
1519 return nsLookAndFeel::GetInstance()->GetDefaultDrawInTitlebar();
1522 void LookAndFeel::GetThemeInfo(nsACString& aOut) {
1523 nsLookAndFeel::GetInstance()->GetThemeInfo(aOut);
1526 uint32_t LookAndFeel::GetMenuAccessKey() {
1527 return StaticPrefs::ui_key_menuAccessKey();
1530 Modifiers LookAndFeel::GetMenuAccessKeyModifiers() {
1531 switch (GetMenuAccessKey()) {
1532 case dom::KeyboardEvent_Binding::DOM_VK_SHIFT:
1533 return MODIFIER_SHIFT;
1534 case dom::KeyboardEvent_Binding::DOM_VK_CONTROL:
1535 return MODIFIER_CONTROL;
1536 case dom::KeyboardEvent_Binding::DOM_VK_ALT:
1537 return MODIFIER_ALT;
1538 case dom::KeyboardEvent_Binding::DOM_VK_META:
1539 case dom::KeyboardEvent_Binding::DOM_VK_WIN:
1540 return MODIFIER_META;
1541 default:
1542 return 0;
1546 // static
1547 void LookAndFeel::Refresh() {
1548 nsLookAndFeel::GetInstance()->RefreshImpl();
1549 widget::Theme::LookAndFeelChanged();
1552 // static
1553 void LookAndFeel::NativeInit() { nsLookAndFeel::GetInstance()->NativeInit(); }
1555 // static
1556 void LookAndFeel::SetData(widget::FullLookAndFeel&& aTables) {
1557 nsLookAndFeel::GetInstance()->SetDataImpl(std::move(aTables));
1560 } // namespace mozilla