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