2 * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
3 * Copyright (C) 2008, 2009 Google, Inc.
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.
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.
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.
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>
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;
60 @implementation LayoutThemeNotificationObserver
62 - (id)initWithTheme:(blink::LayoutTheme *)theme
64 if (!(self = [super init]))
71 - (void)systemColorsDidChange:(NSNotification *)unusedNotification
73 ASSERT_UNUSED(unusedNotification, [[unusedNotification name] isEqualToString:NSSystemColorsDidChangeNotification]);
74 _theme->platformColorsDidChange();
79 @interface NSTextFieldCell (WKDetails)
80 - (CFDictionaryRef)_coreUIDrawOptionsWithFrame:(NSRect)cellFrame inView:(NSView *)controlView includeFocus:(BOOL)includeFocus;
84 @interface WebCoreTextFieldCell : NSTextFieldCell
85 - (CFDictionaryRef)_coreUIDrawOptionsWithFrame:(NSRect)cellFrame inView:(NSView *)controlView includeFocus:(BOOL)includeFocus;
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];
98 @interface RTCMFlippedView : NSView
102 - (NSText *)currentEditor;
106 @implementation RTCMFlippedView
109 return [[NSGraphicsContext currentContext] isFlipped];
112 - (NSText *)currentEditor {
120 using namespace HTMLNames;
122 LayoutThemeMac::LayoutThemeMac()
123 : m_notificationObserver(AdoptNS, [[LayoutThemeNotificationObserver alloc] initWithTheme:this])
126 [[NSNotificationCenter defaultCenter] addObserver:m_notificationObserver.get()
127 selector:@selector(systemColorsDidChange:)
128 name:NSSystemColorsDidChangeNotification
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
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
165 Color LayoutThemeMac::platformInactiveListBoxSelectionForegroundColor() const
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[] = {
208 return fontWeights[appKitFontWeight - 1];
211 static inline NSFont* systemNSFont(CSSValueID systemFontID)
213 switch (systemFontID) {
214 case CSSValueSmallCaption:
215 return [NSFont systemFontOfSize:[NSFont smallSystemFontSize]];
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]];
227 return [NSFont systemFontOfSize:[NSFont systemFontSize]];
231 void LayoutThemeMac::systemFont(CSSValueID systemFontID, FontStyle& fontStyle, FontWeight& fontWeight, float& fontSize, AtomicString& fontFamily) const
233 NSFont* font = systemNSFont(systemFontID);
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]));
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
273 colorSpaceName:NSDeviceRGBColorSpace
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];
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
300 colorSpaceName:NSDeviceRGBColorSpace
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);
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
328 HashMap<int, RGBA32>::iterator it = m_systemColorCache.find(cssValueId);
329 if (it != m_systemColorCache.end())
334 bool needsFallback = false;
335 switch (cssValueId) {
336 case CSSValueActiveborder:
337 color = convertNSColorToColor([NSColor keyboardFocusIndicatorColor]);
339 case CSSValueActivecaption:
340 color = convertNSColorToColor([NSColor windowFrameTextColor]);
342 case CSSValueAppworkspace:
343 color = convertNSColorToColor([NSColor headerColor]);
345 case CSSValueBackground:
346 // Use theme independent default
347 needsFallback = true;
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
355 case CSSValueButtonhighlight:
356 color = convertNSColorToColor([NSColor controlHighlightColor]);
358 case CSSValueButtonshadow:
359 color = convertNSColorToColor([NSColor controlShadowColor]);
361 case CSSValueButtontext:
362 color = convertNSColorToColor([NSColor controlTextColor]);
364 case CSSValueCaptiontext:
365 color = convertNSColorToColor([NSColor textColor]);
367 case CSSValueGraytext:
368 color = convertNSColorToColor([NSColor disabledControlTextColor]);
370 case CSSValueHighlight:
371 color = convertNSColorToColor([NSColor selectedTextBackgroundColor]);
373 case CSSValueHighlighttext:
374 color = convertNSColorToColor([NSColor selectedTextColor]);
376 case CSSValueInactiveborder:
377 color = convertNSColorToColor([NSColor controlBackgroundColor]);
379 case CSSValueInactivecaption:
380 color = convertNSColorToColor([NSColor controlBackgroundColor]);
382 case CSSValueInactivecaptiontext:
383 color = convertNSColorToColor([NSColor textColor]);
385 case CSSValueInfobackground:
386 // There is no corresponding NSColor for this so we use a hard coded
390 case CSSValueInfotext:
391 color = convertNSColorToColor([NSColor textColor]);
394 color = menuBackgroundColor();
396 case CSSValueMenutext:
397 color = convertNSColorToColor([NSColor selectedMenuItemTextColor]);
399 case CSSValueScrollbar:
400 color = convertNSColorToColor([NSColor scrollBarColor]);
403 color = convertNSColorToColor([NSColor textColor]);
405 case CSSValueThreeddarkshadow:
406 color = convertNSColorToColor([NSColor controlDarkShadowColor]);
408 case CSSValueThreedshadow:
409 color = convertNSColorToColor([NSColor shadowColor]);
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
417 case CSSValueThreedhighlight:
418 color = convertNSColorToColor([NSColor highlightColor]);
420 case CSSValueThreedlightshadow:
421 color = convertNSColorToColor([NSColor controlLightHighlightColor]);
423 case CSSValueWebkitFocusRingColor:
424 color = convertNSColorToColor([NSColor keyboardFocusIndicatorColor]);
427 color = convertNSColorToColor([NSColor windowBackgroundColor]);
429 case CSSValueWindowframe:
430 color = convertNSColorToColor([NSColor windowFrameColor]);
432 case CSSValueWindowtext:
433 color = convertNSColorToColor([NSColor windowFrameTextColor]);
436 needsFallback = true;
441 color = LayoutTheme::systemColor(cssValueId);
443 m_systemColorCache.set(cssValueId, color.rgb());
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)
461 // Some other cells don't work well when scaled.
462 if (style.effectiveZoom() != 1) {
463 switch (style.appearance()) {
466 case SearchFieldPart:
467 case SquareButtonPart:
473 return LayoutTheme::isControlStyled(style);
476 void LayoutThemeMac::addVisualOverflow(const LayoutObject& object, IntRect& rect)
478 ControlPart part = object.style()->appearance();
485 case SquareButtonPart:
487 case InnerSpinButtonPart:
488 return LayoutTheme::addVisualOverflow(object, rect);
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);
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)];
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();
551 return NSRegularControlSize;
553 return NSSmallControlSize;
554 return NSMiniControlSize;
557 void LayoutThemeMac::setControlSize(NSCell* cell, const IntSize* sizes, const IntSize& minSize, float zoomLevel)
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;
567 size = NSMiniControlSize;
568 // Only update if we have to, since AppKit does work even if the size is the
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());
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());
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
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();
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] =
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) };
653 const int* LayoutThemeMac::popupButtonPadding(NSControlSize size) const
655 static const int padding[3][4] =
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
679 case RelevancyLevelIndicatorPart:
680 case DiscreteCapacityLevelIndicatorPart:
681 case RatingLevelIndicatorPart:
683 case ContinuousCapacityLevelIndicatorPart:
690 NSLevelIndicatorStyle LayoutThemeMac::levelIndicatorStyleFor(ControlPart part) const
693 case RelevancyLevelIndicatorPart:
694 return NSRelevancyLevelIndicatorStyle;
695 case DiscreteCapacityLevelIndicatorPart:
696 return NSDiscreteCapacityLevelIndicatorStyle;
697 case RatingLevelIndicatorPart:
698 return NSRatingLevelIndicatorStyle;
700 case ContinuousCapacityLevelIndicatorPart:
702 return NSContinuousCapacityLevelIndicatorStyle;
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];
727 case HTMLMeterElement::GaugeRegionSuboptimal:
728 // Make the meter yellow.
729 [cell setWarningValue:value - 1];
730 [cell setCriticalValue:value + 1];
732 case HTMLMeterElement::GaugeRegionEvenLessGood:
733 // Make the meter red.
734 [cell setWarningValue:value - 2];
735 [cell setCriticalValue:value - 1];
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()];
749 const IntSize* LayoutThemeMac::progressBarSizes() const
751 static const IntSize sizes[3] = { IntSize(0, 20), IntSize(0, 12), IntSize(0, 12) };
755 const int* LayoutThemeMac::progressBarMargins(NSControlSize controlSize) const
757 static const int margins[3][4] =
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) };
787 void LayoutThemeMac::adjustMenuListStyle(ComputedStyle& style, Element* e) const
789 NSControlSize controlSize = controlSizeForFont(style);
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
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();
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()));
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();
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();
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) };
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) };
918 static const int* searchFieldHorizontalPaddings()
920 static const int sizes[3] = { 3, 2, 1 };
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())
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
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);
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) };
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) };
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
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));
1023 adjustMediaSliderThumbSize(style);
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];
1035 return m_popupButton.get();
1038 NSSearchFieldCell* LayoutThemeMac::search() const
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];
1058 return m_search.get();
1061 NSTextFieldCell* LayoutThemeMac::textField() const
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]];
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];
1079 return m_textField.get();
1082 String LayoutThemeMac::fileListNameForWidth(Locale& locale, const FileList* fileList, const Font& font, int width) const
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())];
1095 strToTruncate = file->name();
1097 // FIXME: Localization of fileList->length().
1098 return StringTruncator::rightTruncate(locale.queryString(WebLocalizedString::MultipleFileUploadText, String::number(fileList->length())), width, font);
1101 return StringTruncator::centerTruncate(strToTruncate, width, font);
1104 NSView* FlippedView()
1106 static NSView* view = [[RTCMFlippedView alloc] init];
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
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.
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;
1182 } // namespace blink