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