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