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