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