no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / widget / nsXPLookAndFeel.cpp
blob3302e6f3eba81b1778b4d5f04b104cfcbed129f8
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, kEnd, Value> 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.caretVisibleWithSelection",
132 "ui.selectTextfieldsOnKeyFocus",
133 "ui.submenuDelay",
134 "ui.menusCanOverlapOSBar",
135 "ui.useOverlayScrollbars",
136 "ui.allowOverlayScrollbarsOverlap",
137 "ui.skipNavigatingDisabledMenuItem",
138 "ui.dragThresholdX",
139 "ui.dragThresholdY",
140 "ui.useAccessibilityTheme",
141 "ui.scrollArrowStyle",
142 "ui.scrollButtonLeftMouseButtonAction",
143 "ui.scrollButtonMiddleMouseButtonAction",
144 "ui.scrollButtonRightMouseButtonAction",
145 "ui.treeOpenDelay",
146 "ui.treeCloseDelay",
147 "ui.treeLazyScrollDelay",
148 "ui.treeScrollDelay",
149 "ui.treeScrollLinesMax",
150 "accessibility.tabfocus", // Weird one...
151 "ui.chosenMenuItemsShouldBlink",
152 "ui.windowsAccentColorInTitlebar",
153 "ui.macBigSurTheme",
154 "ui.macRTL",
155 "ui.alertNotificationOrigin",
156 "ui.scrollToClick",
157 "ui.IMERawInputUnderlineStyle",
158 "ui.IMESelectedRawTextUnderlineStyle",
159 "ui.IMEConvertedTextUnderlineStyle",
160 "ui.IMESelectedConvertedTextUnderlineStyle",
161 "ui.SpellCheckerUnderlineStyle",
162 "ui.menuBarDrag",
163 "ui.scrollbarButtonAutoRepeatBehavior",
164 "ui.tooltipDelay",
165 "ui.swipeAnimationEnabled",
166 "ui.scrollbarDisplayOnMouseMove",
167 "ui.scrollbarFadeBeginDelay",
168 "ui.scrollbarFadeDuration",
169 "ui.contextMenuOffsetVertical",
170 "ui.contextMenuOffsetHorizontal",
171 "ui.GtkCSDAvailable",
172 "ui.GtkCSDMinimizeButton",
173 "ui.GtkCSDMaximizeButton",
174 "ui.GtkCSDCloseButton",
175 "ui.GtkCSDMinimizeButtonPosition",
176 "ui.GtkCSDMaximizeButtonPosition",
177 "ui.GtkCSDCloseButtonPosition",
178 "ui.GtkCSDReversedPlacement",
179 "ui.systemUsesDarkTheme",
180 "ui.prefersReducedMotion",
181 "ui.prefersReducedTransparency",
182 "ui.invertedColors",
183 "ui.primaryPointerCapabilities",
184 "ui.allPointerCapabilities",
185 "ui.systemScrollbarSize",
186 "ui.touchDeviceSupportPresent",
187 "ui.titlebarRadius",
188 "ui.dynamicRange",
189 "ui.videoDynamicRange",
190 "ui.panelAnimations",
191 "ui.hideCursorWhileTyping",
194 static_assert(ArrayLength(sIntPrefs) == size_t(LookAndFeel::IntID::End),
195 "Should have a pref for each int value");
197 // This array MUST be kept in the same order as the float id list in
198 // LookAndFeel.h
199 // clang-format off
200 static const char sFloatPrefs[][37] = {
201 "ui.IMEUnderlineRelativeSize",
202 "ui.SpellCheckerUnderlineRelativeSize",
203 "ui.caretAspectRatio",
204 "ui.textScaleFactor",
205 "ui.cursorScale",
207 // clang-format on
209 static_assert(ArrayLength(sFloatPrefs) == size_t(LookAndFeel::FloatID::End),
210 "Should have a pref for each float value");
212 // This array MUST be kept in the same order as the color list in
213 // specified/color.rs
214 static const char sColorPrefs[][41] = {
215 "ui.activeborder",
216 "ui.activecaption",
217 "ui.appworkspace",
218 "ui.background",
219 "ui.buttonface",
220 "ui.buttonhighlight",
221 "ui.buttonshadow",
222 "ui.buttontext",
223 "ui.buttonborder",
224 "ui.captiontext",
225 "ui.-moz-field",
226 "ui.-moz-disabledfield",
227 "ui.-moz-fieldtext",
228 "ui.mark",
229 "ui.marktext",
230 "ui.-moz-comboboxtext",
231 "ui.-moz-combobox",
232 "ui.graytext",
233 "ui.highlight",
234 "ui.highlighttext",
235 "ui.inactiveborder",
236 "ui.inactivecaption",
237 "ui.inactivecaptiontext",
238 "ui.infobackground",
239 "ui.infotext",
240 "ui.menu",
241 "ui.menutext",
242 "ui.scrollbar",
243 "ui.threeddarkshadow",
244 "ui.threedface",
245 "ui.threedhighlight",
246 "ui.threedlightshadow",
247 "ui.threedshadow",
248 "ui.window",
249 "ui.windowframe",
250 "ui.windowtext",
251 "ui.-moz-default-color",
252 "ui.-moz-default-background-color",
253 "ui.-moz-dialog",
254 "ui.-moz-dialogtext",
255 "ui.-moz-cellhighlight",
256 "ui.-moz_cellhighlighttext",
257 "ui.selecteditem",
258 "ui.selecteditemtext",
259 "ui.-moz-buttonhoverface",
260 "ui.-moz_buttonhovertext",
261 "ui.-moz_menuhover",
262 "ui.-moz_menuhoverdisabled",
263 "ui.-moz_menuhovertext",
264 "ui.-moz_menubarhovertext",
265 "ui.-moz_eventreerow",
266 "ui.-moz_oddtreerow",
267 "ui.-moz-buttonactivetext",
268 "ui.-moz-buttonactiveface",
269 "ui.-moz-buttondisabledface",
270 "ui.-moz-headerbar",
271 "ui.-moz-headerbartext",
272 "ui.-moz-headerbarinactive",
273 "ui.-moz-headerbarinactivetext",
274 "ui.-moz-mac-defaultbuttontext",
275 "ui.-moz-mac-focusring",
276 "ui.-moz_mac_disabledtoolbartext",
277 "ui.-moz-mac-menupopup",
278 "ui.-moz-mac-menuitem",
279 "ui.-moz-mac-active-menuitem",
280 "ui.-moz-mac-source-list",
281 "ui.-moz-mac-source-list-selection",
282 "ui.-moz-mac-active-source-list-selection",
283 "ui.-moz-mac-tooltip",
284 "ui.accentcolor",
285 "ui.accentcolortext",
286 "ui.-moz-autofill-background",
287 "ui.-moz-nativehyperlinktext",
288 "ui.-moz-nativevisitedhyperlinktext",
289 "ui.-moz-hyperlinktext",
290 "ui.-moz-activehyperlinktext",
291 "ui.-moz-visitedhyperlinktext",
292 "ui.-moz-colheadertext",
293 "ui.-moz-colheaderhovertext",
294 "ui.textSelectDisabledBackground",
295 "ui.textSelectAttentionBackground",
296 "ui.textSelectAttentionForeground",
297 "ui.textHighlightBackground",
298 "ui.textHighlightForeground",
299 "ui.IMERawInputBackground",
300 "ui.IMERawInputForeground",
301 "ui.IMERawInputUnderline",
302 "ui.IMESelectedRawTextBackground",
303 "ui.IMESelectedRawTextForeground",
304 "ui.IMESelectedRawTextUnderline",
305 "ui.IMEConvertedTextBackground",
306 "ui.IMEConvertedTextForeground",
307 "ui.IMEConvertedTextUnderline",
308 "ui.IMESelectedConvertedTextBackground",
309 "ui.IMESelectedConvertedTextForeground",
310 "ui.IMESelectedConvertedTextUnderline",
311 "ui.SpellCheckerUnderline",
312 "ui.themedScrollbar",
313 "ui.themedScrollbarInactive",
314 "ui.themedScrollbarThumb",
315 "ui.themedScrollbarThumbHover",
316 "ui.themedScrollbarThumbActive",
317 "ui.themedScrollbarThumbInactive",
320 static_assert(ArrayLength(sColorPrefs) == size_t(LookAndFeel::ColorID::End),
321 "Should have a pref for each color value");
323 // This array MUST be kept in the same order as the SystemFont enum.
324 static const char sFontPrefs[][41] = {
325 "ui.font.caption",
326 "ui.font.icon",
327 "ui.font.menu",
328 "ui.font.message-box",
329 "ui.font.small-caption",
330 "ui.font.status-bar",
331 "ui.font.-moz-pull-down-menu",
332 "ui.font.-moz-button",
333 "ui.font.-moz-list",
334 "ui.font.-moz-field",
337 static_assert(ArrayLength(sFontPrefs) == size_t(LookAndFeel::FontID::End),
338 "Should have a pref for each font value");
340 const char* nsXPLookAndFeel::GetColorPrefName(ColorID aId) {
341 return sColorPrefs[size_t(aId)];
344 bool nsXPLookAndFeel::sInitialized = false;
346 nsXPLookAndFeel* nsXPLookAndFeel::sInstance = nullptr;
347 bool nsXPLookAndFeel::sShutdown = false;
349 auto LookAndFeel::SystemZoomSettings() -> ZoomSettings {
350 ZoomSettings settings;
351 switch (StaticPrefs::browser_display_os_zoom_behavior()) {
352 case 0:
353 default:
354 break;
355 case 1:
356 settings.mFullZoom = GetTextScaleFactor();
357 break;
358 case 2:
359 settings.mTextZoom = GetTextScaleFactor();
360 break;
362 return settings;
365 // static
366 nsXPLookAndFeel* nsXPLookAndFeel::GetInstance() {
367 if (sInstance) {
368 return sInstance;
371 NS_ENSURE_TRUE(!sShutdown, nullptr);
373 // If we're in a content process, then the parent process will have supplied
374 // us with an initial FullLookAndFeel object.
375 // We grab this data from the ContentChild,
376 // where it's been temporarily stashed, and initialize our new LookAndFeel
377 // object with it.
379 FullLookAndFeel* lnf = nullptr;
381 if (auto* cc = mozilla::dom::ContentChild::GetSingleton()) {
382 lnf = &cc->BorrowLookAndFeelData();
385 if (lnf) {
386 sInstance = new widget::RemoteLookAndFeel(std::move(*lnf));
387 } else if (gfxPlatform::IsHeadless()) {
388 sInstance = new widget::HeadlessLookAndFeel();
389 } else {
390 sInstance = new nsLookAndFeel();
393 // This is only ever used once during initialization, and can be cleared now.
394 if (lnf) {
395 *lnf = {};
398 widget::Theme::Init();
399 return sInstance;
402 // static
403 void nsXPLookAndFeel::Shutdown() {
404 if (sShutdown) {
405 return;
408 sShutdown = true;
409 delete sInstance;
410 sInstance = nullptr;
412 // This keeps strings alive, so need to clear to make leak checking happy.
413 sFontCache.Clear();
415 widget::Theme::Shutdown();
418 static void IntPrefChanged(const nsACString& aPref) {
419 // Most Int prefs can't change our system colors or fonts, but
420 // ui.systemUsesDarkTheme can, since it affects the effective color-scheme
421 // (affecting system colors).
422 auto changeKind = aPref.EqualsLiteral("ui.systemUsesDarkTheme")
423 ? widget::ThemeChangeKind::Style
424 : widget::ThemeChangeKind::MediaQueriesOnly;
425 LookAndFeel::NotifyChangedAllWindows(changeKind);
428 static void FloatPrefChanged(const nsACString& aPref) {
429 // Most float prefs can't change our system colors or fonts, but
430 // textScaleFactor affects layout.
431 auto changeKind = aPref.EqualsLiteral("ui.textScaleFactor")
432 ? widget::ThemeChangeKind::StyleAndLayout
433 : widget::ThemeChangeKind::MediaQueriesOnly;
434 LookAndFeel::NotifyChangedAllWindows(changeKind);
437 static void ColorPrefChanged() {
438 // Color prefs affect style, because they by definition change system colors.
439 LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind::Style);
442 static void FontPrefChanged() {
443 // Color prefs affect style, because they by definition change system fonts.
444 LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind::Style);
447 // static
448 void nsXPLookAndFeel::OnPrefChanged(const char* aPref, void* aClosure) {
449 nsDependentCString prefName(aPref);
450 for (const char* pref : sIntPrefs) {
451 if (prefName.Equals(pref)) {
452 IntPrefChanged(prefName);
453 return;
457 for (const char* pref : sFloatPrefs) {
458 if (prefName.Equals(pref)) {
459 FloatPrefChanged(prefName);
460 return;
464 for (const char* pref : sColorPrefs) {
465 // We use StringBeginsWith to handle .dark prefs too.
466 if (StringBeginsWith(prefName, nsDependentCString(pref))) {
467 ColorPrefChanged();
468 return;
472 for (const char* pref : sFontPrefs) {
473 if (StringBeginsWith(prefName, nsDependentCString(pref))) {
474 FontPrefChanged();
475 return;
480 static constexpr struct {
481 nsLiteralCString mName;
482 widget::ThemeChangeKind mChangeKind =
483 widget::ThemeChangeKind::MediaQueriesOnly;
484 } kMediaQueryPrefs[] = {
485 // Affects whether standins are used for the accent color.
486 {"widget.non-native-theme.use-theme-accent"_ns,
487 widget::ThemeChangeKind::Style},
488 // These three affect system colors on Windows.
489 {"widget.windows.uwp-system-colors.enabled"_ns,
490 widget::ThemeChangeKind::Style},
491 {"widget.windows.uwp-system-colors.highlight-accent"_ns,
492 widget::ThemeChangeKind::Style},
493 // Affects env().
494 {"layout.css.prefers-color-scheme.content-override"_ns,
495 widget::ThemeChangeKind::Style},
496 // Affects media queries and scrollbar sizes, so gotta relayout.
497 {"widget.gtk.overlay-scrollbars.enabled"_ns,
498 widget::ThemeChangeKind::StyleAndLayout},
499 // Affects zoom settings which includes text and full zoom.
500 {"browser.display.os-zoom-behavior"_ns,
501 widget::ThemeChangeKind::StyleAndLayout},
502 // This affects not only the media query, but also the native theme, so we
503 // need to re-layout.
504 {"browser.theme.toolbar-theme"_ns, widget::ThemeChangeKind::AllBits},
505 {"browser.theme.content-theme"_ns},
508 // Read values from the user's preferences.
509 // This is done once at startup, but since the user's preferences
510 // haven't actually been read yet at that time, we also have to
511 // set a callback to inform us of changes to each pref.
512 void nsXPLookAndFeel::Init() {
513 MOZ_RELEASE_ASSERT(NS_IsMainThread());
515 // Say we're already initialized, and take the chance that it might fail;
516 // protects against some other process writing to our static variables.
517 sInitialized = true;
519 if (XRE_IsParentProcess()) {
520 nsLayoutUtils::RecomputeSmoothScrollDefault();
523 // XXX If we could reorganize the pref names, we should separate the branch
524 // for each types. Then, we could reduce the unnecessary loop from
525 // nsXPLookAndFeel::OnPrefChanged().
526 Preferences::RegisterPrefixCallback(OnPrefChanged, "ui.");
527 // We really do just want the accessibility.tabfocus pref, not other prefs
528 // that start with that string.
529 Preferences::RegisterCallback(OnPrefChanged, "accessibility.tabfocus");
531 for (const auto& pref : kMediaQueryPrefs) {
532 Preferences::RegisterCallback(
533 [](const char*, void* aChangeKind) {
534 auto changeKind =
535 widget::ThemeChangeKind(reinterpret_cast<uintptr_t>(aChangeKind));
536 LookAndFeel::NotifyChangedAllWindows(changeKind);
538 pref.mName, reinterpret_cast<void*>(uintptr_t(pref.mChangeKind)));
542 nsXPLookAndFeel::~nsXPLookAndFeel() {
543 NS_ASSERTION(sInstance == this,
544 "This destroying instance isn't the singleton instance");
545 sInstance = nullptr;
548 static bool IsSpecialColor(LookAndFeel::ColorID aID, nscolor aColor) {
549 using ColorID = LookAndFeel::ColorID;
551 if (aColor == NS_SAME_AS_FOREGROUND_COLOR) {
552 return true;
555 switch (aID) {
556 case ColorID::IMESelectedRawTextBackground:
557 case ColorID::IMESelectedConvertedTextBackground:
558 case ColorID::IMERawInputBackground:
559 case ColorID::IMEConvertedTextBackground:
560 case ColorID::IMESelectedRawTextForeground:
561 case ColorID::IMESelectedConvertedTextForeground:
562 case ColorID::IMERawInputForeground:
563 case ColorID::IMEConvertedTextForeground:
564 case ColorID::IMERawInputUnderline:
565 case ColorID::IMEConvertedTextUnderline:
566 case ColorID::IMESelectedRawTextUnderline:
567 case ColorID::IMESelectedConvertedTextUnderline:
568 case ColorID::SpellCheckerUnderline:
569 return NS_IS_SELECTION_SPECIAL_COLOR(aColor);
570 default:
571 break;
574 * In GetColor(), every color that is not a special color is color
575 * corrected. Use false to make other colors color corrected.
577 return false;
580 nscolor nsXPLookAndFeel::GetStandinForNativeColor(ColorID aID,
581 ColorScheme aScheme) {
582 if (aScheme == ColorScheme::Dark) {
583 if (auto color = GenericDarkColor(aID)) {
584 return *color;
588 // The stand-in colors are taken from what the non-native theme needs (for
589 // field/button colors), the Windows 7 Aero theme except Mac-specific colors
590 // which are taken from Mac OS 10.7.
592 #define COLOR(name_, r, g, b) \
593 case ColorID::name_: \
594 return NS_RGB(r, g, b);
596 #define COLORA(name_, r, g, b, a) \
597 case ColorID::name_: \
598 return NS_RGBA(r, g, b, a);
600 switch (aID) {
601 // These are here for the purposes of headless mode.
602 case ColorID::IMESelectedRawTextBackground:
603 case ColorID::IMESelectedConvertedTextBackground:
604 case ColorID::IMERawInputBackground:
605 case ColorID::IMEConvertedTextBackground:
606 return NS_TRANSPARENT;
607 case ColorID::IMESelectedRawTextForeground:
608 case ColorID::IMESelectedConvertedTextForeground:
609 case ColorID::IMERawInputForeground:
610 case ColorID::IMEConvertedTextForeground:
611 return NS_SAME_AS_FOREGROUND_COLOR;
612 case ColorID::IMERawInputUnderline:
613 case ColorID::IMEConvertedTextUnderline:
614 return NS_40PERCENT_FOREGROUND_COLOR;
615 case ColorID::Accentcolor:
616 return widget::sDefaultAccent.ToABGR();
617 case ColorID::Accentcolortext:
618 return widget::sDefaultAccentText.ToABGR();
619 COLOR(SpellCheckerUnderline, 0xff, 0x00, 0x00)
620 COLOR(TextSelectDisabledBackground, 0xaa, 0xaa, 0xaa)
622 // Titlebar colors
623 COLOR(Activeborder, 0xB4, 0xB4, 0xB4)
624 COLOR(Inactiveborder, 0xB4, 0xB4, 0xB4)
625 COLOR(Activecaption, 0xF0, 0xF0, 0xF4)
626 COLOR(Inactivecaption, 0xF0, 0xF0, 0xF4)
627 COLOR(Captiontext, 0x00, 0x00, 0x00)
628 COLOR(Inactivecaptiontext, 0x00, 0x00, 0x00)
630 // CSS 2 colors:
631 COLOR(Appworkspace, 0xAB, 0xAB, 0xAB)
632 COLOR(Background, 0x00, 0x00, 0x00)
633 COLOR(Buttonhighlight, 0xFF, 0xFF, 0xFF)
634 COLOR(Buttonshadow, 0xA0, 0xA0, 0xA0)
636 // Buttons and comboboxes should be kept in sync since they are drawn with
637 // the same colors by the non-native theme.
638 COLOR(Buttonface, 0xe9, 0xe9, 0xed)
639 COLORA(MozButtondisabledface, 0xe9, 0xe9, 0xed, 128)
641 COLOR(MozCombobox, 0xe9, 0xe9, 0xed)
643 COLOR(Buttontext, 0x00, 0x00, 0x00)
644 COLOR(MozComboboxtext, 0x00, 0x00, 0x00)
646 COLOR(Graytext, 0x6D, 0x6D, 0x6D)
647 COLOR(Highlight, 0x33, 0x99, 0xFF)
648 COLOR(Highlighttext, 0xFF, 0xFF, 0xFF)
649 COLOR(Infobackground, 0xFF, 0xFF, 0xE1)
650 COLOR(Infotext, 0x00, 0x00, 0x00)
651 COLOR(Menu, 0xF0, 0xF0, 0xF0)
652 COLOR(Menutext, 0x00, 0x00, 0x00)
653 COLOR(Scrollbar, 0xC8, 0xC8, 0xC8)
654 COLOR(Threeddarkshadow, 0x69, 0x69, 0x69)
655 COLOR(Threedface, 0xF0, 0xF0, 0xF0)
656 COLOR(Threedhighlight, 0xFF, 0xFF, 0xFF)
657 COLOR(Threedlightshadow, 0xE3, 0xE3, 0xE3)
658 COLOR(Threedshadow, 0xA0, 0xA0, 0xA0)
659 COLOR(Buttonborder, 0xE3, 0xE3, 0xE3)
660 COLOR(Mark, 0xFF, 0xFF, 0x00)
661 COLOR(Marktext, 0x00, 0x00, 0x00)
662 COLOR(Window, 0xFF, 0xFF, 0xFF)
663 COLOR(Windowframe, 0x64, 0x64, 0x64)
664 COLOR(Windowtext, 0x00, 0x00, 0x00)
665 COLOR(Field, 0xFF, 0xFF, 0xFF)
666 COLORA(MozDisabledfield, 0xFF, 0xFF, 0xFF, 128)
667 COLOR(Fieldtext, 0x00, 0x00, 0x00)
668 COLOR(MozDialog, 0xF0, 0xF0, 0xF0)
669 COLOR(MozDialogtext, 0x00, 0x00, 0x00)
670 COLOR(MozColheadertext, 0x00, 0x00, 0x00)
671 COLOR(MozColheaderhovertext, 0x00, 0x00, 0x00)
672 COLOR(MozCellhighlight, 0xF0, 0xF0, 0xF0)
673 COLOR(MozCellhighlighttext, 0x00, 0x00, 0x00)
674 COLOR(Selecteditem, 0x33, 0x99, 0xFF)
675 COLOR(Selecteditemtext, 0xFF, 0xFF, 0xFF)
676 COLOR(MozButtonhoverface, 0xd0, 0xd0, 0xd7)
677 COLOR(MozButtonhovertext, 0x00, 0x00, 0x00)
678 COLOR(MozButtonactiveface, 0xb1, 0xb1, 0xb9)
679 COLOR(MozButtonactivetext, 0x00, 0x00, 0x00)
680 COLOR(MozMenuhover, 0x33, 0x99, 0xFF)
681 COLOR(MozMenuhovertext, 0x00, 0x00, 0x00)
682 COLOR(MozMenubarhovertext, 0x00, 0x00, 0x00)
683 COLOR(MozMenuhoverdisabled, 0xF0, 0xF0, 0xF0)
684 COLOR(MozEventreerow, 0xFF, 0xFF, 0xFF)
685 COLOR(MozOddtreerow, 0xFF, 0xFF, 0xFF)
686 COLOR(MozMacFocusring, 0x60, 0x9D, 0xD7)
687 COLOR(MozMacDisabledtoolbartext, 0x3F, 0x3F, 0x3F)
688 COLOR(MozMacMenupopup, 0xe6, 0xe6, 0xe6)
689 COLOR(MozMacMenuitem, 0xe6, 0xe6, 0xe6)
690 COLOR(MozMacActiveMenuitem, 0x0a, 0x64, 0xdc)
691 COLOR(MozMacSourceList, 0xf7, 0xf7, 0xf7)
692 COLOR(MozMacSourceListSelection, 0xc8, 0xc8, 0xc8)
693 COLOR(MozMacActiveSourceListSelection, 0x0a, 0x64, 0xdc)
694 COLOR(MozMacTooltip, 0xf7, 0xf7, 0xf7)
695 // Seems to be the default color (hardcoded because of bug 1065998)
696 COLOR(MozNativehyperlinktext, 0x00, 0x66, 0xCC)
697 COLOR(MozNativevisitedhyperlinktext, 0x55, 0x1A, 0x8B)
698 default:
699 break;
701 return NS_RGB(0xFF, 0xFF, 0xFF);
704 #undef COLOR
705 #undef COLORA
707 // Taken from in-content/common.inc.css's dark theme.
708 Maybe<nscolor> nsXPLookAndFeel::GenericDarkColor(ColorID aID) {
709 nscolor color = NS_RGB(0, 0, 0);
710 static constexpr nscolor kWindowBackground = NS_RGB(28, 27, 34);
711 static constexpr nscolor kWindowText = NS_RGB(251, 251, 254);
712 switch (aID) {
713 case ColorID::Window: // --in-content-page-background
714 case ColorID::Background:
715 color = kWindowBackground;
716 break;
718 case ColorID::Menu:
719 color = NS_RGB(0x2b, 0x2a, 0x33);
720 break;
722 case ColorID::MozMenuhovertext:
723 case ColorID::MozMenubarhovertext:
724 case ColorID::Menutext:
725 color = NS_RGB(0xfb, 0xfb, 0xfe);
726 break;
728 case ColorID::MozMenuhover:
729 color = NS_RGB(0x52, 0x52, 0x5e);
730 break;
732 case ColorID::MozMenuhoverdisabled:
733 color = NS_RGB(0x3a, 0x39, 0x44);
734 break;
736 case ColorID::MozOddtreerow:
737 case ColorID::MozDialog: // --in-content-box-background
738 color = NS_RGB(35, 34, 43);
739 break;
740 case ColorID::Windowtext: // --in-content-page-color
741 case ColorID::MozDialogtext:
742 case ColorID::Fieldtext:
743 case ColorID::Buttontext: // --in-content-button-text-color (via
744 // --in-content-page-color)
745 case ColorID::MozComboboxtext:
746 case ColorID::MozButtonhovertext:
747 case ColorID::MozButtonactivetext:
748 case ColorID::MozHeaderbartext:
749 case ColorID::MozHeaderbarinactivetext:
750 case ColorID::Captiontext:
751 case ColorID::Inactivecaptiontext: // TODO(emilio): Maybe make
752 // Inactivecaptiontext Graytext?
753 color = kWindowText;
754 break;
755 case ColorID::Buttonshadow:
756 case ColorID::Threedshadow:
757 case ColorID::Threedlightshadow:
758 case ColorID::Buttonborder: // --in-content-box-border-color computed
759 // with kWindowText above
760 // kWindowBackground.
761 case ColorID::Graytext: // opacity: 0.4 of kWindowText blended over the
762 // "Window" background color, which happens to be
763 // the same :-)
764 color = NS_ComposeColors(kWindowBackground, NS_RGBA(251, 251, 254, 102));
765 break;
766 case ColorID::MozCellhighlight:
767 case ColorID::Selecteditem: // --in-content-primary-button-background /
768 // --in-content-item-selected
769 color = NS_RGB(0, 221, 255);
770 break;
771 case ColorID::Field:
772 case ColorID::Buttonface: // --in-content-button-background
773 case ColorID::Threedface:
774 case ColorID::MozCombobox:
775 case ColorID::MozCellhighlighttext:
776 case ColorID::Selecteditemtext: // --in-content-primary-button-text-color /
777 // --in-content-item-selected-text
778 color = NS_RGB(43, 42, 51);
779 break;
780 case ColorID::Threeddarkshadow: // Same as Threedlightshadow but with the
781 // background.
782 case ColorID::MozDisabledfield: // opacity: 0.4 of the face above blended
783 // over the "Window" background color.
784 case ColorID::MozButtondisabledface:
785 color = NS_ComposeColors(kWindowBackground, NS_RGBA(43, 42, 51, 102));
786 break;
787 case ColorID::MozButtonhoverface: // --in-content-button-background-hover
788 color = NS_RGB(82, 82, 94);
789 break;
790 case ColorID::MozButtonactiveface: // --in-content-button-background-active
791 color = NS_RGB(91, 91, 102);
792 break;
793 case ColorID::Highlight:
794 color = NS_RGBA(0, 221, 255, 78);
795 break;
796 case ColorID::Highlighttext:
797 color = NS_SAME_AS_FOREGROUND_COLOR;
798 break;
799 case ColorID::MozNativehyperlinktext:
800 // If you change this color, you probably also want to change the default
801 // value of browser.anchor_color.dark.
802 color = NS_RGB(0x8c, 0x8c, 0xff);
803 break;
804 case ColorID::MozNativevisitedhyperlinktext:
805 // If you change this color, you probably also want to change the default
806 // value of browser.visited_color.dark.
807 color = NS_RGB(0xff, 0xad, 0xff);
808 break;
809 case ColorID::SpellCheckerUnderline:
810 // This is the default for active links in dark mode as well
811 // (browser.active_color.dark). See bug 1755564 for some analysis and
812 // other options too.
813 color = NS_RGB(0xff, 0x66, 0x66);
814 break;
815 case ColorID::Activeborder:
816 case ColorID::Inactiveborder:
817 color = NS_RGB(57, 57, 57);
818 break;
819 case ColorID::MozHeaderbar:
820 case ColorID::MozHeaderbarinactive:
821 case ColorID::Activecaption:
822 case ColorID::Inactivecaption:
823 color = NS_RGB(28, 27, 34);
824 break;
825 default:
826 return Nothing();
828 return Some(color);
831 // Uncomment the #define below if you want to debug system color use in a skin
832 // that uses them. When set, it will make all system color pairs that are
833 // appropriate for foreground/background pairing the same. This means if the
834 // skin is using system colors correctly you will not be able to see *any* text.
836 // #define DEBUG_SYSTEM_COLOR_USE
838 #ifdef DEBUG_SYSTEM_COLOR_USE
839 static nsresult SystemColorUseDebuggingColor(LookAndFeel::ColorID aID,
840 nscolor& aResult) {
841 using ColorID = LookAndFeel::ColorID;
843 switch (aID) {
844 // css2 http://www.w3.org/TR/REC-CSS2/ui.html#system-colors
845 case ColorID::Activecaption:
846 // active window caption background
847 case ColorID::Captiontext:
848 // text in active window caption
849 aResult = NS_RGB(0xff, 0x00, 0x00);
850 break;
852 case ColorID::Highlight:
853 // background of selected item
854 case ColorID::Highlighttext:
855 // text of selected item
856 aResult = NS_RGB(0xff, 0xff, 0x00);
857 break;
859 case ColorID::Inactivecaption:
860 // inactive window caption
861 case ColorID::Inactivecaptiontext:
862 // text in inactive window caption
863 aResult = NS_RGB(0x66, 0x66, 0x00);
864 break;
866 case ColorID::Infobackground:
867 // tooltip background color
868 case ColorID::Infotext:
869 // tooltip text color
870 aResult = NS_RGB(0x00, 0xff, 0x00);
871 break;
873 case ColorID::Menu:
874 // menu background
875 case ColorID::Menutext:
876 // menu text
877 aResult = NS_RGB(0x00, 0xff, 0xff);
878 break;
880 case ColorID::Threedface:
881 case ColorID::Buttonface:
882 // 3-D face color
883 case ColorID::Buttontext:
884 // text on push buttons
885 aResult = NS_RGB(0x00, 0x66, 0x66);
886 break;
888 case ColorID::Window:
889 case ColorID::Windowtext:
890 aResult = NS_RGB(0x00, 0x00, 0xff);
891 break;
893 // from the CSS3 working draft (not yet finalized)
894 // http://www.w3.org/tr/2000/wd-css3-userint-20000216.html#color
896 case ColorID::Field:
897 case ColorID::Fieldtext:
898 aResult = NS_RGB(0xff, 0x00, 0xff);
899 break;
901 case ColorID::MozDialog:
902 case ColorID::MozDialogtext:
903 aResult = NS_RGB(0x66, 0x00, 0x66);
904 break;
906 default:
907 return NS_ERROR_NOT_AVAILABLE;
910 return NS_OK;
912 #endif
914 static nsresult GetPrefColor(const char* aPref, nscolor& aResult) {
915 nsAutoCString colorStr;
916 MOZ_TRY(Preferences::GetCString(aPref, colorStr));
917 if (!ServoCSSParser::ComputeColor(nullptr, NS_RGB(0, 0, 0), colorStr,
918 &aResult)) {
919 return NS_ERROR_FAILURE;
921 return NS_OK;
924 static nsresult GetColorFromPref(LookAndFeel::ColorID aID, ColorScheme aScheme,
925 nscolor& aResult) {
926 const char* prefName = sColorPrefs[size_t(aID)];
927 if (aScheme == ColorScheme::Dark) {
928 nsAutoCString darkPrefName(prefName);
929 darkPrefName.Append(".dark");
930 if (NS_SUCCEEDED(GetPrefColor(darkPrefName.get(), aResult))) {
931 return NS_OK;
934 return GetPrefColor(prefName, aResult);
937 // All these routines will return NS_OK if they have a value,
938 // in which case the nsLookAndFeel should use that value;
939 // otherwise we'll return NS_ERROR_NOT_AVAILABLE, in which case, the
940 // platform-specific nsLookAndFeel should use its own values instead.
941 nsresult nsXPLookAndFeel::GetColorValue(ColorID aID, ColorScheme aScheme,
942 UseStandins aUseStandins,
943 nscolor& aResult) {
944 if (!sInitialized) {
945 Init();
948 #ifdef DEBUG_SYSTEM_COLOR_USE
949 if (NS_SUCCEEDED(SystemColorUseDebuggingColor(aID, aResult))) {
950 return NS_OK;
952 #endif
954 auto& cache = sColorCaches.Get(aScheme, aUseStandins);
955 if (const auto* cached = cache.Get(aID)) {
956 if (cached->isNothing()) {
957 return NS_ERROR_FAILURE;
959 aResult = cached->value();
960 return NS_OK;
963 // NOTE: Servo holds a lock and the main thread is paused, so writing to the
964 // global cache here is fine.
965 auto result = GetUncachedColor(aID, aScheme, aUseStandins);
966 cache.Insert(aID, result);
967 if (!result) {
968 return NS_ERROR_FAILURE;
970 aResult = *result;
971 return NS_OK;
974 Maybe<nscolor> nsXPLookAndFeel::GetUncachedColor(ColorID aID,
975 ColorScheme aScheme,
976 UseStandins aUseStandins) {
977 if (aUseStandins == UseStandins::Yes) {
978 return Some(GetStandinForNativeColor(aID, aScheme));
980 nscolor r;
981 if (NS_SUCCEEDED(GetColorFromPref(aID, aScheme, r))) {
982 return Some(r);
984 if (NS_SUCCEEDED(NativeGetColor(aID, aScheme, r))) {
985 if (gfxPlatform::GetCMSMode() == CMSMode::All && !IsSpecialColor(aID, r)) {
986 qcms_transform* transform = gfxPlatform::GetCMSInverseRGBTransform();
987 if (transform) {
988 uint8_t color[4];
989 color[0] = NS_GET_R(r);
990 color[1] = NS_GET_G(r);
991 color[2] = NS_GET_B(r);
992 color[3] = NS_GET_A(r);
993 qcms_transform_data(transform, color, color, 1);
994 r = NS_RGBA(color[0], color[1], color[2], color[3]);
998 return Some(r);
1000 return Nothing();
1003 nsresult nsXPLookAndFeel::GetIntValue(IntID aID, int32_t& aResult) {
1004 if (!sInitialized) {
1005 Init();
1008 if (const auto* cached = sIntCache.Get(aID)) {
1009 if (cached->isNothing()) {
1010 return NS_ERROR_FAILURE;
1012 aResult = cached->value();
1013 return NS_OK;
1016 if (NS_SUCCEEDED(Preferences::GetInt(sIntPrefs[size_t(aID)], &aResult))) {
1017 sIntCache.Insert(aID, Some(aResult));
1018 return NS_OK;
1021 if (NS_FAILED(NativeGetInt(aID, aResult))) {
1022 sIntCache.Insert(aID, Nothing());
1023 return NS_ERROR_FAILURE;
1026 sIntCache.Insert(aID, Some(aResult));
1027 return NS_OK;
1030 nsresult nsXPLookAndFeel::GetFloatValue(FloatID aID, float& aResult) {
1031 if (!sInitialized) {
1032 Init();
1035 if (const auto* cached = sFloatCache.Get(aID)) {
1036 if (cached->isNothing()) {
1037 return NS_ERROR_FAILURE;
1039 aResult = cached->value();
1040 return NS_OK;
1043 int32_t pref = 0;
1044 if (NS_SUCCEEDED(Preferences::GetInt(sFloatPrefs[size_t(aID)], &pref))) {
1045 aResult = float(pref) / 100.0f;
1046 sFloatCache.Insert(aID, Some(aResult));
1047 return NS_OK;
1050 if (NS_FAILED(NativeGetFloat(aID, aResult))) {
1051 sFloatCache.Insert(aID, Nothing());
1052 return NS_ERROR_FAILURE;
1055 sFloatCache.Insert(aID, Some(aResult));
1056 return NS_OK;
1059 bool nsXPLookAndFeel::LookAndFeelFontToStyle(const LookAndFeelFont& aFont,
1060 nsString& aName,
1061 gfxFontStyle& aStyle) {
1062 if (!aFont.haveFont()) {
1063 return false;
1065 aName = aFont.name();
1066 aStyle = gfxFontStyle();
1067 aStyle.size = aFont.size();
1068 aStyle.weight = FontWeight::FromInt(aFont.weight());
1069 aStyle.style =
1070 aFont.italic() ? FontSlantStyle::ITALIC : FontSlantStyle::NORMAL;
1071 aStyle.systemFont = true;
1072 return true;
1075 widget::LookAndFeelFont nsXPLookAndFeel::StyleToLookAndFeelFont(
1076 const nsAString& aName, const gfxFontStyle& aStyle) {
1077 LookAndFeelFont font;
1078 font.haveFont() = true;
1079 font.name() = aName;
1080 font.size() = aStyle.size;
1081 font.weight() = aStyle.weight.ToFloat();
1082 font.italic() = aStyle.style.IsItalic();
1083 MOZ_ASSERT(aStyle.style.IsNormal() || aStyle.style.IsItalic(),
1084 "Cannot handle oblique font style");
1085 #ifdef DEBUG
1087 // Assert that all the remaining font style properties have their
1088 // default values.
1089 gfxFontStyle candidate = aStyle;
1090 gfxFontStyle defaults{};
1091 candidate.size = defaults.size;
1092 candidate.weight = defaults.weight;
1093 candidate.style = defaults.style;
1094 MOZ_ASSERT(candidate.Equals(defaults),
1095 "Some font style properties not supported");
1097 #endif
1098 return font;
1101 bool nsXPLookAndFeel::GetFontValue(FontID aID, nsString& aName,
1102 gfxFontStyle& aStyle) {
1103 if (const LookAndFeelFont* cached = sFontCache.Get(aID)) {
1104 return LookAndFeelFontToStyle(*cached, aName, aStyle);
1107 LookAndFeelFont font;
1108 auto GetFontsFromPrefs = [&]() -> bool {
1109 nsDependentCString pref(sFontPrefs[size_t(aID)]);
1110 if (NS_FAILED(Preferences::GetString(pref.get(), aName))) {
1111 return false;
1113 font.haveFont() = true;
1114 font.name() = aName;
1115 font.size() = Preferences::GetFloat(nsAutoCString(pref + ".size"_ns).get());
1116 // This is written this way rather than using the fallback so that an empty
1117 // pref (such like the one about:config creates) doesn't cause system fonts
1118 // to have zero-size.
1119 if (font.size() < 1.0f) {
1120 font.size() = StyleFONT_MEDIUM_PX;
1122 font.weight() = Preferences::GetFloat(
1123 nsAutoCString(pref + ".weight"_ns).get(), FontWeight::NORMAL.ToFloat());
1124 font.italic() =
1125 Preferences::GetBool(nsAutoCString(pref + ".italic"_ns).get());
1126 return true;
1129 if (GetFontsFromPrefs()) {
1130 LookAndFeelFontToStyle(font, aName, aStyle);
1131 } else if (NativeGetFont(aID, aName, aStyle)) {
1132 font = StyleToLookAndFeelFont(aName, aStyle);
1133 } else {
1134 MOZ_ASSERT(!font.haveFont());
1136 bool success = font.haveFont();
1137 sFontCache.Insert(aID, std::move(font));
1138 return success;
1141 void nsXPLookAndFeel::RefreshImpl() {
1142 // Wipe out our caches.
1143 sColorCaches.Clear();
1144 sFontCache.Clear();
1145 sFloatCache.Clear();
1146 sIntCache.Clear();
1148 if (XRE_IsParentProcess()) {
1149 nsLayoutUtils::RecomputeSmoothScrollDefault();
1150 // Clear any cached FullLookAndFeel data, which is now invalid.
1151 widget::RemoteLookAndFeel::ClearCachedData();
1155 static bool sRecordedLookAndFeelTelemetry = false;
1157 void nsXPLookAndFeel::RecordTelemetry() {
1158 if (!XRE_IsParentProcess()) {
1159 return;
1162 if (sRecordedLookAndFeelTelemetry) {
1163 return;
1166 sRecordedLookAndFeelTelemetry = true;
1168 int32_t i;
1169 Telemetry::ScalarSet(
1170 Telemetry::ScalarID::WIDGET_DARK_MODE,
1171 NS_SUCCEEDED(GetIntValue(IntID::SystemUsesDarkTheme, i)) && i != 0);
1173 RecordLookAndFeelSpecificTelemetry();
1176 namespace mozilla {
1178 bool LookAndFeel::sGlobalThemeChanged;
1179 static widget::ThemeChangeKind sGlobalThemeChangeKind{0};
1181 void LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind aKind) {
1182 sGlobalThemeChanged = true;
1183 sGlobalThemeChangeKind |= aKind;
1185 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
1186 const char16_t kind[] = {char16_t(aKind), 0};
1187 obs->NotifyObservers(nullptr, "internal-look-and-feel-changed", kind);
1191 void LookAndFeel::DoHandleGlobalThemeChange() {
1192 MOZ_ASSERT(sGlobalThemeChanged);
1193 sGlobalThemeChanged = false;
1194 auto kind = std::exchange(sGlobalThemeChangeKind, widget::ThemeChangeKind(0));
1196 // Tell the theme that it changed, so it can flush any handles to stale theme
1197 // data.
1199 // We can use the *DoNotUseDirectly functions directly here, because we want
1200 // to notify all possible themes in a given process (but just once).
1201 if (XRE_IsParentProcess() ||
1202 !StaticPrefs::widget_non_native_theme_enabled()) {
1203 if (nsCOMPtr<nsITheme> theme = do_GetNativeThemeDoNotUseDirectly()) {
1204 theme->ThemeChanged();
1207 if (nsCOMPtr<nsITheme> theme = do_GetBasicNativeThemeDoNotUseDirectly()) {
1208 theme->ThemeChanged();
1211 // Clear all cached LookAndFeel colors.
1212 LookAndFeel::Refresh();
1214 // Reset default background and foreground colors for the document since they
1215 // may be using system colors, color scheme, etc.
1216 PreferenceSheet::Refresh();
1218 // Vector images (SVG) may be using theme colors so we discard all cached
1219 // surfaces. (We could add a vector image only version of DiscardAll, but
1220 // in bug 940625 we decided theme changes are rare enough not to bother.)
1221 image::SurfaceCacheUtils::DiscardAll();
1223 if (XRE_IsParentProcess()) {
1224 dom::ContentParent::BroadcastThemeUpdate(kind);
1227 nsContentUtils::AddScriptRunner(
1228 NS_NewRunnableFunction("HandleGlobalThemeChange", [] {
1229 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
1230 obs->NotifyObservers(nullptr, "look-and-feel-changed", nullptr);
1232 }));
1235 #define BIT_FOR(_c) (1ull << size_t(ColorID::_c))
1237 // We want to use a non-native color scheme for the non-native theme (except in
1238 // high-contrast mode), so spoof some of the colors with stand-ins to prevent
1239 // lack of contrast.
1240 static constexpr std::bitset<size_t(ColorID::End)> sNonNativeThemeStandinColors{
1241 // Used by default button styles.
1242 BIT_FOR(Buttonface) | BIT_FOR(Buttontext) | BIT_FOR(MozButtonhoverface) |
1243 BIT_FOR(MozButtonhovertext) | BIT_FOR(MozButtonactiveface) |
1244 BIT_FOR(MozButtonactivetext) | BIT_FOR(MozButtondisabledface) |
1245 BIT_FOR(Buttonborder) |
1246 // Used by select elements.
1247 BIT_FOR(MozCombobox) | BIT_FOR(MozComboboxtext) |
1248 BIT_FOR(Threedlightshadow) |
1249 // For symmetry with the above.
1250 BIT_FOR(Threeddarkshadow) |
1251 // Used by fieldset borders.
1252 BIT_FOR(Threedface) |
1253 // Used by input / textarea.
1254 BIT_FOR(Field) | BIT_FOR(Fieldtext) |
1255 // Used by disabled form controls.
1256 BIT_FOR(MozDisabledfield) | BIT_FOR(Graytext) |
1257 // Some pages expect these to return windows-like colors, see bug 1773795.
1258 // Also, per spec these should match Canvas/CanvasText, see
1259 // https://drafts.csswg.org/css-color-4/#window
1260 BIT_FOR(Window) | BIT_FOR(Windowtext)};
1261 #undef BIT_FOR
1263 static bool ShouldUseStandinsForNativeColorForNonNativeTheme(
1264 const dom::Document& aDoc, LookAndFeel::ColorID aColor,
1265 const PreferenceSheet::Prefs& aPrefs) {
1266 const bool shouldUseStandinsForColor = [&] {
1267 if (sNonNativeThemeStandinColors[size_t(aColor)]) {
1268 return true;
1270 // There are platforms where we want the content-exposed accent color to be
1271 // the windows blue rather than the system accent color, for now.
1272 return !StaticPrefs::widget_non_native_theme_use_theme_accent() &&
1273 (aColor == LookAndFeel::ColorID::Accentcolor ||
1274 aColor == LookAndFeel::ColorID::Accentcolortext);
1275 }();
1277 return shouldUseStandinsForColor && aDoc.ShouldAvoidNativeTheme() &&
1278 !aPrefs.NonNativeThemeShouldBeHighContrast();
1281 bool LookAndFeel::IsDarkColor(nscolor aColor) {
1282 // Given https://www.w3.org/TR/WCAG20/#contrast-ratiodef, this is the
1283 // threshold that tells us whether contrast is better against white or black.
1285 // Contrast ratio against black is: (L + 0.05) / 0.05
1286 // Contrast ratio against white is: 1.05 / (L + 0.05)
1288 // So the intersection is:
1290 // (L + 0.05) / 0.05 = 1.05 / (L + 0.05)
1292 // And the solution to that equation is:
1294 // sqrt(1.05 * 0.05) - 0.05
1296 // So we consider a color dark if the contrast is below this threshold, and
1297 // it's at least half-opaque.
1298 constexpr float kThreshold = 0.179129;
1299 return NS_GET_A(aColor) > 127 &&
1300 RelativeLuminanceUtils::Compute(aColor) < kThreshold;
1303 ColorScheme LookAndFeel::ColorSchemeForStyle(
1304 const dom::Document& aDoc, const StyleColorSchemeFlags& aFlags,
1305 ColorSchemeMode aMode) {
1306 const auto& prefs = PreferenceSheet::PrefsFor(aDoc);
1307 StyleColorSchemeFlags style(aFlags);
1308 if (!style) {
1309 style._0 = aDoc.GetColorSchemeBits();
1311 const bool supportsDark = bool(style & StyleColorSchemeFlags::DARK);
1312 const bool supportsLight = bool(style & StyleColorSchemeFlags::LIGHT);
1313 if (supportsLight && supportsDark) {
1314 // Both color-schemes are explicitly supported, use the preferred one.
1315 return aDoc.PreferredColorScheme();
1317 if (supportsDark || supportsLight) {
1318 // One color-scheme is explicitly supported and one isn't, so use the one
1319 // the content supports.
1320 return supportsDark ? ColorScheme::Dark : ColorScheme::Light;
1322 // No value specified. Chrome docs, and forced-colors mode always supports
1323 // both, so use the preferred color-scheme.
1324 if (aMode == ColorSchemeMode::Preferred || aDoc.ChromeRulesEnabled() ||
1325 !prefs.mUseDocumentColors) {
1326 return aDoc.PreferredColorScheme();
1328 // Otherwise default content to light.
1329 return ColorScheme::Light;
1332 LookAndFeel::ColorScheme LookAndFeel::ColorSchemeForFrame(
1333 const nsIFrame* aFrame, ColorSchemeMode aMode) {
1334 return ColorSchemeForStyle(*aFrame->PresContext()->Document(),
1335 aFrame->StyleUI()->mColorScheme.bits, aMode);
1338 // static
1339 Maybe<nscolor> LookAndFeel::GetColor(ColorID aId, ColorScheme aScheme,
1340 UseStandins aUseStandins) {
1341 nscolor result;
1342 nsresult rv = nsLookAndFeel::GetInstance()->GetColorValue(
1343 aId, aScheme, aUseStandins, result);
1344 if (NS_FAILED(rv)) {
1345 return Nothing();
1347 return Some(result);
1350 // Returns whether there is a CSS color name for this color.
1351 static bool ColorIsCSSAccessible(LookAndFeel::ColorID aId) {
1352 using ColorID = LookAndFeel::ColorID;
1354 switch (aId) {
1355 case ColorID::TextSelectDisabledBackground:
1356 case ColorID::TextSelectAttentionBackground:
1357 case ColorID::TextSelectAttentionForeground:
1358 case ColorID::TextHighlightBackground:
1359 case ColorID::TextHighlightForeground:
1360 case ColorID::ThemedScrollbar:
1361 case ColorID::ThemedScrollbarInactive:
1362 case ColorID::ThemedScrollbarThumb:
1363 case ColorID::ThemedScrollbarThumbActive:
1364 case ColorID::ThemedScrollbarThumbInactive:
1365 case ColorID::ThemedScrollbarThumbHover:
1366 case ColorID::IMERawInputBackground:
1367 case ColorID::IMERawInputForeground:
1368 case ColorID::IMERawInputUnderline:
1369 case ColorID::IMESelectedRawTextBackground:
1370 case ColorID::IMESelectedRawTextForeground:
1371 case ColorID::IMESelectedRawTextUnderline:
1372 case ColorID::IMEConvertedTextBackground:
1373 case ColorID::IMEConvertedTextForeground:
1374 case ColorID::IMEConvertedTextUnderline:
1375 case ColorID::IMESelectedConvertedTextBackground:
1376 case ColorID::IMESelectedConvertedTextForeground:
1377 case ColorID::IMESelectedConvertedTextUnderline:
1378 case ColorID::SpellCheckerUnderline:
1379 return false;
1380 default:
1381 break;
1384 return true;
1387 LookAndFeel::UseStandins LookAndFeel::ShouldUseStandins(
1388 const dom::Document& aDoc, ColorID aId) {
1389 const auto& prefs = PreferenceSheet::PrefsFor(aDoc);
1390 if (ShouldUseStandinsForNativeColorForNonNativeTheme(aDoc, aId, prefs)) {
1391 return UseStandins::Yes;
1393 if (prefs.mUseStandins && ColorIsCSSAccessible(aId)) {
1394 return UseStandins::Yes;
1396 return UseStandins::No;
1399 Maybe<nscolor> LookAndFeel::GetColor(ColorID aId, const nsIFrame* aFrame) {
1400 const auto* doc = aFrame->PresContext()->Document();
1401 return GetColor(aId, ColorSchemeForFrame(aFrame),
1402 ShouldUseStandins(*doc, aId));
1405 // static
1406 nsresult LookAndFeel::GetInt(IntID aID, int32_t* aResult) {
1407 return nsLookAndFeel::GetInstance()->GetIntValue(aID, *aResult);
1410 // static
1411 nsresult LookAndFeel::GetFloat(FloatID aID, float* aResult) {
1412 return nsLookAndFeel::GetInstance()->GetFloatValue(aID, *aResult);
1415 // static
1416 bool LookAndFeel::GetFont(FontID aID, nsString& aName, gfxFontStyle& aStyle) {
1417 return nsLookAndFeel::GetInstance()->GetFontValue(aID, aName, aStyle);
1420 // static
1421 char16_t LookAndFeel::GetPasswordCharacter() {
1422 return nsLookAndFeel::GetInstance()->GetPasswordCharacterImpl();
1425 // static
1426 bool LookAndFeel::GetEchoPassword() {
1427 if (StaticPrefs::editor_password_mask_delay() >= 0) {
1428 return StaticPrefs::editor_password_mask_delay() > 0;
1430 return nsLookAndFeel::GetInstance()->GetEchoPasswordImpl();
1433 // static
1434 uint32_t LookAndFeel::GetPasswordMaskDelay() {
1435 int32_t delay = StaticPrefs::editor_password_mask_delay();
1436 if (delay < 0) {
1437 return nsLookAndFeel::GetInstance()->GetPasswordMaskDelayImpl();
1439 return delay;
1442 bool LookAndFeel::DrawInTitlebar() {
1443 switch (StaticPrefs::browser_tabs_inTitlebar()) {
1444 case 0:
1445 return false;
1446 case 1:
1447 return true;
1448 default:
1449 break;
1451 return nsLookAndFeel::GetInstance()->GetDefaultDrawInTitlebar();
1454 void LookAndFeel::GetThemeInfo(nsACString& aOut) {
1455 nsLookAndFeel::GetInstance()->GetThemeInfo(aOut);
1458 uint32_t LookAndFeel::GetMenuAccessKey() {
1459 return StaticPrefs::ui_key_menuAccessKey();
1462 Modifiers LookAndFeel::GetMenuAccessKeyModifiers() {
1463 switch (GetMenuAccessKey()) {
1464 case dom::KeyboardEvent_Binding::DOM_VK_SHIFT:
1465 return MODIFIER_SHIFT;
1466 case dom::KeyboardEvent_Binding::DOM_VK_CONTROL:
1467 return MODIFIER_CONTROL;
1468 case dom::KeyboardEvent_Binding::DOM_VK_ALT:
1469 return MODIFIER_ALT;
1470 case dom::KeyboardEvent_Binding::DOM_VK_META:
1471 case dom::KeyboardEvent_Binding::DOM_VK_WIN:
1472 return MODIFIER_META;
1473 default:
1474 return 0;
1478 // static
1479 void LookAndFeel::Refresh() {
1480 nsLookAndFeel::GetInstance()->RefreshImpl();
1481 widget::Theme::LookAndFeelChanged();
1484 // static
1485 void LookAndFeel::NativeInit() { nsLookAndFeel::GetInstance()->NativeInit(); }
1487 // static
1488 void LookAndFeel::SetData(widget::FullLookAndFeel&& aTables) {
1489 nsLookAndFeel::GetInstance()->SetDataImpl(std::move(aTables));
1492 } // namespace mozilla