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