Move SystemPreview code from WebKitAdditions to WebKit
[WebKit-https.git] / Source / WebCore / rendering / RenderThemeIOS.mm
1 /*
2  * Copyright (C) 2005-2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "RenderThemeIOS.h"
28
29 #if PLATFORM(IOS)
30
31 #import "BitmapImage.h"
32 #import "CSSPrimitiveValue.h"
33 #import "CSSToLengthConversionData.h"
34 #import "CSSValueKeywords.h"
35 #import "DateComponents.h"
36 #import "Document.h"
37 #import "File.h"
38 #import "FloatRoundedRect.h"
39 #import "FontCache.h"
40 #import "FontCascade.h"
41 #import "Frame.h"
42 #import "FrameSelection.h"
43 #import "FrameView.h"
44 #import "GeometryUtilities.h"
45 #import "Gradient.h"
46 #import "GraphicsContext.h"
47 #import "GraphicsContextCG.h"
48 #import "HTMLAttachmentElement.h"
49 #import "HTMLInputElement.h"
50 #import "HTMLNames.h"
51 #import "HTMLSelectElement.h"
52 #import "IOSurface.h"
53 #import "Icon.h"
54 #import "LocalizedDateCache.h"
55 #import "NodeRenderStyle.h"
56 #import "Page.h"
57 #import "PaintInfo.h"
58 #import "PathUtilities.h"
59 #import "PlatformLocale.h"
60 #import "RenderAttachment.h"
61 #import "RenderObject.h"
62 #import "RenderProgress.h"
63 #import "RenderStyle.h"
64 #import "RenderView.h"
65 #import "RuntimeEnabledFeatures.h"
66 #import "UTIUtilities.h"
67 #import "UserAgentScripts.h"
68 #import "UserAgentStyleSheets.h"
69 #import "WebCoreThreadRun.h"
70 #import <CoreGraphics/CoreGraphics.h>
71 #import <CoreImage/CoreImage.h>
72 #import <objc/runtime.h>
73 #import <pal/spi/cocoa/CoreTextSPI.h>
74 #import <pal/spi/ios/UIKitSPI.h>
75 #import <wtf/NeverDestroyed.h>
76 #import <wtf/RefPtr.h>
77 #import <wtf/SoftLinking.h>
78 #import <wtf/StdLibExtras.h>
79
80 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 CGColorRef RenderThemeIOS::colorForMarkerLineStyle(DocumentMarkerLineStyle style, bool)
1421 {
1422     switch (style) {
1423     case DocumentMarkerLineStyle::Spelling:
1424         return [getUIColorClass() systemRedColor].CGColor;
1425     case DocumentMarkerLineStyle::DictationAlternatives:
1426     case DocumentMarkerLineStyle::TextCheckingDictationPhraseWithAlternatives:
1427     case DocumentMarkerLineStyle::AutocorrectionReplacement:
1428         return [getUIColorClass() systemBlueColor].CGColor;
1429     case DocumentMarkerLineStyle::Grammar:
1430         return [getUIColorClass() systemGreenColor].CGColor;
1431     }
1432 }
1433
1434 Color RenderThemeIOS::systemColor(CSSValueID cssValueID, OptionSet<StyleColor::Options> options) const
1435 {
1436     const bool forVisitedLink = options.contains(StyleColor::Options::ForVisitedLink);
1437
1438     // The system color cache below can't handle visited links. The only color value
1439     // that cares about visited links is CSSValueWebkitLink, so handle it here by
1440     // calling through to RenderTheme's base implementation.
1441     if (forVisitedLink && cssValueID == CSSValueWebkitLink)
1442         return RenderTheme::systemColor(cssValueID, options);
1443
1444     ASSERT(!forVisitedLink);
1445
1446     auto addResult = m_systemColorCache.add(cssValueID, Color());
1447     if (!addResult.isNewEntry)
1448         return addResult.iterator->value;
1449
1450     Color color;
1451     switch (cssValueID) {
1452     case CSSValueAppleWirelessPlaybackTargetActive:
1453         color = [getUIColorClass() systemBlueColor].CGColor;
1454         break;
1455     case CSSValueAppleSystemBlue:
1456         color = [getUIColorClass() systemBlueColor].CGColor;
1457         break;
1458     case CSSValueAppleSystemGray:
1459         color = [getUIColorClass() systemGrayColor].CGColor;
1460         break;
1461     case CSSValueAppleSystemGreen:
1462         color = [getUIColorClass() systemGreenColor].CGColor;
1463         break;
1464     case CSSValueAppleSystemOrange:
1465         color = [getUIColorClass() systemOrangeColor].CGColor;
1466         break;
1467     case CSSValueAppleSystemPink:
1468         color = [getUIColorClass() systemPinkColor].CGColor;
1469         break;
1470     case CSSValueAppleSystemRed:
1471         color = [getUIColorClass() systemRedColor].CGColor;
1472         break;
1473     case CSSValueAppleSystemYellow:
1474         color = [getUIColorClass() systemYellowColor].CGColor;
1475         break;
1476     default:
1477         break;
1478     }
1479
1480     if (!color.isValid())
1481         color = RenderTheme::systemColor(cssValueID, options);
1482
1483     addResult.iterator->value = color;
1484
1485     return addResult.iterator->value;
1486 }
1487
1488 #if ENABLE(ATTACHMENT_ELEMENT)
1489
1490 const CGSize attachmentSize = { 160, 119 };
1491
1492 const CGFloat attachmentBorderRadius = 16;
1493 static Color attachmentBorderColor() { return Color(204, 204, 204); }
1494
1495 static Color attachmentProgressColor() { return Color(222, 222, 222); }
1496 const CGFloat attachmentProgressBorderThickness = 3;
1497
1498 const CGFloat attachmentProgressSize = 36;
1499 const CGFloat attachmentIconSize = 48;
1500
1501 const CGFloat attachmentItemMargin = 8;
1502
1503 const CGFloat attachmentWrappingTextMaximumWidth = 140;
1504 const CFIndex attachmentWrappingTextMaximumLineCount = 2;
1505
1506 static RetainPtr<CTFontRef> attachmentActionFont()
1507 {
1508     RetainPtr<CTFontDescriptorRef> fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(kCTUIFontTextStyleShortFootnote, RenderThemeIOS::contentSizeCategory(), 0));
1509     RetainPtr<CTFontDescriptorRef> emphasizedFontDescriptor = adoptCF(CTFontDescriptorCreateCopyWithAttributes(fontDescriptor.get(),
1510         (CFDictionaryRef)@{
1511             (id)kCTFontDescriptorTextStyleAttribute: (id)kCTFontDescriptorTextStyleEmphasized
1512     }));
1513     return adoptCF(CTFontCreateWithFontDescriptor(emphasizedFontDescriptor.get(), 0, nullptr));
1514 }
1515
1516 static UIColor *attachmentActionColor(const RenderAttachment& attachment)
1517 {
1518     return [getUIColorClass() colorWithCGColor:cachedCGColor(attachment.style().visitedDependentColor(CSSPropertyColor))];
1519 }
1520
1521 static RetainPtr<CTFontRef> attachmentTitleFont()
1522 {
1523     RetainPtr<CTFontDescriptorRef> fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(kCTUIFontTextStyleShortCaption1, RenderThemeIOS::contentSizeCategory(), 0));
1524     return adoptCF(CTFontCreateWithFontDescriptor(fontDescriptor.get(), 0, nullptr));
1525 }
1526
1527 static UIColor *attachmentTitleColor() { return [getUIColorClass() systemGrayColor]; }
1528
1529 static RetainPtr<CTFontRef> attachmentSubtitleFont() { return attachmentTitleFont(); }
1530 static UIColor *attachmentSubtitleColor() { return [getUIColorClass() systemGrayColor]; }
1531
1532 struct RenderAttachmentInfo {
1533     explicit RenderAttachmentInfo(const RenderAttachment&);
1534
1535     FloatRect iconRect;
1536     FloatRect attachmentRect;
1537     FloatRect progressRect;
1538
1539     BOOL hasProgress { NO };
1540     float progress;
1541
1542     RetainPtr<UIImage> icon;
1543
1544     int baseline { 0 };
1545
1546     struct LabelLine {
1547         FloatRect rect;
1548         RetainPtr<CTLineRef> line;
1549     };
1550     Vector<LabelLine> lines;
1551
1552     CGFloat contentYOrigin { 0 };
1553
1554 private:
1555     void buildWrappedLines(const String&, CTFontRef, UIColor *, unsigned maximumLineCount);
1556     void buildSingleLine(const String&, CTFontRef, UIColor *);
1557
1558     void addLine(CTLineRef);
1559 };
1560
1561 void RenderAttachmentInfo::addLine(CTLineRef line)
1562 {
1563     CGRect lineBounds = CTLineGetBoundsWithOptions(line, kCTLineBoundsExcludeTypographicLeading);
1564     CGFloat trailingWhitespaceWidth = CTLineGetTrailingWhitespaceWidth(line);
1565     CGFloat lineWidthIgnoringTrailingWhitespace = lineBounds.size.width - trailingWhitespaceWidth;
1566     CGFloat lineHeight = CGCeiling(lineBounds.size.height + lineBounds.origin.y);
1567
1568     CGFloat xOffset = (attachmentRect.width() / 2) - (lineWidthIgnoringTrailingWhitespace / 2);
1569     LabelLine labelLine;
1570     labelLine.line = line;
1571     labelLine.rect = FloatRect(xOffset, 0, lineWidthIgnoringTrailingWhitespace, lineHeight);
1572
1573     lines.append(labelLine);
1574 }
1575
1576 void RenderAttachmentInfo::buildWrappedLines(const String& text, CTFontRef font, UIColor *color, unsigned maximumLineCount)
1577 {
1578     if (text.isEmpty())
1579         return;
1580
1581     NSDictionary *textAttributes = @{
1582         (id)kCTFontAttributeName: (id)font,
1583         (id)kCTForegroundColorAttributeName: color
1584     };
1585     RetainPtr<NSAttributedString> attributedText = adoptNS([[NSAttributedString alloc] initWithString:text attributes:textAttributes]);
1586     RetainPtr<CTFramesetterRef> framesetter = adoptCF(CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attributedText.get()));
1587
1588     CFRange fitRange;
1589     CGSize textSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter.get(), CFRangeMake(0, 0), nullptr, CGSizeMake(attachmentWrappingTextMaximumWidth, CGFLOAT_MAX), &fitRange);
1590
1591     RetainPtr<CGPathRef> textPath = adoptCF(CGPathCreateWithRect(CGRectMake(0, 0, textSize.width, textSize.height), nullptr));
1592     RetainPtr<CTFrameRef> textFrame = adoptCF(CTFramesetterCreateFrame(framesetter.get(), fitRange, textPath.get(), nullptr));
1593
1594     CFArrayRef ctLines = CTFrameGetLines(textFrame.get());
1595     CFIndex lineCount = CFArrayGetCount(ctLines);
1596     if (!lineCount)
1597         return;
1598
1599     // Lay out and record the first (maximumLineCount - 1) lines.
1600     CFIndex lineIndex = 0;
1601     CFIndex nonTruncatedLineCount = std::min<CFIndex>(maximumLineCount - 1, lineCount);
1602     for (; lineIndex < nonTruncatedLineCount; ++lineIndex)
1603         addLine((CTLineRef)CFArrayGetValueAtIndex(ctLines, lineIndex));
1604
1605     if (lineIndex == lineCount)
1606         return;
1607
1608     // We had text that didn't fit in the first (maximumLineCount - 1) lines.
1609     // Combine it into one last line, and center-truncate it.
1610     CTLineRef firstRemainingLine = (CTLineRef)CFArrayGetValueAtIndex(ctLines, lineIndex);
1611     CFIndex remainingRangeStart = CTLineGetStringRange(firstRemainingLine).location;
1612     NSRange remainingRange = NSMakeRange(remainingRangeStart, [attributedText length] - remainingRangeStart);
1613     NSAttributedString *remainingString = [attributedText attributedSubstringFromRange:remainingRange];
1614     RetainPtr<CTLineRef> remainingLine = adoptCF(CTLineCreateWithAttributedString((CFAttributedStringRef)remainingString));
1615     RetainPtr<NSAttributedString> ellipsisString = adoptNS([[NSAttributedString alloc] initWithString:@"\u2026" attributes:textAttributes]);
1616     RetainPtr<CTLineRef> ellipsisLine = adoptCF(CTLineCreateWithAttributedString((CFAttributedStringRef)ellipsisString.get()));
1617     RetainPtr<CTLineRef> truncatedLine = adoptCF(CTLineCreateTruncatedLine(remainingLine.get(), attachmentWrappingTextMaximumWidth, kCTLineTruncationMiddle, ellipsisLine.get()));
1618
1619     if (!truncatedLine)
1620         truncatedLine = remainingLine;
1621
1622     addLine(truncatedLine.get());
1623 }
1624
1625 void RenderAttachmentInfo::buildSingleLine(const String& text, CTFontRef font, UIColor *color)
1626 {
1627     if (text.isEmpty())
1628         return;
1629
1630     NSDictionary *textAttributes = @{
1631         (id)kCTFontAttributeName: (id)font,
1632         (id)kCTForegroundColorAttributeName: color
1633     };
1634     RetainPtr<NSAttributedString> attributedText = adoptNS([[NSAttributedString alloc] initWithString:text attributes:textAttributes]);
1635
1636     addLine(adoptCF(CTLineCreateWithAttributedString((CFAttributedStringRef)attributedText.get())).get());
1637 }
1638
1639 static BOOL getAttachmentProgress(const RenderAttachment& attachment, float& progress)
1640 {
1641     auto& progressString = attachment.attachmentElement().attributeWithoutSynchronization(progressAttr);
1642     if (progressString.isEmpty())
1643         return NO;
1644     bool validProgress;
1645     progress = std::max<float>(std::min<float>(progressString.toFloat(&validProgress), 1), 0);
1646     return validProgress;
1647 }
1648
1649 static RetainPtr<UIImage> iconForAttachment(const RenderAttachment& attachment, FloatSize& size)
1650 {
1651     auto documentInteractionController = adoptNS([allocUIDocumentInteractionControllerInstance() init]);
1652
1653     String fileName;
1654     if (File* file = attachment.attachmentElement().file())
1655         fileName = file->name();
1656
1657     if (fileName.isEmpty())
1658         fileName = attachment.attachmentElement().attachmentTitle();
1659     [documentInteractionController setName:fileName];
1660
1661     String attachmentType = attachment.attachmentElement().attachmentType();
1662     if (!attachmentType.isEmpty()) {
1663         String UTI;
1664         if (isDeclaredUTI(attachmentType))
1665             UTI = attachmentType;
1666         else
1667             UTI = UTIFromMIMEType(attachmentType);
1668
1669         [documentInteractionController setUTI:static_cast<NSString *>(UTI)];
1670     }
1671
1672     NSArray *icons = [documentInteractionController icons];
1673     if (!icons.count)
1674         return nil;
1675
1676     RetainPtr<UIImage> result = icons.lastObject;
1677
1678     BOOL useHeightForClosestMatch = [result size].height > [result size].width;
1679     CGFloat bestMatchRatio = -1;
1680
1681     for (UIImage *icon in icons) {
1682         CGFloat iconSize = useHeightForClosestMatch ? icon.size.height : icon.size.width;
1683
1684         CGFloat matchRatio = (attachmentIconSize / iconSize) - 1.0f;
1685         if (matchRatio < 0.3f) {
1686             matchRatio = CGFAbs(matchRatio);
1687             if ((bestMatchRatio == -1) || (matchRatio < bestMatchRatio)) {
1688                 result = icon;
1689                 bestMatchRatio = matchRatio;
1690             }
1691         }
1692     }
1693
1694     CGFloat iconAspect = [result size].width / [result size].height;
1695     size = largestRectWithAspectRatioInsideRect(iconAspect, FloatRect(0, 0, attachmentIconSize, attachmentIconSize)).size();
1696
1697     return result;
1698 }
1699
1700 RenderAttachmentInfo::RenderAttachmentInfo(const RenderAttachment& attachment)
1701 {
1702     attachmentRect = FloatRect(0, 0, attachment.width().toFloat(), attachment.height().toFloat());
1703
1704     hasProgress = getAttachmentProgress(attachment, progress);
1705
1706     String title = attachment.attachmentElement().attachmentTitle();
1707     String action = attachment.attachmentElement().attributeWithoutSynchronization(actionAttr);
1708     String subtitle = attachment.attachmentElement().attributeWithoutSynchronization(subtitleAttr);
1709
1710     CGFloat yOffset = 0;
1711
1712     if (hasProgress) {
1713         progressRect = FloatRect((attachmentRect.width() / 2) - (attachmentProgressSize / 2), 0, attachmentProgressSize, attachmentProgressSize);
1714         yOffset += attachmentProgressSize + attachmentItemMargin;
1715     }
1716
1717     if (action.isEmpty() && !hasProgress) {
1718         FloatSize iconSize;
1719         icon = iconForAttachment(attachment, iconSize);
1720         if (icon) {
1721             iconRect = FloatRect(FloatPoint((attachmentRect.width() / 2) - (iconSize.width() / 2), 0), iconSize);
1722             yOffset += iconRect.height() + attachmentItemMargin;
1723         }
1724     } else
1725         buildWrappedLines(action, attachmentActionFont().get(), attachmentActionColor(attachment), attachmentWrappingTextMaximumLineCount);
1726
1727     bool forceSingleLineTitle = !action.isEmpty() || !subtitle.isEmpty() || hasProgress;
1728     buildWrappedLines(title, attachmentTitleFont().get(), attachmentTitleColor(), forceSingleLineTitle ? 1 : attachmentWrappingTextMaximumLineCount);
1729     buildSingleLine(subtitle, attachmentSubtitleFont().get(), attachmentSubtitleColor());
1730
1731     if (!lines.isEmpty()) {
1732         for (auto& line : lines) {
1733             line.rect.setY(yOffset);
1734             yOffset += line.rect.height() + attachmentItemMargin;
1735         }
1736     }
1737
1738     yOffset -= attachmentItemMargin;
1739
1740     contentYOrigin = (attachmentRect.height() / 2) - (yOffset / 2);
1741 }
1742
1743 LayoutSize RenderThemeIOS::attachmentIntrinsicSize(const RenderAttachment&) const
1744 {
1745     return LayoutSize(FloatSize(attachmentSize));
1746 }
1747
1748 int RenderThemeIOS::attachmentBaseline(const RenderAttachment& attachment) const
1749 {
1750     RenderAttachmentInfo info(attachment);
1751     return info.baseline;
1752 }
1753
1754 static void paintAttachmentIcon(GraphicsContext& context, RenderAttachmentInfo& info)
1755 {
1756     if (!info.icon)
1757         return;
1758
1759     RefPtr<Image> iconImage = BitmapImage::create([info.icon CGImage]);
1760     if (!iconImage)
1761         return;
1762
1763     context.drawImage(*iconImage, info.iconRect);
1764 }
1765
1766 static void paintAttachmentText(GraphicsContext& context, RenderAttachmentInfo& info)
1767 {
1768     for (const auto& line : info.lines) {
1769         GraphicsContextStateSaver saver(context);
1770
1771         context.translate(toFloatSize(line.rect.minXMaxYCorner()));
1772         context.scale(FloatSize(1, -1));
1773
1774         CGContextSetTextPosition(context.platformContext(), 0, 0);
1775         CTLineDraw(line.line.get(), context.platformContext());
1776     }
1777 }
1778
1779 static void paintAttachmentProgress(GraphicsContext& context, RenderAttachmentInfo& info)
1780 {
1781     GraphicsContextStateSaver saver(context);
1782
1783     context.setStrokeThickness(attachmentProgressBorderThickness);
1784     context.setStrokeColor(attachmentProgressColor());
1785     context.setFillColor(attachmentProgressColor());
1786     context.strokeEllipse(info.progressRect);
1787
1788     FloatPoint center = info.progressRect.center();
1789
1790     Path progressPath;
1791     progressPath.moveTo(center);
1792     progressPath.addLineTo(FloatPoint(center.x(), info.progressRect.y()));
1793     progressPath.addArc(center, info.progressRect.width() / 2, -M_PI_2, info.progress * 2 * M_PI - M_PI_2, 0);
1794     progressPath.closeSubpath();
1795     context.fillPath(progressPath);
1796 }
1797
1798 static Path attachmentBorderPath(RenderAttachmentInfo& info)
1799 {
1800     Path borderPath;
1801     borderPath.addRoundedRect(info.attachmentRect, FloatSize(attachmentBorderRadius, attachmentBorderRadius));
1802     return borderPath;
1803 }
1804
1805 static void paintAttachmentBorder(GraphicsContext& context, Path& borderPath)
1806 {
1807     context.setStrokeColor(attachmentBorderColor());
1808     context.setStrokeThickness(1);
1809     context.strokePath(borderPath);
1810 }
1811
1812 bool RenderThemeIOS::paintAttachment(const RenderObject& renderer, const PaintInfo& paintInfo, const IntRect& paintRect)
1813 {
1814     if (!is<RenderAttachment>(renderer))
1815         return false;
1816
1817     const RenderAttachment& attachment = downcast<RenderAttachment>(renderer);
1818
1819     RenderAttachmentInfo info(attachment);
1820
1821     GraphicsContext& context = paintInfo.context();
1822     GraphicsContextStateSaver saver(context);
1823
1824     context.translate(toFloatSize(paintRect.location()));
1825
1826     if (attachment.shouldDrawBorder()) {
1827         auto borderPath = attachmentBorderPath(info);
1828         paintAttachmentBorder(context, borderPath);
1829         context.clipPath(borderPath);
1830     }
1831
1832     context.translate(FloatSize(0, info.contentYOrigin));
1833
1834     if (info.hasProgress)
1835         paintAttachmentProgress(context, info);
1836     else if (info.icon)
1837         paintAttachmentIcon(context, info);
1838
1839     paintAttachmentText(context, info);
1840
1841     return true;
1842 }
1843
1844 #endif // ENABLE(ATTACHMENT_ELEMENT)
1845
1846 #if PLATFORM(WATCHOS)
1847
1848 String RenderThemeIOS::extraDefaultStyleSheet()
1849 {
1850     return "* { -webkit-text-size-adjust: auto; -webkit-hyphens: auto !important; }"_s;
1851 }
1852
1853 #endif
1854
1855 #if USE(SYSTEM_PREVIEW)
1856 static NSBundle *arKitBundle()
1857 {
1858     static NSBundle *arKitBundle;
1859     static dispatch_once_t onceToken;
1860     dispatch_once(&onceToken, ^{
1861 #if PLATFORM(IOS_SIMULATOR)
1862         dlopen("/System/Library/PrivateFrameworks/AssetViewer.framework/AssetViewer", RTLD_NOW);
1863         arKitBundle = [NSBundle bundleForClass:NSClassFromString(@"ASVThumbnailView")];
1864 #else
1865         arKitBundle = [NSBundle bundleWithURL:[NSURL fileURLWithPath:@"/System/Library/PrivateFrameworks/AssetViewer.framework"]];
1866 #endif
1867     });
1868
1869     return arKitBundle;
1870 }
1871
1872 static RetainPtr<CGPDFPageRef> loadARKitPDFPage(NSString *imageName)
1873 {
1874     NSURL *url = [arKitBundle() URLForResource:imageName withExtension:@"pdf"];
1875
1876     if (!url)
1877         return nullptr;
1878
1879     auto document = adoptCF(CGPDFDocumentCreateWithURL((CFURLRef)url));
1880     if (!document)
1881         return nullptr;
1882
1883     if (!CGPDFDocumentGetNumberOfPages(document.get()))
1884         return nullptr;
1885
1886     return CGPDFDocumentGetPage(document.get(), 1);
1887 };
1888
1889 static CGPDFPageRef systemPreviewLogo()
1890 {
1891     static CGPDFPageRef logoPage;
1892     static dispatch_once_t onceToken;
1893     dispatch_once(&onceToken, ^{
1894         logoPage = loadARKitPDFPage(@"ARKitBadge").leakRef();
1895     });
1896
1897     return logoPage;
1898 };
1899
1900 void RenderThemeIOS::paintSystemPreviewBadge(Image& image, const PaintInfo& paintInfo, const FloatRect& rect)
1901 {
1902     static const int largeBadgeDimension = 70;
1903     static const int largeBadgeOffset = 20;
1904
1905     static const int smallBadgeDimension = 35;
1906     static const int smallBadgeOffset = 8;
1907
1908     static const int minimumSizeForLargeBadge = 240;
1909
1910     bool useSmallBadge = rect.width() < minimumSizeForLargeBadge || rect.height() < minimumSizeForLargeBadge;
1911     int badgeOffset = useSmallBadge ? smallBadgeOffset : largeBadgeOffset;
1912     int badgeDimension = useSmallBadge ? smallBadgeDimension : largeBadgeDimension;
1913
1914     int minimumDimension = badgeDimension + 2 * badgeOffset;
1915     if (rect.width() < minimumDimension || rect.height() < minimumDimension)
1916         return;
1917
1918     CGRect absoluteBadgeRect = CGRectMake(rect.x() + rect.width() - badgeDimension - badgeOffset, rect.y() + badgeOffset, badgeDimension, badgeDimension);
1919     CGRect insetBadgeRect = CGRectMake(rect.width() - badgeDimension - badgeOffset, badgeOffset, badgeDimension, badgeDimension);
1920     CGRect badgeRect = CGRectMake(0, 0, badgeDimension, badgeDimension);
1921
1922     CIImage *inputImage = [CIImage imageWithCGImage:image.nativeImage().get()];
1923
1924     // Create a circle to be used for the clipping path in the badge, as well as the drop shadow.
1925     RetainPtr<CGPathRef> circle = adoptCF(CGPathCreateWithRoundedRect(absoluteBadgeRect, badgeDimension / 2, badgeDimension / 2, nullptr));
1926
1927     auto& graphicsContext = paintInfo.context();
1928     if (graphicsContext.paintingDisabled())
1929         return;
1930
1931     GraphicsContextStateSaver stateSaver(graphicsContext);
1932
1933     CGContextRef ctx = graphicsContext.platformContext();
1934     if (!ctx)
1935         return;
1936
1937     CGContextSaveGState(ctx);
1938
1939     // Draw a drop shadow around the circle.
1940     // Use the GraphicsContext function, because it calculates the blur radius in context space,
1941     // rather than screen space.
1942     Color shadowColor = Color { 0.f, 0.f, 0.f, 0.1f };
1943     graphicsContext.setShadow(FloatSize { }, 16, shadowColor);
1944
1945     // The circle must have an alpha channel value of 1 for the shadow color to appear.
1946     CGFloat circleColorComponents[4] = { 0, 0, 0, 1 };
1947     RetainPtr<CGColorRef> circleColor = adoptCF(CGColorCreate(sRGBColorSpaceRef(), circleColorComponents));
1948     CGContextSetFillColorWithColor(ctx, circleColor.get());
1949
1950     // Clip out the circle to only show the shadow.
1951     CGContextBeginPath(ctx);
1952     CGContextAddRect(ctx, rect);
1953     CGContextAddPath(ctx, circle.get());
1954     CGContextClosePath(ctx);
1955     CGContextEOClip(ctx);
1956
1957     // Draw a slightly smaller circle with a shadow, otherwise we'll see a fringe of the solid
1958     // black circle around the edges of the clipped path below.
1959     CGContextBeginPath(ctx);
1960     CGRect slightlySmallerAbsoluteBadgeRect = CGRectMake(absoluteBadgeRect.origin.x + 0.5, absoluteBadgeRect.origin.y + 0.5, badgeDimension - 1, badgeDimension - 1);
1961     RetainPtr<CGPathRef> slightlySmallerCircle = adoptCF(CGPathCreateWithRoundedRect(slightlySmallerAbsoluteBadgeRect, slightlySmallerAbsoluteBadgeRect.size.width / 2, slightlySmallerAbsoluteBadgeRect.size.height / 2, nullptr));
1962     CGContextAddPath(ctx, slightlySmallerCircle.get());
1963     CGContextClosePath(ctx);
1964     CGContextFillPath(ctx);
1965
1966     CGContextRestoreGState(ctx);
1967
1968     // Draw the blurred backdrop. Scale from intrinsic size to render size.
1969     CGAffineTransform transform = CGAffineTransformIdentity;
1970     transform = CGAffineTransformScale(transform, rect.width() / image.width(), rect.height() / image.height());
1971     CIImage *scaledImage = [inputImage imageByApplyingTransform:transform];
1972
1973     // CoreImage coordinates are y-up, so we need to flip the badge rectangle within the image frame.
1974     CGRect flippedInsetBadgeRect = CGRectMake(insetBadgeRect.origin.x, rect.height() - insetBadgeRect.origin.y - insetBadgeRect.size.height, badgeDimension, badgeDimension);
1975
1976     // Create a cropped region with pixel values extending outwards.
1977     CIImage *clampedImage = [scaledImage imageByClampingToRect:flippedInsetBadgeRect];
1978
1979     // Blur.
1980     CIImage *blurredImage = [clampedImage imageByApplyingGaussianBlurWithSigma:10];
1981
1982     // Saturate.
1983     CIFilter *saturationFilter = [CIFilter filterWithName:@"CIColorControls"];
1984     [saturationFilter setValue:blurredImage forKey:kCIInputImageKey];
1985     [saturationFilter setValue:@1.8 forKey:kCIInputSaturationKey];
1986
1987     // Tint.
1988     CIFilter *tintFilter1 = [CIFilter filterWithName:@"CIConstantColorGenerator"];
1989     CIColor *tintColor1 = [CIColor colorWithRed:1 green:1 blue:1 alpha:0.18];
1990     [tintFilter1 setValue:tintColor1 forKey:kCIInputColorKey];
1991
1992     // Blend the tint with the saturated output.
1993     CIFilter *sourceOverFilter = [CIFilter filterWithName:@"CISourceOverCompositing"];
1994     [sourceOverFilter setValue:tintFilter1.outputImage forKey:kCIInputImageKey];
1995     [sourceOverFilter setValue:saturationFilter.outputImage forKey:kCIInputBackgroundImageKey];
1996
1997     if (!m_ciContext)
1998         m_ciContext = [CIContext context];
1999
2000     RetainPtr<CGImageRef> cgImage;
2001 #if HAVE(IOSURFACE)
2002     // Crop the result to the badge location.
2003     CIImage *croppedImage = [sourceOverFilter.outputImage imageByCroppingToRect:flippedInsetBadgeRect];
2004     CIImage *translatedImage = [croppedImage imageByApplyingTransform:CGAffineTransformMakeTranslation(-flippedInsetBadgeRect.origin.x, -flippedInsetBadgeRect.origin.y)];
2005     IOSurfaceRef surface;
2006     if (useSmallBadge) {
2007         if (!m_smallBadgeSurface)
2008             m_smallBadgeSurface = IOSurface::create({ smallBadgeDimension, smallBadgeDimension }, sRGBColorSpaceRef());
2009         surface = m_smallBadgeSurface->surface();
2010     } else {
2011         if (!m_largeBadgeSurface)
2012             m_largeBadgeSurface = IOSurface::create({ largeBadgeDimension, largeBadgeDimension }, sRGBColorSpaceRef());
2013         surface = m_largeBadgeSurface->surface();
2014     }
2015     [m_ciContext.get() render:translatedImage toIOSurface:surface bounds:badgeRect colorSpace:sRGBColorSpaceRef()];
2016     cgImage = useSmallBadge ? m_smallBadgeSurface->createImage() : m_largeBadgeSurface->createImage();
2017 #else
2018     cgImage = adoptCF([m_ciContext.get() createCGImage:sourceOverFilter.outputImage fromRect:flippedInsetBadgeRect]);
2019 #endif
2020
2021     // Before we render the result, we should clip to a circle around the badge rectangle.
2022     CGContextSaveGState(ctx);
2023     CGContextBeginPath(ctx);
2024     CGContextAddPath(ctx, circle.get());
2025     CGContextClosePath(ctx);
2026     CGContextClip(ctx);
2027
2028     CGContextTranslateCTM(ctx, absoluteBadgeRect.origin.x, absoluteBadgeRect.origin.y);
2029     CGContextTranslateCTM(ctx, 0, badgeDimension);
2030     CGContextScaleCTM(ctx, 1, -1);
2031     CGContextDrawImage(ctx, badgeRect, cgImage.get());
2032
2033     if (auto logo = systemPreviewLogo()) {
2034         CGSize pdfSize = CGPDFPageGetBoxRect(logo, kCGPDFMediaBox).size;
2035         CGFloat scaleX = badgeDimension / pdfSize.width;
2036         CGFloat scaleY = badgeDimension / pdfSize.height;
2037         CGContextScaleCTM(ctx, scaleX, scaleY);
2038         CGContextDrawPDFPage(ctx, logo);
2039     }
2040
2041     CGContextFlush(ctx);
2042     CGContextRestoreGState(ctx);
2043 }
2044 #endif
2045
2046 } // namespace WebCore
2047
2048 #endif //PLATFORM(IOS)