Unreviewed build fix
[WebKit-https.git] / Source / WebCore / editing / cocoa / WebContentReaderCocoa.mm
index 1c91351..373f5b1 100644 (file)
 #import "Blob.h"
 #import "BlobURL.h"
 #import "CachedResourceLoader.h"
+#import "CustomHeaderFields.h"
 #import "DOMURL.h"
 #import "Document.h"
 #import "DocumentFragment.h"
 #import "DocumentLoader.h"
+#import "Editor.h"
+#import "EditorClient.h"
 #import "File.h"
-#import "FileSystem.h"
 #import "Frame.h"
 #import "FrameLoader.h"
 #import "FrameLoaderClient.h"
@@ -43,6 +45,7 @@
 #import "HTMLAttachmentElement.h"
 #import "HTMLBRElement.h"
 #import "HTMLBodyElement.h"
+#import "HTMLDivElement.h"
 #import "HTMLIFrameElement.h"
 #import "HTMLImageElement.h"
 #import "HTMLObjectElement.h"
 #import "MIMETypeRegistry.h"
 #import "Page.h"
 #import "PublicURLManager.h"
+#import "RenderView.h"
 #import "RuntimeEnabledFeatures.h"
+#import "SerializedAttachmentData.h"
 #import "Settings.h"
 #import "SocketProvider.h"
 #import "TypedElementDescendantIterator.h"
-#import "URLParser.h"
 #import "UTIUtilities.h"
 #import "WebArchiveResourceFromNSAttributedString.h"
 #import "WebArchiveResourceWebResourceHandler.h"
 #import "WebNSAttributedStringExtras.h"
 #import "markup.h"
 #import <pal/spi/cocoa/NSAttributedStringSPI.h>
+#import <wtf/FileSystem.h>
 #import <wtf/SoftLinking.h>
+#import <wtf/URLParser.h>
 
 #if PLATFORM(MAC)
 #include "LocalDefaultSystemAppearance.h"
 #endif
 
-#if (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300)
+#if (PLATFORM(IOS_FAMILY) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300)
 @interface NSAttributedString ()
 - (NSString *)_htmlDocumentFragmentString:(NSRange)range documentAttributes:(NSDictionary *)dict subresources:(NSArray **)subresources;
 @end
-#elif PLATFORM(IOS)
+#elif PLATFORM(IOS_FAMILY)
 SOFT_LINK_PRIVATE_FRAMEWORK(WebKitLegacy)
 #elif PLATFORM(MAC)
 SOFT_LINK_FRAMEWORK_IN_UMBRELLA(WebKit, WebKitLegacy)
 #endif
 
