1 /* -*- Mode: C++; tab-width: 2; 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 "nsLookAndFeel.h"
7 #include <windows.h>
8 #include <shellapi.h>
9 #include "nsStyleConsts.h"
10 #include "nsUXThemeData.h"
11 #include "nsUXThemeConstants.h"
12 #include "nsWindowsHelpers.h"
13 #include "WinUtils.h"
14 #include "mozilla/FontPropertyTypes.h"
15 #include "mozilla/Telemetry.h"
16 #include "mozilla/WindowsVersion.h"
17 #include "gfxFontConstants.h"
18 #include "gfxWindowsPlatform.h"
19 
20 using namespace mozilla;
21 using namespace mozilla::widget;
22 
23 // static
GetOperatingSystemVersion()24 LookAndFeel::OperatingSystemVersion nsLookAndFeel::GetOperatingSystemVersion() {
25   static OperatingSystemVersion version = OperatingSystemVersion::Unknown;
26 
27   if (version != OperatingSystemVersion::Unknown) {
28     return version;
29   }
30 
31   if (IsWin10OrLater()) {
32     version = OperatingSystemVersion::Windows10;
33   } else if (IsWin8OrLater()) {
34     version = OperatingSystemVersion::Windows8;
35   } else {
36     version = OperatingSystemVersion::Windows7;
37   }
38 
39   return version;
40 }
41 
GetColorFromTheme(nsUXThemeClass cls,int32_t aPart,int32_t aState,int32_t aPropId,nscolor & aColor)42 static nsresult GetColorFromTheme(nsUXThemeClass cls, int32_t aPart,
43                                   int32_t aState, int32_t aPropId,
44                                   nscolor& aColor) {
45   COLORREF color;
46   HRESULT hr = GetThemeColor(nsUXThemeData::GetTheme(cls), aPart, aState,
47                              aPropId, &color);
48   if (hr == S_OK) {
49     aColor = COLOREF_2_NSRGB(color);
50     return NS_OK;
51   }
52   return NS_ERROR_FAILURE;
53 }
54 
GetSystemParam(long flag,int32_t def)55 static int32_t GetSystemParam(long flag, int32_t def) {
56   DWORD value;
57   return ::SystemParametersInfo(flag, 0, &value, 0) ? value : def;
58 }
59 
SystemWantsDarkTheme(int32_t & darkThemeEnabled)60 static nsresult SystemWantsDarkTheme(int32_t& darkThemeEnabled) {
61   if (!IsWin10OrLater()) {
62     darkThemeEnabled = 0;
63     return NS_OK;
64   }
65 
66   nsresult rv = NS_OK;
67   nsCOMPtr<nsIWindowsRegKey> personalizeKey =
68       do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
69   if (NS_WARN_IF(NS_FAILED(rv))) {
70     return rv;
71   }
72 
73   rv = personalizeKey->Open(
74       nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
75       nsLiteralString(
76           u"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"),
77       nsIWindowsRegKey::ACCESS_QUERY_VALUE);
78   if (NS_FAILED(rv)) {
79     return rv;
80   }
81 
82   uint32_t lightThemeEnabled;
83   rv =
84       personalizeKey->ReadIntValue(u"AppsUseLightTheme"_ns, &lightThemeEnabled);
85   if (NS_SUCCEEDED(rv)) {
86     darkThemeEnabled = !lightThemeEnabled;
87   }
88 
89   return rv;
90 }
91 
nsLookAndFeel()92 nsLookAndFeel::nsLookAndFeel()
93     : nsXPLookAndFeel(),
94       mHasColorMenuHoverText(false),
95       mHasColorAccent(false),
96       mHasColorAccentText(false),
97       mHasColorMediaText(false),
98       mHasColorCommunicationsText(false),
99       mInitialized(false) {
100   mozilla::Telemetry::Accumulate(mozilla::Telemetry::TOUCH_ENABLED_DEVICE,
101                                  WinUtils::IsTouchDeviceSupportPresent());
102 }
103 
~nsLookAndFeel()104 nsLookAndFeel::~nsLookAndFeel() {}
105 
NativeInit()106 void nsLookAndFeel::NativeInit() { EnsureInit(); }
107 
108 /* virtual */
RefreshImpl()109 void nsLookAndFeel::RefreshImpl() {
110   nsXPLookAndFeel::RefreshImpl();
111   mInitialized = false;  // Fetch system colors next time they're used.
112 }
113 
NativeGetColor(ColorID aID,ColorScheme,nscolor & aColor)114 nsresult nsLookAndFeel::NativeGetColor(ColorID aID, ColorScheme,
115                                        nscolor& aColor) {
116   EnsureInit();
117 
118   nsresult res = NS_OK;
119 
120   int idx;
121   switch (aID) {
122     case ColorID::WindowBackground:
123       idx = COLOR_WINDOW;
124       break;
125     case ColorID::WindowForeground:
126       idx = COLOR_WINDOWTEXT;
127       break;
128     case ColorID::WidgetBackground:
129       idx = COLOR_BTNFACE;
130       break;
131     case ColorID::WidgetForeground:
132       idx = COLOR_BTNTEXT;
133       break;
134     case ColorID::WidgetSelectBackground:
135       idx = COLOR_HIGHLIGHT;
136       break;
137     case ColorID::WidgetSelectForeground:
138       idx = COLOR_HIGHLIGHTTEXT;
139       break;
140     case ColorID::Widget3DHighlight:
141       idx = COLOR_BTNHIGHLIGHT;
142       break;
143     case ColorID::Widget3DShadow:
144       idx = COLOR_BTNSHADOW;
145       break;
146     case ColorID::TextBackground:
147       idx = COLOR_WINDOW;
148       break;
149     case ColorID::TextForeground:
150       idx = COLOR_WINDOWTEXT;
151       break;
152     case ColorID::TextSelectBackground:
153     case ColorID::IMESelectedRawTextBackground:
154     case ColorID::IMESelectedConvertedTextBackground:
155       idx = COLOR_HIGHLIGHT;
156       break;
157     case ColorID::TextSelectForeground:
158     case ColorID::IMESelectedRawTextForeground:
159     case ColorID::IMESelectedConvertedTextForeground:
160       idx = COLOR_HIGHLIGHTTEXT;
161       break;
162     case ColorID::IMERawInputBackground:
163     case ColorID::IMEConvertedTextBackground:
164       aColor = NS_TRANSPARENT;
165       return NS_OK;
166     case ColorID::IMERawInputForeground:
167     case ColorID::IMEConvertedTextForeground:
168       aColor = NS_SAME_AS_FOREGROUND_COLOR;
169       return NS_OK;
170     case ColorID::IMERawInputUnderline:
171     case ColorID::IMEConvertedTextUnderline:
172       aColor = NS_SAME_AS_FOREGROUND_COLOR;
173       return NS_OK;
174     case ColorID::IMESelectedRawTextUnderline:
175     case ColorID::IMESelectedConvertedTextUnderline:
176       aColor = NS_TRANSPARENT;
177       return NS_OK;
178     case ColorID::SpellCheckerUnderline:
179       aColor = NS_RGB(0xff, 0, 0);
180       return NS_OK;
181 
182     // New CSS 2 Color definitions
183     case ColorID::Activeborder:
184       idx = COLOR_ACTIVEBORDER;
185       break;
186     case ColorID::Activecaption:
187       idx = COLOR_ACTIVECAPTION;
188       break;
189     case ColorID::Appworkspace:
190       idx = COLOR_APPWORKSPACE;
191       break;
192     case ColorID::Background:
193       idx = COLOR_BACKGROUND;
194       break;
195     case ColorID::Buttonface:
196     case ColorID::MozButtonhoverface:
197       idx = COLOR_BTNFACE;
198       break;
199     case ColorID::Buttonhighlight:
200       idx = COLOR_BTNHIGHLIGHT;
201       break;
202     case ColorID::Buttonshadow:
203       idx = COLOR_BTNSHADOW;
204       break;
205     case ColorID::Buttontext:
206     case ColorID::MozButtonhovertext:
207       idx = COLOR_BTNTEXT;
208       break;
209     case ColorID::Captiontext:
210       idx = COLOR_CAPTIONTEXT;
211       break;
212     case ColorID::Graytext:
213       idx = COLOR_GRAYTEXT;
214       break;
215     case ColorID::Highlight:
216     case ColorID::MozHtmlCellhighlight:
217     case ColorID::MozMenuhover:
218       idx = COLOR_HIGHLIGHT;
219       break;
220     case ColorID::MozMenubarhovertext:
221       if (!nsUXThemeData::IsAppThemed()) {
222         idx = nsUXThemeData::AreFlatMenusEnabled() ? COLOR_HIGHLIGHTTEXT
223                                                    : COLOR_MENUTEXT;
224         break;
225       }
226       // Fall through
227     case ColorID::MozMenuhovertext:
228       if (mHasColorMenuHoverText) {
229         aColor = mColorMenuHoverText;
230         return NS_OK;
231       }
232       // Fall through
233     case ColorID::Highlighttext:
234     case ColorID::MozHtmlCellhighlighttext:
235       idx = COLOR_HIGHLIGHTTEXT;
236       break;
237     case ColorID::Inactiveborder:
238       idx = COLOR_INACTIVEBORDER;
239       break;
240     case ColorID::Inactivecaption:
241       idx = COLOR_INACTIVECAPTION;
242       break;
243     case ColorID::Inactivecaptiontext:
244       idx = COLOR_INACTIVECAPTIONTEXT;
245       break;
246     case ColorID::Infobackground:
247       idx = COLOR_INFOBK;
248       break;
249     case ColorID::Infotext:
250       idx = COLOR_INFOTEXT;
251       break;
252     case ColorID::Menu:
253       idx = COLOR_MENU;
254       break;
255     case ColorID::Menutext:
256     case ColorID::MozMenubartext:
257       idx = COLOR_MENUTEXT;
258       break;
259     case ColorID::Scrollbar:
260       idx = COLOR_SCROLLBAR;
261       break;
262     case ColorID::Threeddarkshadow:
263       idx = COLOR_3DDKSHADOW;
264       break;
265     case ColorID::Threedface:
266       idx = COLOR_3DFACE;
267       break;
268     case ColorID::Threedhighlight:
269       idx = COLOR_3DHIGHLIGHT;
270       break;
271     case ColorID::Threedlightshadow:
272       idx = COLOR_3DLIGHT;
273       break;
274     case ColorID::Threedshadow:
275       idx = COLOR_3DSHADOW;
276       break;
277     case ColorID::Window:
278       idx = COLOR_WINDOW;
279       break;
280     case ColorID::Windowframe:
281       idx = COLOR_WINDOWFRAME;
282       break;
283     case ColorID::Windowtext:
284       idx = COLOR_WINDOWTEXT;
285       break;
286     case ColorID::MozEventreerow:
287     case ColorID::MozOddtreerow:
288     case ColorID::Field:
289     case ColorID::MozCombobox:
290       idx = COLOR_WINDOW;
291       break;
292     case ColorID::Fieldtext:
293     case ColorID::MozComboboxtext:
294       idx = COLOR_WINDOWTEXT;
295       break;
296     case ColorID::MozDialog:
297     case ColorID::MozCellhighlight:
298       idx = COLOR_3DFACE;
299       break;
300     case ColorID::MozAccentColor:
301       if (mHasColorAccent) {
302         aColor = mColorAccent;
303       } else {
304         // Seems to be the default color (hardcoded because of bug 1065998)
305         aColor = NS_RGB(0, 120, 215);
306       }
307       return NS_OK;
308     case ColorID::MozAccentColorForeground:
309       if (mHasColorAccentText) {
310         aColor = mColorAccentText;
311       } else {
312         aColor = NS_RGB(255, 255, 255);
313       }
314       return NS_OK;
315     case ColorID::MozWinMediatext:
316       if (mHasColorMediaText) {
317         aColor = mColorMediaText;
318         return NS_OK;
319       }
320       // if we've gotten here just return -moz-dialogtext instead
321       idx = COLOR_WINDOWTEXT;
322       break;
323     case ColorID::MozWinCommunicationstext:
324       if (mHasColorCommunicationsText) {
325         aColor = mColorCommunicationsText;
326         return NS_OK;
327       }
328       // if we've gotten here just return -moz-dialogtext instead
329       idx = COLOR_WINDOWTEXT;
330       break;
331     case ColorID::MozDialogtext:
332     case ColorID::MozCellhighlighttext:
333     case ColorID::MozColheadertext:
334     case ColorID::MozColheaderhovertext:
335       idx = COLOR_WINDOWTEXT;
336       break;
337     case ColorID::MozDragtargetzone:
338       idx = COLOR_HIGHLIGHTTEXT;
339       break;
340     case ColorID::MozButtondefault:
341       idx = COLOR_3DDKSHADOW;
342       break;
343     case ColorID::MozNativehyperlinktext:
344       idx = COLOR_HOTLIGHT;
345       break;
346     default:
347       NS_WARNING("Unknown color for nsLookAndFeel");
348       idx = COLOR_WINDOW;
349       res = NS_ERROR_FAILURE;
350       break;
351   }
352 
353   aColor = GetColorForSysColorIndex(idx);
354 
355   return res;
356 }
357 
NativeGetInt(IntID aID,int32_t & aResult)358 nsresult nsLookAndFeel::NativeGetInt(IntID aID, int32_t& aResult) {
359   nsresult res = NS_OK;
360 
361   switch (aID) {
362     case IntID::ScrollButtonLeftMouseButtonAction:
363       aResult = 0;
364       break;
365     case IntID::ScrollButtonMiddleMouseButtonAction:
366     case IntID::ScrollButtonRightMouseButtonAction:
367       aResult = 3;
368       break;
369     case IntID::CaretBlinkTime:
370       aResult = static_cast<int32_t>(::GetCaretBlinkTime());
371       break;
372     case IntID::CaretWidth:
373       aResult = 1;
374       break;
375     case IntID::ShowCaretDuringSelection:
376       aResult = 0;
377       break;
378     case IntID::SelectTextfieldsOnKeyFocus:
379       // Select textfield content when focused by kbd
380       // used by EventStateManager::sTextfieldSelectModel
381       aResult = 1;
382       break;
383     case IntID::SubmenuDelay:
384       // This will default to the Windows' default
385       // (400ms) on error.
386       aResult = GetSystemParam(SPI_GETMENUSHOWDELAY, 400);
387       break;
388     case IntID::TooltipDelay:
389       aResult = 500;
390       break;
391     case IntID::MenusCanOverlapOSBar:
392       // we want XUL popups to be able to overlap the task bar.
393       aResult = 1;
394       break;
395     case IntID::DragThresholdX:
396       // The system metric is the number of pixels at which a drag should
397       // start.  Our look and feel metric is the number of pixels you can
398       // move before starting a drag, so subtract 1.
399 
400       aResult = ::GetSystemMetrics(SM_CXDRAG) - 1;
401       break;
402     case IntID::DragThresholdY:
403       aResult = ::GetSystemMetrics(SM_CYDRAG) - 1;
404       break;
405     case IntID::UseAccessibilityTheme:
406       // High contrast is a misnomer under Win32 -- any theme can be used with
407       // it, e.g. normal contrast with large fonts, low contrast, etc. The high
408       // contrast flag really means -- use this theme and don't override it.
409       aResult = nsUXThemeData::IsHighContrastOn();
410       break;
411     case IntID::ScrollArrowStyle:
412       aResult = eScrollArrowStyle_Single;
413       break;
414     case IntID::ScrollSliderStyle:
415       aResult = eScrollThumbStyle_Proportional;
416       break;
417     case IntID::TreeOpenDelay:
418       aResult = 1000;
419       break;
420     case IntID::TreeCloseDelay:
421       aResult = 0;
422       break;
423     case IntID::TreeLazyScrollDelay:
424       aResult = 150;
425       break;
426     case IntID::TreeScrollDelay:
427       aResult = 100;
428       break;
429     case IntID::TreeScrollLinesMax:
430       aResult = 3;
431       break;
432     case IntID::WindowsClassic:
433       aResult = !nsUXThemeData::IsAppThemed();
434       break;
435     case IntID::WindowsDefaultTheme:
436       aResult = nsUXThemeData::IsDefaultWindowTheme();
437       break;
438     case IntID::WindowsThemeIdentifier:
439       aResult = nsUXThemeData::GetNativeThemeId();
440       break;
441     case IntID::OperatingSystemVersionIdentifier: {
442       aResult = int32_t(GetOperatingSystemVersion());
443       break;
444     }
445 
446     case IntID::MacGraphiteTheme:
447       aResult = 0;
448       res = NS_ERROR_NOT_IMPLEMENTED;
449       break;
450     case IntID::DWMCompositor:
451       aResult = gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled();
452       break;
453     case IntID::WindowsAccentColorInTitlebar: {
454       nscolor unused;
455       if (NS_WARN_IF(NS_FAILED(GetAccentColor(unused)))) {
456         aResult = 0;
457         break;
458       }
459 
460       uint32_t colorPrevalence;
461       nsresult rv = mDwmKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
462                                   u"SOFTWARE\\Microsoft\\Windows\\DWM"_ns,
463                                   nsIWindowsRegKey::ACCESS_QUERY_VALUE);
464       if (NS_WARN_IF(NS_FAILED(rv))) {
465         return rv;
466       }
467 
468       // The ColorPrevalence value is set to 1 when the "Show color on title
469       // bar" setting in the Color section of Window's Personalization settings
470       // is turned on.
471       aResult = (NS_SUCCEEDED(mDwmKey->ReadIntValue(u"ColorPrevalence"_ns,
472                                                     &colorPrevalence)) &&
473                  colorPrevalence == 1)
474                     ? 1
475                     : 0;
476 
477       mDwmKey->Close();
478     } break;
479     case IntID::WindowsGlass:
480       // Aero Glass is only available prior to Windows 8 when DWM is used.
481       aResult = (gfxWindowsPlatform::GetPlatform()->DwmCompositionEnabled() &&
482                  !IsWin8OrLater());
483       break;
484     case IntID::AlertNotificationOrigin:
485       aResult = 0;
486       {
487         // Get task bar window handle
488         HWND shellWindow = FindWindowW(L"Shell_TrayWnd", nullptr);
489 
490         if (shellWindow != nullptr) {
491           // Determine position
492           APPBARDATA appBarData;
493           appBarData.hWnd = shellWindow;
494           appBarData.cbSize = sizeof(appBarData);
495           if (SHAppBarMessage(ABM_GETTASKBARPOS, &appBarData)) {
496             // Set alert origin as a bit field - see LookAndFeel.h
497             // 0 represents bottom right, sliding vertically.
498             switch (appBarData.uEdge) {
499               case ABE_LEFT:
500                 aResult = NS_ALERT_HORIZONTAL | NS_ALERT_LEFT;
501                 break;
502               case ABE_RIGHT:
503                 aResult = NS_ALERT_HORIZONTAL;
504                 break;
505               case ABE_TOP:
506                 aResult = NS_ALERT_TOP;
507                 // fall through for the right-to-left handling.
508               case ABE_BOTTOM:
509                 // If the task bar is right-to-left,
510                 // move the origin to the left
511                 if (::GetWindowLong(shellWindow, GWL_EXSTYLE) & WS_EX_LAYOUTRTL)
512                   aResult |= NS_ALERT_LEFT;
513                 break;
514             }
515           }
516         }
517       }
518       break;
519     case IntID::IMERawInputUnderlineStyle:
520     case IntID::IMEConvertedTextUnderlineStyle:
521       aResult = NS_STYLE_TEXT_DECORATION_STYLE_DASHED;
522       break;
523     case IntID::IMESelectedRawTextUnderlineStyle:
524     case IntID::IMESelectedConvertedTextUnderline:
525       aResult = NS_STYLE_TEXT_DECORATION_STYLE_NONE;
526       break;
527     case IntID::SpellCheckerUnderlineStyle:
528       aResult = NS_STYLE_TEXT_DECORATION_STYLE_WAVY;
529       break;
530     case IntID::ScrollbarButtonAutoRepeatBehavior:
531       aResult = 0;
532       break;
533     case IntID::SwipeAnimationEnabled:
534       aResult = 0;
535       break;
536     case IntID::UseOverlayScrollbars:
537       aResult = false;
538       break;
539     case IntID::AllowOverlayScrollbarsOverlap:
540       aResult = 0;
541       break;
542     case IntID::ScrollbarDisplayOnMouseMove:
543       aResult = 1;
544       break;
545     case IntID::ScrollbarFadeBeginDelay:
546       aResult = 2500;
547       break;
548     case IntID::ScrollbarFadeDuration:
549       aResult = 350;
550       break;
551     case IntID::ContextMenuOffsetVertical:
552     case IntID::ContextMenuOffsetHorizontal:
553       aResult = 2;
554       break;
555     case IntID::SystemUsesDarkTheme:
556       res = SystemWantsDarkTheme(aResult);
557       break;
558     case IntID::SystemVerticalScrollbarWidth:
559       aResult = WinUtils::GetSystemMetricsForDpi(SM_CXVSCROLL, 96);
560       break;
561     case IntID::SystemHorizontalScrollbarHeight:
562       aResult = WinUtils::GetSystemMetricsForDpi(SM_CXHSCROLL, 96);
563       break;
564     case IntID::PrefersReducedMotion: {
565       BOOL enableAnimation = TRUE;
566       ::SystemParametersInfoW(SPI_GETCLIENTAREAANIMATION, 0, &enableAnimation,
567                               0);
568       aResult = enableAnimation ? 0 : 1;
569       break;
570     }
571     case IntID::PrimaryPointerCapabilities: {
572       aResult = static_cast<int32_t>(
573           widget::WinUtils::GetPrimaryPointerCapabilities());
574       break;
575     }
576     case IntID::AllPointerCapabilities: {
577       aResult =
578           static_cast<int32_t>(widget::WinUtils::GetAllPointerCapabilities());
579       break;
580     }
581     default:
582       aResult = 0;
583       res = NS_ERROR_FAILURE;
584   }
585   return res;
586 }
587 
NativeGetFloat(FloatID aID,float & aResult)588 nsresult nsLookAndFeel::NativeGetFloat(FloatID aID, float& aResult) {
589   nsresult res = NS_OK;
590 
591   switch (aID) {
592     case FloatID::IMEUnderlineRelativeSize:
593       aResult = 1.0f;
594       break;
595     case FloatID::SpellCheckerUnderlineRelativeSize:
596       aResult = 1.0f;
597       break;
598     default:
599       aResult = -1.0;
600       res = NS_ERROR_FAILURE;
601   }
602   return res;
603 }
604 
GetLookAndFeelFontInternal(const LOGFONTW & aLogFont,bool aUseShellDlg)605 LookAndFeelFont nsLookAndFeel::GetLookAndFeelFontInternal(
606     const LOGFONTW& aLogFont, bool aUseShellDlg) {
607   LookAndFeelFont result{};
608 
609   result.haveFont() = false;
610 
611   // Get scaling factor from physical to logical pixels
612   double pixelScale = 1.0 / WinUtils::SystemScaleFactor();
613 
614   // The lfHeight is in pixels, and it needs to be adjusted for the
615   // device it will be displayed on.
616   // Screens and Printers will differ in DPI
617   //
618   // So this accounts for the difference in the DeviceContexts
619   // The pixelScale will typically be 1.0 for the screen
620   // (though larger for hi-dpi screens where the Windows resolution
621   // scale factor is 125% or 150% or even more), and could be
622   // any value when going to a printer, for example pixelScale is
623   // 6.25 when going to a 600dpi printer.
624   float pixelHeight = -aLogFont.lfHeight;
625   if (pixelHeight < 0) {
626     nsAutoFont hFont(::CreateFontIndirectW(&aLogFont));
627     if (!hFont) {
628       return result;
629     }
630 
631     nsAutoHDC dc(::GetDC(nullptr));
632     HGDIOBJ hObject = ::SelectObject(dc, hFont);
633     TEXTMETRIC tm;
634     ::GetTextMetrics(dc, &tm);
635     ::SelectObject(dc, hObject);
636 
637     pixelHeight = tm.tmAscent;
638   }
639 
640   pixelHeight *= pixelScale;
641 
642   // we have problem on Simplified Chinese system because the system
643   // report the default font size is 8 points. but if we use 8, the text
644   // display very ugly. force it to be at 9 points (12 pixels) on that
645   // system (cp936), but leave other sizes alone.
646   if (pixelHeight < 12 && ::GetACP() == 936) {
647     pixelHeight = 12;
648   }
649 
650   result.haveFont() = true;
651 
652   if (aUseShellDlg) {
653     result.name() = u"MS Shell Dlg 2"_ns;
654   } else {
655     result.name() = aLogFont.lfFaceName;
656   }
657 
658   result.size() = pixelHeight;
659   result.italic() = !!aLogFont.lfItalic;
660   // FIXME: Other weights?
661   result.weight() = ((aLogFont.lfWeight == FW_BOLD) ? FontWeight::Bold()
662                                                     : FontWeight::Normal())
663                         .ToFloat();
664 
665   return result;
666 }
667 
GetLookAndFeelFont(LookAndFeel::FontID anID)668 LookAndFeelFont nsLookAndFeel::GetLookAndFeelFont(LookAndFeel::FontID anID) {
669   LookAndFeelFont result{};
670 
671   result.haveFont() = false;
672 
673   // FontID::Icon is handled differently than the others
674   if (anID == LookAndFeel::FontID::Icon) {
675     LOGFONTW logFont;
676     if (::SystemParametersInfoW(SPI_GETICONTITLELOGFONT, sizeof(logFont),
677                                 (PVOID)&logFont, 0)) {
678       result = GetLookAndFeelFontInternal(logFont, false);
679     }
680     return result;
681   }
682 
683   NONCLIENTMETRICSW ncm;
684   ncm.cbSize = sizeof(NONCLIENTMETRICSW);
685   if (!::SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm),
686                                (PVOID)&ncm, 0)) {
687     return result;
688   }
689 
690   switch (anID) {
691     case LookAndFeel::FontID::Menu:
692     case LookAndFeel::FontID::MozPullDownMenu:
693       result = GetLookAndFeelFontInternal(ncm.lfMenuFont, false);
694       break;
695     case LookAndFeel::FontID::Caption:
696       result = GetLookAndFeelFontInternal(ncm.lfCaptionFont, false);
697       break;
698     case LookAndFeel::FontID::SmallCaption:
699       result = GetLookAndFeelFontInternal(ncm.lfSmCaptionFont, false);
700       break;
701     case LookAndFeel::FontID::StatusBar:
702       result = GetLookAndFeelFontInternal(ncm.lfStatusFont, false);
703       break;
704     case LookAndFeel::FontID::MozDialog:
705     case LookAndFeel::FontID::MozButton:
706     case LookAndFeel::FontID::MozField:
707     case LookAndFeel::FontID::MozList:
708       // XXX It's not clear to me whether this is exactly the right
709       // set of LookAndFeel values to map to the dialog font; we may
710       // want to add or remove cases here after reviewing the visual
711       // results under various Windows versions.
712       result = GetLookAndFeelFontInternal(ncm.lfMessageFont, true);
713       break;
714     default:
715       result = GetLookAndFeelFontInternal(ncm.lfMessageFont, false);
716       break;
717   }
718 
719   return result;
720 }
721 
NativeGetFont(LookAndFeel::FontID anID,nsString & aFontName,gfxFontStyle & aFontStyle)722 bool nsLookAndFeel::NativeGetFont(LookAndFeel::FontID anID, nsString& aFontName,
723                                   gfxFontStyle& aFontStyle) {
724   LookAndFeelFont font = GetLookAndFeelFont(anID);
725   return LookAndFeelFontToStyle(font, aFontName, aFontStyle);
726 }
727 
728 /* virtual */
GetPasswordCharacterImpl()729 char16_t nsLookAndFeel::GetPasswordCharacterImpl() {
730 #define UNICODE_BLACK_CIRCLE_CHAR 0x25cf
731   return UNICODE_BLACK_CIRCLE_CHAR;
732 }
733 
734 /* static */
GetAccentColor(nscolor & aColor)735 nsresult nsLookAndFeel::GetAccentColor(nscolor& aColor) {
736   nsresult rv;
737 
738   if (!mDwmKey) {
739     mDwmKey = do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
740     if (NS_WARN_IF(NS_FAILED(rv))) {
741       return rv;
742     }
743   }
744 
745   rv = mDwmKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
746                      u"SOFTWARE\\Microsoft\\Windows\\DWM"_ns,
747                      nsIWindowsRegKey::ACCESS_QUERY_VALUE);
748   if (NS_WARN_IF(NS_FAILED(rv))) {
749     return rv;
750   }
751 
752   uint32_t accentColor;
753   if (NS_SUCCEEDED(mDwmKey->ReadIntValue(u"AccentColor"_ns, &accentColor))) {
754     // The order of the color components in the DWORD stored in the registry
755     // happens to be the same order as we store the components in nscolor
756     // so we can just assign directly here.
757     aColor = accentColor;
758     rv = NS_OK;
759   } else {
760     rv = NS_ERROR_NOT_AVAILABLE;
761   }
762 
763   mDwmKey->Close();
764 
765   return rv;
766 }
767 
768 /* static */
GetAccentColorText(nscolor & aColor)769 nsresult nsLookAndFeel::GetAccentColorText(nscolor& aColor) {
770   nscolor accentColor;
771   nsresult rv = GetAccentColor(accentColor);
772   if (NS_WARN_IF(NS_FAILED(rv))) {
773     return rv;
774   }
775 
776   // We want the color that we return for text that will be drawn over
777   // a background that has the accent color to have good contrast with
778   // the accent color.  Windows itself uses either white or black text
779   // depending on how light or dark the accent color is.  We do the same
780   // here based on the luminance of the accent color with a threshhold
781   // value.  This algorithm should match what Windows does.  It comes from:
782   //
783   // https://docs.microsoft.com/en-us/windows/uwp/style/color
784 
785   float luminance = (NS_GET_R(accentColor) * 2 + NS_GET_G(accentColor) * 5 +
786                      NS_GET_B(accentColor)) /
787                     8;
788 
789   aColor = (luminance <= 128) ? NS_RGB(255, 255, 255) : NS_RGB(0, 0, 0);
790 
791   return NS_OK;
792 }
793 
GetColorForSysColorIndex(int index)794 nscolor nsLookAndFeel::GetColorForSysColorIndex(int index) {
795   MOZ_ASSERT(index >= SYS_COLOR_MIN && index <= SYS_COLOR_MAX);
796   return mSysColorTable[index - SYS_COLOR_MIN];
797 }
798 
EnsureInit()799 void nsLookAndFeel::EnsureInit() {
800   if (mInitialized) {
801     return;
802   }
803   mInitialized = true;
804 
805   nsresult res;
806 
807   res = GetAccentColor(mColorAccent);
808   mHasColorAccent = NS_SUCCEEDED(res);
809 
810   res = GetAccentColorText(mColorAccentText);
811   mHasColorAccentText = NS_SUCCEEDED(res);
812 
813   if (nsUXThemeData::IsAppThemed()) {
814     res = ::GetColorFromTheme(eUXMenu, MENU_POPUPITEM, MPI_HOT, TMT_TEXTCOLOR,
815                               mColorMenuHoverText);
816     mHasColorMenuHoverText = NS_SUCCEEDED(res);
817 
818     res = ::GetColorFromTheme(eUXMediaToolbar, TP_BUTTON, TS_NORMAL,
819                               TMT_TEXTCOLOR, mColorMediaText);
820     mHasColorMediaText = NS_SUCCEEDED(res);
821 
822     res = ::GetColorFromTheme(eUXCommunicationsToolbar, TP_BUTTON, TS_NORMAL,
823                               TMT_TEXTCOLOR, mColorCommunicationsText);
824     mHasColorCommunicationsText = NS_SUCCEEDED(res);
825   }
826 
827   // Fill out the sys color table.
828   for (int i = SYS_COLOR_MIN; i <= SYS_COLOR_MAX; ++i) {
829     DWORD color = ::GetSysColor(i);
830     mSysColorTable[i - SYS_COLOR_MIN] = COLOREF_2_NSRGB(color);
831   }
832 
833   RecordTelemetry();
834 }
835