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