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"
49 #import "RenderLayer.h"
50 #import "RenderMedia.h"
51 #import "RenderMediaControlElements.h"
52 #import "RenderMediaControls.h"
53 #import "RenderProgress.h"
54 #import "RenderSlider.h"
55 #import "RenderSnapshottedPlugIn.h"
56 #import "RenderView.h"
57 #import "SharedBuffer.h"
58 #import "StringTruncator.h"
59 #import "StyleResolver.h"
61 #import "TimeRanges.h"
62 #import "UserAgentScripts.h"
63 #import "UserAgentStyleSheets.h"
64 #import "WebCoreSystemInterface.h"
65 #import <wtf/RetainPtr.h>
66 #import <wtf/RetainPtr.h>
67 #import <wtf/StdLibExtras.h>
68 #import <wtf/text/StringBuilder.h>
69 #import <Carbon/Carbon.h>
70 #import <Cocoa/Cocoa.h>
73 #if ENABLE(METER_ELEMENT)
74 #import "RenderMeter.h"
75 #import "HTMLMeterElement.h"
78 #if defined(__LP64__) && __LP64__
79 #define HAVE_APPKIT_SERVICE_CONTROLS_SUPPORT 1
81 #define HAVE_APPKIT_SERVICE_CONTROLS_SUPPORT 0
84 #if ENABLE(SERVICE_CONTROLS) && HAVE(APPKIT_SERVICE_CONTROLS_SUPPORT)
86 #if __has_include(<AppKit/AppKitDefines_Private.h>)
87 #import <AppKit/AppKitDefines_Private.h>
89 #define APPKIT_PRIVATE_CLASS
92 #if __has_include(<AppKit/NSSharingService_Private.h>)
93 #import <AppKit/NSSharingService_Private.h>
96 NSSharingServicePickerStyleRollover = 1
97 } NSSharingServicePickerStyle;
100 #if __has_include(<AppKit/NSServicesRolloverButtonCell.h>)
101 #import <AppKit/NSServicesRolloverButtonCell.h>
103 @interface NSServicesRolloverButtonCell : NSButtonCell
107 @interface NSServicesRolloverButtonCell (Details)
108 + (NSServicesRolloverButtonCell *)serviceRolloverButtonCellForStyle:(NSSharingServicePickerStyle)style;
109 - (NSRect)rectForBounds:(NSRect)bounds preferredEdge:(NSRectEdge)preferredEdge;
112 #endif // ENABLE(SERVICE_CONTROLS)
114 // The methods in this file are specific to the Mac OS X platform.
116 // FIXME: The platform-independent code in this class should be factored out and merged with RenderThemeSafari.
118 // We estimate the animation rate of a Mac OS X progress bar is 33 fps.
119 // Hard code the value here because we haven't found API for it.
120 const double progressAnimationFrameRate = 0.033;
122 // Mac OS X progress bar animation seems to have 256 frames.
123 const double progressAnimationNumFrames = 256;
125 @interface WebCoreRenderThemeNotificationObserver : NSObject
127 WebCore::RenderTheme *_theme;
130 - (id)initWithTheme:(WebCore::RenderTheme *)theme;
131 - (void)systemColorsDidChange:(NSNotification *)notification;
135 @implementation WebCoreRenderThemeNotificationObserver
137 - (id)initWithTheme:(WebCore::RenderTheme *)theme
139 if (!(self = [super init]))
146 - (void)systemColorsDidChange:(NSNotification *)unusedNotification
148 ASSERT_UNUSED(unusedNotification, [[unusedNotification name] isEqualToString:NSSystemColorsDidChangeNotification]);
149 _theme->platformColorsDidChange();
154 @interface NSTextFieldCell (WKDetails)
155 - (CFDictionaryRef)_coreUIDrawOptionsWithFrame:(NSRect)cellFrame inView:(NSView *)controlView includeFocus:(BOOL)includeFocus;
159 @interface WebCoreTextFieldCell : NSTextFieldCell
160 - (CFDictionaryRef)_coreUIDrawOptionsWithFrame:(NSRect)cellFrame inView:(NSView *)controlView includeFocus:(BOOL)includeFocus;
163 @implementation WebCoreTextFieldCell
164 - (CFDictionaryRef)_coreUIDrawOptionsWithFrame:(NSRect)cellFrame inView:(NSView *)controlView includeFocus:(BOOL)includeFocus
166 // FIXME: This is a post-Lion-only workaround for <rdar://problem/11385461>. When that bug is resolved, we should remove this code.
167 CFMutableDictionaryRef coreUIDrawOptions = CFDictionaryCreateMutableCopy(NULL, 0, [super _coreUIDrawOptionsWithFrame:cellFrame inView:controlView includeFocus:includeFocus]);
168 CFDictionarySetValue(coreUIDrawOptions, @"borders only", kCFBooleanTrue);
169 return (CFDictionaryRef)[NSMakeCollectable(coreUIDrawOptions) autorelease];
173 @interface WebCoreRenderThemeBundle : NSObject
176 @implementation WebCoreRenderThemeBundle
179 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
180 @interface NSSearchFieldCell(Details)
181 @property (getter=isCenteredLook) BOOL centeredLook;
187 using namespace HTMLNames;
203 PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page*)
205 static RenderTheme* rt = RenderThemeMac::create().leakRef();
209 PassRefPtr<RenderTheme> RenderThemeMac::create()
211 return adoptRef(new RenderThemeMac);
214 RenderThemeMac::RenderThemeMac()
215 : m_isSliderThumbHorizontalPressed(false)
216 , m_isSliderThumbVerticalPressed(false)
217 , m_notificationObserver(adoptNS([[WebCoreRenderThemeNotificationObserver alloc] initWithTheme:this]))
219 [[NSNotificationCenter defaultCenter] addObserver:m_notificationObserver.get()
220 selector:@selector(systemColorsDidChange:)
221 name:NSSystemColorsDidChangeNotification
225 RenderThemeMac::~RenderThemeMac()
227 [[NSNotificationCenter defaultCenter] removeObserver:m_notificationObserver.get()];
230 NSView* RenderThemeMac::documentViewFor(const RenderObject& o) const
232 ControlStates states(extractControlStatesForRenderer(o));
233 return ThemeMac::ensuredView(&o.view().frameView(), &states);
237 String RenderThemeMac::mediaControlsStyleSheet()
239 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
240 if (m_mediaControlsStyleSheet.isEmpty())
241 m_mediaControlsStyleSheet = [NSString stringWithContentsOfFile:[[NSBundle bundleForClass:[WebCoreRenderThemeBundle class]] pathForResource:@"mediaControlsApple" ofType:@"css"] encoding:NSUTF8StringEncoding error:nil];
242 return m_mediaControlsStyleSheet;
244 return emptyString();
248 String RenderThemeMac::mediaControlsScript()
250 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
251 if (m_mediaControlsScript.isEmpty()) {
252 StringBuilder scriptBuilder;
253 scriptBuilder.append([NSString stringWithContentsOfFile:[[NSBundle bundleForClass:[WebCoreRenderThemeBundle class]] pathForResource:@"mediaControlsLocalizedStrings" ofType:@"js"] encoding:NSUTF8StringEncoding error:nil]);
254 scriptBuilder.append([NSString stringWithContentsOfFile:[[NSBundle bundleForClass:[WebCoreRenderThemeBundle class]] pathForResource:@"mediaControlsApple" ofType:@"js"] encoding:NSUTF8StringEncoding error:nil]);
255 m_mediaControlsScript = scriptBuilder.toString();
257 return m_mediaControlsScript;
259 return emptyString();
263 #endif // ENABLE(VIDEO)
266 #if ENABLE(SERVICE_CONTROLS)
267 String RenderThemeMac::imageControlsStyleSheet() const
269 return String(imageControlsMacUserAgentStyleSheet, sizeof(imageControlsMacUserAgentStyleSheet));
273 Color RenderThemeMac::platformActiveSelectionBackgroundColor() const
275 NSColor* color = [[NSColor selectedTextBackgroundColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace];
276 return Color(static_cast<int>(255.0 * [color redComponent]), static_cast<int>(255.0 * [color greenComponent]), static_cast<int>(255.0 * [color blueComponent]));
279 Color RenderThemeMac::platformInactiveSelectionBackgroundColor() const
281 NSColor* color = [[NSColor secondarySelectedControlColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace];
282 return Color(static_cast<int>(255.0 * [color redComponent]), static_cast<int>(255.0 * [color greenComponent]), static_cast<int>(255.0 * [color blueComponent]));
285 Color RenderThemeMac::platformActiveListBoxSelectionBackgroundColor() const
287 NSColor* color = [[NSColor alternateSelectedControlColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace];
288 return Color(static_cast<int>(255.0 * [color redComponent]), static_cast<int>(255.0 * [color greenComponent]), static_cast<int>(255.0 * [color blueComponent]));
291 Color RenderThemeMac::platformActiveListBoxSelectionForegroundColor() const
296 Color RenderThemeMac::platformInactiveListBoxSelectionForegroundColor() const
301 Color RenderThemeMac::platformFocusRingColor() const
303 if (usesTestModeFocusRingColor())
304 return oldAquaFocusRingColor();
306 return systemColor(CSSValueWebkitFocusRingColor);
309 int RenderThemeMac::platformFocusRingMaxWidth() const
311 // FIXME: Shouldn't this function be named platformFocusRingMinWidth? But also, I'm not sure if this function is needed - looks like
312 // all platforms just used 0 for this before <http://trac.webkit.org/changeset/168397>.
316 Color RenderThemeMac::platformInactiveListBoxSelectionBackgroundColor() const
318 return platformInactiveSelectionBackgroundColor();
321 static FontWeight toFontWeight(NSInteger appKitFontWeight)
323 ASSERT(appKitFontWeight > 0 && appKitFontWeight < 15);
324 if (appKitFontWeight > 14)
325 appKitFontWeight = 14;
326 else if (appKitFontWeight < 1)
327 appKitFontWeight = 1;
329 static FontWeight fontWeights[] = {
345 return fontWeights[appKitFontWeight - 1];
348 void RenderThemeMac::systemFont(CSSValueID cssValueId, FontDescription& fontDescription) const
350 DEPRECATED_DEFINE_STATIC_LOCAL(FontDescription, systemFont, ());
351 DEPRECATED_DEFINE_STATIC_LOCAL(FontDescription, smallSystemFont, ());
352 DEPRECATED_DEFINE_STATIC_LOCAL(FontDescription, menuFont, ());
353 DEPRECATED_DEFINE_STATIC_LOCAL(FontDescription, labelFont, ());
354 DEPRECATED_DEFINE_STATIC_LOCAL(FontDescription, miniControlFont, ());
355 DEPRECATED_DEFINE_STATIC_LOCAL(FontDescription, smallControlFont, ());
356 DEPRECATED_DEFINE_STATIC_LOCAL(FontDescription, controlFont, ());
358 FontDescription* cachedDesc;
360 switch (cssValueId) {
361 case CSSValueSmallCaption:
362 cachedDesc = &smallSystemFont;
363 if (!smallSystemFont.isAbsoluteSize())
364 font = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]];
367 cachedDesc = &menuFont;
368 if (!menuFont.isAbsoluteSize())
369 font = [NSFont menuFontOfSize:[NSFont systemFontSize]];
371 case CSSValueStatusBar:
372 cachedDesc = &labelFont;
373 if (!labelFont.isAbsoluteSize())
374 font = [NSFont labelFontOfSize:[NSFont labelFontSize]];
376 case CSSValueWebkitMiniControl:
377 cachedDesc = &miniControlFont;
378 if (!miniControlFont.isAbsoluteSize())
379 font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSMiniControlSize]];
381 case CSSValueWebkitSmallControl:
382 cachedDesc = &smallControlFont;
383 if (!smallControlFont.isAbsoluteSize())
384 font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]];
386 case CSSValueWebkitControl:
387 cachedDesc = &controlFont;
388 if (!controlFont.isAbsoluteSize())
389 font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]];
392 cachedDesc = &systemFont;
393 if (!systemFont.isAbsoluteSize())
394 font = [NSFont systemFontOfSize:[NSFont systemFontSize]];
398 NSFontManager *fontManager = [NSFontManager sharedFontManager];
399 cachedDesc->setIsAbsoluteSize(true);
400 cachedDesc->setGenericFamily(FontDescription::NoFamily);
401 cachedDesc->setOneFamily([font webCoreFamilyName]);
402 cachedDesc->setSpecifiedSize([font pointSize]);
403 cachedDesc->setWeight(toFontWeight([fontManager weightOfFont:font]));
404 cachedDesc->setItalic([fontManager traitsOfFont:font] & NSItalicFontMask);
406 fontDescription = *cachedDesc;
409 static RGBA32 convertNSColorToColor(NSColor *color)
411 NSColor *colorInColorSpace = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace];
412 if (colorInColorSpace) {
413 static const double scaleFactor = nextafter(256.0, 0.0);
414 return makeRGB(static_cast<int>(scaleFactor * [colorInColorSpace redComponent]),
415 static_cast<int>(scaleFactor * [colorInColorSpace greenComponent]),
416 static_cast<int>(scaleFactor * [colorInColorSpace blueComponent]));
419 // This conversion above can fail if the NSColor in question is an NSPatternColor
420 // (as many system colors are). These colors are actually a repeating pattern
421 // not just a solid color. To work around this we simply draw a 1x1 image of
422 // the color and use that pixel's color. It might be better to use an average of
423 // the colors in the pattern instead.
424 NSBitmapImageRep *offscreenRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil
431 colorSpaceName:NSDeviceRGBColorSpace
435 [NSGraphicsContext saveGraphicsState];
436 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:offscreenRep]];
437 NSEraseRect(NSMakeRect(0, 0, 1, 1));
438 [color drawSwatchInRect:NSMakeRect(0, 0, 1, 1)];
439 [NSGraphicsContext restoreGraphicsState];
442 [offscreenRep getPixel:pixel atX:0 y:0];
444 [offscreenRep release];
446 return makeRGB(pixel[0], pixel[1], pixel[2]);
449 static RGBA32 menuBackgroundColor()
451 NSBitmapImageRep *offscreenRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil
458 colorSpaceName:NSDeviceRGBColorSpace
462 CGContextRef context = static_cast<CGContextRef>([[NSGraphicsContext graphicsContextWithBitmapImageRep:offscreenRep] graphicsPort]);
463 CGRect rect = CGRectMake(0, 0, 1, 1);
464 HIThemeMenuDrawInfo drawInfo;
465 drawInfo.version = 0;
466 drawInfo.menuType = kThemeMenuTypePopUp;
467 HIThemeDrawMenuBackground(&rect, &drawInfo, context, kHIThemeOrientationInverted);
470 [offscreenRep getPixel:pixel atX:0 y:0];
472 [offscreenRep release];
474 return makeRGB(pixel[0], pixel[1], pixel[2]);
477 void RenderThemeMac::platformColorsDidChange()
479 m_systemColorCache.clear();
480 RenderTheme::platformColorsDidChange();
483 Color RenderThemeMac::systemColor(CSSValueID cssValueId) const
486 HashMap<int, RGBA32>::iterator it = m_systemColorCache.find(cssValueId);
487 if (it != m_systemColorCache.end())
492 switch (cssValueId) {
493 case CSSValueActiveborder:
494 color = convertNSColorToColor([NSColor keyboardFocusIndicatorColor]);
496 case CSSValueActivecaption:
497 color = convertNSColorToColor([NSColor windowFrameTextColor]);
499 case CSSValueAppworkspace:
500 color = convertNSColorToColor([NSColor headerColor]);
502 case CSSValueBackground:
503 // Use theme independent default
505 case CSSValueButtonface:
506 // We use this value instead of NSColor's controlColor to avoid website incompatibilities.
507 // We may want to change this to use the NSColor in future.
510 case CSSValueButtonhighlight:
511 color = convertNSColorToColor([NSColor controlHighlightColor]);
513 case CSSValueButtonshadow:
514 color = convertNSColorToColor([NSColor controlShadowColor]);
516 case CSSValueButtontext:
517 color = convertNSColorToColor([NSColor controlTextColor]);
519 case CSSValueCaptiontext:
520 color = convertNSColorToColor([NSColor textColor]);
522 case CSSValueGraytext:
523 color = convertNSColorToColor([NSColor disabledControlTextColor]);
525 case CSSValueHighlight:
526 color = convertNSColorToColor([NSColor selectedTextBackgroundColor]);
528 case CSSValueHighlighttext:
529 color = convertNSColorToColor([NSColor selectedTextColor]);
531 case CSSValueInactiveborder:
532 color = convertNSColorToColor([NSColor controlBackgroundColor]);
534 case CSSValueInactivecaption:
535 color = convertNSColorToColor([NSColor controlBackgroundColor]);
537 case CSSValueInactivecaptiontext:
538 color = convertNSColorToColor([NSColor textColor]);
540 case CSSValueInfobackground:
541 // There is no corresponding NSColor for this so we use a hard coded value.
544 case CSSValueInfotext:
545 color = convertNSColorToColor([NSColor textColor]);
548 color = menuBackgroundColor();
550 case CSSValueMenutext:
551 color = convertNSColorToColor([NSColor selectedMenuItemTextColor]);
553 case CSSValueScrollbar:
554 color = convertNSColorToColor([NSColor scrollBarColor]);
557 color = convertNSColorToColor([NSColor textColor]);
559 case CSSValueThreeddarkshadow:
560 color = convertNSColorToColor([NSColor controlDarkShadowColor]);
562 case CSSValueThreedshadow:
563 color = convertNSColorToColor([NSColor shadowColor]);
565 case CSSValueThreedface:
566 // We use this value instead of NSColor's controlColor to avoid website incompatibilities.
567 // We may want to change this to use the NSColor in future.
570 case CSSValueThreedhighlight:
571 color = convertNSColorToColor([NSColor highlightColor]);
573 case CSSValueThreedlightshadow:
574 color = convertNSColorToColor([NSColor controlLightHighlightColor]);
576 case CSSValueWebkitFocusRingColor:
577 color = convertNSColorToColor([NSColor keyboardFocusIndicatorColor]);
580 color = convertNSColorToColor([NSColor windowBackgroundColor]);
582 case CSSValueWindowframe:
583 color = convertNSColorToColor([NSColor windowFrameColor]);
585 case CSSValueWindowtext:
586 color = convertNSColorToColor([NSColor windowFrameTextColor]);
592 if (!color.isValid())
593 color = RenderTheme::systemColor(cssValueId);
596 m_systemColorCache.set(cssValueId, color.rgb());
601 bool RenderThemeMac::usesTestModeFocusRingColor() const
603 return WebCore::usesTestModeFocusRingColor();
606 bool RenderThemeMac::isControlStyled(const RenderStyle& style, const BorderData& border,
607 const FillLayer& background, const Color& backgroundColor) const
609 if (style.appearance() == TextFieldPart || style.appearance() == TextAreaPart || style.appearance() == ListboxPart)
610 return style.border() != border;
612 // FIXME: This is horrible, but there is not much else that can be done. Menu lists cannot draw properly when
613 // scaled. They can't really draw properly when transformed either. We can't detect the transform case at style
614 // adjustment time so that will just have to stay broken. We can however detect that we're zooming. If zooming
615 // is in effect we treat it like the control is styled.
616 if (style.appearance() == MenulistPart && style.effectiveZoom() != 1.0f)
619 return RenderTheme::isControlStyled(style, border, background, backgroundColor);
622 static FloatRect inflateRect(const FloatRect& rect, const IntSize& size, const int* margins, float zoomLevel)
624 // Only do the inflation if the available width/height are too small. Otherwise try to
625 // fit the glow/check space into the available box's width/height.
626 int widthDelta = rect.width() - (size.width() + margins[leftMargin] * zoomLevel + margins[rightMargin] * zoomLevel);
627 int heightDelta = rect.height() - (size.height() + margins[topMargin] * zoomLevel + margins[bottomMargin] * zoomLevel);
628 FloatRect result(rect);
629 if (widthDelta < 0) {
630 result.setX(result.x() - margins[leftMargin] * zoomLevel);
631 result.setWidth(result.width() - widthDelta);
633 if (heightDelta < 0) {
634 result.setY(result.y() - margins[topMargin] * zoomLevel);
635 result.setHeight(result.height() - heightDelta);
640 void RenderThemeMac::adjustRepaintRect(const RenderObject& renderer, FloatRect& rect)
642 ControlPart part = renderer.style().appearance();
649 case SquareButtonPart:
650 case DefaultButtonPart:
652 case InnerSpinButtonPart:
653 return RenderTheme::adjustRepaintRect(renderer, rect);
659 float zoomLevel = renderer.style().effectiveZoom();
661 if (part == MenulistPart) {
662 setPopupButtonCellState(renderer, IntSize(rect.size()));
663 IntSize size = popupButtonSizes()[[popupButton() controlSize]];
664 size.setHeight(size.height() * zoomLevel);
665 size.setWidth(rect.width());
666 rect = inflateRect(rect, size, popupButtonMargins(), zoomLevel);
670 FloatRect RenderThemeMac::convertToPaintingRect(const RenderObject& inputRenderer, const RenderObject& partRenderer, const FloatRect& inputRect, const IntRect& r) const
672 FloatRect partRect(inputRect);
674 // Compute an offset between the part renderer and the input renderer
675 FloatSize offsetFromInputRenderer;
676 const RenderObject* renderer = &partRenderer;
677 while (renderer && renderer != &inputRenderer) {
678 RenderElement* containingRenderer = renderer->container();
679 offsetFromInputRenderer -= roundedIntSize(renderer->offsetFromContainer(containingRenderer, LayoutPoint()));
680 renderer = containingRenderer;
682 // If the input renderer was not a container, something went wrong
683 ASSERT(renderer == &inputRenderer);
684 // Move the rect into partRenderer's coords
685 partRect.move(offsetFromInputRenderer);
686 // Account for the local drawing offset (tx, ty)
687 partRect.move(r.x(), r.y());
692 void RenderThemeMac::updateCheckedState(NSCell* cell, const RenderObject& o)
694 bool oldIndeterminate = [cell state] == NSMixedState;
695 bool indeterminate = isIndeterminate(o);
696 bool checked = isChecked(o);
698 if (oldIndeterminate != indeterminate) {
699 [cell setState:indeterminate ? NSMixedState : (checked ? NSOnState : NSOffState)];
703 bool oldChecked = [cell state] == NSOnState;
704 if (checked != oldChecked)
705 [cell setState:checked ? NSOnState : NSOffState];
708 void RenderThemeMac::updateEnabledState(NSCell* cell, const RenderObject& o)
710 bool oldEnabled = [cell isEnabled];
711 bool enabled = isEnabled(o);
712 if (enabled != oldEnabled)
713 [cell setEnabled:enabled];
716 void RenderThemeMac::updateFocusedState(NSCell* cell, const RenderObject& o)
718 bool oldFocused = [cell showsFirstResponder];
719 bool focused = isFocused(o) && o.style().outlineStyleIsAuto();
720 if (focused != oldFocused)
721 [cell setShowsFirstResponder:focused];
724 void RenderThemeMac::updatePressedState(NSCell* cell, const RenderObject& o)
726 bool oldPressed = [cell isHighlighted];
727 bool pressed = o.node() && o.node()->isElementNode() && toElement(o.node())->active();
728 if (pressed != oldPressed)
729 [cell setHighlighted:pressed];
732 bool RenderThemeMac::controlSupportsTints(const RenderObject& o) const
734 // An alternate way to implement this would be to get the appropriate cell object
735 // and call the private _needRedrawOnWindowChangedKeyState method. An advantage of
736 // that would be that we would match AppKit behavior more closely, but a disadvantage
737 // would be that we would rely on an AppKit SPI method.
742 // Checkboxes only have tint when checked.
743 if (o.style().appearance() == CheckboxPart)
746 // For now assume other controls have tint if enabled.
750 NSControlSize RenderThemeMac::controlSizeForFont(RenderStyle& style) const
752 int fontSize = style.fontSize();
754 return NSRegularControlSize;
756 return NSSmallControlSize;
757 return NSMiniControlSize;
760 NSControlSize RenderThemeMac::controlSizeForCell(NSCell*, const IntSize* sizes, const IntSize& minSize, float zoomLevel) const
762 if (minSize.width() >= static_cast<int>(sizes[NSRegularControlSize].width() * zoomLevel)
763 && minSize.height() >= static_cast<int>(sizes[NSRegularControlSize].height() * zoomLevel))
764 return NSRegularControlSize;
766 if (minSize.width() >= static_cast<int>(sizes[NSSmallControlSize].width() * zoomLevel)
767 && minSize.height() >= static_cast<int>(sizes[NSSmallControlSize].height() * zoomLevel))
768 return NSSmallControlSize;
770 return NSMiniControlSize;
773 void RenderThemeMac::setControlSize(NSCell* cell, const IntSize* sizes, const IntSize& minSize, float zoomLevel)
775 NSControlSize size = controlSizeForCell(cell, sizes, minSize, zoomLevel);
776 if (size != [cell controlSize]) // Only update if we have to, since AppKit does work even if the size is the same.
777 [cell setControlSize:size];
780 IntSize RenderThemeMac::sizeForFont(RenderStyle& style, const IntSize* sizes) const
782 if (style.effectiveZoom() != 1.0f) {
783 IntSize result = sizes[controlSizeForFont(style)];
784 return IntSize(result.width() * style.effectiveZoom(), result.height() * style.effectiveZoom());
786 return sizes[controlSizeForFont(style)];
789 IntSize RenderThemeMac::sizeForSystemFont(RenderStyle& style, const IntSize* sizes) const
791 if (style.effectiveZoom() != 1.0f) {
792 IntSize result = sizes[controlSizeForSystemFont(style)];
793 return IntSize(result.width() * style.effectiveZoom(), result.height() * style.effectiveZoom());
795 return sizes[controlSizeForSystemFont(style)];
798 void RenderThemeMac::setSizeFromFont(RenderStyle& style, const IntSize* sizes) const
800 // FIXME: Check is flawed, since it doesn't take min-width/max-width into account.
801 IntSize size = sizeForFont(style, sizes);
802 if (style.width().isIntrinsicOrAuto() && size.width() > 0)
803 style.setWidth(Length(size.width(), Fixed));
804 if (style.height().isAuto() && size.height() > 0)
805 style.setHeight(Length(size.height(), Fixed));
808 void RenderThemeMac::setFontFromControlSize(StyleResolver&, RenderStyle& style, NSControlSize controlSize) const
810 FontDescription fontDescription;
811 fontDescription.setIsAbsoluteSize(true);
812 fontDescription.setGenericFamily(FontDescription::SerifFamily);
814 NSFont* font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:controlSize]];
815 fontDescription.setOneFamily([font webCoreFamilyName]);
816 fontDescription.setComputedSize([font pointSize] * style.effectiveZoom());
817 fontDescription.setSpecifiedSize([font pointSize] * style.effectiveZoom());
820 style.setLineHeight(RenderStyle::initialLineHeight());
822 if (style.setFontDescription(fontDescription))
823 style.font().update(0);
826 NSControlSize RenderThemeMac::controlSizeForSystemFont(RenderStyle& style) const
828 int fontSize = style.fontSize();
829 if (fontSize >= [NSFont systemFontSizeForControlSize:NSRegularControlSize])
830 return NSRegularControlSize;
831 if (fontSize >= [NSFont systemFontSizeForControlSize:NSSmallControlSize])
832 return NSSmallControlSize;
833 return NSMiniControlSize;
836 bool RenderThemeMac::paintTextField(const RenderObject& o, const PaintInfo& paintInfo, const FloatRect& r)
838 LocalCurrentGraphicsContext localContext(paintInfo.context);
840 NSTextFieldCell *textField = this->textField();
842 GraphicsContextStateSaver stateSaver(*paintInfo.context);
844 [textField setEnabled:(isEnabled(o) && !isReadOnlyControl(o))];
845 [textField drawWithFrame:NSRect(r) inView:documentViewFor(o)];
847 [textField setControlView:nil];
852 void RenderThemeMac::adjustTextFieldStyle(StyleResolver&, RenderStyle&, Element&) const
856 bool RenderThemeMac::paintCapsLockIndicator(const RenderObject&, const PaintInfo& paintInfo, const IntRect& r)
858 if (paintInfo.context->paintingDisabled())
861 LocalCurrentGraphicsContext localContext(paintInfo.context);
862 wkDrawCapsLockIndicator(localContext.cgContext(), r);
867 bool RenderThemeMac::paintTextArea(const RenderObject& o, const PaintInfo& paintInfo, const FloatRect& r)
869 LocalCurrentGraphicsContext localContext(paintInfo.context);
870 wkDrawBezeledTextArea(r, isEnabled(o) && !isReadOnlyControl(o));
874 void RenderThemeMac::adjustTextAreaStyle(StyleResolver&, RenderStyle&, Element&) const
878 const int* RenderThemeMac::popupButtonMargins() const
880 static const int margins[3][4] =
886 return margins[[popupButton() controlSize]];
889 const IntSize* RenderThemeMac::popupButtonSizes() const
891 static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) };
895 const int* RenderThemeMac::popupButtonPadding(NSControlSize size) const
897 static const int padding[3][4] =
903 return padding[size];
906 bool RenderThemeMac::paintMenuList(const RenderObject& renderer, const PaintInfo& paintInfo, const FloatRect& rect)
908 LocalCurrentGraphicsContext localContext(paintInfo.context);
909 setPopupButtonCellState(renderer, IntSize(rect.size()));
911 NSPopUpButtonCell* popupButton = this->popupButton();
913 float zoomLevel = renderer.style().effectiveZoom();
914 IntSize size = popupButtonSizes()[[popupButton controlSize]];
915 size.setHeight(size.height() * zoomLevel);
916 size.setWidth(rect.width());
918 // Now inflate it to account for the shadow.
919 FloatRect inflatedRect = rect;
920 if (rect.width() >= minimumMenuListSize(renderer.style()))
921 inflatedRect = inflateRect(rect, size, popupButtonMargins(), zoomLevel);
923 GraphicsContextStateSaver stateSaver(*paintInfo.context);
925 // On Leopard, the cell will draw outside of the given rect, so we have to clip to the rect
926 paintInfo.context->clip(inflatedRect);
928 if (zoomLevel != 1.0f) {
929 inflatedRect.setWidth(inflatedRect.width() / zoomLevel);
930 inflatedRect.setHeight(inflatedRect.height() / zoomLevel);
931 paintInfo.context->translate(inflatedRect.x(), inflatedRect.y());
932 paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel));
933 paintInfo.context->translate(-inflatedRect.x(), -inflatedRect.y());
936 NSView *view = documentViewFor(renderer);
937 [popupButton drawWithFrame:inflatedRect inView:view];
938 if (isFocused(renderer) && renderer.style().outlineStyleIsAuto()) {
939 if (wkDrawCellFocusRingWithFrameAtTime(popupButton, inflatedRect, view, std::numeric_limits<double>::max()))
940 renderer.document().page()->focusController().setFocusedElementNeedsRepaint();
943 [popupButton setControlView:nil];
948 #if ENABLE(METER_ELEMENT)
950 IntSize RenderThemeMac::meterSizeForBounds(const RenderMeter& renderMeter, const IntRect& bounds) const
952 if (NoControlPart == renderMeter.style().appearance())
953 return bounds.size();
955 NSLevelIndicatorCell* cell = levelIndicatorFor(renderMeter);
956 // Makes enough room for cell's intrinsic size.
957 NSSize cellSize = [cell cellSizeForBounds:IntRect(IntPoint(), bounds.size())];
958 return IntSize(bounds.width() < cellSize.width ? cellSize.width : bounds.width(),
959 bounds.height() < cellSize.height ? cellSize.height : bounds.height());
962 bool RenderThemeMac::paintMeter(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
964 if (!renderObject.isMeter())
967 LocalCurrentGraphicsContext localContext(paintInfo.context);
969 NSLevelIndicatorCell* cell = levelIndicatorFor(toRenderMeter(renderObject));
970 GraphicsContextStateSaver stateSaver(*paintInfo.context);
972 [cell drawWithFrame:rect inView:documentViewFor(renderObject)];
973 [cell setControlView:nil];
977 bool RenderThemeMac::supportsMeter(ControlPart part) const
980 case RelevancyLevelIndicatorPart:
981 case DiscreteCapacityLevelIndicatorPart:
982 case RatingLevelIndicatorPart:
984 case ContinuousCapacityLevelIndicatorPart:
991 NSLevelIndicatorStyle RenderThemeMac::levelIndicatorStyleFor(ControlPart part) const
994 case RelevancyLevelIndicatorPart:
995 return NSRelevancyLevelIndicatorStyle;
996 case DiscreteCapacityLevelIndicatorPart:
997 return NSDiscreteCapacityLevelIndicatorStyle;
998 case RatingLevelIndicatorPart:
999 return NSRatingLevelIndicatorStyle;
1001 case ContinuousCapacityLevelIndicatorPart:
1003 return NSContinuousCapacityLevelIndicatorStyle;
1008 NSLevelIndicatorCell* RenderThemeMac::levelIndicatorFor(const RenderMeter& renderMeter) const
1010 const RenderStyle& style = renderMeter.style();
1011 ASSERT(style.appearance() != NoControlPart);
1013 if (!m_levelIndicator)
1014 m_levelIndicator = adoptNS([[NSLevelIndicatorCell alloc] initWithLevelIndicatorStyle:NSContinuousCapacityLevelIndicatorStyle]);
1015 NSLevelIndicatorCell* cell = m_levelIndicator.get();
1017 HTMLMeterElement* element = renderMeter.meterElement();
1018 double value = element->value();
1020 // Because NSLevelIndicatorCell does not support optimum-in-the-middle type coloring,
1021 // we explicitly control the color instead giving low and high value to NSLevelIndicatorCell as is.
1022 switch (element->gaugeRegion()) {
1023 case HTMLMeterElement::GaugeRegionOptimum:
1024 // Make meter the green
1025 [cell setWarningValue:value + 1];
1026 [cell setCriticalValue:value + 2];
1028 case HTMLMeterElement::GaugeRegionSuboptimal:
1029 // Make the meter yellow
1030 [cell setWarningValue:value - 1];
1031 [cell setCriticalValue:value + 1];
1033 case HTMLMeterElement::GaugeRegionEvenLessGood:
1034 // Make the meter red
1035 [cell setWarningValue:value - 2];
1036 [cell setCriticalValue:value - 1];
1040 [cell setLevelIndicatorStyle:levelIndicatorStyleFor(style.appearance())];
1041 [cell setBaseWritingDirection:style.isLeftToRightDirection() ? NSWritingDirectionLeftToRight : NSWritingDirectionRightToLeft];
1042 [cell setMinValue:element->min()];
1043 [cell setMaxValue:element->max()];
1044 RetainPtr<NSNumber> valueObject = [NSNumber numberWithDouble:value];
1045 [cell setObjectValue:valueObject.get()];
1052 const IntSize* RenderThemeMac::progressBarSizes() const
1054 static const IntSize sizes[3] = { IntSize(0, 20), IntSize(0, 12), IntSize(0, 12) };
1058 const int* RenderThemeMac::progressBarMargins(NSControlSize controlSize) const
1060 static const int margins[3][4] =
1066 return margins[controlSize];
1069 IntRect RenderThemeMac::progressBarRectForBounds(const RenderObject& renderObject, const IntRect& bounds) const
1071 // Workaround until <rdar://problem/15855086> is fixed.
1072 int maxDimension = static_cast<int>(std::numeric_limits<ushort>::max());
1073 IntRect progressBarBounds(bounds.x(), bounds.y(), std::min(bounds.width(), maxDimension), std::min(bounds.height(), maxDimension));
1074 if (NoControlPart == renderObject.style().appearance())
1075 return progressBarBounds;
1077 float zoomLevel = renderObject.style().effectiveZoom();
1078 NSControlSize controlSize = controlSizeForFont(renderObject.style());
1079 IntSize size = progressBarSizes()[controlSize];
1080 size.setHeight(size.height() * zoomLevel);
1081 size.setWidth(progressBarBounds.width());
1083 // Now inflate it to account for the shadow.
1084 IntRect inflatedRect = progressBarBounds;
1085 if (progressBarBounds.height() <= minimumProgressBarHeight(renderObject.style()))
1086 inflatedRect = IntRect(inflateRect(inflatedRect, size, progressBarMargins(controlSize), zoomLevel));
1088 return inflatedRect;
1091 int RenderThemeMac::minimumProgressBarHeight(RenderStyle& style) const
1093 return sizeForSystemFont(style, progressBarSizes()).height();
1096 double RenderThemeMac::animationRepeatIntervalForProgressBar(RenderProgress&) const
1098 return progressAnimationFrameRate;
1101 double RenderThemeMac::animationDurationForProgressBar(RenderProgress&) const
1103 return progressAnimationNumFrames * progressAnimationFrameRate;
1106 void RenderThemeMac::adjustProgressBarStyle(StyleResolver&, RenderStyle&, Element&) const
1110 bool RenderThemeMac::paintProgressBar(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1112 if (!renderObject.isProgress())
1115 IntRect inflatedRect = progressBarRectForBounds(renderObject, rect);
1116 NSControlSize controlSize = controlSizeForFont(renderObject.style());
1118 const RenderProgress& renderProgress = *toRenderProgress(&renderObject);
1119 HIThemeTrackDrawInfo trackInfo;
1120 trackInfo.version = 0;
1121 if (controlSize == NSRegularControlSize)
1122 trackInfo.kind = renderProgress.position() < 0 ? kThemeLargeIndeterminateBar : kThemeLargeProgressBar;
1124 trackInfo.kind = renderProgress.position() < 0 ? kThemeMediumIndeterminateBar : kThemeMediumProgressBar;
1126 float deviceScaleFactor = renderObject.document().deviceScaleFactor();
1127 trackInfo.bounds = IntRect(IntPoint(), inflatedRect.size());
1129 trackInfo.max = std::numeric_limits<SInt32>::max();
1130 trackInfo.value = lround(renderProgress.position() * nextafter(trackInfo.max, 0));
1131 trackInfo.trackInfo.progress.phase = lround(renderProgress.animationProgress() * nextafter(progressAnimationNumFrames, 0) * deviceScaleFactor);
1132 trackInfo.attributes = kThemeTrackHorizontal;
1133 trackInfo.enableState = isActive(renderObject) ? kThemeTrackActive : kThemeTrackInactive;
1134 trackInfo.reserved = 0;
1135 trackInfo.filler1 = 0;
1137 std::unique_ptr<ImageBuffer> imageBuffer = ImageBuffer::createCompatibleBuffer(inflatedRect.size(), deviceScaleFactor, ColorSpaceDeviceRGB, paintInfo.context, true);
1141 ContextContainer cgContextContainer(imageBuffer->context());
1142 CGContextRef cgContext = cgContextContainer.context();
1143 HIThemeDrawTrack(&trackInfo, 0, cgContext, kHIThemeOrientationNormal);
1145 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1147 if (!renderProgress.style().isLeftToRightDirection()) {
1148 paintInfo.context->translate(2 * inflatedRect.x() + inflatedRect.width(), 0);
1149 paintInfo.context->scale(FloatSize(-1, 1));
1152 paintInfo.context->drawImageBuffer(imageBuffer.get(), ColorSpaceDeviceRGB, inflatedRect.location());
1156 const float baseFontSize = 11.0f;
1157 const float baseArrowHeight = 4.0f;
1158 const float baseArrowWidth = 5.0f;
1159 const float baseSpaceBetweenArrows = 2.0f;
1160 const int arrowPaddingLeft = 6;
1161 const int arrowPaddingRight = 6;
1162 const int paddingBeforeSeparator = 4;
1163 const int baseBorderRadius = 5;
1164 const int styledPopupPaddingLeft = 8;
1165 const int styledPopupPaddingTop = 1;
1166 const int styledPopupPaddingBottom = 2;
1168 static void TopGradientInterpolate(void*, const CGFloat* inData, CGFloat* outData)
1170 static float dark[4] = { 1.0f, 1.0f, 1.0f, 0.4f };
1171 static float light[4] = { 1.0f, 1.0f, 1.0f, 0.15f };
1172 float a = inData[0];
1174 for (i = 0; i < 4; i++)
1175 outData[i] = (1.0f - a) * dark[i] + a * light[i];
1178 static void BottomGradientInterpolate(void*, const CGFloat* inData, CGFloat* outData)
1180 static float dark[4] = { 1.0f, 1.0f, 1.0f, 0.0f };
1181 static float light[4] = { 1.0f, 1.0f, 1.0f, 0.3f };
1182 float a = inData[0];
1184 for (i = 0; i < 4; i++)
1185 outData[i] = (1.0f - a) * dark[i] + a * light[i];
1188 static void MainGradientInterpolate(void*, const CGFloat* inData, CGFloat* outData)
1190 static float dark[4] = { 0.0f, 0.0f, 0.0f, 0.15f };
1191 static float light[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
1192 float a = inData[0];
1194 for (i = 0; i < 4; i++)
1195 outData[i] = (1.0f - a) * dark[i] + a * light[i];
1198 static void TrackGradientInterpolate(void*, const CGFloat* inData, CGFloat* outData)
1200 static float dark[4] = { 0.0f, 0.0f, 0.0f, 0.678f };
1201 static float light[4] = { 0.0f, 0.0f, 0.0f, 0.13f };
1202 float a = inData[0];
1204 for (i = 0; i < 4; i++)
1205 outData[i] = (1.0f - a) * dark[i] + a * light[i];
1208 void RenderThemeMac::paintMenuListButtonGradients(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1213 ContextContainer cgContextContainer(paintInfo.context);
1214 CGContextRef context = cgContextContainer.context();
1216 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1218 FloatRoundedRect border = FloatRoundedRect(o.style().getRoundedBorderFor(r));
1219 int radius = border.radii().topLeft().width();
1221 CGColorSpaceRef cspace = deviceRGBColorSpaceRef();
1223 FloatRect topGradient(r.x(), r.y(), r.width(), r.height() / 2.0f);
1224 struct CGFunctionCallbacks topCallbacks = { 0, TopGradientInterpolate, NULL };
1225 RetainPtr<CGFunctionRef> topFunction = adoptCF(CGFunctionCreate(NULL, 1, NULL, 4, NULL, &topCallbacks));
1226 RetainPtr<CGShadingRef> topShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(topGradient.x(), topGradient.y()), CGPointMake(topGradient.x(), topGradient.maxY()), topFunction.get(), false, false));
1228 FloatRect bottomGradient(r.x() + radius, r.y() + r.height() / 2.0f, r.width() - 2.0f * radius, r.height() / 2.0f);
1229 struct CGFunctionCallbacks bottomCallbacks = { 0, BottomGradientInterpolate, NULL };
1230 RetainPtr<CGFunctionRef> bottomFunction = adoptCF(CGFunctionCreate(NULL, 1, NULL, 4, NULL, &bottomCallbacks));
1231 RetainPtr<CGShadingRef> bottomShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(bottomGradient.x(), bottomGradient.y()), CGPointMake(bottomGradient.x(), bottomGradient.maxY()), bottomFunction.get(), false, false));
1233 struct CGFunctionCallbacks mainCallbacks = { 0, MainGradientInterpolate, NULL };
1234 RetainPtr<CGFunctionRef> mainFunction = adoptCF(CGFunctionCreate(NULL, 1, NULL, 4, NULL, &mainCallbacks));
1235 RetainPtr<CGShadingRef> mainShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(r.x(), r.y()), CGPointMake(r.x(), r.maxY()), mainFunction.get(), false, false));
1237 RetainPtr<CGShadingRef> leftShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(r.x(), r.y()), CGPointMake(r.x() + radius, r.y()), mainFunction.get(), false, false));
1239 RetainPtr<CGShadingRef> rightShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(r.maxX(), r.y()), CGPointMake(r.maxX() - radius, r.y()), mainFunction.get(), false, false));
1242 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1243 CGContextClipToRect(context, r);
1244 paintInfo.context->clipRoundedRect(border);
1245 context = cgContextContainer.context();
1246 CGContextDrawShading(context, mainShading.get());
1250 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1251 CGContextClipToRect(context, topGradient);
1252 paintInfo.context->clipRoundedRect(FloatRoundedRect(enclosingIntRect(topGradient), border.radii().topLeft(), border.radii().topRight(), IntSize(), IntSize()));
1253 context = cgContextContainer.context();
1254 CGContextDrawShading(context, topShading.get());
1257 if (!bottomGradient.isEmpty()) {
1258 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1259 CGContextClipToRect(context, bottomGradient);
1260 paintInfo.context->clipRoundedRect(FloatRoundedRect(enclosingIntRect(bottomGradient), IntSize(), IntSize(), border.radii().bottomLeft(), border.radii().bottomRight()));
1261 context = cgContextContainer.context();
1262 CGContextDrawShading(context, bottomShading.get());
1266 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1267 CGContextClipToRect(context, r);
1268 paintInfo.context->clipRoundedRect(border);
1269 context = cgContextContainer.context();
1270 CGContextDrawShading(context, leftShading.get());
1271 CGContextDrawShading(context, rightShading.get());
1275 bool RenderThemeMac::paintMenuListButtonDecorations(const RenderObject& renderer, const PaintInfo& paintInfo, const FloatRect& rect)
1277 IntRect bounds = IntRect(rect.x() + renderer.style().borderLeftWidth(),
1278 rect.y() + renderer.style().borderTopWidth(),
1279 rect.width() - renderer.style().borderLeftWidth() - renderer.style().borderRightWidth(),
1280 rect.height() - renderer.style().borderTopWidth() - renderer.style().borderBottomWidth());
1281 // Draw the gradients to give the styled popup menu a button appearance
1282 paintMenuListButtonGradients(renderer, paintInfo, bounds);
1284 // 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
1285 float fontScale = std::min(renderer.style().fontSize() / baseFontSize, bounds.height() / (baseArrowHeight * 2 + baseSpaceBetweenArrows));
1286 float centerY = bounds.y() + bounds.height() / 2.0f;
1287 float arrowHeight = baseArrowHeight * fontScale;
1288 float arrowWidth = baseArrowWidth * fontScale;
1289 float leftEdge = bounds.maxX() - arrowPaddingRight * renderer.style().effectiveZoom() - arrowWidth;
1290 float spaceBetweenArrows = baseSpaceBetweenArrows * fontScale;
1292 if (bounds.width() < arrowWidth + arrowPaddingLeft * renderer.style().effectiveZoom())
1295 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1297 paintInfo.context->setFillColor(renderer.style().visitedDependentColor(CSSPropertyColor), renderer.style().colorSpace());
1298 paintInfo.context->setStrokeStyle(NoStroke);
1300 FloatPoint arrow1[3];
1301 arrow1[0] = FloatPoint(leftEdge, centerY - spaceBetweenArrows / 2.0f);
1302 arrow1[1] = FloatPoint(leftEdge + arrowWidth, centerY - spaceBetweenArrows / 2.0f);
1303 arrow1[2] = FloatPoint(leftEdge + arrowWidth / 2.0f, centerY - spaceBetweenArrows / 2.0f - arrowHeight);
1305 // Draw the top arrow
1306 paintInfo.context->drawConvexPolygon(3, arrow1, true);
1308 FloatPoint arrow2[3];
1309 arrow2[0] = FloatPoint(leftEdge, centerY + spaceBetweenArrows / 2.0f);
1310 arrow2[1] = FloatPoint(leftEdge + arrowWidth, centerY + spaceBetweenArrows / 2.0f);
1311 arrow2[2] = FloatPoint(leftEdge + arrowWidth / 2.0f, centerY + spaceBetweenArrows / 2.0f + arrowHeight);
1313 // Draw the bottom arrow
1314 paintInfo.context->drawConvexPolygon(3, arrow2, true);
1316 Color leftSeparatorColor(0, 0, 0, 40);
1317 Color rightSeparatorColor(255, 255, 255, 40);
1319 // FIXME: Should the separator thickness and space be scaled up by fontScale?
1320 int separatorSpace = 2; // Deliberately ignores zoom since it looks nicer if it stays thin.
1321 int leftEdgeOfSeparator = static_cast<int>(leftEdge - arrowPaddingLeft * renderer.style().effectiveZoom()); // FIXME: Round?
1323 // Draw the separator to the left of the arrows
1324 paintInfo.context->setStrokeThickness(1); // Deliberately ignores zoom since it looks nicer if it stays thin.
1325 paintInfo.context->setStrokeStyle(SolidStroke);
1326 paintInfo.context->setStrokeColor(leftSeparatorColor, ColorSpaceDeviceRGB);
1327 paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator, bounds.y()),
1328 IntPoint(leftEdgeOfSeparator, bounds.maxY()));
1330 paintInfo.context->setStrokeColor(rightSeparatorColor, ColorSpaceDeviceRGB);
1331 paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.y()),
1332 IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.maxY()));
1336 static const IntSize* menuListButtonSizes()
1338 static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) };
1342 void RenderThemeMac::adjustMenuListStyle(StyleResolver& styleResolver, RenderStyle& style, Element& e) const
1344 NSControlSize controlSize = controlSizeForFont(style);
1346 style.resetBorder();
1347 style.resetPadding();
1349 // Height is locked to auto.
1350 style.setHeight(Length(Auto));
1352 // White-space is locked to pre
1353 style.setWhiteSpace(PRE);
1355 // Set the foreground color to black or gray when we have the aqua look.
1356 // Cast to RGB32 is to work around a compiler bug.
1357 style.setColor(!e.isDisabledFormControl() ? static_cast<RGBA32>(Color::black) : Color::darkGray);
1359 // Set the button's vertical size.
1360 setSizeFromFont(style, menuListButtonSizes());
1362 // 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
1363 // a reasonable control size, but once that control size is determined, we throw that font away and use the appropriate
1364 // system font for the control size instead.
1365 setFontFromControlSize(styleResolver, style, controlSize);
1367 style.setBoxShadow(nullptr);
1370 int RenderThemeMac::popupInternalPaddingLeft(RenderStyle& style) const
1372 if (style.appearance() == MenulistPart)
1373 return popupButtonPadding(controlSizeForFont(style))[leftPadding] * style.effectiveZoom();
1374 if (style.appearance() == MenulistButtonPart)
1375 return styledPopupPaddingLeft * style.effectiveZoom();
1379 int RenderThemeMac::popupInternalPaddingRight(RenderStyle& style) const
1381 if (style.appearance() == MenulistPart)
1382 return popupButtonPadding(controlSizeForFont(style))[rightPadding] * style.effectiveZoom();
1383 if (style.appearance() == MenulistButtonPart) {
1384 float fontScale = style.fontSize() / baseFontSize;
1385 float arrowWidth = baseArrowWidth * fontScale;
1386 return static_cast<int>(ceilf(arrowWidth + (arrowPaddingLeft + arrowPaddingRight + paddingBeforeSeparator) * style.effectiveZoom()));
1391 int RenderThemeMac::popupInternalPaddingTop(RenderStyle& style) const
1393 if (style.appearance() == MenulistPart)
1394 return popupButtonPadding(controlSizeForFont(style))[topPadding] * style.effectiveZoom();
1395 if (style.appearance() == MenulistButtonPart)
1396 return styledPopupPaddingTop * style.effectiveZoom();
1400 int RenderThemeMac::popupInternalPaddingBottom(RenderStyle& style) const
1402 if (style.appearance() == MenulistPart)
1403 return popupButtonPadding(controlSizeForFont(style))[bottomPadding] * style.effectiveZoom();
1404 if (style.appearance() == MenulistButtonPart)
1405 return styledPopupPaddingBottom * style.effectiveZoom();
1409 PopupMenuStyle::PopupMenuSize RenderThemeMac::popupMenuSize(const RenderStyle& style, IntRect& rect) const
1411 NSPopUpButtonCell* popupButton = this->popupButton();
1412 NSControlSize size = controlSizeForCell(popupButton, popupButtonSizes(), rect.size(), style.effectiveZoom());
1414 case NSRegularControlSize:
1415 return PopupMenuStyle::PopupMenuSizeNormal;
1416 case NSSmallControlSize:
1417 return PopupMenuStyle::PopupMenuSizeSmall;
1418 case NSMiniControlSize:
1419 return PopupMenuStyle::PopupMenuSizeMini;
1421 return PopupMenuStyle::PopupMenuSizeNormal;
1425 void RenderThemeMac::adjustMenuListButtonStyle(StyleResolver&, RenderStyle& style, Element&) const
1427 float fontScale = style.fontSize() / baseFontSize;
1429 style.resetPadding();
1430 style.setBorderRadius(IntSize(int(baseBorderRadius + fontScale - 1), int(baseBorderRadius + fontScale - 1))); // FIXME: Round up?
1432 const int minHeight = 15;
1433 style.setMinHeight(Length(minHeight, Fixed));
1435 style.setLineHeight(RenderStyle::initialLineHeight());
1438 void RenderThemeMac::setPopupButtonCellState(const RenderObject& o, const IntSize& buttonSize)
1440 NSPopUpButtonCell* popupButton = this->popupButton();
1442 // Set the control size based off the rectangle we're painting into.
1443 setControlSize(popupButton, popupButtonSizes(), buttonSize, o.style().effectiveZoom());
1445 // Update the various states we respond to.
1446 updateCheckedState(popupButton, o);
1447 updateEnabledState(popupButton, o);
1448 updatePressedState(popupButton, o);
1451 const IntSize* RenderThemeMac::menuListSizes() const
1453 static const IntSize sizes[3] = { IntSize(9, 0), IntSize(5, 0), IntSize(0, 0) };
1457 int RenderThemeMac::minimumMenuListSize(RenderStyle& style) const
1459 return sizeForSystemFont(style, menuListSizes()).width();
1462 const int trackWidth = 5;
1463 const int trackRadius = 2;
1465 void RenderThemeMac::adjustSliderTrackStyle(StyleResolver&, RenderStyle& style, Element&) const
1467 style.setBoxShadow(nullptr);
1470 bool RenderThemeMac::paintSliderTrack(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1473 float zoomLevel = o.style().effectiveZoom();
1474 float zoomedTrackWidth = trackWidth * zoomLevel;
1476 if (o.style().appearance() == SliderHorizontalPart || o.style().appearance() == MediaSliderPart) {
1477 bounds.setHeight(zoomedTrackWidth);
1478 bounds.setY(r.y() + r.height() / 2 - zoomedTrackWidth / 2);
1479 } else if (o.style().appearance() == SliderVerticalPart) {
1480 bounds.setWidth(zoomedTrackWidth);
1481 bounds.setX(r.x() + r.width() / 2 - zoomedTrackWidth / 2);
1484 LocalCurrentGraphicsContext localContext(paintInfo.context);
1485 CGContextRef context = localContext.cgContext();
1486 CGColorSpaceRef cspace = deviceRGBColorSpaceRef();
1488 #if ENABLE(DATALIST_ELEMENT)
1489 paintSliderTicks(o, paintInfo, r);
1492 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1493 CGContextClipToRect(context, bounds);
1495 struct CGFunctionCallbacks mainCallbacks = { 0, TrackGradientInterpolate, NULL };
1496 RetainPtr<CGFunctionRef> mainFunction = adoptCF(CGFunctionCreate(NULL, 1, NULL, 4, NULL, &mainCallbacks));
1497 RetainPtr<CGShadingRef> mainShading;
1498 if (o.style().appearance() == SliderVerticalPart)
1499 mainShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(bounds.x(), bounds.maxY()), CGPointMake(bounds.maxX(), bounds.maxY()), mainFunction.get(), false, false));
1501 mainShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(bounds.x(), bounds.y()), CGPointMake(bounds.x(), bounds.maxY()), mainFunction.get(), false, false));
1503 IntSize radius(trackRadius, trackRadius);
1504 paintInfo.context->clipRoundedRect(FloatRoundedRect(bounds, radius, radius, radius, radius));
1505 context = localContext.cgContext();
1506 CGContextDrawShading(context, mainShading.get());
1511 void RenderThemeMac::adjustSliderThumbStyle(StyleResolver& styleResolver, RenderStyle& style, Element& element) const
1513 RenderTheme::adjustSliderThumbStyle(styleResolver, style, element);
1514 style.setBoxShadow(nullptr);
1517 const float verticalSliderHeightPadding = 0.1f;
1519 bool RenderThemeMac::paintSliderThumb(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1521 NSSliderCell* sliderThumbCell = o.style().appearance() == SliderThumbVerticalPart
1522 ? sliderThumbVertical()
1523 : sliderThumbHorizontal();
1525 LocalCurrentGraphicsContext localContext(paintInfo.context);
1527 // Update the various states we respond to.
1528 updateEnabledState(sliderThumbCell, o);
1529 Element* focusDelegate = (o.node() && o.node()->isElementNode()) ? toElement(o.node())->focusDelegate() : 0;
1531 updateFocusedState(sliderThumbCell, *focusDelegate->renderer());
1533 // Update the pressed state using the NSCell tracking methods, since that's how NSSliderCell keeps track of it.
1535 if (o.style().appearance() == SliderThumbVerticalPart)
1536 oldPressed = m_isSliderThumbVerticalPressed;
1538 oldPressed = m_isSliderThumbHorizontalPressed;
1540 bool pressed = isPressed(o);
1542 if (o.style().appearance() == SliderThumbVerticalPart)
1543 m_isSliderThumbVerticalPressed = pressed;
1545 m_isSliderThumbHorizontalPressed = pressed;
1547 if (pressed != oldPressed) {
1549 [sliderThumbCell startTrackingAt:NSPoint() inView:nil];
1551 [sliderThumbCell stopTracking:NSPoint() at:NSPoint() inView:nil mouseIsUp:YES];
1554 FloatRect bounds = r;
1555 // Make the height of the vertical slider slightly larger so NSSliderCell will draw a vertical slider.
1556 if (o.style().appearance() == SliderThumbVerticalPart)
1557 bounds.setHeight(bounds.height() + verticalSliderHeightPadding * o.style().effectiveZoom());
1559 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1560 float zoomLevel = o.style().effectiveZoom();
1562 FloatRect unzoomedRect = bounds;
1563 if (zoomLevel != 1.0f) {
1564 unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel);
1565 unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel);
1566 paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y());
1567 paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel));
1568 paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y());
1571 [sliderThumbCell drawInteriorWithFrame:unzoomedRect inView:documentViewFor(o)];
1572 [sliderThumbCell setControlView:nil];
1577 bool RenderThemeMac::paintSearchField(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1579 LocalCurrentGraphicsContext localContext(paintInfo.context);
1580 NSSearchFieldCell* search = this->search();
1582 setSearchCellState(o, r);
1584 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1586 float zoomLevel = o.style().effectiveZoom();
1588 IntRect unzoomedRect = r;
1590 if (zoomLevel != 1.0f) {
1591 unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel);
1592 unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel);
1593 paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y());
1594 paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel));
1595 paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y());
1598 // Set the search button to nil before drawing. Then reset it so we can draw it later.
1599 [search setSearchButtonCell:nil];
1601 [search drawWithFrame:NSRect(unzoomedRect) inView:documentViewFor(o)];
1603 [search setControlView:nil];
1604 [search resetSearchButtonCell];
1609 void RenderThemeMac::setSearchCellState(const RenderObject& o, const IntRect&)
1611 NSSearchFieldCell* search = this->search();
1613 [search setControlSize:controlSizeForFont(o.style())];
1615 // Update the various states we respond to.
1616 updateEnabledState(search, o);
1617 updateFocusedState(search, o);
1620 const IntSize* RenderThemeMac::searchFieldSizes() const
1622 static const IntSize sizes[3] = { IntSize(0, 22), IntSize(0, 19), IntSize(0, 17) };
1626 void RenderThemeMac::setSearchFieldSize(RenderStyle& style) const
1628 // If the width and height are both specified, then we have nothing to do.
1629 if (!style.width().isIntrinsicOrAuto() && !style.height().isAuto())
1632 // Use the font size to determine the intrinsic width of the control.
1633 setSizeFromFont(style, searchFieldSizes());
1636 void RenderThemeMac::adjustSearchFieldStyle(StyleResolver& styleResolver, RenderStyle& style, Element&) const
1639 style.resetBorder();
1640 const short borderWidth = 2 * style.effectiveZoom();
1641 style.setBorderLeftWidth(borderWidth);
1642 style.setBorderLeftStyle(INSET);
1643 style.setBorderRightWidth(borderWidth);
1644 style.setBorderRightStyle(INSET);
1645 style.setBorderBottomWidth(borderWidth);
1646 style.setBorderBottomStyle(INSET);
1647 style.setBorderTopWidth(borderWidth);
1648 style.setBorderTopStyle(INSET);
1651 style.setHeight(Length(Auto));
1652 setSearchFieldSize(style);
1654 // Override padding size to match AppKit text positioning.
1655 const int padding = 1 * style.effectiveZoom();
1656 style.setPaddingLeft(Length(padding, Fixed));
1657 style.setPaddingRight(Length(padding, Fixed));
1658 style.setPaddingTop(Length(padding, Fixed));
1659 style.setPaddingBottom(Length(padding, Fixed));
1661 NSControlSize controlSize = controlSizeForFont(style);
1662 setFontFromControlSize(styleResolver, style, controlSize);
1664 style.setBoxShadow(nullptr);
1667 bool RenderThemeMac::paintSearchFieldCancelButton(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1669 Element* input = o.node()->shadowHost();
1671 input = toElement(o.node());
1673 if (!input->renderer()->isBox())
1676 LocalCurrentGraphicsContext localContext(paintInfo.context);
1677 setSearchCellState(*input->renderer(), r);
1679 NSSearchFieldCell* search = this->search();
1681 if (!input->isDisabledFormControl() && (input->isTextFormControl() && !toHTMLTextFormControlElement(input)->isReadOnly()))
1682 updatePressedState([search cancelButtonCell], o);
1683 else if ([[search cancelButtonCell] isHighlighted])
1684 [[search cancelButtonCell] setHighlighted:NO];
1686 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1688 float zoomLevel = o.style().effectiveZoom();
1690 FloatRect localBounds = [search cancelButtonRectForBounds:NSRect(input->renderBox()->pixelSnappedBorderBoxRect())];
1691 localBounds = convertToPaintingRect(*input->renderer(), o, localBounds, r);
1693 FloatRect unzoomedRect(localBounds);
1694 if (zoomLevel != 1.0f) {
1695 unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel);
1696 unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel);
1697 paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y());
1698 paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel));
1699 paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y());
1702 [[search cancelButtonCell] drawWithFrame:unzoomedRect inView:documentViewFor(o)];
1703 [[search cancelButtonCell] setControlView:nil];
1707 const IntSize* RenderThemeMac::cancelButtonSizes() const
1709 static const IntSize sizes[3] = { IntSize(16, 13), IntSize(13, 11), IntSize(13, 9) };
1713 void RenderThemeMac::adjustSearchFieldCancelButtonStyle(StyleResolver&, RenderStyle& style, Element&) const
1715 IntSize size = sizeForSystemFont(style, cancelButtonSizes());
1716 style.setWidth(Length(size.width(), Fixed));
1717 style.setHeight(Length(size.height(), Fixed));
1718 style.setBoxShadow(nullptr);
1721 const IntSize* RenderThemeMac::resultsButtonSizes() const
1723 static const IntSize sizes[3] = { IntSize(19, 13), IntSize(17, 11), IntSize(17, 9) };
1727 const int emptyResultsOffset = 9;
1728 void RenderThemeMac::adjustSearchFieldDecorationPartStyle(StyleResolver&, RenderStyle& style, Element&) const
1730 IntSize size = sizeForSystemFont(style, resultsButtonSizes());
1731 style.setWidth(Length(size.width() - emptyResultsOffset, Fixed));
1732 style.setHeight(Length(size.height(), Fixed));
1733 style.setBoxShadow(nullptr);
1736 bool RenderThemeMac::paintSearchFieldDecorationPart(const RenderObject&, const PaintInfo&, const IntRect&)
1741 void RenderThemeMac::adjustSearchFieldResultsDecorationPartStyle(StyleResolver&, RenderStyle& style, Element&) const
1743 IntSize size = sizeForSystemFont(style, resultsButtonSizes());
1744 style.setWidth(Length(size.width(), Fixed));
1745 style.setHeight(Length(size.height(), Fixed));
1746 style.setBoxShadow(nullptr);
1749 bool RenderThemeMac::paintSearchFieldResultsDecorationPart(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1751 Node* input = o.node()->shadowHost();
1754 if (!input->renderer()->isBox())
1757 LocalCurrentGraphicsContext localContext(paintInfo.context);
1758 setSearchCellState(*input->renderer(), r);
1760 NSSearchFieldCell* search = this->search();
1762 if ([search searchMenuTemplate] != nil)
1763 [search setSearchMenuTemplate:nil];
1765 FloatRect localBounds = [search searchButtonRectForBounds:NSRect(input->renderBox()->pixelSnappedBorderBoxRect())];
1766 localBounds = convertToPaintingRect(*input->renderer(), o, localBounds, r);
1768 [[search searchButtonCell] drawWithFrame:localBounds inView:documentViewFor(o)];
1769 [[search searchButtonCell] setControlView:nil];
1773 const int resultsArrowWidth = 5;
1774 void RenderThemeMac::adjustSearchFieldResultsButtonStyle(StyleResolver&, RenderStyle& style, Element&) const
1776 IntSize size = sizeForSystemFont(style, resultsButtonSizes());
1777 style.setWidth(Length(size.width() + resultsArrowWidth, Fixed));
1778 style.setHeight(Length(size.height(), Fixed));
1779 style.setBoxShadow(nullptr);
1782 bool RenderThemeMac::paintSearchFieldResultsButton(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1784 Node* input = o.node()->shadowHost();
1787 if (!input->renderer()->isBox())
1790 LocalCurrentGraphicsContext localContext(paintInfo.context);
1791 setSearchCellState(*input->renderer(), r);
1793 NSSearchFieldCell* search = this->search();
1795 if (![search searchMenuTemplate])
1796 [search setSearchMenuTemplate:searchMenuTemplate()];
1798 GraphicsContextStateSaver stateSaver(*paintInfo.context);
1799 float zoomLevel = o.style().effectiveZoom();
1801 FloatRect localBounds = [search searchButtonRectForBounds:NSRect(input->renderBox()->pixelSnappedBorderBoxRect())];
1802 localBounds = convertToPaintingRect(*input->renderer(), o, localBounds, r);
1804 IntRect unzoomedRect(localBounds);
1805 if (zoomLevel != 1.0f) {
1806 unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel);
1807 unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel);
1808 paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y());
1809 paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel));
1810 paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y());
1813 [[search searchButtonCell] drawWithFrame:unzoomedRect inView:documentViewFor(o)];
1814 [[search searchButtonCell] setControlView:nil];
1819 bool RenderThemeMac::paintSnapshottedPluginOverlay(const RenderObject& o, const PaintInfo& paintInfo, const IntRect&)
1821 if (paintInfo.phase != PaintPhaseBlockBackground)
1824 if (!o.isRenderBlock())
1827 const RenderBlock& renderBlock = *toRenderBlock(&o);
1829 LayoutUnit contentWidth = renderBlock.contentWidth();
1830 LayoutUnit contentHeight = renderBlock.contentHeight();
1831 if (!contentWidth || !contentHeight)
1834 GraphicsContext* context = paintInfo.context;
1836 LayoutSize contentSize(contentWidth, contentHeight);
1837 LayoutPoint contentLocation = renderBlock.location();
1838 contentLocation.move(renderBlock.borderLeft() + renderBlock.paddingLeft(), renderBlock.borderTop() + renderBlock.paddingTop());
1840 LayoutRect rect(contentLocation, contentSize);
1841 IntRect alignedRect = snappedIntRect(rect);
1842 if (alignedRect.width() <= 0 || alignedRect.height() <= 0)
1845 // We need to get the snapshot image from the plugin element, which should be available
1846 // from our node. Assuming this node is the plugin overlay element, we should get to the
1847 // plugin itself by asking for the shadow root parent, and then its parent.
1849 if (!renderBlock.element()->isHTMLElement())
1852 HTMLElement* plugInOverlay = toHTMLElement(renderBlock.element());
1853 Element* parent = plugInOverlay->parentOrShadowHostElement();
1854 while (parent && !parent->isPluginElement())
1855 parent = parent->parentOrShadowHostElement();
1860 HTMLPlugInElement* plugInElement = toHTMLPlugInElement(parent);
1861 if (!plugInElement->isPlugInImageElement())
1864 HTMLPlugInImageElement* plugInImageElement = toHTMLPlugInImageElement(plugInElement);
1866 Image* snapshot = plugInImageElement->snapshotImage();
1870 RenderSnapshottedPlugIn* plugInRenderer = toRenderSnapshottedPlugIn(plugInImageElement->renderer());
1871 FloatPoint snapshotAbsPos = plugInRenderer->localToAbsolute();
1872 snapshotAbsPos.move(plugInRenderer->borderLeft() + plugInRenderer->paddingLeft(), plugInRenderer->borderTop() + plugInRenderer->paddingTop());
1874 // We could draw the snapshot with that coordinates, but we need to make sure there
1875 // isn't a composited layer between us and the plugInRenderer.
1876 const RenderBox* renderBox = toRenderBox(&o);
1877 while (renderBox != plugInRenderer) {
1878 if (renderBox->hasLayer() && renderBox->layer() && renderBox->layer()->isComposited()) {
1879 snapshotAbsPos = -renderBox->location();
1882 renderBox = renderBox->parentBox();
1885 LayoutSize pluginSize(plugInRenderer->contentWidth(), plugInRenderer->contentHeight());
1886 LayoutRect pluginRect(snapshotAbsPos, pluginSize);
1887 IntRect alignedPluginRect = snappedIntRect(pluginRect);
1889 if (alignedPluginRect.width() <= 0 || alignedPluginRect.height() <= 0)
1892 context->drawImage(snapshot, plugInRenderer->style().colorSpace(), alignedPluginRect, CompositeSourceOver);
1896 #if ENABLE(DATALIST_ELEMENT)
1897 IntSize RenderThemeMac::sliderTickSize() const
1899 return IntSize(1, 3);
1902 int RenderThemeMac::sliderTickOffsetFromTrackCenter() const
1908 const int sliderThumbWidth = 15;
1909 const int sliderThumbHeight = 15;
1911 void RenderThemeMac::adjustSliderThumbSize(RenderStyle& style, Element&) const
1913 float zoomLevel = style.effectiveZoom();
1914 if (style.appearance() == SliderThumbHorizontalPart || style.appearance() == SliderThumbVerticalPart) {
1915 style.setWidth(Length(static_cast<int>(sliderThumbWidth * zoomLevel), Fixed));
1916 style.setHeight(Length(static_cast<int>(sliderThumbHeight * zoomLevel), Fixed));
1920 bool RenderThemeMac::shouldShowPlaceholderWhenFocused() const
1925 NSPopUpButtonCell* RenderThemeMac::popupButton() const
1927 if (!m_popupButton) {
1928 m_popupButton = adoptNS([[NSPopUpButtonCell alloc] initTextCell:@"" pullsDown:NO]);
1929 [m_popupButton.get() setUsesItemFromMenu:NO];
1930 [m_popupButton.get() setFocusRingType:NSFocusRingTypeExterior];
1931 // We don't want the app's UI layout direction to affect the appearance of popup buttons in
1932 // web content, which has its own layout direction.
1933 // FIXME: Make this depend on the directionality of the select element, once the rest of the
1934 // rendering code can account for the popup arrows appearing on the other side.
1935 [m_popupButton setUserInterfaceLayoutDirection:NSUserInterfaceLayoutDirectionLeftToRight];
1938 return m_popupButton.get();
1941 NSSearchFieldCell* RenderThemeMac::search() const
1944 m_search = adoptNS([[NSSearchFieldCell alloc] initTextCell:@""]);
1945 [m_search.get() setBezelStyle:NSTextFieldRoundedBezel];
1946 [m_search.get() setBezeled:YES];
1947 [m_search.get() setEditable:YES];
1948 [m_search.get() setFocusRingType:NSFocusRingTypeExterior];
1949 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
1950 [m_search.get() setCenteredLook:NO];
1954 return m_search.get();
1957 NSMenu* RenderThemeMac::searchMenuTemplate() const
1959 if (!m_searchMenuTemplate)
1960 m_searchMenuTemplate = adoptNS([[NSMenu alloc] initWithTitle:@""]);
1962 return m_searchMenuTemplate.get();
1965 NSSliderCell* RenderThemeMac::sliderThumbHorizontal() const
1967 if (!m_sliderThumbHorizontal) {
1968 m_sliderThumbHorizontal = adoptNS([[NSSliderCell alloc] init]);
1969 [m_sliderThumbHorizontal.get() setSliderType:NSLinearSlider];
1970 [m_sliderThumbHorizontal.get() setControlSize:NSSmallControlSize];
1971 [m_sliderThumbHorizontal.get() setFocusRingType:NSFocusRingTypeExterior];
1974 return m_sliderThumbHorizontal.get();
1977 NSSliderCell* RenderThemeMac::sliderThumbVertical() const
1979 if (!m_sliderThumbVertical) {
1980 m_sliderThumbVertical = adoptNS([[NSSliderCell alloc] init]);
1981 [m_sliderThumbVertical.get() setSliderType:NSLinearSlider];
1982 [m_sliderThumbVertical.get() setControlSize:NSSmallControlSize];
1983 [m_sliderThumbVertical.get() setFocusRingType:NSFocusRingTypeExterior];
1986 return m_sliderThumbVertical.get();
1989 NSTextFieldCell* RenderThemeMac::textField() const
1992 m_textField = adoptNS([[WebCoreTextFieldCell alloc] initTextCell:@""]);
1993 [m_textField.get() setBezeled:YES];
1994 [m_textField.get() setEditable:YES];
1995 [m_textField.get() setFocusRingType:NSFocusRingTypeExterior];
1996 // Post-Lion, WebCore can be in charge of paintinng the background thanks to
1997 // the workaround in place for <rdar://problem/11385461>, which is implemented
1998 // above as _coreUIDrawOptionsWithFrame.
1999 [m_textField.get() setDrawsBackground:NO];
2002 return m_textField.get();
2005 String RenderThemeMac::fileListNameForWidth(const FileList* fileList, const Font& font, int width, bool multipleFilesAllowed) const
2010 String strToTruncate;
2011 if (fileList->isEmpty())
2012 strToTruncate = fileListDefaultLabel(multipleFilesAllowed);
2013 else if (fileList->length() == 1)
2014 strToTruncate = [[NSFileManager defaultManager] displayNameAtPath:(fileList->item(0)->path())];
2016 return StringTruncator::rightTruncate(multipleFileUploadText(fileList->length()), width, font, StringTruncator::EnableRoundingHacks);
2018 return StringTruncator::centerTruncate(strToTruncate, width, font, StringTruncator::EnableRoundingHacks);
2021 bool RenderThemeMac::defaultButtonHasAnimation() const
2023 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
2030 #if ENABLE(SERVICE_CONTROLS)
2031 NSServicesRolloverButtonCell* RenderThemeMac::servicesRolloverButtonCell() const
2033 #if HAVE(APPKIT_SERVICE_CONTROLS_SUPPORT)
2034 if (!m_servicesRolloverButton) {
2035 m_servicesRolloverButton = [NSServicesRolloverButtonCell serviceRolloverButtonCellForStyle:NSSharingServicePickerStyleRollover];
2036 [m_servicesRolloverButton setBezelStyle:NSRoundedDisclosureBezelStyle];
2037 [m_servicesRolloverButton setButtonType:NSPushOnPushOffButton];
2038 [m_servicesRolloverButton setImagePosition:NSImageOnly];
2039 [m_servicesRolloverButton setState:NO];
2042 return m_servicesRolloverButton.get();
2048 bool RenderThemeMac::paintImageControlsButton(const RenderObject& renderer, const PaintInfo& paintInfo, const IntRect& rect)
2050 if (paintInfo.phase != PaintPhaseBlockBackground)
2053 #if HAVE(APPKIT_SERVICE_CONTROLS_SUPPORT)
2054 NSServicesRolloverButtonCell *cell = servicesRolloverButtonCell();
2056 LocalCurrentGraphicsContext localContext(paintInfo.context);
2057 GraphicsContextStateSaver stateSaver(*paintInfo.context);
2059 paintInfo.context->translate(rect.x(), rect.y());
2061 IntRect innerFrame(IntPoint(), rect.size());
2062 [cell drawWithFrame:innerFrame inView:documentViewFor(renderer)];
2063 [cell setControlView:nil];
2065 UNUSED_PARAM(renderer);
2072 IntSize RenderThemeMac::imageControlsButtonSize(const RenderObject&) const
2074 #if HAVE(APPKIT_SERVICE_CONTROLS_SUPPORT)
2075 return IntSize(servicesRolloverButtonCell().cellSize);
2081 IntSize RenderThemeMac::imageControlsButtonPositionOffset() const
2083 #if HAVE(APPKIT_SERVICE_CONTROLS_SUPPORT)
2084 // FIXME: Currently the offsets will always be the same no matter what image rect you try with.
2085 // This may not always be true in the future.
2086 static const int dummyDimension = 100;
2087 IntRect dummyImageRect(0, 0, dummyDimension, dummyDimension);
2088 NSRect bounds = [servicesRolloverButtonCell() rectForBounds:dummyImageRect preferredEdge:NSMinYEdge];
2090 return IntSize(dummyDimension - bounds.origin.x, bounds.origin.y);
2097 } // namespace WebCore
2099 #endif // !PLATFORM(IOS)