Merge mozilla-central to autoland. CLOSED TREE
[gecko.git] / widget / nsXPLookAndFeel.cpp
blobb00f82fe9391ea52fca1a5cc407a6b51011ec5a4
1 /* -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "mozilla/ArrayUtils.h"
8 #include "mozilla/LookAndFeel.h"
9 #include "nscore.h"
11 #include "nsXPLookAndFeel.h"
12 #include "nsLookAndFeel.h"
13 #include "HeadlessLookAndFeel.h"
14 #include "RemoteLookAndFeel.h"
15 #include "nsContentUtils.h"
16 #include "nsCRT.h"
17 #include "nsFont.h"
18 #include "nsIFrame.h"
19 #include "nsIXULRuntime.h"
20 #include "nsLayoutUtils.h"
21 #include "Theme.h"
22 #include "SurfaceCacheUtils.h"
23 #include "mozilla/dom/ContentParent.h"
24 #include "mozilla/dom/ContentChild.h"
25 #include "mozilla/Preferences.h"
26 #include "mozilla/Services.h"
27 #include "mozilla/ServoStyleSet.h"
28 #include "mozilla/ServoCSSParser.h"
29 #include "mozilla/StaticPrefs_browser.h"
30 #include "mozilla/StaticPrefs_editor.h"
31 #include "mozilla/StaticPrefs_layout.h"
32 #include "mozilla/StaticPrefs_ui.h"
33 #include "mozilla/StaticPrefs_widget.h"
34 #include "mozilla/dom/Document.h"
35 #include "mozilla/PreferenceSheet.h"
36 #include "mozilla/gfx/2D.h"
37 #include "mozilla/widget/WidgetMessageUtils.h"
38 #include "mozilla/dom/KeyboardEventBinding.h"
39 #include "mozilla/RelativeLuminanceUtils.h"
40 #include "mozilla/Telemetry.h"
41 #include "mozilla/TelemetryScalarEnums.h"
42 #include "mozilla/Try.h"
44 #include "gfxPlatform.h"
45 #include "gfxFont.h"
47 #include "qcms.h"
49 #include <bitset>
51 using namespace mozilla;
53 using IntID = mozilla::LookAndFeel::IntID;
54 using FloatID = mozilla::LookAndFeel::FloatID;
55 using ColorID = mozilla::LookAndFeel::ColorID;
56 using FontID = mozilla::LookAndFeel::FontID;
58 template <typename Index, typename Value, Index kEnd>
59 class EnumeratedCache {
60 mozilla::EnumeratedArray<Index, Value, size_t(kEnd)> mEntries;
61 std::bitset<size_t(kEnd)> mValidity;
63 public:
64 constexpr EnumeratedCache() = default;
66 bool IsValid(Index aIndex) const { return mValidity[size_t(aIndex)]; }
68 const Value* Get(Index aIndex) const {
69 return IsValid(aIndex) ? &mEntries[aIndex] : nullptr;
72 void Insert(Index aIndex, Value aValue) {
73 mValidity[size_t(aIndex)] = true;
74 mEntries[aIndex] = aValue;
77 void Remove(Index aIndex) {
78 mValidity[size_t(aIndex)] = false;
79 mEntries[aIndex] = Value();
82 void Clear() {
83 mValidity.reset();
84 for (auto& entry : mEntries) {
85 entry = Value();
90 using ColorCache = EnumeratedCache<ColorID, Maybe<nscolor>, ColorID::End>;
92 struct ColorCaches {
93 using UseStandins = LookAndFeel::UseStandins;
95 ColorCache mCaches[2][2];
97 constexpr ColorCaches() = default;
99 ColorCache& Get(ColorScheme aScheme, UseStandins aUseStandins) {
100 return mCaches[aScheme == ColorScheme::Dark]
101 [aUseStandins == UseStandins::Yes];
104 void Clear() {
105 for (auto& c : mCaches) {
106 for (auto& cache : c) {
107 cache.Clear();
113 static ColorCaches sColorCaches;
115 static EnumeratedCache<FloatID, Maybe<float>, FloatID::End> sFloatCache;
116 static EnumeratedCache<IntID, Maybe<int32_t>, IntID::End> sIntCache;
117 static EnumeratedCache<FontID, widget::LookAndFeelFont, FontID::End> sFontCache;
119 // To make one of these prefs toggleable from a reftest add a user
120 // pref in testing/profiles/reftest/user.js. For example, to make
121 // ui.useAccessibilityTheme toggleable, add:
123 // user_pref("ui.useAccessibilityTheme", 0);
125 // This needs to be of the same length and in the same order as
126 // LookAndFeel::IntID values.
127 static const char sIntPrefs[][45] = {
128 "ui.caretBlinkTime",
129 "ui.caretBlinkCount",
130 "ui.caretWidth",
131 "ui.selectTextfieldsOnKeyFocus",
132 "ui.submenuDelay",
133 "ui.menusCanOverlapOSBar",
134 "ui.useOverlayScrollbars",
135 "ui.allowOverlayScrollbarsOverlap",
136 "ui.skipNavigatingDisabledMenuItem",
137 "ui.dragThresholdX",
138 "ui.dragThresholdY",
139 "ui.useAccessibilityTheme",
140 "ui.scrollArrowStyle",
141 "ui.scrollButtonLeftMouseButtonAction",
142 "ui.scrollButtonMiddleMouseButtonAction",
143 "ui.scrollButtonRightMouseButtonAction",
144 "ui.treeOpenDelay",
145 "ui.treeCloseDelay",
146 "ui.treeLazyScrollDelay",
147 "ui.treeScrollDelay",
148 "ui.treeScrollLinesMax",
149 "accessibility.tabfocus", // Weird one...
150 "ui.chosenMenuItemsShouldBlink",
151 "ui.windowsAccentColorInTitlebar",
152 "ui.macBigSurTheme",
153 "ui.macRTL",
154 "ui.macTitlebarHeight",
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.swipeAnimationEnabled",
165 "ui.scrollbarDisplayOnMouseMove",
166 "ui.scrollbarFadeBeginDelay",
167 "ui.scrollbarFadeDuration",
168 "ui.contextMenuOffsetVertical",
169 "ui.contextMenuOffsetHorizontal",
170 "ui.tooltipOffsetVertical",
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.titlebarButtonSpacing",
189 "ui.dynamicRange",
190 "ui.panelAnimations",
191 "ui.hideCursorWhileTyping",
192 "ui.gtkThemeFamily",
195 static_assert(ArrayLength(sIntPrefs) == size_t(LookAndFeel::IntID::End),
196 "Should have a pref for each int value");
198 // This array MUST be kept in the same order as the float id list in
199 // LookAndFeel.h
200 // clang-format off
201 static const char sFloatPrefs[][37] = {
202 "ui.IMEUnderlineRelativeSize",
203 "ui.SpellCheckerUnderlineRelativeSize",
204 "ui.caretAspectRatio",
205 "ui.textScaleFactor",
206 "ui.cursorScale",
208 // clang-format on
210 static_assert(ArrayLength(sFloatPrefs) == size_t(LookAndFeel::FloatID::End),
211 "Should have a pref for each float value");
213 // This array MUST be kept in the same order as the color list in
214 // specified/color.rs
215 static const char sColorPrefs[][41] = {
216 "ui.activeborder",
217 "ui.activecaption",
218 "ui.appworkspace",
219 "ui.background",
220 "ui.buttonface",
221 "ui.buttonhighlight",
222 "ui.buttonshadow",
223 "ui.buttontext",
224 "ui.buttonborder",
225 "ui.captiontext",
226 "ui.-moz-field",
227 "ui.-moz-disabledfield",
228 "ui.-moz-fieldtext",
229 "ui.mark",
230 "ui.marktext",
231 "ui.-moz-comboboxtext",
232 "ui.-moz-combobox",
233 "ui.graytext",
234 "ui.highlight",
235 "ui.highlighttext",
236 "ui.inactiveborder",
237 "ui.inactivecaption",
238 "ui.inactivecaptiontext",
239 "ui.infobackground",
240 "ui.infotext",
241 "ui.menu",
242 "ui.menutext",
243 "ui.scrollbar",
244 "ui.threeddarkshadow",
245 "ui.threedface",
246 "ui.threedhighlight",
247 "ui.threedlightshadow",
248 "ui.threedshadow",
249 "ui.window",
250 "ui.windowframe",
251 "ui.windowtext",
252 "ui.-moz-default-color",
253 "ui.-moz-default-background-color",
254 "ui.-moz-dialog",
255 "ui.-moz-dialogtext",
256 "ui.-moz-cellhighlight",
257 "ui.-moz_cellhighlighttext",
258 "ui.selecteditem",
259 "ui.selecteditemtext",
260 "ui.-moz-buttonhoverface",
261 "ui.-moz_buttonhovertext",
262 "ui.-moz_menuhover",
263 "ui.-moz_menuhoverdisabled",
264 "ui.-moz_menuhovertext",
265 "ui.-moz_menubarhovertext",
266 "ui.-moz_eventreerow",
267 "ui.-moz_oddtreerow",
268 "ui.-moz-buttonactivetext",
269 "ui.-moz-buttonactiveface",
270 "ui.-moz-buttondisabledface",
271 "ui.-moz-headerbar",
272 "ui.-moz-headerbartext",
273 "ui.-moz-headerbarinactive",
274 "ui.-moz-headerbarinactivetext",
275 "ui.-moz-mac-defaultbuttontext",
276 "ui.-moz-mac-focusring",
277 "ui.-moz_mac_disabledtoolbartext",
278 "ui.-moz-sidebar",
279 "ui.-moz-sidebartext",
280 "ui.-moz-sidebarborder",
281 "ui.accentcolor",
282 "ui.accentcolortext",
283 "ui.-moz-autofill-background",
284 "ui.-moz-nativehyperlinktext",
285 "ui.-moz-nativevisitedhyperlinktext",
286 "ui.-moz-hyperlinktext",
287 "ui.-moz-activehyperlinktext",
288 "ui.-moz-visitedhyperlinktext",
289 "ui.-moz-colheader",
290 "ui.-moz-colheadertext",
291 "ui.-moz-colheaderhover",
292 "ui.-moz-colheaderhovertext",
293 "ui.-moz-colheaderactive",
294 "ui.-moz-colheaderactivetext",
295 "ui.textSelectDisabledBackground",
296 "ui.textSelectAttentionBackground",
297 "ui.textSelectAttentionForeground",
298 "ui.textHighlightBackground",
299 "ui.textHighlightForeground",
300 "ui.IMERawInputBackground",
301 "ui.IMERawInputForeground",
302 "ui.IMERawInputUnderline",
303 "ui.IMESelectedRawTextBackground",
304 "ui.IMESelectedRawTextForeground",
305 "ui.IMESelectedRawTextUnderline",
306 "ui.IMEConvertedTextBackground",
307 "ui.IMEConvertedTextForeground",
308 "ui.IMEConvertedTextUnderline",
309 "ui.IMESelectedConvertedTextBackground",
310 "ui.IMESelectedConvertedTextForeground",
311 "ui.IMESelectedConvertedTextUnderline",
312 "ui.SpellCheckerUnderline",
313 "ui.themedScrollbar",
314 "ui.themedScrollbarInactive",
315 "ui.themedScrollbarThumb",
316 "ui.themedScrollbarThumbHover",
317 "ui.themedScrollbarThumbActive",
318 "ui.themedScrollbarThumbInactive",
321 static_assert(ArrayLength(sColorPrefs) == size_t(LookAndFeel::ColorID::End),
322 "Should have a pref for each color value");
324 // This array MUST be kept in the same order as the SystemFont enum.
325 static const char sFontPrefs[][41] = {
326 "ui.font.caption",
327 "ui.font.icon",
328 "ui.font.menu",
329 "ui.font.message-box",
330 "ui.font.small-caption",
331 "ui.font.status-bar",
332 "ui.font.-moz-pull-down-menu",
333 "ui.font.-moz-button",
334 "ui.font.-moz-list",
335 "ui.font.-moz-field",
338 static_assert(ArrayLength(sFontPrefs) == size_t(LookAndFeel::FontID::End),
339 "Should have a pref for each font value");
341 const char* nsXPLookAndFeel::GetColorPrefName(ColorID aId) {
342 return sColorPrefs[size_t(aId)];
345 bool nsXPLookAndFeel::sInitialized = false;
347 nsXPLookAndFeel* nsXPLookAndFeel::sInstance = nullptr;
348 bool nsXPLookAndFeel::sShutdown = false;
350 auto LookAndFeel::SystemZoomSettings() -> ZoomSettings {
351 ZoomSettings settings;
352 switch (StaticPrefs::browser_display_os_zoom_behavior()) {
353 case 0:
354 default:
355 break;
356 case 1:
357 settings.mFullZoom = GetTextScaleFactor();
358 break;
359 case 2:
360 settings.mTextZoom = GetTextScaleFactor();
361 break;
363 return settings;
366 // static
367 nsXPLookAndFeel* nsXPLookAndFeel::GetInstance() {
368 if (sInstance) {
369 return sInstance;
372 NS_ENSURE_TRUE(!sShutdown, nullptr);
374 // If we're in a content process, then the parent process will have supplied
375 // us with an initial FullLookAndFeel object.
376 // We grab this data from the ContentChild,
377 // where it's been temporarily stashed, and initialize our new LookAndFeel
378 // object with it.
380 FullLookAndFeel* lnf = nullptr;
382 if (auto* cc = mozilla::dom::ContentChild::GetSingleton()) {
383 lnf = &cc->BorrowLookAndFeelData();
386 if (lnf) {
387 sInstance = new widget::RemoteLookAndFeel(std::move(*lnf));
388 } else if (gfxPlatform::IsHeadless()) {
389 sInstance = new widget::HeadlessLookAndFeel();
390 } else {
391 sInstance = new nsLookAndFeel();
394 // This is only ever used once during initialization, and can be cleared now.
395 if (lnf) {
396 *lnf = {};
399 widget::Theme::Init();
400 return sInstance;
403 // static
404 void nsXPLookAndFeel::Shutdown() {
405 if (sShutdown) {
406 return;
409 sShutdown = true;
410 delete sInstance;
411 sInstance = nullptr;
413 // This keeps strings alive, so need to clear to make leak checking happy.
414 sFontCache.Clear();
416 widget::Theme::Shutdown();
419 static void IntPrefChanged(const nsACString& aPref) {
420 // Most Int prefs can't change our system colors or fonts, but
421 // ui.systemUsesDarkTheme can, since it affects the effective color-scheme
422 // (affecting system colors).
423 auto changeKind = aPref.EqualsLiteral("ui.systemUsesDarkTheme")
424 ? widget::ThemeChangeKind::Style
425 : widget::ThemeChangeKind::MediaQueriesOnly;
426 LookAndFeel::NotifyChangedAllWindows(changeKind);
429 static void FloatPrefChanged(const nsACString& aPref) {
430 // Most float prefs can't change our system colors or fonts, but
431 // textScaleFactor affects layout.
432 auto changeKind = aPref.EqualsLiteral("ui.textScaleFactor")
433 ? widget::ThemeChangeKind::StyleAndLayout
434 : widget::ThemeChangeKind::MediaQueriesOnly;
435 LookAndFeel::NotifyChangedAllWindows(changeKind);
438 static void ColorPrefChanged() {
439 // Color prefs affect style, because they by definition change system colors.
440 LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind::Style);
443 static void FontPrefChanged() {
444 // Color prefs affect style, because they by definition change system fonts.
445 LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind::Style);
448 // static
449 void nsXPLookAndFeel::OnPrefChanged(const char* aPref, void* aClosure) {
450 nsDependentCString prefName(aPref);
451 for (const char* pref : sIntPrefs) {
452 if (prefName.Equals(pref)) {
453 IntPrefChanged(prefName);
454 return;
458 for (const char* pref : sFloatPrefs) {
459 if (prefName.Equals(pref)) {
460 FloatPrefChanged(prefName);
461 return;
465 for (const char* pref : sColorPrefs) {
466 // We use StringBeginsWith to handle .dark prefs too.
467 if (StringBeginsWith(prefName, nsDependentCString(pref))) {
468 ColorPrefChanged();
469 return;
473 for (const char* pref : sFontPrefs) {
474 if (StringBeginsWith(prefName, nsDependentCString(pref))) {
475 FontPrefChanged();
476 return;
481 static constexpr struct {
482 nsLiteralCString mName;
483 widget::ThemeChangeKind mChangeKind =
484 widget::ThemeChangeKind::MediaQueriesOnly;
485 } kMediaQueryPrefs[] = {
486 // Affects whether standins are used for the accent color.
487 {"widget.non-native-theme.use-theme-accent"_ns,
488 widget::ThemeChangeKind::Style},
489 // These three affect system colors on Windows.
490 {"widget.windows.uwp-system-colors.enabled"_ns,
491 widget::ThemeChangeKind::Style},
492 {"widget.windows.uwp-system-colors.highlight-accent"_ns,
493 widget::ThemeChangeKind::Style},
494 // Affects env().
495 {"layout.css.prefers-color-scheme.content-override"_ns,
496 widget::ThemeChangeKind::Style},
497 // Affects media queries and scrollbar sizes, so gotta relayout.
498 {"widget.gtk.overlay-scrollbars.enabled"_ns,
499 widget::ThemeChangeKind::StyleAndLayout},
500 // Affects zoom settings which includes text and full zoom.
501 {"browser.display.os-zoom-behavior"_ns,
502 widget::ThemeChangeKind::StyleAndLayout},
503 // This affects system colors on Linux.
504 {"widget.gtk.libadwaita-colors.enabled"_ns, widget::ThemeChangeKind::Style},
505 // This affects not only the media query, but also the native theme, so we
506 // need to re-layout.
507 {"browser.theme.toolbar-theme"_ns, widget::ThemeChangeKind::AllBits},
508 {"browser.theme.content-theme"_ns},
511 // Read values from the user's preferences.
512 // This is done once at startup, but since the user's preferences
513 // haven't actually been read yet at that time, we also have to
514 // set a callback to inform us of changes to each pref.
515 void nsXPLookAndFeel::Init() {
516 MOZ_RELEASE_ASSERT(NS_IsMainThread());
518 // Say we're already initialized, and take the chance that it might fail;
519 // protects against some other process writing to our static variables.
520 sInitialized = true;
522 if (XRE_IsParentProcess()) {
523 nsLayoutUtils::RecomputeSmoothScrollDefault();
526 // XXX If we could reorganize the pref names, we should separate the branch
527 // for each types. Then, we could reduce the unnecessary loop from
528 // nsXPLookAndFeel::OnPrefChanged().
529 Preferences::RegisterPrefixCallback(OnPrefChanged, "ui.");
530 // We really do just want the accessibility.tabfocus pref, not other prefs
531 // that start with that string.
532 Preferences::RegisterCallback(OnPrefChanged, "accessibility.tabfocus");
534 for (const auto& pref : kMediaQueryPrefs) {
535 Preferences::RegisterCallback(
536 [](const char*, void* aChangeKind) {
537 auto changeKind =
538 widget::ThemeChangeKind(reinterpret_cast<uintptr_t>(aChangeKind));
539 LookAndFeel::NotifyChangedAllWindows(changeKind);
541 pref.mName, reinterpret_cast<void*>(uintptr_t(pref.mChangeKind)));
545 nsXPLookAndFeel::~nsXPLookAndFeel() {
546 NS_ASSERTION(sInstance == this,
547 "This destroying instance isn't the singleton instance");
548 sInstance = nullptr;
551 static bool IsSpecialColor(LookAndFeel::ColorID aID, nscolor aColor) {
552 using ColorID = LookAndFeel::ColorID;
554 if (aColor == NS_SAME_AS_FOREGROUND_COLOR) {
555 return true;
558 switch (aID) {
559 case ColorID::IMESelectedRawTextBackground:
560 case ColorID::IMESelectedConvertedTextBackground:
561 case ColorID::IMERawInputBackground:
562 case ColorID::IMEConvertedTextBackground:
563 case ColorID::IMESelectedRawTextForeground:
564 case ColorID::IMESelectedConvertedTextForeground:
565 case ColorID::IMERawInputForeground:
566 case ColorID::IMEConvertedTextForeground:
567 case ColorID::IMERawInputUnderline:
568 case ColorID::IMEConvertedTextUnderline:
569 case ColorID::IMESelectedRawTextUnderline:
570 case ColorID::IMESelectedConvertedTextUnderline:
571 case ColorID::SpellCheckerUnderline:
572 return NS_IS_SELECTION_SPECIAL_COLOR(aColor);
573 default:
574 break;
577 * In GetColor(), every color that is not a special color is color
578 * corrected. Use false to make other colors color corrected.
580 return false;
583 nscolor nsXPLookAndFeel::GetStandinForNativeColor(ColorID aID,
584 ColorScheme aScheme) {
585 if (aScheme == ColorScheme::Dark) {
586 if (auto color = GenericDarkColor(aID)) {
587 return *color;
591 // The stand-in colors are taken from what the non-native theme needs (for
592 // field/button colors), the Windows 7 Aero theme except Mac-specific colors
593 // which are taken from Mac OS 10.7.
595 #define COLOR(name_, r, g, b) \
596 case ColorID::name_: \
597 return NS_RGB(r, g, b);
599 #define COLORA(name_, r, g, b, a) \
600 case ColorID::name_: \
601 return NS_RGBA(r, g, b, a);
603 switch (aID) {
604 // These are here for the purposes of headless mode.
605 case ColorID::IMESelectedRawTextBackground:
606 case ColorID::IMESelectedConvertedTextBackground:
607 case ColorID::IMERawInputBackground:
608 case ColorID::IMEConvertedTextBackground:
609 return NS_TRANSPARENT;
610 case ColorID::IMESelectedRawTextForeground:
611 case ColorID::IMESelectedConvertedTextForeground:
612 case ColorID::IMERawInputForeground:
613 case ColorID::IMEConvertedTextForeground:
614 return NS_SAME_AS_FOREGROUND_COLOR;
615 case ColorID::IMERawInputUnderline:
616 case ColorID::IMEConvertedTextUnderline:
617 return NS_40PERCENT_FOREGROUND_COLOR;
618 case ColorID::Accentcolor:
619 return widget::sDefaultAccent.ToABGR();
620 case ColorID::Accentcolortext:
621 return widget::sDefaultAccentText.ToABGR();
622 COLOR(SpellCheckerUnderline, 0xff, 0x00, 0x00)
623 COLOR(TextSelectDisabledBackground, 0xAA, 0xAA, 0xAA)
625 // Titlebar colors
626 // deprecated in CSS Color Level 4, same as Buttonborder:
627 COLOR(Activeborder, 0xE3, 0xE3, 0xE3)
628 // deprecated in CSS Color Level 4, same as Buttonborder:
629 COLOR(Inactiveborder, 0xE3, 0xE3, 0xE3)
630 // deprecated in CSS Color Level 4, same as Canvas/Window:
631 COLOR(Activecaption, 0xFF, 0xFF, 0xFF)
632 // deprecated in CSS Color Level 4, same as Canvas/Window:
633 COLOR(Inactivecaption, 0xFF, 0xFF, 0xFF)
634 // deprecated in CSS Color Level 4, same as Canvastext/Windowtext:
635 COLOR(Captiontext, 0x00, 0x00, 0x00)
636 // deprecated in CSS Color Level 4, same as Graytext:
637 COLOR(Inactivecaptiontext, 0x6D, 0x6D, 0x6D)
639 // CSS 2 colors:
640 // deprecated in CSS Color Level 4, same as Canvas/Window:
641 COLOR(Appworkspace, 0xFF, 0xFF, 0xFF)
642 // deprecated in CSS Color Level 4, same as Canvas/Window:
643 COLOR(Background, 0xFF, 0xFF, 0xFF)
644 // deprecated in CSS Color Level 4, same as Buttonface:
645 COLOR(Buttonhighlight, 0xE9, 0xE9, 0xED)
646 // deprecated in CSS Color Level 4, same as Buttonface:
647 COLOR(Buttonshadow, 0xE9, 0xE9, 0xED)
649 // Buttons and comboboxes should be kept in sync since they are drawn with
650 // the same colors by the non-native theme.
651 COLOR(Buttonface, 0xE9, 0xE9, 0xED)
652 COLORA(MozButtondisabledface, 0xE9, 0xE9, 0xED, 128)
654 COLOR(MozCombobox, 0xE9, 0xE9, 0xED)
656 COLOR(Buttontext, 0x00, 0x00, 0x00)
657 COLOR(MozComboboxtext, 0x00, 0x00, 0x00)
659 COLOR(Graytext, 0x6D, 0x6D, 0x6D)
660 COLOR(Highlight, 0x33, 0x99, 0xFF)
661 COLOR(Highlighttext, 0xFF, 0xFF, 0xFF)
662 // deprecated in CSS Color Level 4, same as Canvas/Window:
663 COLOR(Infobackground, 0xFF, 0xFF, 0xFF)
664 // deprecated in CSS Color Level 4, same as Canvastext/Windowtext:
665 COLOR(Infotext, 0x00, 0x00, 0x00)
666 // deprecated in CSS Color Level 4, same as Canvas/Window:
667 COLOR(Menu, 0xFF, 0xFF, 0xFF)
668 // deprecated in CSS Color Level 4, same as Canvastext/Windowtext:
669 COLOR(Menutext, 0x00, 0x00, 0x00)
670 // deprecated in CSS Color Level 4, same as Canvas/Window:
671 COLOR(Scrollbar, 0xFF, 0xFF, 0xFF)
672 // deprecated in CSS Color Level 4, same as Buttonborder:
673 COLOR(Threeddarkshadow, 0xE3, 0xE3, 0xE3)
674 // deprecated in CSS Color Level 4, same as Buttonface:
675 COLOR(Threedface, 0xE9, 0xE9, 0xED)
676 // deprecated in CSS Color Level 4, same as Buttonborder:
677 COLOR(Threedhighlight, 0xE3, 0xE3, 0xE3)
678 COLOR(Threedlightshadow, 0xE3, 0xE3, 0xE3)
679 // deprecated in CSS Color Level 4, same as Buttonborder:
680 COLOR(Threedshadow, 0xE3, 0xE3, 0xE3)
681 COLOR(Buttonborder, 0xE3, 0xE3, 0xE3)
682 COLOR(Mark, 0xFF, 0xFF, 0x00)
683 COLOR(Marktext, 0x00, 0x00, 0x00)
684 COLOR(Window, 0xFF, 0xFF, 0xFF)
685 // deprecated in CSS Color Level 4, same as Buttonborder:
686 COLOR(Windowframe, 0xE3, 0xE3, 0xE3)
687 COLOR(Windowtext, 0x00, 0x00, 0x00)
688 COLOR(Field, 0xFF, 0xFF, 0xFF)
689 COLORA(MozDisabledfield, 0xFF, 0xFF, 0xFF, 128)
690 COLOR(Fieldtext, 0x00, 0x00, 0x00)
691 COLOR(MozDialog, 0xF0, 0xF0, 0xF0)
692 COLOR(MozDialogtext, 0x00, 0x00, 0x00)
693 COLOR(MozColheadertext, 0x00, 0x00, 0x00)
694 COLOR(MozColheaderhovertext, 0x00, 0x00, 0x00)
695 COLOR(MozCellhighlight, 0xF0, 0xF0, 0xF0)
696 COLOR(MozCellhighlighttext, 0x00, 0x00, 0x00)
697 COLOR(Selecteditem, 0x33, 0x99, 0xFF)
698 COLOR(Selecteditemtext, 0xFF, 0xFF, 0xFF)
699 COLOR(MozButtonhoverface, 0xd0, 0xd0, 0xd7)
700 COLOR(MozButtonhovertext, 0x00, 0x00, 0x00)
701 COLOR(MozButtonactiveface, 0xb1, 0xb1, 0xb9)
702 COLOR(MozButtonactivetext, 0x00, 0x00, 0x00)
703 COLOR(MozMenuhover, 0x33, 0x99, 0xFF)
704 COLOR(MozMenuhovertext, 0x00, 0x00, 0x00)
705 COLOR(MozMenubarhovertext, 0x00, 0x00, 0x00)
706 COLOR(MozMenuhoverdisabled, 0xF0, 0xF0, 0xF0)
707 COLOR(MozEventreerow, 0xFF, 0xFF, 0xFF)
708 COLOR(MozOddtreerow, 0xFF, 0xFF, 0xFF)
709 COLOR(MozMacFocusring, 0x60, 0x9D, 0xD7)
710 COLOR(MozMacDisabledtoolbartext, 0x3F, 0x3F, 0x3F)
711 // Seems to be the default color (hardcoded because of bug 1065998)
712 COLOR(MozNativehyperlinktext, 0x00, 0x66, 0xCC)
713 COLOR(MozNativevisitedhyperlinktext, 0x55, 0x1A, 0x8B)
714 default:
715 break;
717 return NS_RGB(0xFF, 0xFF, 0xFF);
720 #undef COLOR
721 #undef COLORA
723 // Taken from in-content/common.inc.css's dark theme.
724 Maybe<nscolor> nsXPLookAndFeel::GenericDarkColor(ColorID aID) {
725 nscolor color = NS_RGB(0, 0, 0);
726 static constexpr nscolor kWindowBackground = NS_RGB(28, 27, 34);
727 static constexpr nscolor kWindowText = NS_RGB(251, 251, 254);
728 switch (aID) {
729 case ColorID::Window: // --in-content-page-background
730 case ColorID::Background:
731 case ColorID::Appworkspace:
732 case ColorID::Scrollbar:
733 case ColorID::Infobackground:
734 color = kWindowBackground;
735 break;
737 case ColorID::Menu:
738 color = NS_RGB(0x2b, 0x2a, 0x33);
739 break;
741 case ColorID::MozMenuhovertext:
742 case ColorID::MozMenubarhovertext:
743 case ColorID::Menutext:
744 color = NS_RGB(0xfb, 0xfb, 0xfe);
745 break;
747 case ColorID::MozMenuhover:
748 color = NS_RGB(0x52, 0x52, 0x5e);
749 break;
751 case ColorID::MozMenuhoverdisabled:
752 color = NS_RGB(0x3a, 0x39, 0x44);
753 break;
755 case ColorID::MozEventreerow:
756 case ColorID::MozOddtreerow:
757 case ColorID::MozDialog: // --in-content-box-background
758 color = NS_RGB(35, 34, 43);
759 break;
760 case ColorID::Windowtext: // --in-content-page-color
761 case ColorID::MozDialogtext:
762 case ColorID::MozSidebartext:
763 case ColorID::Fieldtext:
764 case ColorID::Infotext:
765 case ColorID::Buttontext: // --in-content-button-text-color (via
766 // --in-content-page-color)
767 case ColorID::MozComboboxtext:
768 case ColorID::MozButtonhovertext:
769 case ColorID::MozButtonactivetext:
770 case ColorID::MozHeaderbartext:
771 case ColorID::MozHeaderbarinactivetext:
772 case ColorID::Captiontext:
773 case ColorID::Inactivecaptiontext: // TODO(emilio): Maybe make
774 // Inactivecaptiontext Graytext?
775 case ColorID::MozColheadertext:
776 case ColorID::MozColheaderhovertext:
777 case ColorID::MozColheaderactivetext:
778 color = kWindowText;
779 break;
780 case ColorID::Buttonshadow:
781 case ColorID::Threedshadow:
782 case ColorID::MozSidebarborder:
783 case ColorID::Threedlightshadow:
784 case ColorID::Threedhighlight:
785 case ColorID::Windowframe:
786 case ColorID::Buttonborder: // --in-content-box-border-color computed
787 // with kWindowText above
788 // kWindowBackground.
789 case ColorID::Graytext: // opacity: 0.4 of kWindowText blended over the
790 // "Window" background color, which happens to be
791 // the same :-)
792 color = NS_ComposeColors(kWindowBackground, NS_RGBA(251, 251, 254, 102));
793 break;
794 case ColorID::MozCellhighlight:
795 case ColorID::Selecteditem: // --in-content-primary-button-background /
796 // --in-content-item-selected
797 color = NS_RGB(0, 221, 255);
798 break;
799 case ColorID::MozSidebar:
800 case ColorID::Field:
801 case ColorID::Buttonface: // --in-content-button-background
802 case ColorID::Buttonhighlight:
803 case ColorID::MozColheader:
804 case ColorID::Threedface:
805 case ColorID::MozCombobox:
806 case ColorID::MozCellhighlighttext:
807 case ColorID::Selecteditemtext: // --in-content-primary-button-text-color /
808 // --in-content-item-selected-text
809 color = NS_RGB(43, 42, 51);
810 break;
811 case ColorID::Threeddarkshadow: // Same as Threedlightshadow but with the
812 // background.
813 case ColorID::MozDisabledfield: // opacity: 0.4 of the face above blended
814 // over the "Window" background color.
815 case ColorID::MozButtondisabledface:
816 color = NS_ComposeColors(kWindowBackground, NS_RGBA(43, 42, 51, 102));
817 break;
818 case ColorID::MozButtonhoverface: // --in-content-button-background-hover
819 case ColorID::MozColheaderhover:
820 color = NS_RGB(82, 82, 94);
821 break;
822 case ColorID::MozButtonactiveface: // --in-content-button-background-active
823 case ColorID::MozColheaderactive:
824 color = NS_RGB(91, 91, 102);
825 break;
826 case ColorID::Highlight:
827 color = NS_RGBA(0, 221, 255, 78);
828 break;
829 case ColorID::Highlighttext:
830 color = NS_SAME_AS_FOREGROUND_COLOR;
831 break;
832 case ColorID::MozNativehyperlinktext:
833 // If you change this color, you probably also want to change the default
834 // value of browser.anchor_color.dark.
835 color = NS_RGB(0x8c, 0x8c, 0xff);
836 break;
837 case ColorID::MozNativevisitedhyperlinktext:
838 // If you change this color, you probably also want to change the default
839 // value of browser.visited_color.dark.
840 color = NS_RGB(0xff, 0xad, 0xff);
841 break;
842 case ColorID::SpellCheckerUnderline:
843 // This is the default for active links in dark mode as well
844 // (browser.active_color.dark). See bug 1755564 for some analysis and
845 // other options too.
846 color = NS_RGB(0xff, 0x66, 0x66);
847 break;
848 case ColorID::Activeborder:
849 case ColorID::Inactiveborder:
850 color = NS_RGB(57, 57, 57);
851 break;
852 case ColorID::MozHeaderbar:
853 case ColorID::MozHeaderbarinactive:
854 case ColorID::Activecaption:
855 case ColorID::Inactivecaption:
856 color = NS_RGB(28, 27, 34);
857 break;
858 default:
859 return Nothing();
861 return Some(color);
864 // Uncomment the #define below if you want to debug system color use in a skin
865 // that uses them. When set, it will make all system color pairs that are
866 // appropriate for foreground/background pairing the same. This means if the
867 // skin is using system colors correctly you will not be able to see *any* text.
869 // #define DEBUG_SYSTEM_COLOR_USE
871 #ifdef DEBUG_SYSTEM_COLOR_USE
872 static nsresult SystemColorUseDebuggingColor(LookAndFeel::ColorID aID,
873 nscolor& aResult) {
874 using ColorID = LookAndFeel::ColorID;
876 switch (aID) {
877 // css2 http://www.w3.org/TR/REC-CSS2/ui.html#system-colors
878 case ColorID::Activecaption:
879 // active window caption background
880 case ColorID::Captiontext:
881 // text in active window caption
882 aResult = NS_RGB(0xff, 0x00, 0x00);
883 break;
885 case ColorID::Highlight:
886 // background of selected item
887 case ColorID::Highlighttext:
888 // text of selected item
889 aResult = NS_RGB(0xff, 0xff, 0x00);
890 break;
892 case ColorID::Inactivecaption:
893 // inactive window caption
894 case ColorID::Inactivecaptiontext:
895 // text in inactive window caption
896 aResult = NS_RGB(0x66, 0x66, 0x00);
897 break;
899 case ColorID::Infobackground:
900 // tooltip background color
901 case ColorID::Infotext:
902 // tooltip text color
903 aResult = NS_RGB(0x00, 0xff, 0x00);
904 break;
906 case ColorID::Menu:
907 // menu background
908 case ColorID::Menutext:
909 // menu text
910 aResult = NS_RGB(0x00, 0xff, 0xff);
911 break;
913 case ColorID::Threedface:
914 case ColorID::Buttonface:
915 // 3-D face color
916 case ColorID::Buttontext:
917 // text on push buttons
918 aResult = NS_RGB(0x00, 0x66, 0x66);
919 break;
921 case ColorID::Window:
922 case ColorID::Windowtext:
923 aResult = NS_RGB(0x00, 0x00, 0xff);
924 break;
926 // from the CSS3 working draft (not yet finalized)
927 // http://www.w3.org/tr/2000/wd-css3-userint-20000216.html#color
929 case ColorID::Field:
930 case ColorID::Fieldtext:
931 aResult = NS_RGB(0xff, 0x00, 0xff);
932 break;
934 case ColorID::MozDialog:
935 case ColorID::MozDialogtext:
936 aResult = NS_RGB(0x66, 0x00, 0x66);
937 break;
939 default:
940 return NS_ERROR_NOT_AVAILABLE;
943 return NS_OK;
945 #endif
947 static nsresult GetPrefColor(const char* aPref, nscolor& aResult) {
948 nsAutoCString colorStr;
949 MOZ_TRY(Preferences::GetCString(aPref, colorStr));
950 if (!ServoCSSParser::ComputeColor(nullptr, NS_RGB(0, 0, 0), colorStr,
951 &aResult)) {
952 return NS_ERROR_FAILURE;
954 return NS_OK;
957 static nsresult GetColorFromPref(LookAndFeel::ColorID aID, ColorScheme aScheme,
958 nscolor& aResult) {
959 const char* prefName = sColorPrefs[size_t(aID)];
960 if (aScheme == ColorScheme::Dark) {
961 nsAutoCString darkPrefName(prefName);
962 darkPrefName.Append(".dark");
963 if (NS_SUCCEEDED(GetPrefColor(darkPrefName.get(), aResult))) {
964 return NS_OK;
967 return GetPrefColor(prefName, aResult);
970 // All these routines will return NS_OK if they have a value,
971 // in which case the nsLookAndFeel should use that value;
972 // otherwise we'll return NS_ERROR_NOT_AVAILABLE, in which case, the
973 // platform-specific nsLookAndFeel should use its own values instead.
974 nsresult nsXPLookAndFeel::GetColorValue(ColorID aID, ColorScheme aScheme,
975 UseStandins aUseStandins,
976 nscolor& aResult) {
977 if (!sInitialized) {
978 Init();
981 #ifdef DEBUG_SYSTEM_COLOR_USE
982 if (NS_SUCCEEDED(SystemColorUseDebuggingColor(aID, aResult))) {
983 return NS_OK;
985 #endif
987 auto& cache = sColorCaches.Get(aScheme, aUseStandins);
988 if (const auto* cached = cache.Get(aID)) {
989 if (cached->isNothing()) {
990 return NS_ERROR_FAILURE;
992 aResult = cached->value();
993 return NS_OK;
996 // NOTE: Servo holds a lock and the main thread is paused, so writing to the
997 // global cache here is fine.
998 auto result = GetUncachedColor(aID, aScheme, aUseStandins);
999 cache.Insert(aID, result);
1000 if (!result) {
1001 return NS_ERROR_FAILURE;
1003 aResult = *result;
1004 return NS_OK;
1007 Maybe<nscolor> nsXPLookAndFeel::GetUncachedColor(ColorID aID,
1008 ColorScheme aScheme,
1009 UseStandins aUseStandins) {
1010 if (aUseStandins == UseStandins::Yes) {
1011 return Some(GetStandinForNativeColor(aID, aScheme));
1013 nscolor r;
1014 if (NS_SUCCEEDED(GetColorFromPref(aID, aScheme, r))) {
1015 return Some(r);
1017 if (NS_SUCCEEDED(NativeGetColor(aID, aScheme, r))) {
1018 if (gfxPlatform::GetCMSMode() == CMSMode::All && !IsSpecialColor(aID, r)) {
1019 qcms_transform* transform = gfxPlatform::GetCMSInverseRGBTransform();
1020 if (transform) {
1021 uint8_t color[4];
1022 color[0] = NS_GET_R(r);
1023 color[1] = NS_GET_G(r);
1024 color[2] = NS_GET_B(r);
1025 color[3] = NS_GET_A(r);
1026 qcms_transform_data(transform, color, color, 1);
1027 r = NS_RGBA(color[0], color[1], color[2], color[3]);
1031 return Some(r);
1033 return Nothing();
1036 nsresult nsXPLookAndFeel::GetIntValue(IntID aID, int32_t& aResult) {
1037 if (!sInitialized) {
1038 Init();
1041 if (const auto* cached = sIntCache.Get(aID)) {
1042 if (cached->isNothing()) {
1043 return NS_ERROR_FAILURE;
1045 aResult = cached->value();
1046 return NS_OK;
1049 if (NS_SUCCEEDED(Preferences::GetInt(sIntPrefs[size_t(aID)], &aResult))) {
1050 sIntCache.Insert(aID, Some(aResult));
1051 return NS_OK;
1054 if (NS_FAILED(NativeGetInt(aID, aResult))) {
1055 sIntCache.Insert(aID, Nothing());
1056 return NS_ERROR_FAILURE;
1059 sIntCache.Insert(aID, Some(aResult));
1060 return NS_OK;
1063 nsresult nsXPLookAndFeel::GetFloatValue(FloatID aID, float& aResult) {
1064 if (!sInitialized) {
1065 Init();
1068 if (const auto* cached = sFloatCache.Get(aID)) {
1069 if (cached->isNothing()) {
1070 return NS_ERROR_FAILURE;
1072 aResult = cached->value();
1073 return NS_OK;
1076 int32_t pref = 0;
1077 if (NS_SUCCEEDED(Preferences::GetInt(sFloatPrefs[size_t(aID)], &pref))) {
1078 aResult = float(pref) / 100.0f;
1079 sFloatCache.Insert(aID, Some(aResult));
1080 return NS_OK;
1083 if (NS_FAILED(NativeGetFloat(aID, aResult))) {
1084 sFloatCache.Insert(aID, Nothing());
1085 return NS_ERROR_FAILURE;
1088 sFloatCache.Insert(aID, Some(aResult));
1089 return NS_OK;
1092 bool nsXPLookAndFeel::LookAndFeelFontToStyle(const LookAndFeelFont& aFont,
1093 nsString& aName,
1094 gfxFontStyle& aStyle) {
1095 if (!aFont.haveFont()) {
1096 return false;
1098 aName = aFont.name();
1099 aStyle = gfxFontStyle();
1100 aStyle.size = aFont.size();
1101 aStyle.weight = FontWeight::FromInt(aFont.weight());
1102 aStyle.style =
1103 aFont.italic() ? FontSlantStyle::ITALIC : FontSlantStyle::NORMAL;
1104 aStyle.systemFont = true;
1105 return true;
1108 widget::LookAndFeelFont nsXPLookAndFeel::StyleToLookAndFeelFont(
1109 const nsAString& aName, const gfxFontStyle& aStyle) {
1110 LookAndFeelFont font;
1111 font.haveFont() = true;
1112 font.name() = aName;
1113 font.size() = aStyle.size;
1114 font.weight() = aStyle.weight.ToFloat();
1115 font.italic() = aStyle.style.IsItalic();
1116 MOZ_ASSERT(aStyle.style.IsNormal() || aStyle.style.IsItalic(),
1117 "Cannot handle oblique font style");
1118 #ifdef DEBUG
1120 // Assert that all the remaining font style properties have their
1121 // default values.
1122 gfxFontStyle candidate = aStyle;
1123 gfxFontStyle defaults{};
1124 candidate.size = defaults.size;
1125 candidate.weight = defaults.weight;
1126 candidate.style = defaults.style;
1127 MOZ_ASSERT(candidate.Equals(defaults),
1128 "Some font style properties not supported");
1130 #endif
1131 return font;
1134 bool nsXPLookAndFeel::GetFontValue(FontID aID, nsString& aName,
1135 gfxFontStyle& aStyle) {
1136 if (const LookAndFeelFont* cached = sFontCache.Get(aID)) {
1137 return LookAndFeelFontToStyle(*cached, aName, aStyle);
1140 LookAndFeelFont font;
1141 auto GetFontsFromPrefs = [&]() -> bool {
1142 nsDependentCString pref(sFontPrefs[size_t(aID)]);
1143 if (NS_FAILED(Preferences::GetString(pref.get(), aName))) {
1144 return false;
1146 font.haveFont() = true;
1147 font.name() = aName;
1148 font.size() = Preferences::GetFloat(nsAutoCString(pref + ".size"_ns).get());
1149 // This is written this way rather than using the fallback so that an empty
1150 // pref (such like the one about:config creates) doesn't cause system fonts
1151 // to have zero-size.
1152 if (font.size() < 1.0f) {
1153 font.size() = StyleFONT_MEDIUM_PX;
1155 font.weight() = Preferences::GetFloat(
1156 nsAutoCString(pref + ".weight"_ns).get(), FontWeight::NORMAL.ToFloat());
1157 font.italic() =
1158 Preferences::GetBool(nsAutoCString(pref + ".italic"_ns).get());
1159 return true;
1162 if (GetFontsFromPrefs()) {
1163 LookAndFeelFontToStyle(font, aName, aStyle);
1164 } else if (NativeGetFont(aID, aName, aStyle)) {
1165 font = StyleToLookAndFeelFont(aName, aStyle);
1166 } else {
1167 MOZ_ASSERT(!font.haveFont());
1169 bool success = font.haveFont();
1170 sFontCache.Insert(aID, std::move(font));
1171 return success;
1174 void nsXPLookAndFeel::RefreshImpl() {
1175 // Wipe out our caches.
1176 sColorCaches.Clear();
1177 sFontCache.Clear();
1178 sFloatCache.Clear();
1179 sIntCache.Clear();
1181 if (XRE_IsParentProcess()) {
1182 nsLayoutUtils::RecomputeSmoothScrollDefault();
1183 // Clear any cached FullLookAndFeel data, which is now invalid.
1184 widget::RemoteLookAndFeel::ClearCachedData();
1188 static bool sRecordedLookAndFeelTelemetry = false;
1190 void nsXPLookAndFeel::RecordTelemetry() {
1191 if (!XRE_IsParentProcess()) {
1192 return;
1195 if (sRecordedLookAndFeelTelemetry) {
1196 return;
1199 sRecordedLookAndFeelTelemetry = true;
1201 int32_t i;
1202 Telemetry::ScalarSet(
1203 Telemetry::ScalarID::WIDGET_DARK_MODE,
1204 NS_SUCCEEDED(GetIntValue(IntID::SystemUsesDarkTheme, i)) && i != 0);
1206 RecordLookAndFeelSpecificTelemetry();
1209 namespace mozilla {
1211 bool LookAndFeel::sGlobalThemeChanged;
1212 static widget::ThemeChangeKind sGlobalThemeChangeKind{0};
1214 void LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind aKind) {
1215 sGlobalThemeChanged = true;
1216 sGlobalThemeChangeKind |= aKind;
1218 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
1219 const char16_t kind[] = {char16_t(aKind), 0};
1220 obs->NotifyObservers(nullptr, "internal-look-and-feel-changed", kind);
1224 void LookAndFeel::DoHandleGlobalThemeChange() {
1225 MOZ_ASSERT(sGlobalThemeChanged);
1226 sGlobalThemeChanged = false;
1227 auto kind = std::exchange(sGlobalThemeChangeKind, widget::ThemeChangeKind(0));
1229 // Tell the theme that it changed, so it can flush any handles to stale theme
1230 // data.
1232 // We can use the *DoNotUseDirectly functions directly here, because we want
1233 // to notify all possible themes in a given process (but just once).
1234 if (XRE_IsParentProcess() ||
1235 !StaticPrefs::widget_non_native_theme_enabled()) {
1236 if (nsCOMPtr<nsITheme> theme = do_GetNativeThemeDoNotUseDirectly()) {
1237 theme->ThemeChanged();
1240 if (nsCOMPtr<nsITheme> theme = do_GetBasicNativeThemeDoNotUseDirectly()) {
1241 theme->ThemeChanged();
1244 // Clear all cached LookAndFeel colors.
1245 LookAndFeel::Refresh();
1247 // Reset default background and foreground colors for the document since they
1248 // may be using system colors, color scheme, etc.
1249 PreferenceSheet::Refresh();
1251 // Vector images (SVG) may be using theme colors so we discard all cached
1252 // surfaces. (We could add a vector image only version of DiscardAll, but
1253 // in bug 940625 we decided theme changes are rare enough not to bother.)
1254 image::SurfaceCacheUtils::DiscardAll();
1256 if (XRE_IsParentProcess()) {
1257 dom::ContentParent::BroadcastThemeUpdate(kind);
1260 nsContentUtils::AddScriptRunner(
1261 NS_NewRunnableFunction("HandleGlobalThemeChange", [] {
1262 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
1263 obs->NotifyObservers(nullptr, "look-and-feel-changed", nullptr);
1265 }));
1268 #define BIT_FOR(_c) (1ull << size_t(ColorID::_c))
1270 // We want to use a non-native color scheme for the non-native theme (except in
1271 // high-contrast mode), so spoof some of the colors with stand-ins to prevent
1272 // lack of contrast.
1273 static constexpr std::bitset<size_t(ColorID::End)> sNonNativeThemeStandinColors{
1274 // Used by default button styles.
1275 BIT_FOR(Buttonface) | BIT_FOR(Buttontext) | BIT_FOR(MozButtonhoverface) |
1276 BIT_FOR(MozButtonhovertext) | BIT_FOR(MozButtonactiveface) |
1277 BIT_FOR(MozButtonactivetext) | BIT_FOR(MozButtondisabledface) |
1278 BIT_FOR(Buttonborder) |
1279 // Used by select elements.
1280 BIT_FOR(MozCombobox) | BIT_FOR(MozComboboxtext) |
1281 BIT_FOR(Threedlightshadow) |
1282 // For symmetry with the above.
1283 BIT_FOR(Threeddarkshadow) |
1284 // Used by fieldset borders.
1285 BIT_FOR(Threedface) |
1286 // Used by input / textarea.
1287 BIT_FOR(Field) | BIT_FOR(Fieldtext) |
1288 // Used by disabled form controls.
1289 BIT_FOR(MozDisabledfield) | BIT_FOR(Graytext) |
1290 // Per spec, the following colors are deprecated, see
1291 // https://drafts.csswg.org/css-color-4/#deprecated-system-colors
1292 // should match ButtonFace:
1293 BIT_FOR(Buttonhighlight) | BIT_FOR(Buttonshadow) | BIT_FOR(Threedface) |
1294 // should match ButtonBorder:
1295 BIT_FOR(Activeborder) | BIT_FOR(Inactiveborder) |
1296 BIT_FOR(Threeddarkshadow) | BIT_FOR(Threedhighlight) |
1297 BIT_FOR(Threedshadow) | BIT_FOR(Windowframe) |
1298 // should match GrayText:
1299 BIT_FOR(Inactivecaptiontext) |
1300 // should match Canvas/Window:
1301 BIT_FOR(Appworkspace) | BIT_FOR(Background) | BIT_FOR(Inactivecaption) |
1302 BIT_FOR(Infobackground) | BIT_FOR(Menu) | BIT_FOR(Scrollbar) |
1303 // should match CanvasText/WindowText:
1304 BIT_FOR(Activecaption) | BIT_FOR(Captiontext) | BIT_FOR(Infotext) |
1305 BIT_FOR(Menutext) |
1306 // Some pages expect these to return windows-like colors, see bug 1773795.
1307 // Also, per spec, these should match Canvas/CanvasText, see
1308 // https://drafts.csswg.org/css-color-4/#valdef-color-window and
1309 // https://drafts.csswg.org/css-color-4/#valdef-color-windowtext
1310 BIT_FOR(Window) | BIT_FOR(Windowtext)};
1311 #undef BIT_FOR
1313 static bool ShouldUseStandinsForNativeColorForNonNativeTheme(
1314 const dom::Document& aDoc, LookAndFeel::ColorID aColor,
1315 const PreferenceSheet::Prefs& aPrefs) {
1316 const bool shouldUseStandinsForColor = [&] {
1317 if (sNonNativeThemeStandinColors[size_t(aColor)]) {
1318 return true;
1320 // There are platforms where we want the content-exposed accent color to be
1321 // the windows blue rather than the system accent color, for now.
1322 return !StaticPrefs::widget_non_native_theme_use_theme_accent() &&
1323 (aColor == LookAndFeel::ColorID::Accentcolor ||
1324 aColor == LookAndFeel::ColorID::Accentcolortext);
1325 }();
1327 return shouldUseStandinsForColor && aDoc.ShouldAvoidNativeTheme() &&
1328 !aPrefs.NonNativeThemeShouldBeHighContrast();
1331 bool LookAndFeel::IsDarkColor(nscolor aColor) {
1332 // Given https://www.w3.org/TR/WCAG20/#contrast-ratiodef, this is the
1333 // threshold that tells us whether contrast is better against white or black.
1335 // Contrast ratio against black is: (L + 0.05) / 0.05
1336 // Contrast ratio against white is: 1.05 / (L + 0.05)
1338 // So the intersection is:
1340 // (L + 0.05) / 0.05 = 1.05 / (L + 0.05)
1342 // And the solution to that equation is:
1344 // sqrt(1.05 * 0.05) - 0.05
1346 // So we consider a color dark if the contrast is below this threshold, and
1347 // it's at least half-opaque.
1348 constexpr float kThreshold = 0.179129;
1349 return NS_GET_A(aColor) > 127 &&
1350 RelativeLuminanceUtils::Compute(aColor) < kThreshold;
1353 ColorScheme LookAndFeel::ColorSchemeForStyle(
1354 const dom::Document& aDoc, const StyleColorSchemeFlags& aFlags,
1355 ColorSchemeMode aMode) {
1356 const auto& prefs = PreferenceSheet::PrefsFor(aDoc);
1357 StyleColorSchemeFlags style(aFlags);
1358 if (!style) {
1359 style._0 = aDoc.GetColorSchemeBits();
1361 const bool supportsDark = bool(style & StyleColorSchemeFlags::DARK);
1362 const bool supportsLight = bool(style & StyleColorSchemeFlags::LIGHT);
1363 if (supportsLight && supportsDark) {
1364 // Both color-schemes are explicitly supported, use the preferred one.
1365 return aDoc.PreferredColorScheme();
1367 if (supportsDark || supportsLight) {
1368 // One color-scheme is explicitly supported and one isn't, so use the one
1369 // the content supports.
1370 return supportsDark ? ColorScheme::Dark : ColorScheme::Light;
1372 // No value specified. Chrome docs, and forced-colors mode always supports
1373 // both, so use the preferred color-scheme.
1374 if (aMode == ColorSchemeMode::Preferred || aDoc.ChromeRulesEnabled() ||
1375 !prefs.mUseDocumentColors) {
1376 return aDoc.PreferredColorScheme();
1378 // Otherwise default content to light.
1379 return ColorScheme::Light;
1382 LookAndFeel::ColorScheme LookAndFeel::ColorSchemeForFrame(
1383 const nsIFrame* aFrame, ColorSchemeMode aMode) {
1384 return ColorSchemeForStyle(*aFrame->PresContext()->Document(),
1385 aFrame->StyleUI()->mColorScheme.bits, aMode);
1388 // static
1389 Maybe<nscolor> LookAndFeel::GetColor(ColorID aId, ColorScheme aScheme,
1390 UseStandins aUseStandins) {
1391 nscolor result;
1392 nsresult rv = nsLookAndFeel::GetInstance()->GetColorValue(
1393 aId, aScheme, aUseStandins, result);
1394 if (NS_FAILED(rv)) {
1395 return Nothing();
1397 return Some(result);
1400 // Returns whether there is a CSS color name for this color.
1401 static bool ColorIsCSSAccessible(LookAndFeel::ColorID aId) {
1402 using ColorID = LookAndFeel::ColorID;
1404 switch (aId) {
1405 case ColorID::TextSelectDisabledBackground:
1406 case ColorID::TextSelectAttentionBackground:
1407 case ColorID::TextSelectAttentionForeground:
1408 case ColorID::TextHighlightBackground:
1409 case ColorID::TextHighlightForeground:
1410 case ColorID::ThemedScrollbar:
1411 case ColorID::ThemedScrollbarInactive:
1412 case ColorID::ThemedScrollbarThumb:
1413 case ColorID::ThemedScrollbarThumbActive:
1414 case ColorID::ThemedScrollbarThumbInactive:
1415 case ColorID::ThemedScrollbarThumbHover:
1416 case ColorID::IMERawInputBackground:
1417 case ColorID::IMERawInputForeground:
1418 case ColorID::IMERawInputUnderline:
1419 case ColorID::IMESelectedRawTextBackground:
1420 case ColorID::IMESelectedRawTextForeground:
1421 case ColorID::IMESelectedRawTextUnderline:
1422 case ColorID::IMEConvertedTextBackground:
1423 case ColorID::IMEConvertedTextForeground:
1424 case ColorID::IMEConvertedTextUnderline:
1425 case ColorID::IMESelectedConvertedTextBackground:
1426 case ColorID::IMESelectedConvertedTextForeground:
1427 case ColorID::IMESelectedConvertedTextUnderline:
1428 case ColorID::SpellCheckerUnderline:
1429 return false;
1430 default:
1431 break;
1434 return true;
1437 LookAndFeel::UseStandins LookAndFeel::ShouldUseStandins(
1438 const dom::Document& aDoc, ColorID aId) {
1439 const auto& prefs = PreferenceSheet::PrefsFor(aDoc);
1440 if (ShouldUseStandinsForNativeColorForNonNativeTheme(aDoc, aId, prefs)) {
1441 return UseStandins::Yes;
1443 if (prefs.mUseStandins && ColorIsCSSAccessible(aId)) {
1444 return UseStandins::Yes;
1446 return UseStandins::No;
1449 Maybe<nscolor> LookAndFeel::GetColor(ColorID aId, const nsIFrame* aFrame) {
1450 const auto* doc = aFrame->PresContext()->Document();
1451 return GetColor(aId, ColorSchemeForFrame(aFrame),
1452 ShouldUseStandins(*doc, aId));
1455 // static
1456 nsresult LookAndFeel::GetInt(IntID aID, int32_t* aResult) {
1457 return nsLookAndFeel::GetInstance()->GetIntValue(aID, *aResult);
1460 // static
1461 nsresult LookAndFeel::GetFloat(FloatID aID, float* aResult) {
1462 return nsLookAndFeel::GetInstance()->GetFloatValue(aID, *aResult);
1465 // static
1466 bool LookAndFeel::GetFont(FontID aID, nsString& aName, gfxFontStyle& aStyle) {
1467 return nsLookAndFeel::GetInstance()->GetFontValue(aID, aName, aStyle);
1470 // static
1471 char16_t LookAndFeel::GetPasswordCharacter() {
1472 return nsLookAndFeel::GetInstance()->GetPasswordCharacterImpl();
1475 // static
1476 bool LookAndFeel::GetEchoPassword() {
1477 if (StaticPrefs::editor_password_mask_delay() >= 0) {
1478 return StaticPrefs::editor_password_mask_delay() > 0;
1480 return nsLookAndFeel::GetInstance()->GetEchoPasswordImpl();
1483 // static
1484 uint32_t LookAndFeel::GetPasswordMaskDelay() {
1485 int32_t delay = StaticPrefs::editor_password_mask_delay();
1486 if (delay < 0) {
1487 return nsLookAndFeel::GetInstance()->GetPasswordMaskDelayImpl();
1489 return delay;
1492 bool LookAndFeel::DrawInTitlebar() {
1493 switch (StaticPrefs::browser_tabs_inTitlebar()) {
1494 case 0:
1495 return false;
1496 case 1:
1497 return true;
1498 default:
1499 break;
1501 return nsLookAndFeel::GetInstance()->GetDefaultDrawInTitlebar();
1504 LookAndFeel::TitlebarAction LookAndFeel::GetTitlebarAction(
1505 TitlebarEvent aEvent) {
1506 return nsLookAndFeel::GetInstance()->GetTitlebarAction(aEvent);
1509 void LookAndFeel::GetThemeInfo(nsACString& aOut) {
1510 nsLookAndFeel::GetInstance()->GetThemeInfo(aOut);
1513 uint32_t LookAndFeel::GetMenuAccessKey() {
1514 return StaticPrefs::ui_key_menuAccessKey();
1517 Modifiers LookAndFeel::GetMenuAccessKeyModifiers() {
1518 switch (GetMenuAccessKey()) {
1519 case dom::KeyboardEvent_Binding::DOM_VK_SHIFT:
1520 return MODIFIER_SHIFT;
1521 case dom::KeyboardEvent_Binding::DOM_VK_CONTROL:
1522 return MODIFIER_CONTROL;
1523 case dom::KeyboardEvent_Binding::DOM_VK_ALT:
1524 return MODIFIER_ALT;
1525 case dom::KeyboardEvent_Binding::DOM_VK_META:
1526 case dom::KeyboardEvent_Binding::DOM_VK_WIN:
1527 return MODIFIER_META;
1528 default:
1529 return 0;
1533 // static
1534 void LookAndFeel::Refresh() {
1535 nsLookAndFeel::GetInstance()->RefreshImpl();
1536 widget::Theme::LookAndFeelChanged();
1539 // static
1540 void LookAndFeel::NativeInit() { nsLookAndFeel::GetInstance()->NativeInit(); }
1542 // static
1543 void LookAndFeel::SetData(widget::FullLookAndFeel&& aTables) {
1544 nsLookAndFeel::GetInstance()->SetDataImpl(std::move(aTables));
1547 } // namespace mozilla