widget/cocoa/nsNativeThemeCocoa.mm

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "nsNativeThemeCocoa.h"
     7 #include "nsObjCExceptions.h"
     8 #include "nsNumberControlFrame.h"
     9 #include "nsRangeFrame.h"
    10 #include "nsRenderingContext.h"
    11 #include "nsRect.h"
    12 #include "nsSize.h"
    13 #include "nsThemeConstants.h"
    14 #include "nsIPresShell.h"
    15 #include "nsPresContext.h"
    16 #include "nsIContent.h"
    17 #include "nsIDocument.h"
    18 #include "nsIFrame.h"
    19 #include "nsIAtom.h"
    20 #include "nsNameSpaceManager.h"
    21 #include "nsPresContext.h"
    22 #include "nsGkAtoms.h"
    23 #include "nsCocoaFeatures.h"
    24 #include "nsCocoaWindow.h"
    25 #include "nsNativeThemeColors.h"
    26 #include "nsIScrollableFrame.h"
    27 #include "mozilla/EventStates.h"
    28 #include "mozilla/dom/Element.h"
    29 #include "mozilla/dom/HTMLMeterElement.h"
    30 #include "nsLookAndFeel.h"
    32 #include "gfxContext.h"
    33 #include "gfxQuartzSurface.h"
    34 #include "gfxQuartzNativeDrawing.h"
    35 #include <algorithm>
    37 using namespace mozilla;
    38 using namespace mozilla::gfx;
    39 using mozilla::dom::HTMLMeterElement;
    41 #define DRAW_IN_FRAME_DEBUG 0
    42 #define SCROLLBARS_VISUAL_DEBUG 0
    44 // private Quartz routines needed here
    45 extern "C" {
    46   CG_EXTERN void CGContextSetCTM(CGContextRef, CGAffineTransform);
    47 }
    49 // Workaround for NSCell control tint drawing
    50 // Without this workaround, NSCells are always drawn with the clear control tint
    51 // as long as they're not attached to an NSControl which is a subview of an active window.
    52 // XXXmstange Why doesn't Webkit need this?
    53 @implementation NSCell (ControlTintWorkaround)
    54 - (int)_realControlTint { return [self controlTint]; }
    55 @end
    57 // The purpose of this class is to provide objects that can be used when drawing
    58 // NSCells using drawWithFrame:inView: without causing any harm. The only
    59 // messages that will be sent to such an object are "isFlipped" and
    60 // "currentEditor": isFlipped needs to return YES in order to avoid drawing bugs
    61 // on 10.4 (see bug 465069); currentEditor (which isn't even a method of
    62 // NSView) will be called when drawing search fields, and we only provide it in
    63 // order to prevent "unrecognized selector" exceptions.
    64 // There's no need to pass the actual NSView that we're drawing into to
    65 // drawWithFrame:inView:. What's more, doing so even causes unnecessary
    66 // invalidations as soon as we draw a focusring!
    67 @interface CellDrawView : NSView
    69 @end;
    71 @implementation CellDrawView
    73 - (BOOL)isFlipped
    74 {
    75   return YES;
    76 }
    78 - (NSText*)currentEditor
    79 {
    80   return nil;
    81 }
    83 @end
    85 static void
    86 DrawCellIncludingFocusRing(NSCell* aCell, NSRect aWithFrame, NSView* aInView)
    87 {
    88   [aCell drawWithFrame:aWithFrame inView:aInView];
    90 #if defined(MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
    91   // When building with the 10.8 SDK or higher, focus rings don't draw as part
    92   // of -[NSCell drawWithFrame:inView:] and must be drawn by a separate call
    93   // to -[NSCell drawFocusRingMaskWithFrame:inView:]; .
    94   // See the NSButtonCell section under
    95   // https://developer.apple.com/library/mac/releasenotes/AppKit/RN-AppKitOlderNotes/#X10_8Notes
    96   if ([aCell showsFirstResponder]) {
    97     CGContextRef cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
    98     CGContextSaveGState(cgContext);
   100     // It's important to set the focus ring style before we enter the
   101     // transparency layer so that the transparency layer only contains
   102     // the normal button mask without the focus ring, and the conversion
   103     // to the focus ring shape happens only when the transparency layer is
   104     // ended.
   105     NSSetFocusRingStyle(NSFocusRingOnly);
   107     // We need to draw the whole button into a transparency layer because
   108     // many button types are composed of multiple parts, and if these parts
   109     // were drawn while the focus ring style was active, each individual part
   110     // would produce a focus ring for itself. But we only want one focus ring
   111     // for the whole button. The transparency layer is a way to merge the
   112     // individual button parts together before the focus ring shape is
   113     // calculated.
   114     CGContextBeginTransparencyLayerWithRect(cgContext, NSRectToCGRect(aWithFrame), 0);
   115     [aCell drawFocusRingMaskWithFrame:aWithFrame inView:aInView];
   116     CGContextEndTransparencyLayer(cgContext);
   118     CGContextRestoreGState(cgContext);
   119   }
   120 #endif
   121 }
   123 /**
   124  * NSProgressBarCell is used to draw progress bars of any size.
   125  */
   126 @interface NSProgressBarCell : NSCell
   127 {
   128     /*All instance variables are private*/
   129     double mValue;
   130     double mMax;
   131     bool   mIsIndeterminate;
   132     bool   mIsHorizontal;
   133 }
   135 - (void)setValue:(double)value;
   136 - (double)value;
   137 - (void)setMax:(double)max;
   138 - (double)max;
   139 - (void)setIndeterminate:(bool)aIndeterminate;
   140 - (bool)isIndeterminate;
   141 - (void)setHorizontal:(bool)aIsHorizontal;
   142 - (bool)isHorizontal;
   143 - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView;
   144 @end
   146 @implementation NSProgressBarCell
   148 - (void)setMax:(double)aMax
   149 {
   150   mMax = aMax;
   151 }
   153 - (double)max
   154 {
   155   return mMax;
   156 }
   158 - (void)setValue:(double)aValue
   159 {
   160   mValue = aValue;
   161 }
   163 - (double)value
   164 {
   165   return mValue;
   166 }
   168 - (void)setIndeterminate:(bool)aIndeterminate
   169 {
   170   mIsIndeterminate = aIndeterminate;
   171 }
   173 - (bool)isIndeterminate
   174 {
   175   return mIsIndeterminate;
   176 }
   178 - (void)setHorizontal:(bool)aIsHorizontal
   179 {
   180   mIsHorizontal = aIsHorizontal;
   181 }
   183 - (bool)isHorizontal
   184 {
   185   return mIsHorizontal;
   186 }
   188 - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView
   189 {
   190   CGContext* cgContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
   192   HIThemeTrackDrawInfo tdi;
   194   tdi.version = 0;
   195   tdi.min = 0;
   197   tdi.value = INT32_MAX * (mValue / mMax);
   198   tdi.max = INT32_MAX;
   199   tdi.bounds = NSRectToCGRect(cellFrame);
   200   tdi.attributes = mIsHorizontal ? kThemeTrackHorizontal : 0;
   201   tdi.enableState = [self controlTint] == NSClearControlTint ? kThemeTrackInactive
   202                                                              : kThemeTrackActive;
   204   NSControlSize size = [self controlSize];
   205   if (size == NSRegularControlSize) {
   206     tdi.kind = mIsIndeterminate ? kThemeLargeIndeterminateBar
   207                                 : kThemeLargeProgressBar;
   208   } else {
   209     NS_ASSERTION(size == NSSmallControlSize,
   210                  "We shouldn't have another size than small and regular for the moment");
   211     tdi.kind = mIsIndeterminate ? kThemeMediumIndeterminateBar
   212                                 : kThemeMediumProgressBar;
   213   }
   215   int32_t stepsPerSecond = mIsIndeterminate ? 60 : 30;
   216   int32_t milliSecondsPerStep = 1000 / stepsPerSecond;
   217   tdi.trackInfo.progress.phase = uint8_t(PR_IntervalToMilliseconds(PR_IntervalNow()) /
   218                                          milliSecondsPerStep);
   220   HIThemeDrawTrack(&tdi, NULL, cgContext, kHIThemeOrientationNormal);
   221 }
   223 @end
   225 @interface ContextAwareSearchFieldCell : NSSearchFieldCell
   226 {
   227   nsIFrame* mContext;
   228 }
   230 // setContext: stores the searchfield nsIFrame so that it can be consulted
   231 // during painting. Please reset this by calling setContext:nullptr as soon as
   232 // you're done with painting because we don't want to keep a dangling pointer.
   233 - (void)setContext:(nsIFrame*)aContext;
   234 @end
   236 @implementation ContextAwareSearchFieldCell
   238 - (id)initTextCell:(NSString*)aString
   239 {
   240   if ((self = [super initTextCell:aString])) {
   241     mContext = nullptr;
   242   }
   243   return self;
   244 }
   246 - (void)setContext:(nsIFrame*)aContext
   247 {
   248   mContext = aContext;
   249 }
   251 static BOOL IsToolbarStyleContainer(nsIFrame* aFrame)
   252 {
   253   nsIContent* content = aFrame->GetContent();
   254   if (!content)
   255     return NO;
   257   if (content->Tag() == nsGkAtoms::toolbar ||
   258       content->Tag() == nsGkAtoms::toolbox ||
   259       content->Tag() == nsGkAtoms::statusbar)
   260     return YES;
   262   switch (aFrame->StyleDisplay()->mAppearance) {
   263     case NS_THEME_TOOLBAR:
   264     case NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR:
   265     case NS_THEME_STATUSBAR:
   266       return YES;
   267     default:
   268       return NO;
   269   }
   270 }
   272 - (BOOL)_isToolbarMode
   273 {
   274   // On 10.7, searchfields have two different styles, depending on whether
   275   // the searchfield is on top of of window chrome. This function is called on
   276   // 10.7 during drawing in order to determine which style to use.
   277   for (nsIFrame* frame = mContext; frame; frame = frame->GetParent()) {
   278     if (IsToolbarStyleContainer(frame)) {
   279       return YES;
   280     }
   281   }
   282   return NO;
   283 }
   285 @end
   287 // Workaround for Bug 542048
   288 // On 64-bit, NSSearchFieldCells don't draw focus rings.
   289 #if defined(__x86_64__)
   291 static void DrawFocusRing(NSRect rect, float radius)
   292 {
   293   NSSetFocusRingStyle(NSFocusRingOnly);
   294   NSBezierPath* path = [NSBezierPath bezierPath];
   295   rect = NSInsetRect(rect, radius, radius);
   296   [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMinX(rect), NSMinY(rect)) radius:radius startAngle:180.0 endAngle:270.0];
   297   [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMaxX(rect), NSMinY(rect)) radius:radius startAngle:270.0 endAngle:360.0];
   298   [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMaxX(rect), NSMaxY(rect)) radius:radius startAngle:  0.0 endAngle: 90.0];
   299   [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMinX(rect), NSMaxY(rect)) radius:radius startAngle: 90.0 endAngle:180.0];
   300   [path closePath];
   301   [path fill];
   302 }
   304 @interface SearchFieldCellWithFocusRing : ContextAwareSearchFieldCell {} @end
   306 @implementation SearchFieldCellWithFocusRing
   308 - (void)drawWithFrame:(NSRect)rect inView:(NSView*)controlView
   309 {
   310   [super drawWithFrame:rect inView:controlView];
   311   if ([self showsFirstResponder]) {
   312     DrawFocusRing(rect, NSHeight(rect) / 2);
   313   }
   314 }
   316 @end
   318 #endif
   320 // Copied from nsLookAndFeel.h
   321 // Apple hasn't defined a constant for scollbars with two arrows on each end, so we'll use this one.
   322 static const int kThemeScrollBarArrowsBoth = 2;
   324 #define HITHEME_ORIENTATION kHIThemeOrientationNormal
   325 #define MAX_FOCUS_RING_WIDTH 4
   327 // These enums are for indexing into the margin array.
   328 enum {
   329   leopardOS = 0
   330 };
   332 enum {
   333   miniControlSize,
   334   smallControlSize,
   335   regularControlSize
   336 };
   338 enum {
   339   leftMargin,
   340   topMargin,
   341   rightMargin,
   342   bottomMargin
   343 };
   345 static int EnumSizeForCocoaSize(NSControlSize cocoaControlSize) {
   346   if (cocoaControlSize == NSMiniControlSize)
   347     return miniControlSize;
   348   else if (cocoaControlSize == NSSmallControlSize)
   349     return smallControlSize;
   350   else
   351     return regularControlSize;
   352 }
   354 static NSControlSize CocoaSizeForEnum(int32_t enumControlSize) {
   355   if (enumControlSize == miniControlSize)
   356     return NSMiniControlSize;
   357   else if (enumControlSize == smallControlSize)
   358     return NSSmallControlSize;
   359   else
   360     return NSRegularControlSize;
   361 }
   363 static NSString* CUIControlSizeForCocoaSize(NSControlSize aControlSize)
   364 {
   365   if (aControlSize == NSRegularControlSize)
   366     return @"regular";
   367   else if (aControlSize == NSSmallControlSize)
   368     return @"small";
   369   else
   370     return @"mini";
   371 }
   373 static void InflateControlRect(NSRect* rect, NSControlSize cocoaControlSize, const float marginSet[][3][4])
   374 {
   375   if (!marginSet)
   376     return;
   378   static int osIndex = leopardOS;
   379   int controlSize = EnumSizeForCocoaSize(cocoaControlSize);
   380   const float* buttonMargins = marginSet[osIndex][controlSize];
   381   rect->origin.x -= buttonMargins[leftMargin];
   382   rect->origin.y -= buttonMargins[bottomMargin];
   383   rect->size.width += buttonMargins[leftMargin] + buttonMargins[rightMargin];
   384   rect->size.height += buttonMargins[bottomMargin] + buttonMargins[topMargin];
   385 }
   387 static NSWindow* NativeWindowForFrame(nsIFrame* aFrame,
   388                                       nsIWidget** aTopLevelWidget = NULL)
   389 {
   390   if (!aFrame)
   391     return nil;  
   393   nsIWidget* widget = aFrame->GetNearestWidget();
   394   if (!widget)
   395     return nil;
   397   nsIWidget* topLevelWidget = widget->GetTopLevelWidget();
   398   if (aTopLevelWidget)
   399     *aTopLevelWidget = topLevelWidget;
   401   return (NSWindow*)topLevelWidget->GetNativeData(NS_NATIVE_WINDOW);
   402 }
   404 static NSSize
   405 WindowButtonsSize(nsIFrame* aFrame)
   406 {
   407   NSWindow* window = NativeWindowForFrame(aFrame);
   408   if (!window) {
   409     // Return fallback values.
   410     if (!nsCocoaFeatures::OnLionOrLater())
   411       return NSMakeSize(57, 16);
   412     return NSMakeSize(54, 16);
   413   }
   415   NSRect buttonBox = NSZeroRect;
   416   NSButton* closeButton = [window standardWindowButton:NSWindowCloseButton];
   417   if (closeButton) {
   418     buttonBox = NSUnionRect(buttonBox, [closeButton frame]);
   419   }
   420   NSButton* minimizeButton = [window standardWindowButton:NSWindowMiniaturizeButton];
   421   if (minimizeButton) {
   422     buttonBox = NSUnionRect(buttonBox, [minimizeButton frame]);
   423   }
   424   NSButton* zoomButton = [window standardWindowButton:NSWindowZoomButton];
   425   if (zoomButton) {
   426     buttonBox = NSUnionRect(buttonBox, [zoomButton frame]);
   427   }
   428   return buttonBox.size;
   429 }
   431 static BOOL FrameIsInActiveWindow(nsIFrame* aFrame)
   432 {
   433   nsIWidget* topLevelWidget = NULL;
   434   NSWindow* win = NativeWindowForFrame(aFrame, &topLevelWidget);
   435   if (!topLevelWidget || !win)
   436     return YES;
   438   // XUL popups, e.g. the toolbar customization popup, can't become key windows,
   439   // but controls in these windows should still get the active look.
   440   if (topLevelWidget->WindowType() == eWindowType_popup)
   441     return YES;
   442   if ([win isSheet])
   443     return [win isKeyWindow];
   444   return [win isMainWindow] && ![win attachedSheet];
   445 }
   447 // Toolbar controls and content controls respond to different window
   448 // activeness states.
   449 static BOOL IsActive(nsIFrame* aFrame, BOOL aIsToolbarControl)
   450 {
   451   if (aIsToolbarControl)
   452     return [NativeWindowForFrame(aFrame) isMainWindow];
   453   return FrameIsInActiveWindow(aFrame);
   454 }
   456 NS_IMPL_ISUPPORTS_INHERITED(nsNativeThemeCocoa, nsNativeTheme, nsITheme)
   459 nsNativeThemeCocoa::nsNativeThemeCocoa()
   460 {
   461   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
   463   // provide a local autorelease pool, as this is called during startup
   464   // before the main event-loop pool is in place
   465   nsAutoreleasePool pool;
   467   mHelpButtonCell = [[NSButtonCell alloc] initTextCell:nil];
   468   [mHelpButtonCell setBezelStyle:NSHelpButtonBezelStyle];
   469   [mHelpButtonCell setButtonType:NSMomentaryPushInButton];
   470   [mHelpButtonCell setHighlightsBy:NSPushInCellMask];
   472   mPushButtonCell = [[NSButtonCell alloc] initTextCell:nil];
   473   [mPushButtonCell setButtonType:NSMomentaryPushInButton];
   474   [mPushButtonCell setHighlightsBy:NSPushInCellMask];
   476   mRadioButtonCell = [[NSButtonCell alloc] initTextCell:nil];
   477   [mRadioButtonCell setButtonType:NSRadioButton];
   479   mCheckboxCell = [[NSButtonCell alloc] initTextCell:nil];
   480   [mCheckboxCell setButtonType:NSSwitchButton];
   481   [mCheckboxCell setAllowsMixedState:YES];
   483 #if defined(__x86_64__)
   484   mSearchFieldCell = [[SearchFieldCellWithFocusRing alloc] initTextCell:@""];
   485 #else
   486   mSearchFieldCell = [[ContextAwareSearchFieldCell alloc] initTextCell:@""];
   487 #endif
   488   [mSearchFieldCell setBezelStyle:NSTextFieldRoundedBezel];
   489   [mSearchFieldCell setBezeled:YES];
   490   [mSearchFieldCell setEditable:YES];
   491   [mSearchFieldCell setFocusRingType:NSFocusRingTypeExterior];
   493   mDropdownCell = [[NSPopUpButtonCell alloc] initTextCell:@"" pullsDown:NO];
   495   mComboBoxCell = [[NSComboBoxCell alloc] initTextCell:@""];
   496   [mComboBoxCell setBezeled:YES];
   497   [mComboBoxCell setEditable:YES];
   498   [mComboBoxCell setFocusRingType:NSFocusRingTypeExterior];
   500   mProgressBarCell = [[NSProgressBarCell alloc] init];
   502   mMeterBarCell = [[NSLevelIndicatorCell alloc]
   503                     initWithLevelIndicatorStyle:NSContinuousCapacityLevelIndicatorStyle];
   505   mCellDrawView = [[CellDrawView alloc] init];
   507   NS_OBJC_END_TRY_ABORT_BLOCK;
   508 }
   510 nsNativeThemeCocoa::~nsNativeThemeCocoa()
   511 {
   512   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
   514   [mMeterBarCell release];
   515   [mProgressBarCell release];
   516   [mHelpButtonCell release];
   517   [mPushButtonCell release];
   518   [mRadioButtonCell release];
   519   [mCheckboxCell release];
   520   [mSearchFieldCell release];
   521   [mDropdownCell release];
   522   [mComboBoxCell release];
   523   [mCellDrawView release];
   525   NS_OBJC_END_TRY_ABORT_BLOCK;
   526 }
   528 // Limit on the area of the target rect (in pixels^2) in
   529 // DrawCellWithScaling(), DrawButton() and DrawScrollbar(), above which we
   530 // don't draw the object into a bitmap buffer.  This is to avoid crashes in
   531 // [NSGraphicsContext graphicsContextWithGraphicsPort:flipped:] and
   532 // CGContextDrawImage(), and also to avoid very poor drawing performance in
   533 // CGContextDrawImage() when it scales the bitmap (particularly if xscale or
   534 // yscale is less than but near 1 -- e.g. 0.9).  This value was determined
   535 // by trial and error, on OS X 10.4.11 and 10.5.4, and on systems with
   536 // different amounts of RAM.
   537 #define BITMAP_MAX_AREA 500000
   539 static int
   540 GetBackingScaleFactorForRendering(CGContextRef cgContext)
   541 {
   542   CGAffineTransform ctm = CGContextGetUserSpaceToDeviceSpaceTransform(cgContext);
   543   CGRect transformedUserSpacePixel = CGRectApplyAffineTransform(CGRectMake(0, 0, 1, 1), ctm);
   544   float maxScale = std::max(fabs(transformedUserSpacePixel.size.width),
   545                           fabs(transformedUserSpacePixel.size.height));
   546   return maxScale > 1.0 ? 2 : 1;  
   547 }
   549 /*
   550  * Draw the given NSCell into the given cgContext.
   551  *
   552  * destRect - the size and position of the resulting control rectangle
   553  * controlSize - the NSControlSize which will be given to the NSCell before
   554  *  asking it to render
   555  * naturalSize - The natural dimensions of this control.
   556  *  If the control rect size is not equal to either of these, a scale
   557  *  will be applied to the context so that rendering the control at the
   558  *  natural size will result in it filling the destRect space.
   559  *  If a control has no natural dimensions in either/both axes, pass 0.0f.
   560  * minimumSize - The minimum dimensions of this control.
   561  *  If the control rect size is less than the minimum for a given axis,
   562  *  a scale will be applied to the context so that the minimum is used
   563  *  for drawing.  If a control has no minimum dimensions in either/both
   564  *  axes, pass 0.0f.
   565  * marginSet - an array of margins; a multidimensional array of [2][3][4],
   566  *  with the first dimension being the OS version (Tiger or Leopard),
   567  *  the second being the control size (mini, small, regular), and the third
   568  *  being the 4 margin values (left, top, right, bottom).
   569  * view - The NSView that we're drawing into. As far as I can tell, it doesn't
   570  *  matter if this is really the right view; it just has to return YES when
   571  *  asked for isFlipped. Otherwise we'll get drawing bugs on 10.4.
   572  * mirrorHorizontal - whether to mirror the cell horizontally
   573  */
   574 static void DrawCellWithScaling(NSCell *cell,
   575                                 CGContextRef cgContext,
   576                                 const HIRect& destRect,
   577                                 NSControlSize controlSize,
   578                                 NSSize naturalSize,
   579                                 NSSize minimumSize,
   580                                 const float marginSet[][3][4],
   581                                 NSView* view,
   582                                 BOOL mirrorHorizontal)
   583 {
   584   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
   586   NSRect drawRect = NSMakeRect(destRect.origin.x, destRect.origin.y, destRect.size.width, destRect.size.height);
   588   if (naturalSize.width != 0.0f)
   589     drawRect.size.width = naturalSize.width;
   590   if (naturalSize.height != 0.0f)
   591     drawRect.size.height = naturalSize.height;
   593   // Keep aspect ratio when scaling if one dimension is free.
   594   if (naturalSize.width == 0.0f && naturalSize.height != 0.0f)
   595     drawRect.size.width = destRect.size.width * naturalSize.height / destRect.size.height;
   596   if (naturalSize.height == 0.0f && naturalSize.width != 0.0f)
   597     drawRect.size.height = destRect.size.height * naturalSize.width / destRect.size.width;
   599   // Honor minimum sizes.
   600   if (drawRect.size.width < minimumSize.width)
   601     drawRect.size.width = minimumSize.width;
   602   if (drawRect.size.height < minimumSize.height)
   603     drawRect.size.height = minimumSize.height;
   605   [NSGraphicsContext saveGraphicsState];
   607   // Only skip the buffer if the area of our cell (in pixels^2) is too large.
   608   if (drawRect.size.width * drawRect.size.height > BITMAP_MAX_AREA) {
   609     // Inflate the rect Gecko gave us by the margin for the control.
   610     InflateControlRect(&drawRect, controlSize, marginSet);
   612     NSGraphicsContext* savedContext = [NSGraphicsContext currentContext];
   613     [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:cgContext flipped:YES]];
   615     DrawCellIncludingFocusRing(cell, drawRect, view);
   617     [NSGraphicsContext setCurrentContext:savedContext];
   618   }
   619   else {
   620     float w = ceil(drawRect.size.width);
   621     float h = ceil(drawRect.size.height);
   622     NSRect tmpRect = NSMakeRect(MAX_FOCUS_RING_WIDTH, MAX_FOCUS_RING_WIDTH, w, h);
   624     // inflate to figure out the frame we need to tell NSCell to draw in, to get something that's 0,0,w,h
   625     InflateControlRect(&tmpRect, controlSize, marginSet);
   627     // and then, expand by MAX_FOCUS_RING_WIDTH size to make sure we can capture any focus ring
   628     w += MAX_FOCUS_RING_WIDTH * 2.0;
   629     h += MAX_FOCUS_RING_WIDTH * 2.0;
   631     int backingScaleFactor = GetBackingScaleFactorForRendering(cgContext);
   632     CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
   633     CGContextRef ctx = CGBitmapContextCreate(NULL,
   634                                              (int) w * backingScaleFactor, (int) h * backingScaleFactor,
   635                                              8, (int) w * backingScaleFactor * 4,
   636                                              rgb, kCGImageAlphaPremultipliedFirst);
   637     CGColorSpaceRelease(rgb);
   639     // We need to flip the image twice in order to avoid drawing bugs on 10.4, see bug 465069.
   640     // This is the first flip transform, applied to cgContext.
   641     CGContextScaleCTM(cgContext, 1.0f, -1.0f);
   642     CGContextTranslateCTM(cgContext, 0.0f, -(2.0 * destRect.origin.y + destRect.size.height));
   643     if (mirrorHorizontal) {
   644       CGContextScaleCTM(cgContext, -1.0f, 1.0f);
   645       CGContextTranslateCTM(cgContext, -(2.0 * destRect.origin.x + destRect.size.width), 0.0f);
   646     }
   648     NSGraphicsContext* savedContext = [NSGraphicsContext currentContext];
   649     [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:ctx flipped:YES]];
   651     CGContextScaleCTM(ctx, backingScaleFactor, backingScaleFactor);
   653     // This is the second flip transform, applied to ctx.
   654     CGContextScaleCTM(ctx, 1.0f, -1.0f);
   655     CGContextTranslateCTM(ctx, 0.0f, -(2.0 * tmpRect.origin.y + tmpRect.size.height));
   657     DrawCellIncludingFocusRing(cell, tmpRect, view);
   659     [NSGraphicsContext setCurrentContext:savedContext];
   661     CGImageRef img = CGBitmapContextCreateImage(ctx);
   663     // Drop the image into the original destination rectangle, scaling to fit
   664     // Only scale MAX_FOCUS_RING_WIDTH by xscale/yscale when the resulting rect
   665     // doesn't extend beyond the overflow rect
   666     float xscale = destRect.size.width / drawRect.size.width;
   667     float yscale = destRect.size.height / drawRect.size.height;
   668     float scaledFocusRingX = xscale < 1.0f ? MAX_FOCUS_RING_WIDTH * xscale : MAX_FOCUS_RING_WIDTH;
   669     float scaledFocusRingY = yscale < 1.0f ? MAX_FOCUS_RING_WIDTH * yscale : MAX_FOCUS_RING_WIDTH;
   670     CGContextDrawImage(cgContext, CGRectMake(destRect.origin.x - scaledFocusRingX,
   671                                              destRect.origin.y - scaledFocusRingY,
   672                                              destRect.size.width + scaledFocusRingX * 2,
   673                                              destRect.size.height + scaledFocusRingY * 2),
   674                        img);
   676     CGImageRelease(img);
   677     CGContextRelease(ctx);
   678   }
   680   [NSGraphicsContext restoreGraphicsState];
   682 #if DRAW_IN_FRAME_DEBUG
   683   CGContextSetRGBFillColor(cgContext, 0.0, 0.0, 0.5, 0.25);
   684   CGContextFillRect(cgContext, destRect);
   685 #endif
   687   NS_OBJC_END_TRY_ABORT_BLOCK;
   688 }
   690 struct CellRenderSettings {
   691   // The natural dimensions of the control.
   692   // If a control has no natural dimensions in either/both axes, set to 0.0f.
   693   NSSize naturalSizes[3];
   695   // The minimum dimensions of the control.
   696   // If a control has no minimum dimensions in either/both axes, set to 0.0f.
   697   NSSize minimumSizes[3];
   699   // A three-dimensional array,
   700   // with the first dimension being the OS version (only Leopard for the moment),
   701   // the second being the control size (mini, small, regular), and the third
   702   // being the 4 margin values (left, top, right, bottom).
   703   float margins[1][3][4];
   704 };
   706 /*
   707  * This is a helper method that returns the required NSControlSize given a size
   708  * and the size of the three controls plus a tolerance.
   709  * size - The width or the height of the element to draw.
   710  * sizes - An array with the all the width/height of the element for its
   711  *         different sizes.
   712  * tolerance - The tolerance as passed to DrawCellWithSnapping.
   713  * NOTE: returns NSRegularControlSize if all values in 'sizes' are zero.
   714  */
   715 static NSControlSize FindControlSize(CGFloat size, const CGFloat* sizes, CGFloat tolerance)
   716 {
   717   for (uint32_t i = miniControlSize; i <= regularControlSize; ++i) {
   718     if (sizes[i] == 0) {
   719       continue;
   720     }
   722     CGFloat next = 0;
   723     // Find next value.
   724     for (uint32_t j = i+1; j <= regularControlSize; ++j) {
   725       if (sizes[j] != 0) {
   726         next = sizes[j];
   727         break;
   728       }
   729     }
   731     // If it's the latest value, we pick it.
   732     if (next == 0) {
   733       return CocoaSizeForEnum(i);
   734     }
   736     if (size <= sizes[i] + tolerance && size < next) {
   737       return CocoaSizeForEnum(i);
   738     }
   739   }
   741   // If we are here, that means sizes[] was an array with only empty values
   742   // or the algorithm above is wrong.
   743   // The former can happen but the later would be wrong.
   744   NS_ASSERTION(sizes[0] == 0 && sizes[1] == 0 && sizes[2] == 0,
   745                "We found no control! We shouldn't be there!");
   746   return CocoaSizeForEnum(regularControlSize);
   747 }
   749 /*
   750  * Draw the given NSCell into the given cgContext with a nice control size.
   751  *
   752  * This function is similar to DrawCellWithScaling, but it decides what
   753  * control size to use based on the destRect's size.
   754  * Scaling is only applied when the difference between the destRect's size
   755  * and the next smaller natural size is greater than snapTolerance. Otherwise
   756  * it snaps to the next smaller control size without scaling because unscaled
   757  * controls look nicer.
   758  */
   759 static void DrawCellWithSnapping(NSCell *cell,
   760                                  CGContextRef cgContext,
   761                                  const HIRect& destRect,
   762                                  const CellRenderSettings settings,
   763                                  float verticalAlignFactor,
   764                                  NSView* view,
   765                                  BOOL mirrorHorizontal,
   766                                  float snapTolerance = 2.0f)
   767 {
   768   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
   770   const float rectWidth = destRect.size.width, rectHeight = destRect.size.height;
   771   const NSSize *sizes = settings.naturalSizes;
   772   const NSSize miniSize = sizes[EnumSizeForCocoaSize(NSMiniControlSize)];
   773   const NSSize smallSize = sizes[EnumSizeForCocoaSize(NSSmallControlSize)];
   774   const NSSize regularSize = sizes[EnumSizeForCocoaSize(NSRegularControlSize)];
   776   HIRect drawRect = destRect;
   778   CGFloat controlWidths[3] = { miniSize.width, smallSize.width, regularSize.width };
   779   NSControlSize controlSizeX = FindControlSize(rectWidth, controlWidths, snapTolerance);
   780   CGFloat controlHeights[3] = { miniSize.height, smallSize.height, regularSize.height };
   781   NSControlSize controlSizeY = FindControlSize(rectHeight, controlHeights, snapTolerance);
   783   NSControlSize controlSize = NSRegularControlSize;
   784   int sizeIndex = 0;
   786   // At some sizes, don't scale but snap.
   787   const NSControlSize smallerControlSize =
   788     EnumSizeForCocoaSize(controlSizeX) < EnumSizeForCocoaSize(controlSizeY) ?
   789     controlSizeX : controlSizeY;
   790   const int smallerControlSizeIndex = EnumSizeForCocoaSize(smallerControlSize);
   791   const NSSize size = sizes[smallerControlSizeIndex];
   792   float diffWidth = size.width ? rectWidth - size.width : 0.0f;
   793   float diffHeight = size.height ? rectHeight - size.height : 0.0f;
   794   if (diffWidth >= 0.0f && diffHeight >= 0.0f &&
   795       diffWidth <= snapTolerance && diffHeight <= snapTolerance) {
   796     // Snap to the smaller control size.
   797     controlSize = smallerControlSize;
   798     sizeIndex = smallerControlSizeIndex;
   799     // Resize and center the drawRect.
   800     if (sizes[sizeIndex].width) {
   801       drawRect.origin.x += ceil((destRect.size.width - sizes[sizeIndex].width) / 2);
   802       drawRect.size.width = sizes[sizeIndex].width;
   803     }
   804     if (sizes[sizeIndex].height) {
   805       drawRect.origin.y += floor((destRect.size.height - sizes[sizeIndex].height) * verticalAlignFactor);
   806       drawRect.size.height = sizes[sizeIndex].height;
   807     }
   808   } else {
   809     // Use the larger control size.
   810     controlSize = EnumSizeForCocoaSize(controlSizeX) > EnumSizeForCocoaSize(controlSizeY) ?
   811                   controlSizeX : controlSizeY;
   812     sizeIndex = EnumSizeForCocoaSize(controlSize);
   813    }
   815   [cell setControlSize:controlSize];
   817   NSSize minimumSize = settings.minimumSizes ? settings.minimumSizes[sizeIndex] : NSZeroSize;
   818   DrawCellWithScaling(cell, cgContext, drawRect, controlSize, sizes[sizeIndex],
   819                       minimumSize, settings.margins, view, mirrorHorizontal);
   821   NS_OBJC_END_TRY_ABORT_BLOCK;
   822 }
   824 static float VerticalAlignFactor(nsIFrame *aFrame)
   825 {
   826   if (!aFrame)
   827     return 0.5f; // default: center
   829   const nsStyleCoord& va = aFrame->StyleTextReset()->mVerticalAlign;
   830   uint8_t intval = (va.GetUnit() == eStyleUnit_Enumerated)
   831                      ? va.GetIntValue()
   832                      : NS_STYLE_VERTICAL_ALIGN_MIDDLE;
   833   switch (intval) {
   834     case NS_STYLE_VERTICAL_ALIGN_TOP:
   835     case NS_STYLE_VERTICAL_ALIGN_TEXT_TOP:
   836       return 0.0f;
   838     case NS_STYLE_VERTICAL_ALIGN_SUB:
   839     case NS_STYLE_VERTICAL_ALIGN_SUPER:
   840     case NS_STYLE_VERTICAL_ALIGN_MIDDLE:
   841     case NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE:
   842       return 0.5f;
   844     case NS_STYLE_VERTICAL_ALIGN_BASELINE:
   845     case NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM:
   846     case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
   847       return 1.0f;
   849     default:
   850       NS_NOTREACHED("invalid vertical-align");
   851       return 0.5f;
   852   }
   853 }
   855 // These are the sizes that Gecko needs to request to draw if it wants
   856 // to get a standard-sized Aqua radio button drawn. Note that the rects
   857 // that draw these are actually a little bigger.
   858 static const CellRenderSettings radioSettings = {
   859   {
   860     NSMakeSize(11, 11), // mini
   861     NSMakeSize(13, 13), // small
   862     NSMakeSize(16, 16)  // regular
   863   },
   864   {
   865     NSZeroSize, NSZeroSize, NSZeroSize
   866   },
   867   {
   868     { // Leopard
   869       {0, 0, 0, 0},     // mini
   870       {0, 1, 1, 1},     // small
   871       {0, 0, 0, 0}      // regular
   872     }
   873   }
   874 };
   876 static const CellRenderSettings checkboxSettings = {
   877   {
   878     NSMakeSize(11, 11), // mini
   879     NSMakeSize(13, 13), // small
   880     NSMakeSize(16, 16)  // regular
   881   },
   882   {
   883     NSZeroSize, NSZeroSize, NSZeroSize
   884   },
   885   {
   886     { // Leopard
   887       {0, 1, 0, 0},     // mini
   888       {0, 1, 0, 1},     // small
   889       {0, 1, 0, 1}      // regular
   890     }
   891   }
   892 };
   894 void
   895 nsNativeThemeCocoa::DrawCheckboxOrRadio(CGContextRef cgContext, bool inCheckbox,
   896                                         const HIRect& inBoxRect, bool inSelected,
   897                                         EventStates inState, nsIFrame* aFrame)
   898 {
   899   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
   901   NSButtonCell *cell = inCheckbox ? mCheckboxCell : mRadioButtonCell;
   902   NSCellStateValue state = inSelected ? NSOnState : NSOffState;
   904   // Check if we have an indeterminate checkbox
   905   if (inCheckbox && GetIndeterminate(aFrame))
   906     state = NSMixedState;
   908   [cell setEnabled:!IsDisabled(aFrame, inState)];
   909   [cell setShowsFirstResponder:inState.HasState(NS_EVENT_STATE_FOCUS)];
   910   [cell setState:state];
   911   [cell setHighlighted:inState.HasAllStates(NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_HOVER)];
   912   [cell setControlTint:(FrameIsInActiveWindow(aFrame) ? [NSColor currentControlTint] : NSClearControlTint)];
   914   // Ensure that the control is square.
   915   float length = std::min(inBoxRect.size.width, inBoxRect.size.height);
   916   HIRect drawRect = CGRectMake(inBoxRect.origin.x + (int)((inBoxRect.size.width - length) / 2.0f),
   917                                inBoxRect.origin.y + (int)((inBoxRect.size.height - length) / 2.0f),
   918                                length, length);
   920   DrawCellWithSnapping(cell, cgContext, drawRect,
   921                        inCheckbox ? checkboxSettings : radioSettings,
   922                        VerticalAlignFactor(aFrame), mCellDrawView, NO);
   924   NS_OBJC_END_TRY_ABORT_BLOCK;
   925 }
   927 static const CellRenderSettings searchFieldSettings = {
   928   {
   929     NSMakeSize(0, 16), // mini
   930     NSMakeSize(0, 19), // small
   931     NSMakeSize(0, 22)  // regular
   932   },
   933   {
   934     NSMakeSize(32, 0), // mini
   935     NSMakeSize(38, 0), // small
   936     NSMakeSize(44, 0)  // regular
   937   },
   938   {
   939     { // Leopard
   940       {0, 0, 0, 0},     // mini
   941       {0, 0, 0, 0},     // small
   942       {0, 0, 0, 0}      // regular
   943     }
   944   }
   945 };
   947 void
   948 nsNativeThemeCocoa::DrawSearchField(CGContextRef cgContext, const HIRect& inBoxRect,
   949                                     nsIFrame* aFrame, EventStates inState)
   950 {
   951   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
   953   ContextAwareSearchFieldCell* cell = mSearchFieldCell;
   954   [cell setContext:aFrame];
   955   [cell setEnabled:!IsDisabled(aFrame, inState)];
   956   // NOTE: this could probably use inState
   957   [cell setShowsFirstResponder:IsFocused(aFrame)];
   959   DrawCellWithSnapping(cell, cgContext, inBoxRect, searchFieldSettings,
   960                        VerticalAlignFactor(aFrame), mCellDrawView,
   961                        IsFrameRTL(aFrame));
   963   [cell setContext:nullptr];
   965   NS_OBJC_END_TRY_ABORT_BLOCK;
   966 }
   968 static const NSSize kHelpButtonSize = NSMakeSize(20, 20);
   970 static const CellRenderSettings pushButtonSettings = {
   971   {
   972     NSMakeSize(0, 16), // mini
   973     NSMakeSize(0, 19), // small
   974     NSMakeSize(0, 22)  // regular
   975   },
   976   {
   977     NSMakeSize(18, 0), // mini
   978     NSMakeSize(26, 0), // small
   979     NSMakeSize(30, 0)  // regular
   980   },
   981   {
   982     { // Leopard
   983       {0, 0, 0, 0},    // mini
   984       {4, 0, 4, 1},    // small
   985       {5, 0, 5, 2}     // regular
   986     }
   987   }
   988 };
   990 // The height at which we start doing square buttons instead of rounded buttons
   991 // Rounded buttons look bad if drawn at a height greater than 26, so at that point
   992 // we switch over to doing square buttons which looks fine at any size.
   993 #define DO_SQUARE_BUTTON_HEIGHT 26
   995 void
   996 nsNativeThemeCocoa::DrawPushButton(CGContextRef cgContext, const HIRect& inBoxRect,
   997                                    EventStates inState, uint8_t aWidgetType,
   998                                    nsIFrame* aFrame)
   999 {
  1000   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1002   BOOL isActive = FrameIsInActiveWindow(aFrame);
  1003   BOOL isDisabled = IsDisabled(aFrame, inState);
  1005   NSButtonCell* cell = (aWidgetType == NS_THEME_MOZ_MAC_HELP_BUTTON) ? mHelpButtonCell : mPushButtonCell;
  1006   [cell setEnabled:!isDisabled];
  1007   [cell setHighlighted:isActive &&
  1008                        inState.HasAllStates(NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_HOVER)];
  1009   [cell setShowsFirstResponder:inState.HasState(NS_EVENT_STATE_FOCUS) && !isDisabled && isActive];
  1011   if (aWidgetType == NS_THEME_MOZ_MAC_HELP_BUTTON) {
  1012     DrawCellWithScaling(cell, cgContext, inBoxRect, NSRegularControlSize,
  1013                         NSZeroSize, kHelpButtonSize, NULL, mCellDrawView,
  1014                         false); // Don't mirror icon in RTL.
  1015   } else {
  1016     // If the button is tall enough, draw the square button style so that
  1017     // buttons with non-standard content look good. Otherwise draw normal
  1018     // rounded aqua buttons.
  1019     if (inBoxRect.size.height > DO_SQUARE_BUTTON_HEIGHT) {
  1020       [cell setBezelStyle:NSShadowlessSquareBezelStyle];
  1021       DrawCellWithScaling(cell, cgContext, inBoxRect, NSRegularControlSize,
  1022                           NSZeroSize, NSMakeSize(14, 0), NULL, mCellDrawView,
  1023                           IsFrameRTL(aFrame));
  1024     } else {
  1025       [cell setBezelStyle:NSRoundedBezelStyle];
  1026       DrawCellWithSnapping(cell, cgContext, inBoxRect, pushButtonSettings, 0.5f,
  1027                            mCellDrawView, IsFrameRTL(aFrame), 1.0f);
  1031 #if DRAW_IN_FRAME_DEBUG
  1032   CGContextSetRGBFillColor(cgContext, 0.0, 0.0, 0.5, 0.25);
  1033   CGContextFillRect(cgContext, inBoxRect);
  1034 #endif
  1036   NS_OBJC_END_TRY_ABORT_BLOCK;
  1039 typedef void (*RenderHIThemeControlFunction)(CGContextRef cgContext, const HIRect& aRenderRect, void* aData);
  1041 static void
  1042 RenderTransformedHIThemeControl(CGContextRef aCGContext, const HIRect& aRect,
  1043                                 RenderHIThemeControlFunction aFunc, void* aData,
  1044                                 BOOL mirrorHorizontally = NO)
  1046   CGAffineTransform savedCTM = CGContextGetCTM(aCGContext);
  1047   CGContextTranslateCTM(aCGContext, aRect.origin.x, aRect.origin.y);
  1049   bool drawDirect;
  1050   HIRect drawRect = aRect;
  1051   drawRect.origin = CGPointZero;
  1053   if (!mirrorHorizontally && savedCTM.a == 1.0f && savedCTM.b == 0.0f &&
  1054       savedCTM.c == 0.0f && (savedCTM.d == 1.0f || savedCTM.d == -1.0f)) {
  1055     drawDirect = TRUE;
  1056   } else {
  1057     drawDirect = FALSE;
  1060   // Fall back to no bitmap buffer if the area of our control (in pixels^2)
  1061   // is too large.
  1062   if (drawDirect || (aRect.size.width * aRect.size.height > BITMAP_MAX_AREA)) {
  1063     aFunc(aCGContext, drawRect, aData);
  1064   } else {
  1065     // Inflate the buffer to capture focus rings.
  1066     int w = ceil(drawRect.size.width) + 2 * MAX_FOCUS_RING_WIDTH;
  1067     int h = ceil(drawRect.size.height) + 2 * MAX_FOCUS_RING_WIDTH;
  1069     int backingScaleFactor = GetBackingScaleFactorForRendering(aCGContext);
  1070     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
  1071     CGContextRef bitmapctx = CGBitmapContextCreate(NULL,
  1072                                                    w * backingScaleFactor,
  1073                                                    h * backingScaleFactor,
  1074                                                    8,
  1075                                                    w * backingScaleFactor * 4,
  1076                                                    colorSpace,
  1077                                                    kCGImageAlphaPremultipliedFirst);
  1078     CGColorSpaceRelease(colorSpace);
  1080     CGContextScaleCTM(bitmapctx, backingScaleFactor, backingScaleFactor);
  1081     CGContextTranslateCTM(bitmapctx, MAX_FOCUS_RING_WIDTH, MAX_FOCUS_RING_WIDTH);
  1083     // HITheme always wants to draw into a flipped context, or things
  1084     // get confused.
  1085     CGContextTranslateCTM(bitmapctx, 0.0f, aRect.size.height);
  1086     CGContextScaleCTM(bitmapctx, 1.0f, -1.0f);
  1088     aFunc(bitmapctx, drawRect, aData);
  1090     CGImageRef bitmap = CGBitmapContextCreateImage(bitmapctx);
  1092     CGAffineTransform ctm = CGContextGetCTM(aCGContext);
  1094     // We need to unflip, so that we can do a DrawImage without getting a flipped image.
  1095     CGContextTranslateCTM(aCGContext, 0.0f, aRect.size.height);
  1096     CGContextScaleCTM(aCGContext, 1.0f, -1.0f);
  1098     if (mirrorHorizontally) {
  1099       CGContextTranslateCTM(aCGContext, aRect.size.width, 0);
  1100       CGContextScaleCTM(aCGContext, -1.0f, 1.0f);
  1103     HIRect inflatedDrawRect = CGRectMake(-MAX_FOCUS_RING_WIDTH, -MAX_FOCUS_RING_WIDTH, w, h);
  1104     CGContextDrawImage(aCGContext, inflatedDrawRect, bitmap);
  1106     CGContextSetCTM(aCGContext, ctm);
  1108     CGImageRelease(bitmap);
  1109     CGContextRelease(bitmapctx);
  1112   CGContextSetCTM(aCGContext, savedCTM);
  1115 static void
  1116 RenderButton(CGContextRef cgContext, const HIRect& aRenderRect, void* aData)
  1118   HIThemeButtonDrawInfo* bdi = (HIThemeButtonDrawInfo*)aData;
  1119   HIThemeDrawButton(&aRenderRect, bdi, cgContext, kHIThemeOrientationNormal, NULL);
  1122 void
  1123 nsNativeThemeCocoa::DrawButton(CGContextRef cgContext, ThemeButtonKind inKind,
  1124                                const HIRect& inBoxRect, bool inIsDefault,
  1125                                ThemeButtonValue inValue, ThemeButtonAdornment inAdornment,
  1126                                EventStates inState, nsIFrame* aFrame)
  1128   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1130   BOOL isActive = FrameIsInActiveWindow(aFrame);
  1131   BOOL isDisabled = IsDisabled(aFrame, inState);
  1133   HIThemeButtonDrawInfo bdi;
  1134   bdi.version = 0;
  1135   bdi.kind = inKind;
  1136   bdi.value = inValue;
  1137   bdi.adornment = inAdornment;
  1139   if (isDisabled) {
  1140     bdi.state = kThemeStateUnavailable;
  1142   else if (inState.HasAllStates(NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_HOVER)) {
  1143     bdi.state = kThemeStatePressed;
  1145   else {
  1146     if (inKind == kThemeArrowButton)
  1147       bdi.state = kThemeStateUnavailable; // these are always drawn as unavailable
  1148     else if (!isActive && inKind == kThemeListHeaderButton)
  1149       bdi.state = kThemeStateInactive;
  1150     else
  1151       bdi.state = kThemeStateActive;
  1154   if (inState.HasState(NS_EVENT_STATE_FOCUS) && isActive)
  1155     bdi.adornment |= kThemeAdornmentFocus;
  1157   if (inIsDefault && !isDisabled && isActive &&
  1158       !inState.HasState(NS_EVENT_STATE_ACTIVE)) {
  1159     bdi.adornment |= kThemeAdornmentDefault;
  1160     bdi.animation.time.start = 0;
  1161     bdi.animation.time.current = CFAbsoluteTimeGetCurrent();
  1164   HIRect drawFrame = inBoxRect;
  1166   if (inKind == kThemePushButton) {
  1167     drawFrame.size.height -= 2;
  1168     if (inBoxRect.size.height < pushButtonSettings.naturalSizes[smallControlSize].height) {
  1169       bdi.kind = kThemePushButtonMini;
  1171     else if (inBoxRect.size.height < pushButtonSettings.naturalSizes[regularControlSize].height) {
  1172       bdi.kind = kThemePushButtonSmall;
  1173       drawFrame.origin.y -= 1;
  1174       drawFrame.origin.x += 1;
  1175       drawFrame.size.width -= 2;
  1178   else if (inKind == kThemeListHeaderButton) {
  1179     CGContextClipToRect(cgContext, inBoxRect);
  1180     // Always remove the top border.
  1181     drawFrame.origin.y -= 1;
  1182     drawFrame.size.height += 1;
  1183     // Remove the left border in LTR mode and the right border in RTL mode.
  1184     drawFrame.size.width += 1;
  1185     bool isLast = IsLastTreeHeaderCell(aFrame);
  1186     if (isLast)
  1187       drawFrame.size.width += 1; // Also remove the other border.
  1188     if (!IsFrameRTL(aFrame) || isLast)
  1189       drawFrame.origin.x -= 1;
  1192   RenderTransformedHIThemeControl(cgContext, drawFrame, RenderButton, &bdi,
  1193                                   IsFrameRTL(aFrame));
  1195 #if DRAW_IN_FRAME_DEBUG
  1196   CGContextSetRGBFillColor(cgContext, 0.0, 0.0, 0.5, 0.25);
  1197   CGContextFillRect(cgContext, inBoxRect);
  1198 #endif
  1200   NS_OBJC_END_TRY_ABORT_BLOCK;
  1203 static const CellRenderSettings dropdownSettings = {
  1205     NSMakeSize(0, 16), // mini
  1206     NSMakeSize(0, 19), // small
  1207     NSMakeSize(0, 22)  // regular
  1208   },
  1210     NSMakeSize(18, 0), // mini
  1211     NSMakeSize(38, 0), // small
  1212     NSMakeSize(44, 0)  // regular
  1213   },
  1215     { // Leopard
  1216       {1, 1, 2, 1},    // mini
  1217       {3, 0, 3, 1},    // small
  1218       {3, 0, 3, 0}     // regular
  1221 };
  1223 static const CellRenderSettings editableMenulistSettings = {
  1225     NSMakeSize(0, 15), // mini
  1226     NSMakeSize(0, 18), // small
  1227     NSMakeSize(0, 21)  // regular
  1228   },
  1230     NSMakeSize(18, 0), // mini
  1231     NSMakeSize(38, 0), // small
  1232     NSMakeSize(44, 0)  // regular
  1233   },
  1235     { // Leopard
  1236       {0, 0, 2, 2},    // mini
  1237       {0, 0, 3, 2},    // small
  1238       {0, 1, 3, 3}     // regular
  1241 };
  1243 void
  1244 nsNativeThemeCocoa::DrawDropdown(CGContextRef cgContext, const HIRect& inBoxRect,
  1245                                  EventStates inState, uint8_t aWidgetType,
  1246                                  nsIFrame* aFrame)
  1248   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1250   [mDropdownCell setPullsDown:(aWidgetType == NS_THEME_BUTTON)];
  1252   BOOL isEditable = (aWidgetType == NS_THEME_DROPDOWN_TEXTFIELD);
  1253   NSCell* cell = isEditable ? (NSCell*)mComboBoxCell : (NSCell*)mDropdownCell;
  1255   [cell setEnabled:!IsDisabled(aFrame, inState)];
  1256   [cell setShowsFirstResponder:(IsFocused(aFrame) || inState.HasState(NS_EVENT_STATE_FOCUS))];
  1257   [cell setHighlighted:IsOpenButton(aFrame)];
  1258   [cell setControlTint:(FrameIsInActiveWindow(aFrame) ? [NSColor currentControlTint] : NSClearControlTint)];
  1260   const CellRenderSettings& settings = isEditable ? editableMenulistSettings : dropdownSettings;
  1261   DrawCellWithSnapping(cell, cgContext, inBoxRect, settings,
  1262                        0.5f, mCellDrawView, IsFrameRTL(aFrame));
  1264   NS_OBJC_END_TRY_ABORT_BLOCK;
  1267 static const CellRenderSettings spinnerSettings = {
  1269     NSMakeSize(11, 16), // mini (width trimmed by 2px to reduce blank border)
  1270     NSMakeSize(15, 22), // small
  1271     NSMakeSize(19, 27)  // regular
  1272   },
  1274     NSMakeSize(11, 16), // mini (width trimmed by 2px to reduce blank border)
  1275     NSMakeSize(15, 22), // small
  1276     NSMakeSize(19, 27)  // regular
  1277   },
  1279     { // Leopard
  1280       {0, 0, 0, 0},    // mini
  1281       {0, 0, 0, 0},    // small
  1282       {0, 0, 0, 0}     // regular
  1285 };
  1287 void
  1288 nsNativeThemeCocoa::DrawSpinButtons(CGContextRef cgContext, ThemeButtonKind inKind,
  1289                                     const HIRect& inBoxRect, ThemeDrawState inDrawState,
  1290                                     ThemeButtonAdornment inAdornment,
  1291                                     EventStates inState, nsIFrame* aFrame)
  1293   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1295   HIThemeButtonDrawInfo bdi;
  1296   bdi.version = 0;
  1297   bdi.kind = inKind;
  1298   bdi.value = kThemeButtonOff;
  1299   bdi.adornment = inAdornment;
  1301   if (IsDisabled(aFrame, inState))
  1302     bdi.state = kThemeStateUnavailable;
  1303   else
  1304     bdi.state = FrameIsInActiveWindow(aFrame) ? inDrawState : kThemeStateActive;
  1306   HIThemeDrawButton(&inBoxRect, &bdi, cgContext, HITHEME_ORIENTATION, NULL);
  1308   NS_OBJC_END_TRY_ABORT_BLOCK;
  1311 void
  1312 nsNativeThemeCocoa::DrawSpinButton(CGContextRef cgContext,
  1313                                    ThemeButtonKind inKind,
  1314                                    const HIRect& inBoxRect,
  1315                                    ThemeDrawState inDrawState,
  1316                                    ThemeButtonAdornment inAdornment,
  1317                                    EventStates inState,
  1318                                    nsIFrame* aFrame,
  1319                                    uint8_t aWidgetType)
  1321   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1323   MOZ_ASSERT(aWidgetType == NS_THEME_SPINNER_UP_BUTTON ||
  1324              aWidgetType == NS_THEME_SPINNER_DOWN_BUTTON);
  1326   HIThemeButtonDrawInfo bdi;
  1327   bdi.version = 0;
  1328   bdi.kind = inKind;
  1329   bdi.value = kThemeButtonOff;
  1330   bdi.adornment = inAdornment;
  1332   if (IsDisabled(aFrame, inState))
  1333     bdi.state = kThemeStateUnavailable;
  1334   else
  1335     bdi.state = FrameIsInActiveWindow(aFrame) ? inDrawState : kThemeStateActive;
  1337   // Cocoa only allows kThemeIncDecButton to paint the up and down spin buttons
  1338   // together as a single unit (presumably because when one button is active,
  1339   // the appearance of both changes (in different ways)). Here we have to paint
  1340   // both buttons, using clip to hide the one we don't want to paint.
  1341   HIRect drawRect = inBoxRect;
  1342   drawRect.size.height *= 2;
  1343   if (aWidgetType == NS_THEME_SPINNER_DOWN_BUTTON) {
  1344     drawRect.origin.y -= inBoxRect.size.height;
  1347   // Shift the drawing a little to the left, since cocoa paints with more
  1348   // blank space around the visual buttons than we'd like:
  1349   drawRect.origin.x -= 1;
  1351   CGContextSaveGState(cgContext);
  1352   CGContextClipToRect(cgContext, inBoxRect);
  1354   HIThemeDrawButton(&drawRect, &bdi, cgContext, HITHEME_ORIENTATION, NULL);
  1356   CGContextRestoreGState(cgContext);
  1358   NS_OBJC_END_TRY_ABORT_BLOCK;
  1361 void
  1362 nsNativeThemeCocoa::DrawFrame(CGContextRef cgContext, HIThemeFrameKind inKind,
  1363                               const HIRect& inBoxRect, bool inDisabled,
  1364                               EventStates inState)
  1366   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1368   HIThemeFrameDrawInfo fdi;
  1369   fdi.version = 0;
  1370   fdi.kind = inKind;
  1372   // We don't ever set an inactive state for this because it doesn't
  1373   // look right (see other apps).
  1374   fdi.state = inDisabled ? kThemeStateUnavailable : kThemeStateActive;
  1376   // for some reason focus rings on listboxes draw incorrectly
  1377   if (inKind == kHIThemeFrameListBox)
  1378     fdi.isFocused = 0;
  1379   else
  1380     fdi.isFocused = inState.HasState(NS_EVENT_STATE_FOCUS);
  1382   // HIThemeDrawFrame takes the rect for the content area of the frame, not
  1383   // the bounding rect for the frame. Here we reduce the size of the rect we
  1384   // will pass to make it the size of the content.
  1385   HIRect drawRect = inBoxRect;
  1386   if (inKind == kHIThemeFrameTextFieldSquare) {
  1387     SInt32 frameOutset = 0;
  1388     ::GetThemeMetric(kThemeMetricEditTextFrameOutset, &frameOutset);
  1389     drawRect.origin.x += frameOutset;
  1390     drawRect.origin.y += frameOutset;
  1391     drawRect.size.width -= frameOutset * 2;
  1392     drawRect.size.height -= frameOutset * 2;
  1394   else if (inKind == kHIThemeFrameListBox) {
  1395     SInt32 frameOutset = 0;
  1396     ::GetThemeMetric(kThemeMetricListBoxFrameOutset, &frameOutset);
  1397     drawRect.origin.x += frameOutset;
  1398     drawRect.origin.y += frameOutset;
  1399     drawRect.size.width -= frameOutset * 2;
  1400     drawRect.size.height -= frameOutset * 2;
  1403 #if DRAW_IN_FRAME_DEBUG
  1404   CGContextSetRGBFillColor(cgContext, 0.0, 0.0, 0.5, 0.25);
  1405   CGContextFillRect(cgContext, inBoxRect);
  1406 #endif
  1408   HIThemeDrawFrame(&drawRect, &fdi, cgContext, HITHEME_ORIENTATION);
  1410   NS_OBJC_END_TRY_ABORT_BLOCK;
  1413 static const CellRenderSettings progressSettings[2][2] = {
  1414   // Vertical progress bar.
  1416     // Determined settings.
  1419         NSZeroSize, // mini
  1420         NSMakeSize(10, 0), // small
  1421         NSMakeSize(16, 0)  // regular
  1422       },
  1424         NSZeroSize, NSZeroSize, NSZeroSize
  1425       },
  1427         { // Leopard
  1428           {0, 0, 0, 0},     // mini
  1429           {1, 1, 1, 1},     // small
  1430           {1, 1, 1, 1}      // regular
  1433     },
  1434     // There is no horizontal margin in regular undetermined size.
  1437         NSZeroSize, // mini
  1438         NSMakeSize(10, 0), // small
  1439         NSMakeSize(16, 0)  // regular
  1440       },
  1442         NSZeroSize, NSZeroSize, NSZeroSize
  1443       },
  1445         { // Leopard
  1446           {0, 0, 0, 0},     // mini
  1447           {1, 1, 1, 1},     // small
  1448           {1, 0, 1, 0}      // regular
  1452   },
  1453   // Horizontal progress bar.
  1455     // Determined settings.
  1458         NSZeroSize, // mini
  1459         NSMakeSize(0, 10), // small
  1460         NSMakeSize(0, 16)  // regular
  1461       },
  1463         NSZeroSize, NSZeroSize, NSZeroSize
  1464       },
  1466         { // Leopard
  1467           {0, 0, 0, 0},     // mini
  1468           {1, 1, 1, 1},     // small
  1469           {1, 1, 1, 1}      // regular
  1472     },
  1473     // There is no horizontal margin in regular undetermined size.
  1476         NSZeroSize, // mini
  1477         NSMakeSize(0, 10), // small
  1478         NSMakeSize(0, 16)  // regular
  1479       },
  1481         NSZeroSize, NSZeroSize, NSZeroSize
  1482       },
  1484         { // Leopard
  1485           {0, 0, 0, 0},     // mini
  1486           {1, 1, 1, 1},     // small
  1487           {0, 1, 0, 1}      // regular
  1492 };
  1494 void
  1495 nsNativeThemeCocoa::DrawProgress(CGContextRef cgContext, const HIRect& inBoxRect,
  1496                                  bool inIsIndeterminate, bool inIsHorizontal,
  1497                                  double inValue, double inMaxValue,
  1498                                  nsIFrame* aFrame)
  1500   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1502   NSProgressBarCell* cell = mProgressBarCell;
  1504   [cell setValue:inValue];
  1505   [cell setMax:inMaxValue];
  1506   [cell setIndeterminate:inIsIndeterminate];
  1507   [cell setHorizontal:inIsHorizontal];
  1508   [cell setControlTint:(FrameIsInActiveWindow(aFrame) ? [NSColor currentControlTint]
  1509                                                       : NSClearControlTint)];
  1511   DrawCellWithSnapping(cell, cgContext, inBoxRect,
  1512                        progressSettings[inIsHorizontal][inIsIndeterminate],
  1513                        VerticalAlignFactor(aFrame), mCellDrawView,
  1514                        IsFrameRTL(aFrame));
  1516   NS_OBJC_END_TRY_ABORT_BLOCK;
  1519 static const CellRenderSettings meterSetting = {
  1521     NSMakeSize(0, 16), // mini
  1522     NSMakeSize(0, 16), // small
  1523     NSMakeSize(0, 16)  // regular
  1524   },
  1526     NSZeroSize, NSZeroSize, NSZeroSize
  1527   },
  1529     { // Leopard
  1530       {1, 1, 1, 1},     // mini
  1531       {1, 1, 1, 1},     // small
  1532       {1, 1, 1, 1}      // regular
  1535 };
  1537 void
  1538 nsNativeThemeCocoa::DrawMeter(CGContextRef cgContext, const HIRect& inBoxRect,
  1539                               nsIFrame* aFrame)
  1541   NS_OBJC_BEGIN_TRY_ABORT_BLOCK
  1543   NS_PRECONDITION(aFrame, "aFrame should not be null here!");
  1545   // When using -moz-meterbar on an non meter element, we will not be able to
  1546   // get all the needed information so we just draw an empty meter.
  1547   nsIContent* content = aFrame->GetContent();
  1548   if (!(content && content->IsHTML(nsGkAtoms::meter))) {
  1549     DrawCellWithSnapping(mMeterBarCell, cgContext, inBoxRect,
  1550                          meterSetting, VerticalAlignFactor(aFrame),
  1551                          mCellDrawView, IsFrameRTL(aFrame));
  1552     return;
  1555   HTMLMeterElement* meterElement = static_cast<HTMLMeterElement*>(content);
  1556   double value = meterElement->Value();
  1557   double min = meterElement->Min();
  1558   double max = meterElement->Max();
  1560   NSLevelIndicatorCell* cell = mMeterBarCell;
  1562   [cell setMinValue:min];
  1563   [cell setMaxValue:max];
  1564   [cell setDoubleValue:value];
  1566   /**
  1567    * The way HTML and Cocoa defines the meter/indicator widget are different.
  1568    * So, we are going to use a trick to get the Cocoa widget showing what we
  1569    * are expecting: we set the warningValue or criticalValue to the current
  1570    * value when we want to have the widget to be in the warning or critical
  1571    * state.
  1572    */
  1573   EventStates states = aFrame->GetContent()->AsElement()->State();
  1575   // Reset previously set warning and critical values.
  1576   [cell setWarningValue:max+1];
  1577   [cell setCriticalValue:max+1];
  1579   if (states.HasState(NS_EVENT_STATE_SUB_OPTIMUM)) {
  1580     [cell setWarningValue:value];
  1581   } else if (states.HasState(NS_EVENT_STATE_SUB_SUB_OPTIMUM)) {
  1582     [cell setCriticalValue:value];
  1585   HIRect rect = CGRectStandardize(inBoxRect);
  1586   BOOL vertical = IsVerticalMeter(aFrame);
  1588   CGContextSaveGState(cgContext);
  1590   if (vertical) {
  1591     /**
  1592      * Cocoa doesn't provide a vertical meter bar so to show one, we have to
  1593      * show a rotated horizontal meter bar.
  1594      * Given that we want to show a vertical meter bar, we assume that the rect
  1595      * has vertical dimensions but we can't correctly draw a meter widget inside
  1596      * such a rectangle so we need to inverse width and height (and re-position)
  1597      * to get a rectangle with horizontal dimensions.
  1598      * Finally, we want to show a vertical meter so we want to rotate the result
  1599      * so it is vertical. We do that by changing the context.
  1600      */
  1601     CGFloat tmp = rect.size.width;
  1602     rect.size.width = rect.size.height;
  1603     rect.size.height = tmp;
  1604     rect.origin.x += rect.size.height / 2.f - rect.size.width / 2.f;
  1605     rect.origin.y += rect.size.width / 2.f - rect.size.height / 2.f;
  1607     CGContextTranslateCTM(cgContext, CGRectGetMidX(rect), CGRectGetMidY(rect));
  1608     CGContextRotateCTM(cgContext, -M_PI / 2.f);
  1609     CGContextTranslateCTM(cgContext, -CGRectGetMidX(rect), -CGRectGetMidY(rect));
  1612   DrawCellWithSnapping(cell, cgContext, rect,
  1613                        meterSetting, VerticalAlignFactor(aFrame),
  1614                        mCellDrawView, !vertical && IsFrameRTL(aFrame));
  1616   CGContextRestoreGState(cgContext);
  1618   NS_OBJC_END_TRY_ABORT_BLOCK
  1621 void
  1622 nsNativeThemeCocoa::DrawTabPanel(CGContextRef cgContext, const HIRect& inBoxRect,
  1623                                  nsIFrame* aFrame)
  1625   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1627   HIThemeTabPaneDrawInfo tpdi;
  1629   tpdi.version = 1;
  1630   tpdi.state = FrameIsInActiveWindow(aFrame) ? kThemeStateActive : kThemeStateInactive;
  1631   tpdi.direction = kThemeTabNorth;
  1632   tpdi.size = kHIThemeTabSizeNormal;
  1633   tpdi.kind = kHIThemeTabKindNormal;
  1635   HIThemeDrawTabPane(&inBoxRect, &tpdi, cgContext, HITHEME_ORIENTATION);
  1637   NS_OBJC_END_TRY_ABORT_BLOCK;
  1640 void
  1641 nsNativeThemeCocoa::DrawScale(CGContextRef cgContext, const HIRect& inBoxRect,
  1642                               EventStates inState, bool inIsVertical,
  1643                               bool inIsReverse, int32_t inCurrentValue,
  1644                               int32_t inMinValue, int32_t inMaxValue,
  1645                               nsIFrame* aFrame)
  1647   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1649   HIThemeTrackDrawInfo tdi;
  1651   tdi.version = 0;
  1652   tdi.kind = kThemeMediumSlider;
  1653   tdi.bounds = inBoxRect;
  1654   tdi.min = inMinValue;
  1655   tdi.max = inMaxValue;
  1656   tdi.value = inCurrentValue;
  1657   tdi.attributes = kThemeTrackShowThumb;
  1658   if (!inIsVertical)
  1659     tdi.attributes |= kThemeTrackHorizontal;
  1660   if (inIsReverse)
  1661     tdi.attributes |= kThemeTrackRightToLeft;
  1662   if (inState.HasState(NS_EVENT_STATE_FOCUS))
  1663     tdi.attributes |= kThemeTrackHasFocus;
  1664   if (IsDisabled(aFrame, inState))
  1665     tdi.enableState = kThemeTrackDisabled;
  1666   else
  1667     tdi.enableState = FrameIsInActiveWindow(aFrame) ? kThemeTrackActive : kThemeTrackInactive;
  1668   tdi.trackInfo.slider.thumbDir = kThemeThumbPlain;
  1669   tdi.trackInfo.slider.pressState = 0;
  1671   HIThemeDrawTrack(&tdi, NULL, cgContext, HITHEME_ORIENTATION);
  1673   NS_OBJC_END_TRY_ABORT_BLOCK;
  1676 nsIFrame*
  1677 nsNativeThemeCocoa::SeparatorResponsibility(nsIFrame* aBefore, nsIFrame* aAfter)
  1679   // Usually a separator is drawn by the segment to the right of the
  1680   // separator, but pressed and selected segments have higher priority.
  1681   if (!aBefore || !aAfter)
  1682     return nullptr;
  1683   if (IsSelectedButton(aAfter))
  1684     return aAfter;
  1685   if (IsSelectedButton(aBefore) || IsPressedButton(aBefore))
  1686     return aBefore;
  1687   return aAfter;
  1690 CGRect
  1691 nsNativeThemeCocoa::SeparatorAdjustedRect(CGRect aRect, nsIFrame* aLeft,
  1692                                           nsIFrame* aCurrent, nsIFrame* aRight)
  1694   // A separator between two segments should always be located in the leftmost
  1695   // pixel column of the segment to the right of the separator, regardless of
  1696   // who ends up drawing it.
  1697   // CoreUI draws the separators inside the drawing rect.
  1698   if (aLeft && SeparatorResponsibility(aLeft, aCurrent) == aLeft) {
  1699     // The left button draws the separator, so we need to make room for it.
  1700     aRect.origin.x += 1;
  1701     aRect.size.width -= 1;
  1703   if (SeparatorResponsibility(aCurrent, aRight) == aCurrent) {
  1704     // We draw the right separator, so we need to extend the draw rect into the
  1705     // segment to our right.
  1706     aRect.size.width += 1;
  1708   return aRect;
  1711 static NSString* ToolbarButtonPosition(BOOL aIsFirst, BOOL aIsLast)
  1713   if (aIsFirst) {
  1714     if (aIsLast)
  1715       return @"kCUISegmentPositionOnly";
  1716     return @"kCUISegmentPositionFirst";
  1718   if (aIsLast)
  1719     return @"kCUISegmentPositionLast";
  1720   return @"kCUISegmentPositionMiddle";
  1723 struct SegmentedControlRenderSettings {
  1724   const CGFloat* heights;
  1725   const NSString* widgetName;
  1726   const BOOL ignoresPressedWhenSelected;
  1727   const BOOL isToolbarControl;
  1728 };
  1730 static const CGFloat tabHeights[3] = { 17, 20, 23 };
  1732 static const SegmentedControlRenderSettings tabRenderSettings = {
  1733   tabHeights, @"tab", YES, NO
  1734 };
  1736 static const CGFloat toolbarButtonHeights[3] = { 15, 18, 22 };
  1738 static const SegmentedControlRenderSettings toolbarButtonRenderSettings = {
  1739   toolbarButtonHeights, @"kCUIWidgetButtonSegmentedSCurve", NO, YES
  1740 };
  1742 void
  1743 nsNativeThemeCocoa::DrawSegment(CGContextRef cgContext, const HIRect& inBoxRect,
  1744                                 EventStates inState, nsIFrame* aFrame,
  1745                                 const SegmentedControlRenderSettings& aSettings)
  1747   BOOL isActive = IsActive(aFrame, aSettings.isToolbarControl);
  1748   BOOL isFocused = inState.HasState(NS_EVENT_STATE_FOCUS);
  1749   BOOL isSelected = IsSelectedButton(aFrame);
  1750   BOOL isPressed = IsPressedButton(aFrame);
  1751   if (isSelected && aSettings.ignoresPressedWhenSelected) {
  1752     isPressed = NO;
  1755   BOOL isRTL = IsFrameRTL(aFrame);
  1756   nsIFrame* left = GetAdjacentSiblingFrameWithSameAppearance(aFrame, isRTL);
  1757   nsIFrame* right = GetAdjacentSiblingFrameWithSameAppearance(aFrame, !isRTL);
  1758   CGRect drawRect = SeparatorAdjustedRect(inBoxRect, left, aFrame, right);
  1759   if (drawRect.size.width * drawRect.size.height > CUIDRAW_MAX_AREA) {
  1760     return;
  1762   BOOL drawLeftSeparator = SeparatorResponsibility(left, aFrame) == aFrame;
  1763   BOOL drawRightSeparator = SeparatorResponsibility(aFrame, right) == aFrame;
  1764   NSControlSize controlSize = FindControlSize(drawRect.size.height, aSettings.heights, 4.0f);
  1766   CUIDraw([NSWindow coreUIRenderer], drawRect, cgContext,
  1767           (CFDictionaryRef)[NSDictionary dictionaryWithObjectsAndKeys:
  1768             aSettings.widgetName, @"widget",
  1769             ToolbarButtonPosition(!left, !right), @"kCUIPositionKey",
  1770             [NSNumber numberWithBool:drawLeftSeparator], @"kCUISegmentLeadingSeparatorKey",
  1771             [NSNumber numberWithBool:drawRightSeparator], @"kCUISegmentTrailingSeparatorKey",
  1772             [NSNumber numberWithBool:isSelected], @"value",
  1773             (isPressed ? @"pressed" : (isActive ? @"normal" : @"inactive")), @"state",
  1774             [NSNumber numberWithBool:isFocused], @"focus",
  1775             CUIControlSizeForCocoaSize(controlSize), @"size",
  1776             [NSNumber numberWithBool:YES], @"is.flipped",
  1777             @"up", @"direction",
  1778             nil],
  1779           nil);
  1782 static inline UInt8
  1783 ConvertToPressState(EventStates aButtonState, UInt8 aPressState)
  1785   // If the button is pressed, return the press state passed in. Otherwise, return 0.
  1786   return aButtonState.HasAllStates(NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_HOVER) ? aPressState : 0;
  1789 void 
  1790 nsNativeThemeCocoa::GetScrollbarPressStates(nsIFrame* aFrame,
  1791                                             EventStates aButtonStates[])
  1793   static nsIContent::AttrValuesArray attributeValues[] = {
  1794     &nsGkAtoms::scrollbarUpTop,
  1795     &nsGkAtoms::scrollbarDownTop,
  1796     &nsGkAtoms::scrollbarUpBottom,
  1797     &nsGkAtoms::scrollbarDownBottom,
  1798     nullptr
  1799   };
  1801   // Get the state of any scrollbar buttons in our child frames
  1802   for (nsIFrame *childFrame = aFrame->GetFirstPrincipalChild(); 
  1803        childFrame;
  1804        childFrame = childFrame->GetNextSibling()) {
  1806     nsIContent *childContent = childFrame->GetContent();
  1807     if (!childContent) continue;
  1808     int32_t attrIndex = childContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::sbattr, 
  1809                                                       attributeValues, eCaseMatters);
  1810     if (attrIndex < 0) continue;
  1812     aButtonStates[attrIndex] = GetContentState(childFrame, NS_THEME_BUTTON);
  1816 // Both of the following sets of numbers were derived by loading the testcase in
  1817 // bmo bug 380185 in Safari and observing its behavior for various heights of scrollbar.
  1818 // These magic numbers are the minimum sizes we can draw a scrollbar and still 
  1819 // have room for everything to display, including the thumb
  1820 #define MIN_SCROLLBAR_SIZE_WITH_THUMB 61
  1821 #define MIN_SMALL_SCROLLBAR_SIZE_WITH_THUMB 49
  1822 // And these are the minimum sizes if we don't draw the thumb
  1823 #define MIN_SCROLLBAR_SIZE 56
  1824 #define MIN_SMALL_SCROLLBAR_SIZE 46
  1826 void
  1827 nsNativeThemeCocoa::GetScrollbarDrawInfo(HIThemeTrackDrawInfo& aTdi, nsIFrame *aFrame, 
  1828                                          const CGSize& aSize, bool aShouldGetButtonStates)
  1830   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1832   int32_t curpos = CheckIntAttr(aFrame, nsGkAtoms::curpos, 0);
  1833   int32_t minpos = CheckIntAttr(aFrame, nsGkAtoms::minpos, 0);
  1834   int32_t maxpos = CheckIntAttr(aFrame, nsGkAtoms::maxpos, 100);
  1835   int32_t thumbSize = CheckIntAttr(aFrame, nsGkAtoms::pageincrement, 10);
  1837   bool isHorizontal = aFrame->GetContent()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::orient, 
  1838                                                           nsGkAtoms::horizontal, eCaseMatters);
  1839   bool isSmall = aFrame->StyleDisplay()->mAppearance == NS_THEME_SCROLLBAR_SMALL;
  1841   aTdi.version = 0;
  1842   aTdi.kind = isSmall ? kThemeSmallScrollBar : kThemeMediumScrollBar;
  1843   aTdi.bounds.origin = CGPointZero;
  1844   aTdi.bounds.size = aSize;
  1845   aTdi.min = minpos;
  1846   aTdi.max = maxpos;
  1847   aTdi.value = curpos;
  1848   aTdi.attributes = 0;
  1849   aTdi.enableState = kThemeTrackActive;
  1850   if (isHorizontal)
  1851     aTdi.attributes |= kThemeTrackHorizontal;
  1853   aTdi.trackInfo.scrollbar.viewsize = (SInt32)thumbSize;
  1855   // This should be done early on so things like "kThemeTrackNothingToScroll" can
  1856   // override the active enable state.
  1857   aTdi.enableState = FrameIsInActiveWindow(aFrame) ? kThemeTrackActive : kThemeTrackInactive;
  1859   /* Only display features if we have enough room for them.
  1860    * Gecko still maintains the scrollbar info; this is just a visual issue (bug 380185).
  1861    */
  1862   int32_t longSideLength = (int32_t)(isHorizontal ? (aSize.width) : (aSize.height));
  1863   if (longSideLength >= (isSmall ? MIN_SMALL_SCROLLBAR_SIZE_WITH_THUMB : MIN_SCROLLBAR_SIZE_WITH_THUMB)) {
  1864     aTdi.attributes |= kThemeTrackShowThumb;
  1866   else if (longSideLength < (isSmall ? MIN_SMALL_SCROLLBAR_SIZE : MIN_SCROLLBAR_SIZE)) {
  1867     aTdi.enableState = kThemeTrackNothingToScroll;
  1868     return;
  1871   aTdi.trackInfo.scrollbar.pressState = 0;
  1873   // Only go get these scrollbar button states if we need it. For example,
  1874   // there's no reason to look up scrollbar button states when we're only
  1875   // creating a TrackDrawInfo to determine the size of the thumb. There's
  1876   // also no reason to do this on Lion or later, whose scrollbars have no
  1877   // arrow buttons.
  1878   if (aShouldGetButtonStates && !nsCocoaFeatures::OnLionOrLater()) {
  1879     EventStates buttonStates[4];
  1880     GetScrollbarPressStates(aFrame, buttonStates);
  1881     NSString *buttonPlacement = [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleScrollBarVariant"];
  1882     // It seems that unless all four buttons are showing, kThemeTopOutsideArrowPressed is the correct constant for
  1883     // the up scrollbar button.
  1884     if ([buttonPlacement isEqualToString:@"DoubleBoth"]) {
  1885       aTdi.trackInfo.scrollbar.pressState = ConvertToPressState(buttonStates[0], kThemeTopOutsideArrowPressed) |
  1886                                             ConvertToPressState(buttonStates[1], kThemeTopInsideArrowPressed) |
  1887                                             ConvertToPressState(buttonStates[2], kThemeBottomInsideArrowPressed) |
  1888                                             ConvertToPressState(buttonStates[3], kThemeBottomOutsideArrowPressed);
  1889     } else {
  1890       aTdi.trackInfo.scrollbar.pressState = ConvertToPressState(buttonStates[0], kThemeTopOutsideArrowPressed) |
  1891                                             ConvertToPressState(buttonStates[1], kThemeBottomOutsideArrowPressed) |
  1892                                             ConvertToPressState(buttonStates[2], kThemeTopOutsideArrowPressed) |
  1893                                             ConvertToPressState(buttonStates[3], kThemeBottomOutsideArrowPressed);
  1897   NS_OBJC_END_TRY_ABORT_BLOCK;
  1900 static void
  1901 RenderScrollbar(CGContextRef cgContext, const HIRect& aRenderRect, void* aData)
  1903   HIThemeTrackDrawInfo* tdi = (HIThemeTrackDrawInfo*)aData;
  1904   HIThemeDrawTrack(tdi, NULL, cgContext, HITHEME_ORIENTATION);
  1907 void
  1908 nsNativeThemeCocoa::DrawScrollbar(CGContextRef aCGContext, const HIRect& aBoxRect, nsIFrame *aFrame)
  1910   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1912   HIThemeTrackDrawInfo tdi;
  1913   GetScrollbarDrawInfo(tdi, aFrame, aBoxRect.size, true); // True means we want the press states
  1914   RenderTransformedHIThemeControl(aCGContext, aBoxRect, RenderScrollbar, &tdi);
  1916   NS_OBJC_END_TRY_ABORT_BLOCK;
  1919 nsIFrame*
  1920 nsNativeThemeCocoa::GetParentScrollbarFrame(nsIFrame *aFrame)
  1922   // Walk our parents to find a scrollbar frame
  1923   nsIFrame *scrollbarFrame = aFrame;
  1924   do {
  1925     if (scrollbarFrame->GetType() == nsGkAtoms::scrollbarFrame) break;
  1926   } while ((scrollbarFrame = scrollbarFrame->GetParent()));
  1928   // We return null if we can't find a parent scrollbar frame
  1929   return scrollbarFrame;
  1932 static bool
  1933 ToolbarCanBeUnified(CGContextRef cgContext, const HIRect& inBoxRect, NSWindow* aWindow)
  1935   if (![aWindow isKindOfClass:[ToolbarWindow class]])
  1936     return false;
  1938   ToolbarWindow* win = (ToolbarWindow*)aWindow;
  1939   float unifiedToolbarHeight = [win unifiedToolbarHeight];
  1940   return inBoxRect.origin.x == 0 &&
  1941          inBoxRect.size.width >= [win frame].size.width &&
  1942          CGRectGetMaxY(inBoxRect) <= unifiedToolbarHeight;
  1945 // By default, kCUIWidgetWindowFrame drawing draws rounded corners in the
  1946 // upper corners. Depending on the context type, it fills the background in
  1947 // the corners with black or leaves it transparent. Unfortunately, this corner
  1948 // rounding interacts poorly with the window corner masking we apply during
  1949 // titlebar drawing and results in small remnants of the corner background
  1950 // appearing at the rounded edge.
  1951 // So we draw square corners.
  1952 static void
  1953 DrawNativeTitlebarToolbarWithSquareCorners(CGContextRef aContext, const CGRect& aRect,
  1954                                            CGFloat aUnifiedHeight, BOOL aIsMain)
  1956   // We extend the draw rect horizontally and clip away the rounded corners.
  1957   const CGFloat extendHorizontal = 10;
  1958   CGRect drawRect = CGRectInset(aRect, -extendHorizontal, 0);
  1959   if (drawRect.size.width * drawRect.size.height <= CUIDRAW_MAX_AREA) {
  1960     CGContextSaveGState(aContext);
  1961     CGContextClipToRect(aContext, aRect);
  1963     CUIDraw([NSWindow coreUIRenderer], drawRect, aContext,
  1964             (CFDictionaryRef)[NSDictionary dictionaryWithObjectsAndKeys:
  1965               @"kCUIWidgetWindowFrame", @"widget",
  1966               @"regularwin", @"windowtype",
  1967               (aIsMain ? @"normal" : @"inactive"), @"state",
  1968               [NSNumber numberWithDouble:aUnifiedHeight], @"kCUIWindowFrameUnifiedTitleBarHeightKey",
  1969               [NSNumber numberWithBool:YES], @"kCUIWindowFrameDrawTitleSeparatorKey",
  1970               [NSNumber numberWithBool:YES], @"is.flipped",
  1971               nil],
  1972             nil);
  1974     CGContextRestoreGState(aContext);
  1978 void
  1979 nsNativeThemeCocoa::DrawUnifiedToolbar(CGContextRef cgContext, const HIRect& inBoxRect,
  1980                                        NSWindow* aWindow)
  1982   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  1984   CGContextSaveGState(cgContext);
  1985   CGContextClipToRect(cgContext, inBoxRect);
  1987   CGFloat unifiedHeight = std::max([(ToolbarWindow*)aWindow unifiedToolbarHeight],
  1988                                    inBoxRect.size.height);
  1989   BOOL isMain = [aWindow isMainWindow];
  1990   CGFloat titlebarHeight = unifiedHeight - inBoxRect.size.height;
  1991   CGRect drawRect = CGRectMake(inBoxRect.origin.x, inBoxRect.origin.y - titlebarHeight,
  1992                                inBoxRect.size.width, inBoxRect.size.height + titlebarHeight);
  1993   DrawNativeTitlebarToolbarWithSquareCorners(cgContext, drawRect, unifiedHeight, isMain);
  1995   CGContextRestoreGState(cgContext);
  1997   NS_OBJC_END_TRY_ABORT_BLOCK;
  2000 void
  2001 nsNativeThemeCocoa::DrawStatusBar(CGContextRef cgContext, const HIRect& inBoxRect,
  2002                                   nsIFrame *aFrame)
  2004   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  2006   if (inBoxRect.size.height < 2.0f)
  2007     return;
  2009   CGContextSaveGState(cgContext);
  2010   CGContextClipToRect(cgContext, inBoxRect);
  2012   // kCUIWidgetWindowFrame draws a complete window frame with both title bar
  2013   // and bottom bar. We only want the bottom bar, so we extend the draw rect
  2014   // upwards to make space for the title bar, and then we clip it away.
  2015   CGRect drawRect = inBoxRect;
  2016   const int extendUpwards = 40;
  2017   drawRect.origin.y -= extendUpwards;
  2018   drawRect.size.height += extendUpwards;
  2019   if (drawRect.size.width * drawRect.size.height <= CUIDRAW_MAX_AREA) {
  2020     CUIDraw([NSWindow coreUIRenderer], drawRect, cgContext,
  2021             (CFDictionaryRef)[NSDictionary dictionaryWithObjectsAndKeys:
  2022               @"kCUIWidgetWindowFrame", @"widget",
  2023               @"regularwin", @"windowtype",
  2024               (IsActive(aFrame, YES) ? @"normal" : @"inactive"), @"state",
  2025               [NSNumber numberWithInt:inBoxRect.size.height], @"kCUIWindowFrameBottomBarHeightKey",
  2026               [NSNumber numberWithBool:YES], @"kCUIWindowFrameDrawBottomBarSeparatorKey",
  2027               [NSNumber numberWithBool:YES], @"is.flipped",
  2028               nil],
  2029             nil);
  2032   CGContextRestoreGState(cgContext);
  2034   NS_OBJC_END_TRY_ABORT_BLOCK;
  2037 void
  2038 nsNativeThemeCocoa::DrawNativeTitlebar(CGContextRef aContext, CGRect aTitlebarRect,
  2039                                        CGFloat aUnifiedHeight, BOOL aIsMain)
  2041   CGFloat unifiedHeight = std::max(aUnifiedHeight, aTitlebarRect.size.height);
  2042   DrawNativeTitlebarToolbarWithSquareCorners(aContext, aTitlebarRect, unifiedHeight, aIsMain);
  2045 static void
  2046 RenderResizer(CGContextRef cgContext, const HIRect& aRenderRect, void* aData)
  2048   HIThemeGrowBoxDrawInfo* drawInfo = (HIThemeGrowBoxDrawInfo*)aData;
  2049   HIThemeDrawGrowBox(&CGPointZero, drawInfo, cgContext, kHIThemeOrientationNormal);
  2052 void
  2053 nsNativeThemeCocoa::DrawResizer(CGContextRef cgContext, const HIRect& aRect,
  2054                                 nsIFrame *aFrame)
  2056   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
  2058   HIThemeGrowBoxDrawInfo drawInfo;
  2059   drawInfo.version = 0;
  2060   drawInfo.state = kThemeStateActive;
  2061   drawInfo.kind = kHIThemeGrowBoxKindNormal;
  2062   drawInfo.direction = kThemeGrowRight | kThemeGrowDown;
  2063   drawInfo.size = kHIThemeGrowBoxSizeNormal;
  2065   RenderTransformedHIThemeControl(cgContext, aRect, RenderResizer, &drawInfo,
  2066                                   IsFrameRTL(aFrame));
  2068   NS_OBJC_END_TRY_ABORT_BLOCK;
  2071 static bool
  2072 IsHiDPIContext(nsDeviceContext* aContext)
  2074   return nsPresContext::AppUnitsPerCSSPixel() >=
  2075     2 * aContext->UnscaledAppUnitsPerDevPixel();
  2078 NS_IMETHODIMP
  2079 nsNativeThemeCocoa::DrawWidgetBackground(nsRenderingContext* aContext,
  2080                                          nsIFrame* aFrame,
  2081                                          uint8_t aWidgetType,
  2082                                          const nsRect& aRect,
  2083                                          const nsRect& aDirtyRect)
  2085   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  2087   // setup to draw into the correct port
  2088   int32_t p2a = aContext->AppUnitsPerDevPixel();
  2090   gfxRect nativeDirtyRect(aDirtyRect.x, aDirtyRect.y,
  2091                           aDirtyRect.width, aDirtyRect.height);
  2092   gfxRect nativeWidgetRect(aRect.x, aRect.y, aRect.width, aRect.height);
  2093   nativeWidgetRect.ScaleInverse(gfxFloat(p2a));
  2094   nativeDirtyRect.ScaleInverse(gfxFloat(p2a));
  2095   nativeWidgetRect.Round();
  2096   if (nativeWidgetRect.IsEmpty())
  2097     return NS_OK; // Don't attempt to draw invisible widgets.
  2099   gfxContext* thebesCtx = aContext->ThebesContext();
  2100   if (!thebesCtx)
  2101     return NS_ERROR_FAILURE;
  2103   gfxContextMatrixAutoSaveRestore save(thebesCtx);
  2105   bool hidpi = IsHiDPIContext(aContext->DeviceContext());
  2106   if (hidpi) {
  2107     // Use high-resolution drawing.
  2108     nativeWidgetRect.ScaleInverse(2.0f);
  2109     nativeDirtyRect.ScaleInverse(2.0f);
  2110     thebesCtx->Scale(2.0f, 2.0f);
  2113   gfxQuartzNativeDrawing nativeDrawing(thebesCtx, nativeDirtyRect,
  2114                                        hidpi ? 2.0f : 1.0f);
  2116   CGContextRef cgContext = nativeDrawing.BeginNativeDrawing();
  2117   if (cgContext == nullptr) {
  2118     // The Quartz surface handles 0x0 surfaces by internally
  2119     // making all operations no-ops; there's no cgcontext created for them.
  2120     // Unfortunately, this means that callers that want to render
  2121     // directly to the CGContext need to be aware of this quirk.
  2122     return NS_OK;
  2125 #if 0
  2126   if (1 /*aWidgetType == NS_THEME_TEXTFIELD*/) {
  2127     fprintf(stderr, "Native theme drawing widget %d [%p] dis:%d in rect [%d %d %d %d]\n",
  2128             aWidgetType, aFrame, IsDisabled(aFrame), aRect.x, aRect.y, aRect.width, aRect.height);
  2129     fprintf(stderr, "Cairo matrix: [%f %f %f %f %f %f]\n",
  2130             mat.xx, mat.yx, mat.xy, mat.yy, mat.x0, mat.y0);
  2131     fprintf(stderr, "Native theme xform[0]: [%f %f %f %f %f %f]\n",
  2132             mm0.a, mm0.b, mm0.c, mm0.d, mm0.tx, mm0.ty);
  2133     CGAffineTransform mm = CGContextGetCTM(cgContext);
  2134     fprintf(stderr, "Native theme xform[1]: [%f %f %f %f %f %f]\n",
  2135             mm.a, mm.b, mm.c, mm.d, mm.tx, mm.ty);
  2137 #endif
  2139   CGRect macRect = CGRectMake(nativeWidgetRect.X(), nativeWidgetRect.Y(),
  2140                               nativeWidgetRect.Width(), nativeWidgetRect.Height());
  2142 #if 0
  2143   fprintf(stderr, "    --> macRect %f %f %f %f\n",
  2144           macRect.origin.x, macRect.origin.y, macRect.size.width, macRect.size.height);
  2145   CGRect bounds = CGContextGetClipBoundingBox(cgContext);
  2146   fprintf(stderr, "    --> clip bounds: %f %f %f %f\n",
  2147           bounds.origin.x, bounds.origin.y, bounds.size.width, bounds.size.height);
  2149   //CGContextSetRGBFillColor(cgContext, 0.0, 0.0, 1.0, 0.1);
  2150   //CGContextFillRect(cgContext, bounds);
  2151 #endif
  2153   EventStates eventState = GetContentState(aFrame, aWidgetType);
  2155   switch (aWidgetType) {
  2156     case NS_THEME_DIALOG: {
  2157       HIThemeSetFill(kThemeBrushDialogBackgroundActive, NULL, cgContext, HITHEME_ORIENTATION);
  2158       CGContextFillRect(cgContext, macRect);
  2160       break;
  2162     case NS_THEME_MENUPOPUP: {
  2163       HIThemeMenuDrawInfo mdi;
  2164       memset(&mdi, 0, sizeof(mdi));
  2165       mdi.version = 0;
  2166       mdi.menuType = IsDisabled(aFrame, eventState) ?
  2167                        static_cast<ThemeMenuType>(kThemeMenuTypeInactive) :
  2168                        static_cast<ThemeMenuType>(kThemeMenuTypePopUp);
  2170       bool isLeftOfParent = false;
  2171       if (IsSubmenu(aFrame, &isLeftOfParent) && !isLeftOfParent) {
  2172         mdi.menuType = kThemeMenuTypeHierarchical;
  2175       // The rounded corners draw outside the frame.
  2176       CGRect deflatedRect = CGRectMake(macRect.origin.x, macRect.origin.y + 4,
  2177                                        macRect.size.width, macRect.size.height - 8);
  2178       HIThemeDrawMenuBackground(&deflatedRect, &mdi, cgContext, HITHEME_ORIENTATION);
  2180       break;
  2182     case NS_THEME_MENUITEM: {
  2183       bool isTransparent;
  2184       if (thebesCtx->IsCairo()) {
  2185         isTransparent = thebesCtx->OriginalSurface()->GetContentType() == gfxContentType::COLOR_ALPHA;
  2186       } else {
  2187         SurfaceFormat format  = thebesCtx->GetDrawTarget()->GetFormat();
  2188         isTransparent = (format == SurfaceFormat::R8G8B8A8) ||
  2189                         (format == SurfaceFormat::B8G8R8A8);
  2191       if (isTransparent) {
  2192         // Clear the background to get correct transparency.
  2193         CGContextClearRect(cgContext, macRect);
  2196       // maybe use kThemeMenuItemHierBackground or PopUpBackground instead of just Plain?
  2197       HIThemeMenuItemDrawInfo drawInfo;
  2198       memset(&drawInfo, 0, sizeof(drawInfo));
  2199       drawInfo.version = 0;
  2200       drawInfo.itemType = kThemeMenuItemPlain;
  2201       drawInfo.state = (IsDisabled(aFrame, eventState) ?
  2202                          static_cast<ThemeMenuState>(kThemeMenuDisabled) :
  2203                          CheckBooleanAttr(aFrame, nsGkAtoms::menuactive) ?
  2204                            static_cast<ThemeMenuState>(kThemeMenuSelected) :
  2205                            static_cast<ThemeMenuState>(kThemeMenuActive));
  2207       // XXX pass in the menu rect instead of always using the item rect
  2208       HIRect ignored;
  2209       HIThemeDrawMenuItem(&macRect, &macRect, &drawInfo, cgContext, HITHEME_ORIENTATION, &ignored);
  2211       break;
  2213     case NS_THEME_MENUSEPARATOR: {
  2214       ThemeMenuState menuState;
  2215       if (IsDisabled(aFrame, eventState)) {
  2216         menuState = kThemeMenuDisabled;
  2218       else {
  2219         menuState = CheckBooleanAttr(aFrame, nsGkAtoms::menuactive) ?
  2220                     kThemeMenuSelected : kThemeMenuActive;
  2223       HIThemeMenuItemDrawInfo midi = { 0, kThemeMenuItemPlain, menuState };
  2224       HIThemeDrawMenuSeparator(&macRect, &macRect, &midi, cgContext, HITHEME_ORIENTATION);
  2226       break;
  2228     case NS_THEME_TOOLTIP:
  2229       CGContextSetRGBFillColor(cgContext, 0.996, 1.000, 0.792, 0.950);
  2230       CGContextFillRect(cgContext, macRect);
  2231       break;
  2233     case NS_THEME_CHECKBOX:
  2234     case NS_THEME_RADIO: {
  2235       bool isCheckbox = (aWidgetType == NS_THEME_CHECKBOX);
  2236       DrawCheckboxOrRadio(cgContext, isCheckbox, macRect, GetCheckedOrSelected(aFrame, !isCheckbox),
  2237                           eventState, aFrame);
  2239       break;
  2241     case NS_THEME_BUTTON:
  2242       if (IsDefaultButton(aFrame)) {
  2243         if (!IsDisabled(aFrame, eventState) && FrameIsInActiveWindow(aFrame) &&
  2244             !QueueAnimatedContentForRefresh(aFrame->GetContent(), 10)) {
  2245           NS_WARNING("Unable to animate button!");
  2247         DrawButton(cgContext, kThemePushButton, macRect, true,
  2248                    kThemeButtonOff, kThemeAdornmentNone, eventState, aFrame);
  2249       } else if (IsButtonTypeMenu(aFrame)) {
  2250         DrawDropdown(cgContext, macRect, eventState, aWidgetType, aFrame);
  2251       } else {
  2252         DrawPushButton(cgContext, macRect, eventState, aWidgetType, aFrame);
  2254       break;
  2256     case NS_THEME_MOZ_MAC_HELP_BUTTON:
  2257       DrawPushButton(cgContext, macRect, eventState, aWidgetType, aFrame);
  2258       break;
  2260     case NS_THEME_BUTTON_BEVEL:
  2261       DrawButton(cgContext, kThemeMediumBevelButton, macRect,
  2262                  IsDefaultButton(aFrame), kThemeButtonOff, kThemeAdornmentNone,
  2263                  eventState, aFrame);
  2264       break;
  2266     case NS_THEME_SPINNER: {
  2267       nsIContent* content = aFrame->GetContent();
  2268       if (content->IsHTML()) {
  2269         // In HTML the theming for the spin buttons is drawn individually into
  2270         // their own backgrounds instead of being drawn into the background of
  2271         // their spinner parent as it is for XUL.
  2272         break;
  2274       ThemeDrawState state = kThemeStateActive;
  2275       if (content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::state,
  2276                                NS_LITERAL_STRING("up"), eCaseMatters)) {
  2277         state = kThemeStatePressedUp;
  2279       else if (content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::state,
  2280                                     NS_LITERAL_STRING("down"), eCaseMatters)) {
  2281         state = kThemeStatePressedDown;
  2284       DrawSpinButtons(cgContext, kThemeIncDecButton, macRect, state,
  2285                       kThemeAdornmentNone, eventState, aFrame);
  2287       break;
  2289     case NS_THEME_SPINNER_UP_BUTTON:
  2290     case NS_THEME_SPINNER_DOWN_BUTTON: {
  2291       nsNumberControlFrame* numberControlFrame =
  2292         nsNumberControlFrame::GetNumberControlFrameForSpinButton(aFrame);
  2293       if (numberControlFrame) {
  2294         ThemeDrawState state = kThemeStateActive;
  2295         if (numberControlFrame->SpinnerUpButtonIsDepressed()) {
  2296           state = kThemeStatePressedUp;
  2297         } else if (numberControlFrame->SpinnerDownButtonIsDepressed()) {
  2298           state = kThemeStatePressedDown;
  2300         DrawSpinButton(cgContext, kThemeIncDecButtonMini, macRect, state,
  2301                        kThemeAdornmentNone, eventState, aFrame, aWidgetType);
  2304       break;
  2306     case NS_THEME_TOOLBAR_BUTTON:
  2307       DrawSegment(cgContext, macRect, eventState, aFrame, toolbarButtonRenderSettings);
  2308       break;
  2310     case NS_THEME_TOOLBAR_SEPARATOR: {
  2311       HIThemeSeparatorDrawInfo sdi = { 0, kThemeStateActive };
  2312       HIThemeDrawSeparator(&macRect, &sdi, cgContext, HITHEME_ORIENTATION);
  2314       break;
  2316     case NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR:
  2317     case NS_THEME_TOOLBAR: {
  2318       NSWindow* win = NativeWindowForFrame(aFrame);
  2319       if (ToolbarCanBeUnified(cgContext, macRect, win)) {
  2320         DrawUnifiedToolbar(cgContext, macRect, win);
  2321         break;
  2323       BOOL isMain = [win isMainWindow];
  2324       CGRect drawRect = macRect;
  2326       // top border
  2327       drawRect.size.height = 1.0f;
  2328       DrawNativeGreyColorInRect(cgContext, toolbarTopBorderGrey, drawRect, isMain);
  2330       // background
  2331       drawRect.origin.y += drawRect.size.height;
  2332       drawRect.size.height = macRect.size.height - 2.0f;
  2333       DrawNativeGreyColorInRect(cgContext, toolbarFillGrey, drawRect, isMain);
  2335       // bottom border
  2336       drawRect.origin.y += drawRect.size.height;
  2337       drawRect.size.height = 1.0f;
  2338       DrawNativeGreyColorInRect(cgContext, toolbarBottomBorderGrey, drawRect, isMain);
  2340       break;
  2342     case NS_THEME_WINDOW_TITLEBAR: {
  2343       NSWindow* win = NativeWindowForFrame(aFrame);
  2344       BOOL isMain = [win isMainWindow];
  2345       float unifiedToolbarHeight = [win isKindOfClass:[ToolbarWindow class]] ?
  2346         [(ToolbarWindow*)win unifiedToolbarHeight] : macRect.size.height;
  2347       DrawNativeTitlebar(cgContext, macRect, unifiedToolbarHeight, isMain);
  2349       break;
  2351     case NS_THEME_TOOLBOX: {
  2352       HIThemeHeaderDrawInfo hdi = { 0, kThemeStateActive, kHIThemeHeaderKindWindow };
  2353       HIThemeDrawHeader(&macRect, &hdi, cgContext, HITHEME_ORIENTATION);
  2355       break;
  2357     case NS_THEME_STATUSBAR: 
  2358       DrawStatusBar(cgContext, macRect, aFrame);
  2359       break;
  2361     case NS_THEME_DROPDOWN:
  2362     case NS_THEME_DROPDOWN_TEXTFIELD:
  2363       DrawDropdown(cgContext, macRect, eventState, aWidgetType, aFrame);
  2364       break;
  2366     case NS_THEME_DROPDOWN_BUTTON:
  2367       DrawButton(cgContext, kThemeArrowButton, macRect, false, kThemeButtonOn,
  2368                  kThemeAdornmentArrowDownArrow, eventState, aFrame);
  2369       break;
  2371     case NS_THEME_GROUPBOX: {
  2372       HIThemeGroupBoxDrawInfo gdi = { 0, kThemeStateActive, kHIThemeGroupBoxKindPrimary };
  2373       HIThemeDrawGroupBox(&macRect, &gdi, cgContext, HITHEME_ORIENTATION);
  2374       break;
  2377     case NS_THEME_TEXTFIELD:
  2378     case NS_THEME_NUMBER_INPUT:
  2379       // HIThemeSetFill is not available on 10.3
  2380       CGContextSetRGBFillColor(cgContext, 1.0, 1.0, 1.0, 1.0);
  2381       CGContextFillRect(cgContext, macRect);
  2383       // XUL textboxes set the native appearance on the containing box, while
  2384       // concrete focus is set on the html:input element within it. We can
  2385       // though, check the focused attribute of xul textboxes in this case.
  2386       // On Mac, focus rings are always shown for textboxes, so we do not need
  2387       // to check the window's focus ring state here
  2388       if (aFrame->GetContent()->IsXUL() && IsFocused(aFrame)) {
  2389         eventState |= NS_EVENT_STATE_FOCUS;
  2392       DrawFrame(cgContext, kHIThemeFrameTextFieldSquare, macRect,
  2393                 IsDisabled(aFrame, eventState) || IsReadOnly(aFrame), eventState);
  2394       break;
  2396     case NS_THEME_SEARCHFIELD:
  2397       DrawSearchField(cgContext, macRect, aFrame, eventState);
  2398       break;
  2400     case NS_THEME_PROGRESSBAR:
  2401       if (!QueueAnimatedContentForRefresh(aFrame->GetContent(), 30)) {
  2402         NS_WARNING("Unable to animate progressbar!");
  2404       DrawProgress(cgContext, macRect, IsIndeterminateProgress(aFrame, eventState),
  2405                    aFrame->StyleDisplay()->mOrient != NS_STYLE_ORIENT_VERTICAL,
  2406 		   GetProgressValue(aFrame), GetProgressMaxValue(aFrame), aFrame);
  2407       break;
  2409     case NS_THEME_PROGRESSBAR_VERTICAL:
  2410       DrawProgress(cgContext, macRect, IsIndeterminateProgress(aFrame, eventState),
  2411                    false, GetProgressValue(aFrame),
  2412                    GetProgressMaxValue(aFrame), aFrame);
  2413       break;
  2415     case NS_THEME_METERBAR:
  2416       DrawMeter(cgContext, macRect, aFrame);
  2417       break;
  2419     case NS_THEME_PROGRESSBAR_CHUNK:
  2420     case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
  2421     case NS_THEME_METERBAR_CHUNK:
  2422       // Do nothing: progress and meter bars cases will draw chunks.
  2423       break;
  2425     case NS_THEME_TREEVIEW_TWISTY:
  2426       DrawButton(cgContext, kThemeDisclosureButton, macRect, false,
  2427                  kThemeDisclosureRight, kThemeAdornmentNone, eventState, aFrame);
  2428       break;
  2430     case NS_THEME_TREEVIEW_TWISTY_OPEN:
  2431       DrawButton(cgContext, kThemeDisclosureButton, macRect, false,
  2432                  kThemeDisclosureDown, kThemeAdornmentNone, eventState, aFrame);
  2433       break;
  2435     case NS_THEME_TREEVIEW_HEADER_CELL: {
  2436       TreeSortDirection sortDirection = GetTreeSortDirection(aFrame);
  2437       DrawButton(cgContext, kThemeListHeaderButton, macRect, false,
  2438                  sortDirection == eTreeSortDirection_Natural ? kThemeButtonOff : kThemeButtonOn,
  2439                  sortDirection == eTreeSortDirection_Ascending ?
  2440                  kThemeAdornmentHeaderButtonSortUp : kThemeAdornmentNone, eventState, aFrame);
  2442       break;
  2444     case NS_THEME_TREEVIEW_TREEITEM:
  2445     case NS_THEME_TREEVIEW:
  2446       // HIThemeSetFill is not available on 10.3
  2447       // HIThemeSetFill(kThemeBrushWhite, NULL, cgContext, HITHEME_ORIENTATION);
  2448       CGContextSetRGBFillColor(cgContext, 1.0, 1.0, 1.0, 1.0);
  2449       CGContextFillRect(cgContext, macRect);
  2450       break;
  2452     case NS_THEME_TREEVIEW_HEADER:
  2453       // do nothing, taken care of by individual header cells
  2454     case NS_THEME_TREEVIEW_HEADER_SORTARROW:
  2455       // do nothing, taken care of by treeview header
  2456     case NS_THEME_TREEVIEW_LINE:
  2457       // do nothing, these lines don't exist on macos
  2458       break;
  2460     case NS_THEME_SCALE_HORIZONTAL:
  2461     case NS_THEME_SCALE_VERTICAL: {
  2462       int32_t curpos = CheckIntAttr(aFrame, nsGkAtoms::curpos, 0);
  2463       int32_t minpos = CheckIntAttr(aFrame, nsGkAtoms::minpos, 0);
  2464       int32_t maxpos = CheckIntAttr(aFrame, nsGkAtoms::maxpos, 100);
  2465       if (!maxpos)
  2466         maxpos = 100;
  2468       bool reverse = aFrame->GetContent()->
  2469         AttrValueIs(kNameSpaceID_None, nsGkAtoms::dir,
  2470                     NS_LITERAL_STRING("reverse"), eCaseMatters);
  2471       DrawScale(cgContext, macRect, eventState,
  2472                 (aWidgetType == NS_THEME_SCALE_VERTICAL), reverse,
  2473                 curpos, minpos, maxpos, aFrame);
  2475       break;
  2477     case NS_THEME_SCALE_THUMB_HORIZONTAL:
  2478     case NS_THEME_SCALE_THUMB_VERTICAL:
  2479       // do nothing, drawn by scale
  2480       break;
  2482     case NS_THEME_RANGE: {
  2483       nsRangeFrame *rangeFrame = do_QueryFrame(aFrame);
  2484       if (!rangeFrame) {
  2485         break;
  2487       // DrawScale requires integer min, max and value. This is purely for
  2488       // drawing, so we normalize to a range 0-1000 here.
  2489       int32_t value = int32_t(rangeFrame->GetValueAsFractionOfRange() * 1000);
  2490       int32_t min = 0;
  2491       int32_t max = 1000;
  2492       bool isVertical = !IsRangeHorizontal(aFrame);
  2493       bool reverseDir =
  2494         isVertical ||
  2495         rangeFrame->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
  2496       DrawScale(cgContext, macRect, eventState, isVertical, reverseDir,
  2497                 value, min, max, aFrame);
  2498       break;
  2501     case NS_THEME_SCROLLBAR_SMALL:
  2502     case NS_THEME_SCROLLBAR:
  2503       if (!nsLookAndFeel::UseOverlayScrollbars()) {
  2504         DrawScrollbar(cgContext, macRect, aFrame);
  2506       break;
  2507     case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
  2508     case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
  2509       if (nsLookAndFeel::UseOverlayScrollbars()) {
  2510         BOOL isHorizontal = (aWidgetType == NS_THEME_SCROLLBAR_THUMB_HORIZONTAL);
  2511         BOOL isRolledOver = CheckBooleanAttr(GetParentScrollbarFrame(aFrame),
  2512                                              nsGkAtoms::hover);
  2513         if (!nsCocoaFeatures::OnMountainLionOrLater() || !isRolledOver) {
  2514           if (isHorizontal) {
  2515             macRect.origin.y += 4;
  2516             macRect.size.height -= 4;
  2517           } else {
  2518             if (aFrame->StyleVisibility()->mDirection !=
  2519                 NS_STYLE_DIRECTION_RTL) {
  2520               macRect.origin.x += 4;
  2522             macRect.size.width -= 4;
  2525         const BOOL isOnTopOfDarkBackground = IsDarkBackground(aFrame);
  2526         CUIDraw([NSWindow coreUIRenderer], macRect, cgContext,
  2527                 (CFDictionaryRef)[NSDictionary dictionaryWithObjectsAndKeys:
  2528                   @"kCUIWidgetOverlayScrollBar", @"widget",
  2529                   @"regular", @"size",
  2530                   (isRolledOver ? @"rollover" : @""), @"state",
  2531                   (isHorizontal ? @"kCUIOrientHorizontal" : @"kCUIOrientVertical"), @"kCUIOrientationKey",
  2532                   (isOnTopOfDarkBackground ? @"kCUIVariantWhite" : @""), @"kCUIVariantKey",
  2533                   [NSNumber numberWithBool:YES], @"indiconly",
  2534                   [NSNumber numberWithBool:YES], @"kCUIThumbProportionKey",
  2535                   [NSNumber numberWithBool:YES], @"is.flipped",
  2536                   nil],
  2537                 nil);
  2539       break;
  2540     case NS_THEME_SCROLLBAR_BUTTON_UP:
  2541     case NS_THEME_SCROLLBAR_BUTTON_LEFT:
  2542 #if SCROLLBARS_VISUAL_DEBUG
  2543       CGContextSetRGBFillColor(cgContext, 1.0, 0, 0, 0.6);
  2544       CGContextFillRect(cgContext, macRect);
  2545 #endif
  2546     break;
  2547     case NS_THEME_SCROLLBAR_BUTTON_DOWN:
  2548     case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
  2549 #if SCROLLBARS_VISUAL_DEBUG
  2550       CGContextSetRGBFillColor(cgContext, 0, 1.0, 0, 0.6);
  2551       CGContextFillRect(cgContext, macRect);
  2552 #endif
  2553     break;
  2554     case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
  2555     case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
  2556       if (nsLookAndFeel::UseOverlayScrollbars() &&
  2557           CheckBooleanAttr(GetParentScrollbarFrame(aFrame), nsGkAtoms::hover)) {
  2558         BOOL isHorizontal = (aWidgetType == NS_THEME_SCROLLBAR_TRACK_HORIZONTAL);
  2559         if (!nsCocoaFeatures::OnMountainLionOrLater()) {
  2560           // On OSX 10.7, scrollbars don't grow when hovered. The adjustments
  2561           // below were obtained by trial and error.
  2562           if (isHorizontal) {
  2563             macRect.origin.y += 2.0;
  2564           } else {
  2565             if (aFrame->StyleVisibility()->mDirection !=
  2566                   NS_STYLE_DIRECTION_RTL) {
  2567               macRect.origin.x += 3.0;
  2568             } else {
  2569               macRect.origin.x -= 1.0;
  2573         const BOOL isOnTopOfDarkBackground = IsDarkBackground(aFrame);
  2574         CUIDraw([NSWindow coreUIRenderer], macRect, cgContext,
  2575                 (CFDictionaryRef)[NSDictionary dictionaryWithObjectsAndKeys:
  2576                   @"kCUIWidgetOverlayScrollBar", @"widget",
  2577                   @"regular", @"size",
  2578                   (isHorizontal ? @"kCUIOrientHorizontal" : @"kCUIOrientVertical"), @"kCUIOrientationKey",
  2579                   (isOnTopOfDarkBackground ? @"kCUIVariantWhite" : @""), @"kCUIVariantKey",
  2580                   [NSNumber numberWithBool:YES], @"noindicator",
  2581                   [NSNumber numberWithBool:YES], @"kCUIThumbProportionKey",
  2582                   [NSNumber numberWithBool:YES], @"is.flipped",
  2583                   nil],
  2584                 nil);
  2586       break;
  2588     case NS_THEME_TEXTFIELD_MULTILINE: {
  2589       // we have to draw this by hand because there is no HITheme value for it
  2590       CGContextSetRGBFillColor(cgContext, 1.0, 1.0, 1.0, 1.0);
  2592       CGContextFillRect(cgContext, macRect);
  2594       CGContextSetLineWidth(cgContext, 1.0);
  2595       CGContextSetShouldAntialias(cgContext, false);
  2597       // stroke everything but the top line of the text area
  2598       CGContextSetRGBStrokeColor(cgContext, 0.6, 0.6, 0.6, 1.0);
  2599       CGContextBeginPath(cgContext);
  2600       CGContextMoveToPoint(cgContext, macRect.origin.x, macRect.origin.y + 1);
  2601       CGContextAddLineToPoint(cgContext, macRect.origin.x, macRect.origin.y + macRect.size.height);
  2602       CGContextAddLineToPoint(cgContext, macRect.origin.x + macRect.size.width - 1, macRect.origin.y + macRect.size.height);
  2603       CGContextAddLineToPoint(cgContext, macRect.origin.x + macRect.size.width - 1, macRect.origin.y + 1);
  2604       CGContextStrokePath(cgContext);
  2606       // stroke the line across the top of the text area
  2607       CGContextSetRGBStrokeColor(cgContext, 0.4510, 0.4510, 0.4510, 1.0);
  2608       CGContextBeginPath(cgContext);
  2609       CGContextMoveToPoint(cgContext, macRect.origin.x, macRect.origin.y + 1);
  2610       CGContextAddLineToPoint(cgContext, macRect.origin.x + macRect.size.width - 1, macRect.origin.y + 1);
  2611       CGContextStrokePath(cgContext);
  2613       // draw a focus ring
  2614       if (eventState.HasState(NS_EVENT_STATE_FOCUS)) {
  2615         // We need to bring the rectangle in by 1 pixel on each side.
  2616         CGRect cgr = CGRectMake(macRect.origin.x + 1,
  2617                                 macRect.origin.y + 1,
  2618                                 macRect.size.width - 2,
  2619                                 macRect.size.height - 2);
  2620         HIThemeDrawFocusRect(&cgr, true, cgContext, kHIThemeOrientationNormal);
  2623       break;
  2625     case NS_THEME_LISTBOX: {
  2626       // We have to draw this by hand because kHIThemeFrameListBox drawing
  2627       // is buggy on 10.5, see bug 579259.
  2628       CGContextSetRGBFillColor(cgContext, 1.0, 1.0, 1.0, 1.0);
  2629       CGContextFillRect(cgContext, macRect);
  2631       // #8E8E8E for the top border, #BEBEBE for the rest.
  2632       float x = macRect.origin.x, y = macRect.origin.y;
  2633       float w = macRect.size.width, h = macRect.size.height;
  2634       CGContextSetRGBFillColor(cgContext, 0.557, 0.557, 0.557, 1.0);
  2635       CGContextFillRect(cgContext, CGRectMake(x, y, w, 1));
  2636       CGContextSetRGBFillColor(cgContext, 0.745, 0.745, 0.745, 1.0);
  2637       CGContextFillRect(cgContext, CGRectMake(x, y + 1, 1, h - 1));
  2638       CGContextFillRect(cgContext, CGRectMake(x + w - 1, y + 1, 1, h - 1));
  2639       CGContextFillRect(cgContext, CGRectMake(x + 1, y + h - 1, w - 2, 1));
  2641       break;
  2643     case NS_THEME_TAB:
  2644       DrawSegment(cgContext, macRect, eventState, aFrame, tabRenderSettings);
  2645       break;
  2647     case NS_THEME_TAB_PANELS:
  2648       DrawTabPanel(cgContext, macRect, aFrame);
  2649       break;
  2651     case NS_THEME_RESIZER:
  2652       DrawResizer(cgContext, macRect, aFrame);
  2653       break;
  2656   nativeDrawing.EndNativeDrawing();
  2658   return NS_OK;
  2660   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  2663 nsIntMargin
  2664 nsNativeThemeCocoa::RTLAwareMargin(const nsIntMargin& aMargin, nsIFrame* aFrame)
  2666   if (IsFrameRTL(aFrame)) {
  2667     // Return a copy of aMargin w/ right & left reversed:
  2668     return nsIntMargin(aMargin.top, aMargin.left,
  2669                        aMargin.bottom, aMargin.right);
  2672   return aMargin;
  2675 static const nsIntMargin kAquaDropdownBorder(1, 22, 2, 5);
  2676 static const nsIntMargin kAquaComboboxBorder(3, 20, 3, 4);
  2677 static const nsIntMargin kAquaSearchfieldBorder(3, 5, 2, 19);
  2679 NS_IMETHODIMP
  2680 nsNativeThemeCocoa::GetWidgetBorder(nsDeviceContext* aContext, 
  2681                                     nsIFrame* aFrame,
  2682                                     uint8_t aWidgetType,
  2683                                     nsIntMargin* aResult)
  2685   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  2687   aResult->SizeTo(0, 0, 0, 0);
  2689   switch (aWidgetType) {
  2690     case NS_THEME_BUTTON:
  2692       if (IsButtonTypeMenu(aFrame)) {
  2693         *aResult = RTLAwareMargin(kAquaDropdownBorder, aFrame);
  2694       } else {
  2695         aResult->SizeTo(1, 7, 3, 7);
  2697       break;
  2700     case NS_THEME_TOOLBAR_BUTTON:
  2702       aResult->SizeTo(1, 4, 1, 4);
  2703       break;
  2706     case NS_THEME_CHECKBOX:
  2707     case NS_THEME_RADIO:
  2709       // nsFormControlFrame::GetIntrinsicWidth and nsFormControlFrame::GetIntrinsicHeight
  2710       // assume a border width of 2px.
  2711       aResult->SizeTo(2, 2, 2, 2);
  2712       break;
  2715     case NS_THEME_DROPDOWN:
  2716     case NS_THEME_DROPDOWN_BUTTON:
  2717       *aResult = RTLAwareMargin(kAquaDropdownBorder, aFrame);
  2718       break;
  2720     case NS_THEME_DROPDOWN_TEXTFIELD:
  2721       *aResult = RTLAwareMargin(kAquaComboboxBorder, aFrame);
  2722       break;
  2724     case NS_THEME_NUMBER_INPUT:
  2725     case NS_THEME_TEXTFIELD:
  2727       SInt32 frameOutset = 0;
  2728       ::GetThemeMetric(kThemeMetricEditTextFrameOutset, &frameOutset);
  2730       SInt32 textPadding = 0;
  2731       ::GetThemeMetric(kThemeMetricEditTextWhitespace, &textPadding);
  2733       frameOutset += textPadding;
  2735       aResult->SizeTo(frameOutset, frameOutset, frameOutset, frameOutset);
  2736       break;
  2739     case NS_THEME_TEXTFIELD_MULTILINE:
  2740       aResult->SizeTo(1, 1, 1, 1);
  2741       break;
  2743     case NS_THEME_SEARCHFIELD:
  2744       *aResult = RTLAwareMargin(kAquaSearchfieldBorder, aFrame);
  2745       break;
  2747     case NS_THEME_LISTBOX:
  2749       SInt32 frameOutset = 0;
  2750       ::GetThemeMetric(kThemeMetricListBoxFrameOutset, &frameOutset);
  2751       aResult->SizeTo(frameOutset, frameOutset, frameOutset, frameOutset);
  2752       break;
  2755     case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
  2756     case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
  2758       bool isHorizontal = (aWidgetType == NS_THEME_SCROLLBAR_TRACK_HORIZONTAL);
  2760       // On Lion and later, scrollbars have no arrows.
  2761       if (!nsCocoaFeatures::OnLionOrLater()) {
  2762         // There's only an endcap to worry about when both arrows are on the bottom
  2763         NSString *buttonPlacement = [[NSUserDefaults standardUserDefaults] objectForKey:@"AppleScrollBarVariant"];
  2764         if (!buttonPlacement || [buttonPlacement isEqualToString:@"DoubleMax"]) {
  2765           nsIFrame *scrollbarFrame = GetParentScrollbarFrame(aFrame);
  2766           if (!scrollbarFrame) return NS_ERROR_FAILURE;
  2767           bool isSmall = (scrollbarFrame->StyleDisplay()->mAppearance == NS_THEME_SCROLLBAR_SMALL);
  2769           // There isn't a metric for this, so just hardcode a best guess at the value.
  2770           // This value is even less exact due to the fact that the endcap is partially concave.
  2771           int32_t endcapSize = isSmall ? 5 : 6;
  2773           if (isHorizontal)
  2774             aResult->SizeTo(0, 0, 0, endcapSize);
  2775           else
  2776             aResult->SizeTo(endcapSize, 0, 0, 0);
  2780       if (nsLookAndFeel::UseOverlayScrollbars()) {
  2781         if (isHorizontal) {
  2782           aResult->SizeTo(2, 1, 1, 1);
  2783         } else {
  2784           aResult->SizeTo(1, 1, 1, 2);
  2788       break;
  2791     case NS_THEME_STATUSBAR:
  2792       aResult->SizeTo(1, 0, 0, 0);
  2793       break;
  2796   if (IsHiDPIContext(aContext)) {
  2797     *aResult = *aResult + *aResult; // doubled
  2800   return NS_OK;
  2802   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  2805 // Return false here to indicate that CSS padding values should be used. There is
  2806 // no reason to make a distinction between padding and border values, just specify
  2807 // whatever values you want in GetWidgetBorder and only use this to return true
  2808 // if you want to override CSS padding values.
  2809 bool
  2810 nsNativeThemeCocoa::GetWidgetPadding(nsDeviceContext* aContext, 
  2811                                      nsIFrame* aFrame,
  2812                                      uint8_t aWidgetType,
  2813                                      nsIntMargin* aResult)
  2815   // We don't want CSS padding being used for certain widgets.
  2816   // See bug 381639 for an example of why.
  2817   switch (aWidgetType) {
  2818     case NS_THEME_BUTTON:
  2819     // Radios and checkboxes return a fixed size in GetMinimumWidgetSize
  2820     // and have a meaningful baseline, so they can't have
  2821     // author-specified padding.
  2822     case NS_THEME_CHECKBOX:
  2823     case NS_THEME_RADIO:
  2824       aResult->SizeTo(0, 0, 0, 0);
  2825       return true;
  2827   return false;
  2830 bool
  2831 nsNativeThemeCocoa::GetWidgetOverflow(nsDeviceContext* aContext, nsIFrame* aFrame,
  2832                                       uint8_t aWidgetType, nsRect* aOverflowRect)
  2834   int32_t p2a = aContext->AppUnitsPerDevPixel();
  2835   switch (aWidgetType) {
  2836     case NS_THEME_BUTTON:
  2837     case NS_THEME_MOZ_MAC_HELP_BUTTON:
  2838     case NS_THEME_TOOLBAR_BUTTON:
  2839     case NS_THEME_NUMBER_INPUT:
  2840     case NS_THEME_TEXTFIELD:
  2841     case NS_THEME_TEXTFIELD_MULTILINE:
  2842     case NS_THEME_SEARCHFIELD:
  2843     case NS_THEME_LISTBOX:
  2844     case NS_THEME_DROPDOWN:
  2845     case NS_THEME_DROPDOWN_BUTTON:
  2846     case NS_THEME_DROPDOWN_TEXTFIELD:
  2847     case NS_THEME_CHECKBOX:
  2848     case NS_THEME_RADIO:
  2849     case NS_THEME_TAB:
  2851       // We assume that the above widgets can draw a focus ring that will be less than
  2852       // or equal to 4 pixels thick.
  2853       nsIntMargin extraSize = nsIntMargin(MAX_FOCUS_RING_WIDTH,
  2854                                           MAX_FOCUS_RING_WIDTH,
  2855                                           MAX_FOCUS_RING_WIDTH,
  2856                                           MAX_FOCUS_RING_WIDTH);
  2857       nsMargin m(NSIntPixelsToAppUnits(extraSize.top, p2a),
  2858                  NSIntPixelsToAppUnits(extraSize.right, p2a),
  2859                  NSIntPixelsToAppUnits(extraSize.bottom, p2a),
  2860                  NSIntPixelsToAppUnits(extraSize.left, p2a));
  2861       aOverflowRect->Inflate(m);
  2862       return true;
  2864     case NS_THEME_PROGRESSBAR:
  2866       // Progress bars draw a 2 pixel white shadow under their progress indicators
  2867       nsMargin m(0, 0, NSIntPixelsToAppUnits(2, p2a), 0);
  2868       aOverflowRect->Inflate(m);
  2869       return true;
  2873   return false;
  2876 static const int32_t kRegularScrollbarThumbMinSize = 26;
  2877 static const int32_t kSmallScrollbarThumbMinSize = 26;
  2879 NS_IMETHODIMP
  2880 nsNativeThemeCocoa::GetMinimumWidgetSize(nsRenderingContext* aContext,
  2881                                          nsIFrame* aFrame,
  2882                                          uint8_t aWidgetType,
  2883                                          nsIntSize* aResult,
  2884                                          bool* aIsOverridable)
  2886   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
  2888   aResult->SizeTo(0,0);
  2889   *aIsOverridable = true;
  2891   switch (aWidgetType) {
  2892     case NS_THEME_BUTTON:
  2894       aResult->SizeTo(pushButtonSettings.minimumSizes[miniControlSize].width,
  2895                       pushButtonSettings.naturalSizes[miniControlSize].height);
  2896       break;
  2899     case NS_THEME_MOZ_MAC_HELP_BUTTON:
  2901       aResult->SizeTo(kHelpButtonSize.width, kHelpButtonSize.height);
  2902       *aIsOverridable = false;
  2903       break;
  2906     case NS_THEME_TOOLBAR_BUTTON:
  2908       aResult->SizeTo(0, toolbarButtonHeights[miniControlSize]);
  2909       break;
  2912     case NS_THEME_SPINNER:
  2913     case NS_THEME_SPINNER_UP_BUTTON:
  2914     case NS_THEME_SPINNER_DOWN_BUTTON:
  2916       SInt32 buttonHeight = 0, buttonWidth = 0;
  2917       if (aFrame->GetContent()->IsXUL()) {
  2918         ::GetThemeMetric(kThemeMetricLittleArrowsWidth, &buttonWidth);
  2919         ::GetThemeMetric(kThemeMetricLittleArrowsHeight, &buttonHeight);
  2920       } else {
  2921         NSSize size =
  2922           spinnerSettings.minimumSizes[EnumSizeForCocoaSize(NSMiniControlSize)];
  2923         buttonWidth = size.width;
  2924         buttonHeight = size.height;
  2925         if (aWidgetType != NS_THEME_SPINNER) {
  2926           // the buttons are half the height of the spinner
  2927           buttonHeight /= 2;
  2930       aResult->SizeTo(buttonWidth, buttonHeight);
  2931       *aIsOverridable = true;
  2932       break;
  2935     case NS_THEME_DROPDOWN:
  2936     case NS_THEME_DROPDOWN_BUTTON:
  2938       SInt32 popupHeight = 0;
  2939       ::GetThemeMetric(kThemeMetricPopupButtonHeight, &popupHeight);
  2940       aResult->SizeTo(0, popupHeight);
  2941       break;
  2944     case NS_THEME_NUMBER_INPUT:
  2945     case NS_THEME_TEXTFIELD:
  2946     case NS_THEME_TEXTFIELD_MULTILINE:
  2947     case NS_THEME_SEARCHFIELD:
  2949       // at minimum, we should be tall enough for 9pt text.
  2950       // I'm using hardcoded values here because the appearance manager
  2951       // values for the frame size are incorrect.
  2952       aResult->SizeTo(0, (2 + 2) /* top */ + 9 + (1 + 1) /* bottom */);
  2953       break;
  2956     case NS_THEME_WINDOW_BUTTON_BOX: {
  2957       NSSize size = WindowButtonsSize(aFrame);
  2958       aResult->SizeTo(size.width, size.height);
  2959       *aIsOverridable = false;
  2960       break;
  2963     case NS_THEME_MOZ_MAC_FULLSCREEN_BUTTON: {
  2964       if ([NativeWindowForFrame(aFrame) respondsToSelector:@selector(toggleFullScreen:)]) {
  2965         // This value is hardcoded because it's needed before we can measure the
  2966         // position and size of the fullscreen button.
  2967         aResult->SizeTo(16, 17);
  2969       *aIsOverridable = false;
  2970       break;
  2973     case NS_THEME_PROGRESSBAR:
  2975       SInt32 barHeight = 0;
  2976       ::GetThemeMetric(kThemeMetricNormalProgressBarThickness, &barHeight);
  2977       aResult->SizeTo(0, barHeight);
  2978       break;
  2981     case NS_THEME_TREEVIEW_TWISTY:
  2982     case NS_THEME_TREEVIEW_TWISTY_OPEN:   
  2984       SInt32 twistyHeight = 0, twistyWidth = 0;
  2985       ::GetThemeMetric(kThemeMetricDisclosureButtonWidth, &twistyWidth);
  2986       ::GetThemeMetric(kThemeMetricDisclosureButtonHeight, &twistyHeight);
  2987       aResult->SizeTo(twistyWidth, twistyHeight);
  2988       *aIsOverridable = false;
  2989       break;
  2992     case NS_THEME_TREEVIEW_HEADER:
  2993     case NS_THEME_TREEVIEW_HEADER_CELL:
  2995       SInt32 headerHeight = 0;
  2996       ::GetThemeMetric(kThemeMetricListHeaderHeight, &headerHeight);
  2997       aResult->SizeTo(0, headerHeight - 1); // We don't need the top border.
  2998       break;
  3001     case NS_THEME_TAB:
  3003       aResult->SizeTo(0, tabHeights[miniControlSize]);
  3004       break;
  3007     case NS_THEME_RANGE:
  3009       // The Mac Appearance Manager API (the old API we're currently using)
  3010       // doesn't define constants to obtain a minimum size for sliders. We use
  3011       // the "thickness" of a slider that has default dimensions for both the
  3012       // minimum width and height to get something sane and so that paint
  3013       // invalidation works.
  3014       SInt32 size = 0;
  3015       if (IsRangeHorizontal(aFrame)) {
  3016         ::GetThemeMetric(kThemeMetricHSliderHeight, &size);
  3017       } else {
  3018         ::GetThemeMetric(kThemeMetricVSliderWidth, &size);
  3020       aResult->SizeTo(size, size);
  3021       *aIsOverridable = true;
  3022       break;
  3025     case NS_THEME_RANGE_THUMB:
  3027       SInt32 width = 0;
  3028       SInt32 height = 0;
  3029       ::GetThemeMetric(kThemeMetricSliderMinThumbWidth, &width);
  3030       ::GetThemeMetric(kThemeMetricSliderMinThumbHeight, &height);
  3031       aResult->SizeTo(width, height);
  3032       *aIsOverridable = false;
  3033       break;
  3036     case NS_THEME_SCALE_HORIZONTAL:
  3038       SInt32 scaleHeight = 0;
  3039       ::GetThemeMetric(kThemeMetricHSliderHeight, &scaleHeight);
  3040       aResult->SizeTo(scaleHeight, scaleHeight);
  3041       *aIsOverridable = false;
  3042       break;
  3045     case NS_THEME_SCALE_VERTICAL:
  3047       SInt32 scaleWidth = 0;
  3048       ::GetThemeMetric(kThemeMetricVSliderWidth, &scaleWidth);
  3049       aResult->SizeTo(scaleWidth, scaleWidth);
  3050       *aIsOverridable = false;
  3051       break;
  3054     case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
  3055     case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
  3057       // Find our parent scrollbar frame in order to find out whether we're in
  3058       // a small or a large scrollbar.
  3059       nsIFrame *scrollbarFrame = GetParentScrollbarFrame(aFrame);
  3060       if (!scrollbarFrame)
  3061         return NS_ERROR_FAILURE;
  3063       bool isSmall = (scrollbarFrame->StyleDisplay()->mAppearance == NS_THEME_SCROLLBAR_SMALL);
  3064       bool isHorizontal = (aWidgetType == NS_THEME_SCROLLBAR_THUMB_HORIZONTAL);
  3065       int32_t& minSize = isHorizontal ? aResult->width : aResult->height;
  3066       minSize = isSmall ? kSmallScrollbarThumbMinSize : kRegularScrollbarThumbMinSize;
  3067       break;
  3070     case NS_THEME_SCROLLBAR:
  3071     case NS_THEME_SCROLLBAR_SMALL:
  3072     case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
  3073     case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
  3075       *aIsOverridable = false;
  3077       if (nsLookAndFeel::UseOverlayScrollbars()) {
  3078         nsIFrame* scrollbarFrame = GetParentScrollbarFrame(aFrame);
  3079         if (scrollbarFrame &&
  3080             scrollbarFrame->StyleDisplay()->mAppearance ==
  3081               NS_THEME_SCROLLBAR_SMALL) {
  3082           aResult->SizeTo(14, 14);
  3084         else {
  3085           aResult->SizeTo(16, 16);
  3087         break;
  3090       // yeah, i know i'm cheating a little here, but i figure that it
  3091       // really doesn't matter if the scrollbar is vertical or horizontal
  3092       // and the width metric is a really good metric for every piece
  3093       // of the scrollbar.
  3095       nsIFrame *scrollbarFrame = GetParentScrollbarFrame(aFrame);
  3096       if (!scrollbarFrame) return NS_ERROR_FAILURE;
  3098       int32_t themeMetric = (scrollbarFrame->StyleDisplay()->mAppearance == NS_THEME_SCROLLBAR_SMALL) ?
  3099                             kThemeMetricSmallScrollBarWidth :
  3100                             kThemeMetricScrollBarWidth;
  3101       SInt32 scrollbarWidth = 0;
  3102       ::GetThemeMetric(themeMetric, &scrollbarWidth);
  3103       aResult->SizeTo(scrollbarWidth, scrollbarWidth);
  3104       break;
  3107     case NS_THEME_SCROLLBAR_NON_DISAPPEARING:
  3109       int32_t themeMetric = kThemeMetricScrollBarWidth;
  3111       if (aFrame) {
  3112         nsIFrame* scrollbarFrame = GetParentScrollbarFrame(aFrame);
  3113         if (scrollbarFrame &&
  3114             scrollbarFrame->StyleDisplay()->mAppearance ==
  3115             NS_THEME_SCROLLBAR_SMALL) {
  3116           // XXX We're interested in the width of non-disappearing scrollbars
  3117           // to leave enough space for a dropmarker in non-native styled
  3118           // comboboxes (bug 869314). It isn't clear to me if comboboxes can
  3119           // ever have small scrollbars.
  3120           themeMetric = kThemeMetricSmallScrollBarWidth;
  3124       SInt32 scrollbarWidth = 0;
  3125       ::GetThemeMetric(themeMetric, &scrollbarWidth);
  3126       aResult->SizeTo(scrollbarWidth, scrollbarWidth);
  3127       break;
  3130     case NS_THEME_SCROLLBAR_BUTTON_UP:
  3131     case NS_THEME_SCROLLBAR_BUTTON_DOWN:
  3132     case NS_THEME_SCROLLBAR_BUTTON_LEFT:
  3133     case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
  3135       nsIFrame *scrollbarFrame = GetParentScrollbarFrame(aFrame);
  3136       if (!scrollbarFrame) return NS_ERROR_FAILURE;
  3138       // Since there is no NS_THEME_SCROLLBAR_BUTTON_UP_SMALL we need to ask the parent what appearance style it has.
  3139       int32_t themeMetric = (scrollbarFrame->StyleDisplay()->mAppearance == NS_THEME_SCROLLBAR_SMALL) ?
  3140                             kThemeMetricSmallScrollBarWidth :
  3141                             kThemeMetricScrollBarWidth;
  3142       SInt32 scrollbarWidth = 0;
  3143       ::GetThemeMetric(themeMetric, &scrollbarWidth);
  3145       // It seems that for both sizes of scrollbar, the buttons are one pixel "longer".
  3146       if (aWidgetType == NS_THEME_SCROLLBAR_BUTTON_LEFT || aWidgetType == NS_THEME_SCROLLBAR_BUTTON_RIGHT)
  3147         aResult->SizeTo(scrollbarWidth+1, scrollbarWidth);
  3148       else
  3149         aResult->SizeTo(scrollbarWidth, scrollbarWidth+1);
  3151       *aIsOverridable = false;
  3152       break;
  3154     case NS_THEME_RESIZER:
  3156       HIThemeGrowBoxDrawInfo drawInfo;
  3157       drawInfo.version = 0;
  3158       drawInfo.state = kThemeStateActive;
  3159       drawInfo.kind = kHIThemeGrowBoxKindNormal;
  3160       drawInfo.direction = kThemeGrowRight | kThemeGrowDown;
  3161       drawInfo.size = kHIThemeGrowBoxSizeNormal;
  3162       HIPoint pnt = { 0, 0 };
  3163       HIRect bounds;
  3164       HIThemeGetGrowBoxBounds(&pnt, &drawInfo, &bounds);
  3165       aResult->SizeTo(bounds.size.width, bounds.size.height);
  3166       *aIsOverridable = false;
  3170   if (IsHiDPIContext(aContext->DeviceContext())) {
  3171     *aResult = *aResult * 2;
  3174   return NS_OK;
  3176   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
  3179 NS_IMETHODIMP
  3180 nsNativeThemeCocoa::WidgetStateChanged(nsIFrame* aFrame, uint8_t aWidgetType, 
  3181                                      nsIAtom* aAttribute, bool* aShouldRepaint)
  3183   // Some widget types just never change state.
  3184   switch (aWidgetType) {
  3185     case NS_THEME_WINDOW_TITLEBAR:
  3186     case NS_THEME_TOOLBOX:
  3187     case NS_THEME_TOOLBAR:
  3188     case NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR:
  3189     case NS_THEME_STATUSBAR:
  3190     case NS_THEME_STATUSBAR_PANEL:
  3191     case NS_THEME_STATUSBAR_RESIZER_PANEL:
  3192     case NS_THEME_TOOLTIP:
  3193     case NS_THEME_TAB_PANELS:
  3194     case NS_THEME_TAB_PANEL:
  3195     case NS_THEME_DIALOG:
  3196     case NS_THEME_MENUPOPUP:
  3197     case NS_THEME_GROUPBOX:
  3198     case NS_THEME_PROGRESSBAR_CHUNK:
  3199     case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
  3200     case NS_THEME_PROGRESSBAR:
  3201     case NS_THEME_PROGRESSBAR_VERTICAL:
  3202     case NS_THEME_METERBAR:
  3203     case NS_THEME_METERBAR_CHUNK:
  3204       *aShouldRepaint = false;
  3205       return NS_OK;
  3208   // XXXdwh Not sure what can really be done here.  Can at least guess for
  3209   // specific widgets that they're highly unlikely to have certain states.
  3210   // For example, a toolbar doesn't care about any states.
  3211   if (!aAttribute) {
  3212     // Hover/focus/active changed.  Always repaint.
  3213     *aShouldRepaint = true;
  3214   } else {
  3215     // Check the attribute to see if it's relevant.  
  3216     // disabled, checked, dlgtype, default, etc.
  3217     *aShouldRepaint = false;
  3218     if (aAttribute == nsGkAtoms::disabled ||
  3219         aAttribute == nsGkAtoms::checked ||
  3220         aAttribute == nsGkAtoms::selected ||
  3221         aAttribute == nsGkAtoms::menuactive ||
  3222         aAttribute == nsGkAtoms::sortDirection ||
  3223         aAttribute == nsGkAtoms::focused ||
  3224         aAttribute == nsGkAtoms::_default ||
  3225         aAttribute == nsGkAtoms::open ||
  3226         aAttribute == nsGkAtoms::hover)
  3227       *aShouldRepaint = true;
  3230   return NS_OK;
  3233 NS_IMETHODIMP
  3234 nsNativeThemeCocoa::ThemeChanged()
  3236   // This is unimplemented because we don't care if gecko changes its theme
  3237   // and Mac OS X doesn't have themes.
  3238   return NS_OK;
  3241 bool 
  3242 nsNativeThemeCocoa::ThemeSupportsWidget(nsPresContext* aPresContext, nsIFrame* aFrame,
  3243                                       uint8_t aWidgetType)
  3245   // We don't have CSS set up to render non-native scrollbars on Mac OS X so we
  3246   // render natively even if native theme support is disabled.
  3247   if (aWidgetType != NS_THEME_SCROLLBAR &&
  3248       aPresContext && !aPresContext->PresShell()->IsThemeSupportEnabled())
  3249     return false;
  3251   // if this is a dropdown button in a combobox the answer is always no
  3252   if (aWidgetType == NS_THEME_DROPDOWN_BUTTON) {
  3253     nsIFrame* parentFrame = aFrame->GetParent();
  3254     if (parentFrame && (parentFrame->GetType() == nsGkAtoms::comboboxControlFrame))
  3255       return false;
  3258   switch (aWidgetType) {
  3259     case NS_THEME_LISTBOX:
  3261     case NS_THEME_DIALOG:
  3262     case NS_THEME_WINDOW:
  3263     case NS_THEME_WINDOW_BUTTON_BOX:
  3264     case NS_THEME_WINDOW_TITLEBAR:
  3265     case NS_THEME_MENUPOPUP:
  3266     case NS_THEME_MENUITEM:
  3267     case NS_THEME_MENUSEPARATOR:
  3268     case NS_THEME_MOZ_MAC_FULLSCREEN_BUTTON:
  3269     case NS_THEME_TOOLTIP:
  3271     case NS_THEME_CHECKBOX:
  3272     case NS_THEME_CHECKBOX_CONTAINER:
  3273     case NS_THEME_RADIO:
  3274     case NS_THEME_RADIO_CONTAINER:
  3275     case NS_THEME_GROUPBOX:
  3276     case NS_THEME_MOZ_MAC_HELP_BUTTON:
  3277     case NS_THEME_BUTTON:
  3278     case NS_THEME_BUTTON_BEVEL:
  3279     case NS_THEME_TOOLBAR_BUTTON:
  3280     case NS_THEME_SPINNER:
  3281     case NS_THEME_SPINNER_UP_BUTTON:
  3282     case NS_THEME_SPINNER_DOWN_BUTTON:
  3283     case NS_THEME_TOOLBAR:
  3284     case NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR:
  3285     case NS_THEME_STATUSBAR:
  3286     case NS_THEME_NUMBER_INPUT:
  3287     case NS_THEME_TEXTFIELD:
  3288     case NS_THEME_TEXTFIELD_MULTILINE:
  3289     case NS_THEME_SEARCHFIELD:
  3290     //case NS_THEME_TOOLBOX:
  3291     //case NS_THEME_TOOLBAR_BUTTON:
  3292     case NS_THEME_PROGRESSBAR:
  3293     case NS_THEME_PROGRESSBAR_VERTICAL:
  3294     case NS_THEME_PROGRESSBAR_CHUNK:
  3295     case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
  3296     case NS_THEME_METERBAR:
  3297     case NS_THEME_METERBAR_CHUNK:
  3298     case NS_THEME_TOOLBAR_SEPARATOR:
  3300     case NS_THEME_TAB_PANELS:
  3301     case NS_THEME_TAB:
  3303     case NS_THEME_TREEVIEW_TWISTY:
  3304     case NS_THEME_TREEVIEW_TWISTY_OPEN:
  3305     case NS_THEME_TREEVIEW:
  3306     case NS_THEME_TREEVIEW_HEADER:
  3307     case NS_THEME_TREEVIEW_HEADER_CELL:
  3308     case NS_THEME_TREEVIEW_HEADER_SORTARROW:
  3309     case NS_THEME_TREEVIEW_TREEITEM:
  3310     case NS_THEME_TREEVIEW_LINE:
  3312     case NS_THEME_RANGE:
  3314     case NS_THEME_SCALE_HORIZONTAL:
  3315     case NS_THEME_SCALE_THUMB_HORIZONTAL:
  3316     case NS_THEME_SCALE_VERTICAL:
  3317     case NS_THEME_SCALE_THUMB_VERTICAL:
  3319     case NS_THEME_SCROLLBAR:
  3320     case NS_THEME_SCROLLBAR_SMALL:
  3321     case NS_THEME_SCROLLBAR_BUTTON_UP:
  3322     case NS_THEME_SCROLLBAR_BUTTON_DOWN:
  3323     case NS_THEME_SCROLLBAR_BUTTON_LEFT:
  3324     case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
  3325     case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
  3326     case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
  3327     case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
  3328     case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
  3329     case NS_THEME_SCROLLBAR_NON_DISAPPEARING:
  3331     case NS_THEME_DROPDOWN:
  3332     case NS_THEME_DROPDOWN_BUTTON:
  3333     case NS_THEME_DROPDOWN_TEXT:
  3334     case NS_THEME_DROPDOWN_TEXTFIELD:
  3335       return !IsWidgetStyled(aPresContext, aFrame, aWidgetType);
  3336       break;
  3338     case NS_THEME_RESIZER:
  3340       nsIFrame* parentFrame = aFrame->GetParent();
  3341       if (!parentFrame || parentFrame->GetType() != nsGkAtoms::scrollFrame)
  3342         return true;
  3344       // Note that IsWidgetStyled is not called for resizers on Mac. This is
  3345       // because for scrollable containers, the native resizer looks better
  3346       // when (non-overlay) scrollbars are present even when the style is
  3347       // overriden, and the custom transparent resizer looks better when
  3348       // scrollbars are not present.
  3349       nsIScrollableFrame* scrollFrame = do_QueryFrame(parentFrame);
  3350       return (!nsLookAndFeel::UseOverlayScrollbars() &&
  3351               scrollFrame && scrollFrame->GetScrollbarVisibility());
  3352       break;
  3356   return false;
  3359 bool
  3360 nsNativeThemeCocoa::WidgetIsContainer(uint8_t aWidgetType)
  3362   // flesh this out at some point
  3363   switch (aWidgetType) {
  3364    case NS_THEME_DROPDOWN_BUTTON:
  3365    case NS_THEME_RADIO:
  3366    case NS_THEME_CHECKBOX:
  3367    case NS_THEME_PROGRESSBAR:
  3368    case NS_THEME_METERBAR:
  3369    case NS_THEME_RANGE:
  3370    case NS_THEME_MOZ_MAC_HELP_BUTTON:
  3371     return false;
  3372     break;
  3374   return true;
  3377 bool
  3378 nsNativeThemeCocoa::ThemeDrawsFocusForWidget(uint8_t aWidgetType)
  3380   if (aWidgetType == NS_THEME_DROPDOWN ||
  3381       aWidgetType == NS_THEME_DROPDOWN_TEXTFIELD ||
  3382       aWidgetType == NS_THEME_BUTTON ||
  3383       aWidgetType == NS_THEME_MOZ_MAC_HELP_BUTTON ||
  3384       aWidgetType == NS_THEME_RADIO ||
  3385       aWidgetType == NS_THEME_RANGE ||
  3386       aWidgetType == NS_THEME_CHECKBOX)
  3387     return true;
  3389   return false;
  3392 bool
  3393 nsNativeThemeCocoa::ThemeNeedsComboboxDropmarker()
  3395   return false;
  3398 bool
  3399 nsNativeThemeCocoa::WidgetAppearanceDependsOnWindowFocus(uint8_t aWidgetType)
  3401   switch (aWidgetType) {
  3402     case NS_THEME_DIALOG:
  3403     case NS_THEME_GROUPBOX:
  3404     case NS_THEME_TAB_PANELS:
  3405     case NS_THEME_MENUPOPUP:
  3406     case NS_THEME_MENUITEM:
  3407     case NS_THEME_MENUSEPARATOR:
  3408     case NS_THEME_TOOLTIP:
  3409     case NS_THEME_SPINNER:
  3410     case NS_THEME_SPINNER_UP_BUTTON:
  3411     case NS_THEME_SPINNER_DOWN_BUTTON:
  3412     case NS_THEME_TOOLBAR_SEPARATOR:
  3413     case NS_THEME_TOOLBOX:
  3414     case NS_THEME_NUMBER_INPUT:
  3415     case NS_THEME_TEXTFIELD:
  3416     case NS_THEME_TREEVIEW:
  3417     case NS_THEME_TREEVIEW_LINE:
  3418     case NS_THEME_TEXTFIELD_MULTILINE:
  3419     case NS_THEME_LISTBOX:
  3420     case NS_THEME_RESIZER:
  3421       return false;
  3422     default:
  3423       return true;
  3427 nsITheme::Transparency
  3428 nsNativeThemeCocoa::GetWidgetTransparency(nsIFrame* aFrame, uint8_t aWidgetType)
  3430   switch (aWidgetType) {
  3431   case NS_THEME_MENUPOPUP:
  3432   case NS_THEME_TOOLTIP:
  3433     return eTransparent;
  3435   case NS_THEME_SCROLLBAR_SMALL:
  3436   case NS_THEME_SCROLLBAR:
  3437     return nsLookAndFeel::UseOverlayScrollbars() ? eTransparent : eOpaque;
  3439   case NS_THEME_STATUSBAR:
  3440     // Knowing that scrollbars and statusbars are opaque improves
  3441     // performance, because we create layers for them.
  3442     return eOpaque;
  3444   case NS_THEME_TOOLBAR:
  3445   case NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR:
  3446     return eOpaque;
  3448   default:
  3449     return eUnknownTransparency;

mercurial