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