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