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