forked from mirrors/gecko-dev
		
	 fb06f366ae
			
		
	
	
		fb06f366ae
		
	
	
	
	
		
			
			Most of this code is already dead. The native appearance on macOS doesn't work on dark mode, and on Windows and Linux we are already overriding it. Add a first-column tree property to be able to align the inner borders on macOS properly. Differential Revision: https://phabricator.services.mozilla.com/D206526
		
			
				
	
	
		
			1912 lines
		
	
	
	
		
			66 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1912 lines
		
	
	
	
		
			66 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #include "nsNativeThemeWin.h"
 | |
| 
 | |
| #include <algorithm>
 | |
| #include <malloc.h>
 | |
| 
 | |
| #include "gfxContext.h"
 | |
| #include "gfxPlatform.h"
 | |
| #include "gfxWindowsNativeDrawing.h"
 | |
| #include "gfxWindowsPlatform.h"
 | |
| #include "gfxWindowsSurface.h"
 | |
| #include "mozilla/ClearOnShutdown.h"
 | |
| #include "mozilla/gfx/Types.h"  // for Color::FromABGR
 | |
| #include "mozilla/Logging.h"
 | |
| #include "mozilla/RelativeLuminanceUtils.h"
 | |
| #include "mozilla/StaticPrefs_layout.h"
 | |
| #include "mozilla/StaticPrefs_widget.h"
 | |
| #include "mozilla/dom/XULButtonElement.h"
 | |
| #include "nsColor.h"
 | |
| #include "nsComboboxControlFrame.h"
 | |
| #include "nsDeviceContext.h"
 | |
| #include "nsGkAtoms.h"
 | |
| #include "nsIContent.h"
 | |
| #include "nsIContentInlines.h"
 | |
| #include "nsIFrame.h"
 | |
| #include "nsLayoutUtils.h"
 | |
| #include "nsLookAndFeel.h"
 | |
| #include "nsNameSpaceManager.h"
 | |
| #include "Theme.h"
 | |
| #include "nsPresContext.h"
 | |
| #include "nsRect.h"
 | |
| #include "nsSize.h"
 | |
| #include "nsStyleConsts.h"
 | |
| #include "nsTransform2D.h"
 | |
| #include "nsWindow.h"
 | |
| #include "prinrval.h"
 | |
| #include "WinUtils.h"
 | |
| 
 | |
| using namespace mozilla;
 | |
| using namespace mozilla::gfx;
 | |
| using namespace mozilla::widget;
 | |
| 
 | |
| using ElementState = dom::ElementState;
 | |
| 
 | |
| extern mozilla::LazyLogModule gWindowsLog;
 | |
| 
 | |
