Bug 1837620 - Part 7: Keep JitScripts in a linked list to avoid iterating all BaseScr...
[gecko.git] / widget / nsXPLookAndFeel.cpp
blobfbde834fec2e41a6a073b4dd3ff27ddf239f6a2e
1 /* -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/ArrayUtils.h"
8 #include "mozilla/LookAndFeel.h"
9 #include "nscore.h"
11 #include "nsXPLookAndFeel.h"
12 #include "nsLookAndFeel.h"
13 #include "HeadlessLookAndFeel.h"
14 #include "RemoteLookAndFeel.h"
15 #include "nsContentUtils.h"
16 #include "nsCRT.h"
17 #include "nsFont.h"
18 #include "nsIFrame.h"
19 #include "nsIXULRuntime.h"
20 #include "nsLayoutUtils.h"
21 #include "Theme.h"
22 #include "SurfaceCacheUtils.h"
23 #include "mozilla/dom/ContentParent.h"
24 #include "mozilla/dom/ContentChild.h"
25 #include "mozilla/Preferences.h"
26 #include "mozilla/Services.h"
27 #include "mozilla/ServoStyleSet.h"
28 #include "mozilla/ServoCSSParser.h"
29 #include "mozilla/StaticPrefs_browser.h"
30 #include "mozilla/StaticPrefs_editor.h"
31 #include "mozilla/StaticPrefs_layout.h"
32 #include "mozilla/StaticPrefs_ui.h"
33 #include "mozilla/StaticPrefs_widget.h"
34 #include "mozilla/dom/Document.h"
35 #include "mozilla/PreferenceSheet.h"
36 #include "mozilla/gfx/2D.h"
37 #include "mozilla/widget/WidgetMessageUtils.h"
38 #include "mozilla/dom/KeyboardEventBinding.h"
39 #include "mozilla/RelativeLuminanceUtils.h"
40 #include "mozilla/Telemetry.h"
41 #include "mozilla/TelemetryScalarEnums.h"
43 #include "gfxPlatform.h"
44 #include "gfxFont.h"
46 #include "qcms.h"
48 #include <bitset>
50 using namespace mozilla;
52 using IntID = mozilla::LookAndFeel::IntID;
53 using FloatID = mozilla::LookAndFeel::FloatID;
54 using ColorID = mozilla::LookAndFeel::ColorID;
55 using FontID = mozilla::LookAndFeel::FontID;
57 template <typename Index, typename Value, Index kEnd>
58 class EnumeratedCache {
59 mozilla::EnumeratedArray<Index, kEnd, Value> mEntries;
60 std::bitset<size_t(kEnd)> mValidity;
62 public:
63 constexpr EnumeratedCache() = default;
65 bool IsValid(Index aIndex) const { return mValidity[size_t(aIndex)]; }
67 const Value* Get(Index aIndex) const {
68 return IsValid(aIndex) ? &mEntries[aIndex] : nullptr;
71 void Insert(Index aIndex, Value aValue) {
72 mValidity[size_t(aIndex)] = true;
73 mEntries[aIndex] = aValue;
76 void Remove(Index aIndex) {
77 mValidity[size_t(aIndex)] = false;
78 mEntries[aIndex] = Value();
81 void Clear() {
82 mValidity.reset();
83 for (auto& entry : mEntries) {
84 entry = Value();
89 using ColorCache = EnumeratedCache<ColorID, Maybe<nscolor>, ColorID::End>;
91 struct ColorCaches {
92 using UseStandins = LookAndFeel::UseStandins;
94 ColorCache mCaches[2][2];
96 constexpr ColorCaches() = default;
98 ColorCache& Get(ColorScheme aScheme, UseStandins aUseStandins) {
99 return mCaches[aScheme == ColorScheme::Dark]
100 [aUseStandins == UseStandins::Yes];
103 void Clear() {
104 for (auto& c : mCaches) {
105 for (auto& cache : c) {
106 cache.Clear();
112 static ColorCaches sColorCaches;
114 static EnumeratedCache<FloatID, Maybe<float>, FloatID::End> sFloatCache;
115 static EnumeratedCache<IntID, Maybe<int32_t>, IntID::End> sIntCache;
116 static EnumeratedCache<FontID, widget::LookAndFeelFont, FontID::End> sFontCache;
118 // To make one of these prefs toggleable from a reftest add a user
119 // pref in testing/profiles/reftest/user.js. For example, to make
120 // ui.useAccessibilityTheme toggleable, add:
122 // user_pref("ui.useAccessibilityTheme", 0);
124 // This needs to be of the same length and in the same order as
125 // LookAndFeel::IntID values.
126 static const char sIntPrefs[][45] = {
127 "ui.caretBlinkTime",
128 "ui.caretBlinkCount",
129 "ui.caretWidth",
130 "ui.caretVisibleWithSelection",
131 "ui.selectTextfieldsOnKeyFocus",
132 "ui.submenuDelay",
133 "ui.menusCanOverlapOSBar",
134 "ui.useOverlayScrollbars",
135 "ui.allowOverlayScrollbarsOverlap",
136 "ui.skipNavigatingDisabledMenuItem",
137 "ui.dragThresholdX",
138 "ui.dragThresholdY",
139 "ui.useAccessibilityTheme",
140 "ui.scrollArrowStyle",
141 "ui.scrollButtonLeftMouseButtonAction",
142 "ui.scrollButtonMiddleMouseButtonAction",
143 "ui.scrollButtonRightMouseButtonAction",
144 "ui.treeOpenDelay",
145 "ui.treeCloseDelay",
146 "ui.treeLazyScrollDelay",
147 "ui.treeScrollDelay",
148 "ui.treeScrollLinesMax",
149 "accessibility.tabfocus", // Weird one...
150 "ui.chosenMenuItemsShouldBlink",
151 "ui.windowsAccentColorInTitlebar",
152 "ui.windowsDefaultTheme",
153 "ui.dwmCompositor",
154 "ui.windowsClassic",
155 "ui.windowsGlass",
156 "ui.macGraphiteTheme",
157 "ui.macBigSurTheme",
158 "ui.macRTL",
159 "ui.alertNotificationOrigin",
160 "ui.scrollToClick",
161 "ui.IMERawInputUnderlineStyle",
162 "ui.IMESelectedRawTextUnderlineStyle",
163 "ui.IMEConvertedTextUnderlineStyle",
164 "ui.IMESelectedConvertedTextUnderlineStyle",
165 "ui.SpellCheckerUnderlineStyle",
166 "ui.menuBarDrag",
167 "ui.scrollbarButtonAutoRepeatBehavior",
168 "ui.tooltipDelay",
169 "ui.swipeAnimationEnabled",
170 "ui.scrollbarDisplayOnMouseMove",
171 "ui.scrollbarFadeBeginDelay",
172 "ui.scrollbarFadeDuration",
173 "ui.contextMenuOffsetVertical",
174 "ui.contextMenuOffsetHorizontal",
175 "ui.GtkCSDAvailable",
176 "ui.GtkCSDMinimizeButton",
177 "ui.GtkCSDMaximizeButton",
178 "ui.GtkCSDCloseButton",
179 "ui.GtkCSDMinimizeButtonPosition",
180 "ui.GtkCSDMaximizeButtonPosition",
181 "ui.GtkCSDCloseButtonPosition",
182 "ui.GtkCSDReversedPlacement",
183 "ui.systemUsesDarkTheme",
184 "ui.prefersReducedMotion",
185 "ui.prefersReducedTransparency",
186 "ui.invertedColors",
187 "ui.primaryPointerCapabilities",
188 "ui.allPointerCapabilities",
189 "ui.systemScrollbarSize",
190 "ui.touchDeviceSupportPresent",
191 "ui.titlebarRadius",
192 "ui.dynamicRange",
193 "ui.videoDynamicRange",
194 "ui.panelAnimations",
197 static_assert(ArrayLength(sIntPrefs) == size_t(LookAndFeel::IntID::End),
198 "Should have a pref for each int value");
200 // This array MUST be kept in the same order as the float id list in
201 // LookAndFeel.h
202 // clang-format off
203 static const char sFloatPrefs[][37] = {
204 "ui.IMEUnderlineRelativeSize",
205 "ui.SpellCheckerUnderlineRelativeSize",
206 "ui.caretAspectRatio",
207 "ui.textScaleFactor",
208 "ui.cursorScale",
210 // clang-format on
212 static_assert(ArrayLength(sFloatPrefs) == size_t(LookAndFeel::FloatID::End),
213 "Should have a pref for each float value");
215 // This array MUST be kept in the same order as the color list in
216 // specified/color.rs
217 static const char sColorPrefs[][41] = {
218 "ui.activeborder",
219 "ui.activecaption",
220 "ui.appworkspace",
221 "ui.background",
222 "ui.buttonface",
223 "ui.buttonhighlight",
224 "ui.buttonshadow",
225 "ui.buttontext",
226 "ui.buttonborder",
227 "ui.captiontext",
228 "ui.-moz-field",
229 "ui.-moz-disabledfield",
230 "ui.-moz-fieldtext",
231 "ui.mark",
232 "ui.marktext",
233 "ui.-moz-comboboxtext",
234 "ui.-moz-combobox",
235 "ui.graytext",
236 "ui.highlight",
237 "ui.highlighttext",
238 "ui.inactiveborder",
239 "ui.inactivecaption",
240 "ui.inactivecaptiontext",
241 "ui.infobackground",
242 "ui.infotext",
243 "ui.menu",
244 "ui.menutext",
245 "ui.scrollbar",
246 "ui.threeddarkshadow",
247 "ui.threedface",
248 "ui.threedhighlight",
249 "ui.threedlightshadow",
250 "ui.threedshadow",
251 "ui.window",
252 "ui.windowframe",
253 "ui.windowtext",
254 "ui.-moz-buttondefault",
255 "ui.-moz-default-color",
256 "ui.-moz-default-background-color",
257 "ui.-moz-dialog",
258 "ui.-moz-dialogtext",
259 "ui.-moz-dragtargetzone",
260 "ui.-moz-cellhighlight",
261 "ui.-moz_cellhighlighttext",
262 "ui.selecteditem",
263 "ui.selecteditemtext",
264 "ui.-moz-buttonhoverface",
265 "ui.-moz_buttonhovertext",
266 "ui.-moz_menuhover",
267 "ui.-moz_menuhoverdisabled",
268 "ui.-moz_menuhovertext",
269 "ui.-moz_menubarhovertext",
270 "ui.-moz_eventreerow",
271 "ui.-moz_oddtreerow",
272 "ui.-moz-buttonactivetext",
273 "ui.-moz-buttonactiveface",
274 "ui.-moz-buttondisabledface",
275 "ui.-moz_mac_chrome_active",
276 "ui.-moz_mac_chrome_inactive",
277 "ui.-moz-mac-defaultbuttontext",
278 "ui.-moz-mac-focusring",
279 "ui.-moz-mac-menuselect",
280 "ui.-moz-mac-menushadow",
281 "ui.-moz-mac-menutextdisable",
282 "ui.-moz-mac-menutextselect",
283 "ui.-moz_mac_disabledtoolbartext",
284 "ui.-moz-mac-secondaryhighlight",
285 "ui.-moz-mac-menupopup",
286 "ui.-moz-mac-menuitem",
287 "ui.-moz-mac-active-menuitem",
288 "ui.-moz-mac-source-list",
289 "ui.-moz-mac-source-list-selection",
290 "ui.-moz-mac-active-source-list-selection",
291 "ui.-moz-mac-tooltip",
292 "ui.accentcolor",
293 "ui.accentcolortext",
294 "ui.-moz-autofill-background",
295 "ui.-moz-win-mediatext",
296 "ui.-moz-win-communicationstext",
297 "ui.-moz-nativehyperlinktext",
298 "ui.-moz-nativevisitedhyperlinktext",
299 "ui.-moz-hyperlinktext",
300 "ui.-moz-activehyperlinktext",
301 "ui.-moz-visitedhyperlinktext",
302 "ui.-moz-colheadertext",
303 "ui.-moz-colheaderhovertext",
304 "ui.textSelectDisabledBackground",
305 "ui.textSelectAttentionBackground",
306 "ui.textSelectAttentionForeground",
307 "ui.textHighlightBackground",
308 "ui.textHighlightForeground",
309 "ui.IMERawInputBackground",
310 "ui.IMERawInputForeground",
311 "ui.IMERawInputUnderline",
312 "ui.IMESelectedRawTextBackground",
313 "ui.IMESelectedRawTextForeground",
314 "ui.IMESelectedRawTextUnderline",
315 "ui.IMEConvertedTextBackground",
316 "ui.IMEConvertedTextForeground",
317 "ui.IMEConvertedTextUnderline",
318 "ui.IMESelectedConvertedTextBackground",
319 "ui.IMESelectedConvertedTextForeground",
320 "ui.IMESelectedConvertedTextUnderline",
321 "ui.SpellCheckerUnderline",
322 "ui.themedScrollbar",
323 "ui.themedScrollbarInactive",
324 "ui.themedScrollbarThumb",
325 "ui.themedScrollbarThumbHover",
326 "ui.themedScrollbarThumbActive",
327 "ui.themedScrollbarThumbInactive",
330 static_assert(ArrayLength(sColorPrefs) == size_t(LookAndFeel::ColorID::End),
331 "Should have a pref for each color value");
333 // This array MUST be kept in the same order as the SystemFont enum.
334 static const char sFontPrefs[][41] = {
335 "ui.font.caption",
336 "ui.font.icon",
337 "ui.font.menu",
338 "ui.font.message-box",
339 "ui.font.small-caption",
340 "ui.font.status-bar",
341 "ui.font.-moz-pull-down-menu",
342 "ui.font.-moz-button",
343 "ui.font.-moz-list",
344 "ui.font.-moz-field",
347 static_assert(ArrayLength(sFontPrefs) == size_t(LookAndFeel::FontID::End),
348 "Should have a pref for each font value");
350 const char* nsXPLookAndFeel::GetColorPrefName(ColorID aId) {
351 return sColorPrefs[size_t(aId)];
354 bool nsXPLookAndFeel::sInitialized = false;
356 nsXPLookAndFeel* nsXPLookAndFeel::sInstance = nullptr;
357 bool nsXPLookAndFeel::sShutdown = false;
359 auto LookAndFeel::SystemZoomSettings() -> ZoomSettings {
360 ZoomSettings settings;
361 switch (StaticPrefs::browser_display_os_zoom_behavior()) {
362 case 0:
363 default:
364 break;
365 case 1:
366 settings.mFullZoom = GetTextScaleFactor();
367 break;
368 case 2:
369 settings.mTextZoom = GetTextScaleFactor();
370 break;
372 return settings;
375 // static
376 nsXPLookAndFeel* nsXPLookAndFeel::GetInstance() {
377 if (sInstance) {
378 return sInstance;
381 NS_ENSURE_TRUE(!sShutdown, nullptr);
383 // If we're in a content process, then the parent process will have supplied
384 // us with an initial FullLookAndFeel object.
385 // We grab this data from the ContentChild,
386 // where it's been temporarily stashed, and initialize our new LookAndFeel
387 // object with it.
389 FullLookAndFeel* lnf = nullptr;
391 if (auto* cc = mozilla::dom::ContentChild::GetSingleton()) {
392 lnf = &cc->BorrowLookAndFeelData();
395 if (lnf) {
396 sInstance = new widget::RemoteLookAndFeel(std::move(*lnf));
397 } else if (gfxPlatform::IsHeadless()) {
398 sInstance = new widget::HeadlessLookAndFeel();
399 } else {
400 sInstance = new nsLookAndFeel();
403 // This is only ever used once during initialization, and can be cleared now.
404 if (lnf) {
405 *lnf = {};
408 widget::Theme::Init();
409 return sInstance;
412 // static
413 void nsXPLookAndFeel::Shutdown() {
414 if (sShutdown) {
415 return;
418 sShutdown = true;
419 delete sInstance;
420 sInstance = nullptr;
422 // This keeps strings alive, so need to clear to make leak checking happy.
423 sFontCache.Clear();
425 widget::Theme::Shutdown();
428 static void IntPrefChanged(const nsACString& aPref) {
429 // Most Int prefs can't change our system colors or fonts, but
430 // ui.systemUsesDarkTheme can, since it affects the effective color-scheme
431 // (affecting system colors).
432 auto changeKind = aPref.EqualsLiteral("ui.systemUsesDarkTheme")
433 ? widget::ThemeChangeKind::Style
434 : widget::ThemeChangeKind::MediaQueriesOnly;
435 LookAndFeel::NotifyChangedAllWindows(changeKind);
438 static void FloatPrefChanged(const nsACString& aPref) {
439 // Most float prefs can't change our system colors or fonts, but
440 // textScaleFactor affects layout.
441 auto changeKind = aPref.EqualsLiteral("ui.textScaleFactor")
442 ? widget::ThemeChangeKind::StyleAndLayout
443 : widget::ThemeChangeKind::MediaQueriesOnly;
444 LookAndFeel::NotifyChangedAllWindows(changeKind);
447 static void ColorPrefChanged() {
448 // Color prefs affect style, because they by definition change system colors.
449 LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind::Style);
452 static void FontPrefChanged() {
453 // Color prefs affect style, because they by definition change system fonts.
454 LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind::Style);
457 // static
458 void nsXPLookAndFeel::OnPrefChanged(const char* aPref, void* aClosure) {
459 nsDependentCString prefName(aPref);
460 for (const char* pref : sIntPrefs) {
461 if (prefName.Equals(pref)) {
462 IntPrefChanged(prefName);
463 return;
467 for (const char* pref : sFloatPrefs) {
468 if (prefName.Equals(pref)) {
469 FloatPrefChanged(prefName);
470 return;
474 for (const char* pref : sColorPrefs) {
475 // We use StringBeginsWith to handle .dark prefs too.
476 if (StringBeginsWith(prefName, nsDependentCString(pref))) {
477 ColorPrefChanged();
478 return;
482 for (const char* pref : sFontPrefs) {
483 if (StringBeginsWith(prefName, nsDependentCString(pref))) {
484 FontPrefChanged();
485 return;
490 bool LookAndFeel::WindowsNonNativeMenusEnabled() {
491 switch (StaticPrefs::browser_display_windows_non_native_menus()) {
492 case 0:
493 return false;
494 case 1:
495 return true;
496 default:
497 #ifdef XP_WIN
498 return IsWin10OrLater();
499 #else
500 return false;
501 #endif
505 static constexpr struct {
506 nsLiteralCString mName;
507 widget::ThemeChangeKind mChangeKind =
508 widget::ThemeChangeKind::MediaQueriesOnly;
509 } kMediaQueryPrefs[] = {
510 {"browser.display.windows.non_native_menus"_ns},
511 // Affects whether standins are used for the accent color.
512 {"widget.non-native-theme.use-theme-accent"_ns,
513 widget::ThemeChangeKind::Style},
514 // These two affect system colors on Windows.
515 {"widget.windows.uwp-system-colors.enabled"_ns,
516 widget::ThemeChangeKind::Style},
517 // These two affect system colors on Windows.
518 {"widget.windows.uwp-system-colors.highlight-accent"_ns,
519 widget::ThemeChangeKind::Style},
520 // Affects env().
521 {"layout.css.prefers-color-scheme.content-override"_ns,
522 widget::ThemeChangeKind::Style},
523 // Affects media queries and scrollbar sizes, so gotta relayout.
524 {"widget.gtk.overlay-scrollbars.enabled"_ns,
525 widget::ThemeChangeKind::StyleAndLayout},
526 // Affects zoom settings which includes text and full zoom.
527 {"browser.display.os-zoom-behavior"_ns,
528 widget::ThemeChangeKind::StyleAndLayout},
529 // This affects not only the media query, but also the native theme, so we
530 // need to re-layout.
531 {"browser.theme.toolbar-theme"_ns, widget::ThemeChangeKind::AllBits},
532 {"browser.theme.content-theme"_ns},
533 {"mathml.legacy_maction_and_semantics_implementations.disabled"_ns},
534 {"mathml.ms_lquote_rquote_attributes.disabled"_ns},
535 {"dom.element.popover.enabled"_ns},
538 // Read values from the user's preferences.
539 // This is done once at startup, but since the user's preferences
540 // haven't actually been read yet at that time, we also have to
541 // set a callback to inform us of changes to each pref.
542 void nsXPLookAndFeel::Init() {
543 MOZ_RELEASE_ASSERT(NS_IsMainThread());
545 // Say we're already initialized, and take the chance that it might fail;
546 // protects against some other process writing to our static variables.
547 sInitialized = true;
549 RecomputeColorSchemes();
551 if (XRE_IsParentProcess()) {
552 nsLayoutUtils::RecomputeSmoothScrollDefault();
555 // XXX If we could reorganize the pref names, we should separate the branch
556 // for each types. Then, we could reduce the unnecessary loop from
557 // nsXPLookAndFeel::OnPrefChanged().
558 Preferences::RegisterPrefixCallback(OnPrefChanged, "ui.");
559 // We really do just want the accessibility.tabfocus pref, not other prefs
560 // that start with that string.
561 Preferences::RegisterCallback(OnPrefChanged, "accessibility.tabfocus");
563 for (auto& pref : kMediaQueryPrefs) {
564 Preferences::RegisterCallback(
565 [](const char*, void* aChangeKind) {
566 auto changeKind =
567 widget::ThemeChangeKind(reinterpret_cast<uintptr_t>(aChangeKind));
568 LookAndFeel::NotifyChangedAllWindows(changeKind);
570 pref.mName, reinterpret_cast<void*>(uintptr_t(pref.mChangeKind)));
574 nsXPLookAndFeel::~nsXPLookAndFeel() {
575 NS_ASSERTION(sInstance == this,
576 "This destroying instance isn't the singleton instance");
577 sInstance = nullptr;
580 static bool IsSpecialColor(LookAndFeel::ColorID aID, nscolor aColor) {
581 using ColorID = LookAndFeel::ColorID;
583 if (aColor == NS_SAME_AS_FOREGROUND_COLOR) {
584 return true;
587 switch (aID) {
588 case ColorID::IMESelectedRawTextBackground:
589 case ColorID::IMESelectedConvertedTextBackground:
590 case ColorID::IMERawInputBackground:
591 case ColorID::IMEConvertedTextBackground:
592 case ColorID::IMESelectedRawTextForeground:
593 case ColorID::IMESelectedConvertedTextForeground:
594 case ColorID::IMERawInputForeground:
595 case ColorID::IMEConvertedTextForeground:
596 case ColorID::IMERawInputUnderline:
597 case ColorID::IMEConvertedTextUnderline:
598 case ColorID::IMESelectedRawTextUnderline:
599 case ColorID::IMESelectedConvertedTextUnderline:
600 case ColorID::SpellCheckerUnderline:
601 return NS_IS_SELECTION_SPECIAL_COLOR(aColor);
602 default:
603 break;
606 * In GetColor(), every color that is not a special color is color
607 * corrected. Use false to make other colors color corrected.
609 return false;
612 nscolor nsXPLookAndFeel::GetStandinForNativeColor(ColorID aID,
613 ColorScheme aScheme) {
614 if (aScheme == ColorScheme::Dark) {
615 if (auto color = GenericDarkColor(aID)) {
616 return *color;
620 // The stand-in colors are taken from what the non-native theme needs (for
621 // field/button colors), the Windows 7 Aero theme except Mac-specific colors
622 // which are taken from Mac OS 10.7.
624 #define COLOR(name_, r, g, b) \
625 case ColorID::name_: \
626 return NS_RGB(r, g, b);
628 #define COLORA(name_, r, g, b, a) \
629 case ColorID::name_: \
630 return NS_RGBA(r, g, b, a);
632 switch (aID) {
633 // These are here for the purposes of headless mode.
634 case ColorID::IMESelectedRawTextBackground:
635 case ColorID::IMESelectedConvertedTextBackground:
636 case ColorID::IMERawInputBackground:
637 case ColorID::IMEConvertedTextBackground:
638 return NS_TRANSPARENT;
639 case ColorID::IMESelectedRawTextForeground:
640 case ColorID::IMESelectedConvertedTextForeground:
641 case ColorID::IMERawInputForeground:
642 case ColorID::IMEConvertedTextForeground:
643 return NS_SAME_AS_FOREGROUND_COLOR;
644 case ColorID::IMERawInputUnderline:
645 case ColorID::IMEConvertedTextUnderline:
646 return NS_40PERCENT_FOREGROUND_COLOR;
647 case ColorID::Accentcolor:
648 return widget::sDefaultAccent.ToABGR();
649 case ColorID::Accentcolortext:
650 return widget::sDefaultAccentText.ToABGR();
651 COLOR(SpellCheckerUnderline, 0xff, 0x00, 0x00)
652 COLOR(TextSelectDisabledBackground, 0xaa, 0xaa, 0xaa)
654 // CSS 2 colors:
655 COLOR(Activeborder, 0xB4, 0xB4, 0xB4)
656 COLOR(Activecaption, 0x99, 0xB4, 0xD1)
657 COLOR(Appworkspace, 0xAB, 0xAB, 0xAB)
658 COLOR(Background, 0x00, 0x00, 0x00)
659 COLOR(Buttonhighlight, 0xFF, 0xFF, 0xFF)
660 COLOR(Buttonshadow, 0xA0, 0xA0, 0xA0)
662 // Buttons and comboboxes should be kept in sync since they are drawn with
663 // the same colors by the non-native theme.
664 COLOR(Buttonface, 0xe9, 0xe9, 0xed)
665 COLORA(MozButtondisabledface, 0xe9, 0xe9, 0xed, 128)
667 COLOR(MozCombobox, 0xe9, 0xe9, 0xed)
669 COLOR(Buttontext, 0x00, 0x00, 0x00)
670 COLOR(MozComboboxtext, 0x00, 0x00, 0x00)
672 COLOR(Captiontext, 0x00, 0x00, 0x00)
673 COLOR(Graytext, 0x6D, 0x6D, 0x6D)
674 COLOR(Highlight, 0x33, 0x99, 0xFF)
675 COLOR(Highlighttext, 0xFF, 0xFF, 0xFF)
676 COLOR(Inactiveborder, 0xF4, 0xF7, 0xFC)
677 COLOR(Inactivecaption, 0xBF, 0xCD, 0xDB)
678 COLOR(Inactivecaptiontext, 0x43, 0x4E, 0x54)
679 COLOR(Infobackground, 0xFF, 0xFF, 0xE1)
680 COLOR(Infotext, 0x00, 0x00, 0x00)
681 COLOR(Menu, 0xF0, 0xF0, 0xF0)
682 COLOR(Menutext, 0x00, 0x00, 0x00)
683 COLOR(Scrollbar, 0xC8, 0xC8, 0xC8)
684 COLOR(Threeddarkshadow, 0x69, 0x69, 0x69)
685 COLOR(Threedface, 0xF0, 0xF0, 0xF0)
686 COLOR(Threedhighlight, 0xFF, 0xFF, 0xFF)
687 COLOR(Threedlightshadow, 0xE3, 0xE3, 0xE3)
688 COLOR(Threedshadow, 0xA0, 0xA0, 0xA0)
689 COLOR(Buttonborder, 0xE3, 0xE3, 0xE3)
690 COLOR(Mark, 0xFF, 0xFF, 0x00)
691 COLOR(Marktext, 0x00, 0x00, 0x00)
692 COLOR(Window, 0xFF, 0xFF, 0xFF)
693 COLOR(Windowframe, 0x64, 0x64, 0x64)
694 COLOR(Windowtext, 0x00, 0x00, 0x00)
695 COLOR(MozButtondefault, 0x69, 0x69, 0x69)
696 COLOR(Field, 0xFF, 0xFF, 0xFF)
697 COLORA(MozDisabledfield, 0xFF, 0xFF, 0xFF, 128)
698 COLOR(Fieldtext, 0x00, 0x00, 0x00)
699 COLOR(MozDialog, 0xF0, 0xF0, 0xF0)
700 COLOR(MozDialogtext, 0x00, 0x00, 0x00)
701 COLOR(MozColheadertext, 0x00, 0x00, 0x00)
702 COLOR(MozColheaderhovertext, 0x00, 0x00, 0x00)
703 COLOR(MozDragtargetzone, 0xFF, 0xFF, 0xFF)
704 COLOR(MozCellhighlight, 0xF0, 0xF0, 0xF0)
705 COLOR(MozCellhighlighttext, 0x00, 0x00, 0x00)
706 COLOR(Selecteditem, 0x33, 0x99, 0xFF)
707 COLOR(Selecteditemtext, 0xFF, 0xFF, 0xFF)
708 COLOR(MozButtonhoverface, 0xd0, 0xd0, 0xd7)
709 COLOR(MozButtonhovertext, 0x00, 0x00, 0x00)
710 COLOR(MozButtonactiveface, 0xb1, 0xb1, 0xb9)
711 COLOR(MozButtonactivetext, 0x00, 0x00, 0x00)
712 COLOR(MozMenuhover, 0x33, 0x99, 0xFF)
713 COLOR(MozMenuhovertext, 0x00, 0x00, 0x00)
714 COLOR(MozMenubarhovertext, 0x00, 0x00, 0x00)
715 COLOR(MozMenuhoverdisabled, 0xF0, 0xF0, 0xF0)
716 COLOR(MozEventreerow, 0xFF, 0xFF, 0xFF)
717 COLOR(MozOddtreerow, 0xFF, 0xFF, 0xFF)
718 COLOR(MozMacChromeActive, 0xB2, 0xB2, 0xB2)
719 COLOR(MozMacChromeInactive, 0xE1, 0xE1, 0xE1)
720 COLOR(MozMacFocusring, 0x60, 0x9D, 0xD7)
721 COLOR(MozMacMenuselect, 0x38, 0x75, 0xD7)
722 COLOR(MozMacMenushadow, 0xA3, 0xA3, 0xA3)
723 COLOR(MozMacMenutextdisable, 0x88, 0x88, 0x88)
724 COLOR(MozMacMenutextselect, 0xFF, 0xFF, 0xFF)
725 COLOR(MozMacDisabledtoolbartext, 0x3F, 0x3F, 0x3F)
726 COLOR(MozMacSecondaryhighlight, 0xD4, 0xD4, 0xD4)
727 COLOR(MozMacMenupopup, 0xe6, 0xe6, 0xe6)
728 COLOR(MozMacMenuitem, 0xe6, 0xe6, 0xe6)
729 COLOR(MozMacActiveMenuitem, 0x0a, 0x64, 0xdc)
730 COLOR(MozMacSourceList, 0xf7, 0xf7, 0xf7)
731 COLOR(MozMacSourceListSelection, 0xc8, 0xc8, 0xc8)
732 COLOR(MozMacActiveSourceListSelection, 0x0a, 0x64, 0xdc)
733 COLOR(MozMacTooltip, 0xf7, 0xf7, 0xf7)
734 // Seems to be the default color (hardcoded because of bug 1065998)
735 COLOR(MozWinMediatext, 0xFF, 0xFF, 0xFF)
736 COLOR(MozWinCommunicationstext, 0xFF, 0xFF, 0xFF)
737 COLOR(MozNativehyperlinktext, 0x00, 0x66, 0xCC)
738 COLOR(MozNativevisitedhyperlinktext, 0x55, 0x1A, 0x8B)
739 default:
740 break;
742 return NS_RGB(0xFF, 0xFF, 0xFF);
745 #undef COLOR
746 #undef COLORA
748 // Taken from in-content/common.inc.css's dark theme.
749 Maybe<nscolor> nsXPLookAndFeel::GenericDarkColor(ColorID aID) {
750 nscolor color = NS_RGB(0, 0, 0);
751 static constexpr nscolor kWindowBackground = NS_RGB(28, 27, 34);
752 static constexpr nscolor kWindowText = NS_RGB(251, 251, 254);
753 switch (aID) {
754 case ColorID::Window: // --in-content-page-background
755 case ColorID::Background:
756 color = kWindowBackground;
757 break;
759 case ColorID::Menu:
760 color = NS_RGB(0x2b, 0x2a, 0x33);
761 break;
763 case ColorID::MozMenuhovertext:
764 case ColorID::MozMenubarhovertext:
765 case ColorID::Menutext:
766 color = NS_RGB(0xfb, 0xfb, 0xfe);
767 break;
769 case ColorID::MozMenuhover:
770 color = NS_RGB(0x52, 0x52, 0x5e);
771 break;
773 case ColorID::MozMenuhoverdisabled:
774 color = NS_RGB(0x3a, 0x39, 0x44);
775 break;
777 case ColorID::MozOddtreerow:
778 case ColorID::MozDialog: // --in-content-box-background
779 color = NS_RGB(35, 34, 43);
780 break;
781 case ColorID::Windowtext: // --in-content-page-color
782 case ColorID::MozDialogtext:
783 case ColorID::Fieldtext:
784 case ColorID::Buttontext: // --in-content-button-text-color (via
785 // --in-content-page-color)
786 case ColorID::MozComboboxtext:
787 case ColorID::MozButtonhovertext:
788 case ColorID::MozButtonactivetext:
789 color = kWindowText;
790 break;
791 case ColorID::Buttonshadow:
792 case ColorID::Threedshadow:
793 case ColorID::Threedlightshadow:
794 case ColorID::Buttonborder: // --in-content-box-border-color computed
795 // with kWindowText above
796 // kWindowBackground.
797 case ColorID::Graytext: // opacity: 0.4 of kWindowText blended over the
798 // "Window" background color, which happens to be
799 // the same :-)
800 color = NS_ComposeColors(kWindowBackground, NS_RGBA(251, 251, 254, 102));
801 break;
802 case ColorID::MozCellhighlight:
803 case ColorID::Selecteditem: // --in-content-primary-button-background /
804 // --in-content-item-selected
805 color = NS_RGB(0, 221, 255);
806 break;
807 case ColorID::Field:
808 case ColorID::Buttonface: // --in-content-button-background
809 case ColorID::Threedface:
810 case ColorID::MozCombobox:
811 case ColorID::MozCellhighlighttext:
812 case ColorID::Selecteditemtext: // --in-content-primary-button-text-color /
813 // --in-content-item-selected-text
814 color = NS_RGB(43, 42, 51);
815 break;
816 case ColorID::Threeddarkshadow: // Same as Threedlightshadow but with the
817 // background.
818 case ColorID::MozDisabledfield: // opacity: 0.4 of the face above blended
819 // over the "Window" background color.
820 case ColorID::MozButtondisabledface:
821 color = NS_ComposeColors(kWindowBackground, NS_RGBA(43, 42, 51, 102));
822 break;
823 case ColorID::MozButtonhoverface: // --in-content-button-background-hover
824 color = NS_RGB(82, 82, 94);
825 break;
826 case ColorID::MozButtonactiveface: // --in-content-button-background-active
827 color = NS_RGB(91, 91, 102);
828 break;
829 case ColorID::Highlight:
830 color = NS_RGBA(0, 221, 255, 78);
831 break;
832 case ColorID::Highlighttext:
833 color = NS_SAME_AS_FOREGROUND_COLOR;
834 break;
835 case ColorID::MozNativehyperlinktext:
836 // If you change this color, you probably also want to change the default
837 // value of browser.anchor_color.dark.
838 color = NS_RGB(0x8c, 0x8c, 0xff);
839 break;
840 case ColorID::MozNativevisitedhyperlinktext:
841 // If you change this color, you probably also want to change the default
842 // value of browser.visited_color.dark.
843 color = NS_RGB(0xff, 0xad, 0xff);
844 break;
845 case ColorID::SpellCheckerUnderline:
846 // This is the default for active links in dark mode as well
847 // (browser.active_color.dark). See bug 1755564 for some analysis and
848 // other options too.
849 color = NS_RGB(0xff, 0x66, 0x66);
850 break;
851 default:
852 return Nothing();
854 return Some(color);
857 // Uncomment the #define below if you want to debug system color use in a skin
858 // that uses them. When set, it will make all system color pairs that are
859 // appropriate for foreground/background pairing the same. This means if the
860 // skin is using system colors correctly you will not be able to see *any* text.
862 // #define DEBUG_SYSTEM_COLOR_USE
864 #ifdef DEBUG_SYSTEM_COLOR_USE
865 static nsresult SystemColorUseDebuggingColor(LookAndFeel::ColorID aID,
866 nscolor& aResult) {
867 using ColorID = LookAndFeel::ColorID;
869 switch (aID) {
870 // css2 http://www.w3.org/TR/REC-CSS2/ui.html#system-colors
871 case ColorID::Activecaption:
872 // active window caption background
873 case ColorID::Captiontext:
874 // text in active window caption
875 aResult = NS_RGB(0xff, 0x00, 0x00);
876 break;
878 case ColorID::Highlight:
879 // background of selected item
880 case ColorID::Highlighttext:
881 // text of selected item
882 aResult = NS_RGB(0xff, 0xff, 0x00);
883 break;
885 case ColorID::Inactivecaption:
886 // inactive window caption
887 case ColorID::Inactivecaptiontext:
888 // text in inactive window caption
889 aResult = NS_RGB(0x66, 0x66, 0x00);
890 break;
892 case ColorID::Infobackground:
893 // tooltip background color
894 case ColorID::Infotext:
895 // tooltip text color
896 aResult = NS_RGB(0x00, 0xff, 0x00);
897 break;
899 case ColorID::Menu:
900 // menu background
901 case ColorID::Menutext:
902 // menu text
903 aResult = NS_RGB(0x00, 0xff, 0xff);
904 break;
906 case ColorID::Threedface:
907 case ColorID::Buttonface:
908 // 3-D face color
909 case ColorID::Buttontext:
910 // text on push buttons
911 aResult = NS_RGB(0x00, 0x66, 0x66);
912 break;
914 case ColorID::Window:
915 case ColorID::Windowtext:
916 aResult = NS_RGB(0x00, 0x00, 0xff);
917 break;
919 // from the CSS3 working draft (not yet finalized)
920 // http://www.w3.org/tr/2000/wd-css3-userint-20000216.html#color
922 case ColorID::Field:
923 case ColorID::Fieldtext:
924 aResult = NS_RGB(0xff, 0x00, 0xff);
925 break;
927 case ColorID::MozDialog:
928 case ColorID::MozDialogtext:
929 aResult = NS_RGB(0x66, 0x00, 0x66);
930 break;
932 default:
933 return NS_ERROR_NOT_AVAILABLE;
936 return NS_OK;
938 #endif
940 static nsresult GetPrefColor(const char* aPref, nscolor& aResult) {
941 nsAutoCString colorStr;
942 MOZ_TRY(Preferences::GetCString(aPref, colorStr));
943 if (!ServoCSSParser::ComputeColor(nullptr, NS_RGB(0, 0, 0), colorStr,
944 &aResult)) {
945 return NS_ERROR_FAILURE;
947 return NS_OK;
950 static nsresult GetColorFromPref(LookAndFeel::ColorID aID, ColorScheme aScheme,
951 nscolor& aResult) {
952 const char* prefName = sColorPrefs[size_t(aID)];
953 if (aScheme == ColorScheme::Dark) {
954 nsAutoCString darkPrefName(prefName);
955 darkPrefName.Append(".dark");
956 if (NS_SUCCEEDED(GetPrefColor(darkPrefName.get(), aResult))) {
957 return NS_OK;
960 return GetPrefColor(prefName, aResult);
963 // All these routines will return NS_OK if they have a value,
964 // in which case the nsLookAndFeel should use that value;
965 // otherwise we'll return NS_ERROR_NOT_AVAILABLE, in which case, the
966 // platform-specific nsLookAndFeel should use its own values instead.
967 nsresult nsXPLookAndFeel::GetColorValue(ColorID aID, ColorScheme aScheme,
968 UseStandins aUseStandins,
969 nscolor& aResult) {
970 if (!sInitialized) {
971 Init();
974 #ifdef DEBUG_SYSTEM_COLOR_USE
975 if (NS_SUCCEEDED(SystemColorUseDebuggingColor(aID, aResult))) {
976 return NS_OK;
978 #endif
980 auto& cache = sColorCaches.Get(aScheme, aUseStandins);
981 if (const auto* cached = cache.Get(aID)) {
982 if (cached->isNothing()) {
983 return NS_ERROR_FAILURE;
985 aResult = cached->value();
986 return NS_OK;
989 // NOTE: Servo holds a lock and the main thread is paused, so writing to the
990 // global cache here is fine.
991 auto result = GetUncachedColor(aID, aScheme, aUseStandins);
992 cache.Insert(aID, result);
993 if (!result) {
994 return NS_ERROR_FAILURE;
996 aResult = *result;
997 return NS_OK;
1000 Maybe<nscolor> nsXPLookAndFeel::GetUncachedColor(ColorID aID,
1001 ColorScheme aScheme,
1002 UseStandins aUseStandins) {
1003 if (aUseStandins == UseStandins::Yes) {
1004 return Some(GetStandinForNativeColor(aID, aScheme));
1006 nscolor r;
1007 if (NS_SUCCEEDED(GetColorFromPref(aID, aScheme, r))) {
1008 return Some(r);
1010 if (NS_SUCCEEDED(NativeGetColor(aID, aScheme, r))) {
1011 if (gfxPlatform::GetCMSMode() == CMSMode::All && !IsSpecialColor(aID, r)) {
1012 qcms_transform* transform = gfxPlatform::GetCMSInverseRGBTransform();
1013 if (transform) {
1014 uint8_t color[4];
1015 color[0] = NS_GET_R(r);
1016 color[1] = NS_GET_G(r);
1017 color[2] = NS_GET_B(r);
1018 color[3] = NS_GET_A(r);
1019 qcms_transform_data(transform, color, color, 1);
1020 r = NS_RGBA(color[0], color[1], color[2], color[3]);
1024 return Some(r);
1026 return Nothing();
1029 nsresult nsXPLookAndFeel::GetIntValue(IntID aID, int32_t& aResult) {
1030 if (!sInitialized) {
1031 Init();
1034 if (const auto* cached = sIntCache.Get(aID)) {
1035 if (cached->isNothing()) {
1036 return NS_ERROR_FAILURE;
1038 aResult = cached->value();
1039 return NS_OK;
1042 if (NS_SUCCEEDED(Preferences::GetInt(sIntPrefs[size_t(aID)], &aResult))) {
1043 sIntCache.Insert(aID, Some(aResult));
1044 return NS_OK;
1047 if (NS_FAILED(NativeGetInt(aID, aResult))) {
1048 sIntCache.Insert(aID, Nothing());
1049 return NS_ERROR_FAILURE;
1052 sIntCache.Insert(aID, Some(aResult));
1053 return NS_OK;
1056 nsresult nsXPLookAndFeel::GetFloatValue(FloatID aID, float& aResult) {
1057 if (!sInitialized) {
1058 Init();
1061 if (const auto* cached = sFloatCache.Get(aID)) {
1062 if (cached->isNothing()) {
1063 return NS_ERROR_FAILURE;
1065 aResult = cached->value();
1066 return NS_OK;
1069 int32_t pref = 0;
1070 if (NS_SUCCEEDED(Preferences::GetInt(sFloatPrefs[size_t(aID)], &pref))) {
1071 aResult = float(pref) / 100.0f;
1072 sFloatCache.Insert(aID, Some(aResult));
1073 return NS_OK;
1076 if (NS_FAILED(NativeGetFloat(aID, aResult))) {
1077 sFloatCache.Insert(aID, Nothing());
1078 return NS_ERROR_FAILURE;
1081 sFloatCache.Insert(aID, Some(aResult));
1082 return NS_OK;
1085 bool nsXPLookAndFeel::LookAndFeelFontToStyle(const LookAndFeelFont& aFont,
1086 nsString& aName,
1087 gfxFontStyle& aStyle) {
1088 if (!aFont.haveFont()) {
1089 return false;
1091 aName = aFont.name();
1092 aStyle = gfxFontStyle();
1093 aStyle.size = aFont.size();
1094 aStyle.weight = FontWeight::FromInt(aFont.weight());
1095 aStyle.style =
1096 aFont.italic() ? FontSlantStyle::ITALIC : FontSlantStyle::NORMAL;
1097 aStyle.systemFont = true;
1098 return true;
1101 widget::LookAndFeelFont nsXPLookAndFeel::StyleToLookAndFeelFont(
1102 const nsAString& aName, const gfxFontStyle& aStyle) {
1103 LookAndFeelFont font;
1104 font.haveFont() = true;
1105 font.name() = aName;
1106 font.size() = aStyle.size;
1107 font.weight() = aStyle.weight.ToFloat();
1108 font.italic() = aStyle.style.IsItalic();
1109 MOZ_ASSERT(aStyle.style.IsNormal() || aStyle.style.IsItalic(),
1110 "Cannot handle oblique font style");
1111 #ifdef DEBUG
1113 // Assert that all the remaining font style properties have their
1114 // default values.
1115 gfxFontStyle candidate = aStyle;
1116 gfxFontStyle defaults{};
1117 candidate.size = defaults.size;
1118 candidate.weight = defaults.weight;
1119 candidate.style = defaults.style;
1120 MOZ_ASSERT(candidate.Equals(defaults),
1121 "Some font style properties not supported");
1123 #endif
1124 return font;
1127 bool nsXPLookAndFeel::GetFontValue(FontID aID, nsString& aName,
1128 gfxFontStyle& aStyle) {
1129 if (const LookAndFeelFont* cached = sFontCache.Get(aID)) {
1130 return LookAndFeelFontToStyle(*cached, aName, aStyle);
1133 LookAndFeelFont font;
1134 auto GetFontsFromPrefs = [&]() -> bool {
1135 nsDependentCString pref(sFontPrefs[size_t(aID)]);
1136 if (NS_FAILED(Preferences::GetString(pref.get(), aName))) {
1137 return false;
1139 font.haveFont() = true;
1140 font.name() = aName;
1141 font.size() = Preferences::GetFloat(nsAutoCString(pref + ".size"_ns).get());
1142 // This is written this way rather than using the fallback so that an empty
1143 // pref (such like the one about:config creates) doesn't cause system fonts
1144 // to have zero-size.
1145 if (font.size() < 1.0f) {
1146 font.size() = StyleFONT_MEDIUM_PX;
1148 font.weight() = Preferences::GetFloat(
1149 nsAutoCString(pref + ".weight"_ns).get(), FontWeight::NORMAL.ToFloat());
1150 font.italic() =
1151 Preferences::GetBool(nsAutoCString(pref + ".italic"_ns).get());
1152 return true;
1155 if (GetFontsFromPrefs()) {
1156 LookAndFeelFontToStyle(font, aName, aStyle);
1157 } else if (NativeGetFont(aID, aName, aStyle)) {
1158 font = StyleToLookAndFeelFont(aName, aStyle);
1159 } else {
1160 MOZ_ASSERT(!font.haveFont());
1162 bool success = font.haveFont();
1163 sFontCache.Insert(aID, std::move(font));
1164 return success;
1167 void nsXPLookAndFeel::RefreshImpl() {
1168 // Wipe out our caches.
1169 sColorCaches.Clear();
1170 sFontCache.Clear();
1171 sFloatCache.Clear();
1172 sIntCache.Clear();
1173 RecomputeColorSchemes();
1175 if (XRE_IsParentProcess()) {
1176 nsLayoutUtils::RecomputeSmoothScrollDefault();
1177 // Clear any cached FullLookAndFeel data, which is now invalid.
1178 widget::RemoteLookAndFeel::ClearCachedData();
1182 static bool sRecordedLookAndFeelTelemetry = false;
1184 void nsXPLookAndFeel::RecordTelemetry() {
1185 if (!XRE_IsParentProcess()) {
1186 return;
1189 if (sRecordedLookAndFeelTelemetry) {
1190 return;
1193 sRecordedLookAndFeelTelemetry = true;
1195 int32_t i;
1196 Telemetry::ScalarSet(
1197 Telemetry::ScalarID::WIDGET_DARK_MODE,
1198 NS_SUCCEEDED(GetIntValue(IntID::SystemUsesDarkTheme, i)) && i != 0);
1200 RecordLookAndFeelSpecificTelemetry();
1203 namespace mozilla {
1205 static widget::ThemeChangeKind sGlobalThemeChangeKind{0};
1207 void LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind aKind) {
1208 sGlobalThemeChanged = true;
1209 sGlobalThemeChangeKind |= aKind;
1211 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
1212 const char16_t kind[] = {char16_t(aKind), 0};
1213 obs->NotifyObservers(nullptr, "internal-look-and-feel-changed", kind);
1217 void LookAndFeel::DoHandleGlobalThemeChange() {
1218 MOZ_ASSERT(sGlobalThemeChanged);
1219 sGlobalThemeChanged = false;
1220 auto kind = std::exchange(sGlobalThemeChangeKind, widget::ThemeChangeKind(0));
1222 // Tell the theme that it changed, so it can flush any handles to stale theme
1223 // data.
1225 // We can use the *DoNotUseDirectly functions directly here, because we want
1226 // to notify all possible themes in a given process (but just once).
1227 if (XRE_IsParentProcess() ||
1228 !StaticPrefs::widget_non_native_theme_enabled()) {
1229 if (nsCOMPtr<nsITheme> theme = do_GetNativeThemeDoNotUseDirectly()) {
1230 theme->ThemeChanged();
1233 if (nsCOMPtr<nsITheme> theme = do_GetBasicNativeThemeDoNotUseDirectly()) {
1234 theme->ThemeChanged();
1237 // Clear all cached LookAndFeel colors.
1238 LookAndFeel::Refresh();
1240 // Reset default background and foreground colors for the document since they
1241 // may be using system colors.
1242 PreferenceSheet::Refresh();
1244 // Vector images (SVG) may be using theme colors so we discard all cached
1245 // surfaces. (We could add a vector image only version of DiscardAll, but
1246 // in bug 940625 we decided theme changes are rare enough not to bother.)
1247 image::SurfaceCacheUtils::DiscardAll();
1249 if (XRE_IsParentProcess()) {
1250 dom::ContentParent::BroadcastThemeUpdate(kind);
1253 nsContentUtils::AddScriptRunner(
1254 NS_NewRunnableFunction("HandleGlobalThemeChange", [] {
1255 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
1256 obs->NotifyObservers(nullptr, "look-and-feel-changed", nullptr);
1258 }));
1261 #define BIT_FOR(_c) (1ull << size_t(ColorID::_c))
1263 // We want to use a non-native color scheme for the non-native theme (except in
1264 // high-contrast mode), so spoof some of the colors with stand-ins to prevent
1265 // lack of contrast.
1266 static constexpr std::bitset<size_t(ColorID::End)> sNonNativeThemeStandinColors{
1267 // Used by default button styles.
1268 BIT_FOR(Buttonface) | BIT_FOR(Buttontext) | BIT_FOR(MozButtonhoverface) |
1269 BIT_FOR(MozButtonhovertext) | BIT_FOR(MozButtonactiveface) |
1270 BIT_FOR(MozButtonactivetext) | BIT_FOR(MozButtondisabledface) |
1271 BIT_FOR(Buttonborder) |
1272 // Used by select elements.
1273 BIT_FOR(MozCombobox) | BIT_FOR(MozComboboxtext) |
1274 BIT_FOR(Threedlightshadow) |
1275 // For symmetry with the above.
1276 BIT_FOR(Threeddarkshadow) |
1277 // Used by fieldset borders.
1278 BIT_FOR(Threedface) |
1279 // Used by input / textarea.
1280 BIT_FOR(Field) | BIT_FOR(Fieldtext) |
1281 // Used by disabled form controls.
1282 BIT_FOR(MozDisabledfield) | BIT_FOR(Graytext) |
1283 // Some pages expect these to return windows-like colors, see bug 1773795.
1284 // Also, per spec these should match Canvas/CanvasText, see
1285 // https://drafts.csswg.org/css-color-4/#window
1286 BIT_FOR(Window) | BIT_FOR(Windowtext)};
1287 #undef BIT_FOR
1289 static bool ShouldUseStandinsForNativeColorForNonNativeTheme(
1290 const dom::Document& aDoc, LookAndFeel::ColorID aColor,
1291 const PreferenceSheet::Prefs& aPrefs) {
1292 const bool shouldUseStandinsForColor = [&] {
1293 if (sNonNativeThemeStandinColors[size_t(aColor)]) {
1294 return true;
1296 // There are platforms where we want the content-exposed accent color to be
1297 // the windows blue rather than the system accent color, for now.
1298 return !StaticPrefs::widget_non_native_theme_use_theme_accent() &&
1299 (aColor == LookAndFeel::ColorID::Accentcolor ||
1300 aColor == LookAndFeel::ColorID::Accentcolortext);
1301 }();
1303 return shouldUseStandinsForColor && aDoc.ShouldAvoidNativeTheme() &&
1304 !aPrefs.NonNativeThemeShouldBeHighContrast();
1307 ColorScheme LookAndFeel::sChromeColorScheme;
1308 ColorScheme LookAndFeel::sContentColorScheme;
1309 bool LookAndFeel::sColorSchemeInitialized;
1310 bool LookAndFeel::sGlobalThemeChanged;
1312 bool LookAndFeel::IsDarkColor(nscolor aColor) {
1313 // Given https://www.w3.org/TR/WCAG20/#contrast-ratiodef, this is the
1314 // threshold that tells us whether contrast is better against white or black.
1316 // Contrast ratio against black is: (L + 0.05) / 0.05
1317 // Contrast ratio against white is: 1.05 / (L + 0.05)
1319 // So the intersection is:
1321 // (L + 0.05) / 0.05 = 1.05 / (L + 0.05)
1323 // And the solution to that equation is:
1325 // sqrt(1.05 * 0.05) - 0.05
1327 // So we consider a color dark if the contrast is below this threshold, and
1328 // it's at least half-opaque.
1329 constexpr float kThreshold = 0.179129;
1330 return NS_GET_A(aColor) > 127 &&
1331 RelativeLuminanceUtils::Compute(aColor) < kThreshold;
1334 auto LookAndFeel::ColorSchemeSettingForChrome() -> ChromeColorSchemeSetting {
1335 switch (StaticPrefs::browser_theme_toolbar_theme()) {
1336 case 0: // Dark
1337 return ChromeColorSchemeSetting::Dark;
1338 case 1: // Light
1339 return ChromeColorSchemeSetting::Light;
1340 default:
1341 return ChromeColorSchemeSetting::System;
1345 ColorScheme LookAndFeel::ThemeDerivedColorSchemeForContent() {
1346 switch (StaticPrefs::browser_theme_content_theme()) {
1347 case 0: // Dark
1348 return ColorScheme::Dark;
1349 case 1: // Light
1350 return ColorScheme::Light;
1351 default:
1352 return SystemColorScheme();
1356 void LookAndFeel::RecomputeColorSchemes() {
1357 sColorSchemeInitialized = true;
1359 sChromeColorScheme = [] {
1360 switch (ColorSchemeSettingForChrome()) {
1361 case ChromeColorSchemeSetting::Light:
1362 return ColorScheme::Light;
1363 case ChromeColorSchemeSetting::Dark:
1364 return ColorScheme::Dark;
1365 case ChromeColorSchemeSetting::System:
1366 break;
1368 return SystemColorScheme();
1369 }();
1371 sContentColorScheme = [] {
1372 switch (StaticPrefs::layout_css_prefers_color_scheme_content_override()) {
1373 case 0:
1374 return ColorScheme::Dark;
1375 case 1:
1376 return ColorScheme::Light;
1377 default:
1378 return ThemeDerivedColorSchemeForContent();
1380 }();
1383 ColorScheme LookAndFeel::ColorSchemeForStyle(
1384 const dom::Document& aDoc, const StyleColorSchemeFlags& aFlags,
1385 ColorSchemeMode aMode) {
1386 using Choice = PreferenceSheet::Prefs::ColorSchemeChoice;
1388 const auto& prefs = PreferenceSheet::PrefsFor(aDoc);
1389 switch (prefs.mColorSchemeChoice) {
1390 case Choice::Standard:
1391 break;
1392 case Choice::UserPreferred:
1393 return aDoc.PreferredColorScheme();
1394 case Choice::Light:
1395 return ColorScheme::Light;
1396 case Choice::Dark:
1397 return ColorScheme::Dark;
1400 StyleColorSchemeFlags style(aFlags);
1401 if (!style) {
1402 style.bits = aDoc.GetColorSchemeBits();
1404 const bool supportsDark = bool(style & StyleColorSchemeFlags::DARK);
1405 const bool supportsLight = bool(style & StyleColorSchemeFlags::LIGHT);
1406 if (supportsLight && supportsDark) {
1407 // Both color-schemes are explicitly supported, use the preferred one.
1408 return aDoc.PreferredColorScheme();
1410 if (supportsDark || supportsLight) {
1411 // One color-scheme is explicitly supported and one isn't, so use the one
1412 // the content supports.
1413 return supportsDark ? ColorScheme::Dark : ColorScheme::Light;
1415 // No value specified. Chrome docs always supports both, so use the preferred
1416 // color-scheme.
1417 if (aMode == ColorSchemeMode::Preferred ||
1418 nsContentUtils::IsChromeDoc(&aDoc)) {
1419 return aDoc.PreferredColorScheme();
1421 // Default content to light.
1422 return ColorScheme::Light;
1425 LookAndFeel::ColorScheme LookAndFeel::ColorSchemeForFrame(
1426 const nsIFrame* aFrame, ColorSchemeMode aMode) {
1427 return ColorSchemeForStyle(*aFrame->PresContext()->Document(),
1428 aFrame->StyleUI()->mColorScheme.bits, aMode);
1431 // static
1432 Maybe<nscolor> LookAndFeel::GetColor(ColorID aId, ColorScheme aScheme,
1433 UseStandins aUseStandins) {
1434 nscolor result;
1435 nsresult rv = nsLookAndFeel::GetInstance()->GetColorValue(
1436 aId, aScheme, aUseStandins, result);
1437 if (NS_FAILED(rv)) {
1438 return Nothing();
1440 return Some(result);
1443 // Returns whether there is a CSS color name for this color.
1444 static bool ColorIsCSSAccessible(LookAndFeel::ColorID aId) {
1445 using ColorID = LookAndFeel::ColorID;
1447 switch (aId) {
1448 case ColorID::TextSelectDisabledBackground:
1449 case ColorID::TextSelectAttentionBackground:
1450 case ColorID::TextSelectAttentionForeground:
1451 case ColorID::TextHighlightBackground:
1452 case ColorID::TextHighlightForeground:
1453 case ColorID::ThemedScrollbar:
1454 case ColorID::ThemedScrollbarInactive:
1455 case ColorID::ThemedScrollbarThumb:
1456 case ColorID::ThemedScrollbarThumbActive:
1457 case ColorID::ThemedScrollbarThumbInactive:
1458 case ColorID::ThemedScrollbarThumbHover:
1459 case ColorID::IMERawInputBackground:
1460 case ColorID::IMERawInputForeground:
1461 case ColorID::IMERawInputUnderline:
1462 case ColorID::IMESelectedRawTextBackground:
1463 case ColorID::IMESelectedRawTextForeground:
1464 case ColorID::IMESelectedRawTextUnderline:
1465 case ColorID::IMEConvertedTextBackground:
1466 case ColorID::IMEConvertedTextForeground:
1467 case ColorID::IMEConvertedTextUnderline:
1468 case ColorID::IMESelectedConvertedTextBackground:
1469 case ColorID::IMESelectedConvertedTextForeground:
1470 case ColorID::IMESelectedConvertedTextUnderline:
1471 case ColorID::SpellCheckerUnderline:
1472 return false;
1473 default:
1474 break;
1477 return true;
1480 LookAndFeel::UseStandins LookAndFeel::ShouldUseStandins(
1481 const dom::Document& aDoc, ColorID aId) {
1482 const auto& prefs = PreferenceSheet::PrefsFor(aDoc);
1483 if (ShouldUseStandinsForNativeColorForNonNativeTheme(aDoc, aId, prefs)) {
1484 return UseStandins::Yes;
1486 if (prefs.mUseStandins && ColorIsCSSAccessible(aId)) {
1487 return UseStandins::Yes;
1489 return UseStandins::No;
1492 Maybe<nscolor> LookAndFeel::GetColor(ColorID aId, const nsIFrame* aFrame) {
1493 const auto* doc = aFrame->PresContext()->Document();
1494 return GetColor(aId, ColorSchemeForFrame(aFrame),
1495 ShouldUseStandins(*doc, aId));
1498 // static
1499 nsresult LookAndFeel::GetInt(IntID aID, int32_t* aResult) {
1500 return nsLookAndFeel::GetInstance()->GetIntValue(aID, *aResult);
1503 // static
1504 nsresult LookAndFeel::GetFloat(FloatID aID, float* aResult) {
1505 return nsLookAndFeel::GetInstance()->GetFloatValue(aID, *aResult);
1508 // static
1509 bool LookAndFeel::GetFont(FontID aID, nsString& aName, gfxFontStyle& aStyle) {
1510 return nsLookAndFeel::GetInstance()->GetFontValue(aID, aName, aStyle);
1513 // static
1514 char16_t LookAndFeel::GetPasswordCharacter() {
1515 return nsLookAndFeel::GetInstance()->GetPasswordCharacterImpl();
1518 // static
1519 bool LookAndFeel::GetEchoPassword() {
1520 if (StaticPrefs::editor_password_mask_delay() >= 0) {
1521 return StaticPrefs::editor_password_mask_delay() > 0;
1523 return nsLookAndFeel::GetInstance()->GetEchoPasswordImpl();
1526 // static
1527 uint32_t LookAndFeel::GetPasswordMaskDelay() {
1528 int32_t delay = StaticPrefs::editor_password_mask_delay();
1529 if (delay < 0) {
1530 return nsLookAndFeel::GetInstance()->GetPasswordMaskDelayImpl();
1532 return delay;
1535 bool LookAndFeel::DrawInTitlebar() {
1536 switch (StaticPrefs::browser_tabs_inTitlebar()) {
1537 case 0:
1538 return false;
1539 case 1:
1540 return true;
1541 default:
1542 break;
1544 return nsLookAndFeel::GetInstance()->GetDefaultDrawInTitlebar();
1547 void LookAndFeel::GetThemeInfo(nsACString& aOut) {
1548 nsLookAndFeel::GetInstance()->GetThemeInfo(aOut);
1551 uint32_t LookAndFeel::GetMenuAccessKey() {
1552 return StaticPrefs::ui_key_menuAccessKey();
1555 Modifiers LookAndFeel::GetMenuAccessKeyModifiers() {
1556 switch (GetMenuAccessKey()) {
1557 case dom::KeyboardEvent_Binding::DOM_VK_SHIFT:
1558 return MODIFIER_SHIFT;
1559 case dom::KeyboardEvent_Binding::DOM_VK_CONTROL:
1560 return MODIFIER_CONTROL;
1561 case dom::KeyboardEvent_Binding::DOM_VK_ALT:
1562 return MODIFIER_ALT;
1563 case dom::KeyboardEvent_Binding::DOM_VK_META:
1564 return MODIFIER_META;
1565 case dom::KeyboardEvent_Binding::DOM_VK_WIN:
1566 return MODIFIER_OS;
1567 default:
1568 return 0;
1572 // static
1573 void LookAndFeel::Refresh() {
1574 nsLookAndFeel::GetInstance()->RefreshImpl();
1575 widget::Theme::LookAndFeelChanged();
1578 // static
1579 void LookAndFeel::NativeInit() { nsLookAndFeel::GetInstance()->NativeInit(); }
1581 // static
1582 void LookAndFeel::SetData(widget::FullLookAndFeel&& aTables) {
1583 nsLookAndFeel::GetInstance()->SetDataImpl(std::move(aTables));
1586 } // namespace mozilla