parseHTMLInteger() should take a StringView in parameter
[WebKit-https.git] / Source / WebCore / rendering / RenderThemeIOS.mm
index 12861ba..ffc30d4 100644 (file)
 
 #if PLATFORM(IOS)
 
+#import "BitmapImage.h"
 #import "CSSPrimitiveValue.h"
 #import "CSSToLengthConversionData.h"
 #import "CSSValueKeywords.h"
 #import "CoreTextSPI.h"
 #import "DateComponents.h"
 #import "Document.h"
+#import "File.h"
 #import "FloatRoundedRect.h"
-#import "Font.h"
 #import "FontCache.h"
+#import "FontCascade.h"
 #import "Frame.h"
+#import "FrameSelection.h"
 #import "FrameView.h"
+#import "GeometryUtilities.h"
 #import "Gradient.h"
 #import "GraphicsContext.h"
 #import "GraphicsContextCG.h"
+#import "HTMLAttachmentElement.h"
 #import "HTMLInputElement.h"
 #import "HTMLNames.h"
 #import "HTMLSelectElement.h"
 #import "NodeRenderStyle.h"
 #import "Page.h"
 #import "PaintInfo.h"
+#import "PathUtilities.h"
 #import "PlatformLocale.h"
+#import "RenderAttachment.h"
 #import "RenderObject.h"
 #import "RenderProgress.h"
 #import "RenderStyle.h"
 #import "RenderThemeIOS.h"
 #import "RenderView.h"
 #import "SoftLinking.h"
+#import "UIKitSPI.h"
+#import "UTIUtilities.h"
 #import "UserAgentScripts.h"
 #import "UserAgentStyleSheets.h"
 #import "WebCoreThreadRun.h"
 #import <wtf/RefPtr.h>
 #import <wtf/StdLibExtras.h>
 
-@interface UIApplication
-+ (UIApplication *)sharedApplication;
-@property(nonatomic,copy) NSString *preferredContentSizeCategory;
-@end
-
 SOFT_LINK_FRAMEWORK(UIKit)
 SOFT_LINK_CLASS(UIKit, UIApplication)
+SOFT_LINK_CLASS(UIKit, UIColor)
+SOFT_LINK_CLASS(UIKit, UIDocumentInteractionController)
+SOFT_LINK_CLASS(UIKit, UIImage)
 SOFT_LINK_CONSTANT(UIKit, UIContentSizeCategoryDidChangeNotification, CFStringRef)
 #define UIContentSizeCategoryDidChangeNotification getUIContentSizeCategoryDidChangeNotification()
 
