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