Unreviewed, rolling out r215608.
[WebKit-https.git] / Source / WebCore / editing / cocoa / EditorCocoa.mm
index 70d343f..05c8908 100644 (file)
 #import "ArchiveResource.h"
 #import "CSSValueList.h"
 #import "CSSValuePool.h"
+#import "CachedResourceLoader.h"
+#import "ColorMac.h"
 #import "DocumentFragment.h"
+#import "DocumentLoader.h"
+#import "Editing.h"
 #import "EditingStyle.h"
+#import "EditorClient.h"
 #import "Frame.h"
 #import "FrameSelection.h"
+#import "HTMLConverter.h"
+#import "HTMLImageElement.h"
 #import "HTMLSpanElement.h"
+#import "LegacyWebArchive.h"
 #import "NSAttributedStringSPI.h"
+#import "Pasteboard.h"
 #import "RenderElement.h"
 #import "RenderStyle.h"
 #import "SoftLinking.h"
 #import "Text.h"
-#import "htmlediting.h"
+#import <wtf/BlockObjCExceptions.h>
 
 #if PLATFORM(IOS)
 SOFT_LINK_PRIVATE_FRAMEWORK(WebKitLegacy)
@@ -49,42 +58,11 @@ SOFT_LINK_PRIVATE_FRAMEWORK(WebKitLegacy)
 SOFT_LINK_FRAMEWORK_IN_UMBRELLA(WebKit, WebKitLegacy)
 #endif
 
+// FIXME: Get rid of this and change NSAttributedString conversion so it doesn't use WebKitLegacy (cf. rdar://problem/30597352).
 SOFT_LINK(WebKitLegacy, _WebCreateFragment, void, (WebCore::Document& document, NSAttributedString *string, WebCore::FragmentAndResources& result), (document, string, result))
 
 namespace WebCore {
 
-// FIXME: This figures out the current style by inserting a <span>!
-const RenderStyle* Editor::styleForSelectionStart(Frame* frame, Node*& nodeToRemove)
-{
-    nodeToRemove = nullptr;
-    
-    if (frame->selection().isNone())
-        return nullptr;
-
-    Position position = adjustedSelectionStartForStyleComputation(frame->selection().selection());
-    if (!position.isCandidate() || position.isNull())
-        return nullptr;
-
-    RefPtr<EditingStyle> typingStyle = frame->selection().typingStyle();
-    if (!typingStyle || !typingStyle->style())
-        return &position.deprecatedNode()->renderer()->style();
-
-    auto styleElement = HTMLSpanElement::create(*frame->document());
-
-    String styleText = typingStyle->style()->asText() + " display: inline";
-    styleElement->setAttribute(HTMLNames::styleAttr, styleText);
-
-    styleElement->appendChild(frame->document()->createEditingTextNode(emptyString()));
-
-    if (position.deprecatedNode()->parentNode()->appendChild(styleElement).hasException())
-        return nullptr; 
-
-    nodeToRemove = styleElement.ptr();
-
-    frame->document()->updateStyleIfNeeded();
-    return styleElement->renderer() ? &styleElement->renderer()->style() : nullptr;
-}
-
 void Editor::getTextDecorationAttributesRespectingTypingStyle(const RenderStyle& style, NSMutableDictionary* result) const
 {
     RefPtr<EditingStyle> typingStyle = m_frame.selection().typingStyle();
@@ -93,19 +71,78 @@ void Editor::getTextDecorationAttributesRespectingTypingStyle(const RenderStyle&
         if (value && value->isValueList()) {
             CSSValueList& valueList = downcast<CSSValueList>(*value);
             if (valueList.hasValue(CSSValuePool::singleton().createIdentifierValue(CSSValueLineThrough).ptr()))
-                [result setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle] forKey:NSStrikethroughStyleAttributeName];
+                [result setObject:@(NSUnderlineStyleSingle) forKey:NSStrikethroughStyleAttributeName];
             if (valueList.hasValue(CSSValuePool::singleton().createIdentifierValue(CSSValueUnderline).ptr()))
-                [result setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle] forKey:NSUnderlineStyleAttributeName];
+                [result setObject:@(NSUnderlineStyleSingle) forKey:NSUnderlineStyleAttributeName];
         }
     } else {
         int decoration = style.textDecorationsInEffect();
         if (decoration & TextDecorationLineThrough)
-            [result setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle] forKey:NSStrikethroughStyleAttributeName];
+            [result setObject:@(NSUnderlineStyleSingle) forKey:NSStrikethroughStyleAttributeName];
         if (decoration & TextDecorationUnderline)
-            [result setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle] forKey:NSUnderlineStyleAttributeName];
+            [result setObject:@(NSUnderlineStyleSingle) forKey:NSUnderlineStyleAttributeName];
     }
 }
 
