Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / widget / src / cocoa / nsNativeThemeCocoa.mm
blobc223bbb7ab964d37d3734bf3e736a5f4d0277279
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4  *
5  * The contents of this file are subject to the Mozilla Public License Version
6  * 1.1 (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  * http://www.mozilla.org/MPL/
9  *
10  * Software distributed under the License is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12  * for the specific language governing rights and limitations under the
13  * License.
14  *
15  * The Original Code is mozilla.org code.
16  *
17  * The Initial Developer of the Original Code is
18  * Mike Pinkerton (pinkerton@netscape.com).
19  * Portions created by the Initial Developer are Copyright (C) 2001
20  * the Initial Developer. All Rights Reserved.
21  *
22  * Contributor(s):
23  *    Vladimir Vukicevic <vladimir@pobox.com> (HITheme rewrite)
24  *    Josh Aas <josh@mozilla.com>
25  *    Colin Barrett <cbarrett@mozilla.com>
26  *    Matthew Gregan <kinetik@flim.org>
27  *    Markus Stange <mstange@themasta.com>
28  *
29  * Alternatively, the contents of this file may be used under the terms of
30  * either of the GNU General Public License Version 2 or later (the "GPL"),
31  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
32  * in which case the provisions of the GPL or the LGPL are applicable instead
33  * of those above. If you wish to allow use of your version of this file only
34  * under the terms of either the GPL or the LGPL, and not to allow others to
35  * use your version of this file under the terms of the MPL, indicate your
36  * decision by deleting the provisions above and replace them with the notice
37  * and other provisions required by the GPL or the LGPL. If you do not delete
38  * the provisions above, a recipient may use your version of this file under
39  * the terms of any one of the MPL, the GPL or the LGPL.
40  *
41  * ***** END LICENSE BLOCK ***** */
43 #include "nsNativeThemeCocoa.h"
44 #include "nsObjCExceptions.h"
45 #include "nsIRenderingContext.h"
46 #include "nsRect.h"
47 #include "nsSize.h"
48 #include "nsThemeConstants.h"
49 #include "nsIPresShell.h"
50 #include "nsPresContext.h"
51 #include "nsIContent.h"
52 #include "nsIDocument.h"
53 #include "nsIFrame.h"
54 #include "nsIAtom.h"
55 #include "nsIEventStateManager.h"
56 #include "nsINameSpaceManager.h"
57 #include "nsPresContext.h"
58 #include "nsILookAndFeel.h"
59 #include "nsWidgetAtoms.h"
60 #include "nsToolkit.h"
61 #include "nsCocoaWindow.h"
62 #include "nsNativeThemeColors.h"
64 #include "gfxContext.h"
65 #include "gfxQuartzSurface.h"
66 #include "gfxQuartzNativeDrawing.h"
68 #define DRAW_IN_FRAME_DEBUG 0
69 #define SCROLLBARS_VISUAL_DEBUG 0
71 // private Quartz routines needed here
72 extern "C" {
73   CG_EXTERN void CGContextSetCTM(CGContextRef, CGAffineTransform);
76 // Workaround for NSCell control tint drawing
77 // Without this workaround, NSCells are always drawn with the clear control tint
78 // as long as they're not attached to an NSControl which is a subview of an active window.
79 // XXXmstange Why doesn't Webkit need this?
80 @implementation NSCell (ControlTintWorkaround)
81 - (int)_realControlTint { return [self controlTint]; }
82 @end
84 // On 10.4, NSSearchFieldCells can't draw focus rings.
85 @interface SearchFieldCellWithFocusRing : NSSearchFieldCell {} @end
87 @implementation SearchFieldCellWithFocusRing
89 - (void) drawWithFrame:(NSRect)rect inView:(NSView*)controlView
91   [super drawWithFrame:rect inView:controlView];
92   if (!nsToolkit::OnLeopardOrLater() && [self showsFirstResponder]) {
93     NSSetFocusRingStyle(NSFocusRingOnly);
94     NSBezierPath* path = [NSBezierPath bezierPath];
95     float radius = NSHeight(rect) / 2;
96     rect = NSInsetRect(rect, radius, radius);
97     [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMinX(rect), NSMinY(rect)) radius:radius startAngle:180.0 endAngle:270.0];
98     [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMaxX(rect), NSMinY(rect)) radius:radius startAngle:270.0 endAngle:360.0];
99     [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMaxX(rect), NSMaxY(rect)) radius:radius startAngle:  0.0 endAngle: 90.0];
100     [path appendBezierPathWithArcWithCenter:NSMakePoint(NSMinX(rect), NSMaxY(rect)) radius:radius startAngle: 90.0 endAngle:180.0];
101     [path closePath];
102     [path fill];
103   }
106 @end
108 // Copied from nsLookAndFeel.h
109 // Apple hasn't defined a constant for scollbars with two arrows on each end, so we'll use this one.
110 static const int kThemeScrollBarArrowsBoth = 2;
112 #define HITHEME_ORIENTATION kHIThemeOrientationNormal
113 #define MAX_FOCUS_RING_WIDTH 4
115 // These enums are for indexing into the margin array.
116 enum {
117   tigerOS,
118   leopardOS
121 enum {
122   miniControlSize,
123   smallControlSize,
124   regularControlSize
127 enum {
128   leftMargin,
129   topMargin,
130   rightMargin,
131   bottomMargin
134 static int EnumSizeForCocoaSize(NSControlSize cocoaControlSize) {
135   if (cocoaControlSize == NSMiniControlSize)
136     return miniControlSize;
137   else if (cocoaControlSize == NSSmallControlSize)
138     return smallControlSize;
139   else
140     return regularControlSize;
143 static void InflateControlRect(NSRect* rect, NSControlSize cocoaControlSize, const float marginSet[][3][4])
145   if (!marginSet)
146     return;
147   static int osIndex = nsToolkit::OnLeopardOrLater() ? leopardOS : tigerOS;
148   int controlSize = EnumSizeForCocoaSize(cocoaControlSize);
149   const float* buttonMargins = marginSet[osIndex][controlSize];
150   rect->origin.x -= buttonMargins[leftMargin];
151   rect->origin.y -= buttonMargins[bottomMargin];
152   rect->size.width += buttonMargins[leftMargin] + buttonMargins[rightMargin];
153   rect->size.height += buttonMargins[bottomMargin] + buttonMargins[topMargin];
156 static NSView* NativeViewForFrame(nsIFrame* aFrame)
158   if (!aFrame)
159     return nil;  
161   nsIWidget* widget = aFrame->GetWindow();
162   if (!widget)
163     return nil;
165   return (NSView*)widget->GetNativeData(NS_NATIVE_WIDGET);
168 static NSWindow* NativeWindowForFrame(nsIFrame* aFrame, int* aLevelsUp = NULL,
169                                       nsIWidget** aTopLevelWidget = NULL)
171   if (!aFrame)
172     return nil;  
174   nsIWidget* widget = aFrame->GetWindow();
175   if (!widget)
176     return nil;
178   nsIWidget* topLevelWidget = widget->GetTopLevelWidget(aLevelsUp);
179   if (aTopLevelWidget)
180     *aTopLevelWidget = topLevelWidget;
182   return (NSWindow*)topLevelWidget->GetNativeData(NS_NATIVE_WINDOW);
185 static BOOL FrameIsInActiveWindow(nsIFrame* aFrame)
187   nsIWidget* topLevelWidget = NULL;
188   NSWindow* win = NativeWindowForFrame(aFrame, NULL, &topLevelWidget);
189   if (!topLevelWidget || !win)
190     return YES;
192   // XUL popups, e.g. the toolbar customization popup, can't become key windows,
193   // but controls in these windows should still get the active look.
194   nsWindowType windowType;
195   topLevelWidget->GetWindowType(windowType);
196   if (windowType == eWindowType_popup)
197     return YES;
198   if ([win isSheet])
199     return [win isKeyWindow];
200   return [win isMainWindow] && ![win attachedSheet];
203 NS_IMPL_ISUPPORTS1(nsNativeThemeCocoa, nsITheme)
206 nsNativeThemeCocoa::nsNativeThemeCocoa()
208   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
210   mPushButtonCell = [[NSButtonCell alloc] initTextCell:nil];
211   [mPushButtonCell setButtonType:NSMomentaryPushInButton];
212   [mPushButtonCell setHighlightsBy:NSPushInCellMask];
214   mRadioButtonCell = [[NSButtonCell alloc] initTextCell:nil];
215   [mRadioButtonCell setButtonType:NSRadioButton];
217   mCheckboxCell = [[NSButtonCell alloc] initTextCell:nil];
218   [mCheckboxCell setButtonType:NSSwitchButton];
220   mSearchFieldCell = [[SearchFieldCellWithFocusRing alloc] initTextCell:@""];
221   [mSearchFieldCell setBezelStyle:NSTextFieldRoundedBezel];
222   [mSearchFieldCell setBezeled:YES];
223   [mSearchFieldCell setEditable:YES];
224   [mSearchFieldCell setFocusRingType:NSFocusRingTypeExterior];
226   NS_OBJC_END_TRY_ABORT_BLOCK;
229 nsNativeThemeCocoa::~nsNativeThemeCocoa()
231   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
233   [mPushButtonCell release];
234   [mRadioButtonCell release];
235   [mCheckboxCell release];
236   [mSearchFieldCell release];
238   NS_OBJC_END_TRY_ABORT_BLOCK;
241 // Limit on the area of the target rect (in pixels^2) in
242 // DrawCellWithScaling(), DrawButton() and DrawScrollbar(), above which we
243 // don't draw the object into a bitmap buffer.  This is to avoid crashes in
244 // [NSGraphicsContext graphicsContextWithGraphicsPort:flipped:] and
245 // CGContextDrawImage(), and also to avoid very poor drawing performance in
246 // CGContextDrawImage() when it scales the bitmap (particularly if xscale or
247 // yscale is less than but near 1 -- e.g. 0.9).  This value was determined
248 // by trial and error, on OS X 10.4.11 and 10.5.4, and on systems with
249 // different amounts of RAM.
250 #define BITMAP_MAX_AREA 500000
253  * Draw the given NSCell into the given cgContext.
255  * destRect - the size and position of the resulting control rectangle
256  * controlSize - the NSControlSize which will be given to the NSCell before
257  *  asking it to render
258  * naturalSize - The natural dimensions of this control.
259  *  If the control rect size is not equal to either of these, a scale
260  *  will be applied to the context so that rendering the control at the
261  *  natural size will result in it filling the destRect space.
262  *  If a control has no natural dimensions in either/both axes, pass 0.0f.
263  * minimumSize - The minimum dimensions of this control.
264  *  If the control rect size is less than the minimum for a given axis,
265  *  a scale will be applied to the context so that the minimum is used
266  *  for drawing.  If a control has no minimum dimensions in either/both
267  *  axes, pass 0.0f.
268  * marginSet - an array of margins; a multidimensional array of [2][3][4],
269  *  with the first dimension being the OS version (Tiger or Leopard),
270  *  the second being the control size (mini, small, regular), and the third
271  *  being the 4 margin values (left, top, right, bottom).
272  * flip - Whether to draw the control mirrored
273  * view - The NSView that we're drawing into. As far as I can tell, it doesn't
274  *  matter if this is really the right view; it just has to return YES when
275  *  asked for isFlipped. Otherwise we'll get drawing bugs on 10.4.
276  */
277 static void DrawCellWithScaling(NSCell *cell,
278                                 CGContextRef cgContext,
279                                 const HIRect& destRect,
280                                 NSControlSize controlSize,
281                                 NSSize naturalSize,
282                                 NSSize minimumSize,
283                                 const float marginSet[][3][4],
284                                 NSView* view)
286   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
288   NSRect drawRect = NSMakeRect(destRect.origin.x, destRect.origin.y, destRect.size.width, destRect.size.height);
290   if (naturalSize.width != 0.0f)
291     drawRect.size.width = naturalSize.width;
292   if (naturalSize.height != 0.0f)
293     drawRect.size.height = naturalSize.height;
295   // Keep aspect ratio when scaling if one dimension is free.
296   if (naturalSize.width == 0.0f && naturalSize.height != 0.0f)
297     drawRect.size.width = destRect.size.width * naturalSize.height / destRect.size.height;
298   if (naturalSize.height == 0.0f && naturalSize.width != 0.0f)
299     drawRect.size.height = destRect.size.height * naturalSize.width / destRect.size.width;
301   // Honor minimum sizes.
302   if (drawRect.size.width < minimumSize.width)
303     drawRect.size.width = minimumSize.width;
304   if (drawRect.size.height < minimumSize.height)
305     drawRect.size.height = minimumSize.height;
307   [NSGraphicsContext saveGraphicsState];
309   // Only skip the buffer if the area of our cell (in pixels^2) is too large.
310   if (drawRect.size.width * drawRect.size.height > BITMAP_MAX_AREA) {
311     // Inflate the rect Gecko gave us by the margin for the control.
312     InflateControlRect(&drawRect, controlSize, marginSet);
314     NSGraphicsContext* savedContext = [NSGraphicsContext currentContext];
315     [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:cgContext flipped:YES]];
317     [cell drawWithFrame:drawRect inView:view];
319     [NSGraphicsContext setCurrentContext:savedContext];
320   }
321   else {
322     float w = ceil(drawRect.size.width);
323     float h = ceil(drawRect.size.height);
324     NSRect tmpRect = NSMakeRect(MAX_FOCUS_RING_WIDTH, MAX_FOCUS_RING_WIDTH, w, h);
326     // inflate to figure out the frame we need to tell NSCell to draw in, to get something that's 0,0,w,h
327     InflateControlRect(&tmpRect, controlSize, marginSet);
329     // and then, expand by MAX_FOCUS_RING_WIDTH size to make sure we can capture any focus ring
330     w += MAX_FOCUS_RING_WIDTH * 2.0;
331     h += MAX_FOCUS_RING_WIDTH * 2.0;
333     CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
334     CGContextRef ctx = CGBitmapContextCreate(NULL,
335                                              (int) w, (int) h,
336                                              8, (int) w * 4,
337                                              rgb, kCGImageAlphaPremultipliedFirst);
338     CGColorSpaceRelease(rgb);
340     // We need to flip the image twice in order to avoid drawing bugs on 10.4, see bug 465069.
341     // This is the first flip transform, applied to cgContext.
342     CGContextScaleCTM(cgContext, 1.0f, -1.0f);
343     CGContextTranslateCTM(cgContext, 0.0f, -(2.0 * destRect.origin.y + destRect.size.height));
345     NSGraphicsContext* savedContext = [NSGraphicsContext currentContext];
346     [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithGraphicsPort:ctx flipped:YES]];
348     // This is the second flip transform, applied to ctx.
349     CGContextScaleCTM(ctx, 1.0f, -1.0f);
350     CGContextTranslateCTM(ctx, 0.0f, -(2.0 * tmpRect.origin.y + tmpRect.size.height));
352     [cell drawWithFrame:tmpRect inView:view];
354     [NSGraphicsContext setCurrentContext:savedContext];
356     CGImageRef img = CGBitmapContextCreateImage(ctx);
358     // Drop the image into the original destination rectangle, scaling to fit
359     // Only scale MAX_FOCUS_RING_WIDTH by xscale/yscale when the resulting rect
360     // doesn't extend beyond the overflow rect
361     float xscale = destRect.size.width / drawRect.size.width;
362     float yscale = destRect.size.height / drawRect.size.height;
363     float scaledFocusRingX = xscale < 1.0f ? MAX_FOCUS_RING_WIDTH * xscale : MAX_FOCUS_RING_WIDTH;
364     float scaledFocusRingY = yscale < 1.0f ? MAX_FOCUS_RING_WIDTH * yscale : MAX_FOCUS_RING_WIDTH;
365     CGContextDrawImage(cgContext, CGRectMake(destRect.origin.x - scaledFocusRingX,
366                                              destRect.origin.y - scaledFocusRingY,
367                                              destRect.size.width + scaledFocusRingX * 2,
368                                              destRect.size.height + scaledFocusRingY * 2),
369                        img);
371     CGImageRelease(img);
372     CGContextRelease(ctx);
373   }
375   [NSGraphicsContext restoreGraphicsState];
377 #if DRAW_IN_FRAME_DEBUG
378   CGContextSetRGBFillColor(cgContext, 0.0, 0.0, 0.5, 0.25);
379   CGContextFillRect(cgContext, destRect);
380 #endif
382   NS_OBJC_END_TRY_ABORT_BLOCK;
385 struct CellRenderSettings {
386   // The natural dimensions of the control.
387   // If a control has no natural dimensions in either/both axes, set to 0.0f.
388   NSSize naturalSizes[3];
390   // The minimum dimensions of the control.
391   // If a control has no minimum dimensions in either/both axes, set to 0.0f.
392   NSSize minimumSizes[3];
394   // A multidimensional array of [2][3][4],
395   // with the first dimension being the OS version (Tiger or Leopard),
396   // the second being the control size (mini, small, regular), and the third
397   // being the 4 margin values (left, top, right, bottom).
398   float margins[2][3][4];
402  * Draw the given NSCell into the given cgContext with a nice control size.
404  * This function is similar to DrawCellWithScaling, but it decides what
405  * control size to use based on the destRect's size.
406  * Scaling is only applied when the difference between the destRect's size
407  * and the next smaller natural size is greater than sSnapTolerance. Otherwise
408  * it snaps to the next smaller control size without scaling because unscaled
409  * controls look nicer.
410  */
411 static const float sSnapTolerance = 2.0f;
412 static void DrawCellWithSnapping(NSCell *cell,
413                                  CGContextRef cgContext,
414                                  const HIRect& destRect,
415                                  const CellRenderSettings settings,
416                                  float verticalAlignFactor,
417                                  NSView* view)
419   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
421   const float rectWidth = destRect.size.width, rectHeight = destRect.size.height;
422   const NSSize *sizes = settings.naturalSizes;
423   const NSSize miniSize = sizes[EnumSizeForCocoaSize(NSMiniControlSize)];
424   const NSSize smallSize = sizes[EnumSizeForCocoaSize(NSSmallControlSize)];
425   const NSSize regularSize = sizes[EnumSizeForCocoaSize(NSRegularControlSize)];
427   NSControlSize controlSizeX = NSRegularControlSize, controlSizeY = NSRegularControlSize;
428   HIRect drawRect = destRect;
430   if (rectWidth <= miniSize.width + sSnapTolerance && rectWidth < smallSize.width)
431     controlSizeX = NSMiniControlSize;
432   else if(rectWidth <= smallSize.width + sSnapTolerance && rectWidth < regularSize.width)
433     controlSizeX = NSSmallControlSize;
435   if (rectHeight <= miniSize.height + sSnapTolerance && rectHeight < smallSize.height)
436     controlSizeY = NSMiniControlSize;
437   else if(rectHeight <= smallSize.height + sSnapTolerance && rectHeight < regularSize.height)
438     controlSizeY = NSSmallControlSize;
440   NSControlSize controlSize = NSRegularControlSize;
441   int sizeIndex = 0;
443   // At some sizes, don't scale but snap.
444   const NSControlSize smallerControlSize =
445     EnumSizeForCocoaSize(controlSizeX) < EnumSizeForCocoaSize(controlSizeY) ?
446     controlSizeX : controlSizeY;
447   const int smallerControlSizeIndex = EnumSizeForCocoaSize(smallerControlSize);
448   const NSSize size = sizes[smallerControlSizeIndex];
449   float diffWidth = size.width ? rectWidth - size.width : 0.0f;
450   float diffHeight = size.height ? rectHeight - size.height : 0.0f;
451   if (diffWidth >= 0.0f && diffHeight >= 0.0f &&
452       diffWidth <= sSnapTolerance && diffHeight <= sSnapTolerance) {
453     // Snap to the smaller control size.
454     controlSize = smallerControlSize;
455     sizeIndex = smallerControlSizeIndex;
456     // Resize and center the drawRect.
457     if (sizes[sizeIndex].width) {
458       drawRect.origin.x += ceil((destRect.size.width - sizes[sizeIndex].width) / 2);
459       drawRect.size.width = sizes[sizeIndex].width;
460     }
461     if (sizes[sizeIndex].height) {
462       drawRect.origin.y += floor((destRect.size.height - sizes[sizeIndex].height) * verticalAlignFactor);
463       drawRect.size.height = sizes[sizeIndex].height;
464     }
465   } else {
466     // Use the larger control size.
467     controlSize = EnumSizeForCocoaSize(controlSizeX) > EnumSizeForCocoaSize(controlSizeY) ?
468                   controlSizeX : controlSizeY;
469     sizeIndex = EnumSizeForCocoaSize(controlSize);
470    }
472   [cell setControlSize:controlSize];
474   NSSize minimumSize = settings.minimumSizes ? settings.minimumSizes[sizeIndex] : NSZeroSize;
475   DrawCellWithScaling(cell, cgContext, drawRect, controlSize, sizes[sizeIndex],
476                       minimumSize, settings.margins, view);
478   NS_OBJC_END_TRY_ABORT_BLOCK;
481 static float VerticalAlignFactor(nsIFrame *aFrame)
483   if (!aFrame)
484     return 0.5f; // default: center
486   const nsStyleCoord& va = aFrame->GetStyleTextReset()->mVerticalAlign;
487   PRUint8 intval = (va.GetUnit() == eStyleUnit_Enumerated)
488                      ? va.GetIntValue()
489                      : NS_STYLE_VERTICAL_ALIGN_MIDDLE;
490   switch (intval) {
491     case NS_STYLE_VERTICAL_ALIGN_TOP:
492     case NS_STYLE_VERTICAL_ALIGN_TEXT_TOP:
493       return 0.0f;
495     case NS_STYLE_VERTICAL_ALIGN_SUB:
496     case NS_STYLE_VERTICAL_ALIGN_SUPER:
497     case NS_STYLE_VERTICAL_ALIGN_MIDDLE:
498     case NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE:
499       return 0.5f;
501     case NS_STYLE_VERTICAL_ALIGN_BASELINE:
502     case NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM:
503     case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
504       return 1.0f;
506     default:
507       NS_NOTREACHED("invalid vertical-align");
508       return 0.5f;
509   }
512 // These are the sizes that Gecko needs to request to draw if it wants
513 // to get a standard-sized Aqua radio button drawn. Note that the rects
514 // that draw these are actually a little bigger.
515 static const CellRenderSettings radioSettings = {
516   {
517     NSMakeSize(11, 11), // mini
518     NSMakeSize(13, 13), // small
519     NSMakeSize(16, 16)  // regular
520   },
521   {
522     NSZeroSize, NSZeroSize, NSZeroSize
523   },
524   {
525     { // Tiger
526       {0, 0, 0, 0},     // mini
527       {0, 2, 1, 1},     // small
528       {0, 1, 0, -1}     // regular
529     },
530     { // Leopard
531       {0, 0, 0, 0},     // mini
532       {0, 1, 1, 1},     // small
533       {0, 0, 0, 0}      // regular
534     }
535   }
538 static const CellRenderSettings checkboxSettings = {
539   {
540     NSMakeSize(11, 11), // mini
541     NSMakeSize(13, 13), // small
542     NSMakeSize(16, 16)  // regular
543   },
544   {
545     NSZeroSize, NSZeroSize, NSZeroSize
546   },
547   {
548     { // Tiger
549       {0, 1, 0, 0},     // mini
550       {0, 2, 0, 1},     // small
551       {0, 1, 0, 1}      // regular
552     },
553     { // Leopard
554       {0, 1, 0, 0},     // mini
555       {0, 1, 0, 1},     // small
556       {0, 1, 0, 1}      // regular
557     }
558   }
561 void
562 nsNativeThemeCocoa::DrawCheckboxOrRadio(CGContextRef cgContext, PRBool inCheckbox,
563                                         const HIRect& inBoxRect, PRBool inSelected,
564                                         PRBool inDisabled, PRInt32 inState,
565                                         nsIFrame* aFrame)
567   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
569   NSButtonCell *cell = inCheckbox ? mCheckboxCell : mRadioButtonCell;
571   [cell setEnabled:!inDisabled];
572   [cell setShowsFirstResponder:(inState & NS_EVENT_STATE_FOCUS)];
573   [cell setState:(inSelected ? NSOnState : NSOffState)];
574   [cell setHighlighted:((inState & NS_EVENT_STATE_ACTIVE) && (inState & NS_EVENT_STATE_HOVER))];
575   [cell setControlTint:(FrameIsInActiveWindow(aFrame) ? [NSColor currentControlTint] : NSClearControlTint)];
577   // Ensure that the control is square.
578   float length = PR_MIN(inBoxRect.size.width, inBoxRect.size.height);
579   HIRect drawRect = CGRectMake(inBoxRect.origin.x + (int)((inBoxRect.size.width - length) / 2.0f),
580                                inBoxRect.origin.y + (int)((inBoxRect.size.height - length) / 2.0f),
581                                length, length);
583   DrawCellWithSnapping(cell, cgContext, drawRect,
584                        inCheckbox ? checkboxSettings : radioSettings,
585                        VerticalAlignFactor(aFrame),
586                        NativeViewForFrame(aFrame));
588   NS_OBJC_END_TRY_ABORT_BLOCK;
591 static const CellRenderSettings searchFieldSettings = {
592   {
593     NSMakeSize(0, 16), // mini
594     NSMakeSize(0, 19), // small
595     NSMakeSize(0, 22)  // regular
596   },
597   {
598     NSMakeSize(32, 0), // mini
599     NSMakeSize(38, 0), // small
600     NSMakeSize(44, 0)  // regular
601   },
602   {
603     { // Tiger
604       {0, 0, 0, 0},     // mini
605       {0, 0, 0, 0},     // small
606       {0, 0, 0, 0}      // regular
607     },
608     { // Leopard
609       {0, 0, 0, 0},     // mini
610       {0, 0, 0, 0},     // small
611       {0, 0, 0, 0}      // regular
612     }
613   }
616 void
617 nsNativeThemeCocoa::DrawSearchField(CGContextRef cgContext, const HIRect& inBoxRect,
618                                     nsIFrame* aFrame)
620   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
622   NSSearchFieldCell* cell = mSearchFieldCell;
623   [cell setEnabled:!IsDisabled(aFrame)];
624   [cell setShowsFirstResponder:IsFocused(aFrame)];
626   DrawCellWithSnapping(cell, cgContext, inBoxRect, searchFieldSettings,
627                        VerticalAlignFactor(aFrame), NativeViewForFrame(aFrame));
629   NS_OBJC_END_TRY_ABORT_BLOCK;
633 // These are the sizes that Gecko needs to request to draw if it wants
634 // to get a standard-sized Aqua rounded bevel button drawn. Note that
635 // the rects that draw these are actually a little bigger.
636 #define NATURAL_MINI_ROUNDED_BUTTON_MIN_WIDTH 18
637 #define NATURAL_MINI_ROUNDED_BUTTON_HEIGHT 16
638 #define NATURAL_SMALL_ROUNDED_BUTTON_MIN_WIDTH 26
639 #define NATURAL_SMALL_ROUNDED_BUTTON_HEIGHT 19
640 #define NATURAL_REGULAR_ROUNDED_BUTTON_MIN_WIDTH 30
641 #define NATURAL_REGULAR_ROUNDED_BUTTON_HEIGHT 22
643 // These were calculated by testing all three sizes on the respective operating system.
644 static const float pushButtonMargins[2][3][4] =
646   { // Tiger
647     {1, 1, 1, 1}, // mini
648     {5, 1, 5, 1}, // small
649     {6, 0, 6, 2}  // regular
650   },
651   { // Leopard
652     {0, 0, 0, 0}, // mini
653     {4, 0, 4, 1}, // small
654     {5, 0, 5, 2}  // regular
655   }
658 // The height at which we start doing square buttons instead of rounded buttons
659 // Rounded buttons look bad if drawn at a height greater than 26, so at that point
660 // we switch over to doing square buttons which looks fine at any size.
661 #define DO_SQUARE_BUTTON_HEIGHT 26
663 void
664 nsNativeThemeCocoa::DrawPushButton(CGContextRef cgContext, const HIRect& inBoxRect, PRBool inIsDefault,
665                                    PRBool inDisabled, PRInt32 inState, nsIFrame* aFrame)
667   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
669   NSRect drawRect = NSMakeRect(inBoxRect.origin.x, inBoxRect.origin.y, inBoxRect.size.width, inBoxRect.size.height);
671   BOOL isActive = FrameIsInActiveWindow(aFrame);
673   [mPushButtonCell setEnabled:!inDisabled];
674   [mPushButtonCell setHighlighted:(((inState & NS_EVENT_STATE_ACTIVE) &&
675                                     (inState & NS_EVENT_STATE_HOVER) ||
676                                     (inIsDefault && !inDisabled)) && 
677                                    isActive)];
678   [mPushButtonCell setShowsFirstResponder:(inState & NS_EVENT_STATE_FOCUS) && !inDisabled && isActive];
680   // If the button is tall enough, draw the square button style so that buttons with
681   // non-standard content look good. Otherwise draw normal rounded aqua buttons.
682   if (drawRect.size.height > DO_SQUARE_BUTTON_HEIGHT) {
683     [mPushButtonCell setBezelStyle:NSShadowlessSquareBezelStyle];
684     DrawCellWithScaling(mPushButtonCell, cgContext, inBoxRect, NSRegularControlSize,
685                         NSZeroSize, NSMakeSize(14, 0), NULL, NativeViewForFrame(aFrame));
686   } else {
687     [mPushButtonCell setBezelStyle:NSRoundedBezelStyle];
689     // Figure out what size cell control we're going to draw and grab its
690     // natural height and min width.
691     NSControlSize controlSize = NSRegularControlSize;
692     float naturalHeight = NATURAL_REGULAR_ROUNDED_BUTTON_HEIGHT;
693     float minWidth = NATURAL_REGULAR_ROUNDED_BUTTON_MIN_WIDTH;
694     if (drawRect.size.height <= NATURAL_MINI_ROUNDED_BUTTON_HEIGHT &&
695         drawRect.size.width >= NATURAL_MINI_ROUNDED_BUTTON_MIN_WIDTH) {
696       controlSize = NSMiniControlSize;
697       naturalHeight = NATURAL_MINI_ROUNDED_BUTTON_HEIGHT;
698       minWidth = NATURAL_MINI_ROUNDED_BUTTON_MIN_WIDTH;
699     }
700     else if (drawRect.size.height <= NATURAL_SMALL_ROUNDED_BUTTON_HEIGHT &&
701              drawRect.size.width >= NATURAL_SMALL_ROUNDED_BUTTON_MIN_WIDTH) {
702       controlSize = NSSmallControlSize;
703       naturalHeight = NATURAL_SMALL_ROUNDED_BUTTON_HEIGHT;
704       minWidth = NATURAL_SMALL_ROUNDED_BUTTON_MIN_WIDTH;
705     }
706     [mPushButtonCell setControlSize:controlSize];
708     DrawCellWithScaling(mPushButtonCell, cgContext, inBoxRect, controlSize,
709                         NSMakeSize(0.0f, naturalHeight),
710                         NSMakeSize(minWidth, 0.0f), pushButtonMargins,
711                         NativeViewForFrame(aFrame));
712   }
714 #if DRAW_IN_FRAME_DEBUG
715   CGContextSetRGBFillColor(cgContext, 0.0, 0.0, 0.5, 0.25);
716   CGContextFillRect(cgContext, inBoxRect);
717 #endif
719   NS_OBJC_END_TRY_ABORT_BLOCK;
723 void
724 nsNativeThemeCocoa::DrawButton(CGContextRef cgContext, ThemeButtonKind inKind,
725                                const HIRect& inBoxRect, PRBool inIsDefault, PRBool inDisabled,
726                                ThemeButtonValue inValue, ThemeButtonAdornment inAdornment,
727                                PRInt32 inState, nsIFrame* aFrame)
729   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
731   BOOL isActive = FrameIsInActiveWindow(aFrame);
733   HIThemeButtonDrawInfo bdi;
734   bdi.version = 0;
735   bdi.kind = inKind;
736   bdi.value = inValue;
737   bdi.adornment = inAdornment;
739   if (inDisabled) {
740     bdi.state = kThemeStateUnavailable;
741   }
742   else if ((inState & NS_EVENT_STATE_ACTIVE) && (inState & NS_EVENT_STATE_HOVER)) {
743     bdi.state = kThemeStatePressed;
744   }
745   else {
746     if (inKind == kThemeArrowButton)
747       bdi.state = kThemeStateUnavailable; // these are always drawn as unavailable
748     else if (!isActive && (inKind == kThemeListHeaderButton || inKind == kThemePopupButton))
749       bdi.state = kThemeStateInactive;
750     else
751       bdi.state = kThemeStateActive;
752   }
754   if (inState & NS_EVENT_STATE_FOCUS && isActive)
755     bdi.adornment |= kThemeAdornmentFocus;
757   if (inIsDefault && !inDisabled)
758     bdi.adornment |= kThemeAdornmentDefault;
760   HIRect drawFrame = inBoxRect;
761   PRBool needsScaling = PR_FALSE;
762   int drawWidth = 0, drawHeight = 0;
764   if (inKind == kThemePopupButton) {
765     /* popup buttons draw outside their frame by 1 pixel on each side and
766      * two on the bottom but of the bottom two pixels one is a 'shadow'
767      * and not the frame itself.  That extra pixel should be handled
768      * by GetWidgetOverflow, but we already extend each widget's overflow
769      * by 4px to handle a potential focus ring.
770      */
772     if (nsToolkit::OnLeopardOrLater()) {
773       /* Leopard will happily scale up for buttons that are sized 20px or higher,
774        * drawing 1px below the actual requested area.  (So 20px == 21px.)
775        * but anything below that will be clamped:
776        *  requested: 20 actual: 21 (handled above)
777        *  requested: 19 actual: 18 <- note that there is no way to draw a dropdown that's exactly 20 px in size
778        *  requested: 18 actual: 18
779        *  requested: 17 actual: 18
780        *  requested: 16 actual: 15 (min size)
781        * For those, draw to a buffer and scale
782        */
783       if (drawFrame.size.height != 18 && drawFrame.size.height != 15) {
784         if (drawFrame.size.height > 20) {
785           drawFrame.size.width -= 2;
786           drawFrame.origin.x += 1;
787           drawFrame.size.height -= 1;
788         }
789         else {
790           // pick which native height to use for the small scale
791           float nativeHeight = 15.0f;
792           if (drawFrame.size.height > 16)
793             nativeHeight = 18.0f;
795           drawWidth = (int) drawFrame.size.width;
796           drawHeight = (int) nativeHeight;
798           needsScaling = PR_TRUE;
799         }
800       }
801     }
802     else {
803       // leave things alone on Tiger
804       drawFrame.size.height -= 1;
805     }
806   } else if (inKind == kThemeListHeaderButton) {
807     CGContextClipToRect(cgContext, inBoxRect);
808     // Always remove the top border.
809     drawFrame.origin.y -= 1;
810     drawFrame.size.height += 1;
811     // Remove the left border in LTR mode and the right border in RTL mode.
812     drawFrame.size.width += 1;
813     PRBool isLast = IsLastTreeHeaderCell(aFrame);
814     if (isLast)
815       drawFrame.size.width += 1; // Also remove the other border.
816     if (!IsFrameRTL(aFrame) || isLast)
817       drawFrame.origin.x -= 1;
818   }
820   // Fall back to no bitmap buffer (and no scaling) if the area of our button
821   // (in pixels^2) is too large.
822   if (!needsScaling || (drawWidth * drawHeight > BITMAP_MAX_AREA)) {
823     HIThemeDrawButton(&drawFrame, &bdi, cgContext, kHIThemeOrientationNormal, NULL);
824   } else {
825     int w = drawWidth + MAX_FOCUS_RING_WIDTH*2;
826     int h = drawHeight + MAX_FOCUS_RING_WIDTH*2;
828     CGColorSpaceRef rgb = CGColorSpaceCreateDeviceRGB();
829     CGContextRef ctx = CGBitmapContextCreate(NULL, w, h, 8, w * 4,
830                                              rgb, kCGImageAlphaPremultipliedFirst);
831     CGColorSpaceRelease(rgb);
833     // Flip the context
834     CGContextTranslateCTM(ctx, 0.0f, h);
835     CGContextScaleCTM(ctx, 1.0f, -1.0f);
837     // then draw the button (offset by the focus ring size
838     CGRect tmpFrame = CGRectMake(MAX_FOCUS_RING_WIDTH, MAX_FOCUS_RING_WIDTH, drawWidth, drawHeight);
839     HIThemeDrawButton(&tmpFrame, &bdi, ctx, kHIThemeOrientationNormal, NULL);
841     CGImageRef img = CGBitmapContextCreateImage(ctx);
842     CGRect imgRect = CGRectMake(drawFrame.origin.x - MAX_FOCUS_RING_WIDTH,
843                                 drawFrame.origin.y - MAX_FOCUS_RING_WIDTH,
844                                 drawFrame.size.width + MAX_FOCUS_RING_WIDTH * 2.0,
845                                 drawFrame.size.height + MAX_FOCUS_RING_WIDTH * 2.0);
847     // And then flip the main context here so that the image gets drawn right-side up
848     CGAffineTransform ctm = CGContextGetCTM (cgContext);
850     CGContextTranslateCTM (cgContext, imgRect.origin.x, imgRect.origin.y + imgRect.size.height);
851     CGContextScaleCTM (cgContext, 1.0, -1.0);
853     imgRect.origin.x = imgRect.origin.y = 0.0f;
855     // See comment about why we don't scale MAX_FOCUS_RING in DrawCellWithScaling
856     CGContextDrawImage(cgContext, imgRect, img);
858     CGContextSetCTM (cgContext, ctm);
860     CGImageRelease(img);
861     CGContextRelease(ctx);
862   }
864 #if DRAW_IN_FRAME_DEBUG
865   CGContextSetRGBFillColor(cgContext, 0.0, 0.0, 0.5, 0.25);
866   CGContextFillRect(cgContext, inBoxRect);
867 #endif
869   NS_OBJC_END_TRY_ABORT_BLOCK;
873 void
874 nsNativeThemeCocoa::DrawSpinButtons(CGContextRef cgContext, ThemeButtonKind inKind,
875                                     const HIRect& inBoxRect, PRBool inDisabled,
876                                     ThemeDrawState inDrawState,
877                                     ThemeButtonAdornment inAdornment,
878                                     PRInt32 inState, nsIFrame* aFrame)
880   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
882   HIThemeButtonDrawInfo bdi;
883   bdi.version = 0;
884   bdi.kind = inKind;
885   bdi.value = kThemeButtonOff;
886   bdi.adornment = inAdornment;
888   if (inDisabled)
889     bdi.state = kThemeStateUnavailable;
890   else
891     bdi.state = FrameIsInActiveWindow(aFrame) ? inDrawState : kThemeStateActive;
893   HIThemeDrawButton(&inBoxRect, &bdi, cgContext, HITHEME_ORIENTATION, NULL);
895   NS_OBJC_END_TRY_ABORT_BLOCK;
899 void
900 nsNativeThemeCocoa::DrawFrame(CGContextRef cgContext, HIThemeFrameKind inKind,
901                               const HIRect& inBoxRect, PRBool inIsDisabled, PRInt32 inState)
903   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
905   HIThemeFrameDrawInfo fdi;
906   fdi.version = 0;
907   fdi.kind = inKind;
909   // We don't ever set an inactive state for this because it doesn't
910   // look right (see other apps).
911   fdi.state = inIsDisabled ? kThemeStateUnavailable : kThemeStateActive;
913   // for some reason focus rings on listboxes draw incorrectly
914   if (inKind == kHIThemeFrameListBox)
915     fdi.isFocused = 0;
916   else
917     fdi.isFocused = (inState & NS_EVENT_STATE_FOCUS) != 0;
919   // HIThemeDrawFrame takes the rect for the content area of the frame, not
920   // the bounding rect for the frame. Here we reduce the size of the rect we
921   // will pass to make it the size of the content.
922   HIRect drawRect = inBoxRect;
923   if (inKind == kHIThemeFrameTextFieldSquare) {
924     SInt32 frameOutset = 0;
925     ::GetThemeMetric(kThemeMetricEditTextFrameOutset, &frameOutset);
926     drawRect.origin.x += frameOutset;
927     drawRect.origin.y += frameOutset;
928     drawRect.size.width -= frameOutset * 2;
929     drawRect.size.height -= frameOutset * 2;
930   }
931   else if (inKind == kHIThemeFrameListBox) {
932     SInt32 frameOutset = 0;
933     ::GetThemeMetric(kThemeMetricListBoxFrameOutset, &frameOutset);
934     drawRect.origin.x += frameOutset;
935     drawRect.origin.y += frameOutset;
936     drawRect.size.width -= frameOutset * 2;
937     drawRect.size.height -= frameOutset * 2;
938   }
940 #if DRAW_IN_FRAME_DEBUG
941   CGContextSetRGBFillColor(cgContext, 0.0, 0.0, 0.5, 0.25);
942   CGContextFillRect(cgContext, inBoxRect);
943 #endif
945   HIThemeDrawFrame(&drawRect, &fdi, cgContext, HITHEME_ORIENTATION);
947   NS_OBJC_END_TRY_ABORT_BLOCK;
951 void
952 nsNativeThemeCocoa::DrawProgress(CGContextRef cgContext, const HIRect& inBoxRect,
953                                  PRBool inIsIndeterminate, PRBool inIsHorizontal,
954                                  PRInt32 inValue, PRInt32 inMaxValue,
955                                  nsIFrame* aFrame)
957   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
959   HIThemeTrackDrawInfo tdi;
961   PRInt32 stepsPerSecond = inIsIndeterminate ? 60 : 30;
962   PRInt32 milliSecondsPerStep = 1000 / stepsPerSecond;
964   tdi.version = 0;
965   tdi.kind = inIsIndeterminate ? kThemeMediumIndeterminateBar: kThemeMediumProgressBar;
966   tdi.bounds = inBoxRect;
967   tdi.min = 0;
968   tdi.max = inMaxValue;
969   tdi.value = inValue;
970   tdi.attributes = inIsHorizontal ? kThemeTrackHorizontal : 0;
971   tdi.enableState = FrameIsInActiveWindow(aFrame) ? kThemeTrackActive : kThemeTrackInactive;
972   tdi.trackInfo.progress.phase = PR_IntervalToMilliseconds(PR_IntervalNow()) /
973                                  milliSecondsPerStep % 16;
975   HIThemeDrawTrack(&tdi, NULL, cgContext, HITHEME_ORIENTATION);
977   NS_OBJC_END_TRY_ABORT_BLOCK;
981 void
982 nsNativeThemeCocoa::DrawTabPanel(CGContextRef cgContext, const HIRect& inBoxRect,
983                                  nsIFrame* aFrame)
985   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
987   HIThemeTabPaneDrawInfo tpdi;
989   tpdi.version = 1;
990   tpdi.state = FrameIsInActiveWindow(aFrame) ? kThemeStateActive : kThemeStateInactive;
991   tpdi.direction = kThemeTabNorth;
992   tpdi.size = kHIThemeTabSizeNormal;
993   tpdi.kind = kHIThemeTabKindNormal;
995   HIThemeDrawTabPane(&inBoxRect, &tpdi, cgContext, HITHEME_ORIENTATION);
997   NS_OBJC_END_TRY_ABORT_BLOCK;
1001 void
1002 nsNativeThemeCocoa::DrawScale(CGContextRef cgContext, const HIRect& inBoxRect,
1003                               PRBool inIsDisabled, PRInt32 inState,
1004                               PRBool inIsVertical, PRBool inIsReverse,
1005                               PRInt32 inCurrentValue, PRInt32 inMinValue,
1006                               PRInt32 inMaxValue, nsIFrame* aFrame)
1008   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1010   HIThemeTrackDrawInfo tdi;
1012   tdi.version = 0;
1013   tdi.kind = kThemeMediumSlider;
1014   tdi.bounds = inBoxRect;
1015   tdi.min = inMinValue;
1016   tdi.max = inMaxValue;
1017   tdi.value = inCurrentValue;
1018   tdi.attributes = kThemeTrackShowThumb;
1019   if (!inIsVertical)
1020     tdi.attributes |= kThemeTrackHorizontal;
1021   if (inIsReverse)
1022     tdi.attributes |= kThemeTrackRightToLeft;
1023   if (inState & NS_EVENT_STATE_FOCUS)
1024     tdi.attributes |= kThemeTrackHasFocus;
1025   if (inIsDisabled)
1026     tdi.enableState = kThemeTrackDisabled;
1027   else
1028     tdi.enableState = FrameIsInActiveWindow(aFrame) ? kThemeTrackActive : kThemeTrackInactive;
1029   tdi.trackInfo.slider.thumbDir = kThemeThumbPlain;
1030   tdi.trackInfo.slider.pressState = 0;
1032   HIThemeDrawTrack(&tdi, NULL, cgContext, HITHEME_ORIENTATION);
1034   NS_OBJC_END_TRY_ABORT_BLOCK;
1038 #define NATURAL_MINI_TAB_BUTTON_HEIGHT    17
1039 #define NATURAL_SMALL_TAB_BUTTON_HEIGHT   20
1040 #define NATURAL_REGULAR_TAB_BUTTON_HEIGHT 23
1043 void
1044 nsNativeThemeCocoa::DrawTab(CGContextRef cgContext, HIRect inBoxRect,
1045                             PRInt32 inState, nsIFrame* aFrame)
1047   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1049   HIThemeTabDrawInfo tdi;
1050   tdi.version = 1;
1052   PRBool isSelected = IsSelectedTab(aFrame);
1053   PRBool isDisabled = IsDisabled(aFrame);
1054   if (isSelected) {
1055     if (isDisabled) 
1056       tdi.style = kThemeTabFrontUnavailable;
1057     else
1058       tdi.style = FrameIsInActiveWindow(aFrame) ? kThemeTabFront : kThemeTabFrontInactive;
1059   } else {
1060     if (isDisabled)
1061       tdi.style = kThemeTabNonFrontUnavailable;
1062     else if ((inState & NS_EVENT_STATE_ACTIVE) && (inState & NS_EVENT_STATE_HOVER))
1063       tdi.style = kThemeTabNonFrontPressed;
1064     else
1065       tdi.style = FrameIsInActiveWindow(aFrame) ? kThemeTabNonFront : kThemeTabNonFrontInactive;
1066   }
1068   tdi.direction = kThemeTabNorth;
1069   tdi.size = kHIThemeTabSizeNormal;
1070   if (inBoxRect.size.height < NATURAL_REGULAR_TAB_BUTTON_HEIGHT)
1071     tdi.size = kHIThemeTabSizeSmall;
1072   if (inBoxRect.size.height < NATURAL_SMALL_TAB_BUTTON_HEIGHT)
1073     tdi.size = kHIThemeTabSizeMini;
1075   PRBool isRTL = IsFrameRTL(aFrame);
1076   PRBool isFirst = isRTL ? IsLastTab(aFrame) : IsFirstTab(aFrame);
1077   PRBool isLast = isRTL ? IsFirstTab(aFrame) : IsLastTab(aFrame);
1079   if (isFirst && isLast)
1080     tdi.position = kHIThemeTabPositionOnly;
1081   else if (isFirst)
1082     tdi.position = kHIThemeTabPositionFirst;
1083   else if (isLast)
1084     tdi.position = kHIThemeTabPositionLast;
1085   else
1086     tdi.position = kHIThemeTabPositionMiddle;
1088   // Tab separator management:
1089   // Normal tabs only draw their left separator, in the leftmost pixel row of
1090   // their frame. Selected tabs additionally draw their right separator, outside
1091   // of their frame. To prevent overlapping, the tab to the right of the
1092   // selected tab shouldn't draw its left separator.
1093   tdi.adornment = kHIThemeTabAdornmentNone;
1094   if (isRTL ? IsBeforeSelectedTab(aFrame) : IsAfterSelectedTab(aFrame)) {
1095     if (nsToolkit::OnLeopardOrLater()) {
1096       // On Leopard, the tab's left edge must be shifted 1px to the right.
1097       // On Tiger, this happens automatically when no leading separator is drawn.
1098       inBoxRect.origin.x += 1;
1099       inBoxRect.size.width -= 1;
1100     }
1101   }
1102   else {
1103     tdi.adornment = kHIThemeTabAdornmentLeadingSeparator;
1104   }
1106   if (isSelected && !isLast) {
1107     tdi.adornment |= kHIThemeTabAdornmentTrailingSeparator;
1108     if (nsToolkit::OnLeopardOrLater()) {
1109       // On Tiger, the right separator is drawn outside of the frame.
1110       // On Leopard, the right edge must be shifted 1px to the right.
1111       inBoxRect.size.width += 1;
1112     }
1113   }
1114   
1115   if (inState & NS_EVENT_STATE_FOCUS)
1116     tdi.adornment |= kThemeAdornmentFocus;
1118   HIThemeDrawTab(&inBoxRect, &tdi, cgContext, HITHEME_ORIENTATION, NULL);
1120   NS_OBJC_END_TRY_ABORT_BLOCK;
1124 static inline UInt8
1125 ConvertToPressState(PRInt32 aButtonState, UInt8 aPressState)
1127   // If the button is pressed, return the press state passed in. Otherwise, return 0.
1128   return ((aButtonState & NS_EVENT_STATE_ACTIVE) && (aButtonState & NS_EVENT_STATE_HOVER)) ? aPressState : 0;
1132 void 
1133 nsNativeThemeCocoa::GetScrollbarPressStates(nsIFrame *aFrame, PRInt32 aButtonStates[])
1135   static nsIContent::AttrValuesArray attributeValues[] = {
1136     &nsWidgetAtoms::scrollbarUpTop,
1137     &nsWidgetAtoms::scrollbarDownTop,
1138     &nsWidgetAtoms::scrollbarUpBottom,
1139     &nsWidgetAtoms::scrollbarDownBottom,
1140     nsnull
1141   };
1143   // Get the state of any scrollbar buttons in our child frames
1144   for (nsIFrame *childFrame = aFrame->GetFirstChild(nsnull); 
1145        childFrame;
1146        childFrame = childFrame->GetNextSibling()) {
1148     nsIContent *childContent = childFrame->GetContent();
1149     if (!childContent) continue;
1150     PRInt32 attrIndex = childContent->FindAttrValueIn(kNameSpaceID_None, nsWidgetAtoms::sbattr, 
1151                                                       attributeValues, eCaseMatters);
1152     if (attrIndex < 0) continue;
1154     PRInt32 currentState = GetContentState(childFrame, NS_THEME_BUTTON);
1155     aButtonStates[attrIndex] = currentState;
1156   }
1160 // Both of the following sets of numbers were derived by loading the testcase in
1161 // bmo bug 380185 in Safari and observing its behavior for various heights of scrollbar.
1162 // These magic numbers are the minimum sizes we can draw a scrollbar and still 
1163 // have room for everything to display, including the thumb
1164 #define MIN_SCROLLBAR_SIZE_WITH_THUMB 61
1165 #define MIN_SMALL_SCROLLBAR_SIZE_WITH_THUMB 49
1166 // And these are the minimum sizes if we don't draw the thumb
1167 #define MIN_SCROLLBAR_SIZE 56
1168 #define MIN_SMALL_SCROLLBAR_SIZE 46
1170 void
1171 nsNativeThemeCocoa::GetScrollbarDrawInfo(HIThemeTrackDrawInfo& aTdi, nsIFrame *aFrame, 
1172                                          const HIRect& aRect, PRBool aShouldGetButtonStates)
1174   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1176   PRInt32 curpos = CheckIntAttr(aFrame, nsWidgetAtoms::curpos, 0);
1177   PRInt32 minpos = CheckIntAttr(aFrame, nsWidgetAtoms::minpos, 0);
1178   PRInt32 maxpos = CheckIntAttr(aFrame, nsWidgetAtoms::maxpos, 100);
1179   PRInt32 thumbSize = CheckIntAttr(aFrame, nsWidgetAtoms::pageincrement, 10);
1181   PRBool isHorizontal = aFrame->GetContent()->AttrValueIs(kNameSpaceID_None, nsWidgetAtoms::orient, 
1182                                                           nsWidgetAtoms::horizontal, eCaseMatters);
1183   PRBool isSmall = aFrame->GetStyleDisplay()->mAppearance == NS_THEME_SCROLLBAR_SMALL;
1185   aTdi.version = 0;
1186   aTdi.kind = isSmall ? kThemeSmallScrollBar : kThemeMediumScrollBar;
1187   aTdi.bounds = aRect;
1188   aTdi.min = minpos;
1189   aTdi.max = maxpos;
1190   aTdi.value = curpos;
1191   aTdi.attributes = 0;
1192   aTdi.enableState = kThemeTrackActive;
1193   if (isHorizontal)
1194     aTdi.attributes |= kThemeTrackHorizontal;
1196   aTdi.trackInfo.scrollbar.viewsize = (SInt32)thumbSize;
1198   // This should be done early on so things like "kThemeTrackNothingToScroll" can
1199   // override the active enable state.
1200   aTdi.enableState = FrameIsInActiveWindow(aFrame) ? kThemeTrackActive : kThemeTrackInactive;
1202   /* Only display features if we have enough room for them.
1203    * Gecko still maintains the scrollbar info; this is just a visual issue (bug 380185).
1204    */
1205   PRInt32 longSideLength = (PRInt32)(isHorizontal ? (aRect.size.width) : (aRect.size.height));
1206   if (longSideLength >= (isSmall ? MIN_SMALL_SCROLLBAR_SIZE_WITH_THUMB : MIN_SCROLLBAR_SIZE_WITH_THUMB)) {
1207     aTdi.attributes |= kThemeTrackShowThumb;
1208   }
1209   else if (longSideLength < (isSmall ? MIN_SMALL_SCROLLBAR_SIZE : MIN_SCROLLBAR_SIZE)) {
1210     aTdi.enableState = kThemeTrackNothingToScroll;
1211     return;
1212   }
1214   // Only go get these scrollbar button states if we need it. For example, there's no reaon to look up scrollbar button 
1215   // states when we're only creating a TrackDrawInfo to determine the size of the thumb.
1216   if (aShouldGetButtonStates) {
1217     PRInt32 buttonStates[] = {0, 0, 0, 0};
1218     GetScrollbarPressStates(aFrame, buttonStates);
1219     ThemeScrollBarArrowStyle arrowStyle;
1220     ::GetThemeScrollBarArrowStyle(&arrowStyle);
1221     // If all four buttons are visible
1222     if (arrowStyle == kThemeScrollBarArrowsBoth) {
1223       aTdi.trackInfo.scrollbar.pressState = ConvertToPressState(buttonStates[0], kThemeTopOutsideArrowPressed) |
1224                                             ConvertToPressState(buttonStates[1], kThemeTopInsideArrowPressed) |
1225                                             ConvertToPressState(buttonStates[2], kThemeBottomInsideArrowPressed) |
1226                                             ConvertToPressState(buttonStates[3], kThemeBottomOutsideArrowPressed);
1227     } else {
1228       // It seems that unless all four buttons are showing, kThemeTopOutsideArrowPressed is the correct constant for
1229       // the up scrollbar button.
1230       aTdi.trackInfo.scrollbar.pressState = ConvertToPressState(buttonStates[0], kThemeTopOutsideArrowPressed) |
1231                                             ConvertToPressState(buttonStates[1], kThemeBottomOutsideArrowPressed) |
1232                                             ConvertToPressState(buttonStates[2], kThemeTopOutsideArrowPressed) |
1233                                             ConvertToPressState(buttonStates[3], kThemeBottomOutsideArrowPressed);
1234     }
1235   }
1237   NS_OBJC_END_TRY_ABORT_BLOCK;
1241 void
1242 nsNativeThemeCocoa::DrawScrollbar(CGContextRef aCGContext, const HIRect& aBoxRect, nsIFrame *aFrame)
1244   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1246   // HIThemeDrawTrack is buggy with rotations and scaling
1247   CGAffineTransform savedCTM = CGContextGetCTM(aCGContext);
1248   PRBool drawDirect;
1249   HIRect drawRect = aBoxRect;
1251   if (savedCTM.a == 1.0f && savedCTM.b == 0.0f &&
1252       savedCTM.c == 0.0f && (savedCTM.d == 1.0f || savedCTM.d == -1.0f))
1253   {
1254     drawDirect = TRUE;
1255   } else {
1256     drawRect.origin.x = drawRect.origin.y = 0.0f;
1257     drawDirect = FALSE;
1258   }
1260   HIThemeTrackDrawInfo tdi;
1261   GetScrollbarDrawInfo(tdi, aFrame, drawRect, PR_TRUE); //True means we want the press states
1263   // Fall back to no bitmap buffer if the area of our scrollbar (in pixels^2)
1264   // is too large.
1265   if (drawDirect || (aBoxRect.size.width * aBoxRect.size.height > BITMAP_MAX_AREA)) {
1266     ::HIThemeDrawTrack(&tdi, NULL, aCGContext, HITHEME_ORIENTATION);
1267   } else {
1268     // Note that NSScroller can draw transformed just fine, but HITheme can't.
1269     // However, we can't make NSScroller's parts light up easily (depressed buttons, etc.)
1270     // This is very frustrating.
1272     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
1273     CGContextRef bitmapctx = CGBitmapContextCreate(NULL,
1274                                                    (size_t) ceil(drawRect.size.width),
1275                                                    (size_t) ceil(drawRect.size.height),
1276                                                    8,
1277                                                    (size_t) ceil(drawRect.size.width) * 4,
1278                                                    colorSpace,
1279                                                    kCGImageAlphaPremultipliedFirst);
1280     CGColorSpaceRelease(colorSpace);
1282     // HITheme always wants to draw into a flipped context, or things
1283     // get confused.
1284     CGContextTranslateCTM(bitmapctx, 0.0f, aBoxRect.size.height);
1285     CGContextScaleCTM(bitmapctx, 1.0f, -1.0f);
1287     HIThemeDrawTrack(&tdi, NULL, bitmapctx, HITHEME_ORIENTATION);
1289     CGImageRef bitmap = CGBitmapContextCreateImage(bitmapctx);
1291     CGAffineTransform ctm = CGContextGetCTM(aCGContext);
1293     // We need to unflip, so that we can do a DrawImage without getting a flipped image.
1294     CGContextTranslateCTM(aCGContext, 0.0f, aBoxRect.size.height);
1295     CGContextScaleCTM(aCGContext, 1.0f, -1.0f);
1297     CGContextDrawImage(aCGContext, aBoxRect, bitmap);
1299     CGContextSetCTM(aCGContext, ctm);
1301     CGImageRelease(bitmap);
1302     CGContextRelease(bitmapctx);
1303   }
1305   NS_OBJC_END_TRY_ABORT_BLOCK;
1309 nsIFrame*
1310 nsNativeThemeCocoa::GetParentScrollbarFrame(nsIFrame *aFrame)
1312   // Walk our parents to find a scrollbar frame
1313   nsIFrame *scrollbarFrame = aFrame;
1314   do {
1315     if (scrollbarFrame->GetType() == nsWidgetAtoms::scrollbarFrame) break;
1316   } while ((scrollbarFrame = scrollbarFrame->GetParent()));
1317   
1318   // We return null if we can't find a parent scrollbar frame
1319   return scrollbarFrame;
1323 void
1324 nsNativeThemeCocoa::DrawUnifiedToolbar(CGContextRef cgContext, const HIRect& inBoxRect,
1325                                        nsIFrame *aFrame)
1327   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
1329   float titlebarHeight = 0;
1330   int levelsUp = 0;
1331   NSWindow* win = NativeWindowForFrame(aFrame, &levelsUp);
1333   // If the toolbar is directly below the titlebar in the top level view of a ToolbarWindow
1334   if ([win isKindOfClass:[ToolbarWindow class]] && levelsUp == 0 &&
1335       inBoxRect.origin.y <= 0) {
1336     // Consider the titlebar height when calculating the gradient.
1337     titlebarHeight = [(ToolbarWindow*)win titlebarHeight];
1338     // Notify the window about the toolbar's height so that it can draw the
1339     // correct gradient in the titlebar.
1340     [(ToolbarWindow*)win setUnifiedToolbarHeight:inBoxRect.size.height];
1341   }
1342   
1343   BOOL isMain = win ? [win isMainWindow] : YES;
1345   // Draw the gradient
1346   UnifiedGradientInfo info = { titlebarHeight, inBoxRect.size.height, isMain, NO };
1347   struct CGFunctionCallbacks callbacks = { 0, nsCocoaWindow::UnifiedShading, NULL };
1348   CGFunctionRef function = CGFunctionCreate(&info, 1,  NULL, 4, NULL, &callbacks);
1349   float srcY = inBoxRect.origin.y;
1350   float dstY = srcY + inBoxRect.size.height - 1;
1351   CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
1352   CGShadingRef shading = CGShadingCreateAxial(colorSpace,
1353                                               CGPointMake(0, srcY),
1354                                               CGPointMake(0, dstY), function,
1355                                               NO, NO);
1356   CGColorSpaceRelease(colorSpace);
1357   CGFunctionRelease(function);
1358   CGContextClipToRect(cgContext, inBoxRect);
1359   CGContextDrawShading(cgContext, shading);
1360   CGShadingRelease(shading);
1362   // Draw the border at the bottom of the toolbar.
1363   [NativeGreyColorAsNSColor(headerBorderGrey, isMain) set];
1364   NSRectFill(NSMakeRect(inBoxRect.origin.x, inBoxRect.origin.y +
1365                         inBoxRect.size.height - 1.0f, inBoxRect.size.width, 1.0f));
1367   NS_OBJC_END_TRY_ABORT_BLOCK;
1371 NS_IMETHODIMP
1372 nsNativeThemeCocoa::DrawWidgetBackground(nsIRenderingContext* aContext, nsIFrame* aFrame,
1373                                          PRUint8 aWidgetType, const nsRect& aRect,
1374                                          const nsRect& aDirtyRect)
1376   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1378   // setup to draw into the correct port
1379   nsCOMPtr<nsIDeviceContext> dctx;
1380   aContext->GetDeviceContext(*getter_AddRefs(dctx));
1381   PRInt32 p2a = dctx->AppUnitsPerDevPixel();
1383   gfxRect nativeDirtyRect(aDirtyRect.x, aDirtyRect.y, aDirtyRect.width, aDirtyRect.height);
1384   gfxRect nativeWidgetRect(aRect.x, aRect.y, aRect.width, aRect.height);
1385   nativeWidgetRect.ScaleInverse(gfxFloat(p2a));
1386   nativeDirtyRect.ScaleInverse(gfxFloat(p2a));
1387   nativeWidgetRect.Round();
1388   if (nativeWidgetRect.IsEmpty())
1389     return NS_OK; // Don't attempt to draw invisible widgets.
1391   nsRefPtr<gfxContext> thebesCtx = aContext->ThebesContext();
1392   if (!thebesCtx)
1393     return NS_ERROR_FAILURE;
1395   gfxQuartzNativeDrawing nativeDrawing(thebesCtx, nativeDirtyRect);
1397   CGContextRef cgContext = nativeDrawing.BeginNativeDrawing();
1398   if (cgContext == nsnull) {
1399     // The Quartz surface handles 0x0 surfaces by internally
1400     // making all operations no-ops; there's no cgcontext created for them.
1401     // Unfortunately, this means that callers that want to render
1402     // directly to the CGContext need to be aware of this quirk.
1403     return NS_OK;
1404   }
1406 #if 0
1407   if (1 /*aWidgetType == NS_THEME_TEXTFIELD*/) {
1408     fprintf(stderr, "Native theme drawing widget %d [%p] dis:%d in rect [%d %d %d %d]\n",
1409             aWidgetType, aFrame, IsDisabled(aFrame), aRect.x, aRect.y, aRect.width, aRect.height);
1410     fprintf(stderr, "Cairo matrix: [%f %f %f %f %f %f]\n",
1411             mat.xx, mat.yx, mat.xy, mat.yy, mat.x0, mat.y0);
1412     fprintf(stderr, "Native theme xform[0]: [%f %f %f %f %f %f]\n",
1413             mm0.a, mm0.b, mm0.c, mm0.d, mm0.tx, mm0.ty);
1414     CGAffineTransform mm = CGContextGetCTM(cgContext);
1415     fprintf(stderr, "Native theme xform[1]: [%f %f %f %f %f %f]\n",
1416             mm.a, mm.b, mm.c, mm.d, mm.tx, mm.ty);
1417   }
1418 #endif
1420   CGRect macRect = CGRectMake(nativeWidgetRect.X(), nativeWidgetRect.Y(),
1421                               nativeWidgetRect.Width(), nativeWidgetRect.Height());
1423 #if 0
1424   fprintf(stderr, "    --> macRect %f %f %f %f\n",
1425           macRect.origin.x, macRect.origin.y, macRect.size.width, macRect.size.height);
1426   CGRect bounds = CGContextGetClipBoundingBox(cgContext);
1427   fprintf(stderr, "    --> clip bounds: %f %f %f %f\n",
1428           bounds.origin.x, bounds.origin.y, bounds.size.width, bounds.size.height);
1430   //CGContextSetRGBFillColor(cgContext, 0.0, 0.0, 1.0, 0.1);
1431   //CGContextFillRect(cgContext, bounds);
1432 #endif
1434   PRInt32 eventState = GetContentState(aFrame, aWidgetType);
1436   switch (aWidgetType) {
1437     case NS_THEME_DIALOG: {
1438       HIThemeSetFill(kThemeBrushDialogBackgroundActive, NULL, cgContext, HITHEME_ORIENTATION);
1439       CGContextFillRect(cgContext, macRect);
1440     }
1441       break;
1443     case NS_THEME_MENUPOPUP: {
1444       HIThemeMenuDrawInfo mdi = {
1445         version: 0,
1446         menuType: IsDisabled(aFrame) ? kThemeMenuTypeInactive : kThemeMenuTypePopUp
1447       };
1448       // The rounded corners draw outside the frame.
1449       CGRect deflatedRect = CGRectMake(macRect.origin.x, macRect.origin.y + 4,
1450                                        macRect.size.width, macRect.size.height - 8);
1451       HIThemeDrawMenuBackground(&deflatedRect, &mdi, cgContext, HITHEME_ORIENTATION);
1452     }
1453       break;
1455     case NS_THEME_MENUITEM: {
1456       // Clear the background to get correct transparency.
1457       CGContextClearRect(cgContext, macRect);
1459       // maybe use kThemeMenuItemHierBackground or PopUpBackground instead of just Plain?
1460       HIThemeMenuItemDrawInfo drawInfo = {
1461         version: 0,
1462         itemType: kThemeMenuItemPlain,
1463         state: (IsDisabled(aFrame) ? kThemeMenuDisabled :
1464                 CheckBooleanAttr(aFrame, nsWidgetAtoms::mozmenuactive) ? kThemeMenuSelected :
1465                 kThemeMenuActive)
1466       };
1468       // XXX pass in the menu rect instead of always using the item rect
1469       HIRect ignored;
1470       HIThemeDrawMenuItem(&macRect, &macRect, &drawInfo, cgContext, HITHEME_ORIENTATION, &ignored);
1471     }
1472       break;
1474     case NS_THEME_MENUSEPARATOR: {
1475       ThemeMenuState menuState;
1476       if (IsDisabled(aFrame)) {
1477         menuState = kThemeMenuDisabled;
1478       }
1479       else {
1480         menuState = CheckBooleanAttr(aFrame, nsWidgetAtoms::mozmenuactive) ?
1481                     kThemeMenuSelected : kThemeMenuActive;
1482       }
1484       HIThemeMenuItemDrawInfo midi = { 0, kThemeMenuItemPlain, menuState };
1485       HIThemeDrawMenuSeparator(&macRect, &macRect, &midi, cgContext, HITHEME_ORIENTATION);
1486     }
1487       break;
1489     case NS_THEME_TOOLTIP:
1490       CGContextSetRGBFillColor(cgContext, 1.0, 1.0, 0.78, 1.0);
1491       CGContextFillRect(cgContext, macRect);
1492       break;
1494     case NS_THEME_CHECKBOX:
1495     case NS_THEME_RADIO: {
1496       PRBool isCheckbox = (aWidgetType == NS_THEME_CHECKBOX);
1497       DrawCheckboxOrRadio(cgContext, isCheckbox, macRect, GetCheckedOrSelected(aFrame, !isCheckbox),
1498                           IsDisabled(aFrame), eventState, aFrame);
1499     }
1500       break;
1502     case NS_THEME_BUTTON:
1503       DrawPushButton(cgContext, macRect, IsDefaultButton(aFrame), IsDisabled(aFrame), eventState, aFrame);
1504       break;
1506     case NS_THEME_BUTTON_BEVEL:
1507       DrawButton(cgContext, kThemeMediumBevelButton, macRect,
1508                  IsDefaultButton(aFrame), IsDisabled(aFrame), 
1509                  kThemeButtonOff, kThemeAdornmentNone, eventState, aFrame);
1510       break;
1512     case NS_THEME_SPINNER: {
1513       ThemeDrawState state = kThemeStateActive;
1514       nsIContent* content = aFrame->GetContent();
1515       if (content->AttrValueIs(kNameSpaceID_None, nsWidgetAtoms::state,
1516                                NS_LITERAL_STRING("up"), eCaseMatters)) {
1517         state = kThemeStatePressedUp;
1518       }
1519       else if (content->AttrValueIs(kNameSpaceID_None, nsWidgetAtoms::state,
1520                                     NS_LITERAL_STRING("down"), eCaseMatters)) {
1521         state = kThemeStatePressedDown;
1522       }
1524       DrawSpinButtons(cgContext, kThemeIncDecButton, macRect, IsDisabled(aFrame),
1525                       state, kThemeAdornmentNone, eventState, aFrame);
1526     }
1527       break;
1529     case NS_THEME_TOOLBAR_BUTTON:
1530       DrawButton(cgContext, kThemePushButton, macRect,
1531                  IsDefaultButton(aFrame), IsDisabled(aFrame),
1532                  kThemeButtonOn, kThemeAdornmentNone, eventState, aFrame);
1533       break;
1535     case NS_THEME_TOOLBAR_SEPARATOR: {
1536       HIThemeSeparatorDrawInfo sdi = { 0, kThemeStateActive };
1537       HIThemeDrawSeparator(&macRect, &sdi, cgContext, HITHEME_ORIENTATION);
1538     }
1539       break;
1541     case NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR:
1542       DrawUnifiedToolbar(cgContext, macRect, aFrame);
1543       break;
1545     case NS_THEME_TOOLBAR: {
1546       BOOL isMain = [NativeWindowForFrame(aFrame) isMainWindow];
1548       // top border
1549       [NativeGreyColorAsNSColor(toolbarTopBorderGrey, isMain) set];
1550       NSRectFill(NSMakeRect(macRect.origin.x, macRect.origin.y,
1551                             macRect.size.width, 1.0f));
1553       // background
1554       [NativeGreyColorAsNSColor(headerEndGrey, isMain) set];
1555       NSRectFill(NSMakeRect(macRect.origin.x, macRect.origin.y + 1.0f,
1556                             macRect.size.width, macRect.size.height - 2.0f));
1558       // bottom border
1559       [NativeGreyColorAsNSColor(headerBorderGrey, isMain) set];
1560       NSRectFill(NSMakeRect(macRect.origin.x, macRect.origin.y +
1561                             macRect.size.height - 1.0f, macRect.size.width, 1.0f));
1562     }
1563       break;
1565     case NS_THEME_TOOLBOX:
1566     case NS_THEME_STATUSBAR: {
1567       HIThemeHeaderDrawInfo hdi = { 0, kThemeStateActive, kHIThemeHeaderKindWindow };
1568       HIThemeDrawHeader(&macRect, &hdi, cgContext, HITHEME_ORIENTATION);
1569     }
1570       break;
1571       
1572     case NS_THEME_DROPDOWN:
1573       DrawButton(cgContext, kThemePopupButton, macRect,
1574                  IsDefaultButton(aFrame), IsDisabled(aFrame), 
1575                  kThemeButtonOn, kThemeAdornmentNone, eventState, aFrame);
1576       break;
1578     case NS_THEME_DROPDOWN_BUTTON:
1579       DrawButton(cgContext, kThemeArrowButton, macRect, PR_FALSE,
1580                  IsDisabled(aFrame), kThemeButtonOn,
1581                  kThemeAdornmentArrowDownArrow, eventState, aFrame);
1582       break;
1584     case NS_THEME_GROUPBOX: {
1585       HIThemeGroupBoxDrawInfo gdi = { 0, kThemeStateActive, kHIThemeGroupBoxKindPrimary };
1586       HIThemeDrawGroupBox(&macRect, &gdi, cgContext, HITHEME_ORIENTATION);
1587       break;
1588     }
1590     case NS_THEME_TEXTFIELD:
1591       // HIThemeSetFill is not available on 10.3
1592       CGContextSetRGBFillColor(cgContext, 1.0, 1.0, 1.0, 1.0);
1593       CGContextFillRect(cgContext, macRect);
1595       // XUL textboxes set the native appearance on the containing box, while
1596       // concrete focus is set on the html:input element within it. We can
1597       // though, check the focused attribute of xul textboxes in this case.
1598       if (aFrame->GetContent()->IsNodeOfType(nsINode::eXUL) &&
1599           IsFocused(aFrame)) {
1600         eventState |= NS_EVENT_STATE_FOCUS;
1601       }
1603       DrawFrame(cgContext, kHIThemeFrameTextFieldSquare,
1604                 macRect, (IsDisabled(aFrame) || IsReadOnly(aFrame)), eventState);
1605       break;
1606       
1607     case NS_THEME_SEARCHFIELD:
1608       DrawSearchField(cgContext, macRect, aFrame);
1609       break;
1611     case NS_THEME_PROGRESSBAR:
1612       DrawProgress(cgContext, macRect, IsIndeterminateProgress(aFrame),
1613                    PR_TRUE, GetProgressValue(aFrame),
1614                    GetProgressMaxValue(aFrame), aFrame);
1615       break;
1617     case NS_THEME_PROGRESSBAR_VERTICAL:
1618       DrawProgress(cgContext, macRect, IsIndeterminateProgress(aFrame),
1619                    PR_FALSE, GetProgressValue(aFrame),
1620                    GetProgressMaxValue(aFrame), aFrame);
1621       break;
1623     case NS_THEME_PROGRESSBAR_CHUNK:
1624     case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
1625       // do nothing, covered by the progress bar cases above
1626       break;
1628     case NS_THEME_TREEVIEW_TWISTY:
1629       DrawButton(cgContext, kThemeDisclosureButton, macRect, PR_FALSE, IsDisabled(aFrame), 
1630                  kThemeDisclosureRight, kThemeAdornmentNone, eventState, aFrame);
1631       break;
1633     case NS_THEME_TREEVIEW_TWISTY_OPEN:
1634       DrawButton(cgContext, kThemeDisclosureButton, macRect, PR_FALSE, IsDisabled(aFrame), 
1635                  kThemeDisclosureDown, kThemeAdornmentNone, eventState, aFrame);
1636       break;
1638     case NS_THEME_TREEVIEW_HEADER_CELL: {
1639       TreeSortDirection sortDirection = GetTreeSortDirection(aFrame);
1640       DrawButton(cgContext, kThemeListHeaderButton, macRect, PR_FALSE, IsDisabled(aFrame), 
1641                  sortDirection == eTreeSortDirection_Natural ? kThemeButtonOff : kThemeButtonOn,
1642                  sortDirection == eTreeSortDirection_Ascending ?
1643                  kThemeAdornmentHeaderButtonSortUp : kThemeAdornmentNone, eventState, aFrame);      
1644     }
1645       break;
1647     case NS_THEME_TREEVIEW_TREEITEM:
1648     case NS_THEME_TREEVIEW:
1649       // HIThemeSetFill is not available on 10.3
1650       // HIThemeSetFill(kThemeBrushWhite, NULL, cgContext, HITHEME_ORIENTATION);
1651       CGContextSetRGBFillColor(cgContext, 1.0, 1.0, 1.0, 1.0);
1652       CGContextFillRect(cgContext, macRect);
1653       break;
1655     case NS_THEME_TREEVIEW_HEADER:
1656       // do nothing, taken care of by individual header cells
1657     case NS_THEME_TREEVIEW_HEADER_SORTARROW:
1658       // do nothing, taken care of by treeview header
1659     case NS_THEME_TREEVIEW_LINE:
1660       // do nothing, these lines don't exist on macos
1661       break;
1663     case NS_THEME_SCALE_HORIZONTAL:
1664     case NS_THEME_SCALE_VERTICAL: {
1665       PRInt32 curpos = CheckIntAttr(aFrame, nsWidgetAtoms::curpos, 0);
1666       PRInt32 minpos = CheckIntAttr(aFrame, nsWidgetAtoms::minpos, 0);
1667       PRInt32 maxpos = CheckIntAttr(aFrame, nsWidgetAtoms::maxpos, 100);
1668       if (!maxpos)
1669         maxpos = 100;
1671       PRBool reverse = aFrame->GetContent()->
1672         AttrValueIs(kNameSpaceID_None, nsWidgetAtoms::dir,
1673                     NS_LITERAL_STRING("reverse"), eCaseMatters);
1674       DrawScale(cgContext, macRect, IsDisabled(aFrame), eventState,
1675                 (aWidgetType == NS_THEME_SCALE_VERTICAL), reverse,
1676                 curpos, minpos, maxpos, aFrame);
1677     }
1678       break;
1680     case NS_THEME_SCALE_THUMB_HORIZONTAL:
1681     case NS_THEME_SCALE_THUMB_VERTICAL:
1682       // do nothing, drawn by scale
1683       break;
1685     case NS_THEME_SCROLLBAR_SMALL:
1686     case NS_THEME_SCROLLBAR: {
1687       DrawScrollbar(cgContext, macRect, aFrame);
1688     }
1689       break;
1690     case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
1691     case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
1692 #if SCROLLBARS_VISUAL_DEBUG
1693       CGContextSetRGBFillColor(cgContext, 1.0, 1.0, 0, 0.6);
1694       CGContextFillRect(cgContext, macRect);
1695     break;
1696 #endif
1697     case NS_THEME_SCROLLBAR_BUTTON_UP:
1698     case NS_THEME_SCROLLBAR_BUTTON_LEFT:
1699 #if SCROLLBARS_VISUAL_DEBUG
1700       CGContextSetRGBFillColor(cgContext, 1.0, 0, 0, 0.6);
1701       CGContextFillRect(cgContext, macRect);
1702     break;
1703 #endif
1704     case NS_THEME_SCROLLBAR_BUTTON_DOWN:
1705     case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
1706 #if SCROLLBARS_VISUAL_DEBUG
1707       CGContextSetRGBFillColor(cgContext, 0, 1.0, 0, 0.6);
1708       CGContextFillRect(cgContext, macRect);
1709     break;      
1710 #endif
1711     case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
1712     case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
1713       // do nothing, drawn by scrollbar
1714       break;
1716     case NS_THEME_TEXTFIELD_MULTILINE: {
1717       // we have to draw this by hand because there is no HITheme value for it
1718       CGContextSetRGBFillColor(cgContext, 1.0, 1.0, 1.0, 1.0);
1719       
1720       CGContextFillRect(cgContext, macRect);
1722       CGContextSetLineWidth(cgContext, 1.0);
1723       CGContextSetShouldAntialias(cgContext, false);
1725       // stroke everything but the top line of the text area
1726       CGContextSetRGBStrokeColor(cgContext, 0.6, 0.6, 0.6, 1.0);
1727       CGContextBeginPath(cgContext);
1728       CGContextMoveToPoint(cgContext, macRect.origin.x, macRect.origin.y + 1);
1729       CGContextAddLineToPoint(cgContext, macRect.origin.x, macRect.origin.y + macRect.size.height);
1730       CGContextAddLineToPoint(cgContext, macRect.origin.x + macRect.size.width - 1, macRect.origin.y + macRect.size.height);
1731       CGContextAddLineToPoint(cgContext, macRect.origin.x + macRect.size.width - 1, macRect.origin.y + 1);
1732       CGContextStrokePath(cgContext);
1734       // stroke the line across the top of the text area
1735       CGContextSetRGBStrokeColor(cgContext, 0.4510, 0.4510, 0.4510, 1.0);
1736       CGContextBeginPath(cgContext);
1737       CGContextMoveToPoint(cgContext, macRect.origin.x, macRect.origin.y + 1);
1738       CGContextAddLineToPoint(cgContext, macRect.origin.x + macRect.size.width - 1, macRect.origin.y + 1);
1739       CGContextStrokePath(cgContext);
1741       // draw a focus ring
1742       if (eventState & NS_EVENT_STATE_FOCUS) {
1743         // We need to bring the rectangle in by 1 pixel on each side.
1744         CGRect cgr = CGRectMake(macRect.origin.x + 1,
1745                                 macRect.origin.y + 1,
1746                                 macRect.size.width - 2,
1747                                 macRect.size.height - 2);
1748         HIThemeDrawFocusRect(&cgr, true, cgContext, kHIThemeOrientationNormal);
1749       }
1750     }
1751       break;
1753     case NS_THEME_LISTBOX:
1754       // HIThemeSetFill is not available on 10.3
1755       CGContextSetRGBFillColor(cgContext, 1.0, 1.0, 1.0, 1.0);
1756       CGContextFillRect(cgContext, macRect);
1757       DrawFrame(cgContext, kHIThemeFrameListBox, macRect,
1758                 (IsDisabled(aFrame) || IsReadOnly(aFrame)), eventState);
1759       break;
1760     
1761     case NS_THEME_TAB:
1762       DrawTab(cgContext, macRect, eventState, aFrame);
1763       break;
1765     case NS_THEME_TAB_PANELS:
1766       DrawTabPanel(cgContext, macRect, aFrame);
1767       break;
1768   }
1770   nativeDrawing.EndNativeDrawing();
1772   return NS_OK;
1774   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1778 static const int kAquaDropdownLeftBorder = 5;
1779 static const int kAquaDropdownRightBorder = 22;
1781 NS_IMETHODIMP
1782 nsNativeThemeCocoa::GetWidgetBorder(nsIDeviceContext* aContext, 
1783                                     nsIFrame* aFrame,
1784                                     PRUint8 aWidgetType,
1785                                     nsMargin* aResult)
1787   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1789   aResult->SizeTo(0, 0, 0, 0);
1791   switch (aWidgetType) {
1792     case NS_THEME_BUTTON:
1793     {
1794       aResult->SizeTo(7, 1, 7, 3);
1795       break;
1796     }
1798     case NS_THEME_CHECKBOX:
1799     case NS_THEME_RADIO:
1800     {
1801       // nsFormControlFrame::GetIntrinsicWidth and nsFormControlFrame::GetIntrinsicHeight
1802       // assume a border width of 2px.
1803       aResult->SizeTo(2, 2, 2, 2);
1804       break;
1805     }
1807     case NS_THEME_DROPDOWN:
1808     case NS_THEME_DROPDOWN_BUTTON:
1809       aResult->SizeTo(kAquaDropdownLeftBorder, 2, kAquaDropdownRightBorder, 2);
1810       break;
1812     case NS_THEME_TEXTFIELD:
1813     {
1814       SInt32 frameOutset = 0;
1815       ::GetThemeMetric(kThemeMetricEditTextFrameOutset, &frameOutset);
1817       SInt32 textPadding = 0;
1818       ::GetThemeMetric(kThemeMetricEditTextWhitespace, &textPadding);
1820       frameOutset += textPadding;
1822       aResult->SizeTo(frameOutset, frameOutset, frameOutset, frameOutset);
1823       break;
1824     }
1826     case NS_THEME_TEXTFIELD_MULTILINE:
1827       aResult->SizeTo(1, 1, 1, 1);
1828       break;
1830     case NS_THEME_SEARCHFIELD:
1831       aResult->SizeTo(4, 2, 4, 2);
1832       break;
1834     case NS_THEME_LISTBOX:
1835     {
1836       SInt32 frameOutset = 0;
1837       ::GetThemeMetric(kThemeMetricListBoxFrameOutset, &frameOutset);
1838       aResult->SizeTo(frameOutset, frameOutset, frameOutset, frameOutset);
1839       break;
1840     }
1842     case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
1843     case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
1844     {
1845       // There's only an endcap to worry about when both arrows are on the bottom
1846       ThemeScrollBarArrowStyle arrowStyle;
1847       ::GetThemeScrollBarArrowStyle(&arrowStyle);
1848       if (arrowStyle == kThemeScrollBarArrowsLowerRight) {
1849         PRBool isHorizontal = (aWidgetType == NS_THEME_SCROLLBAR_TRACK_HORIZONTAL);
1851         nsIFrame *scrollbarFrame = GetParentScrollbarFrame(aFrame);
1852         if (!scrollbarFrame) return NS_ERROR_FAILURE;
1853         PRBool isSmall = (scrollbarFrame->GetStyleDisplay()->mAppearance == NS_THEME_SCROLLBAR_SMALL);
1855         // There isn't a metric for this, so just hardcode a best guess at the value.
1856         // This value is even less exact due to the fact that the endcap is partially concave.
1857         PRInt32 endcapSize = isSmall ? 5 : 6;
1859         if (isHorizontal)
1860           aResult->SizeTo(endcapSize, 0, 0, 0);
1861         else
1862           aResult->SizeTo(0, endcapSize, 0, 0);
1863       }
1864       break;
1865     }
1867     case NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR:
1868       aResult->SizeTo(0, 0, 1, 0);
1869       break;
1870   }
1872   return NS_OK;
1874   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
1878 // Return PR_FALSE here to indicate that CSS padding values should be used. There is
1879 // no reason to make a distinction between padding and border values, just specify
1880 // whatever values you want in GetWidgetBorder and only use this to return PR_TRUE
1881 // if you want to override CSS padding values.
1882 PRBool
1883 nsNativeThemeCocoa::GetWidgetPadding(nsIDeviceContext* aContext, 
1884                                      nsIFrame* aFrame,
1885                                      PRUint8 aWidgetType,
1886                                      nsMargin* aResult)
1888   // We don't want CSS padding being used for certain widgets.
1889   // See bug 381639 for an example of why.
1890   switch (aWidgetType) {
1891     case NS_THEME_BUTTON:
1892     // Radios and checkboxes return a fixed size in GetMinimumWidgetSize
1893     // and have a meaningful baseline, so they can't have
1894     // author-specified padding.
1895     case NS_THEME_CHECKBOX:
1896     case NS_THEME_RADIO:
1897       aResult->SizeTo(0, 0, 0, 0);
1898       return PR_TRUE;
1899   }
1900   return PR_FALSE;
1904 PRBool
1905 nsNativeThemeCocoa::GetWidgetOverflow(nsIDeviceContext* aContext, nsIFrame* aFrame,
1906                                       PRUint8 aWidgetType, nsRect* aOverflowRect)
1908   switch (aWidgetType) {
1909     case NS_THEME_BUTTON:
1910     case NS_THEME_TEXTFIELD:
1911     case NS_THEME_TEXTFIELD_MULTILINE:
1912     case NS_THEME_SEARCHFIELD:
1913     case NS_THEME_LISTBOX:
1914     case NS_THEME_DROPDOWN:
1915     case NS_THEME_DROPDOWN_BUTTON:
1916     case NS_THEME_CHECKBOX:
1917     case NS_THEME_RADIO:
1918     case NS_THEME_TAB:
1919     {
1920       // We assume that the above widgets can draw a focus ring that will be less than
1921       // or equal to 4 pixels thick.
1922       nsIntMargin extraSize = nsIntMargin(MAX_FOCUS_RING_WIDTH, MAX_FOCUS_RING_WIDTH, MAX_FOCUS_RING_WIDTH, MAX_FOCUS_RING_WIDTH);
1923       PRInt32 p2a = aContext->AppUnitsPerDevPixel();
1924       nsMargin m(NSIntPixelsToAppUnits(extraSize.left, p2a),
1925                  NSIntPixelsToAppUnits(extraSize.top, p2a),
1926                  NSIntPixelsToAppUnits(extraSize.right, p2a),
1927                  NSIntPixelsToAppUnits(extraSize.bottom, p2a));
1928       aOverflowRect->Inflate(m);
1929       return PR_TRUE;
1930     }
1931   }
1933   return PR_FALSE;
1937 NS_IMETHODIMP
1938 nsNativeThemeCocoa::GetMinimumWidgetSize(nsIRenderingContext* aContext,
1939                                          nsIFrame* aFrame,
1940                                          PRUint8 aWidgetType,
1941                                          nsSize* aResult,
1942                                          PRBool* aIsOverridable)
1944   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
1946   aResult->SizeTo(0,0);
1947   *aIsOverridable = PR_TRUE;
1949   switch (aWidgetType) {
1950     case NS_THEME_BUTTON:
1951     {
1952       aResult->SizeTo(NATURAL_MINI_ROUNDED_BUTTON_MIN_WIDTH, NATURAL_MINI_ROUNDED_BUTTON_HEIGHT);
1953       break;
1954     }
1956     case NS_THEME_SPINNER:
1957     {
1958       SInt32 buttonHeight = 0, buttonWidth = 0;
1959       ::GetThemeMetric(kThemeMetricLittleArrowsWidth, &buttonWidth);
1960       ::GetThemeMetric(kThemeMetricLittleArrowsHeight, &buttonHeight);
1961       aResult->SizeTo(buttonWidth, buttonHeight);
1962       *aIsOverridable = PR_FALSE;
1963       break;
1964     }
1966     case NS_THEME_DROPDOWN:
1967     case NS_THEME_DROPDOWN_BUTTON:
1968     {
1969       SInt32 popupHeight = 0;
1970       ::GetThemeMetric(kThemeMetricPopupButtonHeight, &popupHeight);
1971       aResult->SizeTo(0, popupHeight);
1972       break;
1973     }
1975     case NS_THEME_TEXTFIELD:
1976     case NS_THEME_TEXTFIELD_MULTILINE:
1977     case NS_THEME_SEARCHFIELD:
1978     {
1979       // at minimum, we should be tall enough for 9pt text.
1980       // I'm using hardcoded values here because the appearance manager
1981       // values for the frame size are incorrect.
1982       aResult->SizeTo(0, (2 + 2) /* top */ + 9 + (1 + 1) /* bottom */);
1983       break;
1984     }
1985       
1986     case NS_THEME_PROGRESSBAR:
1987     {
1988       SInt32 barHeight = 0;
1989       ::GetThemeMetric(kThemeMetricNormalProgressBarThickness, &barHeight);
1990       aResult->SizeTo(0, barHeight);
1991       break;
1992     }
1994     case NS_THEME_TREEVIEW_TWISTY:
1995     case NS_THEME_TREEVIEW_TWISTY_OPEN:   
1996     {
1997       SInt32 twistyHeight = 0, twistyWidth = 0;
1998       ::GetThemeMetric(kThemeMetricDisclosureButtonWidth, &twistyWidth);
1999       ::GetThemeMetric(kThemeMetricDisclosureButtonHeight, &twistyHeight);
2000       aResult->SizeTo(twistyWidth, twistyHeight);
2001       *aIsOverridable = PR_FALSE;
2002       break;
2003     }
2004     
2005     case NS_THEME_TREEVIEW_HEADER:
2006     case NS_THEME_TREEVIEW_HEADER_CELL:
2007     {
2008       SInt32 headerHeight = 0;
2009       ::GetThemeMetric(kThemeMetricListHeaderHeight, &headerHeight);
2010       aResult->SizeTo(0, headerHeight - 1); // We don't need the top border.
2011       break;
2012     }
2014     case NS_THEME_TAB:
2015     {
2016       aResult->SizeTo(0, NATURAL_MINI_TAB_BUTTON_HEIGHT);
2017       break;
2018     }
2020     case NS_THEME_SCALE_HORIZONTAL:
2021     {
2022       SInt32 scaleHeight = 0;
2023       ::GetThemeMetric(kThemeMetricHSliderHeight, &scaleHeight);
2024       aResult->SizeTo(scaleHeight, scaleHeight);
2025       *aIsOverridable = PR_FALSE;
2026       break;
2027     }
2029     case NS_THEME_SCALE_VERTICAL:
2030     {
2031       SInt32 scaleWidth = 0;
2032       ::GetThemeMetric(kThemeMetricVSliderWidth, &scaleWidth);
2033       aResult->SizeTo(scaleWidth, scaleWidth);
2034       *aIsOverridable = PR_FALSE;
2035       break;
2036     }
2037       
2038     case NS_THEME_SCROLLBAR_SMALL:
2039     {
2040       SInt32 scrollbarWidth = 0;
2041       ::GetThemeMetric(kThemeMetricSmallScrollBarWidth, &scrollbarWidth);
2042       aResult->SizeTo(scrollbarWidth, scrollbarWidth);
2043       *aIsOverridable = PR_FALSE;
2044       break;
2045     }
2047     // Get the rect of the thumb from HITheme, so we can return it to Gecko, which has different ideas about
2048     // how big the thumb should be. This is kind of a hack.
2049     case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
2050     case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
2051     {
2052       // Find our parent scrollbar frame. If we can't, abort.
2053       nsIFrame *scrollbarFrame = GetParentScrollbarFrame(aFrame);
2054       if (!scrollbarFrame) return NS_ERROR_FAILURE;
2056       nsRect scrollbarRect = scrollbarFrame->GetRect();      
2057       *aIsOverridable = PR_FALSE;
2059       if (scrollbarRect.IsEmpty()) {
2060         // just return (0,0)
2061         return NS_OK;
2062       }
2064       // We need to get the device context to convert from app units :(
2065       nsCOMPtr<nsIDeviceContext> dctx;
2066       aContext->GetDeviceContext(*getter_AddRefs(dctx));
2067       PRInt32 p2a = dctx->AppUnitsPerDevPixel();
2068       CGRect macRect = CGRectMake(NSAppUnitsToIntPixels(scrollbarRect.x, p2a),
2069                                   NSAppUnitsToIntPixels(scrollbarRect.y, p2a),
2070                                   NSAppUnitsToIntPixels(scrollbarRect.width, p2a),
2071                                   NSAppUnitsToIntPixels(scrollbarRect.height, p2a));
2073       // False here means not to get scrollbar button state information.
2074       HIThemeTrackDrawInfo tdi;
2075       GetScrollbarDrawInfo(tdi, scrollbarFrame, macRect, PR_FALSE);
2077       HIRect thumbRect;
2078       ::HIThemeGetTrackPartBounds(&tdi, kControlIndicatorPart, &thumbRect);
2080       // HITheme is just lying to us, I guess...
2081       PRInt32 thumbAdjust = ((scrollbarFrame->GetStyleDisplay()->mAppearance == NS_THEME_SCROLLBAR_SMALL) ?
2082                              2 : 4);
2084       if (aWidgetType == NS_THEME_SCROLLBAR_THUMB_VERTICAL)
2085         aResult->SizeTo(nscoord(thumbRect.size.width), nscoord(thumbRect.size.height - thumbAdjust));
2086       else
2087         aResult->SizeTo(nscoord(thumbRect.size.width - thumbAdjust), nscoord(thumbRect.size.height));
2088       break;
2089     }
2091     case NS_THEME_SCROLLBAR:
2092     case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
2093     case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
2094     {
2095       // yeah, i know i'm cheating a little here, but i figure that it
2096       // really doesn't matter if the scrollbar is vertical or horizontal
2097       // and the width metric is a really good metric for every piece
2098       // of the scrollbar.
2100       nsIFrame *scrollbarFrame = GetParentScrollbarFrame(aFrame);
2101       if (!scrollbarFrame) return NS_ERROR_FAILURE;
2103       PRInt32 themeMetric = (scrollbarFrame->GetStyleDisplay()->mAppearance == NS_THEME_SCROLLBAR_SMALL) ?
2104                             kThemeMetricSmallScrollBarWidth :
2105                             kThemeMetricScrollBarWidth;
2106       SInt32 scrollbarWidth = 0;
2107       ::GetThemeMetric(themeMetric, &scrollbarWidth);
2108       aResult->SizeTo(scrollbarWidth, scrollbarWidth);
2109       *aIsOverridable = PR_FALSE;
2110       break;
2111     }
2113     case NS_THEME_SCROLLBAR_BUTTON_UP:
2114     case NS_THEME_SCROLLBAR_BUTTON_DOWN:
2115     case NS_THEME_SCROLLBAR_BUTTON_LEFT:
2116     case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
2117     {
2118       nsIFrame *scrollbarFrame = GetParentScrollbarFrame(aFrame);
2119       if (!scrollbarFrame) return NS_ERROR_FAILURE;
2121       // Since there is no NS_THEME_SCROLLBAR_BUTTON_UP_SMALL we need to ask the parent what appearance style it has.
2122       PRInt32 themeMetric = (scrollbarFrame->GetStyleDisplay()->mAppearance == NS_THEME_SCROLLBAR_SMALL) ?
2123                             kThemeMetricSmallScrollBarWidth :
2124                             kThemeMetricScrollBarWidth;
2125       SInt32 scrollbarWidth = 0;
2126       ::GetThemeMetric(themeMetric, &scrollbarWidth);
2128       // It seems that for both sizes of scrollbar, the buttons are one pixel "longer".
2129       if (aWidgetType == NS_THEME_SCROLLBAR_BUTTON_LEFT || aWidgetType == NS_THEME_SCROLLBAR_BUTTON_RIGHT)
2130         aResult->SizeTo(scrollbarWidth+1, scrollbarWidth);
2131       else
2132         aResult->SizeTo(scrollbarWidth, scrollbarWidth+1);
2134       *aIsOverridable = PR_FALSE;
2135       break;
2136     }
2137   }
2139   return NS_OK;
2141   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
2145 NS_IMETHODIMP
2146 nsNativeThemeCocoa::WidgetStateChanged(nsIFrame* aFrame, PRUint8 aWidgetType, 
2147                                      nsIAtom* aAttribute, PRBool* aShouldRepaint)
2149   // Some widget types just never change state.
2150   switch (aWidgetType) {
2151     case NS_THEME_TOOLBOX:
2152     case NS_THEME_TOOLBAR:
2153     case NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR:
2154     case NS_THEME_TOOLBAR_BUTTON:
2155     case NS_THEME_SCROLLBAR_TRACK_VERTICAL: 
2156     case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
2157     case NS_THEME_STATUSBAR:
2158     case NS_THEME_STATUSBAR_PANEL:
2159     case NS_THEME_STATUSBAR_RESIZER_PANEL:
2160     case NS_THEME_TOOLTIP:
2161     case NS_THEME_TAB_PANELS:
2162     case NS_THEME_TAB_PANEL:
2163     case NS_THEME_DIALOG:
2164     case NS_THEME_MENUPOPUP:
2165     case NS_THEME_GROUPBOX:
2166       *aShouldRepaint = PR_FALSE;
2167       return NS_OK;
2168     case NS_THEME_PROGRESSBAR_CHUNK:
2169     case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
2170     case NS_THEME_PROGRESSBAR:
2171     case NS_THEME_PROGRESSBAR_VERTICAL:
2172       *aShouldRepaint = (aAttribute == nsWidgetAtoms::step);
2173       return NS_OK;
2174   }
2176   // XXXdwh Not sure what can really be done here.  Can at least guess for
2177   // specific widgets that they're highly unlikely to have certain states.
2178   // For example, a toolbar doesn't care about any states.
2179   if (!aAttribute) {
2180     // Hover/focus/active changed.  Always repaint.
2181     *aShouldRepaint = PR_TRUE;
2182   } else {
2183     // Check the attribute to see if it's relevant.  
2184     // disabled, checked, dlgtype, default, etc.
2185     *aShouldRepaint = PR_FALSE;
2186     if (aAttribute == nsWidgetAtoms::disabled ||
2187         aAttribute == nsWidgetAtoms::checked ||
2188         aAttribute == nsWidgetAtoms::selected ||
2189         aAttribute == nsWidgetAtoms::mozmenuactive ||
2190         aAttribute == nsWidgetAtoms::sortdirection ||
2191         aAttribute == nsWidgetAtoms::focused ||
2192         aAttribute == nsWidgetAtoms::_default)
2193       *aShouldRepaint = PR_TRUE;
2194   }
2196   return NS_OK;
2200 NS_IMETHODIMP
2201 nsNativeThemeCocoa::ThemeChanged()
2203   // This is unimplemented because we don't care if gecko changes its theme
2204   // and Mac OS X doesn't have themes.
2205   return NS_OK;
2209 PRBool 
2210 nsNativeThemeCocoa::ThemeSupportsWidget(nsPresContext* aPresContext, nsIFrame* aFrame,
2211                                       PRUint8 aWidgetType)
2213   // We don't have CSS set up to render non-native scrollbars on Mac OS X so we
2214   // render natively even if native theme support is disabled.
2215   if (aWidgetType != NS_THEME_SCROLLBAR &&
2216       aPresContext && !aPresContext->PresShell()->IsThemeSupportEnabled())
2217     return PR_FALSE;
2219   // if this is a dropdown button in a combobox the answer is always no
2220   if (aWidgetType == NS_THEME_DROPDOWN_BUTTON) {
2221     nsIFrame* parentFrame = aFrame->GetParent();
2222     if (parentFrame && (parentFrame->GetType() == nsWidgetAtoms::comboboxControlFrame))
2223       return PR_FALSE;
2224   }
2226   switch (aWidgetType) {
2227     case NS_THEME_LISTBOX:
2229     case NS_THEME_DIALOG:
2230     case NS_THEME_WINDOW:
2231     case NS_THEME_MENUPOPUP:
2232     case NS_THEME_MENUITEM:
2233     case NS_THEME_MENUSEPARATOR:
2234     case NS_THEME_TOOLTIP:
2235     
2236     case NS_THEME_CHECKBOX:
2237     case NS_THEME_CHECKBOX_CONTAINER:
2238     case NS_THEME_RADIO:
2239     case NS_THEME_RADIO_CONTAINER:
2240     case NS_THEME_GROUPBOX:
2241     case NS_THEME_BUTTON:
2242     case NS_THEME_BUTTON_BEVEL:
2243     case NS_THEME_SPINNER:
2244     case NS_THEME_TOOLBAR:
2245     case NS_THEME_MOZ_MAC_UNIFIED_TOOLBAR:
2246     case NS_THEME_STATUSBAR:
2247     case NS_THEME_TEXTFIELD:
2248     case NS_THEME_TEXTFIELD_MULTILINE:
2249     case NS_THEME_SEARCHFIELD:
2250     //case NS_THEME_TOOLBOX:
2251     //case NS_THEME_TOOLBAR_BUTTON:
2252     case NS_THEME_PROGRESSBAR:
2253     case NS_THEME_PROGRESSBAR_VERTICAL:
2254     case NS_THEME_PROGRESSBAR_CHUNK:
2255     case NS_THEME_PROGRESSBAR_CHUNK_VERTICAL:
2256     case NS_THEME_TOOLBAR_SEPARATOR:
2257     
2258     case NS_THEME_TAB_PANELS:
2259     case NS_THEME_TAB:
2260     
2261     case NS_THEME_TREEVIEW_TWISTY:
2262     case NS_THEME_TREEVIEW_TWISTY_OPEN:
2263     case NS_THEME_TREEVIEW:
2264     case NS_THEME_TREEVIEW_HEADER:
2265     case NS_THEME_TREEVIEW_HEADER_CELL:
2266     case NS_THEME_TREEVIEW_HEADER_SORTARROW:
2267     case NS_THEME_TREEVIEW_TREEITEM:
2268     case NS_THEME_TREEVIEW_LINE:
2270     case NS_THEME_SCALE_HORIZONTAL:
2271     case NS_THEME_SCALE_THUMB_HORIZONTAL:
2272     case NS_THEME_SCALE_VERTICAL:
2273     case NS_THEME_SCALE_THUMB_VERTICAL:
2275     case NS_THEME_SCROLLBAR:
2276     case NS_THEME_SCROLLBAR_SMALL:
2277     case NS_THEME_SCROLLBAR_BUTTON_UP:
2278     case NS_THEME_SCROLLBAR_BUTTON_DOWN:
2279     case NS_THEME_SCROLLBAR_BUTTON_LEFT:
2280     case NS_THEME_SCROLLBAR_BUTTON_RIGHT:
2281     case NS_THEME_SCROLLBAR_THUMB_HORIZONTAL:
2282     case NS_THEME_SCROLLBAR_THUMB_VERTICAL:
2283     case NS_THEME_SCROLLBAR_TRACK_VERTICAL:
2284     case NS_THEME_SCROLLBAR_TRACK_HORIZONTAL:
2286     case NS_THEME_DROPDOWN:
2287     case NS_THEME_DROPDOWN_BUTTON:
2288     case NS_THEME_DROPDOWN_TEXT:
2289       return !IsWidgetStyled(aPresContext, aFrame, aWidgetType);
2290       break;
2291   }
2293   return PR_FALSE;
2297 PRBool
2298 nsNativeThemeCocoa::WidgetIsContainer(PRUint8 aWidgetType)
2300   // flesh this out at some point
2301   switch (aWidgetType) {
2302    case NS_THEME_DROPDOWN_BUTTON:
2303    case NS_THEME_RADIO:
2304    case NS_THEME_CHECKBOX:
2305    case NS_THEME_PROGRESSBAR:
2306     return PR_FALSE;
2307     break;
2308   }
2309   return PR_TRUE;
2313 PRBool
2314 nsNativeThemeCocoa::ThemeDrawsFocusForWidget(nsPresContext* aPresContext, nsIFrame* aFrame, PRUint8 aWidgetType)
2316   if (aWidgetType == NS_THEME_DROPDOWN ||
2317       aWidgetType == NS_THEME_BUTTON ||
2318       aWidgetType == NS_THEME_RADIO ||
2319       aWidgetType == NS_THEME_CHECKBOX)
2320     return PR_TRUE;
2322   return PR_FALSE;
2325 PRBool
2326 nsNativeThemeCocoa::ThemeNeedsComboboxDropmarker()
2328   return PR_FALSE;
2331 nsTransparencyMode
2332 nsNativeThemeCocoa::GetWidgetTransparency(PRUint8 aWidgetType)
2334   if (aWidgetType == NS_THEME_MENUPOPUP)
2335     return eTransparencyTransparent;
2337   return eTransparencyOpaque;