• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2008, 2009 Google, Inc.
4 * Copyright (C) 2009 Kenneth Rohde Christiansen
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// FIXME: we still need to figure out if passing a null view to the cell
23// drawing routines will work. I expect not, and if that's the case we'll have
24// to figure out something else. For now, at least leave the lines commented
25// in, but the procurement of the view if 0'd.
26
27#import "config.h"
28#import "RenderThemeChromiumMac.h"
29
30#import <Carbon/Carbon.h>
31#import <Cocoa/Cocoa.h>
32#import <math.h>
33
34#import "BitmapImage.h"
35#import "ChromiumBridge.h"
36#import "ColorMac.h"
37#import "CSSStyleSelector.h"
38#import "CSSValueKeywords.h"
39#import "Element.h"
40#import "FoundationExtras.h"
41#import "FrameView.h"
42#import "GraphicsContext.h"
43#import "HTMLInputElement.h"
44#import "HTMLMediaElement.h"
45#import "HTMLNames.h"
46#import "Image.h"
47#import "LocalCurrentGraphicsContext.h"
48#import "MediaControlElements.h"
49#import "RenderMedia.h"
50#import "RenderSlider.h"
51#import "RenderView.h"
52#import "SharedBuffer.h"
53#import "WebCoreSystemInterface.h"
54#import <wtf/RetainPtr.h>
55
56#ifdef BUILDING_ON_TIGER
57typedef int NSInteger;
58typedef unsigned NSUInteger;
59#endif
60
61using std::min;
62
63// The methods in this file are specific to the Mac OS X platform.
64
65// FIXME: The platform-independent code in this class should be factored out and merged with RenderThemeSafari.
66
67@interface WebCoreRenderThemeNotificationObserver : NSObject
68{
69    WebCore::RenderTheme *_theme;
70}
71
72- (id)initWithTheme:(WebCore::RenderTheme *)theme;
73- (void)systemColorsDidChange:(NSNotification *)notification;
74
75@end
76
77@implementation WebCoreRenderThemeNotificationObserver
78
79- (id)initWithTheme:(WebCore::RenderTheme *)theme
80{
81    [super init];
82    _theme = theme;
83
84    return self;
85}
86
87- (void)systemColorsDidChange:(NSNotification *)notification
88{
89    ASSERT([[notification name] isEqualToString:NSSystemColorsDidChangeNotification]);
90    _theme->platformColorsDidChange();
91}
92
93@end
94
95namespace WebCore {
96
97using namespace HTMLNames;
98
99enum {
100    TopMargin,
101    RightMargin,
102    BottomMargin,
103    LeftMargin
104};
105
106enum {
107    TopPadding,
108    RightPadding,
109    BottomPadding,
110    LeftPadding
111};
112
113// In our Mac port, we don't define PLATFORM(MAC) and thus don't pick up the
114// |operator NSRect()| on WebCore::IntRect and FloatRect. This substitues for
115// that missing conversion operator.
116NSRect IntRectToNSRect(const IntRect & rect)
117{
118    return NSMakeRect(rect.x(), rect.y(), rect.width(), rect.height());
119}
120
121NSRect FloatRectToNSRect(const FloatRect & rect)
122{
123    return NSMakeRect(rect.x(), rect.y(), rect.width(), rect.height());
124}
125
126IntRect NSRectToIntRect(const NSRect & rect)
127{
128    return IntRect(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
129}
130
131PassRefPtr<RenderTheme> RenderThemeChromiumMac::create()
132{
133    return adoptRef(new RenderThemeChromiumMac);
134}
135
136PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page)
137{
138    static RenderTheme* rt = RenderThemeChromiumMac::create().releaseRef();
139    return rt;
140}
141
142RenderThemeChromiumMac::RenderThemeChromiumMac()
143    : m_isSliderThumbHorizontalPressed(false)
144    , m_isSliderThumbVerticalPressed(false)
145    , m_notificationObserver(AdoptNS, [[WebCoreRenderThemeNotificationObserver alloc] initWithTheme:this])
146{
147    [[NSNotificationCenter defaultCenter] addObserver:m_notificationObserver.get()
148                                                        selector:@selector(systemColorsDidChange:)
149                                                            name:NSSystemColorsDidChangeNotification
150                                                          object:nil];
151}
152
153RenderThemeChromiumMac::~RenderThemeChromiumMac()
154{
155    [[NSNotificationCenter defaultCenter] removeObserver:m_notificationObserver.get()];
156}
157
158Color RenderThemeChromiumMac::platformActiveSelectionBackgroundColor() const
159{
160    NSColor* color = [[NSColor selectedTextBackgroundColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace];
161    return Color(static_cast<int>(255.0 * [color redComponent]), static_cast<int>(255.0 * [color greenComponent]), static_cast<int>(255.0 * [color blueComponent]));
162}
163
164Color RenderThemeChromiumMac::platformInactiveSelectionBackgroundColor() const
165{
166    NSColor* color = [[NSColor secondarySelectedControlColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace];
167    return Color(static_cast<int>(255.0 * [color redComponent]), static_cast<int>(255.0 * [color greenComponent]), static_cast<int>(255.0 * [color blueComponent]));
168}
169
170Color RenderThemeChromiumMac::platformActiveListBoxSelectionBackgroundColor() const
171{
172    NSColor* color = [[NSColor alternateSelectedControlColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace];
173    return Color(static_cast<int>(255.0 * [color redComponent]), static_cast<int>(255.0 * [color greenComponent]), static_cast<int>(255.0 * [color blueComponent]));
174}
175
176Color RenderThemeChromiumMac::platformFocusRingColor() const
177{
178    if (ChromiumBridge::layoutTestMode())
179        return oldAquaFocusRingColor();
180
181    return systemColor(CSSValueWebkitFocusRingColor);
182}
183
184static FontWeight toFontWeight(NSInteger appKitFontWeight)
185{
186    ASSERT(appKitFontWeight > 0 && appKitFontWeight < 15);
187    if (appKitFontWeight > 14)
188        appKitFontWeight = 14;
189    else if (appKitFontWeight < 1)
190        appKitFontWeight = 1;
191
192    static FontWeight fontWeights[] = {
193        FontWeight100,
194        FontWeight100,
195        FontWeight200,
196        FontWeight300,
197        FontWeight400,
198        FontWeight500,
199        FontWeight600,
200        FontWeight600,
201        FontWeight700,
202        FontWeight800,
203        FontWeight800,
204        FontWeight900,
205        FontWeight900,
206        FontWeight900
207    };
208    return fontWeights[appKitFontWeight - 1];
209}
210
211void RenderThemeChromiumMac::systemFont(int cssValueId, FontDescription& fontDescription) const
212{
213    static FontDescription systemFont;
214    static FontDescription smallSystemFont;
215    static FontDescription menuFont;
216    static FontDescription labelFont;
217    static FontDescription miniControlFont;
218    static FontDescription smallControlFont;
219    static FontDescription controlFont;
220
221    FontDescription* cachedDesc;
222    NSFont* font = nil;
223    switch (cssValueId) {
224    case CSSValueSmallCaption:
225        cachedDesc = &smallSystemFont;
226        if (!smallSystemFont.isAbsoluteSize())
227            font = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]];
228        break;
229    case CSSValueMenu:
230        cachedDesc = &menuFont;
231        if (!menuFont.isAbsoluteSize())
232            font = [NSFont menuFontOfSize:[NSFont systemFontSize]];
233        break;
234    case CSSValueStatusBar:
235        cachedDesc = &labelFont;
236        if (!labelFont.isAbsoluteSize())
237            font = [NSFont labelFontOfSize:[NSFont labelFontSize]];
238        break;
239    case CSSValueWebkitMiniControl:
240        cachedDesc = &miniControlFont;
241        if (!miniControlFont.isAbsoluteSize())
242            font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSMiniControlSize]];
243        break;
244    case CSSValueWebkitSmallControl:
245        cachedDesc = &smallControlFont;
246        if (!smallControlFont.isAbsoluteSize())
247            font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]];
248        break;
249    case CSSValueWebkitControl:
250        cachedDesc = &controlFont;
251        if (!controlFont.isAbsoluteSize())
252            font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]];
253        break;
254    default:
255        cachedDesc = &systemFont;
256        if (!systemFont.isAbsoluteSize())
257            font = [NSFont systemFontOfSize:[NSFont systemFontSize]];
258    }
259
260    if (font) {
261        NSFontManager *fontManager = [NSFontManager sharedFontManager];
262        cachedDesc->setIsAbsoluteSize(true);
263        cachedDesc->setGenericFamily(FontDescription::NoFamily);
264        cachedDesc->firstFamily().setFamily([font familyName]);
265        cachedDesc->setSpecifiedSize([font pointSize]);
266        cachedDesc->setWeight(toFontWeight([fontManager weightOfFont:font]));
267        cachedDesc->setItalic([fontManager traitsOfFont:font] & NSItalicFontMask);
268    }
269    fontDescription = *cachedDesc;
270}
271
272static RGBA32 convertNSColorToColor(NSColor *color)
273{
274    NSColor *colorInColorSpace = [color colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
275    if (colorInColorSpace) {
276        static const double scaleFactor = nextafter(256.0, 0.0);
277        return makeRGB(static_cast<int>(scaleFactor * [colorInColorSpace redComponent]),
278            static_cast<int>(scaleFactor * [colorInColorSpace greenComponent]),
279            static_cast<int>(scaleFactor * [colorInColorSpace blueComponent]));
280    }
281
282    // This conversion above can fail if the NSColor in question is an NSPatternColor
283    // (as many system colors are). These colors are actually a repeating pattern
284    // not just a solid color. To work around this we simply draw a 1x1 image of
285    // the color and use that pixel's color. It might be better to use an average of
286    // the colors in the pattern instead.
287    NSBitmapImageRep *offscreenRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil
288                                                                             pixelsWide:1
289                                                                             pixelsHigh:1
290                                                                          bitsPerSample:8
291                                                                        samplesPerPixel:4
292                                                                               hasAlpha:YES
293                                                                               isPlanar:NO
294                                                                         colorSpaceName:NSCalibratedRGBColorSpace
295                                                                            bytesPerRow:4
296                                                                           bitsPerPixel:32];
297
298    [NSGraphicsContext saveGraphicsState];
299    [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:offscreenRep]];
300    NSEraseRect(NSMakeRect(0, 0, 1, 1));
301    [color drawSwatchInRect:NSMakeRect(0, 0, 1, 1)];
302    [NSGraphicsContext restoreGraphicsState];
303
304    NSUInteger pixel[4];
305    [offscreenRep getPixel:pixel atX:0 y:0];
306
307    [offscreenRep release];
308
309    return makeRGB(pixel[0], pixel[1], pixel[2]);
310}
311
312static RGBA32 menuBackgroundColor()
313{
314    NSBitmapImageRep *offscreenRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil
315                                                                             pixelsWide:1
316                                                                             pixelsHigh:1
317                                                                          bitsPerSample:8
318                                                                        samplesPerPixel:4
319                                                                               hasAlpha:YES
320                                                                               isPlanar:NO
321                                                                         colorSpaceName:NSCalibratedRGBColorSpace
322                                                                            bytesPerRow:4
323                                                                           bitsPerPixel:32];
324
325    CGContextRef context = static_cast<CGContextRef>([[NSGraphicsContext graphicsContextWithBitmapImageRep:offscreenRep] graphicsPort]);
326    CGRect rect = CGRectMake(0, 0, 1, 1);
327    HIThemeMenuDrawInfo drawInfo;
328    drawInfo.version =  0;
329    drawInfo.menuType = kThemeMenuTypePopUp;
330    HIThemeDrawMenuBackground(&rect, &drawInfo, context, kHIThemeOrientationInverted);
331
332    NSUInteger pixel[4];
333    [offscreenRep getPixel:pixel atX:0 y:0];
334
335    [offscreenRep release];
336
337    return makeRGB(pixel[0], pixel[1], pixel[2]);
338}
339
340void RenderThemeChromiumMac::platformColorsDidChange()
341{
342    m_systemColorCache.clear();
343    RenderTheme::platformColorsDidChange();
344}
345
346Color RenderThemeChromiumMac::systemColor(int cssValueId) const
347{
348    if (m_systemColorCache.contains(cssValueId))
349        return m_systemColorCache.get(cssValueId);
350
351    Color color;
352    switch (cssValueId) {
353    case CSSValueActiveborder:
354        color = convertNSColorToColor([NSColor keyboardFocusIndicatorColor]);
355        break;
356    case CSSValueActivecaption:
357        color = convertNSColorToColor([NSColor windowFrameTextColor]);
358        break;
359    case CSSValueAppworkspace:
360        color = convertNSColorToColor([NSColor headerColor]);
361        break;
362    case CSSValueBackground:
363        // Use theme independent default
364        break;
365    case CSSValueButtonface:
366        // We use this value instead of NSColor's controlColor to avoid website incompatibilities.
367        // We may want to change this to use the NSColor in future.
368        color = 0xFFC0C0C0;
369        break;
370    case CSSValueButtonhighlight:
371        color = convertNSColorToColor([NSColor controlHighlightColor]);
372        break;
373    case CSSValueButtonshadow:
374        color = convertNSColorToColor([NSColor controlShadowColor]);
375        break;
376    case CSSValueButtontext:
377        color = convertNSColorToColor([NSColor controlTextColor]);
378        break;
379    case CSSValueCaptiontext:
380        color = convertNSColorToColor([NSColor textColor]);
381        break;
382    case CSSValueGraytext:
383        color = convertNSColorToColor([NSColor disabledControlTextColor]);
384        break;
385    case CSSValueHighlight:
386        color = convertNSColorToColor([NSColor selectedTextBackgroundColor]);
387        break;
388    case CSSValueHighlighttext:
389        color = convertNSColorToColor([NSColor selectedTextColor]);
390        break;
391    case CSSValueInactiveborder:
392        color = convertNSColorToColor([NSColor controlBackgroundColor]);
393        break;
394    case CSSValueInactivecaption:
395        color = convertNSColorToColor([NSColor controlBackgroundColor]);
396        break;
397    case CSSValueInactivecaptiontext:
398        color = convertNSColorToColor([NSColor textColor]);
399        break;
400    case CSSValueInfobackground:
401        // There is no corresponding NSColor for this so we use a hard coded value.
402        color = 0xFFFBFCC5;
403        break;
404    case CSSValueInfotext:
405        color = convertNSColorToColor([NSColor textColor]);
406        break;
407    case CSSValueMenu:
408        color = menuBackgroundColor();
409        break;
410    case CSSValueMenutext:
411        color = convertNSColorToColor([NSColor selectedMenuItemTextColor]);
412        break;
413    case CSSValueScrollbar:
414        color = convertNSColorToColor([NSColor scrollBarColor]);
415        break;
416    case CSSValueText:
417        color = convertNSColorToColor([NSColor textColor]);
418        break;
419    case CSSValueThreeddarkshadow:
420        color = convertNSColorToColor([NSColor controlDarkShadowColor]);
421        break;
422    case CSSValueThreedshadow:
423        color = convertNSColorToColor([NSColor shadowColor]);
424        break;
425    case CSSValueThreedface:
426        // We use this value instead of NSColor's controlColor to avoid website incompatibilities.
427        // We may want to change this to use the NSColor in future.
428        color = 0xFFC0C0C0;
429        break;
430    case CSSValueThreedhighlight:
431        color = convertNSColorToColor([NSColor highlightColor]);
432        break;
433    case CSSValueThreedlightshadow:
434        color = convertNSColorToColor([NSColor controlLightHighlightColor]);
435        break;
436    case CSSValueWebkitFocusRingColor:
437        color = convertNSColorToColor([NSColor keyboardFocusIndicatorColor]);
438        break;
439    case CSSValueWindow:
440        color = convertNSColorToColor([NSColor windowBackgroundColor]);
441        break;
442    case CSSValueWindowframe:
443        color = convertNSColorToColor([NSColor windowFrameColor]);
444        break;
445    case CSSValueWindowtext:
446        color = convertNSColorToColor([NSColor windowFrameTextColor]);
447        break;
448    }
449
450    if (!color.isValid())
451        color = RenderTheme::systemColor(cssValueId);
452
453    if (color.isValid())
454        m_systemColorCache.set(cssValueId, color.rgb());
455
456    return color;
457}
458
459bool RenderThemeChromiumMac::isControlStyled(const RenderStyle* style, const BorderData& border,
460                                             const FillLayer& background, const Color& backgroundColor) const
461{
462    if (style->appearance() == TextFieldPart || style->appearance() == TextAreaPart || style->appearance() == ListboxPart)
463        return style->border() != border;
464
465    // FIXME: This is horrible, but there is not much else that can be done.  Menu lists cannot draw properly when
466    // scaled.  They can't really draw properly when transformed either.  We can't detect the transform case at style
467    // adjustment time so that will just have to stay broken.  We can however detect that we're zooming.  If zooming
468    // is in effect we treat it like the control is styled.
469    if (style->appearance() == MenulistPart && style->effectiveZoom() != 1.0f)
470        return true;
471
472    return RenderTheme::isControlStyled(style, border, background, backgroundColor);
473}
474
475// FIXME: Use the code from the old upstream version, before it was converted to the new theme API in r37731.
476void RenderThemeChromiumMac::adjustRepaintRect(const RenderObject* o, IntRect& r)
477{
478    float zoomLevel = o->style()->effectiveZoom();
479
480    switch (o->style()->appearance()) {
481    case CheckboxPart: {
482        // Since we query the prototype cell, we need to update its state to match.
483        setCheckboxCellState(o, r);
484
485        // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox
486        // shadow" and the check.  We don't consider this part of the bounds of the control in WebKit.
487        IntSize size = checkboxSizes()[[checkbox() controlSize]];
488        size.setHeight(size.height() * zoomLevel);
489        size.setWidth(size.width() * zoomLevel);
490        r = inflateRect(r, size, checkboxMargins(), zoomLevel);
491        break;
492    }
493    case RadioPart: {
494        // Since we query the prototype cell, we need to update its state to match.
495        setRadioCellState(o, r);
496
497        // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox
498        // shadow" and the check.  We don't consider this part of the bounds of the control in WebKit.
499        IntSize size = radioSizes()[[radio() controlSize]];
500        size.setHeight(size.height() * zoomLevel);
501        size.setWidth(size.width() * zoomLevel);
502        r = inflateRect(r, size, radioMargins(), zoomLevel);
503        break;
504    }
505    case PushButtonPart:
506    case DefaultButtonPart:
507    case ButtonPart: {
508        // Since we query the prototype cell, we need to update its state to match.
509        setButtonCellState(o, r);
510
511        // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox
512        // shadow" and the check.  We don't consider this part of the bounds of the control in WebKit.
513        if ([button() bezelStyle] == NSRoundedBezelStyle) {
514            IntSize size = buttonSizes()[[button() controlSize]];
515            size.setHeight(size.height() * zoomLevel);
516            size.setWidth(r.width());
517            r = inflateRect(r, size, buttonMargins(), zoomLevel);
518        }
519        break;
520    }
521    case MenulistPart: {
522        setPopupButtonCellState(o, r);
523        IntSize size = popupButtonSizes()[[popupButton() controlSize]];
524        size.setHeight(size.height() * zoomLevel);
525        size.setWidth(r.width());
526        r = inflateRect(r, size, popupButtonMargins(), zoomLevel);
527        break;
528    }
529    default:
530        break;
531    }
532}
533
534IntRect RenderThemeChromiumMac::inflateRect(const IntRect& r, const IntSize& size, const int* margins, float zoomLevel) const
535{
536    // Only do the inflation if the available width/height are too small.  Otherwise try to
537    // fit the glow/check space into the available box's width/height.
538    int widthDelta = r.width() - (size.width() + margins[LeftMargin] * zoomLevel + margins[RightMargin] * zoomLevel);
539    int heightDelta = r.height() - (size.height() + margins[TopMargin] * zoomLevel + margins[BottomMargin] * zoomLevel);
540    IntRect result(r);
541    if (widthDelta < 0) {
542        result.setX(result.x() - margins[LeftMargin] * zoomLevel);
543        result.setWidth(result.width() - widthDelta);
544    }
545    if (heightDelta < 0) {
546        result.setY(result.y() - margins[TopMargin] * zoomLevel);
547        result.setHeight(result.height() - heightDelta);
548    }
549    return result;
550}
551
552void RenderThemeChromiumMac::updateCheckedState(NSCell* cell, const RenderObject* o)
553{
554    bool oldIndeterminate = [cell state] == NSMixedState;
555    bool indeterminate = isIndeterminate(o);
556    bool checked = isChecked(o);
557
558    if (oldIndeterminate != indeterminate) {
559        [cell setState:indeterminate ? NSMixedState : (checked ? NSOnState : NSOffState)];
560        return;
561    }
562
563    bool oldChecked = [cell state] == NSOnState;
564    if (checked != oldChecked)
565        [cell setState:checked ? NSOnState : NSOffState];
566}
567
568void RenderThemeChromiumMac::updateEnabledState(NSCell* cell, const RenderObject* o)
569{
570    bool oldEnabled = [cell isEnabled];
571    bool enabled = isEnabled(o);
572    if (enabled != oldEnabled)
573        [cell setEnabled:enabled];
574}
575
576void RenderThemeChromiumMac::updateFocusedState(NSCell* cell, const RenderObject* o)
577{
578    bool oldFocused = [cell showsFirstResponder];
579    bool focused = isFocused(o) && o->style()->outlineStyleIsAuto();
580    if (focused != oldFocused)
581        [cell setShowsFirstResponder:focused];
582}
583
584void RenderThemeChromiumMac::updatePressedState(NSCell* cell, const RenderObject* o)
585{
586    bool oldPressed = [cell isHighlighted];
587    bool pressed = (o->node() && o->node()->active());
588    if (pressed != oldPressed)
589        [cell setHighlighted:pressed];
590}
591
592// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731.
593int RenderThemeChromiumMac::baselinePosition(const RenderObject* o) const
594{
595    if (!o->isBox())
596        return 0;
597
598    if (o->style()->appearance() == CheckboxPart || o->style()->appearance() == RadioPart) {
599        const RenderBox* box = toRenderBox(o);
600        return box->marginTop() + box->height() - 2 * o->style()->effectiveZoom(); // The baseline is 2px up from the bottom of the checkbox/radio in AppKit.
601    }
602    return RenderTheme::baselinePosition(o);
603}
604
605bool RenderThemeChromiumMac::controlSupportsTints(const RenderObject* o) const
606{
607    // An alternate way to implement this would be to get the appropriate cell object
608    // and call the private _needRedrawOnWindowChangedKeyState method. An advantage of
609    // that would be that we would match AppKit behavior more closely, but a disadvantage
610    // would be that we would rely on an AppKit SPI method.
611
612    if (!isEnabled(o))
613        return false;
614
615    // Checkboxes only have tint when checked.
616    if (o->style()->appearance() == CheckboxPart)
617        return isChecked(o);
618
619    // For now assume other controls have tint if enabled.
620    return true;
621}
622
623NSControlSize RenderThemeChromiumMac::controlSizeForFont(RenderStyle* style) const
624{
625    int fontSize = style->fontSize();
626    if (fontSize >= 16)
627        return NSRegularControlSize;
628    if (fontSize >= 11)
629        return NSSmallControlSize;
630    return NSMiniControlSize;
631}
632
633void RenderThemeChromiumMac::setControlSize(NSCell* cell, const IntSize* sizes, const IntSize& minSize, float zoomLevel)
634{
635    NSControlSize size;
636    if (minSize.width() >= static_cast<int>(sizes[NSRegularControlSize].width() * zoomLevel) &&
637        minSize.height() >= static_cast<int>(sizes[NSRegularControlSize].height() * zoomLevel))
638        size = NSRegularControlSize;
639    else if (minSize.width() >= static_cast<int>(sizes[NSSmallControlSize].width() * zoomLevel) &&
640             minSize.height() >= static_cast<int>(sizes[NSSmallControlSize].height() * zoomLevel))
641        size = NSSmallControlSize;
642    else
643        size = NSMiniControlSize;
644    if (size != [cell controlSize]) // Only update if we have to, since AppKit does work even if the size is the same.
645        [cell setControlSize:size];
646}
647
648IntSize RenderThemeChromiumMac::sizeForFont(RenderStyle* style, const IntSize* sizes) const
649{
650    if (style->effectiveZoom() != 1.0f) {
651        IntSize result = sizes[controlSizeForFont(style)];
652        return IntSize(result.width() * style->effectiveZoom(), result.height() * style->effectiveZoom());
653    }
654    return sizes[controlSizeForFont(style)];
655}
656
657IntSize RenderThemeChromiumMac::sizeForSystemFont(RenderStyle* style, const IntSize* sizes) const
658{
659    if (style->effectiveZoom() != 1.0f) {
660        IntSize result = sizes[controlSizeForSystemFont(style)];
661        return IntSize(result.width() * style->effectiveZoom(), result.height() * style->effectiveZoom());
662    }
663    return sizes[controlSizeForSystemFont(style)];
664}
665
666void RenderThemeChromiumMac::setSizeFromFont(RenderStyle* style, const IntSize* sizes) const
667{
668    // FIXME: Check is flawed, since it doesn't take min-width/max-width into account.
669    IntSize size = sizeForFont(style, sizes);
670    if (style->width().isIntrinsicOrAuto() && size.width() > 0)
671        style->setWidth(Length(size.width(), Fixed));
672    if (style->height().isAuto() && size.height() > 0)
673        style->setHeight(Length(size.height(), Fixed));
674}
675
676void RenderThemeChromiumMac::setFontFromControlSize(CSSStyleSelector* selector, RenderStyle* style, NSControlSize controlSize) const
677{
678    FontDescription fontDescription;
679    fontDescription.setIsAbsoluteSize(true);
680    fontDescription.setGenericFamily(FontDescription::SerifFamily);
681
682    NSFont* font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:controlSize]];
683    fontDescription.firstFamily().setFamily([font familyName]);
684    fontDescription.setComputedSize([font pointSize] * style->effectiveZoom());
685    fontDescription.setSpecifiedSize([font pointSize] * style->effectiveZoom());
686
687    // Reset line height
688    style->setLineHeight(RenderStyle::initialLineHeight());
689
690    if (style->setFontDescription(fontDescription))
691        style->font().update(0);
692}
693
694NSControlSize RenderThemeChromiumMac::controlSizeForSystemFont(RenderStyle* style) const
695{
696    int fontSize = style->fontSize();
697    if (fontSize >= [NSFont systemFontSizeForControlSize:NSRegularControlSize])
698        return NSRegularControlSize;
699    if (fontSize >= [NSFont systemFontSizeForControlSize:NSSmallControlSize])
700        return NSSmallControlSize;
701    return NSMiniControlSize;
702}
703
704// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731.
705bool RenderThemeChromiumMac::paintCheckbox(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
706{
707    LocalCurrentGraphicsContext localContext(paintInfo.context);
708
709    // Determine the width and height needed for the control and prepare the cell for painting.
710    setCheckboxCellState(o, r);
711
712    paintInfo.context->save();
713
714    float zoomLevel = o->style()->effectiveZoom();
715
716    // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox
717    // shadow" and the check.  We don't consider this part of the bounds of the control in WebKit.
718    NSButtonCell* checkbox = this->checkbox();
719    IntSize size = checkboxSizes()[[checkbox controlSize]];
720    size.setWidth(size.width() * zoomLevel);
721    size.setHeight(size.height() * zoomLevel);
722    IntRect inflatedRect = inflateRect(r, size, checkboxMargins(), zoomLevel);
723
724    if (zoomLevel != 1.0f) {
725        inflatedRect.setWidth(inflatedRect.width() / zoomLevel);
726        inflatedRect.setHeight(inflatedRect.height() / zoomLevel);
727        paintInfo.context->translate(inflatedRect.x(), inflatedRect.y());
728        paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel));
729        paintInfo.context->translate(-inflatedRect.x(), -inflatedRect.y());
730    }
731
732    [checkbox drawWithFrame:NSRect(IntRectToNSRect(inflatedRect)) inView:nil];
733    [checkbox setControlView:nil];
734
735    paintInfo.context->restore();
736
737    return false;
738}
739
740// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731.
741const IntSize* RenderThemeChromiumMac::checkboxSizes() const
742{
743    static const IntSize sizes[3] = { IntSize(14, 14), IntSize(12, 12), IntSize(10, 10) };
744    return sizes;
745}
746
747// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731.
748const int* RenderThemeChromiumMac::checkboxMargins() const
749{
750    static const int margins[3][4] =
751    {
752        { 3, 4, 4, 2 },
753        { 4, 3, 3, 3 },
754        { 4, 3, 3, 3 },
755    };
756    return margins[[checkbox() controlSize]];
757}
758
759// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731.
760void RenderThemeChromiumMac::setCheckboxCellState(const RenderObject* o, const IntRect& r)
761{
762    NSButtonCell* checkbox = this->checkbox();
763
764    // Set the control size based off the rectangle we're painting into.
765    setControlSize(checkbox, checkboxSizes(), r.size(), o->style()->effectiveZoom());
766
767    // Update the various states we respond to.
768    updateCheckedState(checkbox, o);
769    updateEnabledState(checkbox, o);
770    updatePressedState(checkbox, o);
771    updateFocusedState(checkbox, o);
772}
773
774// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731.
775void RenderThemeChromiumMac::setCheckboxSize(RenderStyle* style) const
776{
777    // If the width and height are both specified, then we have nothing to do.
778    if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
779        return;
780
781    // Use the font size to determine the intrinsic width of the control.
782    setSizeFromFont(style, checkboxSizes());
783}
784
785// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731.
786bool RenderThemeChromiumMac::paintRadio(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
787{
788    LocalCurrentGraphicsContext localContext(paintInfo.context);
789
790    // Determine the width and height needed for the control and prepare the cell for painting.
791    setRadioCellState(o, r);
792
793    paintInfo.context->save();
794
795    float zoomLevel = o->style()->effectiveZoom();
796
797    // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox
798    // shadow" and the check.  We don't consider this part of the bounds of the control in WebKit.
799    NSButtonCell* radio = this->radio();
800    IntSize size = radioSizes()[[radio controlSize]];
801    size.setWidth(size.width() * zoomLevel);
802    size.setHeight(size.height() * zoomLevel);
803    IntRect inflatedRect = inflateRect(r, size, radioMargins(), zoomLevel);
804
805    if (zoomLevel != 1.0f) {
806        inflatedRect.setWidth(inflatedRect.width() / zoomLevel);
807        inflatedRect.setHeight(inflatedRect.height() / zoomLevel);
808        paintInfo.context->translate(inflatedRect.x(), inflatedRect.y());
809        paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel));
810        paintInfo.context->translate(-inflatedRect.x(), -inflatedRect.y());
811    }
812
813    [radio drawWithFrame:NSRect(IntRectToNSRect(inflatedRect)) inView:nil];
814    [radio setControlView:nil];
815
816    paintInfo.context->restore();
817
818    return false;
819}
820
821// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731.
822const IntSize* RenderThemeChromiumMac::radioSizes() const
823{
824    static const IntSize sizes[3] = { IntSize(14, 15), IntSize(12, 13), IntSize(10, 10) };
825    return sizes;
826}
827
828// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731.
829const int* RenderThemeChromiumMac::radioMargins() const
830{
831    static const int margins[3][4] =
832    {
833        { 2, 2, 4, 2 },
834        { 3, 2, 3, 2 },
835        { 1, 0, 2, 0 },
836    };
837    return margins[[radio() controlSize]];
838}
839
840// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731.
841void RenderThemeChromiumMac::setRadioCellState(const RenderObject* o, const IntRect& r)
842{
843    NSButtonCell* radio = this->radio();
844
845    // Set the control size based off the rectangle we're painting into.
846    setControlSize(radio, radioSizes(), r.size(), o->style()->effectiveZoom());
847
848    // Update the various states we respond to.
849    updateCheckedState(radio, o);
850    updateEnabledState(radio, o);
851    updatePressedState(radio, o);
852    updateFocusedState(radio, o);
853}
854
855// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731.
856void RenderThemeChromiumMac::setRadioSize(RenderStyle* style) const
857{
858    // If the width and height are both specified, then we have nothing to do.
859    if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
860        return;
861
862    // Use the font size to determine the intrinsic width of the control.
863    setSizeFromFont(style, radioSizes());
864}
865
866// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731.
867void RenderThemeChromiumMac::setButtonPaddingFromControlSize(RenderStyle* style, NSControlSize size) const
868{
869    // Just use 8px.  AppKit wants to use 11px for mini buttons, but that padding is just too large
870    // for real-world Web sites (creating a huge necessary minimum width for buttons whose space is
871    // by definition constrained, since we select mini only for small cramped environments.
872    // This also guarantees the HTML4 <button> will match our rendering by default, since we're using a consistent
873    // padding.
874    const int padding = 8 * style->effectiveZoom();
875    style->setPaddingLeft(Length(padding, Fixed));
876    style->setPaddingRight(Length(padding, Fixed));
877    style->setPaddingTop(Length(0, Fixed));
878    style->setPaddingBottom(Length(0, Fixed));
879}
880
881// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731.
882void RenderThemeChromiumMac::adjustButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
883{
884    // There are three appearance constants for buttons.
885    // (1) Push-button is the constant for the default Aqua system button.  Push buttons will not scale vertically and will not allow
886    // custom fonts or colors.  <input>s use this constant.  This button will allow custom colors and font weights/variants but won't
887    // scale vertically.
888    // (2) square-button is the constant for the square button.  This button will allow custom fonts and colors and will scale vertically.
889    // (3) Button is the constant that means "pick the best button as appropriate."  <button>s use this constant.  This button will
890    // also scale vertically and allow custom fonts and colors.  It will attempt to use Aqua if possible and will make this determination
891    // solely on the rectangle of the control.
892
893    // Determine our control size based off our font.
894    NSControlSize controlSize = controlSizeForFont(style);
895
896    if (style->appearance() == PushButtonPart) {
897        // Ditch the border.
898        style->resetBorder();
899
900        // Height is locked to auto.
901        style->setHeight(Length(Auto));
902
903        // White-space is locked to pre
904        style->setWhiteSpace(PRE);
905
906        // Set the button's vertical size.
907        setButtonSize(style);
908
909        // Add in the padding that we'd like to use.
910        setButtonPaddingFromControlSize(style, controlSize);
911
912        // 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
913        // a reasonable control size, but once that control size is determined, we throw that font away and use the appropriate
914        // system font for the control size instead.
915        setFontFromControlSize(selector, style, controlSize);
916    } else {
917        // Set a min-height so that we can't get smaller than the mini button.
918        style->setMinHeight(Length(static_cast<int>(15 * style->effectiveZoom()), Fixed));
919
920        // Reset the top and bottom borders.
921        style->resetBorderTop();
922        style->resetBorderBottom();
923    }
924
925    style->setBoxShadow(0);
926}
927
928// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731.
929const IntSize* RenderThemeChromiumMac::buttonSizes() const
930{
931    static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) };
932    return sizes;
933}
934
935// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731.
936const int* RenderThemeChromiumMac::buttonMargins() const
937{
938    static const int margins[3][4] =
939    {
940        { 4, 6, 7, 6 },
941        { 4, 5, 6, 5 },
942        { 0, 1, 1, 1 },
943    };
944    return margins[[button() controlSize]];
945}
946
947// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731.
948void RenderThemeChromiumMac::setButtonSize(RenderStyle* style) const
949{
950    // If the width and height are both specified, then we have nothing to do.
951    if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
952        return;
953
954    // Use the font size to determine the intrinsic width of the control.
955    setSizeFromFont(style, buttonSizes());
956}
957
958// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731.
959void RenderThemeChromiumMac::setButtonCellState(const RenderObject* o, const IntRect& r)
960{
961    NSButtonCell* button = this->button();
962
963    // Set the control size based off the rectangle we're painting into.
964    if (o->style()->appearance() == SquareButtonPart ||
965        r.height() > buttonSizes()[NSRegularControlSize].height() * o->style()->effectiveZoom()) {
966        // Use the square button
967        if ([button bezelStyle] != NSShadowlessSquareBezelStyle)
968            [button setBezelStyle:NSShadowlessSquareBezelStyle];
969    } else if ([button bezelStyle] != NSRoundedBezelStyle)
970        [button setBezelStyle:NSRoundedBezelStyle];
971
972    setControlSize(button, buttonSizes(), r.size(), o->style()->effectiveZoom());
973
974    NSWindow *window = [nil window];
975    BOOL isDefaultButton = (isDefault(o) && [window isKeyWindow]);
976    [button setKeyEquivalent:(isDefaultButton ? @"\r" : @"")];
977
978    // Update the various states we respond to.
979    updateCheckedState(button, o);
980    updateEnabledState(button, o);
981    updatePressedState(button, o);
982    updateFocusedState(button, o);
983}
984
985// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731.
986bool RenderThemeChromiumMac::paintButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
987{
988    NSButtonCell* button = this->button();
989    LocalCurrentGraphicsContext localContext(paintInfo.context);
990
991    // Determine the width and height needed for the control and prepare the cell for painting.
992    setButtonCellState(o, r);
993
994    paintInfo.context->save();
995
996    // We inflate the rect as needed to account for padding included in the cell to accommodate the button
997    // shadow.  We don't consider this part of the bounds of the control in WebKit.
998    float zoomLevel = o->style()->effectiveZoom();
999    IntSize size = buttonSizes()[[button controlSize]];
1000    size.setWidth(r.width());
1001    size.setHeight(size.height() * zoomLevel);
1002    IntRect inflatedRect = r;
1003    if ([button bezelStyle] == NSRoundedBezelStyle) {
1004        // Center the button within the available space.
1005        if (inflatedRect.height() > size.height()) {
1006            inflatedRect.setY(inflatedRect.y() + (inflatedRect.height() - size.height()) / 2);
1007            inflatedRect.setHeight(size.height());
1008        }
1009
1010        // Now inflate it to account for the shadow.
1011        inflatedRect = inflateRect(inflatedRect, size, buttonMargins(), zoomLevel);
1012
1013        if (zoomLevel != 1.0f) {
1014            inflatedRect.setWidth(inflatedRect.width() / zoomLevel);
1015            inflatedRect.setHeight(inflatedRect.height() / zoomLevel);
1016            paintInfo.context->translate(inflatedRect.x(), inflatedRect.y());
1017            paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel));
1018            paintInfo.context->translate(-inflatedRect.x(), -inflatedRect.y());
1019        }
1020    }
1021
1022    NSView *view = nil;
1023    NSWindow *window = [view window];
1024    NSButtonCell *previousDefaultButtonCell = [window defaultButtonCell];
1025
1026    if (isDefault(o) && [window isKeyWindow]) {
1027        [window setDefaultButtonCell:button];
1028        wkAdvanceDefaultButtonPulseAnimation(button);
1029    } else if ([previousDefaultButtonCell isEqual:button])
1030        [window setDefaultButtonCell:nil];
1031
1032    [button drawWithFrame:NSRect(IntRectToNSRect(inflatedRect)) inView:view];
1033    [button setControlView:nil];
1034
1035    if (![previousDefaultButtonCell isEqual:button])
1036        [window setDefaultButtonCell:previousDefaultButtonCell];
1037
1038    paintInfo.context->restore();
1039
1040    return false;
1041}
1042
1043bool RenderThemeChromiumMac::paintTextField(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
1044{
1045    LocalCurrentGraphicsContext localContext(paintInfo.context);
1046    wkDrawBezeledTextFieldCell(IntRectToNSRect(r), isEnabled(o) && !isReadOnlyControl(o));
1047    return false;
1048}
1049
1050void RenderThemeChromiumMac::adjustTextFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const
1051{
1052}
1053
1054bool RenderThemeChromiumMac::paintCapsLockIndicator(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
1055{
1056    if (paintInfo.context->paintingDisabled())
1057        return true;
1058
1059    LocalCurrentGraphicsContext localContext(paintInfo.context);
1060    wkDrawCapsLockIndicator(paintInfo.context->platformContext(), r);
1061
1062    return false;
1063}
1064
1065bool RenderThemeChromiumMac::paintTextArea(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
1066{
1067    LocalCurrentGraphicsContext localContext(paintInfo.context);
1068    wkDrawBezeledTextArea(IntRectToNSRect(r), isEnabled(o) && !isReadOnlyControl(o));
1069    return false;
1070}
1071
1072void RenderThemeChromiumMac::adjustTextAreaStyle(CSSStyleSelector*, RenderStyle*, Element*) const
1073{
1074}
1075
1076const int* RenderThemeChromiumMac::popupButtonMargins() const
1077{
1078    static const int margins[3][4] =
1079    {
1080        { 0, 3, 1, 3 },
1081        { 0, 3, 2, 3 },
1082        { 0, 1, 0, 1 }
1083    };
1084    return margins[[popupButton() controlSize]];
1085}
1086
1087const IntSize* RenderThemeChromiumMac::popupButtonSizes() const
1088{
1089    static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) };
1090    return sizes;
1091}
1092
1093const int* RenderThemeChromiumMac::popupButtonPadding(NSControlSize size) const
1094{
1095    static const int padding[3][4] =
1096    {
1097        { 2, 26, 3, 8 },
1098        { 2, 23, 3, 8 },
1099        { 2, 22, 3, 10 }
1100    };
1101    return padding[size];
1102}
1103
1104bool RenderThemeChromiumMac::paintMenuList(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
1105{
1106    LocalCurrentGraphicsContext localContext(paintInfo.context);
1107
1108    setPopupButtonCellState(o, r);
1109
1110    NSPopUpButtonCell* popupButton = this->popupButton();
1111
1112    float zoomLevel = o->style()->effectiveZoom();
1113    IntSize size = popupButtonSizes()[[popupButton controlSize]];
1114    size.setHeight(size.height() * zoomLevel);
1115    size.setWidth(r.width());
1116
1117    // Now inflate it to account for the shadow.
1118    IntRect inflatedRect = r;
1119    if (r.width() >= minimumMenuListSize(o->style()))
1120        inflatedRect = inflateRect(inflatedRect, size, popupButtonMargins(), zoomLevel);
1121
1122    paintInfo.context->save();
1123
1124#ifndef BUILDING_ON_TIGER
1125    // On Leopard, the cell will draw outside of the given rect, so we have to clip to the rect
1126    paintInfo.context->clip(inflatedRect);
1127#endif
1128
1129    if (zoomLevel != 1.0f) {
1130        inflatedRect.setWidth(inflatedRect.width() / zoomLevel);
1131        inflatedRect.setHeight(inflatedRect.height() / zoomLevel);
1132        paintInfo.context->translate(inflatedRect.x(), inflatedRect.y());
1133        paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel));
1134        paintInfo.context->translate(-inflatedRect.x(), -inflatedRect.y());
1135    }
1136
1137    [popupButton drawWithFrame:IntRectToNSRect(inflatedRect) inView:nil];
1138    [popupButton setControlView:nil];
1139
1140    paintInfo.context->restore();
1141
1142    return false;
1143}
1144
1145static const float baseFontSize = 11.0f;
1146static const float baseArrowHeight = 4.0f;
1147static const float baseArrowWidth = 5.0f;
1148static const float baseSpaceBetweenArrows = 2.0f;
1149static const int arrowPaddingLeft = 6;
1150static const int arrowPaddingRight = 6;
1151static const int paddingBeforeSeparator = 4;
1152static const int baseBorderRadius = 5;
1153static const int styledPopupPaddingLeft = 8;
1154static const int styledPopupPaddingTop = 1;
1155static const int styledPopupPaddingBottom = 2;
1156
1157static void TopGradientInterpolate(void* info, const CGFloat* inData, CGFloat* outData)
1158{
1159    static float dark[4] = { 1.0f, 1.0f, 1.0f, 0.4f };
1160    static float light[4] = { 1.0f, 1.0f, 1.0f, 0.15f };
1161    float a = inData[0];
1162    int i = 0;
1163    for (i = 0; i < 4; i++)
1164        outData[i] = (1.0f - a) * dark[i] + a * light[i];
1165}
1166
1167static void BottomGradientInterpolate(void* info, const CGFloat* inData, CGFloat* outData)
1168{
1169    static float dark[4] = { 1.0f, 1.0f, 1.0f, 0.0f };
1170    static float light[4] = { 1.0f, 1.0f, 1.0f, 0.3f };
1171    float a = inData[0];
1172    int i = 0;
1173    for (i = 0; i < 4; i++)
1174        outData[i] = (1.0f - a) * dark[i] + a * light[i];
1175}
1176
1177static void MainGradientInterpolate(void* info, const CGFloat* inData, CGFloat* outData)
1178{
1179    static float dark[4] = { 0.0f, 0.0f, 0.0f, 0.15f };
1180    static float light[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
1181    float a = inData[0];
1182    int i = 0;
1183    for (i = 0; i < 4; i++)
1184        outData[i] = (1.0f - a) * dark[i] + a * light[i];
1185}
1186
1187static void TrackGradientInterpolate(void* info, const CGFloat* inData, CGFloat* outData)
1188{
1189    static float dark[4] = { 0.0f, 0.0f, 0.0f, 0.678f };
1190    static float light[4] = { 0.0f, 0.0f, 0.0f, 0.13f };
1191    float a = inData[0];
1192    int i = 0;
1193    for (i = 0; i < 4; i++)
1194        outData[i] = (1.0f - a) * dark[i] + a * light[i];
1195}
1196
1197void RenderThemeChromiumMac::paintMenuListButtonGradients(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
1198{
1199    CGContextRef context = paintInfo.context->platformContext();
1200
1201    paintInfo.context->save();
1202
1203    int radius = o->style()->borderTopLeftRadius().width();
1204
1205    RetainPtr<CGColorSpaceRef> cspace(AdoptCF, CGColorSpaceCreateDeviceRGB());
1206
1207    FloatRect topGradient(r.x(), r.y(), r.width(), r.height() / 2.0f);
1208    struct CGFunctionCallbacks topCallbacks = { 0, TopGradientInterpolate, NULL };
1209    RetainPtr<CGFunctionRef> topFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &topCallbacks));
1210    RetainPtr<CGShadingRef> topShading(AdoptCF, CGShadingCreateAxial(cspace.get(), CGPointMake(topGradient.x(), topGradient.y()), CGPointMake(topGradient.x(), topGradient.bottom()), topFunction.get(), false, false));
1211
1212    FloatRect bottomGradient(r.x() + radius, r.y() + r.height() / 2.0f, r.width() - 2.0f * radius, r.height() / 2.0f);
1213    struct CGFunctionCallbacks bottomCallbacks = { 0, BottomGradientInterpolate, NULL };
1214    RetainPtr<CGFunctionRef> bottomFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &bottomCallbacks));
1215    RetainPtr<CGShadingRef> bottomShading(AdoptCF, CGShadingCreateAxial(cspace.get(), CGPointMake(bottomGradient.x(),  bottomGradient.y()), CGPointMake(bottomGradient.x(), bottomGradient.bottom()), bottomFunction.get(), false, false));
1216
1217    struct CGFunctionCallbacks mainCallbacks = { 0, MainGradientInterpolate, NULL };
1218    RetainPtr<CGFunctionRef> mainFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &mainCallbacks));
1219    RetainPtr<CGShadingRef> mainShading(AdoptCF, CGShadingCreateAxial(cspace.get(), CGPointMake(r.x(),  r.y()), CGPointMake(r.x(), r.bottom()), mainFunction.get(), false, false));
1220
1221    RetainPtr<CGShadingRef> leftShading(AdoptCF, CGShadingCreateAxial(cspace.get(), CGPointMake(r.x(),  r.y()), CGPointMake(r.x() + radius, r.y()), mainFunction.get(), false, false));
1222
1223    RetainPtr<CGShadingRef> rightShading(AdoptCF, CGShadingCreateAxial(cspace.get(), CGPointMake(r.right(),  r.y()), CGPointMake(r.right() - radius, r.y()), mainFunction.get(), false, false));
1224    paintInfo.context->save();
1225    CGContextClipToRect(context, r);
1226    paintInfo.context->addRoundedRectClip(r,
1227        o->style()->borderTopLeftRadius(), o->style()->borderTopRightRadius(),
1228        o->style()->borderBottomLeftRadius(), o->style()->borderBottomRightRadius());
1229    CGContextDrawShading(context, mainShading.get());
1230    paintInfo.context->restore();
1231
1232    paintInfo.context->save();
1233    CGContextClipToRect(context, topGradient);
1234    paintInfo.context->addRoundedRectClip(enclosingIntRect(topGradient),
1235        o->style()->borderTopLeftRadius(), o->style()->borderTopRightRadius(),
1236        IntSize(), IntSize());
1237    CGContextDrawShading(context, topShading.get());
1238    paintInfo.context->restore();
1239
1240    paintInfo.context->save();
1241    CGContextClipToRect(context, bottomGradient);
1242    paintInfo.context->addRoundedRectClip(enclosingIntRect(bottomGradient),
1243        IntSize(), IntSize(),
1244        o->style()->borderBottomLeftRadius(), o->style()->borderBottomRightRadius());
1245    CGContextDrawShading(context, bottomShading.get());
1246    paintInfo.context->restore();
1247
1248    paintInfo.context->save();
1249    CGContextClipToRect(context, r);
1250    paintInfo.context->addRoundedRectClip(r,
1251        o->style()->borderTopLeftRadius(), o->style()->borderTopRightRadius(),
1252        o->style()->borderBottomLeftRadius(), o->style()->borderBottomRightRadius());
1253    CGContextDrawShading(context, leftShading.get());
1254    CGContextDrawShading(context, rightShading.get());
1255    paintInfo.context->restore();
1256
1257    paintInfo.context->restore();
1258}
1259
1260bool RenderThemeChromiumMac::paintMenuListButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
1261{
1262    paintInfo.context->save();
1263
1264    IntRect bounds = IntRect(r.x() + o->style()->borderLeftWidth(),
1265                             r.y() + o->style()->borderTopWidth(),
1266                             r.width() - o->style()->borderLeftWidth() - o->style()->borderRightWidth(),
1267                             r.height() - o->style()->borderTopWidth() - o->style()->borderBottomWidth());
1268    // Draw the gradients to give the styled popup menu a button appearance
1269    paintMenuListButtonGradients(o, paintInfo, bounds);
1270
1271    // 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
1272    float fontScale = min(o->style()->fontSize() / baseFontSize, bounds.height() / (baseArrowHeight * 2 + baseSpaceBetweenArrows));
1273    float centerY = bounds.y() + bounds.height() / 2.0f;
1274    float arrowHeight = baseArrowHeight * fontScale;
1275    float arrowWidth = baseArrowWidth * fontScale;
1276    float leftEdge = bounds.right() - arrowPaddingRight * o->style()->effectiveZoom() - arrowWidth;
1277    float spaceBetweenArrows = baseSpaceBetweenArrows * fontScale;
1278
1279    if (bounds.width() < arrowWidth + arrowPaddingLeft * o->style()->effectiveZoom())
1280        return false;
1281
1282    paintInfo.context->setFillColor(o->style()->color());
1283    paintInfo.context->setStrokeStyle(NoStroke);
1284
1285    FloatPoint arrow1[3];
1286    arrow1[0] = FloatPoint(leftEdge, centerY - spaceBetweenArrows / 2.0f);
1287    arrow1[1] = FloatPoint(leftEdge + arrowWidth, centerY - spaceBetweenArrows / 2.0f);
1288    arrow1[2] = FloatPoint(leftEdge + arrowWidth / 2.0f, centerY - spaceBetweenArrows / 2.0f - arrowHeight);
1289
1290    // Draw the top arrow
1291    paintInfo.context->drawConvexPolygon(3, arrow1, true);
1292
1293    FloatPoint arrow2[3];
1294    arrow2[0] = FloatPoint(leftEdge, centerY + spaceBetweenArrows / 2.0f);
1295    arrow2[1] = FloatPoint(leftEdge + arrowWidth, centerY + spaceBetweenArrows / 2.0f);
1296    arrow2[2] = FloatPoint(leftEdge + arrowWidth / 2.0f, centerY + spaceBetweenArrows / 2.0f + arrowHeight);
1297
1298    // Draw the bottom arrow
1299    paintInfo.context->drawConvexPolygon(3, arrow2, true);
1300
1301    Color leftSeparatorColor(0, 0, 0, 40);
1302    Color rightSeparatorColor(255, 255, 255, 40);
1303
1304    // FIXME: Should the separator thickness and space be scaled up by fontScale?
1305    int separatorSpace = 2; // Deliberately ignores zoom since it looks nicer if it stays thin.
1306    int leftEdgeOfSeparator = static_cast<int>(leftEdge - arrowPaddingLeft * o->style()->effectiveZoom()); // FIXME: Round?
1307
1308    // Draw the separator to the left of the arrows
1309    paintInfo.context->setStrokeThickness(1.0f); // Deliberately ignores zoom since it looks nicer if it stays thin.
1310    paintInfo.context->setStrokeStyle(SolidStroke);
1311    paintInfo.context->setStrokeColor(leftSeparatorColor);
1312    paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator, bounds.y()),
1313                                IntPoint(leftEdgeOfSeparator, bounds.bottom()));
1314
1315    paintInfo.context->setStrokeColor(rightSeparatorColor);
1316    paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.y()),
1317                                IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.bottom()));
1318
1319    paintInfo.context->restore();
1320    return false;
1321}
1322
1323static const IntSize* menuListButtonSizes()
1324{
1325    static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) };
1326    return sizes;
1327}
1328
1329void RenderThemeChromiumMac::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
1330{
1331    NSControlSize controlSize = controlSizeForFont(style);
1332
1333    style->resetBorder();
1334    style->resetPadding();
1335
1336    // Height is locked to auto.
1337    style->setHeight(Length(Auto));
1338
1339    // White-space is locked to pre
1340    style->setWhiteSpace(PRE);
1341
1342    // Set the foreground color to black or gray when we have the aqua look.
1343    // Cast to RGB32 is to work around a compiler bug.
1344    style->setColor(e && e->isEnabledFormControl() ? static_cast<RGBA32>(Color::black) : Color::darkGray);
1345
1346    // Set the button's vertical size.
1347    setSizeFromFont(style, menuListButtonSizes());
1348
1349    // 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
1350    // a reasonable control size, but once that control size is determined, we throw that font away and use the appropriate
1351    // system font for the control size instead.
1352    setFontFromControlSize(selector, style, controlSize);
1353
1354    style->setBoxShadow(0);
1355}
1356
1357int RenderThemeChromiumMac::popupInternalPaddingLeft(RenderStyle* style) const
1358{
1359    if (style->appearance() == MenulistPart)
1360        return popupButtonPadding(controlSizeForFont(style))[LeftPadding] * style->effectiveZoom();
1361    if (style->appearance() == MenulistButtonPart)
1362        return styledPopupPaddingLeft * style->effectiveZoom();
1363    return 0;
1364}
1365
1366int RenderThemeChromiumMac::popupInternalPaddingRight(RenderStyle* style) const
1367{
1368    if (style->appearance() == MenulistPart)
1369        return popupButtonPadding(controlSizeForFont(style))[RightPadding] * style->effectiveZoom();
1370    if (style->appearance() == MenulistButtonPart) {
1371        float fontScale = style->fontSize() / baseFontSize;
1372        float arrowWidth = baseArrowWidth * fontScale;
1373        return static_cast<int>(ceilf(arrowWidth + (arrowPaddingLeft + arrowPaddingRight + paddingBeforeSeparator) * style->effectiveZoom()));
1374    }
1375    return 0;
1376}
1377
1378int RenderThemeChromiumMac::popupInternalPaddingTop(RenderStyle* style) const
1379{
1380    if (style->appearance() == MenulistPart)
1381        return popupButtonPadding(controlSizeForFont(style))[TopPadding] * style->effectiveZoom();
1382    if (style->appearance() == MenulistButtonPart)
1383        return styledPopupPaddingTop * style->effectiveZoom();
1384    return 0;
1385}
1386
1387int RenderThemeChromiumMac::popupInternalPaddingBottom(RenderStyle* style) const
1388{
1389    if (style->appearance() == MenulistPart)
1390        return popupButtonPadding(controlSizeForFont(style))[BottomPadding] * style->effectiveZoom();
1391    if (style->appearance() == MenulistButtonPart)
1392        return styledPopupPaddingBottom * style->effectiveZoom();
1393    return 0;
1394}
1395
1396void RenderThemeChromiumMac::adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
1397{
1398    float fontScale = style->fontSize() / baseFontSize;
1399
1400    style->resetPadding();
1401    style->setBorderRadius(IntSize(int(baseBorderRadius + fontScale - 1), int(baseBorderRadius + fontScale - 1))); // FIXME: Round up?
1402
1403    const int minHeight = 15;
1404    style->setMinHeight(Length(minHeight, Fixed));
1405
1406    style->setLineHeight(RenderStyle::initialLineHeight());
1407}
1408
1409void RenderThemeChromiumMac::setPopupButtonCellState(const RenderObject* o, const IntRect& r)
1410{
1411    NSPopUpButtonCell* popupButton = this->popupButton();
1412
1413    // Set the control size based off the rectangle we're painting into.
1414    setControlSize(popupButton, popupButtonSizes(), r.size(), o->style()->effectiveZoom());
1415
1416    // Update the various states we respond to.
1417    updateCheckedState(popupButton, o);
1418    updateEnabledState(popupButton, o);
1419    updatePressedState(popupButton, o);
1420    updateFocusedState(popupButton, o);
1421}
1422
1423const IntSize* RenderThemeChromiumMac::menuListSizes() const
1424{
1425    static const IntSize sizes[3] = { IntSize(9, 0), IntSize(5, 0), IntSize(0, 0) };
1426    return sizes;
1427}
1428
1429int RenderThemeChromiumMac::minimumMenuListSize(RenderStyle* style) const
1430{
1431    return sizeForSystemFont(style, menuListSizes()).width();
1432}
1433
1434static const int trackWidth = 5;
1435static const int trackRadius = 2;
1436
1437void RenderThemeChromiumMac::adjustSliderTrackStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
1438{
1439    style->setBoxShadow(0);
1440}
1441
1442bool RenderThemeChromiumMac::paintSliderTrack(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
1443{
1444    IntRect bounds = r;
1445    float zoomLevel = o->style()->effectiveZoom();
1446    float zoomedTrackWidth = trackWidth * zoomLevel;
1447
1448    if (o->style()->appearance() ==  SliderHorizontalPart || o->style()->appearance() ==  MediaSliderPart) {
1449        bounds.setHeight(zoomedTrackWidth);
1450        bounds.setY(r.y() + r.height() / 2 - zoomedTrackWidth / 2);
1451    } else if (o->style()->appearance() == SliderVerticalPart) {
1452        bounds.setWidth(zoomedTrackWidth);
1453        bounds.setX(r.x() + r.width() / 2 - zoomedTrackWidth / 2);
1454    }
1455
1456    LocalCurrentGraphicsContext localContext(paintInfo.context);
1457    CGContextRef context = paintInfo.context->platformContext();
1458    RetainPtr<CGColorSpaceRef> cspace(AdoptCF, CGColorSpaceCreateDeviceRGB());
1459
1460    paintInfo.context->save();
1461    CGContextClipToRect(context, bounds);
1462
1463    struct CGFunctionCallbacks mainCallbacks = { 0, TrackGradientInterpolate, NULL };
1464    RetainPtr<CGFunctionRef> mainFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &mainCallbacks));
1465    RetainPtr<CGShadingRef> mainShading;
1466    if (o->style()->appearance() == SliderVerticalPart)
1467        mainShading.adoptCF(CGShadingCreateAxial(cspace.get(), CGPointMake(bounds.x(),  bounds.bottom()), CGPointMake(bounds.right(), bounds.bottom()), mainFunction.get(), false, false));
1468    else
1469        mainShading.adoptCF(CGShadingCreateAxial(cspace.get(), CGPointMake(bounds.x(),  bounds.y()), CGPointMake(bounds.x(), bounds.bottom()), mainFunction.get(), false, false));
1470
1471    IntSize radius(trackRadius, trackRadius);
1472    paintInfo.context->addRoundedRectClip(bounds,
1473        radius, radius,
1474        radius, radius);
1475    CGContextDrawShading(context, mainShading.get());
1476    paintInfo.context->restore();
1477
1478    return false;
1479}
1480
1481void RenderThemeChromiumMac::adjustSliderThumbStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
1482{
1483    style->setBoxShadow(0);
1484}
1485
1486static const float verticalSliderHeightPadding = 0.1f;
1487
1488bool RenderThemeChromiumMac::paintSliderThumb(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
1489{
1490    ASSERT(o->parent()->isSlider());
1491
1492    NSSliderCell* sliderThumbCell = o->style()->appearance() == SliderThumbVerticalPart
1493        ? sliderThumbVertical()
1494        : sliderThumbHorizontal();
1495
1496    LocalCurrentGraphicsContext localContext(paintInfo.context);
1497
1498    // Update the various states we respond to.
1499    updateEnabledState(sliderThumbCell, o->parent());
1500    updateFocusedState(sliderThumbCell, o->parent());
1501
1502    // Update the pressed state using the NSCell tracking methods, since that's how NSSliderCell keeps track of it.
1503    bool oldPressed;
1504    if (o->style()->appearance() == SliderThumbVerticalPart)
1505        oldPressed = m_isSliderThumbVerticalPressed;
1506    else
1507        oldPressed = m_isSliderThumbHorizontalPressed;
1508
1509    bool pressed = toRenderSlider(o->parent())->inDragMode();
1510
1511    if (o->style()->appearance() == SliderThumbVerticalPart)
1512        m_isSliderThumbVerticalPressed = pressed;
1513    else
1514        m_isSliderThumbHorizontalPressed = pressed;
1515
1516    if (pressed != oldPressed) {
1517        if (pressed)
1518            [sliderThumbCell startTrackingAt:NSPoint() inView:nil];
1519        else
1520            [sliderThumbCell stopTracking:NSPoint() at:NSPoint() inView:nil mouseIsUp:YES];
1521    }
1522
1523    FloatRect bounds = r;
1524    // Make the height of the vertical slider slightly larger so NSSliderCell will draw a vertical slider.
1525    if (o->style()->appearance() == SliderThumbVerticalPart)
1526        bounds.setHeight(bounds.height() + verticalSliderHeightPadding * o->style()->effectiveZoom());
1527
1528    paintInfo.context->save();
1529    float zoomLevel = o->style()->effectiveZoom();
1530
1531    FloatRect unzoomedRect = bounds;
1532    if (zoomLevel != 1.0f) {
1533        unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel);
1534        unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel);
1535        paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y());
1536        paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel));
1537        paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y());
1538    }
1539
1540    [sliderThumbCell drawWithFrame:FloatRectToNSRect(unzoomedRect) inView:nil];
1541    [sliderThumbCell setControlView:nil];
1542
1543    paintInfo.context->restore();
1544
1545    return false;
1546}
1547
1548const int sliderThumbWidth = 15;
1549const int sliderThumbHeight = 15;
1550const int mediaSliderThumbWidth = 13;
1551const int mediaSliderThumbHeight = 14;
1552
1553void RenderThemeChromiumMac::adjustSliderThumbSize(RenderObject* o) const
1554{
1555    float zoomLevel = o->style()->effectiveZoom();
1556    if (o->style()->appearance() == SliderThumbHorizontalPart || o->style()->appearance() == SliderThumbVerticalPart) {
1557        o->style()->setWidth(Length(static_cast<int>(sliderThumbWidth * zoomLevel), Fixed));
1558        o->style()->setHeight(Length(static_cast<int>(sliderThumbHeight * zoomLevel), Fixed));
1559    } else if (o->style()->appearance() == MediaSliderThumbPart) {
1560        o->style()->setWidth(Length(mediaSliderThumbWidth, Fixed));
1561        o->style()->setHeight(Length(mediaSliderThumbHeight, Fixed));
1562    }
1563}
1564
1565bool RenderThemeChromiumMac::paintSearchField(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
1566{
1567    NSSearchFieldCell* search = this->search();
1568    LocalCurrentGraphicsContext localContext(paintInfo.context);
1569
1570    setSearchCellState(o, r);
1571
1572    paintInfo.context->save();
1573
1574    float zoomLevel = o->style()->effectiveZoom();
1575
1576    IntRect unzoomedRect = r;
1577
1578    if (zoomLevel != 1.0f) {
1579        unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel);
1580        unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel);
1581        paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y());
1582        paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel));
1583        paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y());
1584    }
1585
1586    // Set the search button to nil before drawing.  Then reset it so we can draw it later.
1587    [search setSearchButtonCell:nil];
1588
1589    [search drawWithFrame:NSRect(IntRectToNSRect(unzoomedRect)) inView:nil];
1590#ifdef BUILDING_ON_TIGER
1591    if ([search showsFirstResponder])
1592        wkDrawTextFieldCellFocusRing(search, NSRect(unzoomedRect));
1593#endif
1594
1595    [search setControlView:nil];
1596    [search resetSearchButtonCell];
1597
1598    paintInfo.context->restore();
1599
1600    return false;
1601}
1602
1603void RenderThemeChromiumMac::setSearchCellState(RenderObject* o, const IntRect& r)
1604{
1605    NSSearchFieldCell* search = this->search();
1606
1607    [search setControlSize:controlSizeForFont(o->style())];
1608
1609    // Update the various states we respond to.
1610    updateEnabledState(search, o);
1611    updateFocusedState(search, o);
1612}
1613
1614const IntSize* RenderThemeChromiumMac::searchFieldSizes() const
1615{
1616    static const IntSize sizes[3] = { IntSize(0, 22), IntSize(0, 19), IntSize(0, 17) };
1617    return sizes;
1618}
1619
1620void RenderThemeChromiumMac::setSearchFieldSize(RenderStyle* style) const
1621{
1622    // If the width and height are both specified, then we have nothing to do.
1623    if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto())
1624        return;
1625
1626    // Use the font size to determine the intrinsic width of the control.
1627    setSizeFromFont(style, searchFieldSizes());
1628}
1629
1630void RenderThemeChromiumMac::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
1631{
1632    // Override border.
1633    style->resetBorder();
1634    const short borderWidth = 2 * style->effectiveZoom();
1635    style->setBorderLeftWidth(borderWidth);
1636    style->setBorderLeftStyle(INSET);
1637    style->setBorderRightWidth(borderWidth);
1638    style->setBorderRightStyle(INSET);
1639    style->setBorderBottomWidth(borderWidth);
1640    style->setBorderBottomStyle(INSET);
1641    style->setBorderTopWidth(borderWidth);
1642    style->setBorderTopStyle(INSET);
1643
1644    // Override height.
1645    style->setHeight(Length(Auto));
1646    setSearchFieldSize(style);
1647
1648    // Override padding size to match AppKit text positioning.
1649    const int padding = 1 * style->effectiveZoom();
1650    style->setPaddingLeft(Length(padding, Fixed));
1651    style->setPaddingRight(Length(padding, Fixed));
1652    style->setPaddingTop(Length(padding, Fixed));
1653    style->setPaddingBottom(Length(padding, Fixed));
1654
1655    NSControlSize controlSize = controlSizeForFont(style);
1656    setFontFromControlSize(selector, style, controlSize);
1657
1658    style->setBoxShadow(0);
1659}
1660
1661bool RenderThemeChromiumMac::paintSearchFieldCancelButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
1662{
1663    LocalCurrentGraphicsContext localContext(paintInfo.context);
1664
1665    Node* input = o->node()->shadowAncestorNode();
1666    setSearchCellState(input->renderer(), r);
1667
1668    NSSearchFieldCell* search = this->search();
1669
1670    updatePressedState([search cancelButtonCell], o);
1671
1672    paintInfo.context->save();
1673
1674    float zoomLevel = o->style()->effectiveZoom();
1675
1676    NSRect bounds = [search cancelButtonRectForBounds:NSRect(IntRectToNSRect(input->renderer()->absoluteBoundingBoxRect()))];
1677
1678    IntRect unzoomedRect(NSRectToIntRect(bounds));
1679    if (zoomLevel != 1.0f) {
1680        unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel);
1681        unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel);
1682        paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y());
1683        paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel));
1684        paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y());
1685    }
1686
1687    [[search cancelButtonCell] drawWithFrame:IntRectToNSRect(unzoomedRect) inView:nil];
1688    [[search cancelButtonCell] setControlView:nil];
1689
1690    paintInfo.context->restore();
1691    return false;
1692}
1693
1694const IntSize* RenderThemeChromiumMac::cancelButtonSizes() const
1695{
1696    static const IntSize sizes[3] = { IntSize(16, 13), IntSize(13, 11), IntSize(13, 9) };
1697    return sizes;
1698}
1699
1700void RenderThemeChromiumMac::adjustSearchFieldCancelButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
1701{
1702    IntSize size = sizeForSystemFont(style, cancelButtonSizes());
1703    style->setWidth(Length(size.width(), Fixed));
1704    style->setHeight(Length(size.height(), Fixed));
1705    style->setBoxShadow(0);
1706}
1707
1708const IntSize* RenderThemeChromiumMac::resultsButtonSizes() const
1709{
1710    static const IntSize sizes[3] = { IntSize(19, 13), IntSize(17, 11), IntSize(17, 9) };
1711    return sizes;
1712}
1713
1714static const int emptyResultsOffset = 9;
1715void RenderThemeChromiumMac::adjustSearchFieldDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
1716{
1717    IntSize size = sizeForSystemFont(style, resultsButtonSizes());
1718    style->setWidth(Length(size.width() - emptyResultsOffset, Fixed));
1719    style->setHeight(Length(size.height(), Fixed));
1720    style->setBoxShadow(0);
1721}
1722
1723bool RenderThemeChromiumMac::paintSearchFieldDecoration(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
1724{
1725    return false;
1726}
1727
1728void RenderThemeChromiumMac::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
1729{
1730    IntSize size = sizeForSystemFont(style, resultsButtonSizes());
1731    style->setWidth(Length(size.width(), Fixed));
1732    style->setHeight(Length(size.height(), Fixed));
1733    style->setBoxShadow(0);
1734}
1735
1736bool RenderThemeChromiumMac::paintSearchFieldResultsDecoration(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
1737{
1738    LocalCurrentGraphicsContext localContext(paintInfo.context);
1739
1740    Node* input = o->node()->shadowAncestorNode();
1741    setSearchCellState(input->renderer(), r);
1742
1743    NSSearchFieldCell* search = this->search();
1744
1745    if ([search searchMenuTemplate] != nil)
1746        [search setSearchMenuTemplate:nil];
1747
1748    NSRect bounds = [search searchButtonRectForBounds:NSRect(IntRectToNSRect(input->renderer()->absoluteBoundingBoxRect()))];
1749    [[search searchButtonCell] drawWithFrame:bounds inView:nil];
1750    [[search searchButtonCell] setControlView:nil];
1751    return false;
1752}
1753
1754static const int resultsArrowWidth = 5;
1755void RenderThemeChromiumMac::adjustSearchFieldResultsButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const
1756{
1757    IntSize size = sizeForSystemFont(style, resultsButtonSizes());
1758    style->setWidth(Length(size.width() + resultsArrowWidth, Fixed));
1759    style->setHeight(Length(size.height(), Fixed));
1760    style->setBoxShadow(0);
1761}
1762
1763bool RenderThemeChromiumMac::paintSearchFieldResultsButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
1764{
1765    LocalCurrentGraphicsContext localContext(paintInfo.context);
1766
1767    Node* input = o->node()->shadowAncestorNode();
1768    setSearchCellState(input->renderer(), r);
1769
1770    NSSearchFieldCell* search = this->search();
1771
1772    if (![search searchMenuTemplate])
1773        [search setSearchMenuTemplate:searchMenuTemplate()];
1774
1775    paintInfo.context->save();
1776
1777    float zoomLevel = o->style()->effectiveZoom();
1778
1779    NSRect bounds = [search searchButtonRectForBounds:NSRect(IntRectToNSRect(input->renderer()->absoluteBoundingBoxRect()))];
1780
1781    IntRect unzoomedRect(NSRectToIntRect(bounds));
1782    if (zoomLevel != 1.0f) {
1783        unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel);
1784        unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel);
1785        paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y());
1786        paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel));
1787        paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y());
1788    }
1789
1790    [[search searchButtonCell] drawWithFrame:IntRectToNSRect(unzoomedRect) inView:nil];
1791    [[search searchButtonCell] setControlView:nil];
1792
1793    paintInfo.context->restore();
1794
1795    return false;
1796}
1797
1798#if ENABLE(VIDEO)
1799// FIXME: This enum is lifted from RenderThemeMac.mm  We need to decide which theme to use for the default controls, or decide to avoid wkDrawMediaUIPart and render our own.
1800typedef enum {
1801    MediaControllerThemeClassic   = 1,
1802    MediaControllerThemeQT        = 2
1803} MediaControllerThemeStyle;
1804
1805enum WKMediaControllerThemeState {
1806    MediaUIPartDisabledFlag = 1 << 0,
1807    MediaUIPartPressedFlag = 1 << 1,
1808    MediaUIPartDrawEndCapsFlag = 1 << 3,
1809};
1810#endif
1811
1812bool RenderThemeChromiumMac::paintMediaFullscreenButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
1813{
1814#if ENABLE(VIDEO)
1815    Node* node = o->node();
1816    if (!node)
1817        return false;
1818
1819    LocalCurrentGraphicsContext localContext(paintInfo.context);
1820    wkDrawMediaUIPart(MediaFullscreenButton, MediaControllerThemeClassic,  paintInfo.context->platformContext(), r,
1821        node->active() ? MediaUIPartPressedFlag : 0);
1822#endif
1823    return false;
1824}
1825
1826bool RenderThemeChromiumMac::paintMediaMuteButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
1827{
1828#if ENABLE(VIDEO)
1829    Node* node = o->node();
1830    Node* mediaNode = node ? node->shadowAncestorNode() : 0;
1831    if (!mediaNode || (!mediaNode->hasTagName(videoTag) && !mediaNode->hasTagName(audioTag)))
1832        return false;
1833
1834    HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(mediaNode);
1835    if (!mediaElement)
1836        return false;
1837
1838    LocalCurrentGraphicsContext localContext(paintInfo.context);
1839    wkDrawMediaUIPart(mediaElement->muted() ? MediaUnMuteButton : MediaMuteButton, MediaControllerThemeClassic, paintInfo.context->platformContext(), r,
1840        node->active() ? MediaUIPartPressedFlag : 0);
1841#endif
1842    return false;
1843}
1844
1845bool RenderThemeChromiumMac::paintMediaPlayButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
1846{
1847#if ENABLE(VIDEO)
1848    Node* node = o->node();
1849    Node* mediaNode = node ? node->shadowAncestorNode() : 0;
1850    if (!mediaNode || (!mediaNode->hasTagName(videoTag) && !mediaNode->hasTagName(audioTag)))
1851        return false;
1852
1853    HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(mediaNode);
1854    if (!mediaElement)
1855        return false;
1856
1857    LocalCurrentGraphicsContext localContext(paintInfo.context);
1858    wkDrawMediaUIPart(mediaElement->canPlay() ? MediaPlayButton : MediaPauseButton, MediaControllerThemeClassic, paintInfo.context->platformContext(), r,
1859        node->active() ? MediaUIPartPressedFlag : 0);
1860#endif
1861    return false;
1862}
1863
1864bool RenderThemeChromiumMac::paintMediaSeekBackButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
1865{
1866#if ENABLE(VIDEO)
1867    Node* node = o->node();
1868    if (!node)
1869        return false;
1870
1871    LocalCurrentGraphicsContext localContext(paintInfo.context);
1872    wkDrawMediaUIPart(MediaSeekBackButton, MediaControllerThemeClassic, paintInfo.context->platformContext(), r,
1873        node->active() ? MediaUIPartPressedFlag : 0);
1874#endif
1875    return false;
1876}
1877
1878bool RenderThemeChromiumMac::paintMediaSeekForwardButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
1879{
1880#if ENABLE(VIDEO)
1881    Node* node = o->node();
1882    if (!node)
1883        return false;
1884
1885    LocalCurrentGraphicsContext localContext(paintInfo.context);
1886    wkDrawMediaUIPart(MediaSeekForwardButton, MediaControllerThemeClassic, paintInfo.context->platformContext(), r,
1887        node->active() ? MediaUIPartPressedFlag : 0);
1888#endif
1889    return false;
1890}
1891
1892bool RenderThemeChromiumMac::paintMediaSliderTrack(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
1893{
1894#if ENABLE(VIDEO)
1895    Node* node = o->node();
1896    Node* mediaNode = node ? node->shadowAncestorNode() : 0;
1897    if (!mediaNode || (!mediaNode->hasTagName(videoTag) && !mediaNode->hasTagName(audioTag)))
1898        return false;
1899
1900    HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(mediaNode);
1901    if (!mediaElement)
1902        return false;
1903
1904    float timeLoaded = 0;
1905    float currentTime = 0;
1906    float duration = 0;
1907    if (MediaPlayer* player = mediaElement->player()) {
1908        duration = player->duration();
1909        timeLoaded = player->maxTimeBuffered();
1910        currentTime = player->currentTime();
1911    }
1912
1913    bool shouldDrawEndCaps = !toRenderMedia(mediaElement->renderer())->shouldShowTimeDisplayControls();
1914    wkDrawMediaSliderTrack(MediaControllerThemeClassic, paintInfo.context->platformContext(), r, timeLoaded, currentTime, duration, shouldDrawEndCaps ? MediaUIPartDrawEndCapsFlag : 0);
1915#endif
1916    return false;
1917}
1918
1919bool RenderThemeChromiumMac::paintMediaSliderThumb(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r)
1920{
1921#if ENABLE(VIDEO)
1922    Node* node = o->node();
1923    if (!node)
1924        return false;
1925
1926    LocalCurrentGraphicsContext localContext(paintInfo.context);
1927    wkDrawMediaUIPart(MediaSliderThumb, MediaControllerThemeClassic, paintInfo.context->platformContext(), r,
1928        node->active() ? MediaUIPartPressedFlag : 0);
1929#endif
1930    return false;
1931}
1932
1933// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731.
1934NSButtonCell* RenderThemeChromiumMac::checkbox() const
1935{
1936    if (!m_checkbox) {
1937        m_checkbox.adoptNS([[NSButtonCell alloc] init]);
1938        [m_checkbox.get() setButtonType:NSSwitchButton];
1939        [m_checkbox.get() setTitle:nil];
1940        [m_checkbox.get() setAllowsMixedState:YES];
1941        [m_checkbox.get() setFocusRingType:NSFocusRingTypeExterior];
1942    }
1943
1944    return m_checkbox.get();
1945}
1946
1947// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731.
1948NSButtonCell* RenderThemeChromiumMac::radio() const
1949{
1950    if (!m_radio) {
1951        m_radio.adoptNS([[NSButtonCell alloc] init]);
1952        [m_radio.get() setButtonType:NSRadioButton];
1953        [m_radio.get() setTitle:nil];
1954        [m_radio.get() setFocusRingType:NSFocusRingTypeExterior];
1955    }
1956
1957    return m_radio.get();
1958}
1959
1960// FIXME: This used to be in the upstream version until it was converted to the new theme API in r37731.
1961NSButtonCell* RenderThemeChromiumMac::button() const
1962{
1963    if (!m_button) {
1964        m_button.adoptNS([[NSButtonCell alloc] init]);
1965        [m_button.get() setTitle:nil];
1966        [m_button.get() setButtonType:NSMomentaryPushInButton];
1967    }
1968
1969    return m_button.get();
1970}
1971
1972NSPopUpButtonCell* RenderThemeChromiumMac::popupButton() const
1973{
1974    if (!m_popupButton) {
1975        m_popupButton.adoptNS([[NSPopUpButtonCell alloc] initTextCell:@"" pullsDown:NO]);
1976        [m_popupButton.get() setUsesItemFromMenu:NO];
1977        [m_popupButton.get() setFocusRingType:NSFocusRingTypeExterior];
1978    }
1979
1980    return m_popupButton.get();
1981}
1982
1983NSSearchFieldCell* RenderThemeChromiumMac::search() const
1984{
1985    if (!m_search) {
1986        m_search.adoptNS([[NSSearchFieldCell alloc] initTextCell:@""]);
1987        [m_search.get() setBezelStyle:NSTextFieldRoundedBezel];
1988        [m_search.get() setBezeled:YES];
1989        [m_search.get() setEditable:YES];
1990        [m_search.get() setFocusRingType:NSFocusRingTypeExterior];
1991    }
1992
1993    return m_search.get();
1994}
1995
1996NSMenu* RenderThemeChromiumMac::searchMenuTemplate() const
1997{
1998    if (!m_searchMenuTemplate)
1999        m_searchMenuTemplate.adoptNS([[NSMenu alloc] initWithTitle:@""]);
2000
2001    return m_searchMenuTemplate.get();
2002}
2003
2004NSSliderCell* RenderThemeChromiumMac::sliderThumbHorizontal() const
2005{
2006    if (!m_sliderThumbHorizontal) {
2007        m_sliderThumbHorizontal.adoptNS([[NSSliderCell alloc] init]);
2008        [m_sliderThumbHorizontal.get() setTitle:nil];
2009        [m_sliderThumbHorizontal.get() setSliderType:NSLinearSlider];
2010        [m_sliderThumbHorizontal.get() setControlSize:NSSmallControlSize];
2011        [m_sliderThumbHorizontal.get() setFocusRingType:NSFocusRingTypeExterior];
2012    }
2013
2014    return m_sliderThumbHorizontal.get();
2015}
2016
2017NSSliderCell* RenderThemeChromiumMac::sliderThumbVertical() const
2018{
2019    if (!m_sliderThumbVertical) {
2020        m_sliderThumbVertical.adoptNS([[NSSliderCell alloc] init]);
2021        [m_sliderThumbVertical.get() setTitle:nil];
2022        [m_sliderThumbVertical.get() setSliderType:NSLinearSlider];
2023        [m_sliderThumbVertical.get() setControlSize:NSSmallControlSize];
2024        [m_sliderThumbVertical.get() setFocusRingType:NSFocusRingTypeExterior];
2025    }
2026
2027    return m_sliderThumbVertical.get();
2028}
2029
2030} // namespace WebCore
2031