+RetainPtr<NSDictionary> Editor::fontAttributesForSelectionStart() const
+{
+    Node* nodeToRemove;
+    auto* style = styleForSelectionStart(&m_frame, nodeToRemove);
+    if (!style)
+        return nil;
+
+    RetainPtr<NSMutableDictionary> attributes = adoptNS([[NSMutableDictionary alloc] init]);
+
+    if (auto ctFont = style->fontCascade().primaryFont().getCTFont())
+        [attributes setObject:(id)ctFont forKey:NSFontAttributeName];
+
+    // FIXME: Why would we not want to retrieve these attributes on iOS?
+#if PLATFORM(MAC)
+    if (style->visitedDependentColor(CSSPropertyBackgroundColor).isVisible())
+        [attributes setObject:nsColor(style->visitedDependentColor(CSSPropertyBackgroundColor)) forKey:NSBackgroundColorAttributeName];
+
+    if (style->visitedDependentColor(CSSPropertyColor).isValid() && !Color::isBlackColor(style->visitedDependentColor(CSSPropertyColor)))
+        [attributes setObject:nsColor(style->visitedDependentColor(CSSPropertyColor)) forKey:NSForegroundColorAttributeName];
+
+    const ShadowData* shadowData = style->textShadow();
+    if (shadowData) {
+        RetainPtr<NSShadow> platformShadow = adoptNS([[NSShadow alloc] init]);
+        [platformShadow setShadowOffset:NSMakeSize(shadowData->x(), shadowData->y())];
+        [platformShadow setShadowBlurRadius:shadowData->radius()];
+        [platformShadow setShadowColor:nsColor(shadowData->color())];
+        [attributes setObject:platformShadow.get() forKey:NSShadowAttributeName];
+    }
+
+    int superscriptInt = 0;
+    switch (style->verticalAlign()) {
+    case BASELINE:
+    case BOTTOM:
+    case BASELINE_MIDDLE:
+    case LENGTH:
+    case MIDDLE:
+    case TEXT_BOTTOM:
+    case TEXT_TOP:
+    case TOP:
+        break;
+    case SUB:
+        superscriptInt = -1;
+        break;
+    case SUPER:
+        superscriptInt = 1;
+        break;
+    }
+    if (superscriptInt)
+        [attributes setObject:@(superscriptInt) forKey:NSSuperscriptAttributeName];
+#endif
+
+    getTextDecorationAttributesRespectingTypingStyle(*style, attributes.get());
+
+    if (nodeToRemove)
+        nodeToRemove->remove();
+
+    return attributes;
+}
+
 FragmentAndResources Editor::createFragment(NSAttributedString *string)
 {
     // FIXME: The algorithm to convert an attributed string into HTML should be implemented here in WebCore.
@@ -115,4 +152,180 @@ FragmentAndResources Editor::createFragment(NSAttributedString *string)
     return result;
 }
 
