Remove "virtual" from all lines that have both "virtual" and "override".
[WebKit-https.git] / Source / WebCore / rendering / RenderThemeIOS.mm
1 /*
2  * Copyright (C) 2005, 2006, 2007, 2008 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
28 #if PLATFORM(IOS)
29
30 #import "CSSPrimitiveValue.h"
31 #import "CSSToLengthConversionData.h"
32 #import "CSSValueKeywords.h"
33 #import "CoreTextSPI.h"
34 #import "DateComponents.h"
35 #import "Document.h"
36 #import "FloatRoundedRect.h"
37 #import "FontCache.h"
38 #import "FontCascade.h"
39 #import "Frame.h"
40 #import "FrameView.h"
41 #import "Gradient.h"
42 #import "GraphicsContext.h"
43 #import "GraphicsContextCG.h"
44 #import "HTMLInputElement.h"
45 #import "HTMLNames.h"
46 #import "HTMLSelectElement.h"
47 #import "Icon.h"
48 #import "LocalizedDateCache.h"
49 #import "NodeRenderStyle.h"
50 #import "Page.h"
51 #import "PaintInfo.h"
52 #import "PlatformLocale.h"
53 #import "RenderObject.h"
54 #import "RenderProgress.h"
55 #import "RenderStyle.h"
56 #import "RenderThemeIOS.h"
57 #import "RenderView.h"
58 #import "SoftLinking.h"
59 #import "UIKitSPI.h"
60 #import "UserAgentScripts.h"
61 #import "UserAgentStyleSheets.h"
62 #import "WebCoreThreadRun.h"
63 #import <CoreGraphics/CoreGraphics.h>
64 #import <objc/runtime.h>
65 #import <wtf/NeverDestroyed.h>
66 #import <wtf/RefPtr.h>
67 #import <wtf/StdLibExtras.h>
68
69 SOFT_LINK_FRAMEWORK(UIKit)
70 SOFT_LINK_CLASS(UIKit, UIApplication)
71 SOFT_LINK_CLASS(UIKit, UIColor)
72 SOFT_LINK_CONSTANT(UIKit, UIContentSizeCategoryDidChangeNotification, CFStringRef)
73 #define UIContentSizeCategoryDidChangeNotification getUIContentSizeCategoryDidChangeNotification()
74
75 @interface WebCoreRenderThemeBundle : NSObject
76 @end
77
78 @implementation WebCoreRenderThemeBundle
79 @end
80
81 namespace WebCore {
82
83 const float ControlBaseHeight = 20;
84 const float ControlBaseFontSize = 11;
85
86 struct IOSGradient {
87     float* start; // points to static float[4]
88     float* end; // points to static float[4]
89     IOSGradient(float start[4], float end[4])
90         : start(start)
91         , end(end)
92     {
93     }
94 };
95
96 typedef IOSGradient* IOSGradientRef;
97
98 enum Interpolation
99 {
100     LinearInterpolation,
101     ExponentialInterpolation
102 };
103
104 static void interpolateLinearGradient(void *info, const CGFloat *inData, CGFloat *outData)
105 {
106     IOSGradientRef gradient = static_cast<IOSGradientRef>(info);
107     float alpha = inData[0];
108     float inverse = 1.0f - alpha;
109
110     outData[0] = inverse * gradient->start[0] + alpha * gradient->end[0];
111     outData[1] = inverse * gradient->start[1] + alpha * gradient->end[1];
112     outData[2] = inverse * gradient->start[2] + alpha * gradient->end[2];
113     outData[3] = inverse * gradient->start[3] + alpha * gradient->end[3];
114 }
115
116 static void interpolateExponentialGradient(void *info, const CGFloat *inData, CGFloat *outData)
117 {
118     IOSGradientRef gradient = static_cast<IOSGradientRef>(info);
119     float a = inData[0];
120     for (int paintInfo = 0; paintInfo < 4; ++paintInfo) {
121         float end = logf(std::max(gradient->end[paintInfo], 0.01f));
122         float start = logf(std::max(gradient->start[paintInfo], 0.01f));
123         outData[paintInfo] = expf(start - (end + start) * a);
124     }
125 }
126
127 static CGFunctionRef getSharedFunctionRef(IOSGradientRef gradient, Interpolation interpolation)
128 {
129     CGFunctionRef function = nullptr;
130
131     static HashMap<IOSGradientRef, CGFunctionRef>* linearFunctionRefs;
132     static HashMap<IOSGradientRef, CGFunctionRef>* exponentialFunctionRefs;
133
134     if (interpolation == LinearInterpolation) {
135         if (!linearFunctionRefs)
136             linearFunctionRefs = new HashMap<IOSGradientRef, CGFunctionRef>;
137         else
138             function = linearFunctionRefs->get(gradient);
139     
140         if (!function) {
141             static struct CGFunctionCallbacks linearFunctionCallbacks =  { 0, interpolateLinearGradient, 0 };
142             linearFunctionRefs->set(gradient, function = CGFunctionCreate(gradient, 1, nullptr, 4, nullptr, &linearFunctionCallbacks));
143         }
144
145         return function;
146     }
147
148     if (!exponentialFunctionRefs)
149         exponentialFunctionRefs = new HashMap<IOSGradientRef, CGFunctionRef>;
150     else
151         function = exponentialFunctionRefs->get(gradient);
152
153     if (!function) {
154         static struct CGFunctionCallbacks exponentialFunctionCallbacks =  { 0, interpolateExponentialGradient, 0 };
155         exponentialFunctionRefs->set(gradient, function = CGFunctionCreate(gradient, 1, 0, 4, 0, &exponentialFunctionCallbacks));
156     }
157
158     return function;
159 }
160
161 static void drawAxialGradient(CGContextRef context, IOSGradientRef gradient, const FloatPoint& startPoint, const FloatPoint& stopPoint, Interpolation interpolation)
162 {
163     RetainPtr<CGShadingRef> shading = adoptCF(CGShadingCreateAxial(sRGBColorSpaceRef(), startPoint, stopPoint, getSharedFunctionRef(gradient, interpolation), false, false));
164     CGContextDrawShading(context, shading.get());
165 }
166
167 static void drawRadialGradient(CGContextRef context, IOSGradientRef gradient, const FloatPoint& startPoint, float startRadius, const FloatPoint& stopPoint, float stopRadius, Interpolation interpolation)
168 {
169     RetainPtr<CGShadingRef> shading = adoptCF(CGShadingCreateRadial(sRGBColorSpaceRef(), startPoint, startRadius, stopPoint, stopRadius, getSharedFunctionRef(gradient, interpolation), false, false));
170     CGContextDrawShading(context, shading.get());
171 }
172
173 enum IOSGradientType {
174     InsetGradient,
175     ShineGradient,
176     ShadeGradient,
177     ConvexGradient,
178     ConcaveGradient,
179     SliderTrackGradient,
180     ReadonlySliderTrackGradient,
181     SliderThumbOpaquePressedGradient,
182 };
183
184 static IOSGradientRef getInsetGradient()
185 {
186     static float end[4] = { 0 / 255.0, 0 / 255.0, 0 / 255.0, 0 };
187     static float start[4] = { 0 / 255.0, 0 / 255.0, 0 / 255.0, 0.2 };
188     static NeverDestroyed<IOSGradient> gradient(start, end);
189     return &gradient.get();
190 }
191
192 static IOSGradientRef getShineGradient()
193 {
194     static float end[4] = { 1, 1, 1, 0.8 };
195     static float start[4] = { 1, 1, 1, 0 };
196     static NeverDestroyed<IOSGradient> gradient(start, end);
197     return &gradient.get();
198 }
199
200 static IOSGradientRef getShadeGradient()
201 {
202     static float end[4] = { 178 / 255.0, 178 / 255.0, 178 / 255.0, 0.65 };
203     static float start[4] = { 252 / 255.0, 252 / 255.0, 252 / 255.0, 0.65 };
204     static NeverDestroyed<IOSGradient> gradient(start, end);
205     return &gradient.get();
206 }
207
208 static IOSGradientRef getConvexGradient()
209 {
210     static float end[4] = { 255 / 255.0, 255 / 255.0, 255 / 255.0, 0.05 };
211     static float start[4] = { 255 / 255.0, 255 / 255.0, 255 / 255.0, 0.43 };
212     static NeverDestroyed<IOSGradient> gradient(start, end);
213     return &gradient.get();
214 }
215
216 static IOSGradientRef getConcaveGradient()
217 {
218     static float end[4] = { 255 / 255.0, 255 / 255.0, 255 / 255.0, 0.46 };
219     static float start[4] = { 255 / 255.0, 255 / 255.0, 255 / 255.0, 0 };
220     static NeverDestroyed<IOSGradient> gradient(start, end);
221     return &gradient.get();
222 }
223
224 static IOSGradientRef getSliderTrackGradient()
225 {
226     static float end[4] = { 132 / 255.0, 132 / 255.0, 132 / 255.0, 1 };
227     static float start[4] = { 74 / 255.0, 77 / 255.0, 80 / 255.0, 1 };
228     static NeverDestroyed<IOSGradient> gradient(start, end);
229     return &gradient.get();
230 }
231
232 static IOSGradientRef getReadonlySliderTrackGradient()
233 {
234     static float end[4] = { 132 / 255.0, 132 / 255.0, 132 / 255.0, 0.4 };
235     static float start[4] = { 74 / 255.0, 77 / 255.0, 80 /255.0, 0.4 };
236     static NeverDestroyed<IOSGradient> gradient(start, end);
237     return &gradient.get();
238 }
239
240 static IOSGradientRef getSliderThumbOpaquePressedGradient()
241 {
242     static float end[4] = { 144 / 255.0, 144 / 255.0, 144 / 255.0, 1};
243     static float start[4] = { 55 / 255.0, 55 / 255.0, 55 / 255.0, 1 };
244     static NeverDestroyed<IOSGradient> gradient(start, end);
245     return &gradient.get();
246 }
247
248 static IOSGradientRef gradientWithName(IOSGradientType gradientType)
249 {
250     switch (gradientType) {
251     case InsetGradient:
252         return getInsetGradient();
253     case ShineGradient:
254         return getShineGradient();
255     case ShadeGradient:
256         return getShadeGradient();
257     case ConvexGradient:
258         return getConvexGradient();
259     case ConcaveGradient:
260         return getConcaveGradient();
261     case SliderTrackGradient:
262         return getSliderTrackGradient();
263     case ReadonlySliderTrackGradient:
264         return getReadonlySliderTrackGradient();
265     case SliderThumbOpaquePressedGradient:
266         return getSliderThumbOpaquePressedGradient();
267     }
268     ASSERT_NOT_REACHED();
269     return nullptr;
270 }
271
272 static void contentSizeCategoryDidChange(CFNotificationCenterRef, void*, CFStringRef name, const void*, CFDictionaryRef)
273 {
274     ASSERT_UNUSED(name, CFEqual(name, UIContentSizeCategoryDidChangeNotification));
275     WebThreadRun(^{
276         Page::updateStyleForAllPagesAfterGlobalChangeInEnvironment();
277     });
278 }
279
280 RenderThemeIOS::RenderThemeIOS()
281 {
282     CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), this, contentSizeCategoryDidChange, UIContentSizeCategoryDidChangeNotification, 0, CFNotificationSuspensionBehaviorDeliverImmediately);
283 }
284
285 PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page*)
286 {
287     static RenderTheme& renderTheme = RenderThemeIOS::create().leakRef();
288     return &renderTheme;
289 }
290
291 Ref<RenderTheme> RenderThemeIOS::create()
292 {
293     return adoptRef(*new RenderThemeIOS);
294 }
295
296 static String& _contentSizeCategory()
297 {
298     static NeverDestroyed<String> _contentSizeCategory;
299     return _contentSizeCategory.get();
300 }
301
302 CFStringRef RenderThemeIOS::contentSizeCategory()
303 {
304     if (!_contentSizeCategory().isNull())
305         return (__bridge CFStringRef)static_cast<NSString*>(_contentSizeCategory());
306     return (CFStringRef)[[getUIApplicationClass() sharedApplication] preferredContentSizeCategory];
307 }
308
309 void RenderThemeIOS::setContentSizeCategory(const String& contentSizeCategory)
310 {
311     _contentSizeCategory() = contentSizeCategory;
312 }
313
314 const Color& RenderThemeIOS::shadowColor() const
315 {
316     static Color color(0.0f, 0.0f, 0.0f, 0.7f);
317     return color;
318 }
319
320 FloatRect RenderThemeIOS::addRoundedBorderClip(const RenderObject& box, GraphicsContext& context, const IntRect& rect)
321 {
322     // To fix inner border bleeding issues <rdar://problem/9812507>, we clip to the outer border and assert that
323     // the border is opaque or transparent, unless we're checked because checked radio/checkboxes show no bleeding.
324     RenderStyle& style = box.style();
325     RoundedRect border = isChecked(box) ? style.getRoundedInnerBorderFor(rect) : style.getRoundedBorderFor(rect);
326
327     if (border.isRounded())
328         context.clipRoundedRect(FloatRoundedRect(border));
329     else
330         context.clip(border.rect());
331
332     if (isChecked(box)) {
333         ASSERT(style.visitedDependentColor(CSSPropertyBorderTopColor).alpha() % 255 == 0);
334         ASSERT(style.visitedDependentColor(CSSPropertyBorderRightColor).alpha() % 255 == 0);
335         ASSERT(style.visitedDependentColor(CSSPropertyBorderBottomColor).alpha() % 255 == 0);
336         ASSERT(style.visitedDependentColor(CSSPropertyBorderLeftColor).alpha() % 255 == 0);
337     }
338
339     return border.rect();
340 }
341
342 void RenderThemeIOS::adjustCheckboxStyle(StyleResolver&, RenderStyle& style, Element*) const
343 {
344     if (!style.width().isIntrinsicOrAuto() && !style.height().isAuto())
345         return;
346
347     Length length = Length(static_cast<int>(ceilf(std::max(style.fontSize(), 10))), Fixed);
348     
349     style.setWidth(length);
350     style.setHeight(length);
351 }
352
353 static CGPoint shortened(CGPoint start, CGPoint end, float width)
354 {
355     float x = end.x - start.x;
356     float y = end.y - start.y;
357     float ratio = (!x && !y) ? 0 : width / sqrtf(x * x + y * y);
358     return CGPointMake(start.x + x * ratio, start.y + y * ratio);
359 }
360
361 static void drawJoinedLines(CGContextRef context, CGPoint points[], unsigned count, bool antialias, CGLineCap lineCap)
362 {
363     CGContextSetShouldAntialias(context, antialias);
364     CGContextBeginPath(context);
365     CGContextSetLineCap(context, lineCap);
366     CGContextMoveToPoint(context, points[0].x, points[0].y);
367     
368     for (unsigned i = 1; i < count; ++i)
369         CGContextAddLineToPoint(context, points[i].x, points[i].y);
370
371     CGContextStrokePath(context);
372 }
373
374 bool RenderThemeIOS::paintCheckboxDecorations(const RenderObject& box, const PaintInfo& paintInfo, const IntRect& rect)
375 {
376     GraphicsContextStateSaver stateSaver(paintInfo.context());
377     FloatRect clip = addRoundedBorderClip(box, paintInfo.context(), rect);
378
379     float width = clip.width();
380     float height = clip.height();
381
382     CGContextRef cgContext = paintInfo.context().platformContext();
383     if (isChecked(box)) {
384         drawAxialGradient(cgContext, gradientWithName(ConcaveGradient), clip.location(), FloatPoint(clip.x(), clip.maxY()), LinearInterpolation);
385
386         static const float thicknessRatio = 2 / 14.0;
387         static const CGSize size = { 14.0f, 14.0f };
388         static const CGPoint pathRatios[3] = {
389             { 2.5f / size.width, 7.5f / size.height },
390             { 5.5f / size.width, 10.5f / size.height },
391             { 11.5f / size.width, 2.5f / size.height }
392         };
393
394         float lineWidth = std::min(width, height) * 2.0f * thicknessRatio;
395
396         CGPoint line[3] = {
397             CGPointMake(clip.x() + width * pathRatios[0].x, clip.y() + height * pathRatios[0].y),
398             CGPointMake(clip.x() + width * pathRatios[1].x, clip.y() + height * pathRatios[1].y),
399             CGPointMake(clip.x() + width * pathRatios[2].x, clip.y() + height * pathRatios[2].y)
400         };
401         CGPoint shadow[3] = {
402             shortened(line[0], line[1], lineWidth / 4.0f),
403             line[1],
404             shortened(line[2], line[1], lineWidth / 4.0f)
405         };
406
407         lineWidth = std::max<float>(lineWidth, 1);
408         CGContextSetLineWidth(cgContext, lineWidth);
409         CGContextSetStrokeColorWithColor(cgContext, cachedCGColor(Color(0.0f, 0.0f, 0.0f, 0.7f)));
410         drawJoinedLines(cgContext, shadow, 3, true, kCGLineCapSquare);
411
412         lineWidth = std::max<float>(std::min(clip.width(), clip.height()) * thicknessRatio, 1);
413         CGContextSetLineWidth(cgContext, lineWidth);
414         CGContextSetStrokeColorWithColor(cgContext, cachedCGColor(Color(1.0f, 1.0f, 1.0f, 240 / 255.0f)));
415         drawJoinedLines(cgContext, line, 3, true, kCGLineCapButt);
416     } else {
417         FloatPoint bottomCenter(clip.x() + clip.width() / 2.0f, clip.maxY());
418         drawAxialGradient(cgContext, gradientWithName(ShadeGradient), clip.location(), FloatPoint(clip.x(), clip.maxY()), LinearInterpolation);
419         drawRadialGradient(cgContext, gradientWithName(ShineGradient), bottomCenter, 0, bottomCenter, sqrtf((width * width) / 4.0f + height * height), ExponentialInterpolation);
420     }
421
422     return false;
423 }
424
425 int RenderThemeIOS::baselinePosition(const RenderBox& box) const
426 {
427     if (box.style().appearance() == CheckboxPart || box.style().appearance() == RadioPart)
428         return box.marginTop() + box.height() - 2; // The baseline is 2px up from the bottom of the checkbox/radio in AppKit.
429     if (box.style().appearance() == MenulistPart)
430         return box.marginTop() + box.height() - 5; // This is to match AppKit. There might be a better way to calculate this though.
431     return RenderTheme::baselinePosition(box);
432 }
433
434 bool RenderThemeIOS::isControlStyled(const RenderStyle& style, const BorderData& border, const FillLayer& background, const Color& backgroundColor) const
435 {
436     // Buttons and MenulistButtons are styled if they contain a background image.
437     if (style.appearance() == PushButtonPart || style.appearance() == MenulistButtonPart)
438         return !style.visitedDependentColor(CSSPropertyBackgroundColor).alpha() || style.backgroundLayers()->hasImage();
439
440     if (style.appearance() == TextFieldPart || style.appearance() == TextAreaPart)
441         return *style.backgroundLayers() != background;
442
443     return RenderTheme::isControlStyled(style, border, background, backgroundColor);
444 }
445
446 void RenderThemeIOS::adjustRadioStyle(StyleResolver&, RenderStyle& style, Element*) const
447 {
448     if (!style.width().isIntrinsicOrAuto() && !style.height().isAuto())
449         return;
450
451     Length length = Length(static_cast<int>(ceilf(std::max(style.fontSize(), 10))), Fixed);
452     style.setWidth(length);
453     style.setHeight(length);
454     style.setBorderRadius(IntSize(length.value() / 2.0f, length.value() / 2.0f));
455 }
456
457 bool RenderThemeIOS::paintRadioDecorations(const RenderObject& box, const PaintInfo& paintInfo, const IntRect& rect)
458 {
459     GraphicsContextStateSaver stateSaver(paintInfo.context());
460     FloatRect clip = addRoundedBorderClip(box, paintInfo.context(), rect);
461
462     CGContextRef cgContext = paintInfo.context().platformContext();
463     if (isChecked(box)) {
464         drawAxialGradient(cgContext, gradientWithName(ConcaveGradient), clip.location(), FloatPoint(clip.x(), clip.maxY()), LinearInterpolation);
465
466         // The inner circle is 6 / 14 the size of the surrounding circle, 
467         // leaving 8 / 14 around it. (8 / 14) / 2 = 2 / 7.
468
469         static const float InnerInverseRatio = 2 / 7.0;
470
471         clip.inflateX(-clip.width() * InnerInverseRatio);
472         clip.inflateY(-clip.height() * InnerInverseRatio);
473
474         paintInfo.context().drawRaisedEllipse(clip, Color::white, shadowColor());
475
476         FloatSize radius(clip.width() / 2.0f, clip.height() / 2.0f);
477         paintInfo.context().clipRoundedRect(FloatRoundedRect(clip, radius, radius, radius, radius));
478     }
479     FloatPoint bottomCenter(clip.x() + clip.width() / 2.0, clip.maxY());
480     drawAxialGradient(cgContext, gradientWithName(ShadeGradient), clip.location(), FloatPoint(clip.x(), clip.maxY()), LinearInterpolation);
481     drawRadialGradient(cgContext, gradientWithName(ShineGradient), bottomCenter, 0, bottomCenter, std::max(clip.width(), clip.height()), ExponentialInterpolation);
482     return false;
483 }
484
485 bool RenderThemeIOS::paintTextFieldDecorations(const RenderObject& box, const PaintInfo& paintInfo, const FloatRect& rect)
486 {
487     RenderStyle& style = box.style();
488     FloatPoint point(rect.x() + style.borderLeftWidth(), rect.y() + style.borderTopWidth());
489
490     GraphicsContextStateSaver stateSaver(paintInfo.context());
491
492     paintInfo.context().clipRoundedRect(style.getRoundedBorderFor(LayoutRect(rect)).pixelSnappedRoundedRectForPainting(box.document().deviceScaleFactor()));
493
494     // This gradient gets drawn black when printing.
495     // Do not draw the gradient if there is no visible top border.
496     bool topBorderIsInvisible = !style.hasBorder() || !style.borderTopWidth() || style.borderTopIsTransparent();
497     if (!box.view().printing() && !topBorderIsInvisible)
498         drawAxialGradient(paintInfo.context().platformContext(), gradientWithName(InsetGradient), point, FloatPoint(CGPointMake(point.x(), point.y() + 3.0f)), LinearInterpolation);
499     return false;
500 }
501
502 bool RenderThemeIOS::paintTextAreaDecorations(const RenderObject& box, const PaintInfo& paintInfo, const FloatRect& rect)
503 {
504     return paintTextFieldDecorations(box, paintInfo, rect);
505 }
506
507 const int MenuListMinHeight = 15;
508
509 const float MenuListBaseHeight = 20;
510 const float MenuListBaseFontSize = 11;
511
512 const float MenuListArrowWidth = 7;
513 const float MenuListArrowHeight = 6;
514 const float MenuListButtonPaddingRight = 19;
515
516 int RenderThemeIOS::popupInternalPaddingRight(const RenderStyle& style) const
517 {
518     if (style.appearance() == MenulistButtonPart)
519         return MenuListButtonPaddingRight + style.borderTopWidth();
520     return 0;
521 }
522
523 void RenderThemeIOS::adjustRoundBorderRadius(RenderStyle& style, RenderBox& box)
524 {
525     if (style.appearance() == NoControlPart || style.backgroundLayers()->hasImage())
526         return;
527
528     // FIXME: We should not be relying on border radius for the appearance of our controls <rdar://problem/7675493>
529     Length radiusWidth(static_cast<int>(std::min(box.width(), box.height()) / 2.0), Fixed);
530     Length radiusHeight(static_cast<int>(box.height() / 2.0), Fixed);
531     style.setBorderRadius(LengthSize(radiusWidth, radiusHeight));
532 }
533
534 static void applyCommonButtonPaddingToStyle(RenderStyle& style, Element& element)
535 {
536     Document& document = element.document();
537     RefPtr<CSSPrimitiveValue> emSize = CSSPrimitiveValue::create(0.5, CSSPrimitiveValue::CSS_EMS);
538     int pixels = emSize->computeLength<int>(CSSToLengthConversionData(&style, document.renderStyle(), document.renderView(), document.frame()->pageZoomFactor()));
539     style.setPaddingBox(LengthBox(0, pixels, 0, pixels));
540 }
541
542 static void adjustSelectListButtonStyle(RenderStyle& style, Element& element)
543 {
544     // Enforce "padding: 0 0.5em".
545     applyCommonButtonPaddingToStyle(style, element);
546
547     // Enforce "line-height: normal".
548     style.setLineHeight(Length(-100.0, Percent));
549 }
550     
551 class RenderThemeMeasureTextClient : public MeasureTextClient {
552 public:
553     RenderThemeMeasureTextClient(const FontCascade& font, RenderObject& renderObject, const RenderStyle& style)
554         : m_font(font)
555         , m_renderObject(renderObject)
556         , m_style(style)
557     {
558     }
559     float measureText(const String& string) const override
560     {
561         TextRun run = RenderBlock::constructTextRun(&m_renderObject, m_font, string, m_style, AllowTrailingExpansion | ForbidLeadingExpansion, DefaultTextRunFlags);
562         return m_font.width(run);
563     }
564 private:
565     const FontCascade& m_font;
566     RenderObject& m_renderObject;
567     const RenderStyle& m_style;
568 };
569
570 static void adjustInputElementButtonStyle(RenderStyle& style, HTMLInputElement& inputElement)
571 {
572     // Always Enforce "padding: 0 0.5em".
573     applyCommonButtonPaddingToStyle(style, inputElement);
574
575     // Don't adjust the style if the width is specified.
576     if (style.width().isFixed() && style.width().value() > 0)
577         return;
578
579     // Don't adjust for unsupported date input types.
580     DateComponents::Type dateType = inputElement.dateType();
581     if (dateType == DateComponents::Invalid || dateType == DateComponents::Week)
582         return;
583
584     // Enforce the width and set the box-sizing to content-box to not conflict with the padding.
585     FontCascade font = style.fontCascade();
586     
587     RenderObject* renderer = inputElement.renderer();
588     if (font.primaryFont().isSVGFont() && !renderer)
589         return;
590     
591     float maximumWidth = localizedDateCache().maximumWidthForDateType(dateType, font, RenderThemeMeasureTextClient(font, *renderer, style));
592
593     ASSERT(maximumWidth >= 0);
594
595     if (maximumWidth > 0) {    
596         int width = static_cast<int>(maximumWidth + MenuListButtonPaddingRight);
597         style.setWidth(Length(width, Fixed));
598         style.setBoxSizing(CONTENT_BOX);
599     }
600 }
601
602 void RenderThemeIOS::adjustMenuListButtonStyle(StyleResolver&, RenderStyle& style, Element* element) const
603 {
604     // Set the min-height to be at least MenuListMinHeight.
605     if (style.height().isAuto())
606         style.setMinHeight(Length(std::max(MenuListMinHeight, static_cast<int>(MenuListBaseHeight / MenuListBaseFontSize * style.fontDescription().computedSize())), Fixed));
607     else
608         style.setMinHeight(Length(MenuListMinHeight, Fixed));
609
610     if (!element)
611         return;
612
613     // Enforce some default styles in the case that this is a non-multiple <select> element,
614     // or a date input. We don't force these if this is just an element with
615     // "-webkit-appearance: menulist-button".
616     if (element->hasTagName(HTMLNames::selectTag) && !element->hasAttribute(HTMLNames::multipleAttr))
617         adjustSelectListButtonStyle(style, *element);
618     else if (element->hasTagName(HTMLNames::inputTag))
619         adjustInputElementButtonStyle(style, static_cast<HTMLInputElement&>(*element));
620 }
621
622 bool RenderThemeIOS::paintMenuListButtonDecorations(const RenderBox& box, const PaintInfo& paintInfo, const FloatRect& rect)
623 {
624     RenderStyle& style = box.style();
625     float borderTopWidth = style.borderTopWidth();
626     FloatRect clip(rect.x() + style.borderLeftWidth(), rect.y() + style.borderTopWidth(), rect.width() - style.borderLeftWidth() - style.borderRightWidth(), rect.height() - style.borderTopWidth() - style.borderBottomWidth());
627     CGContextRef cgContext = paintInfo.context().platformContext();
628
629     float adjustLeft = 0.5;
630     float adjustRight = 0.5;
631     float adjustTop = 0.5;
632     float adjustBottom = 0.5;
633
634     // Paint left-hand title portion.
635     {
636         FloatRect titleClip(clip.x() - adjustLeft, clip.y() - adjustTop, clip.width() - MenuListButtonPaddingRight + adjustLeft, clip.height() + adjustTop + adjustBottom);
637
638         GraphicsContextStateSaver stateSaver(paintInfo.context());
639
640         paintInfo.context().clipRoundedRect(FloatRoundedRect(titleClip,
641             FloatSize(valueForLength(style.borderTopLeftRadius().width(), rect.width()) - style.borderLeftWidth(), valueForLength(style.borderTopLeftRadius().height(), rect.height()) - style.borderTopWidth()), FloatSize(0, 0),
642             FloatSize(valueForLength(style.borderBottomLeftRadius().width(), rect.width()) - style.borderLeftWidth(), valueForLength(style.borderBottomLeftRadius().height(), rect.height()) - style.borderBottomWidth()), FloatSize(0, 0)));
643
644         drawAxialGradient(cgContext, gradientWithName(ShadeGradient), titleClip.location(), FloatPoint(titleClip.x(), titleClip.maxY()), LinearInterpolation);
645         drawAxialGradient(cgContext, gradientWithName(ShineGradient), FloatPoint(titleClip.x(), titleClip.maxY()), titleClip.location(), ExponentialInterpolation);
646     }
647
648     // Draw the separator after the initial padding.
649
650     float separator = clip.maxX() - MenuListButtonPaddingRight;
651
652     box.drawLineForBoxSide(paintInfo.context(), FloatRect(FloatPoint(separator - borderTopWidth, clip.y()), FloatPoint(separator, clip.maxY())), BSRight, style.visitedDependentColor(CSSPropertyBorderTopColor), style.borderTopStyle(), 0, 0);
653
654     FloatRect buttonClip(separator - adjustTop, clip.y() - adjustTop, MenuListButtonPaddingRight + adjustTop + adjustRight, clip.height() + adjustTop + adjustBottom);
655
656     // Now paint the button portion.
657     {
658         GraphicsContextStateSaver stateSaver(paintInfo.context());
659
660         paintInfo.context().clipRoundedRect(FloatRoundedRect(buttonClip,
661             FloatSize(0, 0), FloatSize(valueForLength(style.borderTopRightRadius().width(), rect.width()) - style.borderRightWidth(), valueForLength(style.borderTopRightRadius().height(), rect.height()) - style.borderTopWidth()),
662             FloatSize(0, 0), FloatSize(valueForLength(style.borderBottomRightRadius().width(), rect.width()) - style.borderRightWidth(), valueForLength(style.borderBottomRightRadius().height(), rect.height()) - style.borderBottomWidth())));
663
664         paintInfo.context().fillRect(buttonClip, style.visitedDependentColor(CSSPropertyBorderTopColor));
665
666         drawAxialGradient(cgContext, gradientWithName(isFocused(box) && !isReadOnlyControl(box) ? ConcaveGradient : ConvexGradient), buttonClip.location(), FloatPoint(buttonClip.x(), buttonClip.maxY()), LinearInterpolation);
667     }
668
669     // Paint Indicators.
670
671     if (box.isMenuList() && downcast<HTMLSelectElement>(box.element())->multiple()) {
672         int size = 2;
673         int count = 3;
674         int padding = 3;
675
676         FloatRect ellipse(buttonClip.x() + (buttonClip.width() - count * (size + padding) + padding) / 2.0, buttonClip.maxY() - 10.0, size, size);
677
678         for (int i = 0; i < count; ++i) {
679             paintInfo.context().drawRaisedEllipse(ellipse, Color::white, Color(0.0f, 0.0f, 0.0f, 0.5f));
680             ellipse.move(size + padding, 0);
681         }
682     }  else {
683         float centerX = floorf(buttonClip.x() + buttonClip.width() / 2.0) - 0.5;
684         float centerY = floorf(buttonClip.y() + buttonClip.height() * 3.0 / 8.0);
685
686         Vector<FloatPoint> arrow = {
687             { centerX - MenuListArrowWidth / 2, centerY },
688             { centerX + MenuListArrowWidth / 2, centerY },
689             { centerX, centerY + MenuListArrowHeight }
690         };
691
692         Vector<FloatPoint> shadow = {
693             { arrow[0].x(), arrow[0].y() + 1 },
694             { arrow[1].x(), arrow[1].y() + 1 },
695             { arrow[2].x(), arrow[2].y() + 1 }
696         };
697
698         float opacity = isReadOnlyControl(box) ? 0.2 : 0.5;
699         paintInfo.context().setStrokeColor(Color(0.0f, 0.0f, 0.0f, opacity));
700         paintInfo.context().setFillColor(Color(0.0f, 0.0f, 0.0f, opacity));
701         paintInfo.context().drawPath(Path::polygonPathFromPoints(shadow));
702
703         paintInfo.context().setStrokeColor(Color::white);
704         paintInfo.context().setFillColor(Color::white);
705         paintInfo.context().drawPath(Path::polygonPathFromPoints(arrow));
706     }
707
708     return false;
709 }
710
711 const CGFloat kTrackThickness = 4.0;
712 const CGFloat kTrackRadius = kTrackThickness / 2.0;
713 const int kDefaultSliderThumbSize = 16;
714
715 void RenderThemeIOS::adjustSliderTrackStyle(StyleResolver& selector, RenderStyle& style, Element* element) const
716 {
717     RenderTheme::adjustSliderTrackStyle(selector, style, element);
718
719     // FIXME: We should not be relying on border radius for the appearance of our controls <rdar://problem/7675493>
720     Length radiusWidth(static_cast<int>(kTrackRadius), Fixed);
721     Length radiusHeight(static_cast<int>(kTrackRadius), Fixed);
722     style.setBorderRadius(LengthSize(radiusWidth, radiusHeight));
723 }
724
725 bool RenderThemeIOS::paintSliderTrack(const RenderObject& box, const PaintInfo& paintInfo, const IntRect& rect)
726 {
727     IntRect trackClip = rect;
728     RenderStyle& style = box.style();
729
730     bool isHorizontal = true;
731     switch (style.appearance()) {
732     case SliderHorizontalPart:
733         isHorizontal = true;
734         // Inset slightly so the thumb covers the edge.
735         if (trackClip.width() > 2) {
736             trackClip.setWidth(trackClip.width() - 2);
737             trackClip.setX(trackClip.x() + 1);
738         }
739         trackClip.setHeight(static_cast<int>(kTrackThickness));
740         trackClip.setY(rect.y() + rect.height() / 2 - kTrackThickness / 2);
741         break;
742     case SliderVerticalPart:
743         isHorizontal = false;
744         // Inset slightly so the thumb covers the edge.
745         if (trackClip.height() > 2) {
746             trackClip.setHeight(trackClip.height() - 2);
747             trackClip.setY(trackClip.y() + 1);
748         }
749         trackClip.setWidth(kTrackThickness);
750         trackClip.setX(rect.x() + rect.width() / 2 - kTrackThickness / 2);
751         break;
752     default:
753         ASSERT_NOT_REACHED();
754     }
755
756     ASSERT(trackClip.width() >= 0);
757     ASSERT(trackClip.height() >= 0);
758     CGFloat cornerWidth = trackClip.width() < kTrackThickness ? trackClip.width() / 2.0f : kTrackRadius;
759     CGFloat cornerHeight = trackClip.height() < kTrackThickness ? trackClip.height() / 2.0f : kTrackRadius;
760
761     bool readonly = isReadOnlyControl(box);
762
763 #if ENABLE(DATALIST_ELEMENT)
764     paintSliderTicks(box, paintInfo, trackClip);
765 #endif
766
767     // Draw the track gradient.
768     {
769         GraphicsContextStateSaver stateSaver(paintInfo.context());
770
771         IntSize cornerSize(cornerWidth, cornerHeight);
772         FloatRoundedRect innerBorder(trackClip, cornerSize, cornerSize, cornerSize, cornerSize);
773         paintInfo.context().clipRoundedRect(innerBorder);
774
775         CGContextRef cgContext = paintInfo.context().platformContext();
776         IOSGradientRef gradient = readonly ? gradientWithName(ReadonlySliderTrackGradient) : gradientWithName(SliderTrackGradient);
777         if (isHorizontal)
778             drawAxialGradient(cgContext, gradient, trackClip.location(), FloatPoint(trackClip.x(), trackClip.maxY()), LinearInterpolation);
779         else
780             drawAxialGradient(cgContext, gradient, trackClip.location(), FloatPoint(trackClip.maxX(), trackClip.y()), LinearInterpolation);
781     }
782
783     // Draw the track border.
784     {
785         GraphicsContextStateSaver stateSaver(paintInfo.context());
786
787         CGContextRef cgContext = paintInfo.context().platformContext();
788         if (readonly)
789             paintInfo.context().setStrokeColor(Color(178, 178, 178));
790         else
791             paintInfo.context().setStrokeColor(Color(76, 76, 76));
792
793         RetainPtr<CGMutablePathRef> roundedRectPath = adoptCF(CGPathCreateMutable());
794         CGPathAddRoundedRect(roundedRectPath.get(), 0, trackClip, cornerWidth, cornerHeight);
795         CGContextAddPath(cgContext, roundedRectPath.get());
796         CGContextSetLineWidth(cgContext, 1);
797         CGContextStrokePath(cgContext);
798     }
799
800     return false;
801 }
802
803 void RenderThemeIOS::adjustSliderThumbSize(RenderStyle& style, Element*) const
804 {
805     if (style.appearance() != SliderThumbHorizontalPart && style.appearance() != SliderThumbVerticalPart)
806         return;
807
808     // Enforce "border-radius: 50%".
809     Length length(50.0f, Percent);
810     style.setBorderRadius(LengthSize(length, length));
811
812     // Enforce a 16x16 size if no size is provided.
813     if (style.width().isIntrinsicOrAuto() || style.height().isAuto()) {
814         Length length = Length(kDefaultSliderThumbSize, Fixed);
815         style.setWidth(length);
816         style.setHeight(length);
817     }
818 }
819
820 bool RenderThemeIOS::paintSliderThumbDecorations(const RenderObject& box, const PaintInfo& paintInfo, const IntRect& rect)
821 {
822     GraphicsContextStateSaver stateSaver(paintInfo.context());
823     FloatRect clip = addRoundedBorderClip(box, paintInfo.context(), rect);
824
825     CGContextRef cgContext = paintInfo.context().platformContext();
826     FloatPoint bottomCenter(clip.x() + clip.width() / 2.0f, clip.maxY());
827     if (isPressed(box))
828         drawAxialGradient(cgContext, gradientWithName(SliderThumbOpaquePressedGradient), clip.location(), FloatPoint(clip.x(), clip.maxY()), LinearInterpolation);
829     else {
830         drawAxialGradient(cgContext, gradientWithName(ShadeGradient), clip.location(), FloatPoint(clip.x(), clip.maxY()), LinearInterpolation);
831         drawRadialGradient(cgContext, gradientWithName(ShineGradient), bottomCenter, 0.0f, bottomCenter, std::max(clip.width(), clip.height()), ExponentialInterpolation);
832     }
833
834     return false;
835 }
836
837 double RenderThemeIOS::animationRepeatIntervalForProgressBar(RenderProgress&) const
838 {
839     return 0;
840 }
841
842 double RenderThemeIOS::animationDurationForProgressBar(RenderProgress&) const
843 {
844     return 0;
845 }
846
847 bool RenderThemeIOS::paintProgressBar(const RenderObject& renderer, const PaintInfo& paintInfo, const IntRect& rect)
848 {
849     if (!is<RenderProgress>(renderer))
850         return true;
851
852     const int progressBarHeight = 9;
853     const float verticalOffset = (rect.height() - progressBarHeight) / 2.0;
854
855     GraphicsContextStateSaver stateSaver(paintInfo.context());
856     if (rect.width() < 10 || rect.height() < 9) {
857         // The rect is smaller than the standard progress bar. We clip to the element's rect to avoid
858         // leaking pixels outside the repaint rect.
859         paintInfo.context().clip(rect);
860     }
861
862     // 1) Draw the progress bar track.
863     // 1.1) Draw the white background with grey gradient border.
864     GraphicsContext& context = paintInfo.context();
865     context.setStrokeThickness(0.68);
866     context.setStrokeStyle(SolidStroke);
867
868     const float verticalRenderingPosition = rect.y() + verticalOffset;
869     RefPtr<Gradient> strokeGradient = Gradient::create(FloatPoint(rect.x(), verticalRenderingPosition), FloatPoint(rect.x(), verticalRenderingPosition + progressBarHeight - 1));
870     strokeGradient->addColorStop(0.0, Color(0x8d, 0x8d, 0x8d));
871     strokeGradient->addColorStop(0.45, Color(0xee, 0xee, 0xee));
872     strokeGradient->addColorStop(0.55, Color(0xee, 0xee, 0xee));
873     strokeGradient->addColorStop(1.0, Color(0x8d, 0x8d, 0x8d));
874     context.setStrokeGradient(strokeGradient.releaseNonNull());
875
876     context.setFillColor(Color(255, 255, 255));
877
878     Path trackPath;
879     FloatRect trackRect(rect.x() + 0.25, verticalRenderingPosition + 0.25, rect.width() - 0.5, progressBarHeight - 0.5);
880     FloatSize roundedCornerRadius(5, 4);
881     trackPath.addRoundedRect(trackRect, roundedCornerRadius);
882     context.drawPath(trackPath);
883
884     // 1.2) Draw top gradient on the upper half. It is supposed to overlay the fill from the background and darker the stroked path.
885     FloatRect border(rect.x(), rect.y() + verticalOffset, rect.width(), progressBarHeight);
886     paintInfo.context().clipRoundedRect(FloatRoundedRect(border, roundedCornerRadius, roundedCornerRadius, roundedCornerRadius, roundedCornerRadius));
887
888     float upperGradientHeight = progressBarHeight / 2.;
889     RefPtr<Gradient> upperGradient = Gradient::create(FloatPoint(rect.x(), verticalRenderingPosition + 0.5), FloatPoint(rect.x(), verticalRenderingPosition + upperGradientHeight - 1.5));
890     upperGradient->addColorStop(0.0, Color(133, 133, 133, 188));
891     upperGradient->addColorStop(1.0, Color(18, 18, 18, 51));
892     context.setFillGradient(upperGradient.releaseNonNull());
893
894     context.fillRect(FloatRect(rect.x(), verticalRenderingPosition, rect.width(), upperGradientHeight));
895
896     const auto& renderProgress = downcast<RenderProgress>(renderer);
897     if (renderProgress.isDeterminate()) {
898         // 2) Draw the progress bar.
899         double position = clampTo(renderProgress.position(), 0.0, 1.0);
900         double barWidth = position * rect.width();
901         RefPtr<Gradient> barGradient = Gradient::create(FloatPoint(rect.x(), verticalRenderingPosition + 0.5), FloatPoint(rect.x(), verticalRenderingPosition + progressBarHeight - 1));
902         barGradient->addColorStop(0.0, Color(195, 217, 247));
903         barGradient->addColorStop(0.45, Color(118, 164, 228));
904         barGradient->addColorStop(0.49, Color(118, 164, 228));
905         barGradient->addColorStop(0.51, Color(36, 114, 210));
906         barGradient->addColorStop(0.55, Color(36, 114, 210));
907         barGradient->addColorStop(1.0, Color(57, 142, 244));
908         context.setFillGradient(barGradient.releaseNonNull());
909
910         RefPtr<Gradient> barStrokeGradient = Gradient::create(FloatPoint(rect.x(), verticalRenderingPosition), FloatPoint(rect.x(), verticalRenderingPosition + progressBarHeight - 1));
911         barStrokeGradient->addColorStop(0.0, Color(95, 107, 183));
912         barStrokeGradient->addColorStop(0.5, Color(66, 106, 174, 240));
913         barStrokeGradient->addColorStop(1.0, Color(38, 104, 166));
914         context.setStrokeGradient(barStrokeGradient.releaseNonNull());
915
916         Path barPath;
917         int left = rect.x();
918         if (!renderProgress.style().isLeftToRightDirection())
919             left = rect.maxX() - barWidth;
920         FloatRect barRect(left + 0.25, verticalRenderingPosition + 0.25, std::max(barWidth - 0.5, 0.0), progressBarHeight - 0.5);
921         barPath.addRoundedRect(barRect, roundedCornerRadius);
922         context.drawPath(barPath);
923     }
924
925     return false;
926 }
927
928 #if ENABLE(DATALIST_ELEMENT)
929 IntSize RenderThemeIOS::sliderTickSize() const
930 {
931     // FIXME: <rdar://problem/12271791> MERGEBOT: Correct values for slider tick of <input type="range"> elements (requires ENABLE_DATALIST_ELEMENT)
932     return IntSize(1, 3);
933 }
934
935 int RenderThemeIOS::sliderTickOffsetFromTrackCenter() const
936 {
937     // FIXME: <rdar://problem/12271791> MERGEBOT: Correct values for slider tick of <input type="range"> elements (requires ENABLE_DATALIST_ELEMENT)
938     return -9;
939 }
940 #endif
941
942 void RenderThemeIOS::adjustSearchFieldStyle(StyleResolver& selector, RenderStyle& style, Element* element) const
943 {
944     RenderTheme::adjustSearchFieldStyle(selector, style, element);
945
946     if (!element)
947         return;
948
949     if (!style.hasBorder())
950         return;
951
952     RenderBox* box = element->renderBox();
953     if (!box)
954         return;
955
956     adjustRoundBorderRadius(style, *box);
957 }
958
959 bool RenderThemeIOS::paintSearchFieldDecorations(const RenderObject& box, const PaintInfo& paintInfo, const IntRect& rect)
960 {
961     return paintTextFieldDecorations(box, paintInfo, rect);
962 }
963
964 void RenderThemeIOS::adjustButtonStyle(StyleResolver& selector, RenderStyle& style, Element* element) const
965 {
966     RenderTheme::adjustButtonStyle(selector, style, element);
967
968     // Set padding: 0 1.0em; on buttons.
969     // CSSPrimitiveValue::computeLengthInt only needs the element's style to calculate em lengths.
970     // Since the element might not be in a document, just pass nullptr for the root element style
971     // and the render view.
972     RefPtr<CSSPrimitiveValue> emSize = CSSPrimitiveValue::create(1.0, CSSPrimitiveValue::CSS_EMS);
973     int pixels = emSize->computeLength<int>(CSSToLengthConversionData(&style, nullptr, nullptr, 1.0, false));
974     style.setPaddingBox(LengthBox(0, pixels, 0, pixels));
975
976     if (!element)
977         return;
978
979     RenderBox* box = element->renderBox();
980     if (!box)
981         return;
982
983     adjustRoundBorderRadius(style, *box);
984 }
985
986 bool RenderThemeIOS::paintButtonDecorations(const RenderObject& box, const PaintInfo& paintInfo, const IntRect& rect)
987 {
988     return paintPushButtonDecorations(box, paintInfo, rect);
989 }
990
991 bool RenderThemeIOS::paintPushButtonDecorations(const RenderObject& box, const PaintInfo& paintInfo, const IntRect& rect)
992 {
993     GraphicsContextStateSaver stateSaver(paintInfo.context());
994     FloatRect clip = addRoundedBorderClip(box, paintInfo.context(), rect);
995
996     CGContextRef cgContext = paintInfo.context().platformContext();
997     if (box.style().visitedDependentColor(CSSPropertyBackgroundColor).isDark())
998         drawAxialGradient(cgContext, gradientWithName(ConvexGradient), clip.location(), FloatPoint(clip.x(), clip.maxY()), LinearInterpolation);
999     else {
1000         drawAxialGradient(cgContext, gradientWithName(ShadeGradient), clip.location(), FloatPoint(clip.x(), clip.maxY()), LinearInterpolation);
1001         drawAxialGradient(cgContext, gradientWithName(ShineGradient), FloatPoint(clip.x(), clip.maxY()), clip.location(), ExponentialInterpolation);
1002     }
1003     return false;
1004 }
1005
1006 void RenderThemeIOS::setButtonSize(RenderStyle& style) const
1007 {
1008     // If the width and height are both specified, then we have nothing to do.
1009     if (!style.width().isIntrinsicOrAuto() && !style.height().isAuto())
1010         return;
1011
1012     // Use the font size to determine the intrinsic width of the control.
1013     style.setHeight(Length(static_cast<int>(ControlBaseHeight / ControlBaseFontSize * style.fontDescription().computedSize()), Fixed));
1014 }
1015
1016 const int kThumbnailBorderStrokeWidth = 1;
1017 const int kThumbnailBorderCornerRadius = 1;
1018 const int kVisibleBackgroundImageWidth = 1;
1019 const int kMultipleThumbnailShrinkSize = 2;
1020
1021 bool RenderThemeIOS::paintFileUploadIconDecorations(const RenderObject&, const RenderObject& buttonRenderer, const PaintInfo& paintInfo, const IntRect& rect, Icon* icon, FileUploadDecorations fileUploadDecorations)
1022 {
1023     GraphicsContextStateSaver stateSaver(paintInfo.context());
1024
1025     IntSize cornerSize(kThumbnailBorderCornerRadius, kThumbnailBorderCornerRadius);
1026     Color pictureFrameColor = buttonRenderer.style().visitedDependentColor(CSSPropertyBorderTopColor);
1027
1028     IntRect thumbnailPictureFrameRect = rect;
1029     IntRect thumbnailRect = rect;
1030     thumbnailRect.contract(2 * kThumbnailBorderStrokeWidth, 2 * kThumbnailBorderStrokeWidth);
1031     thumbnailRect.move(kThumbnailBorderStrokeWidth, kThumbnailBorderStrokeWidth);
1032
1033     if (fileUploadDecorations == MultipleFiles) {
1034         // Smaller thumbnails for multiple selection appearance.
1035         thumbnailPictureFrameRect.contract(kMultipleThumbnailShrinkSize, kMultipleThumbnailShrinkSize);
1036         thumbnailRect.contract(kMultipleThumbnailShrinkSize, kMultipleThumbnailShrinkSize);
1037
1038         // Background picture frame and simple background icon with a gradient matching the button.
1039         Color backgroundImageColor = Color(buttonRenderer.style().visitedDependentColor(CSSPropertyBackgroundColor).rgb());
1040         paintInfo.context().fillRoundedRect(FloatRoundedRect(thumbnailPictureFrameRect, cornerSize, cornerSize, cornerSize, cornerSize), pictureFrameColor);
1041         paintInfo.context().fillRect(thumbnailRect, backgroundImageColor);
1042         {
1043             GraphicsContextStateSaver stateSaver2(paintInfo.context());
1044             CGContextRef cgContext = paintInfo.context().platformContext();
1045             paintInfo.context().clip(thumbnailRect);
1046             if (backgroundImageColor.isDark())
1047                 drawAxialGradient(cgContext, gradientWithName(ConvexGradient), thumbnailRect.location(), FloatPoint(thumbnailRect.x(), thumbnailRect.maxY()), LinearInterpolation);
1048             else {
1049                 drawAxialGradient(cgContext, gradientWithName(ShadeGradient), thumbnailRect.location(), FloatPoint(thumbnailRect.x(), thumbnailRect.maxY()), LinearInterpolation);
1050                 drawAxialGradient(cgContext, gradientWithName(ShineGradient), FloatPoint(thumbnailRect.x(), thumbnailRect.maxY()), thumbnailRect.location(), ExponentialInterpolation);
1051             }
1052         }
1053
1054         // Move the rects for the Foreground picture frame and icon.
1055         int inset = kVisibleBackgroundImageWidth + kThumbnailBorderStrokeWidth;
1056         thumbnailPictureFrameRect.move(inset, inset);
1057         thumbnailRect.move(inset, inset);
1058     }
1059
1060     // Foreground picture frame and icon.
1061     paintInfo.context().fillRoundedRect(FloatRoundedRect(thumbnailPictureFrameRect, cornerSize, cornerSize, cornerSize, cornerSize), pictureFrameColor);
1062     icon->paint(paintInfo.context(), thumbnailRect);
1063
1064     return false;
1065 }
1066
1067 Color RenderThemeIOS::platformActiveSelectionBackgroundColor() const
1068 {
1069     return Color::transparent;
1070 }
1071
1072 Color RenderThemeIOS::platformInactiveSelectionBackgroundColor() const
1073 {
1074     return Color::transparent;
1075 }
1076
1077 bool RenderThemeIOS::shouldHaveSpinButton(HTMLInputElement&) const
1078 {
1079     return false;
1080 }
1081
1082 bool RenderThemeIOS::shouldHaveCapsLockIndicator(HTMLInputElement&) const
1083 {
1084     return false;
1085 }
1086
1087 FontCascadeDescription& RenderThemeIOS::cachedSystemFontDescription(CSSValueID valueID) const
1088 {
1089     static NeverDestroyed<FontCascadeDescription> systemFont;
1090     static NeverDestroyed<FontCascadeDescription> headlineFont;
1091     static NeverDestroyed<FontCascadeDescription> bodyFont;
1092     static NeverDestroyed<FontCascadeDescription> subheadlineFont;
1093     static NeverDestroyed<FontCascadeDescription> footnoteFont;
1094     static NeverDestroyed<FontCascadeDescription> caption1Font;
1095     static NeverDestroyed<FontCascadeDescription> caption2Font;
1096     static NeverDestroyed<FontCascadeDescription> shortHeadlineFont;
1097     static NeverDestroyed<FontCascadeDescription> shortBodyFont;
1098     static NeverDestroyed<FontCascadeDescription> shortSubheadlineFont;
1099     static NeverDestroyed<FontCascadeDescription> shortFootnoteFont;
1100     static NeverDestroyed<FontCascadeDescription> shortCaption1Font;
1101     static NeverDestroyed<FontCascadeDescription> tallBodyFont;
1102     static NeverDestroyed<FontCascadeDescription> title1Font;
1103     static NeverDestroyed<FontCascadeDescription> title2Font;
1104     static NeverDestroyed<FontCascadeDescription> title3Font;
1105
1106     static CFStringRef userTextSize = contentSizeCategory();
1107
1108     if (userTextSize != contentSizeCategory()) {
1109         userTextSize = contentSizeCategory();
1110
1111         headlineFont.get().setIsAbsoluteSize(false);
1112         bodyFont.get().setIsAbsoluteSize(false);
1113         subheadlineFont.get().setIsAbsoluteSize(false);
1114         footnoteFont.get().setIsAbsoluteSize(false);
1115         caption1Font.get().setIsAbsoluteSize(false);
1116         caption2Font.get().setIsAbsoluteSize(false);
1117         shortHeadlineFont.get().setIsAbsoluteSize(false);
1118         shortBodyFont.get().setIsAbsoluteSize(false);
1119         shortSubheadlineFont.get().setIsAbsoluteSize(false);
1120         shortFootnoteFont.get().setIsAbsoluteSize(false);
1121         shortCaption1Font.get().setIsAbsoluteSize(false);
1122         tallBodyFont.get().setIsAbsoluteSize(false);
1123     }
1124
1125     switch (valueID) {
1126     case CSSValueAppleSystemHeadline:
1127         return headlineFont;
1128     case CSSValueAppleSystemBody:
1129         return bodyFont;
1130     case CSSValueAppleSystemTitle1:
1131         return title1Font;
1132     case CSSValueAppleSystemTitle2:
1133         return title2Font;
1134     case CSSValueAppleSystemTitle3:
1135         return title3Font;
1136     case CSSValueAppleSystemSubheadline:
1137         return subheadlineFont;
1138     case CSSValueAppleSystemFootnote:
1139         return footnoteFont;
1140     case CSSValueAppleSystemCaption1:
1141         return caption1Font;
1142     case CSSValueAppleSystemCaption2:
1143         return caption2Font;
1144         // Short version.
1145     case CSSValueAppleSystemShortHeadline:
1146         return shortHeadlineFont;
1147     case CSSValueAppleSystemShortBody:
1148         return shortBodyFont;
1149     case CSSValueAppleSystemShortSubheadline:
1150         return shortSubheadlineFont;
1151     case CSSValueAppleSystemShortFootnote:
1152         return shortFootnoteFont;
1153     case CSSValueAppleSystemShortCaption1:
1154         return shortCaption1Font;
1155         // Tall version.
1156     case CSSValueAppleSystemTallBody:
1157         return tallBodyFont;
1158     default:
1159         return systemFont;
1160     }
1161 }
1162
1163 void RenderThemeIOS::updateCachedSystemFontDescription(CSSValueID valueID, FontCascadeDescription& fontDescription) const
1164 {
1165     RetainPtr<CTFontDescriptorRef> fontDescriptor;
1166     CFStringRef textStyle;
1167     switch (valueID) {
1168     case CSSValueAppleSystemHeadline:
1169         textStyle = kCTUIFontTextStyleHeadline;
1170         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1171         break;
1172     case CSSValueAppleSystemBody:
1173         textStyle = kCTUIFontTextStyleBody;
1174         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1175         break;
1176     case CSSValueAppleSystemTitle1:
1177         textStyle = kCTUIFontTextStyleTitle1;
1178         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1179         break;
1180     case CSSValueAppleSystemTitle2:
1181         textStyle = kCTUIFontTextStyleTitle2;
1182         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1183         break;
1184     case CSSValueAppleSystemTitle3:
1185         textStyle = kCTUIFontTextStyleTitle3;
1186         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1187         break;
1188     case CSSValueAppleSystemSubheadline:
1189         textStyle = kCTUIFontTextStyleSubhead;
1190         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1191         break;
1192     case CSSValueAppleSystemFootnote:
1193         textStyle = kCTUIFontTextStyleFootnote;
1194         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1195         break;
1196     case CSSValueAppleSystemCaption1:
1197         textStyle = kCTUIFontTextStyleCaption1;
1198         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1199         break;
1200     case CSSValueAppleSystemCaption2:
1201         textStyle = kCTUIFontTextStyleCaption2;
1202         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1203         break;
1204
1205     // Short version.
1206     case CSSValueAppleSystemShortHeadline:
1207         textStyle = kCTUIFontTextStyleShortHeadline;
1208         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1209         break;
1210     case CSSValueAppleSystemShortBody:
1211         textStyle = kCTUIFontTextStyleShortBody;
1212         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1213         break;
1214     case CSSValueAppleSystemShortSubheadline:
1215         textStyle = kCTUIFontTextStyleShortSubhead;
1216         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1217         break;
1218     case CSSValueAppleSystemShortFootnote:
1219         textStyle = kCTUIFontTextStyleShortFootnote;
1220         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1221         break;
1222     case CSSValueAppleSystemShortCaption1:
1223         textStyle = kCTUIFontTextStyleShortCaption1;
1224         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1225         break;
1226
1227     // Tall version.
1228     case CSSValueAppleSystemTallBody:
1229         textStyle = kCTUIFontTextStyleTallBody;
1230         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1231         break;
1232
1233     default:
1234         textStyle = kCTFontDescriptorTextStyleEmphasized;
1235         fontDescriptor = adoptCF(CTFontDescriptorCreateForUIType(kCTFontUIFontSystem, 0, nullptr));
1236     }
1237
1238     ASSERT(fontDescriptor);
1239     RetainPtr<CTFontRef> font = adoptCF(CTFontCreateWithFontDescriptor(fontDescriptor.get(), 0, nullptr));
1240     font = preparePlatformFont(font.get(), fontDescription.textRenderingMode(), nullptr, nullptr, fontDescription.featureSettings(), fontDescription.variantSettings());
1241     fontDescription.setIsAbsoluteSize(true);
1242     fontDescription.setOneFamily(textStyle);
1243     fontDescription.setSpecifiedSize(CTFontGetSize(font.get()));
1244     fontDescription.setWeight(fontWeightFromCoreText(FontCache::weightOfCTFont(font.get())));
1245     fontDescription.setItalic(FontItalicOff);
1246 }
1247
1248 #if ENABLE(VIDEO)
1249 String RenderThemeIOS::mediaControlsStyleSheet()
1250 {
1251 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
1252     if (m_mediaControlsStyleSheet.isEmpty()) {
1253         StringBuilder builder;
1254         builder.append([NSString stringWithContentsOfFile:[[NSBundle bundleForClass:[WebCoreRenderThemeBundle class]] pathForResource:@"mediaControlsiOS" ofType:@"css"] encoding:NSUTF8StringEncoding error:nil]);
1255         m_mediaControlsStyleSheet = builder.toString();
1256     }
1257     return m_mediaControlsStyleSheet;
1258 #else
1259     return emptyString();
1260 #endif
1261 }
1262
1263 String RenderThemeIOS::mediaControlsScript()
1264 {
1265 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
1266     if (m_mediaControlsScript.isEmpty()) {
1267         StringBuilder scriptBuilder;
1268         scriptBuilder.append([NSString stringWithContentsOfFile:[[NSBundle bundleForClass:[WebCoreRenderThemeBundle class]] pathForResource:@"mediaControlsLocalizedStrings" ofType:@"js"] encoding:NSUTF8StringEncoding error:nil]);
1269         scriptBuilder.append([NSString stringWithContentsOfFile:[[NSBundle bundleForClass:[WebCoreRenderThemeBundle class]] pathForResource:@"mediaControlsApple" ofType:@"js"] encoding:NSUTF8StringEncoding error:nil]);
1270         scriptBuilder.append([NSString stringWithContentsOfFile:[[NSBundle bundleForClass:[WebCoreRenderThemeBundle class]] pathForResource:@"mediaControlsiOS" ofType:@"js"] encoding:NSUTF8StringEncoding error:nil]);
1271         m_mediaControlsScript = scriptBuilder.toString();
1272     }
1273     return m_mediaControlsScript;
1274 #else
1275     return emptyString();
1276 #endif
1277 }
1278 #endif // ENABLE(VIDEO)
1279
1280 Color RenderThemeIOS::systemColor(CSSValueID cssValueID) const
1281 {
1282     auto addResult = m_systemColorCache.add(cssValueID, Color());
1283     if (!addResult.isNewEntry)
1284         return addResult.iterator->value;
1285
1286     Color color;
1287     switch (cssValueID) {
1288     case CSSValueAppleWirelessPlaybackTargetActive:
1289         color = [getUIColorClass() systemBlueColor].CGColor;
1290         break;
1291     case CSSValueAppleSystemBlue:
1292         color = [getUIColorClass() systemBlueColor].CGColor;
1293         break;
1294     case CSSValueAppleSystemGray:
1295         color = [getUIColorClass() systemGrayColor].CGColor;
1296         break;
1297     case CSSValueAppleSystemGreen:
1298         color = [getUIColorClass() systemGreenColor].CGColor;
1299         break;
1300     case CSSValueAppleSystemOrange:
1301         color = [getUIColorClass() systemOrangeColor].CGColor;
1302         break;
1303     case CSSValueAppleSystemPink:
1304         color = [getUIColorClass() systemPinkColor].CGColor;
1305         break;
1306     case CSSValueAppleSystemRed:
1307         color = [getUIColorClass() systemRedColor].CGColor;
1308         break;
1309     case CSSValueAppleSystemYellow:
1310         color = [getUIColorClass() systemYellowColor].CGColor;
1311         break;
1312     default:
1313         break;
1314     }
1315
1316     if (!color.isValid())
1317         color = RenderTheme::systemColor(cssValueID);
1318
1319     addResult.iterator->value = color;
1320
1321     return addResult.iterator->value;
1322 }
1323
1324 }
1325
1326 #endif //PLATFORM(IOS)