1 /* -*- Mode: C++; tab-width: 40; 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 "nsNativeThemeWin.h"
7
8 #include "mozilla/EventStates.h"
9 #include "mozilla/Logging.h"
10 #include "mozilla/WindowsVersion.h"
11 #include "nsDeviceContext.h"
12 #include "nsRect.h"
13 #include "nsSize.h"
14 #include "nsTransform2D.h"
15 #include "nsThemeConstants.h"
16 #include "nsIPresShell.h"
17 #include "nsPresContext.h"
18 #include "nsIContent.h"
19 #include "nsIContentInlines.h"
20 #include "nsIFrame.h"
21 #include "nsNameSpaceManager.h"
22 #include "nsLookAndFeel.h"
23 #include "nsMenuFrame.h"
24 #include "nsGkAtoms.h"
25 #include <malloc.h>
26 #include "nsWindow.h"
27 #include "nsIComboboxControlFrame.h"
28 #include "prinrval.h"
29 #include "WinUtils.h"
30
31 #include "gfxPlatform.h"
32 #include "gfxContext.h"
33 #include "gfxWindowsPlatform.h"
34 #include "gfxWindowsSurface.h"
35 #include "gfxWindowsNativeDrawing.h"
36
37 #include "nsUXThemeData.h"
38 #include "nsUXThemeConstants.h"
39 #include <algorithm>
40
41 using namespace mozilla;
42 using namespace mozilla::widget;
43
44 extern mozilla::LazyLogModule gWindowsLog;
45
NS_IMPL_ISUPPORTS_INHERITED(nsNativeThemeWin,nsNativeTheme,nsITheme)46 NS_IMPL_ISUPPORTS_INHERITED(nsNativeThemeWin, nsNativeTheme, nsITheme)
47
48 nsNativeThemeWin::nsNativeThemeWin()
49 : mProgressDeterminateTimeStamp(TimeStamp::Now()),
50 mProgressIndeterminateTimeStamp(TimeStamp::Now()),
51 mBorderCacheValid(),
52 mMinimumWidgetSizeCacheValid(),
53 mGutterSizeCacheValid(false) {
54 // If there is a relevant change in forms.css for windows platform,
55 // static widget style variables (e.g. sButtonBorderSize) should be
56 // reinitialized here.
57 }
58
~nsNativeThemeWin()59 nsNativeThemeWin::~nsNativeThemeWin() { nsUXThemeData::Invalidate(); }
60
GetTopLevelWindowActiveState(nsIFrame * aFrame)61 static int32_t GetTopLevelWindowActiveState(nsIFrame* aFrame) {
62 // Used by window frame and button box rendering. We can end up in here in
63 // the content process when rendering one of these moz styles freely in a
64 // page. Bail in this case, there is no applicable window focus state.
65 if (!XRE_IsParentProcess()) {
66 return mozilla::widget::themeconst::FS_INACTIVE;
67 }
68 // All headless windows are considered active so they are painted.
69 if (gfxPlatform::IsHeadless()) {
70 return mozilla::widget::themeconst::FS_ACTIVE;
71 }
72 // Get the widget. nsIFrame's GetNearestWidget walks up the view chain
73 // until it finds a real window.
74 nsIWidget* widget = aFrame->GetNearestWidget();
75 nsWindowBase* window = static_cast<nsWindowBase*>(widget);
76 if (!window) return mozilla::widget::themeconst::FS_INACTIVE;
77 if (widget && !window->IsTopLevelWidget() &&
78 !(window = window->GetParentWindowBase(false)))
79 return mozilla::widget::themeconst::FS_INACTIVE;
80
81 if (window->GetWindowHandle() == ::GetActiveWindow())
82 return mozilla::widget::themeconst::FS_ACTIVE;
83 return mozilla::widget::themeconst::FS_INACTIVE;
84 }
85
GetWindowFrameButtonState(nsIFrame * aFrame,EventStates eventState)86 static int32_t GetWindowFrameButtonState(nsIFrame* aFrame,
87 EventStates eventState) {
88 if (GetTopLevelWindowActiveState(aFrame) ==
89 mozilla::widget::themeconst::FS_INACTIVE) {
90 if (eventState.HasState(NS_EVENT_STATE_HOVER))
91 return mozilla::widget::themeconst::BS_HOT;
92 return mozilla::widget::themeconst::BS_INACTIVE;
93 }
94
95 if (eventState.HasState(NS_EVENT_STATE_HOVER)) {
96 if (eventState.HasState(NS_EVENT_STATE_ACTIVE))
97 return mozilla::widget::themeconst::BS_PUSHED;
98 return mozilla::widget::themeconst::BS_HOT;
99 }
100 return mozilla::widget::themeconst::BS_NORMAL;
101 }
102
GetClassicWindowFrameButtonState(EventStates eventState)103 static int32_t GetClassicWindowFrameButtonState(EventStates eventState) {
104 if (eventState.HasState(NS_EVENT_STATE_ACTIVE) &&
105 eventState.HasState(NS_EVENT_STATE_HOVER))
106 return DFCS_BUTTONPUSH | DFCS_PUSHED;
107 return DFCS_BUTTONPUSH;
108 }
109
IsTopLevelMenu(nsIFrame * aFrame)110 static bool IsTopLevelMenu(nsIFrame* aFrame) {
111 bool isTopLevel(false);
112 nsMenuFrame* menuFrame = do_QueryFrame(aFrame);
113 if (menuFrame) {
114 isTopLevel = menuFrame->IsOnMenuBar();
115 }
116 return isTopLevel;
117 }
118
GetCheckboxMargins(HANDLE theme,HDC hdc)119 static MARGINS GetCheckboxMargins(HANDLE theme, HDC hdc) {
120 MARGINS checkboxContent = {0};
121 GetThemeMargins(theme, hdc, MENU_POPUPCHECK, MCB_NORMAL, TMT_CONTENTMARGINS,
122 nullptr, &checkboxContent);
123 return checkboxContent;
124 }
125
GetCheckboxBGSize(HANDLE theme,HDC hdc)126 static SIZE GetCheckboxBGSize(HANDLE theme, HDC hdc) {
127 SIZE checkboxSize;
128 GetThemePartSize(theme, hdc, MENU_POPUPCHECK, MC_CHECKMARKNORMAL, nullptr,
129 TS_TRUE, &checkboxSize);
130
131 MARGINS checkboxMargins = GetCheckboxMargins(theme, hdc);
132
133 int leftMargin = checkboxMargins.cxLeftWidth;
134 int rightMargin = checkboxMargins.cxRightWidth;
135 int topMargin = checkboxMargins.cyTopHeight;
136 int bottomMargin = checkboxMargins.cyBottomHeight;
137
138 int width = leftMargin + checkboxSize.cx + rightMargin;
139 int height = topMargin + checkboxSize.cy + bottomMargin;
140 SIZE ret;
141 ret.cx = width;
142 ret.cy = height;
143 return ret;
144 }
145
GetCheckboxBGBounds(HANDLE theme,HDC hdc)146 static SIZE GetCheckboxBGBounds(HANDLE theme, HDC hdc) {
147 MARGINS checkboxBGSizing = {0};
148 MARGINS checkboxBGContent = {0};
149 GetThemeMargins(theme, hdc, MENU_POPUPCHECKBACKGROUND, MCB_NORMAL,
150 TMT_SIZINGMARGINS, nullptr, &checkboxBGSizing);
151 GetThemeMargins(theme, hdc, MENU_POPUPCHECKBACKGROUND, MCB_NORMAL,
152 TMT_CONTENTMARGINS, nullptr, &checkboxBGContent);
153
154 #define posdx(d) ((d) > 0 ? d : 0)
155
156 int dx =
157 posdx(checkboxBGContent.cxRightWidth - checkboxBGSizing.cxRightWidth) +
158 posdx(checkboxBGContent.cxLeftWidth - checkboxBGSizing.cxLeftWidth);
159 int dy =
160 posdx(checkboxBGContent.cyTopHeight - checkboxBGSizing.cyTopHeight) +
161 posdx(checkboxBGContent.cyBottomHeight - checkboxBGSizing.cyBottomHeight);
162
163 #undef posdx
164
165 SIZE ret(GetCheckboxBGSize(theme, hdc));
166 ret.cx += dx;
167 ret.cy += dy;
168 return ret;
169 }
170
GetGutterSize(HANDLE theme,HDC hdc)171 static SIZE GetGutterSize(HANDLE theme, HDC hdc) {
172 SIZE gutterSize;
173 GetThemePartSize(theme, hdc, MENU_POPUPGUTTER, 0, nullptr, TS_TRUE,
174 &gutterSize);
175
176 SIZE checkboxBGSize(GetCheckboxBGBounds(theme, hdc));
177
178 SIZE itemSize;
179 GetThemePartSize(theme, hdc, MENU_POPUPITEM, MPI_NORMAL, nullptr, TS_TRUE,
180 &itemSize);
181
182 // Figure out how big the menuitem's icon will be (if present) at current DPI
183 // Needs the system scale for consistency with Windows Theme API.
184 double scaleFactor = WinUtils::SystemScaleFactor();
185 int iconDevicePixels = NSToIntRound(16 * scaleFactor);
186 SIZE iconSize = {iconDevicePixels, iconDevicePixels};
187 // Not really sure what margins should be used here, but this seems to work in
188 // practice...
189 MARGINS margins = {0};
190 GetThemeMargins(theme, hdc, MENU_POPUPCHECKBACKGROUND, MCB_NORMAL,
191 TMT_CONTENTMARGINS, nullptr, &margins);
192 iconSize.cx += margins.cxLeftWidth + margins.cxRightWidth;
193 iconSize.cy += margins.cyTopHeight + margins.cyBottomHeight;
194
195 int width = std::max(
196 itemSize.cx, std::max(iconSize.cx, checkboxBGSize.cx) + gutterSize.cx);
197 int height = std::max(itemSize.cy, std::max(iconSize.cy, checkboxBGSize.cy));
198
199 SIZE ret;
200 ret.cx = width;
201 ret.cy = height;
202 return ret;
203 }
204
GetCachedGutterSize(HANDLE theme)205 SIZE nsNativeThemeWin::GetCachedGutterSize(HANDLE theme) {
206 if (mGutterSizeCacheValid) {
207 return mGutterSizeCache;
208 }
209
210 mGutterSizeCache = GetGutterSize(theme, nullptr);
211 mGutterSizeCacheValid = true;
212
213 return mGutterSizeCache;
214 }
215
216 /* DrawThemeBGRTLAware - render a theme part based on rtl state.
217 * Some widgets are not direction-neutral and need to be drawn reversed for
218 * RTL. Windows provides a way to do this with SetLayout, but this reverses
219 * the entire drawing area of a given device context, which means that its
220 * use will also affect the positioning of the widget. There are two ways
221 * to work around this:
222 *
223 * Option 1: Alter the position of the rect that we send so that we cancel
224 * out the positioning effects of SetLayout
225 * Option 2: Create a memory DC with the widgetRect's dimensions, draw onto
226 * that, and then transfer the results back to our DC
227 *
228 * This function tries to implement option 1, under the assumption that the
229 * correct way to reverse the effects of SetLayout is to translate the rect
230 * such that the offset from the DC bitmap's left edge to the old rect's
231 * left edge is equal to the offset from the DC bitmap's right edge to the
232 * new rect's right edge. In other words,
233 * (oldRect.left + vpOrg.x) == ((dcBMP.width - vpOrg.x) - newRect.right)
234 */
DrawThemeBGRTLAware(HANDLE aTheme,HDC aHdc,int aPart,int aState,const RECT * aWidgetRect,const RECT * aClipRect,bool aIsRtl)235 static HRESULT DrawThemeBGRTLAware(HANDLE aTheme, HDC aHdc, int aPart,
236 int aState, const RECT* aWidgetRect,
237 const RECT* aClipRect, bool aIsRtl) {
238 NS_ASSERTION(aTheme, "Bad theme handle.");
239 NS_ASSERTION(aHdc, "Bad hdc.");
240 NS_ASSERTION(aWidgetRect, "Bad rect.");
241 NS_ASSERTION(aClipRect, "Bad clip rect.");
242
243 if (!aIsRtl) {
244 return DrawThemeBackground(aTheme, aHdc, aPart, aState, aWidgetRect,
245 aClipRect);
246 }
247
248 HGDIOBJ hObj = GetCurrentObject(aHdc, OBJ_BITMAP);
249 BITMAP bitmap;
250 POINT vpOrg;
251
252 if (hObj && GetObject(hObj, sizeof(bitmap), &bitmap) &&
253 GetViewportOrgEx(aHdc, &vpOrg)) {
254 RECT newWRect(*aWidgetRect);
255 newWRect.left = bitmap.bmWidth - (aWidgetRect->right + 2 * vpOrg.x);
256 newWRect.right = bitmap.bmWidth - (aWidgetRect->left + 2 * vpOrg.x);
257
258 RECT newCRect;
259 RECT* newCRectPtr = nullptr;
260
261 if (aClipRect) {
262 newCRect.top = aClipRect->top;
263 newCRect.bottom = aClipRect->bottom;
264 newCRect.left = bitmap.bmWidth - (aClipRect->right + 2 * vpOrg.x);
265 newCRect.right = bitmap.bmWidth - (aClipRect->left + 2 * vpOrg.x);
266 newCRectPtr = &newCRect;
267 }
268
269 SetLayout(aHdc, LAYOUT_RTL);
270 HRESULT hr = DrawThemeBackground(aTheme, aHdc, aPart, aState, &newWRect,
271 newCRectPtr);
272 SetLayout(aHdc, 0);
273 if (SUCCEEDED(hr)) {
274 return hr;
275 }
276 }
277 return DrawThemeBackground(aTheme, aHdc, aPart, aState, aWidgetRect,
278 aClipRect);
279 }
280
281 /*
282 * Caption button padding data - 'hot' button padding.
283 * These areas are considered hot, in that they activate
284 * a button when hovered or clicked. The button graphic
285 * is drawn inside the padding border. Unrecognized themes
286 * are treated as their recognized counterparts for now.
287 * left top right bottom
288 * classic min 1 2 0 1
289 * classic max 0 2 1 1
290 * classic close 1 2 2 1
291 *
292 * aero basic min 1 2 0 2
293 * aero basic max 0 2 1 2
294 * aero basic close 1 2 1 2
295 *
296 * 'cold' button padding - generic button padding, should
297 * be handled in css.
298 * left top right bottom
299 * classic min 0 0 0 0
300 * classic max 0 0 0 0
301 * classic close 0 0 0 0
302 *
303 * aero basic min 0 0 1 0
304 * aero basic max 1 0 0 0
305 * aero basic close 0 0 0 0
306 */
307
308 enum CaptionDesktopTheme {
309 CAPTION_CLASSIC = 0,
310 CAPTION_BASIC,
311 };
312
313 enum CaptionButton {
314 CAPTIONBUTTON_MINIMIZE = 0,
315 CAPTIONBUTTON_RESTORE,
316 CAPTIONBUTTON_CLOSE,
317 };
318
319 struct CaptionButtonPadding {
320 RECT hotPadding[3];
321 };
322
323 // RECT: left, top, right, bottom
324 static CaptionButtonPadding buttonData[3] = {
325 {{{1, 2, 0, 1}, {0, 2, 1, 1}, {1, 2, 2, 1}}},
326 {{{1, 2, 0, 2}, {0, 2, 1, 2}, {1, 2, 2, 2}}},
327 {{{0, 2, 0, 2}, {0, 2, 1, 2}, {1, 2, 2, 2}}}};
328
329 // Adds "hot" caption button padding to minimum widget size.
AddPaddingRect(LayoutDeviceIntSize * aSize,CaptionButton button)330 static void AddPaddingRect(LayoutDeviceIntSize* aSize, CaptionButton button) {
331 if (!aSize) return;
332 RECT offset;
333 if (!IsAppThemed())
334 offset = buttonData[CAPTION_CLASSIC].hotPadding[button];
335 else
336 offset = buttonData[CAPTION_BASIC].hotPadding[button];
337 aSize->width += offset.left + offset.right;
338 aSize->height += offset.top + offset.bottom;
339 }
340
341 // If we've added padding to the minimum widget size, offset
342 // the area we draw into to compensate.
OffsetBackgroundRect(RECT & rect,CaptionButton button)343 static void OffsetBackgroundRect(RECT& rect, CaptionButton button) {
344 RECT offset;
345 if (!IsAppThemed())
346 offset = buttonData[CAPTION_CLASSIC].hotPadding[button];
347 else
348 offset = buttonData[CAPTION_BASIC].hotPadding[button];
349 rect.left += offset.left;
350 rect.top += offset.top;
351 rect.right -= offset.right;
352 rect.bottom -= offset.bottom;
353 }
354
355 /*
356 * Notes on progress track and meter part constants:
357 * xp and up:
358 * PP_BAR(_VERT) - base progress track
359 * PP_TRANSPARENTBAR(_VERT) - transparent progress track. this only works if
360 * the underlying surface supports alpha. otherwise
361 * theme lib's DrawThemeBackground falls back on
362 * opaque PP_BAR. we currently don't use this.
363 * PP_CHUNK(_VERT) - xp progress meter. this does not draw an xp style
364 * progress w/chunks, it draws fill using the chunk
365 * graphic.
366 * vista and up:
367 * PP_FILL(_VERT) - progress meter. these have four states/colors.
368 * PP_PULSEOVERLAY(_VERT) - white reflection - an overlay, not sure what this
369 * is used for.
370 * PP_MOVEOVERLAY(_VERT) - green pulse - the pulse effect overlay on
371 * determined progress bars. we also use this for
372 * indeterminate chunk.
373 *
374 * Notes on state constants:
375 * PBBS_NORMAL - green progress
376 * PBBVS_PARTIAL/PBFVS_ERROR - red error progress
377 * PBFS_PAUSED - yellow paused progress
378 *
379 * There is no common controls style indeterminate part on vista and up.
380 */
381
382 /*
383 * Progress bar related constants. These values are found by experimenting and
384 * comparing against native widgets used by the system. They are very unlikely
385 * exact but try to not be too wrong.
386 */
387 // The amount of time we animate progress meters parts across the frame.
388 static const double kProgressDeterminateTimeSpan = 3.0;
389 static const double kProgressIndeterminateTimeSpan = 5.0;
390 // The width of the overlay used to animate the horizontal progress bar (Vista
391 // and later).
392 static const int32_t kProgressHorizontalOverlaySize = 120;
393 // The height of the overlay used to animate the vertical progress bar (Vista
394 // and later).
395 static const int32_t kProgressVerticalOverlaySize = 45;
396 // The height of the overlay used for the vertical indeterminate progress bar
397 // (Vista and later).
398 static const int32_t kProgressVerticalIndeterminateOverlaySize = 60;
399 // The width of the overlay used to animate the indeterminate progress bar
400 // (Windows Classic).
401 static const int32_t kProgressClassicOverlaySize = 40;
402
403 /*
404 * GetProgressOverlayStyle - returns the proper overlay part for themed
405 * progress bars based on os and orientation.
406 */
GetProgressOverlayStyle(bool aIsVertical)407 static int32_t GetProgressOverlayStyle(bool aIsVertical) {
408 return aIsVertical ? PP_MOVEOVERLAYVERT : PP_MOVEOVERLAY;
409 }
410
411 /*
412 * GetProgressOverlaySize - returns the minimum width or height for themed
413 * progress bar overlays. This includes the width of indeterminate chunks
414 * and vista pulse overlays.
415 */
GetProgressOverlaySize(bool aIsVertical,bool aIsIndeterminate)416 static int32_t GetProgressOverlaySize(bool aIsVertical, bool aIsIndeterminate) {
417 if (aIsVertical) {
418 return aIsIndeterminate ? kProgressVerticalIndeterminateOverlaySize
419 : kProgressVerticalOverlaySize;
420 }
421 return kProgressHorizontalOverlaySize;
422 }
423
424 /*
425 * IsProgressMeterFilled - Determines if a progress meter is at 100% fill based
426 * on a comparison of the current value and maximum.
427 */
IsProgressMeterFilled(nsIFrame * aFrame)428 static bool IsProgressMeterFilled(nsIFrame* aFrame) {
429 NS_ENSURE_TRUE(aFrame, false);
430 nsIFrame* parentFrame = aFrame->GetParent();
431 NS_ENSURE_TRUE(parentFrame, false);
432 return nsNativeTheme::GetProgressValue(parentFrame) ==
433 nsNativeTheme::GetProgressMaxValue(parentFrame);
434 }
435
436 /*
437 * CalculateProgressOverlayRect - returns the padded overlay animation rect
438 * used in rendering progress bars. Resulting rects are used in rendering
439 * vista+ pulse overlays and indeterminate progress meters. Graphics should
440 * be rendered at the origin.
441 */
CalculateProgressOverlayRect(nsIFrame * aFrame,RECT * aWidgetRect,bool aIsVertical,bool aIsIndeterminate,bool aIsClassic)442 RECT nsNativeThemeWin::CalculateProgressOverlayRect(nsIFrame* aFrame,
443 RECT* aWidgetRect,
444 bool aIsVertical,
445 bool aIsIndeterminate,
446 bool aIsClassic) {
447 NS_ASSERTION(aFrame, "bad frame pointer");
448 NS_ASSERTION(aWidgetRect, "bad rect pointer");
449
450 int32_t frameSize = aIsVertical ? aWidgetRect->bottom - aWidgetRect->top
451 : aWidgetRect->right - aWidgetRect->left;
452
453 // Recycle a set of progress pulse timers - these timers control the position
454 // of all progress overlays and indeterminate chunks that get rendered.
455 double span = aIsIndeterminate ? kProgressIndeterminateTimeSpan
456 : kProgressDeterminateTimeSpan;
457 TimeDuration period;
458 if (!aIsIndeterminate) {
459 if (TimeStamp::Now() >
460 (mProgressDeterminateTimeStamp + TimeDuration::FromSeconds(span))) {
461 mProgressDeterminateTimeStamp = TimeStamp::Now();
462 }
463 period = TimeStamp::Now() - mProgressDeterminateTimeStamp;
464 } else {
465 if (TimeStamp::Now() >
466 (mProgressIndeterminateTimeStamp + TimeDuration::FromSeconds(span))) {
467 mProgressIndeterminateTimeStamp = TimeStamp::Now();
468 }
469 period = TimeStamp::Now() - mProgressIndeterminateTimeStamp;
470 }
471
472 double percent = period / TimeDuration::FromSeconds(span);
473
474 if (!aIsVertical && IsFrameRTL(aFrame)) percent = 1 - percent;
475
476 RECT overlayRect = *aWidgetRect;
477 int32_t overlaySize;
478 if (!aIsClassic) {
479 overlaySize = GetProgressOverlaySize(aIsVertical, aIsIndeterminate);
480 } else {
481 overlaySize = kProgressClassicOverlaySize;
482 }
483
484 // Calculate a bounds that is larger than the meters frame such that the
485 // overlay starts and ends completely off the edge of the frame:
486 // [overlay][frame][overlay]
487 // This also yields a nice delay on rotation. Use overlaySize as the minimum
488 // size for [overlay] based on the graphics dims. If [frame] is larger, use
489 // the frame size instead.
490 int trackWidth = frameSize > overlaySize ? frameSize : overlaySize;
491 if (!aIsVertical) {
492 int xPos = aWidgetRect->left - trackWidth;
493 xPos += (int)ceil(((double)(trackWidth * 2) * percent));
494 overlayRect.left = xPos;
495 overlayRect.right = xPos + overlaySize;
496 } else {
497 int yPos = aWidgetRect->bottom + trackWidth;
498 yPos -= (int)ceil(((double)(trackWidth * 2) * percent));
499 overlayRect.bottom = yPos;
500 overlayRect.top = yPos - overlaySize;
501 }
502 return overlayRect;
503 }
504
505 /*
506 * DrawProgressMeter - render an appropriate progress meter based on progress
507 * meter style, orientation, and os. Note, this does not render the underlying
508 * progress track.
509 *
510 * @param aFrame the widget frame
511 * @param aWidgetType type of widget
512 * @param aTheme progress theme handle
513 * @param aHdc hdc returned by gfxWindowsNativeDrawing
514 * @param aPart the PP_X progress part
515 * @param aState the theme state
516 * @param aWidgetRect bounding rect for the widget
517 * @param aClipRect dirty rect that needs drawing.
518 * @param aAppUnits app units per device pixel
519 */
DrawThemedProgressMeter(nsIFrame * aFrame,int aWidgetType,HANDLE aTheme,HDC aHdc,int aPart,int aState,RECT * aWidgetRect,RECT * aClipRect)520 void nsNativeThemeWin::DrawThemedProgressMeter(nsIFrame* aFrame,
521 int aWidgetType, HANDLE aTheme,
522 HDC aHdc, int aPart, int aState,
523 RECT* aWidgetRect,
524 RECT* aClipRect) {
525 if (!aFrame || !aTheme || !aHdc) return;
526
527 NS_ASSERTION(aWidgetRect, "bad rect pointer");
528 NS_ASSERTION(aClipRect, "bad clip rect pointer");
529
530 RECT adjWidgetRect, adjClipRect;
531 adjWidgetRect = *aWidgetRect;
532 adjClipRect = *aClipRect;
533
534 nsIFrame* parentFrame = aFrame->GetParent();
535 if (!parentFrame) {
536 // We have no parent to work with, just bail.
537 NS_WARNING("No parent frame for progress rendering. Can't paint.");
538 return;
539 }
540
541 EventStates eventStates = GetContentState(parentFrame, aWidgetType);
542 bool vertical = IsVerticalProgress(parentFrame) ||
543 aWidgetType == NS_THEME_PROGRESSCHUNK_VERTICAL;
544 bool indeterminate = IsIndeterminateProgress(parentFrame, eventStates);
545 bool animate = indeterminate;
546
547 // Vista and up progress meter is fill style, rendered here. We render
548 // the pulse overlay in the follow up section below.
549 DrawThemeBackground(aTheme, aHdc, aPart, aState, &adjWidgetRect,
550 &adjClipRect);
551 if (!IsProgressMeterFilled(aFrame)) {
552 animate = true;
553 }
554
555 if (animate) {
556 // Indeterminate rendering
557 int32_t overlayPart = GetProgressOverlayStyle(vertical);
558 RECT overlayRect = CalculateProgressOverlayRect(
559 aFrame, &adjWidgetRect, vertical, indeterminate, false);
560 DrawThemeBackground(aTheme, aHdc, overlayPart, aState, &overlayRect,
561 &adjClipRect);
562
563 if (!QueueAnimatedContentForRefresh(aFrame->GetContent(), 60)) {
564 NS_WARNING("unable to animate progress widget!");
565 }
566 }
567 }
568
GetCachedWidgetBorder(nsIFrame * aFrame,HTHEME aTheme,nsUXThemeClass aThemeClass,uint8_t aWidgetType,int32_t aPart,int32_t aState,nsIntMargin * aResult)569 nsresult nsNativeThemeWin::GetCachedWidgetBorder(
570 nsIFrame* aFrame, HTHEME aTheme, nsUXThemeClass aThemeClass,
571 uint8_t aWidgetType, int32_t aPart, int32_t aState, nsIntMargin* aResult) {
572 int32_t cacheIndex = aThemeClass * THEME_PART_DISTINCT_VALUE_COUNT + aPart;
573 int32_t cacheBitIndex = cacheIndex / 8;
574 uint8_t cacheBit = 1u << (cacheIndex % 8);
575
576 if (mBorderCacheValid[cacheBitIndex] & cacheBit) {
577 *aResult = mBorderCache[cacheIndex];
578 return NS_OK;
579 }
580
581 // Get our info.
582 RECT outerRect; // Create a fake outer rect.
583 outerRect.top = outerRect.left = 100;
584 outerRect.right = outerRect.bottom = 200;
585 RECT contentRect(outerRect);
586 HRESULT res = GetThemeBackgroundContentRect(aTheme, nullptr, aPart, aState,
587 &outerRect, &contentRect);
588
589 if (FAILED(res)) {
590 return NS_ERROR_FAILURE;
591 }
592
593 // Now compute the delta in each direction and place it in our
594 // nsIntMargin struct.
595 aResult->top = contentRect.top - outerRect.top;
596 aResult->bottom = outerRect.bottom - contentRect.bottom;
597 aResult->left = contentRect.left - outerRect.left;
598 aResult->right = outerRect.right - contentRect.right;
599
600 mBorderCacheValid[cacheBitIndex] |= cacheBit;
601 mBorderCache[cacheIndex] = *aResult;
602
603 return NS_OK;
604 }
605
GetCachedMinimumWidgetSize(nsIFrame * aFrame,HANDLE aTheme,nsUXThemeClass aThemeClass,uint8_t aWidgetType,int32_t aPart,int32_t aState,THEMESIZE aSizeReq,mozilla::LayoutDeviceIntSize * aResult)606 nsresult nsNativeThemeWin::GetCachedMinimumWidgetSize(
607 nsIFrame* aFrame, HANDLE aTheme, nsUXThemeClass aThemeClass,
608 uint8_t aWidgetType, int32_t aPart, int32_t aState, THEMESIZE aSizeReq,
609 mozilla::LayoutDeviceIntSize* aResult) {
610 int32_t cachePart = aPart;
611
612 if (aWidgetType == NS_THEME_BUTTON && aSizeReq == TS_MIN) {
613 // In practice, NS_THEME_BUTTON is the only widget type which has an
614 // aSizeReq that varies for us, and it can only be TS_MIN or TS_TRUE. Just
615 // stuff that extra bit into the aPart part of the cache, since BP_Count is
616 // well below THEME_PART_DISTINCT_VALUE_COUNT anyway.
617 cachePart = BP_Count;
618 }
619
620 MOZ_ASSERT(aPart < THEME_PART_DISTINCT_VALUE_COUNT);
621 int32_t cacheIndex =
622 aThemeClass * THEME_PART_DISTINCT_VALUE_COUNT + cachePart;
623 int32_t cacheBitIndex = cacheIndex / 8;
624 uint8_t cacheBit = 1u << (cacheIndex % 8);
625
626 if (mMinimumWidgetSizeCacheValid[cacheBitIndex] & cacheBit) {
627 *aResult = mMinimumWidgetSizeCache[cacheIndex];
628 return NS_OK;
629 }
630
631 HDC hdc = ::GetDC(NULL);
632 if (!hdc) {
633 return NS_ERROR_FAILURE;
634 }
635
636 SIZE sz;
637 GetThemePartSize(aTheme, hdc, aPart, aState, nullptr, aSizeReq, &sz);
638 aResult->width = sz.cx;
639 aResult->height = sz.cy;
640
641 switch (aWidgetType) {
642 case NS_THEME_INNER_SPIN_BUTTON:
643 case NS_THEME_SPINNER_UPBUTTON:
644 case NS_THEME_SPINNER_DOWNBUTTON:
645 aResult->width++;
646 aResult->height = aResult->height / 2 + 1;
647 break;
648
649 case NS_THEME_MENUSEPARATOR: {
650 SIZE gutterSize(GetGutterSize(aTheme, hdc));
651 aResult->width += gutterSize.cx;
652 break;
653 }
654
655 case NS_THEME_MENUARROW: {
656 // Use the width of the arrow glyph as padding. See the drawing
657 // code for details.
658 aResult->width *= 2;
659 break;
660 }
661 }
662
663 ::ReleaseDC(nullptr, hdc);
664
665 mMinimumWidgetSizeCacheValid[cacheBitIndex] |= cacheBit;
666 mMinimumWidgetSizeCache[cacheIndex] = *aResult;
667
668 return NS_OK;
669 }
670
GetThemeClass(uint8_t aWidgetType)671 mozilla::Maybe<nsUXThemeClass> nsNativeThemeWin::GetThemeClass(
672 uint8_t aWidgetType) {
673 switch (aWidgetType) {
674 case NS_THEME_BUTTON:
675 case NS_THEME_RADIO:
676 case NS_THEME_CHECKBOX:
677 case NS_THEME_GROUPBOX:
678 return Some(eUXButton);
679 case NS_THEME_NUMBER_INPUT:
680 case NS_THEME_TEXTFIELD:
681 case NS_THEME_TEXTFIELD_MULTILINE:
682 case NS_THEME_FOCUS_OUTLINE:
683 return Some(eUXEdit);
684 case NS_THEME_TOOLTIP:
685 return Some(eUXTooltip);
686 case NS_THEME_TOOLBOX:
687 return Some(eUXRebar);
688 case NS_THEME_WIN_MEDIA_TOOLBOX:
689 return Some(eUXMediaRebar);
690 case NS_THEME_WIN_COMMUNICATIONS_TOOLBOX:
691 return Some(eUXCommunicationsRebar);
692 case NS_THEME_WIN_BROWSERTABBAR_TOOLBOX:
693 return Some(eUXBrowserTabBarRebar);
694 case NS_THEME_TOOLBAR:
695 case NS_THEME_TOOLBARBUTTON:
696 case NS_THEME_SEPARATOR:
697 return Some(eUXToolbar);
698 case NS_THEME_PROGRESSBAR:
699 case NS_THEME_PROGRESSBAR_VERTICAL:
700 case NS_THEME_PROGRESSCHUNK:
701 case NS_THEME_PROGRESSCHUNK_VERTICAL:
702 return Some(eUXProgress);
703 case NS_THEME_TAB:
704 case NS_THEME_TABPANEL:
705 case NS_THEME_TABPANELS:
706 return Some(eUXTab);
707 case NS_THEME_SCROLLBAR:
708 case NS_THEME_SCROLLBAR_SMALL:
709 case NS_THEME_SCROLLBAR_VERTICAL:
710 case NS_THEME_SCROLLBAR_HORIZONTAL:
711 case NS_THEME_SCROLLBARBUTTON_UP:
712 case NS_THEME_SCROLLBARBUTTON_DOWN:
713 case NS_THEME_SCROLLBARBUTTON_LEFT:
714 case NS_THEME_SCROLLBARBUTTON_RIGHT:
715 case NS_THEME_SCROLLBARTHUMB_VERTICAL:
716 case NS_THEME_SCROLLBARTHUMB_HORIZONTAL:
717 return Some(eUXScrollbar);
718 case NS_THEME_RANGE:
719 case NS_THEME_RANGE_THUMB:
720 case NS_THEME_SCALE_HORIZONTAL:
721 case NS_THEME_SCALE_VERTICAL:
722 case NS_THEME_SCALETHUMB_HORIZONTAL:
723 case NS_THEME_SCALETHUMB_VERTICAL:
724 return Some(eUXTrackbar);
725 case NS_THEME_INNER_SPIN_BUTTON:
726 case NS_THEME_SPINNER_UPBUTTON:
727 case NS_THEME_SPINNER_DOWNBUTTON:
728 return Some(eUXSpin);
729 case NS_THEME_STATUSBAR:
730 case NS_THEME_STATUSBARPANEL:
731 case NS_THEME_RESIZERPANEL:
732 case NS_THEME_RESIZER:
733 return Some(eUXStatus);
734 case NS_THEME_MENULIST:
735 case NS_THEME_MENULIST_BUTTON:
736 return Some(eUXCombobox);
737 case NS_THEME_TREEHEADERCELL:
738 case NS_THEME_TREEHEADERSORTARROW:
739 return Some(eUXHeader);
740 case NS_THEME_LISTBOX:
741 case NS_THEME_LISTITEM:
742 case NS_THEME_TREEVIEW:
743 case NS_THEME_TREETWISTYOPEN:
744 case NS_THEME_TREEITEM:
745 return Some(eUXListview);
746 case NS_THEME_MENUBAR:
747 case NS_THEME_MENUPOPUP:
748 case NS_THEME_MENUITEM:
749 case NS_THEME_CHECKMENUITEM:
750 case NS_THEME_RADIOMENUITEM:
751 case NS_THEME_MENUCHECKBOX:
752 case NS_THEME_MENURADIO:
753 case NS_THEME_MENUSEPARATOR:
754 case NS_THEME_MENUARROW:
755 case NS_THEME_MENUIMAGE:
756 case NS_THEME_MENUITEMTEXT:
757 return Some(eUXMenu);
758 case NS_THEME_WINDOW_TITLEBAR:
759 case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
760 case NS_THEME_WINDOW_FRAME_LEFT:
761 case NS_THEME_WINDOW_FRAME_RIGHT:
762 case NS_THEME_WINDOW_FRAME_BOTTOM:
763 case NS_THEME_WINDOW_BUTTON_CLOSE:
764 case NS_THEME_WINDOW_BUTTON_MINIMIZE:
765 case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
766 case NS_THEME_WINDOW_BUTTON_RESTORE:
767 case NS_THEME_WINDOW_BUTTON_BOX:
768 case NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED:
769 case NS_THEME_WIN_GLASS:
770 case NS_THEME_WIN_BORDERLESS_GLASS:
771 return Some(eUXWindowFrame);
772 }
773 return Nothing();
774 }
775
776 HANDLE
GetTheme(uint8_t aWidgetType)777 nsNativeThemeWin::GetTheme(uint8_t aWidgetType) {
778 mozilla::Maybe<nsUXThemeClass> themeClass = GetThemeClass(aWidgetType);
779 if (themeClass.isNothing()) {
780 return nullptr;
781 }
782 return nsUXThemeData::GetTheme(themeClass.value());
783 }
784
StandardGetState(nsIFrame * aFrame,uint8_t aWidgetType,bool wantFocused)785 int32_t nsNativeThemeWin::StandardGetState(nsIFrame* aFrame,
786 uint8_t aWidgetType,
787 bool wantFocused) {
788 EventStates eventState = GetContentState(aFrame, aWidgetType);
789 if (eventState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE))
790 return TS_ACTIVE;
791 if (eventState.HasState(NS_EVENT_STATE_HOVER)) return TS_HOVER;
792 if (wantFocused && eventState.HasState(NS_EVENT_STATE_FOCUS))
793 return TS_FOCUSED;
794
795 return TS_NORMAL;
796 }
797
IsMenuActive(nsIFrame * aFrame,uint8_t aWidgetType)798 bool nsNativeThemeWin::IsMenuActive(nsIFrame* aFrame, uint8_t aWidgetType) {
799 nsIContent* content = aFrame->GetContent();
800 if (content->IsXULElement() &&
801 content->NodeInfo()->Equals(nsGkAtoms::richlistitem))
802 return CheckBooleanAttr(aFrame, nsGkAtoms::selected);
803
804 return CheckBooleanAttr(aFrame, nsGkAtoms::menuactive);
805 }
806
807 /**
808 * aPart is filled in with the UXTheme part code. On return, values > 0
809 * are the actual UXTheme part code; -1 means the widget will be drawn by
810 * us; 0 means that we should use part code 0, which isn't a real part code
811 * but elicits some kind of default behaviour from UXTheme when drawing
812 * (but isThemeBackgroundPartiallyTransparent may not work).
813 */
GetThemePartAndState(nsIFrame * aFrame,uint8_t aWidgetType,int32_t & aPart,int32_t & aState)814 nsresult nsNativeThemeWin::GetThemePartAndState(nsIFrame* aFrame,
815 uint8_t aWidgetType,
816 int32_t& aPart,
817 int32_t& aState) {
818 switch (aWidgetType) {
819 case NS_THEME_BUTTON: {
820 aPart = BP_BUTTON;
821 if (!aFrame) {
822 aState = TS_NORMAL;
823 return NS_OK;
824 }
825
826 EventStates eventState = GetContentState(aFrame, aWidgetType);
827 if (IsDisabled(aFrame, eventState)) {
828 aState = TS_DISABLED;
829 return NS_OK;
830 } else if (IsOpenButton(aFrame) || IsCheckedButton(aFrame)) {
831 aState = TS_ACTIVE;
832 return NS_OK;
833 }
834
835 aState = StandardGetState(aFrame, aWidgetType, true);
836
837 // Check for default dialog buttons. These buttons should always look
838 // focused.
839 if (aState == TS_NORMAL && IsDefaultButton(aFrame)) aState = TS_FOCUSED;
840 return NS_OK;
841 }
842 case NS_THEME_CHECKBOX:
843 case NS_THEME_RADIO: {
844 bool isCheckbox = (aWidgetType == NS_THEME_CHECKBOX);
845 aPart = isCheckbox ? BP_CHECKBOX : BP_RADIO;
846
847 enum InputState { UNCHECKED = 0, CHECKED, INDETERMINATE };
848 InputState inputState = UNCHECKED;
849 bool isXULCheckboxRadio = false;
850
851 if (!aFrame) {
852 aState = TS_NORMAL;
853 } else {
854 if (GetCheckedOrSelected(aFrame, !isCheckbox)) {
855 inputState = CHECKED;
856 }
857 if (isCheckbox && GetIndeterminate(aFrame)) {
858 inputState = INDETERMINATE;
859 }
860
861 EventStates eventState = GetContentState(
862 isXULCheckboxRadio ? aFrame->GetParent() : aFrame, aWidgetType);
863 if (IsDisabled(aFrame, eventState)) {
864 aState = TS_DISABLED;
865 } else {
866 aState = StandardGetState(aFrame, aWidgetType, false);
867 }
868 }
869
870 // 4 unchecked states, 4 checked states, 4 indeterminate states.
871 aState += inputState * 4;
872 return NS_OK;
873 }
874 case NS_THEME_GROUPBOX: {
875 aPart = BP_GROUPBOX;
876 aState = TS_NORMAL;
877 // Since we don't support groupbox disabled and GBS_DISABLED looks the
878 // same as GBS_NORMAL don't bother supporting GBS_DISABLED.
879 return NS_OK;
880 }
881 case NS_THEME_NUMBER_INPUT:
882 case NS_THEME_TEXTFIELD:
883 case NS_THEME_TEXTFIELD_MULTILINE: {
884 EventStates eventState = GetContentState(aFrame, aWidgetType);
885
886 /* Note: the NOSCROLL type has a rounded corner in each corner. The more
887 * specific HSCROLL, VSCROLL, HVSCROLL types have side and/or top/bottom
888 * edges rendered as straight horizontal lines with sharp corners to
889 * accommodate a scrollbar. However, the scrollbar gets rendered on top
890 * of this for us, so we don't care, and can just use NOSCROLL here.
891 */
892 aPart = TFP_EDITBORDER_NOSCROLL;
893
894 if (!aFrame) {
895 aState = TFS_EDITBORDER_NORMAL;
896 } else if (IsDisabled(aFrame, eventState)) {
897 aState = TFS_EDITBORDER_DISABLED;
898 } else if (IsReadOnly(aFrame)) {
899 /* no special read-only state */
900 aState = TFS_EDITBORDER_NORMAL;
901 } else {
902 nsIContent* content = aFrame->GetContent();
903
904 /* XUL textboxes don't get focused themselves, because they have child
905 * html:input.. but we can check the XUL focused attributes on them
906 */
907 if (content && content->IsXULElement() && IsFocused(aFrame))
908 aState = TFS_EDITBORDER_FOCUSED;
909 else if (eventState.HasAtLeastOneOfStates(NS_EVENT_STATE_ACTIVE |
910 NS_EVENT_STATE_FOCUS))
911 aState = TFS_EDITBORDER_FOCUSED;
912 else if (eventState.HasState(NS_EVENT_STATE_HOVER))
913 aState = TFS_EDITBORDER_HOVER;
914 else
915 aState = TFS_EDITBORDER_NORMAL;
916 }
917
918 return NS_OK;
919 }
920 case NS_THEME_FOCUS_OUTLINE: {
921 // XXX the EDITBORDER values don't respect DTBG_OMITCONTENT
922 aPart = TFP_TEXTFIELD; // TFP_EDITBORDER_NOSCROLL;
923 aState = TS_FOCUSED; // TFS_EDITBORDER_FOCUSED;
924 return NS_OK;
925 }
926 case NS_THEME_TOOLTIP: {
927 aPart = TTP_STANDARD;
928 aState = TS_NORMAL;
929 return NS_OK;
930 }
931 case NS_THEME_PROGRESSBAR:
932 case NS_THEME_PROGRESSBAR_VERTICAL: {
933 // Note IsVerticalProgress only tests for orient css attrribute,
934 // NS_THEME_PROGRESSBAR_VERTICAL is dedicated to -moz-appearance:
935 // progressbar-vertical.
936 bool vertical = IsVerticalProgress(aFrame) ||
937 aWidgetType == NS_THEME_PROGRESSBAR_VERTICAL;
938 aPart = vertical ? PP_BARVERT : PP_BAR;
939 aState = PBBS_NORMAL;
940 return NS_OK;
941 }
942 case NS_THEME_PROGRESSCHUNK:
943 case NS_THEME_PROGRESSCHUNK_VERTICAL: {
944 nsIFrame* parentFrame = aFrame->GetParent();
945 if (aWidgetType == NS_THEME_PROGRESSCHUNK_VERTICAL ||
946 IsVerticalProgress(parentFrame)) {
947 aPart = PP_FILLVERT;
948 } else {
949 aPart = PP_FILL;
950 }
951
952 aState = PBBVS_NORMAL;
953 return NS_OK;
954 }
955 case NS_THEME_TOOLBARBUTTON: {
956 aPart = BP_BUTTON;
957 if (!aFrame) {
958 aState = TS_NORMAL;
959 return NS_OK;
960 }
961
962 EventStates eventState = GetContentState(aFrame, aWidgetType);
963 if (IsDisabled(aFrame, eventState)) {
964 aState = TS_DISABLED;
965 return NS_OK;
966 }
967 if (IsOpenButton(aFrame)) {
968 aState = TS_ACTIVE;
969 return NS_OK;
970 }
971
972 if (eventState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE))
973 aState = TS_ACTIVE;
974 else if (eventState.HasState(NS_EVENT_STATE_HOVER)) {
975 if (IsCheckedButton(aFrame))
976 aState = TB_HOVER_CHECKED;
977 else
978 aState = TS_HOVER;
979 } else {
980 if (IsCheckedButton(aFrame))
981 aState = TB_CHECKED;
982 else
983 aState = TS_NORMAL;
984 }
985
986 return NS_OK;
987 }
988 case NS_THEME_SEPARATOR: {
989 aPart = TP_SEPARATOR;
990 aState = TS_NORMAL;
991 return NS_OK;
992 }
993 case NS_THEME_SCROLLBARBUTTON_UP:
994 case NS_THEME_SCROLLBARBUTTON_DOWN:
995 case NS_THEME_SCROLLBARBUTTON_LEFT:
996 case NS_THEME_SCROLLBARBUTTON_RIGHT: {
997 aPart = SP_BUTTON;
998 aState = (aWidgetType - NS_THEME_SCROLLBARBUTTON_UP) * 4;
999 EventStates eventState = GetContentState(aFrame, aWidgetType);
1000 if (!aFrame)
1001 aState += TS_NORMAL;
1002 else if (IsDisabled(aFrame, eventState))
1003 aState += TS_DISABLED;
1004 else {
1005 nsIFrame* parent = aFrame->GetParent();
1006 EventStates parentState =
1007 GetContentState(parent, parent->StyleDisplay()->mAppearance);
1008 if (eventState.HasAllStates(NS_EVENT_STATE_HOVER |
1009 NS_EVENT_STATE_ACTIVE))
1010 aState += TS_ACTIVE;
1011 else if (eventState.HasState(NS_EVENT_STATE_HOVER))
1012 aState += TS_HOVER;
1013 else if (parentState.HasState(NS_EVENT_STATE_HOVER))
1014 aState = (aWidgetType - NS_THEME_SCROLLBARBUTTON_UP) +
1015 SP_BUTTON_IMPLICIT_HOVER_BASE;
1016 else
1017 aState += TS_NORMAL;
1018 }
1019 return NS_OK;
1020 }
1021 case NS_THEME_SCROLLBAR_HORIZONTAL:
1022 case NS_THEME_SCROLLBAR_VERTICAL: {
1023 aPart = (aWidgetType == NS_THEME_SCROLLBAR_HORIZONTAL)
1024 ? SP_TRACKSTARTHOR
1025 : SP_TRACKSTARTVERT;
1026 aState = TS_NORMAL;
1027 return NS_OK;
1028 }
1029 case NS_THEME_SCROLLBARTHUMB_HORIZONTAL:
1030 case NS_THEME_SCROLLBARTHUMB_VERTICAL: {
1031 aPart = (aWidgetType == NS_THEME_SCROLLBARTHUMB_HORIZONTAL)
1032 ? SP_THUMBHOR
1033 : SP_THUMBVERT;
1034 EventStates eventState = GetContentState(aFrame, aWidgetType);
1035 if (!aFrame)
1036 aState = TS_NORMAL;
1037 else if (IsDisabled(aFrame, eventState))
1038 aState = TS_DISABLED;
1039 else {
1040 if (eventState.HasState(NS_EVENT_STATE_ACTIVE)) // Hover is not also a
1041 // requirement for the
1042 // thumb, since the
1043 // drag is not canceled
1044 // when you move
1045 // outside the thumb.
1046 aState = TS_ACTIVE;
1047 else if (eventState.HasState(NS_EVENT_STATE_HOVER))
1048 aState = TS_HOVER;
1049 else
1050 aState = TS_NORMAL;
1051 }
1052 return NS_OK;
1053 }
1054 case NS_THEME_RANGE:
1055 case NS_THEME_SCALE_HORIZONTAL:
1056 case NS_THEME_SCALE_VERTICAL: {
1057 if (aWidgetType == NS_THEME_SCALE_HORIZONTAL ||
1058 (aWidgetType == NS_THEME_RANGE && IsRangeHorizontal(aFrame))) {
1059 aPart = TKP_TRACK;
1060 aState = TRS_NORMAL;
1061 } else {
1062 aPart = TKP_TRACKVERT;
1063 aState = TRVS_NORMAL;
1064 }
1065 return NS_OK;
1066 }
1067 case NS_THEME_RANGE_THUMB:
1068 case NS_THEME_SCALETHUMB_HORIZONTAL:
1069 case NS_THEME_SCALETHUMB_VERTICAL: {
1070 if (aWidgetType == NS_THEME_RANGE_THUMB) {
1071 if (IsRangeHorizontal(aFrame)) {
1072 aPart = TKP_THUMBBOTTOM;
1073 } else {
1074 aPart = IsFrameRTL(aFrame) ? TKP_THUMBLEFT : TKP_THUMBRIGHT;
1075 }
1076 } else {
1077 aPart = (aWidgetType == NS_THEME_SCALETHUMB_HORIZONTAL) ? TKP_THUMB
1078 : TKP_THUMBVERT;
1079 }
1080 EventStates eventState = GetContentState(aFrame, aWidgetType);
1081 if (!aFrame)
1082 aState = TS_NORMAL;
1083 else if (IsDisabled(aFrame, eventState)) {
1084 aState = TKP_DISABLED;
1085 } else {
1086 if (eventState.HasState(NS_EVENT_STATE_ACTIVE)) // Hover is not also a
1087 // requirement for the
1088 // thumb, since the
1089 // drag is not canceled
1090 // when you move
1091 // outside the thumb.
1092 aState = TS_ACTIVE;
1093 else if (eventState.HasState(NS_EVENT_STATE_FOCUS))
1094 aState = TKP_FOCUSED;
1095 else if (eventState.HasState(NS_EVENT_STATE_HOVER))
1096 aState = TS_HOVER;
1097 else
1098 aState = TS_NORMAL;
1099 }
1100 return NS_OK;
1101 }
1102 case NS_THEME_INNER_SPIN_BUTTON:
1103 case NS_THEME_SPINNER_UPBUTTON:
1104 case NS_THEME_SPINNER_DOWNBUTTON: {
1105 aPart = (aWidgetType == NS_THEME_SPINNER_UPBUTTON) ? SPNP_UP : SPNP_DOWN;
1106 EventStates eventState = GetContentState(aFrame, aWidgetType);
1107 if (!aFrame)
1108 aState = TS_NORMAL;
1109 else if (IsDisabled(aFrame, eventState))
1110 aState = TS_DISABLED;
1111 else
1112 aState = StandardGetState(aFrame, aWidgetType, false);
1113 return NS_OK;
1114 }
1115 case NS_THEME_TOOLBOX:
1116 case NS_THEME_WIN_MEDIA_TOOLBOX:
1117 case NS_THEME_WIN_COMMUNICATIONS_TOOLBOX:
1118 case NS_THEME_WIN_BROWSERTABBAR_TOOLBOX:
1119 case NS_THEME_STATUSBAR:
1120 case NS_THEME_SCROLLBAR:
1121 case NS_THEME_SCROLLBAR_SMALL: {
1122 aState = 0;
1123 aPart = RP_BACKGROUND;
1124 return NS_OK;
1125 }
1126 case NS_THEME_TOOLBAR: {
1127 // Use -1 to indicate we don't wish to have the theme background drawn
1128 // for this item. We will pass any nessessary information via aState,
1129 // and will render the item using separate code.
1130 aPart = -1;
1131 aState = 0;
1132 if (aFrame) {
1133 nsIContent* content = aFrame->GetContent();
1134 nsIContent* parent = content->GetParent();
1135 // XXXzeniko hiding the first toolbar will result in an unwanted margin
1136 if (parent && parent->GetFirstChild() == content) {
1137 aState = 1;
1138 }
1139 }
1140 return NS_OK;
1141 }
1142 case NS_THEME_STATUSBARPANEL:
1143 case NS_THEME_RESIZERPANEL:
1144 case NS_THEME_RESIZER: {
1145 aPart = (aWidgetType - NS_THEME_STATUSBARPANEL) + 1;
1146 aState = TS_NORMAL;
1147 return NS_OK;
1148 }
1149 case NS_THEME_TREEVIEW:
1150 case NS_THEME_LISTBOX: {
1151 aPart = TREEVIEW_BODY;
1152 aState = TS_NORMAL;
1153 return NS_OK;
1154 }
1155 case NS_THEME_TABPANELS: {
1156 aPart = TABP_PANELS;
1157 aState = TS_NORMAL;
1158 return NS_OK;
1159 }
1160 case NS_THEME_TABPANEL: {
1161 aPart = TABP_PANEL;
1162 aState = TS_NORMAL;
1163 return NS_OK;
1164 }
1165 case NS_THEME_TAB: {
1166 aPart = TABP_TAB;
1167 if (!aFrame) {
1168 aState = TS_NORMAL;
1169 return NS_OK;
1170 }
1171
1172 EventStates eventState = GetContentState(aFrame, aWidgetType);
1173 if (IsDisabled(aFrame, eventState)) {
1174 aState = TS_DISABLED;
1175 return NS_OK;
1176 }
1177
1178 if (IsSelectedTab(aFrame)) {
1179 aPart = TABP_TAB_SELECTED;
1180 aState = TS_ACTIVE; // The selected tab is always "pressed".
1181 } else
1182 aState = StandardGetState(aFrame, aWidgetType, true);
1183
1184 return NS_OK;
1185 }
1186 case NS_THEME_TREEHEADERSORTARROW: {
1187 // XXX Probably will never work due to a bug in the Luna theme.
1188 aPart = 4;
1189 aState = 1;
1190 return NS_OK;
1191 }
1192 case NS_THEME_TREEHEADERCELL: {
1193 aPart = 1;
1194 if (!aFrame) {
1195 aState = TS_NORMAL;
1196 return NS_OK;
1197 }
1198
1199 aState = StandardGetState(aFrame, aWidgetType, true);
1200
1201 return NS_OK;
1202 }
1203 case NS_THEME_MENULIST: {
1204 nsIContent* content = aFrame->GetContent();
1205 bool isHTML = content && content->IsHTMLElement();
1206 bool isChrome = aFrame->GetContent()->IsInChromeDocument();
1207 bool useDropBorder = isHTML || (isChrome && IsMenuListEditable(aFrame));
1208 EventStates eventState = GetContentState(aFrame, aWidgetType);
1209
1210 /* On Vista/Win7, we use CBP_DROPBORDER instead of DROPFRAME for HTML
1211 * content or for editable menulists; this gives us the thin outline,
1212 * instead of the gradient-filled background */
1213 if (useDropBorder)
1214 aPart = CBP_DROPBORDER;
1215 else
1216 aPart = CBP_DROPFRAME;
1217
1218 if (IsDisabled(aFrame, eventState)) {
1219 aState = TS_DISABLED;
1220 } else if (IsReadOnly(aFrame)) {
1221 aState = TS_NORMAL;
1222 } else if (IsOpenButton(aFrame)) {
1223 aState = TS_ACTIVE;
1224 } else {
1225 if (useDropBorder &&
1226 (eventState.HasState(NS_EVENT_STATE_FOCUS) || IsFocused(aFrame)))
1227 aState = TS_ACTIVE;
1228 else if (eventState.HasAllStates(NS_EVENT_STATE_HOVER |
1229 NS_EVENT_STATE_ACTIVE))
1230 aState = TS_ACTIVE;
1231 else if (eventState.HasState(NS_EVENT_STATE_HOVER))
1232 aState = TS_HOVER;
1233 else
1234 aState = TS_NORMAL;
1235 }
1236
1237 return NS_OK;
1238 }
1239 case NS_THEME_MENULIST_BUTTON: {
1240 bool isHTML = IsHTMLContent(aFrame);
1241 nsIFrame* parentFrame = aFrame->GetParent();
1242 bool isMenulist = !isHTML && parentFrame->IsMenuFrame();
1243 bool isOpen = false;
1244
1245 // HTML select and XUL menulist dropdown buttons get state from the
1246 // parent.
1247 if (isHTML || isMenulist) aFrame = parentFrame;
1248
1249 EventStates eventState = GetContentState(aFrame, aWidgetType);
1250 aPart = CBP_DROPMARKER_VISTA;
1251
1252 // For HTML controls with author styling, we should fall
1253 // back to the old dropmarker style to avoid clashes with
1254 // author-specified backgrounds and borders (bug #441034)
1255 if (isHTML &&
1256 IsWidgetStyled(aFrame->PresContext(), aFrame, NS_THEME_MENULIST))
1257 aPart = CBP_DROPMARKER;
1258
1259 if (IsDisabled(aFrame, eventState)) {
1260 aState = TS_DISABLED;
1261 return NS_OK;
1262 }
1263
1264 if (isHTML) {
1265 nsIComboboxControlFrame* ccf = do_QueryFrame(aFrame);
1266 isOpen = (ccf && ccf->IsDroppedDownOrHasParentPopup());
1267 } else
1268 isOpen = IsOpenButton(aFrame);
1269
1270 bool isChrome = aFrame->GetContent()->IsInChromeDocument();
1271 if (isHTML || (isChrome && IsMenuListEditable(aFrame))) {
1272 if (isOpen) {
1273 /* Hover is propagated, but we need to know whether we're hovering
1274 * just the combobox frame, not the dropdown frame. But, we can't get
1275 * that information, since hover is on the content node, and they
1276 * share the same content node. So, instead, we cheat -- if the
1277 * dropdown is open, we always show the hover state. This looks fine
1278 * in practice.
1279 */
1280 aState = TS_HOVER;
1281 return NS_OK;
1282 }
1283 } else {
1284 /* The dropdown indicator on a menulist button in chrome is not given a
1285 * hover effect. When the frame isn't isn't HTML content, we cheat and
1286 * force the dropdown state to be normal. (Bug 430434)
1287 */
1288 aState = TS_NORMAL;
1289 return NS_OK;
1290 }
1291
1292 aState = TS_NORMAL;
1293
1294 // Dropdown button active state doesn't need :hover.
1295 if (eventState.HasState(NS_EVENT_STATE_ACTIVE)) {
1296 if (isOpen && (isHTML || isMenulist)) {
1297 // XXX Button should look active until the mouse is released, but
1298 // without making it look active when the popup is clicked.
1299 return NS_OK;
1300 }
1301 aState = TS_ACTIVE;
1302 } else if (eventState.HasState(NS_EVENT_STATE_HOVER)) {
1303 // No hover effect for XUL menulists and autocomplete dropdown buttons
1304 // while the dropdown menu is open.
1305 if (isOpen) {
1306 // XXX HTML select dropdown buttons should have the hover effect when
1307 // hovering the combobox frame, but not the popup frame.
1308 return NS_OK;
1309 }
1310 aState = TS_HOVER;
1311 }
1312 return NS_OK;
1313 }
1314 case NS_THEME_MENUPOPUP: {
1315 aPart = MENU_POPUPBACKGROUND;
1316 aState = MB_ACTIVE;
1317 return NS_OK;
1318 }
1319 case NS_THEME_MENUITEM:
1320 case NS_THEME_CHECKMENUITEM:
1321 case NS_THEME_RADIOMENUITEM: {
1322 bool isTopLevel = false;
1323 bool isOpen = false;
1324 bool isHover = false;
1325 nsMenuFrame* menuFrame = do_QueryFrame(aFrame);
1326 EventStates eventState = GetContentState(aFrame, aWidgetType);
1327
1328 isTopLevel = IsTopLevelMenu(aFrame);
1329
1330 if (menuFrame) isOpen = menuFrame->IsOpen();
1331
1332 isHover = IsMenuActive(aFrame, aWidgetType);
1333
1334 if (isTopLevel) {
1335 aPart = MENU_BARITEM;
1336
1337 if (isOpen)
1338 aState = MBI_PUSHED;
1339 else if (isHover)
1340 aState = MBI_HOT;
1341 else
1342 aState = MBI_NORMAL;
1343
1344 // the disabled states are offset by 3
1345 if (IsDisabled(aFrame, eventState)) aState += 3;
1346 } else {
1347 aPart = MENU_POPUPITEM;
1348
1349 if (isHover)
1350 aState = MPI_HOT;
1351 else
1352 aState = MPI_NORMAL;
1353
1354 // the disabled states are offset by 2
1355 if (IsDisabled(aFrame, eventState)) aState += 2;
1356 }
1357
1358 return NS_OK;
1359 }
1360 case NS_THEME_MENUSEPARATOR:
1361 aPart = MENU_POPUPSEPARATOR;
1362 aState = 0;
1363 return NS_OK;
1364 case NS_THEME_MENUARROW: {
1365 aPart = MENU_POPUPSUBMENU;
1366 EventStates eventState = GetContentState(aFrame, aWidgetType);
1367 aState = IsDisabled(aFrame, eventState) ? MSM_DISABLED : MSM_NORMAL;
1368 return NS_OK;
1369 }
1370 case NS_THEME_MENUCHECKBOX:
1371 case NS_THEME_MENURADIO: {
1372 EventStates eventState = GetContentState(aFrame, aWidgetType);
1373
1374 aPart = MENU_POPUPCHECK;
1375 aState = MC_CHECKMARKNORMAL;
1376
1377 // Radio states are offset by 2
1378 if (aWidgetType == NS_THEME_MENURADIO) aState += 2;
1379
1380 // the disabled states are offset by 1
1381 if (IsDisabled(aFrame, eventState)) aState += 1;
1382
1383 return NS_OK;
1384 }
1385 case NS_THEME_MENUITEMTEXT:
1386 case NS_THEME_MENUIMAGE:
1387 aPart = -1;
1388 aState = 0;
1389 return NS_OK;
1390
1391 case NS_THEME_WINDOW_TITLEBAR:
1392 aPart = mozilla::widget::themeconst::WP_CAPTION;
1393 aState = GetTopLevelWindowActiveState(aFrame);
1394 return NS_OK;
1395 case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
1396 aPart = mozilla::widget::themeconst::WP_MAXCAPTION;
1397 aState = GetTopLevelWindowActiveState(aFrame);
1398 return NS_OK;
1399 case NS_THEME_WINDOW_FRAME_LEFT:
1400 aPart = mozilla::widget::themeconst::WP_FRAMELEFT;
1401 aState = GetTopLevelWindowActiveState(aFrame);
1402 return NS_OK;
1403 case NS_THEME_WINDOW_FRAME_RIGHT:
1404 aPart = mozilla::widget::themeconst::WP_FRAMERIGHT;
1405 aState = GetTopLevelWindowActiveState(aFrame);
1406 return NS_OK;
1407 case NS_THEME_WINDOW_FRAME_BOTTOM:
1408 aPart = mozilla::widget::themeconst::WP_FRAMEBOTTOM;
1409 aState = GetTopLevelWindowActiveState(aFrame);
1410 return NS_OK;
1411 case NS_THEME_WINDOW_BUTTON_CLOSE:
1412 aPart = mozilla::widget::themeconst::WP_CLOSEBUTTON;
1413 aState = GetWindowFrameButtonState(aFrame,
1414 GetContentState(aFrame, aWidgetType));
1415 return NS_OK;
1416 case NS_THEME_WINDOW_BUTTON_MINIMIZE:
1417 aPart = mozilla::widget::themeconst::WP_MINBUTTON;
1418 aState = GetWindowFrameButtonState(aFrame,
1419 GetContentState(aFrame, aWidgetType));
1420 return NS_OK;
1421 case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
1422 aPart = mozilla::widget::themeconst::WP_MAXBUTTON;
1423 aState = GetWindowFrameButtonState(aFrame,
1424 GetContentState(aFrame, aWidgetType));
1425 return NS_OK;
1426 case NS_THEME_WINDOW_BUTTON_RESTORE:
1427 aPart = mozilla::widget::themeconst::WP_RESTOREBUTTON;
1428 aState = GetWindowFrameButtonState(aFrame,
1429 GetContentState(aFrame, aWidgetType));
1430 return NS_OK;
1431 case NS_THEME_WINDOW_BUTTON_BOX:
1432 case NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED:
1433 case NS_THEME_WIN_GLASS:
1434 case NS_THEME_WIN_BORDERLESS_GLASS:
1435 aPart = -1;
1436 aState = 0;
1437 return NS_OK;
1438 }
1439
1440 aPart = 0;
1441 aState = 0;
1442 return NS_ERROR_FAILURE;
1443 }
1444
AssumeThemePartAndStateAreTransparent(int32_t aPart,int32_t aState)1445 static bool AssumeThemePartAndStateAreTransparent(int32_t aPart,
1446 int32_t aState) {
1447 if (!(IsWin8Point1OrLater() && nsUXThemeData::IsHighContrastOn()) &&
1448 aPart == MENU_POPUPITEM && aState == MBI_NORMAL) {
1449 return true;
1450 }
1451 return false;
1452 }
1453
1454 // When running with per-monitor DPI (on Win8.1+), and rendering on a display
1455 // with a different DPI setting from the system's default scaling, we need to
1456 // apply scaling to native-themed elements as the Windows theme APIs assume
1457 // the system default resolution.
GetThemeDpiScaleFactor(nsIFrame * aFrame)1458 static inline double GetThemeDpiScaleFactor(nsIFrame* aFrame) {
1459 if (WinUtils::IsPerMonitorDPIAware() ||
1460 nsIWidget::DefaultScaleOverride() > 0.0) {
1461 nsIWidget* rootWidget = aFrame->PresContext()->GetRootWidget();
1462 if (rootWidget) {
1463 double systemScale = WinUtils::SystemScaleFactor();
1464 return rootWidget->GetDefaultScale().scale / systemScale;
1465 }
1466 }
1467 return 1.0;
1468 }
1469
1470 NS_IMETHODIMP
DrawWidgetBackground(gfxContext * aContext,nsIFrame * aFrame,uint8_t aWidgetType,const nsRect & aRect,const nsRect & aDirtyRect)1471 nsNativeThemeWin::DrawWidgetBackground(gfxContext* aContext, nsIFrame* aFrame,
1472 uint8_t aWidgetType, const nsRect& aRect,
1473 const nsRect& aDirtyRect) {
1474 HANDLE theme = GetTheme(aWidgetType);
1475 if (!theme)
1476 return ClassicDrawWidgetBackground(aContext, aFrame, aWidgetType, aRect,
1477 aDirtyRect);
1478
1479 // ^^ without the right sdk, assume xp theming and fall through.
1480 if (nsUXThemeData::CheckForCompositor()) {
1481 switch (aWidgetType) {
1482 case NS_THEME_WINDOW_TITLEBAR:
1483 case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
1484 case NS_THEME_WINDOW_FRAME_LEFT:
1485 case NS_THEME_WINDOW_FRAME_RIGHT:
1486 case NS_THEME_WINDOW_FRAME_BOTTOM:
1487 // Nothing to draw, these areas are glass. Minimum dimensions
1488 // should be set, so xul content should be layed out correctly.
1489 return NS_OK;
1490 break;
1491 case NS_THEME_WINDOW_BUTTON_CLOSE:
1492 case NS_THEME_WINDOW_BUTTON_MINIMIZE:
1493 case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
1494 case NS_THEME_WINDOW_BUTTON_RESTORE:
1495 // Not conventional bitmaps, can't be retrieved. If we fall
1496 // through here and call the theme library we'll get aero
1497 // basic bitmaps.
1498 return NS_OK;
1499 break;
1500 case NS_THEME_WIN_GLASS:
1501 case NS_THEME_WIN_BORDERLESS_GLASS:
1502 // Nothing to draw, this is the glass background.
1503 return NS_OK;
1504 case NS_THEME_WINDOW_BUTTON_BOX:
1505 case NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED:
1506 // We handle these through nsIWidget::UpdateThemeGeometries
1507 return NS_OK;
1508 break;
1509 }
1510 }
1511
1512 int32_t part, state;
1513 nsresult rv = GetThemePartAndState(aFrame, aWidgetType, part, state);
1514 if (NS_FAILED(rv)) return rv;
1515
1516 if (AssumeThemePartAndStateAreTransparent(part, state)) {
1517 return NS_OK;
1518 }
1519
1520 RefPtr<gfxContext> ctx = aContext;
1521 gfxContextMatrixAutoSaveRestore save(ctx);
1522
1523 double themeScale = GetThemeDpiScaleFactor(aFrame);
1524 if (themeScale != 1.0) {
1525 ctx->SetMatrix(ctx->CurrentMatrix().PreScale(themeScale, themeScale));
1526 }
1527
1528 gfxFloat p2a = gfxFloat(aFrame->PresContext()->AppUnitsPerDevPixel());
1529 RECT widgetRect;
1530 RECT clipRect;
1531 gfxRect tr(aRect.X(), aRect.Y(), aRect.Width(), aRect.Height()),
1532 dr(aDirtyRect.X(), aDirtyRect.Y(), aDirtyRect.Width(),
1533 aDirtyRect.Height());
1534
1535 tr.Scale(1.0 / (p2a * themeScale));
1536 dr.Scale(1.0 / (p2a * themeScale));
1537
1538 gfxWindowsNativeDrawing nativeDrawing(
1539 ctx, dr, GetWidgetNativeDrawingFlags(aWidgetType));
1540
1541 RENDER_AGAIN:
1542
1543 HDC hdc = nativeDrawing.BeginNativeDrawing();
1544 if (!hdc) return NS_ERROR_FAILURE;
1545
1546 nativeDrawing.TransformToNativeRect(tr, widgetRect);
1547 nativeDrawing.TransformToNativeRect(dr, clipRect);
1548
1549 #if 0
1550 {
1551 MOZ_LOG(gWindowsLog, LogLevel::Error,
1552 (stderr, "xform: %f %f %f %f [%f %f]\n", m._11, m._21, m._12, m._22,
1553 m._31, m._32));
1554 MOZ_LOG(gWindowsLog, LogLevel::Error,
1555 (stderr, "tr: [%d %d %d %d]\ndr: [%d %d %d %d]\noff: [%f %f]\n",
1556 tr.x, tr.y, tr.width, tr.height, dr.x, dr.y, dr.width, dr.height,
1557 offset.x, offset.y));
1558 }
1559 #endif
1560
1561 if (aWidgetType == NS_THEME_WINDOW_TITLEBAR) {
1562 // Clip out the left and right corners of the frame, all we want in
1563 // is the middle section.
1564 widgetRect.left -= GetSystemMetrics(SM_CXFRAME);
1565 widgetRect.right += GetSystemMetrics(SM_CXFRAME);
1566 } else if (aWidgetType == NS_THEME_WINDOW_TITLEBAR_MAXIMIZED) {
1567 // The origin of the window is off screen when maximized and windows
1568 // doesn't compensate for this in rendering the background. Push the
1569 // top of the bitmap down by SM_CYFRAME so we get the full graphic.
1570 widgetRect.top += GetSystemMetrics(SM_CYFRAME);
1571 } else if (aWidgetType == NS_THEME_TAB) {
1572 // For left edge and right edge tabs, we need to adjust the widget
1573 // rects and clip rects so that the edges don't get drawn.
1574 bool isLeft = IsLeftToSelectedTab(aFrame);
1575 bool isRight = !isLeft && IsRightToSelectedTab(aFrame);
1576
1577 if (isLeft || isRight) {
1578 // HACK ALERT: There appears to be no way to really obtain this value, so
1579 // we're forced to just use the default value for Luna (which also happens
1580 // to be correct for all the other skins I've tried).
1581 int32_t edgeSize = 2;
1582
1583 // Armed with the size of the edge, we now need to either shift to the
1584 // left or to the right. The clip rect won't include this extra area, so
1585 // we know that we're effectively shifting the edge out of view (such that
1586 // it won't be painted).
1587 if (isLeft)
1588 // The right edge should not be drawn. Extend our rect by the edge
1589 // size.
1590 widgetRect.right += edgeSize;
1591 else
1592 // The left edge should not be drawn. Move the widget rect's left coord
1593 // back.
1594 widgetRect.left -= edgeSize;
1595 }
1596 } else if (aWidgetType == NS_THEME_WINDOW_BUTTON_MINIMIZE) {
1597 OffsetBackgroundRect(widgetRect, CAPTIONBUTTON_MINIMIZE);
1598 } else if (aWidgetType == NS_THEME_WINDOW_BUTTON_MAXIMIZE ||
1599 aWidgetType == NS_THEME_WINDOW_BUTTON_RESTORE) {
1600 OffsetBackgroundRect(widgetRect, CAPTIONBUTTON_RESTORE);
1601 } else if (aWidgetType == NS_THEME_WINDOW_BUTTON_CLOSE) {
1602 OffsetBackgroundRect(widgetRect, CAPTIONBUTTON_CLOSE);
1603 }
1604
1605 // widgetRect is the bounding box for a widget, yet the scale track is only
1606 // a small portion of this size, so the edges of the scale need to be
1607 // adjusted to the real size of the track.
1608 if (aWidgetType == NS_THEME_RANGE ||
1609 aWidgetType == NS_THEME_SCALE_HORIZONTAL ||
1610 aWidgetType == NS_THEME_SCALE_VERTICAL) {
1611 RECT contentRect;
1612 GetThemeBackgroundContentRect(theme, hdc, part, state, &widgetRect,
1613 &contentRect);
1614
1615 SIZE siz;
1616 GetThemePartSize(theme, hdc, part, state, &widgetRect, TS_TRUE, &siz);
1617
1618 // When rounding is necessary, we round the position of the track
1619 // away from the chevron of the thumb to make it look better.
1620 if (aWidgetType == NS_THEME_SCALE_HORIZONTAL ||
1621 (aWidgetType == NS_THEME_RANGE && IsRangeHorizontal(aFrame))) {
1622 contentRect.top += (contentRect.bottom - contentRect.top - siz.cy) / 2;
1623 contentRect.bottom = contentRect.top + siz.cy;
1624 } else {
1625 if (!IsFrameRTL(aFrame)) {
1626 contentRect.left += (contentRect.right - contentRect.left - siz.cx) / 2;
1627 contentRect.right = contentRect.left + siz.cx;
1628 } else {
1629 contentRect.right -=
1630 (contentRect.right - contentRect.left - siz.cx) / 2;
1631 contentRect.left = contentRect.right - siz.cx;
1632 }
1633 }
1634
1635 DrawThemeBackground(theme, hdc, part, state, &contentRect, &clipRect);
1636 } else if (aWidgetType == NS_THEME_MENUCHECKBOX ||
1637 aWidgetType == NS_THEME_MENURADIO) {
1638 bool isChecked = false;
1639 isChecked = CheckBooleanAttr(aFrame, nsGkAtoms::checked);
1640
1641 if (isChecked) {
1642 int bgState = MCB_NORMAL;
1643 EventStates eventState = GetContentState(aFrame, aWidgetType);
1644
1645 // the disabled states are offset by 1
1646 if (IsDisabled(aFrame, eventState)) bgState += 1;
1647
1648 SIZE checkboxBGSize(GetCheckboxBGSize(theme, hdc));
1649
1650 RECT checkBGRect = widgetRect;
1651 if (IsFrameRTL(aFrame)) {
1652 checkBGRect.left = checkBGRect.right - checkboxBGSize.cx;
1653 } else {
1654 checkBGRect.right = checkBGRect.left + checkboxBGSize.cx;
1655 }
1656
1657 // Center the checkbox background vertically in the menuitem
1658 checkBGRect.top +=
1659 (checkBGRect.bottom - checkBGRect.top) / 2 - checkboxBGSize.cy / 2;
1660 checkBGRect.bottom = checkBGRect.top + checkboxBGSize.cy;
1661
1662 DrawThemeBackground(theme, hdc, MENU_POPUPCHECKBACKGROUND, bgState,
1663 &checkBGRect, &clipRect);
1664
1665 MARGINS checkMargins = GetCheckboxMargins(theme, hdc);
1666 RECT checkRect = checkBGRect;
1667 checkRect.left += checkMargins.cxLeftWidth;
1668 checkRect.right -= checkMargins.cxRightWidth;
1669 checkRect.top += checkMargins.cyTopHeight;
1670 checkRect.bottom -= checkMargins.cyBottomHeight;
1671 DrawThemeBackground(theme, hdc, MENU_POPUPCHECK, state, &checkRect,
1672 &clipRect);
1673 }
1674 } else if (aWidgetType == NS_THEME_MENUPOPUP) {
1675 DrawThemeBackground(theme, hdc, MENU_POPUPBORDERS, /* state */ 0,
1676 &widgetRect, &clipRect);
1677 SIZE borderSize;
1678 GetThemePartSize(theme, hdc, MENU_POPUPBORDERS, 0, nullptr, TS_TRUE,
1679 &borderSize);
1680
1681 RECT bgRect = widgetRect;
1682 bgRect.top += borderSize.cy;
1683 bgRect.bottom -= borderSize.cy;
1684 bgRect.left += borderSize.cx;
1685 bgRect.right -= borderSize.cx;
1686
1687 DrawThemeBackground(theme, hdc, MENU_POPUPBACKGROUND, /* state */ 0,
1688 &bgRect, &clipRect);
1689
1690 SIZE gutterSize(GetGutterSize(theme, hdc));
1691
1692 RECT gutterRect;
1693 gutterRect.top = bgRect.top;
1694 gutterRect.bottom = bgRect.bottom;
1695 if (IsFrameRTL(aFrame)) {
1696 gutterRect.right = bgRect.right;
1697 gutterRect.left = gutterRect.right - gutterSize.cx;
1698 } else {
1699 gutterRect.left = bgRect.left;
1700 gutterRect.right = gutterRect.left + gutterSize.cx;
1701 }
1702
1703 DrawThemeBGRTLAware(theme, hdc, MENU_POPUPGUTTER, /* state */ 0,
1704 &gutterRect, &clipRect, IsFrameRTL(aFrame));
1705 } else if (aWidgetType == NS_THEME_MENUSEPARATOR) {
1706 SIZE gutterSize(GetGutterSize(theme, hdc));
1707
1708 RECT sepRect = widgetRect;
1709 if (IsFrameRTL(aFrame))
1710 sepRect.right -= gutterSize.cx;
1711 else
1712 sepRect.left += gutterSize.cx;
1713
1714 DrawThemeBackground(theme, hdc, MENU_POPUPSEPARATOR, /* state */ 0,
1715 &sepRect, &clipRect);
1716 } else if (aWidgetType == NS_THEME_MENUARROW) {
1717 // We're dpi aware and as such on systems that have dpi > 96 set, the
1718 // theme library expects us to do proper positioning and scaling of glyphs.
1719 // For NS_THEME_MENUARROW, layout may hand us a widget rect larger than the
1720 // glyph rect we request in GetMinimumWidgetSize. To prevent distortion we
1721 // have to position and scale what we draw.
1722
1723 SIZE glyphSize;
1724 GetThemePartSize(theme, hdc, part, state, nullptr, TS_TRUE, &glyphSize);
1725
1726 int32_t widgetHeight = widgetRect.bottom - widgetRect.top;
1727
1728 RECT renderRect = widgetRect;
1729
1730 // We request (glyph width * 2, glyph height) in GetMinimumWidgetSize. In
1731 // Firefox some menu items provide the full height of the item to us, in
1732 // others our widget rect is the exact dims of our arrow glyph. Adjust the
1733 // vertical position by the added space, if any exists.
1734 renderRect.top += ((widgetHeight - glyphSize.cy) / 2);
1735 renderRect.bottom = renderRect.top + glyphSize.cy;
1736 // I'm using the width of the arrow glyph for the arrow-side padding.
1737 // AFAICT there doesn't appear to be a theme constant we can query
1738 // for this value. Generally this looks correct, and has the added
1739 // benefit of being a dpi adjusted value.
1740 if (!IsFrameRTL(aFrame)) {
1741 renderRect.right = widgetRect.right - glyphSize.cx;
1742 renderRect.left = renderRect.right - glyphSize.cx;
1743 } else {
1744 renderRect.left = glyphSize.cx;
1745 renderRect.right = renderRect.left + glyphSize.cx;
1746 }
1747 DrawThemeBGRTLAware(theme, hdc, part, state, &renderRect, &clipRect,
1748 IsFrameRTL(aFrame));
1749 }
1750 // The following widgets need to be RTL-aware
1751 else if (aWidgetType == NS_THEME_RESIZER ||
1752 aWidgetType == NS_THEME_MENULIST_BUTTON) {
1753 DrawThemeBGRTLAware(theme, hdc, part, state, &widgetRect, &clipRect,
1754 IsFrameRTL(aFrame));
1755 } else if (aWidgetType == NS_THEME_NUMBER_INPUT ||
1756 aWidgetType == NS_THEME_TEXTFIELD ||
1757 aWidgetType == NS_THEME_TEXTFIELD_MULTILINE) {
1758 DrawThemeBackground(theme, hdc, part, state, &widgetRect, &clipRect);
1759 if (state == TFS_EDITBORDER_DISABLED) {
1760 InflateRect(&widgetRect, -1, -1);
1761 ::FillRect(hdc, &widgetRect, reinterpret_cast<HBRUSH>(COLOR_BTNFACE + 1));
1762 }
1763 } else if (aWidgetType == NS_THEME_PROGRESSBAR ||
1764 aWidgetType == NS_THEME_PROGRESSBAR_VERTICAL) {
1765 // DrawThemeBackground renders each corner with a solid white pixel.
1766 // Restore these pixels to the underlying color. Tracks are rendered
1767 // using alpha recovery, so this makes the corners transparent.
1768 COLORREF color;
1769 color = GetPixel(hdc, widgetRect.left, widgetRect.top);
1770 DrawThemeBackground(theme, hdc, part, state, &widgetRect, &clipRect);
1771 SetPixel(hdc, widgetRect.left, widgetRect.top, color);
1772 SetPixel(hdc, widgetRect.right - 1, widgetRect.top, color);
1773 SetPixel(hdc, widgetRect.right - 1, widgetRect.bottom - 1, color);
1774 SetPixel(hdc, widgetRect.left, widgetRect.bottom - 1, color);
1775 } else if (aWidgetType == NS_THEME_PROGRESSCHUNK ||
1776 aWidgetType == NS_THEME_PROGRESSCHUNK_VERTICAL) {
1777 DrawThemedProgressMeter(aFrame, aWidgetType, theme, hdc, part, state,
1778 &widgetRect, &clipRect);
1779 } else if (aWidgetType == NS_THEME_FOCUS_OUTLINE) {
1780 // Inflate 'widgetRect' with the focus outline size.
1781 nsIntMargin border;
1782 if (NS_SUCCEEDED(GetWidgetBorder(aFrame->PresContext()->DeviceContext(),
1783 aFrame, aWidgetType, &border))) {
1784 widgetRect.left -= border.left;
1785 widgetRect.right += border.right;
1786 widgetRect.top -= border.top;
1787 widgetRect.bottom += border.bottom;
1788 }
1789
1790 DTBGOPTS opts = {sizeof(DTBGOPTS), DTBG_OMITCONTENT | DTBG_CLIPRECT,
1791 clipRect};
1792 DrawThemeBackgroundEx(theme, hdc, part, state, &widgetRect, &opts);
1793 }
1794 // If part is negative, the element wishes us to not render a themed
1795 // background, instead opting to be drawn specially below.
1796 else if (part >= 0) {
1797 DrawThemeBackground(theme, hdc, part, state, &widgetRect, &clipRect);
1798 }
1799
1800 // Draw focus rectangles for range and scale elements
1801 // XXX it'd be nice to draw these outside of the frame
1802 if (aWidgetType == NS_THEME_RANGE ||
1803 aWidgetType == NS_THEME_SCALE_HORIZONTAL ||
1804 aWidgetType == NS_THEME_SCALE_VERTICAL) {
1805 EventStates contentState = GetContentState(aFrame, aWidgetType);
1806
1807 if (contentState.HasState(NS_EVENT_STATE_FOCUS)) {
1808 POINT vpOrg;
1809 HPEN hPen = nullptr;
1810
1811 uint8_t id = SaveDC(hdc);
1812
1813 ::SelectClipRgn(hdc, nullptr);
1814 ::GetViewportOrgEx(hdc, &vpOrg);
1815 ::SetBrushOrgEx(hdc, vpOrg.x + widgetRect.left, vpOrg.y + widgetRect.top,
1816 nullptr);
1817 ::SetTextColor(hdc, 0);
1818 ::DrawFocusRect(hdc, &widgetRect);
1819 ::RestoreDC(hdc, id);
1820 if (hPen) {
1821 ::DeleteObject(hPen);
1822 }
1823 }
1824 } else if (aWidgetType == NS_THEME_TOOLBAR && state == 0) {
1825 // Draw toolbar separator lines above all toolbars except the first one.
1826 // The lines are part of the Rebar theme, which is loaded for
1827 // NS_THEME_TOOLBOX.
1828 theme = GetTheme(NS_THEME_TOOLBOX);
1829 if (!theme) return NS_ERROR_FAILURE;
1830
1831 widgetRect.bottom = widgetRect.top + TB_SEPARATOR_HEIGHT;
1832 DrawThemeEdge(theme, hdc, RP_BAND, 0, &widgetRect, EDGE_ETCHED, BF_TOP,
1833 nullptr);
1834 } else if (aWidgetType == NS_THEME_SCROLLBARTHUMB_HORIZONTAL ||
1835 aWidgetType == NS_THEME_SCROLLBARTHUMB_VERTICAL) {
1836 // Draw the decorative gripper for the scrollbar thumb button, if it fits
1837
1838 SIZE gripSize;
1839 MARGINS thumbMgns;
1840 int gripPart = (aWidgetType == NS_THEME_SCROLLBARTHUMB_HORIZONTAL)
1841 ? SP_GRIPPERHOR
1842 : SP_GRIPPERVERT;
1843
1844 if (GetThemePartSize(theme, hdc, gripPart, state, nullptr, TS_TRUE,
1845 &gripSize) == S_OK &&
1846 GetThemeMargins(theme, hdc, part, state, TMT_CONTENTMARGINS, nullptr,
1847 &thumbMgns) == S_OK &&
1848 gripSize.cx + thumbMgns.cxLeftWidth + thumbMgns.cxRightWidth <=
1849 widgetRect.right - widgetRect.left &&
1850 gripSize.cy + thumbMgns.cyTopHeight + thumbMgns.cyBottomHeight <=
1851 widgetRect.bottom - widgetRect.top) {
1852 DrawThemeBackground(theme, hdc, gripPart, state, &widgetRect, &clipRect);
1853 }
1854 }
1855
1856 nativeDrawing.EndNativeDrawing();
1857
1858 if (nativeDrawing.ShouldRenderAgain()) goto RENDER_AGAIN;
1859
1860 nativeDrawing.PaintToContext();
1861
1862 return NS_OK;
1863 }
1864
ScaleForFrameDPI(nsIntMargin * aMargin,nsIFrame * aFrame)1865 static void ScaleForFrameDPI(nsIntMargin* aMargin, nsIFrame* aFrame) {
1866 double themeScale = GetThemeDpiScaleFactor(aFrame);
1867 if (themeScale != 1.0) {
1868 aMargin->top = NSToIntRound(aMargin->top * themeScale);
1869 aMargin->left = NSToIntRound(aMargin->left * themeScale);
1870 aMargin->bottom = NSToIntRound(aMargin->bottom * themeScale);
1871 aMargin->right = NSToIntRound(aMargin->right * themeScale);
1872 }
1873 }
1874
ScaleForFrameDPI(LayoutDeviceIntSize * aSize,nsIFrame * aFrame)1875 static void ScaleForFrameDPI(LayoutDeviceIntSize* aSize, nsIFrame* aFrame) {
1876 double themeScale = GetThemeDpiScaleFactor(aFrame);
1877 if (themeScale != 1.0) {
1878 aSize->width = NSToIntRound(aSize->width * themeScale);
1879 aSize->height = NSToIntRound(aSize->height * themeScale);
1880 }
1881 }
1882
1883 NS_IMETHODIMP
GetWidgetBorder(nsDeviceContext * aContext,nsIFrame * aFrame,uint8_t aWidgetType,nsIntMargin * aResult)1884 nsNativeThemeWin::GetWidgetBorder(nsDeviceContext* aContext, nsIFrame* aFrame,
1885 uint8_t aWidgetType, nsIntMargin* aResult) {
1886 mozilla::Maybe<nsUXThemeClass> themeClass = GetThemeClass(aWidgetType);
1887 HTHEME theme = NULL;
1888 if (!themeClass.isNothing()) {
1889 theme = nsUXThemeData::GetTheme(themeClass.value());
1890 }
1891 nsresult rv = NS_OK;
1892 if (!theme) {
1893 rv = ClassicGetWidgetBorder(aContext, aFrame, aWidgetType, aResult);
1894 ScaleForFrameDPI(aResult, aFrame);
1895 return rv;
1896 }
1897
1898 aResult->top = aResult->bottom = aResult->left = aResult->right = 0;
1899
1900 if (!WidgetIsContainer(aWidgetType) || aWidgetType == NS_THEME_TOOLBOX ||
1901 aWidgetType == NS_THEME_WIN_MEDIA_TOOLBOX ||
1902 aWidgetType == NS_THEME_WIN_COMMUNICATIONS_TOOLBOX ||
1903 aWidgetType == NS_THEME_WIN_BROWSERTABBAR_TOOLBOX ||
1904 aWidgetType == NS_THEME_STATUSBAR || aWidgetType == NS_THEME_RESIZER ||
1905 aWidgetType == NS_THEME_TABPANEL ||
1906 aWidgetType == NS_THEME_SCROLLBAR_HORIZONTAL ||
1907 aWidgetType == NS_THEME_SCROLLBAR_VERTICAL ||
1908 aWidgetType == NS_THEME_MENUITEM ||
1909 aWidgetType == NS_THEME_CHECKMENUITEM ||
1910 aWidgetType == NS_THEME_RADIOMENUITEM ||
1911 aWidgetType == NS_THEME_MENUPOPUP || aWidgetType == NS_THEME_MENUIMAGE ||
1912 aWidgetType == NS_THEME_MENUITEMTEXT ||
1913 aWidgetType == NS_THEME_SEPARATOR ||
1914 aWidgetType == NS_THEME_WINDOW_TITLEBAR ||
1915 aWidgetType == NS_THEME_WINDOW_TITLEBAR_MAXIMIZED ||
1916 aWidgetType == NS_THEME_WIN_GLASS ||
1917 aWidgetType == NS_THEME_WIN_BORDERLESS_GLASS)
1918 return NS_OK; // Don't worry about it.
1919
1920 int32_t part, state;
1921 rv = GetThemePartAndState(aFrame, aWidgetType, part, state);
1922 if (NS_FAILED(rv)) return rv;
1923
1924 if (aWidgetType == NS_THEME_TOOLBAR) {
1925 // make space for the separator line above all toolbars but the first
1926 if (state == 0) aResult->top = TB_SEPARATOR_HEIGHT;
1927 return NS_OK;
1928 }
1929
1930 rv = GetCachedWidgetBorder(aFrame, theme, themeClass.value(), aWidgetType,
1931 part, state, aResult);
1932 NS_ENSURE_SUCCESS(rv, rv);
1933
1934 // Remove the edges for tabs that are before or after the selected tab,
1935 if (aWidgetType == NS_THEME_TAB) {
1936 if (IsLeftToSelectedTab(aFrame))
1937 // Remove the right edge, since we won't be drawing it.
1938 aResult->right = 0;
1939 else if (IsRightToSelectedTab(aFrame))
1940 // Remove the left edge, since we won't be drawing it.
1941 aResult->left = 0;
1942 }
1943
1944 if (aFrame && (aWidgetType == NS_THEME_NUMBER_INPUT ||
1945 aWidgetType == NS_THEME_TEXTFIELD ||
1946 aWidgetType == NS_THEME_TEXTFIELD_MULTILINE)) {
1947 nsIContent* content = aFrame->GetContent();
1948 if (content && content->IsHTMLElement()) {
1949 // We need to pad textfields by 1 pixel, since the caret will draw
1950 // flush against the edge by default if we don't.
1951 aResult->top++;
1952 aResult->left++;
1953 aResult->bottom++;
1954 aResult->right++;
1955 }
1956 }
1957
1958 ScaleForFrameDPI(aResult, aFrame);
1959 return rv;
1960 }
1961
GetWidgetPadding(nsDeviceContext * aContext,nsIFrame * aFrame,uint8_t aWidgetType,nsIntMargin * aResult)1962 bool nsNativeThemeWin::GetWidgetPadding(nsDeviceContext* aContext,
1963 nsIFrame* aFrame, uint8_t aWidgetType,
1964 nsIntMargin* aResult) {
1965 switch (aWidgetType) {
1966 // Radios and checkboxes return a fixed size in GetMinimumWidgetSize
1967 // and have a meaningful baseline, so they can't have
1968 // author-specified padding.
1969 case NS_THEME_CHECKBOX:
1970 case NS_THEME_RADIO:
1971 aResult->SizeTo(0, 0, 0, 0);
1972 return true;
1973 }
1974
1975 bool ok = true;
1976
1977 if (aWidgetType == NS_THEME_WINDOW_BUTTON_BOX ||
1978 aWidgetType == NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED) {
1979 aResult->SizeTo(0, 0, 0, 0);
1980
1981 // aero glass doesn't display custom buttons
1982 if (nsUXThemeData::CheckForCompositor()) return true;
1983
1984 // button padding for standard windows
1985 if (aWidgetType == NS_THEME_WINDOW_BUTTON_BOX) {
1986 aResult->top = GetSystemMetrics(SM_CXFRAME);
1987 }
1988 ScaleForFrameDPI(aResult, aFrame);
1989 return ok;
1990 }
1991
1992 // Content padding
1993 if (aWidgetType == NS_THEME_WINDOW_TITLEBAR ||
1994 aWidgetType == NS_THEME_WINDOW_TITLEBAR_MAXIMIZED) {
1995 aResult->SizeTo(0, 0, 0, 0);
1996 // XXX Maximized windows have an offscreen offset equal to
1997 // the border padding. This should be addressed in nsWindow,
1998 // but currently can't be, see UpdateNonClientMargins.
1999 if (aWidgetType == NS_THEME_WINDOW_TITLEBAR_MAXIMIZED)
2000 aResult->top =
2001 GetSystemMetrics(SM_CXFRAME) + GetSystemMetrics(SM_CXPADDEDBORDER);
2002 return ok;
2003 }
2004
2005 HANDLE theme = GetTheme(aWidgetType);
2006 if (!theme) {
2007 ok = ClassicGetWidgetPadding(aContext, aFrame, aWidgetType, aResult);
2008 ScaleForFrameDPI(aResult, aFrame);
2009 return ok;
2010 }
2011
2012 if (aWidgetType == NS_THEME_MENUPOPUP) {
2013 SIZE popupSize;
2014 GetThemePartSize(theme, nullptr, MENU_POPUPBORDERS, /* state */ 0, nullptr,
2015 TS_TRUE, &popupSize);
2016 aResult->top = aResult->bottom = popupSize.cy;
2017 aResult->left = aResult->right = popupSize.cx;
2018 ScaleForFrameDPI(aResult, aFrame);
2019 return ok;
2020 }
2021
2022 if (aWidgetType == NS_THEME_NUMBER_INPUT ||
2023 aWidgetType == NS_THEME_TEXTFIELD ||
2024 aWidgetType == NS_THEME_TEXTFIELD_MULTILINE ||
2025 aWidgetType == NS_THEME_MENULIST) {
2026 // If we have author-specified padding for these elements, don't do the
2027 // fixups below.
2028 if (aFrame->PresContext()->HasAuthorSpecifiedRules(
2029 aFrame, NS_AUTHOR_SPECIFIED_PADDING))
2030 return false;
2031 }
2032
2033 /* textfields need extra pixels on all sides, otherwise they wrap their
2034 * content too tightly. The actual border is drawn 1px inside the specified
2035 * rectangle, so Gecko will end up making the contents look too small.
2036 * Instead, we add 2px padding for the contents and fix this. (Used to be 1px
2037 * added, see bug 430212)
2038 */
2039 if (aWidgetType == NS_THEME_NUMBER_INPUT ||
2040 aWidgetType == NS_THEME_TEXTFIELD ||
2041 aWidgetType == NS_THEME_TEXTFIELD_MULTILINE) {
2042 aResult->top = aResult->bottom = 2;
2043 aResult->left = aResult->right = 2;
2044 ScaleForFrameDPI(aResult, aFrame);
2045 return ok;
2046 } else if (IsHTMLContent(aFrame) && aWidgetType == NS_THEME_MENULIST) {
2047 /* For content menulist controls, we need an extra pixel so that we have
2048 * room to draw our focus rectangle stuff. Otherwise, the focus rect might
2049 * overlap the control's border.
2050 */
2051 aResult->top = aResult->bottom = 1;
2052 aResult->left = aResult->right = 1;
2053 ScaleForFrameDPI(aResult, aFrame);
2054 return ok;
2055 }
2056
2057 int32_t right, left, top, bottom;
2058 right = left = top = bottom = 0;
2059 switch (aWidgetType) {
2060 case NS_THEME_MENUIMAGE:
2061 right = 8;
2062 left = 3;
2063 break;
2064 case NS_THEME_MENUCHECKBOX:
2065 case NS_THEME_MENURADIO:
2066 right = 8;
2067 left = 0;
2068 break;
2069 case NS_THEME_MENUITEMTEXT:
2070 // There seem to be exactly 4 pixels from the edge
2071 // of the gutter to the text: 2px margin (CSS) + 2px padding (here)
2072 {
2073 SIZE size(GetGutterSize(theme, nullptr));
2074 left = size.cx + 2;
2075 }
2076 break;
2077 case NS_THEME_MENUSEPARATOR: {
2078 SIZE size(GetGutterSize(theme, nullptr));
2079 left = size.cx + 5;
2080 top = 10;
2081 bottom = 7;
2082 } break;
2083 default:
2084 return false;
2085 }
2086
2087 if (IsFrameRTL(aFrame)) {
2088 aResult->right = left;
2089 aResult->left = right;
2090 } else {
2091 aResult->right = right;
2092 aResult->left = left;
2093 }
2094
2095 ScaleForFrameDPI(aResult, aFrame);
2096 return ok;
2097 }
2098
GetWidgetOverflow(nsDeviceContext * aContext,nsIFrame * aFrame,uint8_t aWidgetType,nsRect * aOverflowRect)2099 bool nsNativeThemeWin::GetWidgetOverflow(nsDeviceContext* aContext,
2100 nsIFrame* aFrame, uint8_t aWidgetType,
2101 nsRect* aOverflowRect) {
2102 /* This is disabled for now, because it causes invalidation problems --
2103 * see bug 420381. The effect of not updating the overflow area is that
2104 * for dropdown buttons in content areas, there is a 1px border on 3 sides
2105 * where, if invalidated, the dropdown control probably won't be repainted.
2106 * This is fairly minor, as by default there is nothing in that area, and
2107 * a border only shows up if the widget is being hovered.
2108 */
2109 #if 0
2110 /* We explicitly draw dropdown buttons in HTML content 1px bigger up, right,
2111 * and bottom so that they overlap the dropdown's border like they're
2112 * supposed to.
2113 */
2114 if (aWidgetType == NS_THEME_MENULIST_BUTTON &&
2115 IsHTMLContent(aFrame) &&
2116 !IsWidgetStyled(aFrame->GetParent()->PresContext(),
2117 aFrame->GetParent(),
2118 NS_THEME_MENULIST))
2119 {
2120 int32_t p2a = aContext->AppUnitsPerDevPixel();
2121 /* Note: no overflow on the left */
2122 nsMargin m(p2a, p2a, p2a, 0);
2123 aOverflowRect->Inflate (m);
2124 return true;
2125 }
2126 #endif
2127
2128 if (aWidgetType == NS_THEME_FOCUS_OUTLINE) {
2129 nsIntMargin border;
2130 nsresult rv = GetWidgetBorder(aContext, aFrame, aWidgetType, &border);
2131 if (NS_SUCCEEDED(rv)) {
2132 int32_t p2a = aContext->AppUnitsPerDevPixel();
2133 nsMargin m(NSIntPixelsToAppUnits(border.top, p2a),
2134 NSIntPixelsToAppUnits(border.right, p2a),
2135 NSIntPixelsToAppUnits(border.bottom, p2a),
2136 NSIntPixelsToAppUnits(border.left, p2a));
2137 aOverflowRect->Inflate(m);
2138 return true;
2139 }
2140 }
2141
2142 return false;
2143 }
2144
2145 NS_IMETHODIMP
GetMinimumWidgetSize(nsPresContext * aPresContext,nsIFrame * aFrame,uint8_t aWidgetType,LayoutDeviceIntSize * aResult,bool * aIsOverridable)2146 nsNativeThemeWin::GetMinimumWidgetSize(nsPresContext* aPresContext,
2147 nsIFrame* aFrame, uint8_t aWidgetType,
2148 LayoutDeviceIntSize* aResult,
2149 bool* aIsOverridable) {
2150 aResult->width = aResult->height = 0;
2151 *aIsOverridable = true;
2152 nsresult rv = NS_OK;
2153
2154 mozilla::Maybe<nsUXThemeClass> themeClass = GetThemeClass(aWidgetType);
2155 HTHEME theme = NULL;
2156 if (!themeClass.isNothing()) {
2157 theme = nsUXThemeData::GetTheme(themeClass.value());
2158 }
2159 if (!theme) {
2160 rv = ClassicGetMinimumWidgetSize(aFrame, aWidgetType, aResult,
2161 aIsOverridable);
2162 ScaleForFrameDPI(aResult, aFrame);
2163 return rv;
2164 }
2165
2166 switch (aWidgetType) {
2167 case NS_THEME_GROUPBOX:
2168 case NS_THEME_NUMBER_INPUT:
2169 case NS_THEME_TEXTFIELD:
2170 case NS_THEME_TOOLBOX:
2171 case NS_THEME_WIN_MEDIA_TOOLBOX:
2172 case NS_THEME_WIN_COMMUNICATIONS_TOOLBOX:
2173 case NS_THEME_WIN_BROWSERTABBAR_TOOLBOX:
2174 case NS_THEME_TOOLBAR:
2175 case NS_THEME_STATUSBAR:
2176 case NS_THEME_PROGRESSCHUNK:
2177 case NS_THEME_PROGRESSCHUNK_VERTICAL:
2178 case NS_THEME_TABPANELS:
2179 case NS_THEME_TABPANEL:
2180 case NS_THEME_LISTBOX:
2181 case NS_THEME_TREEVIEW:
2182 case NS_THEME_MENUITEMTEXT:
2183 case NS_THEME_WIN_GLASS:
2184 case NS_THEME_WIN_BORDERLESS_GLASS:
2185 return NS_OK; // Don't worry about it.
2186 }
2187
2188 if (aWidgetType == NS_THEME_MENUITEM && IsTopLevelMenu(aFrame))
2189 return NS_OK; // Don't worry about it for top level menus
2190
2191 // Call GetSystemMetrics to determine size for WinXP scrollbars
2192 // (GetThemeSysSize API returns the optimal size for the theme, but
2193 // Windows appears to always use metrics when drawing standard scrollbars)
2194 THEMESIZE sizeReq = TS_TRUE; // Best-fit size
2195 switch (aWidgetType) {
2196 case NS_THEME_SCROLLBARTHUMB_VERTICAL:
2197 case NS_THEME_SCROLLBARTHUMB_HORIZONTAL:
2198 case NS_THEME_SCROLLBARBUTTON_UP:
2199 case NS_THEME_SCROLLBARBUTTON_DOWN:
2200 case NS_THEME_SCROLLBARBUTTON_LEFT:
2201 case NS_THEME_SCROLLBARBUTTON_RIGHT:
2202 case NS_THEME_SCROLLBAR_HORIZONTAL:
2203 case NS_THEME_SCROLLBAR_VERTICAL:
2204 case NS_THEME_MENULIST_BUTTON: {
2205 rv = ClassicGetMinimumWidgetSize(aFrame, aWidgetType, aResult,
2206 aIsOverridable);
2207 ScaleForFrameDPI(aResult, aFrame);
2208 return rv;
2209 }
2210 case NS_THEME_MENUITEM:
2211 case NS_THEME_CHECKMENUITEM:
2212 case NS_THEME_RADIOMENUITEM:
2213 if (!IsTopLevelMenu(aFrame)) {
2214 SIZE gutterSize(GetCachedGutterSize(theme));
2215 aResult->width = gutterSize.cx;
2216 aResult->height = gutterSize.cy;
2217 ScaleForFrameDPI(aResult, aFrame);
2218 return rv;
2219 }
2220 break;
2221
2222 case NS_THEME_MENUIMAGE:
2223 case NS_THEME_MENUCHECKBOX:
2224 case NS_THEME_MENURADIO: {
2225 SIZE boxSize(GetCachedGutterSize(theme));
2226 aResult->width = boxSize.cx + 2;
2227 aResult->height = boxSize.cy;
2228 *aIsOverridable = false;
2229 ScaleForFrameDPI(aResult, aFrame);
2230 return rv;
2231 }
2232
2233 case NS_THEME_MENUITEMTEXT:
2234 return NS_OK;
2235
2236 case NS_THEME_PROGRESSBAR:
2237 case NS_THEME_PROGRESSBAR_VERTICAL:
2238 // Best-fit size for progress meters is too large for most
2239 // themes. We want these widgets to be able to really shrink
2240 // down, so use the min-size request value (of 0).
2241 sizeReq = TS_MIN;
2242 break;
2243
2244 case NS_THEME_RESIZER:
2245 *aIsOverridable = false;
2246 break;
2247
2248 case NS_THEME_RANGE_THUMB:
2249 case NS_THEME_SCALETHUMB_HORIZONTAL:
2250 case NS_THEME_SCALETHUMB_VERTICAL: {
2251 *aIsOverridable = false;
2252 // On Vista, GetThemePartAndState returns odd values for
2253 // scale thumbs, so use a hardcoded size instead.
2254 if (aWidgetType == NS_THEME_SCALETHUMB_HORIZONTAL ||
2255 (aWidgetType == NS_THEME_RANGE_THUMB && IsRangeHorizontal(aFrame))) {
2256 aResult->width = 12;
2257 aResult->height = 20;
2258 } else {
2259 aResult->width = 20;
2260 aResult->height = 12;
2261 }
2262 ScaleForFrameDPI(aResult, aFrame);
2263 return rv;
2264 }
2265
2266 case NS_THEME_SCROLLBAR: {
2267 if (nsLookAndFeel::GetInt(nsLookAndFeel::eIntID_UseOverlayScrollbars) !=
2268 0) {
2269 aResult->SizeTo(::GetSystemMetrics(SM_CXHSCROLL),
2270 ::GetSystemMetrics(SM_CYVSCROLL));
2271 ScaleForFrameDPI(aResult, aFrame);
2272 return rv;
2273 }
2274 break;
2275 }
2276
2277 case NS_THEME_SEPARATOR:
2278 // that's 2px left margin, 2px right margin and 2px separator
2279 // (the margin is drawn as part of the separator, though)
2280 aResult->width = 6;
2281 ScaleForFrameDPI(aResult, aFrame);
2282 return rv;
2283
2284 case NS_THEME_BUTTON:
2285 // We should let HTML buttons shrink to their min size.
2286 // FIXME bug 403934: We should probably really separate
2287 // GetPreferredWidgetSize from GetMinimumWidgetSize, so callers can
2288 // use the one they want.
2289 if (aFrame->GetContent()->IsHTMLElement()) {
2290 sizeReq = TS_MIN;
2291 }
2292 break;
2293
2294 case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
2295 case NS_THEME_WINDOW_BUTTON_RESTORE:
2296 // The only way to get accurate titlebar button info is to query a
2297 // window w/buttons when it's visible. nsWindow takes care of this and
2298 // stores that info in nsUXThemeData.
2299 aResult->width =
2300 nsUXThemeData::GetCommandButtonMetrics(CMDBUTTONIDX_RESTORE).cx;
2301 aResult->height =
2302 nsUXThemeData::GetCommandButtonMetrics(CMDBUTTONIDX_RESTORE).cy;
2303 AddPaddingRect(aResult, CAPTIONBUTTON_RESTORE);
2304 *aIsOverridable = false;
2305 return rv;
2306
2307 case NS_THEME_WINDOW_BUTTON_MINIMIZE:
2308 aResult->width =
2309 nsUXThemeData::GetCommandButtonMetrics(CMDBUTTONIDX_MINIMIZE).cx;
2310 aResult->height =
2311 nsUXThemeData::GetCommandButtonMetrics(CMDBUTTONIDX_MINIMIZE).cy;
2312 AddPaddingRect(aResult, CAPTIONBUTTON_MINIMIZE);
2313 *aIsOverridable = false;
2314 return rv;
2315
2316 case NS_THEME_WINDOW_BUTTON_CLOSE:
2317 aResult->width =
2318 nsUXThemeData::GetCommandButtonMetrics(CMDBUTTONIDX_CLOSE).cx;
2319 aResult->height =
2320 nsUXThemeData::GetCommandButtonMetrics(CMDBUTTONIDX_CLOSE).cy;
2321 AddPaddingRect(aResult, CAPTIONBUTTON_CLOSE);
2322 *aIsOverridable = false;
2323 return rv;
2324
2325 case NS_THEME_WINDOW_TITLEBAR:
2326 case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
2327 aResult->height = GetSystemMetrics(SM_CYCAPTION);
2328 aResult->height += GetSystemMetrics(SM_CYFRAME);
2329 aResult->height += GetSystemMetrics(SM_CXPADDEDBORDER);
2330 // On Win8.1, we don't want this scaling, because Windows doesn't scale
2331 // the non-client area of the window, and we can end up with ugly overlap
2332 // of the window frame controls into the tab bar or content area. But on
2333 // Win10, we render the window controls ourselves, and the result looks
2334 // better if we do apply this scaling (particularly with themes such as
2335 // DevEdition; see bug 1267636).
2336 if (IsWin10OrLater()) {
2337 ScaleForFrameDPI(aResult, aFrame);
2338 }
2339 *aIsOverridable = false;
2340 return rv;
2341
2342 case NS_THEME_WINDOW_BUTTON_BOX:
2343 case NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED:
2344 if (nsUXThemeData::CheckForCompositor()) {
2345 aResult->width = nsUXThemeData::GetCommandButtonBoxMetrics().cx;
2346 aResult->height = nsUXThemeData::GetCommandButtonBoxMetrics().cy -
2347 GetSystemMetrics(SM_CYFRAME) -
2348 GetSystemMetrics(SM_CXPADDEDBORDER);
2349 if (aWidgetType == NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED) {
2350 aResult->width += 1;
2351 aResult->height -= 2;
2352 }
2353 *aIsOverridable = false;
2354 return rv;
2355 }
2356 break;
2357
2358 case NS_THEME_WINDOW_FRAME_LEFT:
2359 case NS_THEME_WINDOW_FRAME_RIGHT:
2360 case NS_THEME_WINDOW_FRAME_BOTTOM:
2361 aResult->width = GetSystemMetrics(SM_CXFRAME);
2362 aResult->height = GetSystemMetrics(SM_CYFRAME);
2363 *aIsOverridable = false;
2364 return rv;
2365 }
2366
2367 int32_t part, state;
2368 rv = GetThemePartAndState(aFrame, aWidgetType, part, state);
2369 if (NS_FAILED(rv)) return rv;
2370
2371 rv = GetCachedMinimumWidgetSize(aFrame, theme, themeClass.value(),
2372 aWidgetType, part, state, sizeReq, aResult);
2373
2374 ScaleForFrameDPI(aResult, aFrame);
2375 return rv;
2376 }
2377
2378 NS_IMETHODIMP
WidgetStateChanged(nsIFrame * aFrame,uint8_t aWidgetType,nsAtom * aAttribute,bool * aShouldRepaint,const nsAttrValue * aOldValue)2379 nsNativeThemeWin::WidgetStateChanged(nsIFrame* aFrame, uint8_t aWidgetType,
2380 nsAtom* aAttribute, bool* aShouldRepaint,
2381 const nsAttrValue* aOldValue) {
2382 // Some widget types just never change state.
2383 if (aWidgetType == NS_THEME_TOOLBOX ||
2384 aWidgetType == NS_THEME_WIN_MEDIA_TOOLBOX ||
2385 aWidgetType == NS_THEME_WIN_COMMUNICATIONS_TOOLBOX ||
2386 aWidgetType == NS_THEME_WIN_BROWSERTABBAR_TOOLBOX ||
2387 aWidgetType == NS_THEME_TOOLBAR || aWidgetType == NS_THEME_STATUSBAR ||
2388 aWidgetType == NS_THEME_STATUSBARPANEL ||
2389 aWidgetType == NS_THEME_RESIZERPANEL ||
2390 aWidgetType == NS_THEME_PROGRESSCHUNK ||
2391 aWidgetType == NS_THEME_PROGRESSCHUNK_VERTICAL ||
2392 aWidgetType == NS_THEME_PROGRESSBAR ||
2393 aWidgetType == NS_THEME_PROGRESSBAR_VERTICAL ||
2394 aWidgetType == NS_THEME_TOOLTIP || aWidgetType == NS_THEME_TABPANELS ||
2395 aWidgetType == NS_THEME_TABPANEL || aWidgetType == NS_THEME_SEPARATOR ||
2396 aWidgetType == NS_THEME_WIN_GLASS ||
2397 aWidgetType == NS_THEME_WIN_BORDERLESS_GLASS) {
2398 *aShouldRepaint = false;
2399 return NS_OK;
2400 }
2401
2402 if (aWidgetType == NS_THEME_WINDOW_TITLEBAR ||
2403 aWidgetType == NS_THEME_WINDOW_TITLEBAR_MAXIMIZED ||
2404 aWidgetType == NS_THEME_WINDOW_FRAME_LEFT ||
2405 aWidgetType == NS_THEME_WINDOW_FRAME_RIGHT ||
2406 aWidgetType == NS_THEME_WINDOW_FRAME_BOTTOM ||
2407 aWidgetType == NS_THEME_WINDOW_BUTTON_CLOSE ||
2408 aWidgetType == NS_THEME_WINDOW_BUTTON_MINIMIZE ||
2409 aWidgetType == NS_THEME_WINDOW_BUTTON_MAXIMIZE ||
2410 aWidgetType == NS_THEME_WINDOW_BUTTON_RESTORE) {
2411 *aShouldRepaint = true;
2412 return NS_OK;
2413 }
2414
2415 // We need to repaint the dropdown arrow in vista HTML combobox controls when
2416 // the control is closed to get rid of the hover effect.
2417 if ((aWidgetType == NS_THEME_MENULIST ||
2418 aWidgetType == NS_THEME_MENULIST_BUTTON) &&
2419 IsHTMLContent(aFrame)) {
2420 *aShouldRepaint = true;
2421 return NS_OK;
2422 }
2423
2424 // XXXdwh Not sure what can really be done here. Can at least guess for
2425 // specific widgets that they're highly unlikely to have certain states.
2426 // For example, a toolbar doesn't care about any states.
2427 if (!aAttribute) {
2428 // Hover/focus/active changed. Always repaint.
2429 *aShouldRepaint = true;
2430 } else {
2431 // Check the attribute to see if it's relevant.
2432 // disabled, checked, dlgtype, default, etc.
2433 *aShouldRepaint = false;
2434 if (aAttribute == nsGkAtoms::disabled || aAttribute == nsGkAtoms::checked ||
2435 aAttribute == nsGkAtoms::selected ||
2436 aAttribute == nsGkAtoms::visuallyselected ||
2437 aAttribute == nsGkAtoms::readonly || aAttribute == nsGkAtoms::open ||
2438 aAttribute == nsGkAtoms::menuactive || aAttribute == nsGkAtoms::focused)
2439 *aShouldRepaint = true;
2440 }
2441
2442 return NS_OK;
2443 }
2444
2445 NS_IMETHODIMP
ThemeChanged()2446 nsNativeThemeWin::ThemeChanged() {
2447 nsUXThemeData::Invalidate();
2448 memset(mBorderCacheValid, 0, sizeof(mBorderCacheValid));
2449 memset(mMinimumWidgetSizeCacheValid, 0, sizeof(mMinimumWidgetSizeCacheValid));
2450 mGutterSizeCacheValid = false;
2451 return NS_OK;
2452 }
2453
ThemeSupportsWidget(nsPresContext * aPresContext,nsIFrame * aFrame,uint8_t aWidgetType)2454 bool nsNativeThemeWin::ThemeSupportsWidget(nsPresContext* aPresContext,
2455 nsIFrame* aFrame,
2456 uint8_t aWidgetType) {
2457 // XXXdwh We can go even further and call the API to ask if support exists for
2458 // specific widgets.
2459
2460 if (aWidgetType == NS_THEME_FOCUS_OUTLINE) {
2461 return true;
2462 }
2463
2464 HANDLE theme = nullptr;
2465 if (aWidgetType == NS_THEME_CHECKBOX_CONTAINER)
2466 theme = GetTheme(NS_THEME_CHECKBOX);
2467 else if (aWidgetType == NS_THEME_RADIO_CONTAINER)
2468 theme = GetTheme(NS_THEME_RADIO);
2469 else
2470 theme = GetTheme(aWidgetType);
2471
2472 if (theme && aWidgetType == NS_THEME_RESIZER) return true;
2473
2474 if ((theme) || (!theme && ClassicThemeSupportsWidget(aFrame, aWidgetType)))
2475 // turn off theming for some HTML widgets styled by the page
2476 return (!IsWidgetStyled(aPresContext, aFrame, aWidgetType));
2477
2478 return false;
2479 }
2480
WidgetIsContainer(uint8_t aWidgetType)2481 bool nsNativeThemeWin::WidgetIsContainer(uint8_t aWidgetType) {
2482 // XXXdwh At some point flesh all of this out.
2483 if (aWidgetType == NS_THEME_MENULIST_BUTTON ||
2484 aWidgetType == NS_THEME_RADIO || aWidgetType == NS_THEME_CHECKBOX)
2485 return false;
2486 return true;
2487 }
2488
ThemeDrawsFocusForWidget(uint8_t aWidgetType)2489 bool nsNativeThemeWin::ThemeDrawsFocusForWidget(uint8_t aWidgetType) {
2490 return false;
2491 }
2492
ThemeNeedsComboboxDropmarker()2493 bool nsNativeThemeWin::ThemeNeedsComboboxDropmarker() { return true; }
2494
WidgetAppearanceDependsOnWindowFocus(uint8_t aWidgetType)2495 bool nsNativeThemeWin::WidgetAppearanceDependsOnWindowFocus(
2496 uint8_t aWidgetType) {
2497 switch (aWidgetType) {
2498 case NS_THEME_WINDOW_TITLEBAR:
2499 case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
2500 case NS_THEME_WINDOW_FRAME_LEFT:
2501 case NS_THEME_WINDOW_FRAME_RIGHT:
2502 case NS_THEME_WINDOW_FRAME_BOTTOM:
2503 case NS_THEME_WINDOW_BUTTON_CLOSE:
2504 case NS_THEME_WINDOW_BUTTON_MINIMIZE:
2505 case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
2506 case NS_THEME_WINDOW_BUTTON_RESTORE:
2507 return true;
2508 default:
2509 return false;
2510 }
2511 }
2512
ThemeGeometryTypeForWidget(nsIFrame * aFrame,uint8_t aWidgetType)2513 nsITheme::ThemeGeometryType nsNativeThemeWin::ThemeGeometryTypeForWidget(
2514 nsIFrame* aFrame, uint8_t aWidgetType) {
2515 switch (aWidgetType) {
2516 case NS_THEME_WINDOW_BUTTON_BOX:
2517 case NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED:
2518 return eThemeGeometryTypeWindowButtons;
2519 default:
2520 return eThemeGeometryTypeUnknown;
2521 }
2522 }
2523
ShouldHideScrollbars()2524 bool nsNativeThemeWin::ShouldHideScrollbars() {
2525 return WinUtils::ShouldHideScrollbars();
2526 }
2527
GetWidgetTransparency(nsIFrame * aFrame,uint8_t aWidgetType)2528 nsITheme::Transparency nsNativeThemeWin::GetWidgetTransparency(
2529 nsIFrame* aFrame, uint8_t aWidgetType) {
2530 switch (aWidgetType) {
2531 case NS_THEME_SCROLLBAR_SMALL:
2532 case NS_THEME_SCROLLBAR:
2533 case NS_THEME_STATUSBAR:
2534 // Knowing that scrollbars and statusbars are opaque improves
2535 // performance, because we create layers for them. This better be
2536 // true across all Windows themes! If it's not true, we should
2537 // paint an opaque background for them to make it true!
2538 return eOpaque;
2539 case NS_THEME_WIN_GLASS:
2540 case NS_THEME_WIN_BORDERLESS_GLASS:
2541 case NS_THEME_SCALE_HORIZONTAL:
2542 case NS_THEME_SCALE_VERTICAL:
2543 case NS_THEME_PROGRESSBAR:
2544 case NS_THEME_PROGRESSBAR_VERTICAL:
2545 case NS_THEME_PROGRESSCHUNK:
2546 case NS_THEME_PROGRESSCHUNK_VERTICAL:
2547 case NS_THEME_RANGE:
2548 return eTransparent;
2549 }
2550
2551 HANDLE theme = GetTheme(aWidgetType);
2552 // For the classic theme we don't really have a way of knowing
2553 if (!theme) {
2554 // menu backgrounds and tooltips which can't be themed are opaque
2555 if (aWidgetType == NS_THEME_MENUPOPUP || aWidgetType == NS_THEME_TOOLTIP) {
2556 return eOpaque;
2557 }
2558 return eUnknownTransparency;
2559 }
2560
2561 int32_t part, state;
2562 nsresult rv = GetThemePartAndState(aFrame, aWidgetType, part, state);
2563 // Fail conservatively
2564 NS_ENSURE_SUCCESS(rv, eUnknownTransparency);
2565
2566 if (part <= 0) {
2567 // Not a real part code, so IsThemeBackgroundPartiallyTransparent may
2568 // not work, so don't call it.
2569 return eUnknownTransparency;
2570 }
2571
2572 if (IsThemeBackgroundPartiallyTransparent(theme, part, state))
2573 return eTransparent;
2574 return eOpaque;
2575 }
2576
2577 /* Windows 9x/NT/2000/Classic XP Theme Support */
2578
ClassicThemeSupportsWidget(nsIFrame * aFrame,uint8_t aWidgetType)2579 bool nsNativeThemeWin::ClassicThemeSupportsWidget(nsIFrame* aFrame,
2580 uint8_t aWidgetType) {
2581 switch (aWidgetType) {
2582 case NS_THEME_RESIZER: {
2583 // The classic native resizer has an opaque grey background which doesn't
2584 // match the usually white background of the scrollable container, so
2585 // only support the native resizer if not in a scrollframe.
2586 nsIFrame* parentFrame = aFrame->GetParent();
2587 return !parentFrame || !parentFrame->IsScrollFrame();
2588 }
2589 case NS_THEME_MENUBAR:
2590 case NS_THEME_MENUPOPUP:
2591 // Classic non-flat menus are handled almost entirely through CSS.
2592 if (!nsUXThemeData::sFlatMenus) return false;
2593 case NS_THEME_BUTTON:
2594 case NS_THEME_NUMBER_INPUT:
2595 case NS_THEME_TEXTFIELD:
2596 case NS_THEME_TEXTFIELD_MULTILINE:
2597 case NS_THEME_CHECKBOX:
2598 case NS_THEME_RADIO:
2599 case NS_THEME_RANGE:
2600 case NS_THEME_RANGE_THUMB:
2601 case NS_THEME_GROUPBOX:
2602 case NS_THEME_SCROLLBARBUTTON_UP:
2603 case NS_THEME_SCROLLBARBUTTON_DOWN:
2604 case NS_THEME_SCROLLBARBUTTON_LEFT:
2605 case NS_THEME_SCROLLBARBUTTON_RIGHT:
2606 case NS_THEME_SCROLLBARTHUMB_VERTICAL:
2607 case NS_THEME_SCROLLBARTHUMB_HORIZONTAL:
2608 case NS_THEME_SCROLLBAR_VERTICAL:
2609 case NS_THEME_SCROLLBAR_HORIZONTAL:
2610 case NS_THEME_SCROLLBAR_NON_DISAPPEARING:
2611 case NS_THEME_SCALE_HORIZONTAL:
2612 case NS_THEME_SCALE_VERTICAL:
2613 case NS_THEME_SCALETHUMB_HORIZONTAL:
2614 case NS_THEME_SCALETHUMB_VERTICAL:
2615 case NS_THEME_MENULIST_BUTTON:
2616 case NS_THEME_INNER_SPIN_BUTTON:
2617 case NS_THEME_SPINNER_UPBUTTON:
2618 case NS_THEME_SPINNER_DOWNBUTTON:
2619 case NS_THEME_LISTBOX:
2620 case NS_THEME_TREEVIEW:
2621 case NS_THEME_MENULIST_TEXTFIELD:
2622 case NS_THEME_MENULIST:
2623 case NS_THEME_TOOLTIP:
2624 case NS_THEME_STATUSBAR:
2625 case NS_THEME_STATUSBARPANEL:
2626 case NS_THEME_RESIZERPANEL:
2627 case NS_THEME_PROGRESSBAR:
2628 case NS_THEME_PROGRESSBAR_VERTICAL:
2629 case NS_THEME_PROGRESSCHUNK:
2630 case NS_THEME_PROGRESSCHUNK_VERTICAL:
2631 case NS_THEME_TAB:
2632 case NS_THEME_TABPANEL:
2633 case NS_THEME_TABPANELS:
2634 case NS_THEME_MENUITEM:
2635 case NS_THEME_CHECKMENUITEM:
2636 case NS_THEME_RADIOMENUITEM:
2637 case NS_THEME_MENUCHECKBOX:
2638 case NS_THEME_MENURADIO:
2639 case NS_THEME_MENUARROW:
2640 case NS_THEME_MENUSEPARATOR:
2641 case NS_THEME_MENUITEMTEXT:
2642 case NS_THEME_WINDOW_TITLEBAR:
2643 case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
2644 case NS_THEME_WINDOW_FRAME_LEFT:
2645 case NS_THEME_WINDOW_FRAME_RIGHT:
2646 case NS_THEME_WINDOW_FRAME_BOTTOM:
2647 case NS_THEME_WINDOW_BUTTON_CLOSE:
2648 case NS_THEME_WINDOW_BUTTON_MINIMIZE:
2649 case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
2650 case NS_THEME_WINDOW_BUTTON_RESTORE:
2651 case NS_THEME_WINDOW_BUTTON_BOX:
2652 case NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED:
2653 return true;
2654 }
2655 return false;
2656 }
2657
ClassicGetWidgetBorder(nsDeviceContext * aContext,nsIFrame * aFrame,uint8_t aWidgetType,nsIntMargin * aResult)2658 nsresult nsNativeThemeWin::ClassicGetWidgetBorder(nsDeviceContext* aContext,
2659 nsIFrame* aFrame,
2660 uint8_t aWidgetType,
2661 nsIntMargin* aResult) {
2662 switch (aWidgetType) {
2663 case NS_THEME_GROUPBOX:
2664 case NS_THEME_BUTTON:
2665 (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right =
2666 2;
2667 break;
2668 case NS_THEME_STATUSBAR:
2669 (*aResult).bottom = (*aResult).left = (*aResult).right = 0;
2670 (*aResult).top = 2;
2671 break;
2672 case NS_THEME_LISTBOX:
2673 case NS_THEME_TREEVIEW:
2674 case NS_THEME_MENULIST:
2675 case NS_THEME_MENULIST_TEXTFIELD:
2676 case NS_THEME_TAB:
2677 case NS_THEME_NUMBER_INPUT:
2678 case NS_THEME_TEXTFIELD:
2679 case NS_THEME_TEXTFIELD_MULTILINE:
2680 case NS_THEME_FOCUS_OUTLINE:
2681 (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right =
2682 2;
2683 break;
2684 case NS_THEME_STATUSBARPANEL:
2685 case NS_THEME_RESIZERPANEL: {
2686 (*aResult).top = 1;
2687 (*aResult).left = 1;
2688 (*aResult).bottom = 1;
2689 (*aResult).right = aFrame->GetNextSibling() ? 3 : 1;
2690 break;
2691 }
2692 case NS_THEME_TOOLTIP:
2693 (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right =
2694 1;
2695 break;
2696 case NS_THEME_PROGRESSBAR:
2697 case NS_THEME_PROGRESSBAR_VERTICAL:
2698 (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right =
2699 1;
2700 break;
2701 case NS_THEME_MENUBAR:
2702 (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right =
2703 0;
2704 break;
2705 case NS_THEME_MENUPOPUP:
2706 (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right =
2707 3;
2708 break;
2709 default:
2710 (*aResult).top = (*aResult).bottom = (*aResult).left = (*aResult).right =
2711 0;
2712 break;
2713 }
2714 return NS_OK;
2715 }
2716
ClassicGetWidgetPadding(nsDeviceContext * aContext,nsIFrame * aFrame,uint8_t aWidgetType,nsIntMargin * aResult)2717 bool nsNativeThemeWin::ClassicGetWidgetPadding(nsDeviceContext* aContext,
2718 nsIFrame* aFrame,
2719 uint8_t aWidgetType,
2720 nsIntMargin* aResult) {
2721 switch (aWidgetType) {
2722 case NS_THEME_MENUITEM:
2723 case NS_THEME_CHECKMENUITEM:
2724 case NS_THEME_RADIOMENUITEM: {
2725 int32_t part, state;
2726 bool focused;
2727
2728 if (NS_FAILED(ClassicGetThemePartAndState(aFrame, aWidgetType, part,
2729 state, focused)))
2730 return false;
2731
2732 if (part == 1) { // top-level menu
2733 if (nsUXThemeData::sFlatMenus || !(state & DFCS_PUSHED)) {
2734 (*aResult).top = (*aResult).bottom = (*aResult).left =
2735 (*aResult).right = 2;
2736 } else {
2737 // make top-level menus look sunken when pushed in the Classic look
2738 (*aResult).top = (*aResult).left = 3;
2739 (*aResult).bottom = (*aResult).right = 1;
2740 }
2741 } else {
2742 (*aResult).top = 0;
2743 (*aResult).bottom = (*aResult).left = (*aResult).right = 2;
2744 }
2745 return true;
2746 }
2747 case NS_THEME_PROGRESSBAR:
2748 case NS_THEME_PROGRESSBAR_VERTICAL:
2749 (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right =
2750 1;
2751 return true;
2752 default:
2753 return false;
2754 }
2755 }
2756
ClassicGetMinimumWidgetSize(nsIFrame * aFrame,uint8_t aWidgetType,LayoutDeviceIntSize * aResult,bool * aIsOverridable)2757 nsresult nsNativeThemeWin::ClassicGetMinimumWidgetSize(
2758 nsIFrame* aFrame, uint8_t aWidgetType, LayoutDeviceIntSize* aResult,
2759 bool* aIsOverridable) {
2760 (*aResult).width = (*aResult).height = 0;
2761 *aIsOverridable = true;
2762 switch (aWidgetType) {
2763 case NS_THEME_RADIO:
2764 case NS_THEME_CHECKBOX:
2765 (*aResult).width = (*aResult).height = 13;
2766 break;
2767 case NS_THEME_MENUCHECKBOX:
2768 case NS_THEME_MENURADIO:
2769 case NS_THEME_MENUARROW:
2770 (*aResult).width = ::GetSystemMetrics(SM_CXMENUCHECK);
2771 (*aResult).height = ::GetSystemMetrics(SM_CYMENUCHECK);
2772 break;
2773 case NS_THEME_INNER_SPIN_BUTTON:
2774 case NS_THEME_SPINNER_UPBUTTON:
2775 case NS_THEME_SPINNER_DOWNBUTTON:
2776 (*aResult).width = ::GetSystemMetrics(SM_CXVSCROLL);
2777 (*aResult).height = 8; // No good metrics available for this
2778 *aIsOverridable = false;
2779 break;
2780 case NS_THEME_SCROLLBARBUTTON_UP:
2781 case NS_THEME_SCROLLBARBUTTON_DOWN:
2782 (*aResult).width = ::GetSystemMetrics(SM_CXVSCROLL);
2783 (*aResult).height = ::GetSystemMetrics(SM_CYVSCROLL);
2784 *aIsOverridable = false;
2785 break;
2786 case NS_THEME_SCROLLBARBUTTON_LEFT:
2787 case NS_THEME_SCROLLBARBUTTON_RIGHT:
2788 (*aResult).width = ::GetSystemMetrics(SM_CXHSCROLL);
2789 (*aResult).height = ::GetSystemMetrics(SM_CYHSCROLL);
2790 *aIsOverridable = false;
2791 break;
2792 case NS_THEME_SCROLLBAR_VERTICAL:
2793 // XXX HACK We should be able to have a minimum height for the scrollbar
2794 // track. However, this causes problems when uncollapsing a scrollbar
2795 // inside a tree. See bug 201379 for details.
2796
2797 // (*aResult).height = ::GetSystemMetrics(SM_CYVTHUMB) << 1;
2798 break;
2799 case NS_THEME_SCROLLBAR_NON_DISAPPEARING: {
2800 aResult->SizeTo(::GetSystemMetrics(SM_CXHSCROLL),
2801 ::GetSystemMetrics(SM_CYVSCROLL));
2802 break;
2803 }
2804 case NS_THEME_RANGE_THUMB: {
2805 if (IsRangeHorizontal(aFrame)) {
2806 (*aResult).width = 12;
2807 (*aResult).height = 20;
2808 } else {
2809 (*aResult).width = 20;
2810 (*aResult).height = 12;
2811 }
2812 *aIsOverridable = false;
2813 break;
2814 }
2815 case NS_THEME_SCALETHUMB_HORIZONTAL:
2816 (*aResult).width = 12;
2817 (*aResult).height = 20;
2818 *aIsOverridable = false;
2819 break;
2820 case NS_THEME_SCALETHUMB_VERTICAL:
2821 (*aResult).width = 20;
2822 (*aResult).height = 12;
2823 *aIsOverridable = false;
2824 break;
2825 case NS_THEME_MENULIST_BUTTON:
2826 (*aResult).width = ::GetSystemMetrics(SM_CXVSCROLL);
2827 break;
2828 case NS_THEME_MENULIST:
2829 case NS_THEME_BUTTON:
2830 case NS_THEME_GROUPBOX:
2831 case NS_THEME_LISTBOX:
2832 case NS_THEME_TREEVIEW:
2833 case NS_THEME_NUMBER_INPUT:
2834 case NS_THEME_TEXTFIELD:
2835 case NS_THEME_TEXTFIELD_MULTILINE:
2836 case NS_THEME_MENULIST_TEXTFIELD:
2837 case NS_THEME_STATUSBAR:
2838 case NS_THEME_STATUSBARPANEL:
2839 case NS_THEME_RESIZERPANEL:
2840 case NS_THEME_PROGRESSCHUNK:
2841 case NS_THEME_PROGRESSCHUNK_VERTICAL:
2842 case NS_THEME_TOOLTIP:
2843 case NS_THEME_PROGRESSBAR:
2844 case NS_THEME_PROGRESSBAR_VERTICAL:
2845 case NS_THEME_TAB:
2846 case NS_THEME_TABPANEL:
2847 case NS_THEME_TABPANELS:
2848 // no minimum widget size
2849 break;
2850 case NS_THEME_RESIZER: {
2851 NONCLIENTMETRICS nc;
2852 nc.cbSize = sizeof(nc);
2853 if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(nc), &nc, 0))
2854 (*aResult).width = (*aResult).height =
2855 abs(nc.lfStatusFont.lfHeight) + 4;
2856 else
2857 (*aResult).width = (*aResult).height = 15;
2858 *aIsOverridable = false;
2859 break;
2860 case NS_THEME_SCROLLBARTHUMB_VERTICAL:
2861 (*aResult).width = ::GetSystemMetrics(SM_CXVSCROLL);
2862 (*aResult).height = ::GetSystemMetrics(SM_CYVTHUMB);
2863 // Without theming, divide the thumb size by two in order to look more
2864 // native
2865 if (!GetTheme(aWidgetType)) (*aResult).height >>= 1;
2866 *aIsOverridable = false;
2867 break;
2868 case NS_THEME_SCROLLBARTHUMB_HORIZONTAL:
2869 (*aResult).width = ::GetSystemMetrics(SM_CXHTHUMB);
2870 (*aResult).height = ::GetSystemMetrics(SM_CYHSCROLL);
2871 // Without theming, divide the thumb size by two in order to look more
2872 // native
2873 if (!GetTheme(aWidgetType)) (*aResult).width >>= 1;
2874 *aIsOverridable = false;
2875 break;
2876 case NS_THEME_SCROLLBAR_HORIZONTAL:
2877 (*aResult).width = ::GetSystemMetrics(SM_CXHTHUMB) << 1;
2878 break;
2879 }
2880 case NS_THEME_MENUSEPARATOR: {
2881 aResult->width = 0;
2882 aResult->height = 10;
2883 break;
2884 }
2885
2886 case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
2887 case NS_THEME_WINDOW_TITLEBAR:
2888 aResult->height = GetSystemMetrics(SM_CYCAPTION);
2889 aResult->height += GetSystemMetrics(SM_CYFRAME);
2890 aResult->width = 0;
2891 break;
2892 case NS_THEME_WINDOW_FRAME_LEFT:
2893 case NS_THEME_WINDOW_FRAME_RIGHT:
2894 aResult->width = GetSystemMetrics(SM_CXFRAME);
2895 aResult->height = 0;
2896 break;
2897
2898 case NS_THEME_WINDOW_FRAME_BOTTOM:
2899 aResult->height = GetSystemMetrics(SM_CYFRAME);
2900 aResult->width = 0;
2901 break;
2902
2903 case NS_THEME_WINDOW_BUTTON_CLOSE:
2904 case NS_THEME_WINDOW_BUTTON_MINIMIZE:
2905 case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
2906 case NS_THEME_WINDOW_BUTTON_RESTORE:
2907 aResult->width = GetSystemMetrics(SM_CXSIZE);
2908 aResult->height = GetSystemMetrics(SM_CYSIZE);
2909 // XXX I have no idea why these caption metrics are always off,
2910 // but they are.
2911 aResult->width -= 2;
2912 aResult->height -= 4;
2913 if (aWidgetType == NS_THEME_WINDOW_BUTTON_MINIMIZE) {
2914 AddPaddingRect(aResult, CAPTIONBUTTON_MINIMIZE);
2915 } else if (aWidgetType == NS_THEME_WINDOW_BUTTON_MAXIMIZE ||
2916 aWidgetType == NS_THEME_WINDOW_BUTTON_RESTORE) {
2917 AddPaddingRect(aResult, CAPTIONBUTTON_RESTORE);
2918 } else if (aWidgetType == NS_THEME_WINDOW_BUTTON_CLOSE) {
2919 AddPaddingRect(aResult, CAPTIONBUTTON_CLOSE);
2920 }
2921 break;
2922
2923 default:
2924 return NS_ERROR_FAILURE;
2925 }
2926 return NS_OK;
2927 }
2928
ClassicGetThemePartAndState(nsIFrame * aFrame,uint8_t aWidgetType,int32_t & aPart,int32_t & aState,bool & aFocused)2929 nsresult nsNativeThemeWin::ClassicGetThemePartAndState(nsIFrame* aFrame,
2930 uint8_t aWidgetType,
2931 int32_t& aPart,
2932 int32_t& aState,
2933 bool& aFocused) {
2934 aFocused = false;
2935 switch (aWidgetType) {
2936 case NS_THEME_BUTTON: {
2937 EventStates contentState;
2938
2939 aPart = DFC_BUTTON;
2940 aState = DFCS_BUTTONPUSH;
2941 aFocused = false;
2942
2943 contentState = GetContentState(aFrame, aWidgetType);
2944 if (IsDisabled(aFrame, contentState))
2945 aState |= DFCS_INACTIVE;
2946 else if (IsOpenButton(aFrame))
2947 aState |= DFCS_PUSHED;
2948 else if (IsCheckedButton(aFrame))
2949 aState |= DFCS_CHECKED;
2950 else {
2951 if (contentState.HasAllStates(NS_EVENT_STATE_ACTIVE |
2952 NS_EVENT_STATE_HOVER)) {
2953 aState |= DFCS_PUSHED;
2954 const nsStyleUserInterface* uiData = aFrame->StyleUserInterface();
2955 // The down state is flat if the button is focusable
2956 if (uiData->mUserFocus == StyleUserFocus::Normal) {
2957 if (!aFrame->GetContent()->IsHTMLElement()) aState |= DFCS_FLAT;
2958
2959 aFocused = true;
2960 }
2961 }
2962 if (contentState.HasState(NS_EVENT_STATE_FOCUS) ||
2963 (aState == DFCS_BUTTONPUSH && IsDefaultButton(aFrame))) {
2964 aFocused = true;
2965 }
2966 }
2967
2968 return NS_OK;
2969 }
2970 case NS_THEME_CHECKBOX:
2971 case NS_THEME_RADIO: {
2972 EventStates contentState;
2973 aFocused = false;
2974
2975 aPart = DFC_BUTTON;
2976 aState = 0;
2977 nsIContent* content = aFrame->GetContent();
2978 bool isCheckbox = (aWidgetType == NS_THEME_CHECKBOX);
2979 bool isChecked = GetCheckedOrSelected(aFrame, !isCheckbox);
2980 bool isIndeterminate = isCheckbox && GetIndeterminate(aFrame);
2981
2982 if (isCheckbox) {
2983 // indeterminate state takes precedence over checkedness.
2984 if (isIndeterminate) {
2985 aState = DFCS_BUTTON3STATE | DFCS_CHECKED;
2986 } else {
2987 aState = DFCS_BUTTONCHECK;
2988 }
2989 } else {
2990 aState = DFCS_BUTTONRADIO;
2991 }
2992 if (isChecked) {
2993 aState |= DFCS_CHECKED;
2994 }
2995
2996 contentState = GetContentState(aFrame, aWidgetType);
2997 if (!content->IsXULElement() &&
2998 contentState.HasState(NS_EVENT_STATE_FOCUS)) {
2999 aFocused = true;
3000 }
3001
3002 if (IsDisabled(aFrame, contentState)) {
3003 aState |= DFCS_INACTIVE;
3004 } else if (contentState.HasAllStates(NS_EVENT_STATE_ACTIVE |
3005 NS_EVENT_STATE_HOVER)) {
3006 aState |= DFCS_PUSHED;
3007 }
3008
3009 return NS_OK;
3010 }
3011 case NS_THEME_MENUITEM:
3012 case NS_THEME_CHECKMENUITEM:
3013 case NS_THEME_RADIOMENUITEM: {
3014 bool isTopLevel = false;
3015 bool isOpen = false;
3016 nsMenuFrame* menuFrame = do_QueryFrame(aFrame);
3017 EventStates eventState = GetContentState(aFrame, aWidgetType);
3018
3019 // We indicate top-level-ness using aPart. 0 is a normal menu item,
3020 // 1 is a top-level menu item. The state of the item is composed of
3021 // DFCS_* flags only.
3022 aPart = 0;
3023 aState = 0;
3024
3025 if (menuFrame) {
3026 // If this is a real menu item, we should check if it is part of the
3027 // main menu bar or not, and if it is a container, as these affect
3028 // rendering.
3029 isTopLevel = menuFrame->IsOnMenuBar();
3030 isOpen = menuFrame->IsOpen();
3031 }
3032
3033 if (IsDisabled(aFrame, eventState)) aState |= DFCS_INACTIVE;
3034
3035 if (isTopLevel) {
3036 aPart = 1;
3037 if (isOpen) aState |= DFCS_PUSHED;
3038 }
3039
3040 if (IsMenuActive(aFrame, aWidgetType)) aState |= DFCS_HOT;
3041
3042 return NS_OK;
3043 }
3044 case NS_THEME_MENUCHECKBOX:
3045 case NS_THEME_MENURADIO:
3046 case NS_THEME_MENUARROW: {
3047 aState = 0;
3048 EventStates eventState = GetContentState(aFrame, aWidgetType);
3049
3050 if (IsDisabled(aFrame, eventState)) aState |= DFCS_INACTIVE;
3051 if (IsMenuActive(aFrame, aWidgetType)) aState |= DFCS_HOT;
3052
3053 if (aWidgetType == NS_THEME_MENUCHECKBOX ||
3054 aWidgetType == NS_THEME_MENURADIO) {
3055 if (IsCheckedButton(aFrame)) aState |= DFCS_CHECKED;
3056 } else if (IsFrameRTL(aFrame)) {
3057 aState |= DFCS_RTL;
3058 }
3059 return NS_OK;
3060 }
3061 case NS_THEME_LISTBOX:
3062 case NS_THEME_TREEVIEW:
3063 case NS_THEME_NUMBER_INPUT:
3064 case NS_THEME_FOCUS_OUTLINE:
3065 case NS_THEME_TEXTFIELD:
3066 case NS_THEME_TEXTFIELD_MULTILINE:
3067 case NS_THEME_MENULIST:
3068 case NS_THEME_MENULIST_TEXTFIELD:
3069 case NS_THEME_RANGE:
3070 case NS_THEME_RANGE_THUMB:
3071 case NS_THEME_SCROLLBARTHUMB_VERTICAL:
3072 case NS_THEME_SCROLLBARTHUMB_HORIZONTAL:
3073 case NS_THEME_SCROLLBAR_VERTICAL:
3074 case NS_THEME_SCROLLBAR_HORIZONTAL:
3075 case NS_THEME_SCALE_HORIZONTAL:
3076 case NS_THEME_SCALE_VERTICAL:
3077 case NS_THEME_SCALETHUMB_HORIZONTAL:
3078 case NS_THEME_SCALETHUMB_VERTICAL:
3079 case NS_THEME_STATUSBAR:
3080 case NS_THEME_STATUSBARPANEL:
3081 case NS_THEME_RESIZERPANEL:
3082 case NS_THEME_PROGRESSCHUNK:
3083 case NS_THEME_PROGRESSCHUNK_VERTICAL:
3084 case NS_THEME_TOOLTIP:
3085 case NS_THEME_PROGRESSBAR:
3086 case NS_THEME_PROGRESSBAR_VERTICAL:
3087 case NS_THEME_TAB:
3088 case NS_THEME_TABPANEL:
3089 case NS_THEME_TABPANELS:
3090 case NS_THEME_MENUBAR:
3091 case NS_THEME_MENUPOPUP:
3092 case NS_THEME_GROUPBOX:
3093 // these don't use DrawFrameControl
3094 return NS_OK;
3095 case NS_THEME_MENULIST_BUTTON: {
3096 aPart = DFC_SCROLL;
3097 aState = DFCS_SCROLLCOMBOBOX;
3098
3099 nsIFrame* parentFrame = aFrame->GetParent();
3100 bool isHTML = IsHTMLContent(aFrame);
3101 bool isMenulist = !isHTML && parentFrame->IsMenuFrame();
3102 bool isOpen = false;
3103
3104 // HTML select and XUL menulist dropdown buttons get state from the
3105 // parent.
3106 if (isHTML || isMenulist) aFrame = parentFrame;
3107
3108 EventStates eventState = GetContentState(aFrame, aWidgetType);
3109
3110 if (IsDisabled(aFrame, eventState)) {
3111 aState |= DFCS_INACTIVE;
3112 return NS_OK;
3113 }
3114
3115 if (isHTML) {
3116 nsIComboboxControlFrame* ccf = do_QueryFrame(aFrame);
3117 isOpen = (ccf && ccf->IsDroppedDownOrHasParentPopup());
3118 } else
3119 isOpen = IsOpenButton(aFrame);
3120
3121 // XXX Button should look active until the mouse is released, but
3122 // without making it look active when the popup is clicked.
3123 if (isOpen && (isHTML || isMenulist)) return NS_OK;
3124
3125 // Dropdown button active state doesn't need :hover.
3126 if (eventState.HasState(NS_EVENT_STATE_ACTIVE))
3127 aState |= DFCS_PUSHED | DFCS_FLAT;
3128
3129 return NS_OK;
3130 }
3131 case NS_THEME_SCROLLBARBUTTON_UP:
3132 case NS_THEME_SCROLLBARBUTTON_DOWN:
3133 case NS_THEME_SCROLLBARBUTTON_LEFT:
3134 case NS_THEME_SCROLLBARBUTTON_RIGHT: {
3135 EventStates contentState = GetContentState(aFrame, aWidgetType);
3136
3137 aPart = DFC_SCROLL;
3138 switch (aWidgetType) {
3139 case NS_THEME_SCROLLBARBUTTON_UP:
3140 aState = DFCS_SCROLLUP;
3141 break;
3142 case NS_THEME_SCROLLBARBUTTON_DOWN:
3143 aState = DFCS_SCROLLDOWN;
3144 break;
3145 case NS_THEME_SCROLLBARBUTTON_LEFT:
3146 aState = DFCS_SCROLLLEFT;
3147 break;
3148 case NS_THEME_SCROLLBARBUTTON_RIGHT:
3149 aState = DFCS_SCROLLRIGHT;
3150 break;
3151 }
3152
3153 if (IsDisabled(aFrame, contentState))
3154 aState |= DFCS_INACTIVE;
3155 else {
3156 if (contentState.HasAllStates(NS_EVENT_STATE_HOVER |
3157 NS_EVENT_STATE_ACTIVE))
3158 aState |= DFCS_PUSHED | DFCS_FLAT;
3159 }
3160
3161 return NS_OK;
3162 }
3163 case NS_THEME_INNER_SPIN_BUTTON:
3164 case NS_THEME_SPINNER_UPBUTTON:
3165 case NS_THEME_SPINNER_DOWNBUTTON: {
3166 EventStates contentState = GetContentState(aFrame, aWidgetType);
3167
3168 aPart = DFC_SCROLL;
3169 switch (aWidgetType) {
3170 case NS_THEME_SPINNER_UPBUTTON:
3171 aState = DFCS_SCROLLUP;
3172 break;
3173 case NS_THEME_INNER_SPIN_BUTTON:
3174 case NS_THEME_SPINNER_DOWNBUTTON:
3175 aState = DFCS_SCROLLDOWN;
3176 break;
3177 }
3178
3179 if (IsDisabled(aFrame, contentState))
3180 aState |= DFCS_INACTIVE;
3181 else {
3182 if (contentState.HasAllStates(NS_EVENT_STATE_HOVER |
3183 NS_EVENT_STATE_ACTIVE))
3184 aState |= DFCS_PUSHED;
3185 }
3186
3187 return NS_OK;
3188 }
3189 case NS_THEME_RESIZER:
3190 aPart = DFC_SCROLL;
3191 aState =
3192 (IsFrameRTL(aFrame)) ? DFCS_SCROLLSIZEGRIPRIGHT : DFCS_SCROLLSIZEGRIP;
3193 return NS_OK;
3194 case NS_THEME_MENUSEPARATOR:
3195 aPart = 0;
3196 aState = 0;
3197 return NS_OK;
3198 case NS_THEME_WINDOW_TITLEBAR:
3199 aPart = mozilla::widget::themeconst::WP_CAPTION;
3200 aState = GetTopLevelWindowActiveState(aFrame);
3201 return NS_OK;
3202 case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
3203 aPart = mozilla::widget::themeconst::WP_MAXCAPTION;
3204 aState = GetTopLevelWindowActiveState(aFrame);
3205 return NS_OK;
3206 case NS_THEME_WINDOW_FRAME_LEFT:
3207 aPart = mozilla::widget::themeconst::WP_FRAMELEFT;
3208 aState = GetTopLevelWindowActiveState(aFrame);
3209 return NS_OK;
3210 case NS_THEME_WINDOW_FRAME_RIGHT:
3211 aPart = mozilla::widget::themeconst::WP_FRAMERIGHT;
3212 aState = GetTopLevelWindowActiveState(aFrame);
3213 return NS_OK;
3214 case NS_THEME_WINDOW_FRAME_BOTTOM:
3215 aPart = mozilla::widget::themeconst::WP_FRAMEBOTTOM;
3216 aState = GetTopLevelWindowActiveState(aFrame);
3217 return NS_OK;
3218 case NS_THEME_WINDOW_BUTTON_CLOSE:
3219 aPart = DFC_CAPTION;
3220 aState = DFCS_CAPTIONCLOSE | GetClassicWindowFrameButtonState(
3221 GetContentState(aFrame, aWidgetType));
3222 return NS_OK;
3223 case NS_THEME_WINDOW_BUTTON_MINIMIZE:
3224 aPart = DFC_CAPTION;
3225 aState = DFCS_CAPTIONMIN | GetClassicWindowFrameButtonState(
3226 GetContentState(aFrame, aWidgetType));
3227 return NS_OK;
3228 case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
3229 aPart = DFC_CAPTION;
3230 aState = DFCS_CAPTIONMAX | GetClassicWindowFrameButtonState(
3231 GetContentState(aFrame, aWidgetType));
3232 return NS_OK;
3233 case NS_THEME_WINDOW_BUTTON_RESTORE:
3234 aPart = DFC_CAPTION;
3235 aState = DFCS_CAPTIONRESTORE | GetClassicWindowFrameButtonState(
3236 GetContentState(aFrame, aWidgetType));
3237 return NS_OK;
3238 }
3239 return NS_ERROR_FAILURE;
3240 }
3241
3242 // Draw classic Windows tab
3243 // (no system API for this, but DrawEdge can draw all the parts of a tab)
DrawTab(HDC hdc,const RECT & R,int32_t aPosition,bool aSelected,bool aDrawLeft,bool aDrawRight)3244 static void DrawTab(HDC hdc, const RECT& R, int32_t aPosition, bool aSelected,
3245 bool aDrawLeft, bool aDrawRight) {
3246 int32_t leftFlag, topFlag, rightFlag, lightFlag, shadeFlag;
3247 RECT topRect, sideRect, bottomRect, lightRect, shadeRect;
3248 int32_t selectedOffset, lOffset, rOffset;
3249
3250 selectedOffset = aSelected ? 1 : 0;
3251 lOffset = aDrawLeft ? 2 : 0;
3252 rOffset = aDrawRight ? 2 : 0;
3253
3254 // Get info for tab orientation/position (Left, Top, Right, Bottom)
3255 switch (aPosition) {
3256 case BF_LEFT:
3257 leftFlag = BF_TOP;
3258 topFlag = BF_LEFT;
3259 rightFlag = BF_BOTTOM;
3260 lightFlag = BF_DIAGONAL_ENDTOPRIGHT;
3261 shadeFlag = BF_DIAGONAL_ENDBOTTOMRIGHT;
3262
3263 ::SetRect(&topRect, R.left, R.top + lOffset, R.right, R.bottom - rOffset);
3264 ::SetRect(&sideRect, R.left + 2, R.top, R.right - 2 + selectedOffset,
3265 R.bottom);
3266 ::SetRect(&bottomRect, R.right - 2, R.top, R.right, R.bottom);
3267 ::SetRect(&lightRect, R.left, R.top, R.left + 3, R.top + 3);
3268 ::SetRect(&shadeRect, R.left + 1, R.bottom - 2, R.left + 2, R.bottom - 1);
3269 break;
3270 case BF_TOP:
3271 leftFlag = BF_LEFT;
3272 topFlag = BF_TOP;
3273 rightFlag = BF_RIGHT;
3274 lightFlag = BF_DIAGONAL_ENDTOPRIGHT;
3275 shadeFlag = BF_DIAGONAL_ENDBOTTOMRIGHT;
3276
3277 ::SetRect(&topRect, R.left + lOffset, R.top, R.right - rOffset, R.bottom);
3278 ::SetRect(&sideRect, R.left, R.top + 2, R.right,
3279 R.bottom - 1 + selectedOffset);
3280 ::SetRect(&bottomRect, R.left, R.bottom - 1, R.right, R.bottom);
3281 ::SetRect(&lightRect, R.left, R.top, R.left + 3, R.top + 3);
3282 ::SetRect(&shadeRect, R.right - 2, R.top + 1, R.right - 1, R.top + 2);
3283 break;
3284 case BF_RIGHT:
3285 leftFlag = BF_TOP;
3286 topFlag = BF_RIGHT;
3287 rightFlag = BF_BOTTOM;
3288 lightFlag = BF_DIAGONAL_ENDTOPLEFT;
3289 shadeFlag = BF_DIAGONAL_ENDBOTTOMLEFT;
3290
3291 ::SetRect(&topRect, R.left, R.top + lOffset, R.right, R.bottom - rOffset);
3292 ::SetRect(&sideRect, R.left + 2 - selectedOffset, R.top, R.right - 2,
3293 R.bottom);
3294 ::SetRect(&bottomRect, R.left, R.top, R.left + 2, R.bottom);
3295 ::SetRect(&lightRect, R.right - 3, R.top, R.right - 1, R.top + 2);
3296 ::SetRect(&shadeRect, R.right - 2, R.bottom - 3, R.right, R.bottom - 1);
3297 break;
3298 case BF_BOTTOM:
3299 leftFlag = BF_LEFT;
3300 topFlag = BF_BOTTOM;
3301 rightFlag = BF_RIGHT;
3302 lightFlag = BF_DIAGONAL_ENDTOPLEFT;
3303 shadeFlag = BF_DIAGONAL_ENDBOTTOMLEFT;
3304
3305 ::SetRect(&topRect, R.left + lOffset, R.top, R.right - rOffset, R.bottom);
3306 ::SetRect(&sideRect, R.left, R.top + 2 - selectedOffset, R.right,
3307 R.bottom - 2);
3308 ::SetRect(&bottomRect, R.left, R.top, R.right, R.top + 2);
3309 ::SetRect(&lightRect, R.left, R.bottom - 3, R.left + 2, R.bottom - 1);
3310 ::SetRect(&shadeRect, R.right - 2, R.bottom - 3, R.right, R.bottom - 1);
3311 break;
3312 default:
3313 MOZ_CRASH();
3314 }
3315
3316 // Background
3317 ::FillRect(hdc, &R, (HBRUSH)(COLOR_3DFACE + 1));
3318
3319 // Tab "Top"
3320 ::DrawEdge(hdc, &topRect, EDGE_RAISED, BF_SOFT | topFlag);
3321
3322 // Tab "Bottom"
3323 if (!aSelected) ::DrawEdge(hdc, &bottomRect, EDGE_RAISED, BF_SOFT | topFlag);
3324
3325 // Tab "Sides"
3326 if (!aDrawLeft) leftFlag = 0;
3327 if (!aDrawRight) rightFlag = 0;
3328 ::DrawEdge(hdc, &sideRect, EDGE_RAISED, BF_SOFT | leftFlag | rightFlag);
3329
3330 // Tab Diagonal Corners
3331 if (aDrawLeft) ::DrawEdge(hdc, &lightRect, EDGE_RAISED, BF_SOFT | lightFlag);
3332
3333 if (aDrawRight) ::DrawEdge(hdc, &shadeRect, EDGE_RAISED, BF_SOFT | shadeFlag);
3334 }
3335
DrawMenuImage(HDC hdc,const RECT & rc,int32_t aComponent,uint32_t aColor)3336 static void DrawMenuImage(HDC hdc, const RECT& rc, int32_t aComponent,
3337 uint32_t aColor) {
3338 // This procedure creates a memory bitmap to contain the check mark, draws
3339 // it into the bitmap (it is a mask image), then composes it onto the menu
3340 // item in appropriate colors.
3341 HDC hMemoryDC = ::CreateCompatibleDC(hdc);
3342 if (hMemoryDC) {
3343 // XXXjgr We should ideally be caching these, but we wont be notified when
3344 // they change currently, so we can't do so easily. Same for the bitmap.
3345 int checkW = ::GetSystemMetrics(SM_CXMENUCHECK);
3346 int checkH = ::GetSystemMetrics(SM_CYMENUCHECK);
3347
3348 HBITMAP hMonoBitmap = ::CreateBitmap(checkW, checkH, 1, 1, nullptr);
3349 if (hMonoBitmap) {
3350 HBITMAP hPrevBitmap = (HBITMAP)::SelectObject(hMemoryDC, hMonoBitmap);
3351 if (hPrevBitmap) {
3352 // XXXjgr This will go pear-shaped if the image is bigger than the
3353 // provided rect. What should we do?
3354 RECT imgRect = {0, 0, checkW, checkH};
3355 POINT imgPos = {rc.left + (rc.right - rc.left - checkW) / 2,
3356 rc.top + (rc.bottom - rc.top - checkH) / 2};
3357
3358 // XXXzeniko Windows renders these 1px lower than you'd expect
3359 if (aComponent == DFCS_MENUCHECK || aComponent == DFCS_MENUBULLET)
3360 imgPos.y++;
3361
3362 ::DrawFrameControl(hMemoryDC, &imgRect, DFC_MENU, aComponent);
3363 COLORREF oldTextCol = ::SetTextColor(hdc, 0x00000000);
3364 COLORREF oldBackCol = ::SetBkColor(hdc, 0x00FFFFFF);
3365 ::BitBlt(hdc, imgPos.x, imgPos.y, checkW, checkH, hMemoryDC, 0, 0,
3366 SRCAND);
3367 ::SetTextColor(hdc, ::GetSysColor(aColor));
3368 ::SetBkColor(hdc, 0x00000000);
3369 ::BitBlt(hdc, imgPos.x, imgPos.y, checkW, checkH, hMemoryDC, 0, 0,
3370 SRCPAINT);
3371 ::SetTextColor(hdc, oldTextCol);
3372 ::SetBkColor(hdc, oldBackCol);
3373 ::SelectObject(hMemoryDC, hPrevBitmap);
3374 }
3375 ::DeleteObject(hMonoBitmap);
3376 }
3377 ::DeleteDC(hMemoryDC);
3378 }
3379 }
3380
DrawCheckedRect(HDC hdc,const RECT & rc,int32_t fore,int32_t back,HBRUSH defaultBack)3381 void nsNativeThemeWin::DrawCheckedRect(HDC hdc, const RECT& rc, int32_t fore,
3382 int32_t back, HBRUSH defaultBack) {
3383 static WORD patBits[8] = {0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55};
3384
3385 HBITMAP patBmp = ::CreateBitmap(8, 8, 1, 1, patBits);
3386 if (patBmp) {
3387 HBRUSH brush = (HBRUSH)::CreatePatternBrush(patBmp);
3388 if (brush) {
3389 COLORREF oldForeColor = ::SetTextColor(hdc, ::GetSysColor(fore));
3390 COLORREF oldBackColor = ::SetBkColor(hdc, ::GetSysColor(back));
3391 POINT vpOrg;
3392
3393 ::UnrealizeObject(brush);
3394 ::GetViewportOrgEx(hdc, &vpOrg);
3395 ::SetBrushOrgEx(hdc, vpOrg.x + rc.left, vpOrg.y + rc.top, nullptr);
3396 HBRUSH oldBrush = (HBRUSH)::SelectObject(hdc, brush);
3397 ::FillRect(hdc, &rc, brush);
3398 ::SetTextColor(hdc, oldForeColor);
3399 ::SetBkColor(hdc, oldBackColor);
3400 ::SelectObject(hdc, oldBrush);
3401 ::DeleteObject(brush);
3402 } else
3403 ::FillRect(hdc, &rc, defaultBack);
3404
3405 ::DeleteObject(patBmp);
3406 }
3407 }
3408
ClassicDrawWidgetBackground(gfxContext * aContext,nsIFrame * aFrame,uint8_t aWidgetType,const nsRect & aRect,const nsRect & aDirtyRect)3409 nsresult nsNativeThemeWin::ClassicDrawWidgetBackground(
3410 gfxContext* aContext, nsIFrame* aFrame, uint8_t aWidgetType,
3411 const nsRect& aRect, const nsRect& aDirtyRect) {
3412 int32_t part, state;
3413 bool focused;
3414 nsresult rv;
3415 rv = ClassicGetThemePartAndState(aFrame, aWidgetType, part, state, focused);
3416 if (NS_FAILED(rv)) return rv;
3417
3418 if (AssumeThemePartAndStateAreTransparent(part, state)) {
3419 return NS_OK;
3420 }
3421
3422 gfxFloat p2a = gfxFloat(aFrame->PresContext()->AppUnitsPerDevPixel());
3423 RECT widgetRect;
3424 gfxRect tr(aRect.X(), aRect.Y(), aRect.Width(), aRect.Height()),
3425 dr(aDirtyRect.X(), aDirtyRect.Y(), aDirtyRect.Width(),
3426 aDirtyRect.Height());
3427
3428 tr.Scale(1.0 / p2a);
3429 dr.Scale(1.0 / p2a);
3430
3431 RefPtr<gfxContext> ctx = aContext;
3432
3433 gfxWindowsNativeDrawing nativeDrawing(
3434 ctx, dr, GetWidgetNativeDrawingFlags(aWidgetType));
3435
3436 RENDER_AGAIN:
3437
3438 HDC hdc = nativeDrawing.BeginNativeDrawing();
3439 if (!hdc) return NS_ERROR_FAILURE;
3440
3441 nativeDrawing.TransformToNativeRect(tr, widgetRect);
3442
3443 rv = NS_OK;
3444 switch (aWidgetType) {
3445 // Draw button
3446 case NS_THEME_BUTTON: {
3447 if (focused) {
3448 // draw dark button focus border first
3449 HBRUSH brush;
3450 brush = ::GetSysColorBrush(COLOR_3DDKSHADOW);
3451 if (brush) ::FrameRect(hdc, &widgetRect, brush);
3452 InflateRect(&widgetRect, -1, -1);
3453 }
3454 // fall-through...
3455 }
3456 // Draw controls supported by DrawFrameControl
3457 case NS_THEME_CHECKBOX:
3458 case NS_THEME_RADIO:
3459 case NS_THEME_SCROLLBARBUTTON_UP:
3460 case NS_THEME_SCROLLBARBUTTON_DOWN:
3461 case NS_THEME_SCROLLBARBUTTON_LEFT:
3462 case NS_THEME_SCROLLBARBUTTON_RIGHT:
3463 case NS_THEME_INNER_SPIN_BUTTON:
3464 case NS_THEME_SPINNER_UPBUTTON:
3465 case NS_THEME_SPINNER_DOWNBUTTON:
3466 case NS_THEME_MENULIST_BUTTON:
3467 case NS_THEME_RESIZER: {
3468 int32_t oldTA;
3469 // setup DC to make DrawFrameControl draw correctly
3470 oldTA = ::SetTextAlign(hdc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
3471 ::DrawFrameControl(hdc, &widgetRect, part, state);
3472 ::SetTextAlign(hdc, oldTA);
3473 break;
3474 }
3475 // Draw controls with 2px 3D inset border
3476 case NS_THEME_NUMBER_INPUT:
3477 case NS_THEME_TEXTFIELD:
3478 case NS_THEME_TEXTFIELD_MULTILINE:
3479 case NS_THEME_LISTBOX:
3480 case NS_THEME_MENULIST:
3481 case NS_THEME_MENULIST_TEXTFIELD: {
3482 // Draw inset edge
3483 ::DrawEdge(hdc, &widgetRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
3484 EventStates eventState = GetContentState(aFrame, aWidgetType);
3485
3486 // Fill in background
3487 if (IsDisabled(aFrame, eventState) ||
3488 (aFrame->GetContent()->IsXULElement() && IsReadOnly(aFrame)))
3489 ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_BTNFACE + 1));
3490 else
3491 ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_WINDOW + 1));
3492
3493 break;
3494 }
3495 case NS_THEME_TREEVIEW: {
3496 // Draw inset edge
3497 ::DrawEdge(hdc, &widgetRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
3498
3499 // Fill in window color background
3500 ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_WINDOW + 1));
3501
3502 break;
3503 }
3504 // Draw ToolTip background
3505 case NS_THEME_TOOLTIP:
3506 ::FrameRect(hdc, &widgetRect, ::GetSysColorBrush(COLOR_WINDOWFRAME));
3507 InflateRect(&widgetRect, -1, -1);
3508 ::FillRect(hdc, &widgetRect, ::GetSysColorBrush(COLOR_INFOBK));
3509
3510 break;
3511 case NS_THEME_GROUPBOX:
3512 ::DrawEdge(hdc, &widgetRect, EDGE_ETCHED, BF_RECT | BF_ADJUST);
3513 ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_BTNFACE + 1));
3514 break;
3515 // Draw 3D face background controls
3516 case NS_THEME_PROGRESSBAR:
3517 case NS_THEME_PROGRESSBAR_VERTICAL:
3518 // Draw 3D border
3519 ::DrawEdge(hdc, &widgetRect, BDR_SUNKENOUTER, BF_RECT | BF_MIDDLE);
3520 InflateRect(&widgetRect, -1, -1);
3521 // fall through
3522 case NS_THEME_TABPANEL:
3523 case NS_THEME_STATUSBAR:
3524 case NS_THEME_RESIZERPANEL: {
3525 ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_BTNFACE + 1));
3526
3527 break;
3528 }
3529 // Draw 3D inset statusbar panel
3530 case NS_THEME_STATUSBARPANEL: {
3531 if (aFrame->GetNextSibling())
3532 widgetRect.right -= 2; // space between sibling status panels
3533
3534 ::DrawEdge(hdc, &widgetRect, BDR_SUNKENOUTER, BF_RECT | BF_MIDDLE);
3535
3536 break;
3537 }
3538 // Draw scrollbar thumb
3539 case NS_THEME_SCROLLBARTHUMB_VERTICAL:
3540 case NS_THEME_SCROLLBARTHUMB_HORIZONTAL:
3541 ::DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_RECT | BF_MIDDLE);
3542
3543 break;
3544 case NS_THEME_RANGE_THUMB:
3545 case NS_THEME_SCALETHUMB_VERTICAL:
3546 case NS_THEME_SCALETHUMB_HORIZONTAL: {
3547 EventStates eventState = GetContentState(aFrame, aWidgetType);
3548
3549 ::DrawEdge(hdc, &widgetRect, EDGE_RAISED,
3550 BF_RECT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
3551 if (IsDisabled(aFrame, eventState)) {
3552 DrawCheckedRect(hdc, widgetRect, COLOR_3DFACE, COLOR_3DHILIGHT,
3553 (HBRUSH)COLOR_3DHILIGHT);
3554 }
3555
3556 break;
3557 }
3558 // Draw scrollbar track background
3559 case NS_THEME_SCROLLBAR_VERTICAL:
3560 case NS_THEME_SCROLLBAR_HORIZONTAL: {
3561 // Windows fills in the scrollbar track differently
3562 // depending on whether these are equal
3563 DWORD color3D, colorScrollbar, colorWindow;
3564
3565 color3D = ::GetSysColor(COLOR_3DFACE);
3566 colorWindow = ::GetSysColor(COLOR_WINDOW);
3567 colorScrollbar = ::GetSysColor(COLOR_SCROLLBAR);
3568
3569 if ((color3D != colorScrollbar) && (colorWindow != colorScrollbar))
3570 // Use solid brush
3571 ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_SCROLLBAR + 1));
3572 else {
3573 DrawCheckedRect(hdc, widgetRect, COLOR_3DHILIGHT, COLOR_3DFACE,
3574 (HBRUSH)COLOR_SCROLLBAR + 1);
3575 }
3576 // XXX should invert the part of the track being clicked here
3577 // but the track is never :active
3578
3579 break;
3580 }
3581 // Draw scale track background
3582 case NS_THEME_RANGE:
3583 case NS_THEME_SCALE_VERTICAL:
3584 case NS_THEME_SCALE_HORIZONTAL: {
3585 const int32_t trackWidth = 4;
3586 // When rounding is necessary, we round the position of the track
3587 // away from the chevron of the thumb to make it look better.
3588 if (aWidgetType == NS_THEME_SCALE_HORIZONTAL ||
3589 (aWidgetType == NS_THEME_RANGE && IsRangeHorizontal(aFrame))) {
3590 widgetRect.top += (widgetRect.bottom - widgetRect.top - trackWidth) / 2;
3591 widgetRect.bottom = widgetRect.top + trackWidth;
3592 } else {
3593 if (!IsFrameRTL(aFrame)) {
3594 widgetRect.left +=
3595 (widgetRect.right - widgetRect.left - trackWidth) / 2;
3596 widgetRect.right = widgetRect.left + trackWidth;
3597 } else {
3598 widgetRect.right -=
3599 (widgetRect.right - widgetRect.left - trackWidth) / 2;
3600 widgetRect.left = widgetRect.right - trackWidth;
3601 }
3602 }
3603
3604 ::DrawEdge(hdc, &widgetRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
3605 ::FillRect(hdc, &widgetRect, (HBRUSH)GetStockObject(GRAY_BRUSH));
3606
3607 break;
3608 }
3609 case NS_THEME_PROGRESSCHUNK_VERTICAL:
3610 ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_HIGHLIGHT + 1));
3611 break;
3612
3613 case NS_THEME_PROGRESSCHUNK: {
3614 nsIFrame* stateFrame = aFrame->GetParent();
3615 EventStates eventStates = GetContentState(stateFrame, aWidgetType);
3616
3617 bool indeterminate = IsIndeterminateProgress(stateFrame, eventStates);
3618 bool vertical = IsVerticalProgress(stateFrame) ||
3619 aWidgetType == NS_THEME_PROGRESSCHUNK_VERTICAL;
3620
3621 nsIContent* content = aFrame->GetContent();
3622 if (!indeterminate || !content) {
3623 ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_HIGHLIGHT + 1));
3624 break;
3625 }
3626
3627 RECT overlayRect = CalculateProgressOverlayRect(
3628 aFrame, &widgetRect, vertical, indeterminate, true);
3629
3630 ::FillRect(hdc, &overlayRect, (HBRUSH)(COLOR_HIGHLIGHT + 1));
3631
3632 if (!QueueAnimatedContentForRefresh(aFrame->GetContent(), 30)) {
3633 NS_WARNING("unable to animate progress widget!");
3634 }
3635 break;
3636 }
3637
3638 // Draw Tab
3639 case NS_THEME_TAB: {
3640 DrawTab(hdc, widgetRect, IsBottomTab(aFrame) ? BF_BOTTOM : BF_TOP,
3641 IsSelectedTab(aFrame), !IsRightToSelectedTab(aFrame),
3642 !IsLeftToSelectedTab(aFrame));
3643
3644 break;
3645 }
3646 case NS_THEME_TABPANELS:
3647 ::DrawEdge(hdc, &widgetRect, EDGE_RAISED,
3648 BF_SOFT | BF_MIDDLE | BF_LEFT | BF_RIGHT | BF_BOTTOM);
3649
3650 break;
3651 case NS_THEME_MENUBAR:
3652 break;
3653 case NS_THEME_MENUPOPUP:
3654 NS_ASSERTION(nsUXThemeData::sFlatMenus,
3655 "Classic menus are styled entirely through CSS");
3656 ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_MENU + 1));
3657 ::FrameRect(hdc, &widgetRect, ::GetSysColorBrush(COLOR_BTNSHADOW));
3658 break;
3659 case NS_THEME_MENUITEM:
3660 case NS_THEME_CHECKMENUITEM:
3661 case NS_THEME_RADIOMENUITEM:
3662 // part == 0 for normal items
3663 // part == 1 for top-level menu items
3664 if (nsUXThemeData::sFlatMenus) {
3665 // Not disabled and hot/pushed.
3666 if ((state & (DFCS_HOT | DFCS_PUSHED)) != 0) {
3667 ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_MENUHILIGHT + 1));
3668 ::FrameRect(hdc, &widgetRect, ::GetSysColorBrush(COLOR_HIGHLIGHT));
3669 }
3670 } else {
3671 if (part == 1) {
3672 if ((state & DFCS_INACTIVE) == 0) {
3673 if ((state & DFCS_PUSHED) != 0) {
3674 ::DrawEdge(hdc, &widgetRect, BDR_SUNKENOUTER, BF_RECT);
3675 } else if ((state & DFCS_HOT) != 0) {
3676 ::DrawEdge(hdc, &widgetRect, BDR_RAISEDINNER, BF_RECT);
3677 }
3678 }
3679 } else {
3680 if ((state & (DFCS_HOT | DFCS_PUSHED)) != 0) {
3681 ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_HIGHLIGHT + 1));
3682 }
3683 }
3684 }
3685 break;
3686 case NS_THEME_MENUCHECKBOX:
3687 case NS_THEME_MENURADIO:
3688 if (!(state & DFCS_CHECKED)) break; // nothin' to do
3689 case NS_THEME_MENUARROW: {
3690 uint32_t color = COLOR_MENUTEXT;
3691 if ((state & DFCS_INACTIVE))
3692 color = COLOR_GRAYTEXT;
3693 else if ((state & DFCS_HOT))
3694 color = COLOR_HIGHLIGHTTEXT;
3695
3696 if (aWidgetType == NS_THEME_MENUCHECKBOX)
3697 DrawMenuImage(hdc, widgetRect, DFCS_MENUCHECK, color);
3698 else if (aWidgetType == NS_THEME_MENURADIO)
3699 DrawMenuImage(hdc, widgetRect, DFCS_MENUBULLET, color);
3700 else if (aWidgetType == NS_THEME_MENUARROW)
3701 DrawMenuImage(hdc, widgetRect,
3702 (state & DFCS_RTL) ? DFCS_MENUARROWRIGHT : DFCS_MENUARROW,
3703 color);
3704 break;
3705 }
3706 case NS_THEME_MENUSEPARATOR: {
3707 // separators are offset by a bit (see menu.css)
3708 widgetRect.left++;
3709 widgetRect.right--;
3710
3711 // This magic number is brought to you by the value in menu.css
3712 widgetRect.top += 4;
3713 // Our rectangles are 1 pixel high (see border size in menu.css)
3714 widgetRect.bottom = widgetRect.top + 1;
3715 ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_3DSHADOW + 1));
3716 widgetRect.top++;
3717 widgetRect.bottom++;
3718 ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_3DHILIGHT + 1));
3719 break;
3720 }
3721
3722 case NS_THEME_WINDOW_TITLEBAR:
3723 case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED: {
3724 RECT rect = widgetRect;
3725 int32_t offset = GetSystemMetrics(SM_CXFRAME);
3726
3727 // first fill the area to the color of the window background
3728 FillRect(hdc, &rect, (HBRUSH)(COLOR_3DFACE + 1));
3729
3730 // inset the caption area so it doesn't overflow.
3731 rect.top += offset;
3732 // if enabled, draw a gradient titlebar background, otherwise
3733 // fill with a solid color.
3734 BOOL bFlag = TRUE;
3735 SystemParametersInfo(SPI_GETGRADIENTCAPTIONS, 0, &bFlag, 0);
3736 if (!bFlag) {
3737 if (state == mozilla::widget::themeconst::FS_ACTIVE)
3738 FillRect(hdc, &rect, (HBRUSH)(COLOR_ACTIVECAPTION + 1));
3739 else
3740 FillRect(hdc, &rect, (HBRUSH)(COLOR_INACTIVECAPTION + 1));
3741 } else {
3742 DWORD startColor, endColor;
3743 if (state == mozilla::widget::themeconst::FS_ACTIVE) {
3744 startColor = GetSysColor(COLOR_ACTIVECAPTION);
3745 endColor = GetSysColor(COLOR_GRADIENTACTIVECAPTION);
3746 } else {
3747 startColor = GetSysColor(COLOR_INACTIVECAPTION);
3748 endColor = GetSysColor(COLOR_GRADIENTINACTIVECAPTION);
3749 }
3750
3751 TRIVERTEX vertex[2];
3752 vertex[0].x = rect.left;
3753 vertex[0].y = rect.top;
3754 vertex[0].Red = GetRValue(startColor) << 8;
3755 vertex[0].Green = GetGValue(startColor) << 8;
3756 vertex[0].Blue = GetBValue(startColor) << 8;
3757 vertex[0].Alpha = 0;
3758
3759 vertex[1].x = rect.right;
3760 vertex[1].y = rect.bottom;
3761 vertex[1].Red = GetRValue(endColor) << 8;
3762 vertex[1].Green = GetGValue(endColor) << 8;
3763 vertex[1].Blue = GetBValue(endColor) << 8;
3764 vertex[1].Alpha = 0;
3765
3766 GRADIENT_RECT gRect;
3767 gRect.UpperLeft = 0;
3768 gRect.LowerRight = 1;
3769 // available on win2k & up
3770 GradientFill(hdc, vertex, 2, &gRect, 1, GRADIENT_FILL_RECT_H);
3771 }
3772
3773 if (aWidgetType == NS_THEME_WINDOW_TITLEBAR) {
3774 // frame things up with a top raised border.
3775 DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_TOP);
3776 }
3777 break;
3778 }
3779
3780 case NS_THEME_WINDOW_FRAME_LEFT:
3781 DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_LEFT);
3782 break;
3783
3784 case NS_THEME_WINDOW_FRAME_RIGHT:
3785 DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_RIGHT);
3786 break;
3787
3788 case NS_THEME_WINDOW_FRAME_BOTTOM:
3789 DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_BOTTOM);
3790 break;
3791
3792 case NS_THEME_WINDOW_BUTTON_CLOSE:
3793 case NS_THEME_WINDOW_BUTTON_MINIMIZE:
3794 case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
3795 case NS_THEME_WINDOW_BUTTON_RESTORE: {
3796 if (aWidgetType == NS_THEME_WINDOW_BUTTON_MINIMIZE) {
3797 OffsetBackgroundRect(widgetRect, CAPTIONBUTTON_MINIMIZE);
3798 } else if (aWidgetType == NS_THEME_WINDOW_BUTTON_MAXIMIZE ||
3799 aWidgetType == NS_THEME_WINDOW_BUTTON_RESTORE) {
3800 OffsetBackgroundRect(widgetRect, CAPTIONBUTTON_RESTORE);
3801 } else if (aWidgetType == NS_THEME_WINDOW_BUTTON_CLOSE) {
3802 OffsetBackgroundRect(widgetRect, CAPTIONBUTTON_CLOSE);
3803 }
3804 int32_t oldTA = SetTextAlign(hdc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
3805 DrawFrameControl(hdc, &widgetRect, part, state);
3806 SetTextAlign(hdc, oldTA);
3807 break;
3808 }
3809
3810 default:
3811 rv = NS_ERROR_FAILURE;
3812 break;
3813 }
3814
3815 nativeDrawing.EndNativeDrawing();
3816
3817 if (NS_FAILED(rv)) return rv;
3818
3819 if (nativeDrawing.ShouldRenderAgain()) goto RENDER_AGAIN;
3820
3821 nativeDrawing.PaintToContext();
3822
3823 return rv;
3824 }
3825
GetWidgetNativeDrawingFlags(uint8_t aWidgetType)3826 uint32_t nsNativeThemeWin::GetWidgetNativeDrawingFlags(uint8_t aWidgetType) {
3827 switch (aWidgetType) {
3828 case NS_THEME_BUTTON:
3829 case NS_THEME_NUMBER_INPUT:
3830 case NS_THEME_FOCUS_OUTLINE:
3831 case NS_THEME_TEXTFIELD:
3832 case NS_THEME_TEXTFIELD_MULTILINE:
3833
3834 case NS_THEME_MENULIST:
3835 case NS_THEME_MENULIST_TEXTFIELD:
3836 return gfxWindowsNativeDrawing::CANNOT_DRAW_TO_COLOR_ALPHA |
3837 gfxWindowsNativeDrawing::CAN_AXIS_ALIGNED_SCALE |
3838 gfxWindowsNativeDrawing::CANNOT_COMPLEX_TRANSFORM;
3839
3840 // need to check these others
3841 case NS_THEME_RANGE:
3842 case NS_THEME_RANGE_THUMB:
3843 case NS_THEME_SCROLLBARBUTTON_UP:
3844 case NS_THEME_SCROLLBARBUTTON_DOWN:
3845 case NS_THEME_SCROLLBARBUTTON_LEFT:
3846 case NS_THEME_SCROLLBARBUTTON_RIGHT:
3847 case NS_THEME_SCROLLBARTHUMB_VERTICAL:
3848 case NS_THEME_SCROLLBARTHUMB_HORIZONTAL:
3849 case NS_THEME_SCROLLBAR_VERTICAL:
3850 case NS_THEME_SCROLLBAR_HORIZONTAL:
3851 case NS_THEME_SCALE_HORIZONTAL:
3852 case NS_THEME_SCALE_VERTICAL:
3853 case NS_THEME_SCALETHUMB_HORIZONTAL:
3854 case NS_THEME_SCALETHUMB_VERTICAL:
3855 case NS_THEME_INNER_SPIN_BUTTON:
3856 case NS_THEME_SPINNER_UPBUTTON:
3857 case NS_THEME_SPINNER_DOWNBUTTON:
3858 case NS_THEME_LISTBOX:
3859 case NS_THEME_TREEVIEW:
3860 case NS_THEME_TOOLTIP:
3861 case NS_THEME_STATUSBAR:
3862 case NS_THEME_STATUSBARPANEL:
3863 case NS_THEME_RESIZERPANEL:
3864 case NS_THEME_RESIZER:
3865 case NS_THEME_PROGRESSBAR:
3866 case NS_THEME_PROGRESSBAR_VERTICAL:
3867 case NS_THEME_PROGRESSCHUNK:
3868 case NS_THEME_PROGRESSCHUNK_VERTICAL:
3869 case NS_THEME_TAB:
3870 case NS_THEME_TABPANEL:
3871 case NS_THEME_TABPANELS:
3872 case NS_THEME_MENUBAR:
3873 case NS_THEME_MENUPOPUP:
3874 case NS_THEME_MENUITEM:
3875 break;
3876
3877 // the dropdown button /almost/ renders correctly with scaling,
3878 // except that the graphic in the dropdown button (the downward arrow)
3879 // doesn't get scaled up.
3880 case NS_THEME_MENULIST_BUTTON:
3881 // these are definitely no; they're all graphics that don't get scaled up
3882 case NS_THEME_CHECKBOX:
3883 case NS_THEME_RADIO:
3884 case NS_THEME_GROUPBOX:
3885 case NS_THEME_CHECKMENUITEM:
3886 case NS_THEME_RADIOMENUITEM:
3887 case NS_THEME_MENUCHECKBOX:
3888 case NS_THEME_MENURADIO:
3889 case NS_THEME_MENUARROW:
3890 return gfxWindowsNativeDrawing::CANNOT_DRAW_TO_COLOR_ALPHA |
3891 gfxWindowsNativeDrawing::CANNOT_AXIS_ALIGNED_SCALE |
3892 gfxWindowsNativeDrawing::CANNOT_COMPLEX_TRANSFORM;
3893 }
3894
3895 return gfxWindowsNativeDrawing::CANNOT_DRAW_TO_COLOR_ALPHA |
3896 gfxWindowsNativeDrawing::CANNOT_AXIS_ALIGNED_SCALE |
3897 gfxWindowsNativeDrawing::CANNOT_COMPLEX_TRANSFORM;
3898 }
3899
3900 ///////////////////////////////////////////
3901 // Creation Routine
3902 ///////////////////////////////////////////
3903
3904 // from nsWindow.cpp
3905 extern bool gDisableNativeTheme;
3906
NS_NewNativeTheme(nsISupports * aOuter,REFNSIID aIID,void ** aResult)3907 nsresult NS_NewNativeTheme(nsISupports* aOuter, REFNSIID aIID, void** aResult) {
3908 if (gDisableNativeTheme) return NS_ERROR_NO_INTERFACE;
3909
3910 if (aOuter) return NS_ERROR_NO_AGGREGATION;
3911
3912 nsNativeThemeWin* theme = new nsNativeThemeWin();
3913 if (!theme) return NS_ERROR_OUT_OF_MEMORY;
3914 return theme->QueryInterface(aIID, aResult);
3915 }
3916