Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* vim: se cin sw=2 ts=2 et : */
2 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 *
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "mozilla/ArrayUtils.h"
9 #include "mozilla/WindowsVersion.h"
11 #include "nsUXThemeData.h"
12 #include "nsDebug.h"
13 #include "nsToolkit.h"
14 #include "nsUXThemeConstants.h"
16 using namespace mozilla;
17 using namespace mozilla::widget;
19 const wchar_t
20 nsUXThemeData::kThemeLibraryName[] = L"uxtheme.dll";
22 HANDLE
23 nsUXThemeData::sThemes[eUXNumClasses];
25 HMODULE
26 nsUXThemeData::sThemeDLL = nullptr;
28 bool
29 nsUXThemeData::sFlatMenus = false;
31 bool nsUXThemeData::sTitlebarInfoPopulatedAero = false;
32 bool nsUXThemeData::sTitlebarInfoPopulatedThemed = false;
33 SIZE nsUXThemeData::sCommandButtons[4];
35 void
36 nsUXThemeData::Teardown() {
37 Invalidate();
38 if(sThemeDLL)
39 FreeLibrary(sThemeDLL);
40 }
42 void
43 nsUXThemeData::Initialize()
44 {
45 ::ZeroMemory(sThemes, sizeof(sThemes));
46 NS_ASSERTION(!sThemeDLL, "nsUXThemeData being initialized twice!");
48 CheckForCompositor(true);
49 Invalidate();
50 }
52 void
53 nsUXThemeData::Invalidate() {
54 for(int i = 0; i < eUXNumClasses; i++) {
55 if(sThemes[i]) {
56 CloseThemeData(sThemes[i]);
57 sThemes[i] = nullptr;
58 }
59 }
60 BOOL useFlat = FALSE;
61 sFlatMenus = ::SystemParametersInfo(SPI_GETFLATMENU, 0, &useFlat, 0) ?
62 useFlat : false;
63 }
65 HANDLE
66 nsUXThemeData::GetTheme(nsUXThemeClass cls) {
67 NS_ASSERTION(cls < eUXNumClasses, "Invalid theme class!");
68 if(!sThemes[cls])
69 {
70 sThemes[cls] = OpenThemeData(nullptr, GetClassName(cls));
71 }
72 return sThemes[cls];
73 }
75 HMODULE
76 nsUXThemeData::GetThemeDLL() {
77 if (!sThemeDLL)
78 sThemeDLL = ::LoadLibraryW(kThemeLibraryName);
79 return sThemeDLL;
80 }
82 const wchar_t *nsUXThemeData::GetClassName(nsUXThemeClass cls) {
83 switch(cls) {
84 case eUXButton:
85 return L"Button";
86 case eUXEdit:
87 return L"Edit";
88 case eUXTooltip:
89 return L"Tooltip";
90 case eUXRebar:
91 return L"Rebar";
92 case eUXMediaRebar:
93 return L"Media::Rebar";
94 case eUXCommunicationsRebar:
95 return L"Communications::Rebar";
96 case eUXBrowserTabBarRebar:
97 return L"BrowserTabBar::Rebar";
98 case eUXToolbar:
99 return L"Toolbar";
100 case eUXMediaToolbar:
101 return L"Media::Toolbar";
102 case eUXCommunicationsToolbar:
103 return L"Communications::Toolbar";
104 case eUXProgress:
105 return L"Progress";
106 case eUXTab:
107 return L"Tab";
108 case eUXScrollbar:
109 return L"Scrollbar";
110 case eUXTrackbar:
111 return L"Trackbar";
112 case eUXSpin:
113 return L"Spin";
114 case eUXStatus:
115 return L"Status";
116 case eUXCombobox:
117 return L"Combobox";
118 case eUXHeader:
119 return L"Header";
120 case eUXListview:
121 return L"Listview";
122 case eUXMenu:
123 return L"Menu";
124 case eUXWindowFrame:
125 return L"Window";
126 default:
127 NS_NOTREACHED("unknown uxtheme class");
128 return L"";
129 }
130 }
132 // static
133 void
134 nsUXThemeData::InitTitlebarInfo()
135 {
136 // Pre-populate with generic metrics. These likley will not match
137 // the current theme, but they insure the buttons at least show up.
138 sCommandButtons[0].cx = GetSystemMetrics(SM_CXSIZE);
139 sCommandButtons[0].cy = GetSystemMetrics(SM_CYSIZE);
140 sCommandButtons[1].cx = sCommandButtons[2].cx = sCommandButtons[0].cx;
141 sCommandButtons[1].cy = sCommandButtons[2].cy = sCommandButtons[0].cy;
142 sCommandButtons[3].cx = sCommandButtons[0].cx * 3;
143 sCommandButtons[3].cy = sCommandButtons[0].cy;
145 // Use system metrics for pre-vista, otherwise trigger a
146 // refresh on the next layout.
147 sTitlebarInfoPopulatedAero = sTitlebarInfoPopulatedThemed =
148 !IsVistaOrLater();
149 }
151 // static
152 void
153 nsUXThemeData::UpdateTitlebarInfo(HWND aWnd)
154 {
155 if (!aWnd)
156 return;
158 if (!sTitlebarInfoPopulatedAero && nsUXThemeData::CheckForCompositor()) {
159 RECT captionButtons;
160 if (SUCCEEDED(WinUtils::dwmGetWindowAttributePtr(aWnd,
161 DWMWA_CAPTION_BUTTON_BOUNDS,
162 &captionButtons,
163 sizeof(captionButtons)))) {
164 sCommandButtons[CMDBUTTONIDX_BUTTONBOX].cx = captionButtons.right - captionButtons.left - 3;
165 sCommandButtons[CMDBUTTONIDX_BUTTONBOX].cy = (captionButtons.bottom - captionButtons.top) - 1;
166 sTitlebarInfoPopulatedAero = true;
167 }
168 }
170 if (sTitlebarInfoPopulatedThemed)
171 return;
173 // Query a temporary, visible window with command buttons to get
174 // the right metrics.
175 nsAutoString className;
176 className.AssignLiteral(kClassNameTemp);
177 WNDCLASSW wc;
178 wc.style = 0;
179 wc.lpfnWndProc = ::DefWindowProcW;
180 wc.cbClsExtra = 0;
181 wc.cbWndExtra = 0;
182 wc.hInstance = nsToolkit::mDllInstance;
183 wc.hIcon = nullptr;
184 wc.hCursor = nullptr;
185 wc.hbrBackground = nullptr;
186 wc.lpszMenuName = nullptr;
187 wc.lpszClassName = className.get();
188 ::RegisterClassW(&wc);
190 // Create a transparent descendant of the window passed in. This
191 // keeps the window from showing up on the desktop or the taskbar.
192 // Note the parent (browser) window is usually still hidden, we
193 // don't want to display it, so we can't query it directly.
194 HWND hWnd = CreateWindowExW(WS_EX_LAYERED,
195 className.get(), L"",
196 WS_OVERLAPPEDWINDOW,
197 0, 0, 0, 0, aWnd, nullptr,
198 nsToolkit::mDllInstance, nullptr);
199 NS_ASSERTION(hWnd, "UpdateTitlebarInfo window creation failed.");
201 ShowWindow(hWnd, SW_SHOW);
202 TITLEBARINFOEX info = {0};
203 info.cbSize = sizeof(TITLEBARINFOEX);
204 SendMessage(hWnd, WM_GETTITLEBARINFOEX, 0, (LPARAM)&info);
205 DestroyWindow(hWnd);
207 // Only set if we have valid data for all three buttons we use.
208 if ((info.rgrect[2].right - info.rgrect[2].left) == 0 ||
209 (info.rgrect[3].right - info.rgrect[3].left) == 0 ||
210 (info.rgrect[5].right - info.rgrect[5].left) == 0) {
211 NS_WARNING("WM_GETTITLEBARINFOEX query failed to find usable metrics.");
212 return;
213 }
214 // minimize
215 sCommandButtons[0].cx = info.rgrect[2].right - info.rgrect[2].left;
216 sCommandButtons[0].cy = info.rgrect[2].bottom - info.rgrect[2].top;
217 // maximize/restore
218 sCommandButtons[1].cx = info.rgrect[3].right - info.rgrect[3].left;
219 sCommandButtons[1].cy = info.rgrect[3].bottom - info.rgrect[3].top;
220 // close
221 sCommandButtons[2].cx = info.rgrect[5].right - info.rgrect[5].left;
222 sCommandButtons[2].cy = info.rgrect[5].bottom - info.rgrect[5].top;
224 sTitlebarInfoPopulatedThemed = true;
225 }
227 // visual style (aero glass, aero basic)
228 // theme (aero, luna, zune)
229 // theme color (silver, olive, blue)
230 // system colors
232 struct THEMELIST {
233 LPCWSTR name;
234 int type;
235 };
237 const THEMELIST knownThemes[] = {
238 { L"aero.msstyles", WINTHEME_AERO },
239 { L"aerolite.msstyles", WINTHEME_AERO_LITE },
240 { L"luna.msstyles", WINTHEME_LUNA },
241 { L"zune.msstyles", WINTHEME_ZUNE },
242 { L"royale.msstyles", WINTHEME_ROYALE }
243 };
245 const THEMELIST knownColors[] = {
246 { L"normalcolor", WINTHEMECOLOR_NORMAL },
247 { L"homestead", WINTHEMECOLOR_HOMESTEAD },
248 { L"metallic", WINTHEMECOLOR_METALLIC }
249 };
251 LookAndFeel::WindowsTheme
252 nsUXThemeData::sThemeId = LookAndFeel::eWindowsTheme_Generic;
254 bool
255 nsUXThemeData::sIsDefaultWindowsTheme = false;
256 bool
257 nsUXThemeData::sIsHighContrastOn = false;
259 // static
260 LookAndFeel::WindowsTheme
261 nsUXThemeData::GetNativeThemeId()
262 {
263 return sThemeId;
264 }
266 // static
267 bool nsUXThemeData::IsDefaultWindowTheme()
268 {
269 return sIsDefaultWindowsTheme;
270 }
272 bool nsUXThemeData::IsHighContrastOn()
273 {
274 return sIsHighContrastOn;
275 }
277 // static
278 bool nsUXThemeData::CheckForCompositor(bool aUpdateCache)
279 {
280 static BOOL sCachedValue = FALSE;
281 if (aUpdateCache && WinUtils::dwmIsCompositionEnabledPtr) {
282 WinUtils::dwmIsCompositionEnabledPtr(&sCachedValue);
283 }
284 return sCachedValue;
285 }
287 // static
288 void
289 nsUXThemeData::UpdateNativeThemeInfo()
290 {
291 // Trigger a refresh of themed button metrics if needed
292 sTitlebarInfoPopulatedThemed = !IsVistaOrLater();
294 sIsDefaultWindowsTheme = false;
295 sThemeId = LookAndFeel::eWindowsTheme_Generic;
297 HIGHCONTRAST highContrastInfo;
298 highContrastInfo.cbSize = sizeof(HIGHCONTRAST);
299 if (SystemParametersInfo(SPI_GETHIGHCONTRAST, 0, &highContrastInfo, 0)) {
300 sIsHighContrastOn = ((highContrastInfo.dwFlags & HCF_HIGHCONTRASTON) != 0);
301 } else {
302 sIsHighContrastOn = false;
303 }
305 if (!IsAppThemed()) {
306 sThemeId = LookAndFeel::eWindowsTheme_Classic;
307 return;
308 }
310 WCHAR themeFileName[MAX_PATH + 1];
311 WCHAR themeColor[MAX_PATH + 1];
312 if (FAILED(GetCurrentThemeName(themeFileName,
313 MAX_PATH,
314 themeColor,
315 MAX_PATH,
316 nullptr, 0))) {
317 sThemeId = LookAndFeel::eWindowsTheme_Classic;
318 return;
319 }
321 LPCWSTR themeName = wcsrchr(themeFileName, L'\\');
322 themeName = themeName ? themeName + 1 : themeFileName;
324 WindowsTheme theme = WINTHEME_UNRECOGNIZED;
325 for (size_t i = 0; i < ArrayLength(knownThemes); ++i) {
326 if (!lstrcmpiW(themeName, knownThemes[i].name)) {
327 theme = (WindowsTheme)knownThemes[i].type;
328 break;
329 }
330 }
332 if (theme == WINTHEME_UNRECOGNIZED)
333 return;
335 // We're using the default theme if we're using any of Aero, Aero Lite, or
336 // luna. However, on Win8, GetCurrentThemeName (see above) returns
337 // AeroLite.msstyles for the 4 builtin highcontrast themes as well. Those
338 // themes "don't count" as default themes, so we specifically check for high
339 // contrast mode in that situation.
340 if (!(IsWin8OrLater() && sIsHighContrastOn) &&
341 (theme == WINTHEME_AERO || theme == WINTHEME_AERO_LITE || theme == WINTHEME_LUNA)) {
342 sIsDefaultWindowsTheme = true;
343 }
345 if (theme != WINTHEME_LUNA) {
346 switch(theme) {
347 case WINTHEME_AERO:
348 sThemeId = LookAndFeel::eWindowsTheme_Aero;
349 return;
350 case WINTHEME_AERO_LITE:
351 sThemeId = LookAndFeel::eWindowsTheme_AeroLite;
352 return;
353 case WINTHEME_ZUNE:
354 sThemeId = LookAndFeel::eWindowsTheme_Zune;
355 return;
356 case WINTHEME_ROYALE:
357 sThemeId = LookAndFeel::eWindowsTheme_Royale;
358 return;
359 default:
360 NS_WARNING("unhandled theme type.");
361 return;
362 }
363 }
365 // calculate the luna color scheme
366 WindowsThemeColor color = WINTHEMECOLOR_UNRECOGNIZED;
367 for (size_t i = 0; i < ArrayLength(knownColors); ++i) {
368 if (!lstrcmpiW(themeColor, knownColors[i].name)) {
369 color = (WindowsThemeColor)knownColors[i].type;
370 break;
371 }
372 }
374 switch(color) {
375 case WINTHEMECOLOR_NORMAL:
376 sThemeId = LookAndFeel::eWindowsTheme_LunaBlue;
377 return;
378 case WINTHEMECOLOR_HOMESTEAD:
379 sThemeId = LookAndFeel::eWindowsTheme_LunaOlive;
380 return;
381 case WINTHEMECOLOR_METALLIC:
382 sThemeId = LookAndFeel::eWindowsTheme_LunaSilver;
383 return;
384 default:
385 NS_WARNING("unhandled theme color.");
386 return;
387 }
388 }