Create a named CSS property for system colors
[WebKit-https.git] / Source / WebCore / rendering / RenderThemeMac.mm
1 /*
2  * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 Apple Inc. All rights reserved.
3  *
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.
8  *
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.
13  *
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.
18  */
19 #if !PLATFORM(IOS)
20
21 #import "config.h"
22 #import "RenderThemeMac.h"
23
24 #import "BitmapImage.h"
25 #import "CSSValueKeywords.h"
26 #import "CSSValueList.h"
27 #import "ColorMac.h"
28 #import "CoreGraphicsSPI.h"
29 #import "Document.h"
30 #import "Element.h"
31 #import "ExceptionCodePlaceholder.h"
32 #import "FileList.h"
33 #import "FloatRoundedRect.h"
34 #import "FocusController.h"
35 #import "Frame.h"
36 #import "FrameSelection.h"
37 #import "FrameView.h"
38 #import "GraphicsContextCG.h"
39 #import "HTMLAttachmentElement.h"
40 #import "HTMLAudioElement.h"
41 #import "HTMLInputElement.h"
42 #import "HTMLMediaElement.h"
43 #import "HTMLNames.h"
44 #import "HTMLPlugInImageElement.h"
45 #import "Icon.h"
46 #import "Image.h"
47 #import "ImageBuffer.h"
48 #import "LocalCurrentGraphicsContext.h"
49 #import "LocalizedStrings.h"
50 #import "MediaControlElements.h"
51 #import "NSColorSPI.h"
52 #import "NSSharingServicePickerSPI.h"
53 #import "Page.h"
54 #import "PaintInfo.h"
55 #import "RenderAttachment.h"
56 #import "RenderLayer.h"
57 #import "RenderMedia.h"
58 #import "RenderMediaControlElements.h"
59 #import "RenderMediaControls.h"
60 #import "RenderProgress.h"
61 #import "RenderSlider.h"
62 #import "RenderSnapshottedPlugIn.h"
63 #import "RenderView.h"
64 #import "SharedBuffer.h"
65 #import "StringTruncator.h"
66 #import "StyleResolver.h"
67 #import "ThemeMac.h"
68 #import "TimeRanges.h"
69 #import "UserAgentScripts.h"
70 #import "UserAgentStyleSheets.h"
71 #import "WebCoreSystemInterface.h"
72 #import <wtf/RetainPtr.h>
73 #import <wtf/RetainPtr.h>
74 #import <wtf/StdLibExtras.h>
75 #import <wtf/text/StringBuilder.h>
76 #import <Carbon/Carbon.h>
77 #import <Cocoa/Cocoa.h>
78 #import <math.h>
79
80 #if ENABLE(METER_ELEMENT)
81 #import "RenderMeter.h"
82 #import "HTMLMeterElement.h"
83 #endif
84
85 #if defined(__LP64__) && __LP64__
86 #define HAVE_APPKIT_SERVICE_CONTROLS_SUPPORT 1
87 #else
88 #define HAVE_APPKIT_SERVICE_CONTROLS_SUPPORT 0
89 #endif
90
91 #if ENABLE(SERVICE_CONTROLS) && HAVE(APPKIT_SERVICE_CONTROLS_SUPPORT)
92
93 #if __has_include(<AppKit/AppKitDefines_Private.h>)
94 #import <AppKit/AppKitDefines_Private.h>
95 #else
96 #define APPKIT_PRIVATE_CLASS
97 #endif
98
99 #if __has_include(<AppKit/NSServicesRolloverButtonCell.h>)
100 #import <AppKit/NSServicesRolloverButtonCell.h>
101 #else
102 @interface NSServicesRolloverButtonCell : NSButtonCell
103 @end
104 #endif
105
106 @interface NSServicesRolloverButtonCell (Details)
107 + (NSServicesRolloverButtonCell *)serviceRolloverButtonCellForStyle:(NSSharingServicePickerStyle)style;
108 - (NSRect)rectForBounds:(NSRect)bounds preferredEdge:(NSRectEdge)preferredEdge;
109 @end
110
111 #endif // ENABLE(SERVICE_CONTROLS)
112
113 // The methods in this file are specific to the Mac OS X platform.
114
115 // FIXME: The platform-independent code in this class should be factored out and merged with RenderThemeSafari.
116
117 // We estimate the animation rate of a Mac OS X progress bar is 33 fps.
118 // Hard code the value here because we haven't found API for it.
119 const double progressAnimationFrameRate = 0.033;
120
121 // Mac OS X progress bar animation seems to have 256 frames.
122 const double progressAnimationNumFrames = 256;
123
124 @interface WebCoreRenderThemeNotificationObserver : NSObject
125 {
126     WebCore::RenderTheme *_theme;
127 }
128
129 - (id)initWithTheme:(WebCore::RenderTheme *)theme;
130 - (void)systemColorsDidChange:(NSNotification *)notification;
131
132 @end
133
134 @implementation WebCoreRenderThemeNotificationObserver
135
136 - (id)initWithTheme:(WebCore::RenderTheme *)theme
137 {
138     if (!(self = [super init]))
139         return nil;
140
141     _theme = theme;
142     return self;
143 }
144
145 - (void)systemColorsDidChange:(NSNotification *)unusedNotification
146 {
147     ASSERT_UNUSED(unusedNotification, [[unusedNotification name] isEqualToString:NSSystemColorsDidChangeNotification]);
148     _theme->platformColorsDidChange();
149 }
150
151 @end
152
153 @interface NSTextFieldCell (WKDetails)
154 - (CFDictionaryRef)_coreUIDrawOptionsWithFrame:(NSRect)cellFrame inView:(NSView *)controlView includeFocus:(BOOL)includeFocus;
155 @end
156
157
158 @interface WebCoreTextFieldCell : NSTextFieldCell
159 - (CFDictionaryRef)_coreUIDrawOptionsWithFrame:(NSRect)cellFrame inView:(NSView *)controlView includeFocus:(BOOL)includeFocus;
160 @end
161
162 @implementation WebCoreTextFieldCell
163 - (CFDictionaryRef)_coreUIDrawOptionsWithFrame:(NSRect)cellFrame inView:(NSView *)controlView includeFocus:(BOOL)includeFocus
164 {
165     // FIXME: This is a post-Lion-only workaround for <rdar://problem/11385461>. When that bug is resolved, we should remove this code.
166     CFMutableDictionaryRef coreUIDrawOptions = CFDictionaryCreateMutableCopy(NULL, 0, [super _coreUIDrawOptionsWithFrame:cellFrame inView:controlView includeFocus:includeFocus]);
167     CFDictionarySetValue(coreUIDrawOptions, @"borders only", kCFBooleanTrue);
168     return (CFDictionaryRef)[NSMakeCollectable(coreUIDrawOptions) autorelease];
169 }
170 @end
171
172 @interface WebCoreRenderThemeBundle : NSObject
173 @end
174
175 @implementation WebCoreRenderThemeBundle
176 @end
177
178 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
179 @interface NSSearchFieldCell(Details)
180 @property (getter=isCenteredLook) BOOL centeredLook;
181 @end
182 #endif
183
184 namespace WebCore {
185
186 using namespace HTMLNames;
187
188 enum {
189     topMargin,
190     rightMargin,
191     bottomMargin,
192     leftMargin
193 };
194
195 enum {
196     topPadding,
197     rightPadding,
198     bottomPadding,
199     leftPadding
200 };
201
202 PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page*)
203 {
204     static RenderTheme* rt = RenderThemeMac::create().leakRef();
205     return rt;
206 }
207
208 PassRefPtr<RenderTheme> RenderThemeMac::create()
209 {
210     return adoptRef(new RenderThemeMac);
211 }
212
213 RenderThemeMac::RenderThemeMac()
214     : m_isSliderThumbHorizontalPressed(false)
215     , m_isSliderThumbVerticalPressed(false)
216     , m_notificationObserver(adoptNS([[WebCoreRenderThemeNotificationObserver alloc] initWithTheme:this]))
217 {
218     [[NSNotificationCenter defaultCenter] addObserver:m_notificationObserver.get()
219                                                         selector:@selector(systemColorsDidChange:)
220                                                             name:NSSystemColorsDidChangeNotification
221                                                           object:nil];
222 }
223
224 RenderThemeMac::~RenderThemeMac()
225 {
226     [[NSNotificationCenter defaultCenter] removeObserver:m_notificationObserver.get()];
227 }
228
229 NSView* RenderThemeMac::documentViewFor(const RenderObject& o) const
230 {
231     ControlStates states(extractControlStatesForRenderer(o));
232     return ThemeMac::ensuredView(&o.view().frameView(), &states);
233 }
234
235 #if ENABLE(VIDEO)
236 String RenderThemeMac::mediaControlsStyleSheet()
237 {
238 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
239     if (m_mediaControlsStyleSheet.isEmpty())
240         m_mediaControlsStyleSheet = [NSString stringWithContentsOfFile:[[NSBundle bundleForClass:[WebCoreRenderThemeBundle class]] pathForResource:@"mediaControlsApple" ofType:@"css"] encoding:NSUTF8StringEncoding error:nil];
241     return m_mediaControlsStyleSheet;
242 #else
243     return emptyString();
244 #endif
245 }
246
247 String RenderThemeMac::mediaControlsScript()
248 {
249 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
250     if (m_mediaControlsScript.isEmpty()) {
251         StringBuilder scriptBuilder;
252         scriptBuilder.append([NSString stringWithContentsOfFile:[[NSBundle bundleForClass:[WebCoreRenderThemeBundle class]] pathForResource:@"mediaControlsLocalizedStrings" ofType:@"js"] encoding:NSUTF8StringEncoding error:nil]);
253         scriptBuilder.append([NSString stringWithContentsOfFile:[[NSBundle bundleForClass:[WebCoreRenderThemeBundle class]] pathForResource:@"mediaControlsApple" ofType:@"js"] encoding:NSUTF8StringEncoding error:nil]);
254         m_mediaControlsScript = scriptBuilder.toString();
255     }
256     return m_mediaControlsScript;
257 #else
258     return emptyString();
259 #endif
260 }
261
262 #endif // ENABLE(VIDEO)
263
264
265 #if ENABLE(SERVICE_CONTROLS)
266 String RenderThemeMac::imageControlsStyleSheet() const
267 {
268     return String(imageControlsMacUserAgentStyleSheet, sizeof(imageControlsMacUserAgentStyleSheet));
269 }
270 #endif
271
272 Color RenderThemeMac::platformActiveSelectionBackgroundColor() const
273 {
274     NSColor* color = [[NSColor selectedTextBackgroundColor] 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]));
276 }
277
278 Color RenderThemeMac::platformInactiveSelectionBackgroundColor() const
279 {
280     NSColor* color = [[NSColor secondarySelectedControlColor] 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]));
282 }
283
284 Color RenderThemeMac::platformActiveListBoxSelectionBackgroundColor() const
285 {
286     NSColor* color = [[NSColor alternateSelectedControlColor] colorUsingColorSpaceName:NSDeviceRGBColorSpace];
287     return Color(static_cast<int>(255.0 * [color redComponent]), static_cast<int>(255.0 * [color greenComponent]), static_cast<int>(255.0 * [color blueComponent]));
288 }
289
290 Color RenderThemeMac::platformActiveListBoxSelectionForegroundColor() const
291 {
292     return Color::white;
293 }
294
295 Color RenderThemeMac::platformInactiveListBoxSelectionForegroundColor() const
296 {
297     return Color::black;
298 }
299
300 Color RenderThemeMac::platformFocusRingColor() const
301 {
302     if (usesTestModeFocusRingColor())
303         return oldAquaFocusRingColor();
304
305     return systemColor(CSSValueWebkitFocusRingColor);
306 }
307
308 int RenderThemeMac::platformFocusRingMaxWidth() const
309 {
310     // FIXME: Shouldn't this function be named platformFocusRingMinWidth? But also, I'm not sure if this function is needed - looks like
311     // all platforms just used 0 for this before <http://trac.webkit.org/changeset/168397>.
312     return 0;
313 }
314
315 Color RenderThemeMac::platformInactiveListBoxSelectionBackgroundColor() const
316 {
317     return platformInactiveSelectionBackgroundColor();
318 }
319
320 static FontWeight toFontWeight(NSInteger appKitFontWeight)
321 {
322     ASSERT(appKitFontWeight > 0 && appKitFontWeight < 15);
323     if (appKitFontWeight > 14)
324         appKitFontWeight = 14;
325     else if (appKitFontWeight < 1)
326         appKitFontWeight = 1;
327
328     static FontWeight fontWeights[] = {
329         FontWeight100,
330         FontWeight100,
331         FontWeight200,
332         FontWeight300,
333         FontWeight400,
334         FontWeight500,
335         FontWeight600,
336         FontWeight600,
337         FontWeight700,
338         FontWeight800,
339         FontWeight800,
340         FontWeight900,
341         FontWeight900,
342         FontWeight900
343     };
344     return fontWeights[appKitFontWeight - 1];
345 }
346
347 void RenderThemeMac::updateCachedSystemFontDescription(CSSValueID cssValueId, FontDescription& fontDescription) const
348 {
349     NSFont* font;
350     switch (cssValueId) {
351         case CSSValueSmallCaption:
352             font = [NSFont systemFontOfSize:[NSFont smallSystemFontSize]];
353             break;
354         case CSSValueMenu:
355             font = [NSFont menuFontOfSize:[NSFont systemFontSize]];
356             break;
357         case CSSValueStatusBar:
358             font = [NSFont labelFontOfSize:[NSFont labelFontSize]];
359             break;
360         case CSSValueWebkitMiniControl:
361             font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSMiniControlSize]];
362             break;
363         case CSSValueWebkitSmallControl:
364             font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]];
365             break;
366         case CSSValueWebkitControl:
367             font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:NSRegularControlSize]];
368             break;
369         default:
370             font = [NSFont systemFontOfSize:[NSFont systemFontSize]];
371     }
372
373     if (!font)
374         return;
375
376     NSFontManager *fontManager = [NSFontManager sharedFontManager];
377     fontDescription.setIsAbsoluteSize(true);
378     fontDescription.setOneFamily([font webCoreFamilyName]);
379     fontDescription.setSpecifiedSize([font pointSize]);
380     fontDescription.setWeight(toFontWeight([fontManager weightOfFont:font]));
381     fontDescription.setIsItalic([fontManager traitsOfFont:font] & NSItalicFontMask);
382 }
383
384 static RGBA32 convertNSColorToColor(NSColor *color)
385 {
386     NSColor *colorInColorSpace = [color colorUsingColorSpaceName:NSDeviceRGBColorSpace];
387     if (colorInColorSpace) {
388         static const double scaleFactor = nextafter(256.0, 0.0);
389         return makeRGB(static_cast<int>(scaleFactor * [colorInColorSpace redComponent]),
390             static_cast<int>(scaleFactor * [colorInColorSpace greenComponent]),
391             static_cast<int>(scaleFactor * [colorInColorSpace blueComponent]));
392     }
393
394     // This conversion above can fail if the NSColor in question is an NSPatternColor
395     // (as many system colors are). These colors are actually a repeating pattern
396     // not just a solid color. To work around this we simply draw a 1x1 image of
397     // the color and use that pixel's color. It might be better to use an average of
398     // the colors in the pattern instead.
399     NSBitmapImageRep *offscreenRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil
400                                                                              pixelsWide:1
401                                                                              pixelsHigh:1
402                                                                           bitsPerSample:8
403                                                                         samplesPerPixel:4
404                                                                                hasAlpha:YES
405                                                                                isPlanar:NO
406                                                                          colorSpaceName:NSDeviceRGBColorSpace
407                                                                             bytesPerRow:4
408                                                                            bitsPerPixel:32];
409
410     [NSGraphicsContext saveGraphicsState];
411     [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:offscreenRep]];
412     NSEraseRect(NSMakeRect(0, 0, 1, 1));
413     [color drawSwatchInRect:NSMakeRect(0, 0, 1, 1)];
414     [NSGraphicsContext restoreGraphicsState];
415
416     NSUInteger pixel[4];
417     [offscreenRep getPixel:pixel atX:0 y:0];
418
419     [offscreenRep release];
420
421     return makeRGB(pixel[0], pixel[1], pixel[2]);
422 }
423
424 static RGBA32 menuBackgroundColor()
425 {
426     NSBitmapImageRep *offscreenRep = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:nil
427                                                                              pixelsWide:1
428                                                                              pixelsHigh:1
429                                                                           bitsPerSample:8
430                                                                         samplesPerPixel:4
431                                                                                hasAlpha:YES
432                                                                                isPlanar:NO
433                                                                          colorSpaceName:NSDeviceRGBColorSpace
434                                                                             bytesPerRow:4
435                                                                            bitsPerPixel:32];
436
437     CGContextRef context = static_cast<CGContextRef>([[NSGraphicsContext graphicsContextWithBitmapImageRep:offscreenRep] graphicsPort]);
438     CGRect rect = CGRectMake(0, 0, 1, 1);
439     HIThemeMenuDrawInfo drawInfo;
440     drawInfo.version =  0;
441     drawInfo.menuType = kThemeMenuTypePopUp;
442     HIThemeDrawMenuBackground(&rect, &drawInfo, context, kHIThemeOrientationInverted);
443
444     NSUInteger pixel[4];
445     [offscreenRep getPixel:pixel atX:0 y:0];
446
447     [offscreenRep release];
448
449     return makeRGB(pixel[0], pixel[1], pixel[2]);
450 }
451
452 void RenderThemeMac::platformColorsDidChange()
453 {
454     m_systemColorCache.clear();
455     RenderTheme::platformColorsDidChange();
456 }
457
458 Color RenderThemeMac::systemColor(CSSValueID cssValueId) const
459 {
460     {
461         auto it = m_systemColorCache.find(cssValueId);
462         if (it != m_systemColorCache.end())
463             return it->value;
464     }
465
466     Color color;
467     switch (cssValueId) {
468     case CSSValueActiveborder:
469         color = convertNSColorToColor([NSColor keyboardFocusIndicatorColor]);
470         break;
471     case CSSValueActivebuttontext:
472 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
473         // There is no corresponding NSColor for this so we use a hard coded value.
474         color = 0xC0FFFFFF;
475 #else
476         color = convertNSColorToColor([NSColor controlTextColor]);
477 #endif
478         break;
479     case CSSValueActivecaption:
480         color = convertNSColorToColor([NSColor windowFrameTextColor]);
481         break;
482     case CSSValueAppworkspace:
483         color = convertNSColorToColor([NSColor headerColor]);
484         break;
485     case CSSValueBackground:
486         // Use theme independent default
487         break;
488     case CSSValueButtonface:
489         // We use this value instead of NSColor's controlColor to avoid website incompatibilities.
490         // We may want to change this to use the NSColor in future.
491         color = 0xFFC0C0C0;
492         break;
493     case CSSValueButtonhighlight:
494         color = convertNSColorToColor([NSColor controlHighlightColor]);
495         break;
496     case CSSValueButtonshadow:
497         color = convertNSColorToColor([NSColor controlShadowColor]);
498         break;
499     case CSSValueButtontext:
500         color = convertNSColorToColor([NSColor controlTextColor]);
501         break;
502     case CSSValueCaptiontext:
503         color = convertNSColorToColor([NSColor textColor]);
504         break;
505     case CSSValueGraytext:
506         color = convertNSColorToColor([NSColor disabledControlTextColor]);
507         break;
508     case CSSValueHighlight:
509         color = convertNSColorToColor([NSColor selectedTextBackgroundColor]);
510         break;
511     case CSSValueHighlighttext:
512         color = convertNSColorToColor([NSColor selectedTextColor]);
513         break;
514     case CSSValueInactiveborder:
515         color = convertNSColorToColor([NSColor controlBackgroundColor]);
516         break;
517     case CSSValueInactivecaption:
518         color = convertNSColorToColor([NSColor controlBackgroundColor]);
519         break;
520     case CSSValueInactivecaptiontext:
521         color = convertNSColorToColor([NSColor textColor]);
522         break;
523     case CSSValueInfobackground:
524         // There is no corresponding NSColor for this so we use a hard coded value.
525         color = 0xFFFBFCC5;
526         break;
527     case CSSValueInfotext:
528         color = convertNSColorToColor([NSColor textColor]);
529         break;
530     case CSSValueMenu:
531         color = menuBackgroundColor();
532         break;
533     case CSSValueMenutext:
534         color = convertNSColorToColor([NSColor selectedMenuItemTextColor]);
535         break;
536     case CSSValueScrollbar:
537         color = convertNSColorToColor([NSColor scrollBarColor]);
538         break;
539     case CSSValueText:
540         color = convertNSColorToColor([NSColor textColor]);
541         break;
542     case CSSValueThreeddarkshadow:
543         color = convertNSColorToColor([NSColor controlDarkShadowColor]);
544         break;
545     case CSSValueThreedshadow:
546         color = convertNSColorToColor([NSColor shadowColor]);
547         break;
548     case CSSValueThreedface:
549         // We use this value instead of NSColor's controlColor to avoid website incompatibilities.
550         // We may want to change this to use the NSColor in future.
551         color = 0xFFC0C0C0;
552         break;
553     case CSSValueThreedhighlight:
554         color = convertNSColorToColor([NSColor highlightColor]);
555         break;
556     case CSSValueThreedlightshadow:
557         color = convertNSColorToColor([NSColor controlLightHighlightColor]);
558         break;
559     case CSSValueWebkitFocusRingColor:
560         color = convertNSColorToColor([NSColor keyboardFocusIndicatorColor]);
561         break;
562     case CSSValueWindow:
563         color = convertNSColorToColor([NSColor windowBackgroundColor]);
564         break;
565     case CSSValueWindowframe:
566         color = convertNSColorToColor([NSColor windowFrameColor]);
567         break;
568     case CSSValueWindowtext:
569         color = convertNSColorToColor([NSColor windowFrameTextColor]);
570         break;
571 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
572     case CSSValueAppleSystemBlue:
573         color = convertNSColorToColor([NSColor systemBlueColor]);
574         break;
575     case CSSValueAppleSystemBrown:
576         color = convertNSColorToColor([NSColor systemBrownColor]);
577         break;
578     case CSSValueAppleSystemGray:
579         color = convertNSColorToColor([NSColor systemGrayColor]);
580         break;
581     case CSSValueAppleSystemGreen:
582         color = convertNSColorToColor([NSColor systemGreenColor]);
583         break;
584     case CSSValueAppleSystemOrange:
585         color = convertNSColorToColor([NSColor systemOrangeColor]);
586         break;
587     case CSSValueAppleSystemPink:
588         color = convertNSColorToColor([NSColor systemPinkColor]);
589         break;
590     case CSSValueAppleSystemPurple:
591         color = convertNSColorToColor([NSColor systemPurpleColor]);
592         break;
593     case CSSValueAppleSystemRed:
594         color = convertNSColorToColor([NSColor systemRedColor]);
595         break;
596     case CSSValueAppleSystemYellow:
597         color = convertNSColorToColor([NSColor systemYellowColor]);
598         break;
599 #endif
600     default:
601         break;
602     }
603
604     if (!color.isValid())
605         color = RenderTheme::systemColor(cssValueId);
606
607     if (color.isValid())
608         m_systemColorCache.set(cssValueId, color.rgb());
609
610     return color;
611 }
612
613 bool RenderThemeMac::usesTestModeFocusRingColor() const
614 {
615     return WebCore::usesTestModeFocusRingColor();
616 }
617
618 bool RenderThemeMac::isControlStyled(const RenderStyle& style, const BorderData& border,
619                                      const FillLayer& background, const Color& backgroundColor) const
620 {
621     if (style.appearance() == TextFieldPart || style.appearance() == TextAreaPart || style.appearance() == ListboxPart)
622         return style.border() != border;
623
624     // FIXME: This is horrible, but there is not much else that can be done.  Menu lists cannot draw properly when
625     // scaled.  They can't really draw properly when transformed either.  We can't detect the transform case at style
626     // adjustment time so that will just have to stay broken.  We can however detect that we're zooming.  If zooming
627     // is in effect we treat it like the control is styled.
628     if (style.appearance() == MenulistPart && style.effectiveZoom() != 1.0f)
629         return true;
630
631     return RenderTheme::isControlStyled(style, border, background, backgroundColor);
632 }
633
634 static FloatRect inflateRect(const FloatRect& rect, const IntSize& size, const int* margins, float zoomLevel)
635 {
636     // Only do the inflation if the available width/height are too small. Otherwise try to
637     // fit the glow/check space into the available box's width/height.
638     int widthDelta = rect.width() - (size.width() + margins[leftMargin] * zoomLevel + margins[rightMargin] * zoomLevel);
639     int heightDelta = rect.height() - (size.height() + margins[topMargin] * zoomLevel + margins[bottomMargin] * zoomLevel);
640     FloatRect result(rect);
641     if (widthDelta < 0) {
642         result.setX(result.x() - margins[leftMargin] * zoomLevel);
643         result.setWidth(result.width() - widthDelta);
644     }
645     if (heightDelta < 0) {
646         result.setY(result.y() - margins[topMargin] * zoomLevel);
647         result.setHeight(result.height() - heightDelta);
648     }
649     return result;
650 }
651
652 void RenderThemeMac::adjustRepaintRect(const RenderObject& renderer, FloatRect& rect)
653 {
654     ControlPart part = renderer.style().appearance();
655
656 #if USE(NEW_THEME)
657     switch (part) {
658         case CheckboxPart:
659         case RadioPart:
660         case PushButtonPart:
661         case SquareButtonPart:
662         case DefaultButtonPart:
663         case ButtonPart:
664         case InnerSpinButtonPart:
665             return RenderTheme::adjustRepaintRect(renderer, rect);
666         default:
667             break;
668     }
669 #endif
670
671     float zoomLevel = renderer.style().effectiveZoom();
672
673     if (part == MenulistPart) {
674         setPopupButtonCellState(renderer, IntSize(rect.size()));
675         IntSize size = popupButtonSizes()[[popupButton() controlSize]];
676         size.setHeight(size.height() * zoomLevel);
677         size.setWidth(rect.width());
678         rect = inflateRect(rect, size, popupButtonMargins(), zoomLevel);
679     }
680 }
681
682 FloatRect RenderThemeMac::convertToPaintingRect(const RenderObject& inputRenderer, const RenderObject& partRenderer, const FloatRect& inputRect, const IntRect& r) const
683 {
684     FloatRect partRect(inputRect);
685
686     // Compute an offset between the part renderer and the input renderer
687     FloatSize offsetFromInputRenderer;
688     const RenderObject* renderer = &partRenderer;
689     while (renderer && renderer != &inputRenderer) {
690         RenderElement* containingRenderer = renderer->container();
691         ASSERT(containingRenderer);
692         offsetFromInputRenderer -= roundedIntSize(renderer->offsetFromContainer(*containingRenderer, LayoutPoint()));
693         renderer = containingRenderer;
694     }
695     // If the input renderer was not a container, something went wrong
696     ASSERT(renderer == &inputRenderer);
697     // Move the rect into partRenderer's coords
698     partRect.move(offsetFromInputRenderer);
699     // Account for the local drawing offset (tx, ty)
700     partRect.move(r.x(), r.y());
701
702     return partRect;
703 }
704
705 void RenderThemeMac::updateCheckedState(NSCell* cell, const RenderObject& o)
706 {
707     bool oldIndeterminate = [cell state] == NSMixedState;
708     bool indeterminate = isIndeterminate(o);
709     bool checked = isChecked(o);
710
711     if (oldIndeterminate != indeterminate) {
712         [cell setState:indeterminate ? NSMixedState : (checked ? NSOnState : NSOffState)];
713         return;
714     }
715
716     bool oldChecked = [cell state] == NSOnState;
717     if (checked != oldChecked)
718         [cell setState:checked ? NSOnState : NSOffState];
719 }
720
721 void RenderThemeMac::updateEnabledState(NSCell* cell, const RenderObject& o)
722 {
723     bool oldEnabled = [cell isEnabled];
724     bool enabled = isEnabled(o);
725     if (enabled != oldEnabled)
726         [cell setEnabled:enabled];
727 }
728
729 void RenderThemeMac::updateFocusedState(NSCell* cell, const RenderObject& o)
730 {
731     bool oldFocused = [cell showsFirstResponder];
732     bool focused = isFocused(o) && o.style().outlineStyleIsAuto();
733     if (focused != oldFocused)
734         [cell setShowsFirstResponder:focused];
735 }
736
737 void RenderThemeMac::updatePressedState(NSCell* cell, const RenderObject& o)
738 {
739     bool oldPressed = [cell isHighlighted];
740     bool pressed = is<Element>(o.node()) && downcast<Element>(*o.node()).active();
741     if (pressed != oldPressed)
742         [cell setHighlighted:pressed];
743 }
744
745 bool RenderThemeMac::controlSupportsTints(const RenderObject& o) const
746 {
747     // An alternate way to implement this would be to get the appropriate cell object
748     // and call the private _needRedrawOnWindowChangedKeyState method. An advantage of
749     // that would be that we would match AppKit behavior more closely, but a disadvantage
750     // would be that we would rely on an AppKit SPI method.
751
752     if (!isEnabled(o))
753         return false;
754
755     // Checkboxes only have tint when checked.
756     if (o.style().appearance() == CheckboxPart)
757         return isChecked(o);
758
759     // For now assume other controls have tint if enabled.
760     return true;
761 }
762
763 NSControlSize RenderThemeMac::controlSizeForFont(RenderStyle& style) const
764 {
765     int fontSize = style.fontSize();
766     if (fontSize >= 16)
767         return NSRegularControlSize;
768     if (fontSize >= 11)
769         return NSSmallControlSize;
770     return NSMiniControlSize;
771 }
772
773 NSControlSize RenderThemeMac::controlSizeForCell(NSCell*, const IntSize* sizes, const IntSize& minSize, float zoomLevel) const
774 {
775     if (minSize.width() >= static_cast<int>(sizes[NSRegularControlSize].width() * zoomLevel)
776         && minSize.height() >= static_cast<int>(sizes[NSRegularControlSize].height() * zoomLevel))
777         return NSRegularControlSize;
778
779     if (minSize.width() >= static_cast<int>(sizes[NSSmallControlSize].width() * zoomLevel)
780         && minSize.height() >= static_cast<int>(sizes[NSSmallControlSize].height() * zoomLevel))
781         return NSSmallControlSize;
782
783     return NSMiniControlSize;
784 }
785
786 void RenderThemeMac::setControlSize(NSCell* cell, const IntSize* sizes, const IntSize& minSize, float zoomLevel)
787 {
788     NSControlSize size = controlSizeForCell(cell, sizes, minSize, zoomLevel);
789     if (size != [cell controlSize]) // Only update if we have to, since AppKit does work even if the size is the same.
790         [cell setControlSize:size];
791 }
792
793 IntSize RenderThemeMac::sizeForFont(RenderStyle& style, const IntSize* sizes) const
794 {
795     if (style.effectiveZoom() != 1.0f) {
796         IntSize result = sizes[controlSizeForFont(style)];
797         return IntSize(result.width() * style.effectiveZoom(), result.height() * style.effectiveZoom());
798     }
799     return sizes[controlSizeForFont(style)];
800 }
801
802 IntSize RenderThemeMac::sizeForSystemFont(RenderStyle& style, const IntSize* sizes) const
803 {
804     if (style.effectiveZoom() != 1.0f) {
805         IntSize result = sizes[controlSizeForSystemFont(style)];
806         return IntSize(result.width() * style.effectiveZoom(), result.height() * style.effectiveZoom());
807     }
808     return sizes[controlSizeForSystemFont(style)];
809 }
810
811 void RenderThemeMac::setSizeFromFont(RenderStyle& style, const IntSize* sizes) const
812 {
813     // FIXME: Check is flawed, since it doesn't take min-width/max-width into account.
814     IntSize size = sizeForFont(style, sizes);
815     if (style.width().isIntrinsicOrAuto() && size.width() > 0)
816         style.setWidth(Length(size.width(), Fixed));
817     if (style.height().isAuto() && size.height() > 0)
818         style.setHeight(Length(size.height(), Fixed));
819 }
820
821 void RenderThemeMac::setFontFromControlSize(StyleResolver&, RenderStyle& style, NSControlSize controlSize) const
822 {
823     FontDescription fontDescription;
824     fontDescription.setIsAbsoluteSize(true);
825
826     NSFont* font = [NSFont systemFontOfSize:[NSFont systemFontSizeForControlSize:controlSize]];
827     fontDescription.setOneFamily([font webCoreFamilyName]);
828     fontDescription.setComputedSize([font pointSize] * style.effectiveZoom());
829     fontDescription.setSpecifiedSize([font pointSize] * style.effectiveZoom());
830
831     // Reset line height
832     style.setLineHeight(RenderStyle::initialLineHeight());
833
834     if (style.setFontDescription(fontDescription))
835         style.fontCascade().update(0);
836 }
837
838 NSControlSize RenderThemeMac::controlSizeForSystemFont(RenderStyle& style) const
839 {
840     int fontSize = style.fontSize();
841     if (fontSize >= [NSFont systemFontSizeForControlSize:NSRegularControlSize])
842         return NSRegularControlSize;
843     if (fontSize >= [NSFont systemFontSizeForControlSize:NSSmallControlSize])
844         return NSSmallControlSize;
845     return NSMiniControlSize;
846 }
847
848 bool RenderThemeMac::paintTextField(const RenderObject& o, const PaintInfo& paintInfo, const FloatRect& r)
849 {
850     LocalCurrentGraphicsContext localContext(paintInfo.context);
851
852     NSTextFieldCell *textField = this->textField();
853
854     GraphicsContextStateSaver stateSaver(*paintInfo.context);
855
856     [textField setEnabled:(isEnabled(o) && !isReadOnlyControl(o))];
857     [textField drawWithFrame:NSRect(r) inView:documentViewFor(o)];
858
859     [textField setControlView:nil];
860
861     return false;
862 }
863
864 void RenderThemeMac::adjustTextFieldStyle(StyleResolver&, RenderStyle&, Element*) const
865 {
866 }
867
868 bool RenderThemeMac::paintTextArea(const RenderObject& o, const PaintInfo& paintInfo, const FloatRect& r)
869 {
870     LocalCurrentGraphicsContext localContext(paintInfo.context);
871     wkDrawBezeledTextArea(r, isEnabled(o) && !isReadOnlyControl(o));
872     return false;
873 }
874
875 void RenderThemeMac::adjustTextAreaStyle(StyleResolver&, RenderStyle&, Element*) const
876 {
877 }
878
879 const int* RenderThemeMac::popupButtonMargins() const
880 {
881     static const int margins[3][4] =
882     {
883         { 0, 3, 1, 3 },
884         { 0, 3, 2, 3 },
885         { 0, 1, 0, 1 }
886     };
887     return margins[[popupButton() controlSize]];
888 }
889
890 const IntSize* RenderThemeMac::popupButtonSizes() const
891 {
892     static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) };
893     return sizes;
894 }
895
896 const int* RenderThemeMac::popupButtonPadding(NSControlSize size) const
897 {
898     static const int padding[3][4] =
899     {
900         { 2, 26, 3, 8 },
901         { 2, 23, 3, 8 },
902         { 2, 22, 3, 10 }
903     };
904     return padding[size];
905 }
906
907 bool RenderThemeMac::paintMenuList(const RenderObject& renderer, const PaintInfo& paintInfo, const FloatRect& rect)
908 {
909     LocalCurrentGraphicsContext localContext(paintInfo.context);
910     setPopupButtonCellState(renderer, IntSize(rect.size()));
911
912     NSPopUpButtonCell* popupButton = this->popupButton();
913
914     float zoomLevel = renderer.style().effectiveZoom();
915     IntSize size = popupButtonSizes()[[popupButton controlSize]];
916     size.setHeight(size.height() * zoomLevel);
917     size.setWidth(rect.width());
918
919     // Now inflate it to account for the shadow.
920     FloatRect inflatedRect = rect;
921     if (rect.width() >= minimumMenuListSize(renderer.style()))
922         inflatedRect = inflateRect(rect, size, popupButtonMargins(), zoomLevel);
923
924     GraphicsContextStateSaver stateSaver(*paintInfo.context);
925
926     // On Leopard, the cell will draw outside of the given rect, so we have to clip to the rect
927     paintInfo.context->clip(inflatedRect);
928
929     if (zoomLevel != 1.0f) {
930         inflatedRect.setWidth(inflatedRect.width() / zoomLevel);
931         inflatedRect.setHeight(inflatedRect.height() / zoomLevel);
932         paintInfo.context->translate(inflatedRect.x(), inflatedRect.y());
933         paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel));
934         paintInfo.context->translate(-inflatedRect.x(), -inflatedRect.y());
935     }
936
937     NSView *view = documentViewFor(renderer);
938     [popupButton drawWithFrame:inflatedRect inView:view];
939     if (isFocused(renderer) && renderer.style().outlineStyleIsAuto()) {
940         if (wkDrawCellFocusRingWithFrameAtTime(popupButton, inflatedRect, view, std::numeric_limits<double>::max()))
941             renderer.document().page()->focusController().setFocusedElementNeedsRepaint();
942     }
943
944     [popupButton setControlView:nil];
945
946     return false;
947 }
948
949 #if ENABLE(METER_ELEMENT)
950
951 IntSize RenderThemeMac::meterSizeForBounds(const RenderMeter& renderMeter, const IntRect& bounds) const
952 {
953     if (NoControlPart == renderMeter.style().appearance())
954         return bounds.size();
955
956     NSLevelIndicatorCell* cell = levelIndicatorFor(renderMeter);
957     // Makes enough room for cell's intrinsic size.
958     NSSize cellSize = [cell cellSizeForBounds:IntRect(IntPoint(), bounds.size())];
959     return IntSize(bounds.width() < cellSize.width ? cellSize.width : bounds.width(),
960                    bounds.height() < cellSize.height ? cellSize.height : bounds.height());
961 }
962
963 bool RenderThemeMac::paintMeter(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
964 {
965     if (!is<RenderMeter>(renderObject))
966         return true;
967
968     LocalCurrentGraphicsContext localContext(paintInfo.context);
969
970     NSLevelIndicatorCell* cell = levelIndicatorFor(downcast<RenderMeter>(renderObject));
971     GraphicsContextStateSaver stateSaver(*paintInfo.context);
972
973     [cell drawWithFrame:rect inView:documentViewFor(renderObject)];
974     [cell setControlView:nil];
975     return false;
976 }
977
978 bool RenderThemeMac::supportsMeter(ControlPart part) const
979 {
980     switch (part) {
981     case RelevancyLevelIndicatorPart:
982     case DiscreteCapacityLevelIndicatorPart:
983     case RatingLevelIndicatorPart:
984     case MeterPart:
985     case ContinuousCapacityLevelIndicatorPart:
986         return true;
987     default:
988         return false;
989     }
990 }
991
992 NSLevelIndicatorStyle RenderThemeMac::levelIndicatorStyleFor(ControlPart part) const
993 {
994     switch (part) {
995     case RelevancyLevelIndicatorPart:
996         return NSRelevancyLevelIndicatorStyle;
997     case DiscreteCapacityLevelIndicatorPart:
998         return NSDiscreteCapacityLevelIndicatorStyle;
999     case RatingLevelIndicatorPart:
1000         return NSRatingLevelIndicatorStyle;
1001     case MeterPart:
1002     case ContinuousCapacityLevelIndicatorPart:
1003     default:
1004         return NSContinuousCapacityLevelIndicatorStyle;
1005     }
1006
1007 }
1008
1009 NSLevelIndicatorCell* RenderThemeMac::levelIndicatorFor(const RenderMeter& renderMeter) const
1010 {
1011     const RenderStyle& style = renderMeter.style();
1012     ASSERT(style.appearance() != NoControlPart);
1013
1014     if (!m_levelIndicator)
1015         m_levelIndicator = adoptNS([[NSLevelIndicatorCell alloc] initWithLevelIndicatorStyle:NSContinuousCapacityLevelIndicatorStyle]);
1016     NSLevelIndicatorCell* cell = m_levelIndicator.get();
1017
1018     HTMLMeterElement* element = renderMeter.meterElement();
1019     double value = element->value();
1020
1021     // Because NSLevelIndicatorCell does not support optimum-in-the-middle type coloring,
1022     // we explicitly control the color instead giving low and high value to NSLevelIndicatorCell as is.
1023     switch (element->gaugeRegion()) {
1024     case HTMLMeterElement::GaugeRegionOptimum:
1025         // Make meter the green
1026         [cell setWarningValue:value + 1];
1027         [cell setCriticalValue:value + 2];
1028         break;
1029     case HTMLMeterElement::GaugeRegionSuboptimal:
1030         // Make the meter yellow
1031         [cell setWarningValue:value - 1];
1032         [cell setCriticalValue:value + 1];
1033         break;
1034     case HTMLMeterElement::GaugeRegionEvenLessGood:
1035         // Make the meter red
1036         [cell setWarningValue:value - 2];
1037         [cell setCriticalValue:value - 1];
1038         break;
1039     }
1040
1041     [cell setLevelIndicatorStyle:levelIndicatorStyleFor(style.appearance())];
1042     [cell setBaseWritingDirection:style.isLeftToRightDirection() ? NSWritingDirectionLeftToRight : NSWritingDirectionRightToLeft];
1043     [cell setMinValue:element->min()];
1044     [cell setMaxValue:element->max()];
1045     RetainPtr<NSNumber> valueObject = [NSNumber numberWithDouble:value];
1046     [cell setObjectValue:valueObject.get()];
1047
1048     return cell;
1049 }
1050
1051 #endif
1052
1053 const IntSize* RenderThemeMac::progressBarSizes() const
1054 {
1055     static const IntSize sizes[3] = { IntSize(0, 20), IntSize(0, 12), IntSize(0, 12) };
1056     return sizes;
1057 }
1058
1059 const int* RenderThemeMac::progressBarMargins(NSControlSize controlSize) const
1060 {
1061     static const int margins[3][4] =
1062     {
1063         { 0, 0, 1, 0 },
1064         { 0, 0, 1, 0 },
1065         { 0, 0, 1, 0 },
1066     };
1067     return margins[controlSize];
1068 }
1069
1070 IntRect RenderThemeMac::progressBarRectForBounds(const RenderObject& renderObject, const IntRect& bounds) const
1071 {
1072     // Workaround until <rdar://problem/15855086> is fixed.
1073     int maxDimension = static_cast<int>(std::numeric_limits<ushort>::max());
1074     IntRect progressBarBounds(bounds.x(), bounds.y(), std::min(bounds.width(), maxDimension), std::min(bounds.height(), maxDimension));
1075     if (NoControlPart == renderObject.style().appearance())
1076         return progressBarBounds;
1077
1078     float zoomLevel = renderObject.style().effectiveZoom();
1079     NSControlSize controlSize = controlSizeForFont(renderObject.style());
1080     IntSize size = progressBarSizes()[controlSize];
1081     size.setHeight(size.height() * zoomLevel);
1082     size.setWidth(progressBarBounds.width());
1083
1084     // Now inflate it to account for the shadow.
1085     IntRect inflatedRect = progressBarBounds;
1086     if (progressBarBounds.height() <= minimumProgressBarHeight(renderObject.style()))
1087         inflatedRect = IntRect(inflateRect(inflatedRect, size, progressBarMargins(controlSize), zoomLevel));
1088
1089     return inflatedRect;
1090 }
1091
1092 int RenderThemeMac::minimumProgressBarHeight(RenderStyle& style) const
1093 {
1094     return sizeForSystemFont(style, progressBarSizes()).height();
1095 }
1096
1097 double RenderThemeMac::animationRepeatIntervalForProgressBar(RenderProgress&) const
1098 {
1099     return progressAnimationFrameRate;
1100 }
1101
1102 double RenderThemeMac::animationDurationForProgressBar(RenderProgress&) const
1103 {
1104     return progressAnimationNumFrames * progressAnimationFrameRate;
1105 }
1106
1107 void RenderThemeMac::adjustProgressBarStyle(StyleResolver&, RenderStyle&, Element*) const
1108 {
1109 }
1110
1111 bool RenderThemeMac::paintProgressBar(const RenderObject& renderObject, const PaintInfo& paintInfo, const IntRect& rect)
1112 {
1113     if (!is<RenderProgress>(renderObject))
1114         return true;
1115
1116     IntRect inflatedRect = progressBarRectForBounds(renderObject, rect);
1117     NSControlSize controlSize = controlSizeForFont(renderObject.style());
1118
1119     const auto& renderProgress = downcast<RenderProgress>(renderObject);
1120     HIThemeTrackDrawInfo trackInfo;
1121     trackInfo.version = 0;
1122     if (controlSize == NSRegularControlSize)
1123         trackInfo.kind = renderProgress.position() < 0 ? kThemeLargeIndeterminateBar : kThemeLargeProgressBar;
1124     else
1125         trackInfo.kind = renderProgress.position() < 0 ? kThemeMediumIndeterminateBar : kThemeMediumProgressBar;
1126
1127     float deviceScaleFactor = renderObject.document().deviceScaleFactor();
1128     trackInfo.bounds = IntRect(IntPoint(), inflatedRect.size());
1129     trackInfo.min = 0;
1130     trackInfo.max = std::numeric_limits<SInt32>::max();
1131     trackInfo.value = lround(renderProgress.position() * nextafter(trackInfo.max, 0));
1132     trackInfo.trackInfo.progress.phase = lround(renderProgress.animationProgress() * nextafter(progressAnimationNumFrames, 0) * deviceScaleFactor);
1133     trackInfo.attributes = kThemeTrackHorizontal;
1134     trackInfo.enableState = isActive(renderObject) ? kThemeTrackActive : kThemeTrackInactive;
1135     trackInfo.reserved = 0;
1136     trackInfo.filler1 = 0;
1137
1138     std::unique_ptr<ImageBuffer> imageBuffer = ImageBuffer::createCompatibleBuffer(inflatedRect.size(), deviceScaleFactor, ColorSpaceDeviceRGB, paintInfo.context, true);
1139     if (!imageBuffer)
1140         return true;
1141
1142     ContextContainer cgContextContainer(imageBuffer->context());
1143     CGContextRef cgContext = cgContextContainer.context();
1144     HIThemeDrawTrack(&trackInfo, 0, cgContext, kHIThemeOrientationNormal);
1145
1146     GraphicsContextStateSaver stateSaver(*paintInfo.context);
1147
1148     if (!renderProgress.style().isLeftToRightDirection()) {
1149         paintInfo.context->translate(2 * inflatedRect.x() + inflatedRect.width(), 0);
1150         paintInfo.context->scale(FloatSize(-1, 1));
1151     }
1152
1153     paintInfo.context->drawImageBuffer(imageBuffer.get(), ColorSpaceDeviceRGB, inflatedRect.location());
1154     return false;
1155 }
1156
1157 const float baseFontSize = 11.0f;
1158 const float baseArrowHeight = 4.0f;
1159 const float baseArrowWidth = 5.0f;
1160 const float baseSpaceBetweenArrows = 2.0f;
1161 const int arrowPaddingLeft = 6;
1162 const int arrowPaddingRight = 6;
1163 const int paddingBeforeSeparator = 4;
1164 const int baseBorderRadius = 5;
1165 const int styledPopupPaddingLeft = 8;
1166 const int styledPopupPaddingTop = 1;
1167 const int styledPopupPaddingBottom = 2;
1168
1169 static void TopGradientInterpolate(void*, const CGFloat* inData, CGFloat* outData)
1170 {
1171     static float dark[4] = { 1.0f, 1.0f, 1.0f, 0.4f };
1172     static float light[4] = { 1.0f, 1.0f, 1.0f, 0.15f };
1173     float a = inData[0];
1174     int i = 0;
1175     for (i = 0; i < 4; i++)
1176         outData[i] = (1.0f - a) * dark[i] + a * light[i];
1177 }
1178
1179 static void BottomGradientInterpolate(void*, const CGFloat* inData, CGFloat* outData)
1180 {
1181     static float dark[4] = { 1.0f, 1.0f, 1.0f, 0.0f };
1182     static float light[4] = { 1.0f, 1.0f, 1.0f, 0.3f };
1183     float a = inData[0];
1184     int i = 0;
1185     for (i = 0; i < 4; i++)
1186         outData[i] = (1.0f - a) * dark[i] + a * light[i];
1187 }
1188
1189 static void MainGradientInterpolate(void*, const CGFloat* inData, CGFloat* outData)
1190 {
1191     static float dark[4] = { 0.0f, 0.0f, 0.0f, 0.15f };
1192     static float light[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
1193     float a = inData[0];
1194     int i = 0;
1195     for (i = 0; i < 4; i++)
1196         outData[i] = (1.0f - a) * dark[i] + a * light[i];
1197 }
1198
1199 static void TrackGradientInterpolate(void*, const CGFloat* inData, CGFloat* outData)
1200 {
1201     static float dark[4] = { 0.0f, 0.0f, 0.0f, 0.678f };
1202     static float light[4] = { 0.0f, 0.0f, 0.0f, 0.13f };
1203     float a = inData[0];
1204     int i = 0;
1205     for (i = 0; i < 4; i++)
1206         outData[i] = (1.0f - a) * dark[i] + a * light[i];
1207 }
1208
1209 void RenderThemeMac::paintMenuListButtonGradients(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1210 {
1211     if (r.isEmpty())
1212         return;
1213
1214     ContextContainer cgContextContainer(paintInfo.context);
1215     CGContextRef context = cgContextContainer.context();
1216
1217     GraphicsContextStateSaver stateSaver(*paintInfo.context);
1218
1219     FloatRoundedRect border = FloatRoundedRect(o.style().getRoundedBorderFor(r));
1220     int radius = border.radii().topLeft().width();
1221
1222     CGColorSpaceRef cspace = deviceRGBColorSpaceRef();
1223
1224     FloatRect topGradient(r.x(), r.y(), r.width(), r.height() / 2.0f);
1225     struct CGFunctionCallbacks topCallbacks = { 0, TopGradientInterpolate, NULL };
1226     RetainPtr<CGFunctionRef> topFunction = adoptCF(CGFunctionCreate(NULL, 1, NULL, 4, NULL, &topCallbacks));
1227     RetainPtr<CGShadingRef> topShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(topGradient.x(), topGradient.y()), CGPointMake(topGradient.x(), topGradient.maxY()), topFunction.get(), false, false));
1228
1229     FloatRect bottomGradient(r.x() + radius, r.y() + r.height() / 2.0f, r.width() - 2.0f * radius, r.height() / 2.0f);
1230     struct CGFunctionCallbacks bottomCallbacks = { 0, BottomGradientInterpolate, NULL };
1231     RetainPtr<CGFunctionRef> bottomFunction = adoptCF(CGFunctionCreate(NULL, 1, NULL, 4, NULL, &bottomCallbacks));
1232     RetainPtr<CGShadingRef> bottomShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(bottomGradient.x(),  bottomGradient.y()), CGPointMake(bottomGradient.x(), bottomGradient.maxY()), bottomFunction.get(), false, false));
1233
1234     struct CGFunctionCallbacks mainCallbacks = { 0, MainGradientInterpolate, NULL };
1235     RetainPtr<CGFunctionRef> mainFunction = adoptCF(CGFunctionCreate(NULL, 1, NULL, 4, NULL, &mainCallbacks));
1236     RetainPtr<CGShadingRef> mainShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(r.x(),  r.y()), CGPointMake(r.x(), r.maxY()), mainFunction.get(), false, false));
1237
1238     RetainPtr<CGShadingRef> leftShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(r.x(),  r.y()), CGPointMake(r.x() + radius, r.y()), mainFunction.get(), false, false));
1239
1240     RetainPtr<CGShadingRef> rightShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(r.maxX(),  r.y()), CGPointMake(r.maxX() - radius, r.y()), mainFunction.get(), false, false));
1241
1242     {
1243         GraphicsContextStateSaver stateSaver(*paintInfo.context);
1244         CGContextClipToRect(context, r);
1245         paintInfo.context->clipRoundedRect(border);
1246         context = cgContextContainer.context();
1247         CGContextDrawShading(context, mainShading.get());
1248     }
1249
1250     {
1251         GraphicsContextStateSaver stateSaver(*paintInfo.context);
1252         CGContextClipToRect(context, topGradient);
1253         paintInfo.context->clipRoundedRect(FloatRoundedRect(enclosingIntRect(topGradient), border.radii().topLeft(), border.radii().topRight(), IntSize(), IntSize()));
1254         context = cgContextContainer.context();
1255         CGContextDrawShading(context, topShading.get());
1256     }
1257
1258     if (!bottomGradient.isEmpty()) {
1259         GraphicsContextStateSaver stateSaver(*paintInfo.context);
1260         CGContextClipToRect(context, bottomGradient);
1261         paintInfo.context->clipRoundedRect(FloatRoundedRect(enclosingIntRect(bottomGradient), IntSize(), IntSize(), border.radii().bottomLeft(), border.radii().bottomRight()));
1262         context = cgContextContainer.context();
1263         CGContextDrawShading(context, bottomShading.get());
1264     }
1265
1266     {
1267         GraphicsContextStateSaver stateSaver(*paintInfo.context);
1268         CGContextClipToRect(context, r);
1269         paintInfo.context->clipRoundedRect(border);
1270         context = cgContextContainer.context();
1271         CGContextDrawShading(context, leftShading.get());
1272         CGContextDrawShading(context, rightShading.get());
1273     }
1274 }
1275
1276 bool RenderThemeMac::paintMenuListButtonDecorations(const RenderObject& renderer, const PaintInfo& paintInfo, const FloatRect& rect)
1277 {
1278     IntRect bounds = IntRect(rect.x() + renderer.style().borderLeftWidth(),
1279         rect.y() + renderer.style().borderTopWidth(),
1280         rect.width() - renderer.style().borderLeftWidth() - renderer.style().borderRightWidth(),
1281         rect.height() - renderer.style().borderTopWidth() - renderer.style().borderBottomWidth());
1282     // Draw the gradients to give the styled popup menu a button appearance
1283     paintMenuListButtonGradients(renderer, paintInfo, bounds);
1284
1285     // 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
1286     float fontScale = std::min(renderer.style().fontSize() / baseFontSize, bounds.height() / (baseArrowHeight * 2 + baseSpaceBetweenArrows));
1287     float centerY = bounds.y() + bounds.height() / 2.0f;
1288     float arrowHeight = baseArrowHeight * fontScale;
1289     float arrowWidth = baseArrowWidth * fontScale;
1290     float leftEdge = bounds.maxX() - arrowPaddingRight * renderer.style().effectiveZoom() - arrowWidth;
1291     float spaceBetweenArrows = baseSpaceBetweenArrows * fontScale;
1292
1293     if (bounds.width() < arrowWidth + arrowPaddingLeft * renderer.style().effectiveZoom())
1294         return false;
1295
1296     GraphicsContextStateSaver stateSaver(*paintInfo.context);
1297
1298     paintInfo.context->setFillColor(renderer.style().visitedDependentColor(CSSPropertyColor), renderer.style().colorSpace());
1299     paintInfo.context->setStrokeStyle(NoStroke);
1300
1301     FloatPoint arrow1[3];
1302     arrow1[0] = FloatPoint(leftEdge, centerY - spaceBetweenArrows / 2.0f);
1303     arrow1[1] = FloatPoint(leftEdge + arrowWidth, centerY - spaceBetweenArrows / 2.0f);
1304     arrow1[2] = FloatPoint(leftEdge + arrowWidth / 2.0f, centerY - spaceBetweenArrows / 2.0f - arrowHeight);
1305
1306     // Draw the top arrow
1307     paintInfo.context->drawConvexPolygon(3, arrow1, true);
1308
1309     FloatPoint arrow2[3];
1310     arrow2[0] = FloatPoint(leftEdge, centerY + spaceBetweenArrows / 2.0f);
1311     arrow2[1] = FloatPoint(leftEdge + arrowWidth, centerY + spaceBetweenArrows / 2.0f);
1312     arrow2[2] = FloatPoint(leftEdge + arrowWidth / 2.0f, centerY + spaceBetweenArrows / 2.0f + arrowHeight);
1313
1314     // Draw the bottom arrow
1315     paintInfo.context->drawConvexPolygon(3, arrow2, true);
1316
1317     Color leftSeparatorColor(0, 0, 0, 40);
1318     Color rightSeparatorColor(255, 255, 255, 40);
1319
1320     // FIXME: Should the separator thickness and space be scaled up by fontScale?
1321     int separatorSpace = 2; // Deliberately ignores zoom since it looks nicer if it stays thin.
1322     int leftEdgeOfSeparator = static_cast<int>(leftEdge - arrowPaddingLeft * renderer.style().effectiveZoom()); // FIXME: Round?
1323
1324     // Draw the separator to the left of the arrows
1325     paintInfo.context->setStrokeThickness(1); // Deliberately ignores zoom since it looks nicer if it stays thin.
1326     paintInfo.context->setStrokeStyle(SolidStroke);
1327     paintInfo.context->setStrokeColor(leftSeparatorColor, ColorSpaceDeviceRGB);
1328     paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator, bounds.y()),
1329                                 IntPoint(leftEdgeOfSeparator, bounds.maxY()));
1330
1331     paintInfo.context->setStrokeColor(rightSeparatorColor, ColorSpaceDeviceRGB);
1332     paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.y()),
1333                                 IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.maxY()));
1334     return false;
1335 }
1336
1337 static const IntSize* menuListButtonSizes()
1338 {
1339     static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) };
1340     return sizes;
1341 }
1342
1343 void RenderThemeMac::adjustMenuListStyle(StyleResolver& styleResolver, RenderStyle& style, Element* e) const
1344 {
1345     NSControlSize controlSize = controlSizeForFont(style);
1346
1347     style.resetBorder();
1348     style.resetPadding();
1349
1350     // Height is locked to auto.
1351     style.setHeight(Length(Auto));
1352
1353     // White-space is locked to pre
1354     style.setWhiteSpace(PRE);
1355
1356     // Set the foreground color to black or gray when we have the aqua look.
1357     // Cast to RGB32 is to work around a compiler bug.
1358     style.setColor(e && !e->isDisabledFormControl() ? static_cast<RGBA32>(Color::black) : Color::darkGray);
1359
1360     // Set the button's vertical size.
1361     setSizeFromFont(style, menuListButtonSizes());
1362
1363     // 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
1364     // a reasonable control size, but once that control size is determined, we throw that font away and use the appropriate
1365     // system font for the control size instead.
1366     setFontFromControlSize(styleResolver, style, controlSize);
1367
1368     style.setBoxShadow(nullptr);
1369 }
1370
1371 int RenderThemeMac::popupInternalPaddingLeft(RenderStyle& style) const
1372 {
1373     if (style.appearance() == MenulistPart)
1374         return popupButtonPadding(controlSizeForFont(style))[leftPadding] * style.effectiveZoom();
1375     if (style.appearance() == MenulistButtonPart)
1376         return styledPopupPaddingLeft * style.effectiveZoom();
1377     return 0;
1378 }
1379
1380 int RenderThemeMac::popupInternalPaddingRight(RenderStyle& style) const
1381 {
1382     if (style.appearance() == MenulistPart)
1383         return popupButtonPadding(controlSizeForFont(style))[rightPadding] * style.effectiveZoom();
1384     if (style.appearance() == MenulistButtonPart) {
1385         float fontScale = style.fontSize() / baseFontSize;
1386         float arrowWidth = baseArrowWidth * fontScale;
1387         return static_cast<int>(ceilf(arrowWidth + (arrowPaddingLeft + arrowPaddingRight + paddingBeforeSeparator) * style.effectiveZoom()));
1388     }
1389     return 0;
1390 }
1391
1392 int RenderThemeMac::popupInternalPaddingTop(RenderStyle& style) const
1393 {
1394     if (style.appearance() == MenulistPart)
1395         return popupButtonPadding(controlSizeForFont(style))[topPadding] * style.effectiveZoom();
1396     if (style.appearance() == MenulistButtonPart)
1397         return styledPopupPaddingTop * style.effectiveZoom();
1398     return 0;
1399 }
1400
1401 int RenderThemeMac::popupInternalPaddingBottom(RenderStyle& style) const
1402 {
1403     if (style.appearance() == MenulistPart)
1404         return popupButtonPadding(controlSizeForFont(style))[bottomPadding] * style.effectiveZoom();
1405     if (style.appearance() == MenulistButtonPart)
1406         return styledPopupPaddingBottom * style.effectiveZoom();
1407     return 0;
1408 }
1409
1410 PopupMenuStyle::PopupMenuSize RenderThemeMac::popupMenuSize(const RenderStyle& style, IntRect& rect) const
1411 {
1412     NSPopUpButtonCell* popupButton = this->popupButton();
1413     NSControlSize size = controlSizeForCell(popupButton, popupButtonSizes(), rect.size(), style.effectiveZoom());
1414     switch (size) {
1415     case NSRegularControlSize:
1416         return PopupMenuStyle::PopupMenuSizeNormal;
1417     case NSSmallControlSize:
1418         return PopupMenuStyle::PopupMenuSizeSmall;
1419     case NSMiniControlSize:
1420         return PopupMenuStyle::PopupMenuSizeMini;
1421     default:
1422         return PopupMenuStyle::PopupMenuSizeNormal;
1423     }
1424 }
1425
1426 void RenderThemeMac::adjustMenuListButtonStyle(StyleResolver&, RenderStyle& style, Element*) const
1427 {
1428     float fontScale = style.fontSize() / baseFontSize;
1429
1430     style.resetPadding();
1431     style.setBorderRadius(IntSize(int(baseBorderRadius + fontScale - 1), int(baseBorderRadius + fontScale - 1))); // FIXME: Round up?
1432
1433     const int minHeight = 15;
1434     style.setMinHeight(Length(minHeight, Fixed));
1435
1436     style.setLineHeight(RenderStyle::initialLineHeight());
1437 }
1438
1439 void RenderThemeMac::setPopupButtonCellState(const RenderObject& o, const IntSize& buttonSize)
1440 {
1441     NSPopUpButtonCell* popupButton = this->popupButton();
1442
1443     // Set the control size based off the rectangle we're painting into.
1444     setControlSize(popupButton, popupButtonSizes(), buttonSize, o.style().effectiveZoom());
1445
1446     // Update the various states we respond to.
1447     updateCheckedState(popupButton, o);
1448     updateEnabledState(popupButton, o);
1449     updatePressedState(popupButton, o);
1450 }
1451
1452 const IntSize* RenderThemeMac::menuListSizes() const
1453 {
1454     static const IntSize sizes[3] = { IntSize(9, 0), IntSize(5, 0), IntSize(0, 0) };
1455     return sizes;
1456 }
1457
1458 int RenderThemeMac::minimumMenuListSize(RenderStyle& style) const
1459 {
1460     return sizeForSystemFont(style, menuListSizes()).width();
1461 }
1462
1463 const int trackWidth = 5;
1464 const int trackRadius = 2;
1465
1466 void RenderThemeMac::adjustSliderTrackStyle(StyleResolver&, RenderStyle& style, Element*) const
1467 {
1468     style.setBoxShadow(nullptr);
1469 }
1470
1471 bool RenderThemeMac::paintSliderTrack(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1472 {
1473     IntRect bounds = r;
1474     float zoomLevel = o.style().effectiveZoom();
1475     float zoomedTrackWidth = trackWidth * zoomLevel;
1476
1477     if (o.style().appearance() ==  SliderHorizontalPart || o.style().appearance() ==  MediaSliderPart) {
1478         bounds.setHeight(zoomedTrackWidth);
1479         bounds.setY(r.y() + r.height() / 2 - zoomedTrackWidth / 2);
1480     } else if (o.style().appearance() == SliderVerticalPart) {
1481         bounds.setWidth(zoomedTrackWidth);
1482         bounds.setX(r.x() + r.width() / 2 - zoomedTrackWidth / 2);
1483     }
1484
1485     LocalCurrentGraphicsContext localContext(paintInfo.context);
1486     CGContextRef context = localContext.cgContext();
1487     CGColorSpaceRef cspace = deviceRGBColorSpaceRef();
1488
1489 #if ENABLE(DATALIST_ELEMENT)
1490     paintSliderTicks(o, paintInfo, r);
1491 #endif
1492
1493     GraphicsContextStateSaver stateSaver(*paintInfo.context);
1494     CGContextClipToRect(context, bounds);
1495
1496     struct CGFunctionCallbacks mainCallbacks = { 0, TrackGradientInterpolate, NULL };
1497     RetainPtr<CGFunctionRef> mainFunction = adoptCF(CGFunctionCreate(NULL, 1, NULL, 4, NULL, &mainCallbacks));
1498     RetainPtr<CGShadingRef> mainShading;
1499     if (o.style().appearance() == SliderVerticalPart)
1500         mainShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(bounds.x(),  bounds.maxY()), CGPointMake(bounds.maxX(), bounds.maxY()), mainFunction.get(), false, false));
1501     else
1502         mainShading = adoptCF(CGShadingCreateAxial(cspace, CGPointMake(bounds.x(),  bounds.y()), CGPointMake(bounds.x(), bounds.maxY()), mainFunction.get(), false, false));
1503
1504     IntSize radius(trackRadius, trackRadius);
1505     paintInfo.context->clipRoundedRect(FloatRoundedRect(bounds, radius, radius, radius, radius));
1506     context = localContext.cgContext();
1507     CGContextDrawShading(context, mainShading.get());
1508
1509     return false;
1510 }
1511
1512 void RenderThemeMac::adjustSliderThumbStyle(StyleResolver& styleResolver, RenderStyle& style, Element* element) const
1513 {
1514     RenderTheme::adjustSliderThumbStyle(styleResolver, style, element);
1515     style.setBoxShadow(nullptr);
1516 }
1517
1518 const float verticalSliderHeightPadding = 0.1f;
1519
1520 bool RenderThemeMac::paintSliderThumb(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1521 {
1522     NSSliderCell* sliderThumbCell = o.style().appearance() == SliderThumbVerticalPart
1523         ? sliderThumbVertical()
1524         : sliderThumbHorizontal();
1525
1526     LocalCurrentGraphicsContext localContext(paintInfo.context);
1527
1528     // Update the various states we respond to.
1529     updateEnabledState(sliderThumbCell, o);
1530         Element* focusDelegate = is<Element>(o.node()) ? downcast<Element>(*o.node()).focusDelegate() : nullptr;
1531     if (focusDelegate)
1532         updateFocusedState(sliderThumbCell, *focusDelegate->renderer());
1533
1534     // Update the pressed state using the NSCell tracking methods, since that's how NSSliderCell keeps track of it.
1535     bool oldPressed;
1536     if (o.style().appearance() == SliderThumbVerticalPart)
1537         oldPressed = m_isSliderThumbVerticalPressed;
1538     else
1539         oldPressed = m_isSliderThumbHorizontalPressed;
1540
1541     bool pressed = isPressed(o);
1542
1543     if (o.style().appearance() == SliderThumbVerticalPart)
1544         m_isSliderThumbVerticalPressed = pressed;
1545     else
1546         m_isSliderThumbHorizontalPressed = pressed;
1547
1548     if (pressed != oldPressed) {
1549         if (pressed)
1550             [sliderThumbCell startTrackingAt:NSPoint() inView:nil];
1551         else
1552             [sliderThumbCell stopTracking:NSPoint() at:NSPoint() inView:nil mouseIsUp:YES];
1553     }
1554
1555     FloatRect bounds = r;
1556     // Make the height of the vertical slider slightly larger so NSSliderCell will draw a vertical slider.
1557     if (o.style().appearance() == SliderThumbVerticalPart)
1558         bounds.setHeight(bounds.height() + verticalSliderHeightPadding * o.style().effectiveZoom());
1559
1560     GraphicsContextStateSaver stateSaver(*paintInfo.context);
1561     float zoomLevel = o.style().effectiveZoom();
1562
1563     FloatRect unzoomedRect = bounds;
1564     if (zoomLevel != 1.0f) {
1565         unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel);
1566         unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel);
1567         paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y());
1568         paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel));
1569         paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y());
1570     }
1571
1572     [sliderThumbCell drawInteriorWithFrame:unzoomedRect inView:documentViewFor(o)];
1573     [sliderThumbCell setControlView:nil];
1574
1575     return false;
1576 }
1577
1578 bool RenderThemeMac::paintSearchField(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1579 {
1580     LocalCurrentGraphicsContext localContext(paintInfo.context);
1581     NSSearchFieldCell* search = this->search();
1582
1583     setSearchCellState(o, r);
1584
1585     GraphicsContextStateSaver stateSaver(*paintInfo.context);
1586
1587     float zoomLevel = o.style().effectiveZoom();
1588
1589     IntRect unzoomedRect = r;
1590
1591     if (zoomLevel != 1.0f) {
1592         unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel);
1593         unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel);
1594         paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y());
1595         paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel));
1596         paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y());
1597     }
1598
1599     // Set the search button to nil before drawing.  Then reset it so we can draw it later.
1600     [search setSearchButtonCell:nil];
1601
1602     NSView *documentView = documentViewFor(o);
1603     [search drawWithFrame:NSRect(unzoomedRect) inView:documentView];
1604
1605     [search setControlView:nil];
1606     [search resetSearchButtonCell];
1607
1608     if (isFocused(o) && o.style().outlineStyleIsAuto()) {
1609         if (wkDrawCellFocusRingWithFrameAtTime(search, NSRect(unzoomedRect), documentView, std::numeric_limits<double>::max()))
1610             o.document().page()->focusController().setFocusedElementNeedsRepaint();
1611     }
1612     
1613     return false;
1614 }
1615
1616 void RenderThemeMac::setSearchCellState(const RenderObject& o, const IntRect&)
1617 {
1618     NSSearchFieldCell* search = this->search();
1619
1620     [search setControlSize:controlSizeForFont(o.style())];
1621
1622     // Update the various states we respond to.
1623     updateEnabledState(search, o);
1624     updateFocusedState(search, o);
1625 }
1626
1627 const IntSize* RenderThemeMac::searchFieldSizes() const
1628 {
1629     static const IntSize sizes[3] = { IntSize(0, 22), IntSize(0, 19), IntSize(0, 17) };
1630     return sizes;
1631 }
1632
1633 void RenderThemeMac::setSearchFieldSize(RenderStyle& style) const
1634 {
1635     // If the width and height are both specified, then we have nothing to do.
1636     if (!style.width().isIntrinsicOrAuto() && !style.height().isAuto())
1637         return;
1638
1639     // Use the font size to determine the intrinsic width of the control.
1640     setSizeFromFont(style, searchFieldSizes());
1641 }
1642
1643 void RenderThemeMac::adjustSearchFieldStyle(StyleResolver& styleResolver, RenderStyle& style, Element*) const
1644 {
1645     // Override border.
1646     style.resetBorder();
1647     const short borderWidth = 2 * style.effectiveZoom();
1648     style.setBorderLeftWidth(borderWidth);
1649     style.setBorderLeftStyle(INSET);
1650     style.setBorderRightWidth(borderWidth);
1651     style.setBorderRightStyle(INSET);
1652     style.setBorderBottomWidth(borderWidth);
1653     style.setBorderBottomStyle(INSET);
1654     style.setBorderTopWidth(borderWidth);
1655     style.setBorderTopStyle(INSET);
1656
1657     // Override height.
1658     style.setHeight(Length(Auto));
1659     setSearchFieldSize(style);
1660
1661     // Override padding size to match AppKit text positioning.
1662     const int padding = 1 * style.effectiveZoom();
1663     style.setPaddingLeft(Length(padding, Fixed));
1664     style.setPaddingRight(Length(padding, Fixed));
1665     style.setPaddingTop(Length(padding, Fixed));
1666     style.setPaddingBottom(Length(padding, Fixed));
1667
1668     NSControlSize controlSize = controlSizeForFont(style);
1669     setFontFromControlSize(styleResolver, style, controlSize);
1670
1671     style.setBoxShadow(nullptr);
1672 }
1673
1674 bool RenderThemeMac::paintSearchFieldCancelButton(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1675 {
1676     Element* input = o.node()->shadowHost();
1677     if (!input)
1678         input = downcast<Element>(o.node());
1679
1680     if (!input->renderer()->isBox())
1681         return false;
1682
1683     LocalCurrentGraphicsContext localContext(paintInfo.context);
1684     setSearchCellState(*input->renderer(), r);
1685
1686     NSSearchFieldCell* search = this->search();
1687
1688     if (!input->isDisabledFormControl() && (is<HTMLTextFormControlElement>(*input) && !downcast<HTMLTextFormControlElement>(*input).isReadOnly()))
1689         updatePressedState([search cancelButtonCell], o);
1690     else if ([[search cancelButtonCell] isHighlighted])
1691         [[search cancelButtonCell] setHighlighted:NO];
1692
1693     GraphicsContextStateSaver stateSaver(*paintInfo.context);
1694
1695     float zoomLevel = o.style().effectiveZoom();
1696
1697     FloatRect localBounds = [search cancelButtonRectForBounds:NSRect(input->renderBox()->pixelSnappedBorderBoxRect())];
1698     localBounds = convertToPaintingRect(*input->renderer(), o, localBounds, r);
1699
1700     FloatRect unzoomedRect(localBounds);
1701     if (zoomLevel != 1.0f) {
1702         unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel);
1703         unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel);
1704         paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y());
1705         paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel));
1706         paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y());
1707     }
1708
1709     [[search cancelButtonCell] drawWithFrame:unzoomedRect inView:documentViewFor(o)];
1710     [[search cancelButtonCell] setControlView:nil];
1711     return false;
1712 }
1713
1714 const IntSize* RenderThemeMac::cancelButtonSizes() const
1715 {
1716     static const IntSize sizes[3] = { IntSize(16, 13), IntSize(13, 11), IntSize(13, 9) };
1717     return sizes;
1718 }
1719
1720 void RenderThemeMac::adjustSearchFieldCancelButtonStyle(StyleResolver&, RenderStyle& style, Element*) const
1721 {
1722     IntSize size = sizeForSystemFont(style, cancelButtonSizes());
1723     style.setWidth(Length(size.width(), Fixed));
1724     style.setHeight(Length(size.height(), Fixed));
1725     style.setBoxShadow(nullptr);
1726 }
1727
1728 const IntSize* RenderThemeMac::resultsButtonSizes() const
1729 {
1730     static const IntSize sizes[3] = { IntSize(19, 13), IntSize(17, 11), IntSize(17, 9) };
1731     return sizes;
1732 }
1733
1734 const int emptyResultsOffset = 9;
1735 void RenderThemeMac::adjustSearchFieldDecorationPartStyle(StyleResolver&, RenderStyle& style, Element*) const
1736 {
1737     IntSize size = sizeForSystemFont(style, resultsButtonSizes());
1738     style.setWidth(Length(size.width() - emptyResultsOffset, Fixed));
1739     style.setHeight(Length(size.height(), Fixed));
1740     style.setBoxShadow(nullptr);
1741 }
1742
1743 bool RenderThemeMac::paintSearchFieldDecorationPart(const RenderObject&, const PaintInfo&, const IntRect&)
1744 {
1745     return false;
1746 }
1747
1748 void RenderThemeMac::adjustSearchFieldResultsDecorationPartStyle(StyleResolver&, RenderStyle& style, Element*) const
1749 {
1750     IntSize size = sizeForSystemFont(style, resultsButtonSizes());
1751     style.setWidth(Length(size.width(), Fixed));
1752     style.setHeight(Length(size.height(), Fixed));
1753     style.setBoxShadow(nullptr);
1754 }
1755
1756 bool RenderThemeMac::paintSearchFieldResultsDecorationPart(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1757 {
1758     Node* input = o.node()->shadowHost();
1759     if (!input)
1760         input = o.node();
1761     if (!input->renderer()->isBox())
1762         return false;
1763
1764     LocalCurrentGraphicsContext localContext(paintInfo.context);
1765     setSearchCellState(*input->renderer(), r);
1766
1767     NSSearchFieldCell* search = this->search();
1768
1769     if ([search searchMenuTemplate] != nil)
1770         [search setSearchMenuTemplate:nil];
1771
1772     FloatRect localBounds = [search searchButtonRectForBounds:NSRect(input->renderBox()->pixelSnappedBorderBoxRect())];
1773     localBounds = convertToPaintingRect(*input->renderer(), o, localBounds, r);
1774
1775     [[search searchButtonCell] drawWithFrame:localBounds inView:documentViewFor(o)];
1776     [[search searchButtonCell] setControlView:nil];
1777     return false;
1778 }
1779
1780 const int resultsArrowWidth = 5;
1781 void RenderThemeMac::adjustSearchFieldResultsButtonStyle(StyleResolver&, RenderStyle& style, Element*) const
1782 {
1783     IntSize size = sizeForSystemFont(style, resultsButtonSizes());
1784     style.setWidth(Length(size.width() + resultsArrowWidth, Fixed));
1785     style.setHeight(Length(size.height(), Fixed));
1786     style.setBoxShadow(nullptr);
1787 }
1788
1789 bool RenderThemeMac::paintSearchFieldResultsButton(const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
1790 {
1791     Node* input = o.node()->shadowHost();
1792     if (!input)
1793         input = o.node();
1794     if (!input->renderer()->isBox())
1795         return false;
1796
1797     LocalCurrentGraphicsContext localContext(paintInfo.context);
1798     setSearchCellState(*input->renderer(), r);
1799
1800     NSSearchFieldCell* search = this->search();
1801
1802     if (![search searchMenuTemplate])
1803         [search setSearchMenuTemplate:searchMenuTemplate()];
1804
1805     GraphicsContextStateSaver stateSaver(*paintInfo.context);
1806     float zoomLevel = o.style().effectiveZoom();
1807
1808     FloatRect localBounds = [search searchButtonRectForBounds:NSRect(input->renderBox()->pixelSnappedBorderBoxRect())];
1809     localBounds = convertToPaintingRect(*input->renderer(), o, localBounds, r);
1810
1811     IntRect unzoomedRect(localBounds);
1812     if (zoomLevel != 1.0f) {
1813         unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel);
1814         unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel);
1815         paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y());
1816         paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel));
1817         paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y());
1818     }
1819
1820     [[search searchButtonCell] drawWithFrame:unzoomedRect inView:documentViewFor(o)];
1821     [[search searchButtonCell] setControlView:nil];
1822
1823     return false;
1824 }
1825
1826 bool RenderThemeMac::paintSnapshottedPluginOverlay(const RenderObject& renderer, const PaintInfo& paintInfo, const IntRect&)
1827 {
1828     if (paintInfo.phase != PaintPhaseBlockBackground)
1829         return true;
1830
1831     if (!is<RenderBlock>(renderer))
1832         return true;
1833
1834     const RenderBlock& renderBlock = downcast<RenderBlock>(renderer);
1835
1836     LayoutUnit contentWidth = renderBlock.contentWidth();
1837     LayoutUnit contentHeight = renderBlock.contentHeight();
1838     if (!contentWidth || !contentHeight)
1839         return true;
1840
1841     GraphicsContext* context = paintInfo.context;
1842
1843     LayoutSize contentSize(contentWidth, contentHeight);
1844     LayoutPoint contentLocation = renderBlock.location();
1845     contentLocation.move(renderBlock.borderLeft() + renderBlock.paddingLeft(), renderBlock.borderTop() + renderBlock.paddingTop());
1846
1847     LayoutRect rect(contentLocation, contentSize);
1848     IntRect alignedRect = snappedIntRect(rect);
1849     if (alignedRect.width() <= 0 || alignedRect.height() <= 0)
1850         return true;
1851
1852     // We need to get the snapshot image from the plugin element, which should be available
1853     // from our node. Assuming this node is the plugin overlay element, we should get to the
1854     // plugin itself by asking for the shadow root parent, and then its parent.
1855
1856     if (!is<HTMLElement>(*renderBlock.element()))
1857         return true;
1858
1859     HTMLElement& plugInOverlay = downcast<HTMLElement>(*renderBlock.element());
1860     Element* parent = plugInOverlay.parentOrShadowHostElement();
1861     while (parent && !is<HTMLPlugInElement>(*parent))
1862         parent = parent->parentOrShadowHostElement();
1863
1864     if (!parent)
1865         return true;
1866
1867     HTMLPlugInElement& plugInElement = downcast<HTMLPlugInElement>(*parent);
1868     if (!is<HTMLPlugInImageElement>(plugInElement))
1869         return true;
1870
1871     HTMLPlugInImageElement& plugInImageElement = downcast<HTMLPlugInImageElement>(plugInElement);
1872
1873     Image* snapshot = plugInImageElement.snapshotImage();
1874     if (!snapshot)
1875         return true;
1876
1877     RenderSnapshottedPlugIn& plugInRenderer = downcast<RenderSnapshottedPlugIn>(*plugInImageElement.renderer());
1878     FloatPoint snapshotAbsPos = plugInRenderer.localToAbsolute();
1879     snapshotAbsPos.move(plugInRenderer.borderLeft() + plugInRenderer.paddingLeft(), plugInRenderer.borderTop() + plugInRenderer.paddingTop());
1880
1881     // We could draw the snapshot with that coordinates, but we need to make sure there
1882     // isn't a composited layer between us and the plugInRenderer.
1883     for (auto* renderBox = &downcast<RenderBox>(renderer); renderBox != &plugInRenderer; renderBox = renderBox->parentBox()) {
1884         if (renderBox->hasLayer() && renderBox->layer() && renderBox->layer()->isComposited()) {
1885             snapshotAbsPos = -renderBox->location();
1886             break;
1887         }
1888     }
1889
1890     LayoutSize pluginSize(plugInRenderer.contentWidth(), plugInRenderer.contentHeight());
1891     LayoutRect pluginRect(snapshotAbsPos, pluginSize);
1892     IntRect alignedPluginRect = snappedIntRect(pluginRect);
1893
1894     if (alignedPluginRect.width() <= 0 || alignedPluginRect.height() <= 0)
1895         return true;
1896
1897     context->drawImage(snapshot, plugInRenderer.style().colorSpace(), alignedPluginRect, CompositeSourceOver);
1898     return false;
1899 }
1900
1901 #if ENABLE(DATALIST_ELEMENT)
1902 IntSize RenderThemeMac::sliderTickSize() const
1903 {
1904     return IntSize(1, 3);
1905 }
1906
1907 int RenderThemeMac::sliderTickOffsetFromTrackCenter() const
1908 {
1909     return -9;
1910 }
1911 #endif
1912
1913 const int sliderThumbWidth = 15;
1914 const int sliderThumbHeight = 15;
1915
1916 void RenderThemeMac::adjustSliderThumbSize(RenderStyle& style, Element*) const
1917 {
1918     float zoomLevel = style.effectiveZoom();
1919     if (style.appearance() == SliderThumbHorizontalPart || style.appearance() == SliderThumbVerticalPart) {
1920         style.setWidth(Length(static_cast<int>(sliderThumbWidth * zoomLevel), Fixed));
1921         style.setHeight(Length(static_cast<int>(sliderThumbHeight * zoomLevel), Fixed));
1922     }
1923 }
1924
1925 bool RenderThemeMac::shouldShowPlaceholderWhenFocused() const
1926 {
1927     return true;
1928 }
1929
1930 bool RenderThemeMac::shouldHaveCapsLockIndicator(HTMLInputElement& element) const
1931 {
1932     return element.isPasswordField();
1933 }
1934
1935 NSPopUpButtonCell* RenderThemeMac::popupButton() const
1936 {
1937     if (!m_popupButton) {
1938         m_popupButton = adoptNS([[NSPopUpButtonCell alloc] initTextCell:@"" pullsDown:NO]);
1939         [m_popupButton.get() setUsesItemFromMenu:NO];
1940         [m_popupButton.get() setFocusRingType:NSFocusRingTypeExterior];
1941         // We don't want the app's UI layout direction to affect the appearance of popup buttons in
1942         // web content, which has its own layout direction.
1943         // FIXME: Make this depend on the directionality of the select element, once the rest of the
1944         // rendering code can account for the popup arrows appearing on the other side.
1945         [m_popupButton setUserInterfaceLayoutDirection:NSUserInterfaceLayoutDirectionLeftToRight];
1946     }
1947
1948     return m_popupButton.get();
1949 }
1950
1951 NSSearchFieldCell* RenderThemeMac::search() const
1952 {
1953     if (!m_search) {
1954         m_search = adoptNS([[NSSearchFieldCell alloc] initTextCell:@""]);
1955         [m_search.get() setBezelStyle:NSTextFieldRoundedBezel];
1956         [m_search.get() setBezeled:YES];
1957         [m_search.get() setEditable:YES];
1958         [m_search.get() setFocusRingType:NSFocusRingTypeExterior];
1959 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
1960         [m_search.get() setCenteredLook:NO];
1961 #endif
1962     }
1963
1964     return m_search.get();
1965 }
1966
1967 NSMenu* RenderThemeMac::searchMenuTemplate() const
1968 {
1969     if (!m_searchMenuTemplate)
1970         m_searchMenuTemplate = adoptNS([[NSMenu alloc] initWithTitle:@""]);
1971
1972     return m_searchMenuTemplate.get();
1973 }
1974
1975 NSSliderCell* RenderThemeMac::sliderThumbHorizontal() const
1976 {
1977     if (!m_sliderThumbHorizontal) {
1978         m_sliderThumbHorizontal = adoptNS([[NSSliderCell alloc] init]);
1979         [m_sliderThumbHorizontal.get() setSliderType:NSLinearSlider];
1980         [m_sliderThumbHorizontal.get() setControlSize:NSSmallControlSize];
1981         [m_sliderThumbHorizontal.get() setFocusRingType:NSFocusRingTypeExterior];
1982     }
1983
1984     return m_sliderThumbHorizontal.get();
1985 }
1986
1987 NSSliderCell* RenderThemeMac::sliderThumbVertical() const
1988 {
1989     if (!m_sliderThumbVertical) {
1990         m_sliderThumbVertical = adoptNS([[NSSliderCell alloc] init]);
1991         [m_sliderThumbVertical.get() setSliderType:NSLinearSlider];
1992         [m_sliderThumbVertical.get() setControlSize:NSSmallControlSize];
1993         [m_sliderThumbVertical.get() setFocusRingType:NSFocusRingTypeExterior];
1994     }
1995
1996     return m_sliderThumbVertical.get();
1997 }
1998
1999 NSTextFieldCell* RenderThemeMac::textField() const
2000 {
2001     if (!m_textField) {
2002         m_textField = adoptNS([[WebCoreTextFieldCell alloc] initTextCell:@""]);
2003         [m_textField.get() setBezeled:YES];
2004         [m_textField.get() setEditable:YES];
2005         [m_textField.get() setFocusRingType:NSFocusRingTypeExterior];
2006         // Post-Lion, WebCore can be in charge of paintinng the background thanks to
2007         // the workaround in place for <rdar://problem/11385461>, which is implemented
2008         // above as _coreUIDrawOptionsWithFrame.
2009         [m_textField.get() setDrawsBackground:NO];
2010     }
2011
2012     return m_textField.get();
2013 }
2014
2015 String RenderThemeMac::fileListNameForWidth(const FileList* fileList, const FontCascade& font, int width, bool multipleFilesAllowed) const
2016 {
2017     if (width <= 0)
2018         return String();
2019
2020     String strToTruncate;
2021     if (fileList->isEmpty())
2022         strToTruncate = fileListDefaultLabel(multipleFilesAllowed);
2023     else if (fileList->length() == 1)
2024         strToTruncate = [[NSFileManager defaultManager] displayNameAtPath:(fileList->item(0)->path())];
2025     else
2026         return StringTruncator::rightTruncate(multipleFileUploadText(fileList->length()), width, font, StringTruncator::EnableRoundingHacks);
2027
2028     return StringTruncator::centerTruncate(strToTruncate, width, font, StringTruncator::EnableRoundingHacks);
2029 }
2030
2031 bool RenderThemeMac::defaultButtonHasAnimation() const
2032 {
2033 #if __MAC_OS_X_VERSION_MIN_REQUIRED >= 101000
2034     return false;
2035 #else
2036     return true;
2037 #endif
2038 }
2039
2040 #if ENABLE(SERVICE_CONTROLS)
2041 NSServicesRolloverButtonCell* RenderThemeMac::servicesRolloverButtonCell() const
2042 {
2043 #if HAVE(APPKIT_SERVICE_CONTROLS_SUPPORT)
2044     if (!m_servicesRolloverButton) {
2045         m_servicesRolloverButton = [NSServicesRolloverButtonCell serviceRolloverButtonCellForStyle:NSSharingServicePickerStyleRollover];
2046         [m_servicesRolloverButton setBezelStyle:NSRoundedDisclosureBezelStyle];
2047         [m_servicesRolloverButton setButtonType:NSPushOnPushOffButton];
2048         [m_servicesRolloverButton setImagePosition:NSImageOnly];
2049         [m_servicesRolloverButton setState:NO];
2050     }
2051
2052     return m_servicesRolloverButton.get();
2053 #else
2054     return nil;
2055 #endif
2056 }
2057
2058 bool RenderThemeMac::paintImageControlsButton(const RenderObject& renderer, const PaintInfo& paintInfo, const IntRect& rect)
2059 {
2060     if (paintInfo.phase != PaintPhaseBlockBackground)
2061         return true;
2062
2063 #if HAVE(APPKIT_SERVICE_CONTROLS_SUPPORT)
2064     NSServicesRolloverButtonCell *cell = servicesRolloverButtonCell();
2065
2066     LocalCurrentGraphicsContext localContext(paintInfo.context);
2067     GraphicsContextStateSaver stateSaver(*paintInfo.context);
2068
2069     paintInfo.context->translate(rect.x(), rect.y());
2070
2071     IntRect innerFrame(IntPoint(), rect.size());
2072     [cell drawWithFrame:innerFrame inView:documentViewFor(renderer)];
2073     [cell setControlView:nil];
2074 #else
2075     UNUSED_PARAM(renderer);
2076     UNUSED_PARAM(rect);
2077 #endif
2078
2079     return true;
2080 }
2081
2082 IntSize RenderThemeMac::imageControlsButtonSize(const RenderObject&) const
2083 {
2084 #if HAVE(APPKIT_SERVICE_CONTROLS_SUPPORT)
2085     return IntSize(servicesRolloverButtonCell().cellSize);
2086 #else
2087     return IntSize();
2088 #endif
2089 }
2090
2091 IntSize RenderThemeMac::imageControlsButtonPositionOffset() const
2092 {
2093 #if HAVE(APPKIT_SERVICE_CONTROLS_SUPPORT)
2094     // FIXME: Currently the offsets will always be the same no matter what image rect you try with.
2095     // This may not always be true in the future.
2096     static const int dummyDimension = 100;
2097     IntRect dummyImageRect(0, 0, dummyDimension, dummyDimension);
2098     NSRect bounds = [servicesRolloverButtonCell() rectForBounds:dummyImageRect preferredEdge:NSMinYEdge];
2099
2100     return IntSize(dummyDimension - bounds.origin.x, bounds.origin.y);
2101 #else
2102     return IntSize();
2103 #endif
2104 }
2105 #endif
2106
2107 #if ENABLE(ATTACHMENT_ELEMENT)
2108 const CGFloat attachmentIconSize = 48;
2109 const CGFloat attachmentIconBackgroundPadding = 6;
2110 const CGFloat attachmentIconBackgroundSize = attachmentIconSize + attachmentIconBackgroundPadding;
2111 const CGFloat attachmentIconSelectionBorderThickness = 1;
2112 const CGFloat attachmentIconBackgroundRadius = 3;
2113 const CGFloat attachmentIconToTitleMargin = 2;
2114
2115 static Color attachmentIconBackgroundColor() { return Color(0, 0, 0, 30); }
2116 static Color attachmentIconBorderColor() { return Color(255, 255, 255, 125); }
2117
2118 const CGFloat attachmentTitleFontSize = 12;
2119 const CGFloat attachmentTitleBackgroundRadius = 3;
2120 const CGFloat attachmentTitleBackgroundPadding = 3;
2121 const CGFloat attachmentTitleMaximumWidth = 100 - (attachmentTitleBackgroundPadding * 2);
2122 const CFIndex attachmentTitleMaximumLineCount = 2;
2123
2124 static Color attachmentTitleInactiveBackgroundColor() { return Color(204, 204, 204, 255); }
2125 static Color attachmentTitleInactiveTextColor() { return Color(100, 100, 100, 255); }
2126
2127 const CGFloat attachmentSubtitleFontSize = 10;
2128 static Color attachmentSubtitleTextColor() { return Color(82, 145, 214, 255); }
2129
2130 const CGFloat attachmentProgressBarWidth = 30;
2131 const CGFloat attachmentProgressBarHeight = 5;
2132 const CGFloat attachmentProgressBarOffset = -9;
2133 const CGFloat attachmentProgressBarBorderWidth = 1;
2134 static Color attachmentProgressBarBackgroundColor() { return Color(0, 0, 0, 89); }
2135 static Color attachmentProgressBarFillColor() { return Color(Color::white); }
2136 static Color attachmentProgressBarBorderColor() { return Color(0, 0, 0, 128); }
2137
2138 const CGFloat attachmentMargin = 3;
2139
2140 struct AttachmentLayout {
2141     explicit AttachmentLayout(const RenderAttachment&);
2142
2143     struct LabelLine {
2144         FloatRect backgroundRect;
2145         FloatPoint origin;
2146         RetainPtr<CTLineRef> line;
2147     };
2148
2149     Vector<LabelLine> lines;
2150
2151     FloatRect iconRect;
2152     FloatRect iconBackgroundRect;
2153     FloatRect attachmentRect;
2154
2155     int baseline;
2156
2157     RetainPtr<CTLineRef> subtitleLine;
2158     FloatRect subtitleTextRect;
2159
2160 private:
2161     void layOutTitle(const RenderAttachment&);
2162     void layOutSubtitle(const RenderAttachment&);
2163
2164     void addTitleLine(CTLineRef, CGFloat& yOffset, Vector<CGPoint> origins, CFIndex lineIndex, const RenderAttachment&);
2165 };
2166
2167 static NSColor *titleTextColorForAttachment(const RenderAttachment& attachment)
2168 {
2169     if (attachment.selectionState() != RenderObject::SelectionNone) {
2170         if (attachment.frame().selection().isFocusedAndActive())
2171             return [NSColor alternateSelectedControlTextColor];    
2172         return (NSColor *)cachedCGColor(attachmentTitleInactiveTextColor(), ColorSpaceDeviceRGB);
2173     }
2174
2175     return [NSColor blackColor];
2176 }
2177
2178 void AttachmentLayout::addTitleLine(CTLineRef line, CGFloat& yOffset, Vector<CGPoint> origins, CFIndex lineIndex, const RenderAttachment& attachment)
2179 {
2180     CGRect lineBounds = CTLineGetBoundsWithOptions(line, 0);
2181     CGFloat trailingWhitespaceWidth = CTLineGetTrailingWhitespaceWidth(line);
2182     CGFloat lineWidthIgnoringTrailingWhitespace = lineBounds.size.width - trailingWhitespaceWidth;
2183     CGFloat lineHeight = CGCeiling(lineBounds.size.height);
2184
2185     // Center the line relative to the icon.
2186     CGFloat xOffset = (attachmentIconBackgroundSize / 2) - (lineWidthIgnoringTrailingWhitespace / 2);
2187
2188     if (lineIndex)
2189         yOffset += origins[lineIndex - 1].y - origins[lineIndex].y;
2190
2191     LabelLine labelLine;
2192     labelLine.origin = FloatPoint(xOffset, yOffset + lineHeight - origins.last().y);
2193     labelLine.line = line;
2194     labelLine.backgroundRect = FloatRect(xOffset, yOffset, lineWidthIgnoringTrailingWhitespace, lineHeight);
2195     labelLine.backgroundRect.inflateX(attachmentTitleBackgroundPadding);
2196     labelLine.backgroundRect = encloseRectToDevicePixels(labelLine.backgroundRect, attachment.document().deviceScaleFactor());
2197
2198     // If the text rects are close in size, the curved enclosing background won't
2199     // look right, so make them the same exact size.
2200     if (!lines.isEmpty()) {
2201         float previousBackgroundRectWidth = lines.last().backgroundRect.width();
2202         if (fabs(labelLine.backgroundRect.width() - previousBackgroundRectWidth) < attachmentTitleBackgroundRadius * 4) {
2203             float newBackgroundRectWidth = std::max(previousBackgroundRectWidth, labelLine.backgroundRect.width());
2204             labelLine.backgroundRect.inflateX((newBackgroundRectWidth - labelLine.backgroundRect.width()) / 2);
2205             lines.last().backgroundRect.inflateX((newBackgroundRectWidth - previousBackgroundRectWidth) / 2);
2206         }
2207     }
2208
2209     lines.append(labelLine);
2210 }
2211
2212 void AttachmentLayout::layOutTitle(const RenderAttachment& attachment)
2213 {
2214     CFStringRef language = 0; // By not specifying a language we use the system language.
2215     RetainPtr<CTFontRef> font = adoptCF(CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, attachmentTitleFontSize, language));
2216     baseline = CGRound(attachmentIconBackgroundSize + attachmentIconToTitleMargin + CTFontGetAscent(font.get()));
2217
2218     String title = attachment.attachmentElement().attachmentTitle();
2219     if (title.isEmpty())
2220         return;
2221
2222     NSDictionary *textAttributes = @{
2223         (id)kCTFontAttributeName: (id)font.get(),
2224         (id)kCTForegroundColorAttributeName: titleTextColorForAttachment(attachment)
2225     };
2226     RetainPtr<NSAttributedString> attributedTitle = adoptNS([[NSAttributedString alloc] initWithString:title attributes:textAttributes]);
2227     RetainPtr<CTFramesetterRef> titleFramesetter = adoptCF(CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attributedTitle.get()));
2228
2229     CFRange fitRange;
2230     CGSize titleTextSize = CTFramesetterSuggestFrameSizeWithConstraints(titleFramesetter.get(), CFRangeMake(0, 0), nullptr, CGSizeMake(attachmentTitleMaximumWidth, CGFLOAT_MAX), &fitRange);
2231
2232     RetainPtr<CGPathRef> titlePath = adoptCF(CGPathCreateWithRect(CGRectMake(0, 0, titleTextSize.width, titleTextSize.height), nullptr));
2233     RetainPtr<CTFrameRef> titleFrame = adoptCF(CTFramesetterCreateFrame(titleFramesetter.get(), fitRange, titlePath.get(), nullptr));
2234
2235     CFArrayRef ctLines = CTFrameGetLines(titleFrame.get());
2236     CFIndex lineCount = CFArrayGetCount(ctLines);
2237     if (!lineCount)
2238         return;
2239
2240     Vector<CGPoint> origins(lineCount);
2241     CTFrameGetLineOrigins(titleFrame.get(), CFRangeMake(0, 0), origins.data());
2242
2243     // Lay out and record the first (attachmentTitleMaximumLineCount - 1) lines.
2244     CFIndex lineIndex = 0;
2245     CGFloat yOffset = attachmentIconBackgroundSize + attachmentIconToTitleMargin;
2246     for (; lineIndex < std::min(attachmentTitleMaximumLineCount - 1, lineCount); ++lineIndex) {
2247         CTLineRef line = (CTLineRef)CFArrayGetValueAtIndex(ctLines, lineIndex);
2248         addTitleLine(line, yOffset, origins, lineIndex, attachment);
2249     }
2250
2251     if (lineIndex == lineCount)
2252         return;
2253
2254     // We had text that didn't fit in the first (attachmentTitleMaximumLineCount - 1) lines.
2255     // Combine it into one last line, and center-truncate it.
2256     CTLineRef firstRemainingLine = (CTLineRef)CFArrayGetValueAtIndex(ctLines, lineIndex);
2257     CFIndex remainingRangeStart = CTLineGetStringRange(firstRemainingLine).location;
2258     NSRange remainingRange = NSMakeRange(remainingRangeStart, [attributedTitle length] - remainingRangeStart);
2259     NSAttributedString *remainingString = [attributedTitle attributedSubstringFromRange:remainingRange];
2260     RetainPtr<CTLineRef> remainingLine = adoptCF(CTLineCreateWithAttributedString((CFAttributedStringRef)remainingString));
2261     RetainPtr<NSAttributedString> ellipsisString = adoptNS([[NSAttributedString alloc] initWithString:@"\u2026" attributes:textAttributes]);
2262     RetainPtr<CTLineRef> ellipsisLine = adoptCF(CTLineCreateWithAttributedString((CFAttributedStringRef)ellipsisString.get()));
2263     RetainPtr<CTLineRef> truncatedLine = adoptCF(CTLineCreateTruncatedLine(remainingLine.get(), attachmentTitleMaximumWidth, kCTLineTruncationMiddle, ellipsisLine.get()));
2264
2265     if (!truncatedLine)
2266         truncatedLine = remainingLine;
2267
2268     addTitleLine(truncatedLine.get(), yOffset, origins, lineIndex, attachment);
2269 }
2270
2271 void AttachmentLayout::layOutSubtitle(const RenderAttachment& attachment)
2272 {
2273     String subtitleText = attachment.attachmentElement().fastGetAttribute(subtitleAttr);
2274
2275     if (subtitleText.isEmpty())
2276         return;
2277
2278     CFStringRef language = 0; // By not specifying a language we use the system language.
2279     RetainPtr<CTFontRef> font = adoptCF(CTFontCreateUIFontForLanguage(kCTFontUIFontSystem, attachmentSubtitleFontSize, language));
2280     NSDictionary *textAttributes = @{
2281         (id)kCTFontAttributeName: (id)font.get(),
2282         (id)kCTForegroundColorAttributeName: (NSColor *)cachedCGColor(attachmentSubtitleTextColor(), ColorSpaceDeviceRGB)
2283     };
2284     RetainPtr<NSAttributedString> attributedSubtitleText = adoptNS([[NSAttributedString alloc] initWithString:subtitleText attributes:textAttributes]);
2285     subtitleLine = adoptCF(CTLineCreateWithAttributedString((CFAttributedStringRef)attributedSubtitleText.get()));
2286
2287     CGRect lineBounds = CTLineGetBoundsWithOptions(subtitleLine.get(), 0);
2288
2289     // Center the line relative to the icon.
2290     CGFloat xOffset = (attachmentIconBackgroundSize / 2) - (lineBounds.size.width / 2);
2291     CGFloat yOffset = 0;
2292
2293     if (!lines.isEmpty())
2294         yOffset = lines.last().backgroundRect.maxY();
2295     else
2296         yOffset = attachmentIconBackgroundSize + attachmentIconToTitleMargin;
2297
2298     LabelLine labelLine;
2299     subtitleTextRect = FloatRect(xOffset, yOffset, lineBounds.size.width, lineBounds.size.height);
2300 }
2301
2302 AttachmentLayout::AttachmentLayout(const RenderAttachment& attachment)
2303 {
2304     layOutTitle(attachment);
2305     layOutSubtitle(attachment);
2306
2307     iconBackgroundRect = FloatRect(0, 0, attachmentIconBackgroundSize, attachmentIconBackgroundSize);
2308
2309     iconRect = iconBackgroundRect;
2310     iconRect.setSize(FloatSize(attachmentIconSize, attachmentIconSize));
2311     iconRect.move(attachmentIconBackgroundPadding / 2, attachmentIconBackgroundPadding / 2);
2312
2313     attachmentRect = iconBackgroundRect;
2314     for (const auto& line : lines)
2315         attachmentRect.unite(line.backgroundRect);
2316     attachmentRect.unite(subtitleTextRect);
2317     attachmentRect.inflate(attachmentMargin);
2318     attachmentRect = encloseRectToDevicePixels(attachmentRect, attachment.document().deviceScaleFactor());
2319 }
2320
2321 LayoutSize RenderThemeMac::attachmentIntrinsicSize(const RenderAttachment& attachment) const
2322 {
2323     AttachmentLayout layout(attachment);
2324     return LayoutSize(layout.attachmentRect.size());
2325 }
2326
2327 int RenderThemeMac::attachmentBaseline(const RenderAttachment& attachment) const
2328 {
2329     AttachmentLayout layout(attachment);
2330     return layout.baseline;
2331 }
2332
2333 static void paintAttachmentIconBackground(const RenderAttachment&, GraphicsContext& context, AttachmentLayout& layout)
2334 {
2335     // FIXME: Finder has a discontinuous behavior here when you have a background color other than white,
2336     // where it switches into 'bordered mode' and the border pops in on top of the background.
2337     bool paintBorder = true;
2338
2339     FloatRect backgroundRect = layout.iconBackgroundRect;
2340     if (paintBorder)
2341         backgroundRect.inflate(-attachmentIconSelectionBorderThickness);
2342
2343     context.fillRoundedRect(FloatRoundedRect(backgroundRect, FloatRoundedRect::Radii(attachmentIconBackgroundRadius)), attachmentIconBackgroundColor(), ColorSpaceDeviceRGB);
2344
2345     if (paintBorder) {
2346         FloatRect borderRect = layout.iconBackgroundRect;
2347         borderRect.inflate(-attachmentIconSelectionBorderThickness / 2);
2348
2349         FloatSize iconBackgroundRadiusSize(attachmentIconBackgroundRadius, attachmentIconBackgroundRadius);
2350         Path borderPath;
2351         borderPath.addRoundedRect(borderRect, iconBackgroundRadiusSize);
2352         context.setStrokeColor(attachmentIconBorderColor(), ColorSpaceDeviceRGB);
2353         context.setStrokeThickness(attachmentIconSelectionBorderThickness);
2354         context.strokePath(borderPath);
2355     }
2356 }
2357
2358 static void paintAttachmentIcon(const RenderAttachment& attachment, GraphicsContext& context, AttachmentLayout& layout)
2359 {
2360     Vector<String> filenames;
2361     if (File* file = attachment.attachmentElement().file())
2362         filenames.append(file->path());
2363     RefPtr<Icon> icon = Icon::createIconForFiles(filenames);
2364     if (!icon)
2365         return;
2366     icon->paint(context, layout.iconRect);
2367 }
2368
2369 static void addAttachmentTitleBackgroundRightCorner(Path& path, const FloatRect* fromRect, const FloatRect* toRect)
2370 {
2371     FloatSize horizontalRadius(attachmentTitleBackgroundRadius, 0);
2372     FloatSize verticalRadius(0, attachmentTitleBackgroundRadius);
2373
2374     if (!fromRect) {
2375         // For the first (top) rect:
2376
2377         path.moveTo(toRect->minXMinYCorner() + horizontalRadius);
2378
2379         // Across the top, towards the right.
2380         path.addLineTo(toRect->maxXMinYCorner() - horizontalRadius);
2381
2382         // Arc the top corner.
2383         path.addArcTo(toRect->maxXMinYCorner(), toRect->maxXMinYCorner() + verticalRadius, attachmentTitleBackgroundRadius);
2384
2385         // Down the right.
2386         path.addLineTo(toRect->maxXMaxYCorner() - verticalRadius);
2387     } else if (!toRect) {
2388         // For the last rect:
2389
2390         // Arc the bottom corner.
2391         path.addArcTo(fromRect->maxXMaxYCorner(), fromRect->maxXMaxYCorner() - horizontalRadius, attachmentTitleBackgroundRadius);
2392     } else {
2393         // For middle rects:
2394
2395         float widthDifference = toRect->width() - fromRect->width();
2396
2397         // Skip over very similar-width rects, because we can't make
2398         // sensible curves between them.
2399         if (fabs(widthDifference) < std::numeric_limits<float>::epsilon())
2400             return;
2401
2402         if (widthDifference < 0) {
2403             // Arc the outer corner.
2404             path.addArcTo(FloatPoint(fromRect->maxX(), toRect->y()), FloatPoint(fromRect->maxX(), toRect->y()) - horizontalRadius, attachmentTitleBackgroundRadius);
2405
2406             // Across the bottom, towards the left.
2407             path.addLineTo(toRect->maxXMinYCorner() + horizontalRadius);
2408
2409             // Arc the inner corner.
2410             path.addArcTo(toRect->maxXMinYCorner(), toRect->maxXMinYCorner() + verticalRadius, attachmentTitleBackgroundRadius);
2411         } else {
2412             // Arc the inner corner.
2413             path.addArcTo(FloatPoint(fromRect->maxX(), toRect->y()), FloatPoint(fromRect->maxX(), toRect->y()) + horizontalRadius, attachmentTitleBackgroundRadius);
2414
2415             // Across the bottom, towards the right.
2416             path.addLineTo(toRect->maxXMinYCorner() - horizontalRadius);
2417
2418             // Arc the outer corner.
2419             path.addArcTo(toRect->maxXMinYCorner(), toRect->maxXMinYCorner() + verticalRadius, attachmentTitleBackgroundRadius);
2420         }
2421
2422         // Down the right.
2423         path.addLineTo(toRect->maxXMaxYCorner() - verticalRadius);
2424     }
2425 }
2426
2427 static void addAttachmentTitleBackgroundLeftCorner(Path& path, const FloatRect* fromRect, const FloatRect* toRect)
2428 {
2429     FloatSize horizontalRadius(attachmentTitleBackgroundRadius, 0);
2430     FloatSize verticalRadius(0, attachmentTitleBackgroundRadius);
2431
2432     if (!fromRect) {
2433         // For the first (bottom) rect:
2434
2435         // Across the bottom, towards the left.
2436         path.addLineTo(toRect->minXMaxYCorner() + horizontalRadius);
2437
2438         // Arc the bottom corner.
2439         path.addArcTo(toRect->minXMaxYCorner(), toRect->minXMaxYCorner() - verticalRadius, attachmentTitleBackgroundRadius);
2440
2441         // Up the left.
2442         path.addLineTo(toRect->minXMinYCorner() + verticalRadius);
2443     } else if (!toRect) {
2444         // For the last (top) rect:
2445
2446         // Arc the top corner.
2447         path.addArcTo(fromRect->minXMinYCorner(), fromRect->minXMinYCorner() + horizontalRadius, attachmentTitleBackgroundRadius);
2448     } else {
2449         // For middle rects:
2450         float widthDifference = toRect->width() - fromRect->width();
2451
2452         // Skip over very similar-width rects, because we can't make
2453         // sensible curves between them.
2454         if (fabs(widthDifference) < std::numeric_limits<float>::epsilon())
2455             return;
2456
2457         if (widthDifference < 0) {
2458             // Arc the inner corner.
2459             path.addArcTo(FloatPoint(fromRect->x(), toRect->maxY()), FloatPoint(fromRect->x(), toRect->maxY()) + horizontalRadius, attachmentTitleBackgroundRadius);
2460
2461             // Across the bottom, towards the right.
2462             path.addLineTo(toRect->minXMaxYCorner() - horizontalRadius);
2463
2464             // Arc the outer corner.
2465             path.addArcTo(toRect->minXMaxYCorner(), toRect->minXMaxYCorner() - verticalRadius, attachmentTitleBackgroundRadius);
2466         } else {
2467             // Arc the outer corner.
2468             path.addArcTo(FloatPoint(fromRect->x(), toRect->maxY()), FloatPoint(fromRect->x(), toRect->maxY()) - horizontalRadius, attachmentTitleBackgroundRadius);
2469
2470             // Across the bottom, towards the left.
2471             path.addLineTo(toRect->minXMaxYCorner() + horizontalRadius);
2472
2473             // Arc the inner corner.
2474             path.addArcTo(toRect->minXMaxYCorner(), toRect->minXMaxYCorner() - verticalRadius, attachmentTitleBackgroundRadius);
2475         }
2476         
2477         // Up the right.
2478         path.addLineTo(toRect->minXMinYCorner() + verticalRadius);
2479     }
2480 }
2481
2482 static void paintAttachmentTitleBackground(const RenderAttachment& attachment, GraphicsContext& context, AttachmentLayout& layout)
2483 {
2484     if (layout.lines.isEmpty())
2485         return;
2486
2487     Path backgroundPath;
2488
2489     for (size_t i = 0; i <= layout.lines.size(); ++i)
2490         addAttachmentTitleBackgroundRightCorner(backgroundPath, i ? &layout.lines[i - 1].backgroundRect : nullptr, i < layout.lines.size() ? &layout.lines[i].backgroundRect : nullptr);
2491
2492     for (size_t i = 0; i <= layout.lines.size(); ++i) {
2493         size_t reverseIndex = layout.lines.size() - i;
2494         addAttachmentTitleBackgroundLeftCorner(backgroundPath, reverseIndex < layout.lines.size() ? &layout.lines[reverseIndex].backgroundRect : nullptr, reverseIndex ? &layout.lines[reverseIndex - 1].backgroundRect : nullptr);
2495     }
2496
2497     backgroundPath.closeSubpath();
2498
2499     Color backgroundColor;
2500     if (attachment.frame().selection().isFocusedAndActive())
2501         backgroundColor = convertNSColorToColor([NSColor alternateSelectedControlColor]);
2502     else
2503         backgroundColor = attachmentTitleInactiveBackgroundColor();
2504
2505     context.setFillColor(backgroundColor, ColorSpaceDeviceRGB);
2506     context.fillPath(backgroundPath);
2507 }
2508
2509 static void paintAttachmentTitle(const RenderAttachment&, GraphicsContext& context, AttachmentLayout& layout)
2510 {
2511     for (const auto& line : layout.lines) {
2512         GraphicsContextStateSaver saver(context);
2513
2514         context.translate(toFloatSize(line.origin));
2515         context.scale(FloatSize(1, -1));
2516
2517         CGContextSetTextMatrix(context.platformContext(), CGAffineTransformIdentity);
2518         CTLineDraw(line.line.get(), context.platformContext());
2519     }
2520 }
2521
2522 static void paintAttachmentSubtitle(const RenderAttachment&, GraphicsContext& context, AttachmentLayout& layout)
2523 {
2524     GraphicsContextStateSaver saver(context);
2525
2526     context.translate(toFloatSize(layout.subtitleTextRect.minXMaxYCorner()));
2527     context.scale(FloatSize(1, -1));
2528
2529     CGContextSetTextMatrix(context.platformContext(), CGAffineTransformIdentity);
2530     CTLineDraw(layout.subtitleLine.get(), context.platformContext());
2531 }
2532
2533 static void paintAttachmentProgress(const RenderAttachment& attachment, GraphicsContext& context, AttachmentLayout& layout)
2534 {
2535     String progressString = attachment.attachmentElement().fastGetAttribute(progressAttr);
2536     if (progressString.isEmpty())
2537         return;
2538     bool validProgress;
2539     float progress = progressString.toFloat(&validProgress);
2540     if (!validProgress)
2541         return;
2542
2543     GraphicsContextStateSaver saver(context);
2544
2545     FloatRect progressBounds((attachmentIconBackgroundSize - attachmentProgressBarWidth) / 2, layout.iconBackgroundRect.maxY() + attachmentProgressBarOffset - attachmentProgressBarHeight, attachmentProgressBarWidth, attachmentProgressBarHeight);
2546
2547     FloatRect borderRect = progressBounds;
2548     borderRect.inflate(-0.5);
2549     FloatRect backgroundRect = borderRect;
2550     backgroundRect.inflate(-attachmentProgressBarBorderWidth / 2);
2551
2552     FloatRoundedRect backgroundRoundedRect(backgroundRect, FloatRoundedRect::Radii(backgroundRect.height() / 2));
2553     context.fillRoundedRect(backgroundRoundedRect, attachmentProgressBarBackgroundColor(), ColorSpaceDeviceRGB);
2554
2555     {
2556         GraphicsContextStateSaver clipSaver(context);
2557         context.clipRoundedRect(backgroundRoundedRect);
2558
2559         FloatRect progressRect = progressBounds;
2560         progressRect.setWidth(progressRect.width() * progress);
2561         progressRect = encloseRectToDevicePixels(progressRect, attachment.document().deviceScaleFactor());
2562
2563         context.fillRect(progressRect, attachmentProgressBarFillColor(), ColorSpaceDeviceRGB);
2564     }
2565
2566     Path borderPath;
2567     float borderRadius = borderRect.height() / 2;
2568     borderPath.addRoundedRect(borderRect, FloatSize(borderRadius, borderRadius));
2569     context.setStrokeColor(attachmentProgressBarBorderColor(), ColorSpaceDeviceRGB);
2570     context.setStrokeThickness(attachmentProgressBarBorderWidth);
2571     context.strokePath(borderPath);
2572 }
2573
2574 bool RenderThemeMac::paintAttachment(const RenderObject& renderer, const PaintInfo& paintInfo, const IntRect& paintRect)
2575 {
2576     if (!is<RenderAttachment>(renderer))
2577         return false;
2578
2579     const RenderAttachment& attachment = downcast<RenderAttachment>(renderer);
2580
2581     AttachmentLayout layout(attachment);
2582
2583     GraphicsContext& context = *paintInfo.context;
2584     LocalCurrentGraphicsContext localContext(&context);
2585     GraphicsContextStateSaver saver(context);
2586
2587     context.translate(toFloatSize(paintRect.location()));
2588     context.translate(FloatSize((layout.attachmentRect.width() - attachmentIconBackgroundSize) / 2, 0));
2589
2590     bool useSelectedStyle = attachment.selectionState() != RenderObject::SelectionNone;
2591
2592     if (useSelectedStyle)
2593         paintAttachmentIconBackground(attachment, context, layout);
2594     paintAttachmentIcon(attachment, context, layout);
2595     if (useSelectedStyle)
2596         paintAttachmentTitleBackground(attachment, context, layout);
2597     paintAttachmentTitle(attachment, context, layout);
2598     paintAttachmentSubtitle(attachment, context, layout);
2599     paintAttachmentProgress(attachment, context, layout);
2600
2601     return true;
2602 }
2603
2604 #endif // ENABLE(ATTACHMENT_ELEMENT)
2605
2606 } // namespace WebCore
2607
2608 #endif // !PLATFORM(IOS)