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/. */
5 
6 #include "mozilla/ArrayUtils.h"
7 
8 #include "nscore.h"
9 
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 "Theme.h"
20 #include "SurfaceCacheUtils.h"
21 #include "mozilla/dom/ContentParent.h"
22 #include "mozilla/dom/ContentChild.h"
23 #include "mozilla/Preferences.h"
24 #include "mozilla/Services.h"
25 #include "mozilla/ServoStyleSet.h"
26 #include "mozilla/ServoCSSParser.h"
27 #include "mozilla/StaticPrefs_browser.h"
28 #include "mozilla/StaticPrefs_editor.h"
29 #include "mozilla/StaticPrefs_ui.h"
30 #include "mozilla/StaticPrefs_widget.h"
31 #include "mozilla/dom/Document.h"
32 #include "mozilla/PreferenceSheet.h"
33 #include "mozilla/gfx/2D.h"
34 #include "mozilla/widget/WidgetMessageUtils.h"
35 #include "mozilla/Telemetry.h"
36 #include "mozilla/TelemetryScalarEnums.h"
37 
38 #include "gfxPlatform.h"
39 #include "gfxFont.h"
40 
41 #include "qcms.h"
42 
43 #ifdef DEBUG
44 #  include "nsSize.h"
45 #endif
46 
47 using namespace mozilla;
48 
49 using IntID = mozilla::LookAndFeel::IntID;
50 using FloatID = mozilla::LookAndFeel::FloatID;
51 using ColorID = mozilla::LookAndFeel::ColorID;
52 using FontID = mozilla::LookAndFeel::FontID;
53 
54 template <typename Index, typename Value, Index kEnd>
55 class EnumeratedCache {
ChunkFor(Index aIndex)56   static constexpr uint32_t ChunkFor(Index aIndex) {
57     return uint32_t(aIndex) >> 5;  // >> 5 is the same as / 32.
58   }
BitFor(Index aIndex)59   static constexpr uint32_t BitFor(Index aIndex) {
60     return 1u << (uint32_t(aIndex) & 31);
61   }
62   static constexpr uint32_t kChunks = ChunkFor(kEnd) + 1;
63 
64   mozilla::EnumeratedArray<Index, kEnd, Value> mEntries;
65   uint32_t mValidity[kChunks] = {0};
66 
67  public:
68   constexpr EnumeratedCache() = default;
69 
IsValid(Index aIndex) const70   bool IsValid(Index aIndex) const {
71     return mValidity[ChunkFor(aIndex)] & BitFor(aIndex);
72   }
73 
Get(Index aIndex) const74   const Value* Get(Index aIndex) const {
75     return IsValid(aIndex) ? &mEntries[aIndex] : nullptr;
76   }
77 
Insert(Index aIndex,Value aValue)78   void Insert(Index aIndex, Value aValue) {
79     mValidity[ChunkFor(aIndex)] |= BitFor(aIndex);
80     mEntries[aIndex] = aValue;
81   }
82 
Remove(Index aIndex)83   void Remove(Index aIndex) {
84     mValidity[ChunkFor(aIndex)] &= ~BitFor(aIndex);
85     mEntries[aIndex] = Value();
86   }
87 
Clear()88   void Clear() {
89     for (auto& chunk : mValidity) {
90       chunk = 0;
91     }
92     for (auto& entry : mEntries) {
93       entry = Value();
94     }
95   }
96 };
97 
98 static EnumeratedCache<ColorID, Maybe<nscolor>, ColorID::End> sLightColorCache;
99 static EnumeratedCache<ColorID, Maybe<nscolor>, ColorID::End> sDarkColorCache;
100 static EnumeratedCache<FloatID, Maybe<float>, FloatID::End> sFloatCache;
101 static EnumeratedCache<IntID, Maybe<int32_t>, IntID::End> sIntCache;
102 static EnumeratedCache<FontID, widget::LookAndFeelFont, FontID::End> sFontCache;
103 
104 // To make one of these prefs toggleable from a reftest add a user
105 // pref in testing/profiles/reftest/user.js. For example, to make
106 // ui.useAccessibilityTheme toggleable, add:
107 //
108 // user_pref("ui.useAccessibilityTheme", 0);
109 //
110 // This needs to be of the same length and in the same order as
111 // LookAndFeel::IntID values.
112 static const char sIntPrefs[][43] = {
113     "ui.caretBlinkTime",
114     "ui.caretBlinkCount",
115     "ui.caretWidth",
116     "ui.caretVisibleWithSelection",
117     "ui.selectTextfieldsOnKeyFocus",
118     "ui.submenuDelay",
119     "ui.menusCanOverlapOSBar",
120     "ui.useOverlayScrollbars",
121     "ui.allowOverlayScrollbarsOverlap",
122     "ui.skipNavigatingDisabledMenuItem",
123     "ui.dragThresholdX",
124     "ui.dragThresholdY",
125     "ui.useAccessibilityTheme",
126     "ui.scrollArrowStyle",
127     "ui.scrollSliderStyle",
128     "ui.scrollButtonLeftMouseButtonAction",
129     "ui.scrollButtonMiddleMouseButtonAction",
130     "ui.scrollButtonRightMouseButtonAction",
131     "ui.treeOpenDelay",
132     "ui.treeCloseDelay",
133     "ui.treeLazyScrollDelay",
134     "ui.treeScrollDelay",
135     "ui.treeScrollLinesMax",
136     "accessibility.tabfocus",  // Weird one...
137     "ui.chosenMenuItemsShouldBlink",
138     "ui.windowsAccentColorInTitlebar",
139     "ui.windowsDefaultTheme",
140     "ui.dwmCompositor",
141     "ui.windowsClassic",
142     "ui.windowsGlass",
143     "ui.macGraphiteTheme",
144     "ui.macBigSurTheme",
145     "ui.macRTL",
146     "ui.alertNotificationOrigin",
147     "ui.scrollToClick",
148     "ui.IMERawInputUnderlineStyle",
149     "ui.IMESelectedRawTextUnderlineStyle",
150     "ui.IMEConvertedTextUnderlineStyle",
151     "ui.IMESelectedConvertedTextUnderlineStyle",
152     "ui.SpellCheckerUnderlineStyle",
153     "ui.menuBarDrag",
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.GtkCSDMinimizeButton",
164     "ui.GtkCSDMaximizeButton",
165     "ui.GtkCSDCloseButton",
166     "ui.GtkCSDMinimizeButtonPosition",
167     "ui.GtkCSDMaximizeButtonPosition",
168     "ui.GtkCSDCloseButtonPosition",
169     "ui.GtkCSDReversedPlacement",
170     "ui.systemUsesDarkTheme",
171     "ui.prefersReducedMotion",
172     "ui.primaryPointerCapabilities",
173     "ui.allPointerCapabilities",
174     "ui.systemVerticalScrollbarWidth",
175     "ui.systemHorizontalScrollbarHeight",
176     "ui.touchDeviceSupportPresent",
177     "ui.titlebarRadius",
178     "ui.GtkMenuRadius",
179 };
180 
181 static_assert(ArrayLength(sIntPrefs) == size_t(LookAndFeel::IntID::End),
182               "Should have a pref for each int value");
183 
184 // This array MUST be kept in the same order as the float id list in
185 // LookAndFeel.h
186 // clang-format off
187 static const char sFloatPrefs[][37] = {
188     "ui.IMEUnderlineRelativeSize",
189     "ui.SpellCheckerUnderlineRelativeSize",
190     "ui.caretAspectRatio",
191     "ui.textScaleFactor",
192     "ui.cursorScale",
193 };
194 // clang-format on
195 
196 static_assert(ArrayLength(sFloatPrefs) == size_t(LookAndFeel::FloatID::End),
197               "Should have a pref for each float value");
198 
199 // This array MUST be kept in the same order as the color list in
200 // specified/color.rs
201 static const char sColorPrefs[][41] = {
202     "ui.textSelectDisabledBackground",
203     "ui.textSelectAttentionBackground",
204     "ui.textSelectAttentionForeground",
205     "ui.textHighlightBackground",
206     "ui.textHighlightForeground",
207     "ui.IMERawInputBackground",
208     "ui.IMERawInputForeground",
209     "ui.IMERawInputUnderline",
210     "ui.IMESelectedRawTextBackground",
211     "ui.IMESelectedRawTextForeground",
212     "ui.IMESelectedRawTextUnderline",
213     "ui.IMEConvertedTextBackground",
214     "ui.IMEConvertedTextForeground",
215     "ui.IMEConvertedTextUnderline",
216     "ui.IMESelectedConvertedTextBackground",
217     "ui.IMESelectedConvertedTextForeground",
218     "ui.IMESelectedConvertedTextUnderline",
219     "ui.SpellCheckerUnderline",
220     "ui.themedScrollbar",
221     "ui.themedScrollbarInactive",
222     "ui.themedScrollbarThumb",
223     "ui.themedScrollbarThumbHover",
224     "ui.themedScrollbarThumbActive",
225     "ui.themedScrollbarThumbInactive",
226     "ui.activeborder",
227     "ui.activecaption",
228     "ui.appworkspace",
229     "ui.background",
230     "ui.buttonface",
231     "ui.buttonhighlight",
232     "ui.buttonshadow",
233     "ui.buttontext",
234     "ui.captiontext",
235     "ui.-moz-field",
236     "ui.-moz-disabledfield",
237     "ui.-moz-fieldtext",
238     "ui.graytext",
239     "ui.highlight",
240     "ui.highlighttext",
241     "ui.inactiveborder",
242     "ui.inactivecaption",
243     "ui.inactivecaptiontext",
244     "ui.infobackground",
245     "ui.infotext",
246     "ui.menu",
247     "ui.menutext",
248     "ui.scrollbar",
249     "ui.threeddarkshadow",
250     "ui.threedface",
251     "ui.threedhighlight",
252     "ui.threedlightshadow",
253     "ui.threedshadow",
254     "ui.window",
255     "ui.windowframe",
256     "ui.windowtext",
257     "ui.-moz-buttondefault",
258     "ui.-moz-default-color",
259     "ui.-moz-default-background-color",
260     "ui.-moz-dialog",
261     "ui.-moz-dialogtext",
262     "ui.-moz-dragtargetzone",
263     "ui.-moz-cellhighlight",
264     "ui.-moz_cellhighlighttext",
265     "ui.selecteditem",
266     "ui.selecteditemtext",
267     "ui.-moz-buttonhoverface",
268     "ui.-moz_buttonhovertext",
269     "ui.-moz_menuhover",
270     "ui.-moz_menuhovertext",
271     "ui.-moz_menubartext",
272     "ui.-moz_menubarhovertext",
273     "ui.-moz_eventreerow",
274     "ui.-moz_oddtreerow",
275     "ui.-moz-buttonactivetext",
276     "ui.-moz-buttonactiveface",
277     "ui.-moz-buttondisabledface",
278     "ui.-moz_mac_chrome_active",
279     "ui.-moz_mac_chrome_inactive",
280     "ui.-moz-mac-defaultbuttontext",
281     "ui.-moz-mac-focusring",
282     "ui.-moz-mac-menuselect",
283     "ui.-moz-mac-menushadow",
284     "ui.-moz-mac-menutextdisable",
285     "ui.-moz-mac-menutextselect",
286     "ui.-moz_mac_disabledtoolbartext",
287     "ui.-moz-mac-secondaryhighlight",
288     "ui.-moz-mac-vibrant-titlebar-light",
289     "ui.-moz-mac-vibrant-titlebar-dark",
290     "ui.-moz-mac-menupopup",
291     "ui.-moz-mac-menuitem",
292     "ui.-moz-mac-active-menuitem",
293     "ui.-moz-mac-source-list",
294     "ui.-moz-mac-source-list-selection",
295     "ui.-moz-mac-active-source-list-selection",
296     "ui.-moz-mac-tooltip",
297     "ui.-moz-accent-color",
298     "ui.-moz-accent-color-foreground",
299     "ui.-moz-autofill-background",
300     "ui.-moz-win-mediatext",
301     "ui.-moz-win-communicationstext",
302     "ui.-moz-nativehyperlinktext",
303     "ui.-moz-nativevisitedhyperlinktext",
304     "ui.-moz-hyperlinktext",
305     "ui.-moz-activehyperlinktext",
306     "ui.-moz-visitedhyperlinktext",
307     "ui.-moz-comboboxtext",
308     "ui.-moz-combobox",
309     "ui.-moz-colheadertext",
310     "ui.-moz-colheaderhovertext",
311 };
312 
313 static_assert(ArrayLength(sColorPrefs) == size_t(LookAndFeel::ColorID::End),
314               "Should have a pref for each color value");
315 
GetColorPrefName(ColorID aId)316 const char* nsXPLookAndFeel::GetColorPrefName(ColorID aId) {
317   return sColorPrefs[size_t(aId)];
318 }
319 
320 bool nsXPLookAndFeel::sInitialized = false;
321 
322 nsXPLookAndFeel* nsXPLookAndFeel::sInstance = nullptr;
323 bool nsXPLookAndFeel::sShutdown = false;
324 
325 // static
GetInstance()326 nsXPLookAndFeel* nsXPLookAndFeel::GetInstance() {
327   if (sInstance) {
328     return sInstance;
329   }
330 
331   NS_ENSURE_TRUE(!sShutdown, nullptr);
332 
333   // If we're in a content process, then the parent process will have supplied
334   // us with an initial FullLookAndFeel object.
335   // We grab this data from the ContentChild,
336   // where it's been temporarily stashed, and initialize our new LookAndFeel
337   // object with it.
338 
339   FullLookAndFeel* lnf = nullptr;
340 
341   if (auto* cc = mozilla::dom::ContentChild::GetSingleton()) {
342     lnf = &cc->BorrowLookAndFeelData();
343   }
344 
345   if (lnf) {
346     sInstance = new widget::RemoteLookAndFeel(std::move(*lnf));
347   } else if (gfxPlatform::IsHeadless()) {
348     sInstance = new widget::HeadlessLookAndFeel();
349   } else {
350     sInstance = new nsLookAndFeel();
351   }
352 
353   // This is only ever used once during initialization, and can be cleared now.
354   if (lnf) {
355     *lnf = {};
356   }
357 
358   widget::Theme::Init();
359   return sInstance;
360 }
361 
362 // static
Shutdown()363 void nsXPLookAndFeel::Shutdown() {
364   if (sShutdown) {
365     return;
366   }
367 
368   sShutdown = true;
369   delete sInstance;
370   sInstance = nullptr;
371 
372   // This keeps strings alive, so need to clear to make leak checking happy.
373   sFontCache.Clear();
374 
375   widget::Theme::Shutdown();
376 }
377 
IntPrefChanged(const nsACString & aPref)378 static void IntPrefChanged(const nsACString& aPref) {
379   // Most Int prefs can't change our system colors or fonts, but
380   // ui.systemUsesDarkTheme can, since it affects the effective color-scheme
381   // (affecting system colors).
382   auto changeKind = aPref.EqualsLiteral("ui.systemUsesDarkTheme")
383                         ? widget::ThemeChangeKind::Style
384                         : widget::ThemeChangeKind::MediaQueriesOnly;
385   LookAndFeel::NotifyChangedAllWindows(changeKind);
386 }
387 
FloatPrefChanged()388 static void FloatPrefChanged() {
389   // Float prefs can't change our system colors or fonts.
390   LookAndFeel::NotifyChangedAllWindows(
391       widget::ThemeChangeKind::MediaQueriesOnly);
392 }
393 
ColorPrefChanged()394 static void ColorPrefChanged() {
395   // Color prefs affect style, because they by definition change system colors.
396   LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind::Style);
397 }
398 
399 // static
OnPrefChanged(const char * aPref,void * aClosure)400 void nsXPLookAndFeel::OnPrefChanged(const char* aPref, void* aClosure) {
401   nsDependentCString prefName(aPref);
402   for (const char* pref : sIntPrefs) {
403     if (prefName.Equals(pref)) {
404       IntPrefChanged(prefName);
405       return;
406     }
407   }
408 
409   for (const char* pref : sFloatPrefs) {
410     if (prefName.Equals(pref)) {
411       FloatPrefChanged();
412       return;
413     }
414   }
415 
416   for (const char* pref : sColorPrefs) {
417     // We use StringBeginsWith to handle .dark prefs too.
418     if (StringBeginsWith(prefName, nsDependentCString(pref))) {
419       ColorPrefChanged();
420       return;
421     }
422   }
423 }
424 
425 static constexpr struct {
426   nsLiteralCString mName;
427   widget::ThemeChangeKind mChangeKind =
428       widget::ThemeChangeKind::MediaQueriesOnly;
429 } kMediaQueryPrefs[] = {
430     {"browser.display.windows.native_menus"_ns},
431     {"browser.proton.places-tooltip.enabled"_ns},
432     {"layout.css.prefers-color-scheme.content-override"_ns},
433     // Affects media queries and scrollbar sizes, so gotta relayout.
434     {"widget.gtk.overlay-scrollbars.enabled"_ns,
435      widget::ThemeChangeKind::StyleAndLayout},
436     // This affects not only the media query, but also the native theme, so we
437     // need to re-layout.
438     {"browser.theme.toolbar-theme"_ns, widget::ThemeChangeKind::AllBits},
439     {"browser.theme.content-theme"_ns},
440 };
441 
442 // Read values from the user's preferences.
443 // This is done once at startup, but since the user's preferences
444 // haven't actually been read yet at that time, we also have to
445 // set a callback to inform us of changes to each pref.
Init()446 void nsXPLookAndFeel::Init() {
447   MOZ_RELEASE_ASSERT(NS_IsMainThread());
448 
449   // Say we're already initialized, and take the chance that it might fail;
450   // protects against some other process writing to our static variables.
451   sInitialized = true;
452 
453   RecomputeColorSchemes();
454 
455   // XXX If we could reorganize the pref names, we should separate the branch
456   //     for each types.  Then, we could reduce the unnecessary loop from
457   //     nsXPLookAndFeel::OnPrefChanged().
458   Preferences::RegisterPrefixCallback(OnPrefChanged, "ui.");
459   // We really do just want the accessibility.tabfocus pref, not other prefs
460   // that start with that string.
461   Preferences::RegisterCallback(OnPrefChanged, "accessibility.tabfocus");
462 
463   for (auto& pref : kMediaQueryPrefs) {
464     Preferences::RegisterCallback(
465         [](const char*, void* aChangeKind) {
466           auto changeKind =
467               widget::ThemeChangeKind(reinterpret_cast<uintptr_t>(aChangeKind));
468           LookAndFeel::NotifyChangedAllWindows(changeKind);
469         },
470         pref.mName, reinterpret_cast<void*>(uintptr_t(pref.mChangeKind)));
471   }
472 }
473 
~nsXPLookAndFeel()474 nsXPLookAndFeel::~nsXPLookAndFeel() {
475   NS_ASSERTION(sInstance == this,
476                "This destroying instance isn't the singleton instance");
477   sInstance = nullptr;
478 }
479 
IsSpecialColor(LookAndFeel::ColorID aID,nscolor aColor)480 static bool IsSpecialColor(LookAndFeel::ColorID aID, nscolor aColor) {
481   using ColorID = LookAndFeel::ColorID;
482 
483   if (aColor == NS_SAME_AS_FOREGROUND_COLOR) {
484     return true;
485   }
486 
487   switch (aID) {
488     case ColorID::IMESelectedRawTextBackground:
489     case ColorID::IMESelectedConvertedTextBackground:
490     case ColorID::IMERawInputBackground:
491     case ColorID::IMEConvertedTextBackground:
492     case ColorID::IMESelectedRawTextForeground:
493     case ColorID::IMESelectedConvertedTextForeground:
494     case ColorID::IMERawInputForeground:
495     case ColorID::IMEConvertedTextForeground:
496     case ColorID::IMERawInputUnderline:
497     case ColorID::IMEConvertedTextUnderline:
498     case ColorID::IMESelectedRawTextUnderline:
499     case ColorID::IMESelectedConvertedTextUnderline:
500     case ColorID::SpellCheckerUnderline:
501       return NS_IS_SELECTION_SPECIAL_COLOR(aColor);
502     default:
503       break;
504   }
505   /*
506    * In GetColor(), every color that is not a special color is color
507    * corrected. Use false to make other colors color corrected.
508    */
509   return false;
510 }
511 
GetStandinForNativeColor(ColorID aID,ColorScheme aScheme)512 nscolor nsXPLookAndFeel::GetStandinForNativeColor(ColorID aID,
513                                                   ColorScheme aScheme) {
514   if (aScheme == ColorScheme::Dark) {
515     if (auto color = GenericDarkColor(aID)) {
516       return *color;
517     }
518   }
519 
520   // The stand-in colors are taken from what the non-native theme needs (for
521   // field/button colors), the Windows 7 Aero theme except Mac-specific colors
522   // which are taken from Mac OS 10.7.
523 
524 #define COLOR(name_, r, g, b) \
525   case ColorID::name_:        \
526     return NS_RGB(r, g, b);
527 
528 #define COLORA(name_, r, g, b, a) \
529   case ColorID::name_:            \
530     return NS_RGBA(r, g, b, a);
531 
532   switch (aID) {
533     // These are here for the purposes of headless mode.
534     case ColorID::IMESelectedRawTextBackground:
535     case ColorID::IMESelectedConvertedTextBackground:
536     case ColorID::IMERawInputBackground:
537     case ColorID::IMEConvertedTextBackground:
538       return NS_TRANSPARENT;
539     case ColorID::IMESelectedRawTextForeground:
540     case ColorID::IMESelectedConvertedTextForeground:
541     case ColorID::IMERawInputForeground:
542     case ColorID::IMEConvertedTextForeground:
543       return NS_SAME_AS_FOREGROUND_COLOR;
544     case ColorID::IMERawInputUnderline:
545     case ColorID::IMEConvertedTextUnderline:
546       return NS_40PERCENT_FOREGROUND_COLOR;
547       COLOR(MozAccentColor, 53, 132, 228)
548       COLOR(MozAccentColorForeground, 0xff, 0xff, 0xff)
549       COLOR(SpellCheckerUnderline, 0xff, 0x00, 0x00)
550       COLOR(TextSelectDisabledBackground, 0xaa, 0xaa, 0xaa)
551 
552       // CSS 2 colors:
553       COLOR(Activeborder, 0xB4, 0xB4, 0xB4)
554       COLOR(Activecaption, 0x99, 0xB4, 0xD1)
555       COLOR(Appworkspace, 0xAB, 0xAB, 0xAB)
556       COLOR(Background, 0x00, 0x00, 0x00)
557       COLOR(Buttonhighlight, 0xFF, 0xFF, 0xFF)
558       COLOR(Buttonshadow, 0xA0, 0xA0, 0xA0)
559 
560       // Buttons and comboboxes should be kept in sync since they are drawn with
561       // the same colors by the non-native theme.
562       COLOR(Buttonface, 0xe9, 0xe9, 0xed)
563       COLORA(MozButtondisabledface, 0xe9, 0xe9, 0xed, 128)
564 
565       COLOR(MozCombobox, 0xe9, 0xe9, 0xed)
566 
567       COLOR(Buttontext, 0x00, 0x00, 0x00)
568       COLOR(MozComboboxtext, 0x00, 0x00, 0x00)
569 
570       COLOR(Captiontext, 0x00, 0x00, 0x00)
571       COLOR(Graytext, 0x6D, 0x6D, 0x6D)
572       COLOR(Highlight, 0x33, 0x99, 0xFF)
573       COLOR(Highlighttext, 0xFF, 0xFF, 0xFF)
574       COLOR(Inactiveborder, 0xF4, 0xF7, 0xFC)
575       COLOR(Inactivecaption, 0xBF, 0xCD, 0xDB)
576       COLOR(Inactivecaptiontext, 0x43, 0x4E, 0x54)
577       COLOR(Infobackground, 0xFF, 0xFF, 0xE1)
578       COLOR(Infotext, 0x00, 0x00, 0x00)
579       COLOR(Menu, 0xF0, 0xF0, 0xF0)
580       COLOR(Menutext, 0x00, 0x00, 0x00)
581       COLOR(Scrollbar, 0xC8, 0xC8, 0xC8)
582       COLOR(Threeddarkshadow, 0x69, 0x69, 0x69)
583       COLOR(Threedface, 0xF0, 0xF0, 0xF0)
584       COLOR(Threedhighlight, 0xFF, 0xFF, 0xFF)
585       COLOR(Threedlightshadow, 0xE3, 0xE3, 0xE3)
586       COLOR(Threedshadow, 0xA0, 0xA0, 0xA0)
587       COLOR(Window, 0xFF, 0xFF, 0xFF)
588       COLOR(Windowframe, 0x64, 0x64, 0x64)
589       COLOR(Windowtext, 0x00, 0x00, 0x00)
590       COLOR(MozButtondefault, 0x69, 0x69, 0x69)
591       COLOR(Field, 0xFF, 0xFF, 0xFF)
592       COLORA(MozDisabledfield, 0xFF, 0xFF, 0xFF, 128)
593       COLOR(Fieldtext, 0x00, 0x00, 0x00)
594       COLOR(MozDialog, 0xF0, 0xF0, 0xF0)
595       COLOR(MozDialogtext, 0x00, 0x00, 0x00)
596       COLOR(MozColheadertext, 0x00, 0x00, 0x00)
597       COLOR(MozColheaderhovertext, 0x00, 0x00, 0x00)
598       COLOR(MozDragtargetzone, 0xFF, 0xFF, 0xFF)
599       COLOR(MozCellhighlight, 0xF0, 0xF0, 0xF0)
600       COLOR(MozCellhighlighttext, 0x00, 0x00, 0x00)
601       COLOR(Selecteditem, 0x33, 0x99, 0xFF)
602       COLOR(Selecteditemtext, 0xFF, 0xFF, 0xFF)
603       COLOR(MozButtonhoverface, 0xd0, 0xd0, 0xd7)
604       COLOR(MozButtonhovertext, 0x00, 0x00, 0x00)
605       COLOR(MozButtonactiveface, 0xb1, 0xb1, 0xb9)
606       COLOR(MozButtonactivetext, 0x00, 0x00, 0x00)
607       COLOR(MozMenuhover, 0x33, 0x99, 0xFF)
608       COLOR(MozMenuhovertext, 0x00, 0x00, 0x00)
609       COLOR(MozMenubartext, 0x00, 0x00, 0x00)
610       COLOR(MozMenubarhovertext, 0x00, 0x00, 0x00)
611       COLOR(MozEventreerow, 0xFF, 0xFF, 0xFF)
612       COLOR(MozOddtreerow, 0xFF, 0xFF, 0xFF)
613       COLOR(MozMacChromeActive, 0xB2, 0xB2, 0xB2)
614       COLOR(MozMacChromeInactive, 0xE1, 0xE1, 0xE1)
615       COLOR(MozMacFocusring, 0x60, 0x9D, 0xD7)
616       COLOR(MozMacMenuselect, 0x38, 0x75, 0xD7)
617       COLOR(MozMacMenushadow, 0xA3, 0xA3, 0xA3)
618       COLOR(MozMacMenutextdisable, 0x88, 0x88, 0x88)
619       COLOR(MozMacMenutextselect, 0xFF, 0xFF, 0xFF)
620       COLOR(MozMacDisabledtoolbartext, 0x3F, 0x3F, 0x3F)
621       COLOR(MozMacSecondaryhighlight, 0xD4, 0xD4, 0xD4)
622       COLOR(MozMacVibrantTitlebarLight, 0xf7, 0xf7, 0xf7)
623       COLOR(MozMacVibrantTitlebarDark, 0x28, 0x28, 0x28)
624       COLOR(MozMacMenupopup, 0xe6, 0xe6, 0xe6)
625       COLOR(MozMacMenuitem, 0xe6, 0xe6, 0xe6)
626       COLOR(MozMacActiveMenuitem, 0x0a, 0x64, 0xdc)
627       COLOR(MozMacSourceList, 0xf7, 0xf7, 0xf7)
628       COLOR(MozMacSourceListSelection, 0xc8, 0xc8, 0xc8)
629       COLOR(MozMacActiveSourceListSelection, 0x0a, 0x64, 0xdc)
630       COLOR(MozMacTooltip, 0xf7, 0xf7, 0xf7)
631       // Seems to be the default color (hardcoded because of bug 1065998)
632       COLOR(MozWinMediatext, 0xFF, 0xFF, 0xFF)
633       COLOR(MozWinCommunicationstext, 0xFF, 0xFF, 0xFF)
634       COLOR(MozNativehyperlinktext, 0x00, 0x66, 0xCC)
635       COLOR(MozNativevisitedhyperlinktext, 0x55, 0x1A, 0x8B)
636     default:
637       break;
638   }
639   return NS_RGB(0xFF, 0xFF, 0xFF);
640 }
641 
642 #undef COLOR
643 #undef COLORA
644 
645 // Taken from in-content/common.inc.css's dark theme.
GenericDarkColor(ColorID aID)646 Maybe<nscolor> nsXPLookAndFeel::GenericDarkColor(ColorID aID) {
647   nscolor color = NS_RGB(0, 0, 0);
648   static constexpr nscolor kWindowBackground = NS_RGB(28, 27, 34);
649   static constexpr nscolor kWindowText = NS_RGB(251, 251, 254);
650   switch (aID) {
651     case ColorID::Window:  // --in-content-page-background
652     case ColorID::Background:
653     case ColorID::Menu:
654       color = kWindowBackground;
655       break;
656     case ColorID::MozOddtreerow:
657     case ColorID::MozDialog:  // --in-content-box-background
658       color = NS_RGB(35, 34, 43);
659       break;
660     case ColorID::Windowtext:  // --in-content-page-color
661     case ColorID::Menutext:
662     case ColorID::MozDialogtext:
663     case ColorID::Fieldtext:
664     case ColorID::Buttontext:  // --in-content-button-text-color (via
665                                // --in-content-page-color)
666     case ColorID::MozComboboxtext:
667     case ColorID::MozButtonhovertext:
668     case ColorID::MozButtonactivetext:
669       color = kWindowText;
670       break;
671     case ColorID::Threedlightshadow:  // --in-content-box-border-color computed
672                                       // with kWindowText above
673                                       // kWindowBackground.
674     case ColorID::Graytext:  // opacity: 0.4 of kWindowText blended over the
675                              // "Window" background color, which happens to be
676                              // the same :-)
677       color = NS_ComposeColors(kWindowBackground, NS_RGBA(251, 251, 254, 102));
678       break;
679     case ColorID::MozCellhighlight:
680     case ColorID::Selecteditem:  // --in-content-primary-button-background /
681                                  // --in-content-item-selected
682       color = NS_RGB(0, 221, 255);
683       break;
684     case ColorID::Field:
685     case ColorID::Buttonface:  // --in-content-button-background
686     case ColorID::Threedface:
687     case ColorID::MozCombobox:
688     case ColorID::MozCellhighlighttext:
689     case ColorID::Selecteditemtext:  // --in-content-primary-button-text-color /
690                                      // --in-content-item-selected-text
691       color = NS_RGB(43, 42, 51);
692       break;
693     case ColorID::Threeddarkshadow:  // Same as Threedlightshadow but with the
694                                      // background.
695     case ColorID::MozDisabledfield:  // opacity: 0.4 of the face above blended
696                                      // over the "Window" background color.
697     case ColorID::MozButtondisabledface:
698       color = NS_ComposeColors(kWindowBackground, NS_RGBA(43, 42, 51, 102));
699       break;
700     case ColorID::MozButtonhoverface:  // --in-content-button-background-hover
701       color = NS_RGB(82, 82, 94);
702       break;
703     case ColorID::MozButtonactiveface:  // --in-content-button-background-active
704       color = NS_RGB(91, 91, 102);
705       break;
706     case ColorID::Highlight:
707       color = NS_RGBA(0, 221, 255, 78);
708       break;
709     case ColorID::Highlighttext:
710       color = NS_SAME_AS_FOREGROUND_COLOR;
711       break;
712     case ColorID::MozNativehyperlinktext:
713       // If you change this color, you probably also want to change the default
714       // value of browser.anchor_color.dark.
715       color = NS_RGB(0x8c, 0x8c, 0xff);
716       break;
717     case ColorID::MozNativevisitedhyperlinktext:
718       // If you change this color, you probably also want to change the default
719       // value of browser.visited_color.dark.
720       color = NS_RGB(0xff, 0xad, 0xff);
721       break;
722 
723     default:
724       return Nothing();
725   }
726   return Some(color);
727 }
728 
729 // Uncomment the #define below if you want to debug system color use in a skin
730 // that uses them.  When set, it will make all system color pairs that are
731 // appropriate for foreground/background pairing the same.  This means if the
732 // skin is using system colors correctly you will not be able to see *any* text.
733 //
734 // #define DEBUG_SYSTEM_COLOR_USE
735 
736 #ifdef DEBUG_SYSTEM_COLOR_USE
SystemColorUseDebuggingColor(LookAndFeel::ColorID aID,nscolor & aResult)737 static nsresult SystemColorUseDebuggingColor(LookAndFeel::ColorID aID,
738                                              nscolor& aResult) {
739   using ColorID = LookAndFeel::ColorID;
740 
741   switch (aID) {
742       // css2  http://www.w3.org/TR/REC-CSS2/ui.html#system-colors
743     case ColorID::Activecaption:
744       // active window caption background
745     case ColorID::Captiontext:
746       // text in active window caption
747       aResult = NS_RGB(0xff, 0x00, 0x00);
748       break;
749 
750     case ColorID::Highlight:
751       // background of selected item
752     case ColorID::Highlighttext:
753       // text of selected item
754       aResult = NS_RGB(0xff, 0xff, 0x00);
755       break;
756 
757     case ColorID::Inactivecaption:
758       // inactive window caption
759     case ColorID::Inactivecaptiontext:
760       // text in inactive window caption
761       aResult = NS_RGB(0x66, 0x66, 0x00);
762       break;
763 
764     case ColorID::Infobackground:
765       // tooltip background color
766     case ColorID::Infotext:
767       // tooltip text color
768       aResult = NS_RGB(0x00, 0xff, 0x00);
769       break;
770 
771     case ColorID::Menu:
772       // menu background
773     case ColorID::Menutext:
774       // menu text
775       aResult = NS_RGB(0x00, 0xff, 0xff);
776       break;
777 
778     case ColorID::Threedface:
779     case ColorID::Buttonface:
780       // 3-D face color
781     case ColorID::Buttontext:
782       // text on push buttons
783       aResult = NS_RGB(0x00, 0x66, 0x66);
784       break;
785 
786     case ColorID::Window:
787     case ColorID::Windowtext:
788       aResult = NS_RGB(0x00, 0x00, 0xff);
789       break;
790 
791       // from the CSS3 working draft (not yet finalized)
792       // http://www.w3.org/tr/2000/wd-css3-userint-20000216.html#color
793 
794     case ColorID::Field:
795     case ColorID::Fieldtext:
796       aResult = NS_RGB(0xff, 0x00, 0xff);
797       break;
798 
799     case ColorID::MozDialog:
800     case ColorID::MozDialogtext:
801       aResult = NS_RGB(0x66, 0x00, 0x66);
802       break;
803 
804     default:
805       return NS_ERROR_NOT_AVAILABLE;
806   }
807 
808   return NS_OK;
809 }
810 #endif
811 
GetPrefColor(const char * aPref,nscolor & aResult)812 static nsresult GetPrefColor(const char* aPref, nscolor& aResult) {
813   nsAutoCString colorStr;
814   MOZ_TRY(Preferences::GetCString(aPref, colorStr));
815   if (!ServoCSSParser::ComputeColor(nullptr, NS_RGB(0, 0, 0), colorStr,
816                                     &aResult)) {
817     return NS_ERROR_FAILURE;
818   }
819   return NS_OK;
820 }
821 
GetColorFromPref(LookAndFeel::ColorID aID,ColorScheme aScheme,nscolor & aResult)822 static nsresult GetColorFromPref(LookAndFeel::ColorID aID, ColorScheme aScheme,
823                                  nscolor& aResult) {
824   const char* prefName = sColorPrefs[size_t(aID)];
825   if (aScheme == ColorScheme::Dark) {
826     nsAutoCString darkPrefName(prefName);
827     darkPrefName.Append(".dark");
828     if (NS_SUCCEEDED(GetPrefColor(darkPrefName.get(), aResult))) {
829       return NS_OK;
830     }
831   }
832   return GetPrefColor(prefName, aResult);
833 }
834 
835 // All these routines will return NS_OK if they have a value,
836 // in which case the nsLookAndFeel should use that value;
837 // otherwise we'll return NS_ERROR_NOT_AVAILABLE, in which case, the
838 // platform-specific nsLookAndFeel should use its own values instead.
GetColorValue(ColorID aID,ColorScheme aScheme,UseStandins aUseStandins,nscolor & aResult)839 nsresult nsXPLookAndFeel::GetColorValue(ColorID aID, ColorScheme aScheme,
840                                         UseStandins aUseStandins,
841                                         nscolor& aResult) {
842   if (!sInitialized) {
843     Init();
844   }
845 
846 #ifdef DEBUG_SYSTEM_COLOR_USE
847   if (NS_SUCCEEDED(SystemColorUseDebuggingColor(aID, aResult))) {
848     return NS_OK;
849   }
850 #endif
851 
852   if (aUseStandins == UseStandins::Yes) {
853     aResult = GetStandinForNativeColor(aID, aScheme);
854     return NS_OK;
855   }
856 
857   auto& cache =
858       aScheme == ColorScheme::Light ? sLightColorCache : sDarkColorCache;
859   if (const auto* cached = cache.Get(aID)) {
860     if (cached->isNothing()) {
861       return NS_ERROR_FAILURE;
862     }
863     aResult = cached->value();
864     return NS_OK;
865   }
866 
867   if (NS_SUCCEEDED(GetColorFromPref(aID, aScheme, aResult))) {
868     cache.Insert(aID, Some(aResult));
869     return NS_OK;
870   }
871 
872   if (NS_SUCCEEDED(NativeGetColor(aID, aScheme, aResult))) {
873     if (gfxPlatform::GetCMSMode() == CMSMode::All &&
874         !IsSpecialColor(aID, aResult)) {
875       qcms_transform* transform = gfxPlatform::GetCMSInverseRGBTransform();
876       if (transform) {
877         uint8_t color[4];
878         color[0] = NS_GET_R(aResult);
879         color[1] = NS_GET_G(aResult);
880         color[2] = NS_GET_B(aResult);
881         color[3] = NS_GET_A(aResult);
882         qcms_transform_data(transform, color, color, 1);
883         aResult = NS_RGBA(color[0], color[1], color[2], color[3]);
884       }
885     }
886 
887     // NOTE: Servo holds a lock and the main thread is paused, so writing to the
888     // global cache here is fine.
889     cache.Insert(aID, Some(aResult));
890     return NS_OK;
891   }
892 
893   cache.Insert(aID, Nothing());
894   return NS_ERROR_FAILURE;
895 }
896 
GetIntValue(IntID aID,int32_t & aResult)897 nsresult nsXPLookAndFeel::GetIntValue(IntID aID, int32_t& aResult) {
898   if (!sInitialized) {
899     Init();
900   }
901 
902   if (const auto* cached = sIntCache.Get(aID)) {
903     if (cached->isNothing()) {
904       return NS_ERROR_FAILURE;
905     }
906     aResult = cached->value();
907     return NS_OK;
908   }
909 
910   if (NS_SUCCEEDED(Preferences::GetInt(sIntPrefs[size_t(aID)], &aResult))) {
911     sIntCache.Insert(aID, Some(aResult));
912     return NS_OK;
913   }
914 
915   if (NS_FAILED(NativeGetInt(aID, aResult))) {
916     sIntCache.Insert(aID, Nothing());
917     return NS_ERROR_FAILURE;
918   }
919 
920   sIntCache.Insert(aID, Some(aResult));
921   return NS_OK;
922 }
923 
GetFloatValue(FloatID aID,float & aResult)924 nsresult nsXPLookAndFeel::GetFloatValue(FloatID aID, float& aResult) {
925   if (!sInitialized) {
926     Init();
927   }
928 
929   if (const auto* cached = sFloatCache.Get(aID)) {
930     if (cached->isNothing()) {
931       return NS_ERROR_FAILURE;
932     }
933     aResult = cached->value();
934     return NS_OK;
935   }
936 
937   int32_t pref = 0;
938   if (NS_SUCCEEDED(Preferences::GetInt(sFloatPrefs[size_t(aID)], &pref))) {
939     aResult = float(pref) / 100.0f;
940     sFloatCache.Insert(aID, Some(aResult));
941     return NS_OK;
942   }
943 
944   if (NS_FAILED(NativeGetFloat(aID, aResult))) {
945     sFloatCache.Insert(aID, Nothing());
946     return NS_ERROR_FAILURE;
947   }
948 
949   sFloatCache.Insert(aID, Some(aResult));
950   return NS_OK;
951 }
952 
LookAndFeelFontToStyle(const LookAndFeelFont & aFont,nsString & aName,gfxFontStyle & aStyle)953 bool nsXPLookAndFeel::LookAndFeelFontToStyle(const LookAndFeelFont& aFont,
954                                              nsString& aName,
955                                              gfxFontStyle& aStyle) {
956   if (!aFont.haveFont()) {
957     return false;
958   }
959   aName = aFont.name();
960   aStyle = gfxFontStyle();
961   aStyle.size = aFont.size();
962   aStyle.weight = FontWeight(aFont.weight());
963   aStyle.style =
964       aFont.italic() ? FontSlantStyle::Italic() : FontSlantStyle::Normal();
965   aStyle.systemFont = true;
966   return true;
967 }
968 
StyleToLookAndFeelFont(const nsAString & aName,const gfxFontStyle & aStyle)969 widget::LookAndFeelFont nsXPLookAndFeel::StyleToLookAndFeelFont(
970     const nsAString& aName, const gfxFontStyle& aStyle) {
971   LookAndFeelFont font;
972   font.haveFont() = true;
973   font.name() = aName;
974   font.size() = aStyle.size;
975   font.weight() = aStyle.weight.ToFloat();
976   font.italic() = aStyle.style.IsItalic();
977   MOZ_ASSERT(aStyle.style.IsNormal() || aStyle.style.IsItalic(),
978              "Cannot handle oblique font style");
979 #ifdef DEBUG
980   {
981     // Assert that all the remaining font style properties have their
982     // default values.
983     gfxFontStyle candidate = aStyle;
984     gfxFontStyle defaults{};
985     candidate.size = defaults.size;
986     candidate.weight = defaults.weight;
987     candidate.style = defaults.style;
988     MOZ_ASSERT(candidate.Equals(defaults),
989                "Some font style properties not supported");
990   }
991 #endif
992   return font;
993 }
994 
GetFontValue(FontID aID,nsString & aName,gfxFontStyle & aStyle)995 bool nsXPLookAndFeel::GetFontValue(FontID aID, nsString& aName,
996                                    gfxFontStyle& aStyle) {
997   if (const LookAndFeelFont* cached = sFontCache.Get(aID)) {
998     return LookAndFeelFontToStyle(*cached, aName, aStyle);
999   }
1000   LookAndFeelFont font;
1001   const bool haveFont = NativeGetFont(aID, aName, aStyle);
1002   font.haveFont() = haveFont;
1003   if (haveFont) {
1004     font = StyleToLookAndFeelFont(aName, aStyle);
1005   }
1006   sFontCache.Insert(aID, std::move(font));
1007   return haveFont;
1008 }
1009 
RefreshImpl()1010 void nsXPLookAndFeel::RefreshImpl() {
1011   // Wipe out our caches.
1012   sLightColorCache.Clear();
1013   sDarkColorCache.Clear();
1014   sFontCache.Clear();
1015   sFloatCache.Clear();
1016   sIntCache.Clear();
1017   RecomputeColorSchemes();
1018 
1019   // Clear any cached FullLookAndFeel data, which is now invalid.
1020   if (XRE_IsParentProcess()) {
1021     widget::RemoteLookAndFeel::ClearCachedData();
1022   }
1023 }
1024 
1025 static bool sRecordedLookAndFeelTelemetry = false;
1026 
RecordTelemetry()1027 void nsXPLookAndFeel::RecordTelemetry() {
1028   if (!XRE_IsParentProcess()) {
1029     return;
1030   }
1031 
1032   if (sRecordedLookAndFeelTelemetry) {
1033     return;
1034   }
1035 
1036   sRecordedLookAndFeelTelemetry = true;
1037 
1038   int32_t i;
1039   Telemetry::ScalarSet(
1040       Telemetry::ScalarID::WIDGET_DARK_MODE,
1041       NS_SUCCEEDED(GetIntValue(IntID::SystemUsesDarkTheme, i)) && i != 0);
1042 
1043   RecordLookAndFeelSpecificTelemetry();
1044 }
1045 
1046 namespace mozilla {
1047 
1048 static widget::ThemeChangeKind sGlobalThemeChangeKind{0};
1049 
NotifyChangedAllWindows(widget::ThemeChangeKind aKind)1050 void LookAndFeel::NotifyChangedAllWindows(widget::ThemeChangeKind aKind) {
1051   sGlobalThemeChanged = true;
1052   sGlobalThemeChangeKind |= aKind;
1053 
1054   if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
1055     const char16_t kind[] = {char16_t(aKind), 0};
1056     obs->NotifyObservers(nullptr, "internal-look-and-feel-changed", kind);
1057   }
1058 }
1059 
DoHandleGlobalThemeChange()1060 void LookAndFeel::DoHandleGlobalThemeChange() {
1061   MOZ_ASSERT(sGlobalThemeChanged);
1062   sGlobalThemeChanged = false;
1063   auto kind = std::exchange(sGlobalThemeChangeKind, widget::ThemeChangeKind(0));
1064 
1065   // Tell the theme that it changed, so it can flush any handles to stale theme
1066   // data.
1067   //
1068   // We can use the *DoNotUseDirectly functions directly here, because we want
1069   // to notify all possible themes in a given process (but just once).
1070   if (XRE_IsParentProcess() ||
1071       !StaticPrefs::widget_non_native_theme_enabled()) {
1072     if (nsCOMPtr<nsITheme> theme = do_GetNativeThemeDoNotUseDirectly()) {
1073       theme->ThemeChanged();
1074     }
1075   }
1076   if (nsCOMPtr<nsITheme> theme = do_GetBasicNativeThemeDoNotUseDirectly()) {
1077     theme->ThemeChanged();
1078   }
1079 
1080   // Clear all cached LookAndFeel colors.
1081   LookAndFeel::Refresh();
1082 
1083   // Reset default background and foreground colors for the document since they
1084   // may be using system colors.
1085   PreferenceSheet::Refresh();
1086 
1087   // Vector images (SVG) may be using theme colors so we discard all cached
1088   // surfaces. (We could add a vector image only version of DiscardAll, but
1089   // in bug 940625 we decided theme changes are rare enough not to bother.)
1090   image::SurfaceCacheUtils::DiscardAll();
1091 
1092   if (XRE_IsParentProcess()) {
1093     dom::ContentParent::BroadcastThemeUpdate(kind);
1094   }
1095 
1096   nsContentUtils::AddScriptRunner(
1097       NS_NewRunnableFunction("HandleGlobalThemeChange", [] {
1098         if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
1099           obs->NotifyObservers(nullptr, "look-and-feel-changed", nullptr);
1100         }
1101       }));
1102 }
1103 
ShouldUseStandinsForNativeColorForNonNativeTheme(const dom::Document & aDoc,LookAndFeel::ColorID aColor)1104 static bool ShouldUseStandinsForNativeColorForNonNativeTheme(
1105     const dom::Document& aDoc, LookAndFeel::ColorID aColor) {
1106   using ColorID = LookAndFeel::ColorID;
1107   if (!aDoc.ShouldAvoidNativeTheme()) {
1108     return false;
1109   }
1110 
1111   // The native theme doesn't use native system colors backgrounds etc, except
1112   // when in high-contrast mode, so spoof some of the colors with stand-ins to
1113   // prevent lack of contrast.
1114   switch (aColor) {
1115     case ColorID::Buttonface:
1116     case ColorID::Buttontext:
1117     case ColorID::MozButtonhoverface:
1118     case ColorID::MozButtonhovertext:
1119     case ColorID::MozButtonactiveface:
1120     case ColorID::MozButtonactivetext:
1121     case ColorID::MozButtondisabledface:
1122 
1123     case ColorID::Threedlightshadow:
1124     case ColorID::Threeddarkshadow:
1125     case ColorID::Threedface:
1126 
1127     case ColorID::MozCombobox:
1128     case ColorID::MozComboboxtext:
1129 
1130     case ColorID::Field:
1131     case ColorID::MozDisabledfield:
1132     case ColorID::Fieldtext:
1133 
1134     case ColorID::Graytext:
1135 
1136       return !PreferenceSheet::PrefsFor(aDoc)
1137                   .NonNativeThemeShouldBeHighContrast();
1138 
1139     default:
1140       break;
1141   }
1142 
1143   return false;
1144 }
1145 
1146 ColorScheme LookAndFeel::sChromeColorScheme;
1147 ColorScheme LookAndFeel::sContentColorScheme;
1148 bool LookAndFeel::sColorSchemeInitialized;
1149 bool LookAndFeel::sGlobalThemeChanged;
1150 
ColorSchemeSettingForChrome()1151 auto LookAndFeel::ColorSchemeSettingForChrome() -> ChromeColorSchemeSetting {
1152   switch (StaticPrefs::browser_theme_toolbar_theme()) {
1153     case 0:  // Dark
1154       return ChromeColorSchemeSetting::Dark;
1155     case 1:  // Light
1156       return ChromeColorSchemeSetting::Light;
1157     default:
1158       return ChromeColorSchemeSetting::System;
1159   }
1160 }
1161 
ThemeDerivedColorSchemeForContent()1162 ColorScheme LookAndFeel::ThemeDerivedColorSchemeForContent() {
1163   switch (StaticPrefs::browser_theme_content_theme()) {
1164     case 0:  // Dark
1165       return ColorScheme::Dark;
1166     case 1:  // Light
1167       return ColorScheme::Light;
1168     default:
1169       return ColorSchemeForChrome();
1170   }
1171 }
1172 
RecomputeColorSchemes()1173 void LookAndFeel::RecomputeColorSchemes() {
1174   sColorSchemeInitialized = true;
1175 
1176   sChromeColorScheme = [] {
1177     switch (ColorSchemeSettingForChrome()) {
1178       case ChromeColorSchemeSetting::Light:
1179         return ColorScheme::Light;
1180       case ChromeColorSchemeSetting::Dark:
1181         return ColorScheme::Dark;
1182       case ChromeColorSchemeSetting::System:
1183         break;
1184     }
1185     return SystemColorScheme();
1186   }();
1187 
1188   sContentColorScheme = [] {
1189     switch (StaticPrefs::layout_css_prefers_color_scheme_content_override()) {
1190       case 0:
1191         return ColorScheme::Dark;
1192       case 1:
1193         return ColorScheme::Light;
1194       case 2:
1195         return SystemColorScheme();
1196       default:
1197         break;  // Use the browser theme.
1198     }
1199 
1200     return ThemeDerivedColorSchemeForContent();
1201   }();
1202 }
1203 
ColorSchemeForStyle(const dom::Document & aDoc,const StyleColorSchemeFlags & aFlags)1204 ColorScheme LookAndFeel::ColorSchemeForStyle(
1205     const dom::Document& aDoc, const StyleColorSchemeFlags& aFlags) {
1206   if (PreferenceSheet::MayForceColors()) {
1207     auto& prefs = PreferenceSheet::PrefsFor(aDoc);
1208     if (!prefs.mUseDocumentColors) {
1209       // When forcing colors, we can use our preferred color-scheme. Do this
1210       // only if we're using system colors, as dark preference colors are not
1211       // exposed on the UI.
1212       //
1213       // Also, use light if we're using a high-contrast-theme on Windows, since
1214       // Windows overrides the light colors with HCM colors when HCM is active.
1215 #ifdef XP_WIN
1216       if (prefs.mUseAccessibilityTheme) {
1217         return ColorScheme::Light;
1218       }
1219 #endif
1220       if (StaticPrefs::browser_display_use_system_colors()) {
1221         return aDoc.PreferredColorScheme();
1222       }
1223       return ColorScheme::Light;
1224     }
1225   }
1226 
1227   StyleColorSchemeFlags style(aFlags);
1228   if (!style) {
1229     style.bits = aDoc.GetColorSchemeBits();
1230   }
1231   const bool supportsDark = bool(style & StyleColorSchemeFlags::DARK);
1232   const bool supportsLight = bool(style & StyleColorSchemeFlags::LIGHT);
1233   if (supportsLight && supportsDark) {
1234     // Both color-schemes are explicitly supported, use the preferred one.
1235     return aDoc.PreferredColorScheme();
1236   }
1237   if (supportsDark || supportsLight) {
1238     // One color-scheme is explicitly supported and one isn't, so use the one
1239     // the content supports.
1240     return supportsDark ? ColorScheme::Dark : ColorScheme::Light;
1241   }
1242   // No value specified. Chrome docs always supports both, so use the preferred
1243   // color-scheme.
1244   if (nsContentUtils::IsChromeDoc(&aDoc)) {
1245     return aDoc.PreferredColorScheme();
1246   }
1247   // Default content to light.
1248   return ColorScheme::Light;
1249 }
1250 
ColorSchemeForFrame(const nsIFrame * aFrame)1251 LookAndFeel::ColorScheme LookAndFeel::ColorSchemeForFrame(
1252     const nsIFrame* aFrame) {
1253   return ColorSchemeForStyle(*aFrame->PresContext()->Document(),
1254                              aFrame->StyleUI()->mColorScheme.bits);
1255 }
1256 
1257 // static
GetColor(ColorID aId,ColorScheme aScheme,UseStandins aUseStandins)1258 Maybe<nscolor> LookAndFeel::GetColor(ColorID aId, ColorScheme aScheme,
1259                                      UseStandins aUseStandins) {
1260   nscolor result;
1261   nsresult rv = nsLookAndFeel::GetInstance()->GetColorValue(
1262       aId, aScheme, aUseStandins, result);
1263   if (NS_FAILED(rv)) {
1264     return Nothing();
1265   }
1266   return Some(result);
1267 }
1268 
1269 // Returns whether there is a CSS color name for this color.
ColorIsCSSAccessible(LookAndFeel::ColorID aId)1270 static bool ColorIsCSSAccessible(LookAndFeel::ColorID aId) {
1271   using ColorID = LookAndFeel::ColorID;
1272 
1273   switch (aId) {
1274     case ColorID::TextSelectDisabledBackground:
1275     case ColorID::TextSelectAttentionBackground:
1276     case ColorID::TextSelectAttentionForeground:
1277     case ColorID::TextHighlightBackground:
1278     case ColorID::TextHighlightForeground:
1279     case ColorID::ThemedScrollbar:
1280     case ColorID::ThemedScrollbarInactive:
1281     case ColorID::ThemedScrollbarThumb:
1282     case ColorID::ThemedScrollbarThumbActive:
1283     case ColorID::ThemedScrollbarThumbInactive:
1284     case ColorID::ThemedScrollbarThumbHover:
1285     case ColorID::IMERawInputBackground:
1286     case ColorID::IMERawInputForeground:
1287     case ColorID::IMERawInputUnderline:
1288     case ColorID::IMESelectedRawTextBackground:
1289     case ColorID::IMESelectedRawTextForeground:
1290     case ColorID::IMESelectedRawTextUnderline:
1291     case ColorID::IMEConvertedTextBackground:
1292     case ColorID::IMEConvertedTextForeground:
1293     case ColorID::IMEConvertedTextUnderline:
1294     case ColorID::IMESelectedConvertedTextBackground:
1295     case ColorID::IMESelectedConvertedTextForeground:
1296     case ColorID::IMESelectedConvertedTextUnderline:
1297     case ColorID::SpellCheckerUnderline:
1298       return false;
1299     default:
1300       break;
1301   }
1302 
1303   return true;
1304 }
1305 
ShouldUseStandins(const dom::Document & aDoc,ColorID aId)1306 LookAndFeel::UseStandins LookAndFeel::ShouldUseStandins(
1307     const dom::Document& aDoc, ColorID aId) {
1308   if (ShouldUseStandinsForNativeColorForNonNativeTheme(aDoc, aId)) {
1309     return UseStandins::Yes;
1310   }
1311   if (nsContentUtils::UseStandinsForNativeColors() &&
1312       ColorIsCSSAccessible(aId) && !nsContentUtils::IsChromeDoc(&aDoc)) {
1313     return UseStandins::Yes;
1314   }
1315   if (aDoc.IsStaticDocument() &&
1316       !PreferenceSheet::ContentPrefs().mUseDocumentColors) {
1317     return UseStandins::Yes;
1318   }
1319   return UseStandins::No;
1320 }
1321 
GetColor(ColorID aId,const nsIFrame * aFrame)1322 Maybe<nscolor> LookAndFeel::GetColor(ColorID aId, const nsIFrame* aFrame) {
1323   const auto* doc = aFrame->PresContext()->Document();
1324   return GetColor(aId, ColorSchemeForFrame(aFrame),
1325                   ShouldUseStandins(*doc, aId));
1326 }
1327 
1328 // static
GetInt(IntID aID,int32_t * aResult)1329 nsresult LookAndFeel::GetInt(IntID aID, int32_t* aResult) {
1330   return nsLookAndFeel::GetInstance()->GetIntValue(aID, *aResult);
1331 }
1332 
1333 // static
GetFloat(FloatID aID,float * aResult)1334 nsresult LookAndFeel::GetFloat(FloatID aID, float* aResult) {
1335   return nsLookAndFeel::GetInstance()->GetFloatValue(aID, *aResult);
1336 }
1337 
1338 // static
GetFont(FontID aID,nsString & aName,gfxFontStyle & aStyle)1339 bool LookAndFeel::GetFont(FontID aID, nsString& aName, gfxFontStyle& aStyle) {
1340   return nsLookAndFeel::GetInstance()->GetFontValue(aID, aName, aStyle);
1341 }
1342 
1343 // static
GetPasswordCharacter()1344 char16_t LookAndFeel::GetPasswordCharacter() {
1345   return nsLookAndFeel::GetInstance()->GetPasswordCharacterImpl();
1346 }
1347 
1348 // static
GetEchoPassword()1349 bool LookAndFeel::GetEchoPassword() {
1350   if (StaticPrefs::editor_password_mask_delay() >= 0) {
1351     return StaticPrefs::editor_password_mask_delay() > 0;
1352   }
1353   return nsLookAndFeel::GetInstance()->GetEchoPasswordImpl();
1354 }
1355 
1356 // static
GetPasswordMaskDelay()1357 uint32_t LookAndFeel::GetPasswordMaskDelay() {
1358   int32_t delay = StaticPrefs::editor_password_mask_delay();
1359   if (delay < 0) {
1360     return nsLookAndFeel::GetInstance()->GetPasswordMaskDelayImpl();
1361   }
1362   return delay;
1363 }
1364 
DrawInTitlebar()1365 bool LookAndFeel::DrawInTitlebar() {
1366   switch (StaticPrefs::browser_tabs_inTitlebar()) {
1367     case 0:
1368       return false;
1369     case 1:
1370       return true;
1371     default:
1372       break;
1373   }
1374   return nsLookAndFeel::GetInstance()->GetDefaultDrawInTitlebar();
1375 }
1376 
GetThemeInfo(nsACString & aOut)1377 void LookAndFeel::GetThemeInfo(nsACString& aOut) {
1378   nsLookAndFeel::GetInstance()->GetThemeInfo(aOut);
1379 }
1380 
1381 // static
Refresh()1382 void LookAndFeel::Refresh() {
1383   nsLookAndFeel::GetInstance()->RefreshImpl();
1384   widget::Theme::LookAndFeelChanged();
1385 }
1386 
1387 // static
NativeInit()1388 void LookAndFeel::NativeInit() { nsLookAndFeel::GetInstance()->NativeInit(); }
1389 
1390 // static
SetData(widget::FullLookAndFeel && aTables)1391 void LookAndFeel::SetData(widget::FullLookAndFeel&& aTables) {
1392   nsLookAndFeel::GetInstance()->SetDataImpl(std::move(aTables));
1393 }
1394 
1395 }  // namespace mozilla
1396