Bug 1766413 [wpt PR 33787] - Fix some cases of "ref test" to be "reftest", a=testonly
[gecko.git] / widget / nsXPLookAndFeel.cpp
blob3cc2c561dbc542ea7b1517d4eafa247f43845d01
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_ui.h"
30 #include "mozilla/StaticPrefs_widget.h"
31 #include "mozilla/dom/Document.h"
32 #include "mozilla/PreferenceSheet.h"
33 #include "mozilla/gfx/2D.h"
34 #include "mozilla/widget/WidgetMessageUtils.h"
35 #include "mozilla/RelativeLuminanceUtils.h"
36 #include "mozilla/Telemetry.h"
37 #include "mozilla/TelemetryScalarEnums.h"
39 #include "gfxPlatform.h"
40 #include "gfxFont.h"
42 #include "qcms.h"
44 #ifdef DEBUG
45 # include "nsSize.h"
46 #endif
48 using namespace mozilla;
50 using IntID = mozilla::LookAndFeel::IntID;
51 using FloatID = mozilla::LookAndFeel::FloatID;
52 using ColorID = mozilla::LookAndFeel::ColorID;
53 using FontID = mozilla::LookAndFeel::FontID;
55 template <typename Index, typename Value, Index kEnd>
56 class EnumeratedCache {
57 static constexpr uint32_t ChunkFor(Index aIndex) {
58 return uint32_t(aIndex) >> 5; // >> 5 is the same as / 32.
60 static constexpr uint32_t BitFor(Index aIndex) {
61 return 1u << (uint32_t(aIndex) & 31);
63 static constexpr uint32_t kChunks = ChunkFor(kEnd) + 1;
65 mozilla::EnumeratedArray<Index, kEnd, Value> mEntries;
66 uint32_t mValidity[kChunks] = {0};
68 public:
69 constexpr EnumeratedCache() = default;
71 bool IsValid(Index aIndex) const {
72 return mValidity[ChunkFor(aIndex)] & BitFor(aIndex);
75 const Value* Get(Index aIndex) const {
76 return IsValid(aIndex) ? &mEntries[aIndex] : nullptr;
79 void Insert(Index aIndex, Value aValue) {
80 mValidity[ChunkFor(aIndex)] |= BitFor(aIndex);
81 mEntries[aIndex] = aValue;
84 void Remove(Index aIndex) {
85 mValidity[ChunkFor(aIndex)] &= ~BitFor(aIndex);
86 mEntries[aIndex] = Value();
89 void Clear() {
90 for (auto& chunk : mValidity) {
91 chunk = 0;
93 for (auto& entry : mEntries) {
94 entry = Value();
99 static EnumeratedCache<ColorID, Maybe<nscolor>, ColorID::End> sLightColorCache;
100 static EnumeratedCache<ColorID, Maybe<nscolor>, ColorID::End> sDarkColorCache;
101 static EnumeratedCache<FloatID, Maybe<float>, FloatID::End> sFloatCache;
102 static EnumeratedCache<IntID, Maybe<int32_t>, IntID::End> sIntCache;
103 static EnumeratedCache<FontID, widget::LookAndFeelFont, FontID::End> sFontCache;
105 // To make one of these prefs toggleable from a reftest add a user
106 // pref in testing/profiles/reftest/user.js. For example, to make
107 // ui.useAccessibilityTheme toggleable, add:
109 // user_pref("ui.useAccessibilityTheme", 0);
111 // This needs to be of the same length and in the same order as
112 // LookAndFeel::IntID values.
113 static const char sIntPrefs[][45] = {
114 "ui.caretBlinkTime",
115 "ui.caretBlinkCount",
116 "ui.caretWidth",
117 "ui.caretVisibleWithSelection",
118 "ui.selectTextfieldsOnKeyFocus",
119 "ui.submenuDelay",
120 "ui.menusCanOverlapOSBar",
121 "ui.useOverlayScrollbars",
122 "ui.allowOverlayScrollbarsOverlap",
123 "ui.skipNavigatingDisabledMenuItem",
124 "ui.dragThresholdX",
125 "ui.dragThresholdY",
126 "ui.useAccessibilityTheme",
127 "ui.scrollArrowStyle",
128 "ui.scrollSliderStyle",
129 "ui.scrollButtonLeftMouseButtonAction",
130 "ui.scrollButtonMiddleMouseButtonAction",
131 "ui.scrollButtonRightMouseButtonAction",
132 "ui.treeOpenDelay",
133 "ui.treeCloseDelay",
134 "ui.treeLazyScrollDelay",
135 "ui.treeScrollDelay",
136 "ui.treeScrollLinesMax",
137 "accessibility.tabfocus", // Weird one...
138 "ui.chosenMenuItemsShouldBlink",
139 "ui.windowsAccentColorInTitlebar",
140 "ui.windowsDefaultTheme",
141 "ui.dwmCompositor",
142 "ui.windowsClassic",
143 "ui.windowsGlass",
144 "ui.macGraphiteTheme",
145 "ui.macBigSurTheme",
146 "ui.macRTL",
147 "ui.alertNotificationOrigin",
148 "ui.scrollToClick",
149 "ui.IMERawInputUnderlineStyle",
150 "ui.IMESelectedRawTextUnderlineStyle",
151 "ui.IMEConvertedTextUnderlineStyle",
152 "ui.IMESelectedConvertedTextUnderlineStyle",
153 "ui.SpellCheckerUnderlineStyle",
154 "ui.menuBarDrag",
155 "ui.scrollbarButtonAutoRepeatBehavior",
156 "ui.tooltipDelay",
157 "ui.swipeAnimationEnabled",
158 "ui.scrollbarDisplayOnMouseMove",
159 "ui.scrollbarFadeBeginDelay",
160 "ui.scrollbarFadeDuration",
161 "ui.contextMenuOffsetVertical",
162 "ui.contextMenuOffsetHorizontal",
163 "ui.GtkCSDAvailable",
164 "ui.GtkCSDMinimizeButton",
165 "ui.GtkCSDMaximizeButton",
166 "ui.GtkCSDCloseButton",
167 "ui.GtkCSDMinimizeButtonPosition",
168 "ui.GtkCSDMaximizeButtonPosition",
169 "ui.GtkCSDCloseButtonPosition",
170 "ui.GtkCSDReversedPlacement",
171 "ui.systemUsesDarkTheme",
172 "ui.prefersReducedMotion",
173 "ui.primaryPointerCapabilities",
174 "ui.allPointerCapabilities",
175 "ui.systemVerticalScrollbarWidth",
176 "ui.systemHorizontalScrollbarHeight",
177 "ui.touchDeviceSupportPresent",
178 "ui.titlebarRadius",
179 "ui.GtkMenuRadius",
180 "ui.dynamicRange",
181 "ui.videoDynamicRange",
184 static_assert(ArrayLength(sIntPrefs) == size_t(LookAndFeel::IntID::End),
185 "Should have a pref for each int value");
187 // This array MUST be kept in the same order as the float id list in
188 // LookAndFeel.h
189 // clang-format off
190 static const char sFloatPrefs[][37] = {
191 "ui.IMEUnderlineRelativeSize",
192 "ui.SpellCheckerUnderlineRelativeSize",
193 "ui.caretAspectRatio",
194 "ui.textScaleFactor",
195 "ui.cursorScale",
197 // clang-format on
199 static_assert(ArrayLength(sFloatPrefs) == size_t(LookAndFeel::FloatID::End),
200 "Should have a pref for each float value");
202 // This array MUST be kept in the same order as the color list in
203 // specified/color.rs
204 static const char sColorPrefs[][41] = {
205 "ui.textSelectDisabledBackground",
206 "ui.textSelectAttentionBackground",
207 "ui.textSelectAttentionForeground",
208 "ui.textHighlightBackground",
209 "ui.textHighlightForeground",
210 "ui.IMERawInputBackground",
211 "ui.IMERawInputForeground",
212 "ui.IMERawInputUnderline",
213 "ui.IMESelectedRawTextBackground",
214 "ui.IMESelectedRawTextForeground",
215 "ui.IMESelectedRawTextUnderline",
216 "ui.IMEConvertedTextBackground",
217 "ui.IMEConvertedTextForeground",
218 "ui.IMEConvertedTextUnderline",
219 "ui.IMESelectedConvertedTextBackground",
220 "ui.IMESelectedConvertedTextForeground",
221 "ui.IMESelectedConvertedTextUnderline",
222 "ui.SpellCheckerUnderline",
223 "ui.themedScrollbar",
224 "ui.themedScrollbarInactive",
225 "ui.themedScrollbarThumb",
226 "ui.themedScrollbarThumbHover",
227 "ui.themedScrollbarThumbActive",
228 "ui.themedScrollbarThumbInactive",
229 "ui.activeborder",
230 "ui.activecaption",
231 "ui.appworkspace",
232 "ui.background",
233 "ui.buttonface",
234 "ui.buttonhighlight",
235 "ui.buttonshadow",
236 "ui.buttontext",
237 "ui.captiontext",
238 "ui.-moz-field",
239 "ui.-moz-disabledfield",
240 "ui.-moz-fieldtext",
241 "ui.graytext",
242 "ui.highlight",
243 "ui.highlighttext",
244 "ui.inactiveborder",
245 "ui.inactivecaption",
246 "ui.inactivecaptiontext",
247 "ui.infobackground",
248 "ui.infotext",
249 "ui.menu",
250 "ui.menutext",
251 "ui.scrollbar",
252 "ui.threeddarkshadow",
253 "ui.threedface",
254 "ui.threedhighlight",
255 "ui.threedlightshadow",
256 "ui.threedshadow",
257 "ui.window",
258 "ui.windowframe",
259 "ui.windowtext",
260 "ui.-moz-buttondefault",
261 "ui.-moz-default-color",
262 "ui.-moz-default-background-color",
263 "ui.-moz-dialog",
264 "ui.-moz-dialogtext",
265 "ui.-moz-dragtargetzone",
266 "ui.-moz-cellhighlight",
267 "ui.-moz_cellhighlighttext",
268 "ui.selecteditem",
269 "ui.selecteditemtext",
270 "ui.-moz-buttonhoverface",
271 "ui.-moz_buttonhovertext",
272 "ui.-moz_menuhover",
273 "ui.-moz_menuhovertext",
274 "ui.-moz_menubartext",
275 "ui.-moz_menubarhovertext",
276 "ui.-moz_eventreerow",
277 "ui.-moz_oddtreerow",
278 "ui.-moz-buttonactivetext",
279 "ui.-moz-buttonactiveface",
280 "ui.-moz-buttondisabledface",
281 "ui.-moz_mac_chrome_active",
282 "ui.-moz_mac_chrome_inactive",
283 "ui.-moz-mac-defaultbuttontext",
284 "ui.-moz-mac-focusring",
285 "ui.-moz-mac-menuselect",
286 "ui.-moz-mac-menushadow",
287 "ui.-moz-mac-menutextdisable",
288 "ui.-moz-mac-menutextselect",
289 "ui.-moz_mac_disabledtoolbartext",
290 "ui.-moz-mac-secondaryhighlight",
291 "ui.-moz-mac-vibrant-titlebar-light",
292 "ui.-moz-mac-vibrant-titlebar-dark",
293 "ui.-moz-mac-menupopup",
294 "ui.-moz-mac-menuitem",
295 "ui.-moz-mac-active-menuitem",
296 "ui.-moz-mac-source-list",
297 "ui.-moz-mac-source-list-selection",
298 "ui.-moz-mac-active-source-list-selection",
299 "ui.-moz-mac-tooltip",
300 "ui.-moz-accent-color",
301 "ui.-moz-accent-color-foreground",
302 "ui.-moz-autofill-background",
303 "ui.-moz-win-mediatext",
304 "ui.-moz-win-communicationstext",
305 "ui.-moz-nativehyperlinktext",
306 "ui.-moz-nativevisitedhyperlinktext",
307 "ui.-moz-hyperlinktext",
308 "ui.-moz-activehyperlinktext",
309 "ui.-moz-visitedhyperlinktext",
310 "ui.-moz-comboboxtext",
311 "ui.-moz-combobox",
312 "ui.-moz-colheadertext",
313 "ui.-moz-colheaderhovertext",
316 static_assert(ArrayLength(sColorPrefs) == size_t(LookAndFeel::ColorID::End),
317 "Should have a pref for each color value");
319 const char* nsXPLookAndFeel::GetColorPrefName(ColorID aId) {
320 return sColorPrefs[size_t(aId)];
323 bool nsXPLookAndFeel::sInitialized = false;
325 nsXPLookAndFeel* nsXPLookAndFeel::sInstance = nullptr;
326 bool nsXPLookAndFeel::sShutdown = false;
328 // static
329 nsXPLookAndFeel* nsXPLookAndFeel::GetInstance() {
330 if (sInstance) {
331 return sInstance;
334 NS_ENSURE_TRUE(!sShutdown, nullptr);
336 // If we're in a content process, then the parent process will have supplied
337 // us with an initial FullLookAndFeel object.
338 // We grab this data from the ContentChild,
339 // where it's been temporarily stashed, and initialize our new LookAndFeel
340 // object with it.
342 FullLookAndFeel* lnf = nullptr;
344 if (auto* cc = mozilla::dom::ContentChild::GetSingleton()) {
345 lnf = &cc->BorrowLookAndFeelData();
348 if (lnf) {
349 sInstance = new widget::RemoteLookAndFeel(std::move(*lnf));
350 } else if (gfxPlatform::IsHeadless()) {
351 sInstance = new widget::HeadlessLookAndFeel();
352 } else {
353 sInstance = new nsLookAndFeel();
356 // This is only ever used once during initialization, and can be cleared now.
357 if (lnf) {
358 *lnf = {};
361 widget::Theme::Init();
362 return sInstance;
365 // static
366 void nsXPLookAndFeel::Shutdown() {
367 if (sShutdown) {
368 return;
371 sShutdown = true;
372 delete sInstance;
373 sInstance = nullptr;
375 // This keeps strings alive, so need to clear to make leak checking happy.
376 sFontCache.Clear();
378 widget::Theme::Shutdown();
381 static void IntPrefChanged(const nsACString& aPref) {
382 // Most Int prefs can't change our system colors or fonts, but
383 // ui.systemUsesDarkTheme can, since it affects the effective color-scheme
384 // (affecting system colors).
385 auto changeKind = aPref.EqualsLiteral("ui.systemUsesDarkTheme")
386 ? widget::ThemeChangeKind::Style
387 : widget::ThemeChangeKind::MediaQueriesOnly;
388 LookAndFeel::NotifyChangedAllWindows(changeKind);
391 static void FloatPrefChanged() {
392 // Float prefs can't change our system colors or fonts.
393 LookAndFeel::NotifyChangedAllWindows(
394 widget::ThemeChangeKind::MediaQueriesOnly);
397 static void ColorPrefChanged() {
398 // Color prefs affect style, because they by definition change system colors.
399 LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind::Style);
402 // static
403 void nsXPLookAndFeel::OnPrefChanged(const char* aPref, void* aClosure) {
404 nsDependentCString prefName(aPref);
405 for (const char* pref : sIntPrefs) {
406 if (prefName.Equals(pref)) {
407 IntPrefChanged(prefName);
408 return;
412 for (const char* pref : sFloatPrefs) {
413 if (prefName.Equals(pref)) {
414 FloatPrefChanged();
415 return;
419 for (const char* pref : sColorPrefs) {
420 // We use StringBeginsWith to handle .dark prefs too.
421 if (StringBeginsWith(prefName, nsDependentCString(pref))) {
422 ColorPrefChanged();
423 return;
428 static constexpr struct {
429 nsLiteralCString mName;
430 widget::ThemeChangeKind mChangeKind =
431 widget::ThemeChangeKind::MediaQueriesOnly;
432 } kMediaQueryPrefs[] = {
433 {"browser.display.windows.native_menus"_ns},
434 // Affects env().
435 {"layout.css.prefers-color-scheme.content-override"_ns,
436 widget::ThemeChangeKind::Style},
437 // Affects media queries and scrollbar sizes, so gotta relayout.
438 {"widget.gtk.overlay-scrollbars.enabled"_ns,
439 widget::ThemeChangeKind::StyleAndLayout},
440 // This affects not only the media query, but also the native theme, so we
441 // need to re-layout.
442 {"browser.theme.toolbar-theme"_ns, widget::ThemeChangeKind::AllBits},
443 {"browser.theme.content-theme"_ns},
446 // Read values from the user's preferences.
447 // This is done once at startup, but since the user's preferences
448 // haven't actually been read yet at that time, we also have to
449 // set a callback to inform us of changes to each pref.
450 void nsXPLookAndFeel::Init() {
451 MOZ_RELEASE_ASSERT(NS_IsMainThread());
453 // Say we're already initialized, and take the chance that it might fail;
454 // protects against some other process writing to our static variables.
455 sInitialized = true;
457 RecomputeColorSchemes();
459 // XXX If we could reorganize the pref names, we should separate the branch
460 // for each types. Then, we could reduce the unnecessary loop from
461 // nsXPLookAndFeel::OnPrefChanged().
462 Preferences::RegisterPrefixCallback(OnPrefChanged, "ui.");
463 // We really do just want the accessibility.tabfocus pref, not other prefs
464 // that start with that string.
465 Preferences::RegisterCallback(OnPrefChanged, "accessibility.tabfocus");
467 for (auto& pref : kMediaQueryPrefs) {
468 Preferences::RegisterCallback(
469 [](const char*, void* aChangeKind) {
470 auto changeKind =
471 widget::ThemeChangeKind(reinterpret_cast<uintptr_t>(aChangeKind));
472 LookAndFeel::NotifyChangedAllWindows(changeKind);
474 pref.mName, reinterpret_cast<void*>(uintptr_t(pref.mChangeKind)));
478 nsXPLookAndFeel::~nsXPLookAndFeel() {
479 NS_ASSERTION(sInstance == this,
480 "This destroying instance isn't the singleton instance");
481 sInstance = nullptr;
484 static bool IsSpecialColor(LookAndFeel::ColorID aID, nscolor aColor) {
485 using ColorID = LookAndFeel::ColorID;
487 if (aColor == NS_SAME_AS_FOREGROUND_COLOR) {
488 return true;
491 switch (aID) {
492 case ColorID::IMESelectedRawTextBackground:
493 case ColorID::IMESelectedConvertedTextBackground:
494 case ColorID::IMERawInputBackground:
495 case ColorID::IMEConvertedTextBackground:
496 case ColorID::IMESelectedRawTextForeground:
497 case ColorID::IMESelectedConvertedTextForeground:
498 case ColorID::IMERawInputForeground:
499 case ColorID::IMEConvertedTextForeground:
500 case ColorID::IMERawInputUnderline:
501 case ColorID::IMEConvertedTextUnderline:
502 case ColorID::IMESelectedRawTextUnderline:
503 case ColorID::IMESelectedConvertedTextUnderline:
504 case ColorID::SpellCheckerUnderline:
505 return NS_IS_SELECTION_SPECIAL_COLOR(aColor);
506 default:
507 break;
510 * In GetColor(), every color that is not a special color is color
511 * corrected. Use false to make other colors color corrected.
513 return false;
516 nscolor nsXPLookAndFeel::GetStandinForNativeColor(ColorID aID,
517 ColorScheme aScheme) {
518 if (aScheme == ColorScheme::Dark) {
519 if (auto color = GenericDarkColor(aID)) {
520 return *color;
524 // The stand-in colors are taken from what the non-native theme needs (for
525 // field/button colors), the Windows 7 Aero theme except Mac-specific colors
526 // which are taken from Mac OS 10.7.
528 #define COLOR(name_, r, g, b) \
529 case ColorID::name_: \
530 return NS_RGB(r, g, b);
532 #define COLORA(name_, r, g, b, a) \
533 case ColorID::name_: \
534 return NS_RGBA(r, g, b, a);
536 switch (aID) {
537 // These are here for the purposes of headless mode.
538 case ColorID::IMESelectedRawTextBackground:
539 case ColorID::IMESelectedConvertedTextBackground:
540 case ColorID::IMERawInputBackground:
541 case ColorID::IMEConvertedTextBackground:
542 return NS_TRANSPARENT;
543 case ColorID::IMESelectedRawTextForeground:
544 case ColorID::IMESelectedConvertedTextForeground:
545 case ColorID::IMERawInputForeground:
546 case ColorID::IMEConvertedTextForeground:
547 return NS_SAME_AS_FOREGROUND_COLOR;
548 case ColorID::IMERawInputUnderline:
549 case ColorID::IMEConvertedTextUnderline:
550 return NS_40PERCENT_FOREGROUND_COLOR;
551 COLOR(MozAccentColor, 53, 132, 228)
552 COLOR(MozAccentColorForeground, 0xff, 0xff, 0xff)
553 COLOR(SpellCheckerUnderline, 0xff, 0x00, 0x00)
554 COLOR(TextSelectDisabledBackground, 0xaa, 0xaa, 0xaa)
556 // CSS 2 colors:
557 COLOR(Activeborder, 0xB4, 0xB4, 0xB4)
558 COLOR(Activecaption, 0x99, 0xB4, 0xD1)
559 COLOR(Appworkspace, 0xAB, 0xAB, 0xAB)
560 COLOR(Background, 0x00, 0x00, 0x00)
561 COLOR(Buttonhighlight, 0xFF, 0xFF, 0xFF)
562 COLOR(Buttonshadow, 0xA0, 0xA0, 0xA0)
564 // Buttons and comboboxes should be kept in sync since they are drawn with
565 // the same colors by the non-native theme.
566 COLOR(Buttonface, 0xe9, 0xe9, 0xed)
567 COLORA(MozButtondisabledface, 0xe9, 0xe9, 0xed, 128)
569 COLOR(MozCombobox, 0xe9, 0xe9, 0xed)
571 COLOR(Buttontext, 0x00, 0x00, 0x00)
572 COLOR(MozComboboxtext, 0x00, 0x00, 0x00)
574 COLOR(Captiontext, 0x00, 0x00, 0x00)
575 COLOR(Graytext, 0x6D, 0x6D, 0x6D)
576 COLOR(Highlight, 0x33, 0x99, 0xFF)
577 COLOR(Highlighttext, 0xFF, 0xFF, 0xFF)
578 COLOR(Inactiveborder, 0xF4, 0xF7, 0xFC)
579 COLOR(Inactivecaption, 0xBF, 0xCD, 0xDB)
580 COLOR(Inactivecaptiontext, 0x43, 0x4E, 0x54)
581 COLOR(Infobackground, 0xFF, 0xFF, 0xE1)
582 COLOR(Infotext, 0x00, 0x00, 0x00)
583 COLOR(Menu, 0xF0, 0xF0, 0xF0)
584 COLOR(Menutext, 0x00, 0x00, 0x00)
585 COLOR(Scrollbar, 0xC8, 0xC8, 0xC8)
586 COLOR(Threeddarkshadow, 0x69, 0x69, 0x69)
587 COLOR(Threedface, 0xF0, 0xF0, 0xF0)
588 COLOR(Threedhighlight, 0xFF, 0xFF, 0xFF)
589 COLOR(Threedlightshadow, 0xE3, 0xE3, 0xE3)
590 COLOR(Threedshadow, 0xA0, 0xA0, 0xA0)
591 COLOR(Window, 0xFF, 0xFF, 0xFF)
592 COLOR(Windowframe, 0x64, 0x64, 0x64)
593 COLOR(Windowtext, 0x00, 0x00, 0x00)
594 COLOR(MozButtondefault, 0x69, 0x69, 0x69)
595 COLOR(Field, 0xFF, 0xFF, 0xFF)
596 COLORA(MozDisabledfield, 0xFF, 0xFF, 0xFF, 128)
597 COLOR(Fieldtext, 0x00, 0x00, 0x00)
598 COLOR(MozDialog, 0xF0, 0xF0, 0xF0)
599 COLOR(MozDialogtext, 0x00, 0x00, 0x00)
600 COLOR(MozColheadertext, 0x00, 0x00, 0x00)
601 COLOR(MozColheaderhovertext, 0x00, 0x00, 0x00)
602 COLOR(MozDragtargetzone, 0xFF, 0xFF, 0xFF)
603 COLOR(MozCellhighlight, 0xF0, 0xF0, 0xF0)
604 COLOR(MozCellhighlighttext, 0x00, 0x00, 0x00)
605 COLOR(Selecteditem, 0x33, 0x99, 0xFF)
606 COLOR(Selecteditemtext, 0xFF, 0xFF, 0xFF)
607 COLOR(MozButtonhoverface, 0xd0, 0xd0, 0xd7)
608 COLOR(MozButtonhovertext, 0x00, 0x00, 0x00)
609 COLOR(MozButtonactiveface, 0xb1, 0xb1, 0xb9)
610 COLOR(MozButtonactivetext, 0x00, 0x00, 0x00)
611 COLOR(MozMenuhover, 0x33, 0x99, 0xFF)
612 COLOR(MozMenuhovertext, 0x00, 0x00, 0x00)
613 COLOR(MozMenubartext, 0x00, 0x00, 0x00)
614 COLOR(MozMenubarhovertext, 0x00, 0x00, 0x00)
615 COLOR(MozEventreerow, 0xFF, 0xFF, 0xFF)
616 COLOR(MozOddtreerow, 0xFF, 0xFF, 0xFF)
617 COLOR(MozMacChromeActive, 0xB2, 0xB2, 0xB2)
618 COLOR(MozMacChromeInactive, 0xE1, 0xE1, 0xE1)
619 COLOR(MozMacFocusring, 0x60, 0x9D, 0xD7)
620 COLOR(MozMacMenuselect, 0x38, 0x75, 0xD7)
621 COLOR(MozMacMenushadow, 0xA3, 0xA3, 0xA3)
622 COLOR(MozMacMenutextdisable, 0x88, 0x88, 0x88)
623 COLOR(MozMacMenutextselect, 0xFF, 0xFF, 0xFF)
624 COLOR(MozMacDisabledtoolbartext, 0x3F, 0x3F, 0x3F)
625 COLOR(MozMacSecondaryhighlight, 0xD4, 0xD4, 0xD4)
626 COLOR(MozMacVibrantTitlebarLight, 0xf7, 0xf7, 0xf7)
627 COLOR(MozMacVibrantTitlebarDark, 0x28, 0x28, 0x28)
628 COLOR(MozMacMenupopup, 0xe6, 0xe6, 0xe6)
629 COLOR(MozMacMenuitem, 0xe6, 0xe6, 0xe6)
630 COLOR(MozMacActiveMenuitem, 0x0a, 0x64, 0xdc)
631 COLOR(MozMacSourceList, 0xf7, 0xf7, 0xf7)
632 COLOR(MozMacSourceListSelection, 0xc8, 0xc8, 0xc8)
633 COLOR(MozMacActiveSourceListSelection, 0x0a, 0x64, 0xdc)
634 COLOR(MozMacTooltip, 0xf7, 0xf7, 0xf7)
635 // Seems to be the default color (hardcoded because of bug 1065998)
636 COLOR(MozWinMediatext, 0xFF, 0xFF, 0xFF)
637 COLOR(MozWinCommunicationstext, 0xFF, 0xFF, 0xFF)
638 COLOR(MozNativehyperlinktext, 0x00, 0x66, 0xCC)
639 COLOR(MozNativevisitedhyperlinktext, 0x55, 0x1A, 0x8B)
640 default:
641 break;
643 return NS_RGB(0xFF, 0xFF, 0xFF);
646 #undef COLOR
647 #undef COLORA
649 // Taken from in-content/common.inc.css's dark theme.
650 Maybe<nscolor> nsXPLookAndFeel::GenericDarkColor(ColorID aID) {
651 nscolor color = NS_RGB(0, 0, 0);
652 static constexpr nscolor kWindowBackground = NS_RGB(28, 27, 34);
653 static constexpr nscolor kWindowText = NS_RGB(251, 251, 254);
654 switch (aID) {
655 case ColorID::Window: // --in-content-page-background
656 case ColorID::Background:
657 case ColorID::Menu:
658 color = kWindowBackground;
659 break;
660 case ColorID::MozOddtreerow:
661 case ColorID::MozDialog: // --in-content-box-background
662 color = NS_RGB(35, 34, 43);
663 break;
664 case ColorID::Windowtext: // --in-content-page-color
665 case ColorID::Menutext:
666 case ColorID::MozDialogtext:
667 case ColorID::Fieldtext:
668 case ColorID::Buttontext: // --in-content-button-text-color (via
669 // --in-content-page-color)
670 case ColorID::MozComboboxtext:
671 case ColorID::MozButtonhovertext:
672 case ColorID::MozButtonactivetext:
673 color = kWindowText;
674 break;
675 case ColorID::Buttonshadow:
676 case ColorID::Threedshadow:
677 case ColorID::Threedlightshadow: // --in-content-box-border-color computed
678 // with kWindowText above
679 // kWindowBackground.
680 case ColorID::Graytext: // opacity: 0.4 of kWindowText blended over the
681 // "Window" background color, which happens to be
682 // the same :-)
683 color = NS_ComposeColors(kWindowBackground, NS_RGBA(251, 251, 254, 102));
684 break;
685 case ColorID::MozCellhighlight:
686 case ColorID::Selecteditem: // --in-content-primary-button-background /
687 // --in-content-item-selected
688 color = NS_RGB(0, 221, 255);
689 break;
690 case ColorID::Field:
691 case ColorID::Buttonface: // --in-content-button-background
692 case ColorID::Threedface:
693 case ColorID::MozCombobox:
694 case ColorID::MozCellhighlighttext:
695 case ColorID::Selecteditemtext: // --in-content-primary-button-text-color /
696 // --in-content-item-selected-text
697 color = NS_RGB(43, 42, 51);
698 break;
699 case ColorID::Threeddarkshadow: // Same as Threedlightshadow but with the
700 // background.
701 case ColorID::MozDisabledfield: // opacity: 0.4 of the face above blended
702 // over the "Window" background color.
703 case ColorID::MozButtondisabledface:
704 color = NS_ComposeColors(kWindowBackground, NS_RGBA(43, 42, 51, 102));
705 break;
706 case ColorID::MozButtonhoverface: // --in-content-button-background-hover
707 color = NS_RGB(82, 82, 94);
708 break;
709 case ColorID::MozButtonactiveface: // --in-content-button-background-active
710 color = NS_RGB(91, 91, 102);
711 break;
712 case ColorID::Highlight:
713 color = NS_RGBA(0, 221, 255, 78);
714 break;
715 case ColorID::Highlighttext:
716 color = NS_SAME_AS_FOREGROUND_COLOR;
717 break;
718 case ColorID::MozNativehyperlinktext:
719 // If you change this color, you probably also want to change the default
720 // value of browser.anchor_color.dark.
721 color = NS_RGB(0x8c, 0x8c, 0xff);
722 break;
723 case ColorID::MozNativevisitedhyperlinktext:
724 // If you change this color, you probably also want to change the default
725 // value of browser.visited_color.dark.
726 color = NS_RGB(0xff, 0xad, 0xff);
727 break;
729 default:
730 return Nothing();
732 return Some(color);
735 // Uncomment the #define below if you want to debug system color use in a skin
736 // that uses them. When set, it will make all system color pairs that are
737 // appropriate for foreground/background pairing the same. This means if the
738 // skin is using system colors correctly you will not be able to see *any* text.
740 // #define DEBUG_SYSTEM_COLOR_USE
742 #ifdef DEBUG_SYSTEM_COLOR_USE
743 static nsresult SystemColorUseDebuggingColor(LookAndFeel::ColorID aID,
744 nscolor& aResult) {
745 using ColorID = LookAndFeel::ColorID;
747 switch (aID) {
748 // css2 http://www.w3.org/TR/REC-CSS2/ui.html#system-colors
749 case ColorID::Activecaption:
750 // active window caption background
751 case ColorID::Captiontext:
752 // text in active window caption
753 aResult = NS_RGB(0xff, 0x00, 0x00);
754 break;
756 case ColorID::Highlight:
757 // background of selected item
758 case ColorID::Highlighttext:
759 // text of selected item
760 aResult = NS_RGB(0xff, 0xff, 0x00);
761 break;
763 case ColorID::Inactivecaption:
764 // inactive window caption
765 case ColorID::Inactivecaptiontext:
766 // text in inactive window caption
767 aResult = NS_RGB(0x66, 0x66, 0x00);
768 break;
770 case ColorID::Infobackground:
771 // tooltip background color
772 case ColorID::Infotext:
773 // tooltip text color
774 aResult = NS_RGB(0x00, 0xff, 0x00);
775 break;
777 case ColorID::Menu:
778 // menu background
779 case ColorID::Menutext:
780 // menu text
781 aResult = NS_RGB(0x00, 0xff, 0xff);
782 break;
784 case ColorID::Threedface:
785 case ColorID::Buttonface:
786 // 3-D face color
787 case ColorID::Buttontext:
788 // text on push buttons
789 aResult = NS_RGB(0x00, 0x66, 0x66);
790 break;
792 case ColorID::Window:
793 case ColorID::Windowtext:
794 aResult = NS_RGB(0x00, 0x00, 0xff);
795 break;
797 // from the CSS3 working draft (not yet finalized)
798 // http://www.w3.org/tr/2000/wd-css3-userint-20000216.html#color
800 case ColorID::Field:
801 case ColorID::Fieldtext:
802 aResult = NS_RGB(0xff, 0x00, 0xff);
803 break;
805 case ColorID::MozDialog:
806 case ColorID::MozDialogtext:
807 aResult = NS_RGB(0x66, 0x00, 0x66);
808 break;
810 default:
811 return NS_ERROR_NOT_AVAILABLE;
814 return NS_OK;
816 #endif
818 static nsresult GetPrefColor(const char* aPref, nscolor& aResult) {
819 nsAutoCString colorStr;
820 MOZ_TRY(Preferences::GetCString(aPref, colorStr));
821 if (!ServoCSSParser::ComputeColor(nullptr, NS_RGB(0, 0, 0), colorStr,
822 &aResult)) {
823 return NS_ERROR_FAILURE;
825 return NS_OK;
828 static nsresult GetColorFromPref(LookAndFeel::ColorID aID, ColorScheme aScheme,
829 nscolor& aResult) {
830 const char* prefName = sColorPrefs[size_t(aID)];
831 if (aScheme == ColorScheme::Dark) {
832 nsAutoCString darkPrefName(prefName);
833 darkPrefName.Append(".dark");
834 if (NS_SUCCEEDED(GetPrefColor(darkPrefName.get(), aResult))) {
835 return NS_OK;
838 return GetPrefColor(prefName, aResult);
841 // All these routines will return NS_OK if they have a value,
842 // in which case the nsLookAndFeel should use that value;
843 // otherwise we'll return NS_ERROR_NOT_AVAILABLE, in which case, the
844 // platform-specific nsLookAndFeel should use its own values instead.
845 nsresult nsXPLookAndFeel::GetColorValue(ColorID aID, ColorScheme aScheme,
846 UseStandins aUseStandins,
847 nscolor& aResult) {
848 if (!sInitialized) {
849 Init();
852 #ifdef DEBUG_SYSTEM_COLOR_USE
853 if (NS_SUCCEEDED(SystemColorUseDebuggingColor(aID, aResult))) {
854 return NS_OK;
856 #endif
858 if (aUseStandins == UseStandins::Yes) {
859 aResult = GetStandinForNativeColor(aID, aScheme);
860 return NS_OK;
863 auto& cache =
864 aScheme == ColorScheme::Light ? sLightColorCache : sDarkColorCache;
865 if (const auto* cached = cache.Get(aID)) {
866 if (cached->isNothing()) {
867 return NS_ERROR_FAILURE;
869 aResult = cached->value();
870 return NS_OK;
873 if (NS_SUCCEEDED(GetColorFromPref(aID, aScheme, aResult))) {
874 cache.Insert(aID, Some(aResult));
875 return NS_OK;
878 if (NS_SUCCEEDED(NativeGetColor(aID, aScheme, aResult))) {
879 if (gfxPlatform::GetCMSMode() == CMSMode::All &&
880 !IsSpecialColor(aID, aResult)) {
881 qcms_transform* transform = gfxPlatform::GetCMSInverseRGBTransform();
882 if (transform) {
883 uint8_t color[4];
884 color[0] = NS_GET_R(aResult);
885 color[1] = NS_GET_G(aResult);
886 color[2] = NS_GET_B(aResult);
887 color[3] = NS_GET_A(aResult);
888 qcms_transform_data(transform, color, color, 1);
889 aResult = NS_RGBA(color[0], color[1], color[2], color[3]);
893 // NOTE: Servo holds a lock and the main thread is paused, so writing to the
894 // global cache here is fine.
895 cache.Insert(aID, Some(aResult));
896 return NS_OK;
899 cache.Insert(aID, Nothing());
900 return NS_ERROR_FAILURE;
903 nsresult nsXPLookAndFeel::GetIntValue(IntID aID, int32_t& aResult) {
904 if (!sInitialized) {
905 Init();
908 if (const auto* cached = sIntCache.Get(aID)) {
909 if (cached->isNothing()) {
910 return NS_ERROR_FAILURE;
912 aResult = cached->value();
913 return NS_OK;
916 if (NS_SUCCEEDED(Preferences::GetInt(sIntPrefs[size_t(aID)], &aResult))) {
917 sIntCache.Insert(aID, Some(aResult));
918 return NS_OK;
921 if (NS_FAILED(NativeGetInt(aID, aResult))) {
922 sIntCache.Insert(aID, Nothing());
923 return NS_ERROR_FAILURE;
926 sIntCache.Insert(aID, Some(aResult));
927 return NS_OK;
930 nsresult nsXPLookAndFeel::GetFloatValue(FloatID aID, float& aResult) {
931 if (!sInitialized) {
932 Init();
935 if (const auto* cached = sFloatCache.Get(aID)) {
936 if (cached->isNothing()) {
937 return NS_ERROR_FAILURE;
939 aResult = cached->value();
940 return NS_OK;
943 int32_t pref = 0;
944 if (NS_SUCCEEDED(Preferences::GetInt(sFloatPrefs[size_t(aID)], &pref))) {
945 aResult = float(pref) / 100.0f;
946 sFloatCache.Insert(aID, Some(aResult));
947 return NS_OK;
950 if (NS_FAILED(NativeGetFloat(aID, aResult))) {
951 sFloatCache.Insert(aID, Nothing());
952 return NS_ERROR_FAILURE;
955 sFloatCache.Insert(aID, Some(aResult));
956 return NS_OK;
959 bool nsXPLookAndFeel::LookAndFeelFontToStyle(const LookAndFeelFont& aFont,
960 nsString& aName,
961 gfxFontStyle& aStyle) {
962 if (!aFont.haveFont()) {
963 return false;
965 aName = aFont.name();
966 aStyle = gfxFontStyle();
967 aStyle.size = aFont.size();
968 aStyle.weight = FontWeight(aFont.weight());
969 aStyle.style =
970 aFont.italic() ? FontSlantStyle::Italic() : FontSlantStyle::Normal();
971 aStyle.systemFont = true;
972 return true;
975 widget::LookAndFeelFont nsXPLookAndFeel::StyleToLookAndFeelFont(
976 const nsAString& aName, const gfxFontStyle& aStyle) {
977 LookAndFeelFont font;
978 font.haveFont() = true;
979 font.name() = aName;
980 font.size() = aStyle.size;
981 font.weight() = aStyle.weight.ToFloat();
982 font.italic() = aStyle.style.IsItalic();
983 MOZ_ASSERT(aStyle.style.IsNormal() || aStyle.style.IsItalic(),
984 "Cannot handle oblique font style");
985 #ifdef DEBUG
987 // Assert that all the remaining font style properties have their
988 // default values.
989 gfxFontStyle candidate = aStyle;
990 gfxFontStyle defaults{};
991 candidate.size = defaults.size;
992 candidate.weight = defaults.weight;
993 candidate.style = defaults.style;
994 MOZ_ASSERT(candidate.Equals(defaults),
995 "Some font style properties not supported");
997 #endif
998 return font;
1001 bool nsXPLookAndFeel::GetFontValue(FontID aID, nsString& aName,
1002 gfxFontStyle& aStyle) {
1003 if (const LookAndFeelFont* cached = sFontCache.Get(aID)) {
1004 return LookAndFeelFontToStyle(*cached, aName, aStyle);
1006 LookAndFeelFont font;
1007 const bool haveFont = NativeGetFont(aID, aName, aStyle);
1008 font.haveFont() = haveFont;
1009 if (haveFont) {
1010 font = StyleToLookAndFeelFont(aName, aStyle);
1012 sFontCache.Insert(aID, std::move(font));
1013 return haveFont;
1016 void nsXPLookAndFeel::RefreshImpl() {
1017 // Wipe out our caches.
1018 sLightColorCache.Clear();
1019 sDarkColorCache.Clear();
1020 sFontCache.Clear();
1021 sFloatCache.Clear();
1022 sIntCache.Clear();
1023 RecomputeColorSchemes();
1025 // Clear any cached FullLookAndFeel data, which is now invalid.
1026 if (XRE_IsParentProcess()) {
1027 widget::RemoteLookAndFeel::ClearCachedData();
1031 static bool sRecordedLookAndFeelTelemetry = false;
1033 void nsXPLookAndFeel::RecordTelemetry() {
1034 if (!XRE_IsParentProcess()) {
1035 return;
1038 if (sRecordedLookAndFeelTelemetry) {
1039 return;
1042 sRecordedLookAndFeelTelemetry = true;
1044 int32_t i;
1045 Telemetry::ScalarSet(
1046 Telemetry::ScalarID::WIDGET_DARK_MODE,
1047 NS_SUCCEEDED(GetIntValue(IntID::SystemUsesDarkTheme, i)) && i != 0);
1049 RecordLookAndFeelSpecificTelemetry();
1052 namespace mozilla {
1054 static widget::ThemeChangeKind sGlobalThemeChangeKind{0};
1056 void LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind aKind) {
1057 sGlobalThemeChanged = true;
1058 sGlobalThemeChangeKind |= aKind;
1060 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
1061 const char16_t kind[] = {char16_t(aKind), 0};
1062 obs->NotifyObservers(nullptr, "internal-look-and-feel-changed", kind);
1066 void LookAndFeel::DoHandleGlobalThemeChange() {
1067 MOZ_ASSERT(sGlobalThemeChanged);
1068 sGlobalThemeChanged = false;
1069 auto kind = std::exchange(sGlobalThemeChangeKind, widget::ThemeChangeKind(0));
1071 // Tell the theme that it changed, so it can flush any handles to stale theme
1072 // data.
1074 // We can use the *DoNotUseDirectly functions directly here, because we want
1075 // to notify all possible themes in a given process (but just once).
1076 if (XRE_IsParentProcess() ||
1077 !StaticPrefs::widget_non_native_theme_enabled()) {
1078 if (nsCOMPtr<nsITheme> theme = do_GetNativeThemeDoNotUseDirectly()) {
1079 theme->ThemeChanged();
1082 if (nsCOMPtr<nsITheme> theme = do_GetBasicNativeThemeDoNotUseDirectly()) {
1083 theme->ThemeChanged();
1086 // Clear all cached LookAndFeel colors.
1087 LookAndFeel::Refresh();
1089 // Reset default background and foreground colors for the document since they
1090 // may be using system colors.
1091 PreferenceSheet::Refresh();
1093 // Vector images (SVG) may be using theme colors so we discard all cached
1094 // surfaces. (We could add a vector image only version of DiscardAll, but
1095 // in bug 940625 we decided theme changes are rare enough not to bother.)
1096 image::SurfaceCacheUtils::DiscardAll();
1098 if (XRE_IsParentProcess()) {
1099 dom::ContentParent::BroadcastThemeUpdate(kind);
1102 nsContentUtils::AddScriptRunner(
1103 NS_NewRunnableFunction("HandleGlobalThemeChange", [] {
1104 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
1105 obs->NotifyObservers(nullptr, "look-and-feel-changed", nullptr);
1107 }));
1110 static bool ShouldUseStandinsForNativeColorForNonNativeTheme(
1111 const dom::Document& aDoc, LookAndFeel::ColorID aColor,
1112 const PreferenceSheet::Prefs& aPrefs) {
1113 using ColorID = LookAndFeel::ColorID;
1114 if (!aDoc.ShouldAvoidNativeTheme()) {
1115 return false;
1118 // The native theme doesn't use native system colors backgrounds etc, except
1119 // when in high-contrast mode, so spoof some of the colors with stand-ins to
1120 // prevent lack of contrast.
1121 switch (aColor) {
1122 case ColorID::Buttonface:
1123 case ColorID::Buttontext:
1124 case ColorID::MozButtonhoverface:
1125 case ColorID::MozButtonhovertext:
1126 case ColorID::MozButtonactiveface:
1127 case ColorID::MozButtonactivetext:
1128 case ColorID::MozButtondisabledface:
1130 case ColorID::Threedlightshadow:
1131 case ColorID::Threeddarkshadow:
1132 case ColorID::Threedface:
1134 case ColorID::MozCombobox:
1135 case ColorID::MozComboboxtext:
1137 case ColorID::Field:
1138 case ColorID::MozDisabledfield:
1139 case ColorID::Fieldtext:
1141 case ColorID::Graytext:
1142 return !aPrefs.NonNativeThemeShouldBeHighContrast();
1144 default:
1145 break;
1148 return false;
1151 ColorScheme LookAndFeel::sChromeColorScheme;
1152 ColorScheme LookAndFeel::sContentColorScheme;
1153 bool LookAndFeel::sColorSchemeInitialized;
1154 bool LookAndFeel::sGlobalThemeChanged;
1156 bool LookAndFeel::IsDarkColor(nscolor aColor) {
1157 // Given https://www.w3.org/TR/WCAG20/#contrast-ratiodef, this is the
1158 // threshold that tells us whether contrast is better against white or black.
1160 // Contrast ratio against black is: (L + 0.05) / 0.05
1161 // Contrast ratio against white is: 1.05 / (L + 0.05)
1163 // So the intersection is:
1165 // (L + 0.05) / 0.05 = 1.05 / (L + 0.05)
1167 // And the solution to that equation is:
1169 // sqrt(1.05 * 0.05) - 0.05
1171 // So we consider a color dark if the contrast is below this threshold, and
1172 // it's at least half-opaque.
1173 constexpr float kThreshold = 0.179129;
1174 return NS_GET_A(aColor) > 127 &&
1175 RelativeLuminanceUtils::Compute(aColor) < kThreshold;
1178 auto LookAndFeel::ColorSchemeSettingForChrome() -> ChromeColorSchemeSetting {
1179 switch (StaticPrefs::browser_theme_toolbar_theme()) {
1180 case 0: // Dark
1181 return ChromeColorSchemeSetting::Dark;
1182 case 1: // Light
1183 return ChromeColorSchemeSetting::Light;
1184 default:
1185 return ChromeColorSchemeSetting::System;
1189 ColorScheme LookAndFeel::ThemeDerivedColorSchemeForContent() {
1190 switch (StaticPrefs::browser_theme_content_theme()) {
1191 case 0: // Dark
1192 return ColorScheme::Dark;
1193 case 1: // Light
1194 return ColorScheme::Light;
1195 default:
1196 return SystemColorScheme();
1200 void LookAndFeel::RecomputeColorSchemes() {
1201 sColorSchemeInitialized = true;
1203 sChromeColorScheme = [] {
1204 switch (ColorSchemeSettingForChrome()) {
1205 case ChromeColorSchemeSetting::Light:
1206 return ColorScheme::Light;
1207 case ChromeColorSchemeSetting::Dark:
1208 return ColorScheme::Dark;
1209 case ChromeColorSchemeSetting::System:
1210 break;
1212 return SystemColorScheme();
1213 }();
1215 sContentColorScheme = [] {
1216 switch (StaticPrefs::layout_css_prefers_color_scheme_content_override()) {
1217 case 0:
1218 return ColorScheme::Dark;
1219 case 1:
1220 return ColorScheme::Light;
1221 case 2:
1222 return SystemColorScheme();
1223 default:
1224 return ThemeDerivedColorSchemeForContent();
1226 }();
1229 ColorScheme LookAndFeel::ColorSchemeForStyle(
1230 const dom::Document& aDoc, const StyleColorSchemeFlags& aFlags) {
1231 using Choice = PreferenceSheet::Prefs::ColorSchemeChoice;
1233 const auto& prefs = PreferenceSheet::PrefsFor(aDoc);
1234 switch (prefs.mColorSchemeChoice) {
1235 case Choice::Standard:
1236 break;
1237 case Choice::UserPreferred:
1238 return aDoc.PreferredColorScheme();
1239 case Choice::Light:
1240 return ColorScheme::Light;
1241 case Choice::Dark:
1242 return ColorScheme::Dark;
1245 StyleColorSchemeFlags style(aFlags);
1246 if (!style) {
1247 style.bits = aDoc.GetColorSchemeBits();
1249 const bool supportsDark = bool(style & StyleColorSchemeFlags::DARK);
1250 const bool supportsLight = bool(style & StyleColorSchemeFlags::LIGHT);
1251 if (supportsLight && supportsDark) {
1252 // Both color-schemes are explicitly supported, use the preferred one.
1253 return aDoc.PreferredColorScheme();
1255 if (supportsDark || supportsLight) {
1256 // One color-scheme is explicitly supported and one isn't, so use the one
1257 // the content supports.
1258 return supportsDark ? ColorScheme::Dark : ColorScheme::Light;
1260 // No value specified. Chrome docs always supports both, so use the preferred
1261 // color-scheme.
1262 if (nsContentUtils::IsChromeDoc(&aDoc)) {
1263 return aDoc.PreferredColorScheme();
1265 // Default content to light.
1266 return ColorScheme::Light;
1269 LookAndFeel::ColorScheme LookAndFeel::ColorSchemeForFrame(
1270 const nsIFrame* aFrame) {
1271 return ColorSchemeForStyle(*aFrame->PresContext()->Document(),
1272 aFrame->StyleUI()->mColorScheme.bits);
1275 // static
1276 Maybe<nscolor> LookAndFeel::GetColor(ColorID aId, ColorScheme aScheme,
1277 UseStandins aUseStandins) {
1278 nscolor result;
1279 nsresult rv = nsLookAndFeel::GetInstance()->GetColorValue(
1280 aId, aScheme, aUseStandins, result);
1281 if (NS_FAILED(rv)) {
1282 return Nothing();
1284 return Some(result);
1287 // Returns whether there is a CSS color name for this color.
1288 static bool ColorIsCSSAccessible(LookAndFeel::ColorID aId) {
1289 using ColorID = LookAndFeel::ColorID;
1291 switch (aId) {
1292 case ColorID::TextSelectDisabledBackground:
1293 case ColorID::TextSelectAttentionBackground:
1294 case ColorID::TextSelectAttentionForeground:
1295 case ColorID::TextHighlightBackground:
1296 case ColorID::TextHighlightForeground:
1297 case ColorID::ThemedScrollbar:
1298 case ColorID::ThemedScrollbarInactive:
1299 case ColorID::ThemedScrollbarThumb:
1300 case ColorID::ThemedScrollbarThumbActive:
1301 case ColorID::ThemedScrollbarThumbInactive:
1302 case ColorID::ThemedScrollbarThumbHover:
1303 case ColorID::IMERawInputBackground:
1304 case ColorID::IMERawInputForeground:
1305 case ColorID::IMERawInputUnderline:
1306 case ColorID::IMESelectedRawTextBackground:
1307 case ColorID::IMESelectedRawTextForeground:
1308 case ColorID::IMESelectedRawTextUnderline:
1309 case ColorID::IMEConvertedTextBackground:
1310 case ColorID::IMEConvertedTextForeground:
1311 case ColorID::IMEConvertedTextUnderline:
1312 case ColorID::IMESelectedConvertedTextBackground:
1313 case ColorID::IMESelectedConvertedTextForeground:
1314 case ColorID::IMESelectedConvertedTextUnderline:
1315 case ColorID::SpellCheckerUnderline:
1316 return false;
1317 default:
1318 break;
1321 return true;
1324 LookAndFeel::UseStandins LookAndFeel::ShouldUseStandins(
1325 const dom::Document& aDoc, ColorID aId) {
1326 const auto& prefs = PreferenceSheet::PrefsFor(aDoc);
1327 if (ShouldUseStandinsForNativeColorForNonNativeTheme(aDoc, aId, prefs)) {
1328 return UseStandins::Yes;
1330 if (prefs.mUseStandins && ColorIsCSSAccessible(aId)) {
1331 return UseStandins::Yes;
1333 return UseStandins::No;
1336 Maybe<nscolor> LookAndFeel::GetColor(ColorID aId, const nsIFrame* aFrame) {
1337 const auto* doc = aFrame->PresContext()->Document();
1338 return GetColor(aId, ColorSchemeForFrame(aFrame),
1339 ShouldUseStandins(*doc, aId));
1342 // static
1343 nsresult LookAndFeel::GetInt(IntID aID, int32_t* aResult) {
1344 return nsLookAndFeel::GetInstance()->GetIntValue(aID, *aResult);
1347 // static
1348 nsresult LookAndFeel::GetFloat(FloatID aID, float* aResult) {
1349 return nsLookAndFeel::GetInstance()->GetFloatValue(aID, *aResult);
1352 // static
1353 bool LookAndFeel::GetFont(FontID aID, nsString& aName, gfxFontStyle& aStyle) {
1354 return nsLookAndFeel::GetInstance()->GetFontValue(aID, aName, aStyle);
1357 // static
1358 char16_t LookAndFeel::GetPasswordCharacter() {
1359 return nsLookAndFeel::GetInstance()->GetPasswordCharacterImpl();
1362 // static
1363 bool LookAndFeel::GetEchoPassword() {
1364 if (StaticPrefs::editor_password_mask_delay() >= 0) {
1365 return StaticPrefs::editor_password_mask_delay() > 0;
1367 return nsLookAndFeel::GetInstance()->GetEchoPasswordImpl();
1370 // static
1371 uint32_t LookAndFeel::GetPasswordMaskDelay() {
1372 int32_t delay = StaticPrefs::editor_password_mask_delay();
1373 if (delay < 0) {
1374 return nsLookAndFeel::GetInstance()->GetPasswordMaskDelayImpl();
1376 return delay;
1379 bool LookAndFeel::DrawInTitlebar() {
1380 switch (StaticPrefs::browser_tabs_inTitlebar()) {
1381 case 0:
1382 return false;
1383 case 1:
1384 return true;
1385 default:
1386 break;
1388 return nsLookAndFeel::GetInstance()->GetDefaultDrawInTitlebar();
1391 void LookAndFeel::GetThemeInfo(nsACString& aOut) {
1392 nsLookAndFeel::GetInstance()->GetThemeInfo(aOut);
1395 // static
1396 void LookAndFeel::Refresh() {
1397 nsLookAndFeel::GetInstance()->RefreshImpl();
1398 widget::Theme::LookAndFeelChanged();
1401 // static
1402 void LookAndFeel::NativeInit() { nsLookAndFeel::GetInstance()->NativeInit(); }
1404 // static
1405 void LookAndFeel::SetData(widget::FullLookAndFeel&& aTables) {
1406 nsLookAndFeel::GetInstance()->SetDataImpl(std::move(aTables));
1409 } // namespace mozilla