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