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 "WinUtils.h"
13 #include "mozilla/Telemetry.h"
14 #include "mozilla/WindowsVersion.h"
15 #include "gfxFontConstants.h"
16 
17 using namespace mozilla;
18 using namespace mozilla::widget;
19 
20 // static
GetOperatingSystemVersion()21 LookAndFeel::OperatingSystemVersion nsLookAndFeel::GetOperatingSystemVersion() {
22   static OperatingSystemVersion version = eOperatingSystemVersion_Unknown;
23 
24   if (version != eOperatingSystemVersion_Unknown) {
25     return version;
26   }
27 
28   if (IsWin10OrLater()) {
29     version = eOperatingSystemVersion_Windows10;
30   } else if (IsWin8OrLater()) {
31     version = eOperatingSystemVersion_Windows8;
32   } else {
33     version = eOperatingSystemVersion_Windows7;
34   }
35 
36   return version;
37 }
38 
GetColorFromTheme(nsUXThemeClass cls,int32_t aPart,int32_t aState,int32_t aPropId,nscolor & aColor)39 static nsresult GetColorFromTheme(nsUXThemeClass cls, int32_t aPart,
40                                   int32_t aState, int32_t aPropId,
41                                   nscolor &aColor) {
42   COLORREF color;
43   HRESULT hr = GetThemeColor(nsUXThemeData::GetTheme(cls), aPart, aState,
44                              aPropId, &color);
45   if (hr == S_OK) {
46     aColor = COLOREF_2_NSRGB(color);
47     return NS_OK;
48   }
49   return NS_ERROR_FAILURE;
50 }
51 
GetSystemParam(long flag,int32_t def)52 static int32_t GetSystemParam(long flag, int32_t def) {
53   DWORD value;
54   return ::SystemParametersInfo(flag, 0, &value, 0) ? value : def;
55 }
56 
nsLookAndFeel()57 nsLookAndFeel::nsLookAndFeel()
58     : nsXPLookAndFeel(),
59       mUseAccessibilityTheme(0),
60       mUseDefaultTheme(0),
61       mNativeThemeId(eWindowsTheme_Generic),
62       mCaretBlinkTime(-1),
63       mHasColorMenuHoverText(false),
64       mHasColorAccent(false),
65       mHasColorAccentText(false),
66       mHasColorMediaText(false),
67       mHasColorCommunicationsText(false),
68       mInitialized(false) {
69   mozilla::Telemetry::Accumulate(mozilla::Telemetry::TOUCH_ENABLED_DEVICE,
70                                  WinUtils::IsTouchDeviceSupportPresent());
71 }
72 
~nsLookAndFeel()73 nsLookAndFeel::~nsLookAndFeel() {}
74 
NativeInit()75 void nsLookAndFeel::NativeInit() { EnsureInit(); }
76 
RefreshImpl()77 /* virtual */ void nsLookAndFeel::RefreshImpl() {
78   nsXPLookAndFeel::RefreshImpl();
79 
80   for (auto e = mSystemFontCache.begin(), end = mSystemFontCache.end();
81        e != end; ++e) {
82     e->mCacheValid = false;
83   }
84   mCaretBlinkTime = -1;
85 
86   mInitialized = false;
87 }
88 
NativeGetColor(ColorID aID,nscolor & aColor)89 nsresult nsLookAndFeel::NativeGetColor(ColorID aID, nscolor &aColor) {
90   EnsureInit();
91 
92   nsresult res = NS_OK;
93 
94   int idx;
95   switch (aID) {
96     case eColorID_WindowBackground:
97       idx = COLOR_WINDOW;
98       break;
99     case eColorID_WindowForeground:
100       idx = COLOR_WINDOWTEXT;
101       break;
102     case eColorID_WidgetBackground:
103       idx = COLOR_BTNFACE;
104       break;
105     case eColorID_WidgetForeground:
106       idx = COLOR_BTNTEXT;
107       break;
108     case eColorID_WidgetSelectBackground:
109       idx = COLOR_HIGHLIGHT;
110       break;
111     case eColorID_WidgetSelectForeground:
112       idx = COLOR_HIGHLIGHTTEXT;
113       break;
114     case eColorID_Widget3DHighlight:
115       idx = COLOR_BTNHIGHLIGHT;
116       break;
117     case eColorID_Widget3DShadow:
118       idx = COLOR_BTNSHADOW;
119       break;
120     case eColorID_TextBackground:
121       idx = COLOR_WINDOW;
122       break;
123     case eColorID_TextForeground:
124       idx = COLOR_WINDOWTEXT;
125       break;
126     case eColorID_TextSelectBackground:
127     case eColorID_IMESelectedRawTextBackground:
128     case eColorID_IMESelectedConvertedTextBackground:
129       idx = COLOR_HIGHLIGHT;
130       break;
131     case eColorID_TextSelectForeground:
132     case eColorID_IMESelectedRawTextForeground:
133     case eColorID_IMESelectedConvertedTextForeground:
134       idx = COLOR_HIGHLIGHTTEXT;
135       break;
136     case eColorID_IMERawInputBackground:
137     case eColorID_IMEConvertedTextBackground:
138       aColor = NS_TRANSPARENT;
139       return NS_OK;
140     case eColorID_IMERawInputForeground:
141     case eColorID_IMEConvertedTextForeground:
142       aColor = NS_SAME_AS_FOREGROUND_COLOR;
143       return NS_OK;
144     case eColorID_IMERawInputUnderline:
145     case eColorID_IMEConvertedTextUnderline:
146       aColor = NS_SAME_AS_FOREGROUND_COLOR;
147       return NS_OK;
148     case eColorID_IMESelectedRawTextUnderline:
149     case eColorID_IMESelectedConvertedTextUnderline:
150       aColor = NS_TRANSPARENT;
151       return NS_OK;
152     case eColorID_SpellCheckerUnderline:
153       aColor = NS_RGB(0xff, 0, 0);
154       return NS_OK;
155 
156     // New CSS 2 Color definitions
157     case eColorID_activeborder:
158       idx = COLOR_ACTIVEBORDER;
159       break;
160     case eColorID_activecaption:
161       idx = COLOR_ACTIVECAPTION;
162       break;
163     case eColorID_appworkspace:
164       idx = COLOR_APPWORKSPACE;
165       break;
166     case eColorID_background:
167       idx = COLOR_BACKGROUND;
168       break;
169     case eColorID_buttonface:
170     case eColorID__moz_buttonhoverface:
171       idx = COLOR_BTNFACE;
172       break;
173     case eColorID_buttonhighlight:
174       idx = COLOR_BTNHIGHLIGHT;
175       break;
176     case eColorID_buttonshadow:
177       idx = COLOR_BTNSHADOW;
178       break;
179     case eColorID_buttontext:
180     case eColorID__moz_buttonhovertext:
181       idx = COLOR_BTNTEXT;
182       break;
183     case eColorID_captiontext:
184       idx = COLOR_CAPTIONTEXT;
185       break;
186     case eColorID_graytext:
187       idx = COLOR_GRAYTEXT;
188       break;
189     case eColorID_highlight:
190     case eColorID__moz_html_cellhighlight:
191     case eColorID__moz_menuhover:
192       idx = COLOR_HIGHLIGHT;
193       break;
194     case eColorID__moz_menubarhovertext:
195       if (!IsAppThemed()) {
196         idx = nsUXThemeData::sFlatMenus ? COLOR_HIGHLIGHTTEXT : COLOR_MENUTEXT;
197         break;
198       }
199       // Fall through
200     case eColorID__moz_menuhovertext:
201       if (mHasColorMenuHoverText) {
202         aColor = mColorMenuHoverText;
203         return NS_OK;
204       }
205       // Fall through
206     case eColorID_highlighttext:
207     case eColorID__moz_html_cellhighlighttext:
208       idx = COLOR_HIGHLIGHTTEXT;
209       break;
210     case eColorID_inactiveborder:
211       idx = COLOR_INACTIVEBORDER;
212       break;
213     case eColorID_inactivecaption:
214       idx = COLOR_INACTIVECAPTION;
215       break;
216     case eColorID_inactivecaptiontext:
217       idx = COLOR_INACTIVECAPTIONTEXT;
218       break;
219     case eColorID_infobackground:
220       idx = COLOR_INFOBK;
221       break;
222     case eColorID_infotext:
223       idx = COLOR_INFOTEXT;
224       break;
225     case eColorID_menu:
226       idx = COLOR_MENU;
227       break;
228     case eColorID_menutext:
229     case eColorID__moz_menubartext:
230       idx = COLOR_MENUTEXT;
231       break;
232     case eColorID_scrollbar:
233       idx = COLOR_SCROLLBAR;
234       break;
235     case eColorID_threeddarkshadow:
236       idx = COLOR_3DDKSHADOW;
237       break;
238     case eColorID_threedface:
239       idx = COLOR_3DFACE;
240       break;
241     case eColorID_threedhighlight:
242       idx = COLOR_3DHIGHLIGHT;
243       break;
244     case eColorID_threedlightshadow:
245       idx = COLOR_3DLIGHT;
246       break;
247     case eColorID_threedshadow:
248       idx = COLOR_3DSHADOW;
249       break;
250     case eColorID_window:
251       idx = COLOR_WINDOW;
252       break;
253     case eColorID_windowframe:
254       idx = COLOR_WINDOWFRAME;
255       break;
256     case eColorID_windowtext:
257       idx = COLOR_WINDOWTEXT;
258       break;
259     case eColorID__moz_eventreerow:
260     case eColorID__moz_oddtreerow:
261     case eColorID__moz_field:
262     case eColorID__moz_combobox:
263       idx = COLOR_WINDOW;
264       break;
265     case eColorID__moz_fieldtext:
266     case eColorID__moz_comboboxtext:
267       idx = COLOR_WINDOWTEXT;
268       break;
269     case eColorID__moz_dialog:
270     case eColorID__moz_cellhighlight:
271       idx = COLOR_3DFACE;
272       break;
273     case eColorID__moz_win_accentcolor:
274       if (mHasColorAccent) {
275         aColor = mColorAccent;
276       } else {
277         // Seems to be the default color (hardcoded because of bug 1065998)
278         aColor = NS_RGB(158, 158, 158);
279       }
280       return NS_OK;
281     case eColorID__moz_win_accentcolortext:
282       if (mHasColorAccentText) {
283         aColor = mColorAccentText;
284       } else {
285         aColor = NS_RGB(0, 0, 0);
286       }
287       return NS_OK;
288     case eColorID__moz_win_mediatext:
289       if (mHasColorMediaText) {
290         aColor = mColorMediaText;
291         return NS_OK;
292       }
293       // if we've gotten here just return -moz-dialogtext instead
294       idx = COLOR_WINDOWTEXT;
295       break;
296     case eColorID__moz_win_communicationstext:
297       if (mHasColorCommunicationsText) {
298         aColor = mColorCommunicationsText;
299         return NS_OK;
300       }
301       // if we've gotten here just return -moz-dialogtext instead
302       idx = COLOR_WINDOWTEXT;
303       break;
304     case eColorID__moz_dialogtext:
305     case eColorID__moz_cellhighlighttext:
306       idx = COLOR_WINDOWTEXT;
307       break;
308     case eColorID__moz_dragtargetzone:
309       idx = COLOR_HIGHLIGHTTEXT;
310       break;
311     case eColorID__moz_buttondefault:
312       idx = COLOR_3DDKSHADOW;
313       break;
314     case eColorID__moz_nativehyperlinktext:
315       idx = COLOR_HOTLIGHT;
316       break;
317     default:
318       NS_WARNING("Unknown color for nsLookAndFeel");
319       idx = COLOR_WINDOW;
320       res = NS_ERROR_FAILURE;
321       break;
322   }
323 
324   aColor = GetColorForSysColorIndex(idx);
325 
326   return res;
327 }
328 
GetIntImpl(IntID aID,int32_t & aResult)329 nsresult nsLookAndFeel::GetIntImpl(IntID aID, int32_t &aResult) {
330   nsresult res = nsXPLookAndFeel::GetIntImpl(aID, aResult);
331   if (NS_SUCCEEDED(res)) return res;
332   res = NS_OK;
333 
334   switch (aID) {
335     case eIntID_CaretBlinkTime:
336       // eIntID_CaretBlinkTime is often called by updating editable text
337       // that has focus. So it should be cached to improve performance.
338       if (mCaretBlinkTime < 0) {
339         mCaretBlinkTime = static_cast<int32_t>(::GetCaretBlinkTime());
340       }
341       aResult = mCaretBlinkTime;
342       break;
343     case eIntID_CaretWidth:
344       aResult = 1;
345       break;
346     case eIntID_ShowCaretDuringSelection:
347       aResult = 0;
348       break;
349     case eIntID_SelectTextfieldsOnKeyFocus:
350       // Select textfield content when focused by kbd
351       // used by EventStateManager::sTextfieldSelectModel
352       aResult = 1;
353       break;
354     case eIntID_SubmenuDelay:
355       // This will default to the Windows' default
356       // (400ms) on error.
357       aResult = GetSystemParam(SPI_GETMENUSHOWDELAY, 400);
358       break;
359     case eIntID_TooltipDelay:
360       aResult = 500;
361       break;
362     case eIntID_MenusCanOverlapOSBar:
363       // we want XUL popups to be able to overlap the task bar.
364       aResult = 1;
365       break;
366     case eIntID_DragThresholdX:
367       // The system metric is the number of pixels at which a drag should
368       // start.  Our look and feel metric is the number of pixels you can
369       // move before starting a drag, so subtract 1.
370 
371       aResult = ::GetSystemMetrics(SM_CXDRAG) - 1;
372       break;
373     case eIntID_DragThresholdY:
374       aResult = ::GetSystemMetrics(SM_CYDRAG) - 1;
375       break;
376     case eIntID_UseAccessibilityTheme:
377       // High contrast is a misnomer under Win32 -- any theme can be used with
378       // it, e.g. normal contrast with large fonts, low contrast, etc. The high
379       // contrast flag really means -- use this theme and don't override it.
380       if (XRE_IsContentProcess()) {
381         // If we're running in the content process, then the parent should
382         // have sent us the accessibility state when nsLookAndFeel
383         // initialized, and stashed it in the mUseAccessibilityTheme cache.
384         aResult = mUseAccessibilityTheme;
385       } else {
386         // Otherwise, we can ask the OS to see if we're using High Contrast
387         // mode.
388         aResult = nsUXThemeData::IsHighContrastOn();
389       }
390       break;
391     case eIntID_ScrollArrowStyle:
392       aResult = eScrollArrowStyle_Single;
393       break;
394     case eIntID_ScrollSliderStyle:
395       aResult = eScrollThumbStyle_Proportional;
396       break;
397     case eIntID_TreeOpenDelay:
398       aResult = 1000;
399       break;
400     case eIntID_TreeCloseDelay:
401       aResult = 0;
402       break;
403     case eIntID_TreeLazyScrollDelay:
404       aResult = 150;
405       break;
406     case eIntID_TreeScrollDelay:
407       aResult = 100;
408       break;
409     case eIntID_TreeScrollLinesMax:
410       aResult = 3;
411       break;
412     case eIntID_WindowsClassic:
413       aResult = !IsAppThemed();
414       break;
415     case eIntID_TouchEnabled:
416       aResult = WinUtils::IsTouchDeviceSupportPresent();
417       break;
418     case eIntID_WindowsDefaultTheme:
419       if (XRE_IsContentProcess()) {
420         aResult = mUseDefaultTheme;
421       } else {
422         aResult = nsUXThemeData::IsDefaultWindowTheme();
423       }
424       break;
425     case eIntID_WindowsThemeIdentifier:
426       if (XRE_IsContentProcess()) {
427         aResult = mNativeThemeId;
428       } else {
429         aResult = nsUXThemeData::GetNativeThemeId();
430       }
431       break;
432 
433     case eIntID_OperatingSystemVersionIdentifier: {
434       aResult = GetOperatingSystemVersion();
435       break;
436     }
437 
438     case eIntID_MacGraphiteTheme:
439       aResult = 0;
440       res = NS_ERROR_NOT_IMPLEMENTED;
441       break;
442     case eIntID_DWMCompositor:
443       aResult = nsUXThemeData::CheckForCompositor();
444       break;
445     case eIntID_WindowsAccentColorInTitlebar: {
446       nscolor unused;
447       if (NS_WARN_IF(NS_FAILED(GetAccentColor(unused)))) {
448         aResult = 0;
449         break;
450       }
451 
452       uint32_t colorPrevalence;
453       nsresult rv =
454           mDwmKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
455                         NS_LITERAL_STRING("SOFTWARE\\Microsoft\\Windows\\DWM"),
456                         nsIWindowsRegKey::ACCESS_QUERY_VALUE);
457       if (NS_WARN_IF(NS_FAILED(rv))) {
458         return rv;
459       }
460 
461       // The ColorPrevalence value is set to 1 when the "Show color on title
462       // bar" setting in the Color section of Window's Personalization settings
463       // is turned on.
464       aResult = (NS_SUCCEEDED(mDwmKey->ReadIntValue(
465                      NS_LITERAL_STRING("ColorPrevalence"), &colorPrevalence)) &&
466                  colorPrevalence == 1)
467                     ? 1
468                     : 0;
469 
470       mDwmKey->Close();
471     } break;
472     case eIntID_WindowsGlass:
473       // Aero Glass is only available prior to Windows 8 when DWM is used.
474       aResult = (nsUXThemeData::CheckForCompositor() && !IsWin8OrLater());
475       break;
476     case eIntID_AlertNotificationOrigin:
477       aResult = 0;
478       {
479         // Get task bar window handle
480         HWND shellWindow = FindWindowW(L"Shell_TrayWnd", nullptr);
481 
482         if (shellWindow != nullptr) {
483           // Determine position
484           APPBARDATA appBarData;
485           appBarData.hWnd = shellWindow;
486           appBarData.cbSize = sizeof(appBarData);
487           if (SHAppBarMessage(ABM_GETTASKBARPOS, &appBarData)) {
488             // Set alert origin as a bit field - see LookAndFeel.h
489             // 0 represents bottom right, sliding vertically.
490             switch (appBarData.uEdge) {
491               case ABE_LEFT:
492                 aResult = NS_ALERT_HORIZONTAL | NS_ALERT_LEFT;
493                 break;
494               case ABE_RIGHT:
495                 aResult = NS_ALERT_HORIZONTAL;
496                 break;
497               case ABE_TOP:
498                 aResult = NS_ALERT_TOP;
499                 // fall through for the right-to-left handling.
500               case ABE_BOTTOM:
501                 // If the task bar is right-to-left,
502                 // move the origin to the left
503                 if (::GetWindowLong(shellWindow, GWL_EXSTYLE) & WS_EX_LAYOUTRTL)
504                   aResult |= NS_ALERT_LEFT;
505                 break;
506             }
507           }
508         }
509       }
510       break;
511     case eIntID_IMERawInputUnderlineStyle:
512     case eIntID_IMEConvertedTextUnderlineStyle:
513       aResult = NS_STYLE_TEXT_DECORATION_STYLE_DASHED;
514       break;
515     case eIntID_IMESelectedRawTextUnderlineStyle:
516     case eIntID_IMESelectedConvertedTextUnderline:
517       aResult = NS_STYLE_TEXT_DECORATION_STYLE_NONE;
518       break;
519     case eIntID_SpellCheckerUnderlineStyle:
520       aResult = NS_STYLE_TEXT_DECORATION_STYLE_WAVY;
521       break;
522     case eIntID_ScrollbarButtonAutoRepeatBehavior:
523       aResult = 0;
524       break;
525     case eIntID_SwipeAnimationEnabled:
526       aResult = 0;
527       break;
528     case eIntID_UseOverlayScrollbars:
529       aResult = false;
530       break;
531     case eIntID_AllowOverlayScrollbarsOverlap:
532       aResult = 0;
533       break;
534     case eIntID_ScrollbarDisplayOnMouseMove:
535       aResult = 1;
536       break;
537     case eIntID_ScrollbarFadeBeginDelay:
538       aResult = 2500;
539       break;
540     case eIntID_ScrollbarFadeDuration:
541       aResult = 350;
542       break;
543     case eIntID_ContextMenuOffsetVertical:
544     case eIntID_ContextMenuOffsetHorizontal:
545       aResult = 2;
546       break;
547     default:
548       aResult = 0;
549       res = NS_ERROR_FAILURE;
550   }
551   return res;
552 }
553 
GetFloatImpl(FloatID aID,float & aResult)554 nsresult nsLookAndFeel::GetFloatImpl(FloatID aID, float &aResult) {
555   nsresult res = nsXPLookAndFeel::GetFloatImpl(aID, aResult);
556   if (NS_SUCCEEDED(res)) return res;
557   res = NS_OK;
558 
559   switch (aID) {
560     case eFloatID_IMEUnderlineRelativeSize:
561       aResult = 1.0f;
562       break;
563     case eFloatID_SpellCheckerUnderlineRelativeSize:
564       aResult = 1.0f;
565       break;
566     default:
567       aResult = -1.0;
568       res = NS_ERROR_FAILURE;
569   }
570   return res;
571 }
572 
GetSysFontInfo(HDC aHDC,LookAndFeel::FontID anID,nsString & aFontName,gfxFontStyle & aFontStyle)573 static bool GetSysFontInfo(HDC aHDC, LookAndFeel::FontID anID,
574                            nsString &aFontName, gfxFontStyle &aFontStyle) {
575   const LOGFONTW *ptrLogFont = nullptr;
576   LOGFONTW logFont;
577   NONCLIENTMETRICSW ncm;
578   char16_t name[LF_FACESIZE];
579   bool useShellDlg = false;
580 
581   // Depending on which stock font we want, there are a couple of
582   // places we might have to look it up.
583   switch (anID) {
584     case LookAndFeel::eFont_Icon:
585       if (!::SystemParametersInfoW(SPI_GETICONTITLELOGFONT, sizeof(logFont),
586                                    (PVOID)&logFont, 0))
587         return false;
588 
589       ptrLogFont = &logFont;
590       break;
591 
592     default:
593       ncm.cbSize = sizeof(NONCLIENTMETRICSW);
594       if (!::SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, sizeof(ncm),
595                                    (PVOID)&ncm, 0))
596         return false;
597 
598       switch (anID) {
599         case LookAndFeel::eFont_Menu:
600         case LookAndFeel::eFont_PullDownMenu:
601           ptrLogFont = &ncm.lfMenuFont;
602           break;
603         case LookAndFeel::eFont_Caption:
604           ptrLogFont = &ncm.lfCaptionFont;
605           break;
606         case LookAndFeel::eFont_SmallCaption:
607           ptrLogFont = &ncm.lfSmCaptionFont;
608           break;
609         case LookAndFeel::eFont_StatusBar:
610         case LookAndFeel::eFont_Tooltips:
611           ptrLogFont = &ncm.lfStatusFont;
612           break;
613         case LookAndFeel::eFont_Widget:
614         case LookAndFeel::eFont_Dialog:
615         case LookAndFeel::eFont_Button:
616         case LookAndFeel::eFont_Field:
617         case LookAndFeel::eFont_List:
618           // XXX It's not clear to me whether this is exactly the right
619           // set of LookAndFeel values to map to the dialog font; we may
620           // want to add or remove cases here after reviewing the visual
621           // results under various Windows versions.
622           useShellDlg = true;
623           // Fall through so that we can get size from lfMessageFont;
624           // but later we'll use the (virtual) "MS Shell Dlg 2" font name
625           // instead of the LOGFONT's.
626         default:
627           ptrLogFont = &ncm.lfMessageFont;
628           break;
629       }
630       break;
631   }
632 
633   // Get scaling factor from physical to logical pixels
634   double pixelScale = 1.0 / WinUtils::SystemScaleFactor();
635 
636   // The lfHeight is in pixels, and it needs to be adjusted for the
637   // device it will be displayed on.
638   // Screens and Printers will differ in DPI
639   //
640   // So this accounts for the difference in the DeviceContexts
641   // The pixelScale will typically be 1.0 for the screen
642   // (though larger for hi-dpi screens where the Windows resolution
643   // scale factor is 125% or 150% or even more), and could be
644   // any value when going to a printer, for example pixelScale is
645   // 6.25 when going to a 600dpi printer.
646   float pixelHeight = -ptrLogFont->lfHeight;
647   if (pixelHeight < 0) {
648     HFONT hFont = ::CreateFontIndirectW(ptrLogFont);
649     if (!hFont) return false;
650     HGDIOBJ hObject = ::SelectObject(aHDC, hFont);
651     TEXTMETRIC tm;
652     ::GetTextMetrics(aHDC, &tm);
653     ::SelectObject(aHDC, hObject);
654     ::DeleteObject(hFont);
655     pixelHeight = tm.tmAscent;
656   }
657   pixelHeight *= pixelScale;
658 
659   // we have problem on Simplified Chinese system because the system
660   // report the default font size is 8 points. but if we use 8, the text
661   // display very ugly. force it to be at 9 points (12 pixels) on that
662   // system (cp936), but leave other sizes alone.
663   if (pixelHeight < 12 && ::GetACP() == 936) pixelHeight = 12;
664 
665   aFontStyle.size = pixelHeight;
666 
667   // FIXME: What about oblique?
668   aFontStyle.style =
669       (ptrLogFont->lfItalic) ? NS_FONT_STYLE_ITALIC : NS_FONT_STYLE_NORMAL;
670 
671   // FIXME: Other weights?
672   aFontStyle.weight = (ptrLogFont->lfWeight == FW_BOLD ? NS_FONT_WEIGHT_BOLD
673                                                        : NS_FONT_WEIGHT_NORMAL);
674 
675   // FIXME: Set aFontStyle->stretch correctly!
676   aFontStyle.stretch = NS_FONT_STRETCH_NORMAL;
677 
678   aFontStyle.systemFont = true;
679 
680   if (useShellDlg) {
681     aFontName = NS_LITERAL_STRING("MS Shell Dlg 2");
682   } else {
683     memcpy(name, ptrLogFont->lfFaceName, LF_FACESIZE * sizeof(char16_t));
684     aFontName = name;
685   }
686 
687   return true;
688 }
689 
GetFontImpl(FontID anID,nsString & aFontName,gfxFontStyle & aFontStyle,float aDevPixPerCSSPixel)690 bool nsLookAndFeel::GetFontImpl(FontID anID, nsString &aFontName,
691                                 gfxFontStyle &aFontStyle,
692                                 float aDevPixPerCSSPixel) {
693   CachedSystemFont &cacheSlot = mSystemFontCache[anID];
694 
695   bool status;
696   if (cacheSlot.mCacheValid) {
697     status = cacheSlot.mHaveFont;
698     if (status) {
699       aFontName = cacheSlot.mFontName;
700       aFontStyle = cacheSlot.mFontStyle;
701     }
702   } else {
703     HDC tdc = GetDC(nullptr);
704     status = GetSysFontInfo(tdc, anID, aFontName, aFontStyle);
705     ReleaseDC(nullptr, tdc);
706 
707     cacheSlot.mCacheValid = true;
708     cacheSlot.mHaveFont = status;
709     if (status) {
710       cacheSlot.mFontName = aFontName;
711       cacheSlot.mFontStyle = aFontStyle;
712     }
713   }
714   // now convert the logical font size from GetSysFontInfo into device pixels
715   // for layout
716   aFontStyle.size *= aDevPixPerCSSPixel;
717   return status;
718 }
719 
720 /* virtual */
GetPasswordCharacterImpl()721 char16_t nsLookAndFeel::GetPasswordCharacterImpl() {
722 #define UNICODE_BLACK_CIRCLE_CHAR 0x25cf
723   return UNICODE_BLACK_CIRCLE_CHAR;
724 }
725 
GetIntCacheImpl()726 nsTArray<LookAndFeelInt> nsLookAndFeel::GetIntCacheImpl() {
727   nsTArray<LookAndFeelInt> lookAndFeelIntCache =
728       nsXPLookAndFeel::GetIntCacheImpl();
729 
730   LookAndFeelInt lafInt;
731   lafInt.id = eIntID_UseAccessibilityTheme;
732   lafInt.value = GetInt(eIntID_UseAccessibilityTheme);
733   lookAndFeelIntCache.AppendElement(lafInt);
734 
735   lafInt.id = eIntID_WindowsDefaultTheme;
736   lafInt.value = GetInt(eIntID_WindowsDefaultTheme);
737   lookAndFeelIntCache.AppendElement(lafInt);
738 
739   lafInt.id = eIntID_WindowsThemeIdentifier;
740   lafInt.value = GetInt(eIntID_WindowsThemeIdentifier);
741   lookAndFeelIntCache.AppendElement(lafInt);
742 
743   return lookAndFeelIntCache;
744 }
745 
SetIntCacheImpl(const nsTArray<LookAndFeelInt> & aLookAndFeelIntCache)746 void nsLookAndFeel::SetIntCacheImpl(
747     const nsTArray<LookAndFeelInt> &aLookAndFeelIntCache) {
748   for (auto entry : aLookAndFeelIntCache) {
749     switch (entry.id) {
750       case eIntID_UseAccessibilityTheme:
751         mUseAccessibilityTheme = entry.value;
752         break;
753       case eIntID_WindowsDefaultTheme:
754         mUseDefaultTheme = entry.value;
755         break;
756       case eIntID_WindowsThemeIdentifier:
757         mNativeThemeId = entry.value;
758         break;
759     }
760   }
761 }
762 
GetAccentColor(nscolor & aColor)763 /* static */ nsresult nsLookAndFeel::GetAccentColor(nscolor &aColor) {
764   nsresult rv;
765 
766   if (!mDwmKey) {
767     mDwmKey = do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv);
768     if (NS_WARN_IF(NS_FAILED(rv))) {
769       return rv;
770     }
771   }
772 
773   rv = mDwmKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
774                      NS_LITERAL_STRING("SOFTWARE\\Microsoft\\Windows\\DWM"),
775                      nsIWindowsRegKey::ACCESS_QUERY_VALUE);
776   if (NS_WARN_IF(NS_FAILED(rv))) {
777     return rv;
778   }
779 
780   uint32_t accentColor;
781   if (NS_SUCCEEDED(mDwmKey->ReadIntValue(NS_LITERAL_STRING("AccentColor"),
782                                          &accentColor))) {
783     // The order of the color components in the DWORD stored in the registry
784     // happens to be the same order as we store the components in nscolor
785     // so we can just assign directly here.
786     aColor = accentColor;
787     rv = NS_OK;
788   } else {
789     rv = NS_ERROR_NOT_AVAILABLE;
790   }
791 
792   mDwmKey->Close();
793 
794   return rv;
795 }
796 
GetAccentColorText(nscolor & aColor)797 /* static */ nsresult nsLookAndFeel::GetAccentColorText(nscolor &aColor) {
798   nscolor accentColor;
799   nsresult rv = GetAccentColor(accentColor);
800   if (NS_WARN_IF(NS_FAILED(rv))) {
801     return rv;
802   }
803 
804   // We want the color that we return for text that will be drawn over
805   // a background that has the accent color to have good contrast with
806   // the accent color.  Windows itself uses either white or black text
807   // depending on how light or dark the accent color is.  We do the same
808   // here based on the luminance of the accent color with a threshhold
809   // value.  This algorithm should match what Windows does.  It comes from:
810   //
811   // https://docs.microsoft.com/en-us/windows/uwp/style/color
812 
813   float luminance = (NS_GET_R(accentColor) * 2 + NS_GET_G(accentColor) * 5 +
814                      NS_GET_B(accentColor)) /
815                     8;
816 
817   aColor = (luminance <= 128) ? NS_RGB(255, 255, 255) : NS_RGB(0, 0, 0);
818 
819   return NS_OK;
820 }
821 
GetColorForSysColorIndex(int index)822 nscolor nsLookAndFeel::GetColorForSysColorIndex(int index) {
823   MOZ_ASSERT(index >= SYS_COLOR_MIN && index <= SYS_COLOR_MAX);
824   return mSysColorTable[index - SYS_COLOR_MIN];
825 }
826 
EnsureInit()827 void nsLookAndFeel::EnsureInit() {
828   if (mInitialized) {
829     return;
830   }
831   mInitialized = true;
832 
833   nsresult res;
834 
835   res = GetAccentColor(mColorAccent);
836   mHasColorAccent = NS_SUCCEEDED(res);
837 
838   res = GetAccentColorText(mColorAccentText);
839   mHasColorAccentText = NS_SUCCEEDED(res);
840 
841   if (IsAppThemed()) {
842     res = ::GetColorFromTheme(eUXMenu, MENU_POPUPITEM, MPI_HOT, TMT_TEXTCOLOR,
843                               mColorMenuHoverText);
844     mHasColorMenuHoverText = NS_SUCCEEDED(res);
845 
846     res = ::GetColorFromTheme(eUXMediaToolbar, TP_BUTTON, TS_NORMAL,
847                               TMT_TEXTCOLOR, mColorMediaText);
848     mHasColorMediaText = NS_SUCCEEDED(res);
849 
850     res = ::GetColorFromTheme(eUXCommunicationsToolbar, TP_BUTTON, TS_NORMAL,
851                               TMT_TEXTCOLOR, mColorCommunicationsText);
852     mHasColorCommunicationsText = NS_SUCCEEDED(res);
853   }
854 
855   // Fill out the sys color table.
856   for (int i = SYS_COLOR_MIN; i <= SYS_COLOR_MAX; ++i) {
857     DWORD color = ::GetSysColor(i);
858     mSysColorTable[i - SYS_COLOR_MIN] = COLOREF_2_NSRGB(color);
859   }
860 }
861