Use a 1-byte enum class for TextDirection
[WebKit-https.git] / Source / WebCore / rendering / RenderThemeIOS.mm
1 /*
2  * Copyright (C) 2005-2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "RenderThemeIOS.h"
28
29 #if PLATFORM(IOS)
30
31 #import "BitmapImage.h"
32 #import "CSSPrimitiveValue.h"
33 #import "CSSToLengthConversionData.h"
34 #import "CSSValueKeywords.h"
35 #import "DateComponents.h"
36 #import "Document.h"
37 #import "File.h"
38 #import "FloatRoundedRect.h"
39 #import "FontCache.h"
40 #import "FontCascade.h"
41 #import "Frame.h"
42 #import "FrameSelection.h"
43 #import "FrameView.h"
44 #import "GeometryUtilities.h"
45 #import "Gradient.h"
46 #import "GraphicsContext.h"
47 #import "GraphicsContextCG.h"
48 #import "HTMLAttachmentElement.h"
49 #import "HTMLInputElement.h"
50 #import "HTMLNames.h"
51 #import "HTMLSelectElement.h"
52 #import "IOSurface.h"
53 #import "Icon.h"
54 #import "LocalizedDateCache.h"
55 #import "NodeRenderStyle.h"
56 #import "Page.h"
57 #import "PaintInfo.h"
58 #import "PathUtilities.h"
59 #import "PlatformLocale.h"
60 #import "RenderAttachment.h"
61 #import "RenderObject.h"
62 #import "RenderProgress.h"
63 #import "RenderStyle.h"
64 #import "RenderView.h"
65 #import "RuntimeEnabledFeatures.h"
66 #import "UTIUtilities.h"
67 #import "UserAgentScripts.h"
68 #import "UserAgentStyleSheets.h"
69 #import "WebCoreThreadRun.h"
70 #import <CoreGraphics/CoreGraphics.h>
71 #import <CoreImage/CoreImage.h>
72 #import <objc/runtime.h>
73 #import <pal/spi/cocoa/CoreTextSPI.h>
74 #import <pal/spi/ios/UIKitSPI.h>
75 #import <wtf/NeverDestroyed.h>
76 #import <wtf/RefPtr.h>
77 #import <wtf/SoftLinking.h>
78 #import <wtf/StdLibExtras.h>
79
80 #if USE(SYSTEM_PREVIEW) && USE(APPLE_INTERNAL_SDK)
81 #import <WebKitAdditions/SystemPreviewArtwork.cpp>
82 #endif
83
84 SOFT_LINK_FRAMEWORK(UIKit)
85 SOFT_LINK_CLASS(UIKit, UIApplication)
86 SOFT_LINK_CLASS(UIKit, UIColor)
87 SOFT_LINK_CLASS(UIKit, UIDocumentInteractionController)
88 SOFT_LINK_CLASS(UIKit, UIImage)
89 SOFT_LINK_CONSTANT(UIKit, UIContentSizeCategoryDidChangeNotification, CFStringRef)
90 #define UIContentSizeCategoryDidChangeNotification getUIContentSizeCategoryDidChangeNotification()
91
92 @interface WebCoreRenderThemeBundle : NSObject
93 @end
94
95 @implementation WebCoreRenderThemeBundle
96 @end
97
98 namespace WebCore {
99
100 using namespace HTMLNames;
101
102 const float ControlBaseHeight = 20;
103 const float ControlBaseFontSize = 11;
104
105 struct IOSGradient {
106     float* start; // points to static float[4]
107     float* end; // points to static float[4]
108     IOSGradient(float start[4], float end[4])
109         : start(start)
110         , end(end)
111     {
112     }
113 };
114
115 typedef IOSGradient* IOSGradientRef;
116
117 enum Interpolation
118 {
119     LinearInterpolation,
120     ExponentialInterpolation
121 };
122
123 static void interpolateLinearGradient(void *info, const CGFloat *inData, CGFloat *outData)
124 {
125     IOSGradientRef gradient = static_cast<IOSGradientRef>(info);
126     float alpha = inData[0];
127     float inverse = 1.0f - alpha;
128
129     outData[0] = inverse * gradient->start[0] + alpha * gradient->end[0];
130     outData[1] = inverse * gradient->start[1] + alpha * gradient->end[1];
131     outData[2] = inverse * gradient->start[2] + alpha * gradient->end[2];
132     outData[3] = inverse * gradient->start[3] + alpha * gradient->end[3];
133 }
134
135 static void interpolateExponentialGradient(void *info, const CGFloat *inData, CGFloat *outData)
136 {
137     IOSGradientRef gradient = static_cast<IOSGradientRef>(info);
138     float a = inData[0];
139     for (int paintInfo = 0; paintInfo < 4; ++paintInfo) {
140         float end = logf(std::max(gradient->end[paintInfo], 0.01f));
141         float start = logf(std::max(gradient->start[paintInfo], 0.01f));
142         outData[paintInfo] = expf(start - (end + start) * a);
143     }
144 }
145
146 static CGFunctionRef getSharedFunctionRef(IOSGradientRef gradient, Interpolation interpolation)
147 {
148     CGFunctionRef function = nullptr;
149
150     static HashMap<IOSGradientRef, CGFunctionRef>* linearFunctionRefs;
151     static HashMap<IOSGradientRef, CGFunctionRef>* exponentialFunctionRefs;
152
153     if (interpolation == LinearInterpolation) {
154         if (!linearFunctionRefs)
155             linearFunctionRefs = new HashMap<IOSGradientRef, CGFunctionRef>;
156         else
157             function = linearFunctionRefs->get(gradient);
158     
159         if (!function) {
160             static struct CGFunctionCallbacks linearFunctionCallbacks =  { 0, interpolateLinearGradient, 0 };
161             linearFunctionRefs->set(gradient, function = CGFunctionCreate(gradient, 1, nullptr, 4, nullptr, &linearFunctionCallbacks));
162         }
163
164         return function;
165     }
166
167     if (!exponentialFunctionRefs)
168         exponentialFunctionRefs = new HashMap<IOSGradientRef, CGFunctionRef>;
169     else
170         function = exponentialFunctionRefs->get(gradient);
171
172     if (!function) {
173         static struct CGFunctionCallbacks exponentialFunctionCallbacks =  { 0, interpolateExponentialGradient, 0 };
174         exponentialFunctionRefs->set(gradient, function = CGFunctionCreate(gradient, 1, 0, 4, 0, &exponentialFunctionCallbacks));
175     }
176
177     return function;
178 }
179
180 static void drawAxialGradient(CGContextRef context, IOSGradientRef gradient, const FloatPoint& startPoint, const FloatPoint& stopPoint, Interpolation interpolation)
181 {
182     RetainPtr<CGShadingRef> shading = adoptCF(CGShadingCreateAxial(sRGBColorSpaceRef(), startPoint, stopPoint, getSharedFunctionRef(gradient, interpolation), false, false));
183     CGContextDrawShading(context, shading.get());
184 }
185
186 static void drawRadialGradient(CGContextRef context, IOSGradientRef gradient, const FloatPoint& startPoint, float startRadius, const FloatPoint& stopPoint, float stopRadius, Interpolation interpolation)
187 {
188     RetainPtr<CGShadingRef> shading = adoptCF(CGShadingCreateRadial(sRGBColorSpaceRef(), startPoint, startRadius, stopPoint, stopRadius, getSharedFunctionRef(gradient, interpolation), false, false));
189     CGContextDrawShading(context, shading.get());
190 }
191
192 enum IOSGradientType {
193     InsetGradient,
194     ShineGradient,
195     ShadeGradient,
196     ConvexGradient,
197     ConcaveGradient,
198     SliderTrackGradient,
199     ReadonlySliderTrackGradient,
200     SliderThumbOpaquePressedGradient,
201 };
202
203 static IOSGradientRef getInsetGradient()
204 {
205     static float end[4] = { 0 / 255.0, 0 / 255.0, 0 / 255.0, 0 };
206     static float start[4] = { 0 / 255.0, 0 / 255.0, 0 / 255.0, 0.2 };
207     static NeverDestroyed<IOSGradient> gradient(start, end);
208     return &gradient.get();
209 }
210
211 static IOSGradientRef getShineGradient()
212 {
213     static float end[4] = { 1, 1, 1, 0.8 };
214     static float start[4] = { 1, 1, 1, 0 };
215     static NeverDestroyed<IOSGradient> gradient(start, end);
216     return &gradient.get();
217 }
218
219 static IOSGradientRef getShadeGradient()
220 {
221     static float end[4] = { 178 / 255.0, 178 / 255.0, 178 / 255.0, 0.65 };
222     static float start[4] = { 252 / 255.0, 252 / 255.0, 252 / 255.0, 0.65 };
223     static NeverDestroyed<IOSGradient> gradient(start, end);
224     return &gradient.get();
225 }
226
227 static IOSGradientRef getConvexGradient()
228 {
229     static float end[4] = { 255 / 255.0, 255 / 255.0, 255 / 255.0, 0.05 };
230     static float start[4] = { 255 / 255.0, 255 / 255.0, 255 / 255.0, 0.43 };
231     static NeverDestroyed<IOSGradient> gradient(start, end);
232     return &gradient.get();
233 }
234
235 static IOSGradientRef getConcaveGradient()
236 {
237     static float end[4] = { 255 / 255.0, 255 / 255.0, 255 / 255.0, 0.46 };
238     static float start[4] = { 255 / 255.0, 255 / 255.0, 255 / 255.0, 0 };
239     static NeverDestroyed<IOSGradient> gradient(start, end);
240     return &gradient.get();
241 }
242
243 static IOSGradientRef getSliderTrackGradient()
244 {
245     static float end[4] = { 132 / 255.0, 132 / 255.0, 132 / 255.0, 1 };
246     static float start[4] = { 74 / 255.0, 77 / 255.0, 80 / 255.0, 1 };
247     static NeverDestroyed<IOSGradient> gradient(start, end);
248     return &gradient.get();
249 }
250
251 static IOSGradientRef getReadonlySliderTrackGradient()
252 {
253     static float end[4] = { 132 / 255.0, 132 / 255.0, 132 / 255.0, 0.4 };
254     static float start[4] = { 74 / 255.0, 77 / 255.0, 80 /255.0, 0.4 };
255     static NeverDestroyed<IOSGradient> gradient(start, end);
256     return &gradient.get();
257 }
258
259 static IOSGradientRef getSliderThumbOpaquePressedGradient()
260 {
261     static float end[4] = { 144 / 255.0, 144 / 255.0, 144 / 255.0, 1};
262     static float start[4] = { 55 / 255.0, 55 / 255.0, 55 / 255.0, 1 };
263     static NeverDestroyed<IOSGradient> gradient(start, end);
264     return &gradient.get();
265 }
266
267 static IOSGradientRef gradientWithName(IOSGradientType gradientType)
268 {
269     switch (gradientType) {
270     case InsetGradient:
271         return getInsetGradient();
272     case ShineGradient:
273         return getShineGradient();
274     case ShadeGradient:
275         return getShadeGradient();
276     case ConvexGradient:
277         return getConvexGradient();
278     case ConcaveGradient:
279         return getConcaveGradient();
280     case SliderTrackGradient:
281         return getSliderTrackGradient();
282     case ReadonlySliderTrackGradient:
283         return getReadonlySliderTrackGradient();
284     case SliderThumbOpaquePressedGradient:
285         return getSliderThumbOpaquePressedGradient();
286     }
287     ASSERT_NOT_REACHED();
288     return nullptr;
289 }
290
291 static void contentSizeCategoryDidChange(CFNotificationCenterRef, void*, CFStringRef name, const void*, CFDictionaryRef)
292 {
293     ASSERT_UNUSED(name, CFEqual(name, UIContentSizeCategoryDidChangeNotification));
294     WebThreadRun(^{
295         Page::updateStyleForAllPagesAfterGlobalChangeInEnvironment();
296     });
297 }
298
299 RenderThemeIOS::RenderThemeIOS()
300 {
301     CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), this, contentSizeCategoryDidChange, UIContentSizeCategoryDidChangeNotification, 0, CFNotificationSuspensionBehaviorDeliverImmediately);
302 }
303
304 RenderTheme& RenderTheme::singleton()
305 {
306     static NeverDestroyed<RenderThemeIOS> theme;
307     return theme;
308 }
309
310 static String& _contentSizeCategory()
311 {
312     static NeverDestroyed<String> _contentSizeCategory;
313     return _contentSizeCategory.get();
314 }
315
316 CFStringRef RenderThemeIOS::contentSizeCategory()
317 {
318     if (!_contentSizeCategory().isNull())
319         return (__bridge CFStringRef)static_cast<NSString*>(_contentSizeCategory());
320     return (CFStringRef)[[getUIApplicationClass() sharedApplication] preferredContentSizeCategory];
321 }
322
323 void RenderThemeIOS::setContentSizeCategory(const String& contentSizeCategory)
324 {
325     _contentSizeCategory() = contentSizeCategory;
326 }
327
328 const Color& RenderThemeIOS::shadowColor() const
329 {
330     static NeverDestroyed<Color> color(0.0f, 0.0f, 0.0f, 0.7f);
331     return color;
332 }
333
334 FloatRect RenderThemeIOS::addRoundedBorderClip(const RenderObject& box, GraphicsContext& context, const IntRect& rect)
335 {
336     // To fix inner border bleeding issues <rdar://problem/9812507>, we clip to the outer border and assert that
337     // the border is opaque or transparent, unless we're checked because checked radio/checkboxes show no bleeding.
338     auto& style = box.style();
339     RoundedRect border = isChecked(box) ? style.getRoundedInnerBorderFor(rect) : style.getRoundedBorderFor(rect);
340
341     if (border.isRounded())
342         context.clipRoundedRect(FloatRoundedRect(border));
343     else
344         context.clip(border.rect());
345
346     if (isChecked(box)) {
347         ASSERT(style.visitedDependentColor(CSSPropertyBorderTopColor).alpha() % 255 == 0);
348         ASSERT(style.visitedDependentColor(CSSPropertyBorderRightColor).alpha() % 255 == 0);
349         ASSERT(style.visitedDependentColor(CSSPropertyBorderBottomColor).alpha() % 255 == 0);
350         ASSERT(style.visitedDependentColor(CSSPropertyBorderLeftColor).alpha() % 255 == 0);
351     }
352
353     return border.rect();
354 }
355
356 void RenderThemeIOS::adjustCheckboxStyle(StyleResolver&, RenderStyle& style, const Element*) const
357 {
358     if (!style.width().isIntrinsicOrAuto() && !style.height().isAuto())
359         return;
360
361     int size = std::max(style.computedFontPixelSize(), 10U);
362     style.setWidth({ size, Fixed });
363     style.setHeight({ size, Fixed });
364 }
365
366 static CGPoint shortened(CGPoint start, CGPoint end, float width)
367 {
368     float x = end.x - start.x;
369     float y = end.y - start.y;
370     float ratio = (!x && !y) ? 0 : width / sqrtf(x * x + y * y);
371     return CGPointMake(start.x + x * ratio, start.y + y * ratio);
372 }
373
374 static void drawJoinedLines(CGContextRef context, const Vector<CGPoint>& points, CGLineCap lineCap, float lineWidth, Color strokeColor)
375 {
376     CGContextSetLineWidth(context, lineWidth);
377     CGContextSetStrokeColorWithColor(context, cachedCGColor(strokeColor));
378     CGContextSetShouldAntialias(context, true);
379     CGContextBeginPath(context);
380     CGContextSetLineCap(context, lineCap);
381     CGContextMoveToPoint(context, points[0].x, points[0].y);
382     
383     for (unsigned i = 1; i < points.size(); ++i)
384         CGContextAddLineToPoint(context, points[i].x, points[i].y);
385
386     CGContextStrokePath(context);
387 }
388
389 bool RenderThemeIOS::paintCheckboxDecorations(const RenderObject& box, const PaintInfo& paintInfo, const IntRect& rect)
390 {
391     GraphicsContextStateSaver stateSaver(paintInfo.context());
392     FloatRect clip = addRoundedBorderClip(box, paintInfo.context(), rect);
393
394     float width = clip.width();
395     float height = clip.height();
396
397     bool checked = isChecked(box);
398     bool indeterminate = isIndeterminate(box);
399
400     CGContextRef cgContext = paintInfo.context().platformContext();
401     if (!checked && !indeterminate) {
402         FloatPoint bottomCenter(clip.x() + clip.width() / 2.0f, clip.maxY());
403         drawAxialGradient(cgContext, gradientWithName(ShadeGradient), clip.location(), FloatPoint(clip.x(), clip.maxY()), LinearInterpolation);
404         drawRadialGradient(cgContext, gradientWithName(ShineGradient), bottomCenter, 0, bottomCenter, sqrtf((width * width) / 4.0f + height * height), ExponentialInterpolation);
405         return false;
406     }
407
408     drawAxialGradient(cgContext, gradientWithName(ConcaveGradient), clip.location(), FloatPoint(clip.x(), clip.maxY()), LinearInterpolation);
409
410     static const float thicknessRatio = 2 / 14.0;
411     static const CGSize size = { 14.0f, 14.0f };
412     float lineWidth = std::min(width, height) * 2.0f * thicknessRatio;
413
414     Vector<CGPoint> line;
415     Vector<CGPoint> shadow;
416
417     if (checked) {
418         static const CGPoint pathRatios[3] = {
419             { 2.5f / size.width, 7.5f / size.height },
420             { 5.5f / size.width, 10.5f / size.height },
421             { 11.5f / size.width, 2.5f / size.height }
422         };
423
424         line = {
425             CGPointMake(clip.x() + width * pathRatios[0].x, clip.y() + height * pathRatios[0].y),
426             CGPointMake(clip.x() + width * pathRatios[1].x, clip.y() + height * pathRatios[1].y),
427             CGPointMake(clip.x() + width * pathRatios[2].x, clip.y() + height * pathRatios[2].y)
428         };
429
430         shadow = {
431             shortened(line[0], line[1], lineWidth / 4.0f),
432             line[1],
433             shortened(line[2], line[1], lineWidth / 4.0f)
434         };
435     } else if (indeterminate) {
436         line = {
437             CGPointMake(clip.x() + 3.5, clip.center().y()),
438             CGPointMake(clip.maxX() - 3.5, clip.center().y())
439         };
440
441         shadow = {
442             shortened(line[0], line[1], lineWidth / 4.0f),
443             shortened(line[1], line[0], lineWidth / 4.0f)
444         };
445     }
446
447     lineWidth = std::max<float>(lineWidth, 1);
448     drawJoinedLines(cgContext, shadow, kCGLineCapSquare, lineWidth, Color(0.0f, 0.0f, 0.0f, 0.7f));
449
450     lineWidth = std::max<float>(std::min(clip.width(), clip.height()) * thicknessRatio, 1);
451     drawJoinedLines(cgContext, line, kCGLineCapButt, lineWidth, Color(1.0f, 1.0f, 1.0f, 240 / 255.0f));
452
453     return false;
454 }
455
456 int RenderThemeIOS::baselinePosition(const RenderBox& box) const
457 {
458     if (box.style().appearance() == CheckboxPart || box.style().appearance() == RadioPart)
459         return box.marginTop() + box.height() - 2; // The baseline is 2px up from the bottom of the checkbox/radio in AppKit.
460     if (box.style().appearance() == MenulistPart)
461         return box.marginTop() + box.height() - 5; // This is to match AppKit. There might be a better way to calculate this though.
462     return RenderTheme::baselinePosition(box);
463 }
464
465 bool RenderThemeIOS::isControlStyled(const RenderStyle& style, const BorderData& border, const FillLayer& background, const Color& backgroundColor) const
466 {
467     // Buttons and MenulistButtons are styled if they contain a background image.
468     if (style.appearance() == PushButtonPart || style.appearance() == MenulistButtonPart)
469         return !style.visitedDependentColor(CSSPropertyBackgroundColor).isVisible() || style.backgroundLayers().hasImage();
470
471     if (style.appearance() == TextFieldPart || style.appearance() == TextAreaPart)
472         return style.backgroundLayers() != background;
473
474     return RenderTheme::isControlStyled(style, border, background, backgroundColor);
475 }
476
477 void RenderThemeIOS::adjustRadioStyle(StyleResolver&, RenderStyle& style, const Element*) const
478 {
479     if (!style.width().isIntrinsicOrAuto() && !style.height().isAuto())
480         return;
481
482     int size = std::max(style.computedFontPixelSize(), 10U);
483     style.setWidth({ size, Fixed });
484     style.setHeight({ size, Fixed });
485     style.setBorderRadius({ size / 2, size / 2 });
486 }
487
488 bool RenderThemeIOS::paintRadioDecorations(const RenderObject& box, const PaintInfo& paintInfo, const IntRect& rect)
489 {
490     GraphicsContextStateSaver stateSaver(paintInfo.context());
491     FloatRect clip = addRoundedBorderClip(box, paintInfo.context(), rect);
492
493     CGContextRef cgContext = paintInfo.context().platformContext();
494     if (isChecked(box)) {
495         drawAxialGradient(cgContext, gradientWithName(ConcaveGradient), clip.location(), FloatPoint(clip.x(), clip.maxY()), LinearInterpolation);
496
497         // The inner circle is 6 / 14 the size of the surrounding circle, 
498         // leaving 8 / 14 around it. (8 / 14) / 2 = 2 / 7.
499
500         static const float InnerInverseRatio = 2 / 7.0;
501
502         clip.inflateX(-clip.width() * InnerInverseRatio);
503         clip.inflateY(-clip.height() * InnerInverseRatio);
504
505         paintInfo.context().drawRaisedEllipse(clip, Color::white, shadowColor());
506
507         FloatSize radius(clip.width() / 2.0f, clip.height() / 2.0f);
508         paintInfo.context().clipRoundedRect(FloatRoundedRect(clip, radius, radius, radius, radius));
509     }
510     FloatPoint bottomCenter(clip.x() + clip.width() / 2.0, clip.maxY());
511     drawAxialGradient(cgContext, gradientWithName(ShadeGradient), clip.location(), FloatPoint(clip.x(), clip.maxY()), LinearInterpolation);
512     drawRadialGradient(cgContext, gradientWithName(ShineGradient), bottomCenter, 0, bottomCenter, std::max(clip.width(), clip.height()), ExponentialInterpolation);
513     return false;
514 }
515
516 bool RenderThemeIOS::paintTextFieldDecorations(const RenderObject& box, const PaintInfo& paintInfo, const FloatRect& rect)
517 {
518     auto& style = box.style();
519     FloatPoint point(rect.x() + style.borderLeftWidth(), rect.y() + style.borderTopWidth());
520
521     GraphicsContextStateSaver stateSaver(paintInfo.context());
522
523     paintInfo.context().clipRoundedRect(style.getRoundedBorderFor(LayoutRect(rect)).pixelSnappedRoundedRectForPainting(box.document().deviceScaleFactor()));
524
525     // This gradient gets drawn black when printing.
526     // Do not draw the gradient if there is no visible top border.
527     bool topBorderIsInvisible = !style.hasBorder() || !style.borderTopWidth() || style.borderTopIsTransparent();
528     if (!box.view().printing() && !topBorderIsInvisible)
529         drawAxialGradient(paintInfo.context().platformContext(), gradientWithName(InsetGradient), point, FloatPoint(CGPointMake(point.x(), point.y() + 3.0f)), LinearInterpolation);
530     return false;
531 }
532
533 bool RenderThemeIOS::paintTextAreaDecorations(const RenderObject& box, const PaintInfo& paintInfo, const FloatRect& rect)
534 {
535     return paintTextFieldDecorations(box, paintInfo, rect);
536 }
537
538 const int MenuListMinHeight = 15;
539
540 const float MenuListBaseHeight = 20;
541 const float MenuListBaseFontSize = 11;
542
543 const float MenuListArrowWidth = 7;
544 const float MenuListArrowHeight = 6;
545 const float MenuListButtonPaddingAfter = 19;
546
547 LengthBox RenderThemeIOS::popupInternalPaddingBox(const RenderStyle& style) const
548 {
549     if (style.appearance() == MenulistButtonPart) {
550         if (style.direction() == TextDirection::RTL)
551             return { 0, 0, 0, static_cast<int>(MenuListButtonPaddingAfter + style.borderTopWidth()) };
552         return { 0, static_cast<int>(MenuListButtonPaddingAfter + style.borderTopWidth()), 0, 0 };
553     }
554     return { 0, 0, 0, 0 };
555 }
556
557 void RenderThemeIOS::adjustRoundBorderRadius(RenderStyle& style, RenderBox& box)
558 {
559     if (style.appearance() == NoControlPart || style.backgroundLayers().hasImage())
560         return;
561
562     // FIXME: We should not be relying on border radius for the appearance of our controls <rdar://problem/7675493>.
563     style.setBorderRadius({ { std::min(box.width(), box.height()) / 2, Fixed }, { box.height() / 2, Fixed } });
564 }
565
566 static void applyCommonButtonPaddingToStyle(RenderStyle& style, const Element& element)
567 {
568     Document& document = element.document();
569     RefPtr<CSSPrimitiveValue> emSize = CSSPrimitiveValue::create(0.5, CSSPrimitiveValue::CSS_EMS);
570     int pixels = emSize->computeLength<int>(CSSToLengthConversionData(&style, document.renderStyle(), document.renderView(), document.frame()->pageZoomFactor()));
571     style.setPaddingBox(LengthBox(0, pixels, 0, pixels));
572 }
573
574 static void adjustSelectListButtonStyle(RenderStyle& style, const Element& element)
575 {
576     // Enforce "padding: 0 0.5em".
577     applyCommonButtonPaddingToStyle(style, element);
578
579     // Enforce "line-height: normal".
580     style.setLineHeight(Length(-100.0, Percent));
581 }
582     
583 class RenderThemeMeasureTextClient : public MeasureTextClient {
584 public:
585     RenderThemeMeasureTextClient(const FontCascade& font, const RenderStyle& style)
586         : m_font(font)
587         , m_style(style)
588     {
589     }
590     float measureText(const String& string) const override
591     {
592         TextRun run = RenderBlock::constructTextRun(string, m_style);
593         return m_font.width(run);
594     }
595 private:
596     const FontCascade& m_font;
597     const RenderStyle& m_style;
598 };
599
600 static void adjustInputElementButtonStyle(RenderStyle& style, const HTMLInputElement& inputElement)
601 {
602     // Always Enforce "padding: 0 0.5em".
603     applyCommonButtonPaddingToStyle(style, inputElement);
604
605     // Don't adjust the style if the width is specified.
606     if (style.width().isFixed() && style.width().value() > 0)
607         return;
608
609     // Don't adjust for unsupported date input types.
610     DateComponents::Type dateType = inputElement.dateType();
611     if (dateType == DateComponents::Invalid || dateType == DateComponents::Week)
612         return;
613
614     // Enforce the width and set the box-sizing to content-box to not conflict with the padding.
615     FontCascade font = style.fontCascade();
616     
617     float maximumWidth = localizedDateCache().maximumWidthForDateType(dateType, font, RenderThemeMeasureTextClient(font, style));
618
619     ASSERT(maximumWidth >= 0);
620
621     if (maximumWidth > 0) {
622         int width = static_cast<int>(maximumWidth + MenuListButtonPaddingAfter);
623         style.setWidth(Length(width, Fixed));
624         style.setBoxSizing(BoxSizing::ContentBox);
625     }
626 }
627
628 void RenderThemeIOS::adjustMenuListButtonStyle(StyleResolver&, RenderStyle& style, const Element* element) const
629 {
630     // Set the min-height to be at least MenuListMinHeight.
631     if (style.height().isAuto())
632         style.setMinHeight(Length(std::max(MenuListMinHeight, static_cast<int>(MenuListBaseHeight / MenuListBaseFontSize * style.fontDescription().computedSize())), Fixed));
633     else
634         style.setMinHeight(Length(MenuListMinHeight, Fixed));
635
636     if (!element)
637         return;
638
639     // Enforce some default styles in the case that this is a non-multiple <select> element,
640     // or a date input. We don't force these if this is just an element with
641     // "-webkit-appearance: menulist-button".
642     if (is<HTMLSelectElement>(*element) && !element->hasAttributeWithoutSynchronization(HTMLNames::multipleAttr))
643         adjustSelectListButtonStyle(style, *element);
644     else if (is<HTMLInputElement>(*element))
645         adjustInputElementButtonStyle(style, downcast<HTMLInputElement>(*element));
646 }
647
648 bool RenderThemeIOS::paintMenuListButtonDecorations(const RenderBox& box, const PaintInfo& paintInfo, const FloatRect& rect)
649 {
650     auto& style = box.style();
651     bool isRTL = style.direction() == TextDirection::RTL;
652     float borderTopWidth = style.borderTopWidth();
653     FloatRect clip(rect.x() + style.borderLeftWidth(), rect.y() + style.borderTopWidth(), rect.width() - style.borderLeftWidth() - style.borderRightWidth(), rect.height() - style.borderTopWidth() - style.borderBottomWidth());
654     CGContextRef cgContext = paintInfo.context().platformContext();
655
656     float adjustLeft = 0.5;
657     float adjustRight = 0.5;
658     float adjustTop = 0.5;
659     float adjustBottom = 0.5;
660
661     // Paint title portion.
662     {
663         float leftInset = isRTL ? MenuListButtonPaddingAfter : 0;
664         FloatRect titleClip(clip.x() + leftInset - adjustLeft, clip.y() - adjustTop, clip.width() - MenuListButtonPaddingAfter + adjustLeft, clip.height() + adjustTop + adjustBottom);
665
666         GraphicsContextStateSaver stateSaver(paintInfo.context());
667
668         FloatSize topLeftRadius;
669         FloatSize topRightRadius;
670         FloatSize bottomLeftRadius;
671         FloatSize bottomRightRadius;
672
673         if (isRTL) {
674             topRightRadius = FloatSize(valueForLength(style.borderTopRightRadius().width, rect.width()) - style.borderRightWidth(), valueForLength(style.borderTopRightRadius().height, rect.height()) - style.borderTopWidth());
675             bottomRightRadius = FloatSize(valueForLength(style.borderBottomRightRadius().width, rect.width()) - style.borderRightWidth(), valueForLength(style.borderBottomRightRadius().height, rect.height()) - style.borderBottomWidth());
676         } else {
677             topLeftRadius = FloatSize(valueForLength(style.borderTopLeftRadius().width, rect.width()) - style.borderLeftWidth(), valueForLength(style.borderTopLeftRadius().height, rect.height()) - style.borderTopWidth());
678             bottomLeftRadius = FloatSize(valueForLength(style.borderBottomLeftRadius().width, rect.width()) - style.borderLeftWidth(), valueForLength(style.borderBottomLeftRadius().height, rect.height()) - style.borderBottomWidth());
679         }
680
681         paintInfo.context().clipRoundedRect(FloatRoundedRect(titleClip,
682             topLeftRadius, topRightRadius,
683             bottomLeftRadius, bottomRightRadius));
684
685         drawAxialGradient(cgContext, gradientWithName(ShadeGradient), titleClip.location(), FloatPoint(titleClip.x(), titleClip.maxY()), LinearInterpolation);
686         drawAxialGradient(cgContext, gradientWithName(ShineGradient), FloatPoint(titleClip.x(), titleClip.maxY()), titleClip.location(), ExponentialInterpolation);
687     }
688
689     // Draw the separator after the initial padding.
690
691     float separatorPosition = isRTL ? (clip.x() + MenuListButtonPaddingAfter) : (clip.maxX() - MenuListButtonPaddingAfter);
692
693     box.drawLineForBoxSide(paintInfo.context(), FloatRect(FloatPoint(separatorPosition - borderTopWidth, clip.y()), FloatPoint(separatorPosition, clip.maxY())), BSRight, style.visitedDependentColor(CSSPropertyBorderTopColor), style.borderTopStyle(), 0, 0);
694
695     FloatRect buttonClip;
696     if (isRTL)
697         buttonClip = FloatRect(clip.x() - adjustTop, clip.y() - adjustTop, MenuListButtonPaddingAfter + adjustTop + adjustLeft, clip.height() + adjustTop + adjustBottom);
698     else
699         buttonClip = FloatRect(separatorPosition - adjustTop, clip.y() - adjustTop, MenuListButtonPaddingAfter + adjustTop + adjustRight, clip.height() + adjustTop + adjustBottom);
700
701     // Now paint the button portion.
702     {
703         GraphicsContextStateSaver stateSaver(paintInfo.context());
704
705         FloatSize topLeftRadius;
706         FloatSize topRightRadius;
707         FloatSize bottomLeftRadius;
708         FloatSize bottomRightRadius;
709
710         if (isRTL) {
711             topLeftRadius = FloatSize(valueForLength(style.borderTopLeftRadius().width, rect.width()) - style.borderLeftWidth(), valueForLength(style.borderTopLeftRadius().height, rect.height()) - style.borderTopWidth());
712             bottomLeftRadius = FloatSize(valueForLength(style.borderBottomLeftRadius().width, rect.width()) - style.borderLeftWidth(), valueForLength(style.borderBottomLeftRadius().height, rect.height()) - style.borderBottomWidth());
713         } else {
714             topRightRadius = FloatSize(valueForLength(style.borderTopRightRadius().width, rect.width()) - style.borderRightWidth(), valueForLength(style.borderTopRightRadius().height, rect.height()) - style.borderTopWidth());
715             bottomRightRadius = FloatSize(valueForLength(style.borderBottomRightRadius().width, rect.width()) - style.borderRightWidth(), valueForLength(style.borderBottomRightRadius().height, rect.height()) - style.borderBottomWidth());
716         }
717
718         paintInfo.context().clipRoundedRect(FloatRoundedRect(buttonClip,
719             topLeftRadius, topRightRadius,
720             bottomLeftRadius, bottomRightRadius));
721
722         paintInfo.context().fillRect(buttonClip, style.visitedDependentColor(CSSPropertyBorderTopColor));
723
724         drawAxialGradient(cgContext, gradientWithName(isFocused(box) && !isReadOnlyControl(box) ? ConcaveGradient : ConvexGradient), buttonClip.location(), FloatPoint(buttonClip.x(), buttonClip.maxY()), LinearInterpolation);
725     }
726
727     // Paint Indicators.
728
729     if (box.isMenuList() && downcast<HTMLSelectElement>(box.element())->multiple()) {
730         int size = 2;
731         int count = 3;
732         int padding = 3;
733
734         FloatRect ellipse(buttonClip.x() + (buttonClip.width() - count * (size + padding) + padding) / 2.0, buttonClip.maxY() - 10.0, size, size);
735
736         for (int i = 0; i < count; ++i) {
737             paintInfo.context().drawRaisedEllipse(ellipse, Color::white, Color(0.0f, 0.0f, 0.0f, 0.5f));
738             ellipse.move(size + padding, 0);
739         }
740     }  else {
741         float centerX = floorf(buttonClip.x() + buttonClip.width() / 2.0) - 0.5;
742         float centerY = floorf(buttonClip.y() + buttonClip.height() * 3.0 / 8.0);
743
744         Vector<FloatPoint> arrow = {
745             { centerX - MenuListArrowWidth / 2, centerY },
746             { centerX + MenuListArrowWidth / 2, centerY },
747             { centerX, centerY + MenuListArrowHeight }
748         };
749
750         Vector<FloatPoint> shadow = {
751             { arrow[0].x(), arrow[0].y() + 1 },
752             { arrow[1].x(), arrow[1].y() + 1 },
753             { arrow[2].x(), arrow[2].y() + 1 }
754         };
755
756         float opacity = isReadOnlyControl(box) ? 0.2 : 0.5;
757         paintInfo.context().setStrokeColor(Color(0.0f, 0.0f, 0.0f, opacity));
758         paintInfo.context().setFillColor(Color(0.0f, 0.0f, 0.0f, opacity));
759         paintInfo.context().drawPath(Path::polygonPathFromPoints(shadow));
760
761         paintInfo.context().setStrokeColor(Color::white);
762         paintInfo.context().setFillColor(Color::white);
763         paintInfo.context().drawPath(Path::polygonPathFromPoints(arrow));
764     }
765
766     return false;
767 }
768
769 const CGFloat kTrackThickness = 4.0;
770 const CGFloat kTrackRadius = kTrackThickness / 2.0;
771 const int kDefaultSliderThumbSize = 16;
772
773 void RenderThemeIOS::adjustSliderTrackStyle(StyleResolver& selector, RenderStyle& style, const Element* element) const
774 {
775     RenderTheme::adjustSliderTrackStyle(selector, style, element);
776
777     // FIXME: We should not be relying on border radius for the appearance of our controls <rdar://problem/7675493>.
778     int radius = static_cast<int>(kTrackRadius);
779     style.setBorderRadius({ { radius, Fixed }, { radius, Fixed } });
780 }
781
782 bool RenderThemeIOS::paintSliderTrack(const RenderObject& box, const PaintInfo& paintInfo, const IntRect& rect)
783 {
784     IntRect trackClip = rect;
785     auto& style = box.style();
786
787     bool isHorizontal = true;
788     switch (style.appearance()) {
789     case SliderHorizontalPart:
790         isHorizontal = true;
791         // Inset slightly so the thumb covers the edge.
792         if (trackClip.width() > 2) {
793             trackClip.setWidth(trackClip.width() - 2);
794             trackClip.setX(trackClip.x() + 1);
795         }
796         trackClip.setHeight(static_cast<int>(kTrackThickness));
797         trackClip.setY(rect.y() + rect.height() / 2 - kTrackThickness / 2);
798         break;
799     case SliderVerticalPart:
800         isHorizontal = false;
801         // Inset slightly so the thumb covers the edge.
802         if (trackClip.height() > 2) {
803             trackClip.setHeight(trackClip.height() - 2);
804             trackClip.setY(trackClip.y() + 1);
805         }
806         trackClip.setWidth(kTrackThickness);
807         trackClip.setX(rect.x() + rect.width() / 2 - kTrackThickness / 2);
808         break;
809     default:
810         ASSERT_NOT_REACHED();
811     }
812
813     ASSERT(trackClip.width() >= 0);
814     ASSERT(trackClip.height() >= 0);
815     CGFloat cornerWidth = trackClip.width() < kTrackThickness ? trackClip.width() / 2.0f : kTrackRadius;
816     CGFloat cornerHeight = trackClip.height() < kTrackThickness ? trackClip.height() / 2.0f : kTrackRadius;
817
818     bool readonly = isReadOnlyControl(box);
819
820 #if ENABLE(DATALIST_ELEMENT)
821     paintSliderTicks(box, paintInfo, trackClip);
822 #endif
823
824     // Draw the track gradient.
825     {
826         GraphicsContextStateSaver stateSaver(paintInfo.context());
827
828         IntSize cornerSize(cornerWidth, cornerHeight);
829         FloatRoundedRect innerBorder(trackClip, cornerSize, cornerSize, cornerSize, cornerSize);
830         paintInfo.context().clipRoundedRect(innerBorder);
831
832         CGContextRef cgContext = paintInfo.context().platformContext();
833         IOSGradientRef gradient = readonly ? gradientWithName(ReadonlySliderTrackGradient) : gradientWithName(SliderTrackGradient);
834         if (isHorizontal)
835             drawAxialGradient(cgContext, gradient, trackClip.location(), FloatPoint(trackClip.x(), trackClip.maxY()), LinearInterpolation);
836         else
837             drawAxialGradient(cgContext, gradient, trackClip.location(), FloatPoint(trackClip.maxX(), trackClip.y()), LinearInterpolation);
838     }
839
840     // Draw the track border.
841     {
842         GraphicsContextStateSaver stateSaver(paintInfo.context());
843
844         CGContextRef cgContext = paintInfo.context().platformContext();
845         if (readonly)
846             paintInfo.context().setStrokeColor(Color(178, 178, 178));
847         else
848             paintInfo.context().setStrokeColor(Color(76, 76, 76));
849
850         RetainPtr<CGMutablePathRef> roundedRectPath = adoptCF(CGPathCreateMutable());
851         CGPathAddRoundedRect(roundedRectPath.get(), 0, trackClip, cornerWidth, cornerHeight);
852         CGContextAddPath(cgContext, roundedRectPath.get());
853         CGContextSetLineWidth(cgContext, 1);
854         CGContextStrokePath(cgContext);
855     }
856
857     return false;
858 }
859
860 void RenderThemeIOS::adjustSliderThumbSize(RenderStyle& style, const Element*) const
861 {
862     if (style.appearance() != SliderThumbHorizontalPart && style.appearance() != SliderThumbVerticalPart)
863         return;
864
865     // Enforce "border-radius: 50%".
866     style.setBorderRadius({ { 50, Percent }, { 50, Percent } });
867
868     // Enforce a 16x16 size if no size is provided.
869     if (style.width().isIntrinsicOrAuto() || style.height().isAuto()) {
870         style.setWidth({ kDefaultSliderThumbSize, Fixed });
871         style.setHeight({ kDefaultSliderThumbSize, Fixed });
872     }
873 }
874
875 bool RenderThemeIOS::paintSliderThumbDecorations(const RenderObject& box, const PaintInfo& paintInfo, const IntRect& rect)
876 {
877     GraphicsContextStateSaver stateSaver(paintInfo.context());
878     FloatRect clip = addRoundedBorderClip(box, paintInfo.context(), rect);
879
880     CGContextRef cgContext = paintInfo.context().platformContext();
881     FloatPoint bottomCenter(clip.x() + clip.width() / 2.0f, clip.maxY());
882     if (isPressed(box))
883         drawAxialGradient(cgContext, gradientWithName(SliderThumbOpaquePressedGradient), clip.location(), FloatPoint(clip.x(), clip.maxY()), LinearInterpolation);
884     else {
885         drawAxialGradient(cgContext, gradientWithName(ShadeGradient), clip.location(), FloatPoint(clip.x(), clip.maxY()), LinearInterpolation);
886         drawRadialGradient(cgContext, gradientWithName(ShineGradient), bottomCenter, 0.0f, bottomCenter, std::max(clip.width(), clip.height()), ExponentialInterpolation);
887     }
888
889     return false;
890 }
891
892 Seconds RenderThemeIOS::animationRepeatIntervalForProgressBar(RenderProgress&) const
893 {
894     return 0_s;
895 }
896
897 Seconds RenderThemeIOS::animationDurationForProgressBar(RenderProgress&) const
898 {
899     return 0_s;
900 }
901
902 bool RenderThemeIOS::paintProgressBar(const RenderObject& renderer, const PaintInfo& paintInfo, const IntRect& rect)
903 {
904     if (!is<RenderProgress>(renderer))
905         return true;
906
907     const int progressBarHeight = 9;
908     const float verticalOffset = (rect.height() - progressBarHeight) / 2.0;
909
910     GraphicsContextStateSaver stateSaver(paintInfo.context());
911     if (rect.width() < 10 || rect.height() < 9) {
912         // The rect is smaller than the standard progress bar. We clip to the element's rect to avoid
913         // leaking pixels outside the repaint rect.
914         paintInfo.context().clip(rect);
915     }
916
917     // 1) Draw the progress bar track.
918     // 1.1) Draw the white background with grey gradient border.
919     GraphicsContext& context = paintInfo.context();
920     context.setStrokeThickness(0.68);
921     context.setStrokeStyle(SolidStroke);
922
923     const float verticalRenderingPosition = rect.y() + verticalOffset;
924     RefPtr<Gradient> strokeGradient = Gradient::create(Gradient::LinearData { FloatPoint(rect.x(), verticalRenderingPosition), FloatPoint(rect.x(), verticalRenderingPosition + progressBarHeight - 1) });
925     strokeGradient->addColorStop(0.0, Color(0x8d, 0x8d, 0x8d));
926     strokeGradient->addColorStop(0.45, Color(0xee, 0xee, 0xee));
927     strokeGradient->addColorStop(0.55, Color(0xee, 0xee, 0xee));
928     strokeGradient->addColorStop(1.0, Color(0x8d, 0x8d, 0x8d));
929     context.setStrokeGradient(strokeGradient.releaseNonNull());
930
931     context.setFillColor(Color(255, 255, 255));
932
933     Path trackPath;
934     FloatRect trackRect(rect.x() + 0.25, verticalRenderingPosition + 0.25, rect.width() - 0.5, progressBarHeight - 0.5);
935     FloatSize roundedCornerRadius(5, 4);
936     trackPath.addRoundedRect(trackRect, roundedCornerRadius);
937     context.drawPath(trackPath);
938
939     // 1.2) Draw top gradient on the upper half. It is supposed to overlay the fill from the background and darker the stroked path.
940     FloatRect border(rect.x(), rect.y() + verticalOffset, rect.width(), progressBarHeight);
941     paintInfo.context().clipRoundedRect(FloatRoundedRect(border, roundedCornerRadius, roundedCornerRadius, roundedCornerRadius, roundedCornerRadius));
942
943     float upperGradientHeight = progressBarHeight / 2.;
944     RefPtr<Gradient> upperGradient = Gradient::create(Gradient::LinearData { FloatPoint(rect.x(), verticalRenderingPosition + 0.5), FloatPoint(rect.x(), verticalRenderingPosition + upperGradientHeight - 1.5) });
945     upperGradient->addColorStop(0.0, Color(133, 133, 133, 188));
946     upperGradient->addColorStop(1.0, Color(18, 18, 18, 51));
947     context.setFillGradient(upperGradient.releaseNonNull());
948
949     context.fillRect(FloatRect(rect.x(), verticalRenderingPosition, rect.width(), upperGradientHeight));
950
951     const auto& renderProgress = downcast<RenderProgress>(renderer);
952     if (renderProgress.isDeterminate()) {
953         // 2) Draw the progress bar.
954         double position = clampTo(renderProgress.position(), 0.0, 1.0);
955         double barWidth = position * rect.width();
956         RefPtr<Gradient> barGradient = Gradient::create(Gradient::LinearData { FloatPoint(rect.x(), verticalRenderingPosition + 0.5), FloatPoint(rect.x(), verticalRenderingPosition + progressBarHeight - 1) });
957         barGradient->addColorStop(0.0, Color(195, 217, 247));
958         barGradient->addColorStop(0.45, Color(118, 164, 228));
959         barGradient->addColorStop(0.49, Color(118, 164, 228));
960         barGradient->addColorStop(0.51, Color(36, 114, 210));
961         barGradient->addColorStop(0.55, Color(36, 114, 210));
962         barGradient->addColorStop(1.0, Color(57, 142, 244));
963         context.setFillGradient(barGradient.releaseNonNull());
964
965         RefPtr<Gradient> barStrokeGradient = Gradient::create(Gradient::LinearData { FloatPoint(rect.x(), verticalRenderingPosition), FloatPoint(rect.x(), verticalRenderingPosition + progressBarHeight - 1) });
966         barStrokeGradient->addColorStop(0.0, Color(95, 107, 183));
967         barStrokeGradient->addColorStop(0.5, Color(66, 106, 174, 240));
968         barStrokeGradient->addColorStop(1.0, Color(38, 104, 166));
969         context.setStrokeGradient(barStrokeGradient.releaseNonNull());
970
971         Path barPath;
972         int left = rect.x();
973         if (!renderProgress.style().isLeftToRightDirection())
974             left = rect.maxX() - barWidth;
975         FloatRect barRect(left + 0.25, verticalRenderingPosition + 0.25, std::max(barWidth - 0.5, 0.0), progressBarHeight - 0.5);
976         barPath.addRoundedRect(barRect, roundedCornerRadius);
977         context.drawPath(barPath);
978     }
979
980     return false;
981 }
982
983 #if ENABLE(DATALIST_ELEMENT)
984 IntSize RenderThemeIOS::sliderTickSize() const
985 {
986     // FIXME: <rdar://problem/12271791> MERGEBOT: Correct values for slider tick of <input type="range"> elements (requires ENABLE_DATALIST_ELEMENT)
987     return IntSize(1, 3);
988 }
989
990 int RenderThemeIOS::sliderTickOffsetFromTrackCenter() const
991 {
992     // FIXME: <rdar://problem/12271791> MERGEBOT: Correct values for slider tick of <input type="range"> elements (requires ENABLE_DATALIST_ELEMENT)
993     return -9;
994 }
995 #endif
996
997 void RenderThemeIOS::adjustSearchFieldStyle(StyleResolver& selector, RenderStyle& style, const Element* element) const
998 {
999     RenderTheme::adjustSearchFieldStyle(selector, style, element);
1000
1001     if (!element)
1002         return;
1003
1004     if (!style.hasBorder())
1005         return;
1006
1007     RenderBox* box = element->renderBox();
1008     if (!box)
1009         return;
1010
1011     adjustRoundBorderRadius(style, *box);
1012 }
1013
1014 bool RenderThemeIOS::paintSearchFieldDecorations(const RenderObject& box, const PaintInfo& paintInfo, const IntRect& rect)
1015 {
1016     return paintTextFieldDecorations(box, paintInfo, rect);
1017 }
1018
1019 void RenderThemeIOS::adjustButtonStyle(StyleResolver& selector, RenderStyle& style, const Element* element) const
1020 {
1021     RenderTheme::adjustButtonStyle(selector, style, element);
1022
1023     // Set padding: 0 1.0em; on buttons.
1024     // CSSPrimitiveValue::computeLengthInt only needs the element's style to calculate em lengths.
1025     // Since the element might not be in a document, just pass nullptr for the root element style
1026     // and the render view.
1027     RefPtr<CSSPrimitiveValue> emSize = CSSPrimitiveValue::create(1.0, CSSPrimitiveValue::CSS_EMS);
1028     int pixels = emSize->computeLength<int>(CSSToLengthConversionData(&style, nullptr, nullptr, 1.0, false));
1029     style.setPaddingBox(LengthBox(0, pixels, 0, pixels));
1030
1031     if (!element)
1032         return;
1033
1034     RenderBox* box = element->renderBox();
1035     if (!box)
1036         return;
1037
1038     adjustRoundBorderRadius(style, *box);
1039 }
1040
1041 bool RenderThemeIOS::paintButtonDecorations(const RenderObject& box, const PaintInfo& paintInfo, const IntRect& rect)
1042 {
1043     return paintPushButtonDecorations(box, paintInfo, rect);
1044 }
1045
1046 bool RenderThemeIOS::paintPushButtonDecorations(const RenderObject& box, const PaintInfo& paintInfo, const IntRect& rect)
1047 {
1048     GraphicsContextStateSaver stateSaver(paintInfo.context());
1049     FloatRect clip = addRoundedBorderClip(box, paintInfo.context(), rect);
1050
1051     CGContextRef cgContext = paintInfo.context().platformContext();
1052     if (box.style().visitedDependentColor(CSSPropertyBackgroundColor).isDark())
1053         drawAxialGradient(cgContext, gradientWithName(ConvexGradient), clip.location(), FloatPoint(clip.x(), clip.maxY()), LinearInterpolation);
1054     else {
1055         drawAxialGradient(cgContext, gradientWithName(ShadeGradient), clip.location(), FloatPoint(clip.x(), clip.maxY()), LinearInterpolation);
1056         drawAxialGradient(cgContext, gradientWithName(ShineGradient), FloatPoint(clip.x(), clip.maxY()), clip.location(), ExponentialInterpolation);
1057     }
1058     return false;
1059 }
1060
1061 void RenderThemeIOS::setButtonSize(RenderStyle& style) const
1062 {
1063     // If the width and height are both specified, then we have nothing to do.
1064     if (!style.width().isIntrinsicOrAuto() && !style.height().isAuto())
1065         return;
1066
1067     // Use the font size to determine the intrinsic width of the control.
1068     style.setHeight(Length(static_cast<int>(ControlBaseHeight / ControlBaseFontSize * style.fontDescription().computedSize()), Fixed));
1069 }
1070
1071 const int kThumbnailBorderStrokeWidth = 1;
1072 const int kThumbnailBorderCornerRadius = 1;
1073 const int kVisibleBackgroundImageWidth = 1;
1074 const int kMultipleThumbnailShrinkSize = 2;
1075
1076 bool RenderThemeIOS::paintFileUploadIconDecorations(const RenderObject&, const RenderObject& buttonRenderer, const PaintInfo& paintInfo, const IntRect& rect, Icon* icon, FileUploadDecorations fileUploadDecorations)
1077 {
1078     GraphicsContextStateSaver stateSaver(paintInfo.context());
1079
1080     IntSize cornerSize(kThumbnailBorderCornerRadius, kThumbnailBorderCornerRadius);
1081     Color pictureFrameColor = buttonRenderer.style().visitedDependentColor(CSSPropertyBorderTopColor);
1082
1083     IntRect thumbnailPictureFrameRect = rect;
1084     IntRect thumbnailRect = rect;
1085     thumbnailRect.contract(2 * kThumbnailBorderStrokeWidth, 2 * kThumbnailBorderStrokeWidth);
1086     thumbnailRect.move(kThumbnailBorderStrokeWidth, kThumbnailBorderStrokeWidth);
1087
1088     if (fileUploadDecorations == MultipleFiles) {
1089         // Smaller thumbnails for multiple selection appearance.
1090         thumbnailPictureFrameRect.contract(kMultipleThumbnailShrinkSize, kMultipleThumbnailShrinkSize);
1091         thumbnailRect.contract(kMultipleThumbnailShrinkSize, kMultipleThumbnailShrinkSize);
1092
1093         // Background picture frame and simple background icon with a gradient matching the button.
1094         Color backgroundImageColor = Color(buttonRenderer.style().visitedDependentColor(CSSPropertyBackgroundColor).rgb());
1095         paintInfo.context().fillRoundedRect(FloatRoundedRect(thumbnailPictureFrameRect, cornerSize, cornerSize, cornerSize, cornerSize), pictureFrameColor);
1096         paintInfo.context().fillRect(thumbnailRect, backgroundImageColor);
1097         {
1098             GraphicsContextStateSaver stateSaver2(paintInfo.context());
1099             CGContextRef cgContext = paintInfo.context().platformContext();
1100             paintInfo.context().clip(thumbnailRect);
1101             if (backgroundImageColor.isDark())
1102                 drawAxialGradient(cgContext, gradientWithName(ConvexGradient), thumbnailRect.location(), FloatPoint(thumbnailRect.x(), thumbnailRect.maxY()), LinearInterpolation);
1103             else {
1104                 drawAxialGradient(cgContext, gradientWithName(ShadeGradient), thumbnailRect.location(), FloatPoint(thumbnailRect.x(), thumbnailRect.maxY()), LinearInterpolation);
1105                 drawAxialGradient(cgContext, gradientWithName(ShineGradient), FloatPoint(thumbnailRect.x(), thumbnailRect.maxY()), thumbnailRect.location(), ExponentialInterpolation);
1106             }
1107         }
1108
1109         // Move the rects for the Foreground picture frame and icon.
1110         int inset = kVisibleBackgroundImageWidth + kThumbnailBorderStrokeWidth;
1111         thumbnailPictureFrameRect.move(inset, inset);
1112         thumbnailRect.move(inset, inset);
1113     }
1114
1115     // Foreground picture frame and icon.
1116     paintInfo.context().fillRoundedRect(FloatRoundedRect(thumbnailPictureFrameRect, cornerSize, cornerSize, cornerSize, cornerSize), pictureFrameColor);
1117     icon->paint(paintInfo.context(), thumbnailRect);
1118
1119     return false;
1120 }
1121
1122 Color RenderThemeIOS::platformActiveSelectionBackgroundColor(OptionSet<StyleColor::Options>) const
1123 {
1124     return Color::transparent;
1125 }
1126
1127 Color RenderThemeIOS::platformInactiveSelectionBackgroundColor(OptionSet<StyleColor::Options>) const
1128 {
1129     return Color::transparent;
1130 }
1131
1132 bool RenderThemeIOS::shouldHaveSpinButton(const HTMLInputElement&) const
1133 {
1134     return false;
1135 }
1136
1137 bool RenderThemeIOS::shouldHaveCapsLockIndicator(const HTMLInputElement&) const
1138 {
1139     return false;
1140 }
1141
1142 FontCascadeDescription& RenderThemeIOS::cachedSystemFontDescription(CSSValueID valueID) const
1143 {
1144     static NeverDestroyed<FontCascadeDescription> systemFont;
1145     static NeverDestroyed<FontCascadeDescription> headlineFont;
1146     static NeverDestroyed<FontCascadeDescription> bodyFont;
1147     static NeverDestroyed<FontCascadeDescription> subheadlineFont;
1148     static NeverDestroyed<FontCascadeDescription> footnoteFont;
1149     static NeverDestroyed<FontCascadeDescription> caption1Font;
1150     static NeverDestroyed<FontCascadeDescription> caption2Font;
1151     static NeverDestroyed<FontCascadeDescription> shortHeadlineFont;
1152     static NeverDestroyed<FontCascadeDescription> shortBodyFont;
1153     static NeverDestroyed<FontCascadeDescription> shortSubheadlineFont;
1154     static NeverDestroyed<FontCascadeDescription> shortFootnoteFont;
1155     static NeverDestroyed<FontCascadeDescription> shortCaption1Font;
1156     static NeverDestroyed<FontCascadeDescription> tallBodyFont;
1157 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
1158     static NeverDestroyed<FontCascadeDescription> title0Font;
1159 #endif
1160     static NeverDestroyed<FontCascadeDescription> title1Font;
1161     static NeverDestroyed<FontCascadeDescription> title2Font;
1162     static NeverDestroyed<FontCascadeDescription> title3Font;
1163 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
1164     static NeverDestroyed<FontCascadeDescription> title4Font;
1165 #endif
1166
1167     static CFStringRef userTextSize = contentSizeCategory();
1168
1169     if (userTextSize != contentSizeCategory()) {
1170         userTextSize = contentSizeCategory();
1171
1172         headlineFont.get().setIsAbsoluteSize(false);
1173         bodyFont.get().setIsAbsoluteSize(false);
1174         subheadlineFont.get().setIsAbsoluteSize(false);
1175         footnoteFont.get().setIsAbsoluteSize(false);
1176         caption1Font.get().setIsAbsoluteSize(false);
1177         caption2Font.get().setIsAbsoluteSize(false);
1178         shortHeadlineFont.get().setIsAbsoluteSize(false);
1179         shortBodyFont.get().setIsAbsoluteSize(false);
1180         shortSubheadlineFont.get().setIsAbsoluteSize(false);
1181         shortFootnoteFont.get().setIsAbsoluteSize(false);
1182         shortCaption1Font.get().setIsAbsoluteSize(false);
1183         tallBodyFont.get().setIsAbsoluteSize(false);
1184     }
1185
1186     switch (valueID) {
1187     case CSSValueAppleSystemHeadline:
1188         return headlineFont;
1189     case CSSValueAppleSystemBody:
1190         return bodyFont;
1191 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
1192     case CSSValueAppleSystemTitle0:
1193         return title0Font;
1194 #endif
1195     case CSSValueAppleSystemTitle1:
1196         return title1Font;
1197     case CSSValueAppleSystemTitle2:
1198         return title2Font;
1199     case CSSValueAppleSystemTitle3:
1200         return title3Font;
1201 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
1202     case CSSValueAppleSystemTitle4:
1203         return title4Font;
1204 #endif
1205     case CSSValueAppleSystemSubheadline:
1206         return subheadlineFont;
1207     case CSSValueAppleSystemFootnote:
1208         return footnoteFont;
1209     case CSSValueAppleSystemCaption1:
1210         return caption1Font;
1211     case CSSValueAppleSystemCaption2:
1212         return caption2Font;
1213         // Short version.
1214     case CSSValueAppleSystemShortHeadline:
1215         return shortHeadlineFont;
1216     case CSSValueAppleSystemShortBody:
1217         return shortBodyFont;
1218     case CSSValueAppleSystemShortSubheadline:
1219         return shortSubheadlineFont;
1220     case CSSValueAppleSystemShortFootnote:
1221         return shortFootnoteFont;
1222     case CSSValueAppleSystemShortCaption1:
1223         return shortCaption1Font;
1224         // Tall version.
1225     case CSSValueAppleSystemTallBody:
1226         return tallBodyFont;
1227     default:
1228         return systemFont;
1229     }
1230 }
1231
1232 static inline FontSelectionValue cssWeightOfSystemFont(CTFontRef font)
1233 {
1234     RetainPtr<CFDictionaryRef> traits = adoptCF(CTFontCopyTraits(font));
1235     CFNumberRef resultRef = (CFNumberRef)CFDictionaryGetValue(traits.get(), kCTFontWeightTrait);
1236     float result = 0;
1237     CFNumberGetValue(resultRef, kCFNumberFloatType, &result);
1238     // These numbers were experimentally gathered from weights of the system font.
1239     static float weightThresholds[] = { -0.6, -0.365, -0.115, 0.130, 0.235, 0.350, 0.5, 0.7 };
1240     for (unsigned i = 0; i < WTF_ARRAY_LENGTH(weightThresholds); ++i) {
1241         if (result < weightThresholds[i])
1242             return FontSelectionValue((static_cast<int>(i) + 1) * 100);
1243     }
1244     return FontSelectionValue(900);
1245 }
1246
1247 void RenderThemeIOS::updateCachedSystemFontDescription(CSSValueID valueID, FontCascadeDescription& fontDescription) const
1248 {
1249     RetainPtr<CTFontDescriptorRef> fontDescriptor;
1250     CFStringRef textStyle;
1251     switch (valueID) {
1252     case CSSValueAppleSystemHeadline:
1253         textStyle = kCTUIFontTextStyleHeadline;
1254         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), nullptr));
1255         break;
1256     case CSSValueAppleSystemBody:
1257         textStyle = kCTUIFontTextStyleBody;
1258         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), nullptr));
1259         break;
1260 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
1261     case CSSValueAppleSystemTitle0:
1262         textStyle = kCTUIFontTextStyleTitle0;
1263         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), nullptr));
1264         break;
1265 #endif
1266     case CSSValueAppleSystemTitle1:
1267         textStyle = kCTUIFontTextStyleTitle1;
1268         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), nullptr));
1269         break;
1270     case CSSValueAppleSystemTitle2:
1271         textStyle = kCTUIFontTextStyleTitle2;
1272         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), nullptr));
1273         break;
1274     case CSSValueAppleSystemTitle3:
1275         textStyle = kCTUIFontTextStyleTitle3;
1276         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), nullptr));
1277         break;
1278 #if __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000
1279     case CSSValueAppleSystemTitle4:
1280         textStyle = kCTUIFontTextStyleTitle4;
1281         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), nullptr));
1282         break;
1283 #endif
1284     case CSSValueAppleSystemSubheadline:
1285         textStyle = kCTUIFontTextStyleSubhead;
1286         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), nullptr));
1287         break;
1288     case CSSValueAppleSystemFootnote:
1289         textStyle = kCTUIFontTextStyleFootnote;
1290         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), nullptr));
1291         break;
1292     case CSSValueAppleSystemCaption1:
1293         textStyle = kCTUIFontTextStyleCaption1;
1294         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), nullptr));
1295         break;
1296     case CSSValueAppleSystemCaption2:
1297         textStyle = kCTUIFontTextStyleCaption2;
1298         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), nullptr));
1299         break;
1300
1301     // Short version.
1302     case CSSValueAppleSystemShortHeadline:
1303         textStyle = kCTUIFontTextStyleShortHeadline;
1304         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), nullptr));
1305         break;
1306     case CSSValueAppleSystemShortBody:
1307         textStyle = kCTUIFontTextStyleShortBody;
1308         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), nullptr));
1309         break;
1310     case CSSValueAppleSystemShortSubheadline:
1311         textStyle = kCTUIFontTextStyleShortSubhead;
1312         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), nullptr));
1313         break;
1314     case CSSValueAppleSystemShortFootnote:
1315         textStyle = kCTUIFontTextStyleShortFootnote;
1316         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), nullptr));
1317         break;
1318     case CSSValueAppleSystemShortCaption1:
1319         textStyle = kCTUIFontTextStyleShortCaption1;
1320         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), nullptr));
1321         break;
1322
1323     // Tall version.
1324     case CSSValueAppleSystemTallBody:
1325         textStyle = kCTUIFontTextStyleTallBody;
1326         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), nullptr));
1327         break;
1328
1329     default:
1330         textStyle = kCTFontDescriptorTextStyleEmphasized;
1331         fontDescriptor = adoptCF(CTFontDescriptorCreateForUIType(kCTFontUIFontSystem, 0, nullptr));
1332     }
1333
1334     ASSERT(fontDescriptor);
1335     RetainPtr<CTFontRef> font = adoptCF(CTFontCreateWithFontDescriptor(fontDescriptor.get(), 0, nullptr));
1336     fontDescription.setIsAbsoluteSize(true);
1337     fontDescription.setOneFamily(textStyle);
1338     fontDescription.setSpecifiedSize(CTFontGetSize(font.get()));
1339     fontDescription.setWeight(cssWeightOfSystemFont(font.get()));
1340     fontDescription.setItalic(normalItalicValue());
1341 }
1342
1343 #if ENABLE(VIDEO)
1344 String RenderThemeIOS::mediaControlsStyleSheet()
1345 {
1346 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
1347     if (m_legacyMediaControlsStyleSheet.isEmpty())
1348         m_legacyMediaControlsStyleSheet = [NSString stringWithContentsOfFile:[[NSBundle bundleForClass:[WebCoreRenderThemeBundle class]] pathForResource:@"mediaControlsiOS" ofType:@"css"] encoding:NSUTF8StringEncoding error:nil];
1349     return m_legacyMediaControlsStyleSheet;
1350 #else
1351     return emptyString();
1352 #endif
1353 }
1354
1355 String RenderThemeIOS::modernMediaControlsStyleSheet()
1356 {
1357 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
1358     if (RuntimeEnabledFeatures::sharedFeatures().modernMediaControlsEnabled()) {
1359         if (m_mediaControlsStyleSheet.isEmpty())
1360             m_mediaControlsStyleSheet = [NSString stringWithContentsOfFile:[[NSBundle bundleForClass:[WebCoreRenderThemeBundle class]] pathForResource:@"modern-media-controls" ofType:@"css" inDirectory:@"modern-media-controls"] encoding:NSUTF8StringEncoding error:nil];
1361         return m_mediaControlsStyleSheet;
1362     }
1363     return emptyString();
1364 #else
1365     return emptyString();
1366 #endif
1367 }
1368
1369 void RenderThemeIOS::purgeCaches()
1370 {
1371     m_legacyMediaControlsScript.clearImplIfNotShared();
1372     m_mediaControlsScript.clearImplIfNotShared();
1373     m_legacyMediaControlsStyleSheet.clearImplIfNotShared();
1374     m_mediaControlsStyleSheet.clearImplIfNotShared();
1375 }
1376
1377 String RenderThemeIOS::mediaControlsScript()
1378 {
1379 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
1380     if (RuntimeEnabledFeatures::sharedFeatures().modernMediaControlsEnabled()) {
1381         if (m_mediaControlsScript.isEmpty()) {
1382             NSBundle *bundle = [NSBundle bundleForClass:[WebCoreRenderThemeBundle class]];
1383
1384             StringBuilder scriptBuilder;
1385             scriptBuilder.append([NSString stringWithContentsOfFile:[bundle pathForResource:@"modern-media-controls-localized-strings" ofType:@"js"] encoding:NSUTF8StringEncoding error:nil]);
1386             scriptBuilder.append([NSString stringWithContentsOfFile:[bundle pathForResource:@"modern-media-controls" ofType:@"js" inDirectory:@"modern-media-controls"] encoding:NSUTF8StringEncoding error:nil]);
1387             m_mediaControlsScript = scriptBuilder.toString();
1388         }
1389         return m_mediaControlsScript;
1390     }
1391
1392     if (m_legacyMediaControlsScript.isEmpty()) {
1393         NSBundle *bundle = [NSBundle bundleForClass:[WebCoreRenderThemeBundle class]];
1394
1395         StringBuilder scriptBuilder;
1396         scriptBuilder.append([NSString stringWithContentsOfFile:[bundle pathForResource:@"mediaControlsLocalizedStrings" ofType:@"js"] encoding:NSUTF8StringEncoding error:nil]);
1397         scriptBuilder.append([NSString stringWithContentsOfFile:[bundle pathForResource:@"mediaControlsApple" ofType:@"js"] encoding:NSUTF8StringEncoding error:nil]);
1398         scriptBuilder.append([NSString stringWithContentsOfFile:[bundle pathForResource:@"mediaControlsiOS" ofType:@"js"] encoding:NSUTF8StringEncoding error:nil]);
1399
1400         m_legacyMediaControlsScript = scriptBuilder.toString();
1401     }
1402     return m_legacyMediaControlsScript;
1403 #else
1404     return emptyString();
1405 #endif
1406 }
1407
1408 String RenderThemeIOS::mediaControlsBase64StringForIconNameAndType(const String& iconName, const String& iconType)
1409 {
1410 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
1411     if (!RuntimeEnabledFeatures::sharedFeatures().modernMediaControlsEnabled())
1412         return emptyString();
1413
1414     String directory = "modern-media-controls/images";
1415     NSBundle *bundle = [NSBundle bundleForClass:[WebCoreRenderThemeBundle class]];
1416     return [[NSData dataWithContentsOfFile:[bundle pathForResource:iconName ofType:iconType inDirectory:directory]] base64EncodedStringWithOptions:0];
1417 #else
1418     return emptyString();
1419 #endif
1420 }
1421
1422 #endif // ENABLE(VIDEO)
1423
1424 Color RenderThemeIOS::systemColor(CSSValueID cssValueID, OptionSet<StyleColor::Options> options) const
1425 {
1426     const bool forVisitedLink = options.contains(StyleColor::Options::ForVisitedLink);
1427
1428     // The system color cache below can't handle visited links. The only color value
1429     // that cares about visited links is CSSValueWebkitLink, so handle it here by
1430     // calling through to RenderTheme's base implementation.
1431     if (forVisitedLink && cssValueID == CSSValueWebkitLink)
1432         return RenderTheme::systemColor(cssValueID, options);
1433
1434     ASSERT(!forVisitedLink);
1435
1436     auto addResult = m_systemColorCache.add(cssValueID, Color());
1437     if (!addResult.isNewEntry)
1438         return addResult.iterator->value;
1439
1440     Color color;
1441     switch (cssValueID) {
1442     case CSSValueAppleWirelessPlaybackTargetActive:
1443         color = [getUIColorClass() systemBlueColor].CGColor;
1444         break;
1445     case CSSValueAppleSystemBlue:
1446         color = [getUIColorClass() systemBlueColor].CGColor;
1447         break;
1448     case CSSValueAppleSystemGray:
1449         color = [getUIColorClass() systemGrayColor].CGColor;
1450         break;
1451     case CSSValueAppleSystemGreen:
1452         color = [getUIColorClass() systemGreenColor].CGColor;
1453         break;
1454     case CSSValueAppleSystemOrange:
1455         color = [getUIColorClass() systemOrangeColor].CGColor;
1456         break;
1457     case CSSValueAppleSystemPink:
1458         color = [getUIColorClass() systemPinkColor].CGColor;
1459         break;
1460     case CSSValueAppleSystemRed:
1461         color = [getUIColorClass() systemRedColor].CGColor;
1462         break;
1463     case CSSValueAppleSystemYellow:
1464         color = [getUIColorClass() systemYellowColor].CGColor;
1465         break;
1466     default:
1467         break;
1468     }
1469
1470     if (!color.isValid())
1471         color = RenderTheme::systemColor(cssValueID, options);
1472
1473     addResult.iterator->value = color;
1474
1475     return addResult.iterator->value;
1476 }
1477
1478 #if ENABLE(ATTACHMENT_ELEMENT)
1479
1480 const CGSize attachmentSize = { 160, 119 };
1481
1482 const CGFloat attachmentBorderRadius = 16;
1483 static Color attachmentBorderColor() { return Color(204, 204, 204); }
1484
1485 static Color attachmentProgressColor() { return Color(222, 222, 222); }
1486 const CGFloat attachmentProgressBorderThickness = 3;
1487
1488 const CGFloat attachmentProgressSize = 36;
1489 const CGFloat attachmentIconSize = 48;
1490
1491 const CGFloat attachmentItemMargin = 8;
1492
1493 const CGFloat attachmentWrappingTextMaximumWidth = 140;
1494 const CFIndex attachmentWrappingTextMaximumLineCount = 2;
1495
1496 static RetainPtr<CTFontRef> attachmentActionFont()
1497 {
1498     RetainPtr<CTFontDescriptorRef> fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(kCTUIFontTextStyleShortFootnote, RenderThemeIOS::contentSizeCategory(), 0));
1499     RetainPtr<CTFontDescriptorRef> emphasizedFontDescriptor = adoptCF(CTFontDescriptorCreateCopyWithAttributes(fontDescriptor.get(),
1500         (CFDictionaryRef)@{
1501             (id)kCTFontDescriptorTextStyleAttribute: (id)kCTFontDescriptorTextStyleEmphasized
1502     }));
1503     return adoptCF(CTFontCreateWithFontDescriptor(emphasizedFontDescriptor.get(), 0, nullptr));
1504 }
1505
1506 static UIColor *attachmentActionColor(const RenderAttachment& attachment)
1507 {
1508     return [getUIColorClass() colorWithCGColor:cachedCGColor(attachment.style().visitedDependentColor(CSSPropertyColor))];
1509 }
1510
1511 static RetainPtr<CTFontRef> attachmentTitleFont()
1512 {
1513     RetainPtr<CTFontDescriptorRef> fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(kCTUIFontTextStyleShortCaption1, RenderThemeIOS::contentSizeCategory(), 0));
1514     return adoptCF(CTFontCreateWithFontDescriptor(fontDescriptor.get(), 0, nullptr));
1515 }
1516
1517 static UIColor *attachmentTitleColor() { return [getUIColorClass() systemGrayColor]; }
1518
1519 static RetainPtr<CTFontRef> attachmentSubtitleFont() { return attachmentTitleFont(); }
1520 static UIColor *attachmentSubtitleColor() { return [getUIColorClass() systemGrayColor]; }
1521
1522 struct RenderAttachmentInfo {
1523     explicit RenderAttachmentInfo(const RenderAttachment&);
1524
1525     FloatRect iconRect;
1526     FloatRect attachmentRect;
1527     FloatRect progressRect;
1528
1529     BOOL hasProgress { NO };
1530     float progress;
1531
1532     RetainPtr<UIImage> icon;
1533
1534     int baseline { 0 };
1535
1536     struct LabelLine {
1537         FloatRect rect;
1538         RetainPtr<CTLineRef> line;
1539     };
1540     Vector<LabelLine> lines;
1541
1542     CGFloat contentYOrigin { 0 };
1543
1544 private:
1545     void buildWrappedLines(const String&, CTFontRef, UIColor *, unsigned maximumLineCount);
1546     void buildSingleLine(const String&, CTFontRef, UIColor *);
1547
1548     void addLine(CTLineRef);
1549 };
1550
1551 void RenderAttachmentInfo::addLine(CTLineRef line)
1552 {
1553     CGRect lineBounds = CTLineGetBoundsWithOptions(line, kCTLineBoundsExcludeTypographicLeading);
1554     CGFloat trailingWhitespaceWidth = CTLineGetTrailingWhitespaceWidth(line);
1555     CGFloat lineWidthIgnoringTrailingWhitespace = lineBounds.size.width - trailingWhitespaceWidth;
1556     CGFloat lineHeight = CGCeiling(lineBounds.size.height + lineBounds.origin.y);
1557
1558     CGFloat xOffset = (attachmentRect.width() / 2) - (lineWidthIgnoringTrailingWhitespace / 2);
1559     LabelLine labelLine;
1560     labelLine.line = line;
1561     labelLine.rect = FloatRect(xOffset, 0, lineWidthIgnoringTrailingWhitespace, lineHeight);
1562
1563     lines.append(labelLine);
1564 }
1565
1566 void RenderAttachmentInfo::buildWrappedLines(const String& text, CTFontRef font, UIColor *color, unsigned maximumLineCount)
1567 {
1568     if (text.isEmpty())
1569         return;
1570
1571     NSDictionary *textAttributes = @{
1572         (id)kCTFontAttributeName: (id)font,
1573         (id)kCTForegroundColorAttributeName: color
1574     };
1575     RetainPtr<NSAttributedString> attributedText = adoptNS([[NSAttributedString alloc] initWithString:text attributes:textAttributes]);
1576     RetainPtr<CTFramesetterRef> framesetter = adoptCF(CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attributedText.get()));
1577
1578     CFRange fitRange;
1579     CGSize textSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter.get(), CFRangeMake(0, 0), nullptr, CGSizeMake(attachmentWrappingTextMaximumWidth, CGFLOAT_MAX), &fitRange);
1580
1581     RetainPtr<CGPathRef> textPath = adoptCF(CGPathCreateWithRect(CGRectMake(0, 0, textSize.width, textSize.height), nullptr));
1582     RetainPtr<CTFrameRef> textFrame = adoptCF(CTFramesetterCreateFrame(framesetter.get(), fitRange, textPath.get(), nullptr));
1583
1584     CFArrayRef ctLines = CTFrameGetLines(textFrame.get());
1585     CFIndex lineCount = CFArrayGetCount(ctLines);
1586     if (!lineCount)
1587         return;
1588
1589     // Lay out and record the first (maximumLineCount - 1) lines.
1590     CFIndex lineIndex = 0;
1591     CFIndex nonTruncatedLineCount = std::min<CFIndex>(maximumLineCount - 1, lineCount);
1592     for (; lineIndex < nonTruncatedLineCount; ++lineIndex)
1593         addLine((CTLineRef)CFArrayGetValueAtIndex(ctLines, lineIndex));
1594
1595     if (lineIndex == lineCount)
1596         return;
1597
1598     // We had text that didn't fit in the first (maximumLineCount - 1) lines.
1599     // Combine it into one last line, and center-truncate it.
1600     CTLineRef firstRemainingLine = (CTLineRef)CFArrayGetValueAtIndex(ctLines, lineIndex);
1601     CFIndex remainingRangeStart = CTLineGetStringRange(firstRemainingLine).location;
1602     NSRange remainingRange = NSMakeRange(remainingRangeStart, [attributedText length] - remainingRangeStart);
1603     NSAttributedString *remainingString = [attributedText attributedSubstringFromRange:remainingRange];
1604     RetainPtr<CTLineRef> remainingLine = adoptCF(CTLineCreateWithAttributedString((CFAttributedStringRef)remainingString));
1605     RetainPtr<NSAttributedString> ellipsisString = adoptNS([[NSAttributedString alloc] initWithString:@"\u2026" attributes:textAttributes]);
1606     RetainPtr<CTLineRef> ellipsisLine = adoptCF(CTLineCreateWithAttributedString((CFAttributedStringRef)ellipsisString.get()));
1607     RetainPtr<CTLineRef> truncatedLine = adoptCF(CTLineCreateTruncatedLine(remainingLine.get(), attachmentWrappingTextMaximumWidth, kCTLineTruncationMiddle, ellipsisLine.get()));
1608
1609     if (!truncatedLine)
1610         truncatedLine = remainingLine;
1611
1612     addLine(truncatedLine.get());
1613 }
1614
1615 void RenderAttachmentInfo::buildSingleLine(const String& text, CTFontRef font, UIColor *color)
1616 {
1617     if (text.isEmpty())
1618         return;
1619
1620     NSDictionary *textAttributes = @{
1621         (id)kCTFontAttributeName: (id)font,
1622         (id)kCTForegroundColorAttributeName: color
1623     };
1624     RetainPtr<NSAttributedString> attributedText = adoptNS([[NSAttributedString alloc] initWithString:text attributes:textAttributes]);
1625
1626     addLine(adoptCF(CTLineCreateWithAttributedString((CFAttributedStringRef)attributedText.get())).get());
1627 }
1628
1629 static BOOL getAttachmentProgress(const RenderAttachment& attachment, float& progress)
1630 {
1631     auto& progressString = attachment.attachmentElement().attributeWithoutSynchronization(progressAttr);
1632     if (progressString.isEmpty())
1633         return NO;
1634     bool validProgress;
1635     progress = std::max<float>(std::min<float>(progressString.toFloat(&validProgress), 1), 0);
1636     return validProgress;
1637 }
1638
1639 static RetainPtr<UIImage> iconForAttachment(const RenderAttachment& attachment, FloatSize& size)
1640 {
1641     auto documentInteractionController = adoptNS([allocUIDocumentInteractionControllerInstance() init]);
1642
1643     String fileName;
1644     if (File* file = attachment.attachmentElement().file())
1645         fileName = file->name();
1646
1647     if (fileName.isEmpty())
1648         fileName = attachment.attachmentElement().attachmentTitle();
1649     [documentInteractionController setName:fileName];
1650
1651     String attachmentType = attachment.attachmentElement().attachmentType();
1652     if (!attachmentType.isEmpty()) {
1653         String UTI;
1654         if (isDeclaredUTI(attachmentType))
1655             UTI = attachmentType;
1656         else
1657             UTI = UTIFromMIMEType(attachmentType);
1658
1659         [documentInteractionController setUTI:static_cast<NSString *>(UTI)];
1660     }
1661
1662     NSArray *icons = [documentInteractionController icons];
1663     if (!icons.count)
1664         return nil;
1665
1666     RetainPtr<UIImage> result = icons.lastObject;
1667
1668     BOOL useHeightForClosestMatch = [result size].height > [result size].width;
1669     CGFloat bestMatchRatio = -1;
1670
1671     for (UIImage *icon in icons) {
1672         CGFloat iconSize = useHeightForClosestMatch ? icon.size.height : icon.size.width;
1673
1674         CGFloat matchRatio = (attachmentIconSize / iconSize) - 1.0f;
1675         if (matchRatio < 0.3f) {
1676             matchRatio = CGFAbs(matchRatio);
1677             if ((bestMatchRatio == -1) || (matchRatio < bestMatchRatio)) {
1678                 result = icon;
1679                 bestMatchRatio = matchRatio;
1680             }
1681         }
1682     }
1683
1684     CGFloat iconAspect = [result size].width / [result size].height;
1685     size = largestRectWithAspectRatioInsideRect(iconAspect, FloatRect(0, 0, attachmentIconSize, attachmentIconSize)).size();
1686
1687     return result;
1688 }
1689
1690 RenderAttachmentInfo::RenderAttachmentInfo(const RenderAttachment& attachment)
1691 {
1692     attachmentRect = FloatRect(0, 0, attachment.width().toFloat(), attachment.height().toFloat());
1693
1694     hasProgress = getAttachmentProgress(attachment, progress);
1695
1696     String title = attachment.attachmentElement().attachmentTitle();
1697     String action = attachment.attachmentElement().attributeWithoutSynchronization(actionAttr);
1698     String subtitle = attachment.attachmentElement().attributeWithoutSynchronization(subtitleAttr);
1699
1700     CGFloat yOffset = 0;
1701
1702     if (hasProgress) {
1703         progressRect = FloatRect((attachmentRect.width() / 2) - (attachmentProgressSize / 2), 0, attachmentProgressSize, attachmentProgressSize);
1704         yOffset += attachmentProgressSize + attachmentItemMargin;
1705     }
1706
1707     if (action.isEmpty() && !hasProgress) {
1708         FloatSize iconSize;
1709         icon = iconForAttachment(attachment, iconSize);
1710         if (icon) {
1711             iconRect = FloatRect(FloatPoint((attachmentRect.width() / 2) - (iconSize.width() / 2), 0), iconSize);
1712             yOffset += iconRect.height() + attachmentItemMargin;
1713         }
1714     } else
1715         buildWrappedLines(action, attachmentActionFont().get(), attachmentActionColor(attachment), attachmentWrappingTextMaximumLineCount);
1716
1717     bool forceSingleLineTitle = !action.isEmpty() || !subtitle.isEmpty() || hasProgress;
1718     buildWrappedLines(title, attachmentTitleFont().get(), attachmentTitleColor(), forceSingleLineTitle ? 1 : attachmentWrappingTextMaximumLineCount);
1719     buildSingleLine(subtitle, attachmentSubtitleFont().get(), attachmentSubtitleColor());
1720
1721     if (!lines.isEmpty()) {
1722         for (auto& line : lines) {
1723             line.rect.setY(yOffset);
1724             yOffset += line.rect.height() + attachmentItemMargin;
1725         }
1726     }
1727
1728     yOffset -= attachmentItemMargin;
1729
1730     contentYOrigin = (attachmentRect.height() / 2) - (yOffset / 2);
1731 }
1732
1733 LayoutSize RenderThemeIOS::attachmentIntrinsicSize(const RenderAttachment&) const
1734 {
1735     return LayoutSize(FloatSize(attachmentSize));
1736 }
1737
1738 int RenderThemeIOS::attachmentBaseline(const RenderAttachment& attachment) const
1739 {
1740     RenderAttachmentInfo info(attachment);
1741     return info.baseline;
1742 }
1743
1744 static void paintAttachmentIcon(GraphicsContext& context, RenderAttachmentInfo& info)
1745 {
1746     if (!info.icon)
1747         return;
1748
1749     RefPtr<Image> iconImage = BitmapImage::create([info.icon CGImage]);
1750     if (!iconImage)
1751         return;
1752
1753     context.drawImage(*iconImage, info.iconRect);
1754 }
1755
1756 static void paintAttachmentText(GraphicsContext& context, RenderAttachmentInfo& info)
1757 {
1758     for (const auto& line : info.lines) {
1759         GraphicsContextStateSaver saver(context);
1760
1761         context.translate(toFloatSize(line.rect.minXMaxYCorner()));
1762         context.scale(FloatSize(1, -1));
1763
1764         CGContextSetTextPosition(context.platformContext(), 0, 0);
1765         CTLineDraw(line.line.get(), context.platformContext());
1766     }
1767 }
1768
1769 static void paintAttachmentProgress(GraphicsContext& context, RenderAttachmentInfo& info)
1770 {
1771     GraphicsContextStateSaver saver(context);
1772
1773     context.setStrokeThickness(attachmentProgressBorderThickness);
1774     context.setStrokeColor(attachmentProgressColor());
1775     context.setFillColor(attachmentProgressColor());
1776     context.strokeEllipse(info.progressRect);
1777
1778     FloatPoint center = info.progressRect.center();
1779
1780     Path progressPath;
1781     progressPath.moveTo(center);
1782     progressPath.addLineTo(FloatPoint(center.x(), info.progressRect.y()));
1783     progressPath.addArc(center, info.progressRect.width() / 2, -M_PI_2, info.progress * 2 * M_PI - M_PI_2, 0);
1784     progressPath.closeSubpath();
1785     context.fillPath(progressPath);
1786 }
1787
1788 static Path attachmentBorderPath(RenderAttachmentInfo& info)
1789 {
1790     Path borderPath;
1791     borderPath.addRoundedRect(info.attachmentRect, FloatSize(attachmentBorderRadius, attachmentBorderRadius));
1792     return borderPath;
1793 }
1794
1795 static void paintAttachmentBorder(GraphicsContext& context, Path& borderPath)
1796 {
1797     context.setStrokeColor(attachmentBorderColor());
1798     context.setStrokeThickness(1);
1799     context.strokePath(borderPath);
1800 }
1801
1802 bool RenderThemeIOS::paintAttachment(const RenderObject& renderer, const PaintInfo& paintInfo, const IntRect& paintRect)
1803 {
1804     if (!is<RenderAttachment>(renderer))
1805         return false;
1806
1807     const RenderAttachment& attachment = downcast<RenderAttachment>(renderer);
1808
1809     RenderAttachmentInfo info(attachment);
1810
1811     GraphicsContext& context = paintInfo.context();
1812     GraphicsContextStateSaver saver(context);
1813
1814     context.translate(toFloatSize(paintRect.location()));
1815
1816     if (attachment.shouldDrawBorder()) {
1817         auto borderPath = attachmentBorderPath(info);
1818         paintAttachmentBorder(context, borderPath);
1819         context.clipPath(borderPath);
1820     }
1821
1822     context.translate(FloatSize(0, info.contentYOrigin));
1823
1824     if (info.hasProgress)
1825         paintAttachmentProgress(context, info);
1826     else if (info.icon)
1827         paintAttachmentIcon(context, info);
1828
1829     paintAttachmentText(context, info);
1830
1831     return true;
1832 }
1833
1834 #endif // ENABLE(ATTACHMENT_ELEMENT)
1835
1836 #if PLATFORM(WATCHOS)
1837
1838 String RenderThemeIOS::extraDefaultStyleSheet()
1839 {
1840     return "* { -webkit-text-size-adjust: auto; -webkit-hyphens: auto !important; }"_s;
1841 }
1842
1843 #endif
1844
1845 #if USE(SYSTEM_PREVIEW)
1846 void RenderThemeIOS::paintSystemPreviewBadge(Image& image, const PaintInfo& paintInfo, const FloatRect& rect)
1847 {
1848     static const int largeBadgeDimension = 70;
1849     static const int largeBadgeOffset = 20;
1850
1851     static const int smallBadgeDimension = 35;
1852     static const int smallBadgeOffset = 8;
1853
1854     static const int minimumSizeForLargeBadge = 240;
1855
1856     bool useSmallBadge = rect.width() < minimumSizeForLargeBadge || rect.height() < minimumSizeForLargeBadge;
1857     int badgeOffset = useSmallBadge ? smallBadgeOffset : largeBadgeOffset;
1858     int badgeDimension = useSmallBadge ? smallBadgeDimension : largeBadgeDimension;
1859
1860     int minimumDimension = badgeDimension + 2 * badgeOffset;
1861     if (rect.width() < minimumDimension || rect.height() < minimumDimension)
1862         return;
1863
1864     CGRect absoluteBadgeRect = CGRectMake(rect.x() + rect.width() - badgeDimension - badgeOffset, rect.y() + badgeOffset, badgeDimension, badgeDimension);
1865     CGRect insetBadgeRect = CGRectMake(rect.width() - badgeDimension - badgeOffset, badgeOffset, badgeDimension, badgeDimension);
1866     CGRect badgeRect = CGRectMake(0, 0, badgeDimension, badgeDimension);
1867
1868     CIImage *inputImage = [CIImage imageWithCGImage:image.nativeImage().get()];
1869
1870     // Create a circle to be used for the clipping path in the badge, as well as the drop shadow.
1871     RetainPtr<CGPathRef> circle = adoptCF(CGPathCreateWithRoundedRect(absoluteBadgeRect, badgeDimension / 2, badgeDimension / 2, nullptr));
1872
1873     auto& graphicsContext = paintInfo.context();
1874     if (graphicsContext.paintingDisabled())
1875         return;
1876
1877     GraphicsContextStateSaver stateSaver(graphicsContext);
1878
1879     CGContextRef ctx = graphicsContext.platformContext();
1880     if (!ctx)
1881         return;
1882
1883     CGContextSaveGState(ctx);
1884
1885     // Draw a drop shadow around the circle.
1886     // Use the GraphicsContext function, because it calculates the blur radius in context space,
1887     // rather than screen space.
1888     Color shadowColor = Color { 0.f, 0.f, 0.f, 0.1f };
1889     graphicsContext.setShadow(FloatSize { }, 16, shadowColor);
1890
1891     // The circle must have an alpha channel value of 1 for the shadow color to appear.
1892     CGFloat circleColorComponents[4] = { 0, 0, 0, 1 };
1893     RetainPtr<CGColorRef> circleColor = adoptCF(CGColorCreate(sRGBColorSpaceRef(), circleColorComponents));
1894     CGContextSetFillColorWithColor(ctx, circleColor.get());
1895
1896     // Clip out the circle to only show the shadow.
1897     CGContextBeginPath(ctx);
1898     CGContextAddRect(ctx, rect);
1899     CGContextAddPath(ctx, circle.get());
1900     CGContextClosePath(ctx);
1901     CGContextEOClip(ctx);
1902
1903     // Draw a slightly smaller circle with a shadow, otherwise we'll see a fringe of the solid
1904     // black circle around the edges of the clipped path below.
1905     CGContextBeginPath(ctx);
1906     CGRect slightlySmallerAbsoluteBadgeRect = CGRectMake(absoluteBadgeRect.origin.x + 0.5, absoluteBadgeRect.origin.y + 0.5, badgeDimension - 1, badgeDimension - 1);
1907     RetainPtr<CGPathRef> slightlySmallerCircle = adoptCF(CGPathCreateWithRoundedRect(slightlySmallerAbsoluteBadgeRect, slightlySmallerAbsoluteBadgeRect.size.width / 2, slightlySmallerAbsoluteBadgeRect.size.height / 2, nullptr));
1908     CGContextAddPath(ctx, slightlySmallerCircle.get());
1909     CGContextClosePath(ctx);
1910     CGContextFillPath(ctx);
1911
1912     CGContextRestoreGState(ctx);
1913
1914     // Draw the blurred backdrop. Scale from intrinsic size to render size.
1915     CGAffineTransform transform = CGAffineTransformIdentity;
1916     transform = CGAffineTransformScale(transform, rect.width() / image.width(), rect.height() / image.height());
1917     CIImage *scaledImage = [inputImage imageByApplyingTransform:transform];
1918
1919     // CoreImage coordinates are y-up, so we need to flip the badge rectangle within the image frame.
1920     CGRect flippedInsetBadgeRect = CGRectMake(insetBadgeRect.origin.x, rect.height() - insetBadgeRect.origin.y - insetBadgeRect.size.height, badgeDimension, badgeDimension);
1921
1922     // Create a cropped region with pixel values extending outwards.
1923     CIImage *clampedImage = [scaledImage imageByClampingToRect:flippedInsetBadgeRect];
1924
1925     // Blur.
1926     CIImage *blurredImage = [clampedImage imageByApplyingGaussianBlurWithSigma:10];
1927
1928     // Saturate.
1929     CIFilter *saturationFilter = [CIFilter filterWithName:@"CIColorControls"];
1930     [saturationFilter setValue:blurredImage forKey:kCIInputImageKey];
1931     [saturationFilter setValue:@1.8 forKey:kCIInputSaturationKey];
1932
1933     // Tint.
1934     CIFilter *tintFilter1 = [CIFilter filterWithName:@"CIConstantColorGenerator"];
1935     CIColor *tintColor1 = [CIColor colorWithRed:1 green:1 blue:1 alpha:0.18];
1936     [tintFilter1 setValue:tintColor1 forKey:kCIInputColorKey];
1937
1938     // Blend the tint with the saturated output.
1939     CIFilter *sourceOverFilter = [CIFilter filterWithName:@"CISourceOverCompositing"];
1940     [sourceOverFilter setValue:tintFilter1.outputImage forKey:kCIInputImageKey];
1941     [sourceOverFilter setValue:saturationFilter.outputImage forKey:kCIInputBackgroundImageKey];
1942
1943     if (!m_ciContext)
1944         m_ciContext = [CIContext context];
1945
1946     RetainPtr<CGImageRef> cgImage;
1947 #if HAVE(IOSURFACE)
1948     // Crop the result to the badge location.
1949     CIImage *croppedImage = [sourceOverFilter.outputImage imageByCroppingToRect:flippedInsetBadgeRect];
1950     CIImage *translatedImage = [croppedImage imageByApplyingTransform:CGAffineTransformMakeTranslation(-flippedInsetBadgeRect.origin.x, -flippedInsetBadgeRect.origin.y)];
1951     IOSurfaceRef surface;
1952     if (useSmallBadge) {
1953         if (!m_smallBadgeSurface)
1954             m_smallBadgeSurface = IOSurface::create({ smallBadgeDimension, smallBadgeDimension }, sRGBColorSpaceRef());
1955         surface = m_smallBadgeSurface->surface();
1956     } else {
1957         if (!m_largeBadgeSurface)
1958             m_largeBadgeSurface = IOSurface::create({ largeBadgeDimension, largeBadgeDimension }, sRGBColorSpaceRef());
1959         surface = m_largeBadgeSurface->surface();
1960     }
1961     [m_ciContext.get() render:translatedImage toIOSurface:surface bounds:badgeRect colorSpace:sRGBColorSpaceRef()];
1962     cgImage = useSmallBadge ? m_smallBadgeSurface->createImage() : m_largeBadgeSurface->createImage();
1963 #else
1964     cgImage = adoptCF([m_ciContext.get() createCGImage:sourceOverFilter.outputImage fromRect:flippedInsetBadgeRect]);
1965 #endif
1966
1967     // Before we render the result, we should clip to a circle around the badge rectangle.
1968     CGContextSaveGState(ctx);
1969     CGContextBeginPath(ctx);
1970     CGContextAddPath(ctx, circle.get());
1971     CGContextClosePath(ctx);
1972     CGContextClip(ctx);
1973
1974     CGContextTranslateCTM(ctx, absoluteBadgeRect.origin.x, absoluteBadgeRect.origin.y);
1975     CGContextTranslateCTM(ctx, 0, badgeDimension);
1976     CGContextScaleCTM(ctx, 1, -1);
1977     CGContextDrawImage(ctx, badgeRect, cgImage.get());
1978
1979 #if USE(APPLE_INTERNAL_SDK)
1980     if (auto logo = systemPreviewLogo()) {
1981         CGSize pdfSize = CGPDFPageGetBoxRect(logo, kCGPDFMediaBox).size;
1982         CGFloat scaleX = badgeDimension / pdfSize.width;
1983         CGFloat scaleY = badgeDimension / pdfSize.height;
1984         CGContextScaleCTM(ctx, scaleX, scaleY);
1985         CGContextDrawPDFPage(ctx, logo);
1986     }
1987 #endif
1988
1989     CGContextFlush(ctx);
1990     CGContextRestoreGState(ctx);
1991 }
1992 #endif
1993
1994 } // namespace WebCore
1995
1996 #endif //PLATFORM(IOS)