@@ -83,6 +90,8 @@ SOFT_LINK_CONSTANT(UIKit, UIContentSizeCategoryDidChangeNotification, CFStringRe
 
 namespace WebCore {
 
+using namespace HTMLNames;
+
 const float ControlBaseHeight = 20;
 const float ControlBaseFontSize = 11;
 
@@ -132,7 +141,7 @@ static CGFunctionRef getSharedFunctionRef(IOSGradientRef gradient, Interpolation
     CGFunctionRef function = nullptr;
 
     static HashMap<IOSGradientRef, CGFunctionRef>* linearFunctionRefs;
-    static HashMap<IOSGradientRef, CGFunctionRef>* exponentialFunctionRefs;;
+    static HashMap<IOSGradientRef, CGFunctionRef>* exponentialFunctionRefs;
 
     if (interpolation == LinearInterpolation) {
         if (!linearFunctionRefs)
@@ -163,13 +172,13 @@ static CGFunctionRef getSharedFunctionRef(IOSGradientRef gradient, Interpolation
 
 static void drawAxialGradient(CGContextRef context, IOSGradientRef gradient, const FloatPoint& startPoint, const FloatPoint& stopPoint, Interpolation interpolation)
 {
-    RetainPtr<CGShadingRef> shading = adoptCF(CGShadingCreateAxial(deviceRGBColorSpaceRef(), startPoint, stopPoint, getSharedFunctionRef(gradient, interpolation), false, false));
+    RetainPtr<CGShadingRef> shading = adoptCF(CGShadingCreateAxial(sRGBColorSpaceRef(), startPoint, stopPoint, getSharedFunctionRef(gradient, interpolation), false, false));
     CGContextDrawShading(context, shading.get());
 }
 
 static void drawRadialGradient(CGContextRef context, IOSGradientRef gradient, const FloatPoint& startPoint, float startRadius, const FloatPoint& stopPoint, float stopRadius, Interpolation interpolation)
 {
-    RetainPtr<CGShadingRef> shading = adoptCF(CGShadingCreateRadial(deviceRGBColorSpaceRef(), startPoint, startRadius, stopPoint, stopRadius, getSharedFunctionRef(gradient, interpolation), false, false));
+    RetainPtr<CGShadingRef> shading = adoptCF(CGShadingCreateRadial(sRGBColorSpaceRef(), startPoint, startRadius, stopPoint, stopRadius, getSharedFunctionRef(gradient, interpolation), false, false));
     CGContextDrawShading(context, shading.get());
 }
 
@@ -287,13 +296,13 @@ RenderThemeIOS::RenderThemeIOS()
 
 PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page*)
 {
-    static RenderTheme* renderTheme = RenderThemeIOS::create().leakRef();
-    return renderTheme;
+    static RenderTheme& renderTheme = RenderThemeIOS::create().leakRef();
+    return &renderTheme;
 }
 
-PassRefPtr<RenderTheme> RenderThemeIOS::create()
+Ref<RenderTheme> RenderThemeIOS::create()
 {
-    return adoptRef(new RenderThemeIOS);
+    return adoptRef(*new RenderThemeIOS);
 }
 
 static String& _contentSizeCategory()
@@ -320,17 +329,17 @@ const Color& RenderThemeIOS::shadowColor() const
     return color;
 }
 
-FloatRect RenderThemeIOS::addRoundedBorderClip(const RenderObject& box, GraphicsContext* context, const IntRect& rect)
+FloatRect RenderThemeIOS::addRoundedBorderClip(const RenderObject& box, GraphicsContext& context, const IntRect& rect)
 {
     // To fix inner border bleeding issues <rdar://problem/9812507>, we clip to the outer border and assert that
     // the border is opaque or transparent, unless we're checked because checked radio/checkboxes show no bleeding.
-    RenderStyle& style = box.style();
+    auto& style = box.style();
     RoundedRect border = isChecked(box) ? style.getRoundedInnerBorderFor(rect) : style.getRoundedBorderFor(rect);
 
     if (border.isRounded())
-        context->clipRoundedRect(FloatRoundedRect(border));
+        context.clipRoundedRect(FloatRoundedRect(border));
     else
-        context->clip(border.rect());
+        context.clip(border.rect());
 
     if (isChecked(box)) {
         ASSERT(style.visitedDependentColor(CSSPropertyBorderTopColor).alpha() % 255 == 0);
@@ -342,7 +351,7 @@ FloatRect RenderThemeIOS::addRoundedBorderClip(const RenderObject& box, Graphics
     return border.rect();
 }
 
-void RenderThemeIOS::adjustCheckboxStyle(StyleResolver&, RenderStyle& style, Element*) const
+void RenderThemeIOS::adjustCheckboxStyle(StyleResolver&, RenderStyle& style, const Element*) const
 {
     if (!style.width().isIntrinsicOrAuto() && !style.height().isAuto())
         return;
@@ -361,21 +370,34 @@ static CGPoint shortened(CGPoint start, CGPoint end, float width)
     return CGPointMake(start.x + x * ratio, start.y + y * ratio);
 }
 
+static void drawJoinedLines(CGContextRef context, CGPoint points[], unsigned count, bool antialias, CGLineCap lineCap)
+{
+    CGContextSetShouldAntialias(context, antialias);
+    CGContextBeginPath(context);
+    CGContextSetLineCap(context, lineCap);
+    CGContextMoveToPoint(context, points[0].x, points[0].y);
+    
+    for (unsigned i = 1; i < count; ++i)
+        CGContextAddLineToPoint(context, points[i].x, points[i].y);
+
+    CGContextStrokePath(context);
+}
+
 bool RenderThemeIOS::paintCheckboxDecorations(const RenderObject& box, const PaintInfo& paintInfo, const IntRect& rect)
 {
-    GraphicsContextStateSaver stateSaver(*paintInfo.context);
-    FloatRect clip = addRoundedBorderClip(box, paintInfo.context, rect);
+    GraphicsContextStateSaver stateSaver(paintInfo.context());
+    FloatRect clip = addRoundedBorderClip(box, paintInfo.context(), rect);
 
     float width = clip.width();
     float height = clip.height();
 
-    CGContextRef cgContext = paintInfo.context->platformContext();
+    CGContextRef cgContext = paintInfo.context().platformContext();
     if (isChecked(box)) {
         drawAxialGradient(cgContext, gradientWithName(ConcaveGradient), clip.location(), FloatPoint(clip.x(), clip.maxY()), LinearInterpolation);
 
-        static float thicknessRatio = 2 / 14.0;
-        static CGSize size = { 14.0f, 14.0f };
-        static CGPoint pathRatios[3] = {
+        static const float thicknessRatio = 2 / 14.0;
+        static const CGSize size = { 14.0f, 14.0f };
+        static const CGPoint pathRatios[3] = {
             { 2.5f / size.width, 7.5f / size.height },
             { 5.5f / size.width, 10.5f / size.height },
             { 11.5f / size.width, 2.5f / size.height }
@@ -394,15 +416,15 @@ bool RenderThemeIOS::paintCheckboxDecorations(const RenderObject& box, const Pai
             shortened(line[2], line[1], lineWidth / 4.0f)
         };
 
-        paintInfo.context->setStrokeThickness(lineWidth);
-        paintInfo.context->setStrokeColor(Color(0.0f, 0.0f, 0.0f, 0.7f), ColorSpaceDeviceRGB);
-
-        paintInfo.context->drawJoinedLines(shadow, 3, true, kCGLineCapSquare);
-
-        paintInfo.context->setStrokeThickness(std::min(clip.width(), clip.height()) * thicknessRatio);
-        paintInfo.context->setStrokeColor(Color(1.0f, 1.0f, 1.0f, 240 / 255.0f), ColorSpaceDeviceRGB);
+        lineWidth = std::max<float>(lineWidth, 1);
+        CGContextSetLineWidth(cgContext, lineWidth);
+        CGContextSetStrokeColorWithColor(cgContext, cachedCGColor(Color(0.0f, 0.0f, 0.0f, 0.7f)));
+        drawJoinedLines(cgContext, shadow, 3, true, kCGLineCapSquare);
 
-        paintInfo.context->drawJoinedLines(line, 3, true);
+        lineWidth = std::max<float>(std::min(clip.width(), clip.height()) * thicknessRatio, 1);
+        CGContextSetLineWidth(cgContext, lineWidth);
+        CGContextSetStrokeColorWithColor(cgContext, cachedCGColor(Color(1.0f, 1.0f, 1.0f, 240 / 255.0f)));
+        drawJoinedLines(cgContext, line, 3, true, kCGLineCapButt);
     } else {
         FloatPoint bottomCenter(clip.x() + clip.width() / 2.0f, clip.maxY());
         drawAxialGradient(cgContext, gradientWithName(ShadeGradient), clip.location(), FloatPoint(clip.x(), clip.maxY()), LinearInterpolation);
@@ -412,13 +434,8 @@ bool RenderThemeIOS::paintCheckboxDecorations(const RenderObject& box, const Pai
     return false;
 }
 
-int RenderThemeIOS::baselinePosition(const RenderObject& renderer) const
+int RenderThemeIOS::baselinePosition(const RenderBox& box) const
 {
-    if (!is<RenderBox>(renderer))
-        return 0;
-
-    const auto& box = downcast<RenderBox>(renderer);
-
     if (box.style().appearance() == CheckboxPart || box.style().appearance() == RadioPart)
         return box.marginTop() + box.height() - 2; // The baseline is 2px up from the bottom of the checkbox/radio in AppKit.
     if (box.style().appearance() == MenulistPart)
@@ -438,7 +455,7 @@ bool RenderThemeIOS::isControlStyled(const RenderStyle& style, const BorderData&
     return RenderTheme::isControlStyled(style, border, background, backgroundColor);
 }
 
-void RenderThemeIOS::adjustRadioStyle(StyleResolver&, RenderStyle& style, Element*) const
+void RenderThemeIOS::adjustRadioStyle(StyleResolver&, RenderStyle& style, const Element*) const
 {
     if (!style.width().isIntrinsicOrAuto() && !style.height().isAuto())
         return;
@@ -451,25 +468,25 @@ void RenderThemeIOS::adjustRadioStyle(StyleResolver&, RenderStyle& style, Elemen
 
 bool RenderThemeIOS::paintRadioDecorations(const RenderObject& box, const PaintInfo& paintInfo, const IntRect& rect)
 {
-    GraphicsContextStateSaver stateSaver(*paintInfo.context);
-    FloatRect clip = addRoundedBorderClip(box, paintInfo.context, rect);
+    GraphicsContextStateSaver stateSaver(paintInfo.context());
+    FloatRect clip = addRoundedBorderClip(box, paintInfo.context(), rect);
 
-    CGContextRef cgContext = paintInfo.context->platformContext();
+    CGContextRef cgContext = paintInfo.context().platformContext();
     if (isChecked(box)) {
         drawAxialGradient(cgContext, gradientWithName(ConcaveGradient), clip.location(), FloatPoint(clip.x(), clip.maxY()), LinearInterpolation);
 
         // The inner circle is 6 / 14 the size of the surrounding circle, 
         // leaving 8 / 14 around it. (8 / 14) / 2 = 2 / 7.
 
-        static float InnerInverseRatio = 2 / 7.0;
+        static const float InnerInverseRatio = 2 / 7.0;
 
         clip.inflateX(-clip.width() * InnerInverseRatio);
         clip.inflateY(-clip.height() * InnerInverseRatio);
 
-        paintInfo.context->drawRaisedEllipse(clip, Color::white, ColorSpaceDeviceRGB, shadowColor(), ColorSpaceDeviceRGB);
+        paintInfo.context().drawRaisedEllipse(clip, Color::white, shadowColor());
 
         FloatSize radius(clip.width() / 2.0f, clip.height() / 2.0f);
-        paintInfo.context->clipRoundedRect(FloatRoundedRect(clip, radius, radius, radius, radius));
+        paintInfo.context().clipRoundedRect(FloatRoundedRect(clip, radius, radius, radius, radius));
     }
     FloatPoint bottomCenter(clip.x() + clip.width() / 2.0, clip.maxY());
     drawAxialGradient(cgContext, gradientWithName(ShadeGradient), clip.location(), FloatPoint(clip.x(), clip.maxY()), LinearInterpolation);
@@ -479,18 +496,18 @@ bool RenderThemeIOS::paintRadioDecorations(const RenderObject& box, const PaintI
 
 bool RenderThemeIOS::paintTextFieldDecorations(const RenderObject& box, const PaintInfo& paintInfo, const FloatRect& rect)
 {
-    RenderStyle& style = box.style();
+    auto& style = box.style();
     FloatPoint point(rect.x() + style.borderLeftWidth(), rect.y() + style.borderTopWidth());
 
-    GraphicsContextStateSaver stateSaver(*paintInfo.context);
+    GraphicsContextStateSaver stateSaver(paintInfo.context());
 
-    paintInfo.context->clipRoundedRect(style.getRoundedBorderFor(LayoutRect(rect)).pixelSnappedRoundedRectForPainting(box.document().deviceScaleFactor()));
+    paintInfo.context().clipRoundedRect(style.getRoundedBorderFor(LayoutRect(rect)).pixelSnappedRoundedRectForPainting(box.document().deviceScaleFactor()));
 
     // This gradient gets drawn black when printing.
     // Do not draw the gradient if there is no visible top border.
     bool topBorderIsInvisible = !style.hasBorder() || !style.borderTopWidth() || style.borderTopIsTransparent();
     if (!box.view().printing() && !topBorderIsInvisible)
-        drawAxialGradient(paintInfo.context->platformContext(), gradientWithName(InsetGradient), point, FloatPoint(CGPointMake(point.x(), point.y() + 3.0f)), LinearInterpolation);
+        drawAxialGradient(paintInfo.context().platformContext(), gradientWithName(InsetGradient), point, FloatPoint(CGPointMake(point.x(), point.y() + 3.0f)), LinearInterpolation);
     return false;
 }
 
@@ -506,13 +523,16 @@ const float MenuListBaseFontSize = 11;
 
 const float MenuListArrowWidth = 7;
 const float MenuListArrowHeight = 6;
-const float MenuListButtonPaddingRight = 19;
+const float MenuListButtonPaddingAfter = 19;
 
-int RenderThemeIOS::popupInternalPaddingRight(RenderStyle& style) const
+LengthBox RenderThemeIOS::popupInternalPaddingBox(const RenderStyle& style) const
 {
-    if (style.appearance() == MenulistButtonPart)
-        return MenuListButtonPaddingRight + style.borderTopWidth();
-    return 0;
+    if (style.appearance() == MenulistButtonPart) {
+        if (style.direction() == RTL)
+            return { 0, 0, 0, static_cast<int>(MenuListButtonPaddingAfter + style.borderTopWidth()) };
+        return { 0, static_cast<int>(MenuListButtonPaddingAfter + style.borderTopWidth()), 0, 0 };
+    }
+    return { 0, 0, 0, 0 };
 }
 
 void RenderThemeIOS::adjustRoundBorderRadius(RenderStyle& style, RenderBox& box)
@@ -526,7 +546,7 @@ void RenderThemeIOS::adjustRoundBorderRadius(RenderStyle& style, RenderBox& box)
     style.setBorderRadius(LengthSize(radiusWidth, radiusHeight));
 }
 
-static void applyCommonButtonPaddingToStyle(RenderStyle& style, Element& element)
+static void applyCommonButtonPaddingToStyle(RenderStyle& style, const Element& element)
 {
     Document& document = element.document();
     RefPtr<CSSPrimitiveValue> emSize = CSSPrimitiveValue::create(0.5, CSSPrimitiveValue::CSS_EMS);
@@ -534,7 +554,7 @@ static void applyCommonButtonPaddingToStyle(RenderStyle& style, Element& element
     style.setPaddingBox(LengthBox(0, pixels, 0, pixels));
 }
 
-static void adjustSelectListButtonStyle(RenderStyle& style, Element& element)
+static void adjustSelectListButtonStyle(RenderStyle& style, const Element& element)
 {
     // Enforce "padding: 0 0.5em".
     applyCommonButtonPaddingToStyle(style, element);
@@ -545,24 +565,22 @@ static void adjustSelectListButtonStyle(RenderStyle& style, Element& element)
     
 class RenderThemeMeasureTextClient : public MeasureTextClient {
 public:
-    RenderThemeMeasureTextClient(const Font& font, RenderObject& renderObject, const RenderStyle& style)
+    RenderThemeMeasureTextClient(const FontCascade& font, const RenderStyle& style)
         : m_font(font)
-        , m_renderObject(renderObject)
         , m_style(style)
     {
     }
-    virtual float measureText(const String& string) const override
+    float measureText(const String& string) const override
     {
-        TextRun run = RenderBlock::constructTextRun(&m_renderObject, m_font, string, m_style, TextRun::AllowTrailingExpansion | TextRun::ForbidLeadingExpansion, DefaultTextRunFlags);
+        TextRun run = RenderBlock::constructTextRun(string, m_style);
         return m_font.width(run);
     }
 private:
-    const Font& m_font;
-    RenderObject& m_renderObject;
+    const FontCascade& m_font;
     const RenderStyle& m_style;
 };
 
-static void adjustInputElementButtonStyle(RenderStyle& style, HTMLInputElement& inputElement)
+static void adjustInputElementButtonStyle(RenderStyle& style, const HTMLInputElement& inputElement)
 {
     // Always Enforce "padding: 0 0.5em".
     applyCommonButtonPaddingToStyle(style, inputElement);
@@ -577,25 +595,20 @@ static void adjustInputElementButtonStyle(RenderStyle& style, HTMLInputElement&
         return;
 
     // Enforce the width and set the box-sizing to content-box to not conflict with the padding.
-    Font font = style.font();
-    
-    RenderObject* renderer = inputElement.renderer();
-    if (font.primaryFont()->isSVGFont() && !renderer)
-        return;
+    FontCascade font = style.fontCascade();
     
-    FontCachePurgePreventer fontCachePurgePreventer;
-    float maximumWidth = localizedDateCache().maximumWidthForDateType(dateType, font, RenderThemeMeasureTextClient(font, *renderer, style));
+    float maximumWidth = localizedDateCache().maximumWidthForDateType(dateType, font, RenderThemeMeasureTextClient(font, style));
 
     ASSERT(maximumWidth >= 0);
 
-    if (maximumWidth > 0) {    
-        int width = static_cast<int>(maximumWidth + MenuListButtonPaddingRight);
+    if (maximumWidth > 0) {
+        int width = static_cast<int>(maximumWidth + MenuListButtonPaddingAfter);
         style.setWidth(Length(width, Fixed));
         style.setBoxSizing(CONTENT_BOX);
     }
 }
 
-void RenderThemeIOS::adjustMenuListButtonStyle(StyleResolver&, RenderStyle& style, Element* element) const
+void RenderThemeIOS::adjustMenuListButtonStyle(StyleResolver&, RenderStyle& style, const Element* element) const
 {
     // Set the min-height to be at least MenuListMinHeight.
     if (style.height().isAuto())
@@ -609,33 +622,48 @@ void RenderThemeIOS::adjustMenuListButtonStyle(StyleResolver&, RenderStyle& styl
     // Enforce some default styles in the case that this is a non-multiple <select> element,
     // or a date input. We don't force these if this is just an element with
     // "-webkit-appearance: menulist-button".
-    if (element->hasTagName(HTMLNames::selectTag) && !element->hasAttribute(HTMLNames::multipleAttr))
+    if (is<HTMLSelectElement>(*element) && !element->hasAttributeWithoutSynchronization(HTMLNames::multipleAttr))
         adjustSelectListButtonStyle(style, *element);
-    else if (element->hasTagName(HTMLNames::inputTag))
-        adjustInputElementButtonStyle(style, static_cast<HTMLInputElement&>(*element));
+    else if (is<HTMLInputElement>(*element))
+        adjustInputElementButtonStyle(style, downcast<HTMLInputElement>(*element));
 }
 
-bool RenderThemeIOS::paintMenuListButtonDecorations(const RenderObject& box, const PaintInfo& paintInfo, const FloatRect& rect)
+bool RenderThemeIOS::paintMenuListButtonDecorations(const RenderBox& box, const PaintInfo& paintInfo, const FloatRect& rect)
 {
-    RenderStyle& style = box.style();
+    auto& style = box.style();
+    bool isRTL = style.direction() == RTL;
     float borderTopWidth = style.borderTopWidth();
     FloatRect clip(rect.x() + style.borderLeftWidth(), rect.y() + style.borderTopWidth(), rect.width() - style.borderLeftWidth() - style.borderRightWidth(), rect.height() - style.borderTopWidth() - style.borderBottomWidth());
-    CGContextRef cgContext = paintInfo.context->platformContext();
+    CGContextRef cgContext = paintInfo.context().platformContext();
 
     float adjustLeft = 0.5;
     float adjustRight = 0.5;
     float adjustTop = 0.5;
     float adjustBottom = 0.5;
 
-    // Paint left-hand title portion.
+    // Paint title portion.
     {
-        FloatRect titleClip(clip.x() - adjustLeft, clip.y() - adjustTop, clip.width() - MenuListButtonPaddingRight + adjustLeft, clip.height() + adjustTop + adjustBottom);
-
-        GraphicsContextStateSaver stateSaver(*paintInfo.context);
+        float leftInset = isRTL ? MenuListButtonPaddingAfter : 0;
+        FloatRect titleClip(clip.x() + leftInset - adjustLeft, clip.y() - adjustTop, clip.width() - MenuListButtonPaddingAfter + adjustLeft, clip.height() + adjustTop + adjustBottom);
+
+        GraphicsContextStateSaver stateSaver(paintInfo.context());
+
+        FloatSize topLeftRadius;
+        FloatSize topRightRadius;
+        FloatSize bottomLeftRadius;
+        FloatSize bottomRightRadius;
+
+        if (isRTL) {
+            topRightRadius = FloatSize(valueForLength(style.borderTopRightRadius().width(), rect.width()) - style.borderRightWidth(), valueForLength(style.borderTopRightRadius().height(), rect.height()) - style.borderTopWidth());
+            bottomRightRadius = FloatSize(valueForLength(style.borderBottomRightRadius().width(), rect.width()) - style.borderRightWidth(), valueForLength(style.borderBottomRightRadius().height(), rect.height()) - style.borderBottomWidth());
+        } else {
+            topLeftRadius = FloatSize(valueForLength(style.borderTopLeftRadius().width(), rect.width()) - style.borderLeftWidth(), valueForLength(style.borderTopLeftRadius().height(), rect.height()) - style.borderTopWidth());
+            bottomLeftRadius = FloatSize(valueForLength(style.borderBottomLeftRadius().width(), rect.width()) - style.borderLeftWidth(), valueForLength(style.borderBottomLeftRadius().height(), rect.height()) - style.borderBottomWidth());
+        }
 
-        paintInfo.context->clipRoundedRect(FloatRoundedRect(titleClip,
-            FloatSize(valueForLength(style.borderTopLeftRadius().width(), rect.width()) - style.borderLeftWidth(), valueForLength(style.borderTopLeftRadius().height(), rect.height()) - style.borderTopWidth()), FloatSize(0, 0),
-            FloatSize(valueForLength(style.borderBottomLeftRadius().width(), rect.width()) - style.borderLeftWidth(), valueForLength(style.borderBottomLeftRadius().height(), rect.height()) - style.borderBottomWidth()), FloatSize(0, 0)));
+        paintInfo.context().clipRoundedRect(FloatRoundedRect(titleClip,
+            topLeftRadius, topRightRadius,
+            bottomLeftRadius, bottomRightRadius));
 
         drawAxialGradient(cgContext, gradientWithName(ShadeGradient), titleClip.location(), FloatPoint(titleClip.x(), titleClip.maxY()), LinearInterpolation);
         drawAxialGradient(cgContext, gradientWithName(ShineGradient), FloatPoint(titleClip.x(), titleClip.maxY()), titleClip.location(), ExponentialInterpolation);
@@ -643,28 +671,45 @@ bool RenderThemeIOS::paintMenuListButtonDecorations(const RenderObject& box, con
 
     // Draw the separator after the initial padding.
 
-    float separator = clip.maxX() - MenuListButtonPaddingRight;
+    float separatorPosition = isRTL ? (clip.x() + MenuListButtonPaddingAfter) : (clip.maxX() - MenuListButtonPaddingAfter);
 
-    box.drawLineForBoxSide(paintInfo.context, separator - borderTopWidth, clip.y(), separator, clip.maxY(), BSRight, style.visitedDependentColor(CSSPropertyBorderTopColor), style.borderTopStyle(), 0, 0);
+    box.drawLineForBoxSide(paintInfo.context(), FloatRect(FloatPoint(separatorPosition - borderTopWidth, clip.y()), FloatPoint(separatorPosition, clip.maxY())), BSRight, style.visitedDependentColor(CSSPropertyBorderTopColor), style.borderTopStyle(), 0, 0);
 
-    FloatRect buttonClip(separator - adjustTop, clip.y() - adjustTop, MenuListButtonPaddingRight + adjustTop + adjustRight, clip.height() + adjustTop + adjustBottom);
+    FloatRect buttonClip;
+    if (isRTL)
+        buttonClip = FloatRect(clip.x() - adjustTop, clip.y() - adjustTop, MenuListButtonPaddingAfter + adjustTop + adjustLeft, clip.height() + adjustTop + adjustBottom);
+    else
+        buttonClip = FloatRect(separatorPosition - adjustTop, clip.y() - adjustTop, MenuListButtonPaddingAfter + adjustTop + adjustRight, clip.height() + adjustTop + adjustBottom);
 
     // Now paint the button portion.
     {
-        GraphicsContextStateSaver stateSaver(*paintInfo.context);
+        GraphicsContextStateSaver stateSaver(paintInfo.context());
+
+        FloatSize topLeftRadius;
+        FloatSize topRightRadius;
+        FloatSize bottomLeftRadius;
+        FloatSize bottomRightRadius;
+
+        if (isRTL) {
+            topLeftRadius = FloatSize(valueForLength(style.borderTopLeftRadius().width(), rect.width()) - style.borderLeftWidth(), valueForLength(style.borderTopLeftRadius().height(), rect.height()) - style.borderTopWidth());
+            bottomLeftRadius = FloatSize(valueForLength(style.borderBottomLeftRadius().width(), rect.width()) - style.borderLeftWidth(), valueForLength(style.borderBottomLeftRadius().height(), rect.height()) - style.borderBottomWidth());
+        } else {
+            topRightRadius = FloatSize(valueForLength(style.borderTopRightRadius().width(), rect.width()) - style.borderRightWidth(), valueForLength(style.borderTopRightRadius().height(), rect.height()) - style.borderTopWidth());
+            bottomRightRadius = FloatSize(valueForLength(style.borderBottomRightRadius().width(), rect.width()) - style.borderRightWidth(), valueForLength(style.borderBottomRightRadius().height(), rect.height()) - style.borderBottomWidth());
+        }
 
-        paintInfo.context->clipRoundedRect(FloatRoundedRect(buttonClip,
-            FloatSize(0, 0), FloatSize(valueForLength(style.borderTopRightRadius().width(), rect.width()) - style.borderRightWidth(), valueForLength(style.borderTopRightRadius().height(), rect.height()) - style.borderTopWidth()),
-            FloatSize(0, 0), FloatSize(valueForLength(style.borderBottomRightRadius().width(), rect.width()) - style.borderRightWidth(), valueForLength(style.borderBottomRightRadius().height(), rect.height()) - style.borderBottomWidth())));
+        paintInfo.context().clipRoundedRect(FloatRoundedRect(buttonClip,
+            topLeftRadius, topRightRadius,
+            bottomLeftRadius, bottomRightRadius));
 
-        paintInfo.context->fillRect(buttonClip, style.visitedDependentColor(CSSPropertyBorderTopColor), style.colorSpace());
+        paintInfo.context().fillRect(buttonClip, style.visitedDependentColor(CSSPropertyBorderTopColor));
 
         drawAxialGradient(cgContext, gradientWithName(isFocused(box) && !isReadOnlyControl(box) ? ConcaveGradient : ConvexGradient), buttonClip.location(), FloatPoint(buttonClip.x(), buttonClip.maxY()), LinearInterpolation);
     }
 
     // Paint Indicators.
 
-    if (box.isMenuList() && downcast<HTMLSelectElement>(box.node())->multiple()) {
+    if (box.isMenuList() && downcast<HTMLSelectElement>(box.element())->multiple()) {
         int size = 2;
         int count = 3;
         int padding = 3;
@@ -672,32 +717,33 @@ bool RenderThemeIOS::paintMenuListButtonDecorations(const RenderObject& box, con
         FloatRect ellipse(buttonClip.x() + (buttonClip.width() - count * (size + padding) + padding) / 2.0, buttonClip.maxY() - 10.0, size, size);
 
         for (int i = 0; i < count; ++i) {
-            paintInfo.context->drawRaisedEllipse(ellipse, Color::white, ColorSpaceDeviceRGB, Color(0.0f, 0.0f, 0.0f, 0.5f), ColorSpaceDeviceRGB);
+            paintInfo.context().drawRaisedEllipse(ellipse, Color::white, Color(0.0f, 0.0f, 0.0f, 0.5f));
             ellipse.move(size + padding, 0);
         }
     }  else {
         float centerX = floorf(buttonClip.x() + buttonClip.width() / 2.0) - 0.5;
         float centerY = floorf(buttonClip.y() + buttonClip.height() * 3.0 / 8.0);
 
-        FloatPoint arrow[3];
-        FloatPoint shadow[3];
-
-        arrow[0] = FloatPoint(centerX - MenuListArrowWidth / 2.0, centerY);
-        arrow[1] = FloatPoint(centerX + MenuListArrowWidth / 2.0, centerY);
-        arrow[2] = FloatPoint(centerX, centerY + MenuListArrowHeight);
+        Vector<FloatPoint> arrow = {
+            { centerX - MenuListArrowWidth / 2, centerY },
+            { centerX + MenuListArrowWidth / 2, centerY },
+            { centerX, centerY + MenuListArrowHeight }
+        };
 
-        shadow[0] = FloatPoint(arrow[0].x(), arrow[0].y() + 1.0f);
-        shadow[1] = FloatPoint(arrow[1].x(), arrow[1].y() + 1.0f);
-        shadow[2] = FloatPoint(arrow[2].x(), arrow[2].y() + 1.0f);
+        Vector<FloatPoint> shadow = {
+            { arrow[0].x(), arrow[0].y() + 1 },
+            { arrow[1].x(), arrow[1].y() + 1 },
+            { arrow[2].x(), arrow[2].y() + 1 }
+        };
 
         float opacity = isReadOnlyControl(box) ? 0.2 : 0.5;
-        paintInfo.context->setStrokeColor(Color(0.0f, 0.0f, 0.0f, opacity), ColorSpaceDeviceRGB);
-        paintInfo.context->setFillColor(Color(0.0f, 0.0f, 0.0f, opacity), ColorSpaceDeviceRGB);
-        paintInfo.context->drawConvexPolygon(3, shadow, true);
+        paintInfo.context().setStrokeColor(Color(0.0f, 0.0f, 0.0f, opacity));
+        paintInfo.context().setFillColor(Color(0.0f, 0.0f, 0.0f, opacity));
+        paintInfo.context().drawPath(Path::polygonPathFromPoints(shadow));
 
-        paintInfo.context->setStrokeColor(Color::white, ColorSpaceDeviceRGB);
-        paintInfo.context->setFillColor(Color::white, ColorSpaceDeviceRGB);
-        paintInfo.context->drawConvexPolygon(3, arrow, true);
+        paintInfo.context().setStrokeColor(Color::white);
+        paintInfo.context().setFillColor(Color::white);
+        paintInfo.context().drawPath(Path::polygonPathFromPoints(arrow));
     }
 
     return false;
@@ -707,7 +753,7 @@ const CGFloat kTrackThickness = 4.0;
 const CGFloat kTrackRadius = kTrackThickness / 2.0;
 const int kDefaultSliderThumbSize = 16;
 
-void RenderThemeIOS::adjustSliderTrackStyle(StyleResolver& selector, RenderStyle& style, Element* element) const
+void RenderThemeIOS::adjustSliderTrackStyle(StyleResolver& selector, RenderStyle& style, const Element* element) const
 {
     RenderTheme::adjustSliderTrackStyle(selector, style, element);
 
@@ -720,7 +766,7 @@ void RenderThemeIOS::adjustSliderTrackStyle(StyleResolver& selector, RenderStyle
 bool RenderThemeIOS::paintSliderTrack(const RenderObject& box, const PaintInfo& paintInfo, const IntRect& rect)
 {
     IntRect trackClip = rect;
-    RenderStyle& style = box.style();
+    auto& style = box.style();
 
     bool isHorizontal = true;
     switch (style.appearance()) {
@@ -761,13 +807,13 @@ bool RenderThemeIOS::paintSliderTrack(const RenderObject& box, const PaintInfo&
 
     // Draw the track gradient.
     {
-        GraphicsContextStateSaver stateSaver(*paintInfo.context);
+        GraphicsContextStateSaver stateSaver(paintInfo.context());
 
         IntSize cornerSize(cornerWidth, cornerHeight);
         FloatRoundedRect innerBorder(trackClip, cornerSize, cornerSize, cornerSize, cornerSize);
-        paintInfo.context->clipRoundedRect(innerBorder);
+        paintInfo.context().clipRoundedRect(innerBorder);
 
-        CGContextRef cgContext = paintInfo.context->platformContext();
+        CGContextRef cgContext = paintInfo.context().platformContext();
         IOSGradientRef gradient = readonly ? gradientWithName(ReadonlySliderTrackGradient) : gradientWithName(SliderTrackGradient);
         if (isHorizontal)
             drawAxialGradient(cgContext, gradient, trackClip.location(), FloatPoint(trackClip.x(), trackClip.maxY()), LinearInterpolation);
@@ -777,13 +823,13 @@ bool RenderThemeIOS::paintSliderTrack(const RenderObject& box, const PaintInfo&
 
     // Draw the track border.
     {
-        GraphicsContextStateSaver stateSaver(*paintInfo.context);
+        GraphicsContextStateSaver stateSaver(paintInfo.context());
 
-        CGContextRef cgContext = paintInfo.context->platformContext();
+        CGContextRef cgContext = paintInfo.context().platformContext();
         if (readonly)
-            paintInfo.context->setStrokeColor(Color(178, 178, 178), ColorSpaceDeviceRGB);
+            paintInfo.context().setStrokeColor(Color(178, 178, 178));
         else
-            paintInfo.context->setStrokeColor(Color(76, 76, 76), ColorSpaceDeviceRGB);
+            paintInfo.context().setStrokeColor(Color(76, 76, 76));
 
         RetainPtr<CGMutablePathRef> roundedRectPath = adoptCF(CGPathCreateMutable());
         CGPathAddRoundedRect(roundedRectPath.get(), 0, trackClip, cornerWidth, cornerHeight);
@@ -795,7 +841,7 @@ bool RenderThemeIOS::paintSliderTrack(const RenderObject& box, const PaintInfo&
     return false;
 }
 
-void RenderThemeIOS::adjustSliderThumbSize(RenderStyle& style, Element*) const
+void RenderThemeIOS::adjustSliderThumbSize(RenderStyle& style, const Element*) const
 {
     if (style.appearance() != SliderThumbHorizontalPart && style.appearance() != SliderThumbVerticalPart)
         return;
@@ -814,10 +860,10 @@ void RenderThemeIOS::adjustSliderThumbSize(RenderStyle& style, Element*) const
 
 bool RenderThemeIOS::paintSliderThumbDecorations(const RenderObject& box, const PaintInfo& paintInfo, const IntRect& rect)
 {
-    GraphicsContextStateSaver stateSaver(*paintInfo.context);
-    FloatRect clip = addRoundedBorderClip(box, paintInfo.context, rect);
+    GraphicsContextStateSaver stateSaver(paintInfo.context());
+    FloatRect clip = addRoundedBorderClip(box, paintInfo.context(), rect);
 
-    CGContextRef cgContext = paintInfo.context->platformContext();
+    CGContextRef cgContext = paintInfo.context().platformContext();
     FloatPoint bottomCenter(clip.x() + clip.width() / 2.0f, clip.maxY());
     if (isPressed(box))
         drawAxialGradient(cgContext, gradientWithName(SliderThumbOpaquePressedGradient), clip.location(), FloatPoint(clip.x(), clip.maxY()), LinearInterpolation);
@@ -847,18 +893,18 @@ bool RenderThemeIOS::paintProgressBar(const RenderObject& renderer, const PaintI
     const int progressBarHeight = 9;
     const float verticalOffset = (rect.height() - progressBarHeight) / 2.0;
 
-    GraphicsContextStateSaver stateSaver(*paintInfo.context);
+    GraphicsContextStateSaver stateSaver(paintInfo.context());
     if (rect.width() < 10 || rect.height() < 9) {
         // The rect is smaller than the standard progress bar. We clip to the element's rect to avoid
         // leaking pixels outside the repaint rect.
-        paintInfo.context->clip(rect);
+        paintInfo.context().clip(rect);
     }
 
     // 1) Draw the progress bar track.
     // 1.1) Draw the white background with grey gradient border.
-    GraphicsContext* context = paintInfo.context;
-    context->setStrokeThickness(0.68);
-    context->setStrokeStyle(SolidStroke);
+    GraphicsContext& context = paintInfo.context();
+    context.setStrokeThickness(0.68);
+    context.setStrokeStyle(SolidStroke);
 
     const float verticalRenderingPosition = rect.y() + verticalOffset;
     RefPtr<Gradient> strokeGradient = Gradient::create(FloatPoint(rect.x(), verticalRenderingPosition), FloatPoint(rect.x(), verticalRenderingPosition + progressBarHeight - 1));
@@ -866,28 +912,27 @@ bool RenderThemeIOS::paintProgressBar(const RenderObject& renderer, const PaintI
     strokeGradient->addColorStop(0.45, Color(0xee, 0xee, 0xee));
     strokeGradient->addColorStop(0.55, Color(0xee, 0xee, 0xee));
     strokeGradient->addColorStop(1.0, Color(0x8d, 0x8d, 0x8d));
-    context->setStrokeGradient(strokeGradient.releaseNonNull());
+    context.setStrokeGradient(strokeGradient.releaseNonNull());
 
-    ColorSpace colorSpace = renderer.style().colorSpace();
-    context->setFillColor(Color(255, 255, 255), colorSpace);
+    context.setFillColor(Color(255, 255, 255));
 
     Path trackPath;
     FloatRect trackRect(rect.x() + 0.25, verticalRenderingPosition + 0.25, rect.width() - 0.5, progressBarHeight - 0.5);
     FloatSize roundedCornerRadius(5, 4);
     trackPath.addRoundedRect(trackRect, roundedCornerRadius);
-    context->drawPath(trackPath);
+    context.drawPath(trackPath);
 
     // 1.2) Draw top gradient on the upper half. It is supposed to overlay the fill from the background and darker the stroked path.
     FloatRect border(rect.x(), rect.y() + verticalOffset, rect.width(), progressBarHeight);
-    paintInfo.context->clipRoundedRect(FloatRoundedRect(border, roundedCornerRadius, roundedCornerRadius, roundedCornerRadius, roundedCornerRadius));
+    paintInfo.context().clipRoundedRect(FloatRoundedRect(border, roundedCornerRadius, roundedCornerRadius, roundedCornerRadius, roundedCornerRadius));
 
     float upperGradientHeight = progressBarHeight / 2.;
     RefPtr<Gradient> upperGradient = Gradient::create(FloatPoint(rect.x(), verticalRenderingPosition + 0.5), FloatPoint(rect.x(), verticalRenderingPosition + upperGradientHeight - 1.5));
     upperGradient->addColorStop(0.0, Color(133, 133, 133, 188));
     upperGradient->addColorStop(1.0, Color(18, 18, 18, 51));
-    context->setFillGradient(upperGradient.releaseNonNull());
+    context.setFillGradient(upperGradient.releaseNonNull());
 
-    context->fillRect(FloatRect(rect.x(), verticalRenderingPosition, rect.width(), upperGradientHeight));
+    context.fillRect(FloatRect(rect.x(), verticalRenderingPosition, rect.width(), upperGradientHeight));
 
     const auto& renderProgress = downcast<RenderProgress>(renderer);
     if (renderProgress.isDeterminate()) {
@@ -901,13 +946,13 @@ bool RenderThemeIOS::paintProgressBar(const RenderObject& renderer, const PaintI
         barGradient->addColorStop(0.51, Color(36, 114, 210));
         barGradient->addColorStop(0.55, Color(36, 114, 210));
         barGradient->addColorStop(1.0, Color(57, 142, 244));
-        context->setFillGradient(barGradient.releaseNonNull());
+        context.setFillGradient(barGradient.releaseNonNull());
 
         RefPtr<Gradient> barStrokeGradient = Gradient::create(FloatPoint(rect.x(), verticalRenderingPosition), FloatPoint(rect.x(), verticalRenderingPosition + progressBarHeight - 1));
         barStrokeGradient->addColorStop(0.0, Color(95, 107, 183));
         barStrokeGradient->addColorStop(0.5, Color(66, 106, 174, 240));
         barStrokeGradient->addColorStop(1.0, Color(38, 104, 166));
-        context->setStrokeGradient(barStrokeGradient.releaseNonNull());
+        context.setStrokeGradient(barStrokeGradient.releaseNonNull());
 
         Path barPath;
         int left = rect.x();
@@ -915,7 +960,7 @@ bool RenderThemeIOS::paintProgressBar(const RenderObject& renderer, const PaintI
             left = rect.maxX() - barWidth;
         FloatRect barRect(left + 0.25, verticalRenderingPosition + 0.25, std::max(barWidth - 0.5, 0.0), progressBarHeight - 0.5);
         barPath.addRoundedRect(barRect, roundedCornerRadius);
-        context->drawPath(barPath);
+        context.drawPath(barPath);
     }
 
     return false;
@@ -935,7 +980,7 @@ int RenderThemeIOS::sliderTickOffsetFromTrackCenter() const
 }
 #endif
 
-void RenderThemeIOS::adjustSearchFieldStyle(StyleResolver& selector, RenderStyle& style, Element* element) const
+void RenderThemeIOS::adjustSearchFieldStyle(StyleResolver& selector, RenderStyle& style, const Element* element) const
 {
     RenderTheme::adjustSearchFieldStyle(selector, style, element);
 
@@ -957,7 +1002,7 @@ bool RenderThemeIOS::paintSearchFieldDecorations(const RenderObject& box, const
     return paintTextFieldDecorations(box, paintInfo, rect);
 }
 
-void RenderThemeIOS::adjustButtonStyle(StyleResolver& selector, RenderStyle& style, Element* element) const
+void RenderThemeIOS::adjustButtonStyle(StyleResolver& selector, RenderStyle& style, const Element* element) const
 {
     RenderTheme::adjustButtonStyle(selector, style, element);
 
@@ -986,10 +1031,10 @@ bool RenderThemeIOS::paintButtonDecorations(const RenderObject& box, const Paint
 
 bool RenderThemeIOS::paintPushButtonDecorations(const RenderObject& box, const PaintInfo& paintInfo, const IntRect& rect)
 {
-    GraphicsContextStateSaver stateSaver(*paintInfo.context);
-    FloatRect clip = addRoundedBorderClip(box, paintInfo.context, rect);
+    GraphicsContextStateSaver stateSaver(paintInfo.context());
+    FloatRect clip = addRoundedBorderClip(box, paintInfo.context(), rect);
 
-    CGContextRef cgContext = paintInfo.context->platformContext();
+    CGContextRef cgContext = paintInfo.context().platformContext();
     if (box.style().visitedDependentColor(CSSPropertyBackgroundColor).isDark())
         drawAxialGradient(cgContext, gradientWithName(ConvexGradient), clip.location(), FloatPoint(clip.x(), clip.maxY()), LinearInterpolation);
     else {
@@ -1016,7 +1061,7 @@ const int kMultipleThumbnailShrinkSize = 2;
 
 bool RenderThemeIOS::paintFileUploadIconDecorations(const RenderObject&, const RenderObject& buttonRenderer, const PaintInfo& paintInfo, const IntRect& rect, Icon* icon, FileUploadDecorations fileUploadDecorations)
 {
-    GraphicsContextStateSaver stateSaver(*paintInfo.context);
+    GraphicsContextStateSaver stateSaver(paintInfo.context());
 
     IntSize cornerSize(kThumbnailBorderCornerRadius, kThumbnailBorderCornerRadius);
     Color pictureFrameColor = buttonRenderer.style().visitedDependentColor(CSSPropertyBorderTopColor);
@@ -1033,12 +1078,12 @@ bool RenderThemeIOS::paintFileUploadIconDecorations(const RenderObject&, const R
 
         // Background picture frame and simple background icon with a gradient matching the button.
         Color backgroundImageColor = Color(buttonRenderer.style().visitedDependentColor(CSSPropertyBackgroundColor).rgb());
-        paintInfo.context->fillRoundedRect(FloatRoundedRect(thumbnailPictureFrameRect, cornerSize, cornerSize, cornerSize, cornerSize), pictureFrameColor, ColorSpaceDeviceRGB);
-        paintInfo.context->fillRect(thumbnailRect, backgroundImageColor, ColorSpaceDeviceRGB);
+        paintInfo.context().fillRoundedRect(FloatRoundedRect(thumbnailPictureFrameRect, cornerSize, cornerSize, cornerSize, cornerSize), pictureFrameColor);
+        paintInfo.context().fillRect(thumbnailRect, backgroundImageColor);
         {
-            GraphicsContextStateSaver stateSaver2(*paintInfo.context);
-            CGContextRef cgContext = paintInfo.context->platformContext();
-            paintInfo.context->clip(thumbnailRect);
+            GraphicsContextStateSaver stateSaver2(paintInfo.context());
+            CGContextRef cgContext = paintInfo.context().platformContext();
+            paintInfo.context().clip(thumbnailRect);
             if (backgroundImageColor.isDark())
                 drawAxialGradient(cgContext, gradientWithName(ConvexGradient), thumbnailRect.location(), FloatPoint(thumbnailRect.x(), thumbnailRect.maxY()), LinearInterpolation);
             else {
@@ -1054,8 +1099,8 @@ bool RenderThemeIOS::paintFileUploadIconDecorations(const RenderObject&, const R
     }
 
     // Foreground picture frame and icon.
-    paintInfo.context->fillRoundedRect(FloatRoundedRect(thumbnailPictureFrameRect, cornerSize, cornerSize, cornerSize, cornerSize), pictureFrameColor, ColorSpaceDeviceRGB);
-    icon->paint(paintInfo.context, thumbnailRect);
+    paintInfo.context().fillRoundedRect(FloatRoundedRect(thumbnailPictureFrameRect, cornerSize, cornerSize, cornerSize, cornerSize), pictureFrameColor);
+    icon->paint(paintInfo.context(), thumbnailRect);
 
     return false;
 }
@@ -1070,55 +1115,34 @@ Color RenderThemeIOS::platformInactiveSelectionBackgroundColor() const
     return Color::transparent;
 }
 
-bool RenderThemeIOS::shouldShowPlaceholderWhenFocused() const
+bool RenderThemeIOS::shouldHaveSpinButton(const HTMLInputElement&) const
 {
-    return true;
+    return false;
 }
 
-bool RenderThemeIOS::shouldHaveSpinButton(HTMLInputElement&) const
+bool RenderThemeIOS::shouldHaveCapsLockIndicator(const HTMLInputElement&) const
 {
     return false;
 }
 
-static FontWeight fromCTFontWeight(float fontWeight)
-{
-    if (fontWeight <= -0.8)
-        return FontWeight100;
-    else if (fontWeight <= -0.4)
-        return FontWeight200;
-    else if (fontWeight <= -0.2)
-        return FontWeight300;
-    else if (fontWeight <= 0.0)
-        return FontWeight400;
-    else if (fontWeight <= 0.2)
-        return FontWeight500;
-    else if (fontWeight <= 0.3)
-        return FontWeight600;
-    else if (fontWeight <= 0.4)
-        return FontWeight700;
-    else if (fontWeight <= 0.6)
-        return FontWeight800;
-    else if (fontWeight <= 0.8)
-        return FontWeight900;
-
-    return FontWeightNormal;
-}
-
-void RenderThemeIOS::systemFont(CSSValueID valueID, FontDescription& fontDescription) const
-{
-    static NeverDestroyed<FontDescription> systemFont;
-    static NeverDestroyed<FontDescription> headlineFont;
-    static NeverDestroyed<FontDescription> bodyFont;
-    static NeverDestroyed<FontDescription> subheadlineFont;
-    static NeverDestroyed<FontDescription> footnoteFont;
-    static NeverDestroyed<FontDescription> caption1Font;
-    static NeverDestroyed<FontDescription> caption2Font;
-    static NeverDestroyed<FontDescription> shortHeadlineFont;
-    static NeverDestroyed<FontDescription> shortBodyFont;
-    static NeverDestroyed<FontDescription> shortSubheadlineFont;
-    static NeverDestroyed<FontDescription> shortFootnoteFont;
-    static NeverDestroyed<FontDescription> shortCaption1Font;
-    static NeverDestroyed<FontDescription> tallBodyFont;
+FontCascadeDescription& RenderThemeIOS::cachedSystemFontDescription(CSSValueID valueID) const
+{
+    static NeverDestroyed<FontCascadeDescription> systemFont;
+    static NeverDestroyed<FontCascadeDescription> headlineFont;
+    static NeverDestroyed<FontCascadeDescription> bodyFont;
+    static NeverDestroyed<FontCascadeDescription> subheadlineFont;
+    static NeverDestroyed<FontCascadeDescription> footnoteFont;
+    static NeverDestroyed<FontCascadeDescription> caption1Font;
+    static NeverDestroyed<FontCascadeDescription> caption2Font;
+    static NeverDestroyed<FontCascadeDescription> shortHeadlineFont;
+    static NeverDestroyed<FontCascadeDescription> shortBodyFont;
+    static NeverDestroyed<FontCascadeDescription> shortSubheadlineFont;
+    static NeverDestroyed<FontCascadeDescription> shortFootnoteFont;
+    static NeverDestroyed<FontCascadeDescription> shortCaption1Font;
+    static NeverDestroyed<FontCascadeDescription> tallBodyFont;
+    static NeverDestroyed<FontCascadeDescription> title1Font;
+    static NeverDestroyed<FontCascadeDescription> title2Font;
+    static NeverDestroyed<FontCascadeDescription> title3Font;
 
     static CFStringRef userTextSize = contentSizeCategory();
 
@@ -1139,104 +1163,127 @@ void RenderThemeIOS::systemFont(CSSValueID valueID, FontDescription& fontDescrip
         tallBodyFont.get().setIsAbsoluteSize(false);
     }
 
-    FontDescription* cachedDesc;
+    switch (valueID) {
+    case CSSValueAppleSystemHeadline:
+        return headlineFont;
+    case CSSValueAppleSystemBody:
+        return bodyFont;
+    case CSSValueAppleSystemTitle1:
+        return title1Font;
+    case CSSValueAppleSystemTitle2:
+        return title2Font;
+    case CSSValueAppleSystemTitle3:
+        return title3Font;
+    case CSSValueAppleSystemSubheadline:
+        return subheadlineFont;
+    case CSSValueAppleSystemFootnote:
+        return footnoteFont;
+    case CSSValueAppleSystemCaption1:
+        return caption1Font;
+    case CSSValueAppleSystemCaption2:
+        return caption2Font;
+        // Short version.
+    case CSSValueAppleSystemShortHeadline:
+        return shortHeadlineFont;
+    case CSSValueAppleSystemShortBody:
+        return shortBodyFont;
+    case CSSValueAppleSystemShortSubheadline:
+        return shortSubheadlineFont;
+    case CSSValueAppleSystemShortFootnote:
+        return shortFootnoteFont;
+    case CSSValueAppleSystemShortCaption1:
+        return shortCaption1Font;
+        // Tall version.
+    case CSSValueAppleSystemTallBody:
+        return tallBodyFont;
+    default:
+        return systemFont;
+    }
+}
+
+void RenderThemeIOS::updateCachedSystemFontDescription(CSSValueID valueID, FontCascadeDescription& fontDescription) const
+{
     RetainPtr<CTFontDescriptorRef> fontDescriptor;
     CFStringRef textStyle;
     switch (valueID) {
     case CSSValueAppleSystemHeadline:
-        cachedDesc = &headlineFont.get();
         textStyle = kCTUIFontTextStyleHeadline;
-        if (!headlineFont.get().isAbsoluteSize())
-            fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, userTextSize, 0));
+        fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
         break;
     case CSSValueAppleSystemBody:
-        cachedDesc = &bodyFont.get();
         textStyle = kCTUIFontTextStyleBody;
-        if (!bodyFont.get().isAbsoluteSize())
-            fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, userTextSize, 0));
+        fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
+        break;
+    case CSSValueAppleSystemTitle1:
+        textStyle = kCTUIFontTextStyleTitle1;
+        fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
+        break;
+    case CSSValueAppleSystemTitle2:
+        textStyle = kCTUIFontTextStyleTitle2;
+        fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
+        break;
+    case CSSValueAppleSystemTitle3:
+        textStyle = kCTUIFontTextStyleTitle3;
+        fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
         break;
     case CSSValueAppleSystemSubheadline:
-        cachedDesc = &subheadlineFont.get();
         textStyle = kCTUIFontTextStyleSubhead;
-        if (!subheadlineFont.get().isAbsoluteSize())
-            fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, userTextSize, 0));
+        fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
         break;
     case CSSValueAppleSystemFootnote:
-        cachedDesc = &footnoteFont.get();
         textStyle = kCTUIFontTextStyleFootnote;
-        if (!footnoteFont.get().isAbsoluteSize())
-            fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, userTextSize, 0));
+        fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
         break;
     case CSSValueAppleSystemCaption1:
-        cachedDesc = &caption1Font.get();
         textStyle = kCTUIFontTextStyleCaption1;
-        if (!caption1Font.get().isAbsoluteSize())
-            fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, userTextSize, 0));
+        fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
         break;
     case CSSValueAppleSystemCaption2:
-        cachedDesc = &caption2Font.get();
         textStyle = kCTUIFontTextStyleCaption2;
-        if (!caption2Font.get().isAbsoluteSize())
-            fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, userTextSize, 0));
+        fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
         break;
 
     // Short version.
     case CSSValueAppleSystemShortHeadline:
-        cachedDesc = &shortHeadlineFont.get();
         textStyle = kCTUIFontTextStyleShortHeadline;
-        if (!shortHeadlineFont.get().isAbsoluteSize())
-            fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, userTextSize, 0));
+        fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
         break;
     case CSSValueAppleSystemShortBody:
-        cachedDesc = &shortBodyFont.get();
         textStyle = kCTUIFontTextStyleShortBody;
-        if (!shortBodyFont.get().isAbsoluteSize())
-            fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, userTextSize, 0));
+        fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
         break;
     case CSSValueAppleSystemShortSubheadline:
-        cachedDesc = &shortSubheadlineFont.get();
         textStyle = kCTUIFontTextStyleShortSubhead;
-        if (!shortSubheadlineFont.get().isAbsoluteSize())
-            fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, userTextSize, 0));
+        fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
         break;
     case CSSValueAppleSystemShortFootnote:
-        cachedDesc = &shortFootnoteFont.get();
         textStyle = kCTUIFontTextStyleShortFootnote;
-        if (!shortFootnoteFont.get().isAbsoluteSize())
-            fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, userTextSize, 0));
+        fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
         break;
     case CSSValueAppleSystemShortCaption1:
-        cachedDesc = &shortCaption1Font.get();
         textStyle = kCTUIFontTextStyleShortCaption1;
-        if (!shortCaption1Font.get().isAbsoluteSize())
-            fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, userTextSize, 0));
+        fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
         break;
 
     // Tall version.
     case CSSValueAppleSystemTallBody:
-        cachedDesc = &tallBodyFont.get();
         textStyle = kCTUIFontTextStyleTallBody;
-        if (!tallBodyFont.get().isAbsoluteSize())
-            fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, userTextSize, 0));
+        fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
         break;
 
     default:
         textStyle = kCTFontDescriptorTextStyleEmphasized;
-        cachedDesc = &systemFont.get();
-        if (!systemFont.get().isAbsoluteSize())
-            fontDescriptor = adoptCF(CTFontDescriptorCreateForUIType(kCTFontSystemFontType, 0, nullptr));
+        fontDescriptor = adoptCF(CTFontDescriptorCreateForUIType(kCTFontUIFontSystem, 0, nullptr));
     }
 
-    if (fontDescriptor) {
-        RetainPtr<CTFontRef> font = adoptCF(CTFontCreateWithFontDescriptor(fontDescriptor.get(), 0, nullptr));
-        cachedDesc->setIsAbsoluteSize(true);
-        cachedDesc->setGenericFamily(FontDescription::NoFamily);
-        cachedDesc->setOneFamily(textStyle);
-        cachedDesc->setSpecifiedSize(CTFontGetSize(font.get()));
-        cachedDesc->setWeight(fromCTFontWeight(FontCache::weightOfCTFont(font.get())));
-        cachedDesc->setItalic(0);
-    }
-    fontDescription = *cachedDesc;
+    ASSERT(fontDescriptor);
+    RetainPtr<CTFontRef> font = adoptCF(CTFontCreateWithFontDescriptor(fontDescriptor.get(), 0, nullptr));
+    font = preparePlatformFont(font.get(), fontDescription.textRenderingMode(), nullptr, nullptr, fontDescription.featureSettings(), fontDescription.variantSettings());
+    fontDescription.setIsAbsoluteSize(true);
+    fontDescription.setOneFamily(textStyle);
+    fontDescription.setSpecifiedSize(CTFontGetSize(font.get()));
+    fontDescription.setWeight(fontWeightFromCoreText(FontCache::weightOfCTFont(font.get())));
+    fontDescription.setItalic(FontItalicOff);
 }
 
 #if ENABLE(VIDEO)
@@ -1245,8 +1292,7 @@ String RenderThemeIOS::mediaControlsStyleSheet()
 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
     if (m_mediaControlsStyleSheet.isEmpty()) {
         StringBuilder builder;
-        builder.append([NSString stringWithContentsOfFile:[[NSBundle bundleForClass:[WebCoreRenderThemeBundle class]] pathForResource:@"mediaControlsiOS" ofType:@"css"] encoding:NSUTF8StringEncoding error:nil]);
-        builder.append(wkGetMediaUIImageData(wkMediaUIPartOptimizedFullscreenButton));
+        builder.append(String([NSString stringWithContentsOfFile:[[NSBundle bundleForClass:[WebCoreRenderThemeBundle class]] pathForResource:@"mediaControlsiOS" ofType:@"css"] encoding:NSUTF8StringEncoding error:nil]));
         m_mediaControlsStyleSheet = builder.toString();
     }
     return m_mediaControlsStyleSheet;
@@ -1260,9 +1306,9 @@ String RenderThemeIOS::mediaControlsScript()
 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
     if (m_mediaControlsScript.isEmpty()) {
         StringBuilder scriptBuilder;
-        scriptBuilder.append([NSString stringWithContentsOfFile:[[NSBundle bundleForClass:[WebCoreRenderThemeBundle class]] pathForResource:@"mediaControlsLocalizedStrings" ofType:@"js"] encoding:NSUTF8StringEncoding error:nil]);
-        scriptBuilder.append([NSString stringWithContentsOfFile:[[NSBundle bundleForClass:[WebCoreRenderThemeBundle class]] pathForResource:@"mediaControlsApple" ofType:@"js"] encoding:NSUTF8StringEncoding error:nil]);
-        scriptBuilder.append([NSString stringWithContentsOfFile:[[NSBundle bundleForClass:[WebCoreRenderThemeBundle class]] pathForResource:@"mediaControlsiOS" ofType:@"js"] encoding:NSUTF8StringEncoding error:nil]);
+        scriptBuilder.append(String([NSString stringWithContentsOfFile:[[NSBundle bundleForClass:[WebCoreRenderThemeBundle class]] pathForResource:@"mediaControlsLocalizedStrings" ofType:@"js"] encoding:NSUTF8StringEncoding error:nil]));
+        scriptBuilder.append(String([NSString stringWithContentsOfFile:[[NSBundle bundleForClass:[WebCoreRenderThemeBundle class]] pathForResource:@"mediaControlsApple" ofType:@"js"] encoding:NSUTF8StringEncoding error:nil]));
+        scriptBuilder.append(String([NSString stringWithContentsOfFile:[[NSBundle bundleForClass:[WebCoreRenderThemeBundle class]] pathForResource:@"mediaControlsiOS" ofType:@"js"] encoding:NSUTF8StringEncoding error:nil]));
         m_mediaControlsScript = scriptBuilder.toString();
     }
     return m_mediaControlsScript;
@@ -1272,6 +1318,407 @@ String RenderThemeIOS::mediaControlsScript()
 }
 #endif // ENABLE(VIDEO)
 
+Color RenderThemeIOS::systemColor(CSSValueID cssValueID) const
+{
+    auto addResult = m_systemColorCache.add(cssValueID, Color());
+    if (!addResult.isNewEntry)
+        return addResult.iterator->value;
+
+    Color color;
+    switch (cssValueID) {
+    case CSSValueAppleWirelessPlaybackTargetActive:
+        color = [getUIColorClass() systemBlueColor].CGColor;
+        break;
+    case CSSValueAppleSystemBlue:
+        color = [getUIColorClass() systemBlueColor].CGColor;
+        break;
+    case CSSValueAppleSystemGray:
+        color = [getUIColorClass() systemGrayColor].CGColor;
+        break;
+    case CSSValueAppleSystemGreen:
+        color = [getUIColorClass() systemGreenColor].CGColor;
+        break;
+    case CSSValueAppleSystemOrange:
+        color = [getUIColorClass() systemOrangeColor].CGColor;
+        break;
+    case CSSValueAppleSystemPink:
+        color = [getUIColorClass() systemPinkColor].CGColor;
+        break;
+    case CSSValueAppleSystemRed:
+        color = [getUIColorClass() systemRedColor].CGColor;
+        break;
+    case CSSValueAppleSystemYellow:
+        color = [getUIColorClass() systemYellowColor].CGColor;
+        break;
+    default:
+        break;
+    }
+
+    if (!color.isValid())
+        color = RenderTheme::systemColor(cssValueID);
+
+    addResult.iterator->value = color;
+
+    return addResult.iterator->value;
+}
+
+#if ENABLE(ATTACHMENT_ELEMENT)
+
+const CGSize attachmentSize = { 160, 119 };
+
+const CGFloat attachmentBorderRadius = 16;
+static Color attachmentBorderColor() { return Color(204, 204, 204); }
+
+static Color attachmentProgressColor() { return Color(222, 222, 222); }
+const CGFloat attachmentProgressBorderThickness = 3;
+
+const CGFloat attachmentProgressSize = 36;
+const CGFloat attachmentIconSize = 48;
+
+const CGFloat attachmentItemMargin = 8;
+
+const CGFloat attachmentWrappingTextMaximumWidth = 140;
+const CFIndex attachmentWrappingTextMaximumLineCount = 2;
+
+static RetainPtr<CTFontRef> attachmentActionFont()
+{
+    RetainPtr<CTFontDescriptorRef> fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(kCTUIFontTextStyleShortFootnote, RenderThemeIOS::contentSizeCategory(), 0));
+    RetainPtr<CTFontDescriptorRef> emphasizedFontDescriptor = adoptCF(CTFontDescriptorCreateCopyWithAttributes(fontDescriptor.get(),
+        (CFDictionaryRef)@{
+            (id)kCTFontDescriptorTextStyleAttribute: (id)kCTFontDescriptorTextStyleEmphasized
+    }));
+    return adoptCF(CTFontCreateWithFontDescriptor(emphasizedFontDescriptor.get(), 0, nullptr));
+}
+
+static UIColor *attachmentActionColor(const RenderAttachment& attachment)
+{
+    return [getUIColorClass() colorWithCGColor:cachedCGColor(attachment.style().visitedDependentColor(CSSPropertyColor))];
+}
+
+static RetainPtr<CTFontRef> attachmentTitleFont()
+{
+    RetainPtr<CTFontDescriptorRef> fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(kCTUIFontTextStyleShortCaption1, RenderThemeIOS::contentSizeCategory(), 0));
+    return adoptCF(CTFontCreateWithFontDescriptor(fontDescriptor.get(), 0, nullptr));
+}
+
+static UIColor *attachmentTitleColor() { return [getUIColorClass() systemGrayColor]; }
+
+static RetainPtr<CTFontRef> attachmentSubtitleFont() { return attachmentTitleFont(); }
+static UIColor *attachmentSubtitleColor() { return [getUIColorClass() systemGrayColor]; }
+
+struct AttachmentInfo {
+    explicit AttachmentInfo(const RenderAttachment&);
+
+    FloatRect iconRect;
+    FloatRect attachmentRect;
+    FloatRect progressRect;
+
+    BOOL hasProgress { NO };
+    float progress;
+
+    RetainPtr<UIImage> icon;
+
+    int baseline { 0 };
+
+    struct LabelLine {
+        FloatRect rect;
+        RetainPtr<CTLineRef> line;
+    };
+    Vector<LabelLine> lines;
+
+    CGFloat contentYOrigin { 0 };
+
+private:
+    void buildWrappedLines(const String&, CTFontRef, UIColor *, unsigned maximumLineCount);
+    void buildSingleLine(const String&, CTFontRef, UIColor *);
+
+    void addLine(CTLineRef);
+};
+
+void AttachmentInfo::addLine(CTLineRef line)
+{
+    CGRect lineBounds = CTLineGetBoundsWithOptions(line, kCTLineBoundsExcludeTypographicLeading);
+    CGFloat trailingWhitespaceWidth = CTLineGetTrailingWhitespaceWidth(line);
+    CGFloat lineWidthIgnoringTrailingWhitespace = lineBounds.size.width - trailingWhitespaceWidth;
+    CGFloat lineHeight = CGCeiling(lineBounds.size.height + lineBounds.origin.y);
+
+    CGFloat xOffset = (attachmentRect.width() / 2) - (lineWidthIgnoringTrailingWhitespace / 2);
+    LabelLine labelLine;
+    labelLine.line = line;
+    labelLine.rect = FloatRect(xOffset, 0, lineWidthIgnoringTrailingWhitespace, lineHeight);
+
+    lines.append(labelLine);
+}
+
+void AttachmentInfo::buildWrappedLines(const String& text, CTFontRef font, UIColor *color, unsigned maximumLineCount)
+{
+    if (text.isEmpty())
+        return;
+
+    NSDictionary *textAttributes = @{
+        (id)kCTFontAttributeName: (id)font,
+        (id)kCTForegroundColorAttributeName: color
+    };
+    RetainPtr<NSAttributedString> attributedText = adoptNS([[NSAttributedString alloc] initWithString:text attributes:textAttributes]);
+    RetainPtr<CTFramesetterRef> framesetter = adoptCF(CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attributedText.get()));
+
+    CFRange fitRange;
+    CGSize textSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter.get(), CFRangeMake(0, 0), nullptr, CGSizeMake(attachmentWrappingTextMaximumWidth, CGFLOAT_MAX), &fitRange);
+
+    RetainPtr<CGPathRef> textPath = adoptCF(CGPathCreateWithRect(CGRectMake(0, 0, textSize.width, textSize.height), nullptr));
+    RetainPtr<CTFrameRef> textFrame = adoptCF(CTFramesetterCreateFrame(framesetter.get(), fitRange, textPath.get(), nullptr));
+
+    CFArrayRef ctLines = CTFrameGetLines(textFrame.get());
+    CFIndex lineCount = CFArrayGetCount(ctLines);
+    if (!lineCount)
+        return;
+
+    // Lay out and record the first (maximumLineCount - 1) lines.
+    CFIndex lineIndex = 0;
+    CFIndex nonTruncatedLineCount = std::min<CFIndex>(maximumLineCount - 1, lineCount);
+    for (; lineIndex < nonTruncatedLineCount; ++lineIndex)
+        addLine((CTLineRef)CFArrayGetValueAtIndex(ctLines, lineIndex));
+
+    if (lineIndex == lineCount)
+        return;
+
+    // We had text that didn't fit in the first (maximumLineCount - 1) lines.
+    // Combine it into one last line, and center-truncate it.
+    CTLineRef firstRemainingLine = (CTLineRef)CFArrayGetValueAtIndex(ctLines, lineIndex);
+    CFIndex remainingRangeStart = CTLineGetStringRange(firstRemainingLine).location;
+    NSRange remainingRange = NSMakeRange(remainingRangeStart, [attributedText length] - remainingRangeStart);
+    NSAttributedString *remainingString = [attributedText attributedSubstringFromRange:remainingRange];
+    RetainPtr<CTLineRef> remainingLine = adoptCF(CTLineCreateWithAttributedString((CFAttributedStringRef)remainingString));
+    RetainPtr<NSAttributedString> ellipsisString = adoptNS([[NSAttributedString alloc] initWithString:@"\u2026" attributes:textAttributes]);
+    RetainPtr<CTLineRef> ellipsisLine = adoptCF(CTLineCreateWithAttributedString((CFAttributedStringRef)ellipsisString.get()));
+    RetainPtr<CTLineRef> truncatedLine = adoptCF(CTLineCreateTruncatedLine(remainingLine.get(), attachmentWrappingTextMaximumWidth, kCTLineTruncationMiddle, ellipsisLine.get()));
+
+    if (!truncatedLine)
+        truncatedLine = remainingLine;
+
+    addLine(truncatedLine.get());
+}
+
+void AttachmentInfo::buildSingleLine(const String& text, CTFontRef font, UIColor *color)
+{
+    if (text.isEmpty())
+        return;
+
+    NSDictionary *textAttributes = @{
+        (id)kCTFontAttributeName: (id)font,
+        (id)kCTForegroundColorAttributeName: color
+    };
+    RetainPtr<NSAttributedString> attributedText = adoptNS([[NSAttributedString alloc] initWithString:text attributes:textAttributes]);
+
+    addLine(adoptCF(CTLineCreateWithAttributedString((CFAttributedStringRef)attributedText.get())).get());
+}
+
+static BOOL getAttachmentProgress(const RenderAttachment& attachment, float& progress)
+{
+    String progressString = attachment.attachmentElement().attributeWithoutSynchronization(progressAttr);
+    if (progressString.isEmpty())
+        return NO;
+    bool validProgress;
+    progress = std::max<float>(std::min<float>(progressString.toFloat(&validProgress), 1), 0);
+    return validProgress;
 }
 
+static RetainPtr<UIImage> iconForAttachment(const RenderAttachment& attachment, FloatSize& size)
+{
+    auto documentInteractionController = adoptNS([[getUIDocumentInteractionControllerClass() alloc] init]);
+
+    String fileName;
+    if (File* file = attachment.attachmentElement().file())
+        fileName = file->name();
+
+    if (fileName.isEmpty())
+        fileName = attachment.attachmentElement().attachmentTitle();
+    [documentInteractionController setName:fileName];
+
+    String attachmentType = attachment.attachmentElement().attachmentType();
+    if (!attachmentType.isEmpty()) {
+        auto attachmentTypeCF = attachmentType.createCFString();
+        RetainPtr<CFStringRef> UTI;
+        if (isDeclaredUTI(attachmentTypeCF.get()))
+            UTI = attachmentTypeCF;
+        else
+            UTI = UTIFromMIMEType(attachmentTypeCF.get());
+
+        [documentInteractionController setUTI:static_cast<NSString *>(UTI.get())];
+    }
+
+    NSArray *icons = [documentInteractionController icons];
+    if (!icons.count)
+        return nil;
+
+    RetainPtr<UIImage> result = icons.lastObject;
+
+    BOOL useHeightForClosestMatch = [result size].height > [result size].width;
+    CGFloat bestMatchRatio = -1;
+
+    for (UIImage *icon in icons) {
+        CGFloat iconSize = useHeightForClosestMatch ? icon.size.height : icon.size.width;
+
+        CGFloat matchRatio = (attachmentIconSize / iconSize) - 1.0f;
+        if (matchRatio < 0.3f) {
+            matchRatio = CGFAbs(matchRatio);
+            if ((bestMatchRatio == -1) || (matchRatio < bestMatchRatio)) {
+                result = icon;
+                bestMatchRatio = matchRatio;
+            }
+        }
+    }
+
+    CGFloat iconAspect = [result size].width / [result size].height;
+    size = largestRectWithAspectRatioInsideRect(iconAspect, FloatRect(0, 0, attachmentIconSize, attachmentIconSize)).size();
+
+    return result;
+}
+
+AttachmentInfo::AttachmentInfo(const RenderAttachment& attachment)
+{
+    attachmentRect = FloatRect(0, 0, attachmentSize.width, attachmentSize.height);
+
+    hasProgress = getAttachmentProgress(attachment, progress);
+
+    String title = attachment.attachmentElement().attachmentTitle();
+    String action = attachment.attachmentElement().attributeWithoutSynchronization(actionAttr);
+    String subtitle = attachment.attachmentElement().attributeWithoutSynchronization(subtitleAttr);
+
+    CGFloat yOffset = 0;
+
+    if (hasProgress) {
+        progressRect = FloatRect((attachmentRect.width() / 2) - (attachmentProgressSize / 2), 0, attachmentProgressSize, attachmentProgressSize);
+        yOffset += attachmentProgressSize + attachmentItemMargin;
+    }
+
+    if (action.isEmpty() && !hasProgress) {
+        FloatSize iconSize;
+        icon = iconForAttachment(attachment, iconSize);
+        if (icon) {
+            iconRect = FloatRect(FloatPoint((attachmentRect.width() / 2) - (iconSize.width() / 2), 0), iconSize);
+            yOffset += iconRect.height() + attachmentItemMargin;
+        }
+    } else
+        buildWrappedLines(action, attachmentActionFont().get(), attachmentActionColor(attachment), attachmentWrappingTextMaximumLineCount);
+
+    bool forceSingleLineTitle = !action.isEmpty() || !subtitle.isEmpty() || hasProgress;
+    buildWrappedLines(title, attachmentTitleFont().get(), attachmentTitleColor(), forceSingleLineTitle ? 1 : attachmentWrappingTextMaximumLineCount);
+    buildSingleLine(subtitle, attachmentSubtitleFont().get(), attachmentSubtitleColor());
+
+    if (!lines.isEmpty()) {
+        for (auto& line : lines) {
+            line.rect.setY(yOffset);
+            yOffset += line.rect.height() + attachmentItemMargin;
+        }
+    }
+
+    yOffset -= attachmentItemMargin;
+
+    contentYOrigin = (attachmentRect.height() / 2) - (yOffset / 2);
+}
+
+LayoutSize RenderThemeIOS::attachmentIntrinsicSize(const RenderAttachment&) const
+{
+    return LayoutSize(FloatSize(attachmentSize));
+}
+
+int RenderThemeIOS::attachmentBaseline(const RenderAttachment& attachment) const
+{
+    AttachmentInfo info(attachment);
+    return info.baseline;
+}
+
+static void paintAttachmentIcon(GraphicsContext& context, AttachmentInfo& info)
+{
+    if (!info.icon)
+        return;
+
+    RefPtr<Image> iconImage = BitmapImage::create([info.icon CGImage]);
+    if (!iconImage)
+        return;
+
+    context.drawImage(*iconImage, info.iconRect);
+}
+
+static void paintAttachmentText(GraphicsContext& context, AttachmentInfo& info)
+{
+    for (const auto& line : info.lines) {
+        GraphicsContextStateSaver saver(context);
+
+        context.translate(toFloatSize(line.rect.minXMaxYCorner()));
+        context.scale(FloatSize(1, -1));
+
+        CGContextSetTextPosition(context.platformContext(), 0, 0);
+        CTLineDraw(line.line.get(), context.platformContext());
+    }
+}
+
+static void paintAttachmentProgress(GraphicsContext& context, AttachmentInfo& info)
+{
+    GraphicsContextStateSaver saver(context);
+
+    context.setStrokeThickness(attachmentProgressBorderThickness);
+    context.setStrokeColor(attachmentProgressColor());
+    context.setFillColor(attachmentProgressColor());
+    context.strokeEllipse(info.progressRect);
+
+    FloatPoint center = info.progressRect.center();
+
+    Path progressPath;
+    progressPath.moveTo(center);
+    progressPath.addLineTo(FloatPoint(center.x(), info.progressRect.y()));
+    progressPath.addArc(center, info.progressRect.width() / 2, -M_PI_2, info.progress * 2 * M_PI - M_PI_2, 0);
+    progressPath.closeSubpath();
+    context.fillPath(progressPath);
+}
+
+static Path attachmentBorderPath(AttachmentInfo& info)
+{
+    Path borderPath;
+    borderPath.addRoundedRect(info.attachmentRect, FloatSize(attachmentBorderRadius, attachmentBorderRadius));
+    return borderPath;
+}
+
+static void paintAttachmentBorder(GraphicsContext& context, Path& borderPath)
+{
+    context.setStrokeColor(attachmentBorderColor());
+    context.setStrokeThickness(1);
+    context.strokePath(borderPath);
+}
+
+bool RenderThemeIOS::paintAttachment(const RenderObject& renderer, const PaintInfo& paintInfo, const IntRect& paintRect)
+{
+    if (!is<RenderAttachment>(renderer))
+        return false;
+
+    const RenderAttachment& attachment = downcast<RenderAttachment>(renderer);
+
+    AttachmentInfo info(attachment);
+
+    GraphicsContext& context = paintInfo.context();
+    GraphicsContextStateSaver saver(context);
+
+    context.translate(toFloatSize(paintRect.location()));
+
+    Path borderPath = attachmentBorderPath(info);
+    paintAttachmentBorder(context, borderPath);
+    context.clipPath(borderPath);
+
+    context.translate(FloatSize(0, info.contentYOrigin));
+
+    if (info.hasProgress)
+        paintAttachmentProgress(context, info);
+    else if (info.icon)
+        paintAttachmentIcon(context, info);
+
+    paintAttachmentText(context, info);
+
+    return true;
+}
+
+#endif // ENABLE(ATTACHMENT_ELEMENT)
+
+} // namespace WebCore
+
 #endif //PLATFORM(IOS)