8aff698a96fc6f0eff6251b1bcff1ccb65660799
[WebKit-https.git] / WebCore / rendering / RenderThemeMac.mm
1 /*
2  * This file is part of the theme implementation for form controls in WebCore.
3  *
4  * Copyright (C) 2005, 2006 Apple Computer, Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public License
17  * along with this library; see the file COPYING.LIB.  If not, write to
18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 #import "config.h"
23 #import "RenderThemeMac.h"
24
25 #import "CSSStyleSelector.h"
26 #import "CSSValueKeywords.h"
27 #import "Document.h"
28 #import "Element.h"
29 #import "FoundationExtras.h"
30 #import "FrameView.h"
31 #import "GraphicsContext.h"
32 #import "HTMLInputElement.h"
33 #import "Image.h"
34 #import "LocalCurrentGraphicsContext.h"
35 #import "RenderSlider.h"
36 #import "RenderView.h"
37 #import "WebCoreSystemInterface.h"
38 #import <Cocoa/Cocoa.h>
39 #import <wtf/RetainPtr.h>
40 #import <math.h>
41
42 using std::min;
43
44 // The methods in this file are specific to the Mac OS X platform.
45
46 // FIXME: The platform-independent code in this class should be factored out and merged with RenderThemeSafari. 
47
48 @interface WebCoreRenderThemeNotificationObserver : NSObject
49 {
50     WebCore::RenderTheme *_theme;
51 }
52
53 - (id)initWithTheme:(WebCore::RenderTheme *)theme;
54 - (void)systemColorsDidChange:(NSNotification *)notification;
55
56 @end
57
58 @implementation WebCoreRenderThemeNotificationObserver
59
60 - (id)initWithTheme:(WebCore::RenderTheme *)theme
61 {
62     [super init];
63     _theme = theme;
64     
65     return self;
66 }
67
68 - (void)systemColorsDidChange:(NSNotification *)notification
69 {
70     ASSERT([[notification name] isEqualToString:NSSystemColorsDidChangeNotification]);
71     _theme->platformColorsDidChange();
72 }
73
74 @end
75
76 namespace WebCore {
77
78 enum {
79     topMargin,
80     rightMargin,
81     bottomMargin,
82     leftMargin
83 };
84
85 enum {
86     topPadding,
87     rightPadding,
88     bottomPadding,
89     leftPadding
90 };
91
92 RenderTheme* theme()
93 {
94     static RenderThemeMac* macTheme = new RenderThemeMac;
95     return macTheme;
96 }
97
98 RenderThemeMac::RenderThemeMac()
99     : m_resizeCornerImage(0)
100     , m_isSliderThumbHorizontalPressed(false)
101     , m_isSliderThumbVerticalPressed(false)
102     , m_notificationObserver(AdoptNS, [[WebCoreRenderThemeNotificationObserver alloc] initWithTheme:this])
103 {
104     [[NSNotificationCenter defaultCenter] addObserver:m_notificationObserver.get()
105                                                         selector:@selector(systemColorsDidChange:)
106                                                             name:NSSystemColorsDidChangeNotification
107                                                           object:nil];
108 }
109
110 RenderThemeMac::~RenderThemeMac()
111 {
112     [[NSNotificationCenter defaultCenter] removeObserver:m_notificationObserver.get()];
113     delete m_resizeCornerImage;
114 }
115
116 Color RenderThemeMac::platformActiveSelectionBackgroundColor() const
117 {
118     NSColor* color = [[NSColor selectedTextBackgroundColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace];
119     return Color(static_cast<int>(255.0 * [color redComponent]), static_cast<int>(255.0 * [color greenComponent]), static_cast<int>(255.0 * [color blueComponent]));
120 }
121
122 Color RenderThemeMac::platformInactiveSelectionBackgroundColor() const
123 {
124     NSColor* color = [[NSColor secondarySelectedControlColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace];
125     return Color(static_cast<int>(255.0 * [color redComponent]), static_cast<int>(255.0 * [color greenComponent]), static_cast<int>(255.0 * [color blueComponent]));
126 }
127
128 Color RenderThemeMac::activeListBoxSelectionBackgroundColor() const
129 {
130     NSColor* color = [[NSColor alternateSelectedControlColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace];
131     return Color(static_cast<int>(255.0 * [color redComponent]), static_cast<int>(255.0 * [color greenComponent]), static_cast<int>(255.0 * [color blueComponent]));
132 }
133
134 void RenderThemeMac::systemFont(int cssValueId, FontDescription& fontDescription) const
135 {
136     static FontDescription systemFont;
137     static FontDescription smallSystemFont;
138     static FontDescription menuFont;
139     static FontDescription labelFont;
140     static FontDescription miniControlFont;
141     static FontDescription smallControlFont;
142     static FontDescription controlFont;
143
144     FontDescription* cachedDesc;
145     NSFont* font = nil;
146     switch (cssValueId) {
147         case CSS_VAL_SMALL_CAPTION:
148             cachedDesc = &smallSystemFont;
149             if (!smallSystemFont.isAbsoluteSize())
150                 font = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]];
151             break;
152         case CSS_VAL_MENU:
153             cachedDesc = &menuFont;
154             if (!menuFont.isAbsoluteSize())
155                 font = [NSFont menuFontOfSize:[NSFont systemFontSize]];
156             break;
157         case CSS_VAL_STATUS_BAR:
158             cachedDesc = &labelFont;
159             if (!labelFont.isAbsoluteSize())
160                 font = [NSFont labelFontOfSize:[NSFont labelFontSize]];
161             break;
162         case CSS_VAL__WEBKIT_MINI_CONTROL:
163             cachedDesc = &miniControlFont;
164             if (!miniControlFont.isAbsoluteSize())
165                 font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSMiniControlSize]];
166             break;
167         case CSS_VAL__WEBKIT_SMALL_CONTROL:
168             cachedDesc = &smallControlFont;
169             if (!smallControlFont.isAbsoluteSize())
170                 font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]];
171             break;
172         case CSS_VAL__WEBKIT_CONTROL:
173             cachedDesc = &controlFont;
174             if (!controlFont.isAbsoluteSize())
175                 font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]];
176             break;
177         default:
178             cachedDesc = &systemFont;
179             if (!systemFont.isAbsoluteSize())
180                 font = [NSFont systemFontOfSize:[NSFont systemFontSize]];
181     }
182
183     if (font) {
184         cachedDesc->setIsAbsoluteSize(true);
185         cachedDesc->setGenericFamily(FontDescription::NoFamily);
186         cachedDesc->firstFamily().setFamily([font familyName]);
187         cachedDesc->setSpecifiedSize([font pointSize]);
188         NSFontTraitMask traits = [[NSFontManager sharedFontManager] traitsOfFont:font];
189         cachedDesc->setBold(traits & NSBoldFontMask);
190         cachedDesc->setItalic(traits & NSItalicFontMask);
191     }
192     fontDescription = *cachedDesc;
193 }
194
195 static RGBA32 convertNSColorToColor(NSColor *color)
196 {
197     NSColor *colorInColorSpace = [color colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
198     if (colorInColorSpace) {
199         static const double scaleFactor = nextafter(256.0, 0.0);
200         return makeRGB(static_cast<int>(scaleFactor * [colorInColorSpace redComponent]),
201             static_cast<int>(scaleFactor * [colorInColorSpace greenComponent]),
202             static_cast<int>(scaleFactor * [colorInColorSpace blueComponent]));
203     }
204
205     // This conversion above can fail if the NSColor in question is an NSPatternColor 
206     // (as many system colors are). These colors are actually a repeating pattern
207     // not just a solid color. To work around this we simply draw a 1x1 image of
208     // the color and use that pixel's color. It might be better to use an average of
209     // the colors in the pattern instead.
210     NSBitmapImageRep *offscreenRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil
211                                                                              pixelsWide:1
212                                                                              pixelsHigh:1
213                                                                           bitsPerSample:8
214                                                                         samplesPerPixel:4
215                                                                                hasAlpha:YES
216                                                                                isPlanar:NO
217                                                                          colorSpaceName:NSCalibratedRGBColorSpace
218                                                                             bytesPerRow:4
219                                                                            bitsPerPixel:32];
220
221     [NSGraphicsContext saveGraphicsState];
222     [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:offscreenRep]];
223     NSEraseRect(NSMakeRect(0, 0, 1, 1));
224     [color drawSwatchInRect:NSMakeRect(0, 0, 1, 1)];
225     [NSGraphicsContext restoreGraphicsState];
226
227     NSUInteger pixel[3];
228     [offscreenRep getPixel:pixel atX:0 y:0];
229
230     [offscreenRep release];
231
232     return makeRGB(pixel[0], pixel[1], pixel[3]);
233 }
234
235 void RenderThemeMac::platformColorsDidChange()
236 {
237     m_systemColorCache.clear();
238     RenderTheme::platformColorsDidChange();
239 }
240
241 Color RenderThemeMac::systemColor(int cssValueId) const
242 {
243     if (m_systemColorCache.contains(cssValueId))
244         return m_systemColorCache.get(cssValueId);
245     
246     Color color;
247     switch (cssValueId) {
248         case CSS_VAL_ACTIVEBORDER:
249             color = convertNSColorToColor([NSColor keyboardFocusIndicatorColor]);
250             break;
251         case CSS_VAL_ACTIVECAPTION:
252             color = convertNSColorToColor([NSColor windowFrameTextColor]);
253             break;
254         case CSS_VAL_APPWORKSPACE:
255             color = convertNSColorToColor([NSColor headerColor]);
256             break;
257         case CSS_VAL_BACKGROUND:
258             // Use theme independent default
259             break;
260         case CSS_VAL_BUTTONFACE:
261             // We use this value instead of NSColor's controlColor to avoid website incompatibilities.
262             // We may want to change this to use the NSColor in future.
263             color = 0xFFC0C0C0;
264             break;
265         case CSS_VAL_BUTTONHIGHLIGHT:
266             color = convertNSColorToColor([NSColor controlHighlightColor]);
267             break;
268         case CSS_VAL_BUTTONSHADOW:
269             color = convertNSColorToColor([NSColor controlShadowColor]);
270             break;
271         case CSS_VAL_BUTTONTEXT:
272             color = convertNSColorToColor([NSColor controlTextColor]);
273             break;
274         case CSS_VAL_CAPTIONTEXT:
275             color = convertNSColorToColor([NSColor textColor]);
276             break;
277         case CSS_VAL_GRAYTEXT:
278             color = convertNSColorToColor([NSColor disabledControlTextColor]);
279             break;
280         case CSS_VAL_HIGHLIGHT:
281             color = convertNSColorToColor([NSColor selectedTextBackgroundColor]);
282             break;
283         case CSS_VAL_HIGHLIGHTTEXT:
284             color = convertNSColorToColor([NSColor selectedTextColor]);
285             break;
286         case CSS_VAL_INACTIVEBORDER:
287             color = convertNSColorToColor([NSColor controlBackgroundColor]);
288             break;
289         case CSS_VAL_INACTIVECAPTION:
290             color = convertNSColorToColor([NSColor controlBackgroundColor]);
291             break;
292         case CSS_VAL_INACTIVECAPTIONTEXT:
293             color = convertNSColorToColor([NSColor textColor]);
294             break;
295         case CSS_VAL_INFOBACKGROUND:
296             // There is no corresponding NSColor for this so we use a hard coded value.
297             color = 0xFFFBFCC5;
298             break;
299         case CSS_VAL_INFOTEXT:
300             color = convertNSColorToColor([NSColor textColor]);
301             break;
302         case CSS_VAL_MENU:
303             color = convertNSColorToColor([NSColor selectedMenuItemColor]);
304             break;
305         case CSS_VAL_MENUTEXT:
306             color = convertNSColorToColor([NSColor selectedMenuItemTextColor]);
307             break;
308         case CSS_VAL_SCROLLBAR:
309             color = convertNSColorToColor([NSColor scrollBarColor]);
310             break;
311         case CSS_VAL_TEXT:
312             color = convertNSColorToColor([NSColor textColor]);
313             break;
314         case CSS_VAL_THREEDDARKSHADOW:
315             color = convertNSColorToColor([NSColor controlDarkShadowColor]);
316             break;
317         case CSS_VAL_THREEDSHADOW:
318             color = convertNSColorToColor([NSColor shadowColor]);
319             break;
320         case CSS_VAL_THREEDFACE:
321             // We use this value instead of NSColor's controlColor to avoid website incompatibilities.
322             // We may want to change this to use the NSColor in future.
323             color = 0xFFC0C0C0;
324             break;
325         case CSS_VAL_THREEDHIGHLIGHT:
326             color = convertNSColorToColor([NSColor highlightColor]);
327             break;
328         case CSS_VAL_THREEDLIGHTSHADOW:
329             color = convertNSColorToColor([NSColor controlLightHighlightColor]);
330             break;
331         case CSS_VAL_WINDOW:
332             color = convertNSColorToColor([NSColor windowBackgroundColor]);
333             break;
334         case CSS_VAL_WINDOWFRAME:
335             color = convertNSColorToColor([NSColor windowFrameColor]);
336             break;
337         case CSS_VAL_WINDOWTEXT:
338             color = convertNSColorToColor([NSColor windowFrameTextColor]);
339             break;
340     }
341
342     if (!color.isValid())
343         color = RenderTheme::systemColor(cssValueId);
344
345     if (color.isValid())
346         m_systemColorCache.set(cssValueId, color.rgb());
347
348     return color;
349 }
350
351 bool RenderThemeMac::isControlStyled(const RenderStyle* style, const BorderData& border,
352                                      const BackgroundLayer& background, const Color& backgroundColor) const
353 {
354     if (style->appearance() == TextFieldAppearance || style->appearance() == TextAreaAppearance || style->appearance() == ListboxAppearance)
355         return style->border() != border;
356     return RenderTheme::isControlStyled(style, border, background, backgroundColor);
357 }
358
359 void RenderThemeMac::paintResizeControl(GraphicsContext* c, const IntRect& r)
360 {
361     Image* resizeCornerImage = this->resizeCornerImage();
362     IntPoint imagePoint(r.right() - resizeCornerImage->width(), r.bottom() - resizeCornerImage->height());
363     c->drawImage(resizeCornerImage, imagePoint);
364 }
365
366 void RenderThemeMac::adjustRepaintRect(const RenderObject* o, IntRect& r)
367 {
368     switch (o->style()->appearance()) {
369         case CheckboxAppearance: {
370             // Since we query the prototype cell, we need to update its state to match.
371             setCheckboxCellState(o, r);
372
373             // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox
374             // shadow" and the check.  We don't consider this part of the bounds of the control in WebKit.
375             r = inflateRect(r, checkboxSizes()[[checkbox() controlSize]], checkboxMargins());
376             break;
377         }
378         case RadioAppearance: {
379             // Since we query the prototype cell, we need to update its state to match.
380             setRadioCellState(o, r);
381
382             // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox
383             // shadow" and the check.  We don't consider this part of the bounds of the control in WebKit.
384             r = inflateRect(r, radioSizes()[[radio() controlSize]], radioMargins());
385             break;
386         }
387         case PushButtonAppearance:
388         case ButtonAppearance: {
389             // Since we query the prototype cell, we need to update its state to match.
390             setButtonCellState(o, r);
391
392             // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox
393             // shadow" and the check.  We don't consider this part of the bounds of the control in WebKit.
394             if ([button() bezelStyle] == NSRoundedBezelStyle)
395                 r = inflateRect(r, buttonSizes()[[button() controlSize]], buttonMargins());
396             break;
397         }
398         case MenulistAppearance: {
399             setPopupButtonCellState(o, r);
400             r = inflateRect(r, popupButtonSizes()[[popupButton() controlSize]], popupButtonMargins());
401             break;
402         }
403         default:
404             break;
405     }
406 }
407
408 IntRect RenderThemeMac::inflateRect(const IntRect& r, const IntSize& size, const int* margins) const
409 {
410     // Only do the inflation if the available width/height are too small.  Otherwise try to
411     // fit the glow/check space into the available box's width/height.
412     int widthDelta = r.width() - (size.width() + margins[leftMargin] + margins[rightMargin]);
413     int heightDelta = r.height() - (size.height() + margins[topMargin] + margins[bottomMargin]);
414     IntRect result(r);
415     if (widthDelta < 0) {
416         result.setX(result.x() - margins[leftMargin]);
417         result.setWidth(result.width() - widthDelta);
418     }
419     if (heightDelta < 0) {
420         result.setY(result.y() - margins[topMargin]);
421         result.setHeight(result.height() - heightDelta);
422     }
423     return result;
424 }
425
426 void RenderThemeMac::updateCheckedState(NSCell* cell, const RenderObject* o)
427 {
428     bool oldIndeterminate = [cell state] == NSMixedState;
429     bool indeterminate = isIndeterminate(o);
430     bool checked = isChecked(o);
431
432     if (oldIndeterminate != indeterminate) {
433         [cell setState:indeterminate ? NSMixedState : (checked ? NSOnState : NSOffState)];
434         return;
435     }
436
437     bool oldChecked = [cell state] == NSOnState;
438     if (checked != oldChecked)
439         [cell setState:checked ? NSOnState : NSOffState];
440 }
441
442 void RenderThemeMac::updateEnabledState(NSCell* cell, const RenderObject* o)
443 {
444     bool oldEnabled = [cell isEnabled];
445     bool enabled = isEnabled(o);
446     if (enabled != oldEnabled)
447         [cell setEnabled:enabled];
448 }
449
450 void RenderThemeMac::updateFocusedState(NSCell* cell, const RenderObject* o)
451 {
452     bool oldFocused = [cell showsFirstResponder];
453     bool focused = isFocused(o) && o->style()->outlineStyleIsAuto();
454     if (focused != oldFocused)
455         [cell setShowsFirstResponder:focused];
456 }
457
458 void RenderThemeMac::updatePressedState(NSCell* cell, const RenderObject* o)
459 {
460     bool oldPressed = [cell isHighlighted];
461     bool pressed = (o->element() && o->element()->active());
462     if (pressed != oldPressed)
463         [cell setHighlighted:pressed];
464 }
465
466 short RenderThemeMac::baselinePosition(const RenderObject* o) const
467 {
468     if (o->style()->appearance() == CheckboxAppearance || o->style()->appearance() == RadioAppearance)
469         return o->marginTop() + o->height() - 2; // The baseline is 2px up from the bottom of the checkbox/radio in AppKit.
470     return RenderTheme::baselinePosition(o);
471 }
472
473 bool RenderThemeMac::controlSupportsTints(const RenderObject* o) const
474 {
475     // An alternate way to implement this would be to get the appropriate cell object
476     // and call the private _needRedrawOnWindowChangedKeyState method. An advantage of
477     // that would be that we would match AppKit behavior more closely, but a disadvantage
478     // would be that we would rely on an AppKit SPI method.
479
480     if (!isEnabled(o))
481         return false;
482
483     // Checkboxes only have tint when checked.
484     if (o->style()->appearance() == CheckboxAppearance)
485         return isChecked(o);
486
487     // For now assume other controls have tint if enabled.
488     return true;
489 }
490
491 NSControlSize RenderThemeMac::controlSizeForFont(RenderStyle* style) const
492 {
493     int fontSize = style->fontSize();
494     if (fontSize >= 16)
495         return NSRegularControlSize;
496     if (fontSize >= 11)
497         return NSSmallControlSize;
498     return NSMiniControlSize;
499 }
500
501 void RenderThemeMac::setControlSize(NSCell* cell, const IntSize* sizes, const IntSize& minSize)
502 {
503     NSControlSize size;
504     if (minSize.width() >= sizes[NSRegularControlSize].width() &&
505         minSize.height() >= sizes[NSRegularControlSize].height())
506         size = NSRegularControlSize;
507     else if (minSize.width() >= sizes[NSSmallControlSize].width() &&
508              minSize.height() >= sizes[NSSmallControlSize].height())
509         size = NSSmallControlSize;
510     else
511         size = NSMiniControlSize;
512     if (size != [cell controlSize]) // Only update if we have to, since AppKit does work even if the size is the same.
513         [cell setControlSize:size];
514 }
515
516 IntSize RenderThemeMac::sizeForFont(RenderStyle* style, const IntSize* sizes) const
517 {
518     return sizes[controlSizeForFont(style)];
519 }
520
521 IntSize RenderThemeMac::sizeForSystemFont(RenderStyle* style, const IntSize* sizes) const
522 {
523     return sizes[controlSizeForSystemFont(style)];
524 }
525
526 void RenderThemeMac::setSizeFromFont(RenderStyle* style, const IntSize* sizes) const
527 {
528     // FIXME: Check is flawed, since it doesn't take min-width/max-width into account.
529     IntSize size = sizeForFont(style, sizes);
530     if (style->width().isIntrinsicOrAuto() && size.width() > 0)
531         style->setWidth(Length(size.width(), Fixed));
532     if (style->height().isAuto() && size.height() > 0)
533         style->setHeight(Length(size.height(), Fixed));
534 }
535
536 void RenderThemeMac::setFontFromControlSize(CSSStyleSelector* selector, RenderStyle* style, NSControlSize controlSize) const
537 {
538     FontDescription fontDescription;
539     fontDescription.setIsAbsoluteSize(true);
540     fontDescription.setGenericFamily(FontDescription::SerifFamily);
541
542     NSFont* font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:controlSize]];
543     fontDescription.firstFamily().setFamily([font familyName]);
544     fontDescription.setComputedSize([font pointSize]);
545     fontDescription.setSpecifiedSize([font pointSize]);
546
547     // Reset line height
548     style->setLineHeight(RenderStyle::initialLineHeight());
549
550     if (style->setFontDescription(fontDescription))
551         style->font().update(0);
552 }
553
554 NSControlSize RenderThemeMac::controlSizeForSystemFont(RenderStyle* style) const
555 {
556     int fontSize = style->fontSize();
557     if (fontSize >= [NSFont systemFontSizeForControlSize:NSRegularControlSize])
558         return NSRegularControlSize;
559     if (fontSize >= [NSFont systemFontSizeForControlSize:NSSmallControlSize])
560         return NSSmallControlSize;
561     return NSMiniControlSize;
562 }
563
564 bool RenderThemeMac::paintCheckbox(RenderObject* o, const RenderObject::PaintInfo&, const IntRect& r)
565 {
566     // Determine the width and height needed for the control and prepare the cell for painting.
567     setCheckboxCellState(o, r);
568
569     // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox
570     // shadow" and the check.  We don't consider this part of the bounds of the control in WebKit.
571     NSButtonCell* checkbox = this->checkbox();
572     IntRect inflatedRect = inflateRect(r, checkboxSizes()[[checkbox controlSize]], checkboxMargins());
573     [checkbox drawWithFrame:NSRect(inflatedRect) inView:o->view()->frameView()->getDocumentView()];
574     [checkbox setControlView:nil];
575
576     return false;
577 }
578
579 const IntSize* RenderThemeMac::checkboxSizes() const
580 {
581     static const IntSize sizes[3] = { IntSize(14, 14), IntSize(12, 12), IntSize(10, 10) };
582     return sizes;
583 }
584
585 const int* RenderThemeMac::checkboxMargins() const
586 {
587     static const int margins[3][4] =
588     {
589         { 3, 4, 4, 2 },
590         { 4, 3, 3, 3 },
591         { 4, 3, 3, 3 },
592     };
593     return margins[[checkbox() controlSize]];
594 }
595
596 void RenderThemeMac::setCheckboxCellState(const RenderObject* o, const IntRect& r)
597 {
598     NSButtonCell* checkbox = this->checkbox();
599
600     // Set the control size based off the rectangle we're painting into.
601     setControlSize(checkbox, checkboxSizes(), r.size());
602
603     // Update the various states we respond to.
604     updateCheckedState(checkbox, o);
605     updateEnabledState(checkbox, o);
606     updatePressedState(checkbox, o);
607     updateFocusedState(checkbox, o);
608 }
609
610 void RenderThemeMac::setCheckboxSize(RenderStyle* style) const
611 {
612     // If the width and height are both specified, then we have nothing to do.
613     if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
614         return;
615
616     // Use the font size to determine the intrinsic width of the control.
617     setSizeFromFont(style, checkboxSizes());
618 }
619
620 bool RenderThemeMac::paintRadio(RenderObject* o, const RenderObject::PaintInfo&, const IntRect& r)
621 {
622     // Determine the width and height needed for the control and prepare the cell for painting.
623     setRadioCellState(o, r);
624
625     // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox
626     // shadow" and the check.  We don't consider this part of the bounds of the control in WebKit.
627     NSButtonCell* radio = this->radio();
628     IntRect inflatedRect = inflateRect(r, radioSizes()[[radio controlSize]], radioMargins());
629     [radio drawWithFrame:NSRect(inflatedRect) inView:o->view()->frameView()->getDocumentView()];
630     [radio setControlView:nil];
631
632     return false;
633 }
634
635 const IntSize* RenderThemeMac::radioSizes() const
636 {
637     static const IntSize sizes[3] = { IntSize(14, 15), IntSize(12, 13), IntSize(10, 10) };
638     return sizes;
639 }
640
641 const int* RenderThemeMac::radioMargins() const
642 {
643     static const int margins[3][4] =
644     {
645         { 2, 2, 4, 2 },
646         { 3, 2, 3, 2 },
647         { 1, 0, 2, 0 },
648     };
649     return margins[[radio() controlSize]];
650 }
651
652 void RenderThemeMac::setRadioCellState(const RenderObject* o, const IntRect& r)
653 {
654     NSButtonCell* radio = this->radio();
655
656     // Set the control size based off the rectangle we're painting into.
657     setControlSize(radio, radioSizes(), r.size());
658
659     // Update the various states we respond to.
660     updateCheckedState(radio, o);
661     updateEnabledState(radio, o);
662     updatePressedState(radio, o);
663     updateFocusedState(radio, o);
664 }
665
666
667 void RenderThemeMac::setRadioSize(RenderStyle* style) const
668 {
669     // If the width and height are both specified, then we have nothing to do.
670     if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
671         return;
672
673     // Use the font size to determine the intrinsic width of the control.
674     setSizeFromFont(style, radioSizes());
675 }
676
677 void RenderThemeMac::setButtonPaddingFromControlSize(RenderStyle* style, NSControlSize size) const
678 {
679     // Just use 8px.  AppKit wants to use 11px for mini buttons, but that padding is just too large
680     // for real-world Web sites (creating a huge necessary minimum width for buttons whose space is
681     // by definition constrained, since we select mini only for small cramped environments.
682     // This also guarantees the HTML4 <button> will match our rendering by default, since we're using a consistent
683     // padding.
684     const int padding = 8;
685     style->setPaddingLeft(Length(padding, Fixed));
686     style->setPaddingRight(Length(padding, Fixed));
687     style->setPaddingTop(Length(0, Fixed));
688     style->setPaddingBottom(Length(0, Fixed));
689 }
690
691 void RenderThemeMac::adjustButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
692 {
693     // There are three appearance constants for buttons.
694     // (1) Push-button is the constant for the default Aqua system button.  Push buttons will not scale vertically and will not allow
695     // custom fonts or colors.  <input>s use this constant.  This button will allow custom colors and font weights/variants but won't
696     // scale vertically.
697     // (2) square-button is the constant for the square button.  This button will allow custom fonts and colors and will scale vertically.
698     // (3) Button is the constant that means "pick the best button as appropriate."  <button>s use this constant.  This button will
699     // also scale vertically and allow custom fonts and colors.  It will attempt to use Aqua if possible and will make this determination
700     // solely on the rectangle of the control.
701
702     // Determine our control size based off our font.
703     NSControlSize controlSize = controlSizeForFont(style);
704
705     if (style->appearance() == PushButtonAppearance) {
706         // Ditch the border.
707         style->resetBorder();
708
709         // Height is locked to auto.
710         style->setHeight(Length(Auto));
711
712         // White-space is locked to pre
713         style->setWhiteSpace(PRE);
714
715         // Set the button's vertical size.
716         setButtonSize(style);
717
718         // Add in the padding that we'd like to use.
719         setButtonPaddingFromControlSize(style, controlSize);
720
721         // Our font is locked to the appropriate system font size for the control.  To clarify, we first use the CSS-specified font to figure out
722         // a reasonable control size, but once that control size is determined, we throw that font away and use the appropriate
723         // system font for the control size instead.
724         setFontFromControlSize(selector, style, controlSize);
725     } else {
726         // Set a min-height so that we can't get smaller than the mini button.
727         style->setMinHeight(Length(15, Fixed));
728
729         // Reset the top and bottom borders.
730         style->resetBorderTop();
731         style->resetBorderBottom();
732     }
733
734     style->setBoxShadow(0);
735 }
736
737 const IntSize* RenderThemeMac::buttonSizes() const
738 {
739     static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) };
740     return sizes;
741 }
742
743 const int* RenderThemeMac::buttonMargins() const
744 {
745     static const int margins[3][4] =
746     {
747         { 4, 6, 7, 6 },
748         { 4, 5, 6, 5 },
749         { 0, 1, 1, 1 },
750     };
751     return margins[[button() controlSize]];
752 }
753
754 void RenderThemeMac::setButtonSize(RenderStyle* style) const
755 {
756     // If the width and height are both specified, then we have nothing to do.
757     if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
758         return;
759
760     // Use the font size to determine the intrinsic width of the control.
761     setSizeFromFont(style, buttonSizes());
762 }
763
764 void RenderThemeMac::setButtonCellState(const RenderObject* o, const IntRect& r)
765 {
766     NSButtonCell* button = this->button();
767
768     // Set the control size based off the rectangle we're painting into.
769     if (o->style()->appearance() == SquareButtonAppearance ||
770         r.height() > buttonSizes()[NSRegularControlSize].height()) {
771         // Use the square button
772         if ([button bezelStyle] != NSShadowlessSquareBezelStyle)
773             [button setBezelStyle:NSShadowlessSquareBezelStyle];
774     } else if ([button bezelStyle] != NSRoundedBezelStyle)
775         [button setBezelStyle:NSRoundedBezelStyle];
776
777     setControlSize(button, buttonSizes(), r.size());
778
779     // Update the various states we respond to.
780     updateCheckedState(button, o);
781     updateEnabledState(button, o);
782     updatePressedState(button, o);
783     updateFocusedState(button, o);
784 }
785
786 bool RenderThemeMac::paintButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
787 {
788     NSButtonCell* button = this->button();
789     LocalCurrentGraphicsContext localContext(paintInfo.context);
790
791     // Determine the width and height needed for the control and prepare the cell for painting.
792     setButtonCellState(o, r);
793
794     // We inflate the rect as needed to account for padding included in the cell to accommodate the button
795     // shadow.  We don't consider this part of the bounds of the control in WebKit.
796     IntSize size = buttonSizes()[[button controlSize]];
797     size.setWidth(r.width());
798     IntRect inflatedRect = r;
799     if ([button bezelStyle] == NSRoundedBezelStyle) {
800         // Center the button within the available space.
801         if (inflatedRect.height() > size.height()) {
802             inflatedRect.setY(inflatedRect.y() + (inflatedRect.height() - size.height()) / 2);
803             inflatedRect.setHeight(size.height());
804         }
805
806         // Now inflate it to account for the shadow.
807         inflatedRect = inflateRect(inflatedRect, size, buttonMargins());
808     }
809
810     [button drawWithFrame:NSRect(inflatedRect) inView:o->view()->frameView()->getDocumentView()];
811     [button setControlView:nil];
812
813     return false;
814 }
815
816 bool RenderThemeMac::paintTextField(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
817 {
818     LocalCurrentGraphicsContext localContext(paintInfo.context);
819     wkDrawBezeledTextFieldCell(r, isEnabled(o) && !isReadOnlyControl(o));
820     return false;
821 }
822
823 void RenderThemeMac::adjustTextFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const
824 {
825 }
826
827 bool RenderThemeMac::paintCapsLockIndicator(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
828 {
829     if (paintInfo.context->paintingDisabled())
830         return true;
831
832     wkDrawCapsLockIndicator(paintInfo.context->platformContext(), r);
833     
834     return false;
835 }
836
837 bool RenderThemeMac::paintTextArea(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
838 {
839     LocalCurrentGraphicsContext localContext(paintInfo.context);
840     wkDrawBezeledTextArea(r, isEnabled(o) && !isReadOnlyControl(o));
841     return false;
842 }
843
844 void RenderThemeMac::adjustTextAreaStyle(CSSStyleSelector*, RenderStyle*, Element*) const
845 {
846 }
847
848 const int* RenderThemeMac::popupButtonMargins() const
849 {
850     static const int margins[3][4] =
851     {
852         { 0, 3, 1, 3 },
853         { 0, 3, 2, 3 },
854         { 0, 1, 0, 1 }
855     };
856     return margins[[popupButton() controlSize]];
857 }
858
859 const IntSize* RenderThemeMac::popupButtonSizes() const
860 {
861     static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) };
862     return sizes;
863 }
864
865 const int* RenderThemeMac::popupButtonPadding(NSControlSize size) const
866 {
867     static const int padding[3][4] =
868     {
869         { 2, 26, 3, 8 },
870         { 2, 23, 3, 8 },
871         { 2, 22, 3, 10 }
872     };
873     return padding[size];
874 }
875
876 bool RenderThemeMac::paintMenuList(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
877 {
878     setPopupButtonCellState(o, r);
879
880     NSPopUpButtonCell* popupButton = this->popupButton();
881
882     IntRect inflatedRect = r;
883     IntSize size = popupButtonSizes()[[popupButton controlSize]];
884     size.setWidth(r.width());
885
886     // Now inflate it to account for the shadow.
887     if (r.width() >= minimumMenuListSize(o->style()))
888         inflatedRect = inflateRect(inflatedRect, size, popupButtonMargins());
889
890 #ifndef BUILDING_ON_TIGER
891     // On Leopard, the cell will draw outside of the given rect, so we have to clip to the rect
892     paintInfo.context->save();
893     paintInfo.context->clip(inflatedRect);
894 #endif
895
896     [popupButton drawWithFrame:inflatedRect inView:o->view()->frameView()->getDocumentView()];
897     [popupButton setControlView:nil];
898
899 #ifndef BUILDING_ON_TIGER
900     paintInfo.context->restore();
901 #endif
902
903     return false;
904 }
905
906 const float baseFontSize = 11.0f;
907 const float baseArrowHeight = 4.0f;
908 const float baseArrowWidth = 5.0f;
909 const float baseSpaceBetweenArrows = 2.0f;
910 const int arrowPaddingLeft = 6;
911 const int arrowPaddingRight = 6;
912 const int paddingBeforeSeparator = 4;
913 const int baseBorderRadius = 5;
914 const int styledPopupPaddingLeft = 8;
915 const int styledPopupPaddingTop = 1;
916 const int styledPopupPaddingBottom = 2;
917
918 static void TopGradientInterpolate(void* info, const CGFloat* inData, CGFloat* outData)
919 {
920     static float dark[4] = { 1.0f, 1.0f, 1.0f, 0.4f };
921     static float light[4] = { 1.0f, 1.0f, 1.0f, 0.15f };
922     float a = inData[0];
923     int i = 0;
924     for (i = 0; i < 4; i++)
925         outData[i] = (1.0f - a) * dark[i] + a * light[i];
926 }
927
928 static void BottomGradientInterpolate(void* info, const CGFloat* inData, CGFloat* outData)
929 {
930     static float dark[4] = { 1.0f, 1.0f, 1.0f, 0.0f };
931     static float light[4] = { 1.0f, 1.0f, 1.0f, 0.3f };
932     float a = inData[0];
933     int i = 0;
934     for (i = 0; i < 4; i++)
935         outData[i] = (1.0f - a) * dark[i] + a * light[i];
936 }
937
938 static void MainGradientInterpolate(void* info, const CGFloat* inData, CGFloat* outData)
939 {
940     static float dark[4] = { 0.0f, 0.0f, 0.0f, 0.15f };
941     static float light[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
942     float a = inData[0];
943     int i = 0;
944     for (i = 0; i < 4; i++)
945         outData[i] = (1.0f - a) * dark[i] + a * light[i];
946 }
947
948 static void TrackGradientInterpolate(void* info, const CGFloat* inData, CGFloat* outData)
949 {
950     static float dark[4] = { 0.0f, 0.0f, 0.0f, 0.678f };
951     static float light[4] = { 0.0f, 0.0f, 0.0f, 0.13f };
952     float a = inData[0];
953     int i = 0;
954     for (i = 0; i < 4; i++)
955         outData[i] = (1.0f - a) * dark[i] + a * light[i];
956 }
957
958 void RenderThemeMac::paintMenuListButtonGradients(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
959 {
960     CGContextRef context = paintInfo.context->platformContext();
961
962     paintInfo.context->save();
963
964     int radius = o->style()->borderTopLeftRadius().width();
965
966     RetainPtr<CGColorSpaceRef> cspace(AdoptCF, CGColorSpaceCreateDeviceRGB());
967
968     FloatRect topGradient(r.x(), r.y(), r.width(), r.height() / 2.0f);
969     struct CGFunctionCallbacks topCallbacks = { 0, TopGradientInterpolate, NULL };
970     RetainPtr<CGFunctionRef> topFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &topCallbacks));
971     RetainPtr<CGShadingRef> topShading(AdoptCF, CGShadingCreateAxial(cspace.get(), CGPointMake(topGradient.x(), topGradient.y()), CGPointMake(topGradient.x(), topGradient.bottom()), topFunction.get(), false, false));
972
973     FloatRect bottomGradient(r.x() + radius, r.y() + r.height() / 2.0f, r.width() - 2.0f * radius, r.height() / 2.0f);
974     struct CGFunctionCallbacks bottomCallbacks = { 0, BottomGradientInterpolate, NULL };
975     RetainPtr<CGFunctionRef> bottomFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &bottomCallbacks));
976     RetainPtr<CGShadingRef> bottomShading(AdoptCF, CGShadingCreateAxial(cspace.get(), CGPointMake(bottomGradient.x(),  bottomGradient.y()), CGPointMake(bottomGradient.x(), bottomGradient.bottom()), bottomFunction.get(), false, false));
977
978     struct CGFunctionCallbacks mainCallbacks = { 0, MainGradientInterpolate, NULL };
979     RetainPtr<CGFunctionRef> mainFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &mainCallbacks));
980     RetainPtr<CGShadingRef> mainShading(AdoptCF, CGShadingCreateAxial(cspace.get(), CGPointMake(r.x(),  r.y()), CGPointMake(r.x(), r.bottom()), mainFunction.get(), false, false));
981
982     RetainPtr<CGShadingRef> leftShading(AdoptCF, CGShadingCreateAxial(cspace.get(), CGPointMake(r.x(),  r.y()), CGPointMake(r.x() + radius, r.y()), mainFunction.get(), false, false));
983
984     RetainPtr<CGShadingRef> rightShading(AdoptCF, CGShadingCreateAxial(cspace.get(), CGPointMake(r.right(),  r.y()), CGPointMake(r.right() - radius, r.y()), mainFunction.get(), false, false));
985     paintInfo.context->save();
986     CGContextClipToRect(context, r);
987     paintInfo.context->addRoundedRectClip(r,
988         o->style()->borderTopLeftRadius(), o->style()->borderTopRightRadius(),
989         o->style()->borderBottomLeftRadius(), o->style()->borderBottomRightRadius());
990     CGContextDrawShading(context, mainShading.get());
991     paintInfo.context->restore();
992
993     paintInfo.context->save();
994     CGContextClipToRect(context, topGradient);
995     paintInfo.context->addRoundedRectClip(enclosingIntRect(topGradient),
996         o->style()->borderTopLeftRadius(), o->style()->borderTopRightRadius(),
997         IntSize(), IntSize());
998     CGContextDrawShading(context, topShading.get());
999     paintInfo.context->restore();
1000
1001     paintInfo.context->save();
1002     CGContextClipToRect(context, bottomGradient);
1003     paintInfo.context->addRoundedRectClip(enclosingIntRect(bottomGradient),
1004         IntSize(), IntSize(),
1005         o->style()->borderBottomLeftRadius(), o->style()->borderBottomRightRadius());
1006     CGContextDrawShading(context, bottomShading.get());
1007     paintInfo.context->restore();
1008
1009     paintInfo.context->save();
1010     CGContextClipToRect(context, r);
1011     paintInfo.context->addRoundedRectClip(r,
1012         o->style()->borderTopLeftRadius(), o->style()->borderTopRightRadius(),
1013         o->style()->borderBottomLeftRadius(), o->style()->borderBottomRightRadius());
1014     CGContextDrawShading(context, leftShading.get());
1015     CGContextDrawShading(context, rightShading.get());
1016     paintInfo.context->restore();
1017
1018     paintInfo.context->restore();
1019 }
1020
1021 bool RenderThemeMac::paintMenuListButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
1022 {
1023     paintInfo.context->save();
1024
1025     IntRect bounds = IntRect(r.x() + o->style()->borderLeftWidth(),
1026                              r.y() + o->style()->borderTopWidth(),
1027                              r.width() - o->style()->borderLeftWidth() - o->style()->borderRightWidth(),
1028                              r.height() - o->style()->borderTopWidth() - o->style()->borderBottomWidth());
1029     // Draw the gradients to give the styled popup menu a button appearance
1030     paintMenuListButtonGradients(o, paintInfo, bounds);
1031
1032     // Since we actually know the size of the control here, we restrict the font scale to make sure the arrows will fit vertically in the bounds
1033     float fontScale = min(o->style()->fontSize() / baseFontSize, bounds.height() / (baseArrowHeight * 2 + baseSpaceBetweenArrows));
1034     float centerY = bounds.y() + bounds.height() / 2.0f;
1035     float arrowHeight = baseArrowHeight * fontScale;
1036     float arrowWidth = baseArrowWidth * fontScale;
1037     float leftEdge = bounds.right() - arrowPaddingRight - arrowWidth;
1038     float spaceBetweenArrows = baseSpaceBetweenArrows * fontScale;
1039
1040     if (bounds.width() < arrowWidth + arrowPaddingLeft)
1041         return false;
1042     
1043     paintInfo.context->setFillColor(o->style()->color());
1044     paintInfo.context->setStrokeStyle(NoStroke);
1045
1046     FloatPoint arrow1[3];
1047     arrow1[0] = FloatPoint(leftEdge, centerY - spaceBetweenArrows / 2.0f);
1048     arrow1[1] = FloatPoint(leftEdge + arrowWidth, centerY - spaceBetweenArrows / 2.0f);
1049     arrow1[2] = FloatPoint(leftEdge + arrowWidth / 2.0f, centerY - spaceBetweenArrows / 2.0f - arrowHeight);
1050
1051     // Draw the top arrow
1052     paintInfo.context->drawConvexPolygon(3, arrow1, true);
1053
1054     FloatPoint arrow2[3];
1055     arrow2[0] = FloatPoint(leftEdge, centerY + spaceBetweenArrows / 2.0f);
1056     arrow2[1] = FloatPoint(leftEdge + arrowWidth, centerY + spaceBetweenArrows / 2.0f);
1057     arrow2[2] = FloatPoint(leftEdge + arrowWidth / 2.0f, centerY + spaceBetweenArrows / 2.0f + arrowHeight);
1058
1059     // Draw the bottom arrow
1060     paintInfo.context->drawConvexPolygon(3, arrow2, true);
1061
1062     Color leftSeparatorColor(0, 0, 0, 40);
1063     Color rightSeparatorColor(255, 255, 255, 40);
1064
1065     // FIXME: Should the separator thickness and space be scaled up by fontScale?
1066     int separatorSpace = 2;
1067     int leftEdgeOfSeparator = static_cast<int>(leftEdge - arrowPaddingLeft); // FIXME: Round?
1068
1069     // Draw the separator to the left of the arrows
1070     paintInfo.context->setStrokeThickness(1.0f);
1071     paintInfo.context->setStrokeStyle(SolidStroke);
1072     paintInfo.context->setStrokeColor(leftSeparatorColor);
1073     paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator, bounds.y()),
1074                                 IntPoint(leftEdgeOfSeparator, bounds.bottom()));
1075
1076     paintInfo.context->setStrokeColor(rightSeparatorColor);
1077     paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.y()),
1078                                 IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.bottom()));
1079
1080     paintInfo.context->restore();
1081     return false;
1082 }
1083
1084 void RenderThemeMac::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
1085 {
1086     NSControlSize controlSize = controlSizeForFont(style);
1087
1088     style->resetBorder();
1089     style->resetPadding();
1090     
1091     // Height is locked to auto.
1092     style->setHeight(Length(Auto));
1093
1094     // White-space is locked to pre
1095     style->setWhiteSpace(PRE);
1096
1097     // Set the foreground color to black or gray when we have the aqua look.
1098     // Cast to RGB32 is to work around a compiler bug.
1099     style->setColor(e->isEnabled() ? static_cast<RGBA32>(Color::black) : Color::darkGray);
1100
1101     // Set the button's vertical size.
1102     setButtonSize(style);
1103
1104     // Our font is locked to the appropriate system font size for the control.  To clarify, we first use the CSS-specified font to figure out
1105     // a reasonable control size, but once that control size is determined, we throw that font away and use the appropriate
1106     // system font for the control size instead.
1107     setFontFromControlSize(selector, style, controlSize);
1108
1109     style->setBoxShadow(0);
1110 }
1111
1112 int RenderThemeMac::popupInternalPaddingLeft(RenderStyle* style) const
1113 {
1114     if (style->appearance() == MenulistAppearance)
1115         return popupButtonPadding(controlSizeForFont(style))[leftPadding];
1116     if (style->appearance() == MenulistButtonAppearance)
1117         return styledPopupPaddingLeft;
1118     return 0;
1119 }
1120
1121 int RenderThemeMac::popupInternalPaddingRight(RenderStyle* style) const
1122 {
1123     if (style->appearance() == MenulistAppearance)
1124         return popupButtonPadding(controlSizeForFont(style))[rightPadding];
1125     if (style->appearance() == MenulistButtonAppearance) {
1126         float fontScale = style->fontSize() / baseFontSize;
1127         float arrowWidth = baseArrowWidth * fontScale;
1128         return static_cast<int>(ceilf(arrowWidth + arrowPaddingLeft + arrowPaddingRight + paddingBeforeSeparator));
1129     }
1130     return 0;
1131 }
1132
1133 int RenderThemeMac::popupInternalPaddingTop(RenderStyle* style) const
1134 {
1135     if (style->appearance() == MenulistAppearance)
1136         return popupButtonPadding(controlSizeForFont(style))[topPadding];
1137     if (style->appearance() == MenulistButtonAppearance)
1138         return styledPopupPaddingTop;
1139     return 0;
1140 }
1141
1142 int RenderThemeMac::popupInternalPaddingBottom(RenderStyle* style) const
1143 {
1144     if (style->appearance() == MenulistAppearance)
1145         return popupButtonPadding(controlSizeForFont(style))[bottomPadding];
1146     if (style->appearance() == MenulistButtonAppearance)
1147         return styledPopupPaddingBottom;
1148     return 0;
1149 }
1150
1151 void RenderThemeMac::adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
1152 {
1153     float fontScale = style->fontSize() / baseFontSize;
1154
1155     style->resetPadding();
1156     style->setBorderRadius(IntSize(int(baseBorderRadius + fontScale - 1), int(baseBorderRadius + fontScale - 1))); // FIXME: Round up?
1157
1158     const int minHeight = 15;
1159     style->setMinHeight(Length(minHeight, Fixed));
1160     
1161     style->setLineHeight(RenderStyle::initialLineHeight());
1162 }
1163
1164 void RenderThemeMac::setPopupButtonCellState(const RenderObject* o, const IntRect& r)
1165 {
1166     NSPopUpButtonCell* popupButton = this->popupButton();
1167
1168     // Set the control size based off the rectangle we're painting into.
1169     setControlSize(popupButton, popupButtonSizes(), r.size());
1170
1171     // Update the various states we respond to.
1172     updateCheckedState(popupButton, o);
1173     updateEnabledState(popupButton, o);
1174     updatePressedState(popupButton, o);
1175     updateFocusedState(popupButton, o);
1176 }
1177
1178 const IntSize* RenderThemeMac::menuListSizes() const
1179 {
1180     static const IntSize sizes[3] = { IntSize(9, 0), IntSize(5, 0), IntSize(0, 0) };
1181     return sizes;
1182 }
1183
1184 int RenderThemeMac::minimumMenuListSize(RenderStyle* style) const
1185 {
1186     return sizeForSystemFont(style, menuListSizes()).width();
1187 }
1188
1189 const int trackWidth = 5;
1190 const int trackRadius = 2;
1191
1192 void RenderThemeMac::adjustSliderTrackStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
1193 {
1194     style->setBoxShadow(0);
1195 }
1196
1197 bool RenderThemeMac::paintSliderTrack(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
1198 {
1199     IntRect bounds = r;
1200
1201     if (o->style()->appearance() ==  SliderHorizontalAppearance) {
1202         bounds.setHeight(trackWidth);
1203         bounds.setY(r.y() + r.height() / 2 - trackWidth / 2);
1204     } else if (o->style()->appearance() == SliderVerticalAppearance) {
1205         bounds.setWidth(trackWidth);
1206         bounds.setX(r.x() + r.width() / 2 - trackWidth / 2);
1207     }
1208
1209     LocalCurrentGraphicsContext localContext(paintInfo.context);
1210     CGContextRef context = paintInfo.context->platformContext();
1211     RetainPtr<CGColorSpaceRef> cspace(AdoptCF, CGColorSpaceCreateDeviceRGB());
1212
1213     paintInfo.context->save();
1214     CGContextClipToRect(context, bounds);
1215
1216     struct CGFunctionCallbacks mainCallbacks = { 0, TrackGradientInterpolate, NULL };
1217     RetainPtr<CGFunctionRef> mainFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &mainCallbacks));
1218     RetainPtr<CGShadingRef> mainShading;
1219     if (o->style()->appearance() == SliderVerticalAppearance)
1220         mainShading.adoptCF(CGShadingCreateAxial(cspace.get(), CGPointMake(bounds.x(),  bounds.bottom()), CGPointMake(bounds.right(), bounds.bottom()), mainFunction.get(), false, false));
1221     else
1222         mainShading.adoptCF(CGShadingCreateAxial(cspace.get(), CGPointMake(bounds.x(),  bounds.y()), CGPointMake(bounds.x(), bounds.bottom()), mainFunction.get(), false, false));
1223
1224     IntSize radius(trackRadius, trackRadius);
1225     paintInfo.context->addRoundedRectClip(bounds,
1226         radius, radius,
1227         radius, radius);
1228     CGContextDrawShading(context, mainShading.get());
1229     paintInfo.context->restore();
1230     
1231     return false;
1232 }
1233
1234 void RenderThemeMac::adjustSliderThumbStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
1235 {
1236     style->setBoxShadow(0);
1237 }
1238
1239 const float verticalSliderHeightPadding = 0.1f;
1240
1241 bool RenderThemeMac::paintSliderThumb(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
1242 {
1243     ASSERT(o->parent()->isSlider());
1244
1245     NSSliderCell* sliderThumbCell = o->style()->appearance() == SliderThumbVerticalAppearance
1246         ? sliderThumbVertical()
1247         : sliderThumbHorizontal();
1248
1249     LocalCurrentGraphicsContext localContext(paintInfo.context);
1250
1251     // Update the various states we respond to.
1252     updateEnabledState(sliderThumbCell, o->parent());
1253     updateFocusedState(sliderThumbCell, o->parent());
1254
1255     // Update the pressed state using the NSCell tracking methods, since that's how NSSliderCell keeps track of it.
1256     bool oldPressed;
1257     if (o->style()->appearance() == SliderThumbVerticalAppearance)
1258         oldPressed = m_isSliderThumbVerticalPressed;
1259     else
1260         oldPressed = m_isSliderThumbHorizontalPressed;
1261
1262     bool pressed = static_cast<RenderSlider*>(o->parent())->inDragMode();
1263
1264     if (o->style()->appearance() == SliderThumbVerticalAppearance)
1265         m_isSliderThumbVerticalPressed = pressed;
1266     else
1267         m_isSliderThumbHorizontalPressed = pressed;
1268
1269     if (pressed != oldPressed) {
1270         if (pressed)
1271             [sliderThumbCell startTrackingAt:NSPoint() inView:nil];
1272         else
1273             [sliderThumbCell stopTracking:NSPoint() at:NSPoint() inView:nil mouseIsUp:YES];
1274     }
1275
1276     FloatRect bounds = r;
1277     // Make the height of the vertical slider slightly larger so NSSliderCell will draw a vertical slider.
1278     if (o->style()->appearance() == SliderThumbVerticalAppearance)
1279         bounds.setHeight(bounds.height() + verticalSliderHeightPadding);
1280
1281     [sliderThumbCell drawWithFrame:NSRect(bounds) inView:o->view()->frameView()->getDocumentView()];
1282     [sliderThumbCell setControlView:nil];
1283
1284     return false;
1285 }
1286
1287 const int sliderThumbWidth = 15;
1288 const int sliderThumbHeight = 15;
1289
1290 void RenderThemeMac::adjustSliderThumbSize(RenderObject* o) const
1291 {
1292     if (o->style()->appearance() == SliderThumbHorizontalAppearance || o->style()->appearance() == SliderThumbVerticalAppearance) {
1293         o->style()->setWidth(Length(sliderThumbWidth, Fixed));
1294         o->style()->setHeight(Length(sliderThumbHeight, Fixed));
1295     }
1296 }
1297
1298 bool RenderThemeMac::paintSearchField(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
1299 {
1300     NSSearchFieldCell* search = this->search();
1301     LocalCurrentGraphicsContext localContext(paintInfo.context);
1302
1303     setSearchCellState(o, r);
1304
1305     // Set the search button to nil before drawing.  Then reset it so we can draw it later.
1306     [search setSearchButtonCell:nil];
1307
1308     [search drawWithFrame:NSRect(r) inView:o->view()->frameView()->getDocumentView()];
1309 #ifdef BUILDING_ON_TIGER
1310     if ([search showsFirstResponder])
1311         wkDrawTextFieldCellFocusRing(search, NSRect(r));
1312 #endif
1313
1314     [search setControlView:nil];
1315     [search resetSearchButtonCell];
1316
1317     return false;
1318 }
1319
1320 void RenderThemeMac::setSearchCellState(RenderObject* o, const IntRect& r)
1321 {
1322     NSSearchFieldCell* search = this->search();
1323
1324     [search setControlSize:controlSizeForFont(o->style())];
1325
1326     // Update the various states we respond to.
1327     updateEnabledState(search, o);
1328     updateFocusedState(search, o);
1329 }
1330
1331 const IntSize* RenderThemeMac::searchFieldSizes() const
1332 {
1333     static const IntSize sizes[3] = { IntSize(0, 22), IntSize(0, 19), IntSize(0, 17) };
1334     return sizes;
1335 }
1336
1337 void RenderThemeMac::setSearchFieldSize(RenderStyle* style) const
1338 {
1339     // If the width and height are both specified, then we have nothing to do.
1340     if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
1341         return;
1342     
1343     // Use the font size to determine the intrinsic width of the control.
1344     setSizeFromFont(style, searchFieldSizes());
1345 }
1346
1347 void RenderThemeMac::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
1348 {
1349     // Override border.
1350     style->resetBorder();
1351     const short borderWidth = 2;
1352     style->setBorderLeftWidth(borderWidth);
1353     style->setBorderLeftStyle(INSET);
1354     style->setBorderRightWidth(borderWidth);
1355     style->setBorderRightStyle(INSET);
1356     style->setBorderBottomWidth(borderWidth);
1357     style->setBorderBottomStyle(INSET);
1358     style->setBorderTopWidth(borderWidth);
1359     style->setBorderTopStyle(INSET);    
1360     
1361     // Override height.
1362     style->setHeight(Length(Auto));
1363     setSearchFieldSize(style);
1364     
1365     // Override padding size to match AppKit text positioning.
1366     const int padding = 1;
1367     style->setPaddingLeft(Length(padding, Fixed));
1368     style->setPaddingRight(Length(padding, Fixed));
1369     style->setPaddingTop(Length(padding, Fixed));
1370     style->setPaddingBottom(Length(padding, Fixed));
1371     
1372     NSControlSize controlSize = controlSizeForFont(style);
1373     setFontFromControlSize(selector, style, controlSize);
1374
1375     style->setBoxShadow(0);
1376 }
1377
1378 bool RenderThemeMac::paintSearchFieldCancelButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
1379 {
1380     Node* input = o->node()->shadowAncestorNode();
1381     setSearchCellState(input->renderer(), r);
1382
1383     NSSearchFieldCell* search = this->search();
1384
1385     updatePressedState([search cancelButtonCell], o);
1386
1387     NSRect bounds = [search cancelButtonRectForBounds:NSRect(input->renderer()->absoluteBoundingBoxRect())];
1388     [[search cancelButtonCell] drawWithFrame:bounds inView:o->view()->frameView()->getDocumentView()];
1389     [[search cancelButtonCell] setControlView:nil];
1390
1391     return false;
1392 }
1393
1394 const IntSize* RenderThemeMac::cancelButtonSizes() const
1395 {
1396     static const IntSize sizes[3] = { IntSize(16, 13), IntSize(13, 11), IntSize(13, 9) };
1397     return sizes;
1398 }
1399
1400 void RenderThemeMac::adjustSearchFieldCancelButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
1401 {
1402     IntSize size = sizeForSystemFont(style, cancelButtonSizes());
1403     style->setWidth(Length(size.width(), Fixed));
1404     style->setHeight(Length(size.height(), Fixed));
1405     style->setBoxShadow(0);
1406 }
1407
1408 const IntSize* RenderThemeMac::resultsButtonSizes() const
1409 {
1410     static const IntSize sizes[3] = { IntSize(19, 13), IntSize(17, 11), IntSize(17, 9) };
1411     return sizes;
1412 }
1413
1414 const int emptyResultsOffset = 9;
1415 void RenderThemeMac::adjustSearchFieldDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
1416 {
1417     IntSize size = sizeForSystemFont(style, resultsButtonSizes());
1418     style->setWidth(Length(size.width() - emptyResultsOffset, Fixed));
1419     style->setHeight(Length(size.height(), Fixed));
1420     style->setBoxShadow(0);
1421 }
1422
1423 bool RenderThemeMac::paintSearchFieldDecoration(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
1424 {
1425     return false;
1426 }
1427
1428 void RenderThemeMac::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
1429 {
1430     IntSize size = sizeForSystemFont(style, resultsButtonSizes());
1431     style->setWidth(Length(size.width(), Fixed));
1432     style->setHeight(Length(size.height(), Fixed));
1433     style->setBoxShadow(0);
1434 }
1435
1436 bool RenderThemeMac::paintSearchFieldResultsDecoration(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
1437 {
1438     Node* input = o->node()->shadowAncestorNode();
1439     setSearchCellState(input->renderer(), r);
1440
1441     NSSearchFieldCell* search = this->search();
1442
1443     if ([search searchMenuTemplate] != nil)
1444         [search setSearchMenuTemplate:nil];
1445
1446     NSRect bounds = [search searchButtonRectForBounds:NSRect(input->renderer()->absoluteBoundingBoxRect())];
1447     [[search searchButtonCell] drawWithFrame:bounds inView:o->view()->frameView()->getDocumentView()];
1448     [[search searchButtonCell] setControlView:nil];
1449     return false;
1450 }
1451
1452 const int resultsArrowWidth = 5;
1453 void RenderThemeMac::adjustSearchFieldResultsButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
1454 {
1455     IntSize size = sizeForSystemFont(style, resultsButtonSizes());
1456     style->setWidth(Length(size.width() + resultsArrowWidth, Fixed));
1457     style->setHeight(Length(size.height(), Fixed));
1458     style->setBoxShadow(0);
1459 }
1460
1461 bool RenderThemeMac::paintSearchFieldResultsButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
1462 {
1463     Node* input = o->node()->shadowAncestorNode();
1464     setSearchCellState(input->renderer(), r);
1465
1466     NSSearchFieldCell* search = this->search();
1467
1468     if (![search searchMenuTemplate])
1469         [search setSearchMenuTemplate:searchMenuTemplate()];
1470
1471     NSRect bounds = [search searchButtonRectForBounds:NSRect(input->renderer()->absoluteBoundingBoxRect())];
1472     [[search searchButtonCell] drawWithFrame:bounds inView:o->view()->frameView()->getDocumentView()];
1473     [[search searchButtonCell] setControlView:nil];
1474     return false;
1475 }
1476
1477 NSButtonCell* RenderThemeMac::checkbox() const
1478 {
1479     if (!m_checkbox) {
1480         m_checkbox.adoptNS([[NSButtonCell alloc] init]);
1481         [m_checkbox.get() setButtonType:NSSwitchButton];
1482         [m_checkbox.get() setTitle:nil];
1483         [m_checkbox.get() setAllowsMixedState:YES];
1484         [m_checkbox.get() setFocusRingType:NSFocusRingTypeExterior];
1485     }
1486     
1487     return m_checkbox.get();
1488 }
1489
1490 NSButtonCell* RenderThemeMac::radio() const
1491 {
1492     if (!m_radio) {
1493         m_radio.adoptNS([[NSButtonCell alloc] init]);
1494         [m_radio.get() setButtonType:NSRadioButton];
1495         [m_radio.get() setTitle:nil];
1496         [m_radio.get() setFocusRingType:NSFocusRingTypeExterior];
1497     }
1498     
1499     return m_radio.get();
1500 }
1501
1502 NSButtonCell* RenderThemeMac::button() const
1503 {
1504     if (!m_button) {
1505         m_button.adoptNS([[NSButtonCell alloc] init]);
1506         [m_button.get() setTitle:nil];
1507         [m_button.get() setButtonType:NSMomentaryPushInButton];
1508     }
1509     
1510     return m_button.get();
1511 }
1512
1513 NSPopUpButtonCell* RenderThemeMac::popupButton() const
1514 {
1515     if (!m_popupButton) {
1516         m_popupButton.adoptNS([[NSPopUpButtonCell alloc] initTextCell:@"" pullsDown:NO]);
1517         [m_popupButton.get() setUsesItemFromMenu:NO];
1518         [m_popupButton.get() setFocusRingType:NSFocusRingTypeExterior];
1519     }
1520     
1521     return m_popupButton.get();
1522 }
1523
1524 NSSearchFieldCell* RenderThemeMac::search() const
1525 {
1526     if (!m_search) {
1527         m_search.adoptNS([[NSSearchFieldCell alloc] initTextCell:@""]);
1528         [m_search.get() setBezelStyle:NSTextFieldRoundedBezel];
1529         [m_search.get() setBezeled:YES];
1530         [m_search.get() setEditable:YES];
1531         [m_search.get() setFocusRingType:NSFocusRingTypeExterior];
1532     }
1533
1534     return m_search.get();
1535 }
1536
1537 NSMenu* RenderThemeMac::searchMenuTemplate() const
1538 {
1539     if (!m_searchMenuTemplate)
1540         m_searchMenuTemplate.adoptNS([[NSMenu alloc] initWithTitle:@""]);
1541
1542     return m_searchMenuTemplate.get();
1543 }
1544
1545 NSSliderCell* RenderThemeMac::sliderThumbHorizontal() const
1546 {
1547     if (!m_sliderThumbHorizontal) {
1548         m_sliderThumbHorizontal.adoptNS([[NSSliderCell alloc] init]);
1549         [m_sliderThumbHorizontal.get() setTitle:nil];
1550         [m_sliderThumbHorizontal.get() setSliderType:NSLinearSlider];
1551         [m_sliderThumbHorizontal.get() setControlSize:NSSmallControlSize];
1552         [m_sliderThumbHorizontal.get() setFocusRingType:NSFocusRingTypeExterior];
1553     }
1554     
1555     return m_sliderThumbHorizontal.get();
1556 }
1557
1558 NSSliderCell* RenderThemeMac::sliderThumbVertical() const
1559 {
1560     if (!m_sliderThumbVertical) {
1561         m_sliderThumbVertical.adoptNS([[NSSliderCell alloc] init]);
1562         [m_sliderThumbVertical.get() setTitle:nil];
1563         [m_sliderThumbVertical.get() setSliderType:NSLinearSlider];
1564         [m_sliderThumbVertical.get() setControlSize:NSSmallControlSize];
1565         [m_sliderThumbVertical.get() setFocusRingType:NSFocusRingTypeExterior];
1566     }
1567     
1568     return m_sliderThumbVertical.get();
1569 }
1570
1571 Image* RenderThemeMac::resizeCornerImage() const
1572 {
1573     if (!m_resizeCornerImage)
1574         m_resizeCornerImage = Image::loadPlatformResource("textAreaResizeCorner");
1575     return m_resizeCornerImage;
1576 }
1577
1578 } // namespace WebCore