Begin implementing <attachment> painting on iOS
[WebKit-https.git] / Source / WebCore / rendering / RenderThemeIOS.mm
1 /*
2  * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27
28 #if PLATFORM(IOS)
29
30 #import "BitmapImage.h"
31 #import "CSSPrimitiveValue.h"
32 #import "CSSToLengthConversionData.h"
33 #import "CSSValueKeywords.h"
34 #import "CoreTextSPI.h"
35 #import "DateComponents.h"
36 #import "Document.h"
37 #import "File.h"
38 #import "FloatRoundedRect.h"
39 #import "FontCache.h"
40 #import "FontCascade.h"
41 #import "Frame.h"
42 #import "FrameSelection.h"
43 #import "FrameView.h"
44 #import "GeometryUtilities.h"
45 #import "Gradient.h"
46 #import "GraphicsContext.h"
47 #import "GraphicsContextCG.h"
48 #import "HTMLAttachmentElement.h"
49 #import "HTMLInputElement.h"
50 #import "HTMLNames.h"
51 #import "HTMLSelectElement.h"
52 #import "Icon.h"
53 #import "LocalizedDateCache.h"
54 #import "NodeRenderStyle.h"
55 #import "Page.h"
56 #import "PaintInfo.h"
57 #import "PathUtilities.h"
58 #import "PlatformLocale.h"
59 #import "RenderAttachment.h"
60 #import "RenderObject.h"
61 #import "RenderProgress.h"
62 #import "RenderStyle.h"
63 #import "RenderThemeIOS.h"
64 #import "RenderView.h"
65 #import "SoftLinking.h"
66 #import "UIKitSPI.h"
67 #import "UTIUtilities.h"
68 #import "UserAgentScripts.h"
69 #import "UserAgentStyleSheets.h"
70 #import "WebCoreThreadRun.h"
71 #import <CoreGraphics/CoreGraphics.h>
72 #import <objc/runtime.h>
73 #import <wtf/NeverDestroyed.h>
74 #import <wtf/RefPtr.h>
75 #import <wtf/StdLibExtras.h>
76
77 SOFT_LINK_FRAMEWORK(MobileCoreServices)
78 SOFT_LINK_CLASS(MobileCoreServices, LSDocumentProxy)
79
80 SOFT_LINK_FRAMEWORK(UIKit)
81 SOFT_LINK_CLASS(UIKit, UIApplication)
82 SOFT_LINK_CLASS(UIKit, UIColor)
83 SOFT_LINK_CLASS(UIKit, UIDocumentInteractionController)
84 SOFT_LINK_CLASS(UIKit, UIFont)
85 SOFT_LINK_CLASS(UIKit, UIImage)
86 SOFT_LINK_CONSTANT(UIKit, UIContentSizeCategoryDidChangeNotification, CFStringRef)
87 SOFT_LINK_CONSTANT(UIKit, UIFontTextStyleFootnote, NSString *)
88 SOFT_LINK_CONSTANT(UIKit, UIFontTextStyleCaption1, NSString *)
89 #define UIContentSizeCategoryDidChangeNotification getUIContentSizeCategoryDidChangeNotification()
90
91 @interface WebCoreRenderThemeBundle : NSObject
92 @end
93
94 @implementation WebCoreRenderThemeBundle
95 @end
96
97 namespace WebCore {
98
99 using namespace HTMLNames;
100
101 const float ControlBaseHeight = 20;
102 const float ControlBaseFontSize = 11;
103
104 struct IOSGradient {
105     float* start; // points to static float[4]
106     float* end; // points to static float[4]
107     IOSGradient(float start[4], float end[4])
108         : start(start)
109         , end(end)
110     {
111     }
112 };
113
114 typedef IOSGradient* IOSGradientRef;
115
116 enum Interpolation
117 {
118     LinearInterpolation,
119     ExponentialInterpolation
120 };
121
122 static void interpolateLinearGradient(void *info, const CGFloat *inData, CGFloat *outData)
123 {
124     IOSGradientRef gradient = static_cast<IOSGradientRef>(info);
125     float alpha = inData[0];
126     float inverse = 1.0f - alpha;
127
128     outData[0] = inverse * gradient->start[0] + alpha * gradient->end[0];
129     outData[1] = inverse * gradient->start[1] + alpha * gradient->end[1];
130     outData[2] = inverse * gradient->start[2] + alpha * gradient->end[2];
131     outData[3] = inverse * gradient->start[3] + alpha * gradient->end[3];
132 }
133
134 static void interpolateExponentialGradient(void *info, const CGFloat *inData, CGFloat *outData)
135 {
136     IOSGradientRef gradient = static_cast<IOSGradientRef>(info);
137     float a = inData[0];
138     for (int paintInfo = 0; paintInfo < 4; ++paintInfo) {
139         float end = logf(std::max(gradient->end[paintInfo], 0.01f));
140         float start = logf(std::max(gradient->start[paintInfo], 0.01f));
141         outData[paintInfo] = expf(start - (end + start) * a);
142     }
143 }
144
145 static CGFunctionRef getSharedFunctionRef(IOSGradientRef gradient, Interpolation interpolation)
146 {
147     CGFunctionRef function = nullptr;
148
149     static HashMap<IOSGradientRef, CGFunctionRef>* linearFunctionRefs;
150     static HashMap<IOSGradientRef, CGFunctionRef>* exponentialFunctionRefs;
151
152     if (interpolation == LinearInterpolation) {
153         if (!linearFunctionRefs)
154             linearFunctionRefs = new HashMap<IOSGradientRef, CGFunctionRef>;
155         else
156             function = linearFunctionRefs->get(gradient);
157     
158         if (!function) {
159             static struct CGFunctionCallbacks linearFunctionCallbacks =  { 0, interpolateLinearGradient, 0 };
160             linearFunctionRefs->set(gradient, function = CGFunctionCreate(gradient, 1, nullptr, 4, nullptr, &linearFunctionCallbacks));
161         }
162
163         return function;
164     }
165
166     if (!exponentialFunctionRefs)
167         exponentialFunctionRefs = new HashMap<IOSGradientRef, CGFunctionRef>;
168     else
169         function = exponentialFunctionRefs->get(gradient);
170
171     if (!function) {
172         static struct CGFunctionCallbacks exponentialFunctionCallbacks =  { 0, interpolateExponentialGradient, 0 };
173         exponentialFunctionRefs->set(gradient, function = CGFunctionCreate(gradient, 1, 0, 4, 0, &exponentialFunctionCallbacks));
174     }
175
176     return function;
177 }
178
179 static void drawAxialGradient(CGContextRef context, IOSGradientRef gradient, const FloatPoint& startPoint, const FloatPoint& stopPoint, Interpolation interpolation)
180 {
181     RetainPtr<CGShadingRef> shading = adoptCF(CGShadingCreateAxial(sRGBColorSpaceRef(), startPoint, stopPoint, getSharedFunctionRef(gradient, interpolation), false, false));
182     CGContextDrawShading(context, shading.get());
183 }
184
185 static void drawRadialGradient(CGContextRef context, IOSGradientRef gradient, const FloatPoint& startPoint, float startRadius, const FloatPoint& stopPoint, float stopRadius, Interpolation interpolation)
186 {
187     RetainPtr<CGShadingRef> shading = adoptCF(CGShadingCreateRadial(sRGBColorSpaceRef(), startPoint, startRadius, stopPoint, stopRadius, getSharedFunctionRef(gradient, interpolation), false, false));
188     CGContextDrawShading(context, shading.get());
189 }
190
191 enum IOSGradientType {
192     InsetGradient,
193     ShineGradient,
194     ShadeGradient,
195     ConvexGradient,
196     ConcaveGradient,
197     SliderTrackGradient,
198     ReadonlySliderTrackGradient,
199     SliderThumbOpaquePressedGradient,
200 };
201
202 static IOSGradientRef getInsetGradient()
203 {
204     static float end[4] = { 0 / 255.0, 0 / 255.0, 0 / 255.0, 0 };
205     static float start[4] = { 0 / 255.0, 0 / 255.0, 0 / 255.0, 0.2 };
206     static NeverDestroyed<IOSGradient> gradient(start, end);
207     return &gradient.get();
208 }
209
210 static IOSGradientRef getShineGradient()
211 {
212     static float end[4] = { 1, 1, 1, 0.8 };
213     static float start[4] = { 1, 1, 1, 0 };
214     static NeverDestroyed<IOSGradient> gradient(start, end);
215     return &gradient.get();
216 }
217
218 static IOSGradientRef getShadeGradient()
219 {
220     static float end[4] = { 178 / 255.0, 178 / 255.0, 178 / 255.0, 0.65 };
221     static float start[4] = { 252 / 255.0, 252 / 255.0, 252 / 255.0, 0.65 };
222     static NeverDestroyed<IOSGradient> gradient(start, end);
223     return &gradient.get();
224 }
225
226 static IOSGradientRef getConvexGradient()
227 {
228     static float end[4] = { 255 / 255.0, 255 / 255.0, 255 / 255.0, 0.05 };
229     static float start[4] = { 255 / 255.0, 255 / 255.0, 255 / 255.0, 0.43 };
230     static NeverDestroyed<IOSGradient> gradient(start, end);
231     return &gradient.get();
232 }
233
234 static IOSGradientRef getConcaveGradient()
235 {
236     static float end[4] = { 255 / 255.0, 255 / 255.0, 255 / 255.0, 0.46 };
237     static float start[4] = { 255 / 255.0, 255 / 255.0, 255 / 255.0, 0 };
238     static NeverDestroyed<IOSGradient> gradient(start, end);
239     return &gradient.get();
240 }
241
242 static IOSGradientRef getSliderTrackGradient()
243 {
244     static float end[4] = { 132 / 255.0, 132 / 255.0, 132 / 255.0, 1 };
245     static float start[4] = { 74 / 255.0, 77 / 255.0, 80 / 255.0, 1 };
246     static NeverDestroyed<IOSGradient> gradient(start, end);
247     return &gradient.get();
248 }
249
250 static IOSGradientRef getReadonlySliderTrackGradient()
251 {
252     static float end[4] = { 132 / 255.0, 132 / 255.0, 132 / 255.0, 0.4 };
253     static float start[4] = { 74 / 255.0, 77 / 255.0, 80 /255.0, 0.4 };
254     static NeverDestroyed<IOSGradient> gradient(start, end);
255     return &gradient.get();
256 }
257
258 static IOSGradientRef getSliderThumbOpaquePressedGradient()
259 {
260     static float end[4] = { 144 / 255.0, 144 / 255.0, 144 / 255.0, 1};
261     static float start[4] = { 55 / 255.0, 55 / 255.0, 55 / 255.0, 1 };
262     static NeverDestroyed<IOSGradient> gradient(start, end);
263     return &gradient.get();
264 }
265
266 static IOSGradientRef gradientWithName(IOSGradientType gradientType)
267 {
268     switch (gradientType) {
269     case InsetGradient:
270         return getInsetGradient();
271     case ShineGradient:
272         return getShineGradient();
273     case ShadeGradient:
274         return getShadeGradient();
275     case ConvexGradient:
276         return getConvexGradient();
277     case ConcaveGradient:
278         return getConcaveGradient();
279     case SliderTrackGradient:
280         return getSliderTrackGradient();
281     case ReadonlySliderTrackGradient:
282         return getReadonlySliderTrackGradient();
283     case SliderThumbOpaquePressedGradient:
284         return getSliderThumbOpaquePressedGradient();
285     }
286     ASSERT_NOT_REACHED();
287     return nullptr;
288 }
289
290 static void contentSizeCategoryDidChange(CFNotificationCenterRef, void*, CFStringRef name, const void*, CFDictionaryRef)
291 {
292     ASSERT_UNUSED(name, CFEqual(name, UIContentSizeCategoryDidChangeNotification));
293     WebThreadRun(^{
294         Page::updateStyleForAllPagesAfterGlobalChangeInEnvironment();
295     });
296 }
297
298 RenderThemeIOS::RenderThemeIOS()
299 {
300     CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), this, contentSizeCategoryDidChange, UIContentSizeCategoryDidChangeNotification, 0, CFNotificationSuspensionBehaviorDeliverImmediately);
301 }
302
303 PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page*)
304 {
305     static RenderTheme& renderTheme = RenderThemeIOS::create().leakRef();
306     return &renderTheme;
307 }
308
309 Ref<RenderTheme> RenderThemeIOS::create()
310 {
311     return adoptRef(*new RenderThemeIOS);
312 }
313
314 static String& _contentSizeCategory()
315 {
316     static NeverDestroyed<String> _contentSizeCategory;
317     return _contentSizeCategory.get();
318 }
319
320 CFStringRef RenderThemeIOS::contentSizeCategory()
321 {
322     if (!_contentSizeCategory().isNull())
323         return (__bridge CFStringRef)static_cast<NSString*>(_contentSizeCategory());
324     return (CFStringRef)[[getUIApplicationClass() sharedApplication] preferredContentSizeCategory];
325 }
326
327 void RenderThemeIOS::setContentSizeCategory(const String& contentSizeCategory)
328 {
329     _contentSizeCategory() = contentSizeCategory;
330 }
331
332 const Color& RenderThemeIOS::shadowColor() const
333 {
334     static Color color(0.0f, 0.0f, 0.0f, 0.7f);
335     return color;
336 }
337
338 FloatRect RenderThemeIOS::addRoundedBorderClip(const RenderObject& box, GraphicsContext& context, const IntRect& rect)
339 {
340     // To fix inner border bleeding issues <rdar://problem/9812507>, we clip to the outer border and assert that
341     // the border is opaque or transparent, unless we're checked because checked radio/checkboxes show no bleeding.
342     RenderStyle& style = box.style();
343     RoundedRect border = isChecked(box) ? style.getRoundedInnerBorderFor(rect) : style.getRoundedBorderFor(rect);
344
345     if (border.isRounded())
346         context.clipRoundedRect(FloatRoundedRect(border));
347     else
348         context.clip(border.rect());
349
350     if (isChecked(box)) {
351         ASSERT(style.visitedDependentColor(CSSPropertyBorderTopColor).alpha() % 255 == 0);
352         ASSERT(style.visitedDependentColor(CSSPropertyBorderRightColor).alpha() % 255 == 0);
353         ASSERT(style.visitedDependentColor(CSSPropertyBorderBottomColor).alpha() % 255 == 0);
354         ASSERT(style.visitedDependentColor(CSSPropertyBorderLeftColor).alpha() % 255 == 0);
355     }
356
357     return border.rect();
358 }
359
360 void RenderThemeIOS::adjustCheckboxStyle(StyleResolver&, RenderStyle& style, Element*) const
361 {
362     if (!style.width().isIntrinsicOrAuto() && !style.height().isAuto())
363         return;
364
365     Length length = Length(static_cast<int>(ceilf(std::max(style.fontSize(), 10))), Fixed);
366     
367     style.setWidth(length);
368     style.setHeight(length);
369 }
370
371 static CGPoint shortened(CGPoint start, CGPoint end, float width)
372 {
373     float x = end.x - start.x;
374     float y = end.y - start.y;
375     float ratio = (!x && !y) ? 0 : width / sqrtf(x * x + y * y);
376     return CGPointMake(start.x + x * ratio, start.y + y * ratio);
377 }
378
379 static void drawJoinedLines(CGContextRef context, CGPoint points[], unsigned count, bool antialias, CGLineCap lineCap)
380 {
381     CGContextSetShouldAntialias(context, antialias);
382     CGContextBeginPath(context);
383     CGContextSetLineCap(context, lineCap);
384     CGContextMoveToPoint(context, points[0].x, points[0].y);
385     
386     for (unsigned i = 1; i < count; ++i)
387         CGContextAddLineToPoint(context, points[i].x, points[i].y);
388
389     CGContextStrokePath(context);
390 }
391
392 bool RenderThemeIOS::paintCheckboxDecorations(const RenderObject& box, const PaintInfo& paintInfo, const IntRect& rect)
393 {
394     GraphicsContextStateSaver stateSaver(paintInfo.context());
395     FloatRect clip = addRoundedBorderClip(box, paintInfo.context(), rect);
396
397     float width = clip.width();
398     float height = clip.height();
399
400     CGContextRef cgContext = paintInfo.context().platformContext();
401     if (isChecked(box)) {
402         drawAxialGradient(cgContext, gradientWithName(ConcaveGradient), clip.location(), FloatPoint(clip.x(), clip.maxY()), LinearInterpolation);
403
404         static const float thicknessRatio = 2 / 14.0;
405         static const CGSize size = { 14.0f, 14.0f };
406         static const CGPoint pathRatios[3] = {
407             { 2.5f / size.width, 7.5f / size.height },
408             { 5.5f / size.width, 10.5f / size.height },
409             { 11.5f / size.width, 2.5f / size.height }
410         };
411
412         float lineWidth = std::min(width, height) * 2.0f * thicknessRatio;
413
414         CGPoint line[3] = {
415             CGPointMake(clip.x() + width * pathRatios[0].x, clip.y() + height * pathRatios[0].y),
416             CGPointMake(clip.x() + width * pathRatios[1].x, clip.y() + height * pathRatios[1].y),
417             CGPointMake(clip.x() + width * pathRatios[2].x, clip.y() + height * pathRatios[2].y)
418         };
419         CGPoint shadow[3] = {
420             shortened(line[0], line[1], lineWidth / 4.0f),
421             line[1],
422             shortened(line[2], line[1], lineWidth / 4.0f)
423         };
424
425         lineWidth = std::max<float>(lineWidth, 1);
426         CGContextSetLineWidth(cgContext, lineWidth);
427         CGContextSetStrokeColorWithColor(cgContext, cachedCGColor(Color(0.0f, 0.0f, 0.0f, 0.7f)));
428         drawJoinedLines(cgContext, shadow, 3, true, kCGLineCapSquare);
429
430         lineWidth = std::max<float>(std::min(clip.width(), clip.height()) * thicknessRatio, 1);
431         CGContextSetLineWidth(cgContext, lineWidth);
432         CGContextSetStrokeColorWithColor(cgContext, cachedCGColor(Color(1.0f, 1.0f, 1.0f, 240 / 255.0f)));
433         drawJoinedLines(cgContext, line, 3, true, kCGLineCapButt);
434     } else {
435         FloatPoint bottomCenter(clip.x() + clip.width() / 2.0f, clip.maxY());
436         drawAxialGradient(cgContext, gradientWithName(ShadeGradient), clip.location(), FloatPoint(clip.x(), clip.maxY()), LinearInterpolation);
437         drawRadialGradient(cgContext, gradientWithName(ShineGradient), bottomCenter, 0, bottomCenter, sqrtf((width * width) / 4.0f + height * height), ExponentialInterpolation);
438     }
439
440     return false;
441 }
442
443 int RenderThemeIOS::baselinePosition(const RenderBox& box) const
444 {
445     if (box.style().appearance() == CheckboxPart || box.style().appearance() == RadioPart)
446         return box.marginTop() + box.height() - 2; // The baseline is 2px up from the bottom of the checkbox/radio in AppKit.
447     if (box.style().appearance() == MenulistPart)
448         return box.marginTop() + box.height() - 5; // This is to match AppKit. There might be a better way to calculate this though.
449     return RenderTheme::baselinePosition(box);
450 }
451
452 bool RenderThemeIOS::isControlStyled(const RenderStyle& style, const BorderData& border, const FillLayer& background, const Color& backgroundColor) const
453 {
454     // Buttons and MenulistButtons are styled if they contain a background image.
455     if (style.appearance() == PushButtonPart || style.appearance() == MenulistButtonPart)
456         return !style.visitedDependentColor(CSSPropertyBackgroundColor).alpha() || style.backgroundLayers()->hasImage();
457
458     if (style.appearance() == TextFieldPart || style.appearance() == TextAreaPart)
459         return *style.backgroundLayers() != background;
460
461     return RenderTheme::isControlStyled(style, border, background, backgroundColor);
462 }
463
464 void RenderThemeIOS::adjustRadioStyle(StyleResolver&, RenderStyle& style, Element*) const
465 {
466     if (!style.width().isIntrinsicOrAuto() && !style.height().isAuto())
467         return;
468
469     Length length = Length(static_cast<int>(ceilf(std::max(style.fontSize(), 10))), Fixed);
470     style.setWidth(length);
471     style.setHeight(length);
472     style.setBorderRadius(IntSize(length.value() / 2.0f, length.value() / 2.0f));
473 }
474
475 bool RenderThemeIOS::paintRadioDecorations(const RenderObject& box, const PaintInfo& paintInfo, const IntRect& rect)
476 {
477     GraphicsContextStateSaver stateSaver(paintInfo.context());
478     FloatRect clip = addRoundedBorderClip(box, paintInfo.context(), rect);
479
480     CGContextRef cgContext = paintInfo.context().platformContext();
481     if (isChecked(box)) {
482         drawAxialGradient(cgContext, gradientWithName(ConcaveGradient), clip.location(), FloatPoint(clip.x(), clip.maxY()), LinearInterpolation);
483
484         // The inner circle is 6 / 14 the size of the surrounding circle, 
485         // leaving 8 / 14 around it. (8 / 14) / 2 = 2 / 7.
486
487         static const float InnerInverseRatio = 2 / 7.0;
488
489         clip.inflateX(-clip.width() * InnerInverseRatio);
490         clip.inflateY(-clip.height() * InnerInverseRatio);
491
492         paintInfo.context().drawRaisedEllipse(clip, Color::white, shadowColor());
493
494         FloatSize radius(clip.width() / 2.0f, clip.height() / 2.0f);
495         paintInfo.context().clipRoundedRect(FloatRoundedRect(clip, radius, radius, radius, radius));
496     }
497     FloatPoint bottomCenter(clip.x() + clip.width() / 2.0, clip.maxY());
498     drawAxialGradient(cgContext, gradientWithName(ShadeGradient), clip.location(), FloatPoint(clip.x(), clip.maxY()), LinearInterpolation);
499     drawRadialGradient(cgContext, gradientWithName(ShineGradient), bottomCenter, 0, bottomCenter, std::max(clip.width(), clip.height()), ExponentialInterpolation);
500     return false;
501 }
502
503 bool RenderThemeIOS::paintTextFieldDecorations(const RenderObject& box, const PaintInfo& paintInfo, const FloatRect& rect)
504 {
505     RenderStyle& style = box.style();
506     FloatPoint point(rect.x() + style.borderLeftWidth(), rect.y() + style.borderTopWidth());
507
508     GraphicsContextStateSaver stateSaver(paintInfo.context());
509
510     paintInfo.context().clipRoundedRect(style.getRoundedBorderFor(LayoutRect(rect)).pixelSnappedRoundedRectForPainting(box.document().deviceScaleFactor()));
511
512     // This gradient gets drawn black when printing.
513     // Do not draw the gradient if there is no visible top border.
514     bool topBorderIsInvisible = !style.hasBorder() || !style.borderTopWidth() || style.borderTopIsTransparent();
515     if (!box.view().printing() && !topBorderIsInvisible)
516         drawAxialGradient(paintInfo.context().platformContext(), gradientWithName(InsetGradient), point, FloatPoint(CGPointMake(point.x(), point.y() + 3.0f)), LinearInterpolation);
517     return false;
518 }
519
520 bool RenderThemeIOS::paintTextAreaDecorations(const RenderObject& box, const PaintInfo& paintInfo, const FloatRect& rect)
521 {
522     return paintTextFieldDecorations(box, paintInfo, rect);
523 }
524
525 const int MenuListMinHeight = 15;
526
527 const float MenuListBaseHeight = 20;
528 const float MenuListBaseFontSize = 11;
529
530 const float MenuListArrowWidth = 7;
531 const float MenuListArrowHeight = 6;
532 const float MenuListButtonPaddingRight = 19;
533
534 int RenderThemeIOS::popupInternalPaddingRight(const RenderStyle& style) const
535 {
536     if (style.appearance() == MenulistButtonPart)
537         return MenuListButtonPaddingRight + style.borderTopWidth();
538     return 0;
539 }
540
541 void RenderThemeIOS::adjustRoundBorderRadius(RenderStyle& style, RenderBox& box)
542 {
543     if (style.appearance() == NoControlPart || style.backgroundLayers()->hasImage())
544         return;
545
546     // FIXME: We should not be relying on border radius for the appearance of our controls <rdar://problem/7675493>
547     Length radiusWidth(static_cast<int>(std::min(box.width(), box.height()) / 2.0), Fixed);
548     Length radiusHeight(static_cast<int>(box.height() / 2.0), Fixed);
549     style.setBorderRadius(LengthSize(radiusWidth, radiusHeight));
550 }
551
552 static void applyCommonButtonPaddingToStyle(RenderStyle& style, Element& element)
553 {
554     Document& document = element.document();
555     RefPtr<CSSPrimitiveValue> emSize = CSSPrimitiveValue::create(0.5, CSSPrimitiveValue::CSS_EMS);
556     int pixels = emSize->computeLength<int>(CSSToLengthConversionData(&style, document.renderStyle(), document.renderView(), document.frame()->pageZoomFactor()));
557     style.setPaddingBox(LengthBox(0, pixels, 0, pixels));
558 }
559
560 static void adjustSelectListButtonStyle(RenderStyle& style, Element& element)
561 {
562     // Enforce "padding: 0 0.5em".
563     applyCommonButtonPaddingToStyle(style, element);
564
565     // Enforce "line-height: normal".
566     style.setLineHeight(Length(-100.0, Percent));
567 }
568     
569 class RenderThemeMeasureTextClient : public MeasureTextClient {
570 public:
571     RenderThemeMeasureTextClient(const FontCascade& font, RenderObject& renderObject, const RenderStyle& style)
572         : m_font(font)
573         , m_renderObject(renderObject)
574         , m_style(style)
575     {
576     }
577     float measureText(const String& string) const override
578     {
579         TextRun run = RenderBlock::constructTextRun(&m_renderObject, m_font, string, m_style, AllowTrailingExpansion | ForbidLeadingExpansion, DefaultTextRunFlags);
580         return m_font.width(run);
581     }
582 private:
583     const FontCascade& m_font;
584     RenderObject& m_renderObject;
585     const RenderStyle& m_style;
586 };
587
588 static void adjustInputElementButtonStyle(RenderStyle& style, HTMLInputElement& inputElement)
589 {
590     // Always Enforce "padding: 0 0.5em".
591     applyCommonButtonPaddingToStyle(style, inputElement);
592
593     // Don't adjust the style if the width is specified.
594     if (style.width().isFixed() && style.width().value() > 0)
595         return;
596
597     // Don't adjust for unsupported date input types.
598     DateComponents::Type dateType = inputElement.dateType();
599     if (dateType == DateComponents::Invalid || dateType == DateComponents::Week)
600         return;
601
602     // Enforce the width and set the box-sizing to content-box to not conflict with the padding.
603     FontCascade font = style.fontCascade();
604     
605     RenderObject* renderer = inputElement.renderer();
606     if (font.primaryFont().isSVGFont() && !renderer)
607         return;
608     
609     float maximumWidth = localizedDateCache().maximumWidthForDateType(dateType, font, RenderThemeMeasureTextClient(font, *renderer, style));
610
611     ASSERT(maximumWidth >= 0);
612
613     if (maximumWidth > 0) {    
614         int width = static_cast<int>(maximumWidth + MenuListButtonPaddingRight);
615         style.setWidth(Length(width, Fixed));
616         style.setBoxSizing(CONTENT_BOX);
617     }
618 }
619
620 void RenderThemeIOS::adjustMenuListButtonStyle(StyleResolver&, RenderStyle& style, Element* element) const
621 {
622     // Set the min-height to be at least MenuListMinHeight.
623     if (style.height().isAuto())
624         style.setMinHeight(Length(std::max(MenuListMinHeight, static_cast<int>(MenuListBaseHeight / MenuListBaseFontSize * style.fontDescription().computedSize())), Fixed));
625     else
626         style.setMinHeight(Length(MenuListMinHeight, Fixed));
627
628     if (!element)
629         return;
630
631     // Enforce some default styles in the case that this is a non-multiple <select> element,
632     // or a date input. We don't force these if this is just an element with
633     // "-webkit-appearance: menulist-button".
634     if (element->hasTagName(HTMLNames::selectTag) && !element->hasAttribute(HTMLNames::multipleAttr))
635         adjustSelectListButtonStyle(style, *element);
636     else if (element->hasTagName(HTMLNames::inputTag))
637         adjustInputElementButtonStyle(style, static_cast<HTMLInputElement&>(*element));
638 }
639
640 bool RenderThemeIOS::paintMenuListButtonDecorations(const RenderBox& box, const PaintInfo& paintInfo, const FloatRect& rect)
641 {
642     RenderStyle& style = box.style();
643     float borderTopWidth = style.borderTopWidth();
644     FloatRect clip(rect.x() + style.borderLeftWidth(), rect.y() + style.borderTopWidth(), rect.width() - style.borderLeftWidth() - style.borderRightWidth(), rect.height() - style.borderTopWidth() - style.borderBottomWidth());
645     CGContextRef cgContext = paintInfo.context().platformContext();
646
647     float adjustLeft = 0.5;
648     float adjustRight = 0.5;
649     float adjustTop = 0.5;
650     float adjustBottom = 0.5;
651
652     // Paint left-hand title portion.
653     {
654         FloatRect titleClip(clip.x() - adjustLeft, clip.y() - adjustTop, clip.width() - MenuListButtonPaddingRight + adjustLeft, clip.height() + adjustTop + adjustBottom);
655
656         GraphicsContextStateSaver stateSaver(paintInfo.context());
657
658         paintInfo.context().clipRoundedRect(FloatRoundedRect(titleClip,
659             FloatSize(valueForLength(style.borderTopLeftRadius().width(), rect.width()) - style.borderLeftWidth(), valueForLength(style.borderTopLeftRadius().height(), rect.height()) - style.borderTopWidth()), FloatSize(0, 0),
660             FloatSize(valueForLength(style.borderBottomLeftRadius().width(), rect.width()) - style.borderLeftWidth(), valueForLength(style.borderBottomLeftRadius().height(), rect.height()) - style.borderBottomWidth()), FloatSize(0, 0)));
661
662         drawAxialGradient(cgContext, gradientWithName(ShadeGradient), titleClip.location(), FloatPoint(titleClip.x(), titleClip.maxY()), LinearInterpolation);
663         drawAxialGradient(cgContext, gradientWithName(ShineGradient), FloatPoint(titleClip.x(), titleClip.maxY()), titleClip.location(), ExponentialInterpolation);
664     }
665
666     // Draw the separator after the initial padding.
667
668     float separator = clip.maxX() - MenuListButtonPaddingRight;
669
670     box.drawLineForBoxSide(paintInfo.context(), FloatRect(FloatPoint(separator - borderTopWidth, clip.y()), FloatPoint(separator, clip.maxY())), BSRight, style.visitedDependentColor(CSSPropertyBorderTopColor), style.borderTopStyle(), 0, 0);
671
672     FloatRect buttonClip(separator - adjustTop, clip.y() - adjustTop, MenuListButtonPaddingRight + adjustTop + adjustRight, clip.height() + adjustTop + adjustBottom);
673
674     // Now paint the button portion.
675     {
676         GraphicsContextStateSaver stateSaver(paintInfo.context());
677
678         paintInfo.context().clipRoundedRect(FloatRoundedRect(buttonClip,
679             FloatSize(0, 0), FloatSize(valueForLength(style.borderTopRightRadius().width(), rect.width()) - style.borderRightWidth(), valueForLength(style.borderTopRightRadius().height(), rect.height()) - style.borderTopWidth()),
680             FloatSize(0, 0), FloatSize(valueForLength(style.borderBottomRightRadius().width(), rect.width()) - style.borderRightWidth(), valueForLength(style.borderBottomRightRadius().height(), rect.height()) - style.borderBottomWidth())));
681
682         paintInfo.context().fillRect(buttonClip, style.visitedDependentColor(CSSPropertyBorderTopColor));
683
684         drawAxialGradient(cgContext, gradientWithName(isFocused(box) && !isReadOnlyControl(box) ? ConcaveGradient : ConvexGradient), buttonClip.location(), FloatPoint(buttonClip.x(), buttonClip.maxY()), LinearInterpolation);
685     }
686
687     // Paint Indicators.
688
689     if (box.isMenuList() && downcast<HTMLSelectElement>(box.element())->multiple()) {
690         int size = 2;
691         int count = 3;
692         int padding = 3;
693
694         FloatRect ellipse(buttonClip.x() + (buttonClip.width() - count * (size + padding) + padding) / 2.0, buttonClip.maxY() - 10.0, size, size);
695
696         for (int i = 0; i < count; ++i) {
697             paintInfo.context().drawRaisedEllipse(ellipse, Color::white, Color(0.0f, 0.0f, 0.0f, 0.5f));
698             ellipse.move(size + padding, 0);
699         }
700     }  else {
701         float centerX = floorf(buttonClip.x() + buttonClip.width() / 2.0) - 0.5;
702         float centerY = floorf(buttonClip.y() + buttonClip.height() * 3.0 / 8.0);
703
704         Vector<FloatPoint> arrow = {
705             { centerX - MenuListArrowWidth / 2, centerY },
706             { centerX + MenuListArrowWidth / 2, centerY },
707             { centerX, centerY + MenuListArrowHeight }
708         };
709
710         Vector<FloatPoint> shadow = {
711             { arrow[0].x(), arrow[0].y() + 1 },
712             { arrow[1].x(), arrow[1].y() + 1 },
713             { arrow[2].x(), arrow[2].y() + 1 }
714         };
715
716         float opacity = isReadOnlyControl(box) ? 0.2 : 0.5;
717         paintInfo.context().setStrokeColor(Color(0.0f, 0.0f, 0.0f, opacity));
718         paintInfo.context().setFillColor(Color(0.0f, 0.0f, 0.0f, opacity));
719         paintInfo.context().drawPath(Path::polygonPathFromPoints(shadow));
720
721         paintInfo.context().setStrokeColor(Color::white);
722         paintInfo.context().setFillColor(Color::white);
723         paintInfo.context().drawPath(Path::polygonPathFromPoints(arrow));
724     }
725
726     return false;
727 }
728
729 const CGFloat kTrackThickness = 4.0;
730 const CGFloat kTrackRadius = kTrackThickness / 2.0;
731 const int kDefaultSliderThumbSize = 16;
732
733 void RenderThemeIOS::adjustSliderTrackStyle(StyleResolver& selector, RenderStyle& style, Element* element) const
734 {
735     RenderTheme::adjustSliderTrackStyle(selector, style, element);
736
737     // FIXME: We should not be relying on border radius for the appearance of our controls <rdar://problem/7675493>
738     Length radiusWidth(static_cast<int>(kTrackRadius), Fixed);
739     Length radiusHeight(static_cast<int>(kTrackRadius), Fixed);
740     style.setBorderRadius(LengthSize(radiusWidth, radiusHeight));
741 }
742
743 bool RenderThemeIOS::paintSliderTrack(const RenderObject& box, const PaintInfo& paintInfo, const IntRect& rect)
744 {
745     IntRect trackClip = rect;
746     RenderStyle& style = box.style();
747
748     bool isHorizontal = true;
749     switch (style.appearance()) {
750     case SliderHorizontalPart:
751         isHorizontal = true;
752         // Inset slightly so the thumb covers the edge.
753         if (trackClip.width() > 2) {
754             trackClip.setWidth(trackClip.width() - 2);
755             trackClip.setX(trackClip.x() + 1);
756         }
757         trackClip.setHeight(static_cast<int>(kTrackThickness));
758         trackClip.setY(rect.y() + rect.height() / 2 - kTrackThickness / 2);
759         break;
760     case SliderVerticalPart:
761         isHorizontal = false;
762         // Inset slightly so the thumb covers the edge.
763         if (trackClip.height() > 2) {
764             trackClip.setHeight(trackClip.height() - 2);
765             trackClip.setY(trackClip.y() + 1);
766         }
767         trackClip.setWidth(kTrackThickness);
768         trackClip.setX(rect.x() + rect.width() / 2 - kTrackThickness / 2);
769         break;
770     default:
771         ASSERT_NOT_REACHED();
772     }
773
774     ASSERT(trackClip.width() >= 0);
775     ASSERT(trackClip.height() >= 0);
776     CGFloat cornerWidth = trackClip.width() < kTrackThickness ? trackClip.width() / 2.0f : kTrackRadius;
777     CGFloat cornerHeight = trackClip.height() < kTrackThickness ? trackClip.height() / 2.0f : kTrackRadius;
778
779     bool readonly = isReadOnlyControl(box);
780
781 #if ENABLE(DATALIST_ELEMENT)
782     paintSliderTicks(box, paintInfo, trackClip);
783 #endif
784
785     // Draw the track gradient.
786     {
787         GraphicsContextStateSaver stateSaver(paintInfo.context());
788
789         IntSize cornerSize(cornerWidth, cornerHeight);
790         FloatRoundedRect innerBorder(trackClip, cornerSize, cornerSize, cornerSize, cornerSize);
791         paintInfo.context().clipRoundedRect(innerBorder);
792
793         CGContextRef cgContext = paintInfo.context().platformContext();
794         IOSGradientRef gradient = readonly ? gradientWithName(ReadonlySliderTrackGradient) : gradientWithName(SliderTrackGradient);
795         if (isHorizontal)
796             drawAxialGradient(cgContext, gradient, trackClip.location(), FloatPoint(trackClip.x(), trackClip.maxY()), LinearInterpolation);
797         else
798             drawAxialGradient(cgContext, gradient, trackClip.location(), FloatPoint(trackClip.maxX(), trackClip.y()), LinearInterpolation);
799     }
800
801     // Draw the track border.
802     {
803         GraphicsContextStateSaver stateSaver(paintInfo.context());
804
805         CGContextRef cgContext = paintInfo.context().platformContext();
806         if (readonly)
807             paintInfo.context().setStrokeColor(Color(178, 178, 178));
808         else
809             paintInfo.context().setStrokeColor(Color(76, 76, 76));
810
811         RetainPtr<CGMutablePathRef> roundedRectPath = adoptCF(CGPathCreateMutable());
812         CGPathAddRoundedRect(roundedRectPath.get(), 0, trackClip, cornerWidth, cornerHeight);
813         CGContextAddPath(cgContext, roundedRectPath.get());
814         CGContextSetLineWidth(cgContext, 1);
815         CGContextStrokePath(cgContext);
816     }
817
818     return false;
819 }
820
821 void RenderThemeIOS::adjustSliderThumbSize(RenderStyle& style, Element*) const
822 {
823     if (style.appearance() != SliderThumbHorizontalPart && style.appearance() != SliderThumbVerticalPart)
824         return;
825
826     // Enforce "border-radius: 50%".
827     Length length(50.0f, Percent);
828     style.setBorderRadius(LengthSize(length, length));
829
830     // Enforce a 16x16 size if no size is provided.
831     if (style.width().isIntrinsicOrAuto() || style.height().isAuto()) {
832         Length length = Length(kDefaultSliderThumbSize, Fixed);
833         style.setWidth(length);
834         style.setHeight(length);
835     }
836 }
837
838 bool RenderThemeIOS::paintSliderThumbDecorations(const RenderObject& box, const PaintInfo& paintInfo, const IntRect& rect)
839 {
840     GraphicsContextStateSaver stateSaver(paintInfo.context());
841     FloatRect clip = addRoundedBorderClip(box, paintInfo.context(), rect);
842
843     CGContextRef cgContext = paintInfo.context().platformContext();
844     FloatPoint bottomCenter(clip.x() + clip.width() / 2.0f, clip.maxY());
845     if (isPressed(box))
846         drawAxialGradient(cgContext, gradientWithName(SliderThumbOpaquePressedGradient), clip.location(), FloatPoint(clip.x(), clip.maxY()), LinearInterpolation);
847     else {
848         drawAxialGradient(cgContext, gradientWithName(ShadeGradient), clip.location(), FloatPoint(clip.x(), clip.maxY()), LinearInterpolation);
849         drawRadialGradient(cgContext, gradientWithName(ShineGradient), bottomCenter, 0.0f, bottomCenter, std::max(clip.width(), clip.height()), ExponentialInterpolation);
850     }
851
852     return false;
853 }
854
855 double RenderThemeIOS::animationRepeatIntervalForProgressBar(RenderProgress&) const
856 {
857     return 0;
858 }
859
860 double RenderThemeIOS::animationDurationForProgressBar(RenderProgress&) const
861 {
862     return 0;
863 }
864
865 bool RenderThemeIOS::paintProgressBar(const RenderObject& renderer, const PaintInfo& paintInfo, const IntRect& rect)
866 {
867     if (!is<RenderProgress>(renderer))
868         return true;
869
870     const int progressBarHeight = 9;
871     const float verticalOffset = (rect.height() - progressBarHeight) / 2.0;
872
873     GraphicsContextStateSaver stateSaver(paintInfo.context());
874     if (rect.width() < 10 || rect.height() < 9) {
875         // The rect is smaller than the standard progress bar. We clip to the element's rect to avoid
876         // leaking pixels outside the repaint rect.
877         paintInfo.context().clip(rect);
878     }
879
880     // 1) Draw the progress bar track.
881     // 1.1) Draw the white background with grey gradient border.
882     GraphicsContext& context = paintInfo.context();
883     context.setStrokeThickness(0.68);
884     context.setStrokeStyle(SolidStroke);
885
886     const float verticalRenderingPosition = rect.y() + verticalOffset;
887     RefPtr<Gradient> strokeGradient = Gradient::create(FloatPoint(rect.x(), verticalRenderingPosition), FloatPoint(rect.x(), verticalRenderingPosition + progressBarHeight - 1));
888     strokeGradient->addColorStop(0.0, Color(0x8d, 0x8d, 0x8d));
889     strokeGradient->addColorStop(0.45, Color(0xee, 0xee, 0xee));
890     strokeGradient->addColorStop(0.55, Color(0xee, 0xee, 0xee));
891     strokeGradient->addColorStop(1.0, Color(0x8d, 0x8d, 0x8d));
892     context.setStrokeGradient(strokeGradient.releaseNonNull());
893
894     context.setFillColor(Color(255, 255, 255));
895
896     Path trackPath;
897     FloatRect trackRect(rect.x() + 0.25, verticalRenderingPosition + 0.25, rect.width() - 0.5, progressBarHeight - 0.5);
898     FloatSize roundedCornerRadius(5, 4);
899     trackPath.addRoundedRect(trackRect, roundedCornerRadius);
900     context.drawPath(trackPath);
901
902     // 1.2) Draw top gradient on the upper half. It is supposed to overlay the fill from the background and darker the stroked path.
903     FloatRect border(rect.x(), rect.y() + verticalOffset, rect.width(), progressBarHeight);
904     paintInfo.context().clipRoundedRect(FloatRoundedRect(border, roundedCornerRadius, roundedCornerRadius, roundedCornerRadius, roundedCornerRadius));
905
906     float upperGradientHeight = progressBarHeight / 2.;
907     RefPtr<Gradient> upperGradient = Gradient::create(FloatPoint(rect.x(), verticalRenderingPosition + 0.5), FloatPoint(rect.x(), verticalRenderingPosition + upperGradientHeight - 1.5));
908     upperGradient->addColorStop(0.0, Color(133, 133, 133, 188));
909     upperGradient->addColorStop(1.0, Color(18, 18, 18, 51));
910     context.setFillGradient(upperGradient.releaseNonNull());
911
912     context.fillRect(FloatRect(rect.x(), verticalRenderingPosition, rect.width(), upperGradientHeight));
913
914     const auto& renderProgress = downcast<RenderProgress>(renderer);
915     if (renderProgress.isDeterminate()) {
916         // 2) Draw the progress bar.
917         double position = clampTo(renderProgress.position(), 0.0, 1.0);
918         double barWidth = position * rect.width();
919         RefPtr<Gradient> barGradient = Gradient::create(FloatPoint(rect.x(), verticalRenderingPosition + 0.5), FloatPoint(rect.x(), verticalRenderingPosition + progressBarHeight - 1));
920         barGradient->addColorStop(0.0, Color(195, 217, 247));
921         barGradient->addColorStop(0.45, Color(118, 164, 228));
922         barGradient->addColorStop(0.49, Color(118, 164, 228));
923         barGradient->addColorStop(0.51, Color(36, 114, 210));
924         barGradient->addColorStop(0.55, Color(36, 114, 210));
925         barGradient->addColorStop(1.0, Color(57, 142, 244));
926         context.setFillGradient(barGradient.releaseNonNull());
927
928         RefPtr<Gradient> barStrokeGradient = Gradient::create(FloatPoint(rect.x(), verticalRenderingPosition), FloatPoint(rect.x(), verticalRenderingPosition + progressBarHeight - 1));
929         barStrokeGradient->addColorStop(0.0, Color(95, 107, 183));
930         barStrokeGradient->addColorStop(0.5, Color(66, 106, 174, 240));
931         barStrokeGradient->addColorStop(1.0, Color(38, 104, 166));
932         context.setStrokeGradient(barStrokeGradient.releaseNonNull());
933
934         Path barPath;
935         int left = rect.x();
936         if (!renderProgress.style().isLeftToRightDirection())
937             left = rect.maxX() - barWidth;
938         FloatRect barRect(left + 0.25, verticalRenderingPosition + 0.25, std::max(barWidth - 0.5, 0.0), progressBarHeight - 0.5);
939         barPath.addRoundedRect(barRect, roundedCornerRadius);
940         context.drawPath(barPath);
941     }
942
943     return false;
944 }
945
946 #if ENABLE(DATALIST_ELEMENT)
947 IntSize RenderThemeIOS::sliderTickSize() const
948 {
949     // FIXME: <rdar://problem/12271791> MERGEBOT: Correct values for slider tick of <input type="range"> elements (requires ENABLE_DATALIST_ELEMENT)
950     return IntSize(1, 3);
951 }
952
953 int RenderThemeIOS::sliderTickOffsetFromTrackCenter() const
954 {
955     // FIXME: <rdar://problem/12271791> MERGEBOT: Correct values for slider tick of <input type="range"> elements (requires ENABLE_DATALIST_ELEMENT)
956     return -9;
957 }
958 #endif
959
960 void RenderThemeIOS::adjustSearchFieldStyle(StyleResolver& selector, RenderStyle& style, Element* element) const
961 {
962     RenderTheme::adjustSearchFieldStyle(selector, style, element);
963
964     if (!element)
965         return;
966
967     if (!style.hasBorder())
968         return;
969
970     RenderBox* box = element->renderBox();
971     if (!box)
972         return;
973
974     adjustRoundBorderRadius(style, *box);
975 }
976
977 bool RenderThemeIOS::paintSearchFieldDecorations(const RenderObject& box, const PaintInfo& paintInfo, const IntRect& rect)
978 {
979     return paintTextFieldDecorations(box, paintInfo, rect);
980 }
981
982 void RenderThemeIOS::adjustButtonStyle(StyleResolver& selector, RenderStyle& style, Element* element) const
983 {
984     RenderTheme::adjustButtonStyle(selector, style, element);
985
986     // Set padding: 0 1.0em; on buttons.
987     // CSSPrimitiveValue::computeLengthInt only needs the element's style to calculate em lengths.
988     // Since the element might not be in a document, just pass nullptr for the root element style
989     // and the render view.
990     RefPtr<CSSPrimitiveValue> emSize = CSSPrimitiveValue::create(1.0, CSSPrimitiveValue::CSS_EMS);
991     int pixels = emSize->computeLength<int>(CSSToLengthConversionData(&style, nullptr, nullptr, 1.0, false));
992     style.setPaddingBox(LengthBox(0, pixels, 0, pixels));
993
994     if (!element)
995         return;
996
997     RenderBox* box = element->renderBox();
998     if (!box)
999         return;
1000
1001     adjustRoundBorderRadius(style, *box);
1002 }
1003
1004 bool RenderThemeIOS::paintButtonDecorations(const RenderObject& box, const PaintInfo& paintInfo, const IntRect& rect)
1005 {
1006     return paintPushButtonDecorations(box, paintInfo, rect);
1007 }
1008
1009 bool RenderThemeIOS::paintPushButtonDecorations(const RenderObject& box, const PaintInfo& paintInfo, const IntRect& rect)
1010 {
1011     GraphicsContextStateSaver stateSaver(paintInfo.context());
1012     FloatRect clip = addRoundedBorderClip(box, paintInfo.context(), rect);
1013
1014     CGContextRef cgContext = paintInfo.context().platformContext();
1015     if (box.style().visitedDependentColor(CSSPropertyBackgroundColor).isDark())
1016         drawAxialGradient(cgContext, gradientWithName(ConvexGradient), clip.location(), FloatPoint(clip.x(), clip.maxY()), LinearInterpolation);
1017     else {
1018         drawAxialGradient(cgContext, gradientWithName(ShadeGradient), clip.location(), FloatPoint(clip.x(), clip.maxY()), LinearInterpolation);
1019         drawAxialGradient(cgContext, gradientWithName(ShineGradient), FloatPoint(clip.x(), clip.maxY()), clip.location(), ExponentialInterpolation);
1020     }
1021     return false;
1022 }
1023
1024 void RenderThemeIOS::setButtonSize(RenderStyle& style) const
1025 {
1026     // If the width and height are both specified, then we have nothing to do.
1027     if (!style.width().isIntrinsicOrAuto() && !style.height().isAuto())
1028         return;
1029
1030     // Use the font size to determine the intrinsic width of the control.
1031     style.setHeight(Length(static_cast<int>(ControlBaseHeight / ControlBaseFontSize * style.fontDescription().computedSize()), Fixed));
1032 }
1033
1034 const int kThumbnailBorderStrokeWidth = 1;
1035 const int kThumbnailBorderCornerRadius = 1;
1036 const int kVisibleBackgroundImageWidth = 1;
1037 const int kMultipleThumbnailShrinkSize = 2;
1038
1039 bool RenderThemeIOS::paintFileUploadIconDecorations(const RenderObject&, const RenderObject& buttonRenderer, const PaintInfo& paintInfo, const IntRect& rect, Icon* icon, FileUploadDecorations fileUploadDecorations)
1040 {
1041     GraphicsContextStateSaver stateSaver(paintInfo.context());
1042
1043     IntSize cornerSize(kThumbnailBorderCornerRadius, kThumbnailBorderCornerRadius);
1044     Color pictureFrameColor = buttonRenderer.style().visitedDependentColor(CSSPropertyBorderTopColor);
1045
1046     IntRect thumbnailPictureFrameRect = rect;
1047     IntRect thumbnailRect = rect;
1048     thumbnailRect.contract(2 * kThumbnailBorderStrokeWidth, 2 * kThumbnailBorderStrokeWidth);
1049     thumbnailRect.move(kThumbnailBorderStrokeWidth, kThumbnailBorderStrokeWidth);
1050
1051     if (fileUploadDecorations == MultipleFiles) {
1052         // Smaller thumbnails for multiple selection appearance.
1053         thumbnailPictureFrameRect.contract(kMultipleThumbnailShrinkSize, kMultipleThumbnailShrinkSize);
1054         thumbnailRect.contract(kMultipleThumbnailShrinkSize, kMultipleThumbnailShrinkSize);
1055
1056         // Background picture frame and simple background icon with a gradient matching the button.
1057         Color backgroundImageColor = Color(buttonRenderer.style().visitedDependentColor(CSSPropertyBackgroundColor).rgb());
1058         paintInfo.context().fillRoundedRect(FloatRoundedRect(thumbnailPictureFrameRect, cornerSize, cornerSize, cornerSize, cornerSize), pictureFrameColor);
1059         paintInfo.context().fillRect(thumbnailRect, backgroundImageColor);
1060         {
1061             GraphicsContextStateSaver stateSaver2(paintInfo.context());
1062             CGContextRef cgContext = paintInfo.context().platformContext();
1063             paintInfo.context().clip(thumbnailRect);
1064             if (backgroundImageColor.isDark())
1065                 drawAxialGradient(cgContext, gradientWithName(ConvexGradient), thumbnailRect.location(), FloatPoint(thumbnailRect.x(), thumbnailRect.maxY()), LinearInterpolation);
1066             else {
1067                 drawAxialGradient(cgContext, gradientWithName(ShadeGradient), thumbnailRect.location(), FloatPoint(thumbnailRect.x(), thumbnailRect.maxY()), LinearInterpolation);
1068                 drawAxialGradient(cgContext, gradientWithName(ShineGradient), FloatPoint(thumbnailRect.x(), thumbnailRect.maxY()), thumbnailRect.location(), ExponentialInterpolation);
1069             }
1070         }
1071
1072         // Move the rects for the Foreground picture frame and icon.
1073         int inset = kVisibleBackgroundImageWidth + kThumbnailBorderStrokeWidth;
1074         thumbnailPictureFrameRect.move(inset, inset);
1075         thumbnailRect.move(inset, inset);
1076     }
1077
1078     // Foreground picture frame and icon.
1079     paintInfo.context().fillRoundedRect(FloatRoundedRect(thumbnailPictureFrameRect, cornerSize, cornerSize, cornerSize, cornerSize), pictureFrameColor);
1080     icon->paint(paintInfo.context(), thumbnailRect);
1081
1082     return false;
1083 }
1084
1085 Color RenderThemeIOS::platformActiveSelectionBackgroundColor() const
1086 {
1087     return Color::transparent;
1088 }
1089
1090 Color RenderThemeIOS::platformInactiveSelectionBackgroundColor() const
1091 {
1092     return Color::transparent;
1093 }
1094
1095 bool RenderThemeIOS::shouldHaveSpinButton(HTMLInputElement&) const
1096 {
1097     return false;
1098 }
1099
1100 bool RenderThemeIOS::shouldHaveCapsLockIndicator(HTMLInputElement&) const
1101 {
1102     return false;
1103 }
1104
1105 FontCascadeDescription& RenderThemeIOS::cachedSystemFontDescription(CSSValueID valueID) const
1106 {
1107     static NeverDestroyed<FontCascadeDescription> systemFont;
1108     static NeverDestroyed<FontCascadeDescription> headlineFont;
1109     static NeverDestroyed<FontCascadeDescription> bodyFont;
1110     static NeverDestroyed<FontCascadeDescription> subheadlineFont;
1111     static NeverDestroyed<FontCascadeDescription> footnoteFont;
1112     static NeverDestroyed<FontCascadeDescription> caption1Font;
1113     static NeverDestroyed<FontCascadeDescription> caption2Font;
1114     static NeverDestroyed<FontCascadeDescription> shortHeadlineFont;
1115     static NeverDestroyed<FontCascadeDescription> shortBodyFont;
1116     static NeverDestroyed<FontCascadeDescription> shortSubheadlineFont;
1117     static NeverDestroyed<FontCascadeDescription> shortFootnoteFont;
1118     static NeverDestroyed<FontCascadeDescription> shortCaption1Font;
1119     static NeverDestroyed<FontCascadeDescription> tallBodyFont;
1120     static NeverDestroyed<FontCascadeDescription> title1Font;
1121     static NeverDestroyed<FontCascadeDescription> title2Font;
1122     static NeverDestroyed<FontCascadeDescription> title3Font;
1123
1124     static CFStringRef userTextSize = contentSizeCategory();
1125
1126     if (userTextSize != contentSizeCategory()) {
1127         userTextSize = contentSizeCategory();
1128
1129         headlineFont.get().setIsAbsoluteSize(false);
1130         bodyFont.get().setIsAbsoluteSize(false);
1131         subheadlineFont.get().setIsAbsoluteSize(false);
1132         footnoteFont.get().setIsAbsoluteSize(false);
1133         caption1Font.get().setIsAbsoluteSize(false);
1134         caption2Font.get().setIsAbsoluteSize(false);
1135         shortHeadlineFont.get().setIsAbsoluteSize(false);
1136         shortBodyFont.get().setIsAbsoluteSize(false);
1137         shortSubheadlineFont.get().setIsAbsoluteSize(false);
1138         shortFootnoteFont.get().setIsAbsoluteSize(false);
1139         shortCaption1Font.get().setIsAbsoluteSize(false);
1140         tallBodyFont.get().setIsAbsoluteSize(false);
1141     }
1142
1143     switch (valueID) {
1144     case CSSValueAppleSystemHeadline:
1145         return headlineFont;
1146     case CSSValueAppleSystemBody:
1147         return bodyFont;
1148     case CSSValueAppleSystemTitle1:
1149         return title1Font;
1150     case CSSValueAppleSystemTitle2:
1151         return title2Font;
1152     case CSSValueAppleSystemTitle3:
1153         return title3Font;
1154     case CSSValueAppleSystemSubheadline:
1155         return subheadlineFont;
1156     case CSSValueAppleSystemFootnote:
1157         return footnoteFont;
1158     case CSSValueAppleSystemCaption1:
1159         return caption1Font;
1160     case CSSValueAppleSystemCaption2:
1161         return caption2Font;
1162         // Short version.
1163     case CSSValueAppleSystemShortHeadline:
1164         return shortHeadlineFont;
1165     case CSSValueAppleSystemShortBody:
1166         return shortBodyFont;
1167     case CSSValueAppleSystemShortSubheadline:
1168         return shortSubheadlineFont;
1169     case CSSValueAppleSystemShortFootnote:
1170         return shortFootnoteFont;
1171     case CSSValueAppleSystemShortCaption1:
1172         return shortCaption1Font;
1173         // Tall version.
1174     case CSSValueAppleSystemTallBody:
1175         return tallBodyFont;
1176     default:
1177         return systemFont;
1178     }
1179 }
1180
1181 void RenderThemeIOS::updateCachedSystemFontDescription(CSSValueID valueID, FontCascadeDescription& fontDescription) const
1182 {
1183     RetainPtr<CTFontDescriptorRef> fontDescriptor;
1184     CFStringRef textStyle;
1185     switch (valueID) {
1186     case CSSValueAppleSystemHeadline:
1187         textStyle = kCTUIFontTextStyleHeadline;
1188         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1189         break;
1190     case CSSValueAppleSystemBody:
1191         textStyle = kCTUIFontTextStyleBody;
1192         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1193         break;
1194     case CSSValueAppleSystemTitle1:
1195         textStyle = kCTUIFontTextStyleTitle1;
1196         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1197         break;
1198     case CSSValueAppleSystemTitle2:
1199         textStyle = kCTUIFontTextStyleTitle2;
1200         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1201         break;
1202     case CSSValueAppleSystemTitle3:
1203         textStyle = kCTUIFontTextStyleTitle3;
1204         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1205         break;
1206     case CSSValueAppleSystemSubheadline:
1207         textStyle = kCTUIFontTextStyleSubhead;
1208         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1209         break;
1210     case CSSValueAppleSystemFootnote:
1211         textStyle = kCTUIFontTextStyleFootnote;
1212         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1213         break;
1214     case CSSValueAppleSystemCaption1:
1215         textStyle = kCTUIFontTextStyleCaption1;
1216         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1217         break;
1218     case CSSValueAppleSystemCaption2:
1219         textStyle = kCTUIFontTextStyleCaption2;
1220         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1221         break;
1222
1223     // Short version.
1224     case CSSValueAppleSystemShortHeadline:
1225         textStyle = kCTUIFontTextStyleShortHeadline;
1226         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1227         break;
1228     case CSSValueAppleSystemShortBody:
1229         textStyle = kCTUIFontTextStyleShortBody;
1230         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1231         break;
1232     case CSSValueAppleSystemShortSubheadline:
1233         textStyle = kCTUIFontTextStyleShortSubhead;
1234         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1235         break;
1236     case CSSValueAppleSystemShortFootnote:
1237         textStyle = kCTUIFontTextStyleShortFootnote;
1238         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1239         break;
1240     case CSSValueAppleSystemShortCaption1:
1241         textStyle = kCTUIFontTextStyleShortCaption1;
1242         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1243         break;
1244
1245     // Tall version.
1246     case CSSValueAppleSystemTallBody:
1247         textStyle = kCTUIFontTextStyleTallBody;
1248         fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1249         break;
1250
1251     default:
1252         textStyle = kCTFontDescriptorTextStyleEmphasized;
1253         fontDescriptor = adoptCF(CTFontDescriptorCreateForUIType(kCTFontUIFontSystem, 0, nullptr));
1254     }
1255
1256     ASSERT(fontDescriptor);
1257     RetainPtr<CTFontRef> font = adoptCF(CTFontCreateWithFontDescriptor(fontDescriptor.get(), 0, nullptr));
1258     font = preparePlatformFont(font.get(), fontDescription.textRenderingMode(), nullptr, nullptr, fontDescription.featureSettings(), fontDescription.variantSettings());
1259     fontDescription.setIsAbsoluteSize(true);
1260     fontDescription.setOneFamily(textStyle);
1261     fontDescription.setSpecifiedSize(CTFontGetSize(font.get()));
1262     fontDescription.setWeight(fontWeightFromCoreText(FontCache::weightOfCTFont(font.get())));
1263     fontDescription.setItalic(FontItalicOff);
1264 }
1265
1266 #if ENABLE(VIDEO)
1267 String RenderThemeIOS::mediaControlsStyleSheet()
1268 {
1269 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
1270     if (m_mediaControlsStyleSheet.isEmpty()) {
1271         StringBuilder builder;
1272         builder.append([NSString stringWithContentsOfFile:[[NSBundle bundleForClass:[WebCoreRenderThemeBundle class]] pathForResource:@"mediaControlsiOS" ofType:@"css"] encoding:NSUTF8StringEncoding error:nil]);
1273         m_mediaControlsStyleSheet = builder.toString();
1274     }
1275     return m_mediaControlsStyleSheet;
1276 #else
1277     return emptyString();
1278 #endif
1279 }
1280
1281 String RenderThemeIOS::mediaControlsScript()
1282 {
1283 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
1284     if (m_mediaControlsScript.isEmpty()) {
1285         StringBuilder scriptBuilder;
1286         scriptBuilder.append([NSString stringWithContentsOfFile:[[NSBundle bundleForClass:[WebCoreRenderThemeBundle class]] pathForResource:@"mediaControlsLocalizedStrings" ofType:@"js"] encoding:NSUTF8StringEncoding error:nil]);
1287         scriptBuilder.append([NSString stringWithContentsOfFile:[[NSBundle bundleForClass:[WebCoreRenderThemeBundle class]] pathForResource:@"mediaControlsApple" ofType:@"js"] encoding:NSUTF8StringEncoding error:nil]);
1288         scriptBuilder.append([NSString stringWithContentsOfFile:[[NSBundle bundleForClass:[WebCoreRenderThemeBundle class]] pathForResource:@"mediaControlsiOS" ofType:@"js"] encoding:NSUTF8StringEncoding error:nil]);
1289         m_mediaControlsScript = scriptBuilder.toString();
1290     }
1291     return m_mediaControlsScript;
1292 #else
1293     return emptyString();
1294 #endif
1295 }
1296 #endif // ENABLE(VIDEO)
1297
1298 Color RenderThemeIOS::systemColor(CSSValueID cssValueID) const
1299 {
1300     auto addResult = m_systemColorCache.add(cssValueID, Color());
1301     if (!addResult.isNewEntry)
1302         return addResult.iterator->value;
1303
1304     Color color;
1305     switch (cssValueID) {
1306     case CSSValueAppleWirelessPlaybackTargetActive:
1307         color = [getUIColorClass() systemBlueColor].CGColor;
1308         break;
1309     case CSSValueAppleSystemBlue:
1310         color = [getUIColorClass() systemBlueColor].CGColor;
1311         break;
1312     case CSSValueAppleSystemGray:
1313         color = [getUIColorClass() systemGrayColor].CGColor;
1314         break;
1315     case CSSValueAppleSystemGreen:
1316         color = [getUIColorClass() systemGreenColor].CGColor;
1317         break;
1318     case CSSValueAppleSystemOrange:
1319         color = [getUIColorClass() systemOrangeColor].CGColor;
1320         break;
1321     case CSSValueAppleSystemPink:
1322         color = [getUIColorClass() systemPinkColor].CGColor;
1323         break;
1324     case CSSValueAppleSystemRed:
1325         color = [getUIColorClass() systemRedColor].CGColor;
1326         break;
1327     case CSSValueAppleSystemYellow:
1328         color = [getUIColorClass() systemYellowColor].CGColor;
1329         break;
1330     default:
1331         break;
1332     }
1333
1334     if (!color.isValid())
1335         color = RenderTheme::systemColor(cssValueID);
1336
1337     addResult.iterator->value = color;
1338
1339     return addResult.iterator->value;
1340 }
1341
1342 #if ENABLE(ATTACHMENT_ELEMENT)
1343
1344 const CGSize attachmentSize = { 160, 119 };
1345
1346 const CGFloat attachmentBorderRadius = 16;
1347 static Color attachmentBorderColor() { return Color(204, 204, 204); }
1348
1349 const CGFloat attachmentProgressSize = 36;
1350 const CGFloat attachmentIconSize = 48;
1351
1352 const CGFloat attachmentItemMargin = 8;
1353
1354 const CGFloat attachmentTitleMaximumWidth = 140;
1355 const CFIndex attachmentTitleMaximumLineCount = 2;
1356
1357 // FIXME: Should be emphasized.
1358 static UIFont *attachmentActionFont() { return [getUIFontClass() preferredFontForTextStyle:getUIFontTextStyleFootnote()]; }
1359 static UIColor *attachmentActionColor() { return [getUIColorClass() systemBlueColor]; }
1360
1361 static UIFont *attachmentTitleFont() { return [getUIFontClass() preferredFontForTextStyle:getUIFontTextStyleCaption1()]; }
1362 static UIColor *attachmentTitleColor() { return [getUIColorClass() systemGrayColor]; }
1363
1364 static UIFont *attachmentSubtitleFont() { return [getUIFontClass() preferredFontForTextStyle:getUIFontTextStyleCaption1()]; }
1365 static UIColor *attachmentSubtitleColor() { return [getUIColorClass() systemGrayColor]; }
1366
1367 struct AttachmentInfo {
1368     explicit AttachmentInfo(const RenderAttachment&);
1369
1370     FloatRect iconRect;
1371     FloatRect attachmentRect;
1372     FloatRect progressRect;
1373
1374     BOOL hasProgress { NO };
1375     float progress;
1376
1377     RetainPtr<UIImage> icon;
1378
1379     int baseline { 0 };
1380
1381     struct LabelLine {
1382         FloatRect rect;
1383         RetainPtr<CTLineRef> line;
1384     };
1385     Vector<LabelLine> lines;
1386
1387     CGFloat contentYOrigin { 0 };
1388
1389 private:
1390     void buildTitleLines(const RenderAttachment&);
1391     void buildSingleLine(const String&, UIFont *, UIColor *);
1392
1393     void addLine(CTLineRef);
1394 };
1395
1396 void AttachmentInfo::addLine(CTLineRef line)
1397 {
1398     CGRect lineBounds = CTLineGetBoundsWithOptions(line, kCTLineBoundsExcludeTypographicLeading);
1399     CGFloat trailingWhitespaceWidth = CTLineGetTrailingWhitespaceWidth(line);
1400     CGFloat lineWidthIgnoringTrailingWhitespace = lineBounds.size.width - trailingWhitespaceWidth;
1401     CGFloat lineHeight = CGCeiling(lineBounds.size.height + lineBounds.origin.y);
1402
1403     CGFloat xOffset = (attachmentRect.width() / 2) - (lineWidthIgnoringTrailingWhitespace / 2);
1404     LabelLine labelLine;
1405     labelLine.line = line;
1406     labelLine.rect = FloatRect(xOffset, 0, lineWidthIgnoringTrailingWhitespace, lineHeight);
1407
1408     lines.append(labelLine);
1409 }
1410
1411 void AttachmentInfo::buildTitleLines(const RenderAttachment& attachment)
1412 {
1413     RetainPtr<UIFont> font = attachmentTitleFont();
1414
1415     String title = attachment.attachmentElement().attachmentTitle();
1416     if (title.isEmpty())
1417         return;
1418
1419     NSDictionary *textAttributes = @{
1420         (id)kCTFontAttributeName: font.get(),
1421         (id)kCTForegroundColorAttributeName: attachmentTitleColor()
1422     };
1423     RetainPtr<NSAttributedString> attributedTitle = adoptNS([[NSAttributedString alloc] initWithString:title attributes:textAttributes]);
1424     RetainPtr<CTFramesetterRef> titleFramesetter = adoptCF(CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attributedTitle.get()));
1425
1426     CFRange fitRange;
1427     CGSize titleTextSize = CTFramesetterSuggestFrameSizeWithConstraints(titleFramesetter.get(), CFRangeMake(0, 0), nullptr, CGSizeMake(attachmentTitleMaximumWidth, CGFLOAT_MAX), &fitRange);
1428
1429     RetainPtr<CGPathRef> titlePath = adoptCF(CGPathCreateWithRect(CGRectMake(0, 0, titleTextSize.width, titleTextSize.height), nullptr));
1430     RetainPtr<CTFrameRef> titleFrame = adoptCF(CTFramesetterCreateFrame(titleFramesetter.get(), fitRange, titlePath.get(), nullptr));
1431
1432     CFArrayRef ctLines = CTFrameGetLines(titleFrame.get());
1433     CFIndex lineCount = CFArrayGetCount(ctLines);
1434     if (!lineCount)
1435         return;
1436
1437     // Lay out and record the first (attachmentTitleMaximumLineCount - 1) lines.
1438     CFIndex lineIndex = 0;
1439     for (; lineIndex < std::min(attachmentTitleMaximumLineCount - 1, lineCount); ++lineIndex) {
1440         CTLineRef line = (CTLineRef)CFArrayGetValueAtIndex(ctLines, lineIndex);
1441         addLine(line);
1442     }
1443
1444     if (lineIndex == lineCount)
1445         return;
1446
1447     // We had text that didn't fit in the first (attachmentTitleMaximumLineCount - 1) lines.
1448     // Combine it into one last line, and center-truncate it.
1449     CTLineRef firstRemainingLine = (CTLineRef)CFArrayGetValueAtIndex(ctLines, lineIndex);
1450     CFIndex remainingRangeStart = CTLineGetStringRange(firstRemainingLine).location;
1451     NSRange remainingRange = NSMakeRange(remainingRangeStart, [attributedTitle length] - remainingRangeStart);
1452     NSAttributedString *remainingString = [attributedTitle attributedSubstringFromRange:remainingRange];
1453     RetainPtr<CTLineRef> remainingLine = adoptCF(CTLineCreateWithAttributedString((CFAttributedStringRef)remainingString));
1454     RetainPtr<NSAttributedString> ellipsisString = adoptNS([[NSAttributedString alloc] initWithString:@"\u2026" attributes:textAttributes]);
1455     RetainPtr<CTLineRef> ellipsisLine = adoptCF(CTLineCreateWithAttributedString((CFAttributedStringRef)ellipsisString.get()));
1456     RetainPtr<CTLineRef> truncatedLine = adoptCF(CTLineCreateTruncatedLine(remainingLine.get(), attachmentTitleMaximumWidth, kCTLineTruncationMiddle, ellipsisLine.get()));
1457
1458     if (!truncatedLine)
1459         truncatedLine = remainingLine;
1460
1461     addLine(truncatedLine.get());
1462 }
1463
1464 void AttachmentInfo::buildSingleLine(const String& text, UIFont *font, UIColor *color)
1465 {
1466     if (text.isEmpty())
1467         return;
1468
1469     NSDictionary *textAttributes = @{
1470         (id)kCTFontAttributeName: font,
1471         (id)kCTForegroundColorAttributeName: color
1472     };
1473     RetainPtr<NSAttributedString> attributedText = adoptNS([[NSAttributedString alloc] initWithString:text attributes:textAttributes]);
1474
1475     addLine(adoptCF(CTLineCreateWithAttributedString((CFAttributedStringRef)attributedText.get())).get());
1476 }
1477
1478 static BOOL getAttachmentProgress(const RenderAttachment& attachment, float& progress)
1479 {
1480     String progressString = attachment.attachmentElement().fastGetAttribute(progressAttr);
1481     if (progressString.isEmpty())
1482         return NO;
1483     bool validProgress;
1484     progress = progressString.toFloat(&validProgress);
1485     return validProgress;
1486 }
1487
1488 static RetainPtr<UIImage> iconForAttachment(const RenderAttachment& attachment, FloatSize& size)
1489 {
1490     String MIMEType = attachment.attachmentElement().attachmentType();
1491
1492     String fileName;
1493     if (File* file = attachment.attachmentElement().file())
1494         fileName = file->name();
1495
1496     if (fileName.isEmpty())
1497         fileName = attachment.attachmentElement().attachmentTitle();
1498
1499     RetainPtr<UIImage> result;
1500
1501     RetainPtr<UIDocumentInteractionController> documentInteractionController = adoptNS([[getUIDocumentInteractionControllerClass() alloc] init]);
1502     [documentInteractionController setName:fileName];
1503     [documentInteractionController setUTI:static_cast<NSString *>(mimeTypeFromUTITree(MIMEType.createCFString().get()).get())];
1504
1505     NSArray *icons = [documentInteractionController icons];
1506     if (!icons.count)
1507         return nil;
1508
1509     result = icons.lastObject;
1510
1511     BOOL useHeightForClosestMatch = [result size].height > [result size].width;
1512     CGFloat bestMatchRatio = -1;
1513
1514     for (UIImage *icon in icons) {
1515         CGFloat iconSize = useHeightForClosestMatch ? icon.size.height : icon.size.width;
1516
1517         CGFloat matchRatio = (attachmentIconSize / iconSize) - 1.0f;
1518         if (matchRatio < 0.3f) {
1519             matchRatio = CGFAbs(matchRatio);
1520             if ((bestMatchRatio == -1) || (matchRatio < bestMatchRatio)) {
1521                 result = icon;
1522                 bestMatchRatio = matchRatio;
1523             }
1524         }
1525     }
1526
1527     CGFloat iconAspect = [result size].width / [result size].height;
1528     size = largestRectWithAspectRatioInsideRect(iconAspect, FloatRect(0, 0, attachmentIconSize, attachmentIconSize)).size();
1529
1530     return result;
1531 }
1532
1533 AttachmentInfo::AttachmentInfo(const RenderAttachment& attachment)
1534 {
1535     attachmentRect = FloatRect(0, 0, attachmentSize.width, attachmentSize.height);
1536
1537     hasProgress = getAttachmentProgress(attachment, progress);
1538
1539     String action = attachment.attachmentElement().fastGetAttribute(actionAttr);
1540     String subtitle = attachment.attachmentElement().fastGetAttribute(subtitleAttr);
1541
1542     CGFloat yOffset = 0;
1543
1544     if (hasProgress) {
1545         progressRect = FloatRect((attachmentRect.width() / 2) - (attachmentProgressSize / 2), 0, attachmentProgressSize, attachmentProgressSize);
1546         yOffset += attachmentProgressSize + attachmentItemMargin;
1547     }
1548
1549     if (action.isEmpty() && !hasProgress) {
1550         FloatSize iconSize;
1551         icon = iconForAttachment(attachment, iconSize);
1552         if (icon) {
1553             iconRect = FloatRect(FloatPoint((attachmentRect.width() / 2) - (iconSize.width() / 2), 0), iconSize);
1554             yOffset += iconRect.height() + attachmentItemMargin;
1555         }
1556     } else
1557         buildSingleLine(action, attachmentActionFont(), attachmentActionColor());
1558
1559     buildTitleLines(attachment);
1560     buildSingleLine(subtitle, attachmentSubtitleFont(), attachmentSubtitleColor());
1561
1562     if (!lines.isEmpty()) {
1563         for (auto& line : lines) {
1564             line.rect.setY(yOffset);
1565             yOffset += line.rect.height() + attachmentItemMargin;
1566         }
1567     }
1568
1569     contentYOrigin = (attachmentRect.height() / 2) - (yOffset / 2);
1570 }
1571
1572 LayoutSize RenderThemeIOS::attachmentIntrinsicSize(const RenderAttachment&) const
1573 {
1574     return LayoutSize(FloatSize(attachmentSize));
1575 }
1576
1577 int RenderThemeIOS::attachmentBaseline(const RenderAttachment& attachment) const
1578 {
1579     AttachmentInfo info(attachment);
1580     return info.baseline;
1581 }
1582
1583 static void paintAttachmentIcon(GraphicsContext& context, AttachmentInfo& info)
1584 {
1585     if (!info.icon)
1586         return;
1587
1588     RefPtr<Image> iconImage = BitmapImage::create([info.icon CGImage]);
1589     if (!iconImage)
1590         return;
1591
1592     context.drawImage(*iconImage, info.iconRect);
1593 }
1594
1595
1596 static void paintAttachmentText(GraphicsContext& context, AttachmentInfo& info)
1597 {
1598     for (const auto& line : info.lines) {
1599         GraphicsContextStateSaver saver(context);
1600
1601         context.translate(toFloatSize(line.rect.minXMaxYCorner()));
1602         context.scale(FloatSize(1, -1));
1603
1604         CGContextSetTextMatrix(context.platformContext(), CGAffineTransformIdentity);
1605         CTLineDraw(line.line.get(), context.platformContext());
1606     }
1607 }
1608
1609 static void paintAttachmentProgress(GraphicsContext& context, AttachmentInfo& info)
1610 {
1611     GraphicsContextStateSaver saver(context);
1612
1613     // FIXME: Implement progress indicator.
1614     context.fillRect(info.progressRect, Color(0, 255, 0));
1615 }
1616
1617 static void paintAttachmentBorder(GraphicsContext& context, AttachmentInfo& info)
1618 {
1619     Path borderPath;
1620     borderPath.addRoundedRect(info.attachmentRect, FloatSize(attachmentBorderRadius, attachmentBorderRadius));
1621     context.setStrokeColor(attachmentBorderColor());
1622     context.setStrokeThickness(1);
1623     context.strokePath(borderPath);
1624 }
1625
1626 bool RenderThemeIOS::paintAttachment(const RenderObject& renderer, const PaintInfo& paintInfo, const IntRect& paintRect)
1627 {
1628     if (!is<RenderAttachment>(renderer))
1629         return false;
1630
1631     const RenderAttachment& attachment = downcast<RenderAttachment>(renderer);
1632
1633     AttachmentInfo info(attachment);
1634
1635     GraphicsContext& context = paintInfo.context();
1636     GraphicsContextStateSaver saver(context);
1637
1638     context.translate(toFloatSize(paintRect.location()));
1639
1640     paintAttachmentBorder(context, info);
1641
1642     context.translate(FloatSize(0, info.contentYOrigin));
1643
1644     if (info.hasProgress)
1645         paintAttachmentProgress(context, info);
1646     else if (info.icon)
1647         paintAttachmentIcon(context, info);
1648
1649     paintAttachmentText(context, info);
1650
1651     return true;
1652 }
1653
1654 #endif // ENABLE(ATTACHMENT_ELEMENT)
1655
1656 } // namespace WebCore
1657
1658 #endif //PLATFORM(IOS)