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