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