OSDN Git Service

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