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