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