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