Bug 1692840 - Add "Open a New Tab" at the top of the tabstrip context menu. r=Gijs
[gecko.git] / widget / nsXPLookAndFeel.cpp
blob8c46321b8f288e32f307e2b83d7bb74d02fbc8c4
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_editor.h"
26 #include "mozilla/StaticPrefs_findbar.h"
27 #include "mozilla/StaticPrefs_ui.h"
28 #include "mozilla/StaticPrefs_widget.h"
29 #include "mozilla/dom/Document.h"
30 #include "mozilla/PreferenceSheet.h"
31 #include "mozilla/gfx/2D.h"
32 #include "mozilla/widget/WidgetMessageUtils.h"
33 #include "mozilla/Telemetry.h"
34 #include "mozilla/TelemetryScalarEnums.h"
36 #include "gfxPlatform.h"
37 #include "gfxFont.h"
39 #include "qcms.h"
41 #ifdef DEBUG
42 # include "nsSize.h"
43 #endif
45 using namespace mozilla;
47 using IntID = mozilla::LookAndFeel::IntID;
48 using FloatID = mozilla::LookAndFeel::FloatID;
49 using ColorID = mozilla::LookAndFeel::ColorID;
50 using FontID = mozilla::LookAndFeel::FontID;
52 template <typename Index, typename Value, Index kEnd>
53 class EnumeratedCache {
54 static constexpr uint32_t ChunkFor(Index aIndex) {
55 return uint32_t(aIndex) >> 5; // >> 5 is the same as / 32.
57 static constexpr uint32_t BitFor(Index aIndex) {
58 return 1u << (uint32_t(aIndex) & 31);
60 static constexpr uint32_t kChunks = ChunkFor(kEnd) + 1;
62 mozilla::EnumeratedArray<Index, kEnd, Value> mEntries;
63 uint32_t mValidity[kChunks] = {0};
65 public:
66 constexpr EnumeratedCache() = default;
68 bool IsValid(Index aIndex) const {
69 return mValidity[ChunkFor(aIndex)] & BitFor(aIndex);
72 const Value* Get(Index aIndex) const {
73 return IsValid(aIndex) ? &mEntries[aIndex] : nullptr;
76 void Insert(Index aIndex, Value aValue) {
77 mValidity[ChunkFor(aIndex)] |= BitFor(aIndex);
78 mEntries[aIndex] = aValue;
81 void Remove(Index aIndex) {
82 mValidity[ChunkFor(aIndex)] &= ~BitFor(aIndex);
83 mEntries[aIndex] = Value();
86 void Clear() {
87 for (auto& chunk : mValidity) {
88 chunk = 0;
90 for (auto& entry : mEntries) {
91 entry = Value();
96 static EnumeratedCache<ColorID, Maybe<nscolor>, ColorID::End> sLightColorCache;
97 static EnumeratedCache<ColorID, Maybe<nscolor>, ColorID::End> sDarkColorCache;
98 static EnumeratedCache<FloatID, Maybe<float>, FloatID::End> sFloatCache;
99 static EnumeratedCache<IntID, Maybe<int32_t>, IntID::End> sIntCache;
100 static EnumeratedCache<FontID, widget::LookAndFeelFont, FontID::End> sFontCache;
102 // To make one of these prefs toggleable from a reftest add a user
103 // pref in testing/profiles/reftest/user.js. For example, to make
104 // ui.useAccessibilityTheme toggleable, add:
106 // user_pref("ui.useAccessibilityTheme", 0);
108 // This needs to be of the same length and in the same order as
109 // LookAndFeel::IntID values.
110 static const char sIntPrefs[][42] = {
111 "ui.caretBlinkTime",
112 "ui.caretWidth",
113 "ui.caretVisibleWithSelection",
114 "ui.selectTextfieldsOnKeyFocus",
115 "ui.submenuDelay",
116 "ui.menusCanOverlapOSBar",
117 "ui.useOverlayScrollbars",
118 "ui.allowOverlayScrollbarsOverlap",
119 "ui.showHideScrollbars",
120 "ui.skipNavigatingDisabledMenuItem",
121 "ui.dragThresholdX",
122 "ui.dragThresholdY",
123 "ui.useAccessibilityTheme",
124 "ui.scrollArrowStyle",
125 "ui.scrollSliderStyle",
126 "ui.scrollButtonLeftMouseButtonAction",
127 "ui.scrollButtonMiddleMouseButtonAction",
128 "ui.scrollButtonRightMouseButtonAction",
129 "ui.treeOpenDelay",
130 "ui.treeCloseDelay",
131 "ui.treeLazyScrollDelay",
132 "ui.treeScrollDelay",
133 "ui.treeScrollLinesMax",
134 "accessibility.tabfocus", // Weird one...
135 "ui.chosenMenuItemsShouldBlink",
136 "ui.windowsAccentColorInTitlebar",
137 "ui.windowsDefaultTheme",
138 "ui.dwmCompositor",
139 "ui.windowsClassic",
140 "ui.windowsGlass",
141 "ui.macGraphiteTheme",
142 "ui.macBigSurTheme",
143 "ui.alertNotificationOrigin",
144 "ui.scrollToClick",
145 "ui.IMERawInputUnderlineStyle",
146 "ui.IMESelectedRawTextUnderlineStyle",
147 "ui.IMEConvertedTextUnderlineStyle",
148 "ui.IMESelectedConvertedTextUnderlineStyle",
149 "ui.SpellCheckerUnderlineStyle",
150 "ui.menuBarDrag",
151 "ui.windowsThemeIdentifier",
152 "ui.operatingSystemVersionIdentifier",
153 "ui.scrollbarButtonAutoRepeatBehavior",
154 "ui.tooltipDelay",
155 "ui.swipeAnimationEnabled",
156 "ui.scrollbarDisplayOnMouseMove",
157 "ui.scrollbarFadeBeginDelay",
158 "ui.scrollbarFadeDuration",
159 "ui.contextMenuOffsetVertical",
160 "ui.contextMenuOffsetHorizontal",
161 "ui.GtkCSDAvailable",
162 "ui.GtkCSDHideTitlebarByDefault",
163 "ui.GtkCSDTransparentBackground",
164 "ui.GtkCSDMinimizeButton",
165 "ui.GtkCSDMaximizeButton",
166 "ui.GtkCSDCloseButton",
167 "ui.GtkCSDMinimizeButtonPosition",
168 "ui.GtkCSDMaximizeButtonPosition",
169 "ui.GtkCSDCloseButtonPosition",
170 "ui.GtkCSDReversedPlacement",
171 "ui.systemUsesDarkTheme",
172 "ui.prefersReducedMotion",
173 "ui.primaryPointerCapabilities",
174 "ui.allPointerCapabilities",
175 "ui.systemVerticalScrollbarWidth",
176 "ui.systemHorizontalScrollbarHeight",
179 static_assert(ArrayLength(sIntPrefs) == size_t(LookAndFeel::IntID::End),
180 "Should have a pref for each int value");
182 // This array MUST be kept in the same order as the float id list in
183 // LookAndFeel.h
184 static const char sFloatPrefs[][37] = {
185 "ui.IMEUnderlineRelativeSize",
186 "ui.SpellCheckerUnderlineRelativeSize",
187 "ui.caretAspectRatio",
188 "ui.textScaleFactor",
191 static_assert(ArrayLength(sFloatPrefs) == size_t(LookAndFeel::FloatID::End),
192 "Should have a pref for each float value");
194 // This array MUST be kept in the same order as the color list in
195 // specified/color.rs
196 static const char sColorPrefs[][41] = {
197 "ui.windowBackground",
198 "ui.windowForeground",
199 "ui.widgetBackground",
200 "ui.widgetForeground",
201 "ui.widgetSelectBackground",
202 "ui.widgetSelectForeground",
203 "ui.widget3DHighlight",
204 "ui.widget3DShadow",
205 "ui.textBackground",
206 "ui.textForeground",
207 "ui.textSelectBackground",
208 "ui.textSelectForeground",
209 "ui.textSelectForegroundCustom",
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-gtk-info-bar-text",
313 "ui.-moz-colheadertext",
314 "ui.-moz-colheaderhovertext"};
316 static_assert(ArrayLength(sColorPrefs) == size_t(LookAndFeel::ColorID::End),
317 "Should have a pref for each color value");
319 bool nsXPLookAndFeel::sInitialized = false;
321 nsXPLookAndFeel* nsXPLookAndFeel::sInstance = nullptr;
322 bool nsXPLookAndFeel::sShutdown = false;
324 // static
325 nsXPLookAndFeel* nsXPLookAndFeel::GetInstance() {
326 if (sInstance) {
327 return sInstance;
330 NS_ENSURE_TRUE(!sShutdown, nullptr);
332 // If we're in a content process, then the parent process will have supplied
333 // us with an initial FullLookAndFeel object.
334 // We grab this data from the ContentChild,
335 // where it's been temporarily stashed, and initialize our new LookAndFeel
336 // object with it.
338 FullLookAndFeel* lnf = nullptr;
340 if (auto* cc = mozilla::dom::ContentChild::GetSingleton()) {
341 lnf = &cc->BorrowLookAndFeelData();
344 if (lnf) {
345 sInstance = new widget::RemoteLookAndFeel(std::move(*lnf));
346 } else if (gfxPlatform::IsHeadless()) {
347 sInstance = new widget::HeadlessLookAndFeel();
348 } else {
349 sInstance = new nsLookAndFeel();
352 // This is only ever used once during initialization, and can be cleared now.
353 if (lnf) {
354 *lnf = {};
357 nsNativeBasicTheme::Init();
358 return sInstance;
361 // static
362 void nsXPLookAndFeel::Shutdown() {
363 if (sShutdown) {
364 return;
367 sShutdown = true;
368 delete sInstance;
369 sInstance = nullptr;
371 // This keeps strings alive, so need to clear to make leak checking happy.
372 sFontCache.Clear();
374 nsNativeBasicTheme::Shutdown();
377 static void IntPrefChanged() {
378 // Int prefs can't change our system colors or fonts.
379 LookAndFeel::NotifyChangedAllWindows(
380 widget::ThemeChangeKind::MediaQueriesOnly);
383 static void FloatPrefChanged() {
384 // Float prefs can't change our system colors or fonts.
385 LookAndFeel::NotifyChangedAllWindows(
386 widget::ThemeChangeKind::MediaQueriesOnly);
389 static void ColorPrefChanged() {
390 // Color prefs affect style, because they by definition change system colors.
391 LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind::Style);
394 // static
395 void nsXPLookAndFeel::OnPrefChanged(const char* aPref, void* aClosure) {
396 nsDependentCString prefName(aPref);
397 for (const char* pref : sIntPrefs) {
398 if (prefName.Equals(pref)) {
399 IntPrefChanged();
400 return;
404 for (const char* pref : sFloatPrefs) {
405 if (prefName.Equals(pref)) {
406 FloatPrefChanged();
407 return;
411 for (const char* pref : sColorPrefs) {
412 if (prefName.Equals(pref)) {
413 ColorPrefChanged();
414 return;
419 static constexpr nsLiteralCString kBoolMediaQueryPrefs[] = {
420 "browser.proton.enabled"_ns,
421 "browser.proton.contextmenus.enabled"_ns,
422 "browser.proton.modals.enabled"_ns,
423 "browser.proton.doorhangers.enabled"_ns,
424 "browser.proton.places-tooltip.enabled"_ns,
427 // Read values from the user's preferences.
428 // This is done once at startup, but since the user's preferences
429 // haven't actually been read yet at that time, we also have to
430 // set a callback to inform us of changes to each pref.
431 void nsXPLookAndFeel::Init() {
432 MOZ_RELEASE_ASSERT(NS_IsMainThread());
434 // Say we're already initialized, and take the chance that it might fail;
435 // protects against some other process writing to our static variables.
436 sInitialized = true;
438 // XXX If we could reorganize the pref names, we should separate the branch
439 // for each types. Then, we could reduce the unnecessary loop from
440 // nsXPLookAndFeel::OnPrefChanged().
441 Preferences::RegisterPrefixCallback(OnPrefChanged, "ui.");
442 // We really do just want the accessibility.tabfocus pref, not other prefs
443 // that start with that string.
444 Preferences::RegisterCallback(OnPrefChanged, "accessibility.tabfocus");
446 for (auto& pref : kBoolMediaQueryPrefs) {
447 Preferences::RegisterCallback(
448 [](const char*, void*) {
449 LookAndFeel::NotifyChangedAllWindows(
450 widget::ThemeChangeKind::MediaQueriesOnly);
452 pref);
456 nsXPLookAndFeel::~nsXPLookAndFeel() {
457 NS_ASSERTION(sInstance == this,
458 "This destroying instance isn't the singleton instance");
459 sInstance = nullptr;
462 static bool IsSpecialColor(LookAndFeel::ColorID aID, nscolor aColor) {
463 using ColorID = LookAndFeel::ColorID;
465 switch (aID) {
466 case ColorID::TextSelectForeground:
467 return aColor == NS_DONT_CHANGE_COLOR;
468 case ColorID::IMESelectedRawTextBackground:
469 case ColorID::IMESelectedConvertedTextBackground:
470 case ColorID::IMERawInputBackground:
471 case ColorID::IMEConvertedTextBackground:
472 case ColorID::IMESelectedRawTextForeground:
473 case ColorID::IMESelectedConvertedTextForeground:
474 case ColorID::IMERawInputForeground:
475 case ColorID::IMEConvertedTextForeground:
476 case ColorID::IMERawInputUnderline:
477 case ColorID::IMEConvertedTextUnderline:
478 case ColorID::IMESelectedRawTextUnderline:
479 case ColorID::IMESelectedConvertedTextUnderline:
480 case ColorID::SpellCheckerUnderline:
481 return NS_IS_SELECTION_SPECIAL_COLOR(aColor);
482 default:
483 break;
486 * In GetColor(), every color that is not a special color is color
487 * corrected. Use false to make other colors color corrected.
489 return false;
492 nscolor nsXPLookAndFeel::GetStandinForNativeColor(ColorID aID) {
493 // The stand-in colors are taken from the Windows 7 Aero theme
494 // except Mac-specific colors which are taken from Mac OS 10.7.
496 #define COLOR(name_, r, g, b) \
497 case ColorID::name_: \
498 return NS_RGB(r, g, b);
500 switch (aID) {
501 // CSS 2 colors:
502 COLOR(Activeborder, 0xB4, 0xB4, 0xB4)
503 COLOR(Activecaption, 0x99, 0xB4, 0xD1)
504 COLOR(Appworkspace, 0xAB, 0xAB, 0xAB)
505 COLOR(Background, 0x00, 0x00, 0x00)
506 COLOR(Buttonface, 0xF0, 0xF0, 0xF0)
507 COLOR(Buttonhighlight, 0xFF, 0xFF, 0xFF)
508 COLOR(Buttonshadow, 0xA0, 0xA0, 0xA0)
509 COLOR(Buttontext, 0x00, 0x00, 0x00)
510 COLOR(Captiontext, 0x00, 0x00, 0x00)
511 COLOR(Graytext, 0x6D, 0x6D, 0x6D)
512 COLOR(Highlight, 0x33, 0x99, 0xFF)
513 COLOR(Highlighttext, 0xFF, 0xFF, 0xFF)
514 COLOR(Inactiveborder, 0xF4, 0xF7, 0xFC)
515 COLOR(Inactivecaption, 0xBF, 0xCD, 0xDB)
516 COLOR(Inactivecaptiontext, 0x43, 0x4E, 0x54)
517 COLOR(Infobackground, 0xFF, 0xFF, 0xE1)
518 COLOR(Infotext, 0x00, 0x00, 0x00)
519 COLOR(Menu, 0xF0, 0xF0, 0xF0)
520 COLOR(Menutext, 0x00, 0x00, 0x00)
521 COLOR(Scrollbar, 0xC8, 0xC8, 0xC8)
522 COLOR(Threeddarkshadow, 0x69, 0x69, 0x69)
523 COLOR(Threedface, 0xF0, 0xF0, 0xF0)
524 COLOR(Threedhighlight, 0xFF, 0xFF, 0xFF)
525 COLOR(Threedlightshadow, 0xE3, 0xE3, 0xE3)
526 COLOR(Threedshadow, 0xA0, 0xA0, 0xA0)
527 COLOR(Window, 0xFF, 0xFF, 0xFF)
528 COLOR(Windowframe, 0x64, 0x64, 0x64)
529 COLOR(Windowtext, 0x00, 0x00, 0x00)
530 COLOR(MozButtondefault, 0x69, 0x69, 0x69)
531 COLOR(Field, 0xFF, 0xFF, 0xFF)
532 COLOR(Fieldtext, 0x00, 0x00, 0x00)
533 COLOR(MozDialog, 0xF0, 0xF0, 0xF0)
534 COLOR(MozDialogtext, 0x00, 0x00, 0x00)
535 COLOR(MozColheadertext, 0x00, 0x00, 0x00)
536 COLOR(MozColheaderhovertext, 0x00, 0x00, 0x00)
537 COLOR(MozDragtargetzone, 0xFF, 0xFF, 0xFF)
538 COLOR(MozCellhighlight, 0xF0, 0xF0, 0xF0)
539 COLOR(MozCellhighlighttext, 0x00, 0x00, 0x00)
540 COLOR(MozHtmlCellhighlight, 0x33, 0x99, 0xFF)
541 COLOR(MozHtmlCellhighlighttext, 0xFF, 0xFF, 0xFF)
542 COLOR(MozButtonhoverface, 0xF0, 0xF0, 0xF0)
543 COLOR(MozGtkButtonactivetext, 0x00, 0x00, 0x00)
544 COLOR(MozButtonhovertext, 0x00, 0x00, 0x00)
545 COLOR(MozMenuhover, 0x33, 0x99, 0xFF)
546 COLOR(MozMenuhovertext, 0x00, 0x00, 0x00)
547 COLOR(MozMenubartext, 0x00, 0x00, 0x00)
548 COLOR(MozMenubarhovertext, 0x00, 0x00, 0x00)
549 COLOR(MozOddtreerow, 0xFF, 0xFF, 0xFF)
550 COLOR(MozMacChromeActive, 0xB2, 0xB2, 0xB2)
551 COLOR(MozMacChromeInactive, 0xE1, 0xE1, 0xE1)
552 COLOR(MozMacFocusring, 0x60, 0x9D, 0xD7)
553 COLOR(MozMacMenuselect, 0x38, 0x75, 0xD7)
554 COLOR(MozMacMenushadow, 0xA3, 0xA3, 0xA3)
555 COLOR(MozMacMenutextdisable, 0x88, 0x88, 0x88)
556 COLOR(MozMacMenutextselect, 0xFF, 0xFF, 0xFF)
557 COLOR(MozMacDisabledtoolbartext, 0x3F, 0x3F, 0x3F)
558 COLOR(MozMacSecondaryhighlight, 0xD4, 0xD4, 0xD4)
559 COLOR(MozMacVibrantTitlebarLight, 0xf7, 0xf7, 0xf7)
560 COLOR(MozMacVibrantTitlebarDark, 0x28, 0x28, 0x28)
561 COLOR(MozMacMenupopup, 0xe6, 0xe6, 0xe6)
562 COLOR(MozMacMenuitem, 0xe6, 0xe6, 0xe6)
563 COLOR(MozMacActiveMenuitem, 0x0a, 0x64, 0xdc)
564 COLOR(MozMacSourceList, 0xf7, 0xf7, 0xf7)
565 COLOR(MozMacSourceListSelection, 0xc8, 0xc8, 0xc8)
566 COLOR(MozMacActiveSourceListSelection, 0x0a, 0x64, 0xdc)
567 COLOR(MozMacTooltip, 0xf7, 0xf7, 0xf7)
568 // Seems to be the default color (hardcoded because of bug 1065998)
569 COLOR(MozWinMediatext, 0xFF, 0xFF, 0xFF)
570 COLOR(MozWinCommunicationstext, 0xFF, 0xFF, 0xFF)
571 COLOR(MozNativehyperlinktext, 0x00, 0x66, 0xCC)
572 COLOR(MozComboboxtext, 0x00, 0x00, 0x00)
573 COLOR(MozCombobox, 0xFF, 0xFF, 0xFF)
574 default:
575 break;
577 return NS_RGB(0xFF, 0xFF, 0xFF);
580 // Uncomment the #define below if you want to debug system color use in a skin
581 // that uses them. When set, it will make all system color pairs that are
582 // appropriate for foreground/background pairing the same. This means if the
583 // skin is using system colors correctly you will not be able to see *any* text.
585 // #define DEBUG_SYSTEM_COLOR_USE
587 #ifdef DEBUG_SYSTEM_COLOR_USE
588 static nsresult SystemColorUseDebuggingColor(LookAndFeel::ColorID aID,
589 nscolor& aResult) {
590 using ColorID = LookAndFeel::ColorID;
592 switch (aID) {
593 // css2 http://www.w3.org/TR/REC-CSS2/ui.html#system-colors
594 case ColorID::Activecaption:
595 // active window caption background
596 case ColorID::Captiontext:
597 // text in active window caption
598 aResult = NS_RGB(0xff, 0x00, 0x00);
599 break;
601 case ColorID::Highlight:
602 // background of selected item
603 case ColorID::Highlighttext:
604 // text of selected item
605 aResult = NS_RGB(0xff, 0xff, 0x00);
606 break;
608 case ColorID::Inactivecaption:
609 // inactive window caption
610 case ColorID::Inactivecaptiontext:
611 // text in inactive window caption
612 aResult = NS_RGB(0x66, 0x66, 0x00);
613 break;
615 case ColorID::Infobackground:
616 // tooltip background color
617 case ColorID::Infotext:
618 // tooltip text color
619 aResult = NS_RGB(0x00, 0xff, 0x00);
620 break;
622 case ColorID::Menu:
623 // menu background
624 case ColorID::Menutext:
625 // menu text
626 aResult = NS_RGB(0x00, 0xff, 0xff);
627 break;
629 case ColorID::Threedface:
630 case ColorID::Buttonface:
631 // 3-D face color
632 case ColorID::Buttontext:
633 // text on push buttons
634 aResult = NS_RGB(0x00, 0x66, 0x66);
635 break;
637 case ColorID::Window:
638 case ColorID::Windowtext:
639 aResult = NS_RGB(0x00, 0x00, 0xff);
640 break;
642 // from the CSS3 working draft (not yet finalized)
643 // http://www.w3.org/tr/2000/wd-css3-userint-20000216.html#color
645 case ColorID::Field:
646 case ColorID::Fieldtext:
647 aResult = NS_RGB(0xff, 0x00, 0xff);
648 break;
650 case ColorID::MozDialog:
651 case ColorID::MozDialogtext:
652 aResult = NS_RGB(0x66, 0x00, 0x66);
653 break;
655 default:
656 return NS_ERROR_NOT_AVAILABLE;
659 return NS_OK;
661 #endif
663 static nsresult GetColorFromPref(LookAndFeel::ColorID aID, nscolor& aResult) {
664 const char* prefName = sColorPrefs[size_t(aID)];
665 nsAutoCString colorStr;
666 MOZ_TRY(Preferences::GetCString(prefName, colorStr));
667 if (!ServoCSSParser::ComputeColor(nullptr, NS_RGB(0, 0, 0), colorStr,
668 &aResult)) {
669 return NS_ERROR_FAILURE;
671 return NS_OK;
674 // All these routines will return NS_OK if they have a value,
675 // in which case the nsLookAndFeel should use that value;
676 // otherwise we'll return NS_ERROR_NOT_AVAILABLE, in which case, the
677 // platform-specific nsLookAndFeel should use its own values instead.
678 nsresult nsXPLookAndFeel::GetColorValue(ColorID aID, ColorScheme aScheme,
679 UseStandins aUseStandins,
680 nscolor& aResult) {
681 if (!sInitialized) {
682 Init();
685 #ifdef DEBUG_SYSTEM_COLOR_USE
686 if (NS_SUCCEEDED(SystemColorUseDebuggingColor(aID, aResult))) {
687 return NS_OK;
689 #endif
691 if (aUseStandins == UseStandins::Yes) {
692 aResult = GetStandinForNativeColor(aID);
693 return NS_OK;
696 auto& cache =
697 aScheme == ColorScheme::Light ? sLightColorCache : sDarkColorCache;
698 if (const auto* cached = cache.Get(aID)) {
699 if (cached->isNothing()) {
700 return NS_ERROR_FAILURE;
702 aResult = cached->value();
703 return NS_OK;
706 if (NS_SUCCEEDED(GetColorFromPref(aID, aResult))) {
707 cache.Insert(aID, Some(aResult));
708 return NS_OK;
711 if (NS_SUCCEEDED(NativeGetColor(aID, aScheme, aResult))) {
712 if (gfxPlatform::GetCMSMode() == CMSMode::All &&
713 !IsSpecialColor(aID, aResult)) {
714 qcms_transform* transform = gfxPlatform::GetCMSInverseRGBTransform();
715 if (transform) {
716 uint8_t color[4];
717 color[0] = NS_GET_R(aResult);
718 color[1] = NS_GET_G(aResult);
719 color[2] = NS_GET_B(aResult);
720 color[3] = NS_GET_A(aResult);
721 qcms_transform_data(transform, color, color, 1);
722 aResult = NS_RGBA(color[0], color[1], color[2], color[3]);
726 // NOTE: Servo holds a lock and the main thread is paused, so writing to the
727 // global cache here is fine.
728 cache.Insert(aID, Some(aResult));
729 return NS_OK;
732 cache.Insert(aID, Nothing());
733 return NS_ERROR_FAILURE;
736 nsresult nsXPLookAndFeel::GetIntValue(IntID aID, int32_t& aResult) {
737 if (!sInitialized) {
738 Init();
741 if (const auto* cached = sIntCache.Get(aID)) {
742 if (cached->isNothing()) {
743 return NS_ERROR_FAILURE;
745 aResult = cached->value();
746 return NS_OK;
749 if (NS_SUCCEEDED(Preferences::GetInt(sIntPrefs[size_t(aID)], &aResult))) {
750 sIntCache.Insert(aID, Some(aResult));
751 return NS_OK;
754 if (NS_FAILED(NativeGetInt(aID, aResult))) {
755 sIntCache.Insert(aID, Nothing());
756 return NS_ERROR_FAILURE;
759 sIntCache.Insert(aID, Some(aResult));
760 return NS_OK;
763 nsresult nsXPLookAndFeel::GetFloatValue(FloatID aID, float& aResult) {
764 if (!sInitialized) {
765 Init();
768 if (const auto* cached = sFloatCache.Get(aID)) {
769 if (cached->isNothing()) {
770 return NS_ERROR_FAILURE;
772 aResult = cached->value();
773 return NS_OK;
776 int32_t pref = 0;
777 if (NS_SUCCEEDED(Preferences::GetInt(sFloatPrefs[size_t(aID)], &pref))) {
778 aResult = float(pref) / 100.0f;
779 sFloatCache.Insert(aID, Some(aResult));
780 return NS_OK;
783 if (NS_FAILED(NativeGetFloat(aID, aResult))) {
784 sFloatCache.Insert(aID, Nothing());
785 return NS_ERROR_FAILURE;
788 sFloatCache.Insert(aID, Some(aResult));
789 return NS_OK;
792 bool nsXPLookAndFeel::LookAndFeelFontToStyle(const LookAndFeelFont& aFont,
793 nsString& aName,
794 gfxFontStyle& aStyle) {
795 if (!aFont.haveFont()) {
796 return false;
798 aName = aFont.name();
799 aStyle = gfxFontStyle();
800 aStyle.size = aFont.size();
801 aStyle.weight = FontWeight(aFont.weight());
802 aStyle.style =
803 aFont.italic() ? FontSlantStyle::Italic() : FontSlantStyle::Normal();
804 aStyle.systemFont = true;
805 return true;
808 widget::LookAndFeelFont nsXPLookAndFeel::StyleToLookAndFeelFont(
809 const nsAString& aName, const gfxFontStyle& aStyle) {
810 LookAndFeelFont font;
811 font.haveFont() = true;
812 font.name() = aName;
813 font.size() = aStyle.size;
814 font.weight() = aStyle.weight.ToFloat();
815 font.italic() = aStyle.style.IsItalic();
816 MOZ_ASSERT(aStyle.style.IsNormal() || aStyle.style.IsItalic(),
817 "Cannot handle oblique font style");
818 #ifdef DEBUG
820 // Assert that all the remaining font style properties have their
821 // default values.
822 gfxFontStyle candidate = aStyle;
823 gfxFontStyle defaults{};
824 candidate.size = defaults.size;
825 candidate.weight = defaults.weight;
826 candidate.style = defaults.style;
827 MOZ_ASSERT(candidate.Equals(defaults),
828 "Some font style properties not supported");
830 #endif
831 return font;
834 bool nsXPLookAndFeel::GetFontValue(FontID aID, nsString& aName,
835 gfxFontStyle& aStyle) {
836 if (const LookAndFeelFont* cached = sFontCache.Get(aID)) {
837 return LookAndFeelFontToStyle(*cached, aName, aStyle);
839 LookAndFeelFont font;
840 const bool haveFont = NativeGetFont(aID, aName, aStyle);
841 font.haveFont() = haveFont;
842 if (haveFont) {
843 font = StyleToLookAndFeelFont(aName, aStyle);
845 sFontCache.Insert(aID, std::move(font));
846 return haveFont;
849 void nsXPLookAndFeel::RefreshImpl() {
850 // Wipe out our caches.
851 sLightColorCache.Clear();
852 sDarkColorCache.Clear();
853 sFontCache.Clear();
854 sFloatCache.Clear();
855 sIntCache.Clear();
857 // Clear any cached FullLookAndFeel data, which is now invalid.
858 if (XRE_IsParentProcess()) {
859 widget::RemoteLookAndFeel::ClearCachedData();
863 static bool sRecordedLookAndFeelTelemetry = false;
865 void nsXPLookAndFeel::RecordTelemetry() {
866 if (!XRE_IsParentProcess()) {
867 return;
870 if (sRecordedLookAndFeelTelemetry) {
871 return;
874 sRecordedLookAndFeelTelemetry = true;
876 int32_t i;
877 Telemetry::ScalarSet(
878 Telemetry::ScalarID::WIDGET_DARK_MODE,
879 NS_SUCCEEDED(GetIntValue(IntID::SystemUsesDarkTheme, i)) && i != 0);
881 RecordLookAndFeelSpecificTelemetry();
884 namespace mozilla {
886 // static
887 void LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind aKind) {
888 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
889 obs->NotifyObservers(nullptr, "look-and-feel-changed",
890 reinterpret_cast<char16_t*>(uintptr_t(aKind)));
894 static bool ShouldUseStandinsForNativeColorForNonNativeTheme(
895 const dom::Document& aDoc, LookAndFeel::ColorID aColor) {
896 using ColorID = LookAndFeel::ColorID;
897 if (!aDoc.ShouldAvoidNativeTheme()) {
898 return false;
901 // The native theme doesn't use system colors backgrounds etc, except when in
902 // high-contrast mode, so spoof some of the colors with stand-ins to prevent
903 // lack of contrast.
904 switch (aColor) {
905 case ColorID::Buttonface:
906 case ColorID::Buttontext:
907 case ColorID::MozButtonhoverface:
908 case ColorID::MozButtonhovertext:
909 case ColorID::MozGtkButtonactivetext:
911 case ColorID::MozCombobox:
912 case ColorID::MozComboboxtext:
914 case ColorID::Field:
915 case ColorID::Fieldtext:
917 case ColorID::Graytext:
919 return !PreferenceSheet::PrefsFor(aDoc)
920 .NonNativeThemeShouldUseSystemColors();
922 default:
923 break;
926 return false;
929 LookAndFeel::ColorScheme LookAndFeel::ColorSchemeForDocument(
930 const dom::Document& aDoc) {
931 #ifdef XP_MACOSX
932 if (nsContentUtils::IsChromeDoc(&aDoc) &&
933 StaticPrefs::widget_macos_respect_system_appearance()) {
934 const auto* doc = &aDoc;
935 while (const auto* parent = doc->GetInProcessParentDocument()) {
936 doc = parent;
938 switch (doc->ThreadSafeGetDocumentLWTheme()) {
939 case dom::Document::Doc_Theme_None:
940 return LookAndFeel::SystemColorScheme();
941 case dom::Document::Doc_Theme_Dark:
942 return LookAndFeel::ColorScheme::Light;
943 case dom::Document::Doc_Theme_Bright:
944 // NOTE(emilio): This looks backwards, but it's actually correct.
945 // Doc_Theme_Bright means that the theme has bright _text_ (and thus
946 // dark background). Tricky!
947 return LookAndFeel::ColorScheme::Dark;
948 default:
949 break;
952 #endif
953 return LookAndFeel::ColorScheme::Light;
956 // static
957 Maybe<nscolor> LookAndFeel::GetColor(ColorID aId, ColorScheme aScheme,
958 UseStandins aUseStandins) {
959 nscolor result;
960 nsresult rv = nsLookAndFeel::GetInstance()->GetColorValue(
961 aId, aScheme, aUseStandins, result);
962 if (NS_FAILED(rv)) {
963 return Nothing();
965 return Some(result);
968 // Returns whether there is a CSS color name for this color.
969 static bool ColorIsCSSAccessible(LookAndFeel::ColorID aId) {
970 using ColorID = LookAndFeel::ColorID;
972 switch (aId) {
973 case ColorID::WindowBackground:
974 case ColorID::WindowForeground:
975 case ColorID::WidgetBackground:
976 case ColorID::WidgetForeground:
977 case ColorID::WidgetSelectBackground:
978 case ColorID::WidgetSelectForeground:
979 case ColorID::Widget3DHighlight:
980 case ColorID::Widget3DShadow:
981 case ColorID::TextBackground:
982 case ColorID::TextForeground:
983 case ColorID::TextSelectBackground:
984 case ColorID::TextSelectForeground:
985 case ColorID::TextSelectBackgroundDisabled:
986 case ColorID::TextSelectBackgroundAttention:
987 case ColorID::TextHighlightBackground:
988 case ColorID::TextHighlightForeground:
989 case ColorID::IMERawInputBackground:
990 case ColorID::IMERawInputForeground:
991 case ColorID::IMERawInputUnderline:
992 case ColorID::IMESelectedRawTextBackground:
993 case ColorID::IMESelectedRawTextForeground:
994 case ColorID::IMESelectedRawTextUnderline:
995 case ColorID::IMEConvertedTextBackground:
996 case ColorID::IMEConvertedTextForeground:
997 case ColorID::IMEConvertedTextUnderline:
998 case ColorID::IMESelectedConvertedTextBackground:
999 case ColorID::IMESelectedConvertedTextForeground:
1000 case ColorID::IMESelectedConvertedTextUnderline:
1001 case ColorID::SpellCheckerUnderline:
1002 return false;
1003 default:
1004 break;
1007 return true;
1010 Maybe<nscolor> LookAndFeel::GetColor(ColorID aId, const dom::Document& aDoc) {
1011 const bool useStandins =
1012 ShouldUseStandinsForNativeColorForNonNativeTheme(aDoc, aId) ||
1013 (nsContentUtils::UseStandinsForNativeColors() &&
1014 !nsContentUtils::IsChromeDoc(&aDoc) && ColorIsCSSAccessible(aId));
1015 return GetColor(aId, ColorSchemeForDocument(aDoc), UseStandins(useStandins));
1018 Maybe<nscolor> LookAndFeel::GetColor(ColorID aId, const nsIFrame* aFrame) {
1019 return GetColor(aId, *aFrame->PresContext()->Document());
1022 // static
1023 nsresult LookAndFeel::GetInt(IntID aID, int32_t* aResult) {
1024 return nsLookAndFeel::GetInstance()->GetIntValue(aID, *aResult);
1027 // static
1028 nsresult LookAndFeel::GetFloat(FloatID aID, float* aResult) {
1029 return nsLookAndFeel::GetInstance()->GetFloatValue(aID, *aResult);
1032 // static
1033 bool LookAndFeel::GetFont(FontID aID, nsString& aName, gfxFontStyle& aStyle) {
1034 return nsLookAndFeel::GetInstance()->GetFontValue(aID, aName, aStyle);
1037 // static
1038 char16_t LookAndFeel::GetPasswordCharacter() {
1039 return nsLookAndFeel::GetInstance()->GetPasswordCharacterImpl();
1042 // static
1043 bool LookAndFeel::GetEchoPassword() {
1044 if (StaticPrefs::editor_password_mask_delay() >= 0) {
1045 return StaticPrefs::editor_password_mask_delay() > 0;
1047 return nsLookAndFeel::GetInstance()->GetEchoPasswordImpl();
1050 // static
1051 uint32_t LookAndFeel::GetPasswordMaskDelay() {
1052 int32_t delay = StaticPrefs::editor_password_mask_delay();
1053 if (delay < 0) {
1054 return nsLookAndFeel::GetInstance()->GetPasswordMaskDelayImpl();
1056 return delay;
1059 // static
1060 void LookAndFeel::Refresh() {
1061 nsLookAndFeel::GetInstance()->RefreshImpl();
1062 nsNativeBasicTheme::LookAndFeelChanged();
1065 // static
1066 void LookAndFeel::NativeInit() { nsLookAndFeel::GetInstance()->NativeInit(); }
1068 // static
1069 void LookAndFeel::SetData(widget::FullLookAndFeel&& aTables) {
1070 nsLookAndFeel::GetInstance()->SetDataImpl(std::move(aTables));
1073 } // namespace mozilla