Title: [197600] trunk/Source/WebCore
Revision
197600
Author
[email protected]
Date
2016-03-04 16:57:19 -0800 (Fri, 04 Mar 2016)

Log Message

Begin implementing <attachment> painting on iOS
https://bugs.webkit.org/show_bug.cgi?id=155046
<rdar://problem/24805991>

Reviewed by Enrica Casucci.

No new tests; there are existing tests that I will unskip and rebaseline
in the near future.

* rendering/RenderThemeIOS.h:
* rendering/RenderThemeIOS.mm:
(WebCore::AttachmentInfo::addLine):
(WebCore::AttachmentInfo::buildTitleLines):
(WebCore::AttachmentInfo::buildSingleLine):
(WebCore::getAttachmentProgress):
(WebCore::iconForAttachment):
(WebCore::AttachmentInfo::AttachmentInfo):
(WebCore::RenderThemeIOS::attachmentIntrinsicSize):
(WebCore::RenderThemeIOS::attachmentBaseline):
(WebCore::paintAttachmentIcon):
(WebCore::paintAttachmentText):
(WebCore::paintAttachmentProgress):
(WebCore::paintAttachmentBorder):
(WebCore::RenderThemeIOS::paintAttachment):
There are still a few missing pieces, but get <attachment> painting a bit on iOS.
We will paint an icon, action, title, and subtitle - in that order - depending on what we have.
The content is vertically and horizontally centered.

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (197599 => 197600)


--- trunk/Source/WebCore/ChangeLog	2016-03-05 00:56:07 UTC (rev 197599)
+++ trunk/Source/WebCore/ChangeLog	2016-03-05 00:57:19 UTC (rev 197600)
@@ -1,3 +1,33 @@
+2016-03-04  Tim Horton  <[email protected]>
+
+        Begin implementing <attachment> painting on iOS
+        https://bugs.webkit.org/show_bug.cgi?id=155046
+        <rdar://problem/24805991>
+
+        Reviewed by Enrica Casucci.
+
+        No new tests; there are existing tests that I will unskip and rebaseline
+        in the near future.
+
+        * rendering/RenderThemeIOS.h:
+        * rendering/RenderThemeIOS.mm:
+        (WebCore::AttachmentInfo::addLine):
+        (WebCore::AttachmentInfo::buildTitleLines):
+        (WebCore::AttachmentInfo::buildSingleLine):
+        (WebCore::getAttachmentProgress):
+        (WebCore::iconForAttachment):
+        (WebCore::AttachmentInfo::AttachmentInfo):
+        (WebCore::RenderThemeIOS::attachmentIntrinsicSize):
+        (WebCore::RenderThemeIOS::attachmentBaseline):
+        (WebCore::paintAttachmentIcon):
+        (WebCore::paintAttachmentText):
+        (WebCore::paintAttachmentProgress):
+        (WebCore::paintAttachmentBorder):
+        (WebCore::RenderThemeIOS::paintAttachment):
+        There are still a few missing pieces, but get <attachment> painting a bit on iOS.
+        We will paint an icon, action, title, and subtitle - in that order - depending on what we have.
+        The content is vertically and horizontally centered.
+
 2016-03-04  Gavin Barraclough  <[email protected]>
 
         Convert DOMTimer interval from int to std::chromo::milliseconds

Modified: trunk/Source/WebCore/rendering/RenderThemeIOS.h (197599 => 197600)


--- trunk/Source/WebCore/rendering/RenderThemeIOS.h	2016-03-05 00:56:07 UTC (rev 197599)
+++ trunk/Source/WebCore/rendering/RenderThemeIOS.h	2016-03-05 00:57:19 UTC (rev 197600)
@@ -34,7 +34,8 @@
     
 class RenderStyle;
 class GraphicsContext;
