Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / platform / mac / ThemeMac.mm
1 /*
2  * Copyright (C) 2008, 2010, 2011, 2012 Apple Inc. All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #import "config.h"
27 #import "platform/mac/ThemeMac.h"
28
29 #import <Carbon/Carbon.h>
30 #import "platform/graphics/GraphicsContextStateSaver.h"
31 #import "platform/mac/BlockExceptions.h"
32 #import "platform/mac/LocalCurrentGraphicsContext.h"
33 #import "platform/mac/WebCoreNSCellExtras.h"
34 #import "platform/scroll/ScrollView.h"
35 #include "wtf/StdLibExtras.h"
36
37 NSRect focusRingClipRect;
38
39 // This is a view whose sole purpose is to tell AppKit that it's flipped.
40 @interface WebCoreFlippedView : NSControl
41 @end
42
43 @implementation WebCoreFlippedView
44
45 - (BOOL)isFlipped
46 {
47     return YES;
48 }
49
50 - (NSText *)currentEditor
51 {
52     return nil;
53 }
54
55 - (BOOL)_automaticFocusRingDisabled
56 {
57     return YES;
58 }
59
60 - (NSRect)_focusRingVisibleRect
61 {
62     if (NSIsEmptyRect(focusRingClipRect))
63         return [self visibleRect];
64
65     NSRect rect = focusRingClipRect;
66     rect.origin.y = [self bounds].size.height - NSMaxY(rect);
67
68     return rect;
69 }
70
71 - (NSView *)_focusRingClipAncestor
72 {
73     return self;
74 }
75
76 @end
77
78 @implementation NSFont (WebCoreTheme)
79
80 - (NSString*)webCoreFamilyName
81 {
82     if ([[self familyName] hasPrefix:@"."])
83         return [self fontName];
84
85     return [self familyName];
86 }
87
88 @end
89
90 namespace blink {
91
92 Theme* platformTheme()
93 {
94     DEFINE_STATIC_LOCAL(ThemeMac, themeMac, ());
95     return &themeMac;
96 }
97
98 // Helper functions used by a bunch of different control parts.
99
100 static NSControlSize controlSizeForFont(const FontDescription& fontDescription)
101 {
102     int fontSize = fontDescription.computedPixelSize();
103     if (fontSize >= 16)
104         return NSRegularControlSize;
105     if (fontSize >= 11)
106         return NSSmallControlSize;
107     return NSMiniControlSize;
108 }
109
110 static LengthSize sizeFromNSControlSize(NSControlSize nsControlSize, const LengthSize& zoomedSize, float zoomFactor, const IntSize* sizes)
111 {
112     IntSize controlSize = sizes[nsControlSize];
113     if (zoomFactor != 1.0f)
114         controlSize = IntSize(controlSize.width() * zoomFactor, controlSize.height() * zoomFactor);
115     LengthSize result = zoomedSize;
116     if (zoomedSize.width().isIntrinsicOrAuto() && controlSize.width() > 0)
117         result.setWidth(Length(controlSize.width(), Fixed));
118     if (zoomedSize.height().isIntrinsicOrAuto() && controlSize.height() > 0)
119         result.setHeight(Length(controlSize.height(), Fixed));
120     return result;
121 }
122
123 static LengthSize sizeFromFont(const FontDescription& fontDescription, const LengthSize& zoomedSize, float zoomFactor, const IntSize* sizes)
124 {
125     return sizeFromNSControlSize(controlSizeForFont(fontDescription), zoomedSize, zoomFactor, sizes);
126 }
127
128 static ControlSize controlSizeFromPixelSize(const IntSize* sizes, const IntSize& minZoomedSize, float zoomFactor)
129 {
130     if (minZoomedSize.width() >= static_cast<int>(sizes[NSRegularControlSize].width() * zoomFactor) &&
131         minZoomedSize.height() >= static_cast<int>(sizes[NSRegularControlSize].height() * zoomFactor))
132         return NSRegularControlSize;
133     if (minZoomedSize.width() >= static_cast<int>(sizes[NSSmallControlSize].width() * zoomFactor) &&
134         minZoomedSize.height() >= static_cast<int>(sizes[NSSmallControlSize].height() * zoomFactor))
135         return NSSmallControlSize;
136     return NSMiniControlSize;
137 }
138
139 static void setControlSize(NSCell* cell, const IntSize* sizes, const IntSize& minZoomedSize, float zoomFactor)
140 {
141     ControlSize size = controlSizeFromPixelSize(sizes, minZoomedSize, zoomFactor);
142     if (size != [cell controlSize]) // Only update if we have to, since AppKit does work even if the size is the same.
143         [cell setControlSize:(NSControlSize)size];
144 }
145
146 static void updateStates(NSCell* cell, ControlStates states)
147 {
148     // Hover state is not supported by Aqua.
149     
150     // Pressed state
151     bool oldPressed = [cell isHighlighted];
152     bool pressed = states & PressedControlState;
153     if (pressed != oldPressed)
154         [cell setHighlighted:pressed];
155     
156     // Enabled state
157     bool oldEnabled = [cell isEnabled];
158     bool enabled = states & EnabledControlState;
159     if (enabled != oldEnabled)
160         [cell setEnabled:enabled];
161     
162 #if BUTTON_CELL_DRAW_WITH_FRAME_DRAWS_FOCUS_RING
163     // Focused state
164     bool oldFocused = [cell showsFirstResponder];
165     bool focused = states & FocusControlState;
166     if (focused != oldFocused)
167         [cell setShowsFirstResponder:focused];
168 #endif
169
170     // Checked and Indeterminate
171     bool oldIndeterminate = [cell state] == NSMixedState;
172     bool indeterminate = (states & IndeterminateControlState);
173     bool checked = states & CheckedControlState;
174     bool oldChecked = [cell state] == NSOnState;
175     if (oldIndeterminate != indeterminate || checked != oldChecked)
176         [cell setState:indeterminate ? NSMixedState : (checked ? NSOnState : NSOffState)];
177         
178     // Window inactive state does not need to be checked explicitly, since we paint parented to 
179     // a view in a window whose key state can be detected.
180 }
181
182 static ThemeDrawState convertControlStatesToThemeDrawState(ThemeButtonKind kind, ControlStates states)
183 {
184     if (states & ReadOnlyControlState)
185         return kThemeStateUnavailableInactive;
186     if (!(states & EnabledControlState))
187         return kThemeStateUnavailableInactive;
188
189     // Do not process PressedState if !EnabledControlState or ReadOnlyControlState.
190     if (states & PressedControlState) {
191         if (kind == kThemeIncDecButton || kind == kThemeIncDecButtonSmall || kind == kThemeIncDecButtonMini)
192             return states & SpinUpControlState ? kThemeStatePressedUp : kThemeStatePressedDown;
193         return kThemeStatePressed;
194     }
195     return kThemeStateActive;
196 }
197
198 // static
199 IntRect ThemeMac::inflateRect(const IntRect& zoomedRect, const IntSize& zoomedSize, const int* margins, float zoomFactor)
200 {
201     // Only do the inflation if the available width/height are too small.  Otherwise try to
202     // fit the glow/check space into the available box's width/height.
203     int widthDelta = zoomedRect.width() - (zoomedSize.width() + margins[LeftMargin] * zoomFactor + margins[RightMargin] * zoomFactor);
204     int heightDelta = zoomedRect.height() - (zoomedSize.height() + margins[TopMargin] * zoomFactor + margins[BottomMargin] * zoomFactor);
205     IntRect result(zoomedRect);
206     if (widthDelta < 0) {
207         result.setX(result.x() - margins[LeftMargin] * zoomFactor);
208         result.setWidth(result.width() - widthDelta);
209     }
210     if (heightDelta < 0) {
211         result.setY(result.y() - margins[TopMargin] * zoomFactor);
212         result.setHeight(result.height() - heightDelta);
213     }
214     return result;
215 }
216
217 // static
218 IntRect ThemeMac::inflateRectForAA(const IntRect& rect) {
219   const int margin = 2;
220   return IntRect(rect.x() - margin, rect.y() - margin, rect.width() + 2 * margin, rect.height() + 2 * margin);
221 }
222
223 // static
224 IntRect ThemeMac::inflateRectForFocusRing(const IntRect& rect) {
225 #if BUTTON_CELL_DRAW_WITH_FRAME_DRAWS_FOCUS_RING
226     // Just put a margin of 16 units around the rect. The UI elements that use this don't appropriately
227     // scale their focus rings appropriately (e.g, paint pickers), or switch to non-native widgets when
228     // scaled (e.g, check boxes and radio buttons).
229     const int margin = 16;
230     IntRect result;
231     result.setX(rect.x() - margin);
232     result.setY(rect.y() - margin);
233     result.setWidth(rect.width() + 2 * margin);
234     result.setHeight(rect.height() + 2 * margin);
235     return result;
236 #else
237     return rect;
238 #endif
239 }
240
241 // Checkboxes
242
243 static const IntSize* checkboxSizes()
244 {
245     static const IntSize sizes[3] = { IntSize(14, 14), IntSize(12, 12), IntSize(10, 10) };
246     return sizes;
247 }
248
249 static const int* checkboxMargins(NSControlSize controlSize)
250 {
251     static const int margins[3][4] =
252     {
253         { 3, 4, 4, 2 },
254         { 4, 3, 3, 3 },
255         { 4, 3, 3, 3 },
256     };
257     return margins[controlSize];
258 }
259
260 static LengthSize checkboxSize(const FontDescription& fontDescription, const LengthSize& zoomedSize, float zoomFactor)
261 {
262     // If the width and height are both specified, then we have nothing to do.
263     if (!zoomedSize.width().isIntrinsicOrAuto() && !zoomedSize.height().isIntrinsicOrAuto())
264         return zoomedSize;
265
266     // Use the font size to determine the intrinsic width of the control.
267     return sizeFromFont(fontDescription, zoomedSize, zoomFactor, checkboxSizes());
268 }
269
270 static NSButtonCell *checkbox(ControlStates states, const IntRect& zoomedRect, float zoomFactor)
271 {
272     static NSButtonCell *checkboxCell;
273     if (!checkboxCell) {
274         checkboxCell = [[NSButtonCell alloc] init];
275         [checkboxCell setButtonType:NSSwitchButton];
276         [checkboxCell setTitle:nil];
277         [checkboxCell setAllowsMixedState:YES];
278         [checkboxCell setFocusRingType:NSFocusRingTypeExterior];
279     }
280     
281     // Set the control size based off the rectangle we're painting into.
282     setControlSize(checkboxCell, checkboxSizes(), zoomedRect.size(), zoomFactor);
283
284     // Update the various states we respond to.
285     updateStates(checkboxCell, states);
286     
287     return checkboxCell;
288 }
289
290 // FIXME: Share more code with radio buttons.
291 static void paintCheckbox(ControlStates states, GraphicsContext* context, const IntRect& zoomedRect, float zoomFactor, ScrollView* scrollView)
292 {
293     BEGIN_BLOCK_OBJC_EXCEPTIONS
294
295     // Determine the width and height needed for the control and prepare the cell for painting.
296     NSButtonCell *checkboxCell = checkbox(states, zoomedRect, zoomFactor);
297     GraphicsContextStateSaver stateSaver(*context);
298
299     NSControlSize controlSize = [checkboxCell controlSize];
300     IntSize zoomedSize = checkboxSizes()[controlSize];
301     zoomedSize.setWidth(zoomedSize.width() * zoomFactor);
302     zoomedSize.setHeight(zoomedSize.height() * zoomFactor);
303     IntRect inflatedRect = ThemeMac::inflateRect(zoomedRect, zoomedSize, checkboxMargins(controlSize), zoomFactor);
304     
305     if (zoomFactor != 1.0f) {
306         inflatedRect.setWidth(inflatedRect.width() / zoomFactor);
307         inflatedRect.setHeight(inflatedRect.height() / zoomFactor);
308         context->translate(inflatedRect.x(), inflatedRect.y());
309         context->scale(zoomFactor, zoomFactor);
310         context->translate(-inflatedRect.x(), -inflatedRect.y());
311     }
312
313     LocalCurrentGraphicsContext localContext(context, ThemeMac::inflateRectForFocusRing(inflatedRect));
314     NSView *view = ThemeMac::ensuredView(scrollView);
315     [checkboxCell drawWithFrame:NSRect(inflatedRect) inView:view];
316 #if !BUTTON_CELL_DRAW_WITH_FRAME_DRAWS_FOCUS_RING
317     if (states & FocusControlState)
318         [checkboxCell _web_drawFocusRingWithFrame:NSRect(inflatedRect) inView:view];
319 #endif
320     [checkboxCell setControlView:nil];
321     
322     END_BLOCK_OBJC_EXCEPTIONS
323 }
324
325 // Radio Buttons
326
327 static const IntSize* radioSizes()
328 {
329     static const IntSize sizes[3] = { IntSize(14, 15), IntSize(12, 13), IntSize(10, 10) };
330     return sizes;
331 }
332
333 static const int* radioMargins(NSControlSize controlSize)
334 {
335     static const int margins[3][4] =
336     {
337         { 2, 2, 4, 2 },
338         { 3, 2, 3, 2 },
339         { 1, 0, 2, 0 },
340     };
341     return margins[controlSize];
342 }
343
344 static LengthSize radioSize(const FontDescription& fontDescription, const LengthSize& zoomedSize, float zoomFactor)
345 {
346     // If the width and height are both specified, then we have nothing to do.
347     if (!zoomedSize.width().isIntrinsicOrAuto() && !zoomedSize.height().isIntrinsicOrAuto())
348         return zoomedSize;
349
350     // Use the font size to determine the intrinsic width of the control.
351     return sizeFromFont(fontDescription, zoomedSize, zoomFactor, radioSizes());
352 }
353
354 static NSButtonCell *radio(ControlStates states, const IntRect& zoomedRect, float zoomFactor)
355 {
356     static NSButtonCell *radioCell;
357     if (!radioCell) {
358         radioCell = [[NSButtonCell alloc] init];
359         [radioCell setButtonType:NSRadioButton];
360         [radioCell setTitle:nil];
361         [radioCell setFocusRingType:NSFocusRingTypeExterior];
362     }
363     
364     // Set the control size based off the rectangle we're painting into.
365     setControlSize(radioCell, radioSizes(), zoomedRect.size(), zoomFactor);
366
367     // Update the various states we respond to.
368     // Cocoa draws NSMixedState NSRadioButton as NSOnState so we don't want that.
369     states &= ~IndeterminateControlState;
370     updateStates(radioCell, states);
371     
372     return radioCell;
373 }
374
375 static void paintRadio(ControlStates states, GraphicsContext* context, const IntRect& zoomedRect, float zoomFactor, ScrollView* scrollView)
376 {
377     // Determine the width and height needed for the control and prepare the cell for painting.
378     NSButtonCell *radioCell = radio(states, zoomedRect, zoomFactor);
379     GraphicsContextStateSaver stateSaver(*context);
380
381     NSControlSize controlSize = [radioCell controlSize];
382     IntSize zoomedSize = radioSizes()[controlSize];
383     zoomedSize.setWidth(zoomedSize.width() * zoomFactor);
384     zoomedSize.setHeight(zoomedSize.height() * zoomFactor);
385     IntRect inflatedRect = ThemeMac::inflateRect(zoomedRect, zoomedSize, radioMargins(controlSize), zoomFactor);
386     
387     if (zoomFactor != 1.0f) {
388         inflatedRect.setWidth(inflatedRect.width() / zoomFactor);
389         inflatedRect.setHeight(inflatedRect.height() / zoomFactor);
390         context->translate(inflatedRect.x(), inflatedRect.y());
391         context->scale(zoomFactor, zoomFactor);
392         context->translate(-inflatedRect.x(), -inflatedRect.y());
393     }
394
395     LocalCurrentGraphicsContext localContext(context, ThemeMac::inflateRectForFocusRing(inflatedRect));
396     BEGIN_BLOCK_OBJC_EXCEPTIONS
397     NSView *view = ThemeMac::ensuredView(scrollView);
398     [radioCell drawWithFrame:NSRect(inflatedRect) inView:view];
399 #if !BUTTON_CELL_DRAW_WITH_FRAME_DRAWS_FOCUS_RING
400     if (states & FocusControlState)
401         [radioCell _web_drawFocusRingWithFrame:NSRect(inflatedRect) inView:view];
402 #endif
403     [radioCell setControlView:nil];
404     END_BLOCK_OBJC_EXCEPTIONS
405 }
406
407 // Buttons
408
409 // Buttons really only constrain height. They respect width.
410 static const IntSize* buttonSizes()
411 {
412     static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) };
413     return sizes;
414 }
415
416 static const int* buttonMargins(NSControlSize controlSize)
417 {
418     static const int margins[3][4] =
419     {
420         { 4, 6, 7, 6 },
421         { 4, 5, 6, 5 },
422         { 0, 1, 1, 1 },
423     };
424     return margins[controlSize];
425 }
426
427 static void setUpButtonCell(NSButtonCell *cell, ControlPart part, ControlStates states, const IntRect& zoomedRect, float zoomFactor)
428 {
429     // Set the control size based off the rectangle we're painting into.
430     const IntSize* sizes = buttonSizes();
431     if (part == SquareButtonPart || zoomedRect.height() > buttonSizes()[NSRegularControlSize].height() * zoomFactor) {
432         // Use the square button
433         if ([cell bezelStyle] != NSShadowlessSquareBezelStyle)
434             [cell setBezelStyle:NSShadowlessSquareBezelStyle];
435     } else if ([cell bezelStyle] != NSRoundedBezelStyle)
436         [cell setBezelStyle:NSRoundedBezelStyle];
437
438     setControlSize(cell, sizes, zoomedRect.size(), zoomFactor);
439
440     // Update the various states we respond to.
441     updateStates(cell, states);
442 }
443
444 static NSButtonCell *button(ControlPart part, ControlStates states, const IntRect& zoomedRect, float zoomFactor)
445 {
446     static NSButtonCell *cell = nil;
447     if (!cell) {
448         cell = [[NSButtonCell alloc] init];
449         [cell setTitle:nil];
450         [cell setButtonType:NSMomentaryPushInButton];
451     }
452     setUpButtonCell(cell, part, states, zoomedRect, zoomFactor);
453     return cell;
454 }
455
456 static void paintButton(ControlPart part, ControlStates states, GraphicsContext* context, const IntRect& zoomedRect, float zoomFactor, ScrollView* scrollView)
457 {
458     BEGIN_BLOCK_OBJC_EXCEPTIONS
459     
460     // Determine the width and height needed for the control and prepare the cell for painting.
461     NSButtonCell *buttonCell = button(part, states, zoomedRect, zoomFactor);
462     GraphicsContextStateSaver stateSaver(*context);
463
464     NSControlSize controlSize = [buttonCell controlSize];
465     IntSize zoomedSize = buttonSizes()[controlSize];
466     zoomedSize.setWidth(zoomedRect.width()); // Buttons don't ever constrain width, so the zoomed width can just be honored.
467     zoomedSize.setHeight(zoomedSize.height() * zoomFactor);
468     IntRect inflatedRect = zoomedRect;
469     if ([buttonCell bezelStyle] == NSRoundedBezelStyle) {
470         // Center the button within the available space.
471         if (inflatedRect.height() > zoomedSize.height()) {
472             inflatedRect.setY(inflatedRect.y() + (inflatedRect.height() - zoomedSize.height()) / 2);
473             inflatedRect.setHeight(zoomedSize.height());
474         }
475
476         // Now inflate it to account for the shadow.
477         inflatedRect = ThemeMac::inflateRect(inflatedRect, zoomedSize, buttonMargins(controlSize), zoomFactor);
478
479         if (zoomFactor != 1.0f) {
480             inflatedRect.setWidth(inflatedRect.width() / zoomFactor);
481             inflatedRect.setHeight(inflatedRect.height() / zoomFactor);
482             context->translate(inflatedRect.x(), inflatedRect.y());
483             context->scale(zoomFactor, zoomFactor);
484             context->translate(-inflatedRect.x(), -inflatedRect.y());
485         }
486     } 
487
488     LocalCurrentGraphicsContext localContext(context, ThemeMac::inflateRectForFocusRing(inflatedRect));
489     NSView *view = ThemeMac::ensuredView(scrollView);
490
491     [buttonCell drawWithFrame:NSRect(inflatedRect) inView:view];
492 #if !BUTTON_CELL_DRAW_WITH_FRAME_DRAWS_FOCUS_RING
493     if (states & FocusControlState)
494         [buttonCell _web_drawFocusRingWithFrame:NSRect(inflatedRect) inView:view];
495 #endif
496     [buttonCell setControlView:nil];
497
498     END_BLOCK_OBJC_EXCEPTIONS
499 }
500
501 // Stepper
502
503 static const IntSize* stepperSizes()
504 {
505     static const IntSize sizes[3] = { IntSize(19, 27), IntSize(15, 22), IntSize(13, 15) };
506     return sizes;
507 }
508
509 // We don't use controlSizeForFont() for steppers because the stepper height
510 // should be equal to or less than the corresponding text field height,
511 static NSControlSize stepperControlSizeForFont(const FontDescription& fontDescription)
512 {
513     int fontSize = fontDescription.computedPixelSize();
514     if (fontSize >= 27)
515         return NSRegularControlSize;
516     if (fontSize >= 22)
517         return NSSmallControlSize;
518     return NSMiniControlSize;
519 }
520
521 static void paintStepper(ControlStates states, GraphicsContext* context, const IntRect& zoomedRect, float zoomFactor, ScrollView*)
522 {
523     // We don't use NSStepperCell because there are no ways to draw an
524     // NSStepperCell with the up button highlighted.
525
526     HIThemeButtonDrawInfo drawInfo;
527     drawInfo.version = 0;
528     drawInfo.state = convertControlStatesToThemeDrawState(kThemeIncDecButton, states);
529     drawInfo.adornment = kThemeAdornmentDefault;
530     ControlSize controlSize = controlSizeFromPixelSize(stepperSizes(), zoomedRect.size(), zoomFactor);
531     if (controlSize == NSSmallControlSize)
532         drawInfo.kind = kThemeIncDecButtonSmall;
533     else if (controlSize == NSMiniControlSize)
534         drawInfo.kind = kThemeIncDecButtonMini;
535     else
536         drawInfo.kind = kThemeIncDecButton;
537
538     IntRect rect(zoomedRect);
539     GraphicsContextStateSaver stateSaver(*context);
540     if (zoomFactor != 1.0f) {
541         rect.setWidth(rect.width() / zoomFactor);
542         rect.setHeight(rect.height() / zoomFactor);
543         context->translate(rect.x(), rect.y());
544         context->scale(zoomFactor, zoomFactor);
545         context->translate(-rect.x(), -rect.y());
546     }
547     CGRect bounds(rect);
548     CGRect backgroundBounds;
549     HIThemeGetButtonBackgroundBounds(&bounds, &drawInfo, &backgroundBounds);
550     // Center the stepper rectangle in the specified area.
551     backgroundBounds.origin.x = bounds.origin.x + (bounds.size.width - backgroundBounds.size.width) / 2;
552     if (backgroundBounds.size.height < bounds.size.height) {
553         int heightDiff = clampToInteger(bounds.size.height - backgroundBounds.size.height);
554         backgroundBounds.origin.y = bounds.origin.y + (heightDiff / 2) + 1;
555     }
556
557     LocalCurrentGraphicsContext localContext(context, rect);
558     HIThemeDrawButton(&backgroundBounds, &drawInfo, localContext.cgContext(), kHIThemeOrientationNormal, 0);
559 }
560
561 // This will ensure that we always return a valid NSView, even if ScrollView doesn't have an associated document NSView.
562 // If the ScrollView doesn't have an NSView, we will return a fake NSView whose sole purpose is to tell AppKit that it's flipped.
563 NSView *ThemeMac::ensuredView(ScrollView* scrollView)
564 {
565
566     // Use a fake flipped view.
567     static NSView *flippedView = [[WebCoreFlippedView alloc] init];
568     [flippedView setFrameSize:NSSizeFromCGSize(scrollView->contentsSize())];
569
570     return flippedView;
571 }
572
573 void ThemeMac::setFocusRingClipRect(const FloatRect& rect)
574 {
575     focusRingClipRect = rect;
576 }
577
578 // Theme overrides
579
580 int ThemeMac::baselinePositionAdjustment(ControlPart part) const
581 {
582     if (part == CheckboxPart || part == RadioPart)
583         return -2;
584     return Theme::baselinePositionAdjustment(part);
585 }
586
587 FontDescription ThemeMac::controlFont(ControlPart part, const FontDescription& fontDescription, float zoomFactor) const
588 {
589     switch (part) {
590         case PushButtonPart: {
591             FontDescription result;
592             result.setIsAbsoluteSize(true);
593             result.setGenericFamily(FontDescription::SerifFamily);
594
595             NSFont* nsFont = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:controlSizeForFont(fontDescription)]];
596             result.firstFamily().setFamily([nsFont webCoreFamilyName]);
597             result.setComputedSize([nsFont pointSize] * zoomFactor);
598             result.setSpecifiedSize([nsFont pointSize] * zoomFactor);
599             return result;
600         }
601         default:
602             return Theme::controlFont(part, fontDescription, zoomFactor);
603     }
604 }
605
606 LengthSize ThemeMac::controlSize(ControlPart part, const FontDescription& fontDescription, const LengthSize& zoomedSize, float zoomFactor) const
607 {
608     switch (part) {
609         case CheckboxPart:
610             return checkboxSize(fontDescription, zoomedSize, zoomFactor);
611         case RadioPart:
612             return radioSize(fontDescription, zoomedSize, zoomFactor);
613         case PushButtonPart:
614             // Height is reset to auto so that specified heights can be ignored.
615             return sizeFromFont(fontDescription, LengthSize(zoomedSize.width(), Length()), zoomFactor, buttonSizes());
616         case InnerSpinButtonPart:
617             if (!zoomedSize.width().isIntrinsicOrAuto() && !zoomedSize.height().isIntrinsicOrAuto())
618                 return zoomedSize;
619             return sizeFromNSControlSize(stepperControlSizeForFont(fontDescription), zoomedSize, zoomFactor, stepperSizes());
620         default:
621             return zoomedSize;
622     }
623 }
624
625 LengthSize ThemeMac::minimumControlSize(ControlPart part, const FontDescription& fontDescription, float zoomFactor) const
626 {
627     switch (part) {
628         case SquareButtonPart:
629         case ButtonPart:
630             return LengthSize(Length(0, Fixed), Length(static_cast<int>(15 * zoomFactor), Fixed));
631         case InnerSpinButtonPart:{
632             IntSize base = stepperSizes()[NSMiniControlSize];
633             return LengthSize(Length(static_cast<int>(base.width() * zoomFactor), Fixed),
634                               Length(static_cast<int>(base.height() * zoomFactor), Fixed));
635         }
636         default:
637             return Theme::minimumControlSize(part, fontDescription, zoomFactor);
638     }
639 }
640
641 LengthBox ThemeMac::controlBorder(ControlPart part, const FontDescription& fontDescription, const LengthBox& zoomedBox, float zoomFactor) const
642 {
643     switch (part) {
644         case SquareButtonPart:
645         case ButtonPart:
646             return LengthBox(0, zoomedBox.right().value(), 0, zoomedBox.left().value());
647         default:
648             return Theme::controlBorder(part, fontDescription, zoomedBox, zoomFactor);
649     }
650 }
651
652 LengthBox ThemeMac::controlPadding(ControlPart part, const FontDescription& fontDescription, const LengthBox& zoomedBox, float zoomFactor) const
653 {
654     switch (part) {
655         case PushButtonPart: {
656             // Just use 8px.  AppKit wants to use 11px for mini buttons, but that padding is just too large
657             // for real-world Web sites (creating a huge necessary minimum width for buttons whose space is
658             // by definition constrained, since we select mini only for small cramped environments.
659             // This also guarantees the HTML <button> will match our rendering by default, since we're using a consistent
660             // padding.
661             const int padding = 8 * zoomFactor;
662             return LengthBox(0, padding, 0, padding);
663         }
664         default:
665             return Theme::controlPadding(part, fontDescription, zoomedBox, zoomFactor);
666     }
667 }
668
669 void ThemeMac::inflateControlPaintRect(ControlPart part, ControlStates states, IntRect& zoomedRect, float zoomFactor) const
670 {
671     BEGIN_BLOCK_OBJC_EXCEPTIONS
672     switch (part) {
673         case CheckboxPart: {
674             // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox
675             // shadow" and the check.  We don't consider this part of the bounds of the control in WebKit.
676             NSCell *cell = checkbox(states, zoomedRect, zoomFactor);
677             NSControlSize controlSize = [cell controlSize];
678             IntSize zoomedSize = checkboxSizes()[controlSize];
679             zoomedSize.setHeight(zoomedSize.height() * zoomFactor);
680             zoomedSize.setWidth(zoomedSize.width() * zoomFactor);
681             zoomedRect = inflateRect(zoomedRect, zoomedSize, checkboxMargins(controlSize), zoomFactor);
682             break;
683         }
684         case RadioPart: {
685             // We inflate the rect as needed to account for padding included in the cell to accommodate the radio button
686             // shadow".  We don't consider this part of the bounds of the control in WebKit.
687             NSCell *cell = radio(states, zoomedRect, zoomFactor);
688             NSControlSize controlSize = [cell controlSize];
689             IntSize zoomedSize = radioSizes()[controlSize];
690             zoomedSize.setHeight(zoomedSize.height() * zoomFactor);
691             zoomedSize.setWidth(zoomedSize.width() * zoomFactor);
692             zoomedRect = inflateRect(zoomedRect, zoomedSize, radioMargins(controlSize), zoomFactor);
693             break;
694         }
695         case PushButtonPart:
696         case ButtonPart: {
697             NSButtonCell *cell = button(part, states, zoomedRect, zoomFactor);
698             NSControlSize controlSize = [cell controlSize];
699
700             // We inflate the rect as needed to account for the Aqua button's shadow.
701             if ([cell bezelStyle] == NSRoundedBezelStyle) {
702                 IntSize zoomedSize = buttonSizes()[controlSize];
703                 zoomedSize.setHeight(zoomedSize.height() * zoomFactor);
704                 zoomedSize.setWidth(zoomedRect.width()); // Buttons don't ever constrain width, so the zoomed width can just be honored.
705                 zoomedRect = inflateRect(zoomedRect, zoomedSize, buttonMargins(controlSize), zoomFactor);
706             }
707             break;
708         }
709         case InnerSpinButtonPart: {
710             static const int stepperMargin[4] = { 0, 0, 0, 0 };
711             ControlSize controlSize = controlSizeFromPixelSize(stepperSizes(), zoomedRect.size(), zoomFactor);
712             IntSize zoomedSize = stepperSizes()[controlSize];
713             zoomedSize.setHeight(zoomedSize.height() * zoomFactor);
714             zoomedSize.setWidth(zoomedSize.width() * zoomFactor);
715             zoomedRect = inflateRect(zoomedRect, zoomedSize, stepperMargin, zoomFactor);
716             break;
717         }
718         default:
719             break;
720     }
721     END_BLOCK_OBJC_EXCEPTIONS
722 }
723
724 void ThemeMac::paint(ControlPart part, ControlStates states, GraphicsContext* context, const IntRect& zoomedRect, float zoomFactor, ScrollView* scrollView) const
725 {
726     switch (part) {
727         case CheckboxPart:
728             paintCheckbox(states, context, zoomedRect, zoomFactor, scrollView);
729             break;
730         case RadioPart:
731             paintRadio(states, context, zoomedRect, zoomFactor, scrollView);
732             break;
733         case PushButtonPart:
734         case ButtonPart:
735         case SquareButtonPart:
736             paintButton(part, states, context, zoomedRect, zoomFactor, scrollView);
737             break;
738         case InnerSpinButtonPart:
739             paintStepper(states, context, zoomedRect, zoomFactor, scrollView);
740             break;
741         default:
742             break;
743     }
744 }
745
746 }