Bug 1731994: part 7) Update documentation of `nsIContentPermissionPrompt`. r=edgar...
[gecko.git] / widget / nsXPLookAndFeel.cpp
blob0ad4d67cad1cd20d9ba60c28563ca804d1c94645
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.macRTL",
145 "ui.alertNotificationOrigin",
146 "ui.scrollToClick",
147 "ui.IMERawInputUnderlineStyle",
148 "ui.IMESelectedRawTextUnderlineStyle",
149 "ui.IMEConvertedTextUnderlineStyle",
150 "ui.IMESelectedConvertedTextUnderlineStyle",
151 "ui.SpellCheckerUnderlineStyle",
152 "ui.menuBarDrag",
153 "ui.windowsThemeIdentifier",
154 "ui.operatingSystemVersionIdentifier",
155 "ui.scrollbarButtonAutoRepeatBehavior",
156 "ui.tooltipDelay",
157 "ui.swipeAnimationEnabled",
158 "ui.scrollbarDisplayOnMouseMove",
159 "ui.scrollbarFadeBeginDelay",
160 "ui.scrollbarFadeDuration",
161 "ui.contextMenuOffsetVertical",
162 "ui.contextMenuOffsetHorizontal",
163 "ui.GtkCSDAvailable",
164 "ui.GtkCSDHideTitlebarByDefault",
165 "ui.GtkCSDTransparentBackground",
166 "ui.GtkCSDMinimizeButton",
167 "ui.GtkCSDMaximizeButton",
168 "ui.GtkCSDCloseButton",
169 "ui.GtkCSDMinimizeButtonPosition",
170 "ui.GtkCSDMaximizeButtonPosition",
171 "ui.GtkCSDCloseButtonPosition",
172 "ui.GtkCSDReversedPlacement",
173 "ui.systemUsesDarkTheme",
174 "ui.prefersReducedMotion",
175 "ui.primaryPointerCapabilities",
176 "ui.allPointerCapabilities",
177 "ui.systemVerticalScrollbarWidth",
178 "ui.systemHorizontalScrollbarHeight",
179 "ui.touchDeviceSupportPresent",
182 static_assert(ArrayLength(sIntPrefs) == size_t(LookAndFeel::IntID::End),
183 "Should have a pref for each int value");
185 // This array MUST be kept in the same order as the float id list in
186 // LookAndFeel.h
187 static const char sFloatPrefs[][37] = {
188 "ui.IMEUnderlineRelativeSize",
189 "ui.SpellCheckerUnderlineRelativeSize",
190 "ui.caretAspectRatio",
191 "ui.textScaleFactor",
194 static_assert(ArrayLength(sFloatPrefs) == size_t(LookAndFeel::FloatID::End),
195 "Should have a pref for each float value");
197 // This array MUST be kept in the same order as the color list in
198 // specified/color.rs
199 static const char sColorPrefs[][41] = {
200 "ui.windowBackground",
201 "ui.windowForeground",
202 "ui.widgetBackground",
203 "ui.widgetForeground",
204 "ui.widgetSelectBackground",
205 "ui.widgetSelectForeground",
206 "ui.widget3DHighlight",
207 "ui.widget3DShadow",
208 "ui.textBackground",
209 "ui.textForeground",
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.selecteditem",
272 "ui.selecteditemtext",
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-nativevisitedhyperlinktext",
308 "ui.-moz-hyperlinktext",
309 "ui.-moz-activehyperlinktext",
310 "ui.-moz-visitedhyperlinktext",
311 "ui.-moz-comboboxtext",
312 "ui.-moz-combobox",
313 "ui.-moz-colheadertext",
314 "ui.-moz-colheaderhovertext",
315 "ui.-moz-gtk-titlebar-text",
316 "ui.-moz-gtk-titlebar-inactive-text",
319 static_assert(ArrayLength(sColorPrefs) == size_t(LookAndFeel::ColorID::End),
320 "Should have a pref for each color value");
322 const char* nsXPLookAndFeel::GetColorPrefName(ColorID aId) {
323 return sColorPrefs[size_t(aId)];
326 bool nsXPLookAndFeel::sInitialized = false;
328 nsXPLookAndFeel* nsXPLookAndFeel::sInstance = nullptr;
329 bool nsXPLookAndFeel::sShutdown = false;
331 // static
332 nsXPLookAndFeel* nsXPLookAndFeel::GetInstance() {
333 if (sInstance) {
334 return sInstance;
337 NS_ENSURE_TRUE(!sShutdown, nullptr);
339 // If we're in a content process, then the parent process will have supplied
340 // us with an initial FullLookAndFeel object.
341 // We grab this data from the ContentChild,
342 // where it's been temporarily stashed, and initialize our new LookAndFeel
343 // object with it.
345 FullLookAndFeel* lnf = nullptr;
347 if (auto* cc = mozilla::dom::ContentChild::GetSingleton()) {
348 lnf = &cc->BorrowLookAndFeelData();
351 if (lnf) {
352 sInstance = new widget::RemoteLookAndFeel(std::move(*lnf));
353 } else if (gfxPlatform::IsHeadless()) {
354 sInstance = new widget::HeadlessLookAndFeel();
355 } else {
356 sInstance = new nsLookAndFeel();
359 // This is only ever used once during initialization, and can be cleared now.
360 if (lnf) {
361 *lnf = {};
364 nsNativeBasicTheme::Init();
365 return sInstance;
368 // static
369 void nsXPLookAndFeel::Shutdown() {
370 if (sShutdown) {
371 return;
374 sShutdown = true;
375 delete sInstance;
376 sInstance = nullptr;
378 // This keeps strings alive, so need to clear to make leak checking happy.
379 sFontCache.Clear();
381 nsNativeBasicTheme::Shutdown();
384 static void IntPrefChanged() {
385 // Int prefs can't change our system colors or fonts.
386 LookAndFeel::NotifyChangedAllWindows(
387 widget::ThemeChangeKind::MediaQueriesOnly);
390 static void FloatPrefChanged() {
391 // Float prefs can't change our system colors or fonts.
392 LookAndFeel::NotifyChangedAllWindows(
393 widget::ThemeChangeKind::MediaQueriesOnly);
396 static void ColorPrefChanged() {
397 // Color prefs affect style, because they by definition change system colors.
398 LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind::Style);
401 // static
402 void nsXPLookAndFeel::OnPrefChanged(const char* aPref, void* aClosure) {
403 nsDependentCString prefName(aPref);
404 for (const char* pref : sIntPrefs) {
405 if (prefName.Equals(pref)) {
406 IntPrefChanged();
407 return;
411 for (const char* pref : sFloatPrefs) {
412 if (prefName.Equals(pref)) {
413 FloatPrefChanged();
414 return;
418 for (const char* pref : sColorPrefs) {
419 if (prefName.Equals(pref)) {
420 ColorPrefChanged();
421 return;
426 static constexpr struct {
427 nsLiteralCString mName;
428 widget::ThemeChangeKind mChangeKind =
429 widget::ThemeChangeKind::MediaQueriesOnly;
430 } kMediaQueryPrefs[] = {
431 {"browser.display.windows.native_menus"_ns},
432 {"browser.proton.enabled"_ns},
433 {"browser.proton.places-tooltip.enabled"_ns},
434 // This affects not only the media query, but also the native theme, so we
435 // need to re-layout.
436 {"browser.theme.toolbar-theme"_ns, widget::ThemeChangeKind::AllBits},
439 // Read values from the user's preferences.
440 // This is done once at startup, but since the user's preferences
441 // haven't actually been read yet at that time, we also have to
442 // set a callback to inform us of changes to each pref.
443 void nsXPLookAndFeel::Init() {
444 MOZ_RELEASE_ASSERT(NS_IsMainThread());
446 // Say we're already initialized, and take the chance that it might fail;
447 // protects against some other process writing to our static variables.
448 sInitialized = true;
450 // XXX If we could reorganize the pref names, we should separate the branch
451 // for each types. Then, we could reduce the unnecessary loop from
452 // nsXPLookAndFeel::OnPrefChanged().
453 Preferences::RegisterPrefixCallback(OnPrefChanged, "ui.");
454 // We really do just want the accessibility.tabfocus pref, not other prefs
455 // that start with that string.
456 Preferences::RegisterCallback(OnPrefChanged, "accessibility.tabfocus");
458 for (auto& pref : kMediaQueryPrefs) {
459 Preferences::RegisterCallback(
460 [](const char*, void* aChangeKind) {
461 auto changeKind =
462 widget::ThemeChangeKind(reinterpret_cast<uintptr_t>(aChangeKind));
463 LookAndFeel::NotifyChangedAllWindows(changeKind);
465 pref.mName, reinterpret_cast<void*>(uintptr_t(pref.mChangeKind)));
469 nsXPLookAndFeel::~nsXPLookAndFeel() {
470 NS_ASSERTION(sInstance == this,
471 "This destroying instance isn't the singleton instance");
472 sInstance = nullptr;
475 static bool IsSpecialColor(LookAndFeel::ColorID aID, nscolor aColor) {
476 using ColorID = LookAndFeel::ColorID;
478 if (aColor == NS_SAME_AS_FOREGROUND_COLOR) {
479 return true;
482 switch (aID) {
483 case ColorID::IMESelectedRawTextBackground:
484 case ColorID::IMESelectedConvertedTextBackground:
485 case ColorID::IMERawInputBackground:
486 case ColorID::IMEConvertedTextBackground:
487 case ColorID::IMESelectedRawTextForeground:
488 case ColorID::IMESelectedConvertedTextForeground:
489 case ColorID::IMERawInputForeground:
490 case ColorID::IMEConvertedTextForeground:
491 case ColorID::IMERawInputUnderline:
492 case ColorID::IMEConvertedTextUnderline:
493 case ColorID::IMESelectedRawTextUnderline:
494 case ColorID::IMESelectedConvertedTextUnderline:
495 case ColorID::SpellCheckerUnderline:
496 return NS_IS_SELECTION_SPECIAL_COLOR(aColor);
497 default:
498 break;
501 * In GetColor(), every color that is not a special color is color
502 * corrected. Use false to make other colors color corrected.
504 return false;
507 nscolor nsXPLookAndFeel::GetStandinForNativeColor(ColorID aID,
508 ColorScheme aScheme) {
509 if (aScheme == ColorScheme::Dark) {
510 if (auto color = GenericDarkColor(aID)) {
511 return *color;
515 // The stand-in colors are taken from the Windows 7 Aero theme
516 // except Mac-specific colors which are taken from Mac OS 10.7.
518 #define COLOR(name_, r, g, b) \
519 case ColorID::name_: \
520 return NS_RGB(r, g, b);
522 switch (aID) {
523 // CSS 2 colors:
524 COLOR(Activeborder, 0xB4, 0xB4, 0xB4)
525 COLOR(Activecaption, 0x99, 0xB4, 0xD1)
526 COLOR(Appworkspace, 0xAB, 0xAB, 0xAB)
527 COLOR(Background, 0x00, 0x00, 0x00)
528 COLOR(Buttonface, 0xF0, 0xF0, 0xF0)
529 COLOR(Buttonhighlight, 0xFF, 0xFF, 0xFF)
530 COLOR(Buttonshadow, 0xA0, 0xA0, 0xA0)
531 COLOR(Buttontext, 0x00, 0x00, 0x00)
532 COLOR(Captiontext, 0x00, 0x00, 0x00)
533 COLOR(Graytext, 0x6D, 0x6D, 0x6D)
534 COLOR(Highlight, 0x33, 0x99, 0xFF)
535 COLOR(Highlighttext, 0xFF, 0xFF, 0xFF)
536 COLOR(Inactiveborder, 0xF4, 0xF7, 0xFC)
537 COLOR(Inactivecaption, 0xBF, 0xCD, 0xDB)
538 COLOR(Inactivecaptiontext, 0x43, 0x4E, 0x54)
539 COLOR(Infobackground, 0xFF, 0xFF, 0xE1)
540 COLOR(Infotext, 0x00, 0x00, 0x00)
541 COLOR(Menu, 0xF0, 0xF0, 0xF0)
542 COLOR(Menutext, 0x00, 0x00, 0x00)
543 COLOR(Scrollbar, 0xC8, 0xC8, 0xC8)
544 COLOR(Threeddarkshadow, 0x69, 0x69, 0x69)
545 COLOR(Threedface, 0xF0, 0xF0, 0xF0)
546 COLOR(Threedhighlight, 0xFF, 0xFF, 0xFF)
547 COLOR(Threedlightshadow, 0xE3, 0xE3, 0xE3)
548 COLOR(Threedshadow, 0xA0, 0xA0, 0xA0)
549 COLOR(Window, 0xFF, 0xFF, 0xFF)
550 COLOR(Windowframe, 0x64, 0x64, 0x64)
551 COLOR(Windowtext, 0x00, 0x00, 0x00)
552 COLOR(MozButtondefault, 0x69, 0x69, 0x69)
553 COLOR(Field, 0xFF, 0xFF, 0xFF)
554 COLOR(Fieldtext, 0x00, 0x00, 0x00)
555 COLOR(MozDialog, 0xF0, 0xF0, 0xF0)
556 COLOR(MozDialogtext, 0x00, 0x00, 0x00)
557 COLOR(MozColheadertext, 0x00, 0x00, 0x00)
558 COLOR(MozColheaderhovertext, 0x00, 0x00, 0x00)
559 COLOR(MozDragtargetzone, 0xFF, 0xFF, 0xFF)
560 COLOR(MozCellhighlight, 0xF0, 0xF0, 0xF0)
561 COLOR(MozCellhighlighttext, 0x00, 0x00, 0x00)
562 COLOR(Selecteditem, 0x33, 0x99, 0xFF)
563 COLOR(Selecteditemtext, 0xFF, 0xFF, 0xFF)
564 COLOR(MozButtonhoverface, 0xF0, 0xF0, 0xF0)
565 COLOR(MozGtkButtonactivetext, 0x00, 0x00, 0x00)
566 COLOR(MozButtonhovertext, 0x00, 0x00, 0x00)
567 COLOR(MozMenuhover, 0x33, 0x99, 0xFF)
568 COLOR(MozMenuhovertext, 0x00, 0x00, 0x00)
569 COLOR(MozMenubartext, 0x00, 0x00, 0x00)
570 COLOR(MozMenubarhovertext, 0x00, 0x00, 0x00)
571 COLOR(MozOddtreerow, 0xFF, 0xFF, 0xFF)
572 COLOR(MozMacChromeActive, 0xB2, 0xB2, 0xB2)
573 COLOR(MozMacChromeInactive, 0xE1, 0xE1, 0xE1)
574 COLOR(MozMacFocusring, 0x60, 0x9D, 0xD7)
575 COLOR(MozMacMenuselect, 0x38, 0x75, 0xD7)
576 COLOR(MozMacMenushadow, 0xA3, 0xA3, 0xA3)
577 COLOR(MozMacMenutextdisable, 0x88, 0x88, 0x88)
578 COLOR(MozMacMenutextselect, 0xFF, 0xFF, 0xFF)
579 COLOR(MozMacDisabledtoolbartext, 0x3F, 0x3F, 0x3F)
580 COLOR(MozMacSecondaryhighlight, 0xD4, 0xD4, 0xD4)
581 COLOR(MozMacVibrantTitlebarLight, 0xf7, 0xf7, 0xf7)
582 COLOR(MozMacVibrantTitlebarDark, 0x28, 0x28, 0x28)
583 COLOR(MozMacMenupopup, 0xe6, 0xe6, 0xe6)
584 COLOR(MozMacMenuitem, 0xe6, 0xe6, 0xe6)
585 COLOR(MozMacActiveMenuitem, 0x0a, 0x64, 0xdc)
586 COLOR(MozMacSourceList, 0xf7, 0xf7, 0xf7)
587 COLOR(MozMacSourceListSelection, 0xc8, 0xc8, 0xc8)
588 COLOR(MozMacActiveSourceListSelection, 0x0a, 0x64, 0xdc)
589 COLOR(MozMacTooltip, 0xf7, 0xf7, 0xf7)
590 // Seems to be the default color (hardcoded because of bug 1065998)
591 COLOR(MozWinMediatext, 0xFF, 0xFF, 0xFF)
592 COLOR(MozWinCommunicationstext, 0xFF, 0xFF, 0xFF)
593 COLOR(MozNativehyperlinktext, 0x00, 0x66, 0xCC)
594 COLOR(MozNativevisitedhyperlinktext, 0x55, 0x1A, 0x8B)
595 COLOR(MozComboboxtext, 0x00, 0x00, 0x00)
596 COLOR(MozCombobox, 0xFF, 0xFF, 0xFF)
597 default:
598 break;
600 return NS_RGB(0xFF, 0xFF, 0xFF);
603 #undef COLOR
605 // Taken from in-content/common.inc.css's dark theme.
606 Maybe<nscolor> nsXPLookAndFeel::GenericDarkColor(ColorID aID) {
607 nscolor color = NS_RGB(0, 0, 0);
608 switch (aID) {
609 case ColorID::Window: // --in-content-page-background
610 case ColorID::WindowBackground:
611 case ColorID::Background:
612 case ColorID::TextBackground:
613 color = NS_RGB(28, 27, 34);
614 break;
615 case ColorID::MozDialog: // --in-content-box-background
616 color = NS_RGB(35, 34, 43);
617 break;
618 case ColorID::Windowtext: // --in-content-page-color
619 case ColorID::WindowForeground:
620 case ColorID::MozDialogtext:
621 case ColorID::TextForeground:
622 case ColorID::Fieldtext:
623 case ColorID::Buttontext: // --in-content-button-text-color (via
624 // --in-content-page-color)
625 color = NS_RGB(251, 251, 254);
626 break;
627 case ColorID::Graytext: // --in-content-deemphasized-text
628 color = NS_RGB(191, 191, 201);
629 break;
630 case ColorID::Selecteditem: // --in-content-primary-button-background /
631 // --in-content-item-selected
632 case ColorID::Highlight:
633 // TODO(emilio): Perhaps for selection (highlight / highlighttext) we want
634 // something more subtle like Android / macOS do.
635 color = NS_RGB(0, 221, 255);
636 break;
637 case ColorID::Field:
638 case ColorID::Buttonface: // --in-content-button-background
639 case ColorID::Selecteditemtext: // --in-content-primary-button-text-color /
640 // --in-content-item-selected-text
641 case ColorID::Highlighttext:
642 color = NS_RGB(43, 42, 51);
643 break;
645 default:
646 return Nothing();
648 return Some(color);
651 // Uncomment the #define below if you want to debug system color use in a skin
652 // that uses them. When set, it will make all system color pairs that are
653 // appropriate for foreground/background pairing the same. This means if the
654 // skin is using system colors correctly you will not be able to see *any* text.
656 // #define DEBUG_SYSTEM_COLOR_USE
658 #ifdef DEBUG_SYSTEM_COLOR_USE
659 static nsresult SystemColorUseDebuggingColor(LookAndFeel::ColorID aID,
660 nscolor& aResult) {
661 using ColorID = LookAndFeel::ColorID;
663 switch (aID) {
664 // css2 http://www.w3.org/TR/REC-CSS2/ui.html#system-colors
665 case ColorID::Activecaption:
666 // active window caption background
667 case ColorID::Captiontext:
668 // text in active window caption
669 aResult = NS_RGB(0xff, 0x00, 0x00);
670 break;
672 case ColorID::Highlight:
673 // background of selected item
674 case ColorID::Highlighttext:
675 // text of selected item
676 aResult = NS_RGB(0xff, 0xff, 0x00);
677 break;
679 case ColorID::Inactivecaption:
680 // inactive window caption
681 case ColorID::Inactivecaptiontext:
682 // text in inactive window caption
683 aResult = NS_RGB(0x66, 0x66, 0x00);
684 break;
686 case ColorID::Infobackground:
687 // tooltip background color
688 case ColorID::Infotext:
689 // tooltip text color
690 aResult = NS_RGB(0x00, 0xff, 0x00);
691 break;
693 case ColorID::Menu:
694 // menu background
695 case ColorID::Menutext:
696 // menu text
697 aResult = NS_RGB(0x00, 0xff, 0xff);
698 break;
700 case ColorID::Threedface:
701 case ColorID::Buttonface:
702 // 3-D face color
703 case ColorID::Buttontext:
704 // text on push buttons
705 aResult = NS_RGB(0x00, 0x66, 0x66);
706 break;
708 case ColorID::Window:
709 case ColorID::Windowtext:
710 aResult = NS_RGB(0x00, 0x00, 0xff);
711 break;
713 // from the CSS3 working draft (not yet finalized)
714 // http://www.w3.org/tr/2000/wd-css3-userint-20000216.html#color
716 case ColorID::Field:
717 case ColorID::Fieldtext:
718 aResult = NS_RGB(0xff, 0x00, 0xff);
719 break;
721 case ColorID::MozDialog:
722 case ColorID::MozDialogtext:
723 aResult = NS_RGB(0x66, 0x00, 0x66);
724 break;
726 default:
727 return NS_ERROR_NOT_AVAILABLE;
730 return NS_OK;
732 #endif
734 static nsresult GetColorFromPref(LookAndFeel::ColorID aID, nscolor& aResult) {
735 const char* prefName = sColorPrefs[size_t(aID)];
736 nsAutoCString colorStr;
737 MOZ_TRY(Preferences::GetCString(prefName, colorStr));
738 if (!ServoCSSParser::ComputeColor(nullptr, NS_RGB(0, 0, 0), colorStr,
739 &aResult)) {
740 return NS_ERROR_FAILURE;
742 return NS_OK;
745 // All these routines will return NS_OK if they have a value,
746 // in which case the nsLookAndFeel should use that value;
747 // otherwise we'll return NS_ERROR_NOT_AVAILABLE, in which case, the
748 // platform-specific nsLookAndFeel should use its own values instead.
749 nsresult nsXPLookAndFeel::GetColorValue(ColorID aID, ColorScheme aScheme,
750 UseStandins aUseStandins,
751 nscolor& aResult) {
752 if (!sInitialized) {
753 Init();
756 #ifdef DEBUG_SYSTEM_COLOR_USE
757 if (NS_SUCCEEDED(SystemColorUseDebuggingColor(aID, aResult))) {
758 return NS_OK;
760 #endif
762 if (aUseStandins == UseStandins::Yes) {
763 aResult = GetStandinForNativeColor(aID, aScheme);
764 return NS_OK;
767 auto& cache =
768 aScheme == ColorScheme::Light ? sLightColorCache : sDarkColorCache;
769 if (const auto* cached = cache.Get(aID)) {
770 if (cached->isNothing()) {
771 return NS_ERROR_FAILURE;
773 aResult = cached->value();
774 return NS_OK;
777 if (NS_SUCCEEDED(GetColorFromPref(aID, aResult))) {
778 cache.Insert(aID, Some(aResult));
779 return NS_OK;
782 if (NS_SUCCEEDED(NativeGetColor(aID, aScheme, aResult))) {
783 if (gfxPlatform::GetCMSMode() == CMSMode::All &&
784 !IsSpecialColor(aID, aResult)) {
785 qcms_transform* transform = gfxPlatform::GetCMSInverseRGBTransform();
786 if (transform) {
787 uint8_t color[4];
788 color[0] = NS_GET_R(aResult);
789 color[1] = NS_GET_G(aResult);
790 color[2] = NS_GET_B(aResult);
791 color[3] = NS_GET_A(aResult);
792 qcms_transform_data(transform, color, color, 1);
793 aResult = NS_RGBA(color[0], color[1], color[2], color[3]);
797 // NOTE: Servo holds a lock and the main thread is paused, so writing to the
798 // global cache here is fine.
799 cache.Insert(aID, Some(aResult));
800 return NS_OK;
803 cache.Insert(aID, Nothing());
804 return NS_ERROR_FAILURE;
807 nsresult nsXPLookAndFeel::GetIntValue(IntID aID, int32_t& aResult) {
808 if (!sInitialized) {
809 Init();
812 if (const auto* cached = sIntCache.Get(aID)) {
813 if (cached->isNothing()) {
814 return NS_ERROR_FAILURE;
816 aResult = cached->value();
817 return NS_OK;
820 if (NS_SUCCEEDED(Preferences::GetInt(sIntPrefs[size_t(aID)], &aResult))) {
821 sIntCache.Insert(aID, Some(aResult));
822 return NS_OK;
825 if (NS_FAILED(NativeGetInt(aID, aResult))) {
826 sIntCache.Insert(aID, Nothing());
827 return NS_ERROR_FAILURE;
830 sIntCache.Insert(aID, Some(aResult));
831 return NS_OK;
834 nsresult nsXPLookAndFeel::GetFloatValue(FloatID aID, float& aResult) {
835 if (!sInitialized) {
836 Init();
839 if (const auto* cached = sFloatCache.Get(aID)) {
840 if (cached->isNothing()) {
841 return NS_ERROR_FAILURE;
843 aResult = cached->value();
844 return NS_OK;
847 int32_t pref = 0;
848 if (NS_SUCCEEDED(Preferences::GetInt(sFloatPrefs[size_t(aID)], &pref))) {
849 aResult = float(pref) / 100.0f;
850 sFloatCache.Insert(aID, Some(aResult));
851 return NS_OK;
854 if (NS_FAILED(NativeGetFloat(aID, aResult))) {
855 sFloatCache.Insert(aID, Nothing());
856 return NS_ERROR_FAILURE;
859 sFloatCache.Insert(aID, Some(aResult));
860 return NS_OK;
863 bool nsXPLookAndFeel::LookAndFeelFontToStyle(const LookAndFeelFont& aFont,
864 nsString& aName,
865 gfxFontStyle& aStyle) {
866 if (!aFont.haveFont()) {
867 return false;
869 aName = aFont.name();
870 aStyle = gfxFontStyle();
871 aStyle.size = aFont.size();
872 aStyle.weight = FontWeight(aFont.weight());
873 aStyle.style =
874 aFont.italic() ? FontSlantStyle::Italic() : FontSlantStyle::Normal();
875 aStyle.systemFont = true;
876 return true;
879 widget::LookAndFeelFont nsXPLookAndFeel::StyleToLookAndFeelFont(
880 const nsAString& aName, const gfxFontStyle& aStyle) {
881 LookAndFeelFont font;
882 font.haveFont() = true;
883 font.name() = aName;
884 font.size() = aStyle.size;
885 font.weight() = aStyle.weight.ToFloat();
886 font.italic() = aStyle.style.IsItalic();
887 MOZ_ASSERT(aStyle.style.IsNormal() || aStyle.style.IsItalic(),
888 "Cannot handle oblique font style");
889 #ifdef DEBUG
891 // Assert that all the remaining font style properties have their
892 // default values.
893 gfxFontStyle candidate = aStyle;
894 gfxFontStyle defaults{};
895 candidate.size = defaults.size;
896 candidate.weight = defaults.weight;
897 candidate.style = defaults.style;
898 MOZ_ASSERT(candidate.Equals(defaults),
899 "Some font style properties not supported");
901 #endif
902 return font;
905 bool nsXPLookAndFeel::GetFontValue(FontID aID, nsString& aName,
906 gfxFontStyle& aStyle) {
907 if (const LookAndFeelFont* cached = sFontCache.Get(aID)) {
908 return LookAndFeelFontToStyle(*cached, aName, aStyle);
910 LookAndFeelFont font;
911 const bool haveFont = NativeGetFont(aID, aName, aStyle);
912 font.haveFont() = haveFont;
913 if (haveFont) {
914 font = StyleToLookAndFeelFont(aName, aStyle);
916 sFontCache.Insert(aID, std::move(font));
917 return haveFont;
920 void nsXPLookAndFeel::RefreshImpl() {
921 // Wipe out our caches.
922 sLightColorCache.Clear();
923 sDarkColorCache.Clear();
924 sFontCache.Clear();
925 sFloatCache.Clear();
926 sIntCache.Clear();
928 // Clear any cached FullLookAndFeel data, which is now invalid.
929 if (XRE_IsParentProcess()) {
930 widget::RemoteLookAndFeel::ClearCachedData();
934 static bool sRecordedLookAndFeelTelemetry = false;
936 void nsXPLookAndFeel::RecordTelemetry() {
937 if (!XRE_IsParentProcess()) {
938 return;
941 if (sRecordedLookAndFeelTelemetry) {
942 return;
945 sRecordedLookAndFeelTelemetry = true;
947 int32_t i;
948 Telemetry::ScalarSet(
949 Telemetry::ScalarID::WIDGET_DARK_MODE,
950 NS_SUCCEEDED(GetIntValue(IntID::SystemUsesDarkTheme, i)) && i != 0);
952 RecordLookAndFeelSpecificTelemetry();
955 namespace mozilla {
957 // static
958 void LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind aKind) {
959 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
960 const char16_t kind[] = {char16_t(aKind), 0};
961 obs->NotifyObservers(nullptr, "look-and-feel-changed", kind);
965 static bool ShouldUseStandinsForNativeColorForNonNativeTheme(
966 const dom::Document& aDoc, LookAndFeel::ColorID aColor) {
967 using ColorID = LookAndFeel::ColorID;
968 if (!aDoc.ShouldAvoidNativeTheme()) {
969 return false;
972 // The native theme doesn't use system colors backgrounds etc, except when in
973 // high-contrast mode, so spoof some of the colors with stand-ins to prevent
974 // lack of contrast.
975 switch (aColor) {
976 case ColorID::Buttonface:
977 case ColorID::Buttontext:
978 case ColorID::MozButtonhoverface:
979 case ColorID::MozButtonhovertext:
980 case ColorID::MozGtkButtonactivetext:
982 case ColorID::MozCombobox:
983 case ColorID::MozComboboxtext:
985 case ColorID::Field:
986 case ColorID::Fieldtext:
988 case ColorID::Graytext:
990 return !PreferenceSheet::PrefsFor(aDoc)
991 .NonNativeThemeShouldUseSystemColors();
993 default:
994 break;
997 return false;
1000 static bool ShouldRespectSystemColorSchemeForChromeDoc() {
1001 #ifdef XP_MACOSX
1002 // macOS follows the global toolbar theme, not the system theme.
1003 // (If the global toolbar theme is set to System, then it *that* follows the
1004 // system theme.)
1005 return false;
1006 #else
1007 // GTK historically has behaved like this. Other platforms don't have support
1008 // for light / dark color schemes yet so it doesn't matter for them.
1009 return true;
1010 #endif
1013 static bool ShouldRespectGlobalToolbarThemeAppearanceForChromeDoc() {
1014 #ifdef XP_MACOSX
1015 // Need to be consistent with AppearanceOverride.mm on macOS, which respects
1016 // the browser.theme.toolbar-theme pref.
1017 // However, if widget.macos.support-dark-appearance is false, we need to
1018 // pretend everything's Light and not follow the toolbar theme.
1019 return StaticPrefs::widget_macos_support_dark_appearance();
1020 #elif defined(MOZ_WIDGET_GTK)
1021 return StaticPrefs::widget_gtk_follow_firefox_theme();
1022 #else
1023 return false;
1024 #endif
1027 LookAndFeel::ColorScheme LookAndFeel::ColorSchemeForChrome() {
1028 if (ShouldRespectGlobalToolbarThemeAppearanceForChromeDoc()) {
1029 switch (StaticPrefs::browser_theme_toolbar_theme()) {
1030 case 0: // Dark
1031 return ColorScheme::Dark;
1032 case 1: // Light
1033 return ColorScheme::Light;
1034 case 2: // System
1035 return SystemColorScheme();
1036 default:
1037 break;
1040 if (ShouldRespectSystemColorSchemeForChromeDoc()) {
1041 return SystemColorScheme();
1043 return ColorScheme::Light;
1046 static LookAndFeel::ColorScheme ColorSchemeForDocument(
1047 const dom::Document& aDoc, bool aContentSupportsDark) {
1048 if (nsContentUtils::IsChromeDoc(&aDoc)) {
1049 return LookAndFeel::ColorSchemeForChrome();
1051 #ifdef MOZ_WIDGET_GTK
1052 if (StaticPrefs::widget_content_allow_gtk_dark_theme()) {
1053 // If users manually tweak allow-gtk-dark-theme, allow content to use the
1054 // system color scheme rather than forcing it to light.
1055 return LookAndFeel::SystemColorScheme();
1057 #endif
1058 return aContentSupportsDark ? LookAndFeel::SystemColorScheme()
1059 : LookAndFeel::ColorScheme::Light;
1062 LookAndFeel::ColorScheme LookAndFeel::ColorSchemeForStyle(
1063 const dom::Document& aDoc, const StyleColorSchemeFlags& aFlags) {
1064 StyleColorSchemeFlags style(aFlags);
1065 if (!style) {
1066 style.bits = aDoc.GetColorSchemeBits();
1068 const bool supportsDark = bool(style & StyleColorSchemeFlags::DARK);
1069 const bool supportsLight = bool(style & StyleColorSchemeFlags::LIGHT);
1070 if (supportsDark && !supportsLight) {
1071 return ColorScheme::Dark;
1073 if (supportsLight && !supportsDark) {
1074 return ColorScheme::Light;
1076 return ColorSchemeForDocument(aDoc, supportsDark);
1079 LookAndFeel::ColorScheme LookAndFeel::ColorSchemeForFrame(
1080 const nsIFrame* aFrame) {
1081 return ColorSchemeForStyle(*aFrame->PresContext()->Document(),
1082 aFrame->StyleUI()->mColorScheme.bits);
1085 // static
1086 Maybe<nscolor> LookAndFeel::GetColor(ColorID aId, ColorScheme aScheme,
1087 UseStandins aUseStandins) {
1088 nscolor result;
1089 nsresult rv = nsLookAndFeel::GetInstance()->GetColorValue(
1090 aId, aScheme, aUseStandins, result);
1091 if (NS_FAILED(rv)) {
1092 return Nothing();
1094 return Some(result);
1097 // Returns whether there is a CSS color name for this color.
1098 static bool ColorIsCSSAccessible(LookAndFeel::ColorID aId) {
1099 using ColorID = LookAndFeel::ColorID;
1101 switch (aId) {
1102 case ColorID::WindowBackground:
1103 case ColorID::WindowForeground:
1104 case ColorID::WidgetBackground:
1105 case ColorID::WidgetForeground:
1106 case ColorID::WidgetSelectBackground:
1107 case ColorID::WidgetSelectForeground:
1108 case ColorID::Widget3DHighlight:
1109 case ColorID::Widget3DShadow:
1110 case ColorID::TextBackground:
1111 case ColorID::TextForeground:
1112 case ColorID::TextSelectBackgroundDisabled:
1113 case ColorID::TextSelectBackgroundAttention:
1114 case ColorID::TextHighlightBackground:
1115 case ColorID::TextHighlightForeground:
1116 case ColorID::ThemedScrollbar:
1117 case ColorID::ThemedScrollbarInactive:
1118 case ColorID::ThemedScrollbarThumb:
1119 case ColorID::ThemedScrollbarThumbActive:
1120 case ColorID::ThemedScrollbarThumbInactive:
1121 case ColorID::ThemedScrollbarThumbHover:
1122 case ColorID::IMERawInputBackground:
1123 case ColorID::IMERawInputForeground:
1124 case ColorID::IMERawInputUnderline:
1125 case ColorID::IMESelectedRawTextBackground:
1126 case ColorID::IMESelectedRawTextForeground:
1127 case ColorID::IMESelectedRawTextUnderline:
1128 case ColorID::IMEConvertedTextBackground:
1129 case ColorID::IMEConvertedTextForeground:
1130 case ColorID::IMEConvertedTextUnderline:
1131 case ColorID::IMESelectedConvertedTextBackground:
1132 case ColorID::IMESelectedConvertedTextForeground:
1133 case ColorID::IMESelectedConvertedTextUnderline:
1134 case ColorID::SpellCheckerUnderline:
1135 return false;
1136 default:
1137 break;
1140 return true;
1143 LookAndFeel::UseStandins LookAndFeel::ShouldAlwaysUseStandinsForColorInContent(
1144 ColorID aId) {
1145 return UseStandins(nsContentUtils::UseStandinsForNativeColors() &&
1146 ColorIsCSSAccessible(aId));
1149 LookAndFeel::UseStandins LookAndFeel::ShouldUseStandins(
1150 const dom::Document& aDoc, ColorID aId) {
1151 if (ShouldUseStandinsForNativeColorForNonNativeTheme(aDoc, aId)) {
1152 return UseStandins::Yes;
1154 if (ShouldAlwaysUseStandinsForColorInContent(aId) == UseStandins::Yes &&
1155 !nsContentUtils::IsChromeDoc(&aDoc)) {
1156 return UseStandins::Yes;
1158 if (aDoc.IsStaticDocument() &&
1159 !PreferenceSheet::ContentPrefs().mUseDocumentColors) {
1160 return UseStandins::Yes;
1162 return UseStandins::No;
1165 Maybe<nscolor> LookAndFeel::GetColor(ColorID aId, const nsIFrame* aFrame) {
1166 const auto* doc = aFrame->PresContext()->Document();
1167 return GetColor(aId, ColorSchemeForFrame(aFrame),
1168 ShouldUseStandins(*doc, aId));
1171 // static
1172 nsresult LookAndFeel::GetInt(IntID aID, int32_t* aResult) {
1173 return nsLookAndFeel::GetInstance()->GetIntValue(aID, *aResult);
1176 // static
1177 nsresult LookAndFeel::GetFloat(FloatID aID, float* aResult) {
1178 return nsLookAndFeel::GetInstance()->GetFloatValue(aID, *aResult);
1181 // static
1182 bool LookAndFeel::GetFont(FontID aID, nsString& aName, gfxFontStyle& aStyle) {
1183 return nsLookAndFeel::GetInstance()->GetFontValue(aID, aName, aStyle);
1186 // static
1187 char16_t LookAndFeel::GetPasswordCharacter() {
1188 return nsLookAndFeel::GetInstance()->GetPasswordCharacterImpl();
1191 // static
1192 bool LookAndFeel::GetEchoPassword() {
1193 if (StaticPrefs::editor_password_mask_delay() >= 0) {
1194 return StaticPrefs::editor_password_mask_delay() > 0;
1196 return nsLookAndFeel::GetInstance()->GetEchoPasswordImpl();
1199 // static
1200 uint32_t LookAndFeel::GetPasswordMaskDelay() {
1201 int32_t delay = StaticPrefs::editor_password_mask_delay();
1202 if (delay < 0) {
1203 return nsLookAndFeel::GetInstance()->GetPasswordMaskDelayImpl();
1205 return delay;
1208 void LookAndFeel::GetThemeInfo(nsACString& aOut) {
1209 nsLookAndFeel::GetInstance()->GetThemeInfo(aOut);
1212 // static
1213 void LookAndFeel::Refresh() {
1214 nsLookAndFeel::GetInstance()->RefreshImpl();
1215 nsNativeBasicTheme::LookAndFeelChanged();
1218 // static
1219 void LookAndFeel::NativeInit() { nsLookAndFeel::GetInstance()->NativeInit(); }
1221 // static
1222 void LookAndFeel::SetData(widget::FullLookAndFeel&& aTables) {
1223 nsLookAndFeel::GetInstance()->SetDataImpl(std::move(aTables));
1226 } // namespace mozilla