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