Bug 1755316 - Add audio tests with simultaneous processes r=alwu
[gecko.git] / widget / nsXPLookAndFeel.cpp
blob9deb3c1f9030917ff4f43f94b7640407793f63ad
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 {"browser.proton.places-tooltip.enabled"_ns},
435 // Affects env().
436 {"layout.css.prefers-color-scheme.content-override"_ns,
437 widget::ThemeChangeKind::Style},
438 // Affects media queries and scrollbar sizes, so gotta relayout.
439 {"widget.gtk.overlay-scrollbars.enabled"_ns,
440 widget::ThemeChangeKind::StyleAndLayout},
441 // This affects not only the media query, but also the native theme, so we
442 // need to re-layout.
443 {"browser.theme.toolbar-theme"_ns, widget::ThemeChangeKind::AllBits},
444 {"browser.theme.content-theme"_ns},
447 // Read values from the user's preferences.
448 // This is done once at startup, but since the user's preferences
449 // haven't actually been read yet at that time, we also have to
450 // set a callback to inform us of changes to each pref.
451 void nsXPLookAndFeel::Init() {
452 MOZ_RELEASE_ASSERT(NS_IsMainThread());
454 // Say we're already initialized, and take the chance that it might fail;
455 // protects against some other process writing to our static variables.
456 sInitialized = true;
458 RecomputeColorSchemes();
460 // XXX If we could reorganize the pref names, we should separate the branch
461 // for each types. Then, we could reduce the unnecessary loop from
462 // nsXPLookAndFeel::OnPrefChanged().
463 Preferences::RegisterPrefixCallback(OnPrefChanged, "ui.");
464 // We really do just want the accessibility.tabfocus pref, not other prefs
465 // that start with that string.
466 Preferences::RegisterCallback(OnPrefChanged, "accessibility.tabfocus");
468 for (auto& pref : kMediaQueryPrefs) {
469 Preferences::RegisterCallback(
470 [](const char*, void* aChangeKind) {
471 auto changeKind =
472 widget::ThemeChangeKind(reinterpret_cast<uintptr_t>(aChangeKind));
473 LookAndFeel::NotifyChangedAllWindows(changeKind);
475 pref.mName, reinterpret_cast<void*>(uintptr_t(pref.mChangeKind)));
479 nsXPLookAndFeel::~nsXPLookAndFeel() {
480 NS_ASSERTION(sInstance == this,
481 "This destroying instance isn't the singleton instance");
482 sInstance = nullptr;
485 static bool IsSpecialColor(LookAndFeel::ColorID aID, nscolor aColor) {
486 using ColorID = LookAndFeel::ColorID;
488 if (aColor == NS_SAME_AS_FOREGROUND_COLOR) {
489 return true;
492 switch (aID) {
493 case ColorID::IMESelectedRawTextBackground:
494 case ColorID::IMESelectedConvertedTextBackground:
495 case ColorID::IMERawInputBackground:
496 case ColorID::IMEConvertedTextBackground:
497 case ColorID::IMESelectedRawTextForeground:
498 case ColorID::IMESelectedConvertedTextForeground:
499 case ColorID::IMERawInputForeground:
500 case ColorID::IMEConvertedTextForeground:
501 case ColorID::IMERawInputUnderline:
502 case ColorID::IMEConvertedTextUnderline:
503 case ColorID::IMESelectedRawTextUnderline:
504 case ColorID::IMESelectedConvertedTextUnderline:
505 case ColorID::SpellCheckerUnderline:
506 return NS_IS_SELECTION_SPECIAL_COLOR(aColor);
507 default:
508 break;
511 * In GetColor(), every color that is not a special color is color
512 * corrected. Use false to make other colors color corrected.
514 return false;
517 nscolor nsXPLookAndFeel::GetStandinForNativeColor(ColorID aID,
518 ColorScheme aScheme) {
519 if (aScheme == ColorScheme::Dark) {
520 if (auto color = GenericDarkColor(aID)) {
521 return *color;
525 // The stand-in colors are taken from what the non-native theme needs (for
526 // field/button colors), the Windows 7 Aero theme except Mac-specific colors
527 // which are taken from Mac OS 10.7.
529 #define COLOR(name_, r, g, b) \
530 case ColorID::name_: \
531 return NS_RGB(r, g, b);
533 #define COLORA(name_, r, g, b, a) \
534 case ColorID::name_: \
535 return NS_RGBA(r, g, b, a);
537 switch (aID) {
538 // These are here for the purposes of headless mode.
539 case ColorID::IMESelectedRawTextBackground:
540 case ColorID::IMESelectedConvertedTextBackground:
541 case ColorID::IMERawInputBackground:
542 case ColorID::IMEConvertedTextBackground:
543 return NS_TRANSPARENT;
544 case ColorID::IMESelectedRawTextForeground:
545 case ColorID::IMESelectedConvertedTextForeground:
546 case ColorID::IMERawInputForeground:
547 case ColorID::IMEConvertedTextForeground:
548 return NS_SAME_AS_FOREGROUND_COLOR;
549 case ColorID::IMERawInputUnderline:
550 case ColorID::IMEConvertedTextUnderline:
551 return NS_40PERCENT_FOREGROUND_COLOR;
552 COLOR(MozAccentColor, 53, 132, 228)
553 COLOR(MozAccentColorForeground, 0xff, 0xff, 0xff)
554 COLOR(SpellCheckerUnderline, 0xff, 0x00, 0x00)
555 COLOR(TextSelectDisabledBackground, 0xaa, 0xaa, 0xaa)
557 // CSS 2 colors:
558 COLOR(Activeborder, 0xB4, 0xB4, 0xB4)
559 COLOR(Activecaption, 0x99, 0xB4, 0xD1)
560 COLOR(Appworkspace, 0xAB, 0xAB, 0xAB)
561 COLOR(Background, 0x00, 0x00, 0x00)
562 COLOR(Buttonhighlight, 0xFF, 0xFF, 0xFF)
563 COLOR(Buttonshadow, 0xA0, 0xA0, 0xA0)
565 // Buttons and comboboxes should be kept in sync since they are drawn with
566 // the same colors by the non-native theme.
567 COLOR(Buttonface, 0xe9, 0xe9, 0xed)
568 COLORA(MozButtondisabledface, 0xe9, 0xe9, 0xed, 128)
570 COLOR(MozCombobox, 0xe9, 0xe9, 0xed)
572 COLOR(Buttontext, 0x00, 0x00, 0x00)
573 COLOR(MozComboboxtext, 0x00, 0x00, 0x00)
575 COLOR(Captiontext, 0x00, 0x00, 0x00)
576 COLOR(Graytext, 0x6D, 0x6D, 0x6D)
577 COLOR(Highlight, 0x33, 0x99, 0xFF)
578 COLOR(Highlighttext, 0xFF, 0xFF, 0xFF)
579 COLOR(Inactiveborder, 0xF4, 0xF7, 0xFC)
580 COLOR(Inactivecaption, 0xBF, 0xCD, 0xDB)
581 COLOR(Inactivecaptiontext, 0x43, 0x4E, 0x54)
582 COLOR(Infobackground, 0xFF, 0xFF, 0xE1)
583 COLOR(Infotext, 0x00, 0x00, 0x00)
584 COLOR(Menu, 0xF0, 0xF0, 0xF0)
585 COLOR(Menutext, 0x00, 0x00, 0x00)
586 COLOR(Scrollbar, 0xC8, 0xC8, 0xC8)
587 COLOR(Threeddarkshadow, 0x69, 0x69, 0x69)
588 COLOR(Threedface, 0xF0, 0xF0, 0xF0)
589 COLOR(Threedhighlight, 0xFF, 0xFF, 0xFF)
590 COLOR(Threedlightshadow, 0xE3, 0xE3, 0xE3)
591 COLOR(Threedshadow, 0xA0, 0xA0, 0xA0)
592 COLOR(Window, 0xFF, 0xFF, 0xFF)
593 COLOR(Windowframe, 0x64, 0x64, 0x64)
594 COLOR(Windowtext, 0x00, 0x00, 0x00)
595 COLOR(MozButtondefault, 0x69, 0x69, 0x69)
596 COLOR(Field, 0xFF, 0xFF, 0xFF)
597 COLORA(MozDisabledfield, 0xFF, 0xFF, 0xFF, 128)
598 COLOR(Fieldtext, 0x00, 0x00, 0x00)
599 COLOR(MozDialog, 0xF0, 0xF0, 0xF0)
600 COLOR(MozDialogtext, 0x00, 0x00, 0x00)
601 COLOR(MozColheadertext, 0x00, 0x00, 0x00)
602 COLOR(MozColheaderhovertext, 0x00, 0x00, 0x00)
603 COLOR(MozDragtargetzone, 0xFF, 0xFF, 0xFF)
604 COLOR(MozCellhighlight, 0xF0, 0xF0, 0xF0)
605 COLOR(MozCellhighlighttext, 0x00, 0x00, 0x00)
606 COLOR(Selecteditem, 0x33, 0x99, 0xFF)
607 COLOR(Selecteditemtext, 0xFF, 0xFF, 0xFF)
608 COLOR(MozButtonhoverface, 0xd0, 0xd0, 0xd7)
609 COLOR(MozButtonhovertext, 0x00, 0x00, 0x00)
610 COLOR(MozButtonactiveface, 0xb1, 0xb1, 0xb9)
611 COLOR(MozButtonactivetext, 0x00, 0x00, 0x00)
612 COLOR(MozMenuhover, 0x33, 0x99, 0xFF)
613 COLOR(MozMenuhovertext, 0x00, 0x00, 0x00)
614 COLOR(MozMenubartext, 0x00, 0x00, 0x00)
615 COLOR(MozMenubarhovertext, 0x00, 0x00, 0x00)
616 COLOR(MozEventreerow, 0xFF, 0xFF, 0xFF)
617 COLOR(MozOddtreerow, 0xFF, 0xFF, 0xFF)
618 COLOR(MozMacChromeActive, 0xB2, 0xB2, 0xB2)
619 COLOR(MozMacChromeInactive, 0xE1, 0xE1, 0xE1)
620 COLOR(MozMacFocusring, 0x60, 0x9D, 0xD7)
621 COLOR(MozMacMenuselect, 0x38, 0x75, 0xD7)
622 COLOR(MozMacMenushadow, 0xA3, 0xA3, 0xA3)
623 COLOR(MozMacMenutextdisable, 0x88, 0x88, 0x88)
624 COLOR(MozMacMenutextselect, 0xFF, 0xFF, 0xFF)
625 COLOR(MozMacDisabledtoolbartext, 0x3F, 0x3F, 0x3F)
626 COLOR(MozMacSecondaryhighlight, 0xD4, 0xD4, 0xD4)
627 COLOR(MozMacVibrantTitlebarLight, 0xf7, 0xf7, 0xf7)
628 COLOR(MozMacVibrantTitlebarDark, 0x28, 0x28, 0x28)
629 COLOR(MozMacMenupopup, 0xe6, 0xe6, 0xe6)
630 COLOR(MozMacMenuitem, 0xe6, 0xe6, 0xe6)
631 COLOR(MozMacActiveMenuitem, 0x0a, 0x64, 0xdc)
632 COLOR(MozMacSourceList, 0xf7, 0xf7, 0xf7)
633 COLOR(MozMacSourceListSelection, 0xc8, 0xc8, 0xc8)
634 COLOR(MozMacActiveSourceListSelection, 0x0a, 0x64, 0xdc)
635 COLOR(MozMacTooltip, 0xf7, 0xf7, 0xf7)
636 // Seems to be the default color (hardcoded because of bug 1065998)
637 COLOR(MozWinMediatext, 0xFF, 0xFF, 0xFF)
638 COLOR(MozWinCommunicationstext, 0xFF, 0xFF, 0xFF)
639 COLOR(MozNativehyperlinktext, 0x00, 0x66, 0xCC)
640 COLOR(MozNativevisitedhyperlinktext, 0x55, 0x1A, 0x8B)
641 default:
642 break;
644 return NS_RGB(0xFF, 0xFF, 0xFF);
647 #undef COLOR
648 #undef COLORA
650 // Taken from in-content/common.inc.css's dark theme.
651 Maybe<nscolor> nsXPLookAndFeel::GenericDarkColor(ColorID aID) {
652 nscolor color = NS_RGB(0, 0, 0);
653 static constexpr nscolor kWindowBackground = NS_RGB(28, 27, 34);
654 static constexpr nscolor kWindowText = NS_RGB(251, 251, 254);
655 switch (aID) {
656 case ColorID::Window: // --in-content-page-background
657 case ColorID::Background:
658 case ColorID::Menu:
659 color = kWindowBackground;
660 break;
661 case ColorID::MozOddtreerow:
662 case ColorID::MozDialog: // --in-content-box-background
663 color = NS_RGB(35, 34, 43);
664 break;
665 case ColorID::Windowtext: // --in-content-page-color
666 case ColorID::Menutext:
667 case ColorID::MozDialogtext:
668 case ColorID::Fieldtext:
669 case ColorID::Buttontext: // --in-content-button-text-color (via
670 // --in-content-page-color)
671 case ColorID::MozComboboxtext:
672 case ColorID::MozButtonhovertext:
673 case ColorID::MozButtonactivetext:
674 color = kWindowText;
675 break;
676 case ColorID::Buttonshadow:
677 case ColorID::Threedshadow:
678 case ColorID::Threedlightshadow: // --in-content-box-border-color computed
679 // with kWindowText above
680 // kWindowBackground.
681 case ColorID::Graytext: // opacity: 0.4 of kWindowText blended over the
682 // "Window" background color, which happens to be
683 // the same :-)
684 color = NS_ComposeColors(kWindowBackground, NS_RGBA(251, 251, 254, 102));
685 break;
686 case ColorID::MozCellhighlight:
687 case ColorID::Selecteditem: // --in-content-primary-button-background /
688 // --in-content-item-selected
689 color = NS_RGB(0, 221, 255);
690 break;
691 case ColorID::Field:
692 case ColorID::Buttonface: // --in-content-button-background
693 case ColorID::Threedface:
694 case ColorID::MozCombobox:
695 case ColorID::MozCellhighlighttext:
696 case ColorID::Selecteditemtext: // --in-content-primary-button-text-color /
697 // --in-content-item-selected-text
698 color = NS_RGB(43, 42, 51);
699 break;
700 case ColorID::Threeddarkshadow: // Same as Threedlightshadow but with the
701 // background.
702 case ColorID::MozDisabledfield: // opacity: 0.4 of the face above blended
703 // over the "Window" background color.
704 case ColorID::MozButtondisabledface:
705 color = NS_ComposeColors(kWindowBackground, NS_RGBA(43, 42, 51, 102));
706 break;
707 case ColorID::MozButtonhoverface: // --in-content-button-background-hover
708 color = NS_RGB(82, 82, 94);
709 break;
710 case ColorID::MozButtonactiveface: // --in-content-button-background-active
711 color = NS_RGB(91, 91, 102);
712 break;
713 case ColorID::Highlight:
714 color = NS_RGBA(0, 221, 255, 78);
715 break;
716 case ColorID::Highlighttext:
717 color = NS_SAME_AS_FOREGROUND_COLOR;
718 break;
719 case ColorID::MozNativehyperlinktext:
720 // If you change this color, you probably also want to change the default
721 // value of browser.anchor_color.dark.
722 color = NS_RGB(0x8c, 0x8c, 0xff);
723 break;
724 case ColorID::MozNativevisitedhyperlinktext:
725 // If you change this color, you probably also want to change the default
726 // value of browser.visited_color.dark.
727 color = NS_RGB(0xff, 0xad, 0xff);
728 break;
730 default:
731 return Nothing();
733 return Some(color);
736 // Uncomment the #define below if you want to debug system color use in a skin
737 // that uses them. When set, it will make all system color pairs that are
738 // appropriate for foreground/background pairing the same. This means if the
739 // skin is using system colors correctly you will not be able to see *any* text.
741 // #define DEBUG_SYSTEM_COLOR_USE
743 #ifdef DEBUG_SYSTEM_COLOR_USE
744 static nsresult SystemColorUseDebuggingColor(LookAndFeel::ColorID aID,
745 nscolor& aResult) {
746 using ColorID = LookAndFeel::ColorID;
748 switch (aID) {
749 // css2 http://www.w3.org/TR/REC-CSS2/ui.html#system-colors
750 case ColorID::Activecaption:
751 // active window caption background
752 case ColorID::Captiontext:
753 // text in active window caption
754 aResult = NS_RGB(0xff, 0x00, 0x00);
755 break;
757 case ColorID::Highlight:
758 // background of selected item
759 case ColorID::Highlighttext:
760 // text of selected item
761 aResult = NS_RGB(0xff, 0xff, 0x00);
762 break;
764 case ColorID::Inactivecaption:
765 // inactive window caption
766 case ColorID::Inactivecaptiontext:
767 // text in inactive window caption
768 aResult = NS_RGB(0x66, 0x66, 0x00);
769 break;
771 case ColorID::Infobackground:
772 // tooltip background color
773 case ColorID::Infotext:
774 // tooltip text color
775 aResult = NS_RGB(0x00, 0xff, 0x00);
776 break;
778 case ColorID::Menu:
779 // menu background
780 case ColorID::Menutext:
781 // menu text
782 aResult = NS_RGB(0x00, 0xff, 0xff);
783 break;
785 case ColorID::Threedface:
786 case ColorID::Buttonface:
787 // 3-D face color
788 case ColorID::Buttontext:
789 // text on push buttons
790 aResult = NS_RGB(0x00, 0x66, 0x66);
791 break;
793 case ColorID::Window:
794 case ColorID::Windowtext:
795 aResult = NS_RGB(0x00, 0x00, 0xff);
796 break;
798 // from the CSS3 working draft (not yet finalized)
799 // http://www.w3.org/tr/2000/wd-css3-userint-20000216.html#color
801 case ColorID::Field:
802 case ColorID::Fieldtext:
803 aResult = NS_RGB(0xff, 0x00, 0xff);
804 break;
806 case ColorID::MozDialog:
807 case ColorID::MozDialogtext:
808 aResult = NS_RGB(0x66, 0x00, 0x66);
809 break;
811 default:
812 return NS_ERROR_NOT_AVAILABLE;
815 return NS_OK;
817 #endif
819 static nsresult GetPrefColor(const char* aPref, nscolor& aResult) {
820 nsAutoCString colorStr;
821 MOZ_TRY(Preferences::GetCString(aPref, colorStr));
822 if (!ServoCSSParser::ComputeColor(nullptr, NS_RGB(0, 0, 0), colorStr,
823 &aResult)) {
824 return NS_ERROR_FAILURE;
826 return NS_OK;
829 static nsresult GetColorFromPref(LookAndFeel::ColorID aID, ColorScheme aScheme,
830 nscolor& aResult) {
831 const char* prefName = sColorPrefs[size_t(aID)];
832 if (aScheme == ColorScheme::Dark) {
833 nsAutoCString darkPrefName(prefName);
834 darkPrefName.Append(".dark");
835 if (NS_SUCCEEDED(GetPrefColor(darkPrefName.get(), aResult))) {
836 return NS_OK;
839 return GetPrefColor(prefName, aResult);
842 // All these routines will return NS_OK if they have a value,
843 // in which case the nsLookAndFeel should use that value;
844 // otherwise we'll return NS_ERROR_NOT_AVAILABLE, in which case, the
845 // platform-specific nsLookAndFeel should use its own values instead.
846 nsresult nsXPLookAndFeel::GetColorValue(ColorID aID, ColorScheme aScheme,
847 UseStandins aUseStandins,
848 nscolor& aResult) {
849 if (!sInitialized) {
850 Init();
853 #ifdef DEBUG_SYSTEM_COLOR_USE
854 if (NS_SUCCEEDED(SystemColorUseDebuggingColor(aID, aResult))) {
855 return NS_OK;
857 #endif
859 if (aUseStandins == UseStandins::Yes) {
860 aResult = GetStandinForNativeColor(aID, aScheme);
861 return NS_OK;
864 auto& cache =
865 aScheme == ColorScheme::Light ? sLightColorCache : sDarkColorCache;
866 if (const auto* cached = cache.Get(aID)) {
867 if (cached->isNothing()) {
868 return NS_ERROR_FAILURE;
870 aResult = cached->value();
871 return NS_OK;
874 if (NS_SUCCEEDED(GetColorFromPref(aID, aScheme, aResult))) {
875 cache.Insert(aID, Some(aResult));
876 return NS_OK;
879 if (NS_SUCCEEDED(NativeGetColor(aID, aScheme, aResult))) {
880 if (gfxPlatform::GetCMSMode() == CMSMode::All &&
881 !IsSpecialColor(aID, aResult)) {
882 qcms_transform* transform = gfxPlatform::GetCMSInverseRGBTransform();
883 if (transform) {
884 uint8_t color[4];
885 color[0] = NS_GET_R(aResult);
886 color[1] = NS_GET_G(aResult);
887 color[2] = NS_GET_B(aResult);
888 color[3] = NS_GET_A(aResult);
889 qcms_transform_data(transform, color, color, 1);
890 aResult = NS_RGBA(color[0], color[1], color[2], color[3]);
894 // NOTE: Servo holds a lock and the main thread is paused, so writing to the
895 // global cache here is fine.
896 cache.Insert(aID, Some(aResult));
897 return NS_OK;
900 cache.Insert(aID, Nothing());
901 return NS_ERROR_FAILURE;
904 nsresult nsXPLookAndFeel::GetIntValue(IntID aID, int32_t& aResult) {
905 if (!sInitialized) {
906 Init();
909 if (const auto* cached = sIntCache.Get(aID)) {
910 if (cached->isNothing()) {
911 return NS_ERROR_FAILURE;
913 aResult = cached->value();
914 return NS_OK;
917 if (NS_SUCCEEDED(Preferences::GetInt(sIntPrefs[size_t(aID)], &aResult))) {
918 sIntCache.Insert(aID, Some(aResult));
919 return NS_OK;
922 if (NS_FAILED(NativeGetInt(aID, aResult))) {
923 sIntCache.Insert(aID, Nothing());
924 return NS_ERROR_FAILURE;
927 sIntCache.Insert(aID, Some(aResult));
928 return NS_OK;
931 nsresult nsXPLookAndFeel::GetFloatValue(FloatID aID, float& aResult) {
932 if (!sInitialized) {
933 Init();
936 if (const auto* cached = sFloatCache.Get(aID)) {
937 if (cached->isNothing()) {
938 return NS_ERROR_FAILURE;
940 aResult = cached->value();
941 return NS_OK;
944 int32_t pref = 0;
945 if (NS_SUCCEEDED(Preferences::GetInt(sFloatPrefs[size_t(aID)], &pref))) {
946 aResult = float(pref) / 100.0f;
947 sFloatCache.Insert(aID, Some(aResult));
948 return NS_OK;
951 if (NS_FAILED(NativeGetFloat(aID, aResult))) {
952 sFloatCache.Insert(aID, Nothing());
953 return NS_ERROR_FAILURE;
956 sFloatCache.Insert(aID, Some(aResult));
957 return NS_OK;
960 bool nsXPLookAndFeel::LookAndFeelFontToStyle(const LookAndFeelFont& aFont,
961 nsString& aName,
962 gfxFontStyle& aStyle) {
963 if (!aFont.haveFont()) {
964 return false;
966 aName = aFont.name();
967 aStyle = gfxFontStyle();
968 aStyle.size = aFont.size();
969 aStyle.weight = FontWeight(aFont.weight());
970 aStyle.style =
971 aFont.italic() ? FontSlantStyle::Italic() : FontSlantStyle::Normal();
972 aStyle.systemFont = true;
973 return true;
976 widget::LookAndFeelFont nsXPLookAndFeel::StyleToLookAndFeelFont(
977 const nsAString& aName, const gfxFontStyle& aStyle) {
978 LookAndFeelFont font;
979 font.haveFont() = true;
980 font.name() = aName;
981 font.size() = aStyle.size;
982 font.weight() = aStyle.weight.ToFloat();
983 font.italic() = aStyle.style.IsItalic();
984 MOZ_ASSERT(aStyle.style.IsNormal() || aStyle.style.IsItalic(),
985 "Cannot handle oblique font style");
986 #ifdef DEBUG
988 // Assert that all the remaining font style properties have their
989 // default values.
990 gfxFontStyle candidate = aStyle;
991 gfxFontStyle defaults{};
992 candidate.size = defaults.size;
993 candidate.weight = defaults.weight;
994 candidate.style = defaults.style;
995 MOZ_ASSERT(candidate.Equals(defaults),
996 "Some font style properties not supported");
998 #endif
999 return font;
1002 bool nsXPLookAndFeel::GetFontValue(FontID aID, nsString& aName,
1003 gfxFontStyle& aStyle) {
1004 if (const LookAndFeelFont* cached = sFontCache.Get(aID)) {
1005 return LookAndFeelFontToStyle(*cached, aName, aStyle);
1007 LookAndFeelFont font;
1008 const bool haveFont = NativeGetFont(aID, aName, aStyle);
1009 font.haveFont() = haveFont;
1010 if (haveFont) {
1011 font = StyleToLookAndFeelFont(aName, aStyle);
1013 sFontCache.Insert(aID, std::move(font));
1014 return haveFont;
1017 void nsXPLookAndFeel::RefreshImpl() {
1018 // Wipe out our caches.
1019 sLightColorCache.Clear();
1020 sDarkColorCache.Clear();
1021 sFontCache.Clear();
1022 sFloatCache.Clear();
1023 sIntCache.Clear();
1024 RecomputeColorSchemes();
1026 // Clear any cached FullLookAndFeel data, which is now invalid.
1027 if (XRE_IsParentProcess()) {
1028 widget::RemoteLookAndFeel::ClearCachedData();
1032 static bool sRecordedLookAndFeelTelemetry = false;
1034 void nsXPLookAndFeel::RecordTelemetry() {
1035 if (!XRE_IsParentProcess()) {
1036 return;
1039 if (sRecordedLookAndFeelTelemetry) {
1040 return;
1043 sRecordedLookAndFeelTelemetry = true;
1045 int32_t i;
1046 Telemetry::ScalarSet(
1047 Telemetry::ScalarID::WIDGET_DARK_MODE,
1048 NS_SUCCEEDED(GetIntValue(IntID::SystemUsesDarkTheme, i)) && i != 0);
1050 RecordLookAndFeelSpecificTelemetry();
1053 namespace mozilla {
1055 static widget::ThemeChangeKind sGlobalThemeChangeKind{0};
1057 void LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind aKind) {
1058 sGlobalThemeChanged = true;
1059 sGlobalThemeChangeKind |= aKind;
1061 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
1062 const char16_t kind[] = {char16_t(aKind), 0};
1063 obs->NotifyObservers(nullptr, "internal-look-and-feel-changed", kind);
1067 void LookAndFeel::DoHandleGlobalThemeChange() {
1068 MOZ_ASSERT(sGlobalThemeChanged);
1069 sGlobalThemeChanged = false;
1070 auto kind = std::exchange(sGlobalThemeChangeKind, widget::ThemeChangeKind(0));
1072 // Tell the theme that it changed, so it can flush any handles to stale theme
1073 // data.
1075 // We can use the *DoNotUseDirectly functions directly here, because we want
1076 // to notify all possible themes in a given process (but just once).
1077 if (XRE_IsParentProcess() ||
1078 !StaticPrefs::widget_non_native_theme_enabled()) {
1079 if (nsCOMPtr<nsITheme> theme = do_GetNativeThemeDoNotUseDirectly()) {
1080 theme->ThemeChanged();
1083 if (nsCOMPtr<nsITheme> theme = do_GetBasicNativeThemeDoNotUseDirectly()) {
1084 theme->ThemeChanged();
1087 // Clear all cached LookAndFeel colors.
1088 LookAndFeel::Refresh();
1090 // Reset default background and foreground colors for the document since they
1091 // may be using system colors.
1092 PreferenceSheet::Refresh();
1094 // Vector images (SVG) may be using theme colors so we discard all cached
1095 // surfaces. (We could add a vector image only version of DiscardAll, but
1096 // in bug 940625 we decided theme changes are rare enough not to bother.)
1097 image::SurfaceCacheUtils::DiscardAll();
1099 if (XRE_IsParentProcess()) {
1100 dom::ContentParent::BroadcastThemeUpdate(kind);
1103 nsContentUtils::AddScriptRunner(
1104 NS_NewRunnableFunction("HandleGlobalThemeChange", [] {
1105 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
1106 obs->NotifyObservers(nullptr, "look-and-feel-changed", nullptr);
1108 }));
1111 static bool ShouldUseStandinsForNativeColorForNonNativeTheme(
1112 const dom::Document& aDoc, LookAndFeel::ColorID aColor,
1113 const PreferenceSheet::Prefs& aPrefs) {
1114 using ColorID = LookAndFeel::ColorID;
1115 if (!aDoc.ShouldAvoidNativeTheme()) {
1116 return false;
1119 // The native theme doesn't use native system colors backgrounds etc, except
1120 // when in high-contrast mode, so spoof some of the colors with stand-ins to
1121 // prevent lack of contrast.
1122 switch (aColor) {
1123 case ColorID::Buttonface:
1124 case ColorID::Buttontext:
1125 case ColorID::MozButtonhoverface:
1126 case ColorID::MozButtonhovertext:
1127 case ColorID::MozButtonactiveface:
1128 case ColorID::MozButtonactivetext:
1129 case ColorID::MozButtondisabledface:
1131 case ColorID::Threedlightshadow:
1132 case ColorID::Threeddarkshadow:
1133 case ColorID::Threedface:
1135 case ColorID::MozCombobox:
1136 case ColorID::MozComboboxtext:
1138 case ColorID::Field:
1139 case ColorID::MozDisabledfield:
1140 case ColorID::Fieldtext:
1142 case ColorID::Graytext:
1143 return !aPrefs.NonNativeThemeShouldBeHighContrast();
1145 default:
1146 break;
1149 return false;
1152 ColorScheme LookAndFeel::sChromeColorScheme;
1153 ColorScheme LookAndFeel::sContentColorScheme;
1154 bool LookAndFeel::sColorSchemeInitialized;
1155 bool LookAndFeel::sGlobalThemeChanged;
1157 bool LookAndFeel::IsDarkColor(nscolor aColor) {
1158 // Given https://www.w3.org/TR/WCAG20/#contrast-ratiodef, this is the
1159 // threshold that tells us whether contrast is better against white or black.
1161 // Contrast ratio against black is: (L + 0.05) / 0.05
1162 // Contrast ratio against white is: 1.05 / (L + 0.05)
1164 // So the intersection is:
1166 // (L + 0.05) / 0.05 = 1.05 / (L + 0.05)
1168 // And the solution to that equation is:
1170 // sqrt(1.05 * 0.05) - 0.05
1172 // So we consider a color dark if the contrast is below this threshold, and
1173 // it's at least half-opaque.
1174 constexpr float kThreshold = 0.179129;
1175 return NS_GET_A(aColor) > 127 &&
1176 RelativeLuminanceUtils::Compute(aColor) < kThreshold;
1179 auto LookAndFeel::ColorSchemeSettingForChrome() -> ChromeColorSchemeSetting {
1180 switch (StaticPrefs::browser_theme_toolbar_theme()) {
1181 case 0: // Dark
1182 return ChromeColorSchemeSetting::Dark;
1183 case 1: // Light
1184 return ChromeColorSchemeSetting::Light;
1185 default:
1186 return ChromeColorSchemeSetting::System;
1190 ColorScheme LookAndFeel::ThemeDerivedColorSchemeForContent() {
1191 switch (StaticPrefs::browser_theme_content_theme()) {
1192 case 0: // Dark
1193 return ColorScheme::Dark;
1194 case 1: // Light
1195 return ColorScheme::Light;
1196 default:
1197 return SystemColorScheme();
1201 void LookAndFeel::RecomputeColorSchemes() {
1202 sColorSchemeInitialized = true;
1204 sChromeColorScheme = [] {
1205 switch (ColorSchemeSettingForChrome()) {
1206 case ChromeColorSchemeSetting::Light:
1207 return ColorScheme::Light;
1208 case ChromeColorSchemeSetting::Dark:
1209 return ColorScheme::Dark;
1210 case ChromeColorSchemeSetting::System:
1211 break;
1213 return SystemColorScheme();
1214 }();
1216 sContentColorScheme = [] {
1217 switch (StaticPrefs::layout_css_prefers_color_scheme_content_override()) {
1218 case 0:
1219 return ColorScheme::Dark;
1220 case 1:
1221 return ColorScheme::Light;
1222 case 2:
1223 return SystemColorScheme();
1224 default:
1225 return ThemeDerivedColorSchemeForContent();
1227 }();
1230 ColorScheme LookAndFeel::ColorSchemeForStyle(
1231 const dom::Document& aDoc, const StyleColorSchemeFlags& aFlags) {
1232 using Choice = PreferenceSheet::Prefs::ColorSchemeChoice;
1234 const auto& prefs = PreferenceSheet::PrefsFor(aDoc);
1235 switch (prefs.mColorSchemeChoice) {
1236 case Choice::Standard:
1237 break;
1238 case Choice::UserPreferred:
1239 return aDoc.PreferredColorScheme();
1240 case Choice::Light:
1241 return ColorScheme::Light;
1242 case Choice::Dark:
1243 return ColorScheme::Dark;
1246 StyleColorSchemeFlags style(aFlags);
1247 if (!style) {
1248 style.bits = aDoc.GetColorSchemeBits();
1250 const bool supportsDark = bool(style & StyleColorSchemeFlags::DARK);
1251 const bool supportsLight = bool(style & StyleColorSchemeFlags::LIGHT);
1252 if (supportsLight && supportsDark) {
1253 // Both color-schemes are explicitly supported, use the preferred one.
1254 return aDoc.PreferredColorScheme();
1256 if (supportsDark || supportsLight) {
1257 // One color-scheme is explicitly supported and one isn't, so use the one
1258 // the content supports.
1259 return supportsDark ? ColorScheme::Dark : ColorScheme::Light;
1261 // No value specified. Chrome docs always supports both, so use the preferred
1262 // color-scheme.
1263 if (nsContentUtils::IsChromeDoc(&aDoc)) {
1264 return aDoc.PreferredColorScheme();
1266 // Default content to light.
1267 return ColorScheme::Light;
1270 LookAndFeel::ColorScheme LookAndFeel::ColorSchemeForFrame(
1271 const nsIFrame* aFrame) {
1272 return ColorSchemeForStyle(*aFrame->PresContext()->Document(),
1273 aFrame->StyleUI()->mColorScheme.bits);
1276 // static
1277 Maybe<nscolor> LookAndFeel::GetColor(ColorID aId, ColorScheme aScheme,
1278 UseStandins aUseStandins) {
1279 nscolor result;
1280 nsresult rv = nsLookAndFeel::GetInstance()->GetColorValue(
1281 aId, aScheme, aUseStandins, result);
1282 if (NS_FAILED(rv)) {
1283 return Nothing();
1285 return Some(result);
1288 // Returns whether there is a CSS color name for this color.
1289 static bool ColorIsCSSAccessible(LookAndFeel::ColorID aId) {
1290 using ColorID = LookAndFeel::ColorID;
1292 switch (aId) {
1293 case ColorID::TextSelectDisabledBackground:
1294 case ColorID::TextSelectAttentionBackground:
1295 case ColorID::TextSelectAttentionForeground:
1296 case ColorID::TextHighlightBackground:
1297 case ColorID::TextHighlightForeground:
1298 case ColorID::ThemedScrollbar:
1299 case ColorID::ThemedScrollbarInactive:
1300 case ColorID::ThemedScrollbarThumb:
1301 case ColorID::ThemedScrollbarThumbActive:
1302 case ColorID::ThemedScrollbarThumbInactive:
1303 case ColorID::ThemedScrollbarThumbHover:
1304 case ColorID::IMERawInputBackground:
1305 case ColorID::IMERawInputForeground:
1306 case ColorID::IMERawInputUnderline:
1307 case ColorID::IMESelectedRawTextBackground:
1308 case ColorID::IMESelectedRawTextForeground:
1309 case ColorID::IMESelectedRawTextUnderline:
1310 case ColorID::IMEConvertedTextBackground:
1311 case ColorID::IMEConvertedTextForeground:
1312 case ColorID::IMEConvertedTextUnderline:
1313 case ColorID::IMESelectedConvertedTextBackground:
1314 case ColorID::IMESelectedConvertedTextForeground:
1315 case ColorID::IMESelectedConvertedTextUnderline:
1316 case ColorID::SpellCheckerUnderline:
1317 return false;
1318 default:
1319 break;
1322 return true;
1325 LookAndFeel::UseStandins LookAndFeel::ShouldUseStandins(
1326 const dom::Document& aDoc, ColorID aId) {
1327 const auto& prefs = PreferenceSheet::PrefsFor(aDoc);
1328 if (ShouldUseStandinsForNativeColorForNonNativeTheme(aDoc, aId, prefs)) {
1329 return UseStandins::Yes;
1331 if (prefs.mUseStandins && ColorIsCSSAccessible(aId)) {
1332 return UseStandins::Yes;
1334 return UseStandins::No;
1337 Maybe<nscolor> LookAndFeel::GetColor(ColorID aId, const nsIFrame* aFrame) {
1338 const auto* doc = aFrame->PresContext()->Document();
1339 return GetColor(aId, ColorSchemeForFrame(aFrame),
1340 ShouldUseStandins(*doc, aId));
1343 // static
1344 nsresult LookAndFeel::GetInt(IntID aID, int32_t* aResult) {
1345 return nsLookAndFeel::GetInstance()->GetIntValue(aID, *aResult);
1348 // static
1349 nsresult LookAndFeel::GetFloat(FloatID aID, float* aResult) {
1350 return nsLookAndFeel::GetInstance()->GetFloatValue(aID, *aResult);
1353 // static
1354 bool LookAndFeel::GetFont(FontID aID, nsString& aName, gfxFontStyle& aStyle) {
1355 return nsLookAndFeel::GetInstance()->GetFontValue(aID, aName, aStyle);
1358 // static
1359 char16_t LookAndFeel::GetPasswordCharacter() {
1360 return nsLookAndFeel::GetInstance()->GetPasswordCharacterImpl();
1363 // static
1364 bool LookAndFeel::GetEchoPassword() {
1365 if (StaticPrefs::editor_password_mask_delay() >= 0) {
1366 return StaticPrefs::editor_password_mask_delay() > 0;
1368 return nsLookAndFeel::GetInstance()->GetEchoPasswordImpl();
1371 // static
1372 uint32_t LookAndFeel::GetPasswordMaskDelay() {
1373 int32_t delay = StaticPrefs::editor_password_mask_delay();
1374 if (delay < 0) {
1375 return nsLookAndFeel::GetInstance()->GetPasswordMaskDelayImpl();
1377 return delay;
1380 bool LookAndFeel::DrawInTitlebar() {
1381 switch (StaticPrefs::browser_tabs_inTitlebar()) {
1382 case 0:
1383 return false;
1384 case 1:
1385 return true;
1386 default:
1387 break;
1389 return nsLookAndFeel::GetInstance()->GetDefaultDrawInTitlebar();
1392 void LookAndFeel::GetThemeInfo(nsACString& aOut) {
1393 nsLookAndFeel::GetInstance()->GetThemeInfo(aOut);
1396 // static
1397 void LookAndFeel::Refresh() {
1398 nsLookAndFeel::GetInstance()->RefreshImpl();
1399 widget::Theme::LookAndFeelChanged();
1402 // static
1403 void LookAndFeel::NativeInit() { nsLookAndFeel::GetInstance()->NativeInit(); }
1405 // static
1406 void LookAndFeel::SetData(widget::FullLookAndFeel&& aTables) {
1407 nsLookAndFeel::GetInstance()->SetDataImpl(std::move(aTables));
1410 } // namespace mozilla