2 * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
30 #import "BitmapImage.h"
31 #import "CSSPrimitiveValue.h"
32 #import "CSSToLengthConversionData.h"
33 #import "CSSValueKeywords.h"
34 #import "CoreTextSPI.h"
35 #import "DateComponents.h"
38 #import "FloatRoundedRect.h"
40 #import "FontCascade.h"
42 #import "FrameSelection.h"
44 #import "GeometryUtilities.h"
46 #import "GraphicsContext.h"
47 #import "GraphicsContextCG.h"
48 #import "HTMLAttachmentElement.h"
49 #import "HTMLInputElement.h"
51 #import "HTMLSelectElement.h"
53 #import "LocalizedDateCache.h"
54 #import "NodeRenderStyle.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"
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>
77 SOFT_LINK_FRAMEWORK(UIKit)
78 SOFT_LINK_CLASS(UIKit, UIApplication)
79 SOFT_LINK_CLASS(UIKit, UIColor)
80 SOFT_LINK_CLASS(UIKit, UIDocumentInteractionController)
81 SOFT_LINK_CLASS(UIKit, UIImage)
82 SOFT_LINK_CONSTANT(UIKit, UIContentSizeCategoryDidChangeNotification, CFStringRef)
83 #define UIContentSizeCategoryDidChangeNotification getUIContentSizeCategoryDidChangeNotification()
85 @interface WebCoreRenderThemeBundle : NSObject
88 @implementation WebCoreRenderThemeBundle
93 using namespace HTMLNames;
95 const float ControlBaseHeight = 20;
96 const float ControlBaseFontSize = 11;
99 float* start; // points to static float[4]
100 float* end; // points to static float[4]
101 IOSGradient(float start[4], float end[4])
108 typedef IOSGradient* IOSGradientRef;
113 ExponentialInterpolation
116 static void interpolateLinearGradient(void *info, const CGFloat *inData, CGFloat *outData)
118 IOSGradientRef gradient = static_cast<IOSGradientRef>(info);
119 float alpha = inData[0];
120 float inverse = 1.0f - alpha;
122 outData[0] = inverse * gradient->start[0] + alpha * gradient->end[0];
123 outData[1] = inverse * gradient->start[1] + alpha * gradient->end[1];
124 outData[2] = inverse * gradient->start[2] + alpha * gradient->end[2];
125 outData[3] = inverse * gradient->start[3] + alpha * gradient->end[3];
128 static void interpolateExponentialGradient(void *info, const CGFloat *inData, CGFloat *outData)
130 IOSGradientRef gradient = static_cast<IOSGradientRef>(info);
132 for (int paintInfo = 0; paintInfo < 4; ++paintInfo) {
133 float end = logf(std::max(gradient->end[paintInfo], 0.01f));
134 float start = logf(std::max(gradient->start[paintInfo], 0.01f));
135 outData[paintInfo] = expf(start - (end + start) * a);
139 static CGFunctionRef getSharedFunctionRef(IOSGradientRef gradient, Interpolation interpolation)
141 CGFunctionRef function = nullptr;
143 static HashMap<IOSGradientRef, CGFunctionRef>* linearFunctionRefs;
144 static HashMap<IOSGradientRef, CGFunctionRef>* exponentialFunctionRefs;
146 if (interpolation == LinearInterpolation) {
147 if (!linearFunctionRefs)
148 linearFunctionRefs = new HashMap<IOSGradientRef, CGFunctionRef>;
150 function = linearFunctionRefs->get(gradient);
153 static struct CGFunctionCallbacks linearFunctionCallbacks = { 0, interpolateLinearGradient, 0 };
154 linearFunctionRefs->set(gradient, function = CGFunctionCreate(gradient, 1, nullptr, 4, nullptr, &linearFunctionCallbacks));
160 if (!exponentialFunctionRefs)
161 exponentialFunctionRefs = new HashMap<IOSGradientRef, CGFunctionRef>;
163 function = exponentialFunctionRefs->get(gradient);
166 static struct CGFunctionCallbacks exponentialFunctionCallbacks = { 0, interpolateExponentialGradient, 0 };
167 exponentialFunctionRefs->set(gradient, function = CGFunctionCreate(gradient, 1, 0, 4, 0, &exponentialFunctionCallbacks));
173 static void drawAxialGradient(CGContextRef context, IOSGradientRef gradient, const FloatPoint& startPoint, const FloatPoint& stopPoint, Interpolation interpolation)
175 RetainPtr<CGShadingRef> shading = adoptCF(CGShadingCreateAxial(sRGBColorSpaceRef(), startPoint, stopPoint, getSharedFunctionRef(gradient, interpolation), false, false));
176 CGContextDrawShading(context, shading.get());
179 static void drawRadialGradient(CGContextRef context, IOSGradientRef gradient, const FloatPoint& startPoint, float startRadius, const FloatPoint& stopPoint, float stopRadius, Interpolation interpolation)
181 RetainPtr<CGShadingRef> shading = adoptCF(CGShadingCreateRadial(sRGBColorSpaceRef(), startPoint, startRadius, stopPoint, stopRadius, getSharedFunctionRef(gradient, interpolation), false, false));
182 CGContextDrawShading(context, shading.get());
185 enum IOSGradientType {
192 ReadonlySliderTrackGradient,
193 SliderThumbOpaquePressedGradient,
196 static IOSGradientRef getInsetGradient()
198 static float end[4] = { 0 / 255.0, 0 / 255.0, 0 / 255.0, 0 };
199 static float start[4] = { 0 / 255.0, 0 / 255.0, 0 / 255.0, 0.2 };
200 static NeverDestroyed<IOSGradient> gradient(start, end);
201 return &gradient.get();
204 static IOSGradientRef getShineGradient()
206 static float end[4] = { 1, 1, 1, 0.8 };
207 static float start[4] = { 1, 1, 1, 0 };
208 static NeverDestroyed<IOSGradient> gradient(start, end);
209 return &gradient.get();
212 static IOSGradientRef getShadeGradient()
214 static float end[4] = { 178 / 255.0, 178 / 255.0, 178 / 255.0, 0.65 };
215 static float start[4] = { 252 / 255.0, 252 / 255.0, 252 / 255.0, 0.65 };
216 static NeverDestroyed<IOSGradient> gradient(start, end);
217 return &gradient.get();
220 static IOSGradientRef getConvexGradient()
222 static float end[4] = { 255 / 255.0, 255 / 255.0, 255 / 255.0, 0.05 };
223 static float start[4] = { 255 / 255.0, 255 / 255.0, 255 / 255.0, 0.43 };
224 static NeverDestroyed<IOSGradient> gradient(start, end);
225 return &gradient.get();
228 static IOSGradientRef getConcaveGradient()
230 static float end[4] = { 255 / 255.0, 255 / 255.0, 255 / 255.0, 0.46 };
231 static float start[4] = { 255 / 255.0, 255 / 255.0, 255 / 255.0, 0 };
232 static NeverDestroyed<IOSGradient> gradient(start, end);
233 return &gradient.get();
236 static IOSGradientRef getSliderTrackGradient()
238 static float end[4] = { 132 / 255.0, 132 / 255.0, 132 / 255.0, 1 };
239 static float start[4] = { 74 / 255.0, 77 / 255.0, 80 / 255.0, 1 };
240 static NeverDestroyed<IOSGradient> gradient(start, end);
241 return &gradient.get();
244 static IOSGradientRef getReadonlySliderTrackGradient()
246 static float end[4] = { 132 / 255.0, 132 / 255.0, 132 / 255.0, 0.4 };
247 static float start[4] = { 74 / 255.0, 77 / 255.0, 80 /255.0, 0.4 };
248 static NeverDestroyed<IOSGradient> gradient(start, end);
249 return &gradient.get();
252 static IOSGradientRef getSliderThumbOpaquePressedGradient()
254 static float end[4] = { 144 / 255.0, 144 / 255.0, 144 / 255.0, 1};
255 static float start[4] = { 55 / 255.0, 55 / 255.0, 55 / 255.0, 1 };
256 static NeverDestroyed<IOSGradient> gradient(start, end);
257 return &gradient.get();
260 static IOSGradientRef gradientWithName(IOSGradientType gradientType)
262 switch (gradientType) {
264 return getInsetGradient();
266 return getShineGradient();
268 return getShadeGradient();
270 return getConvexGradient();
271 case ConcaveGradient:
272 return getConcaveGradient();
273 case SliderTrackGradient:
274 return getSliderTrackGradient();
275 case ReadonlySliderTrackGradient:
276 return getReadonlySliderTrackGradient();
277 case SliderThumbOpaquePressedGradient:
278 return getSliderThumbOpaquePressedGradient();
280 ASSERT_NOT_REACHED();
284 static void contentSizeCategoryDidChange(CFNotificationCenterRef, void*, CFStringRef name, const void*, CFDictionaryRef)
286 ASSERT_UNUSED(name, CFEqual(name, UIContentSizeCategoryDidChangeNotification));
288 Page::updateStyleForAllPagesAfterGlobalChangeInEnvironment();
292 RenderThemeIOS::RenderThemeIOS()
294 CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), this, contentSizeCategoryDidChange, UIContentSizeCategoryDidChangeNotification, 0, CFNotificationSuspensionBehaviorDeliverImmediately);
297 PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page*)
299 static RenderTheme& renderTheme = RenderThemeIOS::create().leakRef();
303 Ref<RenderTheme> RenderThemeIOS::create()
305 return adoptRef(*new RenderThemeIOS);
308 static String& _contentSizeCategory()
310 static NeverDestroyed<String> _contentSizeCategory;
311 return _contentSizeCategory.get();
314 CFStringRef RenderThemeIOS::contentSizeCategory()
316 if (!_contentSizeCategory().isNull())
317 return (__bridge CFStringRef)static_cast<NSString*>(_contentSizeCategory());
318 return (CFStringRef)[[getUIApplicationClass() sharedApplication] preferredContentSizeCategory];
321 void RenderThemeIOS::setContentSizeCategory(const String& contentSizeCategory)
323 _contentSizeCategory() = contentSizeCategory;
326 const Color& RenderThemeIOS::shadowColor() const
328 static Color color(0.0f, 0.0f, 0.0f, 0.7f);
332 FloatRect RenderThemeIOS::addRoundedBorderClip(const RenderObject& box, GraphicsContext& context, const IntRect& rect)
334 // To fix inner border bleeding issues <rdar://problem/9812507>, we clip to the outer border and assert that
335 // the border is opaque or transparent, unless we're checked because checked radio/checkboxes show no bleeding.
336 auto& style = box.style();
337 RoundedRect border = isChecked(box) ? style.getRoundedInnerBorderFor(rect) : style.getRoundedBorderFor(rect);
339 if (border.isRounded())
340 context.clipRoundedRect(FloatRoundedRect(border));
342 context.clip(border.rect());
344 if (isChecked(box)) {
345 ASSERT(style.visitedDependentColor(CSSPropertyBorderTopColor).alpha() % 255 == 0);
346 ASSERT(style.visitedDependentColor(CSSPropertyBorderRightColor).alpha() % 255 == 0);
347 ASSERT(style.visitedDependentColor(CSSPropertyBorderBottomColor).alpha() % 255 == 0);
348 ASSERT(style.visitedDependentColor(CSSPropertyBorderLeftColor).alpha() % 255 == 0);
351 return border.rect();
354 void RenderThemeIOS::adjustCheckboxStyle(StyleResolver&, RenderStyle& style, const Element*) const
356 if (!style.width().isIntrinsicOrAuto() && !style.height().isAuto())
359 Length length = Length(static_cast<int>(ceilf(std::max(style.fontSize(), 10))), Fixed);
361 style.setWidth(length);
362 style.setHeight(length);
365 static CGPoint shortened(CGPoint start, CGPoint end, float width)
367 float x = end.x - start.x;
368 float y = end.y - start.y;
369 float ratio = (!x && !y) ? 0 : width / sqrtf(x * x + y * y);
370 return CGPointMake(start.x + x * ratio, start.y + y * ratio);
373 static void drawJoinedLines(CGContextRef context, CGPoint points[], unsigned count, bool antialias, CGLineCap lineCap)
375 CGContextSetShouldAntialias(context, antialias);
376 CGContextBeginPath(context);
377 CGContextSetLineCap(context, lineCap);
378 CGContextMoveToPoint(context, points[0].x, points[0].y);
380 for (unsigned i = 1; i < count; ++i)
381 CGContextAddLineToPoint(context, points[i].x, points[i].y);
383 CGContextStrokePath(context);
386 bool RenderThemeIOS::paintCheckboxDecorations(const RenderObject& box, const PaintInfo& paintInfo, const IntRect& rect)
388 GraphicsContextStateSaver stateSaver(paintInfo.context());
389 FloatRect clip = addRoundedBorderClip(box, paintInfo.context(), rect);
391 float width = clip.width();
392 float height = clip.height();
394 CGContextRef cgContext = paintInfo.context().platformContext();
395 if (isChecked(box)) {
396 drawAxialGradient(cgContext, gradientWithName(ConcaveGradient), clip.location(), FloatPoint(clip.x(), clip.maxY()), LinearInterpolation);
398 static const float thicknessRatio = 2 / 14.0;
399 static const CGSize size = { 14.0f, 14.0f };
400 static const CGPoint pathRatios[3] = {
401 { 2.5f / size.width, 7.5f / size.height },
402 { 5.5f / size.width, 10.5f / size.height },
403 { 11.5f / size.width, 2.5f / size.height }
406 float lineWidth = std::min(width, height) * 2.0f * thicknessRatio;
409 CGPointMake(clip.x() + width * pathRatios[0].x, clip.y() + height * pathRatios[0].y),
410 CGPointMake(clip.x() + width * pathRatios[1].x, clip.y() + height * pathRatios[1].y),
411 CGPointMake(clip.x() + width * pathRatios[2].x, clip.y() + height * pathRatios[2].y)
413 CGPoint shadow[3] = {
414 shortened(line[0], line[1], lineWidth / 4.0f),
416 shortened(line[2], line[1], lineWidth / 4.0f)
419 lineWidth = std::max<float>(lineWidth, 1);
420 CGContextSetLineWidth(cgContext, lineWidth);
421 CGContextSetStrokeColorWithColor(cgContext, cachedCGColor(Color(0.0f, 0.0f, 0.0f, 0.7f)));
422 drawJoinedLines(cgContext, shadow, 3, true, kCGLineCapSquare);
424 lineWidth = std::max<float>(std::min(clip.width(), clip.height()) * thicknessRatio, 1);
425 CGContextSetLineWidth(cgContext, lineWidth);
426 CGContextSetStrokeColorWithColor(cgContext, cachedCGColor(Color(1.0f, 1.0f, 1.0f, 240 / 255.0f)));
427 drawJoinedLines(cgContext, line, 3, true, kCGLineCapButt);
429 FloatPoint bottomCenter(clip.x() + clip.width() / 2.0f, clip.maxY());
430 drawAxialGradient(cgContext, gradientWithName(ShadeGradient), clip.location(), FloatPoint(clip.x(), clip.maxY()), LinearInterpolation);
431 drawRadialGradient(cgContext, gradientWithName(ShineGradient), bottomCenter, 0, bottomCenter, sqrtf((width * width) / 4.0f + height * height), ExponentialInterpolation);
437 int RenderThemeIOS::baselinePosition(const RenderBox& box) const
439 if (box.style().appearance() == CheckboxPart || box.style().appearance() == RadioPart)
440 return box.marginTop() + box.height() - 2; // The baseline is 2px up from the bottom of the checkbox/radio in AppKit.
441 if (box.style().appearance() == MenulistPart)
442 return box.marginTop() + box.height() - 5; // This is to match AppKit. There might be a better way to calculate this though.
443 return RenderTheme::baselinePosition(box);
446 bool RenderThemeIOS::isControlStyled(const RenderStyle& style, const BorderData& border, const FillLayer& background, const Color& backgroundColor) const
448 // Buttons and MenulistButtons are styled if they contain a background image.
449 if (style.appearance() == PushButtonPart || style.appearance() == MenulistButtonPart)
450 return !style.visitedDependentColor(CSSPropertyBackgroundColor).alpha() || style.backgroundLayers()->hasImage();
452 if (style.appearance() == TextFieldPart || style.appearance() == TextAreaPart)
453 return *style.backgroundLayers() != background;
455 return RenderTheme::isControlStyled(style, border, background, backgroundColor);
458 void RenderThemeIOS::adjustRadioStyle(StyleResolver&, RenderStyle& style, const Element*) const
460 if (!style.width().isIntrinsicOrAuto() && !style.height().isAuto())
463 Length length = Length(static_cast<int>(ceilf(std::max(style.fontSize(), 10))), Fixed);
464 style.setWidth(length);
465 style.setHeight(length);
466 style.setBorderRadius(IntSize(length.value() / 2.0f, length.value() / 2.0f));
469 bool RenderThemeIOS::paintRadioDecorations(const RenderObject& box, const PaintInfo& paintInfo, const IntRect& rect)
471 GraphicsContextStateSaver stateSaver(paintInfo.context());
472 FloatRect clip = addRoundedBorderClip(box, paintInfo.context(), rect);
474 CGContextRef cgContext = paintInfo.context().platformContext();
475 if (isChecked(box)) {
476 drawAxialGradient(cgContext, gradientWithName(ConcaveGradient), clip.location(), FloatPoint(clip.x(), clip.maxY()), LinearInterpolation);
478 // The inner circle is 6 / 14 the size of the surrounding circle,
479 // leaving 8 / 14 around it. (8 / 14) / 2 = 2 / 7.
481 static const float InnerInverseRatio = 2 / 7.0;
483 clip.inflateX(-clip.width() * InnerInverseRatio);
484 clip.inflateY(-clip.height() * InnerInverseRatio);
486 paintInfo.context().drawRaisedEllipse(clip, Color::white, shadowColor());
488 FloatSize radius(clip.width() / 2.0f, clip.height() / 2.0f);
489 paintInfo.context().clipRoundedRect(FloatRoundedRect(clip, radius, radius, radius, radius));
491 FloatPoint bottomCenter(clip.x() + clip.width() / 2.0, clip.maxY());
492 drawAxialGradient(cgContext, gradientWithName(ShadeGradient), clip.location(), FloatPoint(clip.x(), clip.maxY()), LinearInterpolation);
493 drawRadialGradient(cgContext, gradientWithName(ShineGradient), bottomCenter, 0, bottomCenter, std::max(clip.width(), clip.height()), ExponentialInterpolation);
497 bool RenderThemeIOS::paintTextFieldDecorations(const RenderObject& box, const PaintInfo& paintInfo, const FloatRect& rect)
499 auto& style = box.style();
500 FloatPoint point(rect.x() + style.borderLeftWidth(), rect.y() + style.borderTopWidth());
502 GraphicsContextStateSaver stateSaver(paintInfo.context());
504 paintInfo.context().clipRoundedRect(style.getRoundedBorderFor(LayoutRect(rect)).pixelSnappedRoundedRectForPainting(box.document().deviceScaleFactor()));
506 // This gradient gets drawn black when printing.
507 // Do not draw the gradient if there is no visible top border.
508 bool topBorderIsInvisible = !style.hasBorder() || !style.borderTopWidth() || style.borderTopIsTransparent();
509 if (!box.view().printing() && !topBorderIsInvisible)
510 drawAxialGradient(paintInfo.context().platformContext(), gradientWithName(InsetGradient), point, FloatPoint(CGPointMake(point.x(), point.y() + 3.0f)), LinearInterpolation);
514 bool RenderThemeIOS::paintTextAreaDecorations(const RenderObject& box, const PaintInfo& paintInfo, const FloatRect& rect)
516 return paintTextFieldDecorations(box, paintInfo, rect);
519 const int MenuListMinHeight = 15;
521 const float MenuListBaseHeight = 20;
522 const float MenuListBaseFontSize = 11;
524 const float MenuListArrowWidth = 7;
525 const float MenuListArrowHeight = 6;
526 const float MenuListButtonPaddingAfter = 19;
528 LengthBox RenderThemeIOS::popupInternalPaddingBox(const RenderStyle& style) const
530 if (style.appearance() == MenulistButtonPart) {
531 if (style.direction() == RTL)
532 return { 0, 0, 0, static_cast<int>(MenuListButtonPaddingAfter + style.borderTopWidth()) };
533 return { 0, static_cast<int>(MenuListButtonPaddingAfter + style.borderTopWidth()), 0, 0 };
535 return { 0, 0, 0, 0 };
538 void RenderThemeIOS::adjustRoundBorderRadius(RenderStyle& style, RenderBox& box)
540 if (style.appearance() == NoControlPart || style.backgroundLayers()->hasImage())
543 // FIXME: We should not be relying on border radius for the appearance of our controls <rdar://problem/7675493>
544 Length radiusWidth(static_cast<int>(std::min(box.width(), box.height()) / 2.0), Fixed);
545 Length radiusHeight(static_cast<int>(box.height() / 2.0), Fixed);
546 style.setBorderRadius(LengthSize(radiusWidth, radiusHeight));
549 static void applyCommonButtonPaddingToStyle(RenderStyle& style, const Element& element)
551 Document& document = element.document();
552 RefPtr<CSSPrimitiveValue> emSize = CSSPrimitiveValue::create(0.5, CSSPrimitiveValue::CSS_EMS);
553 int pixels = emSize->computeLength<int>(CSSToLengthConversionData(&style, document.renderStyle(), document.renderView(), document.frame()->pageZoomFactor()));
554 style.setPaddingBox(LengthBox(0, pixels, 0, pixels));
557 static void adjustSelectListButtonStyle(RenderStyle& style, const Element& element)
559 // Enforce "padding: 0 0.5em".
560 applyCommonButtonPaddingToStyle(style, element);
562 // Enforce "line-height: normal".
563 style.setLineHeight(Length(-100.0, Percent));
566 class RenderThemeMeasureTextClient : public MeasureTextClient {
568 RenderThemeMeasureTextClient(const FontCascade& font, const RenderStyle& style)
573 float measureText(const String& string) const override
575 TextRun run = RenderBlock::constructTextRun(string, m_style);
576 return m_font.width(run);
579 const FontCascade& m_font;
580 const RenderStyle& m_style;
583 static void adjustInputElementButtonStyle(RenderStyle& style, const HTMLInputElement& inputElement)
585 // Always Enforce "padding: 0 0.5em".
586 applyCommonButtonPaddingToStyle(style, inputElement);
588 // Don't adjust the style if the width is specified.
589 if (style.width().isFixed() && style.width().value() > 0)
592 // Don't adjust for unsupported date input types.
593 DateComponents::Type dateType = inputElement.dateType();
594 if (dateType == DateComponents::Invalid || dateType == DateComponents::Week)
597 // Enforce the width and set the box-sizing to content-box to not conflict with the padding.
598 FontCascade font = style.fontCascade();
600 float maximumWidth = localizedDateCache().maximumWidthForDateType(dateType, font, RenderThemeMeasureTextClient(font, style));
602 ASSERT(maximumWidth >= 0);
604 if (maximumWidth > 0) {
605 int width = static_cast<int>(maximumWidth + MenuListButtonPaddingAfter);
606 style.setWidth(Length(width, Fixed));
607 style.setBoxSizing(CONTENT_BOX);
611 void RenderThemeIOS::adjustMenuListButtonStyle(StyleResolver&, RenderStyle& style, const Element* element) const
613 // Set the min-height to be at least MenuListMinHeight.
614 if (style.height().isAuto())
615 style.setMinHeight(Length(std::max(MenuListMinHeight, static_cast<int>(MenuListBaseHeight / MenuListBaseFontSize * style.fontDescription().computedSize())), Fixed));
617 style.setMinHeight(Length(MenuListMinHeight, Fixed));
622 // Enforce some default styles in the case that this is a non-multiple <select> element,
623 // or a date input. We don't force these if this is just an element with
624 // "-webkit-appearance: menulist-button".
625 if (is<HTMLSelectElement>(*element) && !element->hasAttributeWithoutSynchronization(HTMLNames::multipleAttr))
626 adjustSelectListButtonStyle(style, *element);
627 else if (is<HTMLInputElement>(*element))
628 adjustInputElementButtonStyle(style, downcast<HTMLInputElement>(*element));
631 bool RenderThemeIOS::paintMenuListButtonDecorations(const RenderBox& box, const PaintInfo& paintInfo, const FloatRect& rect)
633 auto& style = box.style();
634 bool isRTL = style.direction() == RTL;
635 float borderTopWidth = style.borderTopWidth();
636 FloatRect clip(rect.x() + style.borderLeftWidth(), rect.y() + style.borderTopWidth(), rect.width() - style.borderLeftWidth() - style.borderRightWidth(), rect.height() - style.borderTopWidth() - style.borderBottomWidth());
637 CGContextRef cgContext = paintInfo.context().platformContext();
639 float adjustLeft = 0.5;
640 float adjustRight = 0.5;
641 float adjustTop = 0.5;
642 float adjustBottom = 0.5;
644 // Paint title portion.
646 float leftInset = isRTL ? MenuListButtonPaddingAfter : 0;
647 FloatRect titleClip(clip.x() + leftInset - adjustLeft, clip.y() - adjustTop, clip.width() - MenuListButtonPaddingAfter + adjustLeft, clip.height() + adjustTop + adjustBottom);
649 GraphicsContextStateSaver stateSaver(paintInfo.context());
651 FloatSize topLeftRadius;
652 FloatSize topRightRadius;
653 FloatSize bottomLeftRadius;
654 FloatSize bottomRightRadius;
657 topRightRadius = FloatSize(valueForLength(style.borderTopRightRadius().width(), rect.width()) - style.borderRightWidth(), valueForLength(style.borderTopRightRadius().height(), rect.height()) - style.borderTopWidth());
658 bottomRightRadius = FloatSize(valueForLength(style.borderBottomRightRadius().width(), rect.width()) - style.borderRightWidth(), valueForLength(style.borderBottomRightRadius().height(), rect.height()) - style.borderBottomWidth());
660 topLeftRadius = FloatSize(valueForLength(style.borderTopLeftRadius().width(), rect.width()) - style.borderLeftWidth(), valueForLength(style.borderTopLeftRadius().height(), rect.height()) - style.borderTopWidth());
661 bottomLeftRadius = FloatSize(valueForLength(style.borderBottomLeftRadius().width(), rect.width()) - style.borderLeftWidth(), valueForLength(style.borderBottomLeftRadius().height(), rect.height()) - style.borderBottomWidth());
664 paintInfo.context().clipRoundedRect(FloatRoundedRect(titleClip,
665 topLeftRadius, topRightRadius,
666 bottomLeftRadius, bottomRightRadius));
668 drawAxialGradient(cgContext, gradientWithName(ShadeGradient), titleClip.location(), FloatPoint(titleClip.x(), titleClip.maxY()), LinearInterpolation);
669 drawAxialGradient(cgContext, gradientWithName(ShineGradient), FloatPoint(titleClip.x(), titleClip.maxY()), titleClip.location(), ExponentialInterpolation);
672 // Draw the separator after the initial padding.
674 float separatorPosition = isRTL ? (clip.x() + MenuListButtonPaddingAfter) : (clip.maxX() - MenuListButtonPaddingAfter);
676 box.drawLineForBoxSide(paintInfo.context(), FloatRect(FloatPoint(separatorPosition - borderTopWidth, clip.y()), FloatPoint(separatorPosition, clip.maxY())), BSRight, style.visitedDependentColor(CSSPropertyBorderTopColor), style.borderTopStyle(), 0, 0);
678 FloatRect buttonClip;
680 buttonClip = FloatRect(clip.x() - adjustTop, clip.y() - adjustTop, MenuListButtonPaddingAfter + adjustTop + adjustLeft, clip.height() + adjustTop + adjustBottom);
682 buttonClip = FloatRect(separatorPosition - adjustTop, clip.y() - adjustTop, MenuListButtonPaddingAfter + adjustTop + adjustRight, clip.height() + adjustTop + adjustBottom);
684 // Now paint the button portion.
686 GraphicsContextStateSaver stateSaver(paintInfo.context());
688 FloatSize topLeftRadius;
689 FloatSize topRightRadius;
690 FloatSize bottomLeftRadius;
691 FloatSize bottomRightRadius;
694 topLeftRadius = FloatSize(valueForLength(style.borderTopLeftRadius().width(), rect.width()) - style.borderLeftWidth(), valueForLength(style.borderTopLeftRadius().height(), rect.height()) - style.borderTopWidth());
695 bottomLeftRadius = FloatSize(valueForLength(style.borderBottomLeftRadius().width(), rect.width()) - style.borderLeftWidth(), valueForLength(style.borderBottomLeftRadius().height(), rect.height()) - style.borderBottomWidth());
697 topRightRadius = FloatSize(valueForLength(style.borderTopRightRadius().width(), rect.width()) - style.borderRightWidth(), valueForLength(style.borderTopRightRadius().height(), rect.height()) - style.borderTopWidth());
698 bottomRightRadius = FloatSize(valueForLength(style.borderBottomRightRadius().width(), rect.width()) - style.borderRightWidth(), valueForLength(style.borderBottomRightRadius().height(), rect.height()) - style.borderBottomWidth());
701 paintInfo.context().clipRoundedRect(FloatRoundedRect(buttonClip,
702 topLeftRadius, topRightRadius,
703 bottomLeftRadius, bottomRightRadius));
705 paintInfo.context().fillRect(buttonClip, style.visitedDependentColor(CSSPropertyBorderTopColor));
707 drawAxialGradient(cgContext, gradientWithName(isFocused(box) && !isReadOnlyControl(box) ? ConcaveGradient : ConvexGradient), buttonClip.location(), FloatPoint(buttonClip.x(), buttonClip.maxY()), LinearInterpolation);
712 if (box.isMenuList() && downcast<HTMLSelectElement>(box.element())->multiple()) {
717 FloatRect ellipse(buttonClip.x() + (buttonClip.width() - count * (size + padding) + padding) / 2.0, buttonClip.maxY() - 10.0, size, size);
719 for (int i = 0; i < count; ++i) {
720 paintInfo.context().drawRaisedEllipse(ellipse, Color::white, Color(0.0f, 0.0f, 0.0f, 0.5f));
721 ellipse.move(size + padding, 0);
724 float centerX = floorf(buttonClip.x() + buttonClip.width() / 2.0) - 0.5;
725 float centerY = floorf(buttonClip.y() + buttonClip.height() * 3.0 / 8.0);
727 Vector<FloatPoint> arrow = {
728 { centerX - MenuListArrowWidth / 2, centerY },
729 { centerX + MenuListArrowWidth / 2, centerY },
730 { centerX, centerY + MenuListArrowHeight }
733 Vector<FloatPoint> shadow = {
734 { arrow[0].x(), arrow[0].y() + 1 },
735 { arrow[1].x(), arrow[1].y() + 1 },
736 { arrow[2].x(), arrow[2].y() + 1 }
739 float opacity = isReadOnlyControl(box) ? 0.2 : 0.5;
740 paintInfo.context().setStrokeColor(Color(0.0f, 0.0f, 0.0f, opacity));
741 paintInfo.context().setFillColor(Color(0.0f, 0.0f, 0.0f, opacity));
742 paintInfo.context().drawPath(Path::polygonPathFromPoints(shadow));
744 paintInfo.context().setStrokeColor(Color::white);
745 paintInfo.context().setFillColor(Color::white);
746 paintInfo.context().drawPath(Path::polygonPathFromPoints(arrow));
752 const CGFloat kTrackThickness = 4.0;
753 const CGFloat kTrackRadius = kTrackThickness / 2.0;
754 const int kDefaultSliderThumbSize = 16;
756 void RenderThemeIOS::adjustSliderTrackStyle(StyleResolver& selector, RenderStyle& style, const Element* element) const
758 RenderTheme::adjustSliderTrackStyle(selector, style, element);
760 // FIXME: We should not be relying on border radius for the appearance of our controls <rdar://problem/7675493>
761 Length radiusWidth(static_cast<int>(kTrackRadius), Fixed);
762 Length radiusHeight(static_cast<int>(kTrackRadius), Fixed);
763 style.setBorderRadius(LengthSize(radiusWidth, radiusHeight));
766 bool RenderThemeIOS::paintSliderTrack(const RenderObject& box, const PaintInfo& paintInfo, const IntRect& rect)
768 IntRect trackClip = rect;
769 auto& style = box.style();
771 bool isHorizontal = true;
772 switch (style.appearance()) {
773 case SliderHorizontalPart:
775 // Inset slightly so the thumb covers the edge.
776 if (trackClip.width() > 2) {
777 trackClip.setWidth(trackClip.width() - 2);
778 trackClip.setX(trackClip.x() + 1);
780 trackClip.setHeight(static_cast<int>(kTrackThickness));
781 trackClip.setY(rect.y() + rect.height() / 2 - kTrackThickness / 2);
783 case SliderVerticalPart:
784 isHorizontal = false;
785 // Inset slightly so the thumb covers the edge.
786 if (trackClip.height() > 2) {
787 trackClip.setHeight(trackClip.height() - 2);
788 trackClip.setY(trackClip.y() + 1);
790 trackClip.setWidth(kTrackThickness);
791 trackClip.setX(rect.x() + rect.width() / 2 - kTrackThickness / 2);
794 ASSERT_NOT_REACHED();
797 ASSERT(trackClip.width() >= 0);
798 ASSERT(trackClip.height() >= 0);
799 CGFloat cornerWidth = trackClip.width() < kTrackThickness ? trackClip.width() / 2.0f : kTrackRadius;
800 CGFloat cornerHeight = trackClip.height() < kTrackThickness ? trackClip.height() / 2.0f : kTrackRadius;
802 bool readonly = isReadOnlyControl(box);
804 #if ENABLE(DATALIST_ELEMENT)
805 paintSliderTicks(box, paintInfo, trackClip);
808 // Draw the track gradient.
810 GraphicsContextStateSaver stateSaver(paintInfo.context());
812 IntSize cornerSize(cornerWidth, cornerHeight);
813 FloatRoundedRect innerBorder(trackClip, cornerSize, cornerSize, cornerSize, cornerSize);
814 paintInfo.context().clipRoundedRect(innerBorder);
816 CGContextRef cgContext = paintInfo.context().platformContext();
817 IOSGradientRef gradient = readonly ? gradientWithName(ReadonlySliderTrackGradient) : gradientWithName(SliderTrackGradient);
819 drawAxialGradient(cgContext, gradient, trackClip.location(), FloatPoint(trackClip.x(), trackClip.maxY()), LinearInterpolation);
821 drawAxialGradient(cgContext, gradient, trackClip.location(), FloatPoint(trackClip.maxX(), trackClip.y()), LinearInterpolation);
824 // Draw the track border.
826 GraphicsContextStateSaver stateSaver(paintInfo.context());
828 CGContextRef cgContext = paintInfo.context().platformContext();
830 paintInfo.context().setStrokeColor(Color(178, 178, 178));
832 paintInfo.context().setStrokeColor(Color(76, 76, 76));
834 RetainPtr<CGMutablePathRef> roundedRectPath = adoptCF(CGPathCreateMutable());
835 CGPathAddRoundedRect(roundedRectPath.get(), 0, trackClip, cornerWidth, cornerHeight);
836 CGContextAddPath(cgContext, roundedRectPath.get());
837 CGContextSetLineWidth(cgContext, 1);
838 CGContextStrokePath(cgContext);
844 void RenderThemeIOS::adjustSliderThumbSize(RenderStyle& style, const Element*) const
846 if (style.appearance() != SliderThumbHorizontalPart && style.appearance() != SliderThumbVerticalPart)
849 // Enforce "border-radius: 50%".
850 Length length(50.0f, Percent);
851 style.setBorderRadius(LengthSize(length, length));
853 // Enforce a 16x16 size if no size is provided.
854 if (style.width().isIntrinsicOrAuto() || style.height().isAuto()) {
855 Length length = Length(kDefaultSliderThumbSize, Fixed);
856 style.setWidth(length);
857 style.setHeight(length);
861 bool RenderThemeIOS::paintSliderThumbDecorations(const RenderObject& box, const PaintInfo& paintInfo, const IntRect& rect)
863 GraphicsContextStateSaver stateSaver(paintInfo.context());
864 FloatRect clip = addRoundedBorderClip(box, paintInfo.context(), rect);
866 CGContextRef cgContext = paintInfo.context().platformContext();
867 FloatPoint bottomCenter(clip.x() + clip.width() / 2.0f, clip.maxY());
869 drawAxialGradient(cgContext, gradientWithName(SliderThumbOpaquePressedGradient), clip.location(), FloatPoint(clip.x(), clip.maxY()), LinearInterpolation);
871 drawAxialGradient(cgContext, gradientWithName(ShadeGradient), clip.location(), FloatPoint(clip.x(), clip.maxY()), LinearInterpolation);
872 drawRadialGradient(cgContext, gradientWithName(ShineGradient), bottomCenter, 0.0f, bottomCenter, std::max(clip.width(), clip.height()), ExponentialInterpolation);
878 double RenderThemeIOS::animationRepeatIntervalForProgressBar(RenderProgress&) const
883 double RenderThemeIOS::animationDurationForProgressBar(RenderProgress&) const
888 bool RenderThemeIOS::paintProgressBar(const RenderObject& renderer, const PaintInfo& paintInfo, const IntRect& rect)
890 if (!is<RenderProgress>(renderer))
893 const int progressBarHeight = 9;
894 const float verticalOffset = (rect.height() - progressBarHeight) / 2.0;
896 GraphicsContextStateSaver stateSaver(paintInfo.context());
897 if (rect.width() < 10 || rect.height() < 9) {
898 // The rect is smaller than the standard progress bar. We clip to the element's rect to avoid
899 // leaking pixels outside the repaint rect.
900 paintInfo.context().clip(rect);
903 // 1) Draw the progress bar track.
904 // 1.1) Draw the white background with grey gradient border.
905 GraphicsContext& context = paintInfo.context();
906 context.setStrokeThickness(0.68);
907 context.setStrokeStyle(SolidStroke);
909 const float verticalRenderingPosition = rect.y() + verticalOffset;
910 RefPtr<Gradient> strokeGradient = Gradient::create(FloatPoint(rect.x(), verticalRenderingPosition), FloatPoint(rect.x(), verticalRenderingPosition + progressBarHeight - 1));
911 strokeGradient->addColorStop(0.0, Color(0x8d, 0x8d, 0x8d));
912 strokeGradient->addColorStop(0.45, Color(0xee, 0xee, 0xee));
913 strokeGradient->addColorStop(0.55, Color(0xee, 0xee, 0xee));
914 strokeGradient->addColorStop(1.0, Color(0x8d, 0x8d, 0x8d));
915 context.setStrokeGradient(strokeGradient.releaseNonNull());
917 context.setFillColor(Color(255, 255, 255));
920 FloatRect trackRect(rect.x() + 0.25, verticalRenderingPosition + 0.25, rect.width() - 0.5, progressBarHeight - 0.5);
921 FloatSize roundedCornerRadius(5, 4);
922 trackPath.addRoundedRect(trackRect, roundedCornerRadius);
923 context.drawPath(trackPath);
925 // 1.2) Draw top gradient on the upper half. It is supposed to overlay the fill from the background and darker the stroked path.
926 FloatRect border(rect.x(), rect.y() + verticalOffset, rect.width(), progressBarHeight);
927 paintInfo.context().clipRoundedRect(FloatRoundedRect(border, roundedCornerRadius, roundedCornerRadius, roundedCornerRadius, roundedCornerRadius));
929 float upperGradientHeight = progressBarHeight / 2.;
930 RefPtr<Gradient> upperGradient = Gradient::create(FloatPoint(rect.x(), verticalRenderingPosition + 0.5), FloatPoint(rect.x(), verticalRenderingPosition + upperGradientHeight - 1.5));
931 upperGradient->addColorStop(0.0, Color(133, 133, 133, 188));
932 upperGradient->addColorStop(1.0, Color(18, 18, 18, 51));
933 context.setFillGradient(upperGradient.releaseNonNull());
935 context.fillRect(FloatRect(rect.x(), verticalRenderingPosition, rect.width(), upperGradientHeight));
937 const auto& renderProgress = downcast<RenderProgress>(renderer);
938 if (renderProgress.isDeterminate()) {
939 // 2) Draw the progress bar.
940 double position = clampTo(renderProgress.position(), 0.0, 1.0);
941 double barWidth = position * rect.width();
942 RefPtr<Gradient> barGradient = Gradient::create(FloatPoint(rect.x(), verticalRenderingPosition + 0.5), FloatPoint(rect.x(), verticalRenderingPosition + progressBarHeight - 1));
943 barGradient->addColorStop(0.0, Color(195, 217, 247));
944 barGradient->addColorStop(0.45, Color(118, 164, 228));
945 barGradient->addColorStop(0.49, Color(118, 164, 228));
946 barGradient->addColorStop(0.51, Color(36, 114, 210));
947 barGradient->addColorStop(0.55, Color(36, 114, 210));
948 barGradient->addColorStop(1.0, Color(57, 142, 244));
949 context.setFillGradient(barGradient.releaseNonNull());
951 RefPtr<Gradient> barStrokeGradient = Gradient::create(FloatPoint(rect.x(), verticalRenderingPosition), FloatPoint(rect.x(), verticalRenderingPosition + progressBarHeight - 1));
952 barStrokeGradient->addColorStop(0.0, Color(95, 107, 183));
953 barStrokeGradient->addColorStop(0.5, Color(66, 106, 174, 240));
954 barStrokeGradient->addColorStop(1.0, Color(38, 104, 166));
955 context.setStrokeGradient(barStrokeGradient.releaseNonNull());
959 if (!renderProgress.style().isLeftToRightDirection())
960 left = rect.maxX() - barWidth;
961 FloatRect barRect(left + 0.25, verticalRenderingPosition + 0.25, std::max(barWidth - 0.5, 0.0), progressBarHeight - 0.5);
962 barPath.addRoundedRect(barRect, roundedCornerRadius);
963 context.drawPath(barPath);
969 #if ENABLE(DATALIST_ELEMENT)
970 IntSize RenderThemeIOS::sliderTickSize() const
972 // FIXME: <rdar://problem/12271791> MERGEBOT: Correct values for slider tick of <input type="range"> elements (requires ENABLE_DATALIST_ELEMENT)
973 return IntSize(1, 3);
976 int RenderThemeIOS::sliderTickOffsetFromTrackCenter() const
978 // FIXME: <rdar://problem/12271791> MERGEBOT: Correct values for slider tick of <input type="range"> elements (requires ENABLE_DATALIST_ELEMENT)
983 void RenderThemeIOS::adjustSearchFieldStyle(StyleResolver& selector, RenderStyle& style, const Element* element) const
985 RenderTheme::adjustSearchFieldStyle(selector, style, element);
990 if (!style.hasBorder())
993 RenderBox* box = element->renderBox();
997 adjustRoundBorderRadius(style, *box);
1000 bool RenderThemeIOS::paintSearchFieldDecorations(const RenderObject& box, const PaintInfo& paintInfo, const IntRect& rect)
1002 return paintTextFieldDecorations(box, paintInfo, rect);
1005 void RenderThemeIOS::adjustButtonStyle(StyleResolver& selector, RenderStyle& style, const Element* element) const
1007 RenderTheme::adjustButtonStyle(selector, style, element);
1009 // Set padding: 0 1.0em; on buttons.
1010 // CSSPrimitiveValue::computeLengthInt only needs the element's style to calculate em lengths.
1011 // Since the element might not be in a document, just pass nullptr for the root element style
1012 // and the render view.
1013 RefPtr<CSSPrimitiveValue> emSize = CSSPrimitiveValue::create(1.0, CSSPrimitiveValue::CSS_EMS);
1014 int pixels = emSize->computeLength<int>(CSSToLengthConversionData(&style, nullptr, nullptr, 1.0, false));
1015 style.setPaddingBox(LengthBox(0, pixels, 0, pixels));
1020 RenderBox* box = element->renderBox();
1024 adjustRoundBorderRadius(style, *box);
1027 bool RenderThemeIOS::paintButtonDecorations(const RenderObject& box, const PaintInfo& paintInfo, const IntRect& rect)
1029 return paintPushButtonDecorations(box, paintInfo, rect);
1032 bool RenderThemeIOS::paintPushButtonDecorations(const RenderObject& box, const PaintInfo& paintInfo, const IntRect& rect)
1034 GraphicsContextStateSaver stateSaver(paintInfo.context());
1035 FloatRect clip = addRoundedBorderClip(box, paintInfo.context(), rect);
1037 CGContextRef cgContext = paintInfo.context().platformContext();
1038 if (box.style().visitedDependentColor(CSSPropertyBackgroundColor).isDark())
1039 drawAxialGradient(cgContext, gradientWithName(ConvexGradient), clip.location(), FloatPoint(clip.x(), clip.maxY()), LinearInterpolation);
1041 drawAxialGradient(cgContext, gradientWithName(ShadeGradient), clip.location(), FloatPoint(clip.x(), clip.maxY()), LinearInterpolation);
1042 drawAxialGradient(cgContext, gradientWithName(ShineGradient), FloatPoint(clip.x(), clip.maxY()), clip.location(), ExponentialInterpolation);
1047 void RenderThemeIOS::setButtonSize(RenderStyle& style) const
1049 // If the width and height are both specified, then we have nothing to do.
1050 if (!style.width().isIntrinsicOrAuto() && !style.height().isAuto())
1053 // Use the font size to determine the intrinsic width of the control.
1054 style.setHeight(Length(static_cast<int>(ControlBaseHeight / ControlBaseFontSize * style.fontDescription().computedSize()), Fixed));
1057 const int kThumbnailBorderStrokeWidth = 1;
1058 const int kThumbnailBorderCornerRadius = 1;
1059 const int kVisibleBackgroundImageWidth = 1;
1060 const int kMultipleThumbnailShrinkSize = 2;
1062 bool RenderThemeIOS::paintFileUploadIconDecorations(const RenderObject&, const RenderObject& buttonRenderer, const PaintInfo& paintInfo, const IntRect& rect, Icon* icon, FileUploadDecorations fileUploadDecorations)
1064 GraphicsContextStateSaver stateSaver(paintInfo.context());
1066 IntSize cornerSize(kThumbnailBorderCornerRadius, kThumbnailBorderCornerRadius);
1067 Color pictureFrameColor = buttonRenderer.style().visitedDependentColor(CSSPropertyBorderTopColor);
1069 IntRect thumbnailPictureFrameRect = rect;
1070 IntRect thumbnailRect = rect;
1071 thumbnailRect.contract(2 * kThumbnailBorderStrokeWidth, 2 * kThumbnailBorderStrokeWidth);
1072 thumbnailRect.move(kThumbnailBorderStrokeWidth, kThumbnailBorderStrokeWidth);
1074 if (fileUploadDecorations == MultipleFiles) {
1075 // Smaller thumbnails for multiple selection appearance.
1076 thumbnailPictureFrameRect.contract(kMultipleThumbnailShrinkSize, kMultipleThumbnailShrinkSize);
1077 thumbnailRect.contract(kMultipleThumbnailShrinkSize, kMultipleThumbnailShrinkSize);
1079 // Background picture frame and simple background icon with a gradient matching the button.
1080 Color backgroundImageColor = Color(buttonRenderer.style().visitedDependentColor(CSSPropertyBackgroundColor).rgb());
1081 paintInfo.context().fillRoundedRect(FloatRoundedRect(thumbnailPictureFrameRect, cornerSize, cornerSize, cornerSize, cornerSize), pictureFrameColor);
1082 paintInfo.context().fillRect(thumbnailRect, backgroundImageColor);
1084 GraphicsContextStateSaver stateSaver2(paintInfo.context());
1085 CGContextRef cgContext = paintInfo.context().platformContext();
1086 paintInfo.context().clip(thumbnailRect);
1087 if (backgroundImageColor.isDark())
1088 drawAxialGradient(cgContext, gradientWithName(ConvexGradient), thumbnailRect.location(), FloatPoint(thumbnailRect.x(), thumbnailRect.maxY()), LinearInterpolation);
1090 drawAxialGradient(cgContext, gradientWithName(ShadeGradient), thumbnailRect.location(), FloatPoint(thumbnailRect.x(), thumbnailRect.maxY()), LinearInterpolation);
1091 drawAxialGradient(cgContext, gradientWithName(ShineGradient), FloatPoint(thumbnailRect.x(), thumbnailRect.maxY()), thumbnailRect.location(), ExponentialInterpolation);
1095 // Move the rects for the Foreground picture frame and icon.
1096 int inset = kVisibleBackgroundImageWidth + kThumbnailBorderStrokeWidth;
1097 thumbnailPictureFrameRect.move(inset, inset);
1098 thumbnailRect.move(inset, inset);
1101 // Foreground picture frame and icon.
1102 paintInfo.context().fillRoundedRect(FloatRoundedRect(thumbnailPictureFrameRect, cornerSize, cornerSize, cornerSize, cornerSize), pictureFrameColor);
1103 icon->paint(paintInfo.context(), thumbnailRect);
1108 Color RenderThemeIOS::platformActiveSelectionBackgroundColor() const
1110 return Color::transparent;
1113 Color RenderThemeIOS::platformInactiveSelectionBackgroundColor() const
1115 return Color::transparent;
1118 bool RenderThemeIOS::shouldHaveSpinButton(const HTMLInputElement&) const
1123 bool RenderThemeIOS::shouldHaveCapsLockIndicator(const HTMLInputElement&) const
1128 FontCascadeDescription& RenderThemeIOS::cachedSystemFontDescription(CSSValueID valueID) const
1130 static NeverDestroyed<FontCascadeDescription> systemFont;
1131 static NeverDestroyed<FontCascadeDescription> headlineFont;
1132 static NeverDestroyed<FontCascadeDescription> bodyFont;
1133 static NeverDestroyed<FontCascadeDescription> subheadlineFont;
1134 static NeverDestroyed<FontCascadeDescription> footnoteFont;
1135 static NeverDestroyed<FontCascadeDescription> caption1Font;
1136 static NeverDestroyed<FontCascadeDescription> caption2Font;
1137 static NeverDestroyed<FontCascadeDescription> shortHeadlineFont;
1138 static NeverDestroyed<FontCascadeDescription> shortBodyFont;
1139 static NeverDestroyed<FontCascadeDescription> shortSubheadlineFont;
1140 static NeverDestroyed<FontCascadeDescription> shortFootnoteFont;
1141 static NeverDestroyed<FontCascadeDescription> shortCaption1Font;
1142 static NeverDestroyed<FontCascadeDescription> tallBodyFont;
1143 static NeverDestroyed<FontCascadeDescription> title1Font;
1144 static NeverDestroyed<FontCascadeDescription> title2Font;
1145 static NeverDestroyed<FontCascadeDescription> title3Font;
1147 static CFStringRef userTextSize = contentSizeCategory();
1149 if (userTextSize != contentSizeCategory()) {
1150 userTextSize = contentSizeCategory();
1152 headlineFont.get().setIsAbsoluteSize(false);
1153 bodyFont.get().setIsAbsoluteSize(false);
1154 subheadlineFont.get().setIsAbsoluteSize(false);
1155 footnoteFont.get().setIsAbsoluteSize(false);
1156 caption1Font.get().setIsAbsoluteSize(false);
1157 caption2Font.get().setIsAbsoluteSize(false);
1158 shortHeadlineFont.get().setIsAbsoluteSize(false);
1159 shortBodyFont.get().setIsAbsoluteSize(false);
1160 shortSubheadlineFont.get().setIsAbsoluteSize(false);
1161 shortFootnoteFont.get().setIsAbsoluteSize(false);
1162 shortCaption1Font.get().setIsAbsoluteSize(false);
1163 tallBodyFont.get().setIsAbsoluteSize(false);
1167 case CSSValueAppleSystemHeadline:
1168 return headlineFont;
1169 case CSSValueAppleSystemBody:
1171 case CSSValueAppleSystemTitle1:
1173 case CSSValueAppleSystemTitle2:
1175 case CSSValueAppleSystemTitle3:
1177 case CSSValueAppleSystemSubheadline:
1178 return subheadlineFont;
1179 case CSSValueAppleSystemFootnote:
1180 return footnoteFont;
1181 case CSSValueAppleSystemCaption1:
1182 return caption1Font;
1183 case CSSValueAppleSystemCaption2:
1184 return caption2Font;
1186 case CSSValueAppleSystemShortHeadline:
1187 return shortHeadlineFont;
1188 case CSSValueAppleSystemShortBody:
1189 return shortBodyFont;
1190 case CSSValueAppleSystemShortSubheadline:
1191 return shortSubheadlineFont;
1192 case CSSValueAppleSystemShortFootnote:
1193 return shortFootnoteFont;
1194 case CSSValueAppleSystemShortCaption1:
1195 return shortCaption1Font;
1197 case CSSValueAppleSystemTallBody:
1198 return tallBodyFont;
1204 void RenderThemeIOS::updateCachedSystemFontDescription(CSSValueID valueID, FontCascadeDescription& fontDescription) const
1206 RetainPtr<CTFontDescriptorRef> fontDescriptor;
1207 CFStringRef textStyle;
1209 case CSSValueAppleSystemHeadline:
1210 textStyle = kCTUIFontTextStyleHeadline;
1211 fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1213 case CSSValueAppleSystemBody:
1214 textStyle = kCTUIFontTextStyleBody;
1215 fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1217 case CSSValueAppleSystemTitle1:
1218 textStyle = kCTUIFontTextStyleTitle1;
1219 fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1221 case CSSValueAppleSystemTitle2:
1222 textStyle = kCTUIFontTextStyleTitle2;
1223 fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1225 case CSSValueAppleSystemTitle3:
1226 textStyle = kCTUIFontTextStyleTitle3;
1227 fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1229 case CSSValueAppleSystemSubheadline:
1230 textStyle = kCTUIFontTextStyleSubhead;
1231 fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1233 case CSSValueAppleSystemFootnote:
1234 textStyle = kCTUIFontTextStyleFootnote;
1235 fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1237 case CSSValueAppleSystemCaption1:
1238 textStyle = kCTUIFontTextStyleCaption1;
1239 fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1241 case CSSValueAppleSystemCaption2:
1242 textStyle = kCTUIFontTextStyleCaption2;
1243 fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1247 case CSSValueAppleSystemShortHeadline:
1248 textStyle = kCTUIFontTextStyleShortHeadline;
1249 fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1251 case CSSValueAppleSystemShortBody:
1252 textStyle = kCTUIFontTextStyleShortBody;
1253 fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1255 case CSSValueAppleSystemShortSubheadline:
1256 textStyle = kCTUIFontTextStyleShortSubhead;
1257 fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1259 case CSSValueAppleSystemShortFootnote:
1260 textStyle = kCTUIFontTextStyleShortFootnote;
1261 fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1263 case CSSValueAppleSystemShortCaption1:
1264 textStyle = kCTUIFontTextStyleShortCaption1;
1265 fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1269 case CSSValueAppleSystemTallBody:
1270 textStyle = kCTUIFontTextStyleTallBody;
1271 fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(textStyle, contentSizeCategory(), 0));
1275 textStyle = kCTFontDescriptorTextStyleEmphasized;
1276 fontDescriptor = adoptCF(CTFontDescriptorCreateForUIType(kCTFontUIFontSystem, 0, nullptr));
1279 ASSERT(fontDescriptor);
1280 RetainPtr<CTFontRef> font = adoptCF(CTFontCreateWithFontDescriptor(fontDescriptor.get(), 0, nullptr));
1281 font = preparePlatformFont(font.get(), fontDescription.textRenderingMode(), nullptr, nullptr, fontDescription.featureSettings(), fontDescription.variantSettings());
1282 fontDescription.setIsAbsoluteSize(true);
1283 fontDescription.setOneFamily(textStyle);
1284 fontDescription.setSpecifiedSize(CTFontGetSize(font.get()));
1285 fontDescription.setWeight(fontWeightFromCoreText(FontCache::weightOfCTFont(font.get())));
1286 fontDescription.setItalic(FontItalicOff);
1290 String RenderThemeIOS::mediaControlsStyleSheet()
1292 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
1293 if (m_mediaControlsStyleSheet.isEmpty()) {
1294 StringBuilder builder;
1295 builder.append(String([NSString stringWithContentsOfFile:[[NSBundle bundleForClass:[WebCoreRenderThemeBundle class]] pathForResource:@"mediaControlsiOS" ofType:@"css"] encoding:NSUTF8StringEncoding error:nil]));
1296 m_mediaControlsStyleSheet = builder.toString();
1298 return m_mediaControlsStyleSheet;
1300 return emptyString();
1304 String RenderThemeIOS::mediaControlsScript()
1306 #if ENABLE(MEDIA_CONTROLS_SCRIPT)
1307 if (m_mediaControlsScript.isEmpty()) {
1308 StringBuilder scriptBuilder;
1309 scriptBuilder.append(String([NSString stringWithContentsOfFile:[[NSBundle bundleForClass:[WebCoreRenderThemeBundle class]] pathForResource:@"mediaControlsLocalizedStrings" ofType:@"js"] encoding:NSUTF8StringEncoding error:nil]));
1310 scriptBuilder.append(String([NSString stringWithContentsOfFile:[[NSBundle bundleForClass:[WebCoreRenderThemeBundle class]] pathForResource:@"mediaControlsApple" ofType:@"js"] encoding:NSUTF8StringEncoding error:nil]));
1311 scriptBuilder.append(String([NSString stringWithContentsOfFile:[[NSBundle bundleForClass:[WebCoreRenderThemeBundle class]] pathForResource:@"mediaControlsiOS" ofType:@"js"] encoding:NSUTF8StringEncoding error:nil]));
1312 m_mediaControlsScript = scriptBuilder.toString();
1314 return m_mediaControlsScript;
1316 return emptyString();
1319 #endif // ENABLE(VIDEO)
1321 Color RenderThemeIOS::systemColor(CSSValueID cssValueID) const
1323 auto addResult = m_systemColorCache.add(cssValueID, Color());
1324 if (!addResult.isNewEntry)
1325 return addResult.iterator->value;
1328 switch (cssValueID) {
1329 case CSSValueAppleWirelessPlaybackTargetActive:
1330 color = [getUIColorClass() systemBlueColor].CGColor;
1332 case CSSValueAppleSystemBlue:
1333 color = [getUIColorClass() systemBlueColor].CGColor;
1335 case CSSValueAppleSystemGray:
1336 color = [getUIColorClass() systemGrayColor].CGColor;
1338 case CSSValueAppleSystemGreen:
1339 color = [getUIColorClass() systemGreenColor].CGColor;
1341 case CSSValueAppleSystemOrange:
1342 color = [getUIColorClass() systemOrangeColor].CGColor;
1344 case CSSValueAppleSystemPink:
1345 color = [getUIColorClass() systemPinkColor].CGColor;
1347 case CSSValueAppleSystemRed:
1348 color = [getUIColorClass() systemRedColor].CGColor;
1350 case CSSValueAppleSystemYellow:
1351 color = [getUIColorClass() systemYellowColor].CGColor;
1357 if (!color.isValid())
1358 color = RenderTheme::systemColor(cssValueID);
1360 addResult.iterator->value = color;
1362 return addResult.iterator->value;
1365 #if ENABLE(ATTACHMENT_ELEMENT)
1367 const CGSize attachmentSize = { 160, 119 };
1369 const CGFloat attachmentBorderRadius = 16;
1370 static Color attachmentBorderColor() { return Color(204, 204, 204); }
1372 static Color attachmentProgressColor() { return Color(222, 222, 222); }
1373 const CGFloat attachmentProgressBorderThickness = 3;
1375 const CGFloat attachmentProgressSize = 36;
1376 const CGFloat attachmentIconSize = 48;
1378 const CGFloat attachmentItemMargin = 8;
1380 const CGFloat attachmentWrappingTextMaximumWidth = 140;
1381 const CFIndex attachmentWrappingTextMaximumLineCount = 2;
1383 static RetainPtr<CTFontRef> attachmentActionFont()
1385 RetainPtr<CTFontDescriptorRef> fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(kCTUIFontTextStyleShortFootnote, RenderThemeIOS::contentSizeCategory(), 0));
1386 RetainPtr<CTFontDescriptorRef> emphasizedFontDescriptor = adoptCF(CTFontDescriptorCreateCopyWithAttributes(fontDescriptor.get(),
1388 (id)kCTFontDescriptorTextStyleAttribute: (id)kCTFontDescriptorTextStyleEmphasized
1390 return adoptCF(CTFontCreateWithFontDescriptor(emphasizedFontDescriptor.get(), 0, nullptr));
1393 static UIColor *attachmentActionColor(const RenderAttachment& attachment)
1395 return [getUIColorClass() colorWithCGColor:cachedCGColor(attachment.style().visitedDependentColor(CSSPropertyColor))];
1398 static RetainPtr<CTFontRef> attachmentTitleFont()
1400 RetainPtr<CTFontDescriptorRef> fontDescriptor = adoptCF(CTFontDescriptorCreateWithTextStyle(kCTUIFontTextStyleShortCaption1, RenderThemeIOS::contentSizeCategory(), 0));
1401 return adoptCF(CTFontCreateWithFontDescriptor(fontDescriptor.get(), 0, nullptr));
1404 static UIColor *attachmentTitleColor() { return [getUIColorClass() systemGrayColor]; }
1406 static RetainPtr<CTFontRef> attachmentSubtitleFont() { return attachmentTitleFont(); }
1407 static UIColor *attachmentSubtitleColor() { return [getUIColorClass() systemGrayColor]; }
1409 struct AttachmentInfo {
1410 explicit AttachmentInfo(const RenderAttachment&);
1413 FloatRect attachmentRect;
1414 FloatRect progressRect;
1416 BOOL hasProgress { NO };
1419 RetainPtr<UIImage> icon;
1425 RetainPtr<CTLineRef> line;
1427 Vector<LabelLine> lines;
1429 CGFloat contentYOrigin { 0 };
1432 void buildWrappedLines(const String&, CTFontRef, UIColor *, unsigned maximumLineCount);
1433 void buildSingleLine(const String&, CTFontRef, UIColor *);
1435 void addLine(CTLineRef);
1438 void AttachmentInfo::addLine(CTLineRef line)
1440 CGRect lineBounds = CTLineGetBoundsWithOptions(line, kCTLineBoundsExcludeTypographicLeading);
1441 CGFloat trailingWhitespaceWidth = CTLineGetTrailingWhitespaceWidth(line);
1442 CGFloat lineWidthIgnoringTrailingWhitespace = lineBounds.size.width - trailingWhitespaceWidth;
1443 CGFloat lineHeight = CGCeiling(lineBounds.size.height + lineBounds.origin.y);
1445 CGFloat xOffset = (attachmentRect.width() / 2) - (lineWidthIgnoringTrailingWhitespace / 2);
1446 LabelLine labelLine;
1447 labelLine.line = line;
1448 labelLine.rect = FloatRect(xOffset, 0, lineWidthIgnoringTrailingWhitespace, lineHeight);
1450 lines.append(labelLine);
1453 void AttachmentInfo::buildWrappedLines(const String& text, CTFontRef font, UIColor *color, unsigned maximumLineCount)
1458 NSDictionary *textAttributes = @{
1459 (id)kCTFontAttributeName: (id)font,
1460 (id)kCTForegroundColorAttributeName: color
1462 RetainPtr<NSAttributedString> attributedText = adoptNS([[NSAttributedString alloc] initWithString:text attributes:textAttributes]);
1463 RetainPtr<CTFramesetterRef> framesetter = adoptCF(CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attributedText.get()));
1466 CGSize textSize = CTFramesetterSuggestFrameSizeWithConstraints(framesetter.get(), CFRangeMake(0, 0), nullptr, CGSizeMake(attachmentWrappingTextMaximumWidth, CGFLOAT_MAX), &fitRange);
1468 RetainPtr<CGPathRef> textPath = adoptCF(CGPathCreateWithRect(CGRectMake(0, 0, textSize.width, textSize.height), nullptr));
1469 RetainPtr<CTFrameRef> textFrame = adoptCF(CTFramesetterCreateFrame(framesetter.get(), fitRange, textPath.get(), nullptr));
1471 CFArrayRef ctLines = CTFrameGetLines(textFrame.get());
1472 CFIndex lineCount = CFArrayGetCount(ctLines);
1476 // Lay out and record the first (maximumLineCount - 1) lines.
1477 CFIndex lineIndex = 0;
1478 CFIndex nonTruncatedLineCount = std::min<CFIndex>(maximumLineCount - 1, lineCount);
1479 for (; lineIndex < nonTruncatedLineCount; ++lineIndex)
1480 addLine((CTLineRef)CFArrayGetValueAtIndex(ctLines, lineIndex));
1482 if (lineIndex == lineCount)
1485 // We had text that didn't fit in the first (maximumLineCount - 1) lines.
1486 // Combine it into one last line, and center-truncate it.
1487 CTLineRef firstRemainingLine = (CTLineRef)CFArrayGetValueAtIndex(ctLines, lineIndex);
1488 CFIndex remainingRangeStart = CTLineGetStringRange(firstRemainingLine).location;
1489 NSRange remainingRange = NSMakeRange(remainingRangeStart, [attributedText length] - remainingRangeStart);
1490 NSAttributedString *remainingString = [attributedText attributedSubstringFromRange:remainingRange];
1491 RetainPtr<CTLineRef> remainingLine = adoptCF(CTLineCreateWithAttributedString((CFAttributedStringRef)remainingString));
1492 RetainPtr<NSAttributedString> ellipsisString = adoptNS([[NSAttributedString alloc] initWithString:@"\u2026" attributes:textAttributes]);
1493 RetainPtr<CTLineRef> ellipsisLine = adoptCF(CTLineCreateWithAttributedString((CFAttributedStringRef)ellipsisString.get()));
1494 RetainPtr<CTLineRef> truncatedLine = adoptCF(CTLineCreateTruncatedLine(remainingLine.get(), attachmentWrappingTextMaximumWidth, kCTLineTruncationMiddle, ellipsisLine.get()));
1497 truncatedLine = remainingLine;
1499 addLine(truncatedLine.get());
1502 void AttachmentInfo::buildSingleLine(const String& text, CTFontRef font, UIColor *color)
1507 NSDictionary *textAttributes = @{
1508 (id)kCTFontAttributeName: (id)font,
1509 (id)kCTForegroundColorAttributeName: color
1511 RetainPtr<NSAttributedString> attributedText = adoptNS([[NSAttributedString alloc] initWithString:text attributes:textAttributes]);
1513 addLine(adoptCF(CTLineCreateWithAttributedString((CFAttributedStringRef)attributedText.get())).get());
1516 static BOOL getAttachmentProgress(const RenderAttachment& attachment, float& progress)
1518 String progressString = attachment.attachmentElement().attributeWithoutSynchronization(progressAttr);
1519 if (progressString.isEmpty())
1522 progress = std::max<float>(std::min<float>(progressString.toFloat(&validProgress), 1), 0);
1523 return validProgress;
1526 static RetainPtr<UIImage> iconForAttachment(const RenderAttachment& attachment, FloatSize& size)
1528 auto documentInteractionController = adoptNS([[getUIDocumentInteractionControllerClass() alloc] init]);
1531 if (File* file = attachment.attachmentElement().file())
1532 fileName = file->name();
1534 if (fileName.isEmpty())
1535 fileName = attachment.attachmentElement().attachmentTitle();
1536 [documentInteractionController setName:fileName];
1538 String attachmentType = attachment.attachmentElement().attachmentType();
1539 if (!attachmentType.isEmpty()) {
1540 auto attachmentTypeCF = attachmentType.createCFString();
1541 RetainPtr<CFStringRef> UTI;
1542 if (isDeclaredUTI(attachmentTypeCF.get()))
1543 UTI = attachmentTypeCF;
1545 UTI = UTIFromMIMEType(attachmentTypeCF.get());
1547 [documentInteractionController setUTI:static_cast<NSString *>(UTI.get())];
1550 NSArray *icons = [documentInteractionController icons];
1554 RetainPtr<UIImage> result = icons.lastObject;
1556 BOOL useHeightForClosestMatch = [result size].height > [result size].width;
1557 CGFloat bestMatchRatio = -1;
1559 for (UIImage *icon in icons) {
1560 CGFloat iconSize = useHeightForClosestMatch ? icon.size.height : icon.size.width;
1562 CGFloat matchRatio = (attachmentIconSize / iconSize) - 1.0f;
1563 if (matchRatio < 0.3f) {
1564 matchRatio = CGFAbs(matchRatio);
1565 if ((bestMatchRatio == -1) || (matchRatio < bestMatchRatio)) {
1567 bestMatchRatio = matchRatio;
1572 CGFloat iconAspect = [result size].width / [result size].height;
1573 size = largestRectWithAspectRatioInsideRect(iconAspect, FloatRect(0, 0, attachmentIconSize, attachmentIconSize)).size();
1578 AttachmentInfo::AttachmentInfo(const RenderAttachment& attachment)
1580 attachmentRect = FloatRect(0, 0, attachmentSize.width, attachmentSize.height);
1582 hasProgress = getAttachmentProgress(attachment, progress);
1584 String title = attachment.attachmentElement().attachmentTitle();
1585 String action = attachment.attachmentElement().attributeWithoutSynchronization(actionAttr);
1586 String subtitle = attachment.attachmentElement().attributeWithoutSynchronization(subtitleAttr);
1588 CGFloat yOffset = 0;
1591 progressRect = FloatRect((attachmentRect.width() / 2) - (attachmentProgressSize / 2), 0, attachmentProgressSize, attachmentProgressSize);
1592 yOffset += attachmentProgressSize + attachmentItemMargin;
1595 if (action.isEmpty() && !hasProgress) {
1597 icon = iconForAttachment(attachment, iconSize);
1599 iconRect = FloatRect(FloatPoint((attachmentRect.width() / 2) - (iconSize.width() / 2), 0), iconSize);
1600 yOffset += iconRect.height() + attachmentItemMargin;
1603 buildWrappedLines(action, attachmentActionFont().get(), attachmentActionColor(attachment), attachmentWrappingTextMaximumLineCount);
1605 bool forceSingleLineTitle = !action.isEmpty() || !subtitle.isEmpty() || hasProgress;
1606 buildWrappedLines(title, attachmentTitleFont().get(), attachmentTitleColor(), forceSingleLineTitle ? 1 : attachmentWrappingTextMaximumLineCount);
1607 buildSingleLine(subtitle, attachmentSubtitleFont().get(), attachmentSubtitleColor());
1609 if (!lines.isEmpty()) {
1610 for (auto& line : lines) {
1611 line.rect.setY(yOffset);
1612 yOffset += line.rect.height() + attachmentItemMargin;
1616 yOffset -= attachmentItemMargin;
1618 contentYOrigin = (attachmentRect.height() / 2) - (yOffset / 2);
1621 LayoutSize RenderThemeIOS::attachmentIntrinsicSize(const RenderAttachment&) const
1623 return LayoutSize(FloatSize(attachmentSize));
1626 int RenderThemeIOS::attachmentBaseline(const RenderAttachment& attachment) const
1628 AttachmentInfo info(attachment);
1629 return info.baseline;
1632 static void paintAttachmentIcon(GraphicsContext& context, AttachmentInfo& info)
1637 RefPtr<Image> iconImage = BitmapImage::create([info.icon CGImage]);
1641 context.drawImage(*iconImage, info.iconRect);
1644 static void paintAttachmentText(GraphicsContext& context, AttachmentInfo& info)
1646 for (const auto& line : info.lines) {
1647 GraphicsContextStateSaver saver(context);
1649 context.translate(toFloatSize(line.rect.minXMaxYCorner()));
1650 context.scale(FloatSize(1, -1));
1652 CGContextSetTextPosition(context.platformContext(), 0, 0);
1653 CTLineDraw(line.line.get(), context.platformContext());
1657 static void paintAttachmentProgress(GraphicsContext& context, AttachmentInfo& info)
1659 GraphicsContextStateSaver saver(context);
1661 context.setStrokeThickness(attachmentProgressBorderThickness);
1662 context.setStrokeColor(attachmentProgressColor());
1663 context.setFillColor(attachmentProgressColor());
1664 context.strokeEllipse(info.progressRect);
1666 FloatPoint center = info.progressRect.center();
1669 progressPath.moveTo(center);
1670 progressPath.addLineTo(FloatPoint(center.x(), info.progressRect.y()));
1671 progressPath.addArc(center, info.progressRect.width() / 2, -M_PI_2, info.progress * 2 * M_PI - M_PI_2, 0);
1672 progressPath.closeSubpath();
1673 context.fillPath(progressPath);
1676 static Path attachmentBorderPath(AttachmentInfo& info)
1679 borderPath.addRoundedRect(info.attachmentRect, FloatSize(attachmentBorderRadius, attachmentBorderRadius));
1683 static void paintAttachmentBorder(GraphicsContext& context, Path& borderPath)
1685 context.setStrokeColor(attachmentBorderColor());
1686 context.setStrokeThickness(1);
1687 context.strokePath(borderPath);
1690 bool RenderThemeIOS::paintAttachment(const RenderObject& renderer, const PaintInfo& paintInfo, const IntRect& paintRect)
1692 if (!is<RenderAttachment>(renderer))
1695 const RenderAttachment& attachment = downcast<RenderAttachment>(renderer);
1697 AttachmentInfo info(attachment);
1699 GraphicsContext& context = paintInfo.context();
1700 GraphicsContextStateSaver saver(context);
1702 context.translate(toFloatSize(paintRect.location()));
1704 Path borderPath = attachmentBorderPath(info);
1705 paintAttachmentBorder(context, borderPath);
1706 context.clipPath(borderPath);
1708 context.translate(FloatSize(0, info.contentYOrigin));
1710 if (info.hasProgress)
1711 paintAttachmentProgress(context, info);
1713 paintAttachmentIcon(context, info);
1715 paintAttachmentText(context, info);
1720 #endif // ENABLE(ATTACHMENT_ELEMENT)
1722 } // namespace WebCore
1724 #endif //PLATFORM(IOS)