| namespace mozilla::widget {
 | |
| 
 | |
| nsNativeThemeWin::nsNativeThemeWin()
 | |
|     : Theme(ScrollbarStyle()),
 | |
|       mProgressDeterminateTimeStamp(TimeStamp::Now()),
 | |
|       mProgressIndeterminateTimeStamp(TimeStamp::Now()),
 | |
|       mBorderCacheValid(),
 | |
|       mMinimumWidgetSizeCacheValid(),
 | |
|       mGutterSizeCacheValid(false) {}
 | |
| 
 | |
| nsNativeThemeWin::~nsNativeThemeWin() { nsUXThemeData::Invalidate(); }
 | |
| 
 | |
| bool nsNativeThemeWin::IsWidgetAlwaysNonNative(nsIFrame* aFrame,
 | |
|                                                StyleAppearance aAppearance) {
 | |
|   return Theme::IsWidgetAlwaysNonNative(aFrame, aAppearance) ||
 | |
|          aAppearance == StyleAppearance::Checkbox ||
 | |
|          aAppearance == StyleAppearance::Radio ||
 | |
|          aAppearance == StyleAppearance::MozMenulistArrowButton ||
 | |
|          aAppearance == StyleAppearance::SpinnerUpbutton ||
 | |
|          aAppearance == StyleAppearance::SpinnerDownbutton;
 | |
| }
 | |
| 
 | |
| auto nsNativeThemeWin::IsWidgetNonNative(nsIFrame* aFrame,
 | |
|                                          StyleAppearance aAppearance)
 | |
|     -> NonNative {
 | |
|   if (IsWidgetAlwaysNonNative(aFrame, aAppearance)) {
 | |
|     return NonNative::Always;
 | |
|   }
 | |
| 
 | |
|   // We only know how to draw light widgets, so we defer to the non-native
 | |
|   // theme when appropriate.
 | |
|   if (Theme::ThemeSupportsWidget(aFrame->PresContext(), aFrame, aAppearance) &&
 | |
|       LookAndFeel::ColorSchemeForFrame(aFrame) ==
 | |
|           LookAndFeel::ColorScheme::Dark) {
 | |
|     return NonNative::BecauseColorMismatch;
 | |
|   }
 | |
|   return NonNative::No;
 | |
| }
 | |
| 
 | |
| static MARGINS GetCheckboxMargins(HANDLE theme, HDC hdc) {
 | |
|   MARGINS checkboxContent = {0};
 | |
|   GetThemeMargins(theme, hdc, MENU_POPUPCHECK, MCB_NORMAL, TMT_CONTENTMARGINS,
 | |
|                   nullptr, &checkboxContent);
 | |
|   return checkboxContent;
 | |
| }
 | |
| 
 | |
| static SIZE GetCheckboxBGSize(HANDLE theme, HDC hdc) {
 | |
|   SIZE checkboxSize;
 | |
|   GetThemePartSize(theme, hdc, MENU_POPUPCHECK, MC_CHECKMARKNORMAL, nullptr,
 | |
|                    TS_TRUE, &checkboxSize);
 | |
| 
 | |
|   MARGINS checkboxMargins = GetCheckboxMargins(theme, hdc);
 | |
| 
 | |
|   int leftMargin = checkboxMargins.cxLeftWidth;
 | |
|   int rightMargin = checkboxMargins.cxRightWidth;
 | |
|   int topMargin = checkboxMargins.cyTopHeight;
 | |
|   int bottomMargin = checkboxMargins.cyBottomHeight;
 | |
| 
 | |
|   int width = leftMargin + checkboxSize.cx + rightMargin;
 | |
|   int height = topMargin + checkboxSize.cy + bottomMargin;
 | |
|   SIZE ret;
 | |
|   ret.cx = width;
 | |
|   ret.cy = height;
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| static SIZE GetCheckboxBGBounds(HANDLE theme, HDC hdc) {
 | |
|   MARGINS checkboxBGSizing = {0};
 | |
|   MARGINS checkboxBGContent = {0};
 | |
|   GetThemeMargins(theme, hdc, MENU_POPUPCHECKBACKGROUND, MCB_NORMAL,
 | |
|                   TMT_SIZINGMARGINS, nullptr, &checkboxBGSizing);
 | |
|   GetThemeMargins(theme, hdc, MENU_POPUPCHECKBACKGROUND, MCB_NORMAL,
 | |
|                   TMT_CONTENTMARGINS, nullptr, &checkboxBGContent);
 | |
| 
 | |
| #define posdx(d) ((d) > 0 ? d : 0)
 | |
| 
 | |
|   int dx =
 | |
|       posdx(checkboxBGContent.cxRightWidth - checkboxBGSizing.cxRightWidth) +
 | |
|       posdx(checkboxBGContent.cxLeftWidth - checkboxBGSizing.cxLeftWidth);
 | |
|   int dy =
 | |
|       posdx(checkboxBGContent.cyTopHeight - checkboxBGSizing.cyTopHeight) +
 | |
|       posdx(checkboxBGContent.cyBottomHeight - checkboxBGSizing.cyBottomHeight);
 | |
| 
 | |
| #undef posdx
 | |
| 
 | |
|   SIZE ret(GetCheckboxBGSize(theme, hdc));
 | |
|   ret.cx += dx;
 | |
|   ret.cy += dy;
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| static SIZE GetGutterSize(HANDLE theme, HDC hdc) {
 | |
|   SIZE gutterSize;
 | |
|   GetThemePartSize(theme, hdc, MENU_POPUPGUTTER, 0, nullptr, TS_TRUE,
 | |
|                    &gutterSize);
 | |
| 
 | |
|   SIZE checkboxBGSize(GetCheckboxBGBounds(theme, hdc));
 | |
| 
 | |
|   SIZE itemSize;
 | |
|   GetThemePartSize(theme, hdc, MENU_POPUPITEM, MPI_NORMAL, nullptr, TS_TRUE,
 | |
|                    &itemSize);
 | |
| 
 | |
|   // Figure out how big the menuitem's icon will be (if present) at current DPI
 | |
|   // Needs the system scale for consistency with Windows Theme API.
 | |
|   double scaleFactor = WinUtils::SystemScaleFactor();
 | |
|   int iconDevicePixels = NSToIntRound(16 * scaleFactor);
 | |
|   SIZE iconSize = {iconDevicePixels, iconDevicePixels};
 | |
|   // Not really sure what margins should be used here, but this seems to work in
 | |
|   // practice...
 | |
|   MARGINS margins = {0};
 | |
|   GetThemeMargins(theme, hdc, MENU_POPUPCHECKBACKGROUND, MCB_NORMAL,
 | |
|                   TMT_CONTENTMARGINS, nullptr, &margins);
 | |
|   iconSize.cx += margins.cxLeftWidth + margins.cxRightWidth;
 | |
|   iconSize.cy += margins.cyTopHeight + margins.cyBottomHeight;
 | |
| 
 | |
|   int width = std::max(
 | |
|       itemSize.cx, std::max(iconSize.cx, checkboxBGSize.cx) + gutterSize.cx);
 | |
|   int height = std::max(itemSize.cy, std::max(iconSize.cy, checkboxBGSize.cy));
 | |
| 
 | |
|   SIZE ret;
 | |
|   ret.cx = width;
 | |
|   ret.cy = height;
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| SIZE nsNativeThemeWin::GetCachedGutterSize(HANDLE theme) {
 | |
|   if (mGutterSizeCacheValid) {
 | |
|     return mGutterSizeCache;
 | |
|   }
 | |
| 
 | |
|   mGutterSizeCache = GetGutterSize(theme, nullptr);
 | |
|   mGutterSizeCacheValid = true;
 | |
| 
 | |
|   return mGutterSizeCache;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Notes on progress track and meter part constants:
 | |
|  * xp and up:
 | |
|  * PP_BAR(_VERT)            - base progress track
 | |
|  * PP_TRANSPARENTBAR(_VERT) - transparent progress track. this only works if
 | |
|  *                            the underlying surface supports alpha. otherwise
 | |
|  *                            theme lib's DrawThemeBackground falls back on
 | |
|  *                            opaque PP_BAR. we currently don't use this.
 | |
|  * PP_CHUNK(_VERT)          - xp progress meter. this does not draw an xp style
 | |
|  *                            progress w/chunks, it draws fill using the chunk
 | |
|  *                            graphic.
 | |
|  * vista and up:
 | |
|  * PP_FILL(_VERT)           - progress meter. these have four states/colors.
 | |
|  * PP_PULSEOVERLAY(_VERT)   - white reflection - an overlay, not sure what this
 | |
|  *                            is used for.
 | |
|  * PP_MOVEOVERLAY(_VERT)    - green pulse - the pulse effect overlay on
 | |
|  *                            determined progress bars. we also use this for
 | |
|  *                            indeterminate chunk.
 | |
|  *
 | |
|  * Notes on state constants:
 | |
|  * PBBS_NORMAL               - green progress
 | |
|  * PBBVS_PARTIAL/PBFVS_ERROR - red error progress
 | |
|  * PBFS_PAUSED               - yellow paused progress
 | |
|  *
 | |
|  * There is no common controls style indeterminate part on vista and up.
 | |
|  */
 | |
| 
 | |
| /*
 | |
|  * Progress bar related constants. These values are found by experimenting and
 | |
|  * comparing against native widgets used by the system. They are very unlikely
 | |
|  * exact but try to not be too wrong.
 | |
|  */
 | |
| // The amount of time we animate progress meters parts across the frame.
 | |
| static const double kProgressDeterminateTimeSpan = 3.0;
 | |
| static const double kProgressIndeterminateTimeSpan = 5.0;
 | |
| // The width of the overlay used to animate the horizontal progress bar (Vista
 | |
| // and later).
 | |
| static const int32_t kProgressHorizontalOverlaySize = 120;
 | |
| // The height of the overlay used to animate the vertical progress bar (Vista
 | |
| // and later).
 | |
| static const int32_t kProgressVerticalOverlaySize = 45;
 | |
| // The height of the overlay used for the vertical indeterminate progress bar
 | |
| // (Vista and later).
 | |
| static const int32_t kProgressVerticalIndeterminateOverlaySize = 60;
 | |
| // The width of the overlay used to animate the indeterminate progress bar
 | |
| // (Windows Classic).
 | |
| static const int32_t kProgressClassicOverlaySize = 40;
 | |
| 
 | |
| /*
 | |
|  * GetProgressOverlayStyle - returns the proper overlay part for themed
 | |
|  * progress bars based on os and orientation.
 | |
|  */
 | |
| static int32_t GetProgressOverlayStyle(bool aIsVertical) {
 | |
|   return aIsVertical ? PP_MOVEOVERLAYVERT : PP_MOVEOVERLAY;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * GetProgressOverlaySize - returns the minimum width or height for themed
 | |
|  * progress bar overlays. This includes the width of indeterminate chunks
 | |
|  * and vista pulse overlays.
 | |
|  */
 | |
| static int32_t GetProgressOverlaySize(bool aIsVertical, bool aIsIndeterminate) {
 | |
|   if (aIsVertical) {
 | |
|     return aIsIndeterminate ? kProgressVerticalIndeterminateOverlaySize
 | |
|                             : kProgressVerticalOverlaySize;
 | |
|   }
 | |
|   return kProgressHorizontalOverlaySize;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * IsProgressMeterFilled - Determines if a progress meter is at 100% fill based
 | |
|  * on a comparison of the current value and maximum.
 | |
|  */
 | |
| static bool IsProgressMeterFilled(nsIFrame* aFrame) {
 | |
|   NS_ENSURE_TRUE(aFrame, false);
 | |
|   nsIFrame* parentFrame = aFrame->GetParent();
 | |
|   NS_ENSURE_TRUE(parentFrame, false);
 | |
|   return nsNativeTheme::GetProgressValue(parentFrame) ==
 | |
|          nsNativeTheme::GetProgressMaxValue(parentFrame);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * CalculateProgressOverlayRect - returns the padded overlay animation rect
 | |
|  * used in rendering progress bars. Resulting rects are used in rendering
 | |
|  * vista+ pulse overlays and indeterminate progress meters. Graphics should
 | |
|  * be rendered at the origin.
 | |
|  */
 | |
| RECT nsNativeThemeWin::CalculateProgressOverlayRect(nsIFrame* aFrame,
 | |
|                                                     RECT* aWidgetRect,
 | |
|                                                     bool aIsVertical,
 | |
|                                                     bool aIsIndeterminate,
 | |
|                                                     bool aIsClassic) {
 | |
|   NS_ASSERTION(aFrame, "bad frame pointer");
 | |
|   NS_ASSERTION(aWidgetRect, "bad rect pointer");
 | |
| 
 | |
|   int32_t frameSize = aIsVertical ? aWidgetRect->bottom - aWidgetRect->top
 | |
|                                   : aWidgetRect->right - aWidgetRect->left;
 | |
| 
 | |
|   // Recycle a set of progress pulse timers - these timers control the position
 | |
|   // of all progress overlays and indeterminate chunks that get rendered.
 | |
|   double span = aIsIndeterminate ? kProgressIndeterminateTimeSpan
 | |
|                                  : kProgressDeterminateTimeSpan;
 | |
|   TimeDuration period;
 | |
|   if (!aIsIndeterminate) {
 | |
|     if (TimeStamp::Now() >
 | |
|         (mProgressDeterminateTimeStamp + TimeDuration::FromSeconds(span))) {
 | |
|       mProgressDeterminateTimeStamp = TimeStamp::Now();
 | |
|     }
 | |
|     period = TimeStamp::Now() - mProgressDeterminateTimeStamp;
 | |
|   } else {
 | |
|     if (TimeStamp::Now() >
 | |
|         (mProgressIndeterminateTimeStamp + TimeDuration::FromSeconds(span))) {
 | |
|       mProgressIndeterminateTimeStamp = TimeStamp::Now();
 | |
|     }
 | |
|     period = TimeStamp::Now() - mProgressIndeterminateTimeStamp;
 | |
|   }
 | |
| 
 | |
|   double percent = period / TimeDuration::FromSeconds(span);
 | |
| 
 | |
|   if (!aIsVertical && IsFrameRTL(aFrame)) percent = 1 - percent;
 | |
| 
 | |
|   RECT overlayRect = *aWidgetRect;
 | |
|   int32_t overlaySize;
 | |
|   if (!aIsClassic) {
 | |
|     overlaySize = GetProgressOverlaySize(aIsVertical, aIsIndeterminate);
 | |
|   } else {
 | |
|     overlaySize = kProgressClassicOverlaySize;
 | |
|   }
 | |
| 
 | |
|   // Calculate a bounds that is larger than the meters frame such that the
 | |
|   // overlay starts and ends completely off the edge of the frame:
 | |
|   // [overlay][frame][overlay]
 | |
|   // This also yields a nice delay on rotation. Use overlaySize as the minimum
 | |
|   // size for [overlay] based on the graphics dims. If [frame] is larger, use
 | |
|   // the frame size instead.
 | |
|   int trackWidth = frameSize > overlaySize ? frameSize : overlaySize;
 | |
|   if (!aIsVertical) {
 | |
|     int xPos = aWidgetRect->left - trackWidth;
 | |
|     xPos += (int)ceil(((double)(trackWidth * 2) * percent));
 | |
|     overlayRect.left = xPos;
 | |
|     overlayRect.right = xPos + overlaySize;
 | |
|   } else {
 | |
|     int yPos = aWidgetRect->bottom + trackWidth;
 | |
|     yPos -= (int)ceil(((double)(trackWidth * 2) * percent));
 | |
|     overlayRect.bottom = yPos;
 | |
|     overlayRect.top = yPos - overlaySize;
 | |
|   }
 | |
|   return overlayRect;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * DrawProgressMeter - render an appropriate progress meter based on progress
 | |
|  * meter style, orientation, and os. Note, this does not render the underlying
 | |
|  * progress track.
 | |
|  *
 | |
|  * @param aFrame       the widget frame
 | |
|  * @param aAppearance  type of widget
 | |
|  * @param aTheme       progress theme handle
 | |
|  * @param aHdc         hdc returned by gfxWindowsNativeDrawing
 | |
|  * @param aPart        the PP_X progress part
 | |
|  * @param aState       the theme state
 | |
|  * @param aWidgetRect  bounding rect for the widget
 | |
|  * @param aClipRect    dirty rect that needs drawing.
 | |
|  * @param aAppUnits    app units per device pixel
 | |
|  */
 | |
| void nsNativeThemeWin::DrawThemedProgressMeter(
 | |
|     nsIFrame* aFrame, StyleAppearance aAppearance, HANDLE aTheme, HDC aHdc,
 | |
|     int aPart, int aState, RECT* aWidgetRect, RECT* aClipRect) {
 | |
|   if (!aFrame || !aTheme || !aHdc) return;
 | |
| 
 | |
|   NS_ASSERTION(aWidgetRect, "bad rect pointer");
 | |
|   NS_ASSERTION(aClipRect, "bad clip rect pointer");
 | |
| 
 | |
|   RECT adjWidgetRect, adjClipRect;
 | |
|   adjWidgetRect = *aWidgetRect;
 | |
|   adjClipRect = *aClipRect;
 | |
| 
 | |
|   nsIFrame* parentFrame = aFrame->GetParent();
 | |
|   if (!parentFrame) {
 | |
|     // We have no parent to work with, just bail.
 | |
|     NS_WARNING("No parent frame for progress rendering. Can't paint.");
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   ElementState elementState = GetContentState(parentFrame, aAppearance);
 | |
|   bool vertical = IsVerticalProgress(parentFrame);
 | |
|   bool indeterminate = elementState.HasState(ElementState::INDETERMINATE);
 | |
|   bool animate = indeterminate;
 | |
| 
 | |
|   // Vista and up progress meter is fill style, rendered here. We render
 | |
|   // the pulse overlay in the follow up section below.
 | |
|   DrawThemeBackground(aTheme, aHdc, aPart, aState, &adjWidgetRect,
 | |
|                       &adjClipRect);
 | |
|   if (!IsProgressMeterFilled(aFrame)) {
 | |
|     animate = true;
 | |
|   }
 | |
| 
 | |
|   if (animate) {
 | |
|     // Indeterminate rendering
 | |
|     int32_t overlayPart = GetProgressOverlayStyle(vertical);
 | |
|     RECT overlayRect = CalculateProgressOverlayRect(
 | |
|         aFrame, &adjWidgetRect, vertical, indeterminate, false);
 | |
|     DrawThemeBackground(aTheme, aHdc, overlayPart, aState, &overlayRect,
 | |
|                         &adjClipRect);
 | |
| 
 | |
|     if (!QueueAnimatedContentForRefresh(aFrame->GetContent(), 60)) {
 | |
|       NS_WARNING("unable to animate progress widget!");
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| LayoutDeviceIntMargin nsNativeThemeWin::GetCachedWidgetBorder(
 | |
|     HTHEME aTheme, nsUXThemeClass aThemeClass, StyleAppearance aAppearance,
 | |
|     int32_t aPart, int32_t aState) {
 | |
|   int32_t cacheIndex = aThemeClass * THEME_PART_DISTINCT_VALUE_COUNT + aPart;
 | |
|   int32_t cacheBitIndex = cacheIndex / 8;
 | |
|   uint8_t cacheBit = 1u << (cacheIndex % 8);
 | |
| 
 | |
|   if (mBorderCacheValid[cacheBitIndex] & cacheBit) {
 | |
|     return mBorderCache[cacheIndex];
 | |
|   }
 | |
| 
 | |
|   // Get our info.
 | |
|   RECT outerRect;  // Create a fake outer rect.
 | |
|   outerRect.top = outerRect.left = 100;
 | |
|   outerRect.right = outerRect.bottom = 200;
 | |
|   RECT contentRect(outerRect);
 | |
|   HRESULT res = GetThemeBackgroundContentRect(aTheme, nullptr, aPart, aState,
 | |
|                                               &outerRect, &contentRect);
 | |
| 
 | |
|   if (FAILED(res)) {
 | |
|     return LayoutDeviceIntMargin();
 | |
|   }
 | |
| 
 | |
|   // Now compute the delta in each direction and place it in our
 | |
|   // nsIntMargin struct.
 | |
|   LayoutDeviceIntMargin result;
 | |
|   result.top = contentRect.top - outerRect.top;
 | |
|   result.bottom = outerRect.bottom - contentRect.bottom;
 | |
|   result.left = contentRect.left - outerRect.left;
 | |
|   result.right = outerRect.right - contentRect.right;
 | |
| 
 | |
|   mBorderCacheValid[cacheBitIndex] |= cacheBit;
 | |
|   mBorderCache[cacheIndex] = result;
 | |
| 
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| nsresult nsNativeThemeWin::GetCachedMinimumWidgetSize(
 | |
|     nsIFrame* aFrame, HANDLE aTheme, nsUXThemeClass aThemeClass,
 | |
|     StyleAppearance aAppearance, int32_t aPart, int32_t aState,
 | |
|     THEMESIZE aSizeReq, mozilla::LayoutDeviceIntSize* aResult) {
 | |
|   int32_t cachePart = aPart;
 | |
| 
 | |
|   if (aAppearance == StyleAppearance::Button && aSizeReq == TS_MIN) {
 | |
|     // In practice, StyleAppearance::Button is the only widget type which has an
 | |
|     // aSizeReq that varies for us, and it can only be TS_MIN or TS_TRUE. Just
 | |
|     // stuff that extra bit into the aPart part of the cache, since BP_Count is
 | |
|     // well below THEME_PART_DISTINCT_VALUE_COUNT anyway.
 | |
|     cachePart = BP_Count;
 | |
|   }
 | |
| 
 | |
|   MOZ_ASSERT(aPart < THEME_PART_DISTINCT_VALUE_COUNT);
 | |
|   int32_t cacheIndex =
 | |
|       aThemeClass * THEME_PART_DISTINCT_VALUE_COUNT + cachePart;
 | |
|   int32_t cacheBitIndex = cacheIndex / 8;
 | |
|   uint8_t cacheBit = 1u << (cacheIndex % 8);
 | |
| 
 | |
|   if (mMinimumWidgetSizeCacheValid[cacheBitIndex] & cacheBit) {
 | |
|     *aResult = mMinimumWidgetSizeCache[cacheIndex];
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   HDC hdc = ::GetDC(NULL);
 | |
|   if (!hdc) {
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
| 
 | |
|   SIZE sz;
 | |
|   GetThemePartSize(aTheme, hdc, aPart, aState, nullptr, aSizeReq, &sz);
 | |
|   aResult->width = sz.cx;
 | |
|   aResult->height = sz.cy;
 | |
| 
 | |
|   ::ReleaseDC(nullptr, hdc);
 | |
| 
 | |
|   mMinimumWidgetSizeCacheValid[cacheBitIndex] |= cacheBit;
 | |
|   mMinimumWidgetSizeCache[cacheIndex] = *aResult;
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| mozilla::Maybe<nsUXThemeClass> nsNativeThemeWin::GetThemeClass(
 | |
|     StyleAppearance aAppearance) {
 | |
|   switch (aAppearance) {
 | |
|     case StyleAppearance::Button:
 | |
|       return Some(eUXButton);
 | |
|     case StyleAppearance::NumberInput:
 | |
|     case StyleAppearance::PasswordInput:
 | |
|     case StyleAppearance::Textfield:
 | |
|     case StyleAppearance::Textarea:
 | |
|       return Some(eUXEdit);
 | |
|     case StyleAppearance::Toolbarbutton:
 | |
|     case StyleAppearance::Separator:
 | |
|       return Some(eUXToolbar);
 | |
|     case StyleAppearance::ProgressBar:
 | |
|     case StyleAppearance::Progresschunk:
 | |
|       return Some(eUXProgress);
 | |
|     case StyleAppearance::Tab:
 | |
|     case StyleAppearance::Tabpanel:
 | |
|     case StyleAppearance::Tabpanels:
 | |
|       return Some(eUXTab);
 | |
|     case StyleAppearance::Range:
 | |
|     case StyleAppearance::RangeThumb:
 | |
|       return Some(eUXTrackbar);
 | |
|     case StyleAppearance::Menulist:
 | |
|     case StyleAppearance::MenulistButton:
 | |
|       return Some(eUXCombobox);
 | |
|     case StyleAppearance::Listbox:
 | |
|       return Some(eUXListview);
 | |
|     default:
 | |
|       return Nothing();
 | |
|   }
 | |
| }
 | |
| 
 | |
| HANDLE
 | |
| nsNativeThemeWin::GetTheme(StyleAppearance aAppearance) {
 | |
|   mozilla::Maybe<nsUXThemeClass> themeClass = GetThemeClass(aAppearance);
 | |
|   if (themeClass.isNothing()) {
 | |
|     return nullptr;
 | |
|   }
 | |
|   return nsUXThemeData::GetTheme(themeClass.value());
 | |
| }
 | |
| 
 | |
| int32_t nsNativeThemeWin::StandardGetState(nsIFrame* aFrame,
 | |
|                                            StyleAppearance aAppearance,
 | |
|                                            bool wantFocused) {
 | |
|   ElementState elementState = GetContentState(aFrame, aAppearance);
 | |
|   if (elementState.HasAllStates(ElementState::HOVER | ElementState::ACTIVE)) {
 | |
|     return TS_ACTIVE;
 | |
|   }
 | |
|   if (elementState.HasState(ElementState::HOVER)) {
 | |
|     return TS_HOVER;
 | |
|   }
 | |
|   if (wantFocused) {
 | |
|     if (elementState.HasState(ElementState::FOCUSRING)) {
 | |
|       return TS_FOCUSED;
 | |
|     }
 | |
|     // On Windows, focused buttons are always drawn as such by the native
 | |
|     // theme, that's why we check ElementState::FOCUS instead of
 | |
|     // ElementState::FOCUSRING.
 | |
|     if (aAppearance == StyleAppearance::Button &&
 | |
|         elementState.HasState(ElementState::FOCUS)) {
 | |
|       return TS_FOCUSED;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return TS_NORMAL;
 | |
| }
 | |
| 
 | |
| bool nsNativeThemeWin::IsMenuActive(nsIFrame* aFrame,
 | |
|                                     StyleAppearance aAppearance) {
 | |
|   nsIContent* content = aFrame->GetContent();
 | |
|   if (content->IsXULElement() &&
 | |
|       content->NodeInfo()->Equals(nsGkAtoms::richlistitem))
 | |
|     return CheckBooleanAttr(aFrame, nsGkAtoms::selected);
 | |
| 
 | |
|   return CheckBooleanAttr(aFrame, nsGkAtoms::menuactive);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * aPart is filled in with the UXTheme part code. On return, values > 0
 | |
|  * are the actual UXTheme part code; -1 means the widget will be drawn by
 | |
|  * us; 0 means that we should use part code 0, which isn't a real part code
 | |
|  * but elicits some kind of default behaviour from UXTheme when drawing
 | |
|  * (but isThemeBackgroundPartiallyTransparent may not work).
 | |
|  */
 | |
| nsresult nsNativeThemeWin::GetThemePartAndState(nsIFrame* aFrame,
 | |
|                                                 StyleAppearance aAppearance,
 | |
|                                                 int32_t& aPart,
 | |
|                                                 int32_t& aState) {
 | |
|   switch (aAppearance) {
 | |
|     case StyleAppearance::Button: {
 | |
|       aPart = BP_BUTTON;
 | |
|       if (!aFrame) {
 | |
|         aState = TS_NORMAL;
 | |
|         return NS_OK;
 | |
|       }
 | |
| 
 | |
|       ElementState elementState = GetContentState(aFrame, aAppearance);
 | |
|       if (elementState.HasState(ElementState::DISABLED)) {
 | |
|         aState = TS_DISABLED;
 | |
|         return NS_OK;
 | |
|       }
 | |
|       if (IsOpenButton(aFrame) || IsCheckedButton(aFrame)) {
 | |
|         aState = TS_ACTIVE;
 | |
|         return NS_OK;
 | |
|       }
 | |
| 
 | |
|       aState = StandardGetState(aFrame, aAppearance, true);
 | |
| 
 | |
|       // Check for default dialog buttons.  These buttons should always look
 | |
|       // focused.
 | |
|       if (aState == TS_NORMAL && IsDefaultButton(aFrame)) aState = TS_FOCUSED;
 | |
|       return NS_OK;
 | |
|     }
 | |
|     case StyleAppearance::NumberInput:
 | |
|     case StyleAppearance::PasswordInput:
 | |
|     case StyleAppearance::Textfield:
 | |
|     case StyleAppearance::Textarea: {
 | |
|       ElementState elementState = GetContentState(aFrame, aAppearance);
 | |
| 
 | |
|       /* Note: the NOSCROLL type has a rounded corner in each corner.  The more
 | |
|        * specific HSCROLL, VSCROLL, HVSCROLL types have side and/or top/bottom
 | |
|        * edges rendered as straight horizontal lines with sharp corners to
 | |
|        * accommodate a scrollbar.  However, the scrollbar gets rendered on top
 | |
|        * of this for us, so we don't care, and can just use NOSCROLL here.
 | |
|        */
 | |
|       aPart = TFP_EDITBORDER_NOSCROLL;
 | |
| 
 | |
|       if (!aFrame) {
 | |
|         aState = TFS_EDITBORDER_NORMAL;
 | |
|       } else if (elementState.HasState(ElementState::DISABLED)) {
 | |
|         aState = TFS_EDITBORDER_DISABLED;
 | |
|       } else if (IsReadOnly(aFrame)) {
 | |
|         /* no special read-only state */
 | |
|         aState = TFS_EDITBORDER_NORMAL;
 | |
|       } else if (elementState.HasAtLeastOneOfStates(ElementState::ACTIVE |
 | |
|                                                     ElementState::FOCUSRING)) {
 | |
|         aState = TFS_EDITBORDER_FOCUSED;
 | |
|       } else if (elementState.HasState(ElementState::HOVER)) {
 | |
|         aState = TFS_EDITBORDER_HOVER;
 | |
|       } else {
 | |
|         aState = TFS_EDITBORDER_NORMAL;
 | |
|       }
 | |
| 
 | |
|       return NS_OK;
 | |
|     }
 | |
|     case StyleAppearance::ProgressBar: {
 | |
|       bool vertical = IsVerticalProgress(aFrame);
 | |
|       aPart = vertical ? PP_BARVERT : PP_BAR;
 | |
|       aState = PBBS_NORMAL;
 | |
|       return NS_OK;
 | |
|     }
 | |
|     case StyleAppearance::Progresschunk: {
 | |
|       nsIFrame* parentFrame = aFrame->GetParent();
 | |
|       if (IsVerticalProgress(parentFrame)) {
 | |
|         aPart = PP_FILLVERT;
 | |
|       } else {
 | |
|         aPart = PP_FILL;
 | |
|       }
 | |
| 
 | |
|       aState = PBBVS_NORMAL;
 | |
|       return NS_OK;
 | |
|     }
 | |
|     case StyleAppearance::Toolbarbutton: {
 | |
|       aPart = BP_BUTTON;
 | |
|       if (!aFrame) {
 | |
|         aState = TS_NORMAL;
 | |
|         return NS_OK;
 | |
|       }
 | |
| 
 | |
|       ElementState elementState = GetContentState(aFrame, aAppearance);
 | |
|       if (elementState.HasState(ElementState::DISABLED)) {
 | |
|         aState = TS_DISABLED;
 | |
|         return NS_OK;
 | |
|       }
 | |
|       if (IsOpenButton(aFrame)) {
 | |
|         aState = TS_ACTIVE;
 | |
|         return NS_OK;
 | |
|       }
 | |
| 
 | |
|       if (elementState.HasAllStates(ElementState::HOVER | ElementState::ACTIVE))
 | |
|         aState = TS_ACTIVE;
 | |
|       else if (elementState.HasState(ElementState::HOVER)) {
 | |
|         if (IsCheckedButton(aFrame))
 | |
|           aState = TB_HOVER_CHECKED;
 | |
|         else
 | |
|           aState = TS_HOVER;
 | |
|       } else {
 | |
|         if (IsCheckedButton(aFrame))
 | |
|           aState = TB_CHECKED;
 | |
|         else
 | |
|           aState = TS_NORMAL;
 | |
|       }
 | |
| 
 | |
|       return NS_OK;
 | |
|     }
 | |
|     case StyleAppearance::Separator: {
 | |
|       aPart = TP_SEPARATOR;
 | |
|       aState = TS_NORMAL;
 | |
|       return NS_OK;
 | |
|     }
 | |
|     case StyleAppearance::Range: {
 | |
|       if (IsRangeHorizontal(aFrame)) {
 | |
|         aPart = TKP_TRACK;
 | |
|         aState = TRS_NORMAL;
 | |
|       } else {
 | |
|         aPart = TKP_TRACKVERT;
 | |
|         aState = TRVS_NORMAL;
 | |
|       }
 | |
|       return NS_OK;
 | |
|     }
 | |
|     case StyleAppearance::RangeThumb: {
 | |
|       if (IsRangeHorizontal(aFrame)) {
 | |
|         aPart = TKP_THUMBBOTTOM;
 | |
|       } else {
 | |
|         aPart = IsFrameRTL(aFrame) ? TKP_THUMBLEFT : TKP_THUMBRIGHT;
 | |
|       }
 | |
|       ElementState elementState = GetContentState(aFrame, aAppearance);
 | |
|       if (!aFrame) {
 | |
|         aState = TS_NORMAL;
 | |
|       } else if (elementState.HasState(ElementState::DISABLED)) {
 | |
|         aState = TKP_DISABLED;
 | |
|       } else {
 | |
|         if (elementState.HasState(
 | |
|                 ElementState::ACTIVE))  // Hover is not also a requirement for
 | |
|                                         // the thumb, since the drag is not
 | |
|                                         // canceled when you move outside the
 | |
|                                         // thumb.
 | |
|           aState = TS_ACTIVE;
 | |
|         else if (elementState.HasState(ElementState::FOCUSRING))
 | |
|           aState = TKP_FOCUSED;
 | |
|         else if (elementState.HasState(ElementState::HOVER))
 | |
|           aState = TS_HOVER;
 | |
|         else
 | |
|           aState = TS_NORMAL;
 | |
|       }
 | |
|       return NS_OK;
 | |
|     }
 | |
|     case StyleAppearance::Listbox: {
 | |
|       aPart = TREEVIEW_BODY;
 | |
|       aState = TS_NORMAL;
 | |
|       return NS_OK;
 | |
|     }
 | |
|     case StyleAppearance::Tabpanels: {
 | |
|       aPart = TABP_PANELS;
 | |
|       aState = TS_NORMAL;
 | |
|       return NS_OK;
 | |
|     }
 | |
|     case StyleAppearance::Tabpanel: {
 | |
|       aPart = TABP_PANEL;
 | |
|       aState = TS_NORMAL;
 | |
|       return NS_OK;
 | |
|     }
 | |
|     case StyleAppearance::Tab: {
 | |
|       aPart = TABP_TAB;
 | |
|       if (!aFrame) {
 | |
|         aState = TS_NORMAL;
 | |
|         return NS_OK;
 | |
|       }
 | |
| 
 | |
|       ElementState elementState = GetContentState(aFrame, aAppearance);
 | |
|       if (elementState.HasState(ElementState::DISABLED)) {
 | |
|         aState = TS_DISABLED;
 | |
|         return NS_OK;
 | |
|       }
 | |
| 
 | |
|       if (IsSelectedTab(aFrame)) {
 | |
|         aPart = TABP_TAB_SELECTED;
 | |
|         aState = TS_ACTIVE;  // The selected tab is always "pressed".
 | |
|       } else
 | |
|         aState = StandardGetState(aFrame, aAppearance, true);
 | |
| 
 | |
|       return NS_OK;
 | |
|     }
 | |
|     case StyleAppearance::MenulistButton:
 | |
|     case StyleAppearance::Menulist: {
 | |
|       nsIContent* content = aFrame->GetContent();
 | |
|       bool useDropBorder = content && content->IsHTMLElement();
 | |
|       ElementState elementState = GetContentState(aFrame, aAppearance);
 | |
| 
 | |
|       /* On Vista/Win7, we use CBP_DROPBORDER instead of DROPFRAME for HTML
 | |
|        * content or for editable menulists; this gives us the thin outline,
 | |
|        * instead of the gradient-filled background */
 | |
|       if (useDropBorder)
 | |
|         aPart = CBP_DROPBORDER;
 | |
|       else
 | |
|         aPart = CBP_DROPFRAME;
 | |
| 
 | |
|       if (elementState.HasState(ElementState::DISABLED)) {
 | |
|         aState = TS_DISABLED;
 | |
|       } else if (IsReadOnly(aFrame)) {
 | |
|         aState = TS_NORMAL;
 | |
|       } else if (IsOpenButton(aFrame)) {
 | |
|         aState = TS_ACTIVE;
 | |
|       } else if (useDropBorder &&
 | |
|                  elementState.HasState(ElementState::FOCUSRING)) {
 | |
|         aState = TS_ACTIVE;
 | |
|       } else if (elementState.HasAllStates(ElementState::HOVER |
 | |
|                                            ElementState::ACTIVE)) {
 | |
|         aState = TS_ACTIVE;
 | |
|       } else if (elementState.HasState(ElementState::HOVER)) {
 | |
|         aState = TS_HOVER;
 | |
|       } else {
 | |
|         aState = TS_NORMAL;
 | |
|       }
 | |
| 
 | |
|       return NS_OK;
 | |
|     }
 | |
|     default:
 | |
|       aPart = 0;
 | |
|       aState = 0;
 | |
|       return NS_ERROR_FAILURE;
 | |
|   }
 | |
| }
 | |
| 
 | |
| static bool AssumeThemePartAndStateAreTransparent(int32_t aPart,
 | |
|                                                   int32_t aState) {
 | |
|   if (!nsUXThemeData::IsHighContrastOn() && aPart == MENU_POPUPITEM &&
 | |
|       aState == MBI_NORMAL) {
 | |
|     return true;
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| // When running with per-monitor DPI (on Win8.1+), and rendering on a display
 | |
| // with a different DPI setting from the system's default scaling, we need to
 | |
| // apply scaling to native-themed elements as the Windows theme APIs assume
 | |
| // the system default resolution.
 | |
| static inline double GetThemeDpiScaleFactor(nsPresContext* aPresContext) {
 | |
|   if (WinUtils::IsPerMonitorDPIAware() ||
 | |
|       StaticPrefs::layout_css_devPixelsPerPx() > 0.0) {
 | |
|     nsCOMPtr<nsIWidget> rootWidget = aPresContext->GetRootWidget();
 | |
|     if (rootWidget) {
 | |
|       double systemScale = WinUtils::SystemScaleFactor();
 | |
|       return rootWidget->GetDefaultScale().scale / systemScale;
 | |
|     }
 | |
|   }
 | |
|   return 1.0;
 | |
| }
 | |
| 
 | |
| static inline double GetThemeDpiScaleFactor(nsIFrame* aFrame) {
 | |
|   return GetThemeDpiScaleFactor(aFrame->PresContext());
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsNativeThemeWin::DrawWidgetBackground(gfxContext* aContext, nsIFrame* aFrame,
 | |
|                                        StyleAppearance aAppearance,
 | |
|                                        const nsRect& aRect,
 | |
|                                        const nsRect& aDirtyRect,
 | |
|                                        DrawOverflow aDrawOverflow) {
 | |
|   if (IsWidgetNonNative(aFrame, aAppearance) != NonNative::No) {
 | |
|     return Theme::DrawWidgetBackground(aContext, aFrame, aAppearance, aRect,
 | |
|                                        aDirtyRect, aDrawOverflow);
 | |
|   }
 | |
| 
 | |
|   HANDLE theme = GetTheme(aAppearance);
 | |
|   if (!theme)
 | |
|     return ClassicDrawWidgetBackground(aContext, aFrame, aAppearance, aRect,
 | |
|                                        aDirtyRect);
 | |
| 
 | |
|   // ^^ without the right sdk, assume xp theming and fall through.
 | |
|   int32_t part, state;
 | |
|   nsresult rv = GetThemePartAndState(aFrame, aAppearance, part, state);
 | |
|   if (NS_FAILED(rv)) return rv;
 | |
| 
 | |
|   if (AssumeThemePartAndStateAreTransparent(part, state)) {
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   gfxContextMatrixAutoSaveRestore save(aContext);
 | |
| 
 | |
|   double themeScale = GetThemeDpiScaleFactor(aFrame);
 | |
|   if (themeScale != 1.0) {
 | |
|     aContext->SetMatrix(
 | |
|         aContext->CurrentMatrix().PreScale(themeScale, themeScale));
 | |
|   }
 | |
| 
 | |
|   gfxFloat p2a = gfxFloat(aFrame->PresContext()->AppUnitsPerDevPixel());
 | |
|   RECT widgetRect;
 | |
|   RECT clipRect;
 | |
|   gfxRect tr(aRect.X(), aRect.Y(), aRect.Width(), aRect.Height()),
 | |
|       dr(aDirtyRect.X(), aDirtyRect.Y(), aDirtyRect.Width(),
 | |
|          aDirtyRect.Height());
 | |
| 
 | |
|   tr.Scale(1.0 / (p2a * themeScale));
 | |
|   dr.Scale(1.0 / (p2a * themeScale));
 | |
| 
 | |
|   gfxWindowsNativeDrawing nativeDrawing(
 | |
|       aContext, dr, GetWidgetNativeDrawingFlags(aAppearance));
 | |
| 
 | |
| RENDER_AGAIN:
 | |
| 
 | |
|   HDC hdc = nativeDrawing.BeginNativeDrawing();
 | |
|   if (!hdc) return NS_ERROR_FAILURE;
 | |
| 
 | |
|   nativeDrawing.TransformToNativeRect(tr, widgetRect);
 | |
|   nativeDrawing.TransformToNativeRect(dr, clipRect);
 | |
| 
 | |
| #if 0
 | |
|   {
 | |
|     MOZ_LOG(gWindowsLog, LogLevel::Error,
 | |
|            (stderr, "xform: %f %f %f %f [%f %f]\n", m._11, m._21, m._12, m._22,
 | |
|             m._31, m._32));
 | |
|     MOZ_LOG(gWindowsLog, LogLevel::Error,
 | |
|            (stderr, "tr: [%d %d %d %d]\ndr: [%d %d %d %d]\noff: [%f %f]\n",
 | |
|             tr.x, tr.y, tr.width, tr.height, dr.x, dr.y, dr.width, dr.height,
 | |
|             offset.x, offset.y));
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   if (aAppearance == StyleAppearance::Tab) {
 | |
|     // For left edge and right edge tabs, we need to adjust the widget
 | |
|     // rects and clip rects so that the edges don't get drawn.
 | |
|     bool isLeft = IsLeftToSelectedTab(aFrame);
 | |
|     bool isRight = !isLeft && IsRightToSelectedTab(aFrame);
 | |
| 
 | |
|     if (isLeft || isRight) {
 | |
|       // HACK ALERT: There appears to be no way to really obtain this value, so
 | |
|       // we're forced to just use the default value for Luna (which also happens
 | |
|       // to be correct for all the other skins I've tried).
 | |
|       int32_t edgeSize = 2;
 | |
| 
 | |
|       // Armed with the size of the edge, we now need to either shift to the
 | |
|       // left or to the right.  The clip rect won't include this extra area, so
 | |
|       // we know that we're effectively shifting the edge out of view (such that
 | |
|       // it won't be painted).
 | |
|       if (isLeft)
 | |
|         // The right edge should not be drawn.  Extend our rect by the edge
 | |
|         // size.
 | |
|         widgetRect.right += edgeSize;
 | |
|       else
 | |
|         // The left edge should not be drawn.  Move the widget rect's left coord
 | |
|         // back.
 | |
|         widgetRect.left -= edgeSize;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // widgetRect is the bounding box for a widget, yet the scale track is only
 | |
|   // a small portion of this size, so the edges of the scale need to be
 | |
|   // adjusted to the real size of the track.
 | |
|   if (aAppearance == StyleAppearance::Range) {
 | |
|     RECT contentRect;
 | |
|     GetThemeBackgroundContentRect(theme, hdc, part, state, &widgetRect,
 | |
|                                   &contentRect);
 | |
| 
 | |
|     SIZE siz;
 | |
|     GetThemePartSize(theme, hdc, part, state, &widgetRect, TS_TRUE, &siz);
 | |
| 
 | |
|     // When rounding is necessary, we round the position of the track
 | |
|     // away from the chevron of the thumb to make it look better.
 | |
|     if (IsRangeHorizontal(aFrame)) {
 | |
|       contentRect.top += (contentRect.bottom - contentRect.top - siz.cy) / 2;
 | |
|       contentRect.bottom = contentRect.top + siz.cy;
 | |
|     } else {
 | |
|       if (!IsFrameRTL(aFrame)) {
 | |
|         contentRect.left += (contentRect.right - contentRect.left - siz.cx) / 2;
 | |
|         contentRect.right = contentRect.left + siz.cx;
 | |
|       } else {
 | |
|         contentRect.right -=
 | |
|             (contentRect.right - contentRect.left - siz.cx) / 2;
 | |
|         contentRect.left = contentRect.right - siz.cx;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     DrawThemeBackground(theme, hdc, part, state, &contentRect, &clipRect);
 | |
|   } else if (aAppearance == StyleAppearance::NumberInput ||
 | |
|              aAppearance == StyleAppearance::PasswordInput ||
 | |
|              aAppearance == StyleAppearance::Textfield ||
 | |
|              aAppearance == StyleAppearance::Textarea) {
 | |
|     DrawThemeBackground(theme, hdc, part, state, &widgetRect, &clipRect);
 | |
| 
 | |
|     if (state == TFS_EDITBORDER_DISABLED) {
 | |
|       InflateRect(&widgetRect, -1, -1);
 | |
|       ::FillRect(hdc, &widgetRect, reinterpret_cast<HBRUSH>(COLOR_BTNFACE + 1));
 | |
|     }
 | |
|   } else if (aAppearance == StyleAppearance::ProgressBar) {
 | |
|     // DrawThemeBackground renders each corner with a solid white pixel.
 | |
|     // Restore these pixels to the underlying color. Tracks are rendered
 | |
|     // using alpha recovery, so this makes the corners transparent.
 | |
|     COLORREF color;
 | |
|     color = GetPixel(hdc, widgetRect.left, widgetRect.top);
 | |
|     DrawThemeBackground(theme, hdc, part, state, &widgetRect, &clipRect);
 | |
|     SetPixel(hdc, widgetRect.left, widgetRect.top, color);
 | |
|     SetPixel(hdc, widgetRect.right - 1, widgetRect.top, color);
 | |
|     SetPixel(hdc, widgetRect.right - 1, widgetRect.bottom - 1, color);
 | |
|     SetPixel(hdc, widgetRect.left, widgetRect.bottom - 1, color);
 | |
|   } else if (aAppearance == StyleAppearance::Progresschunk) {
 | |
|     DrawThemedProgressMeter(aFrame, aAppearance, theme, hdc, part, state,
 | |
|                             &widgetRect, &clipRect);
 | |
|   }
 | |
|   // If part is negative, the element wishes us to not render a themed
 | |
|   // background, instead opting to be drawn specially below.
 | |
|   else if (part >= 0) {
 | |
|     DrawThemeBackground(theme, hdc, part, state, &widgetRect, &clipRect);
 | |
|   }
 | |
| 
 | |
|   // Draw focus rectangles for range elements
 | |
|   // XXX it'd be nice to draw these outside of the frame
 | |
|   if (aAppearance == StyleAppearance::Range) {
 | |
|     ElementState contentState = GetContentState(aFrame, aAppearance);
 | |
| 
 | |
|     if (contentState.HasState(ElementState::FOCUSRING)) {
 | |
|       POINT vpOrg;
 | |
|       HPEN hPen = nullptr;
 | |
| 
 | |
|       uint8_t id = SaveDC(hdc);
 | |
| 
 | |
|       ::SelectClipRgn(hdc, nullptr);
 | |
|       ::GetViewportOrgEx(hdc, &vpOrg);
 | |
|       ::SetBrushOrgEx(hdc, vpOrg.x + widgetRect.left, vpOrg.y + widgetRect.top,
 | |
|                       nullptr);
 | |
|       ::SetTextColor(hdc, 0);
 | |
|       ::DrawFocusRect(hdc, &widgetRect);
 | |
|       ::RestoreDC(hdc, id);
 | |
|       if (hPen) {
 | |
|         ::DeleteObject(hPen);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   nativeDrawing.EndNativeDrawing();
 | |
| 
 | |
|   if (nativeDrawing.ShouldRenderAgain()) goto RENDER_AGAIN;
 | |
| 
 | |
|   nativeDrawing.PaintToContext();
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| bool nsNativeThemeWin::CreateWebRenderCommandsForWidget(
 | |
|     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources,
 | |
|     const layers::StackingContextHelper& aSc,
 | |
|     layers::RenderRootStateManager* aManager, nsIFrame* aFrame,
 | |
|     StyleAppearance aAppearance, const nsRect& aRect) {
 | |
|   if (IsWidgetNonNative(aFrame, aAppearance) != NonNative::No) {
 | |
|     return Theme::CreateWebRenderCommandsForWidget(
 | |
|         aBuilder, aResources, aSc, aManager, aFrame, aAppearance, aRect);
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| static void ScaleForFrameDPI(LayoutDeviceIntMargin* aMargin, nsIFrame* aFrame) {
 | |
|   double themeScale = GetThemeDpiScaleFactor(aFrame);
 | |
|   if (themeScale != 1.0) {
 | |
|     aMargin->top = NSToIntRound(aMargin->top * themeScale);
 | |
|     aMargin->left = NSToIntRound(aMargin->left * themeScale);
 | |
|     aMargin->bottom = NSToIntRound(aMargin->bottom * themeScale);
 | |
|     aMargin->right = NSToIntRound(aMargin->right * themeScale);
 | |
|   }
 | |
| }
 | |
| 
 | |
| static void ScaleForFrameDPI(LayoutDeviceIntSize* aSize, nsIFrame* aFrame) {
 | |
|   double themeScale = GetThemeDpiScaleFactor(aFrame);
 | |
|   if (themeScale != 1.0) {
 | |
|     aSize->width = NSToIntRound(aSize->width * themeScale);
 | |
|     aSize->height = NSToIntRound(aSize->height * themeScale);
 | |
|   }
 | |
| }
 | |
| 
 | |
| LayoutDeviceIntMargin nsNativeThemeWin::GetWidgetBorder(
 | |
|     nsDeviceContext* aContext, nsIFrame* aFrame, StyleAppearance aAppearance) {
 | |
|   if (IsWidgetAlwaysNonNative(aFrame, aAppearance)) {
 | |
|     return Theme::GetWidgetBorder(aContext, aFrame, aAppearance);
 | |
|   }
 | |
| 
 | |
|   LayoutDeviceIntMargin result;
 | |
|   mozilla::Maybe<nsUXThemeClass> themeClass = GetThemeClass(aAppearance);
 | |
|   HTHEME theme = NULL;
 | |
|   if (!themeClass.isNothing()) {
 | |
|     theme = nsUXThemeData::GetTheme(themeClass.value());
 | |
|   }
 | |
|   if (!theme) {
 | |
|     result = ClassicGetWidgetBorder(aContext, aFrame, aAppearance);
 | |
|     ScaleForFrameDPI(&result, aFrame);
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   if (!WidgetIsContainer(aAppearance) ||
 | |
|       aAppearance == StyleAppearance::Tabpanel)
 | |
|     return result;  // Don't worry about it.
 | |
| 
 | |
|   int32_t part, state;
 | |
|   nsresult rv = GetThemePartAndState(aFrame, aAppearance, part, state);
 | |
|   if (NS_FAILED(rv)) return result;
 | |
| 
 | |
|   result = GetCachedWidgetBorder(theme, themeClass.value(), aAppearance, part,
 | |
|                                  state);
 | |
| 
 | |
|   // Remove the edges for tabs that are before or after the selected tab,
 | |
|   if (aAppearance == StyleAppearance::Tab) {
 | |
|     if (IsLeftToSelectedTab(aFrame))
 | |
|       // Remove the right edge, since we won't be drawing it.
 | |
|       result.right = 0;
 | |
|     else if (IsRightToSelectedTab(aFrame))
 | |
|       // Remove the left edge, since we won't be drawing it.
 | |
|       result.left = 0;
 | |
|   }
 | |
| 
 | |
|   if (aFrame && (aAppearance == StyleAppearance::NumberInput ||
 | |
|                  aAppearance == StyleAppearance::PasswordInput ||
 | |
|                  aAppearance == StyleAppearance::Textfield ||
 | |
|                  aAppearance == StyleAppearance::Textarea)) {
 | |
|     nsIContent* content = aFrame->GetContent();
 | |
|     if (content && content->IsHTMLElement()) {
 | |
|       // We need to pad textfields by 1 pixel, since the caret will draw
 | |
|       // flush against the edge by default if we don't.
 | |
|       result.top.value++;
 | |
|       result.left.value++;
 | |
|       result.bottom.value++;
 | |
|       result.right.value++;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   ScaleForFrameDPI(&result, aFrame);
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| bool nsNativeThemeWin::GetWidgetPadding(nsDeviceContext* aContext,
 | |
|                                         nsIFrame* aFrame,
 | |
|                                         StyleAppearance aAppearance,
 | |
|                                         LayoutDeviceIntMargin* aResult) {
 | |
|   if (IsWidgetAlwaysNonNative(aFrame, aAppearance)) {
 | |
|     return Theme::GetWidgetPadding(aContext, aFrame, aAppearance, aResult);
 | |
|   }
 | |
| 
 | |
|   bool ok = true;
 | |
|   HANDLE theme = GetTheme(aAppearance);
 | |
|   if (!theme) {
 | |
|     ok = ClassicGetWidgetPadding(aContext, aFrame, aAppearance, aResult);
 | |
|     ScaleForFrameDPI(aResult, aFrame);
 | |
|     return ok;
 | |
|   }
 | |
| 
 | |
|   /* textfields need extra pixels on all sides, otherwise they wrap their
 | |
|    * content too tightly.  The actual border is drawn 1px inside the specified
 | |
|    * rectangle, so Gecko will end up making the contents look too small.
 | |
|    * Instead, we add 2px padding for the contents and fix this. (Used to be 1px
 | |
|    * added, see bug 430212)
 | |
|    */
 | |
|   if (aAppearance == StyleAppearance::NumberInput ||
 | |
|       aAppearance == StyleAppearance::PasswordInput ||
 | |
|       aAppearance == StyleAppearance::Textfield ||
 | |
|       aAppearance == StyleAppearance::Textarea) {
 | |
|     aResult->top = aResult->bottom = 2;
 | |
|     aResult->left = aResult->right = 2;
 | |
|     ScaleForFrameDPI(aResult, aFrame);
 | |
|     return ok;
 | |
|   } else if (IsHTMLContent(aFrame) &&
 | |
|              (aAppearance == StyleAppearance::Menulist ||
 | |
|               aAppearance == StyleAppearance::MenulistButton)) {
 | |
|     /* For content menulist controls, we need an extra pixel so that we have
 | |
|      * room to draw our focus rectangle stuff. Otherwise, the focus rect might
 | |
|      * overlap the control's border.
 | |
|      */
 | |
|     aResult->top = aResult->bottom = 1;
 | |
|     aResult->left = aResult->right = 1;
 | |
|     ScaleForFrameDPI(aResult, aFrame);
 | |
|     return ok;
 | |
|   }
 | |
| 
 | |
|   int32_t right, left, top, bottom;
 | |
|   right = left = top = bottom = 0;
 | |
|   switch (aAppearance) {
 | |
|     case StyleAppearance::Button:
 | |
|       if (aFrame->GetContent()->IsXULElement()) {
 | |
|         top = 2;
 | |
|         bottom = 3;
 | |
|       }
 | |
|       left = right = 5;
 | |
|       break;
 | |
|     default:
 | |
|       return false;
 | |
|   }
 | |
| 
 | |
|   if (IsFrameRTL(aFrame)) {
 | |
|     aResult->right = left;
 | |
|     aResult->left = right;
 | |
|   } else {
 | |
|     aResult->right = right;
 | |
|     aResult->left = left;
 | |
|   }
 | |
|   aResult->top = top;
 | |
|   aResult->bottom = bottom;
 | |
| 
 | |
|   ScaleForFrameDPI(aResult, aFrame);
 | |
|   return ok;
 | |
| }
 | |
| 
 | |
| bool nsNativeThemeWin::GetWidgetOverflow(nsDeviceContext* aContext,
 | |
|                                          nsIFrame* aFrame,
 | |
|                                          StyleAppearance aAppearance,
 | |
|                                          nsRect* aOverflowRect) {
 | |
|   if (IsWidgetNonNative(aFrame, aAppearance) != NonNative::No) {
 | |
|     return Theme::GetWidgetOverflow(aContext, aFrame, aAppearance,
 | |
|                                     aOverflowRect);
 | |
|   }
 | |
| 
 | |
|   /* This is disabled for now, because it causes invalidation problems --
 | |
|    * see bug 420381.  The effect of not updating the overflow area is that
 | |
|    * for dropdown buttons in content areas, there is a 1px border on 3 sides
 | |
|    * where, if invalidated, the dropdown control probably won't be repainted.
 | |
|    * This is fairly minor, as by default there is nothing in that area, and
 | |
|    * a border only shows up if the widget is being hovered.
 | |
|    *
 | |
|    * TODO(jwatt): Figure out what do to about
 | |
|    * StyleAppearance::MozMenulistArrowButton too.
 | |
|    */
 | |
| #if 0
 | |
|   /* We explicitly draw dropdown buttons in HTML content 1px bigger up, right,
 | |
|    * and bottom so that they overlap the dropdown's border like they're
 | |
|    * supposed to.
 | |
|    */
 | |
|   if (aAppearance == StyleAppearance::MenulistButton &&
 | |
|       IsHTMLContent(aFrame) &&
 | |
|       !IsWidgetStyled(aFrame->GetParent()->PresContext(),
 | |
|                       aFrame->GetParent(),
 | |
|                       StyleAppearance::Menulist))
 | |
|   {
 | |
|     int32_t p2a = aContext->AppUnitsPerDevPixel();
 | |
|     /* Note: no overflow on the left */
 | |
|     nsMargin m(p2a, p2a, p2a, 0);
 | |
|     aOverflowRect->Inflate (m);
 | |
|     return true;
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| LayoutDeviceIntSize nsNativeThemeWin::GetMinimumWidgetSize(
 | |
|     nsPresContext* aPresContext, nsIFrame* aFrame,
 | |
|     StyleAppearance aAppearance) {
 | |
|   if (IsWidgetAlwaysNonNative(aFrame, aAppearance)) {
 | |
|     return Theme::GetMinimumWidgetSize(aPresContext, aFrame, aAppearance);
 | |
|   }
 | |
| 
 | |
|   mozilla::Maybe<nsUXThemeClass> themeClass = GetThemeClass(aAppearance);
 | |
|   HTHEME theme = NULL;
 | |
|   if (!themeClass.isNothing()) {
 | |
|     theme = nsUXThemeData::GetTheme(themeClass.value());
 | |
|   }
 | |
|   if (!theme) {
 | |
|     auto result = ClassicGetMinimumWidgetSize(aFrame, aAppearance);
 | |
|     ScaleForFrameDPI(&result, aFrame);
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   switch (aAppearance) {
 | |
|     case StyleAppearance::NumberInput:
 | |
|     case StyleAppearance::PasswordInput:
 | |
|     case StyleAppearance::Textfield:
 | |
|     case StyleAppearance::Progresschunk:
 | |
|     case StyleAppearance::Tabpanels:
 | |
|     case StyleAppearance::Tabpanel:
 | |
|     case StyleAppearance::Listbox:
 | |
|       return {};  // Don't worry about it.
 | |
|     default:
 | |
|       break;
 | |
|   }
 | |
| 
 | |
|   // Call GetSystemMetrics to determine size for WinXP scrollbars
 | |
|   // (GetThemeSysSize API returns the optimal size for the theme, but
 | |
|   //  Windows appears to always use metrics when drawing standard scrollbars)
 | |
|   THEMESIZE sizeReq = TS_TRUE;  // Best-fit size
 | |
|   switch (aAppearance) {
 | |
|     case StyleAppearance::ProgressBar:
 | |
|       // Best-fit size for progress meters is too large for most
 | |
|       // themes. We want these widgets to be able to really shrink
 | |
|       // down, so use the min-size request value (of 0).
 | |
|       sizeReq = TS_MIN;
 | |
|       break;
 | |
| 
 | |
|     case StyleAppearance::RangeThumb: {
 | |
|       LayoutDeviceIntSize result(12, 20);
 | |
|       if (!IsRangeHorizontal(aFrame)) {
 | |
|         std::swap(result.width, result.height);
 | |
|       }
 | |
|       ScaleForFrameDPI(&result, aFrame);
 | |
|       return result;
 | |
|     }
 | |
| 
 | |
|     case StyleAppearance::Separator: {
 | |
|       // that's 2px left margin, 2px right margin and 2px separator
 | |
|       // (the margin is drawn as part of the separator, though)
 | |
|       LayoutDeviceIntSize result(6, 0);
 | |
|       ScaleForFrameDPI(&result, aFrame);
 | |
|       return result;
 | |
|     }
 | |
| 
 | |
|     case StyleAppearance::Button:
 | |
|       // We should let HTML buttons shrink to their min size.
 | |
|       // FIXME bug 403934: We should probably really separate
 | |
|       // GetPreferredWidgetSize from GetMinimumWidgetSize, so callers can
 | |
|       // use the one they want.
 | |
|       if (aFrame->GetContent()->IsHTMLElement()) {
 | |
|         sizeReq = TS_MIN;
 | |
|       }
 | |
|       break;
 | |
| 
 | |
|     default:
 | |
|       break;
 | |
|   }
 | |
| 
 | |
|   int32_t part, state;
 | |
|   nsresult rv = GetThemePartAndState(aFrame, aAppearance, part, state);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     return {};
 | |
|   }
 | |
| 
 | |
|   LayoutDeviceIntSize result;
 | |
|   rv = GetCachedMinimumWidgetSize(aFrame, theme, themeClass.value(),
 | |
|                                   aAppearance, part, state, sizeReq, &result);
 | |
|   ScaleForFrameDPI(&result, aFrame);
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsNativeThemeWin::WidgetStateChanged(nsIFrame* aFrame,
 | |
|                                      StyleAppearance aAppearance,
 | |
|                                      nsAtom* aAttribute, bool* aShouldRepaint,
 | |
|                                      const nsAttrValue* aOldValue) {
 | |
|   // Some widget types just never change state.
 | |
|   if (aAppearance == StyleAppearance::Progresschunk ||
 | |
|       aAppearance == StyleAppearance::ProgressBar ||
 | |
|       aAppearance == StyleAppearance::Tabpanels ||
 | |
|       aAppearance == StyleAppearance::Tabpanel ||
 | |
|       aAppearance == StyleAppearance::Separator) {
 | |
|     *aShouldRepaint = false;
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   // We need to repaint the dropdown arrow in vista HTML combobox controls when
 | |
|   // the control is closed to get rid of the hover effect.
 | |
|   if ((aAppearance == StyleAppearance::Menulist ||
 | |
|        aAppearance == StyleAppearance::MenulistButton) &&
 | |
|       nsNativeTheme::IsHTMLContent(aFrame)) {
 | |
|     *aShouldRepaint = true;
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   // XXXdwh Not sure what can really be done here.  Can at least guess for
 | |
|   // specific widgets that they're highly unlikely to have certain states.
 | |
|   // For example, a toolbar doesn't care about any states.
 | |
|   if (!aAttribute) {
 | |
|     // Hover/focus/active changed.  Always repaint.
 | |
|     *aShouldRepaint = true;
 | |
|   } else {
 | |
|     // Check the attribute to see if it's relevant.
 | |
|     // disabled, checked, dlgtype, default, etc.
 | |
|     *aShouldRepaint = false;
 | |
|     if (aAttribute == nsGkAtoms::disabled || aAttribute == nsGkAtoms::checked ||
 | |
|         aAttribute == nsGkAtoms::selected ||
 | |
|         aAttribute == nsGkAtoms::visuallyselected ||
 | |
|         aAttribute == nsGkAtoms::readonly || aAttribute == nsGkAtoms::open ||
 | |
|         aAttribute == nsGkAtoms::menuactive || aAttribute == nsGkAtoms::focused)
 | |
|       *aShouldRepaint = true;
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| nsNativeThemeWin::ThemeChanged() {
 | |
|   nsUXThemeData::Invalidate();
 | |
|   memset(mBorderCacheValid, 0, sizeof(mBorderCacheValid));
 | |
|   memset(mMinimumWidgetSizeCacheValid, 0, sizeof(mMinimumWidgetSizeCacheValid));
 | |
|   mGutterSizeCacheValid = false;
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| bool nsNativeThemeWin::ThemeSupportsWidget(nsPresContext* aPresContext,
 | |
|                                            nsIFrame* aFrame,
 | |
|                                            StyleAppearance aAppearance) {
 | |
|   // XXXdwh We can go even further and call the API to ask if support exists for
 | |
|   // specific widgets.
 | |
| 
 | |
|   if (IsWidgetAlwaysNonNative(aFrame, aAppearance)) {
 | |
|     return Theme::ThemeSupportsWidget(aPresContext, aFrame, aAppearance);
 | |
|   }
 | |
| 
 | |
|   HANDLE theme = GetTheme(aAppearance);
 | |
|   if (theme || ClassicThemeSupportsWidget(aFrame, aAppearance))
 | |
|     // turn off theming for some HTML widgets styled by the page
 | |
|     return !IsWidgetStyled(aPresContext, aFrame, aAppearance);
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| bool nsNativeThemeWin::ThemeDrawsFocusForWidget(nsIFrame* aFrame,
 | |
|                                                 StyleAppearance aAppearance) {
 | |
|   if (IsWidgetNonNative(aFrame, aAppearance) != NonNative::No) {
 | |
|     return Theme::ThemeDrawsFocusForWidget(aFrame, aAppearance);
 | |
|   }
 | |
|   switch (aAppearance) {
 | |
|     case StyleAppearance::Menulist:
 | |
|     case StyleAppearance::MenulistButton:
 | |
|     case StyleAppearance::Textarea:
 | |
|     case StyleAppearance::Textfield:
 | |
|     case StyleAppearance::NumberInput:
 | |
|     case StyleAppearance::PasswordInput:
 | |
|       return true;
 | |
|     default:
 | |
|       return false;
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool nsNativeThemeWin::ThemeNeedsComboboxDropmarker() { return true; }
 | |
| 
 | |
| nsITheme::Transparency nsNativeThemeWin::GetWidgetTransparency(
 | |
|     nsIFrame* aFrame, StyleAppearance aAppearance) {
 | |
|   if (IsWidgetNonNative(aFrame, aAppearance) != NonNative::No) {
 | |
|     return Theme::GetWidgetTransparency(aFrame, aAppearance);
 | |
|   }
 | |
| 
 | |
|   switch (aAppearance) {
 | |
|     case StyleAppearance::ProgressBar:
 | |
|     case StyleAppearance::Progresschunk:
 | |
|     case StyleAppearance::Range:
 | |
|       return eTransparent;
 | |
|     default:
 | |
|       break;
 | |
|   }
 | |
| 
 | |
|   HANDLE theme = GetTheme(aAppearance);
 | |
|   // For the classic theme we don't really have a way of knowing
 | |
|   if (!theme) {
 | |
|     return eUnknownTransparency;
 | |
|   }
 | |
| 
 | |
|   int32_t part, state;
 | |
|   nsresult rv = GetThemePartAndState(aFrame, aAppearance, part, state);
 | |
|   // Fail conservatively
 | |
|   NS_ENSURE_SUCCESS(rv, eUnknownTransparency);
 | |
| 
 | |
|   if (part <= 0) {
 | |
|     // Not a real part code, so IsThemeBackgroundPartiallyTransparent may
 | |
|     // not work, so don't call it.
 | |
|     return eUnknownTransparency;
 | |
|   }
 | |
| 
 | |
|   if (IsThemeBackgroundPartiallyTransparent(theme, part, state))
 | |
|     return eTransparent;
 | |
|   return eOpaque;
 | |
| }
 | |
| 
 | |
| /* Windows 9x/NT/2000/Classic XP Theme Support */
 | |
| 
 | |
| bool nsNativeThemeWin::ClassicThemeSupportsWidget(nsIFrame* aFrame,
 | |
|                                                   StyleAppearance aAppearance) {
 | |
|   switch (aAppearance) {
 | |
|     case StyleAppearance::Button:
 | |
|     case StyleAppearance::NumberInput:
 | |
|     case StyleAppearance::PasswordInput:
 | |
|     case StyleAppearance::Textfield:
 | |
|     case StyleAppearance::Textarea:
 | |
|     case StyleAppearance::Range:
 | |
|     case StyleAppearance::RangeThumb:
 | |
|     case StyleAppearance::Menulist:
 | |
|     case StyleAppearance::MenulistButton:
 | |
|     case StyleAppearance::Listbox:
 | |
|     case StyleAppearance::ProgressBar:
 | |
|     case StyleAppearance::Progresschunk:
 | |
|     case StyleAppearance::Tab:
 | |
|     case StyleAppearance::Tabpanel:
 | |
|     case StyleAppearance::Tabpanels:
 | |
|       return true;
 | |
|     default:
 | |
|       return false;
 | |
|   }
 | |
| }
 | |
| 
 | |
| LayoutDeviceIntMargin nsNativeThemeWin::ClassicGetWidgetBorder(
 | |
|     nsDeviceContext* aContext, nsIFrame* aFrame, StyleAppearance aAppearance) {
 | |
|   LayoutDeviceIntMargin result;
 | |
|   switch (aAppearance) {
 | |
|     case StyleAppearance::Button:
 | |
|       result.top = result.left = result.bottom = result.right = 2;
 | |
|       break;
 | |
|     case StyleAppearance::Listbox:
 | |
|     case StyleAppearance::Menulist:
 | |
|     case StyleAppearance::MenulistButton:
 | |
|     case StyleAppearance::Tab:
 | |
|     case StyleAppearance::NumberInput:
 | |
|     case StyleAppearance::PasswordInput:
 | |
|     case StyleAppearance::Textfield:
 | |
|     case StyleAppearance::Textarea:
 | |
|       result.top = result.left = result.bottom = result.right = 2;
 | |
|       break;
 | |
|     case StyleAppearance::ProgressBar:
 | |
|       result.top = result.left = result.bottom = result.right = 1;
 | |
|       break;
 | |
|     default:
 | |
|       result.top = result.bottom = result.left = result.right = 0;
 | |
|       break;
 | |
|   }
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| bool nsNativeThemeWin::ClassicGetWidgetPadding(nsDeviceContext* aContext,
 | |
|                                                nsIFrame* aFrame,
 | |
|                                                StyleAppearance aAppearance,
 | |
|                                                LayoutDeviceIntMargin* aResult) {
 | |
|   switch (aAppearance) {
 | |
|     case StyleAppearance::ProgressBar:
 | |
|       (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right =
 | |
|           1;
 | |
|       return true;
 | |
|     default:
 | |
|       return false;
 | |
|   }
 | |
| }
 | |
| 
 | |
| LayoutDeviceIntSize nsNativeThemeWin::ClassicGetMinimumWidgetSize(
 | |
|     nsIFrame* aFrame, StyleAppearance aAppearance) {
 | |
|   LayoutDeviceIntSize result;
 | |
|   switch (aAppearance) {
 | |
|     case StyleAppearance::RangeThumb: {
 | |
|       if (IsRangeHorizontal(aFrame)) {
 | |
|         result.width = 12;
 | |
|         result.height = 20;
 | |
|       } else {
 | |
|         result.width = 20;
 | |
|         result.height = 12;
 | |
|       }
 | |
|       break;
 | |
|     }
 | |
|     case StyleAppearance::Menulist:
 | |
|     case StyleAppearance::MenulistButton:
 | |
|     case StyleAppearance::Button:
 | |
|     case StyleAppearance::Listbox:
 | |
|     case StyleAppearance::NumberInput:
 | |
|     case StyleAppearance::PasswordInput:
 | |
|     case StyleAppearance::Textfield:
 | |
|     case StyleAppearance::Textarea:
 | |
|     case StyleAppearance::Progresschunk:
 | |
|     case StyleAppearance::ProgressBar:
 | |
|     case StyleAppearance::Tab:
 | |
|     case StyleAppearance::Tabpanel:
 | |
|     case StyleAppearance::Tabpanels:
 | |
|       // no minimum widget size
 | |
|       break;
 | |
| 
 | |
|     default:
 | |
|       break;
 | |
|   }
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| nsresult nsNativeThemeWin::ClassicGetThemePartAndState(
 | |
|     nsIFrame* aFrame, StyleAppearance aAppearance, int32_t& aPart,
 | |
|     int32_t& aState, bool& aFocused) {
 | |
|   aFocused = false;
 | |
|   switch (aAppearance) {
 | |
|     case StyleAppearance::Button: {
 | |
|       aPart = DFC_BUTTON;
 | |
|       aState = DFCS_BUTTONPUSH;
 | |
|       aFocused = false;
 | |
| 
 | |
|       ElementState contentState = GetContentState(aFrame, aAppearance);
 | |
|       if (contentState.HasState(ElementState::DISABLED)) {
 | |
|         aState |= DFCS_INACTIVE;
 | |
|       } else if (IsOpenButton(aFrame)) {
 | |
|         aState |= DFCS_PUSHED;
 | |
|       } else if (IsCheckedButton(aFrame)) {
 | |
|         aState |= DFCS_CHECKED;
 | |
|       } else {
 | |
|         if (contentState.HasAllStates(ElementState::ACTIVE |
 | |
|                                       ElementState::HOVER)) {
 | |
|           aState |= DFCS_PUSHED;
 | |
|           // The down state is flat if the button is focusable
 | |
|           if (aFrame->StyleUI()->UserFocus() == StyleUserFocus::Normal) {
 | |
|             if (!aFrame->GetContent()->IsHTMLElement()) aState |= DFCS_FLAT;
 | |
| 
 | |
|             aFocused = true;
 | |
|           }
 | |
|         }
 | |
|         // On Windows, focused buttons are always drawn as such by the native
 | |
|         // theme, that's why we check ElementState::FOCUS instead of
 | |
|         // ElementState::FOCUSRING.
 | |
|         if (contentState.HasState(ElementState::FOCUS) ||
 | |
|             (aState == DFCS_BUTTONPUSH && IsDefaultButton(aFrame))) {
 | |
|           aFocused = true;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       return NS_OK;
 | |
|     }
 | |
|     case StyleAppearance::Listbox:
 | |
|     case StyleAppearance::NumberInput:
 | |
|     case StyleAppearance::PasswordInput:
 | |
|     case StyleAppearance::Textfield:
 | |
|     case StyleAppearance::Textarea:
 | |
|     case StyleAppearance::Menulist:
 | |
|     case StyleAppearance::MenulistButton:
 | |
|     case StyleAppearance::Range:
 | |
|     case StyleAppearance::RangeThumb:
 | |
|     case StyleAppearance::Progresschunk:
 | |
|     case StyleAppearance::ProgressBar:
 | |
|     case StyleAppearance::Tab:
 | |
|     case StyleAppearance::Tabpanel:
 | |
|     case StyleAppearance::Tabpanels:
 | |
|       // these don't use DrawFrameControl
 | |
|       return NS_OK;
 | |
|     default:
 | |
|       return NS_ERROR_FAILURE;
 | |
|   }
 | |
| }
 | |
| 
 | |
| // Draw classic Windows tab
 | |
| // (no system API for this, but DrawEdge can draw all the parts of a tab)
 | |
| static void DrawTab(HDC hdc, const RECT& R, int32_t aPosition, bool aSelected,
 | |
|                     bool aDrawLeft, bool aDrawRight) {
 | |
|   int32_t leftFlag, topFlag, rightFlag, lightFlag, shadeFlag;
 | |
|   RECT topRect, sideRect, bottomRect, lightRect, shadeRect;
 | |
|   int32_t selectedOffset, lOffset, rOffset;
 | |
| 
 | |
|   selectedOffset = aSelected ? 1 : 0;
 | |
|   lOffset = aDrawLeft ? 2 : 0;
 | |
|   rOffset = aDrawRight ? 2 : 0;
 | |
| 
 | |
|   // Get info for tab orientation/position (Left, Top, Right, Bottom)
 | |
|   switch (aPosition) {
 | |
|     case BF_LEFT:
 | |
|       leftFlag = BF_TOP;
 | |
|       topFlag = BF_LEFT;
 | |
|       rightFlag = BF_BOTTOM;
 | |
|       lightFlag = BF_DIAGONAL_ENDTOPRIGHT;
 | |
|       shadeFlag = BF_DIAGONAL_ENDBOTTOMRIGHT;
 | |
| 
 | |
|       ::SetRect(&topRect, R.left, R.top + lOffset, R.right, R.bottom - rOffset);
 | |
|       ::SetRect(&sideRect, R.left + 2, R.top, R.right - 2 + selectedOffset,
 | |
|                 R.bottom);
 | |
|       ::SetRect(&bottomRect, R.right - 2, R.top, R.right, R.bottom);
 | |
|       ::SetRect(&lightRect, R.left, R.top, R.left + 3, R.top + 3);
 | |
|       ::SetRect(&shadeRect, R.left + 1, R.bottom - 2, R.left + 2, R.bottom - 1);
 | |
|       break;
 | |
|     case BF_TOP:
 | |
|       leftFlag = BF_LEFT;
 | |
|       topFlag = BF_TOP;
 | |
|       rightFlag = BF_RIGHT;
 | |
|       lightFlag = BF_DIAGONAL_ENDTOPRIGHT;
 | |
|       shadeFlag = BF_DIAGONAL_ENDBOTTOMRIGHT;
 | |
| 
 | |
|       ::SetRect(&topRect, R.left + lOffset, R.top, R.right - rOffset, R.bottom);
 | |
|       ::SetRect(&sideRect, R.left, R.top + 2, R.right,
 | |
|                 R.bottom - 1 + selectedOffset);
 | |
|       ::SetRect(&bottomRect, R.left, R.bottom - 1, R.right, R.bottom);
 | |
|       ::SetRect(&lightRect, R.left, R.top, R.left + 3, R.top + 3);
 | |
|       ::SetRect(&shadeRect, R.right - 2, R.top + 1, R.right - 1, R.top + 2);
 | |
|       break;
 | |
|     case BF_RIGHT:
 | |
|       leftFlag = BF_TOP;
 | |
|       topFlag = BF_RIGHT;
 | |
|       rightFlag = BF_BOTTOM;
 | |
|       lightFlag = BF_DIAGONAL_ENDTOPLEFT;
 | |
|       shadeFlag = BF_DIAGONAL_ENDBOTTOMLEFT;
 | |
| 
 | |
|       ::SetRect(&topRect, R.left, R.top + lOffset, R.right, R.bottom - rOffset);
 | |
|       ::SetRect(&sideRect, R.left + 2 - selectedOffset, R.top, R.right - 2,
 | |
|                 R.bottom);
 | |
|       ::SetRect(&bottomRect, R.left, R.top, R.left + 2, R.bottom);
 | |
|       ::SetRect(&lightRect, R.right - 3, R.top, R.right - 1, R.top + 2);
 | |
|       ::SetRect(&shadeRect, R.right - 2, R.bottom - 3, R.right, R.bottom - 1);
 | |
|       break;
 | |
|     case BF_BOTTOM:
 | |
|       leftFlag = BF_LEFT;
 | |
|       topFlag = BF_BOTTOM;
 | |
|       rightFlag = BF_RIGHT;
 | |
|       lightFlag = BF_DIAGONAL_ENDTOPLEFT;
 | |
|       shadeFlag = BF_DIAGONAL_ENDBOTTOMLEFT;
 | |
| 
 | |
|       ::SetRect(&topRect, R.left + lOffset, R.top, R.right - rOffset, R.bottom);
 | |
|       ::SetRect(&sideRect, R.left, R.top + 2 - selectedOffset, R.right,
 | |
|                 R.bottom - 2);
 | |
|       ::SetRect(&bottomRect, R.left, R.top, R.right, R.top + 2);
 | |
|       ::SetRect(&lightRect, R.left, R.bottom - 3, R.left + 2, R.bottom - 1);
 | |
|       ::SetRect(&shadeRect, R.right - 2, R.bottom - 3, R.right, R.bottom - 1);
 | |
|       break;
 | |
|     default:
 | |
|       MOZ_CRASH();
 | |
|   }
 | |
| 
 | |
|   // Background
 | |
|   ::FillRect(hdc, &R, (HBRUSH)(COLOR_3DFACE + 1));
 | |
| 
 | |
|   // Tab "Top"
 | |
|   ::DrawEdge(hdc, &topRect, EDGE_RAISED, BF_SOFT | topFlag);
 | |
| 
 | |
|   // Tab "Bottom"
 | |
|   if (!aSelected) ::DrawEdge(hdc, &bottomRect, EDGE_RAISED, BF_SOFT | topFlag);
 | |
| 
 | |
|   // Tab "Sides"
 | |
|   if (!aDrawLeft) leftFlag = 0;
 | |
|   if (!aDrawRight) rightFlag = 0;
 | |
|   ::DrawEdge(hdc, &sideRect, EDGE_RAISED, BF_SOFT | leftFlag | rightFlag);
 | |
| 
 | |
|   // Tab Diagonal Corners
 | |
|   if (aDrawLeft) ::DrawEdge(hdc, &lightRect, EDGE_RAISED, BF_SOFT | lightFlag);
 | |
| 
 | |
|   if (aDrawRight) ::DrawEdge(hdc, &shadeRect, EDGE_RAISED, BF_SOFT | shadeFlag);
 | |
| }
 | |
| 
 | |
| void nsNativeThemeWin::DrawCheckedRect(HDC hdc, const RECT& rc, int32_t fore,
 | |
|                                        int32_t back, HBRUSH defaultBack) {
 | |
|   static WORD patBits[8] = {0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55};
 | |
| 
 | |
|   HBITMAP patBmp = ::CreateBitmap(8, 8, 1, 1, patBits);
 | |
|   if (patBmp) {
 | |
|     HBRUSH brush = (HBRUSH)::CreatePatternBrush(patBmp);
 | |
|     if (brush) {
 | |
|       COLORREF oldForeColor = ::SetTextColor(hdc, ::GetSysColor(fore));
 | |
|       COLORREF oldBackColor = ::SetBkColor(hdc, ::GetSysColor(back));
 | |
|       POINT vpOrg;
 | |
| 
 | |
|       ::UnrealizeObject(brush);
 | |
|       ::GetViewportOrgEx(hdc, &vpOrg);
 | |
|       ::SetBrushOrgEx(hdc, vpOrg.x + rc.left, vpOrg.y + rc.top, nullptr);
 | |
|       HBRUSH oldBrush = (HBRUSH)::SelectObject(hdc, brush);
 | |
|       ::FillRect(hdc, &rc, brush);
 | |
|       ::SetTextColor(hdc, oldForeColor);
 | |
|       ::SetBkColor(hdc, oldBackColor);
 | |
|       ::SelectObject(hdc, oldBrush);
 | |
|       ::DeleteObject(brush);
 | |
|     } else
 | |
|       ::FillRect(hdc, &rc, defaultBack);
 | |
| 
 | |
|     ::DeleteObject(patBmp);
 | |
|   }
 | |
| }
 | |
| 
 | |
| nsresult nsNativeThemeWin::ClassicDrawWidgetBackground(
 | |
|     gfxContext* aContext, nsIFrame* aFrame, StyleAppearance aAppearance,
 | |
|     const nsRect& aRect, const nsRect& aDirtyRect) {
 | |
|   int32_t part, state;
 | |
|   bool focused;
 | |
|   nsresult rv;
 | |
|   rv = ClassicGetThemePartAndState(aFrame, aAppearance, part, state, focused);
 | |
|   if (NS_FAILED(rv)) return rv;
 | |
| 
 | |
|   if (AssumeThemePartAndStateAreTransparent(part, state)) {
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   gfxFloat p2a = gfxFloat(aFrame->PresContext()->AppUnitsPerDevPixel());
 | |
|   RECT widgetRect;
 | |
|   gfxRect tr(aRect.X(), aRect.Y(), aRect.Width(), aRect.Height()),
 | |
|       dr(aDirtyRect.X(), aDirtyRect.Y(), aDirtyRect.Width(),
 | |
|          aDirtyRect.Height());
 | |
| 
 | |
|   tr.Scale(1.0 / p2a);
 | |
|   dr.Scale(1.0 / p2a);
 | |
| 
 | |
|   gfxWindowsNativeDrawing nativeDrawing(
 | |
|       aContext, dr, GetWidgetNativeDrawingFlags(aAppearance));
 | |
| 
 | |
| RENDER_AGAIN:
 | |
| 
 | |
|   HDC hdc = nativeDrawing.BeginNativeDrawing();
 | |
|   if (!hdc) return NS_ERROR_FAILURE;
 | |
| 
 | |
|   nativeDrawing.TransformToNativeRect(tr, widgetRect);
 | |
| 
 | |
|   rv = NS_OK;
 | |
|   switch (aAppearance) {
 | |
|     // Draw button
 | |
|     case StyleAppearance::Button: {
 | |
|       if (focused) {
 | |
|         // draw dark button focus border first
 | |
|         if (HBRUSH brush = ::GetSysColorBrush(COLOR_3DDKSHADOW)) {
 | |
|           ::FrameRect(hdc, &widgetRect, brush);
 | |
|         }
 | |
|         InflateRect(&widgetRect, -1, -1);
 | |
|       }
 | |
|       // setup DC to make DrawFrameControl draw correctly
 | |
|       int32_t oldTA = ::SetTextAlign(hdc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
 | |
|       ::DrawFrameControl(hdc, &widgetRect, part, state);
 | |
|       ::SetTextAlign(hdc, oldTA);
 | |
|       break;
 | |
|     }
 | |
|     // Draw controls with 2px 3D inset border
 | |
|     case StyleAppearance::NumberInput:
 | |
|     case StyleAppearance::PasswordInput:
 | |
|     case StyleAppearance::Textfield:
 | |
|     case StyleAppearance::Textarea:
 | |
|     case StyleAppearance::Listbox:
 | |
|     case StyleAppearance::Menulist:
 | |
|     case StyleAppearance::MenulistButton: {
 | |
|       // Draw inset edge
 | |
|       ::DrawEdge(hdc, &widgetRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
 | |
| 
 | |
|       ElementState elementState = GetContentState(aFrame, aAppearance);
 | |
| 
 | |
|       // Fill in background
 | |
| 
 | |
|       if (elementState.HasState(ElementState::DISABLED) ||
 | |
|           (aFrame->GetContent()->IsXULElement() && IsReadOnly(aFrame)))
 | |
|         ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_BTNFACE + 1));
 | |
|       else
 | |
|         ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_WINDOW + 1));
 | |
| 
 | |
|       break;
 | |
|     }
 | |
|     // Draw 3D face background controls
 | |
|     case StyleAppearance::ProgressBar:
 | |
|       // Draw 3D border
 | |
|       ::DrawEdge(hdc, &widgetRect, BDR_SUNKENOUTER, BF_RECT | BF_MIDDLE);
 | |
|       InflateRect(&widgetRect, -1, -1);
 | |
|       [[fallthrough]];
 | |
|     case StyleAppearance::Tabpanel: {
 | |
|       ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_BTNFACE + 1));
 | |
|       break;
 | |
|     }
 | |
|     case StyleAppearance::RangeThumb: {
 | |
|       ElementState elementState = GetContentState(aFrame, aAppearance);
 | |
| 
 | |
|       ::DrawEdge(hdc, &widgetRect, EDGE_RAISED,
 | |
|                  BF_RECT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
 | |
|       if (elementState.HasState(ElementState::DISABLED)) {
 | |
|         DrawCheckedRect(hdc, widgetRect, COLOR_3DFACE, COLOR_3DHILIGHT,
 | |
|                         (HBRUSH)COLOR_3DHILIGHT);
 | |
|       }
 | |
| 
 | |
|       break;
 | |
|     }
 | |
|     // Draw scale track background
 | |
|     case StyleAppearance::Range: {
 | |
|       const int32_t trackWidth = 4;
 | |
|       // When rounding is necessary, we round the position of the track
 | |
|       // away from the chevron of the thumb to make it look better.
 | |
|       if (IsRangeHorizontal(aFrame)) {
 | |
|         widgetRect.top += (widgetRect.bottom - widgetRect.top - trackWidth) / 2;
 | |
|         widgetRect.bottom = widgetRect.top + trackWidth;
 | |
|       } else {
 | |
|         if (!IsFrameRTL(aFrame)) {
 | |
|           widgetRect.left +=
 | |
|               (widgetRect.right - widgetRect.left - trackWidth) / 2;
 | |
|           widgetRect.right = widgetRect.left + trackWidth;
 | |
|         } else {
 | |
|           widgetRect.right -=
 | |
|               (widgetRect.right - widgetRect.left - trackWidth) / 2;
 | |
|           widgetRect.left = widgetRect.right - trackWidth;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       ::DrawEdge(hdc, &widgetRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
 | |
|       ::FillRect(hdc, &widgetRect, (HBRUSH)GetStockObject(GRAY_BRUSH));
 | |
| 
 | |
|       break;
 | |
|     }
 | |
|     case StyleAppearance::Progresschunk: {
 | |
|       nsIFrame* stateFrame = aFrame->GetParent();
 | |
|       ElementState elementState = GetContentState(stateFrame, aAppearance);
 | |
| 
 | |
|       const bool indeterminate =
 | |
|           elementState.HasState(ElementState::INDETERMINATE);
 | |
|       bool vertical = IsVerticalProgress(stateFrame);
 | |
| 
 | |
|       nsIContent* content = aFrame->GetContent();
 | |
|       if (!indeterminate || !content) {
 | |
|         ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_HIGHLIGHT + 1));
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       RECT overlayRect = CalculateProgressOverlayRect(
 | |
|           aFrame, &widgetRect, vertical, indeterminate, true);
 | |
| 
 | |
|       ::FillRect(hdc, &overlayRect, (HBRUSH)(COLOR_HIGHLIGHT + 1));
 | |
| 
 | |
|       if (!QueueAnimatedContentForRefresh(aFrame->GetContent(), 30)) {
 | |
|         NS_WARNING("unable to animate progress widget!");
 | |
|       }
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|     // Draw Tab
 | |
|     case StyleAppearance::Tab: {
 | |
|       DrawTab(hdc, widgetRect, IsBottomTab(aFrame) ? BF_BOTTOM : BF_TOP,
 | |
|               IsSelectedTab(aFrame), !IsRightToSelectedTab(aFrame),
 | |
|               !IsLeftToSelectedTab(aFrame));
 | |
| 
 | |
|       break;
 | |
|     }
 | |
|     case StyleAppearance::Tabpanels:
 | |
|       ::DrawEdge(hdc, &widgetRect, EDGE_RAISED,
 | |
|                  BF_SOFT | BF_MIDDLE | BF_LEFT | BF_RIGHT | BF_BOTTOM);
 | |
| 
 | |
|       break;
 | |
| 
 | |
|     default:
 | |
|       rv = NS_ERROR_FAILURE;
 | |
|       break;
 | |
|   }
 | |
| 
 | |
|   nativeDrawing.EndNativeDrawing();
 | |
| 
 | |
|   if (NS_FAILED(rv)) return rv;
 | |
| 
 | |
|   if (nativeDrawing.ShouldRenderAgain()) goto RENDER_AGAIN;
 | |
| 
 | |
|   nativeDrawing.PaintToContext();
 | |
| 
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| uint32_t nsNativeThemeWin::GetWidgetNativeDrawingFlags(
 | |
|     StyleAppearance aAppearance) {
 | |
|   switch (aAppearance) {
 | |
|     case StyleAppearance::Button:
 | |
|     case StyleAppearance::NumberInput:
 | |
|     case StyleAppearance::PasswordInput:
 | |
|     case StyleAppearance::Textfield:
 | |
|     case StyleAppearance::Textarea:
 | |
|     case StyleAppearance::Menulist:
 | |
|     case StyleAppearance::MenulistButton:
 | |
|       return gfxWindowsNativeDrawing::CANNOT_DRAW_TO_COLOR_ALPHA |
 | |
|              gfxWindowsNativeDrawing::CAN_AXIS_ALIGNED_SCALE |
 | |
|              gfxWindowsNativeDrawing::CANNOT_COMPLEX_TRANSFORM;
 | |
| 
 | |
|     // need to check these others
 | |
|     default:
 | |
|       return gfxWindowsNativeDrawing::CANNOT_DRAW_TO_COLOR_ALPHA |
 | |
|              gfxWindowsNativeDrawing::CANNOT_AXIS_ALIGNED_SCALE |
 | |
|              gfxWindowsNativeDrawing::CANNOT_COMPLEX_TRANSFORM;
 | |
|   }
 | |
| }
 | |
| 
 | |
| }  // namespace mozilla::widget
 | |
| 
 | |
| ///////////////////////////////////////////
 | |
| // Creation Routine
 | |
| ///////////////////////////////////////////
 | |
| 
 | |
| already_AddRefed<Theme> do_CreateNativeThemeDoNotUseDirectly() {
 | |
|   return do_AddRef(new nsNativeThemeWin());
 | |
| }
 |