Bug 1835241 - Part 3: Concatenate nested namespaces in GC code as per the coding...
[gecko.git] / widget / nsXPLookAndFeel.cpp
blobfae1444161b409f73b1530d5117c02030c92e621
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_menubartext",
270 "ui.-moz_menubarhovertext",
271 "ui.-moz_eventreerow",
272 "ui.-moz_oddtreerow",
273 "ui.-moz-buttonactivetext",
274 "ui.-moz-buttonactiveface",
275 "ui.-moz-buttondisabledface",
276 "ui.-moz_mac_chrome_active",
277 "ui.-moz_mac_chrome_inactive",
278 "ui.-moz-mac-defaultbuttontext",
279 "ui.-moz-mac-focusring",
280 "ui.-moz-mac-menuselect",
281 "ui.-moz-mac-menushadow",
282 "ui.-moz-mac-menutextdisable",
283 "ui.-moz-mac-menutextselect",
284 "ui.-moz_mac_disabledtoolbartext",
285 "ui.-moz-mac-secondaryhighlight",
286 "ui.-moz-mac-menupopup",
287 "ui.-moz-mac-menuitem",
288 "ui.-moz-mac-active-menuitem",
289 "ui.-moz-mac-source-list",
290 "ui.-moz-mac-source-list-selection",
291 "ui.-moz-mac-active-source-list-selection",
292 "ui.-moz-mac-tooltip",
293 "ui.accentcolor",
294 "ui.accentcolortext",
295 "ui.-moz-autofill-background",
296 "ui.-moz-win-mediatext",
297 "ui.-moz-win-communicationstext",
298 "ui.-moz-nativehyperlinktext",
299 "ui.-moz-nativevisitedhyperlinktext",
300 "ui.-moz-hyperlinktext",
301 "ui.-moz-activehyperlinktext",
302 "ui.-moz-visitedhyperlinktext",
303 "ui.-moz-colheadertext",
304 "ui.-moz-colheaderhovertext",
305 "ui.textSelectDisabledBackground",
306 "ui.textSelectAttentionBackground",
307 "ui.textSelectAttentionForeground",
308 "ui.textHighlightBackground",
309 "ui.textHighlightForeground",
310 "ui.IMERawInputBackground",
311 "ui.IMERawInputForeground",
312 "ui.IMERawInputUnderline",
313 "ui.IMESelectedRawTextBackground",
314 "ui.IMESelectedRawTextForeground",
315 "ui.IMESelectedRawTextUnderline",
316 "ui.IMEConvertedTextBackground",
317 "ui.IMEConvertedTextForeground",
318 "ui.IMEConvertedTextUnderline",
319 "ui.IMESelectedConvertedTextBackground",
320 "ui.IMESelectedConvertedTextForeground",
321 "ui.IMESelectedConvertedTextUnderline",
322 "ui.SpellCheckerUnderline",
323 "ui.themedScrollbar",
324 "ui.themedScrollbarInactive",
325 "ui.themedScrollbarThumb",
326 "ui.themedScrollbarThumbHover",
327 "ui.themedScrollbarThumbActive",
328 "ui.themedScrollbarThumbInactive",
331 static_assert(ArrayLength(sColorPrefs) == size_t(LookAndFeel::ColorID::End),
332 "Should have a pref for each color value");
334 // This array MUST be kept in the same order as the SystemFont enum.
335 static const char sFontPrefs[][41] = {
336 "ui.font.caption",
337 "ui.font.icon",
338 "ui.font.menu",
339 "ui.font.message-box",
340 "ui.font.small-caption",
341 "ui.font.status-bar",
342 "ui.font.-moz-pull-down-menu",
343 "ui.font.-moz-button",
344 "ui.font.-moz-list",
345 "ui.font.-moz-field",
348 static_assert(ArrayLength(sFontPrefs) == size_t(LookAndFeel::FontID::End),
349 "Should have a pref for each font value");
351 const char* nsXPLookAndFeel::GetColorPrefName(ColorID aId) {
352 return sColorPrefs[size_t(aId)];
355 bool nsXPLookAndFeel::sInitialized = false;
357 nsXPLookAndFeel* nsXPLookAndFeel::sInstance = nullptr;
358 bool nsXPLookAndFeel::sShutdown = false;
360 auto LookAndFeel::SystemZoomSettings() -> ZoomSettings {
361 ZoomSettings settings;
362 switch (StaticPrefs::browser_display_os_zoom_behavior()) {
363 case 0:
364 default:
365 break;
366 case 1:
367 settings.mFullZoom = GetTextScaleFactor();
368 break;
369 case 2:
370 settings.mTextZoom = GetTextScaleFactor();
371 break;
373 return settings;
376 // static
377 nsXPLookAndFeel* nsXPLookAndFeel::GetInstance() {
378 if (sInstance) {
379 return sInstance;
382 NS_ENSURE_TRUE(!sShutdown, nullptr);
384 // If we're in a content process, then the parent process will have supplied
385 // us with an initial FullLookAndFeel object.
386 // We grab this data from the ContentChild,
387 // where it's been temporarily stashed, and initialize our new LookAndFeel
388 // object with it.
390 FullLookAndFeel* lnf = nullptr;
392 if (auto* cc = mozilla::dom::ContentChild::GetSingleton()) {
393 lnf = &cc->BorrowLookAndFeelData();
396 if (lnf) {
397 sInstance = new widget::RemoteLookAndFeel(std::move(*lnf));
398 } else if (gfxPlatform::IsHeadless()) {
399 sInstance = new widget::HeadlessLookAndFeel();
400 } else {
401 sInstance = new nsLookAndFeel();
404 // This is only ever used once during initialization, and can be cleared now.
405 if (lnf) {
406 *lnf = {};
409 widget::Theme::Init();
410 return sInstance;
413 // static
414 void nsXPLookAndFeel::Shutdown() {
415 if (sShutdown) {
416 return;
419 sShutdown = true;
420 delete sInstance;
421 sInstance = nullptr;
423 // This keeps strings alive, so need to clear to make leak checking happy.
424 sFontCache.Clear();
426 widget::Theme::Shutdown();
429 static void IntPrefChanged(const nsACString& aPref) {
430 // Most Int prefs can't change our system colors or fonts, but
431 // ui.systemUsesDarkTheme can, since it affects the effective color-scheme
432 // (affecting system colors).
433 auto changeKind = aPref.EqualsLiteral("ui.systemUsesDarkTheme")
434 ? widget::ThemeChangeKind::Style
435 : widget::ThemeChangeKind::MediaQueriesOnly;
436 LookAndFeel::NotifyChangedAllWindows(changeKind);
439 static void FloatPrefChanged(const nsACString& aPref) {
440 // Most float prefs can't change our system colors or fonts, but
441 // textScaleFactor affects layout.
442 auto changeKind = aPref.EqualsLiteral("ui.textScaleFactor")
443 ? widget::ThemeChangeKind::StyleAndLayout
444 : widget::ThemeChangeKind::MediaQueriesOnly;
445 LookAndFeel::NotifyChangedAllWindows(changeKind);
448 static void ColorPrefChanged() {
449 // Color prefs affect style, because they by definition change system colors.
450 LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind::Style);
453 static void FontPrefChanged() {
454 // Color prefs affect style, because they by definition change system fonts.
455 LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind::Style);
458 // static
459 void nsXPLookAndFeel::OnPrefChanged(const char* aPref, void* aClosure) {
460 nsDependentCString prefName(aPref);
461 for (const char* pref : sIntPrefs) {
462 if (prefName.Equals(pref)) {
463 IntPrefChanged(prefName);
464 return;
468 for (const char* pref : sFloatPrefs) {
469 if (prefName.Equals(pref)) {
470 FloatPrefChanged(prefName);
471 return;
475 for (const char* pref : sColorPrefs) {
476 // We use StringBeginsWith to handle .dark prefs too.
477 if (StringBeginsWith(prefName, nsDependentCString(pref))) {
478 ColorPrefChanged();
479 return;
483 for (const char* pref : sFontPrefs) {
484 if (StringBeginsWith(prefName, nsDependentCString(pref))) {
485 FontPrefChanged();
486 return;
491 bool LookAndFeel::WindowsNonNativeMenusEnabled() {
492 switch (StaticPrefs::browser_display_windows_non_native_menus()) {
493 case 0:
494 return false;
495 case 1:
496 return true;
497 default:
498 #ifdef XP_WIN
499 return IsWin10OrLater();
500 #else
501 return false;
502 #endif
506 static constexpr struct {
507 nsLiteralCString mName;
508 widget::ThemeChangeKind mChangeKind =
509 widget::ThemeChangeKind::MediaQueriesOnly;
510 } kMediaQueryPrefs[] = {
511 {"browser.display.windows.non_native_menus"_ns},
512 // Affects whether standins are used for the accent color.
513 {"widget.non-native-theme.use-theme-accent"_ns,
514 widget::ThemeChangeKind::Style},
515 // These two affect system colors on Windows.
516 {"widget.windows.uwp-system-colors.enabled"_ns,
517 widget::ThemeChangeKind::Style},
518 // These two affect system colors on Windows.
519 {"widget.windows.uwp-system-colors.highlight-accent"_ns,
520 widget::ThemeChangeKind::Style},
521 // Affects env().
522 {"layout.css.prefers-color-scheme.content-override"_ns,
523 widget::ThemeChangeKind::Style},
524 // Affects media queries and scrollbar sizes, so gotta relayout.
525 {"widget.gtk.overlay-scrollbars.enabled"_ns,
526 widget::ThemeChangeKind::StyleAndLayout},
527 // Affects zoom settings which includes text and full zoom.
528 {"browser.display.os-zoom-behavior"_ns,
529 widget::ThemeChangeKind::StyleAndLayout},
530 // This affects not only the media query, but also the native theme, so we
531 // need to re-layout.
532 {"browser.theme.toolbar-theme"_ns, widget::ThemeChangeKind::AllBits},
533 {"browser.theme.content-theme"_ns},
534 {"mathml.legacy_maction_and_semantics_implementations.disabled"_ns},
535 {"mathml.ms_lquote_rquote_attributes.disabled"_ns},
536 {"dom.element.popover.enabled"_ns},
539 // Read values from the user's preferences.
540 // This is done once at startup, but since the user's preferences
541 // haven't actually been read yet at that time, we also have to
542 // set a callback to inform us of changes to each pref.
543 void nsXPLookAndFeel::Init() {
544 MOZ_RELEASE_ASSERT(NS_IsMainThread());
546 // Say we're already initialized, and take the chance that it might fail;
547 // protects against some other process writing to our static variables.
548 sInitialized = true;
550 RecomputeColorSchemes();
552 if (XRE_IsParentProcess()) {
553 nsLayoutUtils::RecomputeSmoothScrollDefault();
556 // XXX If we could reorganize the pref names, we should separate the branch
557 // for each types. Then, we could reduce the unnecessary loop from
558 // nsXPLookAndFeel::OnPrefChanged().
559 Preferences::RegisterPrefixCallback(OnPrefChanged, "ui.");
560 // We really do just want the accessibility.tabfocus pref, not other prefs
561 // that start with that string.
562 Preferences::RegisterCallback(OnPrefChanged, "accessibility.tabfocus");
564 for (auto& pref : kMediaQueryPrefs) {
565 Preferences::RegisterCallback(
566 [](const char*, void* aChangeKind) {
567 auto changeKind =
568 widget::ThemeChangeKind(reinterpret_cast<uintptr_t>(aChangeKind));
569 LookAndFeel::NotifyChangedAllWindows(changeKind);
571 pref.mName, reinterpret_cast<void*>(uintptr_t(pref.mChangeKind)));
575 nsXPLookAndFeel::~nsXPLookAndFeel() {
576 NS_ASSERTION(sInstance == this,
577 "This destroying instance isn't the singleton instance");
578 sInstance = nullptr;
581 static bool IsSpecialColor(LookAndFeel::ColorID aID, nscolor aColor) {
582 using ColorID = LookAndFeel::ColorID;
584 if (aColor == NS_SAME_AS_FOREGROUND_COLOR) {
585 return true;
588 switch (aID) {
589 case ColorID::IMESelectedRawTextBackground:
590 case ColorID::IMESelectedConvertedTextBackground:
591 case ColorID::IMERawInputBackground:
592 case ColorID::IMEConvertedTextBackground:
593 case ColorID::IMESelectedRawTextForeground:
594 case ColorID::IMESelectedConvertedTextForeground:
595 case ColorID::IMERawInputForeground:
596 case ColorID::IMEConvertedTextForeground:
597 case ColorID::IMERawInputUnderline:
598 case ColorID::IMEConvertedTextUnderline:
599 case ColorID::IMESelectedRawTextUnderline:
600 case ColorID::IMESelectedConvertedTextUnderline:
601 case ColorID::SpellCheckerUnderline:
602 return NS_IS_SELECTION_SPECIAL_COLOR(aColor);
603 default:
604 break;
607 * In GetColor(), every color that is not a special color is color
608 * corrected. Use false to make other colors color corrected.
610 return false;
613 nscolor nsXPLookAndFeel::GetStandinForNativeColor(ColorID aID,
614 ColorScheme aScheme) {
615 if (aScheme == ColorScheme::Dark) {
616 if (auto color = GenericDarkColor(aID)) {
617 return *color;
621 // The stand-in colors are taken from what the non-native theme needs (for
622 // field/button colors), the Windows 7 Aero theme except Mac-specific colors
623 // which are taken from Mac OS 10.7.
625 #define COLOR(name_, r, g, b) \
626 case ColorID::name_: \
627 return NS_RGB(r, g, b);
629 #define COLORA(name_, r, g, b, a) \
630 case ColorID::name_: \
631 return NS_RGBA(r, g, b, a);
633 switch (aID) {
634 // These are here for the purposes of headless mode.
635 case ColorID::IMESelectedRawTextBackground:
636 case ColorID::IMESelectedConvertedTextBackground:
637 case ColorID::IMERawInputBackground:
638 case ColorID::IMEConvertedTextBackground:
639 return NS_TRANSPARENT;
640 case ColorID::IMESelectedRawTextForeground:
641 case ColorID::IMESelectedConvertedTextForeground:
642 case ColorID::IMERawInputForeground:
643 case ColorID::IMEConvertedTextForeground:
644 return NS_SAME_AS_FOREGROUND_COLOR;
645 case ColorID::IMERawInputUnderline:
646 case ColorID::IMEConvertedTextUnderline:
647 return NS_40PERCENT_FOREGROUND_COLOR;
648 case ColorID::Accentcolor:
649 return widget::sDefaultAccent.ToABGR();
650 case ColorID::Accentcolortext:
651 return widget::sDefaultAccentText.ToABGR();
652 COLOR(SpellCheckerUnderline, 0xff, 0x00, 0x00)
653 COLOR(TextSelectDisabledBackground, 0xaa, 0xaa, 0xaa)
655 // CSS 2 colors:
656 COLOR(Activeborder, 0xB4, 0xB4, 0xB4)
657 COLOR(Activecaption, 0x99, 0xB4, 0xD1)
658 COLOR(Appworkspace, 0xAB, 0xAB, 0xAB)
659 COLOR(Background, 0x00, 0x00, 0x00)
660 COLOR(Buttonhighlight, 0xFF, 0xFF, 0xFF)
661 COLOR(Buttonshadow, 0xA0, 0xA0, 0xA0)
663 // Buttons and comboboxes should be kept in sync since they are drawn with
664 // the same colors by the non-native theme.
665 COLOR(Buttonface, 0xe9, 0xe9, 0xed)
666 COLORA(MozButtondisabledface, 0xe9, 0xe9, 0xed, 128)
668 COLOR(MozCombobox, 0xe9, 0xe9, 0xed)
670 COLOR(Buttontext, 0x00, 0x00, 0x00)
671 COLOR(MozComboboxtext, 0x00, 0x00, 0x00)
673 COLOR(Captiontext, 0x00, 0x00, 0x00)
674 COLOR(Graytext, 0x6D, 0x6D, 0x6D)
675 COLOR(Highlight, 0x33, 0x99, 0xFF)
676 COLOR(Highlighttext, 0xFF, 0xFF, 0xFF)
677 COLOR(Inactiveborder, 0xF4, 0xF7, 0xFC)
678 COLOR(Inactivecaption, 0xBF, 0xCD, 0xDB)
679 COLOR(Inactivecaptiontext, 0x43, 0x4E, 0x54)
680 COLOR(Infobackground, 0xFF, 0xFF, 0xE1)
681 COLOR(Infotext, 0x00, 0x00, 0x00)
682 COLOR(Menu, 0xF0, 0xF0, 0xF0)
683 COLOR(Menutext, 0x00, 0x00, 0x00)
684 COLOR(Scrollbar, 0xC8, 0xC8, 0xC8)
685 COLOR(Threeddarkshadow, 0x69, 0x69, 0x69)
686 COLOR(Threedface, 0xF0, 0xF0, 0xF0)
687 COLOR(Threedhighlight, 0xFF, 0xFF, 0xFF)
688 COLOR(Threedlightshadow, 0xE3, 0xE3, 0xE3)
689 COLOR(Threedshadow, 0xA0, 0xA0, 0xA0)
690 COLOR(Buttonborder, 0xE3, 0xE3, 0xE3)
691 COLOR(Mark, 0xFF, 0xFF, 0x00)
692 COLOR(Marktext, 0x00, 0x00, 0x00)
693 COLOR(Window, 0xFF, 0xFF, 0xFF)
694 COLOR(Windowframe, 0x64, 0x64, 0x64)
695 COLOR(Windowtext, 0x00, 0x00, 0x00)
696 COLOR(MozButtondefault, 0x69, 0x69, 0x69)
697 COLOR(Field, 0xFF, 0xFF, 0xFF)
698 COLORA(MozDisabledfield, 0xFF, 0xFF, 0xFF, 128)
699 COLOR(Fieldtext, 0x00, 0x00, 0x00)
700 COLOR(MozDialog, 0xF0, 0xF0, 0xF0)
701 COLOR(MozDialogtext, 0x00, 0x00, 0x00)
702 COLOR(MozColheadertext, 0x00, 0x00, 0x00)
703 COLOR(MozColheaderhovertext, 0x00, 0x00, 0x00)
704 COLOR(MozDragtargetzone, 0xFF, 0xFF, 0xFF)
705 COLOR(MozCellhighlight, 0xF0, 0xF0, 0xF0)
706 COLOR(MozCellhighlighttext, 0x00, 0x00, 0x00)
707 COLOR(Selecteditem, 0x33, 0x99, 0xFF)
708 COLOR(Selecteditemtext, 0xFF, 0xFF, 0xFF)
709 COLOR(MozButtonhoverface, 0xd0, 0xd0, 0xd7)
710 COLOR(MozButtonhovertext, 0x00, 0x00, 0x00)
711 COLOR(MozButtonactiveface, 0xb1, 0xb1, 0xb9)
712 COLOR(MozButtonactivetext, 0x00, 0x00, 0x00)
713 COLOR(MozMenuhover, 0x33, 0x99, 0xFF)
714 COLOR(MozMenuhovertext, 0x00, 0x00, 0x00)
715 COLOR(MozMenubartext, 0x00, 0x00, 0x00)
716 COLOR(MozMenubarhovertext, 0x00, 0x00, 0x00)
717 COLOR(MozMenuhoverdisabled, 0xF0, 0xF0, 0xF0)
718 COLOR(MozEventreerow, 0xFF, 0xFF, 0xFF)
719 COLOR(MozOddtreerow, 0xFF, 0xFF, 0xFF)
720 COLOR(MozMacChromeActive, 0xB2, 0xB2, 0xB2)
721 COLOR(MozMacChromeInactive, 0xE1, 0xE1, 0xE1)
722 COLOR(MozMacFocusring, 0x60, 0x9D, 0xD7)
723 COLOR(MozMacMenuselect, 0x38, 0x75, 0xD7)
724 COLOR(MozMacMenushadow, 0xA3, 0xA3, 0xA3)
725 COLOR(MozMacMenutextdisable, 0x88, 0x88, 0x88)
726 COLOR(MozMacMenutextselect, 0xFF, 0xFF, 0xFF)
727 COLOR(MozMacDisabledtoolbartext, 0x3F, 0x3F, 0x3F)
728 COLOR(MozMacSecondaryhighlight, 0xD4, 0xD4, 0xD4)
729 COLOR(MozMacMenupopup, 0xe6, 0xe6, 0xe6)
730 COLOR(MozMacMenuitem, 0xe6, 0xe6, 0xe6)
731 COLOR(MozMacActiveMenuitem, 0x0a, 0x64, 0xdc)
732 COLOR(MozMacSourceList, 0xf7, 0xf7, 0xf7)
733 COLOR(MozMacSourceListSelection, 0xc8, 0xc8, 0xc8)
734 COLOR(MozMacActiveSourceListSelection, 0x0a, 0x64, 0xdc)
735 COLOR(MozMacTooltip, 0xf7, 0xf7, 0xf7)
736 // Seems to be the default color (hardcoded because of bug 1065998)
737 COLOR(MozWinMediatext, 0xFF, 0xFF, 0xFF)
738 COLOR(MozWinCommunicationstext, 0xFF, 0xFF, 0xFF)
739 COLOR(MozNativehyperlinktext, 0x00, 0x66, 0xCC)
740 COLOR(MozNativevisitedhyperlinktext, 0x55, 0x1A, 0x8B)
741 default:
742 break;
744 return NS_RGB(0xFF, 0xFF, 0xFF);
747 #undef COLOR
748 #undef COLORA
750 // Taken from in-content/common.inc.css's dark theme.
751 Maybe<nscolor> nsXPLookAndFeel::GenericDarkColor(ColorID aID) {
752 nscolor color = NS_RGB(0, 0, 0);
753 static constexpr nscolor kWindowBackground = NS_RGB(28, 27, 34);
754 static constexpr nscolor kWindowText = NS_RGB(251, 251, 254);
755 switch (aID) {
756 case ColorID::Window: // --in-content-page-background
757 case ColorID::Background:
758 color = kWindowBackground;
759 break;
761 case ColorID::Menu:
762 color = NS_RGB(0x2b, 0x2a, 0x33);
763 break;
765 case ColorID::MozMenuhovertext:
766 case ColorID::MozMenubarhovertext:
767 case ColorID::Menutext:
768 color = NS_RGB(0xfb, 0xfb, 0xfe);
769 break;
771 case ColorID::MozMenuhover:
772 color = NS_RGB(0x52, 0x52, 0x5e);
773 break;
775 case ColorID::MozMenuhoverdisabled:
776 color = NS_RGB(0x3a, 0x39, 0x44);
777 break;
779 case ColorID::MozOddtreerow:
780 case ColorID::MozDialog: // --in-content-box-background
781 color = NS_RGB(35, 34, 43);
782 break;
783 case ColorID::Windowtext: // --in-content-page-color
784 case ColorID::MozDialogtext:
785 case ColorID::Fieldtext:
786 case ColorID::Buttontext: // --in-content-button-text-color (via
787 // --in-content-page-color)
788 case ColorID::MozComboboxtext:
789 case ColorID::MozButtonhovertext:
790 case ColorID::MozButtonactivetext:
791 color = kWindowText;
792 break;
793 case ColorID::Buttonshadow:
794 case ColorID::Threedshadow:
795 case ColorID::Threedlightshadow:
796 case ColorID::Buttonborder: // --in-content-box-border-color computed
797 // with kWindowText above
798 // kWindowBackground.
799 case ColorID::Graytext: // opacity: 0.4 of kWindowText blended over the
800 // "Window" background color, which happens to be
801 // the same :-)
802 color = NS_ComposeColors(kWindowBackground, NS_RGBA(251, 251, 254, 102));
803 break;
804 case ColorID::MozCellhighlight:
805 case ColorID::Selecteditem: // --in-content-primary-button-background /
806 // --in-content-item-selected
807 color = NS_RGB(0, 221, 255);
808 break;
809 case ColorID::Field:
810 case ColorID::Buttonface: // --in-content-button-background
811 case ColorID::Threedface:
812 case ColorID::MozCombobox:
813 case ColorID::MozCellhighlighttext:
814 case ColorID::Selecteditemtext: // --in-content-primary-button-text-color /
815 // --in-content-item-selected-text
816 color = NS_RGB(43, 42, 51);
817 break;
818 case ColorID::Threeddarkshadow: // Same as Threedlightshadow but with the
819 // background.
820 case ColorID::MozDisabledfield: // opacity: 0.4 of the face above blended
821 // over the "Window" background color.
822 case ColorID::MozButtondisabledface:
823 color = NS_ComposeColors(kWindowBackground, NS_RGBA(43, 42, 51, 102));
824 break;
825 case ColorID::MozButtonhoverface: // --in-content-button-background-hover
826 color = NS_RGB(82, 82, 94);
827 break;
828 case ColorID::MozButtonactiveface: // --in-content-button-background-active
829 color = NS_RGB(91, 91, 102);
830 break;
831 case ColorID::Highlight:
832 color = NS_RGBA(0, 221, 255, 78);
833 break;
834 case ColorID::Highlighttext:
835 color = NS_SAME_AS_FOREGROUND_COLOR;
836 break;
837 case ColorID::MozNativehyperlinktext:
838 // If you change this color, you probably also want to change the default
839 // value of browser.anchor_color.dark.
840 color = NS_RGB(0x8c, 0x8c, 0xff);
841 break;
842 case ColorID::MozNativevisitedhyperlinktext:
843 // If you change this color, you probably also want to change the default
844 // value of browser.visited_color.dark.
845 color = NS_RGB(0xff, 0xad, 0xff);
846 break;
847 case ColorID::SpellCheckerUnderline:
848 // This is the default for active links in dark mode as well
849 // (browser.active_color.dark). See bug 1755564 for some analysis and
850 // other options too.
851 color = NS_RGB(0xff, 0x66, 0x66);
852 break;
853 default:
854 return Nothing();
856 return Some(color);
859 // Uncomment the #define below if you want to debug system color use in a skin
860 // that uses them. When set, it will make all system color pairs that are
861 // appropriate for foreground/background pairing the same. This means if the
862 // skin is using system colors correctly you will not be able to see *any* text.
864 // #define DEBUG_SYSTEM_COLOR_USE
866 #ifdef DEBUG_SYSTEM_COLOR_USE
867 static nsresult SystemColorUseDebuggingColor(LookAndFeel::ColorID aID,
868 nscolor& aResult) {
869 using ColorID = LookAndFeel::ColorID;
871 switch (aID) {
872 // css2 http://www.w3.org/TR/REC-CSS2/ui.html#system-colors
873 case ColorID::Activecaption:
874 // active window caption background
875 case ColorID::Captiontext:
876 // text in active window caption
877 aResult = NS_RGB(0xff, 0x00, 0x00);
878 break;
880 case ColorID::Highlight:
881 // background of selected item
882 case ColorID::Highlighttext:
883 // text of selected item
884 aResult = NS_RGB(0xff, 0xff, 0x00);
885 break;
887 case ColorID::Inactivecaption:
888 // inactive window caption
889 case ColorID::Inactivecaptiontext:
890 // text in inactive window caption
891 aResult = NS_RGB(0x66, 0x66, 0x00);
892 break;
894 case ColorID::Infobackground:
895 // tooltip background color
896 case ColorID::Infotext:
897 // tooltip text color
898 aResult = NS_RGB(0x00, 0xff, 0x00);
899 break;
901 case ColorID::Menu:
902 // menu background
903 case ColorID::Menutext:
904 // menu text
905 aResult = NS_RGB(0x00, 0xff, 0xff);
906 break;
908 case ColorID::Threedface:
909 case ColorID::Buttonface:
910 // 3-D face color
911 case ColorID::Buttontext:
912 // text on push buttons
913 aResult = NS_RGB(0x00, 0x66, 0x66);
914 break;
916 case ColorID::Window:
917 case ColorID::Windowtext:
918 aResult = NS_RGB(0x00, 0x00, 0xff);
919 break;
921 // from the CSS3 working draft (not yet finalized)
922 // http://www.w3.org/tr/2000/wd-css3-userint-20000216.html#color
924 case ColorID::Field:
925 case ColorID::Fieldtext:
926 aResult = NS_RGB(0xff, 0x00, 0xff);
927 break;
929 case ColorID::MozDialog:
930 case ColorID::MozDialogtext:
931 aResult = NS_RGB(0x66, 0x00, 0x66);
932 break;
934 default:
935 return NS_ERROR_NOT_AVAILABLE;
938 return NS_OK;
940 #endif
942 static nsresult GetPrefColor(const char* aPref, nscolor& aResult) {
943 nsAutoCString colorStr;
944 MOZ_TRY(Preferences::GetCString(aPref, colorStr));
945 if (!ServoCSSParser::ComputeColor(nullptr, NS_RGB(0, 0, 0), colorStr,
946 &aResult)) {
947 return NS_ERROR_FAILURE;
949 return NS_OK;
952 static nsresult GetColorFromPref(LookAndFeel::ColorID aID, ColorScheme aScheme,
953 nscolor& aResult) {
954 const char* prefName = sColorPrefs[size_t(aID)];
955 if (aScheme == ColorScheme::Dark) {
956 nsAutoCString darkPrefName(prefName);
957 darkPrefName.Append(".dark");
958 if (NS_SUCCEEDED(GetPrefColor(darkPrefName.get(), aResult))) {
959 return NS_OK;
962 return GetPrefColor(prefName, aResult);
965 // All these routines will return NS_OK if they have a value,
966 // in which case the nsLookAndFeel should use that value;
967 // otherwise we'll return NS_ERROR_NOT_AVAILABLE, in which case, the
968 // platform-specific nsLookAndFeel should use its own values instead.
969 nsresult nsXPLookAndFeel::GetColorValue(ColorID aID, ColorScheme aScheme,
970 UseStandins aUseStandins,
971 nscolor& aResult) {
972 if (!sInitialized) {
973 Init();
976 #ifdef DEBUG_SYSTEM_COLOR_USE
977 if (NS_SUCCEEDED(SystemColorUseDebuggingColor(aID, aResult))) {
978 return NS_OK;
980 #endif
982 auto& cache = sColorCaches.Get(aScheme, aUseStandins);
983 if (const auto* cached = cache.Get(aID)) {
984 if (cached->isNothing()) {
985 return NS_ERROR_FAILURE;
987 aResult = cached->value();
988 return NS_OK;
991 // NOTE: Servo holds a lock and the main thread is paused, so writing to the
992 // global cache here is fine.
993 auto result = GetUncachedColor(aID, aScheme, aUseStandins);
994 cache.Insert(aID, result);
995 if (!result) {
996 return NS_ERROR_FAILURE;
998 aResult = *result;
999 return NS_OK;
1002 Maybe<nscolor> nsXPLookAndFeel::GetUncachedColor(ColorID aID,
1003 ColorScheme aScheme,
1004 UseStandins aUseStandins) {
1005 if (aUseStandins == UseStandins::Yes) {
1006 return Some(GetStandinForNativeColor(aID, aScheme));
1008 nscolor r;
1009 if (NS_SUCCEEDED(GetColorFromPref(aID, aScheme, r))) {
1010 return Some(r);
1012 if (NS_SUCCEEDED(NativeGetColor(aID, aScheme, r))) {
1013 if (gfxPlatform::GetCMSMode() == CMSMode::All && !IsSpecialColor(aID, r)) {
1014 qcms_transform* transform = gfxPlatform::GetCMSInverseRGBTransform();
1015 if (transform) {
1016 uint8_t color[4];
1017 color[0] = NS_GET_R(r);
1018 color[1] = NS_GET_G(r);
1019 color[2] = NS_GET_B(r);
1020 color[3] = NS_GET_A(r);
1021 qcms_transform_data(transform, color, color, 1);
1022 r = NS_RGBA(color[0], color[1], color[2], color[3]);
1026 return Some(r);
1028 return Nothing();
1031 nsresult nsXPLookAndFeel::GetIntValue(IntID aID, int32_t& aResult) {
1032 if (!sInitialized) {
1033 Init();
1036 if (const auto* cached = sIntCache.Get(aID)) {
1037 if (cached->isNothing()) {
1038 return NS_ERROR_FAILURE;
1040 aResult = cached->value();
1041 return NS_OK;
1044 if (NS_SUCCEEDED(Preferences::GetInt(sIntPrefs[size_t(aID)], &aResult))) {
1045 sIntCache.Insert(aID, Some(aResult));
1046 return NS_OK;
1049 if (NS_FAILED(NativeGetInt(aID, aResult))) {
1050 sIntCache.Insert(aID, Nothing());
1051 return NS_ERROR_FAILURE;
1054 sIntCache.Insert(aID, Some(aResult));
1055 return NS_OK;
1058 nsresult nsXPLookAndFeel::GetFloatValue(FloatID aID, float& aResult) {
1059 if (!sInitialized) {
1060 Init();
1063 if (const auto* cached = sFloatCache.Get(aID)) {
1064 if (cached->isNothing()) {
1065 return NS_ERROR_FAILURE;
1067 aResult = cached->value();
1068 return NS_OK;
1071 int32_t pref = 0;
1072 if (NS_SUCCEEDED(Preferences::GetInt(sFloatPrefs[size_t(aID)], &pref))) {
1073 aResult = float(pref) / 100.0f;
1074 sFloatCache.Insert(aID, Some(aResult));
1075 return NS_OK;
1078 if (NS_FAILED(NativeGetFloat(aID, aResult))) {
1079 sFloatCache.Insert(aID, Nothing());
1080 return NS_ERROR_FAILURE;
1083 sFloatCache.Insert(aID, Some(aResult));
1084 return NS_OK;
1087 bool nsXPLookAndFeel::LookAndFeelFontToStyle(const LookAndFeelFont& aFont,
1088 nsString& aName,
1089 gfxFontStyle& aStyle) {
1090 if (!aFont.haveFont()) {
1091 return false;
1093 aName = aFont.name();
1094 aStyle = gfxFontStyle();
1095 aStyle.size = aFont.size();
1096 aStyle.weight = FontWeight::FromInt(aFont.weight());
1097 aStyle.style =
1098 aFont.italic() ? FontSlantStyle::ITALIC : FontSlantStyle::NORMAL;
1099 aStyle.systemFont = true;
1100 return true;
1103 widget::LookAndFeelFont nsXPLookAndFeel::StyleToLookAndFeelFont(
1104 const nsAString& aName, const gfxFontStyle& aStyle) {
1105 LookAndFeelFont font;
1106 font.haveFont() = true;
1107 font.name() = aName;
1108 font.size() = aStyle.size;
1109 font.weight() = aStyle.weight.ToFloat();
1110 font.italic() = aStyle.style.IsItalic();
1111 MOZ_ASSERT(aStyle.style.IsNormal() || aStyle.style.IsItalic(),
1112 "Cannot handle oblique font style");
1113 #ifdef DEBUG
1115 // Assert that all the remaining font style properties have their
1116 // default values.
1117 gfxFontStyle candidate = aStyle;
1118 gfxFontStyle defaults{};
1119 candidate.size = defaults.size;
1120 candidate.weight = defaults.weight;
1121 candidate.style = defaults.style;
1122 MOZ_ASSERT(candidate.Equals(defaults),
1123 "Some font style properties not supported");
1125 #endif
1126 return font;
1129 bool nsXPLookAndFeel::GetFontValue(FontID aID, nsString& aName,
1130 gfxFontStyle& aStyle) {
1131 if (const LookAndFeelFont* cached = sFontCache.Get(aID)) {
1132 return LookAndFeelFontToStyle(*cached, aName, aStyle);
1135 LookAndFeelFont font;
1136 auto GetFontsFromPrefs = [&]() -> bool {
1137 nsDependentCString pref(sFontPrefs[size_t(aID)]);
1138 if (NS_FAILED(Preferences::GetString(pref.get(), aName))) {
1139 return false;
1141 font.haveFont() = true;
1142 font.name() = aName;
1143 font.size() = Preferences::GetFloat(nsAutoCString(pref + ".size"_ns).get());
1144 // This is written this way rather than using the fallback so that an empty
1145 // pref (such like the one about:config creates) doesn't cause system fonts
1146 // to have zero-size.
1147 if (font.size() < 1.0f) {
1148 font.size() = StyleFONT_MEDIUM_PX;
1150 font.weight() = Preferences::GetFloat(
1151 nsAutoCString(pref + ".weight"_ns).get(), FontWeight::NORMAL.ToFloat());
1152 font.italic() =
1153 Preferences::GetBool(nsAutoCString(pref + ".italic"_ns).get());
1154 return true;
1157 if (GetFontsFromPrefs()) {
1158 LookAndFeelFontToStyle(font, aName, aStyle);
1159 } else if (NativeGetFont(aID, aName, aStyle)) {
1160 font = StyleToLookAndFeelFont(aName, aStyle);
1161 } else {
1162 MOZ_ASSERT(!font.haveFont());
1164 bool success = font.haveFont();
1165 sFontCache.Insert(aID, std::move(font));
1166 return success;
1169 void nsXPLookAndFeel::RefreshImpl() {
1170 // Wipe out our caches.
1171 sColorCaches.Clear();
1172 sFontCache.Clear();
1173 sFloatCache.Clear();
1174 sIntCache.Clear();
1175 RecomputeColorSchemes();
1177 if (XRE_IsParentProcess()) {
1178 nsLayoutUtils::RecomputeSmoothScrollDefault();
1179 // Clear any cached FullLookAndFeel data, which is now invalid.
1180 widget::RemoteLookAndFeel::ClearCachedData();
1184 static bool sRecordedLookAndFeelTelemetry = false;
1186 void nsXPLookAndFeel::RecordTelemetry() {
1187 if (!XRE_IsParentProcess()) {
1188 return;
1191 if (sRecordedLookAndFeelTelemetry) {
1192 return;
1195 sRecordedLookAndFeelTelemetry = true;
1197 int32_t i;
1198 Telemetry::ScalarSet(
1199 Telemetry::ScalarID::WIDGET_DARK_MODE,
1200 NS_SUCCEEDED(GetIntValue(IntID::SystemUsesDarkTheme, i)) && i != 0);
1202 RecordLookAndFeelSpecificTelemetry();
1205 namespace mozilla {
1207 static widget::ThemeChangeKind sGlobalThemeChangeKind{0};
1209 void LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind aKind) {
1210 sGlobalThemeChanged = true;
1211 sGlobalThemeChangeKind |= aKind;
1213 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
1214 const char16_t kind[] = {char16_t(aKind), 0};
1215 obs->NotifyObservers(nullptr, "internal-look-and-feel-changed", kind);
1219 void LookAndFeel::DoHandleGlobalThemeChange() {
1220 MOZ_ASSERT(sGlobalThemeChanged);
1221 sGlobalThemeChanged = false;
1222 auto kind = std::exchange(sGlobalThemeChangeKind, widget::ThemeChangeKind(0));
1224 // Tell the theme that it changed, so it can flush any handles to stale theme
1225 // data.
1227 // We can use the *DoNotUseDirectly functions directly here, because we want
1228 // to notify all possible themes in a given process (but just once).
1229 if (XRE_IsParentProcess() ||
1230 !StaticPrefs::widget_non_native_theme_enabled()) {
1231 if (nsCOMPtr<nsITheme> theme = do_GetNativeThemeDoNotUseDirectly()) {
1232 theme->ThemeChanged();
1235 if (nsCOMPtr<nsITheme> theme = do_GetBasicNativeThemeDoNotUseDirectly()) {
1236 theme->ThemeChanged();
1239 // Clear all cached LookAndFeel colors.
1240 LookAndFeel::Refresh();
1242 // Reset default background and foreground colors for the document since they
1243 // may be using system colors.
1244 PreferenceSheet::Refresh();
1246 // Vector images (SVG) may be using theme colors so we discard all cached
1247 // surfaces. (We could add a vector image only version of DiscardAll, but
1248 // in bug 940625 we decided theme changes are rare enough not to bother.)
1249 image::SurfaceCacheUtils::DiscardAll();
1251 if (XRE_IsParentProcess()) {
1252 dom::ContentParent::BroadcastThemeUpdate(kind);
1255 nsContentUtils::AddScriptRunner(
1256 NS_NewRunnableFunction("HandleGlobalThemeChange", [] {
1257 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
1258 obs->NotifyObservers(nullptr, "look-and-feel-changed", nullptr);
1260 }));
1263 #define BIT_FOR(_c) (1ull << size_t(ColorID::_c))
1265 // We want to use a non-native color scheme for the non-native theme (except in
1266 // high-contrast mode), so spoof some of the colors with stand-ins to prevent
1267 // lack of contrast.
1268 static constexpr std::bitset<size_t(ColorID::End)> sNonNativeThemeStandinColors{
1269 // Used by default button styles.
1270 BIT_FOR(Buttonface) | BIT_FOR(Buttontext) | BIT_FOR(MozButtonhoverface) |
1271 BIT_FOR(MozButtonhovertext) | BIT_FOR(MozButtonactiveface) |
1272 BIT_FOR(MozButtonactivetext) | BIT_FOR(MozButtondisabledface) |
1273 BIT_FOR(Buttonborder) |
1274 // Used by select elements.
1275 BIT_FOR(MozCombobox) | BIT_FOR(MozComboboxtext) |
1276 BIT_FOR(Threedlightshadow) |
1277 // For symmetry with the above.
1278 BIT_FOR(Threeddarkshadow) |
1279 // Used by fieldset borders.
1280 BIT_FOR(Threedface) |
1281 // Used by input / textarea.
1282 BIT_FOR(Field) | BIT_FOR(Fieldtext) |
1283 // Used by disabled form controls.
1284 BIT_FOR(MozDisabledfield) | BIT_FOR(Graytext) |
1285 // Some pages expect these to return windows-like colors, see bug 1773795.
1286 // Also, per spec these should match Canvas/CanvasText, see
1287 // https://drafts.csswg.org/css-color-4/#window
1288 BIT_FOR(Window) | BIT_FOR(Windowtext)};
1289 #undef BIT_FOR
1291 static bool ShouldUseStandinsForNativeColorForNonNativeTheme(
1292 const dom::Document& aDoc, LookAndFeel::ColorID aColor,
1293 const PreferenceSheet::Prefs& aPrefs) {
1294 const bool shouldUseStandinsForColor = [&] {
1295 if (sNonNativeThemeStandinColors[size_t(aColor)]) {
1296 return true;
1298 // There are platforms where we want the content-exposed accent color to be
1299 // the windows blue rather than the system accent color, for now.
1300 return !StaticPrefs::widget_non_native_theme_use_theme_accent() &&
1301 (aColor == LookAndFeel::ColorID::Accentcolor ||
1302 aColor == LookAndFeel::ColorID::Accentcolortext);
1303 }();
1305 return shouldUseStandinsForColor && aDoc.ShouldAvoidNativeTheme() &&
1306 !aPrefs.NonNativeThemeShouldBeHighContrast();
1309 ColorScheme LookAndFeel::sChromeColorScheme;
1310 ColorScheme LookAndFeel::sContentColorScheme;
1311 bool LookAndFeel::sColorSchemeInitialized;
1312 bool LookAndFeel::sGlobalThemeChanged;
1314 bool LookAndFeel::IsDarkColor(nscolor aColor) {
1315 // Given https://www.w3.org/TR/WCAG20/#contrast-ratiodef, this is the
1316 // threshold that tells us whether contrast is better against white or black.
1318 // Contrast ratio against black is: (L + 0.05) / 0.05
1319 // Contrast ratio against white is: 1.05 / (L + 0.05)
1321 // So the intersection is:
1323 // (L + 0.05) / 0.05 = 1.05 / (L + 0.05)
1325 // And the solution to that equation is:
1327 // sqrt(1.05 * 0.05) - 0.05
1329 // So we consider a color dark if the contrast is below this threshold, and
1330 // it's at least half-opaque.
1331 constexpr float kThreshold = 0.179129;
1332 return NS_GET_A(aColor) > 127 &&
1333 RelativeLuminanceUtils::Compute(aColor) < kThreshold;
1336 auto LookAndFeel::ColorSchemeSettingForChrome() -> ChromeColorSchemeSetting {
1337 switch (StaticPrefs::browser_theme_toolbar_theme()) {
1338 case 0: // Dark
1339 return ChromeColorSchemeSetting::Dark;
1340 case 1: // Light
1341 return ChromeColorSchemeSetting::Light;
1342 default:
1343 return ChromeColorSchemeSetting::System;
1347 ColorScheme LookAndFeel::ThemeDerivedColorSchemeForContent() {
1348 switch (StaticPrefs::browser_theme_content_theme()) {
1349 case 0: // Dark
1350 return ColorScheme::Dark;
1351 case 1: // Light
1352 return ColorScheme::Light;
1353 default:
1354 return SystemColorScheme();
1358 void LookAndFeel::RecomputeColorSchemes() {
1359 sColorSchemeInitialized = true;
1361 sChromeColorScheme = [] {
1362 switch (ColorSchemeSettingForChrome()) {
1363 case ChromeColorSchemeSetting::Light:
1364 return ColorScheme::Light;
1365 case ChromeColorSchemeSetting::Dark:
1366 return ColorScheme::Dark;
1367 case ChromeColorSchemeSetting::System:
1368 break;
1370 return SystemColorScheme();
1371 }();
1373 sContentColorScheme = [] {
1374 switch (StaticPrefs::layout_css_prefers_color_scheme_content_override()) {
1375 case 0:
1376 return ColorScheme::Dark;
1377 case 1:
1378 return ColorScheme::Light;
1379 default:
1380 return ThemeDerivedColorSchemeForContent();
1382 }();
1385 ColorScheme LookAndFeel::ColorSchemeForStyle(
1386 const dom::Document& aDoc, const StyleColorSchemeFlags& aFlags,
1387 ColorSchemeMode aMode) {
1388 using Choice = PreferenceSheet::Prefs::ColorSchemeChoice;
1390 const auto& prefs = PreferenceSheet::PrefsFor(aDoc);
1391 switch (prefs.mColorSchemeChoice) {
1392 case Choice::Standard:
1393 break;
1394 case Choice::UserPreferred:
1395 return aDoc.PreferredColorScheme();
1396 case Choice::Light:
1397 return ColorScheme::Light;
1398 case Choice::Dark:
1399 return ColorScheme::Dark;
1402 StyleColorSchemeFlags style(aFlags);
1403 if (!style) {
1404 style.bits = aDoc.GetColorSchemeBits();
1406 const bool supportsDark = bool(style & StyleColorSchemeFlags::DARK);
1407 const bool supportsLight = bool(style & StyleColorSchemeFlags::LIGHT);
1408 if (supportsLight && supportsDark) {
1409 // Both color-schemes are explicitly supported, use the preferred one.
1410 return aDoc.PreferredColorScheme();
1412 if (supportsDark || supportsLight) {
1413 // One color-scheme is explicitly supported and one isn't, so use the one
1414 // the content supports.
1415 return supportsDark ? ColorScheme::Dark : ColorScheme::Light;
1417 // No value specified. Chrome docs always supports both, so use the preferred
1418 // color-scheme.
1419 if (aMode == ColorSchemeMode::Preferred ||
1420 nsContentUtils::IsChromeDoc(&aDoc)) {
1421 return aDoc.PreferredColorScheme();
1423 // Default content to light.
1424 return ColorScheme::Light;
1427 LookAndFeel::ColorScheme LookAndFeel::ColorSchemeForFrame(
1428 const nsIFrame* aFrame, ColorSchemeMode aMode) {
1429 return ColorSchemeForStyle(*aFrame->PresContext()->Document(),
1430 aFrame->StyleUI()->mColorScheme.bits, aMode);
1433 // static
1434 Maybe<nscolor> LookAndFeel::GetColor(ColorID aId, ColorScheme aScheme,
1435 UseStandins aUseStandins) {
1436 nscolor result;
1437 nsresult rv = nsLookAndFeel::GetInstance()->GetColorValue(
1438 aId, aScheme, aUseStandins, result);
1439 if (NS_FAILED(rv)) {
1440 return Nothing();
1442 return Some(result);
1445 // Returns whether there is a CSS color name for this color.
1446 static bool ColorIsCSSAccessible(LookAndFeel::ColorID aId) {
1447 using ColorID = LookAndFeel::ColorID;
1449 switch (aId) {
1450 case ColorID::TextSelectDisabledBackground:
1451 case ColorID::TextSelectAttentionBackground:
1452 case ColorID::TextSelectAttentionForeground:
1453 case ColorID::TextHighlightBackground:
1454 case ColorID::TextHighlightForeground:
1455 case ColorID::ThemedScrollbar:
1456 case ColorID::ThemedScrollbarInactive:
1457 case ColorID::ThemedScrollbarThumb:
1458 case ColorID::ThemedScrollbarThumbActive:
1459 case ColorID::ThemedScrollbarThumbInactive:
1460 case ColorID::ThemedScrollbarThumbHover:
1461 case ColorID::IMERawInputBackground:
1462 case ColorID::IMERawInputForeground:
1463 case ColorID::IMERawInputUnderline:
1464 case ColorID::IMESelectedRawTextBackground:
1465 case ColorID::IMESelectedRawTextForeground:
1466 case ColorID::IMESelectedRawTextUnderline:
1467 case ColorID::IMEConvertedTextBackground:
1468 case ColorID::IMEConvertedTextForeground:
1469 case ColorID::IMEConvertedTextUnderline:
1470 case ColorID::IMESelectedConvertedTextBackground:
1471 case ColorID::IMESelectedConvertedTextForeground:
1472 case ColorID::IMESelectedConvertedTextUnderline:
1473 case ColorID::SpellCheckerUnderline:
1474 return false;
1475 default:
1476 break;
1479 return true;
1482 LookAndFeel::UseStandins LookAndFeel::ShouldUseStandins(
1483 const dom::Document& aDoc, ColorID aId) {
1484 const auto& prefs = PreferenceSheet::PrefsFor(aDoc);
1485 if (ShouldUseStandinsForNativeColorForNonNativeTheme(aDoc, aId, prefs)) {
1486 return UseStandins::Yes;
1488 if (prefs.mUseStandins && ColorIsCSSAccessible(aId)) {
1489 return UseStandins::Yes;
1491 return UseStandins::No;
1494 Maybe<nscolor> LookAndFeel::GetColor(ColorID aId, const nsIFrame* aFrame) {
1495 const auto* doc = aFrame->PresContext()->Document();
1496 return GetColor(aId, ColorSchemeForFrame(aFrame),
1497 ShouldUseStandins(*doc, aId));
1500 // static
1501 nsresult LookAndFeel::GetInt(IntID aID, int32_t* aResult) {
1502 return nsLookAndFeel::GetInstance()->GetIntValue(aID, *aResult);
1505 // static
1506 nsresult LookAndFeel::GetFloat(FloatID aID, float* aResult) {
1507 return nsLookAndFeel::GetInstance()->GetFloatValue(aID, *aResult);
1510 // static
1511 bool LookAndFeel::GetFont(FontID aID, nsString& aName, gfxFontStyle& aStyle) {
1512 return nsLookAndFeel::GetInstance()->GetFontValue(aID, aName, aStyle);
1515 // static
1516 char16_t LookAndFeel::GetPasswordCharacter() {
1517 return nsLookAndFeel::GetInstance()->GetPasswordCharacterImpl();
1520 // static
1521 bool LookAndFeel::GetEchoPassword() {
1522 if (StaticPrefs::editor_password_mask_delay() >= 0) {
1523 return StaticPrefs::editor_password_mask_delay() > 0;
1525 return nsLookAndFeel::GetInstance()->GetEchoPasswordImpl();
1528 // static
1529 uint32_t LookAndFeel::GetPasswordMaskDelay() {
1530 int32_t delay = StaticPrefs::editor_password_mask_delay();
1531 if (delay < 0) {
1532 return nsLookAndFeel::GetInstance()->GetPasswordMaskDelayImpl();
1534 return delay;
1537 bool LookAndFeel::DrawInTitlebar() {
1538 switch (StaticPrefs::browser_tabs_inTitlebar()) {
1539 case 0:
1540 return false;
1541 case 1:
1542 return true;
1543 default:
1544 break;
1546 return nsLookAndFeel::GetInstance()->GetDefaultDrawInTitlebar();
1549 void LookAndFeel::GetThemeInfo(nsACString& aOut) {
1550 nsLookAndFeel::GetInstance()->GetThemeInfo(aOut);
1553 uint32_t LookAndFeel::GetMenuAccessKey() {
1554 return StaticPrefs::ui_key_menuAccessKey();
1557 Modifiers LookAndFeel::GetMenuAccessKeyModifiers() {
1558 switch (GetMenuAccessKey()) {
1559 case dom::KeyboardEvent_Binding::DOM_VK_SHIFT:
1560 return MODIFIER_SHIFT;
1561 case dom::KeyboardEvent_Binding::DOM_VK_CONTROL:
1562 return MODIFIER_CONTROL;
1563 case dom::KeyboardEvent_Binding::DOM_VK_ALT:
1564 return MODIFIER_ALT;
1565 case dom::KeyboardEvent_Binding::DOM_VK_META:
1566 return MODIFIER_META;
1567 case dom::KeyboardEvent_Binding::DOM_VK_WIN:
1568 return MODIFIER_OS;
1569 default:
1570 return 0;
1574 // static
1575 void LookAndFeel::Refresh() {
1576 nsLookAndFeel::GetInstance()->RefreshImpl();
1577 widget::Theme::LookAndFeelChanged();
1580 // static
1581 void LookAndFeel::NativeInit() { nsLookAndFeel::GetInstance()->NativeInit(); }
1583 // static
1584 void LookAndFeel::SetData(widget::FullLookAndFeel&& aTables) {
1585 nsLookAndFeel::GetInstance()->SetDataImpl(std::move(aTables));
1588 } // namespace mozilla