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