Move parseFontFaceDescriptor to CSSPropertyParser.cpp
[chromium-blink-merge.git] / third_party / WebKit / Source / core / layout / LayoutThemeMac.mm
blobc5e44983674d5648d1803f7565e36b9839b398fc
1 /*
2  * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
3  * Copyright (C) 2008, 2009 Google, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public License
16  * along with this library; see the file COPYING.LIB.  If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
21 #import "config.h"
22 #import "core/layout/LayoutThemeMac.h"
24 #import "core/CSSValueKeywords.h"
25 #import "core/HTMLNames.h"
26 #import "core/fileapi/FileList.h"
27 #import "core/html/HTMLMeterElement.h"
28 #import "core/layout/LayoutMeter.h"
29 #import "core/layout/LayoutProgress.h"
30 #import "core/layout/LayoutView.h"
31 #import "core/paint/MediaControlsPainter.h"
32 #import "core/style/ShadowList.h"
33 #import "platform/LayoutTestSupport.h"
34 #import "platform/PlatformResourceLoader.h"
35 #import "platform/graphics/BitmapImage.h"
36 #import "platform/mac/ColorMac.h"
37 #import "platform/mac/LocalCurrentGraphicsContext.h"
38 #import "platform/mac/ThemeMac.h"
39 #import "platform/mac/VersionUtilMac.h"
40 #import "platform/mac/WebCoreNSCellExtras.h"
41 #import "platform/text/PlatformLocale.h"
42 #import "platform/text/StringTruncator.h"
43 #import <AvailabilityMacros.h>
44 #import <Carbon/Carbon.h>
45 #import <Cocoa/Cocoa.h>
46 #import <math.h>
48 // The methods in this file are specific to the Mac OS X platform.
50 @interface LayoutThemeNotificationObserver : NSObject
52     blink::LayoutTheme *_theme;
55 - (id)initWithTheme:(blink::LayoutTheme *)theme;
56 - (void)systemColorsDidChange:(NSNotification *)notification;
58 @end
60 @implementation LayoutThemeNotificationObserver
62 - (id)initWithTheme:(blink::LayoutTheme *)theme
64     if (!(self = [super init]))
65         return nil;
67     _theme = theme;
68     return self;
71 - (void)systemColorsDidChange:(NSNotification *)unusedNotification
73     ASSERT_UNUSED(unusedNotification, [[unusedNotification name] isEqualToString:NSSystemColorsDidChangeNotification]);
74     _theme->platformColorsDidChange();
77 @end
79 @interface NSTextFieldCell (WKDetails)
80 - (CFDictionaryRef)_coreUIDrawOptionsWithFrame:(NSRect)cellFrame inView:(NSView *)controlView includeFocus:(BOOL)includeFocus;
81 @end
84 @interface WebCoreTextFieldCell : NSTextFieldCell
85 - (CFDictionaryRef)_coreUIDrawOptionsWithFrame:(NSRect)cellFrame inView:(NSView *)controlView includeFocus:(BOOL)includeFocus;
86 @end
88 @implementation WebCoreTextFieldCell
89 - (CFDictionaryRef)_coreUIDrawOptionsWithFrame:(NSRect)cellFrame inView:(NSView *)controlView includeFocus:(BOOL)includeFocus
91     // FIXME: This is a post-Lion-only workaround for <rdar://problem/11385461>. When that bug is resolved, we should remove this code.
92     CFMutableDictionaryRef coreUIDrawOptions = CFDictionaryCreateMutableCopy(NULL, 0, [super _coreUIDrawOptionsWithFrame:cellFrame inView:controlView includeFocus:includeFocus]);
93     CFDictionarySetValue(coreUIDrawOptions, @"borders only", kCFBooleanTrue);
94     return (CFDictionaryRef)[NSMakeCollectable(coreUIDrawOptions) autorelease];
96 @end
98 @interface RTCMFlippedView : NSView
101 - (BOOL)isFlipped;
102 - (NSText *)currentEditor;
104 @end
106 @implementation RTCMFlippedView
108 - (BOOL)isFlipped {
109     return [[NSGraphicsContext currentContext] isFlipped];
112 - (NSText *)currentEditor {
113     return nil;
116 @end
118 namespace blink {
120 using namespace HTMLNames;
122 LayoutThemeMac::LayoutThemeMac()
123     : m_notificationObserver(AdoptNS, [[LayoutThemeNotificationObserver alloc] initWithTheme:this])
124     , m_painter(*this)
126     [[NSNotificationCenter defaultCenter] addObserver:m_notificationObserver.get()
127                                              selector:@selector(systemColorsDidChange:)
128                                                  name:NSSystemColorsDidChangeNotification
129                                                object:nil];
132 LayoutThemeMac::~LayoutThemeMac()
134     [[NSNotificationCenter defaultCenter] removeObserver:m_notificationObserver.get()];
137 Color LayoutThemeMac::platformActiveSelectionBackgroundColor() const
139     NSColor* color = [[NSColor selectedTextBackgroundColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace];
140     return Color(static_cast<int>(255.0 * [color redComponent]), static_cast<int>(255.0 * [color greenComponent]), static_cast<int>(255.0 * [color blueComponent]));
143 Color LayoutThemeMac::platformInactiveSelectionBackgroundColor() const
145     NSColor* color = [[NSColor secondarySelectedControlColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace];
146     return Color(static_cast<int>(255.0 * [color redComponent]), static_cast<int>(255.0 * [color greenComponent]), static_cast<int>(255.0 * [color blueComponent]));
149 Color LayoutThemeMac::platformActiveSelectionForegroundColor() const
151     return Color::black;
154 Color LayoutThemeMac::platformActiveListBoxSelectionBackgroundColor() const
156     NSColor* color = [[NSColor alternateSelectedControlColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace];
157     return Color(static_cast<int>(255.0 * [color redComponent]), static_cast<int>(255.0 * [color greenComponent]), static_cast<int>(255.0 * [color blueComponent]));
160 Color LayoutThemeMac::platformActiveListBoxSelectionForegroundColor() const
162     return Color::white;
165 Color LayoutThemeMac::platformInactiveListBoxSelectionForegroundColor() const
167     return Color::black;
170 Color LayoutThemeMac::platformFocusRingColor() const
172     static const RGBA32 oldAquaFocusRingColor = 0xFF7DADD9;
173     if (usesTestModeFocusRingColor())
174         return oldAquaFocusRingColor;
176     return systemColor(CSSValueWebkitFocusRingColor);
179 Color LayoutThemeMac::platformInactiveListBoxSelectionBackgroundColor() const
181     return platformInactiveSelectionBackgroundColor();
184 static FontWeight toFontWeight(NSInteger appKitFontWeight)
186     ASSERT(appKitFontWeight > 0 && appKitFontWeight < 15);
187     if (appKitFontWeight > 14)
188         appKitFontWeight = 14;
189     else if (appKitFontWeight < 1)
190         appKitFontWeight = 1;
192     static FontWeight fontWeights[] = {
193         FontWeight100,
194         FontWeight100,
195         FontWeight200,
196         FontWeight300,
197         FontWeight400,
198         FontWeight500,
199         FontWeight600,
200         FontWeight600,
201         FontWeight700,
202         FontWeight800,
203         FontWeight800,
204         FontWeight900,
205         FontWeight900,
206         FontWeight900
207     };
208     return fontWeights[appKitFontWeight - 1];
211 static inline NSFont* systemNSFont(CSSValueID systemFontID)
213     switch (systemFontID) {
214     case CSSValueSmallCaption:
215         return [NSFont systemFontOfSize:[NSFont smallSystemFontSize]];
216     case CSSValueMenu:
217         return [NSFont menuFontOfSize:[NSFont systemFontSize]];
218     case CSSValueStatusBar:
219         return [NSFont labelFontOfSize:[NSFont labelFontSize]];
220     case CSSValueWebkitMiniControl:
221         return [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSMiniControlSize]];
222     case CSSValueWebkitSmallControl:
223         return [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]];
224     case CSSValueWebkitControl:
225         return [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]];
226     default:
227         return [NSFont systemFontOfSize:[NSFont systemFontSize]];
228     }
231 void LayoutThemeMac::systemFont(CSSValueID systemFontID, FontStyle& fontStyle, FontWeight& fontWeight, float& fontSize, AtomicString& fontFamily) const
233     NSFont* font = systemNSFont(systemFontID);
234     if (!font)
235         return;
237     NSFontManager *fontManager = [NSFontManager sharedFontManager];
238     fontStyle = ([fontManager traitsOfFont:font] & NSItalicFontMask) ? FontStyleItalic : FontStyleNormal;
239     fontWeight = toFontWeight([fontManager weightOfFont:font]);
240     fontSize = [font pointSize];
241     fontFamily = @"BlinkMacSystemFont";
244 bool LayoutThemeMac::needsHackForTextControlWithFontFamily(const AtomicString& family) const
246     // This hack is only applied on OSX 10.9 and earlier.
247     // https://code.google.com/p/chromium/issues/detail?id=515989#c8
248     return IsOSMavericksOrEarlier() && family == "BlinkMacSystemFont";
251 static RGBA32 convertNSColorToColor(NSColor *color)
253     NSColor *colorInColorSpace = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace];
254     if (colorInColorSpace) {
255         static const double scaleFactor = nextafter(256.0, 0.0);
256         return makeRGB(static_cast<int>(scaleFactor * [colorInColorSpace redComponent]),
257             static_cast<int>(scaleFactor * [colorInColorSpace greenComponent]),
258             static_cast<int>(scaleFactor * [colorInColorSpace blueComponent]));
259     }
261     // This conversion above can fail if the NSColor in question is an NSPatternColor
262     // (as many system colors are). These colors are actually a repeating pattern
263     // not just a solid color. To work around this we simply draw a 1x1 image of
264     // the color and use that pixel's color. It might be better to use an average of
265     // the colors in the pattern instead.
266     NSBitmapImageRep *offscreenRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil
267                                                                              pixelsWide:1
268                                                                              pixelsHigh:1
269                                                                           bitsPerSample:8
270                                                                         samplesPerPixel:4
271                                                                                hasAlpha:YES
272                                                                                isPlanar:NO
273                                                                          colorSpaceName:NSDeviceRGBColorSpace
274                                                                             bytesPerRow:4
275                                                                            bitsPerPixel:32];
277     [NSGraphicsContext saveGraphicsState];
278     [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:offscreenRep]];
279     NSEraseRect(NSMakeRect(0, 0, 1, 1));
280     [color drawSwatchInRect:NSMakeRect(0, 0, 1, 1)];
281     [NSGraphicsContext restoreGraphicsState];
283     NSUInteger pixel[4];
284     [offscreenRep getPixel:pixel atX:0 y:0];
286     [offscreenRep release];
288     return makeRGB(pixel[0], pixel[1], pixel[2]);
291 static RGBA32 menuBackgroundColor()
293     NSBitmapImageRep *offscreenRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil
294                                                                              pixelsWide:1
295                                                                              pixelsHigh:1
296                                                                           bitsPerSample:8
297                                                                         samplesPerPixel:4
298                                                                                hasAlpha:YES
299                                                                                isPlanar:NO
300                                                                          colorSpaceName:NSDeviceRGBColorSpace
301                                                                             bytesPerRow:4
302                                                                            bitsPerPixel:32];
304     CGContextRef context = static_cast<CGContextRef>([[NSGraphicsContext graphicsContextWithBitmapImageRep:offscreenRep] graphicsPort]);
305     CGRect rect = CGRectMake(0, 0, 1, 1);
306     HIThemeMenuDrawInfo drawInfo;
307     drawInfo.version =  0;
308     drawInfo.menuType = kThemeMenuTypePopUp;
309     HIThemeDrawMenuBackground(&rect, &drawInfo, context, kHIThemeOrientationInverted);
311     NSUInteger pixel[4];
312     [offscreenRep getPixel:pixel atX:0 y:0];
314     [offscreenRep release];
316     return makeRGB(pixel[0], pixel[1], pixel[2]);
319 void LayoutThemeMac::platformColorsDidChange()
321     m_systemColorCache.clear();
322     LayoutTheme::platformColorsDidChange();
325 Color LayoutThemeMac::systemColor(CSSValueID cssValueId) const
327     {
328         HashMap<int, RGBA32>::iterator it = m_systemColorCache.find(cssValueId);
329         if (it != m_systemColorCache.end())
330             return it->value;
331     }
333     Color color;
334     bool needsFallback = false;
335     switch (cssValueId) {
336     case CSSValueActiveborder:
337         color = convertNSColorToColor([NSColor keyboardFocusIndicatorColor]);
338         break;
339     case CSSValueActivecaption:
340         color = convertNSColorToColor([NSColor windowFrameTextColor]);
341         break;
342     case CSSValueAppworkspace:
343         color = convertNSColorToColor([NSColor headerColor]);
344         break;
345     case CSSValueBackground:
346         // Use theme independent default
347         needsFallback = true;
348         break;
349     case CSSValueButtonface:
350         // We use this value instead of NSColor's controlColor to avoid website
351         // incompatibilities. We may want to change this to use the NSColor in
352         // future.
353         color = 0xFFC0C0C0;
354         break;
355     case CSSValueButtonhighlight:
356         color = convertNSColorToColor([NSColor controlHighlightColor]);
357         break;
358     case CSSValueButtonshadow:
359         color = convertNSColorToColor([NSColor controlShadowColor]);
360         break;
361     case CSSValueButtontext:
362         color = convertNSColorToColor([NSColor controlTextColor]);
363         break;
364     case CSSValueCaptiontext:
365         color = convertNSColorToColor([NSColor textColor]);
366         break;
367     case CSSValueGraytext:
368         color = convertNSColorToColor([NSColor disabledControlTextColor]);
369         break;
370     case CSSValueHighlight:
371         color = convertNSColorToColor([NSColor selectedTextBackgroundColor]);
372         break;
373     case CSSValueHighlighttext:
374         color = convertNSColorToColor([NSColor selectedTextColor]);
375         break;
376     case CSSValueInactiveborder:
377         color = convertNSColorToColor([NSColor controlBackgroundColor]);
378         break;
379     case CSSValueInactivecaption:
380         color = convertNSColorToColor([NSColor controlBackgroundColor]);
381         break;
382     case CSSValueInactivecaptiontext:
383         color = convertNSColorToColor([NSColor textColor]);
384         break;
385     case CSSValueInfobackground:
386         // There is no corresponding NSColor for this so we use a hard coded
387         // value.
388         color = 0xFFFBFCC5;
389         break;
390     case CSSValueInfotext:
391         color = convertNSColorToColor([NSColor textColor]);
392         break;
393     case CSSValueMenu:
394         color = menuBackgroundColor();
395         break;
396     case CSSValueMenutext:
397         color = convertNSColorToColor([NSColor selectedMenuItemTextColor]);
398         break;
399     case CSSValueScrollbar:
400         color = convertNSColorToColor([NSColor scrollBarColor]);
401         break;
402     case CSSValueText:
403         color = convertNSColorToColor([NSColor textColor]);
404         break;
405     case CSSValueThreeddarkshadow:
406         color = convertNSColorToColor([NSColor controlDarkShadowColor]);
407         break;
408     case CSSValueThreedshadow:
409         color = convertNSColorToColor([NSColor shadowColor]);
410         break;
411     case CSSValueThreedface:
412         // We use this value instead of NSColor's controlColor to avoid website
413         // incompatibilities. We may want to change this to use the NSColor in
414         // future.
415         color = 0xFFC0C0C0;
416         break;
417     case CSSValueThreedhighlight:
418         color = convertNSColorToColor([NSColor highlightColor]);
419         break;
420     case CSSValueThreedlightshadow:
421         color = convertNSColorToColor([NSColor controlLightHighlightColor]);
422         break;
423     case CSSValueWebkitFocusRingColor:
424         color = convertNSColorToColor([NSColor keyboardFocusIndicatorColor]);
425         break;
426     case CSSValueWindow:
427         color = convertNSColorToColor([NSColor windowBackgroundColor]);
428         break;
429     case CSSValueWindowframe:
430         color = convertNSColorToColor([NSColor windowFrameColor]);
431         break;
432     case CSSValueWindowtext:
433         color = convertNSColorToColor([NSColor windowFrameTextColor]);
434         break;
435     default:
436         needsFallback = true;
437         break;
438     }
440     if (needsFallback)
441         color = LayoutTheme::systemColor(cssValueId);
443     m_systemColorCache.set(cssValueId, color.rgb());
445     return color;
448 bool LayoutThemeMac::isControlStyled(const ComputedStyle& style) const
450     if (style.appearance() == TextFieldPart || style.appearance() == TextAreaPart)
451         return style.hasAuthorBorder() || style.boxShadow();
453     // FIXME: This is horrible, but there is not much else that can be done.
454     // Menu lists cannot draw properly when scaled. They can't really draw
455     // properly when transformed either. We can't detect the transform case at
456     // style adjustment time so that will just have to stay broken.  We can
457     // however detect that we're zooming. If zooming is in effect we treat it
458     // like the control is styled.
459     if (style.appearance() == MenulistPart && style.effectiveZoom() != 1.0f)
460         return true;
461     // Some other cells don't work well when scaled.
462     if (style.effectiveZoom() != 1) {
463         switch (style.appearance()) {
464         case ButtonPart:
465         case PushButtonPart:
466         case SearchFieldPart:
467         case SquareButtonPart:
468             return true;
469         default:
470             break;
471         }
472     }
473     return LayoutTheme::isControlStyled(style);
476 void LayoutThemeMac::addVisualOverflow(const LayoutObject& object, IntRect& rect)
478     ControlPart part = object.style()->appearance();
480 #if USE(NEW_THEME)
481     switch (part) {
482     case CheckboxPart:
483     case RadioPart:
484     case PushButtonPart:
485     case SquareButtonPart:
486     case ButtonPart:
487     case InnerSpinButtonPart:
488         return LayoutTheme::addVisualOverflow(object, rect);
489     default:
490         break;
491     }
492 #endif
494     float zoomLevel = object.style()->effectiveZoom();
496     if (part == MenulistPart) {
497         setPopupButtonCellState(&object, rect);
498         IntSize size = popupButtonSizes()[[popupButton() controlSize]];
499         size.setHeight(size.height() * zoomLevel);
500         size.setWidth(rect.width());
501         rect = ThemeMac::inflateRect(rect, size, popupButtonMargins(), zoomLevel);
502     } else if (part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart) {
503         rect.setHeight(rect.height() + sliderThumbShadowBlur);
504     }
507 void LayoutThemeMac::updateCheckedState(NSCell* cell, const LayoutObject* o)
509     bool oldIndeterminate = [cell state] == NSMixedState;
510     bool indeterminate = isIndeterminate(o);
511     bool checked = isChecked(o);
513     if (oldIndeterminate != indeterminate) {
514         [cell setState:indeterminate ? NSMixedState : (checked ? NSOnState : NSOffState)];
515         return;
516     }
518     bool oldChecked = [cell state] == NSOnState;
519     if (checked != oldChecked)
520         [cell setState:checked ? NSOnState : NSOffState];
523 void LayoutThemeMac::updateEnabledState(NSCell* cell, const LayoutObject* o)
525     bool oldEnabled = [cell isEnabled];
526     bool enabled = isEnabled(o);
527     if (enabled != oldEnabled)
528         [cell setEnabled:enabled];
531 void LayoutThemeMac::updateFocusedState(NSCell* cell, const LayoutObject* o)
533     bool oldFocused = [cell showsFirstResponder];
534     bool focused = isFocused(o) && o->style()->outlineStyleIsAuto();
535     if (focused != oldFocused)
536         [cell setShowsFirstResponder:focused];
539 void LayoutThemeMac::updatePressedState(NSCell* cell, const LayoutObject* o)
541     bool oldPressed = [cell isHighlighted];
542     bool pressed = o->node() && o->node()->active();
543     if (pressed != oldPressed)
544         [cell setHighlighted:pressed];
547 NSControlSize LayoutThemeMac::controlSizeForFont(const ComputedStyle& style) const
549     int fontSize = style.fontSize();
550     if (fontSize >= 16)
551         return NSRegularControlSize;
552     if (fontSize >= 11)
553         return NSSmallControlSize;
554     return NSMiniControlSize;
557 void LayoutThemeMac::setControlSize(NSCell* cell, const IntSize* sizes, const IntSize& minSize, float zoomLevel)
559     NSControlSize size;
560     if (minSize.width() >= static_cast<int>(sizes[NSRegularControlSize].width() * zoomLevel) &&
561         minSize.height() >= static_cast<int>(sizes[NSRegularControlSize].height() * zoomLevel))
562         size = NSRegularControlSize;
563     else if (minSize.width() >= static_cast<int>(sizes[NSSmallControlSize].width() * zoomLevel) &&
564              minSize.height() >= static_cast<int>(sizes[NSSmallControlSize].height() * zoomLevel))
565         size = NSSmallControlSize;
566     else
567         size = NSMiniControlSize;
568     // Only update if we have to, since AppKit does work even if the size is the
569     // same.
570     if (size != [cell controlSize])
571         [cell setControlSize:size];
574 IntSize LayoutThemeMac::sizeForFont(const ComputedStyle& style, const IntSize* sizes) const
576     if (style.effectiveZoom() != 1.0f) {
577         IntSize result = sizes[controlSizeForFont(style)];
578         return IntSize(result.width() * style.effectiveZoom(), result.height() * style.effectiveZoom());
579     }
580     return sizes[controlSizeForFont(style)];
583 IntSize LayoutThemeMac::sizeForSystemFont(const ComputedStyle& style, const IntSize* sizes) const
585     if (style.effectiveZoom() != 1.0f) {
586         IntSize result = sizes[controlSizeForSystemFont(style)];
587         return IntSize(result.width() * style.effectiveZoom(), result.height() * style.effectiveZoom());
588     }
589     return sizes[controlSizeForSystemFont(style)];
592 void LayoutThemeMac::setSizeFromFont(ComputedStyle& style, const IntSize* sizes) const
594     // FIXME: Check is flawed, since it doesn't take min-width/max-width into
595     // account.
596     IntSize size = sizeForFont(style, sizes);
597     if (style.width().isIntrinsicOrAuto() && size.width() > 0)
598         style.setWidth(Length(size.width(), Fixed));
599     if (style.height().isAuto() && size.height() > 0)
600         style.setHeight(Length(size.height(), Fixed));
603 void LayoutThemeMac::setFontFromControlSize(ComputedStyle& style, NSControlSize controlSize) const
605     FontDescription fontDescription;
606     fontDescription.setIsAbsoluteSize(true);
607     fontDescription.setGenericFamily(FontDescription::SerifFamily);
609     NSFont* font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:controlSize]];
610     fontDescription.firstFamily().setFamily(@"BlinkMacSystemFont");
611     fontDescription.setComputedSize([font pointSize] * style.effectiveZoom());
612     fontDescription.setSpecifiedSize([font pointSize] * style.effectiveZoom());
614     // Reset line height.
615     style.setLineHeight(ComputedStyle::initialLineHeight());
617     // TODO(esprehn): The fontSelector manual management is buggy and error prone.
618     FontSelector* fontSelector = style.font().fontSelector();
619     if (style.setFontDescription(fontDescription))
620         style.font().update(fontSelector);
623 NSControlSize LayoutThemeMac::controlSizeForSystemFont(const ComputedStyle& style) const
625     float fontSize = style.fontSize();
626     float zoomLevel = style.effectiveZoom();
627     if (zoomLevel != 1)
628         fontSize /= zoomLevel;
629     if (fontSize >= [NSFont systemFontSizeForControlSize:NSRegularControlSize])
630         return NSRegularControlSize;
631     if (fontSize >= [NSFont systemFontSizeForControlSize:NSSmallControlSize])
632         return NSSmallControlSize;
633     return NSMiniControlSize;
636 const int* LayoutThemeMac::popupButtonMargins() const
638     static const int margins[3][4] =
639     {
640         { 0, 3, 1, 3 },
641         { 0, 3, 2, 3 },
642         { 0, 1, 0, 1 }
643     };
644     return margins[[popupButton() controlSize]];
647 const IntSize* LayoutThemeMac::popupButtonSizes() const
649     static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) };
650     return sizes;
653 const int* LayoutThemeMac::popupButtonPadding(NSControlSize size) const
655     static const int padding[3][4] =
656     {
657         { 2, 26, 3, 8 },
658         { 2, 23, 3, 8 },
659         { 2, 22, 3, 10 }
660     };
661     return padding[size];
664 IntSize LayoutThemeMac::meterSizeForBounds(const LayoutMeter* layoutMeter, const IntRect& bounds) const
666     if (NoControlPart == layoutMeter->style()->appearance())
667         return bounds.size();
669     NSLevelIndicatorCell* cell = levelIndicatorFor(layoutMeter);
670     // Makes enough room for cell's intrinsic size.
671     NSSize cellSize = [cell cellSizeForBounds:IntRect(IntPoint(), bounds.size())];
672     return IntSize(bounds.width() < cellSize.width ? cellSize.width : bounds.width(),
673                    bounds.height() < cellSize.height ? cellSize.height : bounds.height());
676 bool LayoutThemeMac::supportsMeter(ControlPart part) const
678     switch (part) {
679     case RelevancyLevelIndicatorPart:
680     case DiscreteCapacityLevelIndicatorPart:
681     case RatingLevelIndicatorPart:
682     case MeterPart:
683     case ContinuousCapacityLevelIndicatorPart:
684         return true;
685     default:
686         return false;
687     }
690 NSLevelIndicatorStyle LayoutThemeMac::levelIndicatorStyleFor(ControlPart part) const
692     switch (part) {
693     case RelevancyLevelIndicatorPart:
694         return NSRelevancyLevelIndicatorStyle;
695     case DiscreteCapacityLevelIndicatorPart:
696         return NSDiscreteCapacityLevelIndicatorStyle;
697     case RatingLevelIndicatorPart:
698         return NSRatingLevelIndicatorStyle;
699     case MeterPart:
700     case ContinuousCapacityLevelIndicatorPart:
701     default:
702         return NSContinuousCapacityLevelIndicatorStyle;
703     }
706 NSLevelIndicatorCell* LayoutThemeMac::levelIndicatorFor(const LayoutMeter* layoutMeter) const
708     const ComputedStyle& style = layoutMeter->styleRef();
709     ASSERT(style.appearance() != NoControlPart);
711     if (!m_levelIndicator)
712         m_levelIndicator.adoptNS([[NSLevelIndicatorCell alloc] initWithLevelIndicatorStyle:NSContinuousCapacityLevelIndicatorStyle]);
713     NSLevelIndicatorCell* cell = m_levelIndicator.get();
715     HTMLMeterElement* element = layoutMeter->meterElement();
716     double value = element->value();
718     // Because NSLevelIndicatorCell does not support optimum-in-the-middle type
719     // coloring, we explicitly control the color instead giving low and high
720     // value to NSLevelIndicatorCell as is.
721     switch (element->gaugeRegion()) {
722     case HTMLMeterElement::GaugeRegionOptimum:
723         // Make meter the green.
724         [cell setWarningValue:value + 1];
725         [cell setCriticalValue:value + 2];
726         break;
727     case HTMLMeterElement::GaugeRegionSuboptimal:
728         // Make the meter yellow.
729         [cell setWarningValue:value - 1];
730         [cell setCriticalValue:value + 1];
731         break;
732     case HTMLMeterElement::GaugeRegionEvenLessGood:
733         // Make the meter red.
734         [cell setWarningValue:value - 2];
735         [cell setCriticalValue:value - 1];
736         break;
737     }
739     [cell setLevelIndicatorStyle:levelIndicatorStyleFor(style.appearance())];
740     [cell setBaseWritingDirection:style.isLeftToRightDirection() ? NSWritingDirectionLeftToRight : NSWritingDirectionRightToLeft];
741     [cell setMinValue:element->min()];
742     [cell setMaxValue:element->max()];
743     RetainPtr<NSNumber> valueObject = [NSNumber numberWithDouble:value];
744     [cell setObjectValue:valueObject.get()];
746     return cell;
749 const IntSize* LayoutThemeMac::progressBarSizes() const
751     static const IntSize sizes[3] = { IntSize(0, 20), IntSize(0, 12), IntSize(0, 12) };
752     return sizes;
755 const int* LayoutThemeMac::progressBarMargins(NSControlSize controlSize) const
757     static const int margins[3][4] =
758     {
759         { 0, 0, 1, 0 },
760         { 0, 0, 1, 0 },
761         { 0, 0, 1, 0 },
762     };
763     return margins[controlSize];
766 int LayoutThemeMac::minimumProgressBarHeight(const ComputedStyle& style) const
768     return sizeForSystemFont(style, progressBarSizes()).height();
771 double LayoutThemeMac::animationRepeatIntervalForProgressBar() const
773     return progressAnimationFrameRate;
776 double LayoutThemeMac::animationDurationForProgressBar() const
778     return progressAnimationNumFrames * progressAnimationFrameRate;
781 static const IntSize* menuListButtonSizes()
783     static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) };
784     return sizes;
787 void LayoutThemeMac::adjustMenuListStyle(ComputedStyle& style, Element* e) const
789     NSControlSize controlSize = controlSizeForFont(style);
791     style.resetBorder();
792     style.resetPadding();
794     // Height is locked to auto.
795     style.setHeight(Length(Auto));
797     // White-space is locked to pre.
798     style.setWhiteSpace(PRE);
800     // Set the foreground color to black or gray when we have the aqua look.
801     // Cast to RGB32 is to work around a compiler bug.
802     style.setColor(e && !e->isDisabledFormControl() ? static_cast<RGBA32>(Color::black) : Color::darkGray);
804     // Set the button's vertical size.
805     setSizeFromFont(style, menuListButtonSizes());
807     // Our font is locked to the appropriate system font size for the
808     // control. To clarify, we first use the CSS-specified font to figure out a
809     // reasonable control size, but once that control size is determined, we
810     // throw that font away and use the appropriate system font for the control
811     // size instead.
812     setFontFromControlSize(style, controlSize);
815 static const int paddingBeforeSeparator = 4;
816 static const int baseBorderRadius = 5;
817 static const int styledPopupPaddingLeft = 8;
818 static const int styledPopupPaddingTop = 1;
819 static const int styledPopupPaddingBottom = 2;
821 // These functions are called with MenuListPart or MenulistButtonPart appearance
822 // by LayoutMenuList.
823 int LayoutThemeMac::popupInternalPaddingLeft(const ComputedStyle& style) const
825     if (style.appearance() == MenulistPart)
826         return popupButtonPadding(controlSizeForFont(style))[ThemeMac::LeftMargin] * style.effectiveZoom();
827     if (style.appearance() == MenulistButtonPart)
828         return styledPopupPaddingLeft * style.effectiveZoom();
829     return 0;
832 int LayoutThemeMac::popupInternalPaddingRight(const ComputedStyle& style) const
834     if (style.appearance() == MenulistPart)
835         return popupButtonPadding(controlSizeForFont(style))[ThemeMac::RightMargin] * style.effectiveZoom();
836     if (style.appearance() == MenulistButtonPart) {
837         float fontScale = style.fontSize() / baseFontSize;
838         float arrowWidth = menuListBaseArrowWidth * fontScale;
839         return static_cast<int>(ceilf(arrowWidth + (menuListArrowPaddingLeft + menuListArrowPaddingRight + paddingBeforeSeparator) * style.effectiveZoom()));
840     }
841     return 0;
844 int LayoutThemeMac::popupInternalPaddingTop(const ComputedStyle& style) const
846     if (style.appearance() == MenulistPart)
847         return popupButtonPadding(controlSizeForFont(style))[ThemeMac::TopMargin] * style.effectiveZoom();
848     if (style.appearance() == MenulistButtonPart)
849         return styledPopupPaddingTop * style.effectiveZoom();
850     return 0;
853 int LayoutThemeMac::popupInternalPaddingBottom(const ComputedStyle& style) const
855     if (style.appearance() == MenulistPart)
856         return popupButtonPadding(controlSizeForFont(style))[ThemeMac::BottomMargin] * style.effectiveZoom();
857     if (style.appearance() == MenulistButtonPart)
858         return styledPopupPaddingBottom * style.effectiveZoom();
859     return 0;
862 void LayoutThemeMac::adjustMenuListButtonStyle(ComputedStyle& style, Element*) const
864     float fontScale = style.fontSize() / baseFontSize;
866     style.resetPadding();
867     style.setBorderRadius(IntSize(int(baseBorderRadius + fontScale - 1), int(baseBorderRadius + fontScale - 1))); // FIXME: Round up?
869     const int minHeight = 15;
870     style.setMinHeight(Length(minHeight, Fixed));
872     style.setLineHeight(ComputedStyle::initialLineHeight());
875 void LayoutThemeMac::setPopupButtonCellState(const LayoutObject* object, const IntRect& rect)
877     NSPopUpButtonCell* popupButton = this->popupButton();
879     // Set the control size based off the rectangle we're painting into.
880     setControlSize(popupButton, popupButtonSizes(), rect.size(), object->style()->effectiveZoom());
882     // Update the various states we respond to.
883     updateActiveState(popupButton, object);
884     updateCheckedState(popupButton, object);
885     updateEnabledState(popupButton, object);
886     updatePressedState(popupButton, object);
887     if (ThemeMac::drawWithFrameDrawsFocusRing())
888         updateFocusedState(popupButton, object);
891 const IntSize* LayoutThemeMac::menuListSizes() const
893     static const IntSize sizes[3] = { IntSize(9, 0), IntSize(5, 0), IntSize(0, 0) };
894     return sizes;
897 int LayoutThemeMac::minimumMenuListSize(const ComputedStyle& style) const
899     return sizeForSystemFont(style, menuListSizes()).width();
902 void LayoutThemeMac::setSearchCellState(LayoutObject* o, const IntRect&)
904     NSSearchFieldCell* search = this->search();
906     // Update the various states we respond to.
907     updateActiveState(search, o);
908     updateEnabledState(search, o);
909     updateFocusedState(search, o);
912 const IntSize* LayoutThemeMac::searchFieldSizes() const
914     static const IntSize sizes[3] = { IntSize(0, 22), IntSize(0, 19), IntSize(0, 15) };
915     return sizes;
918 static const int* searchFieldHorizontalPaddings()
920     static const int sizes[3] = { 3, 2, 1 };
921     return sizes;
924 void LayoutThemeMac::setSearchFieldSize(ComputedStyle& style) const
926     // If the width and height are both specified, then we have nothing to do.
927     if (!style.width().isIntrinsicOrAuto() && !style.height().isAuto())
928         return;
930     // Use the font size to determine the intrinsic width of the control.
931     setSizeFromFont(style, searchFieldSizes());
934 const int searchFieldBorderWidth = 2;
935 void LayoutThemeMac::adjustSearchFieldStyle(ComputedStyle& style) const
937     // Override border.
938     style.resetBorder();
939     const short borderWidth = searchFieldBorderWidth * style.effectiveZoom();
940     style.setBorderLeftWidth(borderWidth);
941     style.setBorderLeftStyle(INSET);
942     style.setBorderRightWidth(borderWidth);
943     style.setBorderRightStyle(INSET);
944     style.setBorderBottomWidth(borderWidth);
945     style.setBorderBottomStyle(INSET);
946     style.setBorderTopWidth(borderWidth);
947     style.setBorderTopStyle(INSET);
949     // Override height.
950     style.setHeight(Length(Auto));
951     setSearchFieldSize(style);
953     NSControlSize controlSize = controlSizeForFont(style);
955     // Override padding size to match AppKit text positioning.
956     const int verticalPadding = 1 * style.effectiveZoom();
957     const int horizontalPadding = searchFieldHorizontalPaddings()[controlSize] * style.effectiveZoom();
958     style.setPaddingLeft(Length(horizontalPadding, Fixed));
959     style.setPaddingRight(Length(horizontalPadding, Fixed));
960     style.setPaddingTop(Length(verticalPadding, Fixed));
961     style.setPaddingBottom(Length(verticalPadding, Fixed));
963     setFontFromControlSize(style, controlSize);
965     style.setBoxShadow(nullptr);
968 const IntSize* LayoutThemeMac::cancelButtonSizes() const
970     static const IntSize sizes[3] = { IntSize(14, 14), IntSize(11, 11), IntSize(9, 9) };
971     return sizes;
974 void LayoutThemeMac::adjustSearchFieldCancelButtonStyle(ComputedStyle& style) const
976     IntSize size = sizeForSystemFont(style, cancelButtonSizes());
977     style.setWidth(Length(size.width(), Fixed));
978     style.setHeight(Length(size.height(), Fixed));
979     style.setBoxShadow(nullptr);
982 const IntSize* LayoutThemeMac::resultsButtonSizes() const
984     static const IntSize sizes[3] = { IntSize(15, 14), IntSize(16, 13), IntSize(14, 11) };
985     return sizes;
988 void LayoutThemeMac::adjustSearchFieldDecorationStyle(ComputedStyle& style) const
990     NSControlSize controlSize = controlSizeForSystemFont(style);
991     IntSize searchFieldSize = searchFieldSizes()[controlSize];
992     int width = searchFieldSize.height() / 2 - searchFieldBorderWidth - searchFieldHorizontalPaddings()[controlSize];
993     style.setWidth(Length(width, Fixed));
994     style.setHeight(Length(0, Fixed));
995     style.setBoxShadow(nullptr);
998 void LayoutThemeMac::adjustSearchFieldResultsDecorationStyle(ComputedStyle& style) const
1000     IntSize size = sizeForSystemFont(style, resultsButtonSizes());
1001     style.setWidth(Length(size.width(), Fixed));
1002     style.setHeight(Length(size.height(), Fixed));
1003     style.setBoxShadow(nullptr);
1006 IntSize LayoutThemeMac::sliderTickSize() const
1008     return IntSize(1, 3);
1011 int LayoutThemeMac::sliderTickOffsetFromTrackCenter() const
1013     return -9;
1016 void LayoutThemeMac::adjustSliderThumbSize(ComputedStyle& style) const
1018     float zoomLevel = style.effectiveZoom();
1019     if (style.appearance() == SliderThumbHorizontalPart || style.appearance() == SliderThumbVerticalPart) {
1020         style.setWidth(Length(static_cast<int>(sliderThumbWidth * zoomLevel), Fixed));
1021         style.setHeight(Length(static_cast<int>(sliderThumbHeight * zoomLevel), Fixed));
1022     } else {
1023         adjustMediaSliderThumbSize(style);
1024     }
1027 NSPopUpButtonCell* LayoutThemeMac::popupButton() const
1029     if (!m_popupButton) {
1030         m_popupButton.adoptNS([[NSPopUpButtonCell alloc] initTextCell:@"" pullsDown:NO]);
1031         [m_popupButton.get() setUsesItemFromMenu:NO];
1032         [m_popupButton.get() setFocusRingType:NSFocusRingTypeExterior];
1033     }
1035     return m_popupButton.get();
1038 NSSearchFieldCell* LayoutThemeMac::search() const
1040     if (!m_search) {
1041         m_search.adoptNS([[NSSearchFieldCell alloc] initTextCell:@""]);
1042         [m_search.get() setBezelStyle:NSTextFieldRoundedBezel];
1043         [m_search.get() setBezeled:YES];
1044         [m_search.get() setEditable:YES];
1045         [m_search.get() setFocusRingType:NSFocusRingTypeExterior];
1046         SEL sel = @selector(setCenteredLook:);
1047         if ([m_search.get() respondsToSelector:sel]) {
1048             BOOL boolValue = NO;
1049             NSMethodSignature* signature = [NSSearchFieldCell instanceMethodSignatureForSelector:sel];
1050             NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:signature];
1051             [invocation setTarget:m_search.get()];
1052             [invocation setSelector:sel];
1053             [invocation setArgument:&boolValue atIndex:2];
1054             [invocation invoke];
1055         }
1056     }
1058     return m_search.get();
1061 NSTextFieldCell* LayoutThemeMac::textField() const
1063     if (!m_textField) {
1064         m_textField.adoptNS([[WebCoreTextFieldCell alloc] initTextCell:@""]);
1065         [m_textField.get() setBezeled:YES];
1066         [m_textField.get() setEditable:YES];
1067         [m_textField.get() setFocusRingType:NSFocusRingTypeExterior];
1068 #if __MAC_OS_X_VERSION_MIN_REQUIRED <= 1070
1069         [m_textField.get() setDrawsBackground:YES];
1070         [m_textField.get() setBackgroundColor:[NSColor whiteColor]];
1071 #else
1072         // Post-Lion, WebCore can be in charge of paintinng the background
1073         // thanks to the workaround in place for <rdar://problem/11385461>,
1074         // which is implemented above as _coreUIDrawOptionsWithFrame.
1075         [m_textField.get() setDrawsBackground:NO];
1076 #endif
1077     }
1079     return m_textField.get();
1082 String LayoutThemeMac::fileListNameForWidth(Locale& locale, const FileList* fileList, const Font& font, int width) const
1084     if (width <= 0)
1085         return String();
1087     String strToTruncate;
1088     if (fileList->isEmpty()) {
1089         strToTruncate = locale.queryString(WebLocalizedString::FileButtonNoFileSelectedLabel);
1090     } else if (fileList->length() == 1) {
1091         File* file = fileList->item(0);
1092         if (file->userVisibility() == File::IsUserVisible)
1093             strToTruncate = [[NSFileManager defaultManager] displayNameAtPath:(fileList->item(0)->path())];
1094         else
1095             strToTruncate = file->name();
1096     } else {
1097         // FIXME: Localization of fileList->length().
1098         return StringTruncator::rightTruncate(locale.queryString(WebLocalizedString::MultipleFileUploadText, String::number(fileList->length())), width, font);
1099     }
1101     return StringTruncator::centerTruncate(strToTruncate, width, font);
1104 NSView* FlippedView()
1106     static NSView* view = [[RTCMFlippedView alloc] init];
1107     return view;
1110 LayoutTheme& LayoutTheme::theme()
1112     DEFINE_STATIC_REF(LayoutTheme, layoutTheme, (LayoutThemeMac::create()));
1113     return *layoutTheme;
1116 PassRefPtr<LayoutTheme> LayoutThemeMac::create()
1118     return adoptRef(new LayoutThemeMac);
1121 bool LayoutThemeMac::usesTestModeFocusRingColor() const
1123     return LayoutTestSupport::isRunningLayoutTest();
1126 NSView* LayoutThemeMac::documentViewFor(LayoutObject*) const
1128     return FlippedView();
1131 // Updates the control tint (a.k.a. active state) of |cell| (from |o|).  In the
1132 // Chromium port, the layoutObject runs as a background process and controls'
1133 // NSCell(s) lack a parent NSView. Therefore controls don't have their tint
1134 // color updated correctly when the application is activated/deactivated.
1135 // FocusController's setActive() is called when the application is
1136 // activated/deactivated, which causes a paint invalidation at which time this
1137 // code is called.
1138 // This function should be called before drawing any NSCell-derived controls,
1139 // unless you're sure it isn't needed.
1140 void LayoutThemeMac::updateActiveState(NSCell* cell, const LayoutObject* o)
1142     NSControlTint oldTint = [cell controlTint];
1143     NSControlTint tint = isActive(o) ? [NSColor currentControlTint] :
1144                                        static_cast<NSControlTint>(NSClearControlTint);
1146     if (tint != oldTint)
1147         [cell setControlTint:tint];
1150 void LayoutThemeMac::adjustMediaSliderThumbSize(ComputedStyle& style) const
1152     MediaControlsPainter::adjustMediaSliderThumbSize(style);
1155 String LayoutThemeMac::extraFullScreenStyleSheet()
1157     // FIXME: Chromium may wish to style its default media controls differently in fullscreen.
1158     return String();
1161 String LayoutThemeMac::extraDefaultStyleSheet()
1163     return LayoutTheme::extraDefaultStyleSheet() +
1164         loadResourceAsASCIIString("themeChromium.css") +
1165         loadResourceAsASCIIString("themeInputMultipleFields.css") +
1166         loadResourceAsASCIIString("themeMac.css");
1169 bool LayoutThemeMac::supportsFocusRing(const ComputedStyle& style) const
1171     return (style.hasAppearance() && style.appearance() != TextFieldPart && style.appearance() != TextAreaPart && style.appearance() != MenulistButtonPart && style.appearance() != ListboxPart && !shouldUseFallbackTheme(style));
1174 bool LayoutThemeMac::shouldUseFallbackTheme(const ComputedStyle& style) const
1176     ControlPart part = style.appearance();
1177     if (part == CheckboxPart || part == RadioPart)
1178         return style.effectiveZoom() != 1;
1179     return false;
1182 } // namespace blink