Home | History | Annotate | Download | only in chromium
      1 /*
      2  * Copyright (C) 2008 Apple Inc. All Rights Reserved.
      3  * Copyright (C) 2009 Google Inc. All Rights Reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions
      7  * are met:
      8  * 1. Redistributions of source code must retain the above copyright
      9  *    notice, this list of conditions and the following disclaimer.
     10  * 2. Redistributions in binary form must reproduce the above copyright
     11  *    notice, this list of conditions and the following disclaimer in the
     12  *    documentation and/or other materials provided with the distribution.
     13  *
     14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
     15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
     18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     25  */
     26 
     27 #import "config.h"
     28 #import "ThemeChromiumMac.h"
     29 
     30 #import "BlockExceptions.h"
     31 #import "GraphicsContext.h"
     32 #import "LocalCurrentGraphicsContext.h"
     33 #import "ScrollView.h"
     34 #import "WebCoreSystemInterface.h"
     35 #include <wtf/StdLibExtras.h>
     36 #import <objc/runtime.h>
     37 
     38 using namespace std;
     39 
     40 // This file (and its associated .h file) is a clone of ThemeMac.mm.
     41 // Because the original file is designed to run in-process inside a Cocoa view,
     42 // we must maintain a fork. Please maintain this file by performing parallel
     43 // changes to it.
     44 //
     45 // The only changes from ThemeMac should be:
     46 // - The classname change from ThemeMac to ThemeChromiumMac.
     47 // - The import of FlippedView() and its use as the parent view for cell
     48 //   rendering.
     49 // - In updateStates() the code to update the cells' inactive state.
     50 // - In paintButton() the code to save/restore the window's default button cell.
     51 // - The Snow Leopard focus ring bug fix and its use around every call to
     52 //   -[NSButtonCell drawWithFrame:inView:].
     53 //
     54 // For all other differences, if it was introduced in this file, then the
     55 // maintainer forgot to include it in the list; otherwise it is an update that
     56 // should have been applied to this file but was not.
     57 
     58 // FIXME: Default buttons really should be more like push buttons and not like buttons.
     59 
     60 // --- START fix for Snow Leopard focus ring bug ---
     61 
     62 // There is a bug in the Cocoa focus ring drawing code. The code calls +[NSView
     63 // focusView] (to get the currently focused view) and then calls an NSRect-
     64 // returning method on that view to obtain a clipping rect. However, if there is
     65 // no focused view (as there won't be if the destination is a context), the rect
     66 // returned from the method invocation on nil is garbage.
     67 //
     68 // The garbage fortunately does not clip the focus ring on Leopard, but
     69 // unfortunately does so on Snow Leopard. Therefore, if a runtime test shows
     70 // that focus ring drawing fails, we swizzle NSView to ensure it returns a valid
     71 // view with a valid clipping rectangle.
     72 //
     73 // FIXME: After the referenced bug is fixed on all supported platforms, remove
     74 // this code.
     75 //
     76 // References:
     77 //  <http://crbug.com/27493>
     78 //  <rdar://problem/7604051> (<http://openradar.appspot.com/7604051>)
     79 
     80 @interface TCMVisibleView : NSView
     81 
     82 @end
     83 
     84 @implementation TCMVisibleView
     85 
     86 - (struct CGRect)_focusRingVisibleRect
     87 {
     88     return CGRectZero;
     89 }
     90 
     91 - (id)_focusRingClipAncestor
     92 {
     93     return self;
     94 }
     95 
     96 @end
     97 
     98 @interface NSView (TCMInterposing)
     99 + (NSView *)TCMInterposing_focusView;
    100 @end
    101 
    102 namespace FocusIndicationFix {
    103 
    104 bool currentOSHasSetFocusRingStyleInBitmapBug()
    105 {
    106     UInt32 pixel = 0;
    107     UInt32* pixelPlane = &pixel;
    108     UInt32** pixelPlanes = &pixelPlane;
    109     NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:(UInt8**)pixelPlanes
    110                                                                        pixelsWide:1
    111                                                                        pixelsHigh:1
    112                                                                     bitsPerSample:8
    113                                                                   samplesPerPixel:4
    114                                                                          hasAlpha:YES
    115                                                                          isPlanar:NO
    116                                                                    colorSpaceName:NSCalibratedRGBColorSpace
    117                                                                      bitmapFormat:NSAlphaFirstBitmapFormat
    118                                                                       bytesPerRow:4
    119                                                                      bitsPerPixel:32];
    120     [NSGraphicsContext saveGraphicsState];
    121     [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
    122     NSSetFocusRingStyle(NSFocusRingOnly);
    123     NSRectFill(NSMakeRect(0, 0, 1, 1));
    124     [NSGraphicsContext restoreGraphicsState];
    125     [bitmap release];
    126 
    127     return !pixel;
    128 }
    129 
    130 bool swizzleFocusView()
    131 {
    132     if (!currentOSHasSetFocusRingStyleInBitmapBug())
    133         return false;
    134 
    135     Class nsview = [NSView class];
    136     Method m1 = class_getClassMethod(nsview, @selector(focusView));
    137     Method m2 = class_getClassMethod(nsview, @selector(TCMInterposing_focusView));
    138     if (m1 && m2) {
    139         method_exchangeImplementations(m1, m2);
    140         return true;
    141     }
    142 
    143     return false;
    144 }
    145 
    146 static bool interpose = false;
    147 
    148 // A class to restrict the amount of time spent messing with interposing. It
    149 // only stacks one-deep.
    150 class ScopedFixer {
    151 public:
    152     ScopedFixer()
    153     {
    154         static bool swizzled = swizzleFocusView();
    155         interpose = swizzled;
    156     }
    157 
    158     ~ScopedFixer()
    159     {
    160         interpose = false;
    161     }
    162 };
    163 
    164 }  // namespace FocusIndicationFix
    165 
    166 @implementation NSView (TCMInterposing)
    167 
    168 + (NSView *)TCMInterposing_focusView
    169 {
    170     NSView *view = [self TCMInterposing_focusView];  // call original (was swizzled)
    171     if (!view && FocusIndicationFix::interpose) {
    172         static TCMVisibleView* fixedView = [[TCMVisibleView alloc] init];
    173         view = fixedView;
    174     }
    175 
    176     return view;
    177 }
    178 
    179 @end
    180 
    181 // --- END fix for Snow Leopard focus ring bug ---
    182 
    183 namespace WebCore {
    184 
    185 // Pick up utility function from RenderThemeChromiumMac.
    186 extern NSView* FlippedView();
    187 
    188 enum {
    189     topMargin,
    190     rightMargin,
    191     bottomMargin,
    192     leftMargin
    193 };
    194 
    195 Theme* platformTheme()
    196 {
    197     DEFINE_STATIC_LOCAL(ThemeChromiumMac, themeMac, ());
    198     return &themeMac;
    199 }
    200 
    201 // Helper functions used by a bunch of different control parts.
    202 
    203 static NSControlSize controlSizeForFont(const Font& font)
    204 {
    205     int fontSize = font.pixelSize();
    206     if (fontSize >= 16)
    207         return NSRegularControlSize;
    208     if (fontSize >= 11)
    209         return NSSmallControlSize;
    210     return NSMiniControlSize;
    211 }
    212 
    213 static LengthSize sizeFromFont(const Font& font, const LengthSize& zoomedSize, float zoomFactor, const IntSize* sizes)
    214 {
    215     IntSize controlSize = sizes[controlSizeForFont(font)];
    216     if (zoomFactor != 1.0f)
    217         controlSize = IntSize(controlSize.width() * zoomFactor, controlSize.height() * zoomFactor);
    218     LengthSize result = zoomedSize;
    219     if (zoomedSize.width().isIntrinsicOrAuto() && controlSize.width() > 0)
    220         result.setWidth(Length(controlSize.width(), Fixed));
    221     if (zoomedSize.height().isIntrinsicOrAuto() && controlSize.height() > 0)
    222         result.setHeight(Length(controlSize.height(), Fixed));
    223     return result;
    224 }
    225 
    226 static void setControlSize(NSCell* cell, const IntSize* sizes, const IntSize& minZoomedSize, float zoomFactor)
    227 {
    228     NSControlSize size;
    229     if (minZoomedSize.width() >= static_cast<int>(sizes[NSRegularControlSize].width() * zoomFactor) &&
    230         minZoomedSize.height() >= static_cast<int>(sizes[NSRegularControlSize].height() * zoomFactor))
    231         size = NSRegularControlSize;
    232     else if (minZoomedSize.width() >= static_cast<int>(sizes[NSSmallControlSize].width() * zoomFactor) &&
    233              minZoomedSize.height() >= static_cast<int>(sizes[NSSmallControlSize].height() * zoomFactor))
    234         size = NSSmallControlSize;
    235     else
    236         size = NSMiniControlSize;
    237     if (size != [cell controlSize]) // Only update if we have to, since AppKit does work even if the size is the same.
    238         [cell setControlSize:size];
    239 }
    240 
    241 static void updateStates(NSCell* cell, ControlStates states)
    242 {
    243     // Hover state is not supported by Aqua.
    244 
    245     // Pressed state
    246     bool oldPressed = [cell isHighlighted];
    247     bool pressed = states & PressedState;
    248     if (pressed != oldPressed)
    249         [cell setHighlighted:pressed];
    250 
    251     // Enabled state
    252     bool oldEnabled = [cell isEnabled];
    253     bool enabled = states & EnabledState;
    254     if (enabled != oldEnabled)
    255         [cell setEnabled:enabled];
    256 
    257     // Focused state
    258     bool oldFocused = [cell showsFirstResponder];
    259     bool focused = states & FocusState;
    260     if (focused != oldFocused)
    261         [cell setShowsFirstResponder:focused];
    262 
    263     // Checked and Indeterminate
    264     bool oldIndeterminate = [cell state] == NSMixedState;
    265     bool indeterminate = (states & IndeterminateState);
    266     bool checked = states & CheckedState;
    267     bool oldChecked = [cell state] == NSOnState;
    268     if (oldIndeterminate != indeterminate || checked != oldChecked)
    269         [cell setState:indeterminate ? NSMixedState : (checked ? NSOnState : NSOffState)];
    270 
    271     // Window Inactive state
    272     NSControlTint oldTint = [cell controlTint];
    273     bool windowInactive = (states & WindowInactiveState);
    274     NSControlTint tint = windowInactive ? NSClearControlTint : [NSColor currentControlTint];
    275     if (tint != oldTint)
    276         [cell setControlTint:tint];
    277 }
    278 
    279 static IntRect inflateRect(const IntRect& zoomedRect, const IntSize& zoomedSize, const int* margins, float zoomFactor)
    280 {
    281     // Only do the inflation if the available width/height are too small.  Otherwise try to
    282     // fit the glow/check space into the available box's width/height.
    283     int widthDelta = zoomedRect.width() - (zoomedSize.width() + margins[leftMargin] * zoomFactor + margins[rightMargin] * zoomFactor);
    284     int heightDelta = zoomedRect.height() - (zoomedSize.height() + margins[topMargin] * zoomFactor + margins[bottomMargin] * zoomFactor);
    285     IntRect result(zoomedRect);
    286     if (widthDelta < 0) {
    287         result.setX(result.x() - margins[leftMargin] * zoomFactor);
    288         result.setWidth(result.width() - widthDelta);
    289     }
    290     if (heightDelta < 0) {
    291         result.setY(result.y() - margins[topMargin] * zoomFactor);
    292         result.setHeight(result.height() - heightDelta);
    293     }
    294     return result;
    295 }
    296 
    297 // Checkboxes
    298 
    299 static const IntSize* checkboxSizes()
    300 {
    301     static const IntSize sizes[3] = { IntSize(14, 14), IntSize(12, 12), IntSize(10, 10) };
    302     return sizes;
    303 }
    304 
    305 static const int* checkboxMargins(NSControlSize controlSize)
    306 {
    307     static const int margins[3][4] =
    308     {
    309         { 3, 4, 4, 2 },
    310         { 4, 3, 3, 3 },
    311         { 4, 3, 3, 3 },
    312     };
    313     return margins[controlSize];
    314 }
    315 
    316 static LengthSize checkboxSize(const Font& font, const LengthSize& zoomedSize, float zoomFactor)
    317 {
    318     // If the width and height are both specified, then we have nothing to do.
    319     if (!zoomedSize.width().isIntrinsicOrAuto() && !zoomedSize.height().isIntrinsicOrAuto())
    320         return zoomedSize;
    321 
    322     // Use the font size to determine the intrinsic width of the control.
    323     return sizeFromFont(font, zoomedSize, zoomFactor, checkboxSizes());
    324 }
    325 
    326 static NSButtonCell *checkbox(ControlStates states, const IntRect& zoomedRect, float zoomFactor)
    327 {
    328     static NSButtonCell *checkboxCell;
    329     if (!checkboxCell) {
    330         checkboxCell = [[NSButtonCell alloc] init];
    331         [checkboxCell setButtonType:NSSwitchButton];
    332         [checkboxCell setTitle:nil];
    333         [checkboxCell setAllowsMixedState:YES];
    334         [checkboxCell setFocusRingType:NSFocusRingTypeExterior];
    335     }
    336 
    337     // Set the control size based off the rectangle we're painting into.
    338     setControlSize(checkboxCell, checkboxSizes(), zoomedRect.size(), zoomFactor);
    339 
    340     // Update the various states we respond to.
    341     updateStates(checkboxCell, states);
    342 
    343     return checkboxCell;
    344 }
    345 
    346 // FIXME: Share more code with radio buttons.
    347 static void paintCheckbox(ControlStates states, GraphicsContext* context, const IntRect& zoomedRect, float zoomFactor, ScrollView* scrollView)
    348 {
    349     BEGIN_BLOCK_OBJC_EXCEPTIONS
    350 
    351     // Determine the width and height needed for the control and prepare the cell for painting.
    352     NSButtonCell *checkboxCell = checkbox(states, zoomedRect, zoomFactor);
    353 
    354     context->save();
    355 
    356     NSControlSize controlSize = [checkboxCell controlSize];
    357     IntSize zoomedSize = checkboxSizes()[controlSize];
    358     zoomedSize.setWidth(zoomedSize.width() * zoomFactor);
    359     zoomedSize.setHeight(zoomedSize.height() * zoomFactor);
    360     IntRect inflatedRect = inflateRect(zoomedRect, zoomedSize, checkboxMargins(controlSize), zoomFactor);
    361 
    362     if (zoomFactor != 1.0f) {
    363         inflatedRect.setWidth(inflatedRect.width() / zoomFactor);
    364         inflatedRect.setHeight(inflatedRect.height() / zoomFactor);
    365         context->translate(inflatedRect.x(), inflatedRect.y());
    366         context->scale(FloatSize(zoomFactor, zoomFactor));
    367         context->translate(-inflatedRect.x(), -inflatedRect.y());
    368     }
    369 
    370     {
    371         FocusIndicationFix::ScopedFixer fix;
    372         [checkboxCell drawWithFrame:NSRect(inflatedRect) inView:FlippedView()];
    373     }
    374     [checkboxCell setControlView:nil];
    375 
    376     context->restore();
    377 
    378     END_BLOCK_OBJC_EXCEPTIONS
    379 }
    380 
    381 // Radio Buttons
    382 
    383 static const IntSize* radioSizes()
    384 {
    385     static const IntSize sizes[3] = { IntSize(14, 15), IntSize(12, 13), IntSize(10, 10) };
    386     return sizes;
    387 }
    388 
    389 static const int* radioMargins(NSControlSize controlSize)
    390 {
    391     static const int margins[3][4] =
    392     {
    393         { 2, 2, 4, 2 },
    394         { 3, 2, 3, 2 },
    395         { 1, 0, 2, 0 },
    396     };
    397     return margins[controlSize];
    398 }
    399 
    400 static LengthSize radioSize(const Font& font, const LengthSize& zoomedSize, float zoomFactor)
    401 {
    402     // If the width and height are both specified, then we have nothing to do.
    403     if (!zoomedSize.width().isIntrinsicOrAuto() && !zoomedSize.height().isIntrinsicOrAuto())
    404         return zoomedSize;
    405 
    406     // Use the font size to determine the intrinsic width of the control.
    407     return sizeFromFont(font, zoomedSize, zoomFactor, radioSizes());
    408 }
    409 
    410 static NSButtonCell *radio(ControlStates states, const IntRect& zoomedRect, float zoomFactor)
    411 {
    412     static NSButtonCell *radioCell;
    413     if (!radioCell) {
    414         radioCell = [[NSButtonCell alloc] init];
    415         [radioCell setButtonType:NSRadioButton];
    416         [radioCell setTitle:nil];
    417         [radioCell setFocusRingType:NSFocusRingTypeExterior];
    418     }
    419 
    420     // Set the control size based off the rectangle we're painting into.
    421     setControlSize(radioCell, radioSizes(), zoomedRect.size(), zoomFactor);
    422 
    423     // Update the various states we respond to.
    424     updateStates(radioCell, states);
    425 
    426     return radioCell;
    427 }
    428 
    429 static void paintRadio(ControlStates states, GraphicsContext* context, const IntRect& zoomedRect, float zoomFactor, ScrollView* scrollView)
    430 {
    431     // Determine the width and height needed for the control and prepare the cell for painting.
    432     NSButtonCell *radioCell = radio(states, zoomedRect, zoomFactor);
    433 
    434     context->save();
    435 
    436     NSControlSize controlSize = [radioCell controlSize];
    437     IntSize zoomedSize = radioSizes()[controlSize];
    438     zoomedSize.setWidth(zoomedSize.width() * zoomFactor);
    439     zoomedSize.setHeight(zoomedSize.height() * zoomFactor);
    440     IntRect inflatedRect = inflateRect(zoomedRect, zoomedSize, radioMargins(controlSize), zoomFactor);
    441 
    442     if (zoomFactor != 1.0f) {
    443         inflatedRect.setWidth(inflatedRect.width() / zoomFactor);
    444         inflatedRect.setHeight(inflatedRect.height() / zoomFactor);
    445         context->translate(inflatedRect.x(), inflatedRect.y());
    446         context->scale(FloatSize(zoomFactor, zoomFactor));
    447         context->translate(-inflatedRect.x(), -inflatedRect.y());
    448     }
    449 
    450     BEGIN_BLOCK_OBJC_EXCEPTIONS
    451     {
    452         FocusIndicationFix::ScopedFixer fix;
    453         [radioCell drawWithFrame:NSRect(inflatedRect) inView:FlippedView()];
    454     }
    455     [radioCell setControlView:nil];
    456     END_BLOCK_OBJC_EXCEPTIONS
    457 
    458     context->restore();
    459 }
    460 
    461 // Buttons
    462 
    463 // Buttons really only constrain height. They respect width.
    464 static const IntSize* buttonSizes()
    465 {
    466     static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) };
    467     return sizes;
    468 }
    469 
    470 #if ENABLE(DATALIST)
    471 static const IntSize* listButtonSizes()
    472 {
    473     static const IntSize sizes[3] = { IntSize(21, 21), IntSize(19, 18), IntSize(17, 16) };
    474     return sizes;
    475 }
    476 #endif
    477 
    478 static const int* buttonMargins(NSControlSize controlSize)
    479 {
    480     static const int margins[3][4] =
    481     {
    482         { 4, 6, 7, 6 },
    483         { 4, 5, 6, 5 },
    484         { 0, 1, 1, 1 },
    485     };
    486     return margins[controlSize];
    487 }
    488 
    489 static void setupButtonCell(NSButtonCell *&buttonCell, ControlPart part, ControlStates states, const IntRect& zoomedRect, float zoomFactor)
    490 {
    491     if (!buttonCell) {
    492         buttonCell = [[NSButtonCell alloc] init];
    493         [buttonCell setTitle:nil];
    494         [buttonCell setButtonType:NSMomentaryPushInButton];
    495         if (states & DefaultState)
    496             [buttonCell setKeyEquivalent:@"\r"];
    497     }
    498 
    499     // Set the control size based off the rectangle we're painting into.
    500     const IntSize* sizes = buttonSizes();
    501 #if ENABLE(DATALIST)
    502     if (part == ListButtonPart) {
    503         [buttonCell setBezelStyle:NSRoundedDisclosureBezelStyle];
    504         sizes = listButtonSizes();
    505     } else
    506 #endif
    507     if (part == SquareButtonPart || zoomedRect.height() > buttonSizes()[NSRegularControlSize].height() * zoomFactor) {
    508         // Use the square button
    509         if ([buttonCell bezelStyle] != NSShadowlessSquareBezelStyle)
    510             [buttonCell setBezelStyle:NSShadowlessSquareBezelStyle];
    511     } else if ([buttonCell bezelStyle] != NSRoundedBezelStyle)
    512         [buttonCell setBezelStyle:NSRoundedBezelStyle];
    513 
    514     setControlSize(buttonCell, sizes, zoomedRect.size(), zoomFactor);
    515 
    516     // Update the various states we respond to.
    517     updateStates(buttonCell, states);
    518 }
    519 
    520 static NSButtonCell *button(ControlPart part, ControlStates states, const IntRect& zoomedRect, float zoomFactor)
    521 {
    522     bool isDefault = states & DefaultState;
    523     static NSButtonCell *cells[2];
    524     setupButtonCell(cells[isDefault], part, states, zoomedRect, zoomFactor);
    525     return cells[isDefault];
    526 }
    527 
    528 static void paintButton(ControlPart part, ControlStates states, GraphicsContext* context, const IntRect& zoomedRect, float zoomFactor, ScrollView* scrollView)
    529 {
    530     BEGIN_BLOCK_OBJC_EXCEPTIONS
    531 
    532     // Determine the width and height needed for the control and prepare the cell for painting.
    533     NSButtonCell *buttonCell = button(part, states, zoomedRect, zoomFactor);
    534     LocalCurrentGraphicsContext localContext(context);
    535 
    536     NSControlSize controlSize = [buttonCell controlSize];
    537 #if ENABLE(DATALIST)
    538     IntSize zoomedSize = (part == ListButtonPart ? listButtonSizes() : buttonSizes())[controlSize];
    539 #else
    540     IntSize zoomedSize = buttonSizes()[controlSize];
    541 #endif
    542     zoomedSize.setWidth(zoomedRect.width()); // Buttons don't ever constrain width, so the zoomed width can just be honored.
    543     zoomedSize.setHeight(zoomedSize.height() * zoomFactor);
    544     IntRect inflatedRect = zoomedRect;
    545     if ([buttonCell bezelStyle] == NSRoundedBezelStyle) {
    546         // Center the button within the available space.
    547         if (inflatedRect.height() > zoomedSize.height()) {
    548             inflatedRect.setY(inflatedRect.y() + (inflatedRect.height() - zoomedSize.height()) / 2);
    549             inflatedRect.setHeight(zoomedSize.height());
    550         }
    551 
    552         // Now inflate it to account for the shadow.
    553         inflatedRect = inflateRect(inflatedRect, zoomedSize, buttonMargins(controlSize), zoomFactor);
    554 
    555         if (zoomFactor != 1.0f) {
    556             inflatedRect.setWidth(inflatedRect.width() / zoomFactor);
    557             inflatedRect.setHeight(inflatedRect.height() / zoomFactor);
    558             context->translate(inflatedRect.x(), inflatedRect.y());
    559             context->scale(FloatSize(zoomFactor, zoomFactor));
    560             context->translate(-inflatedRect.x(), -inflatedRect.y());
    561         }
    562     }
    563 
    564     {
    565         FocusIndicationFix::ScopedFixer fix;
    566         [buttonCell drawWithFrame:NSRect(inflatedRect) inView:FlippedView()];
    567     }
    568     [buttonCell setControlView:nil];
    569 
    570     END_BLOCK_OBJC_EXCEPTIONS
    571 }
    572 
    573 // Theme overrides
    574 
    575 int ThemeChromiumMac::baselinePositionAdjustment(ControlPart part) const
    576 {
    577     if (part == CheckboxPart || part == RadioPart)
    578         return -2;
    579     return Theme::baselinePositionAdjustment(part);
    580 }
    581 
    582 FontDescription ThemeChromiumMac::controlFont(ControlPart part, const Font& font, float zoomFactor) const
    583 {
    584     switch (part) {
    585         case PushButtonPart: {
    586             FontDescription fontDescription;
    587             fontDescription.setIsAbsoluteSize(true);
    588             fontDescription.setGenericFamily(FontDescription::SerifFamily);
    589 
    590             NSFont* nsFont = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:controlSizeForFont(font)]];
    591             fontDescription.firstFamily().setFamily([nsFont familyName]);
    592             fontDescription.setComputedSize([nsFont pointSize] * zoomFactor);
    593             fontDescription.setSpecifiedSize([nsFont pointSize] * zoomFactor);
    594             return fontDescription;
    595         }
    596         default:
    597             return Theme::controlFont(part, font, zoomFactor);
    598     }
    599 }
    600 
    601 LengthSize ThemeChromiumMac::controlSize(ControlPart part, const Font& font, const LengthSize& zoomedSize, float zoomFactor) const
    602 {
    603     switch (part) {
    604         case CheckboxPart:
    605             return checkboxSize(font, zoomedSize, zoomFactor);
    606         case RadioPart:
    607             return radioSize(font, zoomedSize, zoomFactor);
    608         case PushButtonPart:
    609             // Height is reset to auto so that specified heights can be ignored.
    610             return sizeFromFont(font, LengthSize(zoomedSize.width(), Length()), zoomFactor, buttonSizes());
    611 #if ENABLE(DATALIST)
    612         case ListButtonPart:
    613             return sizeFromFont(font, LengthSize(zoomedSize.width(), Length()), zoomFactor, listButtonSizes());
    614 #endif
    615         default:
    616             return zoomedSize;
    617     }
    618 }
    619 
    620 LengthSize ThemeChromiumMac::minimumControlSize(ControlPart part, const Font& font, float zoomFactor) const
    621 {
    622     switch (part) {
    623         case SquareButtonPart:
    624         case DefaultButtonPart:
    625         case ButtonPart:
    626         case ListButtonPart:
    627             return LengthSize(Length(0, Fixed), Length(static_cast<int>(15 * zoomFactor), Fixed));
    628         default:
    629             return Theme::minimumControlSize(part, font, zoomFactor);
    630     }
    631 }
    632 
    633 LengthBox ThemeChromiumMac::controlBorder(ControlPart part, const Font& font, const LengthBox& zoomedBox, float zoomFactor) const
    634 {
    635     switch (part) {
    636         case SquareButtonPart:
    637         case DefaultButtonPart:
    638         case ButtonPart:
    639         case ListButtonPart:
    640             return LengthBox(0, zoomedBox.right().value(), 0, zoomedBox.left().value());
    641         default:
    642             return Theme::controlBorder(part, font, zoomedBox, zoomFactor);
    643     }
    644 }
    645 
    646 LengthBox ThemeChromiumMac::controlPadding(ControlPart part, const Font& font, const LengthBox& zoomedBox, float zoomFactor) const
    647 {
    648     switch (part) {
    649         case PushButtonPart: {
    650             // Just use 8px.  AppKit wants to use 11px for mini buttons, but that padding is just too large
    651             // for real-world Web sites (creating a huge necessary minimum width for buttons whose space is
    652             // by definition constrained, since we select mini only for small cramped environments.
    653             // This also guarantees the HTML <button> will match our rendering by default, since we're using a consistent
    654             // padding.
    655             const int padding = 8 * zoomFactor;
    656             return LengthBox(0, padding, 0, padding);
    657         }
    658         default:
    659             return Theme::controlPadding(part, font, zoomedBox, zoomFactor);
    660     }
    661 }
    662 
    663 void ThemeChromiumMac::inflateControlPaintRect(ControlPart part, ControlStates states, IntRect& zoomedRect, float zoomFactor) const
    664 {
    665     BEGIN_BLOCK_OBJC_EXCEPTIONS
    666     switch (part) {
    667         case CheckboxPart: {
    668             // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox
    669             // shadow" and the check.  We don't consider this part of the bounds of the control in WebKit.
    670             NSCell *cell = checkbox(states, zoomedRect, zoomFactor);
    671             NSControlSize controlSize = [cell controlSize];
    672             IntSize zoomedSize = checkboxSizes()[controlSize];
    673             zoomedSize.setHeight(zoomedSize.height() * zoomFactor);
    674             zoomedSize.setWidth(zoomedSize.width() * zoomFactor);
    675             zoomedRect = inflateRect(zoomedRect, zoomedSize, checkboxMargins(controlSize), zoomFactor);
    676             break;
    677         }
    678         case RadioPart: {
    679             // We inflate the rect as needed to account for padding included in the cell to accommodate the radio button
    680             // shadow".  We don't consider this part of the bounds of the control in WebKit.
    681             NSCell *cell = radio(states, zoomedRect, zoomFactor);
    682             NSControlSize controlSize = [cell controlSize];
    683             IntSize zoomedSize = radioSizes()[controlSize];
    684             zoomedSize.setHeight(zoomedSize.height() * zoomFactor);
    685             zoomedSize.setWidth(zoomedSize.width() * zoomFactor);
    686             zoomedRect = inflateRect(zoomedRect, zoomedSize, radioMargins(controlSize), zoomFactor);
    687             break;
    688         }
    689         case PushButtonPart:
    690         case DefaultButtonPart:
    691         case ButtonPart: {
    692             NSButtonCell *cell = button(part, states, zoomedRect, zoomFactor);
    693             NSControlSize controlSize = [cell controlSize];
    694 
    695             // We inflate the rect as needed to account for the Aqua button's shadow.
    696             if ([cell bezelStyle] == NSRoundedBezelStyle) {
    697                 IntSize zoomedSize = buttonSizes()[controlSize];
    698                 zoomedSize.setHeight(zoomedSize.height() * zoomFactor);
    699                 zoomedSize.setWidth(zoomedRect.width()); // Buttons don't ever constrain width, so the zoomed width can just be honored.
    700                 zoomedRect = inflateRect(zoomedRect, zoomedSize, buttonMargins(controlSize), zoomFactor);
    701             }
    702             break;
    703         }
    704         default:
    705             break;
    706     }
    707     END_BLOCK_OBJC_EXCEPTIONS
    708 }
    709 
    710 void ThemeChromiumMac::paint(ControlPart part, ControlStates states, GraphicsContext* context, const IntRect& zoomedRect, float zoomFactor, ScrollView* scrollView) const
    711 {
    712     switch (part) {
    713         case CheckboxPart:
    714             paintCheckbox(states, context, zoomedRect, zoomFactor, scrollView);
    715             break;
    716         case RadioPart:
    717             paintRadio(states, context, zoomedRect, zoomFactor, scrollView);
    718             break;
    719         case PushButtonPart:
    720         case DefaultButtonPart:
    721         case ButtonPart:
    722         case SquareButtonPart:
    723         case ListButtonPart:
    724             paintButton(part, states, context, zoomedRect, zoomFactor, scrollView);
    725             break;
    726         default:
    727             break;
    728     }
    729 }
    730 
    731 }
    732