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