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"
30 #import "ExceptionCodePlaceholder.h"
32 #import "FloatRoundedRect.h"
33 #import "FocusController.h"
36 #import "GraphicsContextCG.h"
37 #import "HTMLAudioElement.h"
38 #import "HTMLInputElement.h"
39 #import "HTMLMediaElement.h"
41 #import "HTMLPlugInImageElement.h"
43 #import "ImageBuffer.h"
44 #import "LocalCurrentGraphicsContext.h"
45 #import "LocalizedStrings.h"
46 #import "MediaControlElements.h"
47 #import "NSSharingServicePickerSPI.h"
50 #import "RenderLayer.h"
51 #import "RenderMedia.h"
52 #import "RenderMediaControlElements.h"
53 #import "RenderMediaControls.h"
54 #import "RenderProgress.h"
55 #import "RenderSlider.h"
56 #import "RenderSnapshottedPlugIn.h"
57 #import "RenderView.h"
58 #import "SharedBuffer.h"
59 #import "StringTruncator.h"
60 #import "StyleResolver.h"
62 #import "TimeRanges.h"
63 #import "UserAgentScripts.h"
64 #import "UserAgentStyleSheets.h"
65 #import "WebCoreSystemInterface.h"
66 #import <wtf/RetainPtr.h>
67 #import <wtf/RetainPtr.h>
68 #import <wtf/StdLibExtras.h>
69 #import <wtf/text/StringBuilder.h>
70 #import <Carbon/Carbon.h>
71 #import <Cocoa/Cocoa.h>
74 #if ENABLE(METER_ELEMENT)
75 #import "RenderMeter.h"
76 #import "HTMLMeterElement.h"
79 #if defined(__LP64__) && __LP64__
80 #define HAVE_APPKIT_SERVICE_CONTROLS_SUPPORT 1
82 #define HAVE_APPKIT_SERVICE_CONTROLS_SUPPORT 0
85 #if ENABLE(SERVICE_CONTROLS) && HAVE(APPKIT_SERVICE_CONTROLS_SUPPORT)
87 #if __has_include(<AppKit/AppKitDefines_Private.h>)
88 #import <AppKit/AppKitDefines_Private.h>
90 #define APPKIT_PRIVATE_CLASS
93 #if __has_include(<AppKit/NSServicesRolloverButtonCell.h>)
94 #import <AppKit/NSServicesRolloverButtonCell.h>
96 @interface NSServicesRolloverButtonCell : NSButtonCell
100 @interface NSServicesRolloverButtonCell (Details)
101 + (NSServicesRolloverButtonCell *)serviceRolloverButtonCellForStyle:(NSSharingServicePickerStyle)style;
102 - (NSRect)rectForBounds:(NSRect)bounds preferredEdge:(NSRectEdge)preferredEdge;
105 #endif // ENABLE(SERVICE_CONTROLS)
107 // The methods in this file are specific to the Mac OS X platform.
109 // FIXME: The platform-independent code in this class should be factored out and merged with RenderThemeSafari.
111 // We estimate the animation rate of a Mac OS X progress bar is 33 fps.
112 // Hard code the value here because we haven't found API for it.
113 const double progressAnimationFrameRate = 0.033;
115 // Mac OS X progress bar animation seems to have 256 frames.
116 const double progressAnimationNumFrames = 256;
118 @interface WebCoreRenderThemeNotificationObserver : NSObject
120 WebCore::RenderTheme *_theme;
123 - (id)initWithTheme:(WebCore::RenderTheme *)theme;
124 - (void)systemColorsDidChange:(NSNotification *)notification;
128 @implementation WebCoreRenderThemeNotificationObserver
130 - (id)initWithTheme:(WebCore::RenderTheme *)theme
132 if (!(self = [super init]))
139 - (void)systemColorsDidChange:(NSNotification *)unusedNotification
141 ASSERT_UNUSED(unusedNotification, [[unusedNotification name] isEqualToString:NSSystemColorsDidChangeNotification]);
142 _theme->platformColorsDidChange();
147 @interface NSTextFieldCell (WKDetails)
148 - (CFDictionaryRef)_coreUIDrawOptionsWithFrame:(NSRect)cellFrame inView:(NSView *)controlView includeFocus:(BOOL)includeFocus;
152 @interface WebCoreTextFieldCell : NSTextFieldCell
153 - (CFDictionaryRef)_coreUIDrawOptionsWithFrame:(NSRect)cellFrame inView:(NSView *)controlView includeFocus:(BOOL)includeFocus;
156 @implementation WebCoreTextFieldCell
157 - (CFDictionaryRef)_coreUIDrawOptionsWithFrame:(NSRect)cellFrame inView:(NSView *)controlView includeFocus:(BOOL)includeFocus
159 // FIXME: This is a post-Lion-only workaround for <rdar://problem/11385461>. When that bug is resolved, we should remove this code.
160 CFMutableDictionaryRef coreUIDrawOptions = CFDictionaryCreateMutableCopy(NULL, 0, [super _coreUIDrawOptionsWithFrame:cellFrame inView:controlView includeFocus:includeFocus]);
161 CFDictionarySetValue(coreUIDrawOptions, @"borders only", kCFBooleanTrue);
162 return (CFDictionaryRef)[NSMakeCollectable(coreUIDrawOptions) autorelease];
166 @interface WebCoreRenderThemeBundle : NSObject
169 @implementation WebCoreRenderThemeBundle
172 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
173 @interface NSSearchFieldCell(Details)
174 @property (getter=isCenteredLook) BOOL centeredLook;
180 using namespace HTMLNames;
196 PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page*)
198 static RenderTheme* rt = RenderThemeMac::create().leakRef();
202 PassRefPtr<RenderTheme> RenderThemeMac::create()
204 return adoptRef(new RenderThemeMac);
207 RenderThemeMac::RenderThemeMac()
208 : m_isSliderThumbHorizontalPressed(false)
209 , m_isSliderThumbVerticalPressed(false)
210 , m_notificationObserver(adoptNS([[WebCoreRenderThemeNotificationObserver alloc] initWithTheme:this]))
212 [[NSNotificationCenter defaultCenter] addObserver:m_notificationObserver.get()
213 selector:@selector(systemColorsDidChange:)
214 name:NSSystemColorsDidChangeNotification
218 RenderThemeMac::~RenderThemeMac()
220 [[NSNotificationCenter defaultCenter] removeObserver:m_notificationObserver.get()];
223 NSView* RenderThemeMac::documentViewFor(const RenderObject& o) const
225 ControlStates states(extractControlStatesForRenderer(o));
226 return ThemeMac::ensuredView(&o.view().frameView(), &states);
230 String RenderThemeMac::mediaControlsStyleSheet()
232 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
233 if (m_mediaControlsStyleSheet.isEmpty())
234 m_mediaControlsStyleSheet = [NSString stringWithContentsOfFile:[[NSBundle bundleForClass:[WebCoreRenderThemeBundle class]] pathForResource:@"mediaControlsApple" ofType:@"css"] encoding:NSUTF8StringEncoding error:nil];
235 return m_mediaControlsStyleSheet;
237 return emptyString();
241 String RenderThemeMac::mediaControlsScript()
243 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
244 if (m_mediaControlsScript.isEmpty()) {
245 StringBuilder scriptBuilder;
246 scriptBuilder.append([NSString stringWithContentsOfFile:[[NSBundle bundleForClass:[WebCoreRenderThemeBundle class]] pathForResource:@"mediaControlsLocalizedStrings" ofType:@"js"] encoding:NSUTF8StringEncoding error:nil]);
247 scriptBuilder.append([NSString stringWithContentsOfFile:[[NSBundle bundleForClass:[WebCoreRenderThemeBundle class]] pathForResource:@"mediaControlsApple" ofType:@"js"] encoding:NSUTF8StringEncoding error:nil]);
248 m_mediaControlsScript = scriptBuilder.toString();
250 return m_mediaControlsScript;
252 return emptyString();
256 #endif // ENABLE(VIDEO)
259 #if ENABLE(SERVICE_CONTROLS)
260 String RenderThemeMac::imageControlsStyleSheet() const
262 return String(imageControlsMacUserAgentStyleSheet, sizeof(imageControlsMacUserAgentStyleSheet));
266 Color RenderThemeMac::platformActiveSelectionBackgroundColor() const
268 NSColor* color = [[NSColor selectedTextBackgroundColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace];
269 return Color(static_cast<int>(255.0 * [color redComponent]), static_cast<int>(255.0 * [color greenComponent]), static_cast<int>(255.0 * [color blueComponent]));
272 Color RenderThemeMac::platformInactiveSelectionBackgroundColor() const
274 NSColor* color = [[NSColor secondarySelectedControlColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace];
275 return Color(static_cast<int>(255.0 * [color redComponent]), static_cast<int>(255.0 * [color greenComponent]), static_cast<int>(255.0 * [color blueComponent]));
278 Color RenderThemeMac::platformActiveListBoxSelectionBackgroundColor() const
280 NSColor* color = [[NSColor alternateSelectedControlColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace];
281 return Color(static_cast<int>(255.0 * [color redComponent]), static_cast<int>(255.0 * [color greenComponent]), static_cast<int>(255.0 * [color blueComponent]));
284 Color RenderThemeMac::platformActiveListBoxSelectionForegroundColor() const
289 Color RenderThemeMac::platformInactiveListBoxSelectionForegroundColor() const
294 Color RenderThemeMac::platformFocusRingColor() const
296 if (usesTestModeFocusRingColor())
297 return oldAquaFocusRingColor();
299 return systemColor(CSSValueWebkitFocusRingColor);
302 int RenderThemeMac::platformFocusRingMaxWidth() const
304 // FIXME: Shouldn't this function be named platformFocusRingMinWidth? But also, I'm not sure if this function is needed - looks like
305 // all platforms just used 0 for this before <http://trac.webkit.org/changeset/168397>.
309 Color RenderThemeMac::platformInactiveListBoxSelectionBackgroundColor() const
311 return platformInactiveSelectionBackgroundColor();
314 static FontWeight toFontWeight(NSInteger appKitFontWeight)
316 ASSERT(appKitFontWeight > 0 && appKitFontWeight < 15);
317 if (appKitFontWeight > 14)
318 appKitFontWeight = 14;
319 else if (appKitFontWeight < 1)
320 appKitFontWeight = 1;
322 static FontWeight fontWeights[] = {
338 return fontWeights[appKitFontWeight - 1];
341 void RenderThemeMac::systemFont(CSSValueID cssValueId, FontDescription& fontDescription) const
343 DEPRECATED_DEFINE_STATIC_LOCAL(FontDescription, systemFont, ());
344 DEPRECATED_DEFINE_STATIC_LOCAL(FontDescription, smallSystemFont, ());
345 DEPRECATED_DEFINE_STATIC_LOCAL(FontDescription, menuFont, ());
346 DEPRECATED_DEFINE_STATIC_LOCAL(FontDescription, labelFont, ());
347 DEPRECATED_DEFINE_STATIC_LOCAL(FontDescription, miniControlFont, ());
348 DEPRECATED_DEFINE_STATIC_LOCAL(FontDescription, smallControlFont, ());
349 DEPRECATED_DEFINE_STATIC_LOCAL(FontDescription, controlFont, ());
351 FontDescription* cachedDesc;
353 switch (cssValueId) {
354 case CSSValueSmallCaption:
355 cachedDesc = &smallSystemFont;
356 if (!smallSystemFont.isAbsoluteSize())
357 font = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]];
360 cachedDesc = &menuFont;
361 if (!menuFont.isAbsoluteSize())
362 font = [NSFont menuFontOfSize:[NSFont systemFontSize]];
364 case CSSValueStatusBar:
365 cachedDesc = &labelFont;
366 if (!labelFont.isAbsoluteSize())
367 font = [NSFont labelFontOfSize:[NSFont labelFontSize]];
369 case CSSValueWebkitMiniControl:
370 cachedDesc = &miniControlFont;
371 if (!miniControlFont.isAbsoluteSize())
372 font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSMiniControlSize]];
374 case CSSValueWebkitSmallControl:
375 cachedDesc = &smallControlFont;
376 if (!smallControlFont.isAbsoluteSize())
377 font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]];
379 case CSSValueWebkitControl:
380 cachedDesc = &controlFont;
381 if (!controlFont.isAbsoluteSize())
382 font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]];
385 cachedDesc = &systemFont;
386 if (!systemFont.isAbsoluteSize())
387 font = [NSFont systemFontOfSize:[NSFont systemFontSize]];
391 NSFontManager *fontManager = [NSFontManager sharedFontManager];
392 cachedDesc->setIsAbsoluteSize(true);
393 cachedDesc->setOneFamily([font webCoreFamilyName]);
394 cachedDesc->setSpecifiedSize([font pointSize]);
395 cachedDesc->setWeight(toFontWeight([fontManager weightOfFont:font]));
396 cachedDesc->setIsItalic([fontManager traitsOfFont:font] & NSItalicFontMask);
398 fontDescription = *cachedDesc;
401 static RGBA32 convertNSColorToColor(NSColor *color)
403 NSColor *colorInColorSpace = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace];
404 if (colorInColorSpace) {
405 static const double scaleFactor = nextafter(256.0, 0.0);
406 return makeRGB(static_cast<int>(scaleFactor * [colorInColorSpace redComponent]),
407 static_cast<int>(scaleFactor * [colorInColorSpace greenComponent]),
408 static_cast<int>(scaleFactor * [colorInColorSpace blueComponent]));
411 // This conversion above can fail if the NSColor in question is an NSPatternColor
412 // (as many system colors are). These colors are actually a repeating pattern
413 // not just a solid color. To work around this we simply draw a 1x1 image of
414 // the color and use that pixel's color. It might be better to use an average of
415 // the colors in the pattern instead.
416 NSBitmapImageRep *offscreenRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil
423 colorSpaceName:NSDeviceRGBColorSpace
427 [NSGraphicsContext saveGraphicsState];
428 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:offscreenRep]];
429 NSEraseRect(NSMakeRect(0, 0, 1, 1));
430 [color drawSwatchInRect:NSMakeRect(0, 0, 1, 1)];
431 [NSGraphicsContext restoreGraphicsState];
434 [offscreenRep getPixel:pixel atX:0 y:0];
436 [offscreenRep release];
438 return makeRGB(pixel[0], pixel[1], pixel[2]);
441 static RGBA32 menuBackgroundColor()
443 NSBitmapImageRep *offscreenRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil
450 colorSpaceName:NSDeviceRGBColorSpace
454 CGContextRef context = static_cast<CGContextRef>([[NSGraphicsContext graphicsContextWithBitmapImageRep:offscreenRep] graphicsPort]);
455 CGRect rect = CGRectMake(0, 0, 1, 1);
456 HIThemeMenuDrawInfo drawInfo;
457 drawInfo.version = 0;
458 drawInfo.menuType = kThemeMenuTypePopUp;
459 HIThemeDrawMenuBackground(&rect, &drawInfo, context, kHIThemeOrientationInverted);
462 [offscreenRep getPixel:pixel atX:0 y:0];
464 [offscreenRep release];
466 return makeRGB(pixel[0], pixel[1], pixel[2]);
469 void RenderThemeMac::platformColorsDidChange()
471 m_systemColorCache.clear();
472 RenderTheme::platformColorsDidChange();
475 Color RenderThemeMac::systemColor(CSSValueID cssValueId) const
478 HashMap<int, RGBA32>::iterator it = m_systemColorCache.find(cssValueId);
479 if (it != m_systemColorCache.end())
484 switch (cssValueId) {
485 case CSSValueActiveborder:
486 color = convertNSColorToColor([NSColor keyboardFocusIndicatorColor]);
488 case CSSValueActivecaption:
489 color = convertNSColorToColor([NSColor windowFrameTextColor]);
491 case CSSValueAppworkspace:
492 color = convertNSColorToColor([NSColor headerColor]);
494 case CSSValueBackground:
495 // Use theme independent default
497 case CSSValueButtonface:
498 // We use this value instead of NSColor's controlColor to avoid website incompatibilities.
499 // We may want to change this to use the NSColor in future.
502 case CSSValueButtonhighlight:
503 color = convertNSColorToColor([NSColor controlHighlightColor]);
505 case CSSValueButtonshadow:
506 color = convertNSColorToColor([NSColor controlShadowColor]);
508 case CSSValueButtontext:
509 color = convertNSColorToColor([NSColor controlTextColor]);
511 case CSSValueActivebuttontext:
512 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
516 case CSSValueCaptiontext:
517 color = convertNSColorToColor([NSColor textColor]);
519 case CSSValueGraytext:
520 color = convertNSColorToColor([NSColor disabledControlTextColor]);
522 case CSSValueHighlight:
523 color = convertNSColorToColor([NSColor selectedTextBackgroundColor]);
525 case CSSValueHighlighttext:
526 color = convertNSColorToColor([NSColor selectedTextColor]);
528 case CSSValueInactiveborder:
529 color = convertNSColorToColor([NSColor controlBackgroundColor]);
531 case CSSValueInactivecaption:
532 color = convertNSColorToColor([NSColor controlBackgroundColor]);
534 case CSSValueInactivecaptiontext:
535 color = convertNSColorToColor([NSColor textColor]);
537 case CSSValueInfobackground:
538 // There is no corresponding NSColor for this so we use a hard coded value.
541 case CSSValueInfotext:
542 color = convertNSColorToColor([NSColor textColor]);
545 color = menuBackgroundColor();
547 case CSSValueMenutext:
548 color = convertNSColorToColor([NSColor selectedMenuItemTextColor]);
550 case CSSValueScrollbar:
551 color = convertNSColorToColor([NSColor scrollBarColor]);
554 color = convertNSColorToColor([NSColor textColor]);
556 case CSSValueThreeddarkshadow:
557 color = convertNSColorToColor([NSColor controlDarkShadowColor]);
559 case CSSValueThreedshadow:
560 color = convertNSColorToColor([NSColor shadowColor]);
562 case CSSValueThreedface:
563 // We use this value instead of NSColor's controlColor to avoid website incompatibilities.
564 // We may want to change this to use the NSColor in future.
567 case CSSValueThreedhighlight:
568 color = convertNSColorToColor([NSColor highlightColor]);
570 case CSSValueThreedlightshadow:
571 color = convertNSColorToColor([NSColor controlLightHighlightColor]);
573 case CSSValueWebkitFocusRingColor:
574 color = convertNSColorToColor([NSColor keyboardFocusIndicatorColor]);
577 color = convertNSColorToColor([NSColor windowBackgroundColor]);
579 case CSSValueWindowframe:
580 color = convertNSColorToColor([NSColor windowFrameColor]);
582 case CSSValueWindowtext:
583 color = convertNSColorToColor([NSColor windowFrameTextColor]);
589 if (!color.isValid())
590 color = RenderTheme::systemColor(cssValueId);
593 m_systemColorCache.set(cssValueId, color.rgb());
598 bool RenderThemeMac::usesTestModeFocusRingColor() const
600 return WebCore::usesTestModeFocusRingColor();
603 bool RenderThemeMac::isControlStyled(const RenderStyle& style, const BorderData& border,
604 const FillLayer& background, const Color& backgroundColor) const
606 if (style.appearance() == TextFieldPart || style.appearance() == TextAreaPart || style.appearance() == ListboxPart)
607 return style.border() != border;
609 // FIXME: This is horrible, but there is not much else that can be done. Menu lists cannot draw properly when
610 // scaled. They can't really draw properly when transformed either. We can't detect the transform case at style
611 // adjustment time so that will just have to stay broken. We can however detect that we're zooming. If zooming
612 // is in effect we treat it like the control is styled.
613 if (style.appearance() == MenulistPart && style.effectiveZoom() != 1.0f)
616 return RenderTheme::isControlStyled(style, border, background, backgroundColor);
619 static FloatRect inflateRect(const FloatRect& rect, const IntSize& size, const int* margins, float zoomLevel)
621 // Only do the inflation if the available width/height are too small. Otherwise try to
622 // fit the glow/check space into the available box's width/height.
623 int widthDelta = rect.width() - (size.width() + margins[leftMargin] * zoomLevel + margins[rightMargin] * zoomLevel);
624 int heightDelta = rect.height() - (size.height() + margins[topMargin] * zoomLevel + margins[bottomMargin] * zoomLevel);
625 FloatRect result(rect);
626 if (widthDelta < 0) {
627 result.setX(result.x() - margins[leftMargin] * zoomLevel);
628 result.setWidth(result.width() - widthDelta);
630 if (heightDelta < 0) {
631 result.setY(result.y() - margins[topMargin] * zoomLevel);
632 result.setHeight(result.height() - heightDelta);
637 void RenderThemeMac::adjustRepaintRect(const RenderObject& renderer, FloatRect& rect)
639 ControlPart part = renderer.style().appearance();
646 case SquareButtonPart:
647 case DefaultButtonPart:
649 case InnerSpinButtonPart:
650 return RenderTheme::adjustRepaintRect(renderer, rect);
656 float zoomLevel = renderer.style().effectiveZoom();
658 if (part == MenulistPart) {
659 setPopupButtonCellState(renderer, IntSize(rect.size()));
660 IntSize size = popupButtonSizes()[[popupButton() controlSize]];
661 size.setHeight(size.height() * zoomLevel);
662 size.setWidth(rect.width());
663 rect = inflateRect(rect, size, popupButtonMargins(), zoomLevel);
667 FloatRect RenderThemeMac::convertToPaintingRect(const RenderObject& inputRenderer, const RenderObject& partRenderer, const FloatRect& inputRect, const IntRect& r) const
669 FloatRect partRect(inputRect);
671 // Compute an offset between the part renderer and the input renderer
672 FloatSize offsetFromInputRenderer;
673 const RenderObject* renderer = &partRenderer;
674 while (renderer && renderer != &inputRenderer) {
675 RenderElement* containingRenderer = renderer->container();
676 ASSERT(containingRenderer);
677 offsetFromInputRenderer -= roundedIntSize(renderer->offsetFromContainer(*containingRenderer, LayoutPoint()));
678 renderer = containingRenderer;
680 // If the input renderer was not a container, something went wrong
681 ASSERT(renderer == &inputRenderer);
682 // Move the rect into partRenderer's coords
683 partRect.move(offsetFromInputRenderer);
684 // Account for the local drawing offset (tx, ty)
685 partRect.move(r.x(), r.y());
690 void RenderThemeMac::updateCheckedState(NSCell* cell, const RenderObject& o)
692 bool oldIndeterminate = [cell state] == NSMixedState;
693 bool indeterminate = isIndeterminate(o);
694 bool checked = isChecked(o);
696 if (oldIndeterminate != indeterminate) {
697 [cell setState:indeterminate ? NSMixedState : (checked ? NSOnState : NSOffState)];
701 bool oldChecked = [cell state] == NSOnState;
702 if (checked != oldChecked)
703 [cell setState:checked ? NSOnState : NSOffState];
706 void RenderThemeMac::updateEnabledState(NSCell* cell, const RenderObject& o)
708 bool oldEnabled = [cell isEnabled];
709 bool enabled = isEnabled(o);
710 if (enabled != oldEnabled)
711 [cell setEnabled:enabled];
714 void RenderThemeMac::updateFocusedState(NSCell* cell, const RenderObject& o)
716 bool oldFocused = [cell showsFirstResponder];
717 bool focused = isFocused(o) && o.style().outlineStyleIsAuto();
718 if (focused != oldFocused)
719 [cell setShowsFirstResponder:focused];
722 void RenderThemeMac::updatePressedState(NSCell* cell, const RenderObject& o)
724 bool oldPressed = [cell isHighlighted];
725 bool pressed = is<Element>(o.node()) && downcast<Element>(*o.node()).active();
726 if (pressed != oldPressed)
727 [cell setHighlighted:pressed];
730 bool RenderThemeMac::controlSupportsTints(const RenderObject& o) const
732 // An alternate way to implement this would be to get the appropriate cell object
733 // and call the private _needRedrawOnWindowChangedKeyState method. An advantage of
734 // that would be that we would match AppKit behavior more closely, but a disadvantage
735 // would be that we would rely on an AppKit SPI method.
740 // Checkboxes only have tint when checked.
741 if (o.style().appearance() == CheckboxPart)
744 // For now assume other controls have tint if enabled.
748 NSControlSize RenderThemeMac::controlSizeForFont(RenderStyle& style) const
750 int fontSize = style.fontSize();
752 return NSRegularControlSize;
754 return NSSmallControlSize;
755 return NSMiniControlSize;
758 NSControlSize RenderThemeMac::controlSizeForCell(NSCell*, const IntSize* sizes, const IntSize& minSize, float zoomLevel) const
760 if (minSize.width() >= static_cast<int>(sizes[NSRegularControlSize].width() * zoomLevel)
761 && minSize.height() >= static_cast<int>(sizes[NSRegularControlSize].height() * zoomLevel))
762 return NSRegularControlSize;
764 if (minSize.width() >= static_cast<int>(sizes[NSSmallControlSize].width() * zoomLevel)
765 && minSize.height() >= static_cast<int>(sizes[NSSmallControlSize].height() * zoomLevel))
766 return NSSmallControlSize;
768 return NSMiniControlSize;
771 void RenderThemeMac::setControlSize(NSCell* cell, const IntSize* sizes, const IntSize& minSize, float zoomLevel)
773 NSControlSize size = controlSizeForCell(cell, sizes, minSize, zoomLevel);
774 if (size != [cell controlSize]) // Only update if we have to, since AppKit does work even if the size is the same.
775 [cell setControlSize:size];
778 IntSize RenderThemeMac::sizeForFont(RenderStyle& style, const IntSize* sizes) const
780 if (style.effectiveZoom() != 1.0f) {
781 IntSize result = sizes[controlSizeForFont(style)];
782 return IntSize(result.width() * style.effectiveZoom(), result.height() * style.effectiveZoom());
784 return sizes[controlSizeForFont(style)];
787 IntSize RenderThemeMac::sizeForSystemFont(RenderStyle& style, const IntSize* sizes) const
789 if (style.effectiveZoom() != 1.0f) {
790 IntSize result = sizes[controlSizeForSystemFont(style)];
791 return IntSize(result.width() * style.effectiveZoom(), result.height() * style.effectiveZoom());
793 return sizes[controlSizeForSystemFont(style)];
796 void RenderThemeMac::setSizeFromFont(RenderStyle& style, const IntSize* sizes) const
798 // FIXME: Check is flawed, since it doesn't take min-width/max-width into account.
799 IntSize size = sizeForFont(style, sizes);
800 if (style.width().isIntrinsicOrAuto() && size.width() > 0)
801 style.setWidth(Length(size.width(), Fixed));
802 if (style.height().isAuto() && size.height() > 0)
803 style.setHeight(Length(size.height(), Fixed));
806 void RenderThemeMac::setFontFromControlSize(StyleResolver&, RenderStyle& style, NSControlSize controlSize) const
808 FontDescription fontDescription;
809 fontDescription.setIsAbsoluteSize(true);
811 NSFont* font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:controlSize]];
812 fontDescription.setOneFamily([font webCoreFamilyName]);
813 fontDescription.setComputedSize([font pointSize] * style.effectiveZoom());
814 fontDescription.setSpecifiedSize([font pointSize] * style.effectiveZoom());
817 style.setLineHeight(RenderStyle::initialLineHeight());
819 if (style.setFontDescription(fontDescription))
820 style.fontCascade().update(0);
823 NSControlSize RenderThemeMac::controlSizeForSystemFont(RenderStyle& style) const
825 int fontSize = style.fontSize();
826 if (fontSize >= [NSFont systemFontSizeForControlSize:NSRegularControlSize])
827 return NSRegularControlSize;
828 if (fontSize >= [NSFont systemFontSizeForControlSize:NSSmallControlSize])
829 return NSSmallControlSize;
830 return NSMiniControlSize;
833 bool RenderThemeMac::paintTextField(const RenderObject& o, const PaintInfo& paintInfo, const FloatRect& r)
835 LocalCurrentGraphicsContext localContext(paintInfo.context);
837 NSTextFieldCell *textField = this->textField();
839 GraphicsContextStateSaver stateSaver(*paintInfo.context);
841 [textField setEnabled:(isEnabled(o) && !isReadOnlyControl(o))];
842 [textField drawWithFrame:NSRect(r) inView:documentViewFor(o)];
844 [textField setControlView:nil];
849 void RenderThemeMac::adjustTextFieldStyle(StyleResolver&, RenderStyle&, Element*) const
853 bool RenderThemeMac::paintCapsLockIndicator(const RenderObject&, const PaintInfo& paintInfo, const IntRect& r)
855 if (paintInfo.context->paintingDisabled())
858 LocalCurrentGraphicsContext localContext(paintInfo.context);
859 wkDrawCapsLockIndicator(localContext.cgContext(), r);
864 bool RenderThemeMac::paintTextArea(const RenderObject& o, const PaintInfo& paintInfo, const FloatRect& r)
866 LocalCurrentGraphicsContext localContext(paintInfo.context);
867 wkDrawBezeledTextArea(r, isEnabled(o) && !isReadOnlyControl(o));
871 void RenderThemeMac::adjustTextAreaStyle(StyleResolver&, RenderStyle&, Element*) const
875 const int* RenderThemeMac::popupButtonMargins() const
877 static const int margins[3][4] =
883 return margins[[popupButton() controlSize]];
886 const IntSize* RenderThemeMac::popupButtonSizes() const
888 static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) };
892 const int* RenderThemeMac::popupButtonPadding(NSControlSize size) const
894 static const int padding[3][4] =
900 return padding[size];
903 bool RenderThemeMac::paintMenuList(const RenderObject& renderer, const PaintInfo& paintInfo, const FloatRect& rect)
905 LocalCurrentGraphicsContext localContext(paintInfo.context);
906 setPopupButtonCellState(renderer, IntSize(rect.size()));
908 NSPopUpButtonCell* popupButton = this->popupButton();
910 float zoomLevel = renderer.style().effectiveZoom();
911 IntSize size = popupButtonSizes()[[popupButton controlSize]];
912 size.setHeight(size.height() * zoomLevel);
913 size.setWidth(rect.width());
915 // Now inflate it to account for the shadow.
916 FloatRect inflatedRect = rect;
917 if (rect.width() >= minimumMenuListSize(renderer.style()))
918 inflatedRect = inflateRect(rect, size, popupButtonMargins(), zoomLevel);
920 GraphicsContextStateSaver stateSaver(*paintInfo.context);
922 // On Leopard, the cell will draw outside of the given rect, so we have to clip to the rect
923 paintInfo.context->clip(inflatedRect);
925 if (zoomLevel != 1.0f) {
926 inflatedRect.setWidth(inflatedRect.width() / zoomLevel);
927 inflatedRect.setHeight(inflatedRect.height() / zoomLevel);
928 paintInfo.context->translate(inflatedRect.x(), inflatedRect.y());
929 paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel));
930 paintInfo.context->translate(-inflatedRect.x(), -inflatedRect.y());
933 NSView *view = documentViewFor(renderer);
934 [popupButton drawWithFrame:inflatedRect inView:view];
935 if (isFocused(renderer) && renderer.style().outlineStyleIsAuto()) {
936 if (wkDrawCellFocusRingWithFrameAtTime(popupButton, inflatedRect, view, std::numeric_limits<double>::max()))
937 renderer.document().page()->focusController().setFocusedElementNeedsRepaint();
940 [popupButton setControlView:nil];
945 #if ENABLE(METER_ELEMENT)
947 IntSize RenderThemeMac::meterSizeForBounds(const RenderMeter& renderMeter, const IntRect& bounds) const
949 if (NoControlPart == renderMeter.style().appearance())
950 return bounds.size();
952 NSLevelIndicatorCell* cell = levelIndicatorFor(renderMeter);
953 // Makes enough room for cell's intrinsic size.
954 NSSize cellSize = [cell cellSizeForBounds:IntRect(IntPoint(), bounds.size())];
955 return IntSize(bounds.width() < cellSize.width ? cellSize.width : bounds.width(),
956 bounds.height() < cellSize.height ? cellSize.height : bounds.height());
959 bool RenderThemeMac::paintMeter(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
961 if (!is<RenderMeter>(renderObject))
964 LocalCurrentGraphicsContext localContext(paintInfo.context);
966 NSLevelIndicatorCell* cell = levelIndicatorFor(downcast<RenderMeter>(renderObject));
967 GraphicsContextStateSaver stateSaver(*paintInfo.context);
969 [cell drawWithFrame:rect inView:documentViewFor(renderObject)];
970 [cell setControlView:nil];
974 bool RenderThemeMac::supportsMeter(ControlPart part) const
977 case RelevancyLevelIndicatorPart:
978 case DiscreteCapacityLevelIndicatorPart:
979 case RatingLevelIndicatorPart:
981 case ContinuousCapacityLevelIndicatorPart:
988 NSLevelIndicatorStyle RenderThemeMac::levelIndicatorStyleFor(ControlPart part) const
991 case RelevancyLevelIndicatorPart:
992 return NSRelevancyLevelIndicatorStyle;
993 case DiscreteCapacityLevelIndicatorPart:
994 return NSDiscreteCapacityLevelIndicatorStyle;
995 case RatingLevelIndicatorPart:
996 return NSRatingLevelIndicatorStyle;
998 case ContinuousCapacityLevelIndicatorPart:
1000 return NSContinuousCapacityLevelIndicatorStyle;
1005 NSLevelIndicatorCell* RenderThemeMac::levelIndicatorFor(const RenderMeter& renderMeter) const
1007 const RenderStyle& style = renderMeter.style();
1008 ASSERT(style.appearance() != NoControlPart);
1010 if (!m_levelIndicator)
1011 m_levelIndicator = adoptNS([[NSLevelIndicatorCell alloc] initWithLevelIndicatorStyle:NSContinuousCapacityLevelIndicatorStyle]);
1012 NSLevelIndicatorCell* cell = m_levelIndicator.get();
1014 HTMLMeterElement* element = renderMeter.meterElement();
1015 double value = element->value();
1017 // Because NSLevelIndicatorCell does not support optimum-in-the-middle type coloring,
1018 // we explicitly control the color instead giving low and high value to NSLevelIndicatorCell as is.
1019 switch (element->gaugeRegion()) {
1020 case HTMLMeterElement::GaugeRegionOptimum:
1021 // Make meter the green
1022 [cell setWarningValue:value + 1];
1023 [cell setCriticalValue:value + 2];
1025 case HTMLMeterElement::GaugeRegionSuboptimal:
1026 // Make the meter yellow
1027 [cell setWarningValue:value - 1];
1028 [cell setCriticalValue:value + 1];
1030 case HTMLMeterElement::GaugeRegionEvenLessGood:
1031 // Make the meter red
1032 [cell setWarningValue:value - 2];
1033 [cell setCriticalValue:value - 1];
1037 [cell setLevelIndicatorStyle:levelIndicatorStyleFor(style.appearance())];
1038 [cell setBaseWritingDirection:style.isLeftToRightDirection() ? NSWritingDirectionLeftToRight : NSWritingDirectionRightToLeft];
1039 [cell setMinValue:element->min()];
1040 [cell setMaxValue:element->max()];
1041 RetainPtr<NSNumber> valueObject = [NSNumber numberWithDouble:value];
1042 [cell setObjectValue:valueObject.get()];
1049 const IntSize* RenderThemeMac::progressBarSizes() const
1051 static const IntSize sizes[3] = { IntSize(0, 20), IntSize(0, 12), IntSize(0, 12) };
1055 const int* RenderThemeMac::progressBarMargins(NSControlSize controlSize) const
1057 static const int margins[3][4] =
1063 return margins[controlSize];
1066 IntRect RenderThemeMac::progressBarRectForBounds(const RenderObject& renderObject, const IntRect& bounds) const
1068 // Workaround until <rdar://problem/15855086> is fixed.
1069 int maxDimension = static_cast<int>(std::numeric_limits<ushort>::max());
1070 IntRect progressBarBounds(bounds.x(), bounds.y(), std::min(bounds.width(), maxDimension), std::min(bounds.height(), maxDimension));
1071 if (NoControlPart == renderObject.style().appearance())
1072 return progressBarBounds;
1074 float zoomLevel = renderObject.style().effectiveZoom();
1075 NSControlSize controlSize = controlSizeForFont(renderObject.style());
1076 IntSize size = progressBarSizes()[controlSize];
1077 size.setHeight(size.height() * zoomLevel);
1078 size.setWidth(progressBarBounds.width());
1080 // Now inflate it to account for the shadow.
1081 IntRect inflatedRect = progressBarBounds;
1082 if (progressBarBounds.height() <= minimumProgressBarHeight(renderObject.style()))
1083 inflatedRect = IntRect(inflateRect(inflatedRect, size, progressBarMargins(controlSize), zoomLevel));
1085 return inflatedRect;
1088 int RenderThemeMac::minimumProgressBarHeight(RenderStyle& style) const
1090 return sizeForSystemFont(style, progressBarSizes()).height();
1093 double RenderThemeMac::animationRepeatIntervalForProgressBar(RenderProgress&) const
1095 return progressAnimationFrameRate;
1098 double RenderThemeMac::animationDurationForProgressBar(RenderProgress&) const
1100 return progressAnimationNumFrames * progressAnimationFrameRate;
1103 void RenderThemeMac::adjustProgressBarStyle(StyleResolver&, RenderStyle&, Element*) const
1107 bool RenderThemeMac::paintProgressBar(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1109 if (!is<RenderProgress>(renderObject))
1112 IntRect inflatedRect = progressBarRectForBounds(renderObject, rect);
1113 NSControlSize controlSize = controlSizeForFont(renderObject.style());
1115 const auto& renderProgress = downcast<RenderProgress>(renderObject);
1116 HIThemeTrackDrawInfo trackInfo;
1117 trackInfo.version = 0;
1118 if (controlSize == NSRegularControlSize)
1119 trackInfo.kind = renderProgress.position() < 0 ? kThemeLargeIndeterminateBar : kThemeLargeProgressBar;
1121 trackInfo.kind = renderProgress.position() < 0 ? kThemeMediumIndeterminateBar : kThemeMediumProgressBar;
1123 float deviceScaleFactor = renderObject.document().deviceScaleFactor();
1124 trackInfo.bounds = IntRect(IntPoint(), inflatedRect.size());
1126 trackInfo.max = std::numeric_limits<SInt32>::max();
1127 trackInfo.value = lround(renderProgress.position() * nextafter(trackInfo.max, 0));
1128 trackInfo.trackInfo.progress.phase = lround(renderProgress.animationProgress() * nextafter(progressAnimationNumFrames, 0) * deviceScaleFactor);
1129 trackInfo.attributes = kThemeTrackHorizontal;
1130 trackInfo.enableState = isActive(renderObject) ? kThemeTrackActive : kThemeTrackInactive;
1131 trackInfo.reserved = 0;
1132 trackInfo.filler1 = 0;
1134 std::unique_ptr<ImageBuffer> imageBuffer = ImageBuffer::createCompatibleBuffer(inflatedRect.size(), deviceScaleFactor, ColorSpaceDeviceRGB, paintInfo.context, true);
1138 ContextContainer cgContextContainer(imageBuffer->context());
1139 CGContextRef cgContext = cgContextContainer.context();
1140 HIThemeDrawTrack(&trackInfo, 0, cgContext, kHIThemeOrientationNormal);
1142 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1144 if (!renderProgress.style().isLeftToRightDirection()) {
1145 paintInfo.context->translate(2 * inflatedRect.x() + inflatedRect.width(), 0);
1146 paintInfo.context->scale(FloatSize(-1, 1));
1149 paintInfo.context->drawImageBuffer(imageBuffer.get(), ColorSpaceDeviceRGB, inflatedRect.location());
1153 const float baseFontSize = 11.0f;
1154 const float baseArrowHeight = 4.0f;
1155 const float baseArrowWidth = 5.0f;
1156 const float baseSpaceBetweenArrows = 2.0f;
1157 const int arrowPaddingLeft = 6;
1158 const int arrowPaddingRight = 6;
1159 const int paddingBeforeSeparator = 4;
1160 const int baseBorderRadius = 5;
1161 const int styledPopupPaddingLeft = 8;
1162 const int styledPopupPaddingTop = 1;
1163 const int styledPopupPaddingBottom = 2;
1165 static void TopGradientInterpolate(void*, const CGFloat* inData, CGFloat* outData)
1167 static float dark[4] = { 1.0f, 1.0f, 1.0f, 0.4f };
1168 static float light[4] = { 1.0f, 1.0f, 1.0f, 0.15f };
1169 float a = inData[0];
1171 for (i = 0; i < 4; i++)
1172 outData[i] = (1.0f - a) * dark[i] + a * light[i];
1175 static void BottomGradientInterpolate(void*, const CGFloat* inData, CGFloat* outData)
1177 static float dark[4] = { 1.0f, 1.0f, 1.0f, 0.0f };
1178 static float light[4] = { 1.0f, 1.0f, 1.0f, 0.3f };
1179 float a = inData[0];
1181 for (i = 0; i < 4; i++)
1182 outData[i] = (1.0f - a) * dark[i] + a * light[i];
1185 static void MainGradientInterpolate(void*, const CGFloat* inData, CGFloat* outData)
1187 static float dark[4] = { 0.0f, 0.0f, 0.0f, 0.15f };
1188 static float light[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
1189 float a = inData[0];
1191 for (i = 0; i < 4; i++)
1192 outData[i] = (1.0f - a) * dark[i] + a * light[i];
1195 static void TrackGradientInterpolate(void*, const CGFloat* inData, CGFloat* outData)
1197 static float dark[4] = { 0.0f, 0.0f, 0.0f, 0.678f };
1198 static float light[4] = { 0.0f, 0.0f, 0.0f, 0.13f };
1199 float a = inData[0];
1201 for (i = 0; i < 4; i++)
1202 outData[i] = (1.0f - a) * dark[i] + a * light[i];
1205 void RenderThemeMac::paintMenuListButtonGradients(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1210 ContextContainer cgContextContainer(paintInfo.context);
1211 CGContextRef context = cgContextContainer.context();
1213 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1215 FloatRoundedRect border = FloatRoundedRect(o.style().getRoundedBorderFor(r));
1216 int radius = border.radii().topLeft().width();
1218 CGColorSpaceRef cspace = deviceRGBColorSpaceRef();
1220 FloatRect topGradient(r.x(), r.y(), r.width(), r.height() / 2.0f);
1221 struct CGFunctionCallbacks topCallbacks = { 0, TopGradientInterpolate, NULL };
1222 RetainPtr<CGFunctionRef> topFunction = adoptCF(CGFunctionCreate(NULL, 1, NULL, 4, NULL, &topCallbacks));
1223 RetainPtr<CGShadingRef> topShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(topGradient.x(), topGradient.y()), CGPointMake(topGradient.x(), topGradient.maxY()), topFunction.get(), false, false));
1225 FloatRect bottomGradient(r.x() + radius, r.y() + r.height() / 2.0f, r.width() - 2.0f * radius, r.height() / 2.0f);
1226 struct CGFunctionCallbacks bottomCallbacks = { 0, BottomGradientInterpolate, NULL };
1227 RetainPtr<CGFunctionRef> bottomFunction = adoptCF(CGFunctionCreate(NULL, 1, NULL, 4, NULL, &bottomCallbacks));
1228 RetainPtr<CGShadingRef> bottomShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(bottomGradient.x(), bottomGradient.y()), CGPointMake(bottomGradient.x(), bottomGradient.maxY()), bottomFunction.get(), false, false));
1230 struct CGFunctionCallbacks mainCallbacks = { 0, MainGradientInterpolate, NULL };
1231 RetainPtr<CGFunctionRef> mainFunction = adoptCF(CGFunctionCreate(NULL, 1, NULL, 4, NULL, &mainCallbacks));
1232 RetainPtr<CGShadingRef> mainShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(r.x(), r.y()), CGPointMake(r.x(), r.maxY()), mainFunction.get(), false, false));
1234 RetainPtr<CGShadingRef> leftShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(r.x(), r.y()), CGPointMake(r.x() + radius, r.y()), mainFunction.get(), false, false));
1236 RetainPtr<CGShadingRef> rightShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(r.maxX(), r.y()), CGPointMake(r.maxX() - radius, r.y()), mainFunction.get(), false, false));
1239 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1240 CGContextClipToRect(context, r);
1241 paintInfo.context->clipRoundedRect(border);
1242 context = cgContextContainer.context();
1243 CGContextDrawShading(context, mainShading.get());
1247 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1248 CGContextClipToRect(context, topGradient);
1249 paintInfo.context->clipRoundedRect(FloatRoundedRect(enclosingIntRect(topGradient), border.radii().topLeft(), border.radii().topRight(), IntSize(), IntSize()));
1250 context = cgContextContainer.context();
1251 CGContextDrawShading(context, topShading.get());
1254 if (!bottomGradient.isEmpty()) {
1255 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1256 CGContextClipToRect(context, bottomGradient);
1257 paintInfo.context->clipRoundedRect(FloatRoundedRect(enclosingIntRect(bottomGradient), IntSize(), IntSize(), border.radii().bottomLeft(), border.radii().bottomRight()));
1258 context = cgContextContainer.context();
1259 CGContextDrawShading(context, bottomShading.get());
1263 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1264 CGContextClipToRect(context, r);
1265 paintInfo.context->clipRoundedRect(border);
1266 context = cgContextContainer.context();
1267 CGContextDrawShading(context, leftShading.get());
1268 CGContextDrawShading(context, rightShading.get());
1272 bool RenderThemeMac::paintMenuListButtonDecorations(const RenderObject& renderer, const PaintInfo& paintInfo, const FloatRect& rect)
1274 IntRect bounds = IntRect(rect.x() + renderer.style().borderLeftWidth(),
1275 rect.y() + renderer.style().borderTopWidth(),
1276 rect.width() - renderer.style().borderLeftWidth() - renderer.style().borderRightWidth(),
1277 rect.height() - renderer.style().borderTopWidth() - renderer.style().borderBottomWidth());
1278 // Draw the gradients to give the styled popup menu a button appearance
1279 paintMenuListButtonGradients(renderer, paintInfo, bounds);
1281 // 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
1282 float fontScale = std::min(renderer.style().fontSize() / baseFontSize, bounds.height() / (baseArrowHeight * 2 + baseSpaceBetweenArrows));
1283 float centerY = bounds.y() + bounds.height() / 2.0f;
1284 float arrowHeight = baseArrowHeight * fontScale;
1285 float arrowWidth = baseArrowWidth * fontScale;
1286 float leftEdge = bounds.maxX() - arrowPaddingRight * renderer.style().effectiveZoom() - arrowWidth;
1287 float spaceBetweenArrows = baseSpaceBetweenArrows * fontScale;
1289 if (bounds.width() < arrowWidth + arrowPaddingLeft * renderer.style().effectiveZoom())
1292 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1294 paintInfo.context->setFillColor(renderer.style().visitedDependentColor(CSSPropertyColor), renderer.style().colorSpace());
1295 paintInfo.context->setStrokeStyle(NoStroke);
1297 FloatPoint arrow1[3];
1298 arrow1[0] = FloatPoint(leftEdge, centerY - spaceBetweenArrows / 2.0f);
1299 arrow1[1] = FloatPoint(leftEdge + arrowWidth, centerY - spaceBetweenArrows / 2.0f);
1300 arrow1[2] = FloatPoint(leftEdge + arrowWidth / 2.0f, centerY - spaceBetweenArrows / 2.0f - arrowHeight);
1302 // Draw the top arrow
1303 paintInfo.context->drawConvexPolygon(3, arrow1, true);
1305 FloatPoint arrow2[3];
1306 arrow2[0] = FloatPoint(leftEdge, centerY + spaceBetweenArrows / 2.0f);
1307 arrow2[1] = FloatPoint(leftEdge + arrowWidth, centerY + spaceBetweenArrows / 2.0f);
1308 arrow2[2] = FloatPoint(leftEdge + arrowWidth / 2.0f, centerY + spaceBetweenArrows / 2.0f + arrowHeight);
1310 // Draw the bottom arrow
1311 paintInfo.context->drawConvexPolygon(3, arrow2, true);
1313 Color leftSeparatorColor(0, 0, 0, 40);
1314 Color rightSeparatorColor(255, 255, 255, 40);
1316 // FIXME: Should the separator thickness and space be scaled up by fontScale?
1317 int separatorSpace = 2; // Deliberately ignores zoom since it looks nicer if it stays thin.
1318 int leftEdgeOfSeparator = static_cast<int>(leftEdge - arrowPaddingLeft * renderer.style().effectiveZoom()); // FIXME: Round?
1320 // Draw the separator to the left of the arrows
1321 paintInfo.context->setStrokeThickness(1); // Deliberately ignores zoom since it looks nicer if it stays thin.
1322 paintInfo.context->setStrokeStyle(SolidStroke);
1323 paintInfo.context->setStrokeColor(leftSeparatorColor, ColorSpaceDeviceRGB);
1324 paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator, bounds.y()),
1325 IntPoint(leftEdgeOfSeparator, bounds.maxY()));
1327 paintInfo.context->setStrokeColor(rightSeparatorColor, ColorSpaceDeviceRGB);
1328 paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.y()),
1329 IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.maxY()));
1333 static const IntSize* menuListButtonSizes()
1335 static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) };
1339 void RenderThemeMac::adjustMenuListStyle(StyleResolver& styleResolver, RenderStyle& style, Element* e) const
1341 NSControlSize controlSize = controlSizeForFont(style);
1343 style.resetBorder();
1344 style.resetPadding();
1346 // Height is locked to auto.
1347 style.setHeight(Length(Auto));
1349 // White-space is locked to pre
1350 style.setWhiteSpace(PRE);
1352 // Set the foreground color to black or gray when we have the aqua look.
1353 // Cast to RGB32 is to work around a compiler bug.
1354 style.setColor(e && !e->isDisabledFormControl() ? static_cast<RGBA32>(Color::black) : Color::darkGray);
1356 // Set the button's vertical size.
1357 setSizeFromFont(style, menuListButtonSizes());
1359 // 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
1360 // a reasonable control size, but once that control size is determined, we throw that font away and use the appropriate
1361 // system font for the control size instead.
1362 setFontFromControlSize(styleResolver, style, controlSize);
1364 style.setBoxShadow(nullptr);
1367 int RenderThemeMac::popupInternalPaddingLeft(RenderStyle& style) const
1369 if (style.appearance() == MenulistPart)
1370 return popupButtonPadding(controlSizeForFont(style))[leftPadding] * style.effectiveZoom();
1371 if (style.appearance() == MenulistButtonPart)
1372 return styledPopupPaddingLeft * style.effectiveZoom();
1376 int RenderThemeMac::popupInternalPaddingRight(RenderStyle& style) const
1378 if (style.appearance() == MenulistPart)
1379 return popupButtonPadding(controlSizeForFont(style))[rightPadding] * style.effectiveZoom();
1380 if (style.appearance() == MenulistButtonPart) {
1381 float fontScale = style.fontSize() / baseFontSize;
1382 float arrowWidth = baseArrowWidth * fontScale;
1383 return static_cast<int>(ceilf(arrowWidth + (arrowPaddingLeft + arrowPaddingRight + paddingBeforeSeparator) * style.effectiveZoom()));
1388 int RenderThemeMac::popupInternalPaddingTop(RenderStyle& style) const
1390 if (style.appearance() == MenulistPart)
1391 return popupButtonPadding(controlSizeForFont(style))[topPadding] * style.effectiveZoom();
1392 if (style.appearance() == MenulistButtonPart)
1393 return styledPopupPaddingTop * style.effectiveZoom();
1397 int RenderThemeMac::popupInternalPaddingBottom(RenderStyle& style) const
1399 if (style.appearance() == MenulistPart)
1400 return popupButtonPadding(controlSizeForFont(style))[bottomPadding] * style.effectiveZoom();
1401 if (style.appearance() == MenulistButtonPart)
1402 return styledPopupPaddingBottom * style.effectiveZoom();
1406 PopupMenuStyle::PopupMenuSize RenderThemeMac::popupMenuSize(const RenderStyle& style, IntRect& rect) const
1408 NSPopUpButtonCell* popupButton = this->popupButton();
1409 NSControlSize size = controlSizeForCell(popupButton, popupButtonSizes(), rect.size(), style.effectiveZoom());
1411 case NSRegularControlSize:
1412 return PopupMenuStyle::PopupMenuSizeNormal;
1413 case NSSmallControlSize:
1414 return PopupMenuStyle::PopupMenuSizeSmall;
1415 case NSMiniControlSize:
1416 return PopupMenuStyle::PopupMenuSizeMini;
1418 return PopupMenuStyle::PopupMenuSizeNormal;
1422 void RenderThemeMac::adjustMenuListButtonStyle(StyleResolver&, RenderStyle& style, Element*) const
1424 float fontScale = style.fontSize() / baseFontSize;
1426 style.resetPadding();
1427 style.setBorderRadius(IntSize(int(baseBorderRadius + fontScale - 1), int(baseBorderRadius + fontScale - 1))); // FIXME: Round up?
1429 const int minHeight = 15;
1430 style.setMinHeight(Length(minHeight, Fixed));
1432 style.setLineHeight(RenderStyle::initialLineHeight());
1435 void RenderThemeMac::setPopupButtonCellState(const RenderObject& o, const IntSize& buttonSize)
1437 NSPopUpButtonCell* popupButton = this->popupButton();
1439 // Set the control size based off the rectangle we're painting into.
1440 setControlSize(popupButton, popupButtonSizes(), buttonSize, o.style().effectiveZoom());
1442 // Update the various states we respond to.
1443 updateCheckedState(popupButton, o);
1444 updateEnabledState(popupButton, o);
1445 updatePressedState(popupButton, o);
1448 const IntSize* RenderThemeMac::menuListSizes() const
1450 static const IntSize sizes[3] = { IntSize(9, 0), IntSize(5, 0), IntSize(0, 0) };
1454 int RenderThemeMac::minimumMenuListSize(RenderStyle& style) const
1456 return sizeForSystemFont(style, menuListSizes()).width();
1459 const int trackWidth = 5;
1460 const int trackRadius = 2;
1462 void RenderThemeMac::adjustSliderTrackStyle(StyleResolver&, RenderStyle& style, Element*) const
1464 style.setBoxShadow(nullptr);
1467 bool RenderThemeMac::paintSliderTrack(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1470 float zoomLevel = o.style().effectiveZoom();
1471 float zoomedTrackWidth = trackWidth * zoomLevel;
1473 if (o.style().appearance() == SliderHorizontalPart || o.style().appearance() == MediaSliderPart) {
1474 bounds.setHeight(zoomedTrackWidth);
1475 bounds.setY(r.y() + r.height() / 2 - zoomedTrackWidth / 2);
1476 } else if (o.style().appearance() == SliderVerticalPart) {
1477 bounds.setWidth(zoomedTrackWidth);
1478 bounds.setX(r.x() + r.width() / 2 - zoomedTrackWidth / 2);
1481 LocalCurrentGraphicsContext localContext(paintInfo.context);
1482 CGContextRef context = localContext.cgContext();
1483 CGColorSpaceRef cspace = deviceRGBColorSpaceRef();
1485 #if ENABLE(DATALIST_ELEMENT)
1486 paintSliderTicks(o, paintInfo, r);
1489 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1490 CGContextClipToRect(context, bounds);
1492 struct CGFunctionCallbacks mainCallbacks = { 0, TrackGradientInterpolate, NULL };
1493 RetainPtr<CGFunctionRef> mainFunction = adoptCF(CGFunctionCreate(NULL, 1, NULL, 4, NULL, &mainCallbacks));
1494 RetainPtr<CGShadingRef> mainShading;
1495 if (o.style().appearance() == SliderVerticalPart)
1496 mainShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(bounds.x(), bounds.maxY()), CGPointMake(bounds.maxX(), bounds.maxY()), mainFunction.get(), false, false));
1498 mainShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(bounds.x(), bounds.y()), CGPointMake(bounds.x(), bounds.maxY()), mainFunction.get(), false, false));
1500 IntSize radius(trackRadius, trackRadius);
1501 paintInfo.context->clipRoundedRect(FloatRoundedRect(bounds, radius, radius, radius, radius));
1502 context = localContext.cgContext();
1503 CGContextDrawShading(context, mainShading.get());
1508 void RenderThemeMac::adjustSliderThumbStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const
1510 RenderTheme::adjustSliderThumbStyle(styleResolver, style, element);
1511 style.setBoxShadow(nullptr);
1514 const float verticalSliderHeightPadding = 0.1f;
1516 bool RenderThemeMac::paintSliderThumb(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1518 NSSliderCell* sliderThumbCell = o.style().appearance() == SliderThumbVerticalPart
1519 ? sliderThumbVertical()
1520 : sliderThumbHorizontal();
1522 LocalCurrentGraphicsContext localContext(paintInfo.context);
1524 // Update the various states we respond to.
1525 updateEnabledState(sliderThumbCell, o);
1526 Element* focusDelegate = is<Element>(o.node()) ? downcast<Element>(*o.node()).focusDelegate() : nullptr;
1528 updateFocusedState(sliderThumbCell, *focusDelegate->renderer());
1530 // Update the pressed state using the NSCell tracking methods, since that's how NSSliderCell keeps track of it.
1532 if (o.style().appearance() == SliderThumbVerticalPart)
1533 oldPressed = m_isSliderThumbVerticalPressed;
1535 oldPressed = m_isSliderThumbHorizontalPressed;
1537 bool pressed = isPressed(o);
1539 if (o.style().appearance() == SliderThumbVerticalPart)
1540 m_isSliderThumbVerticalPressed = pressed;
1542 m_isSliderThumbHorizontalPressed = pressed;
1544 if (pressed != oldPressed) {
1546 [sliderThumbCell startTrackingAt:NSPoint() inView:nil];
1548 [sliderThumbCell stopTracking:NSPoint() at:NSPoint() inView:nil mouseIsUp:YES];
1551 FloatRect bounds = r;
1552 // Make the height of the vertical slider slightly larger so NSSliderCell will draw a vertical slider.
1553 if (o.style().appearance() == SliderThumbVerticalPart)
1554 bounds.setHeight(bounds.height() + verticalSliderHeightPadding * o.style().effectiveZoom());
1556 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1557 float zoomLevel = o.style().effectiveZoom();
1559 FloatRect unzoomedRect = bounds;
1560 if (zoomLevel != 1.0f) {
1561 unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel);
1562 unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel);
1563 paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y());
1564 paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel));
1565 paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y());
1568 [sliderThumbCell drawInteriorWithFrame:unzoomedRect inView:documentViewFor(o)];
1569 [sliderThumbCell setControlView:nil];
1574 bool RenderThemeMac::paintSearchField(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1576 LocalCurrentGraphicsContext localContext(paintInfo.context);
1577 NSSearchFieldCell* search = this->search();
1579 setSearchCellState(o, r);
1581 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1583 float zoomLevel = o.style().effectiveZoom();
1585 IntRect unzoomedRect = r;
1587 if (zoomLevel != 1.0f) {
1588 unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel);
1589 unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel);
1590 paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y());
1591 paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel));
1592 paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y());
1595 // Set the search button to nil before drawing. Then reset it so we can draw it later.
1596 [search setSearchButtonCell:nil];
1598 [search drawWithFrame:NSRect(unzoomedRect) inView:documentViewFor(o)];
1600 [search setControlView:nil];
1601 [search resetSearchButtonCell];
1606 void RenderThemeMac::setSearchCellState(const RenderObject& o, const IntRect&)
1608 NSSearchFieldCell* search = this->search();
1610 [search setControlSize:controlSizeForFont(o.style())];
1612 // Update the various states we respond to.
1613 updateEnabledState(search, o);
1614 updateFocusedState(search, o);
1617 const IntSize* RenderThemeMac::searchFieldSizes() const
1619 static const IntSize sizes[3] = { IntSize(0, 22), IntSize(0, 19), IntSize(0, 17) };
1623 void RenderThemeMac::setSearchFieldSize(RenderStyle& style) const
1625 // If the width and height are both specified, then we have nothing to do.
1626 if (!style.width().isIntrinsicOrAuto() && !style.height().isAuto())
1629 // Use the font size to determine the intrinsic width of the control.
1630 setSizeFromFont(style, searchFieldSizes());
1633 void RenderThemeMac::adjustSearchFieldStyle(StyleResolver& styleResolver, RenderStyle& style, Element*) const
1636 style.resetBorder();
1637 const short borderWidth = 2 * style.effectiveZoom();
1638 style.setBorderLeftWidth(borderWidth);
1639 style.setBorderLeftStyle(INSET);
1640 style.setBorderRightWidth(borderWidth);
1641 style.setBorderRightStyle(INSET);
1642 style.setBorderBottomWidth(borderWidth);
1643 style.setBorderBottomStyle(INSET);
1644 style.setBorderTopWidth(borderWidth);
1645 style.setBorderTopStyle(INSET);
1648 style.setHeight(Length(Auto));
1649 setSearchFieldSize(style);
1651 // Override padding size to match AppKit text positioning.
1652 const int padding = 1 * style.effectiveZoom();
1653 style.setPaddingLeft(Length(padding, Fixed));
1654 style.setPaddingRight(Length(padding, Fixed));
1655 style.setPaddingTop(Length(padding, Fixed));
1656 style.setPaddingBottom(Length(padding, Fixed));
1658 NSControlSize controlSize = controlSizeForFont(style);
1659 setFontFromControlSize(styleResolver, style, controlSize);
1661 style.setBoxShadow(nullptr);
1664 bool RenderThemeMac::paintSearchFieldCancelButton(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1666 Element* input = o.node()->shadowHost();
1668 input = downcast<Element>(o.node());
1670 if (!input->renderer()->isBox())
1673 LocalCurrentGraphicsContext localContext(paintInfo.context);
1674 setSearchCellState(*input->renderer(), r);
1676 NSSearchFieldCell* search = this->search();
1678 if (!input->isDisabledFormControl() && (is<HTMLTextFormControlElement>(*input) && !downcast<HTMLTextFormControlElement>(*input).isReadOnly()))
1679 updatePressedState([search cancelButtonCell], o);
1680 else if ([[search cancelButtonCell] isHighlighted])
1681 [[search cancelButtonCell] setHighlighted:NO];
1683 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1685 float zoomLevel = o.style().effectiveZoom();
1687 FloatRect localBounds = [search cancelButtonRectForBounds:NSRect(input->renderBox()->pixelSnappedBorderBoxRect())];
1688 localBounds = convertToPaintingRect(*input->renderer(), o, localBounds, r);
1690 FloatRect unzoomedRect(localBounds);
1691 if (zoomLevel != 1.0f) {
1692 unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel);
1693 unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel);
1694 paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y());
1695 paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel));
1696 paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y());
1699 [[search cancelButtonCell] drawWithFrame:unzoomedRect inView:documentViewFor(o)];
1700 [[search cancelButtonCell] setControlView:nil];
1704 const IntSize* RenderThemeMac::cancelButtonSizes() const
1706 static const IntSize sizes[3] = { IntSize(16, 13), IntSize(13, 11), IntSize(13, 9) };
1710 void RenderThemeMac::adjustSearchFieldCancelButtonStyle(StyleResolver&, RenderStyle& style, Element*) const
1712 IntSize size = sizeForSystemFont(style, cancelButtonSizes());
1713 style.setWidth(Length(size.width(), Fixed));
1714 style.setHeight(Length(size.height(), Fixed));
1715 style.setBoxShadow(nullptr);
1718 const IntSize* RenderThemeMac::resultsButtonSizes() const
1720 static const IntSize sizes[3] = { IntSize(19, 13), IntSize(17, 11), IntSize(17, 9) };
1724 const int emptyResultsOffset = 9;
1725 void RenderThemeMac::adjustSearchFieldDecorationPartStyle(StyleResolver&, RenderStyle& style, Element*) const
1727 IntSize size = sizeForSystemFont(style, resultsButtonSizes());
1728 style.setWidth(Length(size.width() - emptyResultsOffset, Fixed));
1729 style.setHeight(Length(size.height(), Fixed));
1730 style.setBoxShadow(nullptr);
1733 bool RenderThemeMac::paintSearchFieldDecorationPart(const RenderObject&, const PaintInfo&, const IntRect&)
1738 void RenderThemeMac::adjustSearchFieldResultsDecorationPartStyle(StyleResolver&, RenderStyle& style, Element*) const
1740 IntSize size = sizeForSystemFont(style, resultsButtonSizes());
1741 style.setWidth(Length(size.width(), Fixed));
1742 style.setHeight(Length(size.height(), Fixed));
1743 style.setBoxShadow(nullptr);
1746 bool RenderThemeMac::paintSearchFieldResultsDecorationPart(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1748 Node* input = o.node()->shadowHost();
1751 if (!input->renderer()->isBox())
1754 LocalCurrentGraphicsContext localContext(paintInfo.context);
1755 setSearchCellState(*input->renderer(), r);
1757 NSSearchFieldCell* search = this->search();
1759 if ([search searchMenuTemplate] != nil)
1760 [search setSearchMenuTemplate:nil];
1762 FloatRect localBounds = [search searchButtonRectForBounds:NSRect(input->renderBox()->pixelSnappedBorderBoxRect())];
1763 localBounds = convertToPaintingRect(*input->renderer(), o, localBounds, r);
1765 [[search searchButtonCell] drawWithFrame:localBounds inView:documentViewFor(o)];
1766 [[search searchButtonCell] setControlView:nil];
1770 const int resultsArrowWidth = 5;
1771 void RenderThemeMac::adjustSearchFieldResultsButtonStyle(StyleResolver&, RenderStyle& style, Element*) const
1773 IntSize size = sizeForSystemFont(style, resultsButtonSizes());
1774 style.setWidth(Length(size.width() + resultsArrowWidth, Fixed));
1775 style.setHeight(Length(size.height(), Fixed));
1776 style.setBoxShadow(nullptr);
1779 bool RenderThemeMac::paintSearchFieldResultsButton(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1781 Node* input = o.node()->shadowHost();
1784 if (!input->renderer()->isBox())
1787 LocalCurrentGraphicsContext localContext(paintInfo.context);
1788 setSearchCellState(*input->renderer(), r);
1790 NSSearchFieldCell* search = this->search();
1792 if (![search searchMenuTemplate])
1793 [search setSearchMenuTemplate:searchMenuTemplate()];
1795 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1796 float zoomLevel = o.style().effectiveZoom();
1798 FloatRect localBounds = [search searchButtonRectForBounds:NSRect(input->renderBox()->pixelSnappedBorderBoxRect())];
1799 localBounds = convertToPaintingRect(*input->renderer(), o, localBounds, r);
1801 IntRect unzoomedRect(localBounds);
1802 if (zoomLevel != 1.0f) {
1803 unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel);
1804 unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel);
1805 paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y());
1806 paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel));
1807 paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y());
1810 [[search searchButtonCell] drawWithFrame:unzoomedRect inView:documentViewFor(o)];
1811 [[search searchButtonCell] setControlView:nil];
1816 bool RenderThemeMac::paintSnapshottedPluginOverlay(const RenderObject& renderer, const PaintInfo& paintInfo, const IntRect&)
1818 if (paintInfo.phase != PaintPhaseBlockBackground)
1821 if (!is<RenderBlock>(renderer))
1824 const RenderBlock& renderBlock = downcast<RenderBlock>(renderer);
1826 LayoutUnit contentWidth = renderBlock.contentWidth();
1827 LayoutUnit contentHeight = renderBlock.contentHeight();
1828 if (!contentWidth || !contentHeight)
1831 GraphicsContext* context = paintInfo.context;
1833 LayoutSize contentSize(contentWidth, contentHeight);
1834 LayoutPoint contentLocation = renderBlock.location();
1835 contentLocation.move(renderBlock.borderLeft() + renderBlock.paddingLeft(), renderBlock.borderTop() + renderBlock.paddingTop());
1837 LayoutRect rect(contentLocation, contentSize);
1838 IntRect alignedRect = snappedIntRect(rect);
1839 if (alignedRect.width() <= 0 || alignedRect.height() <= 0)
1842 // We need to get the snapshot image from the plugin element, which should be available
1843 // from our node. Assuming this node is the plugin overlay element, we should get to the
1844 // plugin itself by asking for the shadow root parent, and then its parent.
1846 if (!is<HTMLElement>(*renderBlock.element()))
1849 HTMLElement& plugInOverlay = downcast<HTMLElement>(*renderBlock.element());
1850 Element* parent = plugInOverlay.parentOrShadowHostElement();
1851 while (parent && !is<HTMLPlugInElement>(*parent))
1852 parent = parent->parentOrShadowHostElement();
1857 HTMLPlugInElement& plugInElement = downcast<HTMLPlugInElement>(*parent);
1858 if (!is<HTMLPlugInImageElement>(plugInElement))
1861 HTMLPlugInImageElement& plugInImageElement = downcast<HTMLPlugInImageElement>(plugInElement);
1863 Image* snapshot = plugInImageElement.snapshotImage();
1867 RenderSnapshottedPlugIn& plugInRenderer = downcast<RenderSnapshottedPlugIn>(*plugInImageElement.renderer());
1868 FloatPoint snapshotAbsPos = plugInRenderer.localToAbsolute();
1869 snapshotAbsPos.move(plugInRenderer.borderLeft() + plugInRenderer.paddingLeft(), plugInRenderer.borderTop() + plugInRenderer.paddingTop());
1871 // We could draw the snapshot with that coordinates, but we need to make sure there
1872 // isn't a composited layer between us and the plugInRenderer.
1873 for (auto* renderBox = &downcast<RenderBox>(renderer); renderBox != &plugInRenderer; renderBox = renderBox->parentBox()) {
1874 if (renderBox->hasLayer() && renderBox->layer() && renderBox->layer()->isComposited()) {
1875 snapshotAbsPos = -renderBox->location();
1880 LayoutSize pluginSize(plugInRenderer.contentWidth(), plugInRenderer.contentHeight());
1881 LayoutRect pluginRect(snapshotAbsPos, pluginSize);
1882 IntRect alignedPluginRect = snappedIntRect(pluginRect);
1884 if (alignedPluginRect.width() <= 0 || alignedPluginRect.height() <= 0)
1887 context->drawImage(snapshot, plugInRenderer.style().colorSpace(), alignedPluginRect, CompositeSourceOver);
1891 #if ENABLE(DATALIST_ELEMENT)
1892 IntSize RenderThemeMac::sliderTickSize() const
1894 return IntSize(1, 3);
1897 int RenderThemeMac::sliderTickOffsetFromTrackCenter() const
1903 const int sliderThumbWidth = 15;
1904 const int sliderThumbHeight = 15;
1906 void RenderThemeMac::adjustSliderThumbSize(RenderStyle& style, Element*) const
1908 float zoomLevel = style.effectiveZoom();
1909 if (style.appearance() == SliderThumbHorizontalPart || style.appearance() == SliderThumbVerticalPart) {
1910 style.setWidth(Length(static_cast<int>(sliderThumbWidth * zoomLevel), Fixed));
1911 style.setHeight(Length(static_cast<int>(sliderThumbHeight * zoomLevel), Fixed));
1915 bool RenderThemeMac::shouldShowPlaceholderWhenFocused() const
1920 NSPopUpButtonCell* RenderThemeMac::popupButton() const
1922 if (!m_popupButton) {
1923 m_popupButton = adoptNS([[NSPopUpButtonCell alloc] initTextCell:@"" pullsDown:NO]);
1924 [m_popupButton.get() setUsesItemFromMenu:NO];
1925 [m_popupButton.get() setFocusRingType:NSFocusRingTypeExterior];
1926 // We don't want the app's UI layout direction to affect the appearance of popup buttons in
1927 // web content, which has its own layout direction.
1928 // FIXME: Make this depend on the directionality of the select element, once the rest of the
1929 // rendering code can account for the popup arrows appearing on the other side.
1930 [m_popupButton setUserInterfaceLayoutDirection:NSUserInterfaceLayoutDirectionLeftToRight];
1933 return m_popupButton.get();
1936 NSSearchFieldCell* RenderThemeMac::search() const
1939 m_search = adoptNS([[NSSearchFieldCell alloc] initTextCell:@""]);
1940 [m_search.get() setBezelStyle:NSTextFieldRoundedBezel];
1941 [m_search.get() setBezeled:YES];
1942 [m_search.get() setEditable:YES];
1943 [m_search.get() setFocusRingType:NSFocusRingTypeExterior];
1944 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
1945 [m_search.get() setCenteredLook:NO];
1949 return m_search.get();
1952 NSMenu* RenderThemeMac::searchMenuTemplate() const
1954 if (!m_searchMenuTemplate)
1955 m_searchMenuTemplate = adoptNS([[NSMenu alloc] initWithTitle:@""]);
1957 return m_searchMenuTemplate.get();
1960 NSSliderCell* RenderThemeMac::sliderThumbHorizontal() const
1962 if (!m_sliderThumbHorizontal) {
1963 m_sliderThumbHorizontal = adoptNS([[NSSliderCell alloc] init]);
1964 [m_sliderThumbHorizontal.get() setSliderType:NSLinearSlider];
1965 [m_sliderThumbHorizontal.get() setControlSize:NSSmallControlSize];
1966 [m_sliderThumbHorizontal.get() setFocusRingType:NSFocusRingTypeExterior];
1969 return m_sliderThumbHorizontal.get();
1972 NSSliderCell* RenderThemeMac::sliderThumbVertical() const
1974 if (!m_sliderThumbVertical) {
1975 m_sliderThumbVertical = adoptNS([[NSSliderCell alloc] init]);
1976 [m_sliderThumbVertical.get() setSliderType:NSLinearSlider];
1977 [m_sliderThumbVertical.get() setControlSize:NSSmallControlSize];
1978 [m_sliderThumbVertical.get() setFocusRingType:NSFocusRingTypeExterior];
1981 return m_sliderThumbVertical.get();
1984 NSTextFieldCell* RenderThemeMac::textField() const
1987 m_textField = adoptNS([[WebCoreTextFieldCell alloc] initTextCell:@""]);
1988 [m_textField.get() setBezeled:YES];
1989 [m_textField.get() setEditable:YES];
1990 [m_textField.get() setFocusRingType:NSFocusRingTypeExterior];
1991 // Post-Lion, WebCore can be in charge of paintinng the background thanks to
1992 // the workaround in place for <rdar://problem/11385461>, which is implemented
1993 // above as _coreUIDrawOptionsWithFrame.
1994 [m_textField.get() setDrawsBackground:NO];
1997 return m_textField.get();
2000 String RenderThemeMac::fileListNameForWidth(const FileList* fileList, const FontCascade& font, int width, bool multipleFilesAllowed) const
2005 String strToTruncate;
2006 if (fileList->isEmpty())
2007 strToTruncate = fileListDefaultLabel(multipleFilesAllowed);
2008 else if (fileList->length() == 1)
2009 strToTruncate = [[NSFileManager defaultManager] displayNameAtPath:(fileList->item(0)->path())];
2011 return StringTruncator::rightTruncate(multipleFileUploadText(fileList->length()), width, font, StringTruncator::EnableRoundingHacks);
2013 return StringTruncator::centerTruncate(strToTruncate, width, font, StringTruncator::EnableRoundingHacks);
2016 bool RenderThemeMac::defaultButtonHasAnimation() const
2018 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
2025 #if ENABLE(SERVICE_CONTROLS)
2026 NSServicesRolloverButtonCell* RenderThemeMac::servicesRolloverButtonCell() const
2028 #if HAVE(APPKIT_SERVICE_CONTROLS_SUPPORT)
2029 if (!m_servicesRolloverButton) {
2030 m_servicesRolloverButton = [NSServicesRolloverButtonCell serviceRolloverButtonCellForStyle:NSSharingServicePickerStyleRollover];
2031 [m_servicesRolloverButton setBezelStyle:NSRoundedDisclosureBezelStyle];
2032 [m_servicesRolloverButton setButtonType:NSPushOnPushOffButton];
2033 [m_servicesRolloverButton setImagePosition:NSImageOnly];
2034 [m_servicesRolloverButton setState:NO];
2037 return m_servicesRolloverButton.get();
2043 bool RenderThemeMac::paintImageControlsButton(const RenderObject& renderer, const PaintInfo& paintInfo, const IntRect& rect)
2045 if (paintInfo.phase != PaintPhaseBlockBackground)
2048 #if HAVE(APPKIT_SERVICE_CONTROLS_SUPPORT)
2049 NSServicesRolloverButtonCell *cell = servicesRolloverButtonCell();
2051 LocalCurrentGraphicsContext localContext(paintInfo.context);
2052 GraphicsContextStateSaver stateSaver(*paintInfo.context);
2054 paintInfo.context->translate(rect.x(), rect.y());
2056 IntRect innerFrame(IntPoint(), rect.size());
2057 [cell drawWithFrame:innerFrame inView:documentViewFor(renderer)];
2058 [cell setControlView:nil];
2060 UNUSED_PARAM(renderer);
2067 IntSize RenderThemeMac::imageControlsButtonSize(const RenderObject&) const
2069 #if HAVE(APPKIT_SERVICE_CONTROLS_SUPPORT)
2070 return IntSize(servicesRolloverButtonCell().cellSize);
2076 IntSize RenderThemeMac::imageControlsButtonPositionOffset() const
2078 #if HAVE(APPKIT_SERVICE_CONTROLS_SUPPORT)
2079 // FIXME: Currently the offsets will always be the same no matter what image rect you try with.
2080 // This may not always be true in the future.
2081 static const int dummyDimension = 100;
2082 IntRect dummyImageRect(0, 0, dummyDimension, dummyDimension);
2083 NSRect bounds = [servicesRolloverButtonCell() rectForBounds:dummyImageRect preferredEdge:NSMinYEdge];
2085 return IntSize(dummyDimension - bounds.origin.x, bounds.origin.y);
2092 } // namespace WebCore
2094 #endif // !PLATFORM(IOS)