-    
+struct AttachmentLayout;
+
 class RenderThemeIOS final : public RenderTheme {
 public:
     static Ref<RenderTheme> create();
@@ -110,6 +111,12 @@
     String mediaControlsScript() override;
 #endif
 
+#if ENABLE(ATTACHMENT_ELEMENT)
+    LayoutSize attachmentIntrinsicSize(const RenderAttachment&) const override;
+    int attachmentBaseline(const RenderAttachment&) const override;
+    bool paintAttachment(const RenderObject&, const PaintInfo&, const IntRect&) override;
+#endif
+
 private:
     RenderThemeIOS();
     virtual ~RenderThemeIOS() { }

Modified: trunk/Source/WebCore/rendering/RenderThemeIOS.mm (197599 => 197600)


--- trunk/Source/WebCore/rendering/RenderThemeIOS.mm	2016-03-05 00:56:07 UTC (rev 197599)
+++ trunk/Source/WebCore/rendering/RenderThemeIOS.mm	2016-03-05 00:57:19 UTC (rev 197600)
@@ -27,20 +27,25 @@
 
 #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 "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"
@@ -49,7 +54,9 @@
 #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"
@@ -57,6 +64,7 @@
 #import "RenderView.h"
 #import "SoftLinking.h"
 #import "UIKitSPI.h"
+#import "UTIUtilities.h"
 #import "UserAgentScripts.h"
 #import "UserAgentStyleSheets.h"
 #import "WebCoreThreadRun.h"
@@ -66,10 +74,18 @@
 #import <wtf/RefPtr.h>
 #import <wtf/StdLibExtras.h>
 
+SOFT_LINK_FRAMEWORK(MobileCoreServices)
+SOFT_LINK_CLASS(MobileCoreServices, LSDocumentProxy)
+
 SOFT_LINK_FRAMEWORK(UIKit)
 SOFT_LINK_CLASS(UIKit, UIApplication)
 SOFT_LINK_CLASS(UIKit, UIColor)
+SOFT_LINK_CLASS(UIKit, UIDocumentInteractionController)
+SOFT_LINK_CLASS(UIKit, UIFont)
+SOFT_LINK_CLASS(UIKit, UIImage)
 SOFT_LINK_CONSTANT(UIKit, UIContentSizeCategoryDidChangeNotification, CFStringRef)
+SOFT_LINK_CONSTANT(UIKit, UIFontTextStyleFootnote, NSString *)
+SOFT_LINK_CONSTANT(UIKit, UIFontTextStyleCaption1, NSString *)
 #define UIContentSizeCategoryDidChangeNotification getUIContentSizeCategoryDidChangeNotification()
 
 @interface WebCoreRenderThemeBundle : NSObject
@@ -80,6 +96,8 @@
 
 namespace WebCore {
 
+using namespace HTMLNames;
+
 const float ControlBaseHeight = 20;
 const float ControlBaseFontSize = 11;
 
@@ -1321,6 +1339,320 @@
     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); }
