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