Bug 1716846 [wpt PR 29402] - Update wpt metadata, a=testonly
[gecko.git] / widget / nsXPLookAndFeel.cpp
blob22d557f4f47497e6dd2d809b4b1241e27cabca33
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 "nscore.h"
10 #include "nsXPLookAndFeel.h"
11 #include "nsLookAndFeel.h"
12 #include "HeadlessLookAndFeel.h"
13 #include "RemoteLookAndFeel.h"
14 #include "nsContentUtils.h"
15 #include "nsCRT.h"
16 #include "nsFont.h"
17 #include "nsIFrame.h"
18 #include "nsIXULRuntime.h"
19 #include "nsNativeBasicTheme.h"
20 #include "mozilla/dom/ContentChild.h"
21 #include "mozilla/Preferences.h"
22 #include "mozilla/Services.h"
23 #include "mozilla/ServoStyleSet.h"
24 #include "mozilla/ServoCSSParser.h"
25 #include "mozilla/StaticPrefs_browser.h"
26 #include "mozilla/StaticPrefs_editor.h"
27 #include "mozilla/StaticPrefs_findbar.h"
28 #include "mozilla/StaticPrefs_ui.h"
29 #include "mozilla/StaticPrefs_widget.h"
30 #include "mozilla/dom/Document.h"
31 #include "mozilla/PreferenceSheet.h"
32 #include "mozilla/gfx/2D.h"
33 #include "mozilla/widget/WidgetMessageUtils.h"
34 #include "mozilla/Telemetry.h"
35 #include "mozilla/TelemetryScalarEnums.h"
37 #include "gfxPlatform.h"
38 #include "gfxFont.h"
40 #include "qcms.h"
42 #ifdef DEBUG
43 # include "nsSize.h"
44 #endif
46 using namespace mozilla;
48 using IntID = mozilla::LookAndFeel::IntID;
49 using FloatID = mozilla::LookAndFeel::FloatID;
50 using ColorID = mozilla::LookAndFeel::ColorID;
51 using FontID = mozilla::LookAndFeel::FontID;
53 template <typename Index, typename Value, Index kEnd>
54 class EnumeratedCache {
55 static constexpr uint32_t ChunkFor(Index aIndex) {
56 return uint32_t(aIndex) >> 5; // >> 5 is the same as / 32.
58 static constexpr uint32_t BitFor(Index aIndex) {
59 return 1u << (uint32_t(aIndex) & 31);
61 static constexpr uint32_t kChunks = ChunkFor(kEnd) + 1;
63 mozilla::EnumeratedArray<Index, kEnd, Value> mEntries;
64 uint32_t mValidity[kChunks] = {0};
66 public:
67 constexpr EnumeratedCache() = default;
69 bool IsValid(Index aIndex) const {
70 return mValidity[ChunkFor(aIndex)] & BitFor(aIndex);
73 const Value* Get(Index aIndex) const {
74 return IsValid(aIndex) ? &mEntries[aIndex] : nullptr;
77 void Insert(Index aIndex, Value aValue) {
78 mValidity[ChunkFor(aIndex)] |= BitFor(aIndex);
79 mEntries[aIndex] = aValue;
82 void Remove(Index aIndex) {
83 mValidity[ChunkFor(aIndex)] &= ~BitFor(aIndex);
84 mEntries[aIndex] = Value();
87 void Clear() {
88 for (auto& chunk : mValidity) {
89 chunk = 0;
91 for (auto& entry : mEntries) {
92 entry = Value();
97 static EnumeratedCache<ColorID, Maybe<nscolor>, ColorID::End> sLightColorCache;
98 static EnumeratedCache<ColorID, Maybe<nscolor>, ColorID::End> sDarkColorCache;
99 static EnumeratedCache<FloatID, Maybe<float>, FloatID::End> sFloatCache;
100 static EnumeratedCache<IntID, Maybe<int32_t>, IntID::End> sIntCache;
101 static EnumeratedCache<FontID, widget::LookAndFeelFont, FontID::End> sFontCache;
103 // To make one of these prefs toggleable from a reftest add a user
104 // pref in testing/profiles/reftest/user.js. For example, to make
105 // ui.useAccessibilityTheme toggleable, add:
107 // user_pref("ui.useAccessibilityTheme", 0);
109 // This needs to be of the same length and in the same order as
110 // LookAndFeel::IntID values.
111 static const char sIntPrefs[][42] = {
112 "ui.caretBlinkTime",
113 "ui.caretWidth",
114 "ui.caretVisibleWithSelection",
115 "ui.selectTextfieldsOnKeyFocus",
116 "ui.submenuDelay",
117 "ui.menusCanOverlapOSBar",
118 "ui.useOverlayScrollbars",
119 "ui.allowOverlayScrollbarsOverlap",
120 "ui.showHideScrollbars",
121 "ui.skipNavigatingDisabledMenuItem",
122 "ui.dragThresholdX",
123 "ui.dragThresholdY",
124 "ui.useAccessibilityTheme",
125 "ui.scrollArrowStyle",
126 "ui.scrollSliderStyle",
127 "ui.scrollButtonLeftMouseButtonAction",
128 "ui.scrollButtonMiddleMouseButtonAction",
129 "ui.scrollButtonRightMouseButtonAction",
130 "ui.treeOpenDelay",
131 "ui.treeCloseDelay",
132 "ui.treeLazyScrollDelay",
133 "ui.treeScrollDelay",
134 "ui.treeScrollLinesMax",
135 "accessibility.tabfocus", // Weird one...
136 "ui.chosenMenuItemsShouldBlink",
137 "ui.windowsAccentColorInTitlebar",
138 "ui.windowsDefaultTheme",
139 "ui.dwmCompositor",
140 "ui.windowsClassic",
141 "ui.windowsGlass",
142 "ui.macGraphiteTheme",
143 "ui.macBigSurTheme",
144 "ui.alertNotificationOrigin",
145 "ui.scrollToClick",
146 "ui.IMERawInputUnderlineStyle",
147 "ui.IMESelectedRawTextUnderlineStyle",
148 "ui.IMEConvertedTextUnderlineStyle",
149 "ui.IMESelectedConvertedTextUnderlineStyle",
150 "ui.SpellCheckerUnderlineStyle",
151 "ui.menuBarDrag",
152 "ui.windowsThemeIdentifier",
153 "ui.operatingSystemVersionIdentifier",
154 "ui.scrollbarButtonAutoRepeatBehavior",
155 "ui.tooltipDelay",
156 "ui.swipeAnimationEnabled",
157 "ui.scrollbarDisplayOnMouseMove",
158 "ui.scrollbarFadeBeginDelay",
159 "ui.scrollbarFadeDuration",
160 "ui.contextMenuOffsetVertical",
161 "ui.contextMenuOffsetHorizontal",
162 "ui.GtkCSDAvailable",
163 "ui.GtkCSDHideTitlebarByDefault",
164 "ui.GtkCSDTransparentBackground",
165 "ui.GtkCSDMinimizeButton",
166 "ui.GtkCSDMaximizeButton",
167 "ui.GtkCSDCloseButton",
168 "ui.GtkCSDMinimizeButtonPosition",
169 "ui.GtkCSDMaximizeButtonPosition",
170 "ui.GtkCSDCloseButtonPosition",
171 "ui.GtkCSDReversedPlacement",
172 "ui.systemUsesDarkTheme",
173 "ui.prefersReducedMotion",
174 "ui.primaryPointerCapabilities",
175 "ui.allPointerCapabilities",
176 "ui.systemVerticalScrollbarWidth",
177 "ui.systemHorizontalScrollbarHeight",
180 static_assert(ArrayLength(sIntPrefs) == size_t(LookAndFeel::IntID::End),
181 "Should have a pref for each int value");
183 // This array MUST be kept in the same order as the float id list in
184 // LookAndFeel.h
185 static const char sFloatPrefs[][37] = {
186 "ui.IMEUnderlineRelativeSize",
187 "ui.SpellCheckerUnderlineRelativeSize",
188 "ui.caretAspectRatio",
189 "ui.textScaleFactor",
192 static_assert(ArrayLength(sFloatPrefs) == size_t(LookAndFeel::FloatID::End),
193 "Should have a pref for each float value");
195 // This array MUST be kept in the same order as the color list in
196 // specified/color.rs
197 static const char sColorPrefs[][41] = {
198 "ui.windowBackground",
199 "ui.windowForeground",
200 "ui.widgetBackground",
201 "ui.widgetForeground",
202 "ui.widgetSelectBackground",
203 "ui.widgetSelectForeground",
204 "ui.widget3DHighlight",
205 "ui.widget3DShadow",
206 "ui.textBackground",
207 "ui.textForeground",
208 "ui.textSelectBackground",
209 "ui.textSelectForeground",
210 "ui.textSelectBackgroundDisabled",
211 "ui.textSelectBackgroundAttention",
212 "ui.textHighlightBackground",
213 "ui.textHighlightForeground",
214 "ui.IMERawInputBackground",
215 "ui.IMERawInputForeground",
216 "ui.IMERawInputUnderline",
217 "ui.IMESelectedRawTextBackground",
218 "ui.IMESelectedRawTextForeground",
219 "ui.IMESelectedRawTextUnderline",
220 "ui.IMEConvertedTextBackground",
221 "ui.IMEConvertedTextForeground",
222 "ui.IMEConvertedTextUnderline",
223 "ui.IMESelectedConvertedTextBackground",
224 "ui.IMESelectedConvertedTextForeground",
225 "ui.IMESelectedConvertedTextUnderline",
226 "ui.SpellCheckerUnderline",
227 "ui.themedScrollbar",
228 "ui.themedScrollbarInactive",
229 "ui.themedScrollbarThumb",
230 "ui.themedScrollbarThumbHover",
231 "ui.themedScrollbarThumbActive",
232 "ui.themedScrollbarThumbInactive",
233 "ui.activeborder",
234 "ui.activecaption",
235 "ui.appworkspace",
236 "ui.background",
237 "ui.buttonface",
238 "ui.buttonhighlight",
239 "ui.buttonshadow",
240 "ui.buttontext",
241 "ui.captiontext",
242 "ui.-moz-field",
243 "ui.-moz-fieldtext",
244 "ui.graytext",
245 "ui.highlight",
246 "ui.highlighttext",
247 "ui.inactiveborder",
248 "ui.inactivecaption",
249 "ui.inactivecaptiontext",
250 "ui.infobackground",
251 "ui.infotext",
252 "ui.menu",
253 "ui.menutext",
254 "ui.scrollbar",
255 "ui.threeddarkshadow",
256 "ui.threedface",
257 "ui.threedhighlight",
258 "ui.threedlightshadow",
259 "ui.threedshadow",
260 "ui.window",
261 "ui.windowframe",
262 "ui.windowtext",
263 "ui.-moz-buttondefault",
264 "ui.-moz-default-color",
265 "ui.-moz-default-background-color",
266 "ui.-moz-dialog",
267 "ui.-moz-dialogtext",
268 "ui.-moz-dragtargetzone",
269 "ui.-moz-cellhighlight",
270 "ui.-moz_cellhighlighttext",
271 "ui.-moz-html-cellhighlight",
272 "ui.-moz-html-cellhighlighttext",
273 "ui.-moz-buttonhoverface",
274 "ui.-moz_buttonhovertext",
275 "ui.-moz_menuhover",
276 "ui.-moz_menuhovertext",
277 "ui.-moz_menubartext",
278 "ui.-moz_menubarhovertext",
279 "ui.-moz_eventreerow",
280 "ui.-moz_oddtreerow",
281 "ui.-moz-gtk-buttonactivetext",
282 "ui.-moz-mac-buttonactivetext",
283 "ui.-moz_mac_chrome_active",
284 "ui.-moz_mac_chrome_inactive",
285 "ui.-moz-mac-defaultbuttontext",
286 "ui.-moz-mac-focusring",
287 "ui.-moz-mac-menuselect",
288 "ui.-moz-mac-menushadow",
289 "ui.-moz-mac-menutextdisable",
290 "ui.-moz-mac-menutextselect",
291 "ui.-moz_mac_disabledtoolbartext",
292 "ui.-moz-mac-secondaryhighlight",
293 "ui.-moz-mac-vibrant-titlebar-light",
294 "ui.-moz-mac-vibrant-titlebar-dark",
295 "ui.-moz-mac-menupopup",
296 "ui.-moz-mac-menuitem",
297 "ui.-moz-mac-active-menuitem",
298 "ui.-moz-mac-source-list",
299 "ui.-moz-mac-source-list-selection",
300 "ui.-moz-mac-active-source-list-selection",
301 "ui.-moz-mac-tooltip",
302 "ui.-moz-accent-color",
303 "ui.-moz-accent-color-foreground",
304 "ui.-moz-win-mediatext",
305 "ui.-moz-win-communicationstext",
306 "ui.-moz-nativehyperlinktext",
307 "ui.-moz-hyperlinktext",
308 "ui.-moz-activehyperlinktext",
309 "ui.-moz-visitedhyperlinktext",
310 "ui.-moz-comboboxtext",
311 "ui.-moz-combobox",
312 "ui.-moz-colheadertext",
313 "ui.-moz-colheaderhovertext",
314 "ui.-moz-gtk-titlebar-text",
315 "ui.-moz-gtk-titlebar-inactive-text",
318 static_assert(ArrayLength(sColorPrefs) == size_t(LookAndFeel::ColorID::End),
319 "Should have a pref for each color value");
321 const char* nsXPLookAndFeel::GetColorPrefName(ColorID aId) {
322 return sColorPrefs[size_t(aId)];
325 bool nsXPLookAndFeel::sInitialized = false;
327 nsXPLookAndFeel* nsXPLookAndFeel::sInstance = nullptr;
328 bool nsXPLookAndFeel::sShutdown = false;
330 // static
331 nsXPLookAndFeel* nsXPLookAndFeel::GetInstance() {
332 if (sInstance) {
333 return sInstance;
336 NS_ENSURE_TRUE(!sShutdown, nullptr);
338 // If we're in a content process, then the parent process will have supplied
339 // us with an initial FullLookAndFeel object.
340 // We grab this data from the ContentChild,
341 // where it's been temporarily stashed, and initialize our new LookAndFeel
342 // object with it.
344 FullLookAndFeel* lnf = nullptr;
346 if (auto* cc = mozilla::dom::ContentChild::GetSingleton()) {
347 lnf = &cc->BorrowLookAndFeelData();
350 if (lnf) {
351 sInstance = new widget::RemoteLookAndFeel(std::move(*lnf));
352 } else if (gfxPlatform::IsHeadless()) {
353 sInstance = new widget::HeadlessLookAndFeel();
354 } else {
355 sInstance = new nsLookAndFeel();
358 // This is only ever used once during initialization, and can be cleared now.
359 if (lnf) {
360 *lnf = {};
363 nsNativeBasicTheme::Init();
364 return sInstance;
367 // static
368 void nsXPLookAndFeel::Shutdown() {
369 if (sShutdown) {
370 return;
373 sShutdown = true;
374 delete sInstance;
375 sInstance = nullptr;
377 // This keeps strings alive, so need to clear to make leak checking happy.
378 sFontCache.Clear();
380 nsNativeBasicTheme::Shutdown();
383 static void IntPrefChanged() {
384 // Int prefs can't change our system colors or fonts.
385 LookAndFeel::NotifyChangedAllWindows(
386 widget::ThemeChangeKind::MediaQueriesOnly);
389 static void FloatPrefChanged() {
390 // Float prefs can't change our system colors or fonts.
391 LookAndFeel::NotifyChangedAllWindows(
392 widget::ThemeChangeKind::MediaQueriesOnly);
395 static void ColorPrefChanged() {
396 // Color prefs affect style, because they by definition change system colors.
397 LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind::Style);
400 // static
401 void nsXPLookAndFeel::OnPrefChanged(const char* aPref, void* aClosure) {
402 nsDependentCString prefName(aPref);
403 for (const char* pref : sIntPrefs) {
404 if (prefName.Equals(pref)) {
405 IntPrefChanged();
406 return;
410 for (const char* pref : sFloatPrefs) {
411 if (prefName.Equals(pref)) {
412 FloatPrefChanged();
413 return;
417 for (const char* pref : sColorPrefs) {
418 if (prefName.Equals(pref)) {
419 ColorPrefChanged();
420 return;
425 static constexpr nsLiteralCString kMediaQueryPrefs[] = {
426 "browser.display.windows.native_menus"_ns,
427 "browser.proton.enabled"_ns,
428 "browser.proton.places-tooltip.enabled"_ns,
429 "browser.theme.toolbar-theme"_ns,
432 // Read values from the user's preferences.
433 // This is done once at startup, but since the user's preferences
434 // haven't actually been read yet at that time, we also have to
435 // set a callback to inform us of changes to each pref.
436 void nsXPLookAndFeel::Init() {
437 MOZ_RELEASE_ASSERT(NS_IsMainThread());
439 // Say we're already initialized, and take the chance that it might fail;
440 // protects against some other process writing to our static variables.
441 sInitialized = true;
443 // XXX If we could reorganize the pref names, we should separate the branch
444 // for each types. Then, we could reduce the unnecessary loop from
445 // nsXPLookAndFeel::OnPrefChanged().
446 Preferences::RegisterPrefixCallback(OnPrefChanged, "ui.");
447 // We really do just want the accessibility.tabfocus pref, not other prefs
448 // that start with that string.
449 Preferences::RegisterCallback(OnPrefChanged, "accessibility.tabfocus");
451 for (auto& pref : kMediaQueryPrefs) {
452 Preferences::RegisterCallback(
453 [](const char*, void*) {
454 LookAndFeel::NotifyChangedAllWindows(
455 widget::ThemeChangeKind::MediaQueriesOnly);
457 pref);
461 nsXPLookAndFeel::~nsXPLookAndFeel() {
462 NS_ASSERTION(sInstance == this,
463 "This destroying instance isn't the singleton instance");
464 sInstance = nullptr;
467 static bool IsSpecialColor(LookAndFeel::ColorID aID, nscolor aColor) {
468 using ColorID = LookAndFeel::ColorID;
470 switch (aID) {
471 case ColorID::TextSelectForeground:
472 case ColorID::IMESelectedRawTextBackground:
473 case ColorID::IMESelectedConvertedTextBackground:
474 case ColorID::IMERawInputBackground:
475 case ColorID::IMEConvertedTextBackground:
476 case ColorID::IMESelectedRawTextForeground:
477 case ColorID::IMESelectedConvertedTextForeground:
478 case ColorID::IMERawInputForeground:
479 case ColorID::IMEConvertedTextForeground:
480 case ColorID::IMERawInputUnderline:
481 case ColorID::IMEConvertedTextUnderline:
482 case ColorID::IMESelectedRawTextUnderline:
483 case ColorID::IMESelectedConvertedTextUnderline:
484 case ColorID::SpellCheckerUnderline:
485 return NS_IS_SELECTION_SPECIAL_COLOR(aColor);
486 default:
487 break;
490 * In GetColor(), every color that is not a special color is color
491 * corrected. Use false to make other colors color corrected.
493 return false;
496 nscolor nsXPLookAndFeel::GetStandinForNativeColor(ColorID aID) {
497 // The stand-in colors are taken from the Windows 7 Aero theme
498 // except Mac-specific colors which are taken from Mac OS 10.7.
500 #define COLOR(name_, r, g, b) \
501 case ColorID::name_: \
502 return NS_RGB(r, g, b);
504 switch (aID) {
505 // CSS 2 colors:
506 COLOR(Activeborder, 0xB4, 0xB4, 0xB4)
507 COLOR(Activecaption, 0x99, 0xB4, 0xD1)
508 COLOR(Appworkspace, 0xAB, 0xAB, 0xAB)
509 COLOR(Background, 0x00, 0x00, 0x00)
510 COLOR(Buttonface, 0xF0, 0xF0, 0xF0)
511 COLOR(Buttonhighlight, 0xFF, 0xFF, 0xFF)
512 COLOR(Buttonshadow, 0xA0, 0xA0, 0xA0)
513 COLOR(Buttontext, 0x00, 0x00, 0x00)
514 COLOR(Captiontext, 0x00, 0x00, 0x00)
515 COLOR(Graytext, 0x6D, 0x6D, 0x6D)
516 COLOR(Highlight, 0x33, 0x99, 0xFF)
517 COLOR(Highlighttext, 0xFF, 0xFF, 0xFF)
518 COLOR(Inactiveborder, 0xF4, 0xF7, 0xFC)
519 COLOR(Inactivecaption, 0xBF, 0xCD, 0xDB)
520 COLOR(Inactivecaptiontext, 0x43, 0x4E, 0x54)
521 COLOR(Infobackground, 0xFF, 0xFF, 0xE1)
522 COLOR(Infotext, 0x00, 0x00, 0x00)
523 COLOR(Menu, 0xF0, 0xF0, 0xF0)
524 COLOR(Menutext, 0x00, 0x00, 0x00)
525 COLOR(Scrollbar, 0xC8, 0xC8, 0xC8)
526 COLOR(Threeddarkshadow, 0x69, 0x69, 0x69)
527 COLOR(Threedface, 0xF0, 0xF0, 0xF0)
528 COLOR(Threedhighlight, 0xFF, 0xFF, 0xFF)
529 COLOR(Threedlightshadow, 0xE3, 0xE3, 0xE3)
530 COLOR(Threedshadow, 0xA0, 0xA0, 0xA0)
531 COLOR(Window, 0xFF, 0xFF, 0xFF)
532 COLOR(Windowframe, 0x64, 0x64, 0x64)
533 COLOR(Windowtext, 0x00, 0x00, 0x00)
534 COLOR(MozButtondefault, 0x69, 0x69, 0x69)
535 COLOR(Field, 0xFF, 0xFF, 0xFF)
536 COLOR(Fieldtext, 0x00, 0x00, 0x00)
537 COLOR(MozDialog, 0xF0, 0xF0, 0xF0)
538 COLOR(MozDialogtext, 0x00, 0x00, 0x00)
539 COLOR(MozColheadertext, 0x00, 0x00, 0x00)
540 COLOR(MozColheaderhovertext, 0x00, 0x00, 0x00)
541 COLOR(MozDragtargetzone, 0xFF, 0xFF, 0xFF)
542 COLOR(MozCellhighlight, 0xF0, 0xF0, 0xF0)
543 COLOR(MozCellhighlighttext, 0x00, 0x00, 0x00)
544 COLOR(MozHtmlCellhighlight, 0x33, 0x99, 0xFF)
545 COLOR(MozHtmlCellhighlighttext, 0xFF, 0xFF, 0xFF)
546 COLOR(MozButtonhoverface, 0xF0, 0xF0, 0xF0)
547 COLOR(MozGtkButtonactivetext, 0x00, 0x00, 0x00)
548 COLOR(MozButtonhovertext, 0x00, 0x00, 0x00)
549 COLOR(MozMenuhover, 0x33, 0x99, 0xFF)
550 COLOR(MozMenuhovertext, 0x00, 0x00, 0x00)
551 COLOR(MozMenubartext, 0x00, 0x00, 0x00)
552 COLOR(MozMenubarhovertext, 0x00, 0x00, 0x00)
553 COLOR(MozOddtreerow, 0xFF, 0xFF, 0xFF)
554 COLOR(MozMacChromeActive, 0xB2, 0xB2, 0xB2)
555 COLOR(MozMacChromeInactive, 0xE1, 0xE1, 0xE1)
556 COLOR(MozMacFocusring, 0x60, 0x9D, 0xD7)
557 COLOR(MozMacMenuselect, 0x38, 0x75, 0xD7)
558 COLOR(MozMacMenushadow, 0xA3, 0xA3, 0xA3)
559 COLOR(MozMacMenutextdisable, 0x88, 0x88, 0x88)
560 COLOR(MozMacMenutextselect, 0xFF, 0xFF, 0xFF)
561 COLOR(MozMacDisabledtoolbartext, 0x3F, 0x3F, 0x3F)
562 COLOR(MozMacSecondaryhighlight, 0xD4, 0xD4, 0xD4)
563 COLOR(MozMacVibrantTitlebarLight, 0xf7, 0xf7, 0xf7)
564 COLOR(MozMacVibrantTitlebarDark, 0x28, 0x28, 0x28)
565 COLOR(MozMacMenupopup, 0xe6, 0xe6, 0xe6)
566 COLOR(MozMacMenuitem, 0xe6, 0xe6, 0xe6)
567 COLOR(MozMacActiveMenuitem, 0x0a, 0x64, 0xdc)
568 COLOR(MozMacSourceList, 0xf7, 0xf7, 0xf7)
569 COLOR(MozMacSourceListSelection, 0xc8, 0xc8, 0xc8)
570 COLOR(MozMacActiveSourceListSelection, 0x0a, 0x64, 0xdc)
571 COLOR(MozMacTooltip, 0xf7, 0xf7, 0xf7)
572 // Seems to be the default color (hardcoded because of bug 1065998)
573 COLOR(MozWinMediatext, 0xFF, 0xFF, 0xFF)
574 COLOR(MozWinCommunicationstext, 0xFF, 0xFF, 0xFF)
575 COLOR(MozNativehyperlinktext, 0x00, 0x66, 0xCC)
576 COLOR(MozComboboxtext, 0x00, 0x00, 0x00)
577 COLOR(MozCombobox, 0xFF, 0xFF, 0xFF)
578 default:
579 break;
581 return NS_RGB(0xFF, 0xFF, 0xFF);
584 // Uncomment the #define below if you want to debug system color use in a skin
585 // that uses them. When set, it will make all system color pairs that are
586 // appropriate for foreground/background pairing the same. This means if the
587 // skin is using system colors correctly you will not be able to see *any* text.
589 // #define DEBUG_SYSTEM_COLOR_USE
591 #ifdef DEBUG_SYSTEM_COLOR_USE
592 static nsresult SystemColorUseDebuggingColor(LookAndFeel::ColorID aID,
593 nscolor& aResult) {
594 using ColorID = LookAndFeel::ColorID;
596 switch (aID) {
597 // css2 http://www.w3.org/TR/REC-CSS2/ui.html#system-colors
598 case ColorID::Activecaption:
599 // active window caption background
600 case ColorID::Captiontext:
601 // text in active window caption
602 aResult = NS_RGB(0xff, 0x00, 0x00);
603 break;
605 case ColorID::Highlight:
606 // background of selected item
607 case ColorID::Highlighttext:
608 // text of selected item
609 aResult = NS_RGB(0xff, 0xff, 0x00);
610 break;
612 case ColorID::Inactivecaption:
613 // inactive window caption
614 case ColorID::Inactivecaptiontext:
615 // text in inactive window caption
616 aResult = NS_RGB(0x66, 0x66, 0x00);
617 break;
619 case ColorID::Infobackground:
620 // tooltip background color
621 case ColorID::Infotext:
622 // tooltip text color
623 aResult = NS_RGB(0x00, 0xff, 0x00);
624 break;
626 case ColorID::Menu:
627 // menu background
628 case ColorID::Menutext:
629 // menu text
630 aResult = NS_RGB(0x00, 0xff, 0xff);
631 break;
633 case ColorID::Threedface:
634 case ColorID::Buttonface:
635 // 3-D face color
636 case ColorID::Buttontext:
637 // text on push buttons
638 aResult = NS_RGB(0x00, 0x66, 0x66);
639 break;
641 case ColorID::Window:
642 case ColorID::Windowtext:
643 aResult = NS_RGB(0x00, 0x00, 0xff);
644 break;
646 // from the CSS3 working draft (not yet finalized)
647 // http://www.w3.org/tr/2000/wd-css3-userint-20000216.html#color
649 case ColorID::Field:
650 case ColorID::Fieldtext:
651 aResult = NS_RGB(0xff, 0x00, 0xff);
652 break;
654 case ColorID::MozDialog:
655 case ColorID::MozDialogtext:
656 aResult = NS_RGB(0x66, 0x00, 0x66);
657 break;
659 default:
660 return NS_ERROR_NOT_AVAILABLE;
663 return NS_OK;
665 #endif
667 static nsresult GetColorFromPref(LookAndFeel::ColorID aID, nscolor& aResult) {
668 const char* prefName = sColorPrefs[size_t(aID)];
669 nsAutoCString colorStr;
670 MOZ_TRY(Preferences::GetCString(prefName, colorStr));
671 if (!ServoCSSParser::ComputeColor(nullptr, NS_RGB(0, 0, 0), colorStr,
672 &aResult)) {
673 return NS_ERROR_FAILURE;
675 return NS_OK;
678 // All these routines will return NS_OK if they have a value,
679 // in which case the nsLookAndFeel should use that value;
680 // otherwise we'll return NS_ERROR_NOT_AVAILABLE, in which case, the
681 // platform-specific nsLookAndFeel should use its own values instead.
682 nsresult nsXPLookAndFeel::GetColorValue(ColorID aID, ColorScheme aScheme,
683 UseStandins aUseStandins,
684 nscolor& aResult) {
685 if (!sInitialized) {
686 Init();
689 #ifdef DEBUG_SYSTEM_COLOR_USE
690 if (NS_SUCCEEDED(SystemColorUseDebuggingColor(aID, aResult))) {
691 return NS_OK;
693 #endif
695 if (aUseStandins == UseStandins::Yes) {
696 aResult = GetStandinForNativeColor(aID);
697 return NS_OK;
700 auto& cache =
701 aScheme == ColorScheme::Light ? sLightColorCache : sDarkColorCache;
702 if (const auto* cached = cache.Get(aID)) {
703 if (cached->isNothing()) {
704 return NS_ERROR_FAILURE;
706 aResult = cached->value();
707 return NS_OK;
710 if (NS_SUCCEEDED(GetColorFromPref(aID, aResult))) {
711 cache.Insert(aID, Some(aResult));
712 return NS_OK;
715 if (NS_SUCCEEDED(NativeGetColor(aID, aScheme, aResult))) {
716 if (gfxPlatform::GetCMSMode() == CMSMode::All &&
717 !IsSpecialColor(aID, aResult)) {
718 qcms_transform* transform = gfxPlatform::GetCMSInverseRGBTransform();
719 if (transform) {
720 uint8_t color[4];
721 color[0] = NS_GET_R(aResult);
722 color[1] = NS_GET_G(aResult);
723 color[2] = NS_GET_B(aResult);
724 color[3] = NS_GET_A(aResult);
725 qcms_transform_data(transform, color, color, 1);
726 aResult = NS_RGBA(color[0], color[1], color[2], color[3]);
730 // NOTE: Servo holds a lock and the main thread is paused, so writing to the
731 // global cache here is fine.
732 cache.Insert(aID, Some(aResult));
733 return NS_OK;
736 cache.Insert(aID, Nothing());
737 return NS_ERROR_FAILURE;
740 nsresult nsXPLookAndFeel::GetIntValue(IntID aID, int32_t& aResult) {
741 if (!sInitialized) {
742 Init();
745 if (const auto* cached = sIntCache.Get(aID)) {
746 if (cached->isNothing()) {
747 return NS_ERROR_FAILURE;
749 aResult = cached->value();
750 return NS_OK;
753 if (NS_SUCCEEDED(Preferences::GetInt(sIntPrefs[size_t(aID)], &aResult))) {
754 sIntCache.Insert(aID, Some(aResult));
755 return NS_OK;
758 if (NS_FAILED(NativeGetInt(aID, aResult))) {
759 sIntCache.Insert(aID, Nothing());
760 return NS_ERROR_FAILURE;
763 sIntCache.Insert(aID, Some(aResult));
764 return NS_OK;
767 nsresult nsXPLookAndFeel::GetFloatValue(FloatID aID, float& aResult) {
768 if (!sInitialized) {
769 Init();
772 if (const auto* cached = sFloatCache.Get(aID)) {
773 if (cached->isNothing()) {
774 return NS_ERROR_FAILURE;
776 aResult = cached->value();
777 return NS_OK;
780 int32_t pref = 0;
781 if (NS_SUCCEEDED(Preferences::GetInt(sFloatPrefs[size_t(aID)], &pref))) {
782 aResult = float(pref) / 100.0f;
783 sFloatCache.Insert(aID, Some(aResult));
784 return NS_OK;
787 if (NS_FAILED(NativeGetFloat(aID, aResult))) {
788 sFloatCache.Insert(aID, Nothing());
789 return NS_ERROR_FAILURE;
792 sFloatCache.Insert(aID, Some(aResult));
793 return NS_OK;
796 bool nsXPLookAndFeel::LookAndFeelFontToStyle(const LookAndFeelFont& aFont,
797 nsString& aName,
798 gfxFontStyle& aStyle) {
799 if (!aFont.haveFont()) {
800 return false;
802 aName = aFont.name();
803 aStyle = gfxFontStyle();
804 aStyle.size = aFont.size();
805 aStyle.weight = FontWeight(aFont.weight());
806 aStyle.style =
807 aFont.italic() ? FontSlantStyle::Italic() : FontSlantStyle::Normal();
808 aStyle.systemFont = true;
809 return true;
812 widget::LookAndFeelFont nsXPLookAndFeel::StyleToLookAndFeelFont(
813 const nsAString& aName, const gfxFontStyle& aStyle) {
814 LookAndFeelFont font;
815 font.haveFont() = true;
816 font.name() = aName;
817 font.size() = aStyle.size;
818 font.weight() = aStyle.weight.ToFloat();
819 font.italic() = aStyle.style.IsItalic();
820 MOZ_ASSERT(aStyle.style.IsNormal() || aStyle.style.IsItalic(),
821 "Cannot handle oblique font style");
822 #ifdef DEBUG
824 // Assert that all the remaining font style properties have their
825 // default values.
826 gfxFontStyle candidate = aStyle;
827 gfxFontStyle defaults{};
828 candidate.size = defaults.size;
829 candidate.weight = defaults.weight;
830 candidate.style = defaults.style;
831 MOZ_ASSERT(candidate.Equals(defaults),
832 "Some font style properties not supported");
834 #endif
835 return font;
838 bool nsXPLookAndFeel::GetFontValue(FontID aID, nsString& aName,
839 gfxFontStyle& aStyle) {
840 if (const LookAndFeelFont* cached = sFontCache.Get(aID)) {
841 return LookAndFeelFontToStyle(*cached, aName, aStyle);
843 LookAndFeelFont font;
844 const bool haveFont = NativeGetFont(aID, aName, aStyle);
845 font.haveFont() = haveFont;
846 if (haveFont) {
847 font = StyleToLookAndFeelFont(aName, aStyle);
849 sFontCache.Insert(aID, std::move(font));
850 return haveFont;
853 void nsXPLookAndFeel::RefreshImpl() {
854 // Wipe out our caches.
855 sLightColorCache.Clear();
856 sDarkColorCache.Clear();
857 sFontCache.Clear();
858 sFloatCache.Clear();
859 sIntCache.Clear();
861 // Clear any cached FullLookAndFeel data, which is now invalid.
862 if (XRE_IsParentProcess()) {
863 widget::RemoteLookAndFeel::ClearCachedData();
867 static bool sRecordedLookAndFeelTelemetry = false;
869 void nsXPLookAndFeel::RecordTelemetry() {
870 if (!XRE_IsParentProcess()) {
871 return;
874 if (sRecordedLookAndFeelTelemetry) {
875 return;
878 sRecordedLookAndFeelTelemetry = true;
880 int32_t i;
881 Telemetry::ScalarSet(
882 Telemetry::ScalarID::WIDGET_DARK_MODE,
883 NS_SUCCEEDED(GetIntValue(IntID::SystemUsesDarkTheme, i)) && i != 0);
885 RecordLookAndFeelSpecificTelemetry();
888 namespace mozilla {
890 // static
891 void LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind aKind) {
892 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
893 const char16_t kind[] = {char16_t(aKind), 0};
894 obs->NotifyObservers(nullptr, "look-and-feel-changed", kind);
898 static bool ShouldUseStandinsForNativeColorForNonNativeTheme(
899 const dom::Document& aDoc, LookAndFeel::ColorID aColor) {
900 using ColorID = LookAndFeel::ColorID;
901 if (!aDoc.ShouldAvoidNativeTheme()) {
902 return false;
905 // The native theme doesn't use system colors backgrounds etc, except when in
906 // high-contrast mode, so spoof some of the colors with stand-ins to prevent
907 // lack of contrast.
908 switch (aColor) {
909 case ColorID::Buttonface:
910 case ColorID::Buttontext:
911 case ColorID::MozButtonhoverface:
912 case ColorID::MozButtonhovertext:
913 case ColorID::MozGtkButtonactivetext:
915 case ColorID::MozCombobox:
916 case ColorID::MozComboboxtext:
918 case ColorID::Field:
919 case ColorID::Fieldtext:
921 case ColorID::Graytext:
923 return !PreferenceSheet::PrefsFor(aDoc)
924 .NonNativeThemeShouldUseSystemColors();
926 default:
927 break;
930 return false;
933 static bool ShouldRespectSystemColorSchemeForChromeDoc() {
934 #ifdef XP_MACOSX
935 // macOS follows the global toolbar theme, not the system theme.
936 // (If the global toolbar theme is set to System, then it *that* follows the
937 // system theme.)
938 return false;
939 #else
940 // GTK historically has behaved like this. Other platforms don't have support
941 // for light / dark color schemes yet so it doesn't matter for them.
942 return true;
943 #endif
946 static bool ShouldRespectGlobalToolbarThemeAppearanceForChromeDoc() {
947 #ifdef XP_MACOSX
948 // Need to be consistent with AppearanceOverride.mm on macOS, which respects
949 // the browser.theme.toolbar-theme pref.
950 // However, if widget.macos.support-dark-appearance is false, we need to
951 // pretend everything's Light and not follow the toolbar theme.
952 return StaticPrefs::widget_macos_support_dark_appearance();
953 #elif defined(MOZ_WIDGET_GTK)
954 return StaticPrefs::widget_gtk_follow_firefox_theme();
955 #else
956 return false;
957 #endif
960 LookAndFeel::ColorScheme LookAndFeel::ColorSchemeForDocument(
961 const dom::Document& aDoc) {
962 if (nsContentUtils::IsChromeDoc(&aDoc)) {
963 if (ShouldRespectGlobalToolbarThemeAppearanceForChromeDoc()) {
964 switch (StaticPrefs::browser_theme_toolbar_theme()) {
965 case 0: // Dark
966 return ColorScheme::Dark;
967 case 1: // Light
968 return ColorScheme::Light;
969 case 2: // System
970 return SystemColorScheme();
971 default:
972 break;
975 if (ShouldRespectSystemColorSchemeForChromeDoc()) {
976 return SystemColorScheme();
979 return LookAndFeel::ColorScheme::Light;
982 // static
983 Maybe<nscolor> LookAndFeel::GetColor(ColorID aId, ColorScheme aScheme,
984 UseStandins aUseStandins) {
985 nscolor result;
986 nsresult rv = nsLookAndFeel::GetInstance()->GetColorValue(
987 aId, aScheme, aUseStandins, result);
988 if (NS_FAILED(rv)) {
989 return Nothing();
991 return Some(result);
994 // Returns whether there is a CSS color name for this color.
995 static bool ColorIsCSSAccessible(LookAndFeel::ColorID aId) {
996 using ColorID = LookAndFeel::ColorID;
998 switch (aId) {
999 case ColorID::WindowBackground:
1000 case ColorID::WindowForeground:
1001 case ColorID::WidgetBackground:
1002 case ColorID::WidgetForeground:
1003 case ColorID::WidgetSelectBackground:
1004 case ColorID::WidgetSelectForeground:
1005 case ColorID::Widget3DHighlight:
1006 case ColorID::Widget3DShadow:
1007 case ColorID::TextBackground:
1008 case ColorID::TextForeground:
1009 case ColorID::TextSelectBackground:
1010 case ColorID::TextSelectForeground:
1011 case ColorID::TextSelectBackgroundDisabled:
1012 case ColorID::TextSelectBackgroundAttention:
1013 case ColorID::TextHighlightBackground:
1014 case ColorID::TextHighlightForeground:
1015 case ColorID::IMERawInputBackground:
1016 case ColorID::IMERawInputForeground:
1017 case ColorID::IMERawInputUnderline:
1018 case ColorID::IMESelectedRawTextBackground:
1019 case ColorID::IMESelectedRawTextForeground:
1020 case ColorID::IMESelectedRawTextUnderline:
1021 case ColorID::IMEConvertedTextBackground:
1022 case ColorID::IMEConvertedTextForeground:
1023 case ColorID::IMEConvertedTextUnderline:
1024 case ColorID::IMESelectedConvertedTextBackground:
1025 case ColorID::IMESelectedConvertedTextForeground:
1026 case ColorID::IMESelectedConvertedTextUnderline:
1027 case ColorID::SpellCheckerUnderline:
1028 return false;
1029 default:
1030 break;
1033 return true;
1036 LookAndFeel::UseStandins LookAndFeel::ShouldUseStandins(
1037 const dom::Document& aDoc, ColorID aId) {
1038 return UseStandins(
1039 ShouldUseStandinsForNativeColorForNonNativeTheme(aDoc, aId) ||
1040 (nsContentUtils::UseStandinsForNativeColors() &&
1041 !nsContentUtils::IsChromeDoc(&aDoc) && ColorIsCSSAccessible(aId)));
1044 Maybe<nscolor> LookAndFeel::GetColor(ColorID aId, const dom::Document& aDoc) {
1045 return GetColor(aId, ColorSchemeForDocument(aDoc),
1046 ShouldUseStandins(aDoc, aId));
1049 Maybe<nscolor> LookAndFeel::GetColor(ColorID aId, const nsIFrame* aFrame) {
1050 return GetColor(aId, *aFrame->PresContext()->Document());
1053 // static
1054 nsresult LookAndFeel::GetInt(IntID aID, int32_t* aResult) {
1055 return nsLookAndFeel::GetInstance()->GetIntValue(aID, *aResult);
1058 // static
1059 nsresult LookAndFeel::GetFloat(FloatID aID, float* aResult) {
1060 return nsLookAndFeel::GetInstance()->GetFloatValue(aID, *aResult);
1063 // static
1064 bool LookAndFeel::GetFont(FontID aID, nsString& aName, gfxFontStyle& aStyle) {
1065 return nsLookAndFeel::GetInstance()->GetFontValue(aID, aName, aStyle);
1068 // static
1069 char16_t LookAndFeel::GetPasswordCharacter() {
1070 return nsLookAndFeel::GetInstance()->GetPasswordCharacterImpl();
1073 // static
1074 bool LookAndFeel::GetEchoPassword() {
1075 if (StaticPrefs::editor_password_mask_delay() >= 0) {
1076 return StaticPrefs::editor_password_mask_delay() > 0;
1078 return nsLookAndFeel::GetInstance()->GetEchoPasswordImpl();
1081 // static
1082 uint32_t LookAndFeel::GetPasswordMaskDelay() {
1083 int32_t delay = StaticPrefs::editor_password_mask_delay();
1084 if (delay < 0) {
1085 return nsLookAndFeel::GetInstance()->GetPasswordMaskDelayImpl();
1087 return delay;
1090 // static
1091 void LookAndFeel::Refresh() {
1092 nsLookAndFeel::GetInstance()->RefreshImpl();
1093 nsNativeBasicTheme::LookAndFeelChanged();
1096 // static
1097 void LookAndFeel::NativeInit() { nsLookAndFeel::GetInstance()->NativeInit(); }
1099 // static
1100 void LookAndFeel::SetData(widget::FullLookAndFeel&& aTables) {
1101 nsLookAndFeel::GetInstance()->SetDataImpl(std::move(aTables));
1104 } // namespace mozilla