+
+const CGFloat attachmentProgressSize = 36;
+const CGFloat attachmentIconSize = 48;
+
+const CGFloat attachmentItemMargin = 8;
+
+const CGFloat attachmentTitleMaximumWidth = 140;
+const CFIndex attachmentTitleMaximumLineCount = 2;
+
+// FIXME: Should be emphasized.
+static UIFont *attachmentActionFont() { return [getUIFontClass() preferredFontForTextStyle:getUIFontTextStyleFootnote()]; }
+static UIColor *attachmentActionColor() { return [getUIColorClass() systemBlueColor]; }
+
+static UIFont *attachmentTitleFont() { return [getUIFontClass() preferredFontForTextStyle:getUIFontTextStyleCaption1()]; }
+static UIColor *attachmentTitleColor() { return [getUIColorClass() systemGrayColor]; }
+
+static UIFont *attachmentSubtitleFont() { return [getUIFontClass() preferredFontForTextStyle:getUIFontTextStyleCaption1()]; }
+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 buildTitleLines(const RenderAttachment&);
+    void buildSingleLine(const String&, UIFont *, 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::buildTitleLines(const RenderAttachment& attachment)
+{
+    RetainPtr<UIFont> font = attachmentTitleFont();
+
+    String title = attachment.attachmentElement().attachmentTitle();
+    if (title.isEmpty())
+        return;
+
+    NSDictionary *textAttributes = @{
+        (id)kCTFontAttributeName: font.get(),
+        (id)kCTForegroundColorAttributeName: attachmentTitleColor()
+    };
+    RetainPtr<NSAttributedString> attributedTitle = adoptNS([[NSAttributedString alloc] initWithString:title attributes:textAttributes]);
+    RetainPtr<CTFramesetterRef> titleFramesetter = adoptCF(CTFramesetterCreateWithAttributedString((CFAttributedStringRef)attributedTitle.get()));
+
+    CFRange fitRange;
+    CGSize titleTextSize = CTFramesetterSuggestFrameSizeWithConstraints(titleFramesetter.get(), CFRangeMake(0, 0), nullptr, CGSizeMake(attachmentTitleMaximumWidth, CGFLOAT_MAX), &fitRange);
+
+    RetainPtr<CGPathRef> titlePath = adoptCF(CGPathCreateWithRect(CGRectMake(0, 0, titleTextSize.width, titleTextSize.height), nullptr));
+    RetainPtr<CTFrameRef> titleFrame = adoptCF(CTFramesetterCreateFrame(titleFramesetter.get(), fitRange, titlePath.get(), nullptr));
+
+    CFArrayRef ctLines = CTFrameGetLines(titleFrame.get());
+    CFIndex lineCount = CFArrayGetCount(ctLines);
+    if (!lineCount)
+        return;
+
+    // Lay out and record the first (attachmentTitleMaximumLineCount - 1) lines.
+    CFIndex lineIndex = 0;
+    for (; lineIndex < std::min(attachmentTitleMaximumLineCount - 1, lineCount); ++lineIndex) {
+        CTLineRef line = (CTLineRef)CFArrayGetValueAtIndex(ctLines, lineIndex);
+        addLine(line);
+    }
+
+    if (lineIndex == lineCount)
+        return;
+
+    // We had text that didn't fit in the first (attachmentTitleMaximumLineCount - 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, [attributedTitle length] - remainingRangeStart);
+    NSAttributedString *remainingString = [attributedTitle 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(), attachmentTitleMaximumWidth, kCTLineTruncationMiddle, ellipsisLine.get()));
+
+    if (!truncatedLine)
+        truncatedLine = remainingLine;
+
+    addLine(truncatedLine.get());
+}
+
+void AttachmentInfo::buildSingleLine(const String& text, UIFont *font, UIColor *color)
+{
+    if (text.isEmpty())
+        return;
+
+    NSDictionary *textAttributes = @{
+        (id)kCTFontAttributeName: 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().fastGetAttribute(progressAttr);
+    if (progressString.isEmpty())
+        return NO;
+    bool validProgress;
+    progress = progressString.toFloat(&validProgress);
+    return validProgress;
+}
+
+static RetainPtr<UIImage> iconForAttachment(const RenderAttachment& attachment, FloatSize& size)
+{
+    String MIMEType = attachment.attachmentElement().attachmentType();
+
+    String fileName;
+    if (File* file = attachment.attachmentElement().file())
+        fileName = file->name();
+
+    if (fileName.isEmpty())
+        fileName = attachment.attachmentElement().attachmentTitle();
+
+    RetainPtr<UIImage> result;
+
+    RetainPtr<UIDocumentInteractionController> documentInteractionController = adoptNS([[getUIDocumentInteractionControllerClass() alloc] init]);
+    [documentInteractionController setName:fileName];
+    [documentInteractionController setUTI:static_cast<NSString *>(mimeTypeFromUTITree(MIMEType.createCFString().get()).get())];
+
+    NSArray *icons = [documentInteractionController icons];
+    if (!icons.count)
+        return nil;
+
+    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 action = ""
+    String subtitle = attachment.attachmentElement().fastGetAttribute(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
+        buildSingleLine(action, attachmentActionFont(), attachmentActionColor());
+
+    buildTitleLines(attachment);
+    buildSingleLine(subtitle, attachmentSubtitleFont(), attachmentSubtitleColor());
+
+    if (!lines.isEmpty()) {
+        for (auto& line : lines) {
+            line.rect.setY(yOffset);
+            yOffset += line.rect.height() + 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));
+
+        CGContextSetTextMatrix(context.platformContext(), CGAffineTransformIdentity);
+        CTLineDraw(line.line.get(), context.platformContext());
+    }
+}
+
+static void paintAttachmentProgress(GraphicsContext& context, AttachmentInfo& info)
+{
+    GraphicsContextStateSaver saver(context);
+
+    // FIXME: Implement progress indicator.
+    context.fillRect(info.progressRect, Color(0, 255, 0));
+}
+
+static void paintAttachmentBorder(GraphicsContext& context, AttachmentInfo& info)
+{
+    Path borderPath;
+    borderPath.addRoundedRect(info.attachmentRect, FloatSize(attachmentBorderRadius, attachmentBorderRadius));
+    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()));
+
+    paintAttachmentBorder(context, info);
+
+    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)
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to