-#if (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED < 110000) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101300)
+#if (PLATFORM(IOS_FAMILY) && __IPHONE_OS_VERSION_MIN_REQUIRED < 110000) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101300)
 SOFT_LINK(WebKitLegacy, _WebCreateFragment, void, (WebCore::Document& document, NSAttributedString *string, WebCore::FragmentAndResources& result), (document, string, result))
 #endif
 
 namespace WebCore {
 
-#if (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300)
+#if PLATFORM(IOSMAC)
+
+static FragmentAndResources createFragment(Frame&, NSAttributedString *)
+{
+    return { };
+}
+
+#elif (PLATFORM(IOS_FAMILY) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300)
 
 static NSDictionary *attributesForAttributedStringConversion()
 {
@@ -108,7 +121,7 @@ static NSDictionary *attributesForAttributedStringConversion()
         [excludedElements addObject:@"object"];
 #endif
 
-#if PLATFORM(IOS)
+#if PLATFORM(IOS_FAMILY)
     static NSString * const NSExcludedElementsDocumentAttribute = @"ExcludedElements";
 #endif
 
@@ -132,8 +145,8 @@ static FragmentAndResources createFragment(Frame& frame, NSAttributedString *str
     Document& document = *frame.document();
 
 #if PLATFORM(MAC)
-    auto* page = frame.page();
-    LocalDefaultSystemAppearance localAppearance(page->useSystemAppearance(), page->useDarkAppearance());
+    auto* view = frame.view();
+    LocalDefaultSystemAppearance localAppearance(view ? view->useDarkAppearance() : false);
 #endif
 
     NSArray *subresources = nil;
@@ -208,9 +221,20 @@ static bool shouldReplaceRichContentWithAttachments()
 
 #if ENABLE(ATTACHMENT_ELEMENT)
 
+static String mimeTypeFromContentType(const String& contentType)
+{
+    if (contentType == String(kUTTypeVCard)) {
+        // CoreServices erroneously reports that "public.vcard" maps to "text/directory", rather
+        // than either "text/vcard" or "text/x-vcard". Work around this by special casing the
+        // "public.vcard" UTI type. See <rdar://problem/49478229> for more detail.
+        return "text/vcard"_s;
+    }
+    return isDeclaredUTI(contentType) ? MIMETypeFromUTI(contentType) : contentType;
+}
+
 static bool contentTypeIsSuitableForInlineImageRepresentation(const String& contentType)
 {
-    return MIMETypeRegistry::isSupportedImageMIMEType(isDeclaredUTI(contentType) ? MIMETypeFromUTI(contentType) : contentType);
+    return MIMETypeRegistry::isSupportedImageMIMEType(mimeTypeFromContentType(contentType));
 }
 
 static bool supportsClientSideAttachmentData(const Frame& frame)
@@ -275,6 +299,20 @@ static void replaceRichContentWithAttachments(Frame& frame, DocumentFragment& fr
             urlToResourceMap.set(url.string(), subresource.copyRef());
     }
 
+    Vector<SerializedAttachmentData> serializedAttachmentData;
+    for (auto& attachment : descendantsOfType<HTMLAttachmentElement>(fragment)) {
+        auto resourceURL = HTMLAttachmentElement::archiveResourceURL(attachment.uniqueIdentifier());
+        auto resourceEntry = urlToResourceMap.find(resourceURL.string());
+        if (resourceEntry == urlToResourceMap.end())
+            continue;
+
+        auto& resource = resourceEntry->value;
+        serializedAttachmentData.append({ attachment.uniqueIdentifier(), resource->mimeType(), resource->data() });
+    }
+
+    if (!serializedAttachmentData.isEmpty())
+        frame.editor().registerAttachments(WTFMove(serializedAttachmentData));
+
     Vector<Ref<Element>> elementsToRemove;
     Vector<AttachmentInsertionInfo> attachmentInsertionInfo;
     for (auto& image : descendantsOfType<HTMLImageElement>(fragment)) {
@@ -286,7 +324,9 @@ static void replaceRichContentWithAttachments(Frame& frame, DocumentFragment& fr
         if (resource == urlToResourceMap.end())
             continue;
 
-        auto name = URLParser { resourceURLString }.result().lastPathComponent();
+        auto name = image.attributeWithoutSynchronization(HTMLNames::altAttr);
+        if (name.isEmpty())
+            name = URL({ }, resourceURLString).lastPathComponent();
         if (name.isEmpty())
             name = AtomicString("media");
 
@@ -304,7 +344,7 @@ static void replaceRichContentWithAttachments(Frame& frame, DocumentFragment& fr
         if (resource == urlToResourceMap.end())
             continue;
 
-        auto name = URLParser { resourceURLString }.result().lastPathComponent();
+        auto name = URL({ }, resourceURLString).lastPathComponent();
         if (name.isEmpty())
             name = AtomicString("file");
 
@@ -322,7 +362,7 @@ static void replaceRichContentWithAttachments(Frame& frame, DocumentFragment& fr
             if (is<HTMLImageElement>(originalElement.get()) && contentTypeIsSuitableForInlineImageRepresentation(info.contentType)) {
                 auto& image = downcast<HTMLImageElement>(originalElement.get());
                 image.setAttributeWithoutSynchronization(HTMLNames::srcAttr, DOMURL::createObjectURL(*frame.document(), Blob::create(info.data, info.contentType)));
-                image.setAttachmentElement(WTFMove(attachment));
+                image.setAttachmentElement(attachment.copyRef());
             } else {
                 attachment->updateAttributes(info.data->size(), info.contentType, info.fileName);
                 parent->replaceChild(attachment, WTFMove(originalElement));
@@ -342,31 +382,6 @@ static void replaceRichContentWithAttachments(Frame& frame, DocumentFragment& fr
 #endif
 }
 
-static void replaceSubresourceURLsWithURLsFromClient(DocumentFragment& fragment, const Vector<Ref<ArchiveResource>>& subresources, Vector<Ref<ArchiveResource>>& outUnreplacedResources)
-{
-    ASSERT(fragment.document().frame());
-    auto& frame = *fragment.document().frame();
-    HashMap<AtomicString, AtomicString> subresourceURLToClientURLMap;
-    for (auto& subresource : subresources) {
-        auto& originalURL = subresource->url();
-        if (!shouldReplaceSubresourceURL(originalURL)) {
-            outUnreplacedResources.append(subresource.copyRef());
-            continue;
-        }
-
-        auto replacementURL = frame.editor().clientReplacementURLForResource(subresource->data(), subresource->mimeType());
-        if (replacementURL.isEmpty()) {
-            outUnreplacedResources.append(subresource.copyRef());
-            continue;
-        }
-
-        subresourceURLToClientURLMap.set(originalURL.string(), replacementURL);
-    }
-
-    if (!subresourceURLToClientURLMap.isEmpty())
-        replaceSubresourceURLs(fragment, WTFMove(subresourceURLToClientURLMap));
-}
-
 RefPtr<DocumentFragment> createFragmentAndAddResources(Frame& frame, NSAttributedString *string)
 {
     if (!frame.page() || !frame.document())
@@ -389,16 +404,13 @@ RefPtr<DocumentFragment> createFragmentAndAddResources(Frame& frame, NSAttribute
         return WTFMove(fragmentAndResources.fragment);
     }
 
-    Vector<Ref<ArchiveResource>> unreplacedResources;
-    replaceSubresourceURLsWithURLsFromClient(*fragmentAndResources.fragment, fragmentAndResources.resources, unreplacedResources);
-
     if (shouldReplaceRichContentWithAttachments()) {
-        replaceRichContentWithAttachments(frame, *fragmentAndResources.fragment, unreplacedResources);
+        replaceRichContentWithAttachments(frame, *fragmentAndResources.fragment, fragmentAndResources.resources);
         return WTFMove(fragmentAndResources.fragment);
     }
 
     HashMap<AtomicString, AtomicString> blobURLMap;
-    for (const Ref<ArchiveResource>& subresource : unreplacedResources) {
+    for (const Ref<ArchiveResource>& subresource : fragmentAndResources.resources) {
         auto blob = Blob::create(subresource->data(), subresource->mimeType());
         String blobURL = DOMURL::createObjectURL(document, blob);
         blobURLMap.set(subresource->url().string(), blobURL);
@@ -414,19 +426,19 @@ struct MarkupAndArchive {
     Ref<Archive> archive;
 };
 
-static std::optional<MarkupAndArchive> extractMarkupAndArchive(SharedBuffer& buffer, const std::function<bool(const String)>& canShowMIMETypeAsHTML)
+static Optional<MarkupAndArchive> extractMarkupAndArchive(SharedBuffer& buffer, const std::function<bool(const String)>& canShowMIMETypeAsHTML)
 {
     auto archive = LegacyWebArchive::create(URL(), buffer);
     if (!archive)
-        return std::nullopt;
+        return WTF::nullopt;
 
     RefPtr<ArchiveResource> mainResource = archive->mainResource();
     if (!mainResource)
-        return std::nullopt;
+        return WTF::nullopt;
 
     auto type = mainResource->mimeType();
     if (!canShowMIMETypeAsHTML(type))
-        return std::nullopt;
+        return WTF::nullopt;
 
     return MarkupAndArchive { String::fromUTF8(mainResource->data().data(), mainResource->data().size()), mainResource.releaseNonNull(), archive.releaseNonNull() };
 }
@@ -438,16 +450,13 @@ static String sanitizeMarkupWithArchive(Frame& frame, Document& destinationDocum
     ASSERT(stagingDocument);
     auto fragment = createFragmentFromMarkup(*stagingDocument, markupAndArchive.markup, markupAndArchive.mainResource->url(), DisallowScriptingAndPluginContent);
 
-    Vector<Ref<ArchiveResource>> unreplacedResources;
-    replaceSubresourceURLsWithURLsFromClient(fragment, markupAndArchive.archive->subresources(), unreplacedResources);
-
     if (shouldReplaceRichContentWithAttachments()) {
-        replaceRichContentWithAttachments(frame, fragment, unreplacedResources);
+        replaceRichContentWithAttachments(frame, fragment, markupAndArchive.archive->subresources());
         return sanitizedMarkupForFragmentInDocument(WTFMove(fragment), *stagingDocument, msoListQuirks, markupAndArchive.markup);
     }
 
     HashMap<AtomicString, AtomicString> blobURLMap;
-    for (const Ref<ArchiveResource>& subresource : unreplacedResources) {
+    for (const Ref<ArchiveResource>& subresource : markupAndArchive.archive->subresources()) {
         auto& subresourceURL = subresource->url();
         if (!shouldReplaceSubresourceURL(subresourceURL))
             continue;
@@ -516,7 +525,7 @@ bool WebContentReader::readWebArchive(SharedBuffer& buffer)
     String sanitizedMarkup = sanitizeMarkupWithArchive(frame, *frame.document(), *result, msoListQuirksForMarkup(), [&] (const String& type) {
         return frame.loader().client().canShowMIMETypeAsHTML(type);
     });
-    fragment = createFragmentFromMarkup(*frame.document(), sanitizedMarkup, blankURL(), DisallowScriptingAndPluginContent);
+    fragment = createFragmentFromMarkup(*frame.document(), sanitizedMarkup, WTF::blankURL(), DisallowScriptingAndPluginContent);
 
     if (!fragment)
         return false;
@@ -627,7 +636,7 @@ bool WebContentMarkupReader::readRTFD(SharedBuffer& buffer)
     if (!fragment)
         return false;
 
-    markup = createMarkup(*fragment);
+    markup = serializeFragment(*fragment, SerializedNodes::SubtreeIncludingNode);
     return true;
 }
 
@@ -653,7 +662,7 @@ bool WebContentMarkupReader::readRTF(SharedBuffer& buffer)
     auto fragment = createFragmentAndAddResources(frame, string.get());
     if (!fragment)
         return false;
-    markup = createMarkup(*fragment);
+    markup = serializeFragment(*fragment, SerializedNodes::SubtreeIncludingNode);
     return true;
 }
 
@@ -672,13 +681,6 @@ bool WebContentReader::readImage(Ref<SharedBuffer>&& buffer, const String& type)
 {
     ASSERT(frame.document());
     auto& document = *frame.document();
-
-    auto replacementURL = frame.editor().clientReplacementURLForResource(buffer.copyRef(), isDeclaredUTI(type) ? MIMETypeFromUTI(type) : type);
-    if (!replacementURL.isEmpty()) {
-        addFragment(createFragmentForImageAndURL(document, replacementURL));
-        return true;
-    }
-
     if (shouldReplaceRichContentWithAttachments())
         addFragment(createFragmentForImageAttachment(frame, document, WTFMove(buffer), type));
     else
@@ -687,6 +689,79 @@ bool WebContentReader::readImage(Ref<SharedBuffer>&& buffer, const String& type)
     return fragment;
 }
 
+#if ENABLE(ATTACHMENT_ELEMENT)
+
+static Ref<HTMLElement> attachmentForFilePath(Frame& frame, const String& path)
+{
+    auto document = makeRef(*frame.document());
+    auto attachment = HTMLAttachmentElement::create(HTMLNames::attachmentTag, document);
+    if (!supportsClientSideAttachmentData(frame)) {
+        attachment->setFile(File::create(path), HTMLAttachmentElement::UpdateDisplayAttributes::Yes);
+        return attachment;
+    }
+
+    String contentType;
+    Optional<uint64_t> fileSizeForDisplay;
+    if (FileSystem::fileIsDirectory(path, FileSystem::ShouldFollowSymbolicLinks::Yes))
+        contentType = kUTTypeDirectory;
+    else {
+        long long fileSize;
+        FileSystem::getFileSize(path, fileSize);
+        fileSizeForDisplay = fileSize;
+        contentType = File::contentTypeForFile(path);
+        if (contentType.isEmpty())
+            contentType = kUTTypeData;
+    }
+
+    frame.editor().registerAttachmentIdentifier(attachment->ensureUniqueIdentifier(), contentType, path);
+
+    if (contentTypeIsSuitableForInlineImageRepresentation(contentType)) {
+        auto image = HTMLImageElement::create(document);
+        image->setAttributeWithoutSynchronization(HTMLNames::srcAttr, DOMURL::createObjectURL(document, File::create(path)));
+        image->setAttachmentElement(WTFMove(attachment));
+        return image;
+    }
+
+    attachment->updateAttributes(WTFMove(fileSizeForDisplay), contentType, FileSystem::pathGetFileName(path));
+    return attachment;
+}
+
+static Ref<HTMLElement> attachmentForData(Frame& frame, SharedBuffer& buffer, const String& contentType, const String& name)
+{
+    auto document = makeRef(*frame.document());
+    auto attachment = HTMLAttachmentElement::create(HTMLNames::attachmentTag, document);
+    auto mimeType = mimeTypeFromContentType(contentType);
+    auto typeForAttachmentElement = mimeType.isEmpty() ? contentType : mimeType;
+
+    // FIXME: We should instead ask CoreServices for a preferred name corresponding to the given content type.
+    static const char* defaultAttachmentName = "file";
+
+    String fileName;
+    if (name.isEmpty())
+        fileName = defaultAttachmentName;
+    else
+        fileName = name;
+
+    if (!supportsClientSideAttachmentData(frame)) {
+        attachment->setFile(File::create(Blob::create(buffer, WTFMove(typeForAttachmentElement)), fileName));
+        return attachment;
+    }
+
+    frame.editor().registerAttachmentIdentifier(attachment->ensureUniqueIdentifier(), typeForAttachmentElement, fileName, buffer);
+
+    if (contentTypeIsSuitableForInlineImageRepresentation(typeForAttachmentElement)) {
+        auto image = HTMLImageElement::create(document);
+        image->setAttributeWithoutSynchronization(HTMLNames::srcAttr, DOMURL::createObjectURL(document, File::create(Blob::create(buffer, WTFMove(typeForAttachmentElement)), WTFMove(fileName))));
+        image->setAttachmentElement(WTFMove(attachment));
+        return image;
+    }
+
+    attachment->updateAttributes({ buffer.size() }, WTFMove(typeForAttachmentElement), WTFMove(fileName));
+    return attachment;
+}
+
+#endif // ENABLE(ATTACHMENT_ELEMENT)
+
 bool WebContentReader::readFilePaths(const Vector<String>& paths)
 {
     if (paths.isEmpty() || !frame.document())
@@ -698,27 +773,8 @@ bool WebContentReader::readFilePaths(const Vector<String>& paths)
 
 #if ENABLE(ATTACHMENT_ELEMENT)
     if (RuntimeEnabledFeatures::sharedFeatures().attachmentElementEnabled()) {
-        for (auto& path : paths) {
-            auto attachment = HTMLAttachmentElement::create(HTMLNames::attachmentTag, document);
-            if (supportsClientSideAttachmentData(frame)) {
-                long long fileSize { 0 };
-                FileSystem::getFileSize(path, fileSize);
-                auto contentType = File::contentTypeForFile(path);
-                frame.editor().registerAttachmentIdentifier(attachment->ensureUniqueIdentifier(), contentType, path);
-                if (contentTypeIsSuitableForInlineImageRepresentation(contentType)) {
-                    auto image = HTMLImageElement::create(document);
-                    image->setAttributeWithoutSynchronization(HTMLNames::srcAttr, DOMURL::createObjectURL(document, File::create(path)));
-                    image->setAttachmentElement(WTFMove(attachment));
-                    fragment->appendChild(image);
-                } else {
-                    attachment->updateAttributes(fileSize, contentType, FileSystem::pathGetFileName(path));
-                    fragment->appendChild(attachment);
-                }
-            } else {
-                attachment->setFile(File::create(path), HTMLAttachmentElement::UpdateDisplayAttributes::Yes);
-                fragment->appendChild(attachment);
-            }
-        }
+        for (auto& path : paths)
+            fragment->appendChild(attachmentForFilePath(frame, path));
     }
 #endif
 
@@ -730,7 +786,7 @@ bool WebContentReader::readURL(const URL& url, const String& title)
     if (url.isEmpty())
         return false;
 
-#if PLATFORM(IOS)
+#if PLATFORM(IOS_FAMILY)
     // FIXME: This code shouldn't be accessing selection and changing the behavior.
     if (!frame.editor().client()->hasRichlyEditableSelection()) {
         if (readPlainText([(NSURL *)url absoluteString]))
@@ -739,7 +795,7 @@ bool WebContentReader::readURL(const URL& url, const String& title)
 
     if ([(NSURL *)url isFileURL])
         return false;
-#endif // PLATFORM(IOS)
+#endif // PLATFORM(IOS_FAMILY)
 
     auto document = makeRef(*frame.document());
     auto anchor = HTMLAnchorElement::create(document.get());
@@ -756,4 +812,28 @@ bool WebContentReader::readURL(const URL& url, const String& title)
     return true;
 }
 
+bool WebContentReader::readDataBuffer(SharedBuffer& buffer, const String& type, const String& name)
+{
+    if (buffer.isEmpty())
+        return false;
+
+    if (!shouldReplaceRichContentWithAttachments())
+        return false;
+
+    auto document = makeRefPtr(frame.document());
+    if (!document)
+        return false;
+
+    if (!fragment)
+        fragment = document->createDocumentFragment();
+
+#if ENABLE(ATTACHMENT_ELEMENT)
+    fragment->appendChild(attachmentForData(frame, buffer, type, name));
+#else
+    UNUSED_PARAM(type);
+    UNUSED_PARAM(name);
+#endif
+    return true;
+}
+
 }