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