summaryrefslogtreecommitdiffstats
path: root/widget/windows/nsNativeThemeWin.cpp
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /widget/windows/nsNativeThemeWin.cpp
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'widget/windows/nsNativeThemeWin.cpp')
-rw-r--r--widget/windows/nsNativeThemeWin.cpp4168
1 files changed, 4168 insertions, 0 deletions
diff --git a/widget/windows/nsNativeThemeWin.cpp b/widget/windows/nsNativeThemeWin.cpp
new file mode 100644
index 000000000..4ff6b0af9
--- /dev/null
+++ b/widget/windows/nsNativeThemeWin.cpp
@@ -0,0 +1,4168 @@
+/* -*- 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 "mozilla/EventStates.h"
+#include "mozilla/Logging.h"
+#include "mozilla/WindowsVersion.h"
+#include "nsDeviceContext.h"
+#include "nsRenderingContext.h"
+#include "nsRect.h"
+#include "nsSize.h"
+#include "nsTransform2D.h"
+#include "nsThemeConstants.h"
+#include "nsIPresShell.h"
+#include "nsPresContext.h"
+#include "nsIContent.h"
+#include "nsIFrame.h"
+#include "nsNameSpaceManager.h"
+#include "nsIDOMHTMLInputElement.h"
+#include "nsLookAndFeel.h"
+#include "nsMenuFrame.h"
+#include "nsGkAtoms.h"
+#include <malloc.h>
+#include "nsWindow.h"
+#include "nsIComboboxControlFrame.h"
+#include "prinrval.h"
+#include "WinUtils.h"
+
+#include "gfxPlatform.h"
+#include "gfxContext.h"
+#include "gfxWindowsPlatform.h"
+#include "gfxWindowsSurface.h"
+#include "gfxWindowsNativeDrawing.h"
+
+#include "nsUXThemeData.h"
+#include "nsUXThemeConstants.h"
+#include <algorithm>
+
+using mozilla::IsVistaOrLater;
+using namespace mozilla;
+using namespace mozilla::widget;
+
+extern mozilla::LazyLogModule gWindowsLog;
+
+NS_IMPL_ISUPPORTS_INHERITED(nsNativeThemeWin, nsNativeTheme, nsITheme)
+
+nsNativeThemeWin::nsNativeThemeWin() :
+ mProgressDeterminateTimeStamp(TimeStamp::Now()),
+ mProgressIndeterminateTimeStamp(TimeStamp::Now())
+{
+ // If there is a relevant change in forms.css for windows platform,
+ // static widget style variables (e.g. sButtonBorderSize) should be
+ // reinitialized here.
+}
+
+nsNativeThemeWin::~nsNativeThemeWin()
+{
+ nsUXThemeData::Invalidate();
+}
+
+static int32_t
+GetTopLevelWindowActiveState(nsIFrame *aFrame)
+{
+ // Used by window frame and button box rendering. We can end up in here in
+ // the content process when rendering one of these moz styles freely in a
+ // page. Bail in this case, there is no applicable window focus state.
+ if (!XRE_IsParentProcess()) {
+ return mozilla::widget::themeconst::FS_INACTIVE;
+ }
+ // Get the widget. nsIFrame's GetNearestWidget walks up the view chain
+ // until it finds a real window.
+ nsIWidget* widget = aFrame->GetNearestWidget();
+ nsWindowBase * window = static_cast<nsWindowBase*>(widget);
+ if (!window)
+ return mozilla::widget::themeconst::FS_INACTIVE;
+ if (widget && !window->IsTopLevelWidget() &&
+ !(window = window->GetParentWindowBase(false)))
+ return mozilla::widget::themeconst::FS_INACTIVE;
+
+ if (window->GetWindowHandle() == ::GetActiveWindow())
+ return mozilla::widget::themeconst::FS_ACTIVE;
+ return mozilla::widget::themeconst::FS_INACTIVE;
+}
+
+static int32_t
+GetWindowFrameButtonState(nsIFrame* aFrame, EventStates eventState)
+{
+ if (GetTopLevelWindowActiveState(aFrame) ==
+ mozilla::widget::themeconst::FS_INACTIVE) {
+ if (eventState.HasState(NS_EVENT_STATE_HOVER))
+ return mozilla::widget::themeconst::BS_HOT;
+ return mozilla::widget::themeconst::BS_INACTIVE;
+ }
+
+ if (eventState.HasState(NS_EVENT_STATE_HOVER)) {
+ if (eventState.HasState(NS_EVENT_STATE_ACTIVE))
+ return mozilla::widget::themeconst::BS_PUSHED;
+ return mozilla::widget::themeconst::BS_HOT;
+ }
+ return mozilla::widget::themeconst::BS_NORMAL;
+}
+
+static int32_t
+GetClassicWindowFrameButtonState(EventStates eventState)
+{
+ if (eventState.HasState(NS_EVENT_STATE_ACTIVE) &&
+ eventState.HasState(NS_EVENT_STATE_HOVER))
+ return DFCS_BUTTONPUSH|DFCS_PUSHED;
+ return DFCS_BUTTONPUSH;
+}
+
+static bool
+IsTopLevelMenu(nsIFrame *aFrame)
+{
+ bool isTopLevel(false);
+ nsMenuFrame *menuFrame = do_QueryFrame(aFrame);
+ if (menuFrame) {
+ isTopLevel = menuFrame->IsOnMenuBar();
+ }
+ return isTopLevel;
+}
+
+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
+ double scaleFactor = nsIWidget::DefaultScaleOverride();
+ if (scaleFactor <= 0.0) {
+ scaleFactor = WinUtils::LogToPhysFactor(hdc);
+ }
+ 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;
+}
+
+/* DrawThemeBGRTLAware - render a theme part based on rtl state.
+ * Some widgets are not direction-neutral and need to be drawn reversed for
+ * RTL. Windows provides a way to do this with SetLayout, but this reverses
+ * the entire drawing area of a given device context, which means that its
+ * use will also affect the positioning of the widget. There are two ways
+ * to work around this:
+ *
+ * Option 1: Alter the position of the rect that we send so that we cancel
+ * out the positioning effects of SetLayout
+ * Option 2: Create a memory DC with the widgetRect's dimensions, draw onto
+ * that, and then transfer the results back to our DC
+ *
+ * This function tries to implement option 1, under the assumption that the
+ * correct way to reverse the effects of SetLayout is to translate the rect
+ * such that the offset from the DC bitmap's left edge to the old rect's
+ * left edge is equal to the offset from the DC bitmap's right edge to the
+ * new rect's right edge. In other words,
+ * (oldRect.left + vpOrg.x) == ((dcBMP.width - vpOrg.x) - newRect.right)
+ */
+static HRESULT
+DrawThemeBGRTLAware(HANDLE aTheme, HDC aHdc, int aPart, int aState,
+ const RECT *aWidgetRect, const RECT *aClipRect,
+ bool aIsRtl)
+{
+ NS_ASSERTION(aTheme, "Bad theme handle.");
+ NS_ASSERTION(aHdc, "Bad hdc.");
+ NS_ASSERTION(aWidgetRect, "Bad rect.");
+ NS_ASSERTION(aClipRect, "Bad clip rect.");
+
+ if (!aIsRtl) {
+ return DrawThemeBackground(aTheme, aHdc, aPart, aState,
+ aWidgetRect, aClipRect);
+ }
+
+ HGDIOBJ hObj = GetCurrentObject(aHdc, OBJ_BITMAP);
+ BITMAP bitmap;
+ POINT vpOrg;
+
+ if (hObj && GetObject(hObj, sizeof(bitmap), &bitmap) &&
+ GetViewportOrgEx(aHdc, &vpOrg)) {
+ RECT newWRect(*aWidgetRect);
+ newWRect.left = bitmap.bmWidth - (aWidgetRect->right + 2*vpOrg.x);
+ newWRect.right = bitmap.bmWidth - (aWidgetRect->left + 2*vpOrg.x);
+
+ RECT newCRect;
+ RECT *newCRectPtr = nullptr;
+
+ if (aClipRect) {
+ newCRect.top = aClipRect->top;
+ newCRect.bottom = aClipRect->bottom;
+ newCRect.left = bitmap.bmWidth - (aClipRect->right + 2*vpOrg.x);
+ newCRect.right = bitmap.bmWidth - (aClipRect->left + 2*vpOrg.x);
+ newCRectPtr = &newCRect;
+ }
+
+ SetLayout(aHdc, LAYOUT_RTL);
+ HRESULT hr = DrawThemeBackground(aTheme, aHdc, aPart, aState, &newWRect,
+ newCRectPtr);
+ SetLayout(aHdc, 0);
+ if (SUCCEEDED(hr)) {
+ return hr;
+ }
+ }
+ return DrawThemeBackground(aTheme, aHdc, aPart, aState,
+ aWidgetRect, aClipRect);
+}
+
+/*
+ * Caption button padding data - 'hot' button padding.
+ * These areas are considered hot, in that they activate
+ * a button when hovered or clicked. The button graphic
+ * is drawn inside the padding border. Unrecognized themes
+ * are treated as their recognized counterparts for now.
+ * left top right bottom
+ * classic min 1 2 0 1
+ * classic max 0 2 1 1
+ * classic close 1 2 2 1
+ *
+ * aero basic min 1 2 0 2
+ * aero basic max 0 2 1 2
+ * aero basic close 1 2 1 2
+ *
+ * xp theme min 0 2 0 2
+ * xp theme max 0 2 1 2
+ * xp theme close 1 2 2 2
+ *
+ * 'cold' button padding - generic button padding, should
+ * be handled in css.
+ * left top right bottom
+ * classic min 0 0 0 0
+ * classic max 0 0 0 0
+ * classic close 0 0 0 0
+ *
+ * aero basic min 0 0 1 0
+ * aero basic max 1 0 0 0
+ * aero basic close 0 0 0 0
+ *
+ * xp theme min 0 0 1 0
+ * xp theme max 1 0 0 0
+ * xp theme close 0 0 0 0
+ */
+
+enum CaptionDesktopTheme {
+ CAPTION_CLASSIC = 0,
+ CAPTION_BASIC,
+ CAPTION_XPTHEME,
+};
+
+enum CaptionButton {
+ CAPTIONBUTTON_MINIMIZE = 0,
+ CAPTIONBUTTON_RESTORE,
+ CAPTIONBUTTON_CLOSE,
+};
+
+struct CaptionButtonPadding {
+ RECT hotPadding[3];
+};
+
+// RECT: left, top, right, bottom
+static CaptionButtonPadding buttonData[3] = {
+ {
+ { { 1, 2, 0, 1 }, { 0, 2, 1, 1 }, { 1, 2, 2, 1 } }
+ },
+ {
+ { { 1, 2, 0, 2 }, { 0, 2, 1, 2 }, { 1, 2, 2, 2 } }
+ },
+ {
+ { { 0, 2, 0, 2 }, { 0, 2, 1, 2 }, { 1, 2, 2, 2 } }
+ }
+};
+
+// Adds "hot" caption button padding to minimum widget size.
+static void
+AddPaddingRect(LayoutDeviceIntSize* aSize, CaptionButton button) {
+ if (!aSize)
+ return;
+ RECT offset;
+ if (!IsAppThemed())
+ offset = buttonData[CAPTION_CLASSIC].hotPadding[button];
+ else if (!IsVistaOrLater())
+ offset = buttonData[CAPTION_XPTHEME].hotPadding[button];
+ else
+ offset = buttonData[CAPTION_BASIC].hotPadding[button];
+ aSize->width += offset.left + offset.right;
+ aSize->height += offset.top + offset.bottom;
+}
+
+// If we've added padding to the minimum widget size, offset
+// the area we draw into to compensate.
+static void
+OffsetBackgroundRect(RECT& rect, CaptionButton button) {
+ RECT offset;
+ if (!IsAppThemed())
+ offset = buttonData[CAPTION_CLASSIC].hotPadding[button];
+ else if (!IsVistaOrLater())
+ offset = buttonData[CAPTION_XPTHEME].hotPadding[button];
+ else
+ offset = buttonData[CAPTION_BASIC].hotPadding[button];
+ rect.left += offset.left;
+ rect.top += offset.top;
+ rect.right -= offset.right;
+ rect.bottom -= offset.bottom;
+}
+
+/*
+ * 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 kProgressHorizontalVistaOverlaySize = 120;
+// The width of the overlay used for the horizontal indeterminate progress bars on XP.
+static const int32_t kProgressHorizontalXPOverlaySize = 55;
+// 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)
+{
+ if (aIsVertical) {
+ if (IsVistaOrLater()) {
+ return PP_MOVEOVERLAYVERT;
+ }
+ return PP_CHUNKVERT;
+ } else {
+ if (IsVistaOrLater()) {
+ return PP_MOVEOVERLAY;
+ }
+ return PP_CHUNK;
+ }
+}
+
+/*
+ * 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 (IsVistaOrLater()) {
+ if (aIsVertical) {
+ return aIsIndeterminate ? kProgressVerticalIndeterminateOverlaySize
+ : kProgressVerticalOverlaySize;
+ }
+ return kProgressHorizontalVistaOverlaySize;
+ }
+ return kProgressHorizontalXPOverlaySize;
+}
+
+/*
+ * 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;
+}
+
+/*
+ * DrawChunkProgressMeter - renders an xp style chunked progress meter. Called
+ * by DrawProgressMeter.
+ *
+ * @param aTheme progress theme handle
+ * @param aHdc hdc returned by gfxWindowsNativeDrawing
+ * @param aPart the PP_X progress part
+ * @param aState the theme state
+ * @param aFrame the elements frame
+ * @param aWidgetRect bounding rect for the widget
+ * @param aClipRect dirty rect that needs drawing.
+ * @param aAppUnits app units per device pixel
+ * @param aIsIndeterm is an indeterminate progress?
+ * @param aIsVertical render a vertical progress?
+ * @param aIsRtl direction is rtl
+ */
+static void
+DrawChunkProgressMeter(HTHEME aTheme, HDC aHdc, int aPart,
+ int aState, nsIFrame* aFrame, RECT* aWidgetRect,
+ RECT* aClipRect, gfxFloat aAppUnits, bool aIsIndeterm,
+ bool aIsVertical, bool aIsRtl)
+{
+ NS_ASSERTION(aTheme, "Bad theme.");
+ NS_ASSERTION(aHdc, "Bad hdc.");
+ NS_ASSERTION(aWidgetRect, "Bad rect.");
+ NS_ASSERTION(aClipRect, "Bad clip rect.");
+ NS_ASSERTION(aFrame, "Bad frame.");
+
+ // For horizontal meters, the theme lib paints the right graphic but doesn't
+ // paint the chunks, so we do that manually. For vertical meters, the theme
+ // library draws everything correctly.
+ if (aIsVertical) {
+ DrawThemeBackground(aTheme, aHdc, aPart, aState, aWidgetRect, aClipRect);
+ return;
+ }
+
+ // query for the proper chunk metrics
+ int chunkSize, spaceSize;
+ if (FAILED(GetThemeMetric(aTheme, aHdc, aPart, aState,
+ TMT_PROGRESSCHUNKSIZE, &chunkSize)) ||
+ FAILED(GetThemeMetric(aTheme, aHdc, aPart, aState,
+ TMT_PROGRESSSPACESIZE, &spaceSize))) {
+ DrawThemeBackground(aTheme, aHdc, aPart, aState, aWidgetRect, aClipRect);
+ return;
+ }
+
+ // render chunks
+ if (!aIsRtl || aIsIndeterm) {
+ for (int chunk = aWidgetRect->left; chunk <= aWidgetRect->right;
+ chunk += (chunkSize+spaceSize)) {
+ if (!aIsIndeterm && ((chunk + chunkSize) > aWidgetRect->right)) {
+ // aWidgetRect->right represents the end of the meter. Partial blocks
+ // don't get rendered with one exception, so exit here if we don't have
+ // a full chunk to draw.
+ // The above is true *except* when the meter is at 100% fill, in which
+ // case Windows renders any remaining partial block. Query the parent
+ // frame to find out if we're at 100%.
+ if (!IsProgressMeterFilled(aFrame)) {
+ break;
+ }
+ }
+ RECT bounds =
+ { chunk, aWidgetRect->top, chunk + chunkSize, aWidgetRect->bottom };
+ DrawThemeBackground(aTheme, aHdc, aPart, aState, &bounds, aClipRect);
+ }
+ } else {
+ // rtl needs to grow in the opposite direction to look right.
+ for (int chunk = aWidgetRect->right; chunk >= aWidgetRect->left;
+ chunk -= (chunkSize+spaceSize)) {
+ if ((chunk - chunkSize) < aWidgetRect->left) {
+ if (!IsProgressMeterFilled(aFrame)) {
+ break;
+ }
+ }
+ RECT bounds =
+ { chunk - chunkSize, aWidgetRect->top, chunk, aWidgetRect->bottom };
+ DrawThemeBackground(aTheme, aHdc, aPart, aState, &bounds, aClipRect);
+ }
+ }
+}
+
+/*
+ * 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 aWidgetType 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, int aWidgetType,
+ HANDLE aTheme, HDC aHdc,
+ int aPart, int aState,
+ RECT* aWidgetRect, RECT* aClipRect,
+ gfxFloat aAppUnits)
+{
+ 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;
+ if (!IsVistaOrLater()) {
+ // Adjust clipping out by one pixel. XP progress meters are inset,
+ // Vista+ are not.
+ InflateRect(&adjWidgetRect, 1, 1);
+ InflateRect(&adjClipRect, 1, 1);
+ }
+
+ 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;
+ }
+
+ EventStates eventStates = GetContentState(parentFrame, aWidgetType);
+ bool vertical = IsVerticalProgress(parentFrame) ||
+ aWidgetType == NS_THEME_PROGRESSCHUNK_VERTICAL;
+ bool indeterminate = IsIndeterminateProgress(parentFrame, eventStates);
+ bool animate = indeterminate;
+
+ if (IsVistaOrLater()) {
+ // 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;
+ }
+ } else if (!indeterminate) {
+ // XP progress meters are 'chunk' style.
+ DrawChunkProgressMeter(aTheme, aHdc, aPart, aState, aFrame,
+ &adjWidgetRect, &adjClipRect, aAppUnits,
+ indeterminate, vertical, IsFrameRTL(aFrame));
+ }
+
+ if (animate) {
+ // Indeterminate rendering
+ int32_t overlayPart = GetProgressOverlayStyle(vertical);
+ RECT overlayRect =
+ CalculateProgressOverlayRect(aFrame, &adjWidgetRect, vertical,
+ indeterminate, false);
+ if (IsVistaOrLater()) {
+ DrawThemeBackground(aTheme, aHdc, overlayPart, aState, &overlayRect,
+ &adjClipRect);
+ } else {
+ DrawChunkProgressMeter(aTheme, aHdc, overlayPart, aState, aFrame,
+ &overlayRect, &adjClipRect, aAppUnits,
+ indeterminate, vertical, IsFrameRTL(aFrame));
+ }
+
+ if (!QueueAnimatedContentForRefresh(aFrame->GetContent(), 60)) {
+ NS_WARNING("unable to animate progress widget!");
+ }
+ }
+}
+
+HANDLE
+nsNativeThemeWin::GetTheme(uint8_t aWidgetType)
+{
+ if (!IsVistaOrLater()) {
+ // On XP or earlier, render dropdowns as textfields;
+ // doing it the right way works fine with the MS themes,
+ // but breaks on a lot of custom themes (presumably because MS
+ // apps do the textfield border business as well).
+ if (aWidgetType == NS_THEME_MENULIST)
+ aWidgetType = NS_THEME_TEXTFIELD;
+ }
+
+ switch (aWidgetType) {
+ case NS_THEME_BUTTON:
+ case NS_THEME_RADIO:
+ case NS_THEME_CHECKBOX:
+ case NS_THEME_GROUPBOX:
+ return nsUXThemeData::GetTheme(eUXButton);
+ case NS_THEME_NUMBER_INPUT:
+ case NS_THEME_TEXTFIELD:
+ case NS_THEME_TEXTFIELD_MULTILINE:
+ case NS_THEME_FOCUS_OUTLINE:
+ return nsUXThemeData::GetTheme(eUXEdit);
+ case NS_THEME_TOOLTIP:
+ // XP/2K3 should force a classic treatment of tooltips
+ return !IsVistaOrLater() ?
+ nullptr : nsUXThemeData::GetTheme(eUXTooltip);
+ case NS_THEME_TOOLBOX:
+ return nsUXThemeData::GetTheme(eUXRebar);
+ case NS_THEME_WIN_MEDIA_TOOLBOX:
+ return nsUXThemeData::GetTheme(eUXMediaRebar);
+ case NS_THEME_WIN_COMMUNICATIONS_TOOLBOX:
+ return nsUXThemeData::GetTheme(eUXCommunicationsRebar);
+ case NS_THEME_WIN_BROWSERTABBAR_TOOLBOX:
+ return nsUXThemeData::GetTheme(eUXBrowserTabBarRebar);
+ case NS_THEME_TOOLBAR:
+ case NS_THEME_TOOLBARBUTTON:
+ case NS_THEME_SEPARATOR:
+ return nsUXThemeData::GetTheme(eUXToolbar);
+ case NS_THEME_PROGRESSBAR:
+ case NS_THEME_PROGRESSBAR_VERTICAL:
+ case NS_THEME_PROGRESSCHUNK:
+ case NS_THEME_PROGRESSCHUNK_VERTICAL:
+ return nsUXThemeData::GetTheme(eUXProgress);
+ case NS_THEME_TAB:
+ case NS_THEME_TABPANEL:
+ case NS_THEME_TABPANELS:
+ return nsUXThemeData::GetTheme(eUXTab);
+ case NS_THEME_SCROLLBAR:
+ case NS_THEME_SCROLLBAR_SMALL:
+ case NS_THEME_SCROLLBAR_VERTICAL:
+ case NS_THEME_SCROLLBAR_HORIZONTAL:
+ case NS_THEME_SCROLLBARBUTTON_UP:
+ case NS_THEME_SCROLLBARBUTTON_DOWN:
+ case NS_THEME_SCROLLBARBUTTON_LEFT:
+ case NS_THEME_SCROLLBARBUTTON_RIGHT:
+ case NS_THEME_SCROLLBARTHUMB_VERTICAL:
+ case NS_THEME_SCROLLBARTHUMB_HORIZONTAL:
+ return nsUXThemeData::GetTheme(eUXScrollbar);
+ case NS_THEME_RANGE:
+ case NS_THEME_RANGE_THUMB:
+ case NS_THEME_SCALE_HORIZONTAL:
+ case NS_THEME_SCALE_VERTICAL:
+ case NS_THEME_SCALETHUMB_HORIZONTAL:
+ case NS_THEME_SCALETHUMB_VERTICAL:
+ return nsUXThemeData::GetTheme(eUXTrackbar);
+ case NS_THEME_SPINNER_UPBUTTON:
+ case NS_THEME_SPINNER_DOWNBUTTON:
+ return nsUXThemeData::GetTheme(eUXSpin);
+ case NS_THEME_STATUSBAR:
+ case NS_THEME_STATUSBARPANEL:
+ case NS_THEME_RESIZERPANEL:
+ case NS_THEME_RESIZER:
+ return nsUXThemeData::GetTheme(eUXStatus);
+ case NS_THEME_MENULIST:
+ case NS_THEME_MENULIST_BUTTON:
+ return nsUXThemeData::GetTheme(eUXCombobox);
+ case NS_THEME_TREEHEADERCELL:
+ case NS_THEME_TREEHEADERSORTARROW:
+ return nsUXThemeData::GetTheme(eUXHeader);
+ case NS_THEME_LISTBOX:
+ case NS_THEME_LISTITEM:
+ case NS_THEME_TREEVIEW:
+ case NS_THEME_TREETWISTYOPEN:
+ case NS_THEME_TREEITEM:
+ return nsUXThemeData::GetTheme(eUXListview);
+ case NS_THEME_MENUBAR:
+ case NS_THEME_MENUPOPUP:
+ case NS_THEME_MENUITEM:
+ case NS_THEME_CHECKMENUITEM:
+ case NS_THEME_RADIOMENUITEM:
+ case NS_THEME_MENUCHECKBOX:
+ case NS_THEME_MENURADIO:
+ case NS_THEME_MENUSEPARATOR:
+ case NS_THEME_MENUARROW:
+ case NS_THEME_MENUIMAGE:
+ case NS_THEME_MENUITEMTEXT:
+ return nsUXThemeData::GetTheme(eUXMenu);
+ case NS_THEME_WINDOW_TITLEBAR:
+ case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
+ case NS_THEME_WINDOW_FRAME_LEFT:
+ case NS_THEME_WINDOW_FRAME_RIGHT:
+ case NS_THEME_WINDOW_FRAME_BOTTOM:
+ case NS_THEME_WINDOW_BUTTON_CLOSE:
+ case NS_THEME_WINDOW_BUTTON_MINIMIZE:
+ case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
+ case NS_THEME_WINDOW_BUTTON_RESTORE:
+ case NS_THEME_WINDOW_BUTTON_BOX:
+ case NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED:
+ case NS_THEME_WIN_GLASS:
+ case NS_THEME_WIN_BORDERLESS_GLASS:
+ return nsUXThemeData::GetTheme(eUXWindowFrame);
+ }
+ return nullptr;
+}
+
+int32_t
+nsNativeThemeWin::StandardGetState(nsIFrame* aFrame, uint8_t aWidgetType,
+ bool wantFocused)
+{
+ EventStates eventState = GetContentState(aFrame, aWidgetType);
+ if (eventState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE))
+ return TS_ACTIVE;
+ if (eventState.HasState(NS_EVENT_STATE_HOVER))
+ return TS_HOVER;
+ if (wantFocused && eventState.HasState(NS_EVENT_STATE_FOCUS))
+ return TS_FOCUSED;
+
+ return TS_NORMAL;
+}
+
+bool
+nsNativeThemeWin::IsMenuActive(nsIFrame *aFrame, uint8_t aWidgetType)
+{
+ 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, uint8_t aWidgetType,
+ int32_t& aPart, int32_t& aState)
+{
+ if (!IsVistaOrLater()) {
+ // See GetTheme
+ if (aWidgetType == NS_THEME_MENULIST)
+ aWidgetType = NS_THEME_TEXTFIELD;
+ }
+
+ switch (aWidgetType) {
+ case NS_THEME_BUTTON: {
+ aPart = BP_BUTTON;
+ if (!aFrame) {
+ aState = TS_NORMAL;
+ return NS_OK;
+ }
+
+ EventStates eventState = GetContentState(aFrame, aWidgetType);
+ if (IsDisabled(aFrame, eventState)) {
+ aState = TS_DISABLED;
+ return NS_OK;
+ } else if (IsOpenButton(aFrame) ||
+ IsCheckedButton(aFrame)) {
+ aState = TS_ACTIVE;
+ return NS_OK;
+ }
+
+ aState = StandardGetState(aFrame, aWidgetType, 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 NS_THEME_CHECKBOX:
+ case NS_THEME_RADIO: {
+ bool isCheckbox = (aWidgetType == NS_THEME_CHECKBOX);
+ aPart = isCheckbox ? BP_CHECKBOX : BP_RADIO;
+
+ enum InputState {
+ UNCHECKED = 0, CHECKED, INDETERMINATE
+ };
+ InputState inputState = UNCHECKED;
+ bool isXULCheckboxRadio = false;
+
+ if (!aFrame) {
+ aState = TS_NORMAL;
+ } else {
+ if (GetCheckedOrSelected(aFrame, !isCheckbox)) {
+ inputState = CHECKED;
+ } if (isCheckbox && GetIndeterminate(aFrame)) {
+ inputState = INDETERMINATE;
+ }
+
+ EventStates eventState =
+ GetContentState(isXULCheckboxRadio ? aFrame->GetParent() : aFrame,
+ aWidgetType);
+ if (IsDisabled(aFrame, eventState)) {
+ aState = TS_DISABLED;
+ } else {
+ aState = StandardGetState(aFrame, aWidgetType, false);
+ }
+ }
+
+ // 4 unchecked states, 4 checked states, 4 indeterminate states.
+ aState += inputState * 4;
+ return NS_OK;
+ }
+ case NS_THEME_GROUPBOX: {
+ aPart = BP_GROUPBOX;
+ aState = TS_NORMAL;
+ // Since we don't support groupbox disabled and GBS_DISABLED looks the
+ // same as GBS_NORMAL don't bother supporting GBS_DISABLED.
+ return NS_OK;
+ }
+ case NS_THEME_NUMBER_INPUT:
+ case NS_THEME_TEXTFIELD:
+ case NS_THEME_TEXTFIELD_MULTILINE: {
+ EventStates eventState = GetContentState(aFrame, aWidgetType);
+
+ if (IsVistaOrLater()) {
+ /* 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 (IsDisabled(aFrame, eventState)) {
+ aState = TFS_EDITBORDER_DISABLED;
+ } else if (IsReadOnly(aFrame)) {
+ /* no special read-only state */
+ aState = TFS_EDITBORDER_NORMAL;
+ } else {
+ nsIContent* content = aFrame->GetContent();
+
+ /* XUL textboxes don't get focused themselves, because they have child
+ * html:input.. but we can check the XUL focused attributes on them
+ */
+ if (content && content->IsXULElement() && IsFocused(aFrame))
+ aState = TFS_EDITBORDER_FOCUSED;
+ else if (eventState.HasAtLeastOneOfStates(NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_FOCUS))
+ aState = TFS_EDITBORDER_FOCUSED;
+ else if (eventState.HasState(NS_EVENT_STATE_HOVER))
+ aState = TFS_EDITBORDER_HOVER;
+ else
+ aState = TFS_EDITBORDER_NORMAL;
+ }
+ } else {
+ aPart = TFP_TEXTFIELD;
+
+ if (!aFrame)
+ aState = TS_NORMAL;
+ else if (IsDisabled(aFrame, eventState))
+ aState = TS_DISABLED;
+ else if (IsReadOnly(aFrame))
+ aState = TFS_READONLY;
+ else
+ aState = StandardGetState(aFrame, aWidgetType, true);
+ }
+
+ return NS_OK;
+ }
+ case NS_THEME_FOCUS_OUTLINE: {
+ if (IsVistaOrLater()) {
+ // XXX the EDITBORDER values don't respect DTBG_OMITCONTENT
+ aPart = TFP_TEXTFIELD; //TFP_EDITBORDER_NOSCROLL;
+ aState = TS_FOCUSED; //TFS_EDITBORDER_FOCUSED;
+ } else {
+ aPart = TFP_TEXTFIELD;
+ aState = TS_FOCUSED;
+ }
+ return NS_OK;
+ }
+ case NS_THEME_TOOLTIP: {
+ aPart = TTP_STANDARD;
+ aState = TS_NORMAL;
+ return NS_OK;
+ }
+ case NS_THEME_PROGRESSBAR:
+ case NS_THEME_PROGRESSBAR_VERTICAL: {
+ // Note IsVerticalProgress only tests for orient css attrribute,
+ // NS_THEME_PROGRESSBAR_VERTICAL is dedicated to -moz-appearance:
+ // progressbar-vertical.
+ bool vertical = IsVerticalProgress(aFrame) ||
+ aWidgetType == NS_THEME_PROGRESSBAR_VERTICAL;
+ aPart = vertical ? PP_BARVERT : PP_BAR;
+ aState = PBBS_NORMAL;
+ return NS_OK;
+ }
+ case NS_THEME_PROGRESSCHUNK:
+ case NS_THEME_PROGRESSCHUNK_VERTICAL: {
+ nsIFrame* parentFrame = aFrame->GetParent();
+ if (aWidgetType == NS_THEME_PROGRESSCHUNK_VERTICAL ||
+ IsVerticalProgress(parentFrame)) {
+ aPart = IsVistaOrLater() ?
+ PP_FILLVERT : PP_CHUNKVERT;
+ } else {
+ aPart = IsVistaOrLater() ?
+ PP_FILL : PP_CHUNK;
+ }
+
+ aState = PBBVS_NORMAL;
+ return NS_OK;
+ }
+ case NS_THEME_TOOLBARBUTTON: {
+ aPart = BP_BUTTON;
+ if (!aFrame) {
+ aState = TS_NORMAL;
+ return NS_OK;
+ }
+
+ EventStates eventState = GetContentState(aFrame, aWidgetType);
+ if (IsDisabled(aFrame, eventState)) {
+ aState = TS_DISABLED;
+ return NS_OK;
+ }
+ if (IsOpenButton(aFrame)) {
+ aState = TS_ACTIVE;
+ return NS_OK;
+ }
+
+ if (eventState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE))
+ aState = TS_ACTIVE;
+ else if (eventState.HasState(NS_EVENT_STATE_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 NS_THEME_SEPARATOR: {
+ aPart = TP_SEPARATOR;
+ aState = TS_NORMAL;
+ return NS_OK;
+ }
+ case NS_THEME_SCROLLBARBUTTON_UP:
+ case NS_THEME_SCROLLBARBUTTON_DOWN:
+ case NS_THEME_SCROLLBARBUTTON_LEFT:
+ case NS_THEME_SCROLLBARBUTTON_RIGHT: {
+ aPart = SP_BUTTON;
+ aState = (aWidgetType - NS_THEME_SCROLLBARBUTTON_UP)*4;
+ EventStates eventState = GetContentState(aFrame, aWidgetType);
+ if (!aFrame)
+ aState += TS_NORMAL;
+ else if (IsDisabled(aFrame, eventState))
+ aState += TS_DISABLED;
+ else {
+ nsIFrame *parent = aFrame->GetParent();
+ EventStates parentState =
+ GetContentState(parent, parent->StyleDisplay()->mAppearance);
+ if (eventState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE))
+ aState += TS_ACTIVE;
+ else if (eventState.HasState(NS_EVENT_STATE_HOVER))
+ aState += TS_HOVER;
+ else if (IsVistaOrLater() &&
+ parentState.HasState(NS_EVENT_STATE_HOVER))
+ aState = (aWidgetType - NS_THEME_SCROLLBARBUTTON_UP) + SP_BUTTON_IMPLICIT_HOVER_BASE;
+ else
+ aState += TS_NORMAL;
+ }
+ return NS_OK;
+ }
+ case NS_THEME_SCROLLBAR_HORIZONTAL:
+ case NS_THEME_SCROLLBAR_VERTICAL: {
+ aPart = (aWidgetType == NS_THEME_SCROLLBAR_HORIZONTAL) ?
+ SP_TRACKSTARTHOR : SP_TRACKSTARTVERT;
+ aState = TS_NORMAL;
+ return NS_OK;
+ }
+ case NS_THEME_SCROLLBARTHUMB_HORIZONTAL:
+ case NS_THEME_SCROLLBARTHUMB_VERTICAL: {
+ aPart = (aWidgetType == NS_THEME_SCROLLBARTHUMB_HORIZONTAL) ?
+ SP_THUMBHOR : SP_THUMBVERT;
+ EventStates eventState = GetContentState(aFrame, aWidgetType);
+ if (!aFrame)
+ aState = TS_NORMAL;
+ else if (IsDisabled(aFrame, eventState))
+ aState = TS_DISABLED;
+ else {
+ if (eventState.HasState(NS_EVENT_STATE_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 (eventState.HasState(NS_EVENT_STATE_HOVER))
+ aState = TS_HOVER;
+ else
+ aState = TS_NORMAL;
+ }
+ return NS_OK;
+ }
+ case NS_THEME_RANGE:
+ case NS_THEME_SCALE_HORIZONTAL:
+ case NS_THEME_SCALE_VERTICAL: {
+ if (aWidgetType == NS_THEME_SCALE_HORIZONTAL ||
+ (aWidgetType == NS_THEME_RANGE &&
+ IsRangeHorizontal(aFrame))) {
+ aPart = TKP_TRACK;
+ aState = TRS_NORMAL;
+ } else {
+ aPart = TKP_TRACKVERT;
+ aState = TRVS_NORMAL;
+ }
+ return NS_OK;
+ }
+ case NS_THEME_RANGE_THUMB:
+ case NS_THEME_SCALETHUMB_HORIZONTAL:
+ case NS_THEME_SCALETHUMB_VERTICAL: {
+ if (aWidgetType == NS_THEME_RANGE_THUMB) {
+ if (IsRangeHorizontal(aFrame)) {
+ aPart = TKP_THUMBBOTTOM;
+ } else {
+ aPart = IsFrameRTL(aFrame) ? TKP_THUMBLEFT : TKP_THUMBRIGHT;
+ }
+ } else {
+ aPart = (aWidgetType == NS_THEME_SCALETHUMB_HORIZONTAL) ?
+ TKP_THUMB : TKP_THUMBVERT;
+ }
+ EventStates eventState = GetContentState(aFrame, aWidgetType);
+ if (!aFrame)
+ aState = TS_NORMAL;
+ else if (IsDisabled(aFrame, eventState)) {
+ aState = TKP_DISABLED;
+ }
+ else {
+ if (eventState.HasState(NS_EVENT_STATE_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 (eventState.HasState(NS_EVENT_STATE_FOCUS))
+ aState = TKP_FOCUSED;
+ else if (eventState.HasState(NS_EVENT_STATE_HOVER))
+ aState = TS_HOVER;
+ else
+ aState = TS_NORMAL;
+ }
+ return NS_OK;
+ }
+ case NS_THEME_SPINNER_UPBUTTON:
+ case NS_THEME_SPINNER_DOWNBUTTON: {
+ aPart = (aWidgetType == NS_THEME_SPINNER_UPBUTTON) ?
+ SPNP_UP : SPNP_DOWN;
+ EventStates eventState = GetContentState(aFrame, aWidgetType);
+ if (!aFrame)
+ aState = TS_NORMAL;
+ else if (IsDisabled(aFrame, eventState))
+ aState = TS_DISABLED;
+ else
+ aState = StandardGetState(aFrame, aWidgetType, false);
+ return NS_OK;
+ }
+ case NS_THEME_TOOLBOX:
+ case NS_THEME_WIN_MEDIA_TOOLBOX:
+ case NS_THEME_WIN_COMMUNICATIONS_TOOLBOX:
+ case NS_THEME_WIN_BROWSERTABBAR_TOOLBOX:
+ case NS_THEME_STATUSBAR:
+ case NS_THEME_SCROLLBAR:
+ case NS_THEME_SCROLLBAR_SMALL: {
+ aState = 0;
+ if (IsVistaOrLater()) {
+ // On vista, they have a part
+ aPart = RP_BACKGROUND;
+ } else {
+ // Otherwise, they don't. (But I bet
+ // RP_BACKGROUND would work here, too);
+ aPart = 0;
+ }
+ return NS_OK;
+ }
+ case NS_THEME_TOOLBAR: {
+ // Use -1 to indicate we don't wish to have the theme background drawn
+ // for this item. We will pass any nessessary information via aState,
+ // and will render the item using separate code.
+ aPart = -1;
+ aState = 0;
+ if (aFrame) {
+ nsIContent* content = aFrame->GetContent();
+ nsIContent* parent = content->GetParent();
+ // XXXzeniko hiding the first toolbar will result in an unwanted margin
+ if (parent && parent->GetFirstChild() == content) {
+ aState = 1;
+ }
+ }
+ return NS_OK;
+ }
+ case NS_THEME_STATUSBARPANEL:
+ case NS_THEME_RESIZERPANEL:
+ case NS_THEME_RESIZER: {
+ aPart = (aWidgetType - NS_THEME_STATUSBARPANEL) + 1;
+ aState = TS_NORMAL;
+ return NS_OK;
+ }
+ case NS_THEME_TREEVIEW:
+ case NS_THEME_LISTBOX: {
+ aPart = TREEVIEW_BODY;
+ aState = TS_NORMAL;
+ return NS_OK;
+ }
+ case NS_THEME_TABPANELS: {
+ aPart = TABP_PANELS;
+ aState = TS_NORMAL;
+ return NS_OK;
+ }
+ case NS_THEME_TABPANEL: {
+ aPart = TABP_PANEL;
+ aState = TS_NORMAL;
+ return NS_OK;
+ }
+ case NS_THEME_TAB: {
+ aPart = TABP_TAB;
+ if (!aFrame) {
+ aState = TS_NORMAL;
+ return NS_OK;
+ }
+
+ EventStates eventState = GetContentState(aFrame, aWidgetType);
+ if (IsDisabled(aFrame, eventState)) {
+ 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, aWidgetType, true);
+
+ return NS_OK;
+ }
+ case NS_THEME_TREEHEADERSORTARROW: {
+ // XXX Probably will never work due to a bug in the Luna theme.
+ aPart = 4;
+ aState = 1;
+ return NS_OK;
+ }
+ case NS_THEME_TREEHEADERCELL: {
+ aPart = 1;
+ if (!aFrame) {
+ aState = TS_NORMAL;
+ return NS_OK;
+ }
+
+ aState = StandardGetState(aFrame, aWidgetType, true);
+
+ return NS_OK;
+ }
+ case NS_THEME_MENULIST: {
+ nsIContent* content = aFrame->GetContent();
+ bool isHTML = content && content->IsHTMLElement();
+ bool useDropBorder = isHTML || IsMenuListEditable(aFrame);
+ EventStates eventState = GetContentState(aFrame, aWidgetType);
+
+ /* 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 (IsDisabled(aFrame, eventState)) {
+ aState = TS_DISABLED;
+ } else if (IsReadOnly(aFrame)) {
+ aState = TS_NORMAL;
+ } else if (IsOpenButton(aFrame)) {
+ aState = TS_ACTIVE;
+ } else {
+ if (useDropBorder && (eventState.HasState(NS_EVENT_STATE_FOCUS) || IsFocused(aFrame)))
+ aState = TS_ACTIVE;
+ else if (eventState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE))
+ aState = TS_ACTIVE;
+ else if (eventState.HasState(NS_EVENT_STATE_HOVER))
+ aState = TS_HOVER;
+ else
+ aState = TS_NORMAL;
+ }
+
+ return NS_OK;
+ }
+ case NS_THEME_MENULIST_BUTTON: {
+ bool isHTML = IsHTMLContent(aFrame);
+ nsIFrame* parentFrame = aFrame->GetParent();
+ bool isMenulist = !isHTML && parentFrame->GetType() == nsGkAtoms::menuFrame;
+ bool isOpen = false;
+
+ // HTML select and XUL menulist dropdown buttons get state from the parent.
+ if (isHTML || isMenulist)
+ aFrame = parentFrame;
+
+ EventStates eventState = GetContentState(aFrame, aWidgetType);
+ aPart = IsVistaOrLater() ?
+ CBP_DROPMARKER_VISTA : CBP_DROPMARKER;
+
+ // For HTML controls with author styling, we should fall
+ // back to the old dropmarker style to avoid clashes with
+ // author-specified backgrounds and borders (bug #441034)
+ if (isHTML && IsWidgetStyled(aFrame->PresContext(), aFrame, NS_THEME_MENULIST))
+ aPart = CBP_DROPMARKER;
+
+ if (IsDisabled(aFrame, eventState)) {
+ aState = TS_DISABLED;
+ return NS_OK;
+ }
+
+ if (isHTML) {
+ nsIComboboxControlFrame* ccf = do_QueryFrame(aFrame);
+ isOpen = (ccf && ccf->IsDroppedDownOrHasParentPopup());
+ }
+ else
+ isOpen = IsOpenButton(aFrame);
+
+ if (IsVistaOrLater()) {
+ if (isHTML || IsMenuListEditable(aFrame)) {
+ if (isOpen) {
+ /* Hover is propagated, but we need to know whether we're
+ * hovering just the combobox frame, not the dropdown frame.
+ * But, we can't get that information, since hover is on the
+ * content node, and they share the same content node. So,
+ * instead, we cheat -- if the dropdown is open, we always
+ * show the hover state. This looks fine in practice.
+ */
+ aState = TS_HOVER;
+ return NS_OK;
+ }
+ } else {
+ /* On Vista, the dropdown indicator on a menulist button in
+ * chrome is not given a hover effect. When the frame isn't
+ * isn't HTML content, we cheat and force the dropdown state
+ * to be normal. (Bug 430434)
+ */
+ aState = TS_NORMAL;
+ return NS_OK;
+ }
+ }
+
+ aState = TS_NORMAL;
+
+ // Dropdown button active state doesn't need :hover.
+ if (eventState.HasState(NS_EVENT_STATE_ACTIVE)) {
+ if (isOpen && (isHTML || isMenulist)) {
+ // XXX Button should look active until the mouse is released, but
+ // without making it look active when the popup is clicked.
+ return NS_OK;
+ }
+ aState = TS_ACTIVE;
+ }
+ else if (eventState.HasState(NS_EVENT_STATE_HOVER)) {
+ // No hover effect for XUL menulists and autocomplete dropdown buttons
+ // while the dropdown menu is open.
+ if (isOpen) {
+ // XXX HTML select dropdown buttons should have the hover effect when
+ // hovering the combobox frame, but not the popup frame.
+ return NS_OK;
+ }
+ aState = TS_HOVER;
+ }
+ return NS_OK;
+ }
+ case NS_THEME_MENUPOPUP: {
+ aPart = MENU_POPUPBACKGROUND;
+ aState = MB_ACTIVE;
+ return NS_OK;
+ }
+ case NS_THEME_MENUITEM:
+ case NS_THEME_CHECKMENUITEM:
+ case NS_THEME_RADIOMENUITEM: {
+ bool isTopLevel = false;
+ bool isOpen = false;
+ bool isHover = false;
+ nsMenuFrame *menuFrame = do_QueryFrame(aFrame);
+ EventStates eventState = GetContentState(aFrame, aWidgetType);
+
+ isTopLevel = IsTopLevelMenu(aFrame);
+
+ if (menuFrame)
+ isOpen = menuFrame->IsOpen();
+
+ isHover = IsMenuActive(aFrame, aWidgetType);
+
+ if (isTopLevel) {
+ aPart = MENU_BARITEM;
+
+ if (isOpen)
+ aState = MBI_PUSHED;
+ else if (isHover)
+ aState = MBI_HOT;
+ else
+ aState = MBI_NORMAL;
+
+ // the disabled states are offset by 3
+ if (IsDisabled(aFrame, eventState))
+ aState += 3;
+ } else {
+ aPart = MENU_POPUPITEM;
+
+ if (isHover)
+ aState = MPI_HOT;
+ else
+ aState = MPI_NORMAL;
+
+ // the disabled states are offset by 2
+ if (IsDisabled(aFrame, eventState))
+ aState += 2;
+ }
+
+ return NS_OK;
+ }
+ case NS_THEME_MENUSEPARATOR:
+ aPart = MENU_POPUPSEPARATOR;
+ aState = 0;
+ return NS_OK;
+ case NS_THEME_MENUARROW:
+ {
+ aPart = MENU_POPUPSUBMENU;
+ EventStates eventState = GetContentState(aFrame, aWidgetType);
+ aState = IsDisabled(aFrame, eventState) ? MSM_DISABLED : MSM_NORMAL;
+ return NS_OK;
+ }
+ case NS_THEME_MENUCHECKBOX:
+ case NS_THEME_MENURADIO:
+ {
+ EventStates eventState = GetContentState(aFrame, aWidgetType);
+
+ aPart = MENU_POPUPCHECK;
+ aState = MC_CHECKMARKNORMAL;
+
+ // Radio states are offset by 2
+ if (aWidgetType == NS_THEME_MENURADIO)
+ aState += 2;
+
+ // the disabled states are offset by 1
+ if (IsDisabled(aFrame, eventState))
+ aState += 1;
+
+ return NS_OK;
+ }
+ case NS_THEME_MENUITEMTEXT:
+ case NS_THEME_MENUIMAGE:
+ aPart = -1;
+ aState = 0;
+ return NS_OK;
+
+ case NS_THEME_WINDOW_TITLEBAR:
+ aPart = mozilla::widget::themeconst::WP_CAPTION;
+ aState = GetTopLevelWindowActiveState(aFrame);
+ return NS_OK;
+ case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
+ aPart = mozilla::widget::themeconst::WP_MAXCAPTION;
+ aState = GetTopLevelWindowActiveState(aFrame);
+ return NS_OK;
+ case NS_THEME_WINDOW_FRAME_LEFT:
+ aPart = mozilla::widget::themeconst::WP_FRAMELEFT;
+ aState = GetTopLevelWindowActiveState(aFrame);
+ return NS_OK;
+ case NS_THEME_WINDOW_FRAME_RIGHT:
+ aPart = mozilla::widget::themeconst::WP_FRAMERIGHT;
+ aState = GetTopLevelWindowActiveState(aFrame);
+ return NS_OK;
+ case NS_THEME_WINDOW_FRAME_BOTTOM:
+ aPart = mozilla::widget::themeconst::WP_FRAMEBOTTOM;
+ aState = GetTopLevelWindowActiveState(aFrame);
+ return NS_OK;
+ case NS_THEME_WINDOW_BUTTON_CLOSE:
+ aPart = mozilla::widget::themeconst::WP_CLOSEBUTTON;
+ aState = GetWindowFrameButtonState(aFrame, GetContentState(aFrame, aWidgetType));
+ return NS_OK;
+ case NS_THEME_WINDOW_BUTTON_MINIMIZE:
+ aPart = mozilla::widget::themeconst::WP_MINBUTTON;
+ aState = GetWindowFrameButtonState(aFrame, GetContentState(aFrame, aWidgetType));
+ return NS_OK;
+ case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
+ aPart = mozilla::widget::themeconst::WP_MAXBUTTON;
+ aState = GetWindowFrameButtonState(aFrame, GetContentState(aFrame, aWidgetType));
+ return NS_OK;
+ case NS_THEME_WINDOW_BUTTON_RESTORE:
+ aPart = mozilla::widget::themeconst::WP_RESTOREBUTTON;
+ aState = GetWindowFrameButtonState(aFrame, GetContentState(aFrame, aWidgetType));
+ return NS_OK;
+ case NS_THEME_WINDOW_BUTTON_BOX:
+ case NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED:
+ case NS_THEME_WIN_GLASS:
+ case NS_THEME_WIN_BORDERLESS_GLASS:
+ aPart = -1;
+ aState = 0;
+ return NS_OK;
+ }
+
+ aPart = 0;
+ aState = 0;
+ return NS_ERROR_FAILURE;
+}
+
+static bool
+AssumeThemePartAndStateAreTransparent(int32_t aPart, int32_t aState)
+{
+ if (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(nsIFrame* aFrame)
+{
+ if (WinUtils::IsPerMonitorDPIAware()) {
+ nsIWidget* rootWidget = aFrame->PresContext()->GetRootWidget();
+ if (rootWidget) {
+ double systemScale = WinUtils::SystemScaleFactor();
+ return rootWidget->GetDefaultScale().scale / systemScale;
+ }
+ }
+ return 1.0;
+}
+
+NS_IMETHODIMP
+nsNativeThemeWin::DrawWidgetBackground(nsRenderingContext* aContext,
+ nsIFrame* aFrame,
+ uint8_t aWidgetType,
+ const nsRect& aRect,
+ const nsRect& aDirtyRect)
+{
+ HANDLE theme = GetTheme(aWidgetType);
+ if (!theme)
+ return ClassicDrawWidgetBackground(aContext, aFrame, aWidgetType, aRect, aDirtyRect);
+
+ // ^^ without the right sdk, assume xp theming and fall through.
+ if (nsUXThemeData::CheckForCompositor()) {
+ switch (aWidgetType) {
+ case NS_THEME_WINDOW_TITLEBAR:
+ case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
+ case NS_THEME_WINDOW_FRAME_LEFT:
+ case NS_THEME_WINDOW_FRAME_RIGHT:
+ case NS_THEME_WINDOW_FRAME_BOTTOM:
+ // Nothing to draw, these areas are glass. Minimum dimensions
+ // should be set, so xul content should be layed out correctly.
+ return NS_OK;
+ break;
+ case NS_THEME_WINDOW_BUTTON_CLOSE:
+ case NS_THEME_WINDOW_BUTTON_MINIMIZE:
+ case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
+ case NS_THEME_WINDOW_BUTTON_RESTORE:
+ // Not conventional bitmaps, can't be retrieved. If we fall
+ // through here and call the theme library we'll get aero
+ // basic bitmaps.
+ return NS_OK;
+ break;
+ case NS_THEME_WIN_GLASS:
+ case NS_THEME_WIN_BORDERLESS_GLASS:
+ // Nothing to draw, this is the glass background.
+ return NS_OK;
+ case NS_THEME_WINDOW_BUTTON_BOX:
+ case NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED:
+ // We handle these through nsIWidget::UpdateThemeGeometries
+ return NS_OK;
+ break;
+ }
+ }
+
+ int32_t part, state;
+ nsresult rv = GetThemePartAndState(aFrame, aWidgetType, part, state);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (AssumeThemePartAndStateAreTransparent(part, state)) {
+ return NS_OK;
+ }
+
+ RefPtr<gfxContext> ctx = aContext->ThebesContext();
+ gfxContextMatrixAutoSaveRestore save(ctx);
+
+ double themeScale = GetThemeDpiScaleFactor(aFrame);
+ if (themeScale != 1.0) {
+ ctx->SetMatrix(ctx->CurrentMatrix().Scale(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.ScaleInverse(p2a * themeScale);
+ dr.ScaleInverse(p2a * themeScale);
+
+ gfxWindowsNativeDrawing nativeDrawing(ctx, dr, GetWidgetNativeDrawingFlags(aWidgetType));
+
+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 (aWidgetType == NS_THEME_WINDOW_TITLEBAR) {
+ // Clip out the left and right corners of the frame, all we want in
+ // is the middle section.
+ widgetRect.left -= GetSystemMetrics(SM_CXFRAME);
+ widgetRect.right += GetSystemMetrics(SM_CXFRAME);
+ } else if (aWidgetType == NS_THEME_WINDOW_TITLEBAR_MAXIMIZED) {
+ // The origin of the window is off screen when maximized and windows
+ // doesn't compensate for this in rendering the background. Push the
+ // top of the bitmap down by SM_CYFRAME so we get the full graphic.
+ widgetRect.top += GetSystemMetrics(SM_CYFRAME);
+ } else if (aWidgetType == NS_THEME_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;
+ }
+ }
+ else if (aWidgetType == NS_THEME_WINDOW_BUTTON_MINIMIZE) {
+ OffsetBackgroundRect(widgetRect, CAPTIONBUTTON_MINIMIZE);
+ }
+ else if (aWidgetType == NS_THEME_WINDOW_BUTTON_MAXIMIZE ||
+ aWidgetType == NS_THEME_WINDOW_BUTTON_RESTORE) {
+ OffsetBackgroundRect(widgetRect, CAPTIONBUTTON_RESTORE);
+ }
+ else if (aWidgetType == NS_THEME_WINDOW_BUTTON_CLOSE) {
+ OffsetBackgroundRect(widgetRect, CAPTIONBUTTON_CLOSE);
+ }
+
+ // 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 (aWidgetType == NS_THEME_RANGE ||
+ aWidgetType == NS_THEME_SCALE_HORIZONTAL ||
+ aWidgetType == NS_THEME_SCALE_VERTICAL) {
+ 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 (aWidgetType == NS_THEME_SCALE_HORIZONTAL ||
+ (aWidgetType == NS_THEME_RANGE && 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 (aWidgetType == NS_THEME_MENUCHECKBOX || aWidgetType == NS_THEME_MENURADIO)
+ {
+ bool isChecked = false;
+ isChecked = CheckBooleanAttr(aFrame, nsGkAtoms::checked);
+
+ if (isChecked)
+ {
+ int bgState = MCB_NORMAL;
+ EventStates eventState = GetContentState(aFrame, aWidgetType);
+
+ // the disabled states are offset by 1
+ if (IsDisabled(aFrame, eventState))
+ bgState += 1;
+
+ SIZE checkboxBGSize(GetCheckboxBGSize(theme, hdc));
+
+ RECT checkBGRect = widgetRect;
+ if (IsFrameRTL(aFrame)) {
+ checkBGRect.left = checkBGRect.right-checkboxBGSize.cx;
+ } else {
+ checkBGRect.right = checkBGRect.left+checkboxBGSize.cx;
+ }
+
+ // Center the checkbox background vertically in the menuitem
+ checkBGRect.top += (checkBGRect.bottom - checkBGRect.top)/2 - checkboxBGSize.cy/2;
+ checkBGRect.bottom = checkBGRect.top + checkboxBGSize.cy;
+
+ DrawThemeBackground(theme, hdc, MENU_POPUPCHECKBACKGROUND, bgState, &checkBGRect, &clipRect);
+
+ MARGINS checkMargins = GetCheckboxMargins(theme, hdc);
+ RECT checkRect = checkBGRect;
+ checkRect.left += checkMargins.cxLeftWidth;
+ checkRect.right -= checkMargins.cxRightWidth;
+ checkRect.top += checkMargins.cyTopHeight;
+ checkRect.bottom -= checkMargins.cyBottomHeight;
+ DrawThemeBackground(theme, hdc, MENU_POPUPCHECK, state, &checkRect, &clipRect);
+ }
+ }
+ else if (aWidgetType == NS_THEME_MENUPOPUP)
+ {
+ DrawThemeBackground(theme, hdc, MENU_POPUPBORDERS, /* state */ 0, &widgetRect, &clipRect);
+ SIZE borderSize;
+ GetThemePartSize(theme, hdc, MENU_POPUPBORDERS, 0, nullptr, TS_TRUE, &borderSize);
+
+ RECT bgRect = widgetRect;
+ bgRect.top += borderSize.cy;
+ bgRect.bottom -= borderSize.cy;
+ bgRect.left += borderSize.cx;
+ bgRect.right -= borderSize.cx;
+
+ DrawThemeBackground(theme, hdc, MENU_POPUPBACKGROUND, /* state */ 0, &bgRect, &clipRect);
+
+ SIZE gutterSize(GetGutterSize(theme, hdc));
+
+ RECT gutterRect;
+ gutterRect.top = bgRect.top;
+ gutterRect.bottom = bgRect.bottom;
+ if (IsFrameRTL(aFrame)) {
+ gutterRect.right = bgRect.right;
+ gutterRect.left = gutterRect.right-gutterSize.cx;
+ } else {
+ gutterRect.left = bgRect.left;
+ gutterRect.right = gutterRect.left+gutterSize.cx;
+ }
+
+ DrawThemeBGRTLAware(theme, hdc, MENU_POPUPGUTTER, /* state */ 0,
+ &gutterRect, &clipRect, IsFrameRTL(aFrame));
+ }
+ else if (aWidgetType == NS_THEME_MENUSEPARATOR)
+ {
+ SIZE gutterSize(GetGutterSize(theme,hdc));
+
+ RECT sepRect = widgetRect;
+ if (IsFrameRTL(aFrame))
+ sepRect.right -= gutterSize.cx;
+ else
+ sepRect.left += gutterSize.cx;
+
+ DrawThemeBackground(theme, hdc, MENU_POPUPSEPARATOR, /* state */ 0, &sepRect, &clipRect);
+ }
+ else if (aWidgetType == NS_THEME_MENUARROW)
+ {
+ // We're dpi aware and as such on systems that have dpi > 96 set, the
+ // theme library expects us to do proper positioning and scaling of glyphs.
+ // For NS_THEME_MENUARROW, layout may hand us a widget rect larger than the
+ // glyph rect we request in GetMinimumWidgetSize. To prevent distortion we
+ // have to position and scale what we draw.
+
+ SIZE glyphSize;
+ GetThemePartSize(theme, hdc, part, state, nullptr, TS_TRUE, &glyphSize);
+
+ int32_t widgetHeight = widgetRect.bottom - widgetRect.top;
+
+ RECT renderRect = widgetRect;
+
+ // We request (glyph width * 2, glyph height) in GetMinimumWidgetSize. In
+ // Firefox some menu items provide the full height of the item to us, in
+ // others our widget rect is the exact dims of our arrow glyph. Adjust the
+ // vertical position by the added space, if any exists.
+ renderRect.top += ((widgetHeight - glyphSize.cy) / 2);
+ renderRect.bottom = renderRect.top + glyphSize.cy;
+ // I'm using the width of the arrow glyph for the arrow-side padding.
+ // AFAICT there doesn't appear to be a theme constant we can query
+ // for this value. Generally this looks correct, and has the added
+ // benefit of being a dpi adjusted value.
+ if (!IsFrameRTL(aFrame)) {
+ renderRect.right = widgetRect.right - glyphSize.cx;
+ renderRect.left = renderRect.right - glyphSize.cx;
+ } else {
+ renderRect.left = glyphSize.cx;
+ renderRect.right = renderRect.left + glyphSize.cx;
+ }
+ DrawThemeBGRTLAware(theme, hdc, part, state, &renderRect, &clipRect,
+ IsFrameRTL(aFrame));
+ }
+ // The following widgets need to be RTL-aware
+ else if (aWidgetType == NS_THEME_RESIZER ||
+ aWidgetType == NS_THEME_MENULIST_BUTTON)
+ {
+ DrawThemeBGRTLAware(theme, hdc, part, state,
+ &widgetRect, &clipRect, IsFrameRTL(aFrame));
+ }
+ else if (aWidgetType == NS_THEME_NUMBER_INPUT ||
+ aWidgetType == NS_THEME_TEXTFIELD ||
+ aWidgetType == NS_THEME_TEXTFIELD_MULTILINE) {
+ 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 (aWidgetType == NS_THEME_PROGRESSBAR ||
+ aWidgetType == NS_THEME_PROGRESSBAR_VERTICAL) {
+ // 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 (aWidgetType == NS_THEME_PROGRESSCHUNK ||
+ aWidgetType == NS_THEME_PROGRESSCHUNK_VERTICAL) {
+ DrawThemedProgressMeter(aFrame, aWidgetType, theme, hdc, part, state,
+ &widgetRect, &clipRect, p2a);
+ }
+ else if (aWidgetType == NS_THEME_FOCUS_OUTLINE) {
+ // Inflate 'widgetRect' with the focus outline size.
+ nsIntMargin border;
+ if (NS_SUCCEEDED(GetWidgetBorder(aFrame->PresContext()->DeviceContext(),
+ aFrame, aWidgetType, &border))) {
+ widgetRect.left -= border.left;
+ widgetRect.right += border.right;
+ widgetRect.top -= border.top;
+ widgetRect.bottom += border.bottom;
+ }
+
+ DTBGOPTS opts = {
+ sizeof(DTBGOPTS),
+ DTBG_OMITCONTENT | DTBG_CLIPRECT,
+ clipRect
+ };
+ DrawThemeBackgroundEx(theme, hdc, part, state, &widgetRect, &opts);
+ }
+ // 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 and scale elements
+ // XXX it'd be nice to draw these outside of the frame
+ if (aWidgetType == NS_THEME_RANGE ||
+ aWidgetType == NS_THEME_SCALE_HORIZONTAL ||
+ aWidgetType == NS_THEME_SCALE_VERTICAL) {
+ EventStates contentState = GetContentState(aFrame, aWidgetType);
+
+ if (contentState.HasState(NS_EVENT_STATE_FOCUS)) {
+ 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);
+ }
+ }
+ }
+ else if (aWidgetType == NS_THEME_TOOLBAR && state == 0) {
+ // Draw toolbar separator lines above all toolbars except the first one.
+ // The lines are part of the Rebar theme, which is loaded for NS_THEME_TOOLBOX.
+ theme = GetTheme(NS_THEME_TOOLBOX);
+ if (!theme)
+ return NS_ERROR_FAILURE;
+
+ widgetRect.bottom = widgetRect.top + TB_SEPARATOR_HEIGHT;
+ DrawThemeEdge(theme, hdc, RP_BAND, 0, &widgetRect, EDGE_ETCHED, BF_TOP, nullptr);
+ }
+ else if (aWidgetType == NS_THEME_SCROLLBARTHUMB_HORIZONTAL ||
+ aWidgetType == NS_THEME_SCROLLBARTHUMB_VERTICAL)
+ {
+ // Draw the decorative gripper for the scrollbar thumb button, if it fits
+
+ SIZE gripSize;
+ MARGINS thumbMgns;
+ int gripPart = (aWidgetType == NS_THEME_SCROLLBARTHUMB_HORIZONTAL) ?
+ SP_GRIPPERHOR : SP_GRIPPERVERT;
+
+ if (GetThemePartSize(theme, hdc, gripPart, state, nullptr, TS_TRUE, &gripSize) == S_OK &&
+ GetThemeMargins(theme, hdc, part, state, TMT_CONTENTMARGINS, nullptr, &thumbMgns) == S_OK &&
+ gripSize.cx + thumbMgns.cxLeftWidth + thumbMgns.cxRightWidth <= widgetRect.right - widgetRect.left &&
+ gripSize.cy + thumbMgns.cyTopHeight + thumbMgns.cyBottomHeight <= widgetRect.bottom - widgetRect.top)
+ {
+ DrawThemeBackground(theme, hdc, gripPart, state, &widgetRect, &clipRect);
+ }
+ }
+
+ nativeDrawing.EndNativeDrawing();
+
+ if (nativeDrawing.ShouldRenderAgain())
+ goto RENDER_AGAIN;
+
+ nativeDrawing.PaintToContext();
+
+ return NS_OK;
+}
+
+static void
+ScaleForFrameDPI(nsIntMargin* 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);
+ }
+}
+
+NS_IMETHODIMP
+nsNativeThemeWin::GetWidgetBorder(nsDeviceContext* aContext,
+ nsIFrame* aFrame,
+ uint8_t aWidgetType,
+ nsIntMargin* aResult)
+{
+ HANDLE theme = GetTheme(aWidgetType);
+ nsresult rv = NS_OK;
+ if (!theme) {
+ rv = ClassicGetWidgetBorder(aContext, aFrame, aWidgetType, aResult);
+ ScaleForFrameDPI(aResult, aFrame);
+ return rv;
+ }
+
+ aResult->top = aResult->bottom = aResult->left = aResult->right = 0;
+
+ if (!WidgetIsContainer(aWidgetType) ||
+ aWidgetType == NS_THEME_TOOLBOX ||
+ aWidgetType == NS_THEME_WIN_MEDIA_TOOLBOX ||
+ aWidgetType == NS_THEME_WIN_COMMUNICATIONS_TOOLBOX ||
+ aWidgetType == NS_THEME_WIN_BROWSERTABBAR_TOOLBOX ||
+ aWidgetType == NS_THEME_STATUSBAR ||
+ aWidgetType == NS_THEME_RESIZER || aWidgetType == NS_THEME_TABPANEL ||
+ aWidgetType == NS_THEME_SCROLLBAR_HORIZONTAL ||
+ aWidgetType == NS_THEME_SCROLLBAR_VERTICAL ||
+ aWidgetType == NS_THEME_MENUITEM || aWidgetType == NS_THEME_CHECKMENUITEM ||
+ aWidgetType == NS_THEME_RADIOMENUITEM || aWidgetType == NS_THEME_MENUPOPUP ||
+ aWidgetType == NS_THEME_MENUIMAGE || aWidgetType == NS_THEME_MENUITEMTEXT ||
+ aWidgetType == NS_THEME_SEPARATOR ||
+ aWidgetType == NS_THEME_WINDOW_TITLEBAR ||
+ aWidgetType == NS_THEME_WINDOW_TITLEBAR_MAXIMIZED ||
+ aWidgetType == NS_THEME_WIN_GLASS || aWidgetType == NS_THEME_WIN_BORDERLESS_GLASS)
+ return NS_OK; // Don't worry about it.
+
+ int32_t part, state;
+ rv = GetThemePartAndState(aFrame, aWidgetType, part, state);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (aWidgetType == NS_THEME_TOOLBAR) {
+ // make space for the separator line above all toolbars but the first
+ if (state == 0)
+ aResult->top = TB_SEPARATOR_HEIGHT;
+ return NS_OK;
+ }
+
+ // 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(theme, nullptr, part, state, &outerRect, &contentRect);
+
+ if (FAILED(res))
+ return NS_ERROR_FAILURE;
+
+ // Now compute the delta in each direction and place it in our
+ // nsIntMargin struct.
+ aResult->top = contentRect.top - outerRect.top;
+ aResult->bottom = outerRect.bottom - contentRect.bottom;
+ aResult->left = contentRect.left - outerRect.left;
+ aResult->right = outerRect.right - contentRect.right;
+
+ // Remove the edges for tabs that are before or after the selected tab,
+ if (aWidgetType == NS_THEME_TAB) {
+ if (IsLeftToSelectedTab(aFrame))
+ // Remove the right edge, since we won't be drawing it.
+ aResult->right = 0;
+ else if (IsRightToSelectedTab(aFrame))
+ // Remove the left edge, since we won't be drawing it.
+ aResult->left = 0;
+ }
+
+ if (aFrame && (aWidgetType == NS_THEME_NUMBER_INPUT ||
+ aWidgetType == NS_THEME_TEXTFIELD ||
+ aWidgetType == NS_THEME_TEXTFIELD_MULTILINE)) {
+ 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.
+ aResult->top++;
+ aResult->left++;
+ aResult->bottom++;
+ aResult->right++;
+ }
+ }
+
+ ScaleForFrameDPI(aResult, aFrame);
+ return rv;
+}
+
+bool
+nsNativeThemeWin::GetWidgetPadding(nsDeviceContext* aContext,
+ nsIFrame* aFrame,
+ uint8_t aWidgetType,
+ nsIntMargin* aResult)
+{
+ switch (aWidgetType) {
+ // Radios and checkboxes return a fixed size in GetMinimumWidgetSize
+ // and have a meaningful baseline, so they can't have
+ // author-specified padding.
+ case NS_THEME_CHECKBOX:
+ case NS_THEME_RADIO:
+ aResult->SizeTo(0, 0, 0, 0);
+ return true;
+ }
+
+ bool ok = true;
+
+ if (aWidgetType == NS_THEME_WINDOW_BUTTON_BOX ||
+ aWidgetType == NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED) {
+ aResult->SizeTo(0, 0, 0, 0);
+
+ // aero glass doesn't display custom buttons
+ if (nsUXThemeData::CheckForCompositor())
+ return true;
+
+ // button padding for standard windows
+ if (aWidgetType == NS_THEME_WINDOW_BUTTON_BOX) {
+ aResult->top = GetSystemMetrics(SM_CXFRAME);
+ }
+ ScaleForFrameDPI(aResult, aFrame);
+ return ok;
+ }
+
+ // Content padding
+ if (aWidgetType == NS_THEME_WINDOW_TITLEBAR ||
+ aWidgetType == NS_THEME_WINDOW_TITLEBAR_MAXIMIZED) {
+ aResult->SizeTo(0, 0, 0, 0);
+ // XXX Maximized windows have an offscreen offset equal to
+ // the border padding. This should be addressed in nsWindow,
+ // but currently can't be, see UpdateNonClientMargins.
+ if (aWidgetType == NS_THEME_WINDOW_TITLEBAR_MAXIMIZED)
+ aResult->top = GetSystemMetrics(SM_CXFRAME)
+ + GetSystemMetrics(SM_CXPADDEDBORDER);
+ return ok;
+ }
+
+ HANDLE theme = GetTheme(aWidgetType);
+ if (!theme) {
+ ok = ClassicGetWidgetPadding(aContext, aFrame, aWidgetType, aResult);
+ ScaleForFrameDPI(aResult, aFrame);
+ return ok;
+ }
+
+ if (aWidgetType == NS_THEME_MENUPOPUP)
+ {
+ SIZE popupSize;
+ GetThemePartSize(theme, nullptr, MENU_POPUPBORDERS, /* state */ 0, nullptr, TS_TRUE, &popupSize);
+ aResult->top = aResult->bottom = popupSize.cy;
+ aResult->left = aResult->right = popupSize.cx;
+ ScaleForFrameDPI(aResult, aFrame);
+ return ok;
+ }
+
+ if (IsVistaOrLater()) {
+ if (aWidgetType == NS_THEME_NUMBER_INPUT ||
+ aWidgetType == NS_THEME_TEXTFIELD ||
+ aWidgetType == NS_THEME_TEXTFIELD_MULTILINE ||
+ aWidgetType == NS_THEME_MENULIST)
+ {
+ /* If we have author-specified padding for these elements, don't do the fixups below */
+ if (aFrame->PresContext()->HasAuthorSpecifiedRules(aFrame, NS_AUTHOR_SPECIFIED_PADDING))
+ return false;
+ }
+
+ /* 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 (aWidgetType == NS_THEME_NUMBER_INPUT ||
+ aWidgetType == NS_THEME_TEXTFIELD ||
+ aWidgetType == NS_THEME_TEXTFIELD_MULTILINE) {
+ aResult->top = aResult->bottom = 2;
+ aResult->left = aResult->right = 2;
+ ScaleForFrameDPI(aResult, aFrame);
+ return ok;
+ } else if (IsHTMLContent(aFrame) && aWidgetType == NS_THEME_MENULIST) {
+ /* 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 (aWidgetType)
+ {
+ case NS_THEME_MENUIMAGE:
+ right = 8;
+ left = 3;
+ break;
+ case NS_THEME_MENUCHECKBOX:
+ case NS_THEME_MENURADIO:
+ right = 8;
+ left = 0;
+ break;
+ case NS_THEME_MENUITEMTEXT:
+ // There seem to be exactly 4 pixels from the edge
+ // of the gutter to the text: 2px margin (CSS) + 2px padding (here)
+ {
+ SIZE size(GetGutterSize(theme, nullptr));
+ left = size.cx + 2;
+ }
+ break;
+ case NS_THEME_MENUSEPARATOR:
+ {
+ SIZE size(GetGutterSize(theme, nullptr));
+ left = size.cx + 5;
+ top = 10;
+ bottom = 7;
+ }
+ break;
+ default:
+ return false;
+ }
+
+ if (IsFrameRTL(aFrame))
+ {
+ aResult->right = left;
+ aResult->left = right;
+ }
+ else
+ {
+ aResult->right = right;
+ aResult->left = left;
+ }
+
+ ScaleForFrameDPI(aResult, aFrame);
+ return ok;
+}
+
+bool
+nsNativeThemeWin::GetWidgetOverflow(nsDeviceContext* aContext,
+ nsIFrame* aFrame,
+ uint8_t aWidgetType,
+ nsRect* 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.
+ */
+#if 0
+ if (IsVistaOrLater()) {
+ /* 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 (aWidgetType == NS_THEME_MENULIST_BUTTON &&
+ IsHTMLContent(aFrame) &&
+ !IsWidgetStyled(aFrame->GetParent()->PresContext(),
+ aFrame->GetParent(),
+ NS_THEME_MENULIST))
+ {
+ int32_t p2a = aContext->AppUnitsPerDevPixel();
+ /* Note: no overflow on the left */
+ nsMargin m(p2a, p2a, p2a, 0);
+ aOverflowRect->Inflate (m);
+ return true;
+ }
+ }
+#endif
+
+ if (aWidgetType == NS_THEME_FOCUS_OUTLINE) {
+ nsIntMargin border;
+ nsresult rv = GetWidgetBorder(aContext, aFrame, aWidgetType, &border);
+ if (NS_SUCCEEDED(rv)) {
+ int32_t p2a = aContext->AppUnitsPerDevPixel();
+ nsMargin m(NSIntPixelsToAppUnits(border.top, p2a),
+ NSIntPixelsToAppUnits(border.right, p2a),
+ NSIntPixelsToAppUnits(border.bottom, p2a),
+ NSIntPixelsToAppUnits(border.left, p2a));
+ aOverflowRect->Inflate(m);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+NS_IMETHODIMP
+nsNativeThemeWin::GetMinimumWidgetSize(nsPresContext* aPresContext, nsIFrame* aFrame,
+ uint8_t aWidgetType,
+ LayoutDeviceIntSize* aResult, bool* aIsOverridable)
+{
+ aResult->width = aResult->height = 0;
+ *aIsOverridable = true;
+ nsresult rv = NS_OK;
+
+ HANDLE theme = GetTheme(aWidgetType);
+ if (!theme) {
+ rv = ClassicGetMinimumWidgetSize(aFrame, aWidgetType, aResult, aIsOverridable);
+ ScaleForFrameDPI(aResult, aFrame);
+ return rv;
+ }
+ switch (aWidgetType) {
+ case NS_THEME_GROUPBOX:
+ case NS_THEME_NUMBER_INPUT:
+ case NS_THEME_TEXTFIELD:
+ case NS_THEME_TOOLBOX:
+ case NS_THEME_WIN_MEDIA_TOOLBOX:
+ case NS_THEME_WIN_COMMUNICATIONS_TOOLBOX:
+ case NS_THEME_WIN_BROWSERTABBAR_TOOLBOX:
+ case NS_THEME_TOOLBAR:
+ case NS_THEME_STATUSBAR:
+ case NS_THEME_PROGRESSCHUNK:
+ case NS_THEME_PROGRESSCHUNK_VERTICAL:
+ case NS_THEME_TABPANELS:
+ case NS_THEME_TABPANEL:
+ case NS_THEME_LISTBOX:
+ case NS_THEME_TREEVIEW:
+ case NS_THEME_MENUITEMTEXT:
+ case NS_THEME_WIN_GLASS:
+ case NS_THEME_WIN_BORDERLESS_GLASS:
+ return NS_OK; // Don't worry about it.
+ }
+
+ if (aWidgetType == NS_THEME_MENUITEM && IsTopLevelMenu(aFrame))
+ return NS_OK; // Don't worry about it for top level menus
+
+ // 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 (aWidgetType) {
+ case NS_THEME_SCROLLBARTHUMB_VERTICAL:
+ case NS_THEME_SCROLLBARTHUMB_HORIZONTAL:
+ case NS_THEME_SCROLLBARBUTTON_UP:
+ case NS_THEME_SCROLLBARBUTTON_DOWN:
+ case NS_THEME_SCROLLBARBUTTON_LEFT:
+ case NS_THEME_SCROLLBARBUTTON_RIGHT:
+ case NS_THEME_SCROLLBAR_HORIZONTAL:
+ case NS_THEME_SCROLLBAR_VERTICAL:
+ case NS_THEME_MENULIST_BUTTON: {
+ rv = ClassicGetMinimumWidgetSize(aFrame, aWidgetType, aResult, aIsOverridable);
+ ScaleForFrameDPI(aResult, aFrame);
+ return rv;
+ }
+ case NS_THEME_MENUITEM:
+ case NS_THEME_CHECKMENUITEM:
+ case NS_THEME_RADIOMENUITEM:
+ if(!IsTopLevelMenu(aFrame))
+ {
+ SIZE gutterSize(GetGutterSize(theme, nullptr));
+ aResult->width = gutterSize.cx;
+ aResult->height = gutterSize.cy;
+ ScaleForFrameDPI(aResult, aFrame);
+ return rv;
+ }
+ break;
+
+ case NS_THEME_MENUIMAGE:
+ case NS_THEME_MENUCHECKBOX:
+ case NS_THEME_MENURADIO:
+ {
+ SIZE boxSize(GetGutterSize(theme, nullptr));
+ aResult->width = boxSize.cx+2;
+ aResult->height = boxSize.cy;
+ *aIsOverridable = false;
+ ScaleForFrameDPI(aResult, aFrame);
+ return rv;
+ }
+
+ case NS_THEME_MENUITEMTEXT:
+ return NS_OK;
+
+ case NS_THEME_PROGRESSBAR:
+ case NS_THEME_PROGRESSBAR_VERTICAL:
+ // 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 NS_THEME_RESIZER:
+ *aIsOverridable = false;
+ break;
+
+ case NS_THEME_RANGE_THUMB:
+ case NS_THEME_SCALETHUMB_HORIZONTAL:
+ case NS_THEME_SCALETHUMB_VERTICAL:
+ {
+ *aIsOverridable = false;
+ // on Vista, GetThemePartAndState returns odd values for
+ // scale thumbs, so use a hardcoded size instead.
+ if (IsVistaOrLater()) {
+ if (aWidgetType == NS_THEME_SCALETHUMB_HORIZONTAL ||
+ (aWidgetType == NS_THEME_RANGE_THUMB && IsRangeHorizontal(aFrame))) {
+ aResult->width = 12;
+ aResult->height = 20;
+ }
+ else {
+ aResult->width = 20;
+ aResult->height = 12;
+ }
+ ScaleForFrameDPI(aResult, aFrame);
+ return rv;
+ }
+ break;
+ }
+
+ case NS_THEME_SCROLLBAR:
+ {
+ if (nsLookAndFeel::GetInt(
+ nsLookAndFeel::eIntID_UseOverlayScrollbars) != 0) {
+ aResult->SizeTo(::GetSystemMetrics(SM_CXHSCROLL),
+ ::GetSystemMetrics(SM_CYVSCROLL));
+ ScaleForFrameDPI(aResult, aFrame);
+ return rv;
+ }
+ break;
+ }
+
+ case NS_THEME_SEPARATOR:
+ // that's 2px left margin, 2px right margin and 2px separator
+ // (the margin is drawn as part of the separator, though)
+ aResult->width = 6;
+ ScaleForFrameDPI(aResult, aFrame);
+ return rv;
+
+ case NS_THEME_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;
+
+ case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
+ case NS_THEME_WINDOW_BUTTON_RESTORE:
+ // The only way to get accurate titlebar button info is to query a
+ // window w/buttons when it's visible. nsWindow takes care of this and
+ // stores that info in nsUXThemeData.
+ aResult->width = nsUXThemeData::sCommandButtons[CMDBUTTONIDX_RESTORE].cx;
+ aResult->height = nsUXThemeData::sCommandButtons[CMDBUTTONIDX_RESTORE].cy;
+ // For XP, subtract 4 from system metrics dimensions.
+ if (!IsVistaOrLater()) {
+ aResult->width -= 4;
+ aResult->height -= 4;
+ }
+ AddPaddingRect(aResult, CAPTIONBUTTON_RESTORE);
+ *aIsOverridable = false;
+ return rv;
+
+ case NS_THEME_WINDOW_BUTTON_MINIMIZE:
+ aResult->width = nsUXThemeData::sCommandButtons[CMDBUTTONIDX_MINIMIZE].cx;
+ aResult->height = nsUXThemeData::sCommandButtons[CMDBUTTONIDX_MINIMIZE].cy;
+ if (!IsVistaOrLater()) {
+ aResult->width -= 4;
+ aResult->height -= 4;
+ }
+ AddPaddingRect(aResult, CAPTIONBUTTON_MINIMIZE);
+ *aIsOverridable = false;
+ return rv;
+
+ case NS_THEME_WINDOW_BUTTON_CLOSE:
+ aResult->width = nsUXThemeData::sCommandButtons[CMDBUTTONIDX_CLOSE].cx;
+ aResult->height = nsUXThemeData::sCommandButtons[CMDBUTTONIDX_CLOSE].cy;
+ if (!IsVistaOrLater()) {
+ aResult->width -= 4;
+ aResult->height -= 4;
+ }
+ AddPaddingRect(aResult, CAPTIONBUTTON_CLOSE);
+ *aIsOverridable = false;
+ return rv;
+
+ case NS_THEME_WINDOW_TITLEBAR:
+ case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
+ aResult->height = GetSystemMetrics(SM_CYCAPTION);
+ aResult->height += GetSystemMetrics(SM_CYFRAME);
+ aResult->height += GetSystemMetrics(SM_CXPADDEDBORDER);
+ // On Win8.1, we don't want this scaling, because Windows doesn't scale
+ // the non-client area of the window, and we can end up with ugly overlap
+ // of the window frame controls into the tab bar or content area. But on
+ // Win10, we render the window controls ourselves, and the result looks
+ // better if we do apply this scaling (particularly with themes such as
+ // DevEdition; see bug 1267636).
+ if (IsWin10OrLater()) {
+ ScaleForFrameDPI(aResult, aFrame);
+ }
+ *aIsOverridable = false;
+ return rv;
+
+ case NS_THEME_WINDOW_BUTTON_BOX:
+ case NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED:
+ if (nsUXThemeData::CheckForCompositor()) {
+ aResult->width = nsUXThemeData::sCommandButtons[CMDBUTTONIDX_BUTTONBOX].cx;
+ aResult->height = nsUXThemeData::sCommandButtons[CMDBUTTONIDX_BUTTONBOX].cy
+ - GetSystemMetrics(SM_CYFRAME)
+ - GetSystemMetrics(SM_CXPADDEDBORDER);
+ if (aWidgetType == NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED) {
+ aResult->width += 1;
+ aResult->height -= 2;
+ }
+ *aIsOverridable = false;
+ return rv;
+ }
+ break;
+
+ case NS_THEME_WINDOW_FRAME_LEFT:
+ case NS_THEME_WINDOW_FRAME_RIGHT:
+ case NS_THEME_WINDOW_FRAME_BOTTOM:
+ aResult->width = GetSystemMetrics(SM_CXFRAME);
+ aResult->height = GetSystemMetrics(SM_CYFRAME);
+ *aIsOverridable = false;
+ return rv;
+ }
+
+ int32_t part, state;
+ rv = GetThemePartAndState(aFrame, aWidgetType, part, state);
+ if (NS_FAILED(rv))
+ return rv;
+
+ HDC hdc = ::GetDC(NULL);
+ if (!hdc)
+ return NS_ERROR_FAILURE;
+
+ SIZE sz;
+ GetThemePartSize(theme, hdc, part, state, nullptr, sizeReq, &sz);
+ aResult->width = sz.cx;
+ aResult->height = sz.cy;
+
+ switch(aWidgetType) {
+ case NS_THEME_SPINNER_UPBUTTON:
+ case NS_THEME_SPINNER_DOWNBUTTON:
+ aResult->width++;
+ aResult->height = aResult->height / 2 + 1;
+ break;
+
+ case NS_THEME_MENUSEPARATOR:
+ {
+ SIZE gutterSize(GetGutterSize(theme, hdc));
+ aResult->width += gutterSize.cx;
+ break;
+ }
+
+ case NS_THEME_MENUARROW:
+ {
+ // Use the width of the arrow glyph as padding. See the drawing
+ // code for details.
+ aResult->width *= 2;
+ break;
+ }
+ }
+
+ ::ReleaseDC(nullptr, hdc);
+
+ ScaleForFrameDPI(aResult, aFrame);
+ return rv;
+}
+
+NS_IMETHODIMP
+nsNativeThemeWin::WidgetStateChanged(nsIFrame* aFrame, uint8_t aWidgetType,
+ nsIAtom* aAttribute, bool* aShouldRepaint,
+ const nsAttrValue* aOldValue)
+{
+ // Some widget types just never change state.
+ if (aWidgetType == NS_THEME_TOOLBOX ||
+ aWidgetType == NS_THEME_WIN_MEDIA_TOOLBOX ||
+ aWidgetType == NS_THEME_WIN_COMMUNICATIONS_TOOLBOX ||
+ aWidgetType == NS_THEME_WIN_BROWSERTABBAR_TOOLBOX ||
+ aWidgetType == NS_THEME_TOOLBAR ||
+ aWidgetType == NS_THEME_STATUSBAR || aWidgetType == NS_THEME_STATUSBARPANEL ||
+ aWidgetType == NS_THEME_RESIZERPANEL ||
+ aWidgetType == NS_THEME_PROGRESSCHUNK ||
+ aWidgetType == NS_THEME_PROGRESSCHUNK_VERTICAL ||
+ aWidgetType == NS_THEME_PROGRESSBAR ||
+ aWidgetType == NS_THEME_PROGRESSBAR_VERTICAL ||
+ aWidgetType == NS_THEME_TOOLTIP ||
+ aWidgetType == NS_THEME_TABPANELS ||
+ aWidgetType == NS_THEME_TABPANEL ||
+ aWidgetType == NS_THEME_SEPARATOR ||
+ aWidgetType == NS_THEME_WIN_GLASS ||
+ aWidgetType == NS_THEME_WIN_BORDERLESS_GLASS) {
+ *aShouldRepaint = false;
+ return NS_OK;
+ }
+
+ if (aWidgetType == NS_THEME_WINDOW_TITLEBAR ||
+ aWidgetType == NS_THEME_WINDOW_TITLEBAR_MAXIMIZED ||
+ aWidgetType == NS_THEME_WINDOW_FRAME_LEFT ||
+ aWidgetType == NS_THEME_WINDOW_FRAME_RIGHT ||
+ aWidgetType == NS_THEME_WINDOW_FRAME_BOTTOM ||
+ aWidgetType == NS_THEME_WINDOW_BUTTON_CLOSE ||
+ aWidgetType == NS_THEME_WINDOW_BUTTON_MINIMIZE ||
+ aWidgetType == NS_THEME_WINDOW_BUTTON_MAXIMIZE ||
+ aWidgetType == NS_THEME_WINDOW_BUTTON_RESTORE) {
+ *aShouldRepaint = true;
+ return NS_OK;
+ }
+
+ // On Vista, the scrollbar buttons need to change state when the track has/doesn't have hover
+ if (!IsVistaOrLater() &&
+ (aWidgetType == NS_THEME_SCROLLBAR_VERTICAL ||
+ aWidgetType == NS_THEME_SCROLLBAR_HORIZONTAL)) {
+ *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 (IsVistaOrLater() &&
+ (aWidgetType == NS_THEME_MENULIST || aWidgetType == NS_THEME_MENULIST_BUTTON) &&
+ 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();
+ return NS_OK;
+}
+
+bool
+nsNativeThemeWin::ThemeSupportsWidget(nsPresContext* aPresContext,
+ nsIFrame* aFrame,
+ uint8_t aWidgetType)
+{
+ // XXXdwh We can go even further and call the API to ask if support exists for
+ // specific widgets.
+
+ if (aPresContext && !aPresContext->PresShell()->IsThemeSupportEnabled())
+ return false;
+
+ if (aWidgetType == NS_THEME_FOCUS_OUTLINE) {
+ return true;
+ }
+
+ HANDLE theme = nullptr;
+ if (aWidgetType == NS_THEME_CHECKBOX_CONTAINER)
+ theme = GetTheme(NS_THEME_CHECKBOX);
+ else if (aWidgetType == NS_THEME_RADIO_CONTAINER)
+ theme = GetTheme(NS_THEME_RADIO);
+ else
+ theme = GetTheme(aWidgetType);
+
+ if (theme && aWidgetType == NS_THEME_RESIZER)
+ return true;
+
+ if ((theme) || (!theme && ClassicThemeSupportsWidget(aFrame, aWidgetType)))
+ // turn off theming for some HTML widgets styled by the page
+ return (!IsWidgetStyled(aPresContext, aFrame, aWidgetType));
+
+ return false;
+}
+
+bool
+nsNativeThemeWin::WidgetIsContainer(uint8_t aWidgetType)
+{
+ // XXXdwh At some point flesh all of this out.
+ if (aWidgetType == NS_THEME_MENULIST_BUTTON ||
+ aWidgetType == NS_THEME_RADIO ||
+ aWidgetType == NS_THEME_CHECKBOX)
+ return false;
+ return true;
+}
+
+bool
+nsNativeThemeWin::ThemeDrawsFocusForWidget(uint8_t aWidgetType)
+{
+ return false;
+}
+
+bool
+nsNativeThemeWin::ThemeNeedsComboboxDropmarker()
+{
+ return true;
+}
+
+bool
+nsNativeThemeWin::WidgetAppearanceDependsOnWindowFocus(uint8_t aWidgetType)
+{
+ switch (aWidgetType) {
+ case NS_THEME_WINDOW_TITLEBAR:
+ case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
+ case NS_THEME_WINDOW_FRAME_LEFT:
+ case NS_THEME_WINDOW_FRAME_RIGHT:
+ case NS_THEME_WINDOW_FRAME_BOTTOM:
+ case NS_THEME_WINDOW_BUTTON_CLOSE:
+ case NS_THEME_WINDOW_BUTTON_MINIMIZE:
+ case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
+ case NS_THEME_WINDOW_BUTTON_RESTORE:
+ return true;
+ default:
+ return false;
+ }
+}
+
+nsITheme::ThemeGeometryType
+nsNativeThemeWin::ThemeGeometryTypeForWidget(nsIFrame* aFrame,
+ uint8_t aWidgetType)
+{
+ switch (aWidgetType) {
+ case NS_THEME_WINDOW_BUTTON_BOX:
+ case NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED:
+ return eThemeGeometryTypeWindowButtons;
+ default:
+ return eThemeGeometryTypeUnknown;
+ }
+}
+
+bool
+nsNativeThemeWin::ShouldHideScrollbars()
+{
+ return WinUtils::ShouldHideScrollbars();
+}
+
+nsITheme::Transparency
+nsNativeThemeWin::GetWidgetTransparency(nsIFrame* aFrame, uint8_t aWidgetType)
+{
+ switch (aWidgetType) {
+ case NS_THEME_SCROLLBAR_SMALL:
+ case NS_THEME_SCROLLBAR:
+ case NS_THEME_STATUSBAR:
+ // Knowing that scrollbars and statusbars are opaque improves
+ // performance, because we create layers for them. This better be
+ // true across all Windows themes! If it's not true, we should
+ // paint an opaque background for them to make it true!
+ return eOpaque;
+ case NS_THEME_WIN_GLASS:
+ case NS_THEME_WIN_BORDERLESS_GLASS:
+ case NS_THEME_SCALE_HORIZONTAL:
+ case NS_THEME_SCALE_VERTICAL:
+ case NS_THEME_PROGRESSBAR:
+ case NS_THEME_PROGRESSBAR_VERTICAL:
+ case NS_THEME_PROGRESSCHUNK:
+ case NS_THEME_PROGRESSCHUNK_VERTICAL:
+ case NS_THEME_RANGE:
+ return eTransparent;
+ }
+
+ HANDLE theme = GetTheme(aWidgetType);
+ // For the classic theme we don't really have a way of knowing
+ if (!theme) {
+ // menu backgrounds and tooltips which can't be themed are opaque
+ if (aWidgetType == NS_THEME_MENUPOPUP || aWidgetType == NS_THEME_TOOLTIP) {
+ return eOpaque;
+ }
+ return eUnknownTransparency;
+ }
+
+ int32_t part, state;
+ nsresult rv = GetThemePartAndState(aFrame, aWidgetType, 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,
+ uint8_t aWidgetType)
+{
+ switch (aWidgetType) {
+ case NS_THEME_RESIZER:
+ {
+ // The classic native resizer has an opaque grey background which doesn't
+ // match the usually white background of the scrollable container, so
+ // only support the native resizer if not in a scrollframe.
+ nsIFrame* parentFrame = aFrame->GetParent();
+ return (!parentFrame || parentFrame->GetType() != nsGkAtoms::scrollFrame);
+ }
+ case NS_THEME_MENUBAR:
+ case NS_THEME_MENUPOPUP:
+ // Classic non-flat menus are handled almost entirely through CSS.
+ if (!nsUXThemeData::sFlatMenus)
+ return false;
+ case NS_THEME_BUTTON:
+ case NS_THEME_NUMBER_INPUT:
+ case NS_THEME_TEXTFIELD:
+ case NS_THEME_TEXTFIELD_MULTILINE:
+ case NS_THEME_CHECKBOX:
+ case NS_THEME_RADIO:
+ case NS_THEME_RANGE:
+ case NS_THEME_RANGE_THUMB:
+ case NS_THEME_GROUPBOX:
+ case NS_THEME_SCROLLBARBUTTON_UP:
+ case NS_THEME_SCROLLBARBUTTON_DOWN:
+ case NS_THEME_SCROLLBARBUTTON_LEFT:
+ case NS_THEME_SCROLLBARBUTTON_RIGHT:
+ case NS_THEME_SCROLLBARTHUMB_VERTICAL:
+ case NS_THEME_SCROLLBARTHUMB_HORIZONTAL:
+ case NS_THEME_SCROLLBAR_VERTICAL:
+ case NS_THEME_SCROLLBAR_HORIZONTAL:
+ case NS_THEME_SCROLLBAR_NON_DISAPPEARING:
+ case NS_THEME_SCALE_HORIZONTAL:
+ case NS_THEME_SCALE_VERTICAL:
+ case NS_THEME_SCALETHUMB_HORIZONTAL:
+ case NS_THEME_SCALETHUMB_VERTICAL:
+ case NS_THEME_MENULIST_BUTTON:
+ case NS_THEME_SPINNER_UPBUTTON:
+ case NS_THEME_SPINNER_DOWNBUTTON:
+ case NS_THEME_LISTBOX:
+ case NS_THEME_TREEVIEW:
+ case NS_THEME_MENULIST_TEXTFIELD:
+ case NS_THEME_MENULIST:
+ case NS_THEME_TOOLTIP:
+ case NS_THEME_STATUSBAR:
+ case NS_THEME_STATUSBARPANEL:
+ case NS_THEME_RESIZERPANEL:
+ case NS_THEME_PROGRESSBAR:
+ case NS_THEME_PROGRESSBAR_VERTICAL:
+ case NS_THEME_PROGRESSCHUNK:
+ case NS_THEME_PROGRESSCHUNK_VERTICAL:
+ case NS_THEME_TAB:
+ case NS_THEME_TABPANEL:
+ case NS_THEME_TABPANELS:
+ case NS_THEME_MENUITEM:
+ case NS_THEME_CHECKMENUITEM:
+ case NS_THEME_RADIOMENUITEM:
+ case NS_THEME_MENUCHECKBOX:
+ case NS_THEME_MENURADIO:
+ case NS_THEME_MENUARROW:
+ case NS_THEME_MENUSEPARATOR:
+ case NS_THEME_MENUITEMTEXT:
+ case NS_THEME_WINDOW_TITLEBAR:
+ case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
+ case NS_THEME_WINDOW_FRAME_LEFT:
+ case NS_THEME_WINDOW_FRAME_RIGHT:
+ case NS_THEME_WINDOW_FRAME_BOTTOM:
+ case NS_THEME_WINDOW_BUTTON_CLOSE:
+ case NS_THEME_WINDOW_BUTTON_MINIMIZE:
+ case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
+ case NS_THEME_WINDOW_BUTTON_RESTORE:
+ case NS_THEME_WINDOW_BUTTON_BOX:
+ case NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED:
+ return true;
+ }
+ return false;
+}
+
+nsresult
+nsNativeThemeWin::ClassicGetWidgetBorder(nsDeviceContext* aContext,
+ nsIFrame* aFrame,
+ uint8_t aWidgetType,
+ nsIntMargin* aResult)
+{
+ switch (aWidgetType) {
+ case NS_THEME_GROUPBOX:
+ case NS_THEME_BUTTON:
+ (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right = 2;
+ break;
+ case NS_THEME_STATUSBAR:
+ (*aResult).bottom = (*aResult).left = (*aResult).right = 0;
+ (*aResult).top = 2;
+ break;
+ case NS_THEME_LISTBOX:
+ case NS_THEME_TREEVIEW:
+ case NS_THEME_MENULIST:
+ case NS_THEME_MENULIST_TEXTFIELD:
+ case NS_THEME_TAB:
+ case NS_THEME_NUMBER_INPUT:
+ case NS_THEME_TEXTFIELD:
+ case NS_THEME_TEXTFIELD_MULTILINE:
+ case NS_THEME_FOCUS_OUTLINE:
+ (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right = 2;
+ break;
+ case NS_THEME_STATUSBARPANEL:
+ case NS_THEME_RESIZERPANEL: {
+ (*aResult).top = 1;
+ (*aResult).left = 1;
+ (*aResult).bottom = 1;
+ (*aResult).right = aFrame->GetNextSibling() ? 3 : 1;
+ break;
+ }
+ case NS_THEME_TOOLTIP:
+ (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right = 1;
+ break;
+ case NS_THEME_PROGRESSBAR:
+ case NS_THEME_PROGRESSBAR_VERTICAL:
+ (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right = 1;
+ break;
+ case NS_THEME_MENUBAR:
+ (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right = 0;
+ break;
+ case NS_THEME_MENUPOPUP:
+ (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right = 3;
+ break;
+ default:
+ (*aResult).top = (*aResult).bottom = (*aResult).left = (*aResult).right = 0;
+ break;
+ }
+ return NS_OK;
+}
+
+bool
+nsNativeThemeWin::ClassicGetWidgetPadding(nsDeviceContext* aContext,
+ nsIFrame* aFrame,
+ uint8_t aWidgetType,
+ nsIntMargin* aResult)
+{
+ switch (aWidgetType) {
+ case NS_THEME_MENUITEM:
+ case NS_THEME_CHECKMENUITEM:
+ case NS_THEME_RADIOMENUITEM: {
+ int32_t part, state;
+ bool focused;
+
+ if (NS_FAILED(ClassicGetThemePartAndState(aFrame, aWidgetType, part, state, focused)))
+ return false;
+
+ if (part == 1) { // top-level menu
+ if (nsUXThemeData::sFlatMenus || !(state & DFCS_PUSHED)) {
+ (*aResult).top = (*aResult).bottom = (*aResult).left = (*aResult).right = 2;
+ }
+ else {
+ // make top-level menus look sunken when pushed in the Classic look
+ (*aResult).top = (*aResult).left = 3;
+ (*aResult).bottom = (*aResult).right = 1;
+ }
+ }
+ else {
+ (*aResult).top = 0;
+ (*aResult).bottom = (*aResult).left = (*aResult).right = 2;
+ }
+ return true;
+ }
+ case NS_THEME_PROGRESSBAR:
+ case NS_THEME_PROGRESSBAR_VERTICAL:
+ (*aResult).top = (*aResult).left = (*aResult).bottom = (*aResult).right = 1;
+ return true;
+ default:
+ return false;
+ }
+}
+
+nsresult
+nsNativeThemeWin::ClassicGetMinimumWidgetSize(nsIFrame* aFrame,
+ uint8_t aWidgetType,
+ LayoutDeviceIntSize* aResult,
+ bool* aIsOverridable)
+{
+ (*aResult).width = (*aResult).height = 0;
+ *aIsOverridable = true;
+ switch (aWidgetType) {
+ case NS_THEME_RADIO:
+ case NS_THEME_CHECKBOX:
+ (*aResult).width = (*aResult).height = 13;
+ break;
+ case NS_THEME_MENUCHECKBOX:
+ case NS_THEME_MENURADIO:
+ case NS_THEME_MENUARROW:
+ (*aResult).width = ::GetSystemMetrics(SM_CXMENUCHECK);
+ (*aResult).height = ::GetSystemMetrics(SM_CYMENUCHECK);
+ break;
+ case NS_THEME_SPINNER_UPBUTTON:
+ case NS_THEME_SPINNER_DOWNBUTTON:
+ (*aResult).width = ::GetSystemMetrics(SM_CXVSCROLL);
+ (*aResult).height = 8; // No good metrics available for this
+ *aIsOverridable = false;
+ break;
+ case NS_THEME_SCROLLBARBUTTON_UP:
+ case NS_THEME_SCROLLBARBUTTON_DOWN:
+ (*aResult).width = ::GetSystemMetrics(SM_CXVSCROLL);
+ (*aResult).height = ::GetSystemMetrics(SM_CYVSCROLL);
+ *aIsOverridable = false;
+ break;
+ case NS_THEME_SCROLLBARBUTTON_LEFT:
+ case NS_THEME_SCROLLBARBUTTON_RIGHT:
+ (*aResult).width = ::GetSystemMetrics(SM_CXHSCROLL);
+ (*aResult).height = ::GetSystemMetrics(SM_CYHSCROLL);
+ *aIsOverridable = false;
+ break;
+ case NS_THEME_SCROLLBAR_VERTICAL:
+ // XXX HACK We should be able to have a minimum height for the scrollbar
+ // track. However, this causes problems when uncollapsing a scrollbar
+ // inside a tree. See bug 201379 for details.
+
+ // (*aResult).height = ::GetSystemMetrics(SM_CYVTHUMB) << 1;
+ break;
+ case NS_THEME_SCROLLBAR_NON_DISAPPEARING:
+ {
+ aResult->SizeTo(::GetSystemMetrics(SM_CXHSCROLL),
+ ::GetSystemMetrics(SM_CYVSCROLL));
+ break;
+ }
+ case NS_THEME_RANGE_THUMB: {
+ if (IsRangeHorizontal(aFrame)) {
+ (*aResult).width = 12;
+ (*aResult).height = 20;
+ } else {
+ (*aResult).width = 20;
+ (*aResult).height = 12;
+ }
+ *aIsOverridable = false;
+ break;
+ }
+ case NS_THEME_SCALETHUMB_HORIZONTAL:
+ (*aResult).width = 12;
+ (*aResult).height = 20;
+ *aIsOverridable = false;
+ break;
+ case NS_THEME_SCALETHUMB_VERTICAL:
+ (*aResult).width = 20;
+ (*aResult).height = 12;
+ *aIsOverridable = false;
+ break;
+ case NS_THEME_MENULIST_BUTTON:
+ (*aResult).width = ::GetSystemMetrics(SM_CXVSCROLL);
+ break;
+ case NS_THEME_MENULIST:
+ case NS_THEME_BUTTON:
+ case NS_THEME_GROUPBOX:
+ case NS_THEME_LISTBOX:
+ case NS_THEME_TREEVIEW:
+ case NS_THEME_NUMBER_INPUT:
+ case NS_THEME_TEXTFIELD:
+ case NS_THEME_TEXTFIELD_MULTILINE:
+ case NS_THEME_MENULIST_TEXTFIELD:
+ case NS_THEME_STATUSBAR:
+ case NS_THEME_STATUSBARPANEL:
+ case NS_THEME_RESIZERPANEL:
+ case NS_THEME_PROGRESSCHUNK:
+ case NS_THEME_PROGRESSCHUNK_VERTICAL:
+ case NS_THEME_TOOLTIP:
+ case NS_THEME_PROGRESSBAR:
+ case NS_THEME_PROGRESSBAR_VERTICAL:
+ case NS_THEME_TAB:
+ case NS_THEME_TABPANEL:
+ case NS_THEME_TABPANELS:
+ // no minimum widget size
+ break;
+ case NS_THEME_RESIZER: {
+ NONCLIENTMETRICS nc;
+ nc.cbSize = sizeof(nc);
+ if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(nc), &nc, 0))
+ (*aResult).width = (*aResult).height = abs(nc.lfStatusFont.lfHeight) + 4;
+ else
+ (*aResult).width = (*aResult).height = 15;
+ *aIsOverridable = false;
+ break;
+ case NS_THEME_SCROLLBARTHUMB_VERTICAL:
+ (*aResult).width = ::GetSystemMetrics(SM_CXVSCROLL);
+ (*aResult).height = ::GetSystemMetrics(SM_CYVTHUMB);
+ // Without theming, divide the thumb size by two in order to look more
+ // native
+ if (!GetTheme(aWidgetType))
+ (*aResult).height >>= 1;
+ *aIsOverridable = false;
+ break;
+ case NS_THEME_SCROLLBARTHUMB_HORIZONTAL:
+ (*aResult).width = ::GetSystemMetrics(SM_CXHTHUMB);
+ (*aResult).height = ::GetSystemMetrics(SM_CYHSCROLL);
+ // Without theming, divide the thumb size by two in order to look more
+ // native
+ if (!GetTheme(aWidgetType))
+ (*aResult).width >>= 1;
+ *aIsOverridable = false;
+ break;
+ case NS_THEME_SCROLLBAR_HORIZONTAL:
+ (*aResult).width = ::GetSystemMetrics(SM_CXHTHUMB) << 1;
+ break;
+ }
+ case NS_THEME_MENUSEPARATOR:
+ {
+ aResult->width = 0;
+ aResult->height = 10;
+ break;
+ }
+
+ case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
+ case NS_THEME_WINDOW_TITLEBAR:
+ aResult->height = GetSystemMetrics(SM_CYCAPTION);
+ aResult->height += GetSystemMetrics(SM_CYFRAME);
+ aResult->width = 0;
+ break;
+ case NS_THEME_WINDOW_FRAME_LEFT:
+ case NS_THEME_WINDOW_FRAME_RIGHT:
+ aResult->width = GetSystemMetrics(SM_CXFRAME);
+ aResult->height = 0;
+ break;
+
+ case NS_THEME_WINDOW_FRAME_BOTTOM:
+ aResult->height = GetSystemMetrics(SM_CYFRAME);
+ aResult->width = 0;
+ break;
+
+ case NS_THEME_WINDOW_BUTTON_CLOSE:
+ case NS_THEME_WINDOW_BUTTON_MINIMIZE:
+ case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
+ case NS_THEME_WINDOW_BUTTON_RESTORE:
+ aResult->width = GetSystemMetrics(SM_CXSIZE);
+ aResult->height = GetSystemMetrics(SM_CYSIZE);
+ // XXX I have no idea why these caption metrics are always off,
+ // but they are.
+ aResult->width -= 2;
+ aResult->height -= 4;
+ if (aWidgetType == NS_THEME_WINDOW_BUTTON_MINIMIZE) {
+ AddPaddingRect(aResult, CAPTIONBUTTON_MINIMIZE);
+ }
+ else if (aWidgetType == NS_THEME_WINDOW_BUTTON_MAXIMIZE ||
+ aWidgetType == NS_THEME_WINDOW_BUTTON_RESTORE) {
+ AddPaddingRect(aResult, CAPTIONBUTTON_RESTORE);
+ }
+ else if (aWidgetType == NS_THEME_WINDOW_BUTTON_CLOSE) {
+ AddPaddingRect(aResult, CAPTIONBUTTON_CLOSE);
+ }
+ break;
+
+ default:
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+
+nsresult nsNativeThemeWin::ClassicGetThemePartAndState(nsIFrame* aFrame, uint8_t aWidgetType,
+ int32_t& aPart, int32_t& aState, bool& aFocused)
+{
+ aFocused = false;
+ switch (aWidgetType) {
+ case NS_THEME_BUTTON: {
+ EventStates contentState;
+
+ aPart = DFC_BUTTON;
+ aState = DFCS_BUTTONPUSH;
+ aFocused = false;
+
+ contentState = GetContentState(aFrame, aWidgetType);
+ if (IsDisabled(aFrame, contentState))
+ aState |= DFCS_INACTIVE;
+ else if (IsOpenButton(aFrame))
+ aState |= DFCS_PUSHED;
+ else if (IsCheckedButton(aFrame))
+ aState |= DFCS_CHECKED;
+ else {
+ if (contentState.HasAllStates(NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_HOVER)) {
+ aState |= DFCS_PUSHED;
+ const nsStyleUserInterface *uiData = aFrame->StyleUserInterface();
+ // The down state is flat if the button is focusable
+ if (uiData->mUserFocus == StyleUserFocus::Normal) {
+ if (!aFrame->GetContent()->IsHTMLElement())
+ aState |= DFCS_FLAT;
+
+ aFocused = true;
+ }
+ }
+ if (contentState.HasState(NS_EVENT_STATE_FOCUS) ||
+ (aState == DFCS_BUTTONPUSH && IsDefaultButton(aFrame))) {
+ aFocused = true;
+ }
+
+ }
+
+ return NS_OK;
+ }
+ case NS_THEME_CHECKBOX:
+ case NS_THEME_RADIO: {
+ EventStates contentState;
+ aFocused = false;
+
+ aPart = DFC_BUTTON;
+ aState = 0;
+ nsIContent* content = aFrame->GetContent();
+ bool isCheckbox = (aWidgetType == NS_THEME_CHECKBOX);
+ bool isChecked = GetCheckedOrSelected(aFrame, !isCheckbox);
+ bool isIndeterminate = isCheckbox && GetIndeterminate(aFrame);
+
+ if (isCheckbox) {
+ // indeterminate state takes precedence over checkedness.
+ if (isIndeterminate) {
+ aState = DFCS_BUTTON3STATE | DFCS_CHECKED;
+ } else {
+ aState = DFCS_BUTTONCHECK;
+ }
+ } else {
+ aState = DFCS_BUTTONRADIO;
+ }
+ if (isChecked) {
+ aState |= DFCS_CHECKED;
+ }
+
+ contentState = GetContentState(aFrame, aWidgetType);
+ if (!content->IsXULElement() &&
+ contentState.HasState(NS_EVENT_STATE_FOCUS)) {
+ aFocused = true;
+ }
+
+ if (IsDisabled(aFrame, contentState)) {
+ aState |= DFCS_INACTIVE;
+ } else if (contentState.HasAllStates(NS_EVENT_STATE_ACTIVE |
+ NS_EVENT_STATE_HOVER)) {
+ aState |= DFCS_PUSHED;
+ }
+
+ return NS_OK;
+ }
+ case NS_THEME_MENUITEM:
+ case NS_THEME_CHECKMENUITEM:
+ case NS_THEME_RADIOMENUITEM: {
+ bool isTopLevel = false;
+ bool isOpen = false;
+ nsMenuFrame *menuFrame = do_QueryFrame(aFrame);
+ EventStates eventState = GetContentState(aFrame, aWidgetType);
+
+ // We indicate top-level-ness using aPart. 0 is a normal menu item,
+ // 1 is a top-level menu item. The state of the item is composed of
+ // DFCS_* flags only.
+ aPart = 0;
+ aState = 0;
+
+ if (menuFrame) {
+ // If this is a real menu item, we should check if it is part of the
+ // main menu bar or not, and if it is a container, as these affect
+ // rendering.
+ isTopLevel = menuFrame->IsOnMenuBar();
+ isOpen = menuFrame->IsOpen();
+ }
+
+ if (IsDisabled(aFrame, eventState))
+ aState |= DFCS_INACTIVE;
+
+ if (isTopLevel) {
+ aPart = 1;
+ if (isOpen)
+ aState |= DFCS_PUSHED;
+ }
+
+ if (IsMenuActive(aFrame, aWidgetType))
+ aState |= DFCS_HOT;
+
+ return NS_OK;
+ }
+ case NS_THEME_MENUCHECKBOX:
+ case NS_THEME_MENURADIO:
+ case NS_THEME_MENUARROW: {
+ aState = 0;
+ EventStates eventState = GetContentState(aFrame, aWidgetType);
+
+ if (IsDisabled(aFrame, eventState))
+ aState |= DFCS_INACTIVE;
+ if (IsMenuActive(aFrame, aWidgetType))
+ aState |= DFCS_HOT;
+
+ if (aWidgetType == NS_THEME_MENUCHECKBOX || aWidgetType == NS_THEME_MENURADIO) {
+ if (IsCheckedButton(aFrame))
+ aState |= DFCS_CHECKED;
+ } else if (IsFrameRTL(aFrame)) {
+ aState |= DFCS_RTL;
+ }
+ return NS_OK;
+ }
+ case NS_THEME_LISTBOX:
+ case NS_THEME_TREEVIEW:
+ case NS_THEME_NUMBER_INPUT:
+ case NS_THEME_FOCUS_OUTLINE:
+ case NS_THEME_TEXTFIELD:
+ case NS_THEME_TEXTFIELD_MULTILINE:
+ case NS_THEME_MENULIST:
+ case NS_THEME_MENULIST_TEXTFIELD:
+ case NS_THEME_RANGE:
+ case NS_THEME_RANGE_THUMB:
+ case NS_THEME_SCROLLBARTHUMB_VERTICAL:
+ case NS_THEME_SCROLLBARTHUMB_HORIZONTAL:
+ case NS_THEME_SCROLLBAR_VERTICAL:
+ case NS_THEME_SCROLLBAR_HORIZONTAL:
+ case NS_THEME_SCALE_HORIZONTAL:
+ case NS_THEME_SCALE_VERTICAL:
+ case NS_THEME_SCALETHUMB_HORIZONTAL:
+ case NS_THEME_SCALETHUMB_VERTICAL:
+ case NS_THEME_STATUSBAR:
+ case NS_THEME_STATUSBARPANEL:
+ case NS_THEME_RESIZERPANEL:
+ case NS_THEME_PROGRESSCHUNK:
+ case NS_THEME_PROGRESSCHUNK_VERTICAL:
+ case NS_THEME_TOOLTIP:
+ case NS_THEME_PROGRESSBAR:
+ case NS_THEME_PROGRESSBAR_VERTICAL:
+ case NS_THEME_TAB:
+ case NS_THEME_TABPANEL:
+ case NS_THEME_TABPANELS:
+ case NS_THEME_MENUBAR:
+ case NS_THEME_MENUPOPUP:
+ case NS_THEME_GROUPBOX:
+ // these don't use DrawFrameControl
+ return NS_OK;
+ case NS_THEME_MENULIST_BUTTON: {
+
+ aPart = DFC_SCROLL;
+ aState = DFCS_SCROLLCOMBOBOX;
+
+ nsIFrame* parentFrame = aFrame->GetParent();
+ bool isHTML = IsHTMLContent(aFrame);
+ bool isMenulist = !isHTML && parentFrame->GetType() == nsGkAtoms::menuFrame;
+ bool isOpen = false;
+
+ // HTML select and XUL menulist dropdown buttons get state from the parent.
+ if (isHTML || isMenulist)
+ aFrame = parentFrame;
+
+ EventStates eventState = GetContentState(aFrame, aWidgetType);
+
+ if (IsDisabled(aFrame, eventState)) {
+ aState |= DFCS_INACTIVE;
+ return NS_OK;
+ }
+
+ if (isHTML) {
+ nsIComboboxControlFrame* ccf = do_QueryFrame(aFrame);
+ isOpen = (ccf && ccf->IsDroppedDownOrHasParentPopup());
+ }
+ else
+ isOpen = IsOpenButton(aFrame);
+
+ // XXX Button should look active until the mouse is released, but
+ // without making it look active when the popup is clicked.
+ if (isOpen && (isHTML || isMenulist))
+ return NS_OK;
+
+ // Dropdown button active state doesn't need :hover.
+ if (eventState.HasState(NS_EVENT_STATE_ACTIVE))
+ aState |= DFCS_PUSHED | DFCS_FLAT;
+
+ return NS_OK;
+ }
+ case NS_THEME_SCROLLBARBUTTON_UP:
+ case NS_THEME_SCROLLBARBUTTON_DOWN:
+ case NS_THEME_SCROLLBARBUTTON_LEFT:
+ case NS_THEME_SCROLLBARBUTTON_RIGHT: {
+ EventStates contentState = GetContentState(aFrame, aWidgetType);
+
+ aPart = DFC_SCROLL;
+ switch (aWidgetType) {
+ case NS_THEME_SCROLLBARBUTTON_UP:
+ aState = DFCS_SCROLLUP;
+ break;
+ case NS_THEME_SCROLLBARBUTTON_DOWN:
+ aState = DFCS_SCROLLDOWN;
+ break;
+ case NS_THEME_SCROLLBARBUTTON_LEFT:
+ aState = DFCS_SCROLLLEFT;
+ break;
+ case NS_THEME_SCROLLBARBUTTON_RIGHT:
+ aState = DFCS_SCROLLRIGHT;
+ break;
+ }
+
+ if (IsDisabled(aFrame, contentState))
+ aState |= DFCS_INACTIVE;
+ else {
+ if (contentState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE))
+ aState |= DFCS_PUSHED | DFCS_FLAT;
+ }
+
+ return NS_OK;
+ }
+ case NS_THEME_SPINNER_UPBUTTON:
+ case NS_THEME_SPINNER_DOWNBUTTON: {
+ EventStates contentState = GetContentState(aFrame, aWidgetType);
+
+ aPart = DFC_SCROLL;
+ switch (aWidgetType) {
+ case NS_THEME_SPINNER_UPBUTTON:
+ aState = DFCS_SCROLLUP;
+ break;
+ case NS_THEME_SPINNER_DOWNBUTTON:
+ aState = DFCS_SCROLLDOWN;
+ break;
+ }
+
+ if (IsDisabled(aFrame, contentState))
+ aState |= DFCS_INACTIVE;
+ else {
+ if (contentState.HasAllStates(NS_EVENT_STATE_HOVER | NS_EVENT_STATE_ACTIVE))
+ aState |= DFCS_PUSHED;
+ }
+
+ return NS_OK;
+ }
+ case NS_THEME_RESIZER:
+ aPart = DFC_SCROLL;
+ aState = (IsFrameRTL(aFrame)) ?
+ DFCS_SCROLLSIZEGRIPRIGHT : DFCS_SCROLLSIZEGRIP;
+ return NS_OK;
+ case NS_THEME_MENUSEPARATOR:
+ aPart = 0;
+ aState = 0;
+ return NS_OK;
+ case NS_THEME_WINDOW_TITLEBAR:
+ aPart = mozilla::widget::themeconst::WP_CAPTION;
+ aState = GetTopLevelWindowActiveState(aFrame);
+ return NS_OK;
+ case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
+ aPart = mozilla::widget::themeconst::WP_MAXCAPTION;
+ aState = GetTopLevelWindowActiveState(aFrame);
+ return NS_OK;
+ case NS_THEME_WINDOW_FRAME_LEFT:
+ aPart = mozilla::widget::themeconst::WP_FRAMELEFT;
+ aState = GetTopLevelWindowActiveState(aFrame);
+ return NS_OK;
+ case NS_THEME_WINDOW_FRAME_RIGHT:
+ aPart = mozilla::widget::themeconst::WP_FRAMERIGHT;
+ aState = GetTopLevelWindowActiveState(aFrame);
+ return NS_OK;
+ case NS_THEME_WINDOW_FRAME_BOTTOM:
+ aPart = mozilla::widget::themeconst::WP_FRAMEBOTTOM;
+ aState = GetTopLevelWindowActiveState(aFrame);
+ return NS_OK;
+ case NS_THEME_WINDOW_BUTTON_CLOSE:
+ aPart = DFC_CAPTION;
+ aState = DFCS_CAPTIONCLOSE |
+ GetClassicWindowFrameButtonState(GetContentState(aFrame,
+ aWidgetType));
+ return NS_OK;
+ case NS_THEME_WINDOW_BUTTON_MINIMIZE:
+ aPart = DFC_CAPTION;
+ aState = DFCS_CAPTIONMIN |
+ GetClassicWindowFrameButtonState(GetContentState(aFrame,
+ aWidgetType));
+ return NS_OK;
+ case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
+ aPart = DFC_CAPTION;
+ aState = DFCS_CAPTIONMAX |
+ GetClassicWindowFrameButtonState(GetContentState(aFrame,
+ aWidgetType));
+ return NS_OK;
+ case NS_THEME_WINDOW_BUTTON_RESTORE:
+ aPart = DFC_CAPTION;
+ aState = DFCS_CAPTIONRESTORE |
+ GetClassicWindowFrameButtonState(GetContentState(aFrame,
+ aWidgetType));
+ return NS_OK;
+ }
+ 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);
+}
+
+static void DrawMenuImage(HDC hdc, const RECT& rc, int32_t aComponent, uint32_t aColor)
+{
+ // This procedure creates a memory bitmap to contain the check mark, draws
+ // it into the bitmap (it is a mask image), then composes it onto the menu
+ // item in appropriate colors.
+ HDC hMemoryDC = ::CreateCompatibleDC(hdc);
+ if (hMemoryDC) {
+ // XXXjgr We should ideally be caching these, but we wont be notified when
+ // they change currently, so we can't do so easily. Same for the bitmap.
+ int checkW = ::GetSystemMetrics(SM_CXMENUCHECK);
+ int checkH = ::GetSystemMetrics(SM_CYMENUCHECK);
+
+ HBITMAP hMonoBitmap = ::CreateBitmap(checkW, checkH, 1, 1, nullptr);
+ if (hMonoBitmap) {
+
+ HBITMAP hPrevBitmap = (HBITMAP) ::SelectObject(hMemoryDC, hMonoBitmap);
+ if (hPrevBitmap) {
+
+ // XXXjgr This will go pear-shaped if the image is bigger than the
+ // provided rect. What should we do?
+ RECT imgRect = { 0, 0, checkW, checkH };
+ POINT imgPos = {
+ rc.left + (rc.right - rc.left - checkW) / 2,
+ rc.top + (rc.bottom - rc.top - checkH) / 2
+ };
+
+ // XXXzeniko Windows renders these 1px lower than you'd expect
+ if (aComponent == DFCS_MENUCHECK || aComponent == DFCS_MENUBULLET)
+ imgPos.y++;
+
+ ::DrawFrameControl(hMemoryDC, &imgRect, DFC_MENU, aComponent);
+ COLORREF oldTextCol = ::SetTextColor(hdc, 0x00000000);
+ COLORREF oldBackCol = ::SetBkColor(hdc, 0x00FFFFFF);
+ ::BitBlt(hdc, imgPos.x, imgPos.y, checkW, checkH, hMemoryDC, 0, 0, SRCAND);
+ ::SetTextColor(hdc, ::GetSysColor(aColor));
+ ::SetBkColor(hdc, 0x00000000);
+ ::BitBlt(hdc, imgPos.x, imgPos.y, checkW, checkH, hMemoryDC, 0, 0, SRCPAINT);
+ ::SetTextColor(hdc, oldTextCol);
+ ::SetBkColor(hdc, oldBackCol);
+ ::SelectObject(hMemoryDC, hPrevBitmap);
+ }
+ ::DeleteObject(hMonoBitmap);
+ }
+ ::DeleteDC(hMemoryDC);
+ }
+}
+
+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(nsRenderingContext* aContext,
+ nsIFrame* aFrame,
+ uint8_t aWidgetType,
+ const nsRect& aRect,
+ const nsRect& aDirtyRect)
+{
+ int32_t part, state;
+ bool focused;
+ nsresult rv;
+ rv = ClassicGetThemePartAndState(aFrame, aWidgetType, 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.ScaleInverse(p2a);
+ dr.ScaleInverse(p2a);
+
+ RefPtr<gfxContext> ctx = aContext->ThebesContext();
+
+ gfxWindowsNativeDrawing nativeDrawing(ctx, dr, GetWidgetNativeDrawingFlags(aWidgetType));
+
+RENDER_AGAIN:
+
+ HDC hdc = nativeDrawing.BeginNativeDrawing();
+ if (!hdc)
+ return NS_ERROR_FAILURE;
+
+ nativeDrawing.TransformToNativeRect(tr, widgetRect);
+
+ rv = NS_OK;
+ switch (aWidgetType) {
+ // Draw button
+ case NS_THEME_BUTTON: {
+ if (focused) {
+ // draw dark button focus border first
+ HBRUSH brush;
+ brush = ::GetSysColorBrush(COLOR_3DDKSHADOW);
+ if (brush)
+ ::FrameRect(hdc, &widgetRect, brush);
+ InflateRect(&widgetRect, -1, -1);
+ }
+ // fall-through...
+ }
+ // Draw controls supported by DrawFrameControl
+ case NS_THEME_CHECKBOX:
+ case NS_THEME_RADIO:
+ case NS_THEME_SCROLLBARBUTTON_UP:
+ case NS_THEME_SCROLLBARBUTTON_DOWN:
+ case NS_THEME_SCROLLBARBUTTON_LEFT:
+ case NS_THEME_SCROLLBARBUTTON_RIGHT:
+ case NS_THEME_SPINNER_UPBUTTON:
+ case NS_THEME_SPINNER_DOWNBUTTON:
+ case NS_THEME_MENULIST_BUTTON:
+ case NS_THEME_RESIZER: {
+ int32_t oldTA;
+ // setup DC to make DrawFrameControl draw correctly
+ 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 NS_THEME_NUMBER_INPUT:
+ case NS_THEME_TEXTFIELD:
+ case NS_THEME_TEXTFIELD_MULTILINE:
+ case NS_THEME_LISTBOX:
+ case NS_THEME_MENULIST:
+ case NS_THEME_MENULIST_TEXTFIELD: {
+ // Draw inset edge
+ ::DrawEdge(hdc, &widgetRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
+ EventStates eventState = GetContentState(aFrame, aWidgetType);
+
+ // Fill in background
+ if (IsDisabled(aFrame, eventState) ||
+ (aFrame->GetContent()->IsXULElement() &&
+ IsReadOnly(aFrame)))
+ ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_BTNFACE+1));
+ else
+ ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_WINDOW+1));
+
+ break;
+ }
+ case NS_THEME_TREEVIEW: {
+ // Draw inset edge
+ ::DrawEdge(hdc, &widgetRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST);
+
+ // Fill in window color background
+ ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_WINDOW+1));
+
+ break;
+ }
+ // Draw ToolTip background
+ case NS_THEME_TOOLTIP:
+ ::FrameRect(hdc, &widgetRect, ::GetSysColorBrush(COLOR_WINDOWFRAME));
+ InflateRect(&widgetRect, -1, -1);
+ ::FillRect(hdc, &widgetRect, ::GetSysColorBrush(COLOR_INFOBK));
+
+ break;
+ case NS_THEME_GROUPBOX:
+ ::DrawEdge(hdc, &widgetRect, EDGE_ETCHED, BF_RECT | BF_ADJUST);
+ ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_BTNFACE+1));
+ break;
+ // Draw 3D face background controls
+ case NS_THEME_PROGRESSBAR:
+ case NS_THEME_PROGRESSBAR_VERTICAL:
+ // Draw 3D border
+ ::DrawEdge(hdc, &widgetRect, BDR_SUNKENOUTER, BF_RECT | BF_MIDDLE);
+ InflateRect(&widgetRect, -1, -1);
+ // fall through
+ case NS_THEME_TABPANEL:
+ case NS_THEME_STATUSBAR:
+ case NS_THEME_RESIZERPANEL: {
+ ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_BTNFACE+1));
+
+ break;
+ }
+ // Draw 3D inset statusbar panel
+ case NS_THEME_STATUSBARPANEL: {
+ if (aFrame->GetNextSibling())
+ widgetRect.right -= 2; // space between sibling status panels
+
+ ::DrawEdge(hdc, &widgetRect, BDR_SUNKENOUTER, BF_RECT | BF_MIDDLE);
+
+ break;
+ }
+ // Draw scrollbar thumb
+ case NS_THEME_SCROLLBARTHUMB_VERTICAL:
+ case NS_THEME_SCROLLBARTHUMB_HORIZONTAL:
+ ::DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_RECT | BF_MIDDLE);
+
+ break;
+ case NS_THEME_RANGE_THUMB:
+ case NS_THEME_SCALETHUMB_VERTICAL:
+ case NS_THEME_SCALETHUMB_HORIZONTAL: {
+ EventStates eventState = GetContentState(aFrame, aWidgetType);
+
+ ::DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_RECT | BF_SOFT | BF_MIDDLE | BF_ADJUST);
+ if (IsDisabled(aFrame, eventState)) {
+ DrawCheckedRect(hdc, widgetRect, COLOR_3DFACE, COLOR_3DHILIGHT,
+ (HBRUSH) COLOR_3DHILIGHT);
+ }
+
+ break;
+ }
+ // Draw scrollbar track background
+ case NS_THEME_SCROLLBAR_VERTICAL:
+ case NS_THEME_SCROLLBAR_HORIZONTAL: {
+
+ // Windows fills in the scrollbar track differently
+ // depending on whether these are equal
+ DWORD color3D, colorScrollbar, colorWindow;
+
+ color3D = ::GetSysColor(COLOR_3DFACE);
+ colorWindow = ::GetSysColor(COLOR_WINDOW);
+ colorScrollbar = ::GetSysColor(COLOR_SCROLLBAR);
+
+ if ((color3D != colorScrollbar) && (colorWindow != colorScrollbar))
+ // Use solid brush
+ ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_SCROLLBAR+1));
+ else
+ {
+ DrawCheckedRect(hdc, widgetRect, COLOR_3DHILIGHT, COLOR_3DFACE,
+ (HBRUSH) COLOR_SCROLLBAR+1);
+ }
+ // XXX should invert the part of the track being clicked here
+ // but the track is never :active
+
+ break;
+ }
+ // Draw scale track background
+ case NS_THEME_RANGE:
+ case NS_THEME_SCALE_VERTICAL:
+ case NS_THEME_SCALE_HORIZONTAL: {
+ 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 (aWidgetType == NS_THEME_SCALE_HORIZONTAL ||
+ (aWidgetType == NS_THEME_RANGE && 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 NS_THEME_PROGRESSCHUNK_VERTICAL:
+ ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_HIGHLIGHT+1));
+ break;
+
+ case NS_THEME_PROGRESSCHUNK: {
+ nsIFrame* stateFrame = aFrame->GetParent();
+ EventStates eventStates = GetContentState(stateFrame, aWidgetType);
+
+ bool indeterminate = IsIndeterminateProgress(stateFrame, eventStates);
+ bool vertical = IsVerticalProgress(stateFrame) ||
+ aWidgetType == NS_THEME_PROGRESSCHUNK_VERTICAL;
+
+ 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 NS_THEME_TAB: {
+ DrawTab(hdc, widgetRect,
+ IsBottomTab(aFrame) ? BF_BOTTOM : BF_TOP,
+ IsSelectedTab(aFrame),
+ !IsRightToSelectedTab(aFrame),
+ !IsLeftToSelectedTab(aFrame));
+
+ break;
+ }
+ case NS_THEME_TABPANELS:
+ ::DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_SOFT | BF_MIDDLE |
+ BF_LEFT | BF_RIGHT | BF_BOTTOM);
+
+ break;
+ case NS_THEME_MENUBAR:
+ break;
+ case NS_THEME_MENUPOPUP:
+ NS_ASSERTION(nsUXThemeData::sFlatMenus, "Classic menus are styled entirely through CSS");
+ ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_MENU+1));
+ ::FrameRect(hdc, &widgetRect, ::GetSysColorBrush(COLOR_BTNSHADOW));
+ break;
+ case NS_THEME_MENUITEM:
+ case NS_THEME_CHECKMENUITEM:
+ case NS_THEME_RADIOMENUITEM:
+ // part == 0 for normal items
+ // part == 1 for top-level menu items
+ if (nsUXThemeData::sFlatMenus) {
+ // Not disabled and hot/pushed.
+ if ((state & (DFCS_HOT | DFCS_PUSHED)) != 0) {
+ ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_MENUHILIGHT+1));
+ ::FrameRect(hdc, &widgetRect, ::GetSysColorBrush(COLOR_HIGHLIGHT));
+ }
+ } else {
+ if (part == 1) {
+ if ((state & DFCS_INACTIVE) == 0) {
+ if ((state & DFCS_PUSHED) != 0) {
+ ::DrawEdge(hdc, &widgetRect, BDR_SUNKENOUTER, BF_RECT);
+ } else if ((state & DFCS_HOT) != 0) {
+ ::DrawEdge(hdc, &widgetRect, BDR_RAISEDINNER, BF_RECT);
+ }
+ }
+ } else {
+ if ((state & (DFCS_HOT | DFCS_PUSHED)) != 0) {
+ ::FillRect(hdc, &widgetRect, (HBRUSH) (COLOR_HIGHLIGHT+1));
+ }
+ }
+ }
+ break;
+ case NS_THEME_MENUCHECKBOX:
+ case NS_THEME_MENURADIO:
+ if (!(state & DFCS_CHECKED))
+ break; // nothin' to do
+ case NS_THEME_MENUARROW: {
+ uint32_t color = COLOR_MENUTEXT;
+ if ((state & DFCS_INACTIVE))
+ color = COLOR_GRAYTEXT;
+ else if ((state & DFCS_HOT))
+ color = COLOR_HIGHLIGHTTEXT;
+
+ if (aWidgetType == NS_THEME_MENUCHECKBOX)
+ DrawMenuImage(hdc, widgetRect, DFCS_MENUCHECK, color);
+ else if (aWidgetType == NS_THEME_MENURADIO)
+ DrawMenuImage(hdc, widgetRect, DFCS_MENUBULLET, color);
+ else if (aWidgetType == NS_THEME_MENUARROW)
+ DrawMenuImage(hdc, widgetRect,
+ (state & DFCS_RTL) ? DFCS_MENUARROWRIGHT : DFCS_MENUARROW,
+ color);
+ break;
+ }
+ case NS_THEME_MENUSEPARATOR: {
+ // separators are offset by a bit (see menu.css)
+ widgetRect.left++;
+ widgetRect.right--;
+
+ // This magic number is brought to you by the value in menu.css
+ widgetRect.top += 4;
+ // Our rectangles are 1 pixel high (see border size in menu.css)
+ widgetRect.bottom = widgetRect.top+1;
+ ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_3DSHADOW+1));
+ widgetRect.top++;
+ widgetRect.bottom++;
+ ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_3DHILIGHT+1));
+ break;
+ }
+
+ case NS_THEME_WINDOW_TITLEBAR:
+ case NS_THEME_WINDOW_TITLEBAR_MAXIMIZED:
+ {
+ RECT rect = widgetRect;
+ int32_t offset = GetSystemMetrics(SM_CXFRAME);
+
+ // first fill the area to the color of the window background
+ FillRect(hdc, &rect, (HBRUSH)(COLOR_3DFACE+1));
+
+ // inset the caption area so it doesn't overflow.
+ rect.top += offset;
+ // if enabled, draw a gradient titlebar background, otherwise
+ // fill with a solid color.
+ BOOL bFlag = TRUE;
+ SystemParametersInfo(SPI_GETGRADIENTCAPTIONS, 0, &bFlag, 0);
+ if (!bFlag) {
+ if (state == mozilla::widget::themeconst::FS_ACTIVE)
+ FillRect(hdc, &rect, (HBRUSH)(COLOR_ACTIVECAPTION+1));
+ else
+ FillRect(hdc, &rect, (HBRUSH)(COLOR_INACTIVECAPTION+1));
+ } else {
+ DWORD startColor, endColor;
+ if (state == mozilla::widget::themeconst::FS_ACTIVE) {
+ startColor = GetSysColor(COLOR_ACTIVECAPTION);
+ endColor = GetSysColor(COLOR_GRADIENTACTIVECAPTION);
+ } else {
+ startColor = GetSysColor(COLOR_INACTIVECAPTION);
+ endColor = GetSysColor(COLOR_GRADIENTINACTIVECAPTION);
+ }
+
+ TRIVERTEX vertex[2];
+ vertex[0].x = rect.left;
+ vertex[0].y = rect.top;
+ vertex[0].Red = GetRValue(startColor) << 8;
+ vertex[0].Green = GetGValue(startColor) << 8;
+ vertex[0].Blue = GetBValue(startColor) << 8;
+ vertex[0].Alpha = 0;
+
+ vertex[1].x = rect.right;
+ vertex[1].y = rect.bottom;
+ vertex[1].Red = GetRValue(endColor) << 8;
+ vertex[1].Green = GetGValue(endColor) << 8;
+ vertex[1].Blue = GetBValue(endColor) << 8;
+ vertex[1].Alpha = 0;
+
+ GRADIENT_RECT gRect;
+ gRect.UpperLeft = 0;
+ gRect.LowerRight = 1;
+ // available on win2k & up
+ GradientFill(hdc, vertex, 2, &gRect, 1, GRADIENT_FILL_RECT_H);
+ }
+
+ if (aWidgetType == NS_THEME_WINDOW_TITLEBAR) {
+ // frame things up with a top raised border.
+ DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_TOP);
+ }
+ break;
+ }
+
+ case NS_THEME_WINDOW_FRAME_LEFT:
+ DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_LEFT);
+ break;
+
+ case NS_THEME_WINDOW_FRAME_RIGHT:
+ DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_RIGHT);
+ break;
+
+ case NS_THEME_WINDOW_FRAME_BOTTOM:
+ DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_BOTTOM);
+ break;
+
+ case NS_THEME_WINDOW_BUTTON_CLOSE:
+ case NS_THEME_WINDOW_BUTTON_MINIMIZE:
+ case NS_THEME_WINDOW_BUTTON_MAXIMIZE:
+ case NS_THEME_WINDOW_BUTTON_RESTORE:
+ {
+ if (aWidgetType == NS_THEME_WINDOW_BUTTON_MINIMIZE) {
+ OffsetBackgroundRect(widgetRect, CAPTIONBUTTON_MINIMIZE);
+ }
+ else if (aWidgetType == NS_THEME_WINDOW_BUTTON_MAXIMIZE ||
+ aWidgetType == NS_THEME_WINDOW_BUTTON_RESTORE) {
+ OffsetBackgroundRect(widgetRect, CAPTIONBUTTON_RESTORE);
+ }
+ else if (aWidgetType == NS_THEME_WINDOW_BUTTON_CLOSE) {
+ OffsetBackgroundRect(widgetRect, CAPTIONBUTTON_CLOSE);
+ }
+ int32_t oldTA = SetTextAlign(hdc, TA_TOP | TA_LEFT | TA_NOUPDATECP);
+ DrawFrameControl(hdc, &widgetRect, part, state);
+ SetTextAlign(hdc, oldTA);
+ 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(uint8_t aWidgetType)
+{
+ switch (aWidgetType) {
+ case NS_THEME_BUTTON:
+ case NS_THEME_NUMBER_INPUT:
+ case NS_THEME_FOCUS_OUTLINE:
+ case NS_THEME_TEXTFIELD:
+ case NS_THEME_TEXTFIELD_MULTILINE:
+
+ case NS_THEME_MENULIST:
+ case NS_THEME_MENULIST_TEXTFIELD:
+ return
+ gfxWindowsNativeDrawing::CANNOT_DRAW_TO_COLOR_ALPHA |
+ gfxWindowsNativeDrawing::CAN_AXIS_ALIGNED_SCALE |
+ gfxWindowsNativeDrawing::CANNOT_COMPLEX_TRANSFORM;
+
+ // need to check these others
+ case NS_THEME_RANGE:
+ case NS_THEME_RANGE_THUMB:
+ case NS_THEME_SCROLLBARBUTTON_UP:
+ case NS_THEME_SCROLLBARBUTTON_DOWN:
+ case NS_THEME_SCROLLBARBUTTON_LEFT:
+ case NS_THEME_SCROLLBARBUTTON_RIGHT:
+ case NS_THEME_SCROLLBARTHUMB_VERTICAL:
+ case NS_THEME_SCROLLBARTHUMB_HORIZONTAL:
+ case NS_THEME_SCROLLBAR_VERTICAL:
+ case NS_THEME_SCROLLBAR_HORIZONTAL:
+ case NS_THEME_SCALE_HORIZONTAL:
+ case NS_THEME_SCALE_VERTICAL:
+ case NS_THEME_SCALETHUMB_HORIZONTAL:
+ case NS_THEME_SCALETHUMB_VERTICAL:
+ case NS_THEME_SPINNER_UPBUTTON:
+ case NS_THEME_SPINNER_DOWNBUTTON:
+ case NS_THEME_LISTBOX:
+ case NS_THEME_TREEVIEW:
+ case NS_THEME_TOOLTIP:
+ case NS_THEME_STATUSBAR:
+ case NS_THEME_STATUSBARPANEL:
+ case NS_THEME_RESIZERPANEL:
+ case NS_THEME_RESIZER:
+ case NS_THEME_PROGRESSBAR:
+ case NS_THEME_PROGRESSBAR_VERTICAL:
+ case NS_THEME_PROGRESSCHUNK:
+ case NS_THEME_PROGRESSCHUNK_VERTICAL:
+ case NS_THEME_TAB:
+ case NS_THEME_TABPANEL:
+ case NS_THEME_TABPANELS:
+ case NS_THEME_MENUBAR:
+ case NS_THEME_MENUPOPUP:
+ case NS_THEME_MENUITEM:
+ break;
+
+ // the dropdown button /almost/ renders correctly with scaling,
+ // except that the graphic in the dropdown button (the downward arrow)
+ // doesn't get scaled up.
+ case NS_THEME_MENULIST_BUTTON:
+ // these are definitely no; they're all graphics that don't get scaled up
+ case NS_THEME_CHECKBOX:
+ case NS_THEME_RADIO:
+ case NS_THEME_GROUPBOX:
+ case NS_THEME_CHECKMENUITEM:
+ case NS_THEME_RADIOMENUITEM:
+ case NS_THEME_MENUCHECKBOX:
+ case NS_THEME_MENURADIO:
+ case NS_THEME_MENUARROW:
+ return
+ gfxWindowsNativeDrawing::CANNOT_DRAW_TO_COLOR_ALPHA |
+ gfxWindowsNativeDrawing::CANNOT_AXIS_ALIGNED_SCALE |
+ gfxWindowsNativeDrawing::CANNOT_COMPLEX_TRANSFORM;
+ }
+
+ return
+ gfxWindowsNativeDrawing::CANNOT_DRAW_TO_COLOR_ALPHA |
+ gfxWindowsNativeDrawing::CANNOT_AXIS_ALIGNED_SCALE |
+ gfxWindowsNativeDrawing::CANNOT_COMPLEX_TRANSFORM;
+}
+
+///////////////////////////////////////////
+// Creation Routine
+///////////////////////////////////////////
+
+// from nsWindow.cpp
+extern bool gDisableNativeTheme;
+
+nsresult NS_NewNativeTheme(nsISupports *aOuter, REFNSIID aIID, void **aResult)
+{
+ if (gDisableNativeTheme)
+ return NS_ERROR_NO_INTERFACE;
+
+ if (aOuter)
+ return NS_ERROR_NO_AGGREGATION;
+
+ nsNativeThemeWin* theme = new nsNativeThemeWin();
+ if (!theme)
+ return NS_ERROR_OUT_OF_MEMORY;
+ return theme->QueryInterface(aIID, aResult);
+}