Bug 1726647 [wpt PR 30094] - Ignore exceptions as some command is not implemented...
[gecko.git] / widget / nsXPLookAndFeel.cpp
blob4d2714ff02de4b9092c705770e8bfbd4b1cb95ef
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[][43] = {
112 "ui.caretBlinkTime",
113 "ui.caretBlinkCount",
114 "ui.caretWidth",
115 "ui.caretVisibleWithSelection",
116 "ui.selectTextfieldsOnKeyFocus",
117 "ui.submenuDelay",
118 "ui.menusCanOverlapOSBar",
119 "ui.useOverlayScrollbars",
120 "ui.allowOverlayScrollbarsOverlap",
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",
178 "ui.touchDeviceSupportPresent",
181 static_assert(ArrayLength(sIntPrefs) == size_t(LookAndFeel::IntID::End),
182 "Should have a pref for each int value");
184 // This array MUST be kept in the same order as the float id list in
185 // LookAndFeel.h
186 static const char sFloatPrefs[][37] = {
187 "ui.IMEUnderlineRelativeSize",
188 "ui.SpellCheckerUnderlineRelativeSize",
189 "ui.caretAspectRatio",
190 "ui.textScaleFactor",
193 static_assert(ArrayLength(sFloatPrefs) == size_t(LookAndFeel::FloatID::End),
194 "Should have a pref for each float value");
196 // This array MUST be kept in the same order as the color list in
197 // specified/color.rs
198 static const char sColorPrefs[][41] = {
199 "ui.windowBackground",
200 "ui.windowForeground",
201 "ui.widgetBackground",
202 "ui.widgetForeground",
203 "ui.widgetSelectBackground",
204 "ui.widgetSelectForeground",
205 "ui.widget3DHighlight",
206 "ui.widget3DShadow",
207 "ui.textBackground",
208 "ui.textForeground",
209 "ui.textSelectBackground",
210 "ui.textSelectForeground",
211 "ui.textSelectBackgroundDisabled",
212 "ui.textSelectBackgroundAttention",
213 "ui.textHighlightBackground",
214 "ui.textHighlightForeground",
215 "ui.IMERawInputBackground",
216 "ui.IMERawInputForeground",
217 "ui.IMERawInputUnderline",
218 "ui.IMESelectedRawTextBackground",
219 "ui.IMESelectedRawTextForeground",
220 "ui.IMESelectedRawTextUnderline",
221 "ui.IMEConvertedTextBackground",
222 "ui.IMEConvertedTextForeground",
223 "ui.IMEConvertedTextUnderline",
224 "ui.IMESelectedConvertedTextBackground",
225 "ui.IMESelectedConvertedTextForeground",
226 "ui.IMESelectedConvertedTextUnderline",
227 "ui.SpellCheckerUnderline",
228 "ui.themedScrollbar",
229 "ui.themedScrollbarInactive",
230 "ui.themedScrollbarThumb",
231 "ui.themedScrollbarThumbHover",
232 "ui.themedScrollbarThumbActive",
233 "ui.themedScrollbarThumbInactive",
234 "ui.activeborder",
235 "ui.activecaption",
236 "ui.appworkspace",
237 "ui.background",
238 "ui.buttonface",
239 "ui.buttonhighlight",
240 "ui.buttonshadow",
241 "ui.buttontext",
242 "ui.captiontext",
243 "ui.-moz-field",
244 "ui.-moz-fieldtext",
245 "ui.graytext",
246 "ui.highlight",
247 "ui.highlighttext",
248 "ui.inactiveborder",
249 "ui.inactivecaption",
250 "ui.inactivecaptiontext",
251 "ui.infobackground",
252 "ui.infotext",
253 "ui.menu",
254 "ui.menutext",
255 "ui.scrollbar",
256 "ui.threeddarkshadow",
257 "ui.threedface",
258 "ui.threedhighlight",
259 "ui.threedlightshadow",
260 "ui.threedshadow",
261 "ui.window",
262 "ui.windowframe",
263 "ui.windowtext",
264 "ui.-moz-buttondefault",
265 "ui.-moz-default-color",
266 "ui.-moz-default-background-color",
267 "ui.-moz-dialog",
268 "ui.-moz-dialogtext",
269 "ui.-moz-dragtargetzone",
270 "ui.-moz-cellhighlight",
271 "ui.-moz_cellhighlighttext",
272 "ui.-moz-html-cellhighlight",
273 "ui.-moz-html-cellhighlighttext",
274 "ui.-moz-buttonhoverface",
275 "ui.-moz_buttonhovertext",
276 "ui.-moz_menuhover",
277 "ui.-moz_menuhovertext",
278 "ui.-moz_menubartext",
279 "ui.-moz_menubarhovertext",
280 "ui.-moz_eventreerow",
281 "ui.-moz_oddtreerow",
282 "ui.-moz-gtk-buttonactivetext",
283 "ui.-moz-mac-buttonactivetext",
284 "ui.-moz_mac_chrome_active",
285 "ui.-moz_mac_chrome_inactive",
286 "ui.-moz-mac-defaultbuttontext",
287 "ui.-moz-mac-focusring",
288 "ui.-moz-mac-menuselect",
289 "ui.-moz-mac-menushadow",
290 "ui.-moz-mac-menutextdisable",
291 "ui.-moz-mac-menutextselect",
292 "ui.-moz_mac_disabledtoolbartext",
293 "ui.-moz-mac-secondaryhighlight",
294 "ui.-moz-mac-vibrant-titlebar-light",
295 "ui.-moz-mac-vibrant-titlebar-dark",
296 "ui.-moz-mac-menupopup",
297 "ui.-moz-mac-menuitem",
298 "ui.-moz-mac-active-menuitem",
299 "ui.-moz-mac-source-list",
300 "ui.-moz-mac-source-list-selection",
301 "ui.-moz-mac-active-source-list-selection",
302 "ui.-moz-mac-tooltip",
303 "ui.-moz-accent-color",
304 "ui.-moz-accent-color-foreground",
305 "ui.-moz-win-mediatext",
306 "ui.-moz-win-communicationstext",
307 "ui.-moz-nativehyperlinktext",
308 "ui.-moz-nativevisitedhyperlinktext",
309 "ui.-moz-hyperlinktext",
310 "ui.-moz-activehyperlinktext",
311 "ui.-moz-visitedhyperlinktext",
312 "ui.-moz-comboboxtext",
313 "ui.-moz-combobox",
314 "ui.-moz-colheadertext",
315 "ui.-moz-colheaderhovertext",
316 "ui.-moz-gtk-titlebar-text",
317 "ui.-moz-gtk-titlebar-inactive-text",
320 static_assert(ArrayLength(sColorPrefs) == size_t(LookAndFeel::ColorID::End),
321 "Should have a pref for each color value");
323 const char* nsXPLookAndFeel::GetColorPrefName(ColorID aId) {
324 return sColorPrefs[size_t(aId)];
327 bool nsXPLookAndFeel::sInitialized = false;
329 nsXPLookAndFeel* nsXPLookAndFeel::sInstance = nullptr;
330 bool nsXPLookAndFeel::sShutdown = false;
332 // static
333 nsXPLookAndFeel* nsXPLookAndFeel::GetInstance() {
334 if (sInstance) {
335 return sInstance;
338 NS_ENSURE_TRUE(!sShutdown, nullptr);
340 // If we're in a content process, then the parent process will have supplied
341 // us with an initial FullLookAndFeel object.
342 // We grab this data from the ContentChild,
343 // where it's been temporarily stashed, and initialize our new LookAndFeel
344 // object with it.
346 FullLookAndFeel* lnf = nullptr;
348 if (auto* cc = mozilla::dom::ContentChild::GetSingleton()) {
349 lnf = &cc->BorrowLookAndFeelData();
352 if (lnf) {
353 sInstance = new widget::RemoteLookAndFeel(std::move(*lnf));
354 } else if (gfxPlatform::IsHeadless()) {
355 sInstance = new widget::HeadlessLookAndFeel();
356 } else {
357 sInstance = new nsLookAndFeel();
360 // This is only ever used once during initialization, and can be cleared now.
361 if (lnf) {
362 *lnf = {};
365 nsNativeBasicTheme::Init();
366 return sInstance;
369 // static
370 void nsXPLookAndFeel::Shutdown() {
371 if (sShutdown) {
372 return;
375 sShutdown = true;
376 delete sInstance;
377 sInstance = nullptr;
379 // This keeps strings alive, so need to clear to make leak checking happy.
380 sFontCache.Clear();
382 nsNativeBasicTheme::Shutdown();
385 static void IntPrefChanged() {
386 // Int prefs can't change our system colors or fonts.
387 LookAndFeel::NotifyChangedAllWindows(
388 widget::ThemeChangeKind::MediaQueriesOnly);
391 static void FloatPrefChanged() {
392 // Float prefs can't change our system colors or fonts.
393 LookAndFeel::NotifyChangedAllWindows(
394 widget::ThemeChangeKind::MediaQueriesOnly);
397 static void ColorPrefChanged() {
398 // Color prefs affect style, because they by definition change system colors.
399 LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind::Style);
402 // static
403 void nsXPLookAndFeel::OnPrefChanged(const char* aPref, void* aClosure) {
404 nsDependentCString prefName(aPref);
405 for (const char* pref : sIntPrefs) {
406 if (prefName.Equals(pref)) {
407 IntPrefChanged();
408 return;
412 for (const char* pref : sFloatPrefs) {
413 if (prefName.Equals(pref)) {
414 FloatPrefChanged();
415 return;
419 for (const char* pref : sColorPrefs) {
420 if (prefName.Equals(pref)) {
421 ColorPrefChanged();
422 return;
427 static constexpr struct {
428 nsLiteralCString mName;
429 widget::ThemeChangeKind mChangeKind =
430 widget::ThemeChangeKind::MediaQueriesOnly;
431 } kMediaQueryPrefs[] = {
432 {"browser.display.windows.native_menus"_ns},
433 {"browser.proton.enabled"_ns},
434 {"browser.proton.places-tooltip.enabled"_ns},
435 // This affects not only the media query, but also the native theme, so we
436 // need to re-layout.
437 {"browser.theme.toolbar-theme"_ns, widget::ThemeChangeKind::AllBits},
440 // Read values from the user's preferences.
441 // This is done once at startup, but since the user's preferences
442 // haven't actually been read yet at that time, we also have to
443 // set a callback to inform us of changes to each pref.
444 void nsXPLookAndFeel::Init() {
445 MOZ_RELEASE_ASSERT(NS_IsMainThread());
447 // Say we're already initialized, and take the chance that it might fail;
448 // protects against some other process writing to our static variables.
449 sInitialized = true;
451 // XXX If we could reorganize the pref names, we should separate the branch
452 // for each types. Then, we could reduce the unnecessary loop from
453 // nsXPLookAndFeel::OnPrefChanged().
454 Preferences::RegisterPrefixCallback(OnPrefChanged, "ui.");
455 // We really do just want the accessibility.tabfocus pref, not other prefs
456 // that start with that string.
457 Preferences::RegisterCallback(OnPrefChanged, "accessibility.tabfocus");
459 for (auto& pref : kMediaQueryPrefs) {
460 Preferences::RegisterCallback(
461 [](const char*, void* aChangeKind) {
462 auto changeKind =
463 widget::ThemeChangeKind(reinterpret_cast<uintptr_t>(aChangeKind));
464 LookAndFeel::NotifyChangedAllWindows(changeKind);
466 pref.mName, reinterpret_cast<void*>(uintptr_t(pref.mChangeKind)));
470 nsXPLookAndFeel::~nsXPLookAndFeel() {
471 NS_ASSERTION(sInstance == this,
472 "This destroying instance isn't the singleton instance");
473 sInstance = nullptr;
476 static bool IsSpecialColor(LookAndFeel::ColorID aID, nscolor aColor) {
477 using ColorID = LookAndFeel::ColorID;
479 switch (aID) {
480 case ColorID::TextSelectForeground:
481 case ColorID::IMESelectedRawTextBackground:
482 case ColorID::IMESelectedConvertedTextBackground:
483 case ColorID::IMERawInputBackground:
484 case ColorID::IMEConvertedTextBackground:
485 case ColorID::IMESelectedRawTextForeground:
486 case ColorID::IMESelectedConvertedTextForeground:
487 case ColorID::IMERawInputForeground:
488 case ColorID::IMEConvertedTextForeground:
489 case ColorID::IMERawInputUnderline:
490 case ColorID::IMEConvertedTextUnderline:
491 case ColorID::IMESelectedRawTextUnderline:
492 case ColorID::IMESelectedConvertedTextUnderline:
493 case ColorID::SpellCheckerUnderline:
494 return NS_IS_SELECTION_SPECIAL_COLOR(aColor);
495 default:
496 break;
499 * In GetColor(), every color that is not a special color is color
500 * corrected. Use false to make other colors color corrected.
502 return false;
505 nscolor nsXPLookAndFeel::GetStandinForNativeColor(ColorID aID) {
506 // The stand-in colors are taken from the Windows 7 Aero theme
507 // except Mac-specific colors which are taken from Mac OS 10.7.
509 #define COLOR(name_, r, g, b) \
510 case ColorID::name_: \
511 return NS_RGB(r, g, b);
513 switch (aID) {
514 // CSS 2 colors:
515 COLOR(Activeborder, 0xB4, 0xB4, 0xB4)
516 COLOR(Activecaption, 0x99, 0xB4, 0xD1)
517 COLOR(Appworkspace, 0xAB, 0xAB, 0xAB)
518 COLOR(Background, 0x00, 0x00, 0x00)
519 COLOR(Buttonface, 0xF0, 0xF0, 0xF0)
520 COLOR(Buttonhighlight, 0xFF, 0xFF, 0xFF)
521 COLOR(Buttonshadow, 0xA0, 0xA0, 0xA0)
522 COLOR(Buttontext, 0x00, 0x00, 0x00)
523 COLOR(Captiontext, 0x00, 0x00, 0x00)
524 COLOR(Graytext, 0x6D, 0x6D, 0x6D)
525 COLOR(Highlight, 0x33, 0x99, 0xFF)
526 COLOR(Highlighttext, 0xFF, 0xFF, 0xFF)
527 COLOR(Inactiveborder, 0xF4, 0xF7, 0xFC)
528 COLOR(Inactivecaption, 0xBF, 0xCD, 0xDB)
529 COLOR(Inactivecaptiontext, 0x43, 0x4E, 0x54)
530 COLOR(Infobackground, 0xFF, 0xFF, 0xE1)
531 COLOR(Infotext, 0x00, 0x00, 0x00)
532 COLOR(Menu, 0xF0, 0xF0, 0xF0)
533 COLOR(Menutext, 0x00, 0x00, 0x00)
534 COLOR(Scrollbar, 0xC8, 0xC8, 0xC8)
535 COLOR(Threeddarkshadow, 0x69, 0x69, 0x69)
536 COLOR(Threedface, 0xF0, 0xF0, 0xF0)
537 COLOR(Threedhighlight, 0xFF, 0xFF, 0xFF)
538 COLOR(Threedlightshadow, 0xE3, 0xE3, 0xE3)
539 COLOR(Threedshadow, 0xA0, 0xA0, 0xA0)
540 COLOR(Window, 0xFF, 0xFF, 0xFF)
541 COLOR(Windowframe, 0x64, 0x64, 0x64)
542 COLOR(Windowtext, 0x00, 0x00, 0x00)
543 COLOR(MozButtondefault, 0x69, 0x69, 0x69)
544 COLOR(Field, 0xFF, 0xFF, 0xFF)
545 COLOR(Fieldtext, 0x00, 0x00, 0x00)
546 COLOR(MozDialog, 0xF0, 0xF0, 0xF0)
547 COLOR(MozDialogtext, 0x00, 0x00, 0x00)
548 COLOR(MozColheadertext, 0x00, 0x00, 0x00)
549 COLOR(MozColheaderhovertext, 0x00, 0x00, 0x00)
550 COLOR(MozDragtargetzone, 0xFF, 0xFF, 0xFF)
551 COLOR(MozCellhighlight, 0xF0, 0xF0, 0xF0)
552 COLOR(MozCellhighlighttext, 0x00, 0x00, 0x00)
553 COLOR(MozHtmlCellhighlight, 0x33, 0x99, 0xFF)
554 COLOR(MozHtmlCellhighlighttext, 0xFF, 0xFF, 0xFF)
555 COLOR(MozButtonhoverface, 0xF0, 0xF0, 0xF0)
556 COLOR(MozGtkButtonactivetext, 0x00, 0x00, 0x00)
557 COLOR(MozButtonhovertext, 0x00, 0x00, 0x00)
558 COLOR(MozMenuhover, 0x33, 0x99, 0xFF)
559 COLOR(MozMenuhovertext, 0x00, 0x00, 0x00)
560 COLOR(MozMenubartext, 0x00, 0x00, 0x00)
561 COLOR(MozMenubarhovertext, 0x00, 0x00, 0x00)
562 COLOR(MozOddtreerow, 0xFF, 0xFF, 0xFF)
563 COLOR(MozMacChromeActive, 0xB2, 0xB2, 0xB2)
564 COLOR(MozMacChromeInactive, 0xE1, 0xE1, 0xE1)
565 COLOR(MozMacFocusring, 0x60, 0x9D, 0xD7)
566 COLOR(MozMacMenuselect, 0x38, 0x75, 0xD7)
567 COLOR(MozMacMenushadow, 0xA3, 0xA3, 0xA3)
568 COLOR(MozMacMenutextdisable, 0x88, 0x88, 0x88)
569 COLOR(MozMacMenutextselect, 0xFF, 0xFF, 0xFF)
570 COLOR(MozMacDisabledtoolbartext, 0x3F, 0x3F, 0x3F)
571 COLOR(MozMacSecondaryhighlight, 0xD4, 0xD4, 0xD4)
572 COLOR(MozMacVibrantTitlebarLight, 0xf7, 0xf7, 0xf7)
573 COLOR(MozMacVibrantTitlebarDark, 0x28, 0x28, 0x28)
574 COLOR(MozMacMenupopup, 0xe6, 0xe6, 0xe6)
575 COLOR(MozMacMenuitem, 0xe6, 0xe6, 0xe6)
576 COLOR(MozMacActiveMenuitem, 0x0a, 0x64, 0xdc)
577 COLOR(MozMacSourceList, 0xf7, 0xf7, 0xf7)
578 COLOR(MozMacSourceListSelection, 0xc8, 0xc8, 0xc8)
579 COLOR(MozMacActiveSourceListSelection, 0x0a, 0x64, 0xdc)
580 COLOR(MozMacTooltip, 0xf7, 0xf7, 0xf7)
581 // Seems to be the default color (hardcoded because of bug 1065998)
582 COLOR(MozWinMediatext, 0xFF, 0xFF, 0xFF)
583 COLOR(MozWinCommunicationstext, 0xFF, 0xFF, 0xFF)
584 COLOR(MozNativehyperlinktext, 0x00, 0x66, 0xCC)
585 COLOR(MozNativevisitedhyperlinktext, 0x55, 0x1A, 0x8B)
586 COLOR(MozComboboxtext, 0x00, 0x00, 0x00)
587 COLOR(MozCombobox, 0xFF, 0xFF, 0xFF)
588 default:
589 break;
591 return NS_RGB(0xFF, 0xFF, 0xFF);
594 // Uncomment the #define below if you want to debug system color use in a skin
595 // that uses them. When set, it will make all system color pairs that are
596 // appropriate for foreground/background pairing the same. This means if the
597 // skin is using system colors correctly you will not be able to see *any* text.
599 // #define DEBUG_SYSTEM_COLOR_USE
601 #ifdef DEBUG_SYSTEM_COLOR_USE
602 static nsresult SystemColorUseDebuggingColor(LookAndFeel::ColorID aID,
603 nscolor& aResult) {
604 using ColorID = LookAndFeel::ColorID;
606 switch (aID) {
607 // css2 http://www.w3.org/TR/REC-CSS2/ui.html#system-colors
608 case ColorID::Activecaption:
609 // active window caption background
610 case ColorID::Captiontext:
611 // text in active window caption
612 aResult = NS_RGB(0xff, 0x00, 0x00);
613 break;
615 case ColorID::Highlight:
616 // background of selected item
617 case ColorID::Highlighttext:
618 // text of selected item
619 aResult = NS_RGB(0xff, 0xff, 0x00);
620 break;
622 case ColorID::Inactivecaption:
623 // inactive window caption
624 case ColorID::Inactivecaptiontext:
625 // text in inactive window caption
626 aResult = NS_RGB(0x66, 0x66, 0x00);
627 break;
629 case ColorID::Infobackground:
630 // tooltip background color
631 case ColorID::Infotext:
632 // tooltip text color
633 aResult = NS_RGB(0x00, 0xff, 0x00);
634 break;
636 case ColorID::Menu:
637 // menu background
638 case ColorID::Menutext:
639 // menu text
640 aResult = NS_RGB(0x00, 0xff, 0xff);
641 break;
643 case ColorID::Threedface:
644 case ColorID::Buttonface:
645 // 3-D face color
646 case ColorID::Buttontext:
647 // text on push buttons
648 aResult = NS_RGB(0x00, 0x66, 0x66);
649 break;
651 case ColorID::Window:
652 case ColorID::Windowtext:
653 aResult = NS_RGB(0x00, 0x00, 0xff);
654 break;
656 // from the CSS3 working draft (not yet finalized)
657 // http://www.w3.org/tr/2000/wd-css3-userint-20000216.html#color
659 case ColorID::Field:
660 case ColorID::Fieldtext:
661 aResult = NS_RGB(0xff, 0x00, 0xff);
662 break;
664 case ColorID::MozDialog:
665 case ColorID::MozDialogtext:
666 aResult = NS_RGB(0x66, 0x00, 0x66);
667 break;
669 default:
670 return NS_ERROR_NOT_AVAILABLE;
673 return NS_OK;
675 #endif
677 static nsresult GetColorFromPref(LookAndFeel::ColorID aID, nscolor& aResult) {
678 const char* prefName = sColorPrefs[size_t(aID)];
679 nsAutoCString colorStr;
680 MOZ_TRY(Preferences::GetCString(prefName, colorStr));
681 if (!ServoCSSParser::ComputeColor(nullptr, NS_RGB(0, 0, 0), colorStr,
682 &aResult)) {
683 return NS_ERROR_FAILURE;
685 return NS_OK;
688 // All these routines will return NS_OK if they have a value,
689 // in which case the nsLookAndFeel should use that value;
690 // otherwise we'll return NS_ERROR_NOT_AVAILABLE, in which case, the
691 // platform-specific nsLookAndFeel should use its own values instead.
692 nsresult nsXPLookAndFeel::GetColorValue(ColorID aID, ColorScheme aScheme,
693 UseStandins aUseStandins,
694 nscolor& aResult) {
695 if (!sInitialized) {
696 Init();
699 #ifdef DEBUG_SYSTEM_COLOR_USE
700 if (NS_SUCCEEDED(SystemColorUseDebuggingColor(aID, aResult))) {
701 return NS_OK;
703 #endif
705 if (aUseStandins == UseStandins::Yes) {
706 aResult = GetStandinForNativeColor(aID);
707 return NS_OK;
710 auto& cache =
711 aScheme == ColorScheme::Light ? sLightColorCache : sDarkColorCache;
712 if (const auto* cached = cache.Get(aID)) {
713 if (cached->isNothing()) {
714 return NS_ERROR_FAILURE;
716 aResult = cached->value();
717 return NS_OK;
720 if (NS_SUCCEEDED(GetColorFromPref(aID, aResult))) {
721 cache.Insert(aID, Some(aResult));
722 return NS_OK;
725 if (NS_SUCCEEDED(NativeGetColor(aID, aScheme, aResult))) {
726 if (gfxPlatform::GetCMSMode() == CMSMode::All &&
727 !IsSpecialColor(aID, aResult)) {
728 qcms_transform* transform = gfxPlatform::GetCMSInverseRGBTransform();
729 if (transform) {
730 uint8_t color[4];
731 color[0] = NS_GET_R(aResult);
732 color[1] = NS_GET_G(aResult);
733 color[2] = NS_GET_B(aResult);
734 color[3] = NS_GET_A(aResult);
735 qcms_transform_data(transform, color, color, 1);
736 aResult = NS_RGBA(color[0], color[1], color[2], color[3]);
740 // NOTE: Servo holds a lock and the main thread is paused, so writing to the
741 // global cache here is fine.
742 cache.Insert(aID, Some(aResult));
743 return NS_OK;
746 cache.Insert(aID, Nothing());
747 return NS_ERROR_FAILURE;
750 nsresult nsXPLookAndFeel::GetIntValue(IntID aID, int32_t& aResult) {
751 if (!sInitialized) {
752 Init();
755 if (const auto* cached = sIntCache.Get(aID)) {
756 if (cached->isNothing()) {
757 return NS_ERROR_FAILURE;
759 aResult = cached->value();
760 return NS_OK;
763 if (NS_SUCCEEDED(Preferences::GetInt(sIntPrefs[size_t(aID)], &aResult))) {
764 sIntCache.Insert(aID, Some(aResult));
765 return NS_OK;
768 if (NS_FAILED(NativeGetInt(aID, aResult))) {
769 sIntCache.Insert(aID, Nothing());
770 return NS_ERROR_FAILURE;
773 sIntCache.Insert(aID, Some(aResult));
774 return NS_OK;
777 nsresult nsXPLookAndFeel::GetFloatValue(FloatID aID, float& aResult) {
778 if (!sInitialized) {
779 Init();
782 if (const auto* cached = sFloatCache.Get(aID)) {
783 if (cached->isNothing()) {
784 return NS_ERROR_FAILURE;
786 aResult = cached->value();
787 return NS_OK;
790 int32_t pref = 0;
791 if (NS_SUCCEEDED(Preferences::GetInt(sFloatPrefs[size_t(aID)], &pref))) {
792 aResult = float(pref) / 100.0f;
793 sFloatCache.Insert(aID, Some(aResult));
794 return NS_OK;
797 if (NS_FAILED(NativeGetFloat(aID, aResult))) {
798 sFloatCache.Insert(aID, Nothing());
799 return NS_ERROR_FAILURE;
802 sFloatCache.Insert(aID, Some(aResult));
803 return NS_OK;
806 bool nsXPLookAndFeel::LookAndFeelFontToStyle(const LookAndFeelFont& aFont,
807 nsString& aName,
808 gfxFontStyle& aStyle) {
809 if (!aFont.haveFont()) {
810 return false;
812 aName = aFont.name();
813 aStyle = gfxFontStyle();
814 aStyle.size = aFont.size();
815 aStyle.weight = FontWeight(aFont.weight());
816 aStyle.style =
817 aFont.italic() ? FontSlantStyle::Italic() : FontSlantStyle::Normal();
818 aStyle.systemFont = true;
819 return true;
822 widget::LookAndFeelFont nsXPLookAndFeel::StyleToLookAndFeelFont(
823 const nsAString& aName, const gfxFontStyle& aStyle) {
824 LookAndFeelFont font;
825 font.haveFont() = true;
826 font.name() = aName;
827 font.size() = aStyle.size;
828 font.weight() = aStyle.weight.ToFloat();
829 font.italic() = aStyle.style.IsItalic();
830 MOZ_ASSERT(aStyle.style.IsNormal() || aStyle.style.IsItalic(),
831 "Cannot handle oblique font style");
832 #ifdef DEBUG
834 // Assert that all the remaining font style properties have their
835 // default values.
836 gfxFontStyle candidate = aStyle;
837 gfxFontStyle defaults{};
838 candidate.size = defaults.size;
839 candidate.weight = defaults.weight;
840 candidate.style = defaults.style;
841 MOZ_ASSERT(candidate.Equals(defaults),
842 "Some font style properties not supported");
844 #endif
845 return font;
848 bool nsXPLookAndFeel::GetFontValue(FontID aID, nsString& aName,
849 gfxFontStyle& aStyle) {
850 if (const LookAndFeelFont* cached = sFontCache.Get(aID)) {
851 return LookAndFeelFontToStyle(*cached, aName, aStyle);
853 LookAndFeelFont font;
854 const bool haveFont = NativeGetFont(aID, aName, aStyle);
855 font.haveFont() = haveFont;
856 if (haveFont) {
857 font = StyleToLookAndFeelFont(aName, aStyle);
859 sFontCache.Insert(aID, std::move(font));
860 return haveFont;
863 void nsXPLookAndFeel::RefreshImpl() {
864 // Wipe out our caches.
865 sLightColorCache.Clear();
866 sDarkColorCache.Clear();
867 sFontCache.Clear();
868 sFloatCache.Clear();
869 sIntCache.Clear();
871 // Clear any cached FullLookAndFeel data, which is now invalid.
872 if (XRE_IsParentProcess()) {
873 widget::RemoteLookAndFeel::ClearCachedData();
877 static bool sRecordedLookAndFeelTelemetry = false;
879 void nsXPLookAndFeel::RecordTelemetry() {
880 if (!XRE_IsParentProcess()) {
881 return;
884 if (sRecordedLookAndFeelTelemetry) {
885 return;
888 sRecordedLookAndFeelTelemetry = true;
890 int32_t i;
891 Telemetry::ScalarSet(
892 Telemetry::ScalarID::WIDGET_DARK_MODE,
893 NS_SUCCEEDED(GetIntValue(IntID::SystemUsesDarkTheme, i)) && i != 0);
895 RecordLookAndFeelSpecificTelemetry();
898 namespace mozilla {
900 // static
901 void LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind aKind) {
902 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
903 const char16_t kind[] = {char16_t(aKind), 0};
904 obs->NotifyObservers(nullptr, "look-and-feel-changed", kind);
908 static bool ShouldUseStandinsForNativeColorForNonNativeTheme(
909 const dom::Document& aDoc, LookAndFeel::ColorID aColor) {
910 using ColorID = LookAndFeel::ColorID;
911 if (!aDoc.ShouldAvoidNativeTheme()) {
912 return false;
915 // The native theme doesn't use system colors backgrounds etc, except when in
916 // high-contrast mode, so spoof some of the colors with stand-ins to prevent
917 // lack of contrast.
918 switch (aColor) {
919 case ColorID::Buttonface:
920 case ColorID::Buttontext:
921 case ColorID::MozButtonhoverface:
922 case ColorID::MozButtonhovertext:
923 case ColorID::MozGtkButtonactivetext:
925 case ColorID::MozCombobox:
926 case ColorID::MozComboboxtext:
928 case ColorID::Field:
929 case ColorID::Fieldtext:
931 case ColorID::Graytext:
933 return !PreferenceSheet::PrefsFor(aDoc)
934 .NonNativeThemeShouldUseSystemColors();
936 default:
937 break;
940 return false;
943 static bool ShouldRespectSystemColorSchemeForChromeDoc() {
944 #ifdef XP_MACOSX
945 // macOS follows the global toolbar theme, not the system theme.
946 // (If the global toolbar theme is set to System, then it *that* follows the
947 // system theme.)
948 return false;
949 #else
950 // GTK historically has behaved like this. Other platforms don't have support
951 // for light / dark color schemes yet so it doesn't matter for them.
952 return true;
953 #endif
956 static bool ShouldRespectGlobalToolbarThemeAppearanceForChromeDoc() {
957 #ifdef XP_MACOSX
958 // Need to be consistent with AppearanceOverride.mm on macOS, which respects
959 // the browser.theme.toolbar-theme pref.
960 // However, if widget.macos.support-dark-appearance is false, we need to
961 // pretend everything's Light and not follow the toolbar theme.
962 return StaticPrefs::widget_macos_support_dark_appearance();
963 #elif defined(MOZ_WIDGET_GTK)
964 return StaticPrefs::widget_gtk_follow_firefox_theme();
965 #else
966 return false;
967 #endif
970 LookAndFeel::ColorScheme LookAndFeel::ColorSchemeForChrome() {
971 if (ShouldRespectGlobalToolbarThemeAppearanceForChromeDoc()) {
972 switch (StaticPrefs::browser_theme_toolbar_theme()) {
973 case 0: // Dark
974 return ColorScheme::Dark;
975 case 1: // Light
976 return ColorScheme::Light;
977 case 2: // System
978 return SystemColorScheme();
979 default:
980 break;
983 if (ShouldRespectSystemColorSchemeForChromeDoc()) {
984 return SystemColorScheme();
986 return ColorScheme::Light;
989 static LookAndFeel::ColorScheme ColorSchemeForDocument(
990 const dom::Document& aDoc, bool aContentSupportsDark) {
991 if (nsContentUtils::IsChromeDoc(&aDoc)) {
992 return LookAndFeel::ColorSchemeForChrome();
994 #ifdef MOZ_WIDGET_GTK
995 if (StaticPrefs::widget_content_allow_gtk_dark_theme()) {
996 // If users manually tweak allow-gtk-dark-theme, allow content to use the
997 // system color scheme rather than forcing it to light.
998 return LookAndFeel::SystemColorScheme();
1000 #endif
1001 return aContentSupportsDark ? LookAndFeel::SystemColorScheme()
1002 : LookAndFeel::ColorScheme::Light;
1005 LookAndFeel::ColorScheme LookAndFeel::ColorSchemeForStyle(
1006 const dom::Document& aDoc, const StyleColorSchemeFlags& aFlags) {
1007 StyleColorSchemeFlags style(aFlags);
1008 if (!style) {
1009 style.bits = aDoc.GetColorSchemeBits();
1011 const bool supportsDark = bool(style & StyleColorSchemeFlags::DARK);
1012 const bool supportsLight = bool(style & StyleColorSchemeFlags::LIGHT);
1013 if (supportsDark && !supportsLight) {
1014 return ColorScheme::Dark;
1016 if (supportsLight && !supportsDark) {
1017 return ColorScheme::Light;
1019 return ColorSchemeForDocument(aDoc, supportsDark);
1022 LookAndFeel::ColorScheme LookAndFeel::ColorSchemeForFrame(
1023 const nsIFrame* aFrame) {
1024 return ColorSchemeForStyle(*aFrame->PresContext()->Document(),
1025 aFrame->StyleUI()->mColorScheme.bits);
1028 // static
1029 Maybe<nscolor> LookAndFeel::GetColor(ColorID aId, ColorScheme aScheme,
1030 UseStandins aUseStandins) {
1031 nscolor result;
1032 nsresult rv = nsLookAndFeel::GetInstance()->GetColorValue(
1033 aId, aScheme, aUseStandins, result);
1034 if (NS_FAILED(rv)) {
1035 return Nothing();
1037 return Some(result);
1040 // Returns whether there is a CSS color name for this color.
1041 static bool ColorIsCSSAccessible(LookAndFeel::ColorID aId) {
1042 using ColorID = LookAndFeel::ColorID;
1044 switch (aId) {
1045 case ColorID::WindowBackground:
1046 case ColorID::WindowForeground:
1047 case ColorID::WidgetBackground:
1048 case ColorID::WidgetForeground:
1049 case ColorID::WidgetSelectBackground:
1050 case ColorID::WidgetSelectForeground:
1051 case ColorID::Widget3DHighlight:
1052 case ColorID::Widget3DShadow:
1053 case ColorID::TextBackground:
1054 case ColorID::TextForeground:
1055 case ColorID::TextSelectBackground:
1056 case ColorID::TextSelectForeground:
1057 case ColorID::TextSelectBackgroundDisabled:
1058 case ColorID::TextSelectBackgroundAttention:
1059 case ColorID::TextHighlightBackground:
1060 case ColorID::TextHighlightForeground:
1061 case ColorID::IMERawInputBackground:
1062 case ColorID::IMERawInputForeground:
1063 case ColorID::IMERawInputUnderline:
1064 case ColorID::IMESelectedRawTextBackground:
1065 case ColorID::IMESelectedRawTextForeground:
1066 case ColorID::IMESelectedRawTextUnderline:
1067 case ColorID::IMEConvertedTextBackground:
1068 case ColorID::IMEConvertedTextForeground:
1069 case ColorID::IMEConvertedTextUnderline:
1070 case ColorID::IMESelectedConvertedTextBackground:
1071 case ColorID::IMESelectedConvertedTextForeground:
1072 case ColorID::IMESelectedConvertedTextUnderline:
1073 case ColorID::SpellCheckerUnderline:
1074 return false;
1075 default:
1076 break;
1079 return true;
1082 LookAndFeel::UseStandins LookAndFeel::ShouldUseStandins(
1083 const dom::Document& aDoc, ColorID aId) {
1084 if (ShouldUseStandinsForNativeColorForNonNativeTheme(aDoc, aId)) {
1085 return UseStandins::Yes;
1087 if (nsContentUtils::UseStandinsForNativeColors() &&
1088 !nsContentUtils::IsChromeDoc(&aDoc) && ColorIsCSSAccessible(aId)) {
1089 return UseStandins::Yes;
1091 if (aDoc.IsStaticDocument() &&
1092 !PreferenceSheet::ContentPrefs().mUseDocumentColors) {
1093 return UseStandins::Yes;
1095 return UseStandins::No;
1098 Maybe<nscolor> LookAndFeel::GetColor(ColorID aId, const nsIFrame* aFrame) {
1099 const auto* doc = aFrame->PresContext()->Document();
1100 return GetColor(aId, ColorSchemeForFrame(aFrame),
1101 ShouldUseStandins(*doc, aId));
1104 // static
1105 nsresult LookAndFeel::GetInt(IntID aID, int32_t* aResult) {
1106 return nsLookAndFeel::GetInstance()->GetIntValue(aID, *aResult);
1109 // static
1110 nsresult LookAndFeel::GetFloat(FloatID aID, float* aResult) {
1111 return nsLookAndFeel::GetInstance()->GetFloatValue(aID, *aResult);
1114 // static
1115 bool LookAndFeel::GetFont(FontID aID, nsString& aName, gfxFontStyle& aStyle) {
1116 return nsLookAndFeel::GetInstance()->GetFontValue(aID, aName, aStyle);
1119 // static
1120 char16_t LookAndFeel::GetPasswordCharacter() {
1121 return nsLookAndFeel::GetInstance()->GetPasswordCharacterImpl();
1124 // static
1125 bool LookAndFeel::GetEchoPassword() {
1126 if (StaticPrefs::editor_password_mask_delay() >= 0) {
1127 return StaticPrefs::editor_password_mask_delay() > 0;
1129 return nsLookAndFeel::GetInstance()->GetEchoPasswordImpl();
1132 // static
1133 uint32_t LookAndFeel::GetPasswordMaskDelay() {
1134 int32_t delay = StaticPrefs::editor_password_mask_delay();
1135 if (delay < 0) {
1136 return nsLookAndFeel::GetInstance()->GetPasswordMaskDelayImpl();
1138 return delay;
1141 void LookAndFeel::GetThemeInfo(nsACString& aOut) {
1142 nsLookAndFeel::GetInstance()->GetThemeInfo(aOut);
1145 // static
1146 void LookAndFeel::Refresh() {
1147 nsLookAndFeel::GetInstance()->RefreshImpl();
1148 nsNativeBasicTheme::LookAndFeelChanged();
1151 // static
1152 void LookAndFeel::NativeInit() { nsLookAndFeel::GetInstance()->NativeInit(); }
1154 // static
1155 void LookAndFeel::SetData(widget::FullLookAndFeel&& aTables) {
1156 nsLookAndFeel::GetInstance()->SetDataImpl(std::move(aTables));
1159 } // namespace mozilla