Backed out 17 changesets (bug 1898153) as reuqested by Aryx for causing wasm crashes.
[gecko.git] / widget / nsXPLookAndFeel.cpp
blobea3561ccfd57c87c243593f27f403f653506a121
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"
42 #include "mozilla/Try.h"
44 #include "gfxPlatform.h"
45 #include "gfxFont.h"
47 #include "qcms.h"
49 #include <bitset>
51 using namespace mozilla;
53 using IntID = mozilla::LookAndFeel::IntID;
54 using FloatID = mozilla::LookAndFeel::FloatID;
55 using ColorID = mozilla::LookAndFeel::ColorID;
56 using FontID = mozilla::LookAndFeel::FontID;
58 template <typename Index, typename Value, Index kEnd>
59 class EnumeratedCache {
60 mozilla::EnumeratedArray<Index, Value, size_t(kEnd)> mEntries;
61 std::bitset<size_t(kEnd)> mValidity;
63 public:
64 constexpr EnumeratedCache() = default;
66 bool IsValid(Index aIndex) const { return mValidity[size_t(aIndex)]; }
68 const Value* Get(Index aIndex) const {
69 return IsValid(aIndex) ? &mEntries[aIndex] : nullptr;
72 void Insert(Index aIndex, Value aValue) {
73 mValidity[size_t(aIndex)] = true;
74 mEntries[aIndex] = aValue;
77 void Remove(Index aIndex) {
78 mValidity[size_t(aIndex)] = false;
79 mEntries[aIndex] = Value();
82 void Clear() {
83 mValidity.reset();
84 for (auto& entry : mEntries) {
85 entry = Value();
90 using ColorCache = EnumeratedCache<ColorID, Maybe<nscolor>, ColorID::End>;
92 struct ColorCaches {
93 using UseStandins = LookAndFeel::UseStandins;
95 ColorCache mCaches[2][2];
97 constexpr ColorCaches() = default;
99 ColorCache& Get(ColorScheme aScheme, UseStandins aUseStandins) {
100 return mCaches[aScheme == ColorScheme::Dark]
101 [aUseStandins == UseStandins::Yes];
104 void Clear() {
105 for (auto& c : mCaches) {
106 for (auto& cache : c) {
107 cache.Clear();
113 static ColorCaches sColorCaches;
115 static EnumeratedCache<FloatID, Maybe<float>, FloatID::End> sFloatCache;
116 static EnumeratedCache<IntID, Maybe<int32_t>, IntID::End> sIntCache;
117 static EnumeratedCache<FontID, widget::LookAndFeelFont, FontID::End> sFontCache;
119 // To make one of these prefs toggleable from a reftest add a user
120 // pref in testing/profiles/reftest/user.js. For example, to make
121 // ui.useAccessibilityTheme toggleable, add:
123 // user_pref("ui.useAccessibilityTheme", 0);
125 // This needs to be of the same length and in the same order as
126 // LookAndFeel::IntID values.
127 static const char sIntPrefs[][45] = {
128 "ui.caretBlinkTime",
129 "ui.caretBlinkCount",
130 "ui.caretWidth",
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 "ui.chosenMenuItemsShouldBlink",
150 "ui.windowsAccentColorInTitlebar",
151 "ui.macBigSurTheme",
152 "ui.macRTL",
153 "ui.macTitlebarHeight",
154 "ui.alertNotificationOrigin",
155 "ui.scrollToClick",
156 "ui.IMERawInputUnderlineStyle",
157 "ui.IMESelectedRawTextUnderlineStyle",
158 "ui.IMEConvertedTextUnderlineStyle",
159 "ui.IMESelectedConvertedTextUnderlineStyle",
160 "ui.SpellCheckerUnderlineStyle",
161 "ui.menuBarDrag",
162 "ui.scrollbarButtonAutoRepeatBehavior",
163 "ui.swipeAnimationEnabled",
164 "ui.scrollbarDisplayOnMouseMove",
165 "ui.scrollbarFadeBeginDelay",
166 "ui.scrollbarFadeDuration",
167 "ui.contextMenuOffsetVertical",
168 "ui.contextMenuOffsetHorizontal",
169 "ui.tooltipOffsetVertical",
170 "ui.GtkCSDAvailable",
171 "ui.GtkCSDMinimizeButton",
172 "ui.GtkCSDMaximizeButton",
173 "ui.GtkCSDCloseButton",
174 "ui.GtkCSDMinimizeButtonPosition",
175 "ui.GtkCSDMaximizeButtonPosition",
176 "ui.GtkCSDCloseButtonPosition",
177 "ui.GtkCSDReversedPlacement",
178 "ui.systemUsesDarkTheme",
179 "ui.prefersReducedMotion",
180 "ui.prefersReducedTransparency",
181 "ui.invertedColors",
182 "ui.primaryPointerCapabilities",
183 "ui.allPointerCapabilities",
184 "ui.systemScrollbarSize",
185 "ui.touchDeviceSupportPresent",
186 "ui.titlebarRadius",
187 "ui.titlebarButtonSpacing",
188 "ui.dynamicRange",
189 "ui.panelAnimations",
190 "ui.hideCursorWhileTyping",
191 "ui.gtkThemeFamily",
192 "ui.fullKeyboardAccess",
195 static_assert(ArrayLength(sIntPrefs) == size_t(LookAndFeel::IntID::End),
196 "Should have a pref for each int value");
198 // This array MUST be kept in the same order as the float id list in
199 // LookAndFeel.h
200 // clang-format off
201 static const char sFloatPrefs[][37] = {
202 "ui.IMEUnderlineRelativeSize",
203 "ui.SpellCheckerUnderlineRelativeSize",
204 "ui.caretAspectRatio",
205 "ui.textScaleFactor",
206 "ui.cursorScale",
208 // clang-format on
210 static_assert(ArrayLength(sFloatPrefs) == size_t(LookAndFeel::FloatID::End),
211 "Should have a pref for each float value");
213 // This array MUST be kept in the same order as the color list in
214 // specified/color.rs
215 static const char sColorPrefs[][41] = {
216 "ui.activeborder",
217 "ui.activecaption",
218 "ui.appworkspace",
219 "ui.background",
220 "ui.buttonface",
221 "ui.buttonhighlight",
222 "ui.buttonshadow",
223 "ui.buttontext",
224 "ui.buttonborder",
225 "ui.captiontext",
226 "ui.-moz-field",
227 "ui.-moz-disabledfield",
228 "ui.-moz-fieldtext",
229 "ui.mark",
230 "ui.marktext",
231 "ui.-moz-comboboxtext",
232 "ui.-moz-combobox",
233 "ui.graytext",
234 "ui.highlight",
235 "ui.highlighttext",
236 "ui.inactiveborder",
237 "ui.inactivecaption",
238 "ui.inactivecaptiontext",
239 "ui.infobackground",
240 "ui.infotext",
241 "ui.menu",
242 "ui.menutext",
243 "ui.scrollbar",
244 "ui.threeddarkshadow",
245 "ui.threedface",
246 "ui.threedhighlight",
247 "ui.threedlightshadow",
248 "ui.threedshadow",
249 "ui.window",
250 "ui.windowframe",
251 "ui.windowtext",
252 "ui.-moz-default-color",
253 "ui.-moz-default-background-color",
254 "ui.-moz-dialog",
255 "ui.-moz-dialogtext",
256 "ui.-moz-cellhighlight",
257 "ui.-moz_cellhighlighttext",
258 "ui.selecteditem",
259 "ui.selecteditemtext",
260 "ui.-moz-buttonhoverface",
261 "ui.-moz_buttonhovertext",
262 "ui.-moz_menuhover",
263 "ui.-moz_menuhoverdisabled",
264 "ui.-moz_menuhovertext",
265 "ui.-moz_menubarhovertext",
266 "ui.-moz_eventreerow",
267 "ui.-moz_oddtreerow",
268 "ui.-moz-buttonactivetext",
269 "ui.-moz-buttonactiveface",
270 "ui.-moz-buttondisabledface",
271 "ui.-moz-headerbar",
272 "ui.-moz-headerbartext",
273 "ui.-moz-headerbarinactive",
274 "ui.-moz-headerbarinactivetext",
275 "ui.-moz-mac-defaultbuttontext",
276 "ui.-moz-mac-focusring",
277 "ui.-moz_mac_disabledtoolbartext",
278 "ui.-moz-sidebar",
279 "ui.-moz-sidebartext",
280 "ui.-moz-sidebarborder",
281 "ui.accentcolor",
282 "ui.accentcolortext",
283 "ui.-moz-autofill-background",
284 "ui.-moz-nativehyperlinktext",
285 "ui.-moz-nativevisitedhyperlinktext",
286 "ui.-moz-hyperlinktext",
287 "ui.-moz-activehyperlinktext",
288 "ui.-moz-visitedhyperlinktext",
289 "ui.-moz-colheader",
290 "ui.-moz-colheadertext",
291 "ui.-moz-colheaderhover",
292 "ui.-moz-colheaderhovertext",
293 "ui.-moz-colheaderactive",
294 "ui.-moz-colheaderactivetext",
295 "ui.textSelectDisabledBackground",
296 "ui.textSelectAttentionBackground",
297 "ui.textSelectAttentionForeground",
298 "ui.textHighlightBackground",
299 "ui.textHighlightForeground",
300 "ui.targetTextBackground",
301 "ui.targetTextForeground",
302 "ui.IMERawInputBackground",
303 "ui.IMERawInputForeground",
304 "ui.IMERawInputUnderline",
305 "ui.IMESelectedRawTextBackground",
306 "ui.IMESelectedRawTextForeground",
307 "ui.IMESelectedRawTextUnderline",
308 "ui.IMEConvertedTextBackground",
309 "ui.IMEConvertedTextForeground",
310 "ui.IMEConvertedTextUnderline",
311 "ui.IMESelectedConvertedTextBackground",
312 "ui.IMESelectedConvertedTextForeground",
313 "ui.IMESelectedConvertedTextUnderline",
314 "ui.SpellCheckerUnderline",
315 "ui.themedScrollbar",
316 "ui.themedScrollbarInactive",
317 "ui.themedScrollbarThumb",
318 "ui.themedScrollbarThumbHover",
319 "ui.themedScrollbarThumbActive",
320 "ui.themedScrollbarThumbInactive",
323 static_assert(ArrayLength(sColorPrefs) == size_t(LookAndFeel::ColorID::End),
324 "Should have a pref for each color value");
326 // This array MUST be kept in the same order as the SystemFont enum.
327 static const char sFontPrefs[][41] = {
328 "ui.font.caption",
329 "ui.font.icon",
330 "ui.font.menu",
331 "ui.font.message-box",
332 "ui.font.small-caption",
333 "ui.font.status-bar",
334 "ui.font.-moz-pull-down-menu",
335 "ui.font.-moz-button",
336 "ui.font.-moz-list",
337 "ui.font.-moz-field",
340 static_assert(ArrayLength(sFontPrefs) == size_t(LookAndFeel::FontID::End),
341 "Should have a pref for each font value");
343 const char* nsXPLookAndFeel::GetColorPrefName(ColorID aId) {
344 return sColorPrefs[size_t(aId)];
347 bool nsXPLookAndFeel::sInitialized = false;
349 nsXPLookAndFeel* nsXPLookAndFeel::sInstance = nullptr;
350 bool nsXPLookAndFeel::sShutdown = false;
352 auto LookAndFeel::SystemZoomSettings() -> ZoomSettings {
353 ZoomSettings settings;
354 switch (StaticPrefs::browser_display_os_zoom_behavior()) {
355 case 0:
356 default:
357 break;
358 case 1:
359 settings.mFullZoom = GetTextScaleFactor();
360 break;
361 case 2:
362 settings.mTextZoom = GetTextScaleFactor();
363 break;
365 return settings;
368 // static
369 nsXPLookAndFeel* nsXPLookAndFeel::GetInstance() {
370 if (sInstance) {
371 return sInstance;
374 NS_ENSURE_TRUE(!sShutdown, nullptr);
376 // If we're in a content process, then the parent process will have supplied
377 // us with an initial FullLookAndFeel object.
378 // We grab this data from the ContentChild,
379 // where it's been temporarily stashed, and initialize our new LookAndFeel
380 // object with it.
382 FullLookAndFeel* lnf = nullptr;
384 if (auto* cc = mozilla::dom::ContentChild::GetSingleton()) {
385 lnf = &cc->BorrowLookAndFeelData();
388 if (lnf) {
389 sInstance = new widget::RemoteLookAndFeel(std::move(*lnf));
390 } else if (gfxPlatform::IsHeadless()) {
391 sInstance = new widget::HeadlessLookAndFeel();
392 } else {
393 sInstance = new nsLookAndFeel();
396 // This is only ever used once during initialization, and can be cleared now.
397 if (lnf) {
398 *lnf = {};
401 widget::Theme::Init();
402 return sInstance;
405 // static
406 void nsXPLookAndFeel::Shutdown() {
407 if (sShutdown) {
408 return;
411 sShutdown = true;
412 delete sInstance;
413 sInstance = nullptr;
415 // This keeps strings alive, so need to clear to make leak checking happy.
416 sFontCache.Clear();
418 widget::Theme::Shutdown();
421 static void IntPrefChanged(const nsACString& aPref) {
422 // Most Int prefs can't change our system colors or fonts, but
423 // ui.systemUsesDarkTheme can, since it affects the effective color-scheme
424 // (affecting system colors).
425 auto changeKind = aPref.EqualsLiteral("ui.systemUsesDarkTheme")
426 ? widget::ThemeChangeKind::Style
427 : widget::ThemeChangeKind::MediaQueriesOnly;
428 LookAndFeel::NotifyChangedAllWindows(changeKind);
431 static void FloatPrefChanged(const nsACString& aPref) {
432 // Most float prefs can't change our system colors or fonts, but
433 // textScaleFactor affects layout.
434 auto changeKind = aPref.EqualsLiteral("ui.textScaleFactor")
435 ? widget::ThemeChangeKind::StyleAndLayout
436 : widget::ThemeChangeKind::MediaQueriesOnly;
437 LookAndFeel::NotifyChangedAllWindows(changeKind);
440 static void ColorPrefChanged() {
441 // Color prefs affect style, because they by definition change system colors.
442 LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind::Style);
445 static void FontPrefChanged() {
446 // Color prefs affect style, because they by definition change system fonts.
447 LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind::Style);
450 // static
451 void nsXPLookAndFeel::OnPrefChanged(const char* aPref, void* aClosure) {
452 nsDependentCString prefName(aPref);
453 for (const char* pref : sIntPrefs) {
454 if (prefName.Equals(pref)) {
455 IntPrefChanged(prefName);
456 return;
460 for (const char* pref : sFloatPrefs) {
461 if (prefName.Equals(pref)) {
462 FloatPrefChanged(prefName);
463 return;
467 for (const char* pref : sColorPrefs) {
468 // We use StringBeginsWith to handle .dark prefs too.
469 if (StringBeginsWith(prefName, nsDependentCString(pref))) {
470 ColorPrefChanged();
471 return;
475 for (const char* pref : sFontPrefs) {
476 if (StringBeginsWith(prefName, nsDependentCString(pref))) {
477 FontPrefChanged();
478 return;
483 static constexpr struct {
484 nsLiteralCString mName;
485 widget::ThemeChangeKind mChangeKind =
486 widget::ThemeChangeKind::MediaQueriesOnly;
487 } kMediaQueryPrefs[] = {
488 // Affects whether standins are used for the accent color.
489 {"widget.non-native-theme.use-theme-accent"_ns,
490 widget::ThemeChangeKind::Style},
491 // These three affect system colors on Windows.
492 {"widget.windows.uwp-system-colors.enabled"_ns,
493 widget::ThemeChangeKind::Style},
494 {"widget.windows.uwp-system-colors.highlight-accent"_ns,
495 widget::ThemeChangeKind::Style},
496 // Affects env().
497 {"layout.css.prefers-color-scheme.content-override"_ns,
498 widget::ThemeChangeKind::Style},
499 // Affects media queries and scrollbar sizes, so gotta relayout.
500 {"widget.gtk.overlay-scrollbars.enabled"_ns,
501 widget::ThemeChangeKind::StyleAndLayout},
502 // Affects zoom settings which includes text and full zoom.
503 {"browser.display.os-zoom-behavior"_ns,
504 widget::ThemeChangeKind::StyleAndLayout},
505 // This affects system colors on Linux.
506 {"widget.gtk.libadwaita-colors.enabled"_ns, widget::ThemeChangeKind::Style},
507 // This affects not only the media query, but also the native theme, so we
508 // need to re-layout.
509 {"browser.theme.toolbar-theme"_ns, widget::ThemeChangeKind::AllBits},
510 {"browser.theme.content-theme"_ns},
513 // Read values from the user's preferences.
514 // This is done once at startup, but since the user's preferences
515 // haven't actually been read yet at that time, we also have to
516 // set a callback to inform us of changes to each pref.
517 void nsXPLookAndFeel::Init() {
518 MOZ_RELEASE_ASSERT(NS_IsMainThread());
520 // Say we're already initialized, and take the chance that it might fail;
521 // protects against some other process writing to our static variables.
522 sInitialized = true;
524 if (XRE_IsParentProcess()) {
525 nsLayoutUtils::RecomputeSmoothScrollDefault();
528 // XXX If we could reorganize the pref names, we should separate the branch
529 // for each types. Then, we could reduce the unnecessary loop from
530 // nsXPLookAndFeel::OnPrefChanged().
531 Preferences::RegisterPrefixCallback(OnPrefChanged, "ui.");
533 for (const auto& pref : kMediaQueryPrefs) {
534 Preferences::RegisterCallback(
535 [](const char*, void* aChangeKind) {
536 auto changeKind =
537 widget::ThemeChangeKind(reinterpret_cast<uintptr_t>(aChangeKind));
538 LookAndFeel::NotifyChangedAllWindows(changeKind);
540 pref.mName, reinterpret_cast<void*>(uintptr_t(pref.mChangeKind)));
544 nsXPLookAndFeel::~nsXPLookAndFeel() {
545 NS_ASSERTION(sInstance == this,
546 "This destroying instance isn't the singleton instance");
547 sInstance = nullptr;
550 static bool IsSpecialColor(LookAndFeel::ColorID aID, nscolor aColor) {
551 using ColorID = LookAndFeel::ColorID;
553 if (aColor == NS_SAME_AS_FOREGROUND_COLOR) {
554 return true;
557 switch (aID) {
558 case ColorID::IMESelectedRawTextBackground:
559 case ColorID::IMESelectedConvertedTextBackground:
560 case ColorID::IMERawInputBackground:
561 case ColorID::IMEConvertedTextBackground:
562 case ColorID::IMESelectedRawTextForeground:
563 case ColorID::IMESelectedConvertedTextForeground:
564 case ColorID::IMERawInputForeground:
565 case ColorID::IMEConvertedTextForeground:
566 case ColorID::IMERawInputUnderline:
567 case ColorID::IMEConvertedTextUnderline:
568 case ColorID::IMESelectedRawTextUnderline:
569 case ColorID::IMESelectedConvertedTextUnderline:
570 case ColorID::SpellCheckerUnderline:
571 return NS_IS_SELECTION_SPECIAL_COLOR(aColor);
572 default:
573 break;
576 * In GetColor(), every color that is not a special color is color
577 * corrected. Use false to make other colors color corrected.
579 return false;
582 nscolor nsXPLookAndFeel::GetStandinForNativeColor(ColorID aID,
583 ColorScheme aScheme) {
584 if (aScheme == ColorScheme::Dark) {
585 if (auto color = GenericDarkColor(aID)) {
586 return *color;
590 // The stand-in colors are taken from what the non-native theme needs (for
591 // field/button colors), the Windows 7 Aero theme except Mac-specific colors
592 // which are taken from Mac OS 10.7.
594 #define COLOR(name_, r, g, b) \
595 case ColorID::name_: \
596 return NS_RGB(r, g, b);
598 #define COLORA(name_, r, g, b, a) \
599 case ColorID::name_: \
600 return NS_RGBA(r, g, b, a);
602 switch (aID) {
603 // These are here for the purposes of headless mode.
604 case ColorID::IMESelectedRawTextBackground:
605 case ColorID::IMESelectedConvertedTextBackground:
606 case ColorID::IMERawInputBackground:
607 case ColorID::IMEConvertedTextBackground:
608 return NS_TRANSPARENT;
609 case ColorID::IMESelectedRawTextForeground:
610 case ColorID::IMESelectedConvertedTextForeground:
611 case ColorID::IMERawInputForeground:
612 case ColorID::IMEConvertedTextForeground:
613 return NS_SAME_AS_FOREGROUND_COLOR;
614 case ColorID::IMERawInputUnderline:
615 case ColorID::IMEConvertedTextUnderline:
616 return NS_40PERCENT_FOREGROUND_COLOR;
617 case ColorID::Accentcolor:
618 return widget::sDefaultAccent.ToABGR();
619 case ColorID::Accentcolortext:
620 return widget::sDefaultAccentText.ToABGR();
621 COLOR(SpellCheckerUnderline, 0xff, 0x00, 0x00)
622 COLOR(TextSelectDisabledBackground, 0xAA, 0xAA, 0xAA)
624 // Titlebar colors
625 // deprecated in CSS Color Level 4, same as Buttonborder:
626 COLOR(Activeborder, 0xE3, 0xE3, 0xE3)
627 // deprecated in CSS Color Level 4, same as Buttonborder:
628 COLOR(Inactiveborder, 0xE3, 0xE3, 0xE3)
629 // deprecated in CSS Color Level 4, same as Canvas/Window:
630 COLOR(Activecaption, 0xFF, 0xFF, 0xFF)
631 // deprecated in CSS Color Level 4, same as Canvas/Window:
632 COLOR(Inactivecaption, 0xFF, 0xFF, 0xFF)
633 // deprecated in CSS Color Level 4, same as Canvastext/Windowtext:
634 COLOR(Captiontext, 0x00, 0x00, 0x00)
635 // deprecated in CSS Color Level 4, same as Graytext:
636 COLOR(Inactivecaptiontext, 0x6D, 0x6D, 0x6D)
638 // CSS 2 colors:
639 // deprecated in CSS Color Level 4, same as Canvas/Window:
640 COLOR(Appworkspace, 0xFF, 0xFF, 0xFF)
641 // deprecated in CSS Color Level 4, same as Canvas/Window:
642 COLOR(Background, 0xFF, 0xFF, 0xFF)
643 // deprecated in CSS Color Level 4, same as Buttonface:
644 COLOR(Buttonhighlight, 0xE9, 0xE9, 0xED)
645 // deprecated in CSS Color Level 4, same as Buttonface:
646 COLOR(Buttonshadow, 0xE9, 0xE9, 0xED)
648 // Buttons and comboboxes should be kept in sync since they are drawn with
649 // the same colors by the non-native theme.
650 COLOR(Buttonface, 0xE9, 0xE9, 0xED)
651 COLORA(MozButtondisabledface, 0xE9, 0xE9, 0xED, 128)
653 COLOR(MozCombobox, 0xE9, 0xE9, 0xED)
655 COLOR(Buttontext, 0x00, 0x00, 0x00)
656 COLOR(MozComboboxtext, 0x00, 0x00, 0x00)
658 COLOR(Graytext, 0x6D, 0x6D, 0x6D)
659 COLOR(Highlight, 0x33, 0x99, 0xFF)
660 COLOR(Highlighttext, 0xFF, 0xFF, 0xFF)
661 // deprecated in CSS Color Level 4, same as Canvas/Window:
662 COLOR(Infobackground, 0xFF, 0xFF, 0xFF)
663 // deprecated in CSS Color Level 4, same as Canvastext/Windowtext:
664 COLOR(Infotext, 0x00, 0x00, 0x00)
665 // deprecated in CSS Color Level 4, same as Canvas/Window:
666 COLOR(Menu, 0xFF, 0xFF, 0xFF)
667 // deprecated in CSS Color Level 4, same as Canvastext/Windowtext:
668 COLOR(Menutext, 0x00, 0x00, 0x00)
669 // deprecated in CSS Color Level 4, same as Canvas/Window:
670 COLOR(Scrollbar, 0xFF, 0xFF, 0xFF)
671 // deprecated in CSS Color Level 4, same as Buttonborder:
672 COLOR(Threeddarkshadow, 0xE3, 0xE3, 0xE3)
673 // deprecated in CSS Color Level 4, same as Buttonface:
674 COLOR(Threedface, 0xE9, 0xE9, 0xED)
675 // deprecated in CSS Color Level 4, same as Buttonborder:
676 COLOR(Threedhighlight, 0xE3, 0xE3, 0xE3)
677 COLOR(Threedlightshadow, 0xE3, 0xE3, 0xE3)
678 // deprecated in CSS Color Level 4, same as Buttonborder:
679 COLOR(Threedshadow, 0xE3, 0xE3, 0xE3)
680 COLOR(Buttonborder, 0xE3, 0xE3, 0xE3)
681 COLOR(Mark, 0xFF, 0xFF, 0x00)
682 COLOR(Marktext, 0x00, 0x00, 0x00)
683 COLOR(Window, 0xFF, 0xFF, 0xFF)
684 // deprecated in CSS Color Level 4, same as Buttonborder:
685 COLOR(Windowframe, 0xE3, 0xE3, 0xE3)
686 COLOR(Windowtext, 0x00, 0x00, 0x00)
687 COLOR(Field, 0xFF, 0xFF, 0xFF)
688 COLORA(MozDisabledfield, 0xFF, 0xFF, 0xFF, 128)
689 COLOR(Fieldtext, 0x00, 0x00, 0x00)
690 COLOR(MozDialog, 0xF0, 0xF0, 0xF0)
691 COLOR(MozDialogtext, 0x00, 0x00, 0x00)
692 COLOR(MozColheadertext, 0x00, 0x00, 0x00)
693 COLOR(MozColheaderhovertext, 0x00, 0x00, 0x00)
694 COLOR(MozCellhighlight, 0xF0, 0xF0, 0xF0)
695 COLOR(MozCellhighlighttext, 0x00, 0x00, 0x00)
696 COLOR(Selecteditem, 0x33, 0x99, 0xFF)
697 COLOR(Selecteditemtext, 0xFF, 0xFF, 0xFF)
698 COLOR(MozButtonhoverface, 0xd0, 0xd0, 0xd7)
699 COLOR(MozButtonhovertext, 0x00, 0x00, 0x00)
700 COLOR(MozButtonactiveface, 0xb1, 0xb1, 0xb9)
701 COLOR(MozButtonactivetext, 0x00, 0x00, 0x00)
702 COLOR(MozMenuhover, 0x33, 0x99, 0xFF)
703 COLOR(MozMenuhovertext, 0x00, 0x00, 0x00)
704 COLOR(MozMenubarhovertext, 0x00, 0x00, 0x00)
705 COLOR(MozMenuhoverdisabled, 0xF0, 0xF0, 0xF0)
706 COLOR(MozEventreerow, 0xFF, 0xFF, 0xFF)
707 COLOR(MozOddtreerow, 0xFF, 0xFF, 0xFF)
708 COLOR(MozMacFocusring, 0x60, 0x9D, 0xD7)
709 COLOR(MozMacDisabledtoolbartext, 0x3F, 0x3F, 0x3F)
710 // Seems to be the default color (hardcoded because of bug 1065998)
711 COLOR(MozNativehyperlinktext, 0x00, 0x66, 0xCC)
712 COLOR(MozNativevisitedhyperlinktext, 0x55, 0x1A, 0x8B)
713 COLOR(MozAutofillBackground, 0xff, 0xfc, 0xc8)
714 COLOR(TargetTextBackground, 0xff, 0xeb, 0xcd)
715 COLOR(TargetTextForeground, 0x00, 0x00, 0x00)
716 default:
717 break;
719 return NS_RGB(0xFF, 0xFF, 0xFF);
722 #undef COLOR
723 #undef COLORA
725 // Taken from in-content/common.inc.css's dark theme.
726 Maybe<nscolor> nsXPLookAndFeel::GenericDarkColor(ColorID aID) {
727 nscolor color = NS_RGB(0, 0, 0);
728 static constexpr nscolor kWindowBackground = NS_RGB(28, 27, 34);
729 static constexpr nscolor kWindowText = NS_RGB(251, 251, 254);
730 switch (aID) {
731 case ColorID::Window: // --in-content-page-background
732 case ColorID::Background:
733 case ColorID::Appworkspace:
734 case ColorID::Scrollbar:
735 case ColorID::Infobackground:
736 color = kWindowBackground;
737 break;
739 case ColorID::Menu:
740 color = NS_RGB(0x2b, 0x2a, 0x33);
741 break;
743 case ColorID::MozMenuhovertext:
744 case ColorID::MozMenubarhovertext:
745 case ColorID::Menutext:
746 color = NS_RGB(0xfb, 0xfb, 0xfe);
747 break;
749 case ColorID::MozMenuhover:
750 color = NS_RGB(0x52, 0x52, 0x5e);
751 break;
753 case ColorID::MozMenuhoverdisabled:
754 color = NS_RGB(0x3a, 0x39, 0x44);
755 break;
757 case ColorID::MozEventreerow:
758 case ColorID::MozOddtreerow:
759 case ColorID::MozDialog: // --in-content-box-background
760 color = NS_RGB(35, 34, 43);
761 break;
762 case ColorID::Windowtext: // --in-content-page-color
763 case ColorID::MozDialogtext:
764 case ColorID::MozSidebartext:
765 case ColorID::Fieldtext:
766 case ColorID::Infotext:
767 case ColorID::Buttontext: // --in-content-button-text-color (via
768 // --in-content-page-color)
769 case ColorID::MozComboboxtext:
770 case ColorID::MozButtonhovertext:
771 case ColorID::MozButtonactivetext:
772 case ColorID::MozHeaderbartext:
773 case ColorID::MozHeaderbarinactivetext:
774 case ColorID::Captiontext:
775 case ColorID::Inactivecaptiontext: // TODO(emilio): Maybe make
776 // Inactivecaptiontext Graytext?
777 case ColorID::MozColheadertext:
778 case ColorID::MozColheaderhovertext:
779 case ColorID::MozColheaderactivetext:
780 color = kWindowText;
781 break;
782 case ColorID::Buttonshadow:
783 case ColorID::Threedshadow:
784 case ColorID::MozSidebarborder:
785 case ColorID::Threedlightshadow:
786 case ColorID::Threedhighlight:
787 case ColorID::Windowframe:
788 case ColorID::Buttonborder: // --in-content-box-border-color computed
789 // with kWindowText above
790 // kWindowBackground.
791 case ColorID::Graytext: // opacity: 0.4 of kWindowText blended over the
792 // "Window" background color, which happens to be
793 // the same :-)
794 color = NS_ComposeColors(kWindowBackground, NS_RGBA(251, 251, 254, 102));
795 break;
796 case ColorID::MozCellhighlight:
797 case ColorID::Selecteditem: // --in-content-primary-button-background /
798 // --in-content-item-selected
799 color = NS_RGB(0, 221, 255);
800 break;
801 case ColorID::MozSidebar:
802 case ColorID::Field:
803 case ColorID::Buttonface: // --in-content-button-background
804 case ColorID::Buttonhighlight:
805 case ColorID::MozColheader:
806 case ColorID::Threedface:
807 case ColorID::MozCombobox:
808 case ColorID::MozCellhighlighttext:
809 case ColorID::Selecteditemtext: // --in-content-primary-button-text-color /
810 // --in-content-item-selected-text
811 color = NS_RGB(43, 42, 51);
812 break;
813 case ColorID::Threeddarkshadow: // Same as Threedlightshadow but with the
814 // background.
815 case ColorID::MozDisabledfield: // opacity: 0.4 of the face above blended
816 // over the "Window" background color.
817 case ColorID::MozButtondisabledface:
818 color = NS_ComposeColors(kWindowBackground, NS_RGBA(43, 42, 51, 102));
819 break;
820 case ColorID::MozButtonhoverface: // --in-content-button-background-hover
821 case ColorID::MozColheaderhover:
822 color = NS_RGB(82, 82, 94);
823 break;
824 case ColorID::MozButtonactiveface: // --in-content-button-background-active
825 case ColorID::MozColheaderactive:
826 color = NS_RGB(91, 91, 102);
827 break;
828 case ColorID::Highlight:
829 color = NS_RGBA(0, 221, 255, 78);
830 break;
831 case ColorID::Highlighttext:
832 color = NS_SAME_AS_FOREGROUND_COLOR;
833 break;
834 case ColorID::MozNativehyperlinktext:
835 // If you change this color, you probably also want to change the default
836 // value of browser.anchor_color.dark.
837 color = NS_RGB(0x8c, 0x8c, 0xff);
838 break;
839 case ColorID::MozNativevisitedhyperlinktext:
840 // If you change this color, you probably also want to change the default
841 // value of browser.visited_color.dark.
842 color = NS_RGB(0xff, 0xad, 0xff);
843 break;
844 case ColorID::SpellCheckerUnderline:
845 // This is the default for active links in dark mode as well
846 // (browser.active_color.dark). See bug 1755564 for some analysis and
847 // other options too.
848 color = NS_RGB(0xff, 0x66, 0x66);
849 break;
850 case ColorID::Activeborder:
851 case ColorID::Inactiveborder:
852 color = NS_RGB(57, 57, 57);
853 break;
854 case ColorID::MozHeaderbar:
855 case ColorID::MozHeaderbarinactive:
856 case ColorID::Activecaption:
857 case ColorID::Inactivecaption:
858 color = NS_RGB(28, 27, 34);
859 break;
860 case ColorID::MozAutofillBackground:
861 // This is the light version of this color, but darkened to have good
862 // contrast with our white-ish FieldText.
863 color = NS_RGB(0x72, 0x6c, 0x00);
864 break;
865 default:
866 return Nothing();
868 return Some(color);
871 // Uncomment the #define below if you want to debug system color use in a skin
872 // that uses them. When set, it will make all system color pairs that are
873 // appropriate for foreground/background pairing the same. This means if the
874 // skin is using system colors correctly you will not be able to see *any* text.
876 // #define DEBUG_SYSTEM_COLOR_USE
878 #ifdef DEBUG_SYSTEM_COLOR_USE
879 static nsresult SystemColorUseDebuggingColor(LookAndFeel::ColorID aID,
880 nscolor& aResult) {
881 using ColorID = LookAndFeel::ColorID;
883 switch (aID) {
884 // css2 http://www.w3.org/TR/REC-CSS2/ui.html#system-colors
885 case ColorID::Activecaption:
886 // active window caption background
887 case ColorID::Captiontext:
888 // text in active window caption
889 aResult = NS_RGB(0xff, 0x00, 0x00);
890 break;
892 case ColorID::Highlight:
893 // background of selected item
894 case ColorID::Highlighttext:
895 // text of selected item
896 aResult = NS_RGB(0xff, 0xff, 0x00);
897 break;
899 case ColorID::Inactivecaption:
900 // inactive window caption
901 case ColorID::Inactivecaptiontext:
902 // text in inactive window caption
903 aResult = NS_RGB(0x66, 0x66, 0x00);
904 break;
906 case ColorID::Infobackground:
907 // tooltip background color
908 case ColorID::Infotext:
909 // tooltip text color
910 aResult = NS_RGB(0x00, 0xff, 0x00);
911 break;
913 case ColorID::Menu:
914 // menu background
915 case ColorID::Menutext:
916 // menu text
917 aResult = NS_RGB(0x00, 0xff, 0xff);
918 break;
920 case ColorID::Threedface:
921 case ColorID::Buttonface:
922 // 3-D face color
923 case ColorID::Buttontext:
924 // text on push buttons
925 aResult = NS_RGB(0x00, 0x66, 0x66);
926 break;
928 case ColorID::Window:
929 case ColorID::Windowtext:
930 aResult = NS_RGB(0x00, 0x00, 0xff);
931 break;
933 // from the CSS3 working draft (not yet finalized)
934 // http://www.w3.org/tr/2000/wd-css3-userint-20000216.html#color
936 case ColorID::Field:
937 case ColorID::Fieldtext:
938 aResult = NS_RGB(0xff, 0x00, 0xff);
939 break;
941 case ColorID::MozDialog:
942 case ColorID::MozDialogtext:
943 aResult = NS_RGB(0x66, 0x00, 0x66);
944 break;
946 default:
947 return NS_ERROR_NOT_AVAILABLE;
950 return NS_OK;
952 #endif
954 static nsresult GetPrefColor(const char* aPref, nscolor& aResult) {
955 nsAutoCString colorStr;
956 MOZ_TRY(Preferences::GetCString(aPref, colorStr));
957 if (!ServoCSSParser::ComputeColor(nullptr, NS_RGB(0, 0, 0), colorStr,
958 &aResult)) {
959 return NS_ERROR_FAILURE;
961 return NS_OK;
964 static nsresult GetColorFromPref(LookAndFeel::ColorID aID, ColorScheme aScheme,
965 nscolor& aResult) {
966 const char* prefName = sColorPrefs[size_t(aID)];
967 if (aScheme == ColorScheme::Dark) {
968 nsAutoCString darkPrefName(prefName);
969 darkPrefName.Append(".dark");
970 if (NS_SUCCEEDED(GetPrefColor(darkPrefName.get(), aResult))) {
971 return NS_OK;
974 return GetPrefColor(prefName, aResult);
977 // All these routines will return NS_OK if they have a value,
978 // in which case the nsLookAndFeel should use that value;
979 // otherwise we'll return NS_ERROR_NOT_AVAILABLE, in which case, the
980 // platform-specific nsLookAndFeel should use its own values instead.
981 nsresult nsXPLookAndFeel::GetColorValue(ColorID aID, ColorScheme aScheme,
982 UseStandins aUseStandins,
983 nscolor& aResult) {
984 if (!sInitialized) {
985 Init();
988 #ifdef DEBUG_SYSTEM_COLOR_USE
989 if (NS_SUCCEEDED(SystemColorUseDebuggingColor(aID, aResult))) {
990 return NS_OK;
992 #endif
994 auto& cache = sColorCaches.Get(aScheme, aUseStandins);
995 if (const auto* cached = cache.Get(aID)) {
996 if (cached->isNothing()) {
997 return NS_ERROR_FAILURE;
999 aResult = cached->value();
1000 return NS_OK;
1003 // NOTE: Servo holds a lock and the main thread is paused, so writing to the
1004 // global cache here is fine.
1005 auto result = GetUncachedColor(aID, aScheme, aUseStandins);
1006 cache.Insert(aID, result);
1007 if (!result) {
1008 return NS_ERROR_FAILURE;
1010 aResult = *result;
1011 return NS_OK;
1014 Maybe<nscolor> nsXPLookAndFeel::GetUncachedColor(ColorID aID,
1015 ColorScheme aScheme,
1016 UseStandins aUseStandins) {
1017 if (aUseStandins == UseStandins::Yes) {
1018 return Some(GetStandinForNativeColor(aID, aScheme));
1020 nscolor r;
1021 if (NS_SUCCEEDED(GetColorFromPref(aID, aScheme, r))) {
1022 return Some(r);
1024 if (NS_SUCCEEDED(NativeGetColor(aID, aScheme, r))) {
1025 if (gfxPlatform::GetCMSMode() == CMSMode::All && !IsSpecialColor(aID, r)) {
1026 qcms_transform* transform = gfxPlatform::GetCMSInverseRGBTransform();
1027 if (transform) {
1028 uint8_t color[4];
1029 color[0] = NS_GET_R(r);
1030 color[1] = NS_GET_G(r);
1031 color[2] = NS_GET_B(r);
1032 color[3] = NS_GET_A(r);
1033 qcms_transform_data(transform, color, color, 1);
1034 r = NS_RGBA(color[0], color[1], color[2], color[3]);
1038 return Some(r);
1040 return Nothing();
1043 nsresult nsXPLookAndFeel::GetIntValue(IntID aID, int32_t& aResult) {
1044 if (!sInitialized) {
1045 Init();
1048 if (const auto* cached = sIntCache.Get(aID)) {
1049 if (cached->isNothing()) {
1050 return NS_ERROR_FAILURE;
1052 aResult = cached->value();
1053 return NS_OK;
1056 if (NS_SUCCEEDED(Preferences::GetInt(sIntPrefs[size_t(aID)], &aResult))) {
1057 sIntCache.Insert(aID, Some(aResult));
1058 return NS_OK;
1061 if (NS_FAILED(NativeGetInt(aID, aResult))) {
1062 sIntCache.Insert(aID, Nothing());
1063 return NS_ERROR_FAILURE;
1066 sIntCache.Insert(aID, Some(aResult));
1067 return NS_OK;
1070 nsresult nsXPLookAndFeel::GetFloatValue(FloatID aID, float& aResult) {
1071 if (!sInitialized) {
1072 Init();
1075 if (const auto* cached = sFloatCache.Get(aID)) {
1076 if (cached->isNothing()) {
1077 return NS_ERROR_FAILURE;
1079 aResult = cached->value();
1080 return NS_OK;
1083 int32_t pref = 0;
1084 if (NS_SUCCEEDED(Preferences::GetInt(sFloatPrefs[size_t(aID)], &pref))) {
1085 aResult = float(pref) / 100.0f;
1086 sFloatCache.Insert(aID, Some(aResult));
1087 return NS_OK;
1090 if (NS_FAILED(NativeGetFloat(aID, aResult))) {
1091 sFloatCache.Insert(aID, Nothing());
1092 return NS_ERROR_FAILURE;
1095 sFloatCache.Insert(aID, Some(aResult));
1096 return NS_OK;
1099 bool nsXPLookAndFeel::LookAndFeelFontToStyle(const LookAndFeelFont& aFont,
1100 nsString& aName,
1101 gfxFontStyle& aStyle) {
1102 if (!aFont.haveFont()) {
1103 return false;
1105 aName = aFont.name();
1106 aStyle = gfxFontStyle();
1107 aStyle.size = aFont.size();
1108 aStyle.weight = FontWeight::FromInt(aFont.weight());
1109 aStyle.style =
1110 aFont.italic() ? FontSlantStyle::ITALIC : FontSlantStyle::NORMAL;
1111 aStyle.systemFont = true;
1112 return true;
1115 widget::LookAndFeelFont nsXPLookAndFeel::StyleToLookAndFeelFont(
1116 const nsAString& aName, const gfxFontStyle& aStyle) {
1117 LookAndFeelFont font;
1118 font.haveFont() = true;
1119 font.name() = aName;
1120 font.size() = aStyle.size;
1121 font.weight() = aStyle.weight.ToFloat();
1122 font.italic() = aStyle.style.IsItalic();
1123 MOZ_ASSERT(aStyle.style.IsNormal() || aStyle.style.IsItalic(),
1124 "Cannot handle oblique font style");
1125 #ifdef DEBUG
1127 // Assert that all the remaining font style properties have their
1128 // default values.
1129 gfxFontStyle candidate = aStyle;
1130 gfxFontStyle defaults{};
1131 candidate.size = defaults.size;
1132 candidate.weight = defaults.weight;
1133 candidate.style = defaults.style;
1134 MOZ_ASSERT(candidate.Equals(defaults),
1135 "Some font style properties not supported");
1137 #endif
1138 return font;
1141 bool nsXPLookAndFeel::GetFontValue(FontID aID, nsString& aName,
1142 gfxFontStyle& aStyle) {
1143 if (const LookAndFeelFont* cached = sFontCache.Get(aID)) {
1144 return LookAndFeelFontToStyle(*cached, aName, aStyle);
1147 LookAndFeelFont font;
1148 auto GetFontsFromPrefs = [&]() -> bool {
1149 nsDependentCString pref(sFontPrefs[size_t(aID)]);
1150 if (NS_FAILED(Preferences::GetString(pref.get(), aName))) {
1151 return false;
1153 font.haveFont() = true;
1154 font.name() = aName;
1155 font.size() = Preferences::GetFloat(nsAutoCString(pref + ".size"_ns).get());
1156 // This is written this way rather than using the fallback so that an empty
1157 // pref (such like the one about:config creates) doesn't cause system fonts
1158 // to have zero-size.
1159 if (font.size() < 1.0f) {
1160 font.size() = StyleFONT_MEDIUM_PX;
1162 font.weight() = Preferences::GetFloat(
1163 nsAutoCString(pref + ".weight"_ns).get(), FontWeight::NORMAL.ToFloat());
1164 font.italic() =
1165 Preferences::GetBool(nsAutoCString(pref + ".italic"_ns).get());
1166 return true;
1169 if (GetFontsFromPrefs()) {
1170 LookAndFeelFontToStyle(font, aName, aStyle);
1171 } else if (NativeGetFont(aID, aName, aStyle)) {
1172 font = StyleToLookAndFeelFont(aName, aStyle);
1173 } else {
1174 MOZ_ASSERT(!font.haveFont());
1176 bool success = font.haveFont();
1177 sFontCache.Insert(aID, std::move(font));
1178 return success;
1181 void nsXPLookAndFeel::RefreshImpl() {
1182 // Wipe out our caches.
1183 sColorCaches.Clear();
1184 sFontCache.Clear();
1185 sFloatCache.Clear();
1186 sIntCache.Clear();
1188 if (XRE_IsParentProcess()) {
1189 nsLayoutUtils::RecomputeSmoothScrollDefault();
1190 // Clear any cached FullLookAndFeel data, which is now invalid.
1191 widget::RemoteLookAndFeel::ClearCachedData();
1195 static bool sRecordedLookAndFeelTelemetry = false;
1197 void nsXPLookAndFeel::RecordTelemetry() {
1198 if (!XRE_IsParentProcess()) {
1199 return;
1202 if (sRecordedLookAndFeelTelemetry) {
1203 return;
1206 sRecordedLookAndFeelTelemetry = true;
1208 int32_t i;
1209 Telemetry::ScalarSet(
1210 Telemetry::ScalarID::WIDGET_DARK_MODE,
1211 NS_SUCCEEDED(GetIntValue(IntID::SystemUsesDarkTheme, i)) && i != 0);
1213 RecordLookAndFeelSpecificTelemetry();
1216 namespace mozilla {
1218 bool LookAndFeel::sGlobalThemeChanged;
1219 static widget::ThemeChangeKind sGlobalThemeChangeKind{0};
1221 void LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind aKind) {
1222 sGlobalThemeChanged = true;
1223 sGlobalThemeChangeKind |= aKind;
1225 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
1226 const char16_t kind[] = {char16_t(aKind), 0};
1227 obs->NotifyObservers(nullptr, "internal-look-and-feel-changed", kind);
1231 void LookAndFeel::DoHandleGlobalThemeChange() {
1232 MOZ_ASSERT(sGlobalThemeChanged);
1233 sGlobalThemeChanged = false;
1234 auto kind = std::exchange(sGlobalThemeChangeKind, widget::ThemeChangeKind(0));
1236 // Tell the theme that it changed, so it can flush any handles to stale theme
1237 // data.
1239 // We can use the *DoNotUseDirectly functions directly here, because we want
1240 // to notify all possible themes in a given process (but just once).
1241 if (XRE_IsParentProcess()) {
1242 if (nsCOMPtr<nsITheme> theme = do_GetNativeThemeDoNotUseDirectly()) {
1243 theme->ThemeChanged();
1246 if (nsCOMPtr<nsITheme> theme = do_GetBasicNativeThemeDoNotUseDirectly()) {
1247 theme->ThemeChanged();
1250 // Clear all cached LookAndFeel colors.
1251 LookAndFeel::Refresh();
1253 // Reset default background and foreground colors for the document since they
1254 // may be using system colors, color scheme, etc.
1255 PreferenceSheet::Refresh();
1257 // Vector images (SVG) may be using theme colors so we discard all cached
1258 // surfaces. (We could add a vector image only version of DiscardAll, but
1259 // in bug 940625 we decided theme changes are rare enough not to bother.)
1260 image::SurfaceCacheUtils::DiscardAll();
1262 if (XRE_IsParentProcess()) {
1263 dom::ContentParent::BroadcastThemeUpdate(kind);
1266 nsContentUtils::AddScriptRunner(
1267 NS_NewRunnableFunction("HandleGlobalThemeChange", [] {
1268 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
1269 obs->NotifyObservers(nullptr, "look-and-feel-changed", nullptr);
1271 }));
1274 #define BIT_FOR(_c) (1ull << size_t(ColorID::_c))
1276 // We want to use a non-native color scheme for the non-native theme (except in
1277 // high-contrast mode), so spoof some of the colors with stand-ins to prevent
1278 // lack of contrast.
1279 static constexpr std::bitset<size_t(ColorID::End)> sNonNativeThemeStandinColors{
1280 // Used by default button styles.
1281 BIT_FOR(Buttonface) | BIT_FOR(Buttontext) | BIT_FOR(MozButtonhoverface) |
1282 BIT_FOR(MozButtonhovertext) | BIT_FOR(MozButtonactiveface) |
1283 BIT_FOR(MozButtonactivetext) | BIT_FOR(MozButtondisabledface) |
1284 BIT_FOR(Buttonborder) |
1285 // Used by select elements.
1286 BIT_FOR(MozCombobox) | BIT_FOR(MozComboboxtext) |
1287 BIT_FOR(Threedlightshadow) |
1288 // For symmetry with the above.
1289 BIT_FOR(Threeddarkshadow) |
1290 // Used by fieldset borders.
1291 BIT_FOR(Threedface) |
1292 // Used by input / textarea.
1293 BIT_FOR(Field) | BIT_FOR(Fieldtext) |
1294 // Used by disabled form controls.
1295 BIT_FOR(MozDisabledfield) | BIT_FOR(Graytext) |
1296 // Per spec, the following colors are deprecated, see
1297 // https://drafts.csswg.org/css-color-4/#deprecated-system-colors
1298 // should match ButtonFace:
1299 BIT_FOR(Buttonhighlight) | BIT_FOR(Buttonshadow) | BIT_FOR(Threedface) |
1300 // should match ButtonBorder:
1301 BIT_FOR(Activeborder) | BIT_FOR(Inactiveborder) |
1302 BIT_FOR(Threeddarkshadow) | BIT_FOR(Threedhighlight) |
1303 BIT_FOR(Threedshadow) | BIT_FOR(Windowframe) |
1304 // should match GrayText:
1305 BIT_FOR(Inactivecaptiontext) |
1306 // should match Canvas/Window:
1307 BIT_FOR(Appworkspace) | BIT_FOR(Background) | BIT_FOR(Inactivecaption) |
1308 BIT_FOR(Infobackground) | BIT_FOR(Menu) | BIT_FOR(Scrollbar) |
1309 // should match CanvasText/WindowText:
1310 BIT_FOR(Activecaption) | BIT_FOR(Captiontext) | BIT_FOR(Infotext) |
1311 BIT_FOR(Menutext) |
1312 // Some pages expect these to return windows-like colors, see bug 1773795.
1313 // Also, per spec, these should match Canvas/CanvasText, see
1314 // https://drafts.csswg.org/css-color-4/#valdef-color-window and
1315 // https://drafts.csswg.org/css-color-4/#valdef-color-windowtext
1316 BIT_FOR(Window) | BIT_FOR(Windowtext)};
1317 #undef BIT_FOR
1319 static bool ShouldUseStandinsForNativeColorForNonNativeTheme(
1320 const dom::Document& aDoc, LookAndFeel::ColorID aColor,
1321 const PreferenceSheet::Prefs& aPrefs) {
1322 const bool shouldUseStandinsForColor = [&] {
1323 if (sNonNativeThemeStandinColors[size_t(aColor)]) {
1324 return true;
1326 // There are platforms where we want the content-exposed accent color to be
1327 // the windows blue rather than the system accent color, for now.
1328 return !StaticPrefs::widget_non_native_theme_use_theme_accent() &&
1329 (aColor == LookAndFeel::ColorID::Accentcolor ||
1330 aColor == LookAndFeel::ColorID::Accentcolortext);
1331 }();
1333 return shouldUseStandinsForColor && aDoc.ShouldAvoidNativeTheme() &&
1334 !aPrefs.NonNativeThemeShouldBeHighContrast();
1337 bool LookAndFeel::IsDarkColor(nscolor aColor) {
1338 // Given https://www.w3.org/TR/WCAG20/#contrast-ratiodef, this is the
1339 // threshold that tells us whether contrast is better against white or black.
1341 // Contrast ratio against black is: (L + 0.05) / 0.05
1342 // Contrast ratio against white is: 1.05 / (L + 0.05)
1344 // So the intersection is:
1346 // (L + 0.05) / 0.05 = 1.05 / (L + 0.05)
1348 // And the solution to that equation is:
1350 // sqrt(1.05 * 0.05) - 0.05
1352 // So we consider a color dark if the contrast is below this threshold, and
1353 // it's at least half-opaque.
1354 constexpr float kThreshold = 0.179129;
1355 return NS_GET_A(aColor) > 127 &&
1356 RelativeLuminanceUtils::Compute(aColor) < kThreshold;
1359 ColorScheme LookAndFeel::ColorSchemeForStyle(
1360 const dom::Document& aDoc, const StyleColorSchemeFlags& aFlags,
1361 ColorSchemeMode aMode) {
1362 const auto& prefs = PreferenceSheet::PrefsFor(aDoc);
1363 StyleColorSchemeFlags style(aFlags);
1364 if (!style) {
1365 style._0 = aDoc.GetColorSchemeBits();
1367 const bool supportsDark = bool(style & StyleColorSchemeFlags::DARK);
1368 const bool supportsLight = bool(style & StyleColorSchemeFlags::LIGHT);
1369 if (supportsLight && supportsDark) {
1370 // Both color-schemes are explicitly supported, use the preferred one.
1371 return aDoc.PreferredColorScheme();
1373 if (supportsDark || supportsLight) {
1374 // One color-scheme is explicitly supported and one isn't, so use the one
1375 // the content supports.
1376 return supportsDark ? ColorScheme::Dark : ColorScheme::Light;
1378 // No value specified. Chrome docs, and forced-colors mode always supports
1379 // both, so use the preferred color-scheme.
1380 if (aMode == ColorSchemeMode::Preferred || aDoc.ChromeRulesEnabled() ||
1381 !prefs.mUseDocumentColors) {
1382 return aDoc.PreferredColorScheme();
1384 // Otherwise default content to light.
1385 return ColorScheme::Light;
1388 LookAndFeel::ColorScheme LookAndFeel::ColorSchemeForFrame(
1389 const nsIFrame* aFrame, ColorSchemeMode aMode) {
1390 return ColorSchemeForStyle(*aFrame->PresContext()->Document(),
1391 aFrame->StyleUI()->mColorScheme.bits, aMode);
1394 // static
1395 Maybe<nscolor> LookAndFeel::GetColor(ColorID aId, ColorScheme aScheme,
1396 UseStandins aUseStandins) {
1397 nscolor result;
1398 nsresult rv = nsLookAndFeel::GetInstance()->GetColorValue(
1399 aId, aScheme, aUseStandins, result);
1400 if (NS_FAILED(rv)) {
1401 return Nothing();
1403 return Some(result);
1406 // Returns whether there is a CSS color name for this color.
1407 static bool ColorIsCSSAccessible(LookAndFeel::ColorID aId) {
1408 using ColorID = LookAndFeel::ColorID;
1410 switch (aId) {
1411 case ColorID::TextSelectDisabledBackground:
1412 case ColorID::TextSelectAttentionBackground:
1413 case ColorID::TextSelectAttentionForeground:
1414 case ColorID::TextHighlightBackground:
1415 case ColorID::TextHighlightForeground:
1416 case ColorID::ThemedScrollbar:
1417 case ColorID::ThemedScrollbarInactive:
1418 case ColorID::ThemedScrollbarThumb:
1419 case ColorID::ThemedScrollbarThumbActive:
1420 case ColorID::ThemedScrollbarThumbInactive:
1421 case ColorID::ThemedScrollbarThumbHover:
1422 case ColorID::IMERawInputBackground:
1423 case ColorID::IMERawInputForeground:
1424 case ColorID::IMERawInputUnderline:
1425 case ColorID::IMESelectedRawTextBackground:
1426 case ColorID::IMESelectedRawTextForeground:
1427 case ColorID::IMESelectedRawTextUnderline:
1428 case ColorID::IMEConvertedTextBackground:
1429 case ColorID::IMEConvertedTextForeground:
1430 case ColorID::IMEConvertedTextUnderline:
1431 case ColorID::IMESelectedConvertedTextBackground:
1432 case ColorID::IMESelectedConvertedTextForeground:
1433 case ColorID::IMESelectedConvertedTextUnderline:
1434 case ColorID::SpellCheckerUnderline:
1435 return false;
1436 default:
1437 break;
1440 return true;
1443 LookAndFeel::UseStandins LookAndFeel::ShouldUseStandins(
1444 const dom::Document& aDoc, ColorID aId) {
1445 const auto& prefs = PreferenceSheet::PrefsFor(aDoc);
1446 if (ShouldUseStandinsForNativeColorForNonNativeTheme(aDoc, aId, prefs)) {
1447 return UseStandins::Yes;
1449 if (prefs.mUseStandins && ColorIsCSSAccessible(aId)) {
1450 return UseStandins::Yes;
1452 return UseStandins::No;
1455 Maybe<nscolor> LookAndFeel::GetColor(ColorID aId, const nsIFrame* aFrame) {
1456 const auto* doc = aFrame->PresContext()->Document();
1457 return GetColor(aId, ColorSchemeForFrame(aFrame),
1458 ShouldUseStandins(*doc, aId));
1461 // static
1462 nsresult LookAndFeel::GetInt(IntID aID, int32_t* aResult) {
1463 return nsLookAndFeel::GetInstance()->GetIntValue(aID, *aResult);
1466 // static
1467 nsresult LookAndFeel::GetFloat(FloatID aID, float* aResult) {
1468 return nsLookAndFeel::GetInstance()->GetFloatValue(aID, *aResult);
1471 // static
1472 bool LookAndFeel::GetFont(FontID aID, nsString& aName, gfxFontStyle& aStyle) {
1473 return nsLookAndFeel::GetInstance()->GetFontValue(aID, aName, aStyle);
1476 // static
1477 char16_t LookAndFeel::GetPasswordCharacter() {
1478 return nsLookAndFeel::GetInstance()->GetPasswordCharacterImpl();
1481 // static
1482 bool LookAndFeel::GetEchoPassword() {
1483 if (StaticPrefs::editor_password_mask_delay() >= 0) {
1484 return StaticPrefs::editor_password_mask_delay() > 0;
1486 return nsLookAndFeel::GetInstance()->GetEchoPasswordImpl();
1489 // static
1490 uint32_t LookAndFeel::GetPasswordMaskDelay() {
1491 int32_t delay = StaticPrefs::editor_password_mask_delay();
1492 if (delay < 0) {
1493 return nsLookAndFeel::GetInstance()->GetPasswordMaskDelayImpl();
1495 return delay;
1498 bool LookAndFeel::DrawInTitlebar() {
1499 switch (StaticPrefs::browser_tabs_inTitlebar()) {
1500 case 0:
1501 return false;
1502 case 1:
1503 return true;
1504 default:
1505 break;
1507 return nsLookAndFeel::GetInstance()->GetDefaultDrawInTitlebar();
1510 LookAndFeel::TitlebarAction LookAndFeel::GetTitlebarAction(
1511 TitlebarEvent aEvent) {
1512 return nsLookAndFeel::GetInstance()->GetTitlebarAction(aEvent);
1515 void LookAndFeel::GetThemeInfo(nsACString& aOut) {
1516 nsLookAndFeel::GetInstance()->GetThemeInfo(aOut);
1519 uint32_t LookAndFeel::GetMenuAccessKey() {
1520 return StaticPrefs::ui_key_menuAccessKey();
1523 Modifiers LookAndFeel::GetMenuAccessKeyModifiers() {
1524 switch (GetMenuAccessKey()) {
1525 case dom::KeyboardEvent_Binding::DOM_VK_SHIFT:
1526 return MODIFIER_SHIFT;
1527 case dom::KeyboardEvent_Binding::DOM_VK_CONTROL:
1528 return MODIFIER_CONTROL;
1529 case dom::KeyboardEvent_Binding::DOM_VK_ALT:
1530 return MODIFIER_ALT;
1531 case dom::KeyboardEvent_Binding::DOM_VK_META:
1532 case dom::KeyboardEvent_Binding::DOM_VK_WIN:
1533 return MODIFIER_META;
1534 default:
1535 return 0;
1539 // static
1540 void LookAndFeel::Refresh() {
1541 nsLookAndFeel::GetInstance()->RefreshImpl();
1542 widget::Theme::LookAndFeelChanged();
1545 // static
1546 void LookAndFeel::NativeInit() { nsLookAndFeel::GetInstance()->NativeInit(); }
1548 // static
1549 void LookAndFeel::SetData(widget::FullLookAndFeel&& aTables) {
1550 nsLookAndFeel::GetInstance()->SetDataImpl(std::move(aTables));
1553 } // namespace mozilla