+static RefPtr<SharedBuffer> archivedDataForAttributedString(NSAttributedString *attributedString)
+{
+    if (!attributedString.length)
+        return nullptr;
+
+    return SharedBuffer::wrapNSData([NSKeyedArchiver archivedDataWithRootObject:attributedString]);
+}
+
+void Editor::writeSelectionToPasteboard(Pasteboard& pasteboard)
+{
+    NSAttributedString *attributedString = attributedStringFromRange(*selectedRange());
+
+    PasteboardWebContent content;
+    content.canSmartCopyOrDelete = canSmartCopyOrDelete();
+    content.dataInWebArchiveFormat = selectionInWebArchiveFormat();
+    content.dataInRTFDFormat = attributedString.containsAttachments ? dataInRTFDFormat(attributedString) : nullptr;
+    content.dataInRTFFormat = dataInRTFFormat(attributedString);
+    content.dataInAttributedStringFormat = archivedDataForAttributedString(attributedString);
+    // FIXME: Why don't we want this on iOS?
+#if PLATFORM(MAC)
+    content.dataInHTMLFormat = selectionInHTMLFormat();
+#endif
+    content.dataInStringFormat = stringSelectionForPasteboardWithImageAltText();
+    client()->getClientPasteboardDataForRange(selectedRange().get(), content.clientTypes, content.clientData);
+
+    pasteboard.write(content);
+}
+
+void Editor::writeSelection(PasteboardWriterData& pasteboardWriterData)
+{
+    NSAttributedString *attributedString = attributedStringFromRange(*selectedRange());
+
+    PasteboardWriterData::WebContent webContent;
+    webContent.canSmartCopyOrDelete = canSmartCopyOrDelete();
+    webContent.dataInWebArchiveFormat = selectionInWebArchiveFormat();
+    webContent.dataInRTFDFormat = attributedString.containsAttachments ? dataInRTFDFormat(attributedString) : nullptr;
+    webContent.dataInRTFFormat = dataInRTFFormat(attributedString);
+    webContent.dataInAttributedStringFormat = archivedDataForAttributedString(attributedString);
+    // FIXME: Why don't we want this on iOS?
+#if PLATFORM(MAC)
+    webContent.dataInHTMLFormat = selectionInHTMLFormat();
+#endif
+    webContent.dataInStringFormat = stringSelectionForPasteboardWithImageAltText();
+    client()->getClientPasteboardDataForRange(selectedRange().get(), webContent.clientTypes, webContent.clientData);
+
+    pasteboardWriterData.setWebContent(WTFMove(webContent));
+}
+
+RefPtr<SharedBuffer> Editor::selectionInWebArchiveFormat()
+{
+    RefPtr<LegacyWebArchive> archive = LegacyWebArchive::createFromSelection(&m_frame);
+    if (!archive)
+        return nullptr;
+    return SharedBuffer::wrapCFData(archive->rawDataRepresentation().get());
+}
+
+// FIXME: Makes no sense that selectedTextForDataTransfer always includes alt text, but stringSelectionForPasteboard does not.
+// This was left in a bad state when selectedTextForDataTransfer was added. Need to look over clients and fix this.
+String Editor::stringSelectionForPasteboard()
+{
+    if (!canCopy())
+        return emptyString();
+    String text = selectedText();
+    text.replace(noBreakSpace, ' ');
+    return text;
+}
+
+String Editor::stringSelectionForPasteboardWithImageAltText()
+{
+    if (!canCopy())
+        return emptyString();
+    String text = selectedTextForDataTransfer();
+    text.replace(noBreakSpace, ' ');
+    return text;
+}
+
+void Editor::replaceSelectionWithAttributedString(NSAttributedString *attributedString, MailBlockquoteHandling mailBlockquoteHandling)
+{
+    if (m_frame.selection().isNone())
+        return;
+
+    if (m_frame.selection().selection().isContentRichlyEditable()) {
+        RefPtr<DocumentFragment> fragment = createFragmentAndAddResources(attributedString);
+        if (fragment && shouldInsertFragment(fragment, selectedRange(), EditorInsertAction::Pasted))
+            pasteAsFragment(fragment.releaseNonNull(), false, false, mailBlockquoteHandling);
+    } else {
+        String text = attributedString.string;
+        if (shouldInsertText(text, selectedRange().get(), EditorInsertAction::Pasted))
+            pasteAsPlainText(text, false);
+    }
+}
+
+RefPtr<DocumentFragment> Editor::createFragmentForImageResourceAndAddResource(RefPtr<ArchiveResource>&& resource)
+{
+    if (!resource)
+        return nullptr;
+
+    // FIXME: Why is this different?
+#if PLATFORM(MAC)
+    String resourceURL = resource->url().string();
+#else
+    NSURL *URL = resource->url();
+    String resourceURL = URL.isFileURL ? URL.absoluteString : resource->url();
+#endif
+
+    if (DocumentLoader* loader = m_frame.loader().documentLoader())
+        loader->addArchiveResource(resource.releaseNonNull());
+
+    auto imageElement = HTMLImageElement::create(*m_frame.document());
+    imageElement->setAttributeWithoutSynchronization(HTMLNames::srcAttr, resourceURL);
+
+    auto fragment = m_frame.document()->createDocumentFragment();
+    fragment->appendChild(imageElement);
+    
+    return WTFMove(fragment);
+}
+
+RefPtr<SharedBuffer> Editor::dataInRTFDFormat(NSAttributedString *string)
+{
+    NSUInteger length = string.length;
+    if (!length)
+        return nullptr;
+
+    BEGIN_BLOCK_OBJC_EXCEPTIONS;
+    return SharedBuffer::wrapNSData([string RTFDFromRange:NSMakeRange(0, length) documentAttributes:@{ }]);
+    END_BLOCK_OBJC_EXCEPTIONS;
+
+    return nullptr;
+}
+
+RefPtr<SharedBuffer> Editor::dataInRTFFormat(NSAttributedString *string)
+{
+    NSUInteger length = string.length;
+    if (!length)
+        return nullptr;
+
+    BEGIN_BLOCK_OBJC_EXCEPTIONS;
+    return SharedBuffer::wrapNSData([string RTFFromRange:NSMakeRange(0, length) documentAttributes:@{ }]);
+    END_BLOCK_OBJC_EXCEPTIONS;
+
+    return nullptr;
+}
+
+RefPtr<DocumentFragment> Editor::createFragmentAndAddResources(NSAttributedString *string)
+{
+    if (!m_frame.page() || !m_frame.document())
+        return nullptr;
+
+    auto& document = *m_frame.document();
+    if (!document.isHTMLDocument() || !string)
+        return nullptr;
+
+    bool wasDeferringCallbacks = m_frame.page()->defersLoading();
+    if (!wasDeferringCallbacks)
+        m_frame.page()->setDefersLoading(true);
+
+    auto& cachedResourceLoader = document.cachedResourceLoader();
+    bool wasImagesEnabled = cachedResourceLoader.imagesEnabled();
+    if (wasImagesEnabled)
+        cachedResourceLoader.setImagesEnabled(false);
+
+    auto fragmentAndResources = createFragment(string);
+
+    if (auto* loader = m_frame.loader().documentLoader()) {
+        for (auto& resource : fragmentAndResources.resources)
+            loader->addArchiveResource(WTFMove(resource));
+    }
+
+    if (wasImagesEnabled)
+        cachedResourceLoader.setImagesEnabled(true);
+    if (!wasDeferringCallbacks)
+        m_frame.page()->setDefersLoading(false);
+    
+    return WTFMove(fragmentAndResources.fragment);
+}
+
 }