Bug 1793629 - Implement attention indicator for the unified extensions button, r...
[gecko.git] / widget / nsXPLookAndFeel.cpp
blobeab28052deca98758d97dec277d703377fae4d17
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 "nscore.h"
10 #include "nsXPLookAndFeel.h"
11 #include "nsLookAndFeel.h"
12 #include "HeadlessLookAndFeel.h"
13 #include "RemoteLookAndFeel.h"
14 #include "nsContentUtils.h"
15 #include "nsCRT.h"
16 #include "nsFont.h"
17 #include "nsIFrame.h"
18 #include "nsIXULRuntime.h"
19 #include "Theme.h"
20 #include "SurfaceCacheUtils.h"
21 #include "mozilla/dom/ContentParent.h"
22 #include "mozilla/dom/ContentChild.h"
23 #include "mozilla/Preferences.h"
24 #include "mozilla/Services.h"
25 #include "mozilla/ServoStyleSet.h"
26 #include "mozilla/ServoCSSParser.h"
27 #include "mozilla/StaticPrefs_browser.h"
28 #include "mozilla/StaticPrefs_editor.h"
29 #include "mozilla/StaticPrefs_layout.h"
30 #include "mozilla/StaticPrefs_ui.h"
31 #include "mozilla/StaticPrefs_widget.h"
32 #include "mozilla/dom/Document.h"
33 #include "mozilla/PreferenceSheet.h"
34 #include "mozilla/gfx/2D.h"
35 #include "mozilla/widget/WidgetMessageUtils.h"
36 #include "mozilla/RelativeLuminanceUtils.h"
37 #include "mozilla/Telemetry.h"
38 #include "mozilla/TelemetryScalarEnums.h"
40 #include "gfxPlatform.h"
41 #include "gfxFont.h"
43 #include "qcms.h"
45 #include <bitset>
47 using namespace mozilla;
49 using IntID = mozilla::LookAndFeel::IntID;
50 using FloatID = mozilla::LookAndFeel::FloatID;
51 using ColorID = mozilla::LookAndFeel::ColorID;
52 using FontID = mozilla::LookAndFeel::FontID;
54 template <typename Index, typename Value, Index kEnd>
55 class EnumeratedCache {
56 mozilla::EnumeratedArray<Index, kEnd, Value> mEntries;
57 std::bitset<size_t(kEnd)> mValidity;
59 public:
60 constexpr EnumeratedCache() = default;
62 bool IsValid(Index aIndex) const { return mValidity[size_t(aIndex)]; }
64 const Value* Get(Index aIndex) const {
65 return IsValid(aIndex) ? &mEntries[aIndex] : nullptr;
68 void Insert(Index aIndex, Value aValue) {
69 mValidity[size_t(aIndex)] = true;
70 mEntries[aIndex] = aValue;
73 void Remove(Index aIndex) {
74 mValidity[size_t(aIndex)] = false;
75 mEntries[aIndex] = Value();
78 void Clear() {
79 mValidity.reset();
80 for (auto& entry : mEntries) {
81 entry = Value();
86 using ColorCache = EnumeratedCache<ColorID, Maybe<nscolor>, ColorID::End>;
88 struct ColorCaches {
89 using UseStandins = LookAndFeel::UseStandins;
91 ColorCache mCaches[2][2];
93 constexpr ColorCaches() = default;
95 ColorCache& Get(ColorScheme aScheme, UseStandins aUseStandins) {
96 return mCaches[aScheme == ColorScheme::Dark]
97 [aUseStandins == UseStandins::Yes];
100 void Clear() {
101 for (auto& c : mCaches) {
102 for (auto& cache : c) {
103 cache.Clear();
109 static ColorCaches sColorCaches;
111 static EnumeratedCache<FloatID, Maybe<float>, FloatID::End> sFloatCache;
112 static EnumeratedCache<IntID, Maybe<int32_t>, IntID::End> sIntCache;
113 static EnumeratedCache<FontID, widget::LookAndFeelFont, FontID::End> sFontCache;
115 // To make one of these prefs toggleable from a reftest add a user
116 // pref in testing/profiles/reftest/user.js. For example, to make
117 // ui.useAccessibilityTheme toggleable, add:
119 // user_pref("ui.useAccessibilityTheme", 0);
121 // This needs to be of the same length and in the same order as
122 // LookAndFeel::IntID values.
123 static const char sIntPrefs[][45] = {
124 "ui.caretBlinkTime",
125 "ui.caretBlinkCount",
126 "ui.caretWidth",
127 "ui.caretVisibleWithSelection",
128 "ui.selectTextfieldsOnKeyFocus",
129 "ui.submenuDelay",
130 "ui.menusCanOverlapOSBar",
131 "ui.useOverlayScrollbars",
132 "ui.allowOverlayScrollbarsOverlap",
133 "ui.skipNavigatingDisabledMenuItem",
134 "ui.dragThresholdX",
135 "ui.dragThresholdY",
136 "ui.useAccessibilityTheme",
137 "ui.scrollArrowStyle",
138 "ui.scrollButtonLeftMouseButtonAction",
139 "ui.scrollButtonMiddleMouseButtonAction",
140 "ui.scrollButtonRightMouseButtonAction",
141 "ui.treeOpenDelay",
142 "ui.treeCloseDelay",
143 "ui.treeLazyScrollDelay",
144 "ui.treeScrollDelay",
145 "ui.treeScrollLinesMax",
146 "accessibility.tabfocus", // Weird one...
147 "ui.chosenMenuItemsShouldBlink",
148 "ui.windowsAccentColorInTitlebar",
149 "ui.windowsDefaultTheme",
150 "ui.dwmCompositor",
151 "ui.windowsClassic",
152 "ui.windowsGlass",
153 "ui.macGraphiteTheme",
154 "ui.macBigSurTheme",
155 "ui.macRTL",
156 "ui.alertNotificationOrigin",
157 "ui.scrollToClick",
158 "ui.IMERawInputUnderlineStyle",
159 "ui.IMESelectedRawTextUnderlineStyle",
160 "ui.IMEConvertedTextUnderlineStyle",
161 "ui.IMESelectedConvertedTextUnderlineStyle",
162 "ui.SpellCheckerUnderlineStyle",
163 "ui.menuBarDrag",
164 "ui.scrollbarButtonAutoRepeatBehavior",
165 "ui.tooltipDelay",
166 "ui.swipeAnimationEnabled",
167 "ui.scrollbarDisplayOnMouseMove",
168 "ui.scrollbarFadeBeginDelay",
169 "ui.scrollbarFadeDuration",
170 "ui.contextMenuOffsetVertical",
171 "ui.contextMenuOffsetHorizontal",
172 "ui.GtkCSDAvailable",
173 "ui.GtkCSDMinimizeButton",
174 "ui.GtkCSDMaximizeButton",
175 "ui.GtkCSDCloseButton",
176 "ui.GtkCSDMinimizeButtonPosition",
177 "ui.GtkCSDMaximizeButtonPosition",
178 "ui.GtkCSDCloseButtonPosition",
179 "ui.GtkCSDReversedPlacement",
180 "ui.systemUsesDarkTheme",
181 "ui.prefersReducedMotion",
182 "ui.primaryPointerCapabilities",
183 "ui.allPointerCapabilities",
184 "ui.systemVerticalScrollbarWidth",
185 "ui.systemHorizontalScrollbarHeight",
186 "ui.touchDeviceSupportPresent",
187 "ui.titlebarRadius",
188 "ui.GtkMenuRadius",
189 "ui.dynamicRange",
190 "ui.videoDynamicRange",
193 static_assert(ArrayLength(sIntPrefs) == size_t(LookAndFeel::IntID::End),
194 "Should have a pref for each int value");
196 // This array MUST be kept in the same order as the float id list in
197 // LookAndFeel.h
198 // clang-format off
199 static const char sFloatPrefs[][37] = {
200 "ui.IMEUnderlineRelativeSize",
201 "ui.SpellCheckerUnderlineRelativeSize",
202 "ui.caretAspectRatio",
203 "ui.textScaleFactor",
204 "ui.cursorScale",
206 // clang-format on
208 static_assert(ArrayLength(sFloatPrefs) == size_t(LookAndFeel::FloatID::End),
209 "Should have a pref for each float value");
211 // This array MUST be kept in the same order as the color list in
212 // specified/color.rs
213 static const char sColorPrefs[][41] = {
214 "ui.activeborder",
215 "ui.activecaption",
216 "ui.appworkspace",
217 "ui.background",
218 "ui.buttonface",
219 "ui.buttonhighlight",
220 "ui.buttonshadow",
221 "ui.buttontext",
222 "ui.captiontext",
223 "ui.-moz-field",
224 "ui.-moz-disabledfield",
225 "ui.-moz-fieldtext",
226 "ui.-moz-comboboxtext",
227 "ui.-moz-combobox",
228 "ui.graytext",
229 "ui.highlight",
230 "ui.highlighttext",
231 "ui.inactiveborder",
232 "ui.inactivecaption",
233 "ui.inactivecaptiontext",
234 "ui.infobackground",
235 "ui.infotext",
236 "ui.menu",
237 "ui.menutext",
238 "ui.scrollbar",
239 "ui.threeddarkshadow",
240 "ui.threedface",
241 "ui.threedhighlight",
242 "ui.threedlightshadow",
243 "ui.threedshadow",
244 "ui.window",
245 "ui.windowframe",
246 "ui.windowtext",
247 "ui.-moz-buttondefault",
248 "ui.-moz-default-color",
249 "ui.-moz-default-background-color",
250 "ui.-moz-dialog",
251 "ui.-moz-dialogtext",
252 "ui.-moz-dragtargetzone",
253 "ui.-moz-cellhighlight",
254 "ui.-moz_cellhighlighttext",
255 "ui.selecteditem",
256 "ui.selecteditemtext",
257 "ui.-moz-buttonhoverface",
258 "ui.-moz_buttonhovertext",
259 "ui.-moz_menuhover",
260 "ui.-moz_menuhoverdisabled",
261 "ui.-moz_menuhovertext",
262 "ui.-moz_menubartext",
263 "ui.-moz_menubarhovertext",
264 "ui.-moz_eventreerow",
265 "ui.-moz_oddtreerow",
266 "ui.-moz-buttonactivetext",
267 "ui.-moz-buttonactiveface",
268 "ui.-moz-buttondisabledface",
269 "ui.-moz_mac_chrome_active",
270 "ui.-moz_mac_chrome_inactive",
271 "ui.-moz-mac-defaultbuttontext",
272 "ui.-moz-mac-focusring",
273 "ui.-moz-mac-menuselect",
274 "ui.-moz-mac-menushadow",
275 "ui.-moz-mac-menutextdisable",
276 "ui.-moz-mac-menutextselect",
277 "ui.-moz_mac_disabledtoolbartext",
278 "ui.-moz-mac-secondaryhighlight",
279 "ui.-moz-mac-menupopup",
280 "ui.-moz-mac-menuitem",
281 "ui.-moz-mac-active-menuitem",
282 "ui.-moz-mac-source-list",
283 "ui.-moz-mac-source-list-selection",
284 "ui.-moz-mac-active-source-list-selection",
285 "ui.-moz-mac-tooltip",
286 "ui.accentcolor",
287 "ui.accentcolortext",
288 "ui.-moz-autofill-background",
289 "ui.-moz-win-mediatext",
290 "ui.-moz-win-communicationstext",
291 "ui.-moz-nativehyperlinktext",
292 "ui.-moz-nativevisitedhyperlinktext",
293 "ui.-moz-hyperlinktext",
294 "ui.-moz-activehyperlinktext",
295 "ui.-moz-visitedhyperlinktext",
296 "ui.-moz-colheadertext",
297 "ui.-moz-colheaderhovertext",
298 "ui.textSelectDisabledBackground",
299 "ui.textSelectAttentionBackground",
300 "ui.textSelectAttentionForeground",
301 "ui.textHighlightBackground",
302 "ui.textHighlightForeground",
303 "ui.IMERawInputBackground",
304 "ui.IMERawInputForeground",
305 "ui.IMERawInputUnderline",
306 "ui.IMESelectedRawTextBackground",
307 "ui.IMESelectedRawTextForeground",
308 "ui.IMESelectedRawTextUnderline",
309 "ui.IMEConvertedTextBackground",
310 "ui.IMEConvertedTextForeground",
311 "ui.IMEConvertedTextUnderline",
312 "ui.IMESelectedConvertedTextBackground",
313 "ui.IMESelectedConvertedTextForeground",
314 "ui.IMESelectedConvertedTextUnderline",
315 "ui.SpellCheckerUnderline",
316 "ui.themedScrollbar",
317 "ui.themedScrollbarInactive",
318 "ui.themedScrollbarThumb",
319 "ui.themedScrollbarThumbHover",
320 "ui.themedScrollbarThumbActive",
321 "ui.themedScrollbarThumbInactive",
324 static_assert(ArrayLength(sColorPrefs) == size_t(LookAndFeel::ColorID::End),
325 "Should have a pref for each color value");
327 const char* nsXPLookAndFeel::GetColorPrefName(ColorID aId) {
328 return sColorPrefs[size_t(aId)];
331 bool nsXPLookAndFeel::sInitialized = false;
333 nsXPLookAndFeel* nsXPLookAndFeel::sInstance = nullptr;
334 bool nsXPLookAndFeel::sShutdown = false;
336 auto LookAndFeel::SystemZoomSettings() -> ZoomSettings {
337 ZoomSettings settings;
338 switch (StaticPrefs::browser_display_os_zoom_behavior()) {
339 case 0:
340 default:
341 break;
342 case 1:
343 settings.mFullZoom = GetTextScaleFactor();
344 break;
345 case 2:
346 settings.mTextZoom = GetTextScaleFactor();
347 break;
349 return settings;
352 // static
353 nsXPLookAndFeel* nsXPLookAndFeel::GetInstance() {
354 if (sInstance) {
355 return sInstance;
358 NS_ENSURE_TRUE(!sShutdown, nullptr);
360 // If we're in a content process, then the parent process will have supplied
361 // us with an initial FullLookAndFeel object.
362 // We grab this data from the ContentChild,
363 // where it's been temporarily stashed, and initialize our new LookAndFeel
364 // object with it.
366 FullLookAndFeel* lnf = nullptr;
368 if (auto* cc = mozilla::dom::ContentChild::GetSingleton()) {
369 lnf = &cc->BorrowLookAndFeelData();
372 if (lnf) {
373 sInstance = new widget::RemoteLookAndFeel(std::move(*lnf));
374 } else if (gfxPlatform::IsHeadless()) {
375 sInstance = new widget::HeadlessLookAndFeel();
376 } else {
377 sInstance = new nsLookAndFeel();
380 // This is only ever used once during initialization, and can be cleared now.
381 if (lnf) {
382 *lnf = {};
385 widget::Theme::Init();
386 return sInstance;
389 // static
390 void nsXPLookAndFeel::Shutdown() {
391 if (sShutdown) {
392 return;
395 sShutdown = true;
396 delete sInstance;
397 sInstance = nullptr;
399 // This keeps strings alive, so need to clear to make leak checking happy.
400 sFontCache.Clear();
402 widget::Theme::Shutdown();
405 static void IntPrefChanged(const nsACString& aPref) {
406 // Most Int prefs can't change our system colors or fonts, but
407 // ui.systemUsesDarkTheme can, since it affects the effective color-scheme
408 // (affecting system colors).
409 auto changeKind = aPref.EqualsLiteral("ui.systemUsesDarkTheme")
410 ? widget::ThemeChangeKind::Style
411 : widget::ThemeChangeKind::MediaQueriesOnly;
412 LookAndFeel::NotifyChangedAllWindows(changeKind);
415 static void FloatPrefChanged(const nsACString& aPref) {
416 // Most float prefs can't change our system colors or fonts, but
417 // textScaleFactor affects layout.
418 auto changeKind = aPref.EqualsLiteral("ui.textScaleFactor")
419 ? widget::ThemeChangeKind::StyleAndLayout
420 : widget::ThemeChangeKind::MediaQueriesOnly;
421 LookAndFeel::NotifyChangedAllWindows(changeKind);
424 static void ColorPrefChanged() {
425 // Color prefs affect style, because they by definition change system colors.
426 LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind::Style);
429 // static
430 void nsXPLookAndFeel::OnPrefChanged(const char* aPref, void* aClosure) {
431 nsDependentCString prefName(aPref);
432 for (const char* pref : sIntPrefs) {
433 if (prefName.Equals(pref)) {
434 IntPrefChanged(prefName);
435 return;
439 for (const char* pref : sFloatPrefs) {
440 if (prefName.Equals(pref)) {
441 FloatPrefChanged(prefName);
442 return;
446 for (const char* pref : sColorPrefs) {
447 // We use StringBeginsWith to handle .dark prefs too.
448 if (StringBeginsWith(prefName, nsDependentCString(pref))) {
449 ColorPrefChanged();
450 return;
455 bool LookAndFeel::WindowsNonNativeMenusEnabled() {
456 switch (StaticPrefs::browser_display_windows_non_native_menus()) {
457 case 0:
458 return false;
459 case 1:
460 return true;
461 default:
462 #ifdef XP_WIN
463 return GetInt(IntID::WindowsDefaultTheme) && IsWin10OrLater();
464 #else
465 return false;
466 #endif
470 static constexpr struct {
471 nsLiteralCString mName;
472 widget::ThemeChangeKind mChangeKind =
473 widget::ThemeChangeKind::MediaQueriesOnly;
474 } kMediaQueryPrefs[] = {
475 {"browser.display.windows.non_native_menus"_ns},
476 // Affects whether standins are used for the accent color.
477 {"widget.non-native-theme.use-theme-accent"_ns,
478 widget::ThemeChangeKind::Style},
479 // These two affect system colors on Windows.
480 {"widget.windows.uwp-system-colors.enabled"_ns,
481 widget::ThemeChangeKind::Style},
482 // These two affect system colors on Windows.
483 {"widget.windows.uwp-system-colors.highlight-accent"_ns,
484 widget::ThemeChangeKind::Style},
485 // Affects env().
486 {"layout.css.prefers-color-scheme.content-override"_ns,
487 widget::ThemeChangeKind::Style},
488 // Affects media queries and scrollbar sizes, so gotta relayout.
489 {"widget.gtk.overlay-scrollbars.enabled"_ns,
490 widget::ThemeChangeKind::StyleAndLayout},
491 // Affects zoom settings which includes text and full zoom.
492 {"browser.display.os-zoom-behavior"_ns,
493 widget::ThemeChangeKind::StyleAndLayout},
494 // This affects not only the media query, but also the native theme, so we
495 // need to re-layout.
496 {"browser.theme.toolbar-theme"_ns, widget::ThemeChangeKind::AllBits},
497 {"browser.theme.content-theme"_ns},
498 {"layout.css.moz-box-flexbox-emulation.enabled"_ns},
499 {"mathml.legacy_maction_and_semantics_implementations.disabled"_ns},
500 {"mathml.ms_lquote_rquote_attributes.disabled"_ns},
503 // Read values from the user's preferences.
504 // This is done once at startup, but since the user's preferences
505 // haven't actually been read yet at that time, we also have to
506 // set a callback to inform us of changes to each pref.
507 void nsXPLookAndFeel::Init() {
508 MOZ_RELEASE_ASSERT(NS_IsMainThread());
510 // Say we're already initialized, and take the chance that it might fail;
511 // protects against some other process writing to our static variables.
512 sInitialized = true;
514 RecomputeColorSchemes();
516 // XXX If we could reorganize the pref names, we should separate the branch
517 // for each types. Then, we could reduce the unnecessary loop from
518 // nsXPLookAndFeel::OnPrefChanged().
519 Preferences::RegisterPrefixCallback(OnPrefChanged, "ui.");
520 // We really do just want the accessibility.tabfocus pref, not other prefs
521 // that start with that string.
522 Preferences::RegisterCallback(OnPrefChanged, "accessibility.tabfocus");
524 for (auto& pref : kMediaQueryPrefs) {
525 Preferences::RegisterCallback(
526 [](const char*, void* aChangeKind) {
527 auto changeKind =
528 widget::ThemeChangeKind(reinterpret_cast<uintptr_t>(aChangeKind));
529 LookAndFeel::NotifyChangedAllWindows(changeKind);
531 pref.mName, reinterpret_cast<void*>(uintptr_t(pref.mChangeKind)));
535 nsXPLookAndFeel::~nsXPLookAndFeel() {
536 NS_ASSERTION(sInstance == this,
537 "This destroying instance isn't the singleton instance");
538 sInstance = nullptr;
541 static bool IsSpecialColor(LookAndFeel::ColorID aID, nscolor aColor) {
542 using ColorID = LookAndFeel::ColorID;
544 if (aColor == NS_SAME_AS_FOREGROUND_COLOR) {
545 return true;
548 switch (aID) {
549 case ColorID::IMESelectedRawTextBackground:
550 case ColorID::IMESelectedConvertedTextBackground:
551 case ColorID::IMERawInputBackground:
552 case ColorID::IMEConvertedTextBackground:
553 case ColorID::IMESelectedRawTextForeground:
554 case ColorID::IMESelectedConvertedTextForeground:
555 case ColorID::IMERawInputForeground:
556 case ColorID::IMEConvertedTextForeground:
557 case ColorID::IMERawInputUnderline:
558 case ColorID::IMEConvertedTextUnderline:
559 case ColorID::IMESelectedRawTextUnderline:
560 case ColorID::IMESelectedConvertedTextUnderline:
561 case ColorID::SpellCheckerUnderline:
562 return NS_IS_SELECTION_SPECIAL_COLOR(aColor);
563 default:
564 break;
567 * In GetColor(), every color that is not a special color is color
568 * corrected. Use false to make other colors color corrected.
570 return false;
573 nscolor nsXPLookAndFeel::GetStandinForNativeColor(ColorID aID,
574 ColorScheme aScheme) {
575 if (aScheme == ColorScheme::Dark) {
576 if (auto color = GenericDarkColor(aID)) {
577 return *color;
581 // The stand-in colors are taken from what the non-native theme needs (for
582 // field/button colors), the Windows 7 Aero theme except Mac-specific colors
583 // which are taken from Mac OS 10.7.
585 #define COLOR(name_, r, g, b) \
586 case ColorID::name_: \
587 return NS_RGB(r, g, b);
589 #define COLORA(name_, r, g, b, a) \
590 case ColorID::name_: \
591 return NS_RGBA(r, g, b, a);
593 switch (aID) {
594 // These are here for the purposes of headless mode.
595 case ColorID::IMESelectedRawTextBackground:
596 case ColorID::IMESelectedConvertedTextBackground:
597 case ColorID::IMERawInputBackground:
598 case ColorID::IMEConvertedTextBackground:
599 return NS_TRANSPARENT;
600 case ColorID::IMESelectedRawTextForeground:
601 case ColorID::IMESelectedConvertedTextForeground:
602 case ColorID::IMERawInputForeground:
603 case ColorID::IMEConvertedTextForeground:
604 return NS_SAME_AS_FOREGROUND_COLOR;
605 case ColorID::IMERawInputUnderline:
606 case ColorID::IMEConvertedTextUnderline:
607 return NS_40PERCENT_FOREGROUND_COLOR;
608 case ColorID::Accentcolor:
609 return widget::sDefaultAccent.ToABGR();
610 case ColorID::Accentcolortext:
611 return widget::sDefaultAccentText.ToABGR();
612 COLOR(SpellCheckerUnderline, 0xff, 0x00, 0x00)
613 COLOR(TextSelectDisabledBackground, 0xaa, 0xaa, 0xaa)
615 // CSS 2 colors:
616 COLOR(Activeborder, 0xB4, 0xB4, 0xB4)
617 COLOR(Activecaption, 0x99, 0xB4, 0xD1)
618 COLOR(Appworkspace, 0xAB, 0xAB, 0xAB)
619 COLOR(Background, 0x00, 0x00, 0x00)
620 COLOR(Buttonhighlight, 0xFF, 0xFF, 0xFF)
621 COLOR(Buttonshadow, 0xA0, 0xA0, 0xA0)
623 // Buttons and comboboxes should be kept in sync since they are drawn with
624 // the same colors by the non-native theme.
625 COLOR(Buttonface, 0xe9, 0xe9, 0xed)
626 COLORA(MozButtondisabledface, 0xe9, 0xe9, 0xed, 128)
628 COLOR(MozCombobox, 0xe9, 0xe9, 0xed)
630 COLOR(Buttontext, 0x00, 0x00, 0x00)
631 COLOR(MozComboboxtext, 0x00, 0x00, 0x00)
633 COLOR(Captiontext, 0x00, 0x00, 0x00)
634 COLOR(Graytext, 0x6D, 0x6D, 0x6D)
635 COLOR(Highlight, 0x33, 0x99, 0xFF)
636 COLOR(Highlighttext, 0xFF, 0xFF, 0xFF)
637 COLOR(Inactiveborder, 0xF4, 0xF7, 0xFC)
638 COLOR(Inactivecaption, 0xBF, 0xCD, 0xDB)
639 COLOR(Inactivecaptiontext, 0x43, 0x4E, 0x54)
640 COLOR(Infobackground, 0xFF, 0xFF, 0xE1)
641 COLOR(Infotext, 0x00, 0x00, 0x00)
642 COLOR(Menu, 0xF0, 0xF0, 0xF0)
643 COLOR(Menutext, 0x00, 0x00, 0x00)
644 COLOR(Scrollbar, 0xC8, 0xC8, 0xC8)
645 COLOR(Threeddarkshadow, 0x69, 0x69, 0x69)
646 COLOR(Threedface, 0xF0, 0xF0, 0xF0)
647 COLOR(Threedhighlight, 0xFF, 0xFF, 0xFF)
648 COLOR(Threedlightshadow, 0xE3, 0xE3, 0xE3)
649 COLOR(Threedshadow, 0xA0, 0xA0, 0xA0)
650 COLOR(Window, 0xFF, 0xFF, 0xFF)
651 COLOR(Windowframe, 0x64, 0x64, 0x64)
652 COLOR(Windowtext, 0x00, 0x00, 0x00)
653 COLOR(MozButtondefault, 0x69, 0x69, 0x69)
654 COLOR(Field, 0xFF, 0xFF, 0xFF)
655 COLORA(MozDisabledfield, 0xFF, 0xFF, 0xFF, 128)
656 COLOR(Fieldtext, 0x00, 0x00, 0x00)
657 COLOR(MozDialog, 0xF0, 0xF0, 0xF0)
658 COLOR(MozDialogtext, 0x00, 0x00, 0x00)
659 COLOR(MozColheadertext, 0x00, 0x00, 0x00)
660 COLOR(MozColheaderhovertext, 0x00, 0x00, 0x00)
661 COLOR(MozDragtargetzone, 0xFF, 0xFF, 0xFF)
662 COLOR(MozCellhighlight, 0xF0, 0xF0, 0xF0)
663 COLOR(MozCellhighlighttext, 0x00, 0x00, 0x00)
664 COLOR(Selecteditem, 0x33, 0x99, 0xFF)
665 COLOR(Selecteditemtext, 0xFF, 0xFF, 0xFF)
666 COLOR(MozButtonhoverface, 0xd0, 0xd0, 0xd7)
667 COLOR(MozButtonhovertext, 0x00, 0x00, 0x00)
668 COLOR(MozButtonactiveface, 0xb1, 0xb1, 0xb9)
669 COLOR(MozButtonactivetext, 0x00, 0x00, 0x00)
670 COLOR(MozMenuhover, 0x33, 0x99, 0xFF)
671 COLOR(MozMenuhovertext, 0x00, 0x00, 0x00)
672 COLOR(MozMenubartext, 0x00, 0x00, 0x00)
673 COLOR(MozMenubarhovertext, 0x00, 0x00, 0x00)
674 COLOR(MozMenuhoverdisabled, 0xF0, 0xF0, 0xF0)
675 COLOR(MozEventreerow, 0xFF, 0xFF, 0xFF)
676 COLOR(MozOddtreerow, 0xFF, 0xFF, 0xFF)
677 COLOR(MozMacChromeActive, 0xB2, 0xB2, 0xB2)
678 COLOR(MozMacChromeInactive, 0xE1, 0xE1, 0xE1)
679 COLOR(MozMacFocusring, 0x60, 0x9D, 0xD7)
680 COLOR(MozMacMenuselect, 0x38, 0x75, 0xD7)
681 COLOR(MozMacMenushadow, 0xA3, 0xA3, 0xA3)
682 COLOR(MozMacMenutextdisable, 0x88, 0x88, 0x88)
683 COLOR(MozMacMenutextselect, 0xFF, 0xFF, 0xFF)
684 COLOR(MozMacDisabledtoolbartext, 0x3F, 0x3F, 0x3F)
685 COLOR(MozMacSecondaryhighlight, 0xD4, 0xD4, 0xD4)
686 COLOR(MozMacMenupopup, 0xe6, 0xe6, 0xe6)
687 COLOR(MozMacMenuitem, 0xe6, 0xe6, 0xe6)
688 COLOR(MozMacActiveMenuitem, 0x0a, 0x64, 0xdc)
689 COLOR(MozMacSourceList, 0xf7, 0xf7, 0xf7)
690 COLOR(MozMacSourceListSelection, 0xc8, 0xc8, 0xc8)
691 COLOR(MozMacActiveSourceListSelection, 0x0a, 0x64, 0xdc)
692 COLOR(MozMacTooltip, 0xf7, 0xf7, 0xf7)
693 // Seems to be the default color (hardcoded because of bug 1065998)
694 COLOR(MozWinMediatext, 0xFF, 0xFF, 0xFF)
695 COLOR(MozWinCommunicationstext, 0xFF, 0xFF, 0xFF)
696 COLOR(MozNativehyperlinktext, 0x00, 0x66, 0xCC)
697 COLOR(MozNativevisitedhyperlinktext, 0x55, 0x1A, 0x8B)
698 default:
699 break;
701 return NS_RGB(0xFF, 0xFF, 0xFF);
704 #undef COLOR
705 #undef COLORA
707 // Taken from in-content/common.inc.css's dark theme.
708 Maybe<nscolor> nsXPLookAndFeel::GenericDarkColor(ColorID aID) {
709 nscolor color = NS_RGB(0, 0, 0);
710 static constexpr nscolor kWindowBackground = NS_RGB(28, 27, 34);
711 static constexpr nscolor kWindowText = NS_RGB(251, 251, 254);
712 switch (aID) {
713 case ColorID::Window: // --in-content-page-background
714 case ColorID::Background:
715 color = kWindowBackground;
716 break;
718 case ColorID::Menu:
719 color = NS_RGB(0x2b, 0x2a, 0x33);
720 break;
722 case ColorID::MozMenuhovertext:
723 case ColorID::MozMenubarhovertext:
724 case ColorID::Menutext:
725 color = NS_RGB(0xfb, 0xfb, 0xfe);
726 break;
728 case ColorID::MozMenuhover:
729 color = NS_RGB(0x52, 0x52, 0x5e);
730 break;
732 case ColorID::MozMenuhoverdisabled:
733 color = NS_RGB(0x3a, 0x39, 0x44);
734 break;
736 case ColorID::MozOddtreerow:
737 case ColorID::MozDialog: // --in-content-box-background
738 color = NS_RGB(35, 34, 43);
739 break;
740 case ColorID::Windowtext: // --in-content-page-color
741 case ColorID::MozDialogtext:
742 case ColorID::Fieldtext:
743 case ColorID::Buttontext: // --in-content-button-text-color (via
744 // --in-content-page-color)
745 case ColorID::MozComboboxtext:
746 case ColorID::MozButtonhovertext:
747 case ColorID::MozButtonactivetext:
748 color = kWindowText;
749 break;
750 case ColorID::Buttonshadow:
751 case ColorID::Threedshadow:
752 case ColorID::Threedlightshadow: // --in-content-box-border-color computed
753 // with kWindowText above
754 // kWindowBackground.
755 case ColorID::Graytext: // opacity: 0.4 of kWindowText blended over the
756 // "Window" background color, which happens to be
757 // the same :-)
758 color = NS_ComposeColors(kWindowBackground, NS_RGBA(251, 251, 254, 102));
759 break;
760 case ColorID::MozCellhighlight:
761 case ColorID::Selecteditem: // --in-content-primary-button-background /
762 // --in-content-item-selected
763 color = NS_RGB(0, 221, 255);
764 break;
765 case ColorID::Field:
766 case ColorID::Buttonface: // --in-content-button-background
767 case ColorID::Threedface:
768 case ColorID::MozCombobox:
769 case ColorID::MozCellhighlighttext:
770 case ColorID::Selecteditemtext: // --in-content-primary-button-text-color /
771 // --in-content-item-selected-text
772 color = NS_RGB(43, 42, 51);
773 break;
774 case ColorID::Threeddarkshadow: // Same as Threedlightshadow but with the
775 // background.
776 case ColorID::MozDisabledfield: // opacity: 0.4 of the face above blended
777 // over the "Window" background color.
778 case ColorID::MozButtondisabledface:
779 color = NS_ComposeColors(kWindowBackground, NS_RGBA(43, 42, 51, 102));
780 break;
781 case ColorID::MozButtonhoverface: // --in-content-button-background-hover
782 color = NS_RGB(82, 82, 94);
783 break;
784 case ColorID::MozButtonactiveface: // --in-content-button-background-active
785 color = NS_RGB(91, 91, 102);
786 break;
787 case ColorID::Highlight:
788 color = NS_RGBA(0, 221, 255, 78);
789 break;
790 case ColorID::Highlighttext:
791 color = NS_SAME_AS_FOREGROUND_COLOR;
792 break;
793 case ColorID::MozNativehyperlinktext:
794 // If you change this color, you probably also want to change the default
795 // value of browser.anchor_color.dark.
796 color = NS_RGB(0x8c, 0x8c, 0xff);
797 break;
798 case ColorID::MozNativevisitedhyperlinktext:
799 // If you change this color, you probably also want to change the default
800 // value of browser.visited_color.dark.
801 color = NS_RGB(0xff, 0xad, 0xff);
802 break;
804 default:
805 return Nothing();
807 return Some(color);
810 // Uncomment the #define below if you want to debug system color use in a skin
811 // that uses them. When set, it will make all system color pairs that are
812 // appropriate for foreground/background pairing the same. This means if the
813 // skin is using system colors correctly you will not be able to see *any* text.
815 // #define DEBUG_SYSTEM_COLOR_USE
817 #ifdef DEBUG_SYSTEM_COLOR_USE
818 static nsresult SystemColorUseDebuggingColor(LookAndFeel::ColorID aID,
819 nscolor& aResult) {
820 using ColorID = LookAndFeel::ColorID;
822 switch (aID) {
823 // css2 http://www.w3.org/TR/REC-CSS2/ui.html#system-colors
824 case ColorID::Activecaption:
825 // active window caption background
826 case ColorID::Captiontext:
827 // text in active window caption
828 aResult = NS_RGB(0xff, 0x00, 0x00);
829 break;
831 case ColorID::Highlight:
832 // background of selected item
833 case ColorID::Highlighttext:
834 // text of selected item
835 aResult = NS_RGB(0xff, 0xff, 0x00);
836 break;
838 case ColorID::Inactivecaption:
839 // inactive window caption
840 case ColorID::Inactivecaptiontext:
841 // text in inactive window caption
842 aResult = NS_RGB(0x66, 0x66, 0x00);
843 break;
845 case ColorID::Infobackground:
846 // tooltip background color
847 case ColorID::Infotext:
848 // tooltip text color
849 aResult = NS_RGB(0x00, 0xff, 0x00);
850 break;
852 case ColorID::Menu:
853 // menu background
854 case ColorID::Menutext:
855 // menu text
856 aResult = NS_RGB(0x00, 0xff, 0xff);
857 break;
859 case ColorID::Threedface:
860 case ColorID::Buttonface:
861 // 3-D face color
862 case ColorID::Buttontext:
863 // text on push buttons
864 aResult = NS_RGB(0x00, 0x66, 0x66);
865 break;
867 case ColorID::Window:
868 case ColorID::Windowtext:
869 aResult = NS_RGB(0x00, 0x00, 0xff);
870 break;
872 // from the CSS3 working draft (not yet finalized)
873 // http://www.w3.org/tr/2000/wd-css3-userint-20000216.html#color
875 case ColorID::Field:
876 case ColorID::Fieldtext:
877 aResult = NS_RGB(0xff, 0x00, 0xff);
878 break;
880 case ColorID::MozDialog:
881 case ColorID::MozDialogtext:
882 aResult = NS_RGB(0x66, 0x00, 0x66);
883 break;
885 default:
886 return NS_ERROR_NOT_AVAILABLE;
889 return NS_OK;
891 #endif
893 static nsresult GetPrefColor(const char* aPref, nscolor& aResult) {
894 nsAutoCString colorStr;
895 MOZ_TRY(Preferences::GetCString(aPref, colorStr));
896 if (!ServoCSSParser::ComputeColor(nullptr, NS_RGB(0, 0, 0), colorStr,
897 &aResult)) {
898 return NS_ERROR_FAILURE;
900 return NS_OK;
903 static nsresult GetColorFromPref(LookAndFeel::ColorID aID, ColorScheme aScheme,
904 nscolor& aResult) {
905 const char* prefName = sColorPrefs[size_t(aID)];
906 if (aScheme == ColorScheme::Dark) {
907 nsAutoCString darkPrefName(prefName);
908 darkPrefName.Append(".dark");
909 if (NS_SUCCEEDED(GetPrefColor(darkPrefName.get(), aResult))) {
910 return NS_OK;
913 return GetPrefColor(prefName, aResult);
916 // All these routines will return NS_OK if they have a value,
917 // in which case the nsLookAndFeel should use that value;
918 // otherwise we'll return NS_ERROR_NOT_AVAILABLE, in which case, the
919 // platform-specific nsLookAndFeel should use its own values instead.
920 nsresult nsXPLookAndFeel::GetColorValue(ColorID aID, ColorScheme aScheme,
921 UseStandins aUseStandins,
922 nscolor& aResult) {
923 if (!sInitialized) {
924 Init();
927 #ifdef DEBUG_SYSTEM_COLOR_USE
928 if (NS_SUCCEEDED(SystemColorUseDebuggingColor(aID, aResult))) {
929 return NS_OK;
931 #endif
933 auto& cache = sColorCaches.Get(aScheme, aUseStandins);
934 if (const auto* cached = cache.Get(aID)) {
935 if (cached->isNothing()) {
936 return NS_ERROR_FAILURE;
938 aResult = cached->value();
939 return NS_OK;
942 // NOTE: Servo holds a lock and the main thread is paused, so writing to the
943 // global cache here is fine.
944 auto result = GetUncachedColor(aID, aScheme, aUseStandins);
945 cache.Insert(aID, result);
946 if (!result) {
947 return NS_ERROR_FAILURE;
949 aResult = *result;
950 return NS_OK;
953 Maybe<nscolor> nsXPLookAndFeel::GetUncachedColor(ColorID aID,
954 ColorScheme aScheme,
955 UseStandins aUseStandins) {
956 if (aUseStandins == UseStandins::Yes) {
957 return Some(GetStandinForNativeColor(aID, aScheme));
959 nscolor r;
960 if (NS_SUCCEEDED(GetColorFromPref(aID, aScheme, r))) {
961 return Some(r);
963 if (NS_SUCCEEDED(NativeGetColor(aID, aScheme, r))) {
964 if (gfxPlatform::GetCMSMode() == CMSMode::All && !IsSpecialColor(aID, r)) {
965 qcms_transform* transform = gfxPlatform::GetCMSInverseRGBTransform();
966 if (transform) {
967 uint8_t color[4];
968 color[0] = NS_GET_R(r);
969 color[1] = NS_GET_G(r);
970 color[2] = NS_GET_B(r);
971 color[3] = NS_GET_A(r);
972 qcms_transform_data(transform, color, color, 1);
973 r = NS_RGBA(color[0], color[1], color[2], color[3]);
977 return Some(r);
979 return Nothing();
982 nsresult nsXPLookAndFeel::GetIntValue(IntID aID, int32_t& aResult) {
983 if (!sInitialized) {
984 Init();
987 if (const auto* cached = sIntCache.Get(aID)) {
988 if (cached->isNothing()) {
989 return NS_ERROR_FAILURE;
991 aResult = cached->value();
992 return NS_OK;
995 if (NS_SUCCEEDED(Preferences::GetInt(sIntPrefs[size_t(aID)], &aResult))) {
996 sIntCache.Insert(aID, Some(aResult));
997 return NS_OK;
1000 if (NS_FAILED(NativeGetInt(aID, aResult))) {
1001 sIntCache.Insert(aID, Nothing());
1002 return NS_ERROR_FAILURE;
1005 sIntCache.Insert(aID, Some(aResult));
1006 return NS_OK;
1009 nsresult nsXPLookAndFeel::GetFloatValue(FloatID aID, float& aResult) {
1010 if (!sInitialized) {
1011 Init();
1014 if (const auto* cached = sFloatCache.Get(aID)) {
1015 if (cached->isNothing()) {
1016 return NS_ERROR_FAILURE;
1018 aResult = cached->value();
1019 return NS_OK;
1022 int32_t pref = 0;
1023 if (NS_SUCCEEDED(Preferences::GetInt(sFloatPrefs[size_t(aID)], &pref))) {
1024 aResult = float(pref) / 100.0f;
1025 sFloatCache.Insert(aID, Some(aResult));
1026 return NS_OK;
1029 if (NS_FAILED(NativeGetFloat(aID, aResult))) {
1030 sFloatCache.Insert(aID, Nothing());
1031 return NS_ERROR_FAILURE;
1034 sFloatCache.Insert(aID, Some(aResult));
1035 return NS_OK;
1038 bool nsXPLookAndFeel::LookAndFeelFontToStyle(const LookAndFeelFont& aFont,
1039 nsString& aName,
1040 gfxFontStyle& aStyle) {
1041 if (!aFont.haveFont()) {
1042 return false;
1044 aName = aFont.name();
1045 aStyle = gfxFontStyle();
1046 aStyle.size = aFont.size();
1047 aStyle.weight = FontWeight::FromInt(aFont.weight());
1048 aStyle.style =
1049 aFont.italic() ? FontSlantStyle::ITALIC : FontSlantStyle::NORMAL;
1050 aStyle.systemFont = true;
1051 return true;
1054 widget::LookAndFeelFont nsXPLookAndFeel::StyleToLookAndFeelFont(
1055 const nsAString& aName, const gfxFontStyle& aStyle) {
1056 LookAndFeelFont font;
1057 font.haveFont() = true;
1058 font.name() = aName;
1059 font.size() = aStyle.size;
1060 font.weight() = aStyle.weight.ToFloat();
1061 font.italic() = aStyle.style.IsItalic();
1062 MOZ_ASSERT(aStyle.style.IsNormal() || aStyle.style.IsItalic(),
1063 "Cannot handle oblique font style");
1064 #ifdef DEBUG
1066 // Assert that all the remaining font style properties have their
1067 // default values.
1068 gfxFontStyle candidate = aStyle;
1069 gfxFontStyle defaults{};
1070 candidate.size = defaults.size;
1071 candidate.weight = defaults.weight;
1072 candidate.style = defaults.style;
1073 MOZ_ASSERT(candidate.Equals(defaults),
1074 "Some font style properties not supported");
1076 #endif
1077 return font;
1080 bool nsXPLookAndFeel::GetFontValue(FontID aID, nsString& aName,
1081 gfxFontStyle& aStyle) {
1082 if (const LookAndFeelFont* cached = sFontCache.Get(aID)) {
1083 return LookAndFeelFontToStyle(*cached, aName, aStyle);
1085 LookAndFeelFont font;
1086 const bool haveFont = NativeGetFont(aID, aName, aStyle);
1087 font.haveFont() = haveFont;
1088 if (haveFont) {
1089 font = StyleToLookAndFeelFont(aName, aStyle);
1091 sFontCache.Insert(aID, std::move(font));
1092 return haveFont;
1095 void nsXPLookAndFeel::RefreshImpl() {
1096 // Wipe out our caches.
1097 sColorCaches.Clear();
1098 sFontCache.Clear();
1099 sFloatCache.Clear();
1100 sIntCache.Clear();
1101 RecomputeColorSchemes();
1103 // Clear any cached FullLookAndFeel data, which is now invalid.
1104 if (XRE_IsParentProcess()) {
1105 widget::RemoteLookAndFeel::ClearCachedData();
1109 static bool sRecordedLookAndFeelTelemetry = false;
1111 void nsXPLookAndFeel::RecordTelemetry() {
1112 if (!XRE_IsParentProcess()) {
1113 return;
1116 if (sRecordedLookAndFeelTelemetry) {
1117 return;
1120 sRecordedLookAndFeelTelemetry = true;
1122 int32_t i;
1123 Telemetry::ScalarSet(
1124 Telemetry::ScalarID::WIDGET_DARK_MODE,
1125 NS_SUCCEEDED(GetIntValue(IntID::SystemUsesDarkTheme, i)) && i != 0);
1127 RecordLookAndFeelSpecificTelemetry();
1130 namespace mozilla {
1132 static widget::ThemeChangeKind sGlobalThemeChangeKind{0};
1134 void LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind aKind) {
1135 sGlobalThemeChanged = true;
1136 sGlobalThemeChangeKind |= aKind;
1138 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
1139 const char16_t kind[] = {char16_t(aKind), 0};
1140 obs->NotifyObservers(nullptr, "internal-look-and-feel-changed", kind);
1144 void LookAndFeel::DoHandleGlobalThemeChange() {
1145 MOZ_ASSERT(sGlobalThemeChanged);
1146 sGlobalThemeChanged = false;
1147 auto kind = std::exchange(sGlobalThemeChangeKind, widget::ThemeChangeKind(0));
1149 // Tell the theme that it changed, so it can flush any handles to stale theme
1150 // data.
1152 // We can use the *DoNotUseDirectly functions directly here, because we want
1153 // to notify all possible themes in a given process (but just once).
1154 if (XRE_IsParentProcess() ||
1155 !StaticPrefs::widget_non_native_theme_enabled()) {
1156 if (nsCOMPtr<nsITheme> theme = do_GetNativeThemeDoNotUseDirectly()) {
1157 theme->ThemeChanged();
1160 if (nsCOMPtr<nsITheme> theme = do_GetBasicNativeThemeDoNotUseDirectly()) {
1161 theme->ThemeChanged();
1164 // Clear all cached LookAndFeel colors.
1165 LookAndFeel::Refresh();
1167 // Reset default background and foreground colors for the document since they
1168 // may be using system colors.
1169 PreferenceSheet::Refresh();
1171 // Vector images (SVG) may be using theme colors so we discard all cached
1172 // surfaces. (We could add a vector image only version of DiscardAll, but
1173 // in bug 940625 we decided theme changes are rare enough not to bother.)
1174 image::SurfaceCacheUtils::DiscardAll();
1176 if (XRE_IsParentProcess()) {
1177 dom::ContentParent::BroadcastThemeUpdate(kind);
1180 nsContentUtils::AddScriptRunner(
1181 NS_NewRunnableFunction("HandleGlobalThemeChange", [] {
1182 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
1183 obs->NotifyObservers(nullptr, "look-and-feel-changed", nullptr);
1185 }));
1188 #define BIT_FOR(_c) (1ull << size_t(ColorID::_c))
1190 // We want to use a non-native color scheme for the non-native theme (except in
1191 // high-contrast mode), so spoof some of the colors with stand-ins to prevent
1192 // lack of contrast.
1193 static constexpr std::bitset<size_t(ColorID::End)> sNonNativeThemeStandinColors{
1194 // Used by default button styles.
1195 BIT_FOR(Buttonface) | BIT_FOR(Buttontext) | BIT_FOR(MozButtonhoverface) |
1196 BIT_FOR(MozButtonhovertext) | BIT_FOR(MozButtonactiveface) |
1197 BIT_FOR(MozButtonactivetext) | BIT_FOR(MozButtondisabledface) |
1198 // Used by select elements.
1199 BIT_FOR(MozCombobox) | BIT_FOR(MozComboboxtext) |
1200 BIT_FOR(Threedlightshadow) |
1201 // For symmetry with the above.
1202 BIT_FOR(Threeddarkshadow) |
1203 // Used by fieldset borders.
1204 BIT_FOR(Threedface) |
1205 // Used by input / textarea.
1206 BIT_FOR(Field) | BIT_FOR(Fieldtext) |
1207 // Used by disabled form controls.
1208 BIT_FOR(MozDisabledfield) | BIT_FOR(Graytext) |
1209 // Some pages expect these to return windows-like colors, see bug 1773795.
1210 // Also, per spec these should match Canvas/CanvasText, see
1211 // https://drafts.csswg.org/css-color-4/#window
1212 BIT_FOR(Window) | BIT_FOR(Windowtext)};
1213 #undef BIT_FOR
1215 static bool ShouldUseStandinsForNativeColorForNonNativeTheme(
1216 const dom::Document& aDoc, LookAndFeel::ColorID aColor,
1217 const PreferenceSheet::Prefs& aPrefs) {
1218 const bool shouldUseStandinsForColor = [&] {
1219 if (sNonNativeThemeStandinColors[size_t(aColor)]) {
1220 return true;
1222 // There are platforms where we want the content-exposed accent color to be
1223 // the windows blue rather than the system accent color, for now.
1224 return !StaticPrefs::widget_non_native_theme_use_theme_accent() &&
1225 (aColor == LookAndFeel::ColorID::Accentcolor ||
1226 aColor == LookAndFeel::ColorID::Accentcolortext);
1227 }();
1229 return shouldUseStandinsForColor && aDoc.ShouldAvoidNativeTheme() &&
1230 !aPrefs.NonNativeThemeShouldBeHighContrast();
1233 ColorScheme LookAndFeel::sChromeColorScheme;
1234 ColorScheme LookAndFeel::sContentColorScheme;
1235 bool LookAndFeel::sColorSchemeInitialized;
1236 bool LookAndFeel::sGlobalThemeChanged;
1238 bool LookAndFeel::IsDarkColor(nscolor aColor) {
1239 // Given https://www.w3.org/TR/WCAG20/#contrast-ratiodef, this is the
1240 // threshold that tells us whether contrast is better against white or black.
1242 // Contrast ratio against black is: (L + 0.05) / 0.05
1243 // Contrast ratio against white is: 1.05 / (L + 0.05)
1245 // So the intersection is:
1247 // (L + 0.05) / 0.05 = 1.05 / (L + 0.05)
1249 // And the solution to that equation is:
1251 // sqrt(1.05 * 0.05) - 0.05
1253 // So we consider a color dark if the contrast is below this threshold, and
1254 // it's at least half-opaque.
1255 constexpr float kThreshold = 0.179129;
1256 return NS_GET_A(aColor) > 127 &&
1257 RelativeLuminanceUtils::Compute(aColor) < kThreshold;
1260 auto LookAndFeel::ColorSchemeSettingForChrome() -> ChromeColorSchemeSetting {
1261 switch (StaticPrefs::browser_theme_toolbar_theme()) {
1262 case 0: // Dark
1263 return ChromeColorSchemeSetting::Dark;
1264 case 1: // Light
1265 return ChromeColorSchemeSetting::Light;
1266 default:
1267 return ChromeColorSchemeSetting::System;
1271 ColorScheme LookAndFeel::ThemeDerivedColorSchemeForContent() {
1272 switch (StaticPrefs::browser_theme_content_theme()) {
1273 case 0: // Dark
1274 return ColorScheme::Dark;
1275 case 1: // Light
1276 return ColorScheme::Light;
1277 default:
1278 return SystemColorScheme();
1282 void LookAndFeel::RecomputeColorSchemes() {
1283 sColorSchemeInitialized = true;
1285 sChromeColorScheme = [] {
1286 switch (ColorSchemeSettingForChrome()) {
1287 case ChromeColorSchemeSetting::Light:
1288 return ColorScheme::Light;
1289 case ChromeColorSchemeSetting::Dark:
1290 return ColorScheme::Dark;
1291 case ChromeColorSchemeSetting::System:
1292 break;
1294 return SystemColorScheme();
1295 }();
1297 sContentColorScheme = [] {
1298 switch (StaticPrefs::layout_css_prefers_color_scheme_content_override()) {
1299 case 0:
1300 return ColorScheme::Dark;
1301 case 1:
1302 return ColorScheme::Light;
1303 default:
1304 return ThemeDerivedColorSchemeForContent();
1306 }();
1309 ColorScheme LookAndFeel::ColorSchemeForStyle(
1310 const dom::Document& aDoc, const StyleColorSchemeFlags& aFlags,
1311 ColorSchemeMode aMode) {
1312 using Choice = PreferenceSheet::Prefs::ColorSchemeChoice;
1314 const auto& prefs = PreferenceSheet::PrefsFor(aDoc);
1315 switch (prefs.mColorSchemeChoice) {
1316 case Choice::Standard:
1317 break;
1318 case Choice::UserPreferred:
1319 return aDoc.PreferredColorScheme();
1320 case Choice::Light:
1321 return ColorScheme::Light;
1322 case Choice::Dark:
1323 return ColorScheme::Dark;
1326 StyleColorSchemeFlags style(aFlags);
1327 if (!style) {
1328 style.bits = aDoc.GetColorSchemeBits();
1330 const bool supportsDark = bool(style & StyleColorSchemeFlags::DARK);
1331 const bool supportsLight = bool(style & StyleColorSchemeFlags::LIGHT);
1332 if (supportsLight && supportsDark) {
1333 // Both color-schemes are explicitly supported, use the preferred one.
1334 return aDoc.PreferredColorScheme();
1336 if (supportsDark || supportsLight) {
1337 // One color-scheme is explicitly supported and one isn't, so use the one
1338 // the content supports.
1339 return supportsDark ? ColorScheme::Dark : ColorScheme::Light;
1341 // No value specified. Chrome docs always supports both, so use the preferred
1342 // color-scheme.
1343 if (aMode == ColorSchemeMode::Preferred ||
1344 nsContentUtils::IsChromeDoc(&aDoc)) {
1345 return aDoc.PreferredColorScheme();
1347 // Default content to light.
1348 return ColorScheme::Light;
1351 LookAndFeel::ColorScheme LookAndFeel::ColorSchemeForFrame(
1352 const nsIFrame* aFrame, ColorSchemeMode aMode) {
1353 return ColorSchemeForStyle(*aFrame->PresContext()->Document(),
1354 aFrame->StyleUI()->mColorScheme.bits, aMode);
1357 // static
1358 Maybe<nscolor> LookAndFeel::GetColor(ColorID aId, ColorScheme aScheme,
1359 UseStandins aUseStandins) {
1360 nscolor result;
1361 nsresult rv = nsLookAndFeel::GetInstance()->GetColorValue(
1362 aId, aScheme, aUseStandins, result);
1363 if (NS_FAILED(rv)) {
1364 return Nothing();
1366 return Some(result);
1369 // Returns whether there is a CSS color name for this color.
1370 static bool ColorIsCSSAccessible(LookAndFeel::ColorID aId) {
1371 using ColorID = LookAndFeel::ColorID;
1373 switch (aId) {
1374 case ColorID::TextSelectDisabledBackground:
1375 case ColorID::TextSelectAttentionBackground:
1376 case ColorID::TextSelectAttentionForeground:
1377 case ColorID::TextHighlightBackground:
1378 case ColorID::TextHighlightForeground:
1379 case ColorID::ThemedScrollbar:
1380 case ColorID::ThemedScrollbarInactive:
1381 case ColorID::ThemedScrollbarThumb:
1382 case ColorID::ThemedScrollbarThumbActive:
1383 case ColorID::ThemedScrollbarThumbInactive:
1384 case ColorID::ThemedScrollbarThumbHover:
1385 case ColorID::IMERawInputBackground:
1386 case ColorID::IMERawInputForeground:
1387 case ColorID::IMERawInputUnderline:
1388 case ColorID::IMESelectedRawTextBackground:
1389 case ColorID::IMESelectedRawTextForeground:
1390 case ColorID::IMESelectedRawTextUnderline:
1391 case ColorID::IMEConvertedTextBackground:
1392 case ColorID::IMEConvertedTextForeground:
1393 case ColorID::IMEConvertedTextUnderline:
1394 case ColorID::IMESelectedConvertedTextBackground:
1395 case ColorID::IMESelectedConvertedTextForeground:
1396 case ColorID::IMESelectedConvertedTextUnderline:
1397 case ColorID::SpellCheckerUnderline:
1398 return false;
1399 default:
1400 break;
1403 return true;
1406 LookAndFeel::UseStandins LookAndFeel::ShouldUseStandins(
1407 const dom::Document& aDoc, ColorID aId) {
1408 const auto& prefs = PreferenceSheet::PrefsFor(aDoc);
1409 if (ShouldUseStandinsForNativeColorForNonNativeTheme(aDoc, aId, prefs)) {
1410 return UseStandins::Yes;
1412 if (prefs.mUseStandins && ColorIsCSSAccessible(aId)) {
1413 return UseStandins::Yes;
1415 return UseStandins::No;
1418 Maybe<nscolor> LookAndFeel::GetColor(ColorID aId, const nsIFrame* aFrame) {
1419 const auto* doc = aFrame->PresContext()->Document();
1420 return GetColor(aId, ColorSchemeForFrame(aFrame),
1421 ShouldUseStandins(*doc, aId));
1424 // static
1425 nsresult LookAndFeel::GetInt(IntID aID, int32_t* aResult) {
1426 return nsLookAndFeel::GetInstance()->GetIntValue(aID, *aResult);
1429 // static
1430 nsresult LookAndFeel::GetFloat(FloatID aID, float* aResult) {
1431 return nsLookAndFeel::GetInstance()->GetFloatValue(aID, *aResult);
1434 // static
1435 bool LookAndFeel::GetFont(FontID aID, nsString& aName, gfxFontStyle& aStyle) {
1436 return nsLookAndFeel::GetInstance()->GetFontValue(aID, aName, aStyle);
1439 // static
1440 char16_t LookAndFeel::GetPasswordCharacter() {
1441 return nsLookAndFeel::GetInstance()->GetPasswordCharacterImpl();
1444 // static
1445 bool LookAndFeel::GetEchoPassword() {
1446 if (StaticPrefs::editor_password_mask_delay() >= 0) {
1447 return StaticPrefs::editor_password_mask_delay() > 0;
1449 return nsLookAndFeel::GetInstance()->GetEchoPasswordImpl();
1452 // static
1453 uint32_t LookAndFeel::GetPasswordMaskDelay() {
1454 int32_t delay = StaticPrefs::editor_password_mask_delay();
1455 if (delay < 0) {
1456 return nsLookAndFeel::GetInstance()->GetPasswordMaskDelayImpl();
1458 return delay;
1461 bool LookAndFeel::DrawInTitlebar() {
1462 switch (StaticPrefs::browser_tabs_inTitlebar()) {
1463 case 0:
1464 return false;
1465 case 1:
1466 return true;
1467 default:
1468 break;
1470 return nsLookAndFeel::GetInstance()->GetDefaultDrawInTitlebar();
1473 void LookAndFeel::GetThemeInfo(nsACString& aOut) {
1474 nsLookAndFeel::GetInstance()->GetThemeInfo(aOut);
1477 // static
1478 void LookAndFeel::Refresh() {
1479 nsLookAndFeel::GetInstance()->RefreshImpl();
1480 widget::Theme::LookAndFeelChanged();
1483 // static
1484 void LookAndFeel::NativeInit() { nsLookAndFeel::GetInstance()->NativeInit(); }
1486 // static
1487 void LookAndFeel::SetData(widget::FullLookAndFeel&& aTables) {
1488 nsLookAndFeel::GetInstance()->SetDataImpl(std::move(aTables));
1491 } // namespace mozilla