2 * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved.
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
22 #import "RenderThemeMac.h"
24 #import "BitmapImage.h"
25 #import "CSSValueKeywords.h"
26 #import "CSSValueList.h"
28 #import "CoreGraphicsSPI.h"
31 #import "ExceptionCodePlaceholder.h"
33 #import "FloatRoundedRect.h"
34 #import "FocusController.h"
37 #import "GraphicsContextCG.h"
38 #import "HTMLAttachmentElement.h"
39 #import "HTMLAudioElement.h"
40 #import "HTMLInputElement.h"
41 #import "HTMLMediaElement.h"
43 #import "HTMLPlugInImageElement.h"
44 #import "IconServicesSPI.h"
46 #import "ImageBuffer.h"
47 #import "LaunchServicesSPI.h"
48 #import "LocalCurrentGraphicsContext.h"
49 #import "LocalizedStrings.h"
50 #import "MediaControlElements.h"
51 #import "NSSharingServicePickerSPI.h"
54 #import "RenderAttachment.h"
55 #import "RenderLayer.h"
56 #import "RenderMedia.h"
57 #import "RenderMediaControlElements.h"
58 #import "RenderMediaControls.h"
59 #import "RenderProgress.h"
60 #import "RenderSlider.h"
61 #import "RenderSnapshottedPlugIn.h"
62 #import "RenderView.h"
63 #import "SharedBuffer.h"
64 #import "StringTruncator.h"
65 #import "StyleResolver.h"
67 #import "TimeRanges.h"
68 #import "UserAgentScripts.h"
69 #import "UserAgentStyleSheets.h"
70 #import "WebCoreSystemInterface.h"
71 #import <wtf/RetainPtr.h>
72 #import <wtf/RetainPtr.h>
73 #import <wtf/StdLibExtras.h>
74 #import <wtf/text/StringBuilder.h>
75 #import <Carbon/Carbon.h>
76 #import <Cocoa/Cocoa.h>
79 #if ENABLE(METER_ELEMENT)
80 #import "RenderMeter.h"
81 #import "HTMLMeterElement.h"
84 #if defined(__LP64__) && __LP64__
85 #define HAVE_APPKIT_SERVICE_CONTROLS_SUPPORT 1
87 #define HAVE_APPKIT_SERVICE_CONTROLS_SUPPORT 0
90 #if ENABLE(SERVICE_CONTROLS) && HAVE(APPKIT_SERVICE_CONTROLS_SUPPORT)
92 #if __has_include(<AppKit/AppKitDefines_Private.h>)
93 #import <AppKit/AppKitDefines_Private.h>
95 #define APPKIT_PRIVATE_CLASS
98 #if __has_include(<AppKit/NSServicesRolloverButtonCell.h>)
99 #import <AppKit/NSServicesRolloverButtonCell.h>
101 @interface NSServicesRolloverButtonCell : NSButtonCell
105 @interface NSServicesRolloverButtonCell (Details)
106 + (NSServicesRolloverButtonCell *)serviceRolloverButtonCellForStyle:(NSSharingServicePickerStyle)style;
107 - (NSRect)rectForBounds:(NSRect)bounds preferredEdge:(NSRectEdge)preferredEdge;
110 #endif // ENABLE(SERVICE_CONTROLS)
112 // The methods in this file are specific to the Mac OS X platform.
114 // FIXME: The platform-independent code in this class should be factored out and merged with RenderThemeSafari.
116 // We estimate the animation rate of a Mac OS X progress bar is 33 fps.
117 // Hard code the value here because we haven't found API for it.
118 const double progressAnimationFrameRate = 0.033;
120 // Mac OS X progress bar animation seems to have 256 frames.
121 const double progressAnimationNumFrames = 256;
123 @interface WebCoreRenderThemeNotificationObserver : NSObject
125 WebCore::RenderTheme *_theme;
128 - (id)initWithTheme:(WebCore::RenderTheme *)theme;
129 - (void)systemColorsDidChange:(NSNotification *)notification;
133 @implementation WebCoreRenderThemeNotificationObserver
135 - (id)initWithTheme:(WebCore::RenderTheme *)theme
137 if (!(self = [super init]))
144 - (void)systemColorsDidChange:(NSNotification *)unusedNotification
146 ASSERT_UNUSED(unusedNotification, [[unusedNotification name] isEqualToString:NSSystemColorsDidChangeNotification]);
147 _theme->platformColorsDidChange();
152 @interface NSTextFieldCell (WKDetails)
153 - (CFDictionaryRef)_coreUIDrawOptionsWithFrame:(NSRect)cellFrame inView:(NSView *)controlView includeFocus:(BOOL)includeFocus;
157 @interface WebCoreTextFieldCell : NSTextFieldCell
158 - (CFDictionaryRef)_coreUIDrawOptionsWithFrame:(NSRect)cellFrame inView:(NSView *)controlView includeFocus:(BOOL)includeFocus;
161 @implementation WebCoreTextFieldCell
162 - (CFDictionaryRef)_coreUIDrawOptionsWithFrame:(NSRect)cellFrame inView:(NSView *)controlView includeFocus:(BOOL)includeFocus
164 // FIXME: This is a post-Lion-only workaround for <rdar://problem/11385461>. When that bug is resolved, we should remove this code.
165 CFMutableDictionaryRef coreUIDrawOptions = CFDictionaryCreateMutableCopy(NULL, 0, [super _coreUIDrawOptionsWithFrame:cellFrame inView:controlView includeFocus:includeFocus]);
166 CFDictionarySetValue(coreUIDrawOptions, @"borders only", kCFBooleanTrue);
167 return (CFDictionaryRef)[NSMakeCollectable(coreUIDrawOptions) autorelease];
171 @interface WebCoreRenderThemeBundle : NSObject
174 @implementation WebCoreRenderThemeBundle
177 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
178 @interface NSSearchFieldCell(Details)
179 @property (getter=isCenteredLook) BOOL centeredLook;
185 using namespace HTMLNames;
201 PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page*)
203 static RenderTheme* rt = RenderThemeMac::create().leakRef();
207 PassRefPtr<RenderTheme> RenderThemeMac::create()
209 return adoptRef(new RenderThemeMac);
212 RenderThemeMac::RenderThemeMac()
213 : m_isSliderThumbHorizontalPressed(false)
214 , m_isSliderThumbVerticalPressed(false)
215 , m_notificationObserver(adoptNS([[WebCoreRenderThemeNotificationObserver alloc] initWithTheme:this]))
217 [[NSNotificationCenter defaultCenter] addObserver:m_notificationObserver.get()
218 selector:@selector(systemColorsDidChange:)
219 name:NSSystemColorsDidChangeNotification
223 RenderThemeMac::~RenderThemeMac()
225 [[NSNotificationCenter defaultCenter] removeObserver:m_notificationObserver.get()];
228 NSView* RenderThemeMac::documentViewFor(const RenderObject& o) const
230 ControlStates states(extractControlStatesForRenderer(o));
231 return ThemeMac::ensuredView(&o.view().frameView(), &states);
235 String RenderThemeMac::mediaControlsStyleSheet()
237 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
238 if (m_mediaControlsStyleSheet.isEmpty())
239 m_mediaControlsStyleSheet = [NSString stringWithContentsOfFile:[[NSBundle bundleForClass:[WebCoreRenderThemeBundle class]] pathForResource:@"mediaControlsApple" ofType:@"css"] encoding:NSUTF8StringEncoding error:nil];
240 return m_mediaControlsStyleSheet;
242 return emptyString();
246 String RenderThemeMac::mediaControlsScript()
248 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
249 if (m_mediaControlsScript.isEmpty()) {
250 StringBuilder scriptBuilder;
251 scriptBuilder.append([NSString stringWithContentsOfFile:[[NSBundle bundleForClass:[WebCoreRenderThemeBundle class]] pathForResource:@"mediaControlsLocalizedStrings" ofType:@"js"] encoding:NSUTF8StringEncoding error:nil]);
252 scriptBuilder.append([NSString stringWithContentsOfFile:[[NSBundle bundleForClass:[WebCoreRenderThemeBundle class]] pathForResource:@"mediaControlsApple" ofType:@"js"] encoding:NSUTF8StringEncoding error:nil]);
253 m_mediaControlsScript = scriptBuilder.toString();
255 return m_mediaControlsScript;
257 return emptyString();
261 #endif // ENABLE(VIDEO)
264 #if ENABLE(SERVICE_CONTROLS)
265 String RenderThemeMac::imageControlsStyleSheet() const
267 return String(imageControlsMacUserAgentStyleSheet, sizeof(imageControlsMacUserAgentStyleSheet));
271 Color RenderThemeMac::platformActiveSelectionBackgroundColor() const
273 NSColor* color = [[NSColor selectedTextBackgroundColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace];
274 return Color(static_cast<int>(255.0 * [color redComponent]), static_cast<int>(255.0 * [color greenComponent]), static_cast<int>(255.0 * [color blueComponent]));
277 Color RenderThemeMac::platformInactiveSelectionBackgroundColor() const
279 NSColor* color = [[NSColor secondarySelectedControlColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace];
280 return Color(static_cast<int>(255.0 * [color redComponent]), static_cast<int>(255.0 * [color greenComponent]), static_cast<int>(255.0 * [color blueComponent]));
283 Color RenderThemeMac::platformActiveListBoxSelectionBackgroundColor() const
285 NSColor* color = [[NSColor alternateSelectedControlColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace];
286 return Color(static_cast<int>(255.0 * [color redComponent]), static_cast<int>(255.0 * [color greenComponent]), static_cast<int>(255.0 * [color blueComponent]));
289 Color RenderThemeMac::platformActiveListBoxSelectionForegroundColor() const
294 Color RenderThemeMac::platformInactiveListBoxSelectionForegroundColor() const
299 Color RenderThemeMac::platformFocusRingColor() const
301 if (usesTestModeFocusRingColor())
302 return oldAquaFocusRingColor();
304 return systemColor(CSSValueWebkitFocusRingColor);
307 int RenderThemeMac::platformFocusRingMaxWidth() const
309 // FIXME: Shouldn't this function be named platformFocusRingMinWidth? But also, I'm not sure if this function is needed - looks like
310 // all platforms just used 0 for this before <http://trac.webkit.org/changeset/168397>.
314 Color RenderThemeMac::platformInactiveListBoxSelectionBackgroundColor() const
316 return platformInactiveSelectionBackgroundColor();
319 static FontWeight toFontWeight(NSInteger appKitFontWeight)
321 ASSERT(appKitFontWeight > 0 && appKitFontWeight < 15);
322 if (appKitFontWeight > 14)
323 appKitFontWeight = 14;
324 else if (appKitFontWeight < 1)
325 appKitFontWeight = 1;
327 static FontWeight fontWeights[] = {
343 return fontWeights[appKitFontWeight - 1];
346 void RenderThemeMac::updateCachedSystemFontDescription(CSSValueID cssValueId, FontDescription& fontDescription) const
349 switch (cssValueId) {
350 case CSSValueSmallCaption:
351 font = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]];
354 font = [NSFont menuFontOfSize:[NSFont systemFontSize]];
356 case CSSValueStatusBar:
357 font = [NSFont labelFontOfSize:[NSFont labelFontSize]];
359 case CSSValueWebkitMiniControl:
360 font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSMiniControlSize]];
362 case CSSValueWebkitSmallControl:
363 font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]];
365 case CSSValueWebkitControl:
366 font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]];
369 font = [NSFont systemFontOfSize:[NSFont systemFontSize]];
375 NSFontManager *fontManager = [NSFontManager sharedFontManager];
376 fontDescription.setIsAbsoluteSize(true);
377 fontDescription.setOneFamily([font webCoreFamilyName]);
378 fontDescription.setSpecifiedSize([font pointSize]);
379 fontDescription.setWeight(toFontWeight([fontManager weightOfFont:font]));
380 fontDescription.setIsItalic([fontManager traitsOfFont:font] & NSItalicFontMask);
383 static RGBA32 convertNSColorToColor(NSColor *color)
385 NSColor *colorInColorSpace = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace];
386 if (colorInColorSpace) {
387 static const double scaleFactor = nextafter(256.0, 0.0);
388 return makeRGB(static_cast<int>(scaleFactor * [colorInColorSpace redComponent]),
389 static_cast<int>(scaleFactor * [colorInColorSpace greenComponent]),
390 static_cast<int>(scaleFactor * [colorInColorSpace blueComponent]));
393 // This conversion above can fail if the NSColor in question is an NSPatternColor
394 // (as many system colors are). These colors are actually a repeating pattern
395 // not just a solid color. To work around this we simply draw a 1x1 image of
396 // the color and use that pixel's color. It might be better to use an average of
397 // the colors in the pattern instead.
398 NSBitmapImageRep *offscreenRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil
405 colorSpaceName:NSDeviceRGBColorSpace
409 [NSGraphicsContext saveGraphicsState];
410 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:offscreenRep]];
411 NSEraseRect(NSMakeRect(0, 0, 1, 1));
412 [color drawSwatchInRect:NSMakeRect(0, 0, 1, 1)];
413 [NSGraphicsContext restoreGraphicsState];
416 [offscreenRep getPixel:pixel atX:0 y:0];
418 [offscreenRep release];
420 return makeRGB(pixel[0], pixel[1], pixel[2]);
423 static RGBA32 menuBackgroundColor()
425 NSBitmapImageRep *offscreenRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil
432 colorSpaceName:NSDeviceRGBColorSpace
436 CGContextRef context = static_cast<CGContextRef>([[NSGraphicsContext graphicsContextWithBitmapImageRep:offscreenRep] graphicsPort]);
437 CGRect rect = CGRectMake(0, 0, 1, 1);
438 HIThemeMenuDrawInfo drawInfo;
439 drawInfo.version = 0;
440 drawInfo.menuType = kThemeMenuTypePopUp;
441 HIThemeDrawMenuBackground(&rect, &drawInfo, context, kHIThemeOrientationInverted);
444 [offscreenRep getPixel:pixel atX:0 y:0];
446 [offscreenRep release];
448 return makeRGB(pixel[0], pixel[1], pixel[2]);
451 void RenderThemeMac::platformColorsDidChange()
453 m_systemColorCache.clear();
454 RenderTheme::platformColorsDidChange();
457 Color RenderThemeMac::systemColor(CSSValueID cssValueId) const
460 HashMap<int, RGBA32>::iterator it = m_systemColorCache.find(cssValueId);
461 if (it != m_systemColorCache.end())
466 switch (cssValueId) {
467 case CSSValueActiveborder:
468 color = convertNSColorToColor([NSColor keyboardFocusIndicatorColor]);
470 case CSSValueActivecaption:
471 color = convertNSColorToColor([NSColor windowFrameTextColor]);
473 case CSSValueAppworkspace:
474 color = convertNSColorToColor([NSColor headerColor]);
476 case CSSValueBackground:
477 // Use theme independent default
479 case CSSValueButtonface:
480 // We use this value instead of NSColor's controlColor to avoid website incompatibilities.
481 // We may want to change this to use the NSColor in future.
484 case CSSValueButtonhighlight:
485 color = convertNSColorToColor([NSColor controlHighlightColor]);
487 case CSSValueButtonshadow:
488 color = convertNSColorToColor([NSColor controlShadowColor]);
490 case CSSValueButtontext:
491 color = convertNSColorToColor([NSColor controlTextColor]);
493 case CSSValueActivebuttontext:
494 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
498 case CSSValueCaptiontext:
499 color = convertNSColorToColor([NSColor textColor]);
501 case CSSValueGraytext:
502 color = convertNSColorToColor([NSColor disabledControlTextColor]);
504 case CSSValueHighlight:
505 color = convertNSColorToColor([NSColor selectedTextBackgroundColor]);
507 case CSSValueHighlighttext:
508 color = convertNSColorToColor([NSColor selectedTextColor]);
510 case CSSValueInactiveborder:
511 color = convertNSColorToColor([NSColor controlBackgroundColor]);
513 case CSSValueInactivecaption:
514 color = convertNSColorToColor([NSColor controlBackgroundColor]);
516 case CSSValueInactivecaptiontext:
517 color = convertNSColorToColor([NSColor textColor]);
519 case CSSValueInfobackground:
520 // There is no corresponding NSColor for this so we use a hard coded value.
523 case CSSValueInfotext:
524 color = convertNSColorToColor([NSColor textColor]);
527 color = menuBackgroundColor();
529 case CSSValueMenutext:
530 color = convertNSColorToColor([NSColor selectedMenuItemTextColor]);
532 case CSSValueScrollbar:
533 color = convertNSColorToColor([NSColor scrollBarColor]);
536 color = convertNSColorToColor([NSColor textColor]);
538 case CSSValueThreeddarkshadow:
539 color = convertNSColorToColor([NSColor controlDarkShadowColor]);
541 case CSSValueThreedshadow:
542 color = convertNSColorToColor([NSColor shadowColor]);
544 case CSSValueThreedface:
545 // We use this value instead of NSColor's controlColor to avoid website incompatibilities.
546 // We may want to change this to use the NSColor in future.
549 case CSSValueThreedhighlight:
550 color = convertNSColorToColor([NSColor highlightColor]);
552 case CSSValueThreedlightshadow:
553 color = convertNSColorToColor([NSColor controlLightHighlightColor]);
555 case CSSValueWebkitFocusRingColor:
556 color = convertNSColorToColor([NSColor keyboardFocusIndicatorColor]);
559 color = convertNSColorToColor([NSColor windowBackgroundColor]);
561 case CSSValueWindowframe:
562 color = convertNSColorToColor([NSColor windowFrameColor]);
564 case CSSValueWindowtext:
565 color = convertNSColorToColor([NSColor windowFrameTextColor]);
571 if (!color.isValid())
572 color = RenderTheme::systemColor(cssValueId);
575 m_systemColorCache.set(cssValueId, color.rgb());
580 bool RenderThemeMac::usesTestModeFocusRingColor() const
582 return WebCore::usesTestModeFocusRingColor();
585 bool RenderThemeMac::isControlStyled(const RenderStyle& style, const BorderData& border,
586 const FillLayer& background, const Color& backgroundColor) const
588 if (style.appearance() == TextFieldPart || style.appearance() == TextAreaPart || style.appearance() == ListboxPart)
589 return style.border() != border;
591 // FIXME: This is horrible, but there is not much else that can be done. Menu lists cannot draw properly when
592 // scaled. They can't really draw properly when transformed either. We can't detect the transform case at style
593 // adjustment time so that will just have to stay broken. We can however detect that we're zooming. If zooming
594 // is in effect we treat it like the control is styled.
595 if (style.appearance() == MenulistPart && style.effectiveZoom() != 1.0f)
598 return RenderTheme::isControlStyled(style, border, background, backgroundColor);
601 static FloatRect inflateRect(const FloatRect& rect, const IntSize& size, const int* margins, float zoomLevel)
603 // Only do the inflation if the available width/height are too small. Otherwise try to
604 // fit the glow/check space into the available box's width/height.
605 int widthDelta = rect.width() - (size.width() + margins[leftMargin] * zoomLevel + margins[rightMargin] * zoomLevel);
606 int heightDelta = rect.height() - (size.height() + margins[topMargin] * zoomLevel + margins[bottomMargin] * zoomLevel);
607 FloatRect result(rect);
608 if (widthDelta < 0) {
609 result.setX(result.x() - margins[leftMargin] * zoomLevel);
610 result.setWidth(result.width() - widthDelta);
612 if (heightDelta < 0) {
613 result.setY(result.y() - margins[topMargin] * zoomLevel);
614 result.setHeight(result.height() - heightDelta);
619 void RenderThemeMac::adjustRepaintRect(const RenderObject& renderer, FloatRect& rect)
621 ControlPart part = renderer.style().appearance();
628 case SquareButtonPart:
629 case DefaultButtonPart:
631 case InnerSpinButtonPart:
632 return RenderTheme::adjustRepaintRect(renderer, rect);
638 float zoomLevel = renderer.style().effectiveZoom();
640 if (part == MenulistPart) {
641 setPopupButtonCellState(renderer, IntSize(rect.size()));
642 IntSize size = popupButtonSizes()[[popupButton() controlSize]];
643 size.setHeight(size.height() * zoomLevel);
644 size.setWidth(rect.width());
645 rect = inflateRect(rect, size, popupButtonMargins(), zoomLevel);
649 FloatRect RenderThemeMac::convertToPaintingRect(const RenderObject& inputRenderer, const RenderObject& partRenderer, const FloatRect& inputRect, const IntRect& r) const
651 FloatRect partRect(inputRect);
653 // Compute an offset between the part renderer and the input renderer
654 FloatSize offsetFromInputRenderer;
655 const RenderObject* renderer = &partRenderer;
656 while (renderer && renderer != &inputRenderer) {
657 RenderElement* containingRenderer = renderer->container();
658 ASSERT(containingRenderer);
659 offsetFromInputRenderer -= roundedIntSize(renderer->offsetFromContainer(*containingRenderer, LayoutPoint()));
660 renderer = containingRenderer;
662 // If the input renderer was not a container, something went wrong
663 ASSERT(renderer == &inputRenderer);
664 // Move the rect into partRenderer's coords
665 partRect.move(offsetFromInputRenderer);
666 // Account for the local drawing offset (tx, ty)
667 partRect.move(r.x(), r.y());
672 void RenderThemeMac::updateCheckedState(NSCell* cell, const RenderObject& o)
674 bool oldIndeterminate = [cell state] == NSMixedState;
675 bool indeterminate = isIndeterminate(o);
676 bool checked = isChecked(o);
678 if (oldIndeterminate != indeterminate) {
679 [cell setState:indeterminate ? NSMixedState : (checked ? NSOnState : NSOffState)];
683 bool oldChecked = [cell state] == NSOnState;
684 if (checked != oldChecked)
685 [cell setState:checked ? NSOnState : NSOffState];
688 void RenderThemeMac::updateEnabledState(NSCell* cell, const RenderObject& o)
690 bool oldEnabled = [cell isEnabled];
691 bool enabled = isEnabled(o);
692 if (enabled != oldEnabled)
693 [cell setEnabled:enabled];
696 void RenderThemeMac::updateFocusedState(NSCell* cell, const RenderObject& o)
698 bool oldFocused = [cell showsFirstResponder];
699 bool focused = isFocused(o) && o.style().outlineStyleIsAuto();
700 if (focused != oldFocused)
701 [cell setShowsFirstResponder:focused];
704 void RenderThemeMac::updatePressedState(NSCell* cell, const RenderObject& o)
706 bool oldPressed = [cell isHighlighted];
707 bool pressed = is<Element>(o.node()) && downcast<Element>(*o.node()).active();
708 if (pressed != oldPressed)
709 [cell setHighlighted:pressed];
712 bool RenderThemeMac::controlSupportsTints(const RenderObject& o) const
714 // An alternate way to implement this would be to get the appropriate cell object
715 // and call the private _needRedrawOnWindowChangedKeyState method. An advantage of
716 // that would be that we would match AppKit behavior more closely, but a disadvantage
717 // would be that we would rely on an AppKit SPI method.
722 // Checkboxes only have tint when checked.
723 if (o.style().appearance() == CheckboxPart)
726 // For now assume other controls have tint if enabled.
730 NSControlSize RenderThemeMac::controlSizeForFont(RenderStyle& style) const
732 int fontSize = style.fontSize();
734 return NSRegularControlSize;
736 return NSSmallControlSize;
737 return NSMiniControlSize;
740 NSControlSize RenderThemeMac::controlSizeForCell(NSCell*, const IntSize* sizes, const IntSize& minSize, float zoomLevel) const
742 if (minSize.width() >= static_cast<int>(sizes[NSRegularControlSize].width() * zoomLevel)
743 && minSize.height() >= static_cast<int>(sizes[NSRegularControlSize].height() * zoomLevel))
744 return NSRegularControlSize;
746 if (minSize.width() >= static_cast<int>(sizes[NSSmallControlSize].width() * zoomLevel)
747 && minSize.height() >= static_cast<int>(sizes[NSSmallControlSize].height() * zoomLevel))
748 return NSSmallControlSize;
750 return NSMiniControlSize;
753 void RenderThemeMac::setControlSize(NSCell* cell, const IntSize* sizes, const IntSize& minSize, float zoomLevel)
755 NSControlSize size = controlSizeForCell(cell, sizes, minSize, zoomLevel);
756 if (size != [cell controlSize]) // Only update if we have to, since AppKit does work even if the size is the same.
757 [cell setControlSize:size];
760 IntSize RenderThemeMac::sizeForFont(RenderStyle& style, const IntSize* sizes) const
762 if (style.effectiveZoom() != 1.0f) {
763 IntSize result = sizes[controlSizeForFont(style)];
764 return IntSize(result.width() * style.effectiveZoom(), result.height() * style.effectiveZoom());
766 return sizes[controlSizeForFont(style)];
769 IntSize RenderThemeMac::sizeForSystemFont(RenderStyle& style, const IntSize* sizes) const
771 if (style.effectiveZoom() != 1.0f) {
772 IntSize result = sizes[controlSizeForSystemFont(style)];
773 return IntSize(result.width() * style.effectiveZoom(), result.height() * style.effectiveZoom());
775 return sizes[controlSizeForSystemFont(style)];
778 void RenderThemeMac::setSizeFromFont(RenderStyle& style, const IntSize* sizes) const
780 // FIXME: Check is flawed, since it doesn't take min-width/max-width into account.
781 IntSize size = sizeForFont(style, sizes);
782 if (style.width().isIntrinsicOrAuto() && size.width() > 0)
783 style.setWidth(Length(size.width(), Fixed));
784 if (style.height().isAuto() && size.height() > 0)
785 style.setHeight(Length(size.height(), Fixed));
788 void RenderThemeMac::setFontFromControlSize(StyleResolver&, RenderStyle& style, NSControlSize controlSize) const
790 FontDescription fontDescription;
791 fontDescription.setIsAbsoluteSize(true);
793 NSFont* font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:controlSize]];
794 fontDescription.setOneFamily([font webCoreFamilyName]);
795 fontDescription.setComputedSize([font pointSize] * style.effectiveZoom());
796 fontDescription.setSpecifiedSize([font pointSize] * style.effectiveZoom());
799 style.setLineHeight(RenderStyle::initialLineHeight());
801 if (style.setFontDescription(fontDescription))
802 style.fontCascade().update(0);
805 NSControlSize RenderThemeMac::controlSizeForSystemFont(RenderStyle& style) const
807 int fontSize = style.fontSize();
808 if (fontSize >= [NSFont systemFontSizeForControlSize:NSRegularControlSize])
809 return NSRegularControlSize;
810 if (fontSize >= [NSFont systemFontSizeForControlSize:NSSmallControlSize])
811 return NSSmallControlSize;
812 return NSMiniControlSize;
815 bool RenderThemeMac::paintTextField(const RenderObject& o, const PaintInfo& paintInfo, const FloatRect& r)
817 LocalCurrentGraphicsContext localContext(paintInfo.context);
819 NSTextFieldCell *textField = this->textField();
821 GraphicsContextStateSaver stateSaver(*paintInfo.context);
823 [textField setEnabled:(isEnabled(o) && !isReadOnlyControl(o))];
824 [textField drawWithFrame:NSRect(r) inView:documentViewFor(o)];
826 [textField setControlView:nil];
831 void RenderThemeMac::adjustTextFieldStyle(StyleResolver&, RenderStyle&, Element*) const
835 bool RenderThemeMac::paintTextArea(const RenderObject& o, const PaintInfo& paintInfo, const FloatRect& r)
837 LocalCurrentGraphicsContext localContext(paintInfo.context);
838 wkDrawBezeledTextArea(r, isEnabled(o) && !isReadOnlyControl(o));
842 void RenderThemeMac::adjustTextAreaStyle(StyleResolver&, RenderStyle&, Element*) const
846 const int* RenderThemeMac::popupButtonMargins() const
848 static const int margins[3][4] =
854 return margins[[popupButton() controlSize]];
857 const IntSize* RenderThemeMac::popupButtonSizes() const
859 static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) };
863 const int* RenderThemeMac::popupButtonPadding(NSControlSize size) const
865 static const int padding[3][4] =
871 return padding[size];
874 bool RenderThemeMac::paintMenuList(const RenderObject& renderer, const PaintInfo& paintInfo, const FloatRect& rect)
876 LocalCurrentGraphicsContext localContext(paintInfo.context);
877 setPopupButtonCellState(renderer, IntSize(rect.size()));
879 NSPopUpButtonCell* popupButton = this->popupButton();
881 float zoomLevel = renderer.style().effectiveZoom();
882 IntSize size = popupButtonSizes()[[popupButton controlSize]];
883 size.setHeight(size.height() * zoomLevel);
884 size.setWidth(rect.width());
886 // Now inflate it to account for the shadow.
887 FloatRect inflatedRect = rect;
888 if (rect.width() >= minimumMenuListSize(renderer.style()))
889 inflatedRect = inflateRect(rect, size, popupButtonMargins(), zoomLevel);
891 GraphicsContextStateSaver stateSaver(*paintInfo.context);
893 // On Leopard, the cell will draw outside of the given rect, so we have to clip to the rect
894 paintInfo.context->clip(inflatedRect);
896 if (zoomLevel != 1.0f) {
897 inflatedRect.setWidth(inflatedRect.width() / zoomLevel);
898 inflatedRect.setHeight(inflatedRect.height() / zoomLevel);
899 paintInfo.context->translate(inflatedRect.x(), inflatedRect.y());
900 paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel));
901 paintInfo.context->translate(-inflatedRect.x(), -inflatedRect.y());
904 NSView *view = documentViewFor(renderer);
905 [popupButton drawWithFrame:inflatedRect inView:view];
906 if (isFocused(renderer) && renderer.style().outlineStyleIsAuto()) {
907 if (wkDrawCellFocusRingWithFrameAtTime(popupButton, inflatedRect, view, std::numeric_limits<double>::max()))
908 renderer.document().page()->focusController().setFocusedElementNeedsRepaint();
911 [popupButton setControlView:nil];
916 #if ENABLE(METER_ELEMENT)
918 IntSize RenderThemeMac::meterSizeForBounds(const RenderMeter& renderMeter, const IntRect& bounds) const
920 if (NoControlPart == renderMeter.style().appearance())
921 return bounds.size();
923 NSLevelIndicatorCell* cell = levelIndicatorFor(renderMeter);
924 // Makes enough room for cell's intrinsic size.
925 NSSize cellSize = [cell cellSizeForBounds:IntRect(IntPoint(), bounds.size())];
926 return IntSize(bounds.width() < cellSize.width ? cellSize.width : bounds.width(),
927 bounds.height() < cellSize.height ? cellSize.height : bounds.height());
930 bool RenderThemeMac::paintMeter(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
932 if (!is<RenderMeter>(renderObject))
935 LocalCurrentGraphicsContext localContext(paintInfo.context);
937 NSLevelIndicatorCell* cell = levelIndicatorFor(downcast<RenderMeter>(renderObject));
938 GraphicsContextStateSaver stateSaver(*paintInfo.context);
940 [cell drawWithFrame:rect inView:documentViewFor(renderObject)];
941 [cell setControlView:nil];
945 bool RenderThemeMac::supportsMeter(ControlPart part) const
948 case RelevancyLevelIndicatorPart:
949 case DiscreteCapacityLevelIndicatorPart:
950 case RatingLevelIndicatorPart:
952 case ContinuousCapacityLevelIndicatorPart:
959 NSLevelIndicatorStyle RenderThemeMac::levelIndicatorStyleFor(ControlPart part) const
962 case RelevancyLevelIndicatorPart:
963 return NSRelevancyLevelIndicatorStyle;
964 case DiscreteCapacityLevelIndicatorPart:
965 return NSDiscreteCapacityLevelIndicatorStyle;
966 case RatingLevelIndicatorPart:
967 return NSRatingLevelIndicatorStyle;
969 case ContinuousCapacityLevelIndicatorPart:
971 return NSContinuousCapacityLevelIndicatorStyle;
976 NSLevelIndicatorCell* RenderThemeMac::levelIndicatorFor(const RenderMeter& renderMeter) const
978 const RenderStyle& style = renderMeter.style();
979 ASSERT(style.appearance() != NoControlPart);
981 if (!m_levelIndicator)
982 m_levelIndicator = adoptNS([[NSLevelIndicatorCell alloc] initWithLevelIndicatorStyle:NSContinuousCapacityLevelIndicatorStyle]);
983 NSLevelIndicatorCell* cell = m_levelIndicator.get();
985 HTMLMeterElement* element = renderMeter.meterElement();
986 double value = element->value();
988 // Because NSLevelIndicatorCell does not support optimum-in-the-middle type coloring,
989 // we explicitly control the color instead giving low and high value to NSLevelIndicatorCell as is.
990 switch (element->gaugeRegion()) {
991 case HTMLMeterElement::GaugeRegionOptimum:
992 // Make meter the green
993 [cell setWarningValue:value + 1];
994 [cell setCriticalValue:value + 2];
996 case HTMLMeterElement::GaugeRegionSuboptimal:
997 // Make the meter yellow
998 [cell setWarningValue:value - 1];
999 [cell setCriticalValue:value + 1];
1001 case HTMLMeterElement::GaugeRegionEvenLessGood:
1002 // Make the meter red
1003 [cell setWarningValue:value - 2];
1004 [cell setCriticalValue:value - 1];
1008 [cell setLevelIndicatorStyle:levelIndicatorStyleFor(style.appearance())];
1009 [cell setBaseWritingDirection:style.isLeftToRightDirection() ? NSWritingDirectionLeftToRight : NSWritingDirectionRightToLeft];
1010 [cell setMinValue:element->min()];
1011 [cell setMaxValue:element->max()];
1012 RetainPtr<NSNumber> valueObject = [NSNumber numberWithDouble:value];
1013 [cell setObjectValue:valueObject.get()];
1020 const IntSize* RenderThemeMac::progressBarSizes() const
1022 static const IntSize sizes[3] = { IntSize(0, 20), IntSize(0, 12), IntSize(0, 12) };
1026 const int* RenderThemeMac::progressBarMargins(NSControlSize controlSize) const
1028 static const int margins[3][4] =
1034 return margins[controlSize];
1037 IntRect RenderThemeMac::progressBarRectForBounds(const RenderObject& renderObject, const IntRect& bounds) const
1039 // Workaround until <rdar://problem/15855086> is fixed.
1040 int maxDimension = static_cast<int>(std::numeric_limits<ushort>::max());
1041 IntRect progressBarBounds(bounds.x(), bounds.y(), std::min(bounds.width(), maxDimension), std::min(bounds.height(), maxDimension));
1042 if (NoControlPart == renderObject.style().appearance())
1043 return progressBarBounds;
1045 float zoomLevel = renderObject.style().effectiveZoom();
1046 NSControlSize controlSize = controlSizeForFont(renderObject.style());
1047 IntSize size = progressBarSizes()[controlSize];
1048 size.setHeight(size.height() * zoomLevel);
1049 size.setWidth(progressBarBounds.width());
1051 // Now inflate it to account for the shadow.
1052 IntRect inflatedRect = progressBarBounds;
1053 if (progressBarBounds.height() <= minimumProgressBarHeight(renderObject.style()))
1054 inflatedRect = IntRect(inflateRect(inflatedRect, size, progressBarMargins(controlSize), zoomLevel));
1056 return inflatedRect;
1059 int RenderThemeMac::minimumProgressBarHeight(RenderStyle& style) const
1061 return sizeForSystemFont(style, progressBarSizes()).height();
1064 double RenderThemeMac::animationRepeatIntervalForProgressBar(RenderProgress&) const
1066 return progressAnimationFrameRate;
1069 double RenderThemeMac::animationDurationForProgressBar(RenderProgress&) const
1071 return progressAnimationNumFrames * progressAnimationFrameRate;
1074 void RenderThemeMac::adjustProgressBarStyle(StyleResolver&, RenderStyle&, Element*) const
1078 bool RenderThemeMac::paintProgressBar(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1080 if (!is<RenderProgress>(renderObject))
1083 IntRect inflatedRect = progressBarRectForBounds(renderObject, rect);
1084 NSControlSize controlSize = controlSizeForFont(renderObject.style());
1086 const auto& renderProgress = downcast<RenderProgress>(renderObject);
1087 HIThemeTrackDrawInfo trackInfo;
1088 trackInfo.version = 0;
1089 if (controlSize == NSRegularControlSize)
1090 trackInfo.kind = renderProgress.position() < 0 ? kThemeLargeIndeterminateBar : kThemeLargeProgressBar;
1092 trackInfo.kind = renderProgress.position() < 0 ? kThemeMediumIndeterminateBar : kThemeMediumProgressBar;
1094 float deviceScaleFactor = renderObject.document().deviceScaleFactor();
1095 trackInfo.bounds = IntRect(IntPoint(), inflatedRect.size());
1097 trackInfo.max = std::numeric_limits<SInt32>::max();
1098 trackInfo.value = lround(renderProgress.position() * nextafter(trackInfo.max, 0));
1099 trackInfo.trackInfo.progress.phase = lround(renderProgress.animationProgress() * nextafter(progressAnimationNumFrames, 0) * deviceScaleFactor);
1100 trackInfo.attributes = kThemeTrackHorizontal;
1101 trackInfo.enableState = isActive(renderObject) ? kThemeTrackActive : kThemeTrackInactive;
1102 trackInfo.reserved = 0;
1103 trackInfo.filler1 = 0;
1105 std::unique_ptr<ImageBuffer> imageBuffer = ImageBuffer::createCompatibleBuffer(inflatedRect.size(), deviceScaleFactor, ColorSpaceDeviceRGB, paintInfo.context, true);
1109 ContextContainer cgContextContainer(imageBuffer->context());
1110 CGContextRef cgContext = cgContextContainer.context();
1111 HIThemeDrawTrack(&trackInfo, 0, cgContext, kHIThemeOrientationNormal);
1113 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1115 if (!renderProgress.style().isLeftToRightDirection()) {
1116 paintInfo.context->translate(2 * inflatedRect.x() + inflatedRect.width(), 0);
1117 paintInfo.context->scale(FloatSize(-1, 1));
1120 paintInfo.context->drawImageBuffer(imageBuffer.get(), ColorSpaceDeviceRGB, inflatedRect.location());
1124 const float baseFontSize = 11.0f;
1125 const float baseArrowHeight = 4.0f;
1126 const float baseArrowWidth = 5.0f;
1127 const float baseSpaceBetweenArrows = 2.0f;
1128 const int arrowPaddingLeft = 6;
1129 const int arrowPaddingRight = 6;
1130 const int paddingBeforeSeparator = 4;
1131 const int baseBorderRadius = 5;
1132 const int styledPopupPaddingLeft = 8;
1133 const int styledPopupPaddingTop = 1;
1134 const int styledPopupPaddingBottom = 2;
1136 static void TopGradientInterpolate(void*, const CGFloat* inData, CGFloat* outData)
1138 static float dark[4] = { 1.0f, 1.0f, 1.0f, 0.4f };
1139 static float light[4] = { 1.0f, 1.0f, 1.0f, 0.15f };
1140 float a = inData[0];
1142 for (i = 0; i < 4; i++)
1143 outData[i] = (1.0f - a) * dark[i] + a * light[i];
1146 static void BottomGradientInterpolate(void*, const CGFloat* inData, CGFloat* outData)
1148 static float dark[4] = { 1.0f, 1.0f, 1.0f, 0.0f };
1149 static float light[4] = { 1.0f, 1.0f, 1.0f, 0.3f };
1150 float a = inData[0];
1152 for (i = 0; i < 4; i++)
1153 outData[i] = (1.0f - a) * dark[i] + a * light[i];
1156 static void MainGradientInterpolate(void*, const CGFloat* inData, CGFloat* outData)
1158 static float dark[4] = { 0.0f, 0.0f, 0.0f, 0.15f };
1159 static float light[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
1160 float a = inData[0];
1162 for (i = 0; i < 4; i++)
1163 outData[i] = (1.0f - a) * dark[i] + a * light[i];
1166 static void TrackGradientInterpolate(void*, const CGFloat* inData, CGFloat* outData)
1168 static float dark[4] = { 0.0f, 0.0f, 0.0f, 0.678f };
1169 static float light[4] = { 0.0f, 0.0f, 0.0f, 0.13f };
1170 float a = inData[0];
1172 for (i = 0; i < 4; i++)
1173 outData[i] = (1.0f - a) * dark[i] + a * light[i];
1176 void RenderThemeMac::paintMenuListButtonGradients(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1181 ContextContainer cgContextContainer(paintInfo.context);
1182 CGContextRef context = cgContextContainer.context();
1184 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1186 FloatRoundedRect border = FloatRoundedRect(o.style().getRoundedBorderFor(r));
1187 int radius = border.radii().topLeft().width();
1189 CGColorSpaceRef cspace = deviceRGBColorSpaceRef();
1191 FloatRect topGradient(r.x(), r.y(), r.width(), r.height() / 2.0f);
1192 struct CGFunctionCallbacks topCallbacks = { 0, TopGradientInterpolate, NULL };
1193 RetainPtr<CGFunctionRef> topFunction = adoptCF(CGFunctionCreate(NULL, 1, NULL, 4, NULL, &topCallbacks));
1194 RetainPtr<CGShadingRef> topShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(topGradient.x(), topGradient.y()), CGPointMake(topGradient.x(), topGradient.maxY()), topFunction.get(), false, false));
1196 FloatRect bottomGradient(r.x() + radius, r.y() + r.height() / 2.0f, r.width() - 2.0f * radius, r.height() / 2.0f);
1197 struct CGFunctionCallbacks bottomCallbacks = { 0, BottomGradientInterpolate, NULL };
1198 RetainPtr<CGFunctionRef> bottomFunction = adoptCF(CGFunctionCreate(NULL, 1, NULL, 4, NULL, &bottomCallbacks));
1199 RetainPtr<CGShadingRef> bottomShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(bottomGradient.x(), bottomGradient.y()), CGPointMake(bottomGradient.x(), bottomGradient.maxY()), bottomFunction.get(), false, false));
1201 struct CGFunctionCallbacks mainCallbacks = { 0, MainGradientInterpolate, NULL };
1202 RetainPtr<CGFunctionRef> mainFunction = adoptCF(CGFunctionCreate(NULL, 1, NULL, 4, NULL, &mainCallbacks));
1203 RetainPtr<CGShadingRef> mainShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(r.x(), r.y()), CGPointMake(r.x(), r.maxY()), mainFunction.get(), false, false));
1205 RetainPtr<CGShadingRef> leftShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(r.x(), r.y()), CGPointMake(r.x() + radius, r.y()), mainFunction.get(), false, false));
1207 RetainPtr<CGShadingRef> rightShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(r.maxX(), r.y()), CGPointMake(r.maxX() - radius, r.y()), mainFunction.get(), false, false));
1210 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1211 CGContextClipToRect(context, r);
1212 paintInfo.context->clipRoundedRect(border);
1213 context = cgContextContainer.context();
1214 CGContextDrawShading(context, mainShading.get());
1218 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1219 CGContextClipToRect(context, topGradient);
1220 paintInfo.context->clipRoundedRect(FloatRoundedRect(enclosingIntRect(topGradient), border.radii().topLeft(), border.radii().topRight(), IntSize(), IntSize()));
1221 context = cgContextContainer.context();
1222 CGContextDrawShading(context, topShading.get());
1225 if (!bottomGradient.isEmpty()) {
1226 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1227 CGContextClipToRect(context, bottomGradient);
1228 paintInfo.context->clipRoundedRect(FloatRoundedRect(enclosingIntRect(bottomGradient), IntSize(), IntSize(), border.radii().bottomLeft(), border.radii().bottomRight()));
1229 context = cgContextContainer.context();
1230 CGContextDrawShading(context, bottomShading.get());
1234 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1235 CGContextClipToRect(context, r);
1236 paintInfo.context->clipRoundedRect(border);
1237 context = cgContextContainer.context();
1238 CGContextDrawShading(context, leftShading.get());
1239 CGContextDrawShading(context, rightShading.get());
1243 bool RenderThemeMac::paintMenuListButtonDecorations(const RenderObject& renderer, const PaintInfo& paintInfo, const FloatRect& rect)
1245 IntRect bounds = IntRect(rect.x() + renderer.style().borderLeftWidth(),
1246 rect.y() + renderer.style().borderTopWidth(),
1247 rect.width() - renderer.style().borderLeftWidth() - renderer.style().borderRightWidth(),
1248 rect.height() - renderer.style().borderTopWidth() - renderer.style().borderBottomWidth());
1249 // Draw the gradients to give the styled popup menu a button appearance
1250 paintMenuListButtonGradients(renderer, paintInfo, bounds);
1252 // Since we actually know the size of the control here, we restrict the font scale to make sure the arrows will fit vertically in the bounds
1253 float fontScale = std::min(renderer.style().fontSize() / baseFontSize, bounds.height() / (baseArrowHeight * 2 + baseSpaceBetweenArrows));
1254 float centerY = bounds.y() + bounds.height() / 2.0f;
1255 float arrowHeight = baseArrowHeight * fontScale;
1256 float arrowWidth = baseArrowWidth * fontScale;
1257 float leftEdge = bounds.maxX() - arrowPaddingRight * renderer.style().effectiveZoom() - arrowWidth;
1258 float spaceBetweenArrows = baseSpaceBetweenArrows * fontScale;
1260 if (bounds.width() < arrowWidth + arrowPaddingLeft * renderer.style().effectiveZoom())
1263 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1265 paintInfo.context->setFillColor(renderer.style().visitedDependentColor(CSSPropertyColor), renderer.style().colorSpace());
1266 paintInfo.context->setStrokeStyle(NoStroke);
1268 FloatPoint arrow1[3];
1269 arrow1[0] = FloatPoint(leftEdge, centerY - spaceBetweenArrows / 2.0f);
1270 arrow1[1] = FloatPoint(leftEdge + arrowWidth, centerY - spaceBetweenArrows / 2.0f);
1271 arrow1[2] = FloatPoint(leftEdge + arrowWidth / 2.0f, centerY - spaceBetweenArrows / 2.0f - arrowHeight);
1273 // Draw the top arrow
1274 paintInfo.context->drawConvexPolygon(3, arrow1, true);
1276 FloatPoint arrow2[3];
1277 arrow2[0] = FloatPoint(leftEdge, centerY + spaceBetweenArrows / 2.0f);
1278 arrow2[1] = FloatPoint(leftEdge + arrowWidth, centerY + spaceBetweenArrows / 2.0f);
1279 arrow2[2] = FloatPoint(leftEdge + arrowWidth / 2.0f, centerY + spaceBetweenArrows / 2.0f + arrowHeight);
1281 // Draw the bottom arrow
1282 paintInfo.context->drawConvexPolygon(3, arrow2, true);
1284 Color leftSeparatorColor(0, 0, 0, 40);
1285 Color rightSeparatorColor(255, 255, 255, 40);
1287 // FIXME: Should the separator thickness and space be scaled up by fontScale?
1288 int separatorSpace = 2; // Deliberately ignores zoom since it looks nicer if it stays thin.
1289 int leftEdgeOfSeparator = static_cast<int>(leftEdge - arrowPaddingLeft * renderer.style().effectiveZoom()); // FIXME: Round?
1291 // Draw the separator to the left of the arrows
1292 paintInfo.context->setStrokeThickness(1); // Deliberately ignores zoom since it looks nicer if it stays thin.
1293 paintInfo.context->setStrokeStyle(SolidStroke);
1294 paintInfo.context->setStrokeColor(leftSeparatorColor, ColorSpaceDeviceRGB);
1295 paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator, bounds.y()),
1296 IntPoint(leftEdgeOfSeparator, bounds.maxY()));
1298 paintInfo.context->setStrokeColor(rightSeparatorColor, ColorSpaceDeviceRGB);
1299 paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.y()),
1300 IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.maxY()));
1304 static const IntSize* menuListButtonSizes()
1306 static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) };
1310 void RenderThemeMac::adjustMenuListStyle(StyleResolver& styleResolver, RenderStyle& style, Element* e) const
1312 NSControlSize controlSize = controlSizeForFont(style);
1314 style.resetBorder();
1315 style.resetPadding();
1317 // Height is locked to auto.
1318 style.setHeight(Length(Auto));
1320 // White-space is locked to pre
1321 style.setWhiteSpace(PRE);
1323 // Set the foreground color to black or gray when we have the aqua look.
1324 // Cast to RGB32 is to work around a compiler bug.
1325 style.setColor(e && !e->isDisabledFormControl() ? static_cast<RGBA32>(Color::black) : Color::darkGray);
1327 // Set the button's vertical size.
1328 setSizeFromFont(style, menuListButtonSizes());
1330 // Our font is locked to the appropriate system font size for the control. To clarify, we first use the CSS-specified font to figure out
1331 // a reasonable control size, but once that control size is determined, we throw that font away and use the appropriate
1332 // system font for the control size instead.
1333 setFontFromControlSize(styleResolver, style, controlSize);
1335 style.setBoxShadow(nullptr);
1338 int RenderThemeMac::popupInternalPaddingLeft(RenderStyle& style) const
1340 if (style.appearance() == MenulistPart)
1341 return popupButtonPadding(controlSizeForFont(style))[leftPadding] * style.effectiveZoom();
1342 if (style.appearance() == MenulistButtonPart)
1343 return styledPopupPaddingLeft * style.effectiveZoom();
1347 int RenderThemeMac::popupInternalPaddingRight(RenderStyle& style) const
1349 if (style.appearance() == MenulistPart)
1350 return popupButtonPadding(controlSizeForFont(style))[rightPadding] * style.effectiveZoom();
1351 if (style.appearance() == MenulistButtonPart) {
1352 float fontScale = style.fontSize() / baseFontSize;
1353 float arrowWidth = baseArrowWidth * fontScale;
1354 return static_cast<int>(ceilf(arrowWidth + (arrowPaddingLeft + arrowPaddingRight + paddingBeforeSeparator) * style.effectiveZoom()));
1359 int RenderThemeMac::popupInternalPaddingTop(RenderStyle& style) const
1361 if (style.appearance() == MenulistPart)
1362 return popupButtonPadding(controlSizeForFont(style))[topPadding] * style.effectiveZoom();
1363 if (style.appearance() == MenulistButtonPart)
1364 return styledPopupPaddingTop * style.effectiveZoom();
1368 int RenderThemeMac::popupInternalPaddingBottom(RenderStyle& style) const
1370 if (style.appearance() == MenulistPart)
1371 return popupButtonPadding(controlSizeForFont(style))[bottomPadding] * style.effectiveZoom();
1372 if (style.appearance() == MenulistButtonPart)
1373 return styledPopupPaddingBottom * style.effectiveZoom();
1377 PopupMenuStyle::PopupMenuSize RenderThemeMac::popupMenuSize(const RenderStyle& style, IntRect& rect) const
1379 NSPopUpButtonCell* popupButton = this->popupButton();
1380 NSControlSize size = controlSizeForCell(popupButton, popupButtonSizes(), rect.size(), style.effectiveZoom());
1382 case NSRegularControlSize:
1383 return PopupMenuStyle::PopupMenuSizeNormal;
1384 case NSSmallControlSize:
1385 return PopupMenuStyle::PopupMenuSizeSmall;
1386 case NSMiniControlSize:
1387 return PopupMenuStyle::PopupMenuSizeMini;
1389 return PopupMenuStyle::PopupMenuSizeNormal;
1393 void RenderThemeMac::adjustMenuListButtonStyle(StyleResolver&, RenderStyle& style, Element*) const
1395 float fontScale = style.fontSize() / baseFontSize;
1397 style.resetPadding();
1398 style.setBorderRadius(IntSize(int(baseBorderRadius + fontScale - 1), int(baseBorderRadius + fontScale - 1))); // FIXME: Round up?
1400 const int minHeight = 15;
1401 style.setMinHeight(Length(minHeight, Fixed));
1403 style.setLineHeight(RenderStyle::initialLineHeight());
1406 void RenderThemeMac::setPopupButtonCellState(const RenderObject& o, const IntSize& buttonSize)
1408 NSPopUpButtonCell* popupButton = this->popupButton();
1410 // Set the control size based off the rectangle we're painting into.
1411 setControlSize(popupButton, popupButtonSizes(), buttonSize, o.style().effectiveZoom());
1413 // Update the various states we respond to.
1414 updateCheckedState(popupButton, o);
1415 updateEnabledState(popupButton, o);
1416 updatePressedState(popupButton, o);
1419 const IntSize* RenderThemeMac::menuListSizes() const
1421 static const IntSize sizes[3] = { IntSize(9, 0), IntSize(5, 0), IntSize(0, 0) };
1425 int RenderThemeMac::minimumMenuListSize(RenderStyle& style) const
1427 return sizeForSystemFont(style, menuListSizes()).width();
1430 const int trackWidth = 5;
1431 const int trackRadius = 2;
1433 void RenderThemeMac::adjustSliderTrackStyle(StyleResolver&, RenderStyle& style, Element*) const
1435 style.setBoxShadow(nullptr);
1438 bool RenderThemeMac::paintSliderTrack(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1441 float zoomLevel = o.style().effectiveZoom();
1442 float zoomedTrackWidth = trackWidth * zoomLevel;
1444 if (o.style().appearance() == SliderHorizontalPart || o.style().appearance() == MediaSliderPart) {
1445 bounds.setHeight(zoomedTrackWidth);
1446 bounds.setY(r.y() + r.height() / 2 - zoomedTrackWidth / 2);
1447 } else if (o.style().appearance() == SliderVerticalPart) {
1448 bounds.setWidth(zoomedTrackWidth);
1449 bounds.setX(r.x() + r.width() / 2 - zoomedTrackWidth / 2);
1452 LocalCurrentGraphicsContext localContext(paintInfo.context);
1453 CGContextRef context = localContext.cgContext();
1454 CGColorSpaceRef cspace = deviceRGBColorSpaceRef();
1456 #if ENABLE(DATALIST_ELEMENT)
1457 paintSliderTicks(o, paintInfo, r);
1460 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1461 CGContextClipToRect(context, bounds);
1463 struct CGFunctionCallbacks mainCallbacks = { 0, TrackGradientInterpolate, NULL };
1464 RetainPtr<CGFunctionRef> mainFunction = adoptCF(CGFunctionCreate(NULL, 1, NULL, 4, NULL, &mainCallbacks));
1465 RetainPtr<CGShadingRef> mainShading;
1466 if (o.style().appearance() == SliderVerticalPart)
1467 mainShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(bounds.x(), bounds.maxY()), CGPointMake(bounds.maxX(), bounds.maxY()), mainFunction.get(), false, false));
1469 mainShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(bounds.x(), bounds.y()), CGPointMake(bounds.x(), bounds.maxY()), mainFunction.get(), false, false));
1471 IntSize radius(trackRadius, trackRadius);
1472 paintInfo.context->clipRoundedRect(FloatRoundedRect(bounds, radius, radius, radius, radius));
1473 context = localContext.cgContext();
1474 CGContextDrawShading(context, mainShading.get());
1479 void RenderThemeMac::adjustSliderThumbStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const
1481 RenderTheme::adjustSliderThumbStyle(styleResolver, style, element);
1482 style.setBoxShadow(nullptr);
1485 const float verticalSliderHeightPadding = 0.1f;
1487 bool RenderThemeMac::paintSliderThumb(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1489 NSSliderCell* sliderThumbCell = o.style().appearance() == SliderThumbVerticalPart
1490 ? sliderThumbVertical()
1491 : sliderThumbHorizontal();
1493 LocalCurrentGraphicsContext localContext(paintInfo.context);
1495 // Update the various states we respond to.
1496 updateEnabledState(sliderThumbCell, o);
1497 Element* focusDelegate = is<Element>(o.node()) ? downcast<Element>(*o.node()).focusDelegate() : nullptr;
1499 updateFocusedState(sliderThumbCell, *focusDelegate->renderer());
1501 // Update the pressed state using the NSCell tracking methods, since that's how NSSliderCell keeps track of it.
1503 if (o.style().appearance() == SliderThumbVerticalPart)
1504 oldPressed = m_isSliderThumbVerticalPressed;
1506 oldPressed = m_isSliderThumbHorizontalPressed;
1508 bool pressed = isPressed(o);
1510 if (o.style().appearance() == SliderThumbVerticalPart)
1511 m_isSliderThumbVerticalPressed = pressed;
1513 m_isSliderThumbHorizontalPressed = pressed;
1515 if (pressed != oldPressed) {
1517 [sliderThumbCell startTrackingAt:NSPoint() inView:nil];
1519 [sliderThumbCell stopTracking:NSPoint() at:NSPoint() inView:nil mouseIsUp:YES];
1522 FloatRect bounds = r;
1523 // Make the height of the vertical slider slightly larger so NSSliderCell will draw a vertical slider.
1524 if (o.style().appearance() == SliderThumbVerticalPart)
1525 bounds.setHeight(bounds.height() + verticalSliderHeightPadding * o.style().effectiveZoom());
1527 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1528 float zoomLevel = o.style().effectiveZoom();
1530 FloatRect unzoomedRect = bounds;
1531 if (zoomLevel != 1.0f) {
1532 unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel);
1533 unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel);
1534 paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y());
1535 paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel));
1536 paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y());
1539 [sliderThumbCell drawInteriorWithFrame:unzoomedRect inView:documentViewFor(o)];
1540 [sliderThumbCell setControlView:nil];
1545 bool RenderThemeMac::paintSearchField(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1547 LocalCurrentGraphicsContext localContext(paintInfo.context);
1548 NSSearchFieldCell* search = this->search();
1550 setSearchCellState(o, r);
1552 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1554 float zoomLevel = o.style().effectiveZoom();
1556 IntRect unzoomedRect = r;
1558 if (zoomLevel != 1.0f) {
1559 unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel);
1560 unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel);
1561 paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y());
1562 paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel));
1563 paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y());
1566 // Set the search button to nil before drawing. Then reset it so we can draw it later.
1567 [search setSearchButtonCell:nil];
1569 NSView *documentView = documentViewFor(o);
1570 [search drawWithFrame:NSRect(unzoomedRect) inView:documentView];
1572 [search setControlView:nil];
1573 [search resetSearchButtonCell];
1575 if (isFocused(o) && o.style().outlineStyleIsAuto()) {
1576 if (wkDrawCellFocusRingWithFrameAtTime(search, NSRect(unzoomedRect), documentView, std::numeric_limits<double>::max()))
1577 o.document().page()->focusController().setFocusedElementNeedsRepaint();
1583 void RenderThemeMac::setSearchCellState(const RenderObject& o, const IntRect&)
1585 NSSearchFieldCell* search = this->search();
1587 [search setControlSize:controlSizeForFont(o.style())];
1589 // Update the various states we respond to.
1590 updateEnabledState(search, o);
1591 updateFocusedState(search, o);
1594 const IntSize* RenderThemeMac::searchFieldSizes() const
1596 static const IntSize sizes[3] = { IntSize(0, 22), IntSize(0, 19), IntSize(0, 17) };
1600 void RenderThemeMac::setSearchFieldSize(RenderStyle& style) const
1602 // If the width and height are both specified, then we have nothing to do.
1603 if (!style.width().isIntrinsicOrAuto() && !style.height().isAuto())
1606 // Use the font size to determine the intrinsic width of the control.
1607 setSizeFromFont(style, searchFieldSizes());
1610 void RenderThemeMac::adjustSearchFieldStyle(StyleResolver& styleResolver, RenderStyle& style, Element*) const
1613 style.resetBorder();
1614 const short borderWidth = 2 * style.effectiveZoom();
1615 style.setBorderLeftWidth(borderWidth);
1616 style.setBorderLeftStyle(INSET);
1617 style.setBorderRightWidth(borderWidth);
1618 style.setBorderRightStyle(INSET);
1619 style.setBorderBottomWidth(borderWidth);
1620 style.setBorderBottomStyle(INSET);
1621 style.setBorderTopWidth(borderWidth);
1622 style.setBorderTopStyle(INSET);
1625 style.setHeight(Length(Auto));
1626 setSearchFieldSize(style);
1628 // Override padding size to match AppKit text positioning.
1629 const int padding = 1 * style.effectiveZoom();
1630 style.setPaddingLeft(Length(padding, Fixed));
1631 style.setPaddingRight(Length(padding, Fixed));
1632 style.setPaddingTop(Length(padding, Fixed));
1633 style.setPaddingBottom(Length(padding, Fixed));
1635 NSControlSize controlSize = controlSizeForFont(style);
1636 setFontFromControlSize(styleResolver, style, controlSize);
1638 style.setBoxShadow(nullptr);
1641 bool RenderThemeMac::paintSearchFieldCancelButton(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1643 Element* input = o.node()->shadowHost();
1645 input = downcast<Element>(o.node());
1647 if (!input->renderer()->isBox())
1650 LocalCurrentGraphicsContext localContext(paintInfo.context);
1651 setSearchCellState(*input->renderer(), r);
1653 NSSearchFieldCell* search = this->search();
1655 if (!input->isDisabledFormControl() && (is<HTMLTextFormControlElement>(*input) && !downcast<HTMLTextFormControlElement>(*input).isReadOnly()))
1656 updatePressedState([search cancelButtonCell], o);
1657 else if ([[search cancelButtonCell] isHighlighted])
1658 [[search cancelButtonCell] setHighlighted:NO];
1660 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1662 float zoomLevel = o.style().effectiveZoom();
1664 FloatRect localBounds = [search cancelButtonRectForBounds:NSRect(input->renderBox()->pixelSnappedBorderBoxRect())];
1665 localBounds = convertToPaintingRect(*input->renderer(), o, localBounds, r);
1667 FloatRect unzoomedRect(localBounds);
1668 if (zoomLevel != 1.0f) {
1669 unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel);
1670 unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel);
1671 paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y());
1672 paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel));
1673 paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y());
1676 [[search cancelButtonCell] drawWithFrame:unzoomedRect inView:documentViewFor(o)];
1677 [[search cancelButtonCell] setControlView:nil];
1681 const IntSize* RenderThemeMac::cancelButtonSizes() const
1683 static const IntSize sizes[3] = { IntSize(16, 13), IntSize(13, 11), IntSize(13, 9) };
1687 void RenderThemeMac::adjustSearchFieldCancelButtonStyle(StyleResolver&, RenderStyle& style, Element*) const
1689 IntSize size = sizeForSystemFont(style, cancelButtonSizes());
1690 style.setWidth(Length(size.width(), Fixed));
1691 style.setHeight(Length(size.height(), Fixed));
1692 style.setBoxShadow(nullptr);
1695 const IntSize* RenderThemeMac::resultsButtonSizes() const
1697 static const IntSize sizes[3] = { IntSize(19, 13), IntSize(17, 11), IntSize(17, 9) };
1701 const int emptyResultsOffset = 9;
1702 void RenderThemeMac::adjustSearchFieldDecorationPartStyle(StyleResolver&, RenderStyle& style, Element*) const
1704 IntSize size = sizeForSystemFont(style, resultsButtonSizes());
1705 style.setWidth(Length(size.width() - emptyResultsOffset, Fixed));
1706 style.setHeight(Length(size.height(), Fixed));
1707 style.setBoxShadow(nullptr);
1710 bool RenderThemeMac::paintSearchFieldDecorationPart(const RenderObject&, const PaintInfo&, const IntRect&)
1715 void RenderThemeMac::adjustSearchFieldResultsDecorationPartStyle(StyleResolver&, RenderStyle& style, Element*) const
1717 IntSize size = sizeForSystemFont(style, resultsButtonSizes());
1718 style.setWidth(Length(size.width(), Fixed));
1719 style.setHeight(Length(size.height(), Fixed));
1720 style.setBoxShadow(nullptr);
1723 bool RenderThemeMac::paintSearchFieldResultsDecorationPart(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1725 Node* input = o.node()->shadowHost();
1728 if (!input->renderer()->isBox())
1731 LocalCurrentGraphicsContext localContext(paintInfo.context);
1732 setSearchCellState(*input->renderer(), r);
1734 NSSearchFieldCell* search = this->search();
1736 if ([search searchMenuTemplate] != nil)
1737 [search setSearchMenuTemplate:nil];
1739 FloatRect localBounds = [search searchButtonRectForBounds:NSRect(input->renderBox()->pixelSnappedBorderBoxRect())];
1740 localBounds = convertToPaintingRect(*input->renderer(), o, localBounds, r);
1742 [[search searchButtonCell] drawWithFrame:localBounds inView:documentViewFor(o)];
1743 [[search searchButtonCell] setControlView:nil];
1747 const int resultsArrowWidth = 5;
1748 void RenderThemeMac::adjustSearchFieldResultsButtonStyle(StyleResolver&, RenderStyle& style, Element*) const
1750 IntSize size = sizeForSystemFont(style, resultsButtonSizes());
1751 style.setWidth(Length(size.width() + resultsArrowWidth, Fixed));
1752 style.setHeight(Length(size.height(), Fixed));
1753 style.setBoxShadow(nullptr);
1756 bool RenderThemeMac::paintSearchFieldResultsButton(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1758 Node* input = o.node()->shadowHost();
1761 if (!input->renderer()->isBox())
1764 LocalCurrentGraphicsContext localContext(paintInfo.context);
1765 setSearchCellState(*input->renderer(), r);
1767 NSSearchFieldCell* search = this->search();
1769 if (![search searchMenuTemplate])
1770 [search setSearchMenuTemplate:searchMenuTemplate()];
1772 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1773 float zoomLevel = o.style().effectiveZoom();
1775 FloatRect localBounds = [search searchButtonRectForBounds:NSRect(input->renderBox()->pixelSnappedBorderBoxRect())];
1776 localBounds = convertToPaintingRect(*input->renderer(), o, localBounds, r);
1778 IntRect unzoomedRect(localBounds);
1779 if (zoomLevel != 1.0f) {
1780 unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel);
1781 unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel);
1782 paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y());
1783 paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel));
1784 paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y());
1787 [[search searchButtonCell] drawWithFrame:unzoomedRect inView:documentViewFor(o)];
1788 [[search searchButtonCell] setControlView:nil];
1793 bool RenderThemeMac::paintSnapshottedPluginOverlay(const RenderObject& renderer, const PaintInfo& paintInfo, const IntRect&)
1795 if (paintInfo.phase != PaintPhaseBlockBackground)
1798 if (!is<RenderBlock>(renderer))
1801 const RenderBlock& renderBlock = downcast<RenderBlock>(renderer);
1803 LayoutUnit contentWidth = renderBlock.contentWidth();
1804 LayoutUnit contentHeight = renderBlock.contentHeight();
1805 if (!contentWidth || !contentHeight)
1808 GraphicsContext* context = paintInfo.context;
1810 LayoutSize contentSize(contentWidth, contentHeight);
1811 LayoutPoint contentLocation = renderBlock.location();
1812 contentLocation.move(renderBlock.borderLeft() + renderBlock.paddingLeft(), renderBlock.borderTop() + renderBlock.paddingTop());
1814 LayoutRect rect(contentLocation, contentSize);
1815 IntRect alignedRect = snappedIntRect(rect);
1816 if (alignedRect.width() <= 0 || alignedRect.height() <= 0)
1819 // We need to get the snapshot image from the plugin element, which should be available
1820 // from our node. Assuming this node is the plugin overlay element, we should get to the
1821 // plugin itself by asking for the shadow root parent, and then its parent.
1823 if (!is<HTMLElement>(*renderBlock.element()))
1826 HTMLElement& plugInOverlay = downcast<HTMLElement>(*renderBlock.element());
1827 Element* parent = plugInOverlay.parentOrShadowHostElement();
1828 while (parent && !is<HTMLPlugInElement>(*parent))
1829 parent = parent->parentOrShadowHostElement();
1834 HTMLPlugInElement& plugInElement = downcast<HTMLPlugInElement>(*parent);
1835 if (!is<HTMLPlugInImageElement>(plugInElement))
1838 HTMLPlugInImageElement& plugInImageElement = downcast<HTMLPlugInImageElement>(plugInElement);
1840 Image* snapshot = plugInImageElement.snapshotImage();
1844 RenderSnapshottedPlugIn& plugInRenderer = downcast<RenderSnapshottedPlugIn>(*plugInImageElement.renderer());
1845 FloatPoint snapshotAbsPos = plugInRenderer.localToAbsolute();
1846 snapshotAbsPos.move(plugInRenderer.borderLeft() + plugInRenderer.paddingLeft(), plugInRenderer.borderTop() + plugInRenderer.paddingTop());
1848 // We could draw the snapshot with that coordinates, but we need to make sure there
1849 // isn't a composited layer between us and the plugInRenderer.
1850 for (auto* renderBox = &downcast<RenderBox>(renderer); renderBox != &plugInRenderer; renderBox = renderBox->parentBox()) {
1851 if (renderBox->hasLayer() && renderBox->layer() && renderBox->layer()->isComposited()) {
1852 snapshotAbsPos = -renderBox->location();
1857 LayoutSize pluginSize(plugInRenderer.contentWidth(), plugInRenderer.contentHeight());
1858 LayoutRect pluginRect(snapshotAbsPos, pluginSize);
1859 IntRect alignedPluginRect = snappedIntRect(pluginRect);
1861 if (alignedPluginRect.width() <= 0 || alignedPluginRect.height() <= 0)
1864 context->drawImage(snapshot, plugInRenderer.style().colorSpace(), alignedPluginRect, CompositeSourceOver);
1868 #if ENABLE(DATALIST_ELEMENT)
1869 IntSize RenderThemeMac::sliderTickSize() const
1871 return IntSize(1, 3);
1874 int RenderThemeMac::sliderTickOffsetFromTrackCenter() const
1880 const int sliderThumbWidth = 15;
1881 const int sliderThumbHeight = 15;
1883 void RenderThemeMac::adjustSliderThumbSize(RenderStyle& style, Element*) const
1885 float zoomLevel = style.effectiveZoom();
1886 if (style.appearance() == SliderThumbHorizontalPart || style.appearance() == SliderThumbVerticalPart) {
1887 style.setWidth(Length(static_cast<int>(sliderThumbWidth * zoomLevel), Fixed));
1888 style.setHeight(Length(static_cast<int>(sliderThumbHeight * zoomLevel), Fixed));
1892 bool RenderThemeMac::shouldShowPlaceholderWhenFocused() const
1897 bool RenderThemeMac::shouldHaveCapsLockIndicator(HTMLInputElement& element) const
1899 return element.isPasswordField();
1902 NSPopUpButtonCell* RenderThemeMac::popupButton() const
1904 if (!m_popupButton) {
1905 m_popupButton = adoptNS([[NSPopUpButtonCell alloc] initTextCell:@"" pullsDown:NO]);
1906 [m_popupButton.get() setUsesItemFromMenu:NO];
1907 [m_popupButton.get() setFocusRingType:NSFocusRingTypeExterior];
1908 // We don't want the app's UI layout direction to affect the appearance of popup buttons in
1909 // web content, which has its own layout direction.
1910 // FIXME: Make this depend on the directionality of the select element, once the rest of the
1911 // rendering code can account for the popup arrows appearing on the other side.
1912 [m_popupButton setUserInterfaceLayoutDirection:NSUserInterfaceLayoutDirectionLeftToRight];
1915 return m_popupButton.get();
1918 NSSearchFieldCell* RenderThemeMac::search() const
1921 m_search = adoptNS([[NSSearchFieldCell alloc] initTextCell:@""]);
1922 [m_search.get() setBezelStyle:NSTextFieldRoundedBezel];
1923 [m_search.get() setBezeled:YES];
1924 [m_search.get() setEditable:YES];
1925 [m_search.get() setFocusRingType:NSFocusRingTypeExterior];
1926 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
1927 [m_search.get() setCenteredLook:NO];
1931 return m_search.get();
1934 NSMenu* RenderThemeMac::searchMenuTemplate() const
1936 if (!m_searchMenuTemplate)
1937 m_searchMenuTemplate = adoptNS([[NSMenu alloc] initWithTitle:@""]);
1939 return m_searchMenuTemplate.get();
1942 NSSliderCell* RenderThemeMac::sliderThumbHorizontal() const
1944 if (!m_sliderThumbHorizontal) {
1945 m_sliderThumbHorizontal = adoptNS([[NSSliderCell alloc] init]);
1946 [m_sliderThumbHorizontal.get() setSliderType:NSLinearSlider];
1947 [m_sliderThumbHorizontal.get() setControlSize:NSSmallControlSize];
1948 [m_sliderThumbHorizontal.get() setFocusRingType:NSFocusRingTypeExterior];
1951 return m_sliderThumbHorizontal.get();
1954 NSSliderCell* RenderThemeMac::sliderThumbVertical() const
1956 if (!m_sliderThumbVertical) {
1957 m_sliderThumbVertical = adoptNS([[NSSliderCell alloc] init]);
1958 [m_sliderThumbVertical.get() setSliderType:NSLinearSlider];
1959 [m_sliderThumbVertical.get() setControlSize:NSSmallControlSize];
1960 [m_sliderThumbVertical.get() setFocusRingType:NSFocusRingTypeExterior];
1963 return m_sliderThumbVertical.get();
1966 NSTextFieldCell* RenderThemeMac::textField() const
1969 m_textField = adoptNS([[WebCoreTextFieldCell alloc] initTextCell:@""]);
1970 [m_textField.get() setBezeled:YES];
1971 [m_textField.get() setEditable:YES];
1972 [m_textField.get() setFocusRingType:NSFocusRingTypeExterior];
1973 // Post-Lion, WebCore can be in charge of paintinng the background thanks to
1974 // the workaround in place for <rdar://problem/11385461>, which is implemented
1975 // above as _coreUIDrawOptionsWithFrame.
1976 [m_textField.get() setDrawsBackground:NO];
1979 return m_textField.get();
1982 String RenderThemeMac::fileListNameForWidth(const FileList* fileList, const FontCascade& font, int width, bool multipleFilesAllowed) const
1987 String strToTruncate;
1988 if (fileList->isEmpty())
1989 strToTruncate = fileListDefaultLabel(multipleFilesAllowed);
1990 else if (fileList->length() == 1)
1991 strToTruncate = [[NSFileManager defaultManager] displayNameAtPath:(fileList->item(0)->path())];
1993 return StringTruncator::rightTruncate(multipleFileUploadText(fileList->length()), width, font, StringTruncator::EnableRoundingHacks);
1995 return StringTruncator::centerTruncate(strToTruncate, width, font, StringTruncator::EnableRoundingHacks);
1998 bool RenderThemeMac::defaultButtonHasAnimation() const
2000 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
2007 #if ENABLE(SERVICE_CONTROLS)
2008 NSServicesRolloverButtonCell* RenderThemeMac::servicesRolloverButtonCell() const
2010 #if HAVE(APPKIT_SERVICE_CONTROLS_SUPPORT)
2011 if (!m_servicesRolloverButton) {
2012 m_servicesRolloverButton = [NSServicesRolloverButtonCell serviceRolloverButtonCellForStyle:NSSharingServicePickerStyleRollover];
2013 [m_servicesRolloverButton setBezelStyle:NSRoundedDisclosureBezelStyle];
2014 [m_servicesRolloverButton setButtonType:NSPushOnPushOffButton];
2015 [m_servicesRolloverButton setImagePosition:NSImageOnly];
2016 [m_servicesRolloverButton setState:NO];
2019 return m_servicesRolloverButton.get();
2025 bool RenderThemeMac::paintImageControlsButton(const RenderObject& renderer, const PaintInfo& paintInfo, const IntRect& rect)
2027 if (paintInfo.phase != PaintPhaseBlockBackground)
2030 #if HAVE(APPKIT_SERVICE_CONTROLS_SUPPORT)
2031 NSServicesRolloverButtonCell *cell = servicesRolloverButtonCell();
2033 LocalCurrentGraphicsContext localContext(paintInfo.context);
2034 GraphicsContextStateSaver stateSaver(*paintInfo.context);
2036 paintInfo.context->translate(rect.x(), rect.y());
2038 IntRect innerFrame(IntPoint(), rect.size());
2039 [cell drawWithFrame:innerFrame inView:documentViewFor(renderer)];
2040 [cell setControlView:nil];
2042 UNUSED_PARAM(renderer);
2049 IntSize RenderThemeMac::imageControlsButtonSize(const RenderObject&) const
2051 #if HAVE(APPKIT_SERVICE_CONTROLS_SUPPORT)
2052 return IntSize(servicesRolloverButtonCell().cellSize);
2058 IntSize RenderThemeMac::imageControlsButtonPositionOffset() const
2060 #if HAVE(APPKIT_SERVICE_CONTROLS_SUPPORT)
2061 // FIXME: Currently the offsets will always be the same no matter what image rect you try with.
2062 // This may not always be true in the future.
2063 static const int dummyDimension = 100;
2064 IntRect dummyImageRect(0, 0, dummyDimension, dummyDimension);
2065 NSRect bounds = [servicesRolloverButtonCell() rectForBounds:dummyImageRect preferredEdge:NSMinYEdge];
2067 return IntSize(dummyDimension - bounds.origin.x, bounds.origin.y);
2074 #if ENABLE(ATTACHMENT_ELEMENT)
2075 const CGFloat attachmentIconSize = 48;
2076 const CGFloat attachmentIconBackgroundPadding = 6;
2077 const CGFloat attachmentIconBackgroundSize = attachmentIconSize + attachmentIconBackgroundPadding;
2078 const CGFloat attachmentIconSelectionBorderThickness = 1;
2079 const CGFloat attachmentIconBackgroundRadius = 3;
2080 const CGFloat attachmentIconToLabelMargin = 2;
2082 static Color attachmentIconBackgroundColor() { return Color(0, 0, 0, 30); }
2083 static Color attachmentIconBorderColor() { return Color(255, 255, 255, 125); }
2085 const CGFloat attachmentLabelFontSize = 12;
2086 const CGFloat attachmentLabelBackgroundRadius = 3;
2087 const CGFloat attachmentLabelBackgroundPadding = 3;
2089 const CGFloat attachmentMargin = 3;
2091 struct AttachmentLayout {
2092 AttachmentLayout(const RenderAttachment&);
2095 FloatRect textBackgroundRect;
2097 FloatRect iconBackgroundRect;
2098 FloatRect attachmentRect;
2102 RetainPtr<CTFontRef> labelFont;
2103 FontCascade labelFontCascade;
2104 std::unique_ptr<TextRun> labelTextRun;
2107 AttachmentLayout::AttachmentLayout(const RenderAttachment& attachment)
2109 // FIXME: We should have a limit on the width of the label.
2110 // FIXME: We should support line-breaking (up to two lines) and always middle-truncate the second line.
2111 File* file = attachment.attachmentElement().file();
2113 labelFont = adoptCF(CTFontCreateUIFontForLanguage(kCTFontSystemFontType, attachmentLabelFontSize, nullptr));
2114 labelFontCascade = FontCascade(FontPlatformData(labelFont.get(), attachmentLabelFontSize));
2116 String filename = file ? file->name() : String();
2117 labelTextRun = std::make_unique<TextRun>(filename);
2118 labelTextRun->setDirection(filename.defaultWritingDirection() == U_LEFT_TO_RIGHT ? LTR : RTL);
2119 float textWidth = labelFontCascade.width(*labelTextRun);
2120 float textHeight = labelFontCascade.fontMetrics().height();
2121 float xOffset = (attachmentIconBackgroundSize / 2) - (textWidth / 2);
2123 baseline = CGRound(attachmentIconBackgroundSize + attachmentIconToLabelMargin + labelFontCascade.fontMetrics().ascent());
2125 textRect = FloatRect(xOffset, attachmentIconBackgroundSize + attachmentIconToLabelMargin, textWidth, textHeight);
2126 textBackgroundRect = textRect;
2127 textBackgroundRect.inflateX(attachmentLabelBackgroundPadding);
2128 textBackgroundRect = encloseRectToDevicePixels(textBackgroundRect, attachment.document().deviceScaleFactor());
2130 iconBackgroundRect = FloatRect(0, 0, attachmentIconBackgroundSize, attachmentIconBackgroundSize);
2132 iconRect = iconBackgroundRect;
2133 iconRect.setSize(FloatSize(attachmentIconSize, attachmentIconSize));
2134 iconRect.move(attachmentIconBackgroundPadding / 2, attachmentIconBackgroundPadding / 2);
2136 attachmentRect = iconBackgroundRect;
2137 attachmentRect.unite(textBackgroundRect);
2138 attachmentRect.inflate(attachmentMargin);
2139 attachmentRect = encloseRectToDevicePixels(attachmentRect, attachment.document().deviceScaleFactor());
2142 LayoutSize RenderThemeMac::attachmentIntrinsicSize(const RenderAttachment& attachment) const
2144 AttachmentLayout layout(attachment);
2145 return LayoutSize(layout.attachmentRect.size());
2148 int RenderThemeMac::attachmentBaseline(const RenderAttachment& attachment) const
2150 AttachmentLayout layout(attachment);
2151 return layout.baseline;
2154 static void paintAttachmentIconBackground(const RenderAttachment&, GraphicsContext& context, AttachmentLayout& layout)
2156 // FIXME: Finder has a discontinuous behavior here when you have a background color other than white,
2157 // where it switches into 'bordered mode' and the border pops in on top of the background.
2158 bool paintBorder = true;
2160 FloatRect backgroundRect = layout.iconBackgroundRect;
2162 backgroundRect.inflate(-attachmentIconSelectionBorderThickness);
2164 FloatSize iconBackgroundRadiusSize(attachmentIconBackgroundRadius, attachmentIconBackgroundRadius);
2166 Path backgroundPath;
2167 backgroundPath.addRoundedRect(backgroundRect, iconBackgroundRadiusSize);
2168 context.setFillColor(attachmentIconBackgroundColor(), ColorSpaceDeviceRGB);
2169 context.fillPath(backgroundPath);
2172 FloatRect borderRect = layout.iconBackgroundRect;
2173 borderRect.inflate(-attachmentIconSelectionBorderThickness / 2);
2176 borderPath.addRoundedRect(borderRect, iconBackgroundRadiusSize);
2177 context.setStrokeColor(attachmentIconBorderColor(), ColorSpaceDeviceRGB);
2178 context.setStrokeThickness(attachmentIconSelectionBorderThickness);
2179 context.strokePath(borderPath);
2183 static void paintAttachmentIcon(const RenderAttachment& attachment, GraphicsContext& context, AttachmentLayout& layout)
2185 File* file = attachment.attachmentElement().file();
2186 RetainPtr<LSBindingRef> lsBinding = adoptCF(_LSBindingCreateWithURL(kCFAllocatorDefault, (CFURLRef)[NSURL fileURLWithPath:file ? file->path() : String()]));
2190 // FIXME: This should take transforms and page scale into account, not just deviceScaleFactor.
2191 FloatSize iconSizeInPoints(attachmentIconSize, attachmentIconSize);
2192 iconSizeInPoints.scale(attachment.document().deviceScaleFactor());
2194 RetainPtr<CGImageRef> icon = adoptCF(_ISCreateCGImageFromBindingWithSizeScaleAndOptions(lsBinding.get(), iconSizeInPoints, 1, nil));
2198 context.drawNativeImage(icon.get(), iconSizeInPoints, ColorSpaceDeviceRGB, layout.iconRect, FloatRect(FloatPoint(), iconSizeInPoints));
2201 void RenderThemeMac::paintAttachmentLabelBackground(const RenderAttachment&, GraphicsContext& context, AttachmentLayout& layout) const
2203 Path backgroundPath;
2204 backgroundPath.addRoundedRect(layout.textBackgroundRect, FloatSize(attachmentLabelBackgroundRadius, attachmentLabelBackgroundRadius));
2205 context.setFillColor(convertNSColorToColor([NSColor alternateSelectedControlColor]), ColorSpaceDeviceRGB);
2206 context.fillPath(backgroundPath);
2209 void RenderThemeMac::paintAttachmentLabel(const RenderAttachment&, GraphicsContext& context, AttachmentLayout& layout, bool useSelectedStyle) const
2211 FloatPoint textLocation = layout.textRect.minXMaxYCorner();
2212 textLocation.move(0, -layout.labelFontCascade.fontMetrics().descent());
2214 context.setFillColor(useSelectedStyle ? convertNSColorToColor([NSColor alternateSelectedControlTextColor]) : Color::black, ColorSpaceDeviceRGB);
2215 context.drawBidiText(layout.labelFontCascade, *layout.labelTextRun, textLocation);
2218 bool RenderThemeMac::paintAttachment(const RenderObject& renderer, const PaintInfo& paintInfo, const IntRect& paintRect)
2220 if (!is<RenderAttachment>(renderer))
2223 const RenderAttachment& attachment = downcast<RenderAttachment>(renderer);
2225 AttachmentLayout layout(attachment);
2227 GraphicsContext& context = *paintInfo.context;
2229 GraphicsContextStateSaver saver(context);
2231 context.translate(toFloatSize(paintRect.location()));
2232 context.translate(FloatSize((layout.attachmentRect.width() - attachmentIconBackgroundSize) / 2, 0));
2234 bool useSelectedStyle = attachment.isSelectedOrFocused();
2236 if (useSelectedStyle)
2237 paintAttachmentIconBackground(attachment, context, layout);
2238 paintAttachmentIcon(attachment, context, layout);
2239 if (useSelectedStyle)
2240 paintAttachmentLabelBackground(attachment, context, layout);
2241 paintAttachmentLabel(attachment, context, layout, useSelectedStyle);
2246 #endif // ENABLE(ATTACHMENT_ELEMENT)
2248 } // namespace WebCore
2250 #endif // !PLATFORM(IOS)