Add injected bundle SPI to replace subresource URLs when dropping or pasting rich...
[WebKit-https.git] / Source / WebCore / editing / cocoa / WebContentReaderCocoa.mm
1 /*
2  * Copyright (C) 2006-2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
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.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "WebContentReader.h"
28
29 #import "ArchiveResource.h"
30 #import "Blob.h"
31 #import "BlobURL.h"
32 #import "CachedResourceLoader.h"
33 #import "DOMURL.h"
34 #import "DeprecatedGlobalSettings.h"
35 #import "Document.h"
36 #import "DocumentFragment.h"
37 #import "DocumentLoader.h"
38 #import "File.h"
39 #import "Frame.h"
40 #import "FrameLoader.h"
41 #import "FrameLoaderClient.h"
42 #import "HTMLAttachmentElement.h"
43 #import "HTMLBodyElement.h"
44 #import "HTMLIFrameElement.h"
45 #import "HTMLImageElement.h"
46 #import "HTMLObjectElement.h"
47 #import "LegacyWebArchive.h"
48 #import "MainFrame.h"
49 #import "Page.h"
50 #import "PublicURLManager.h"
51 #import "RuntimeEnabledFeatures.h"
52 #import "Settings.h"
53 #import "SocketProvider.h"
54 #import "TypedElementDescendantIterator.h"
55 #import "URLParser.h"
56 #import "WebArchiveResourceFromNSAttributedString.h"
57 #import "WebArchiveResourceWebResourceHandler.h"
58 #import "WebNSAttributedStringExtras.h"
59 #import "markup.h"
60 #import <pal/spi/cocoa/NSAttributedStringSPI.h>
61 #import <wtf/SoftLinking.h>
62
63 #if (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300)
64 @interface NSAttributedString ()
65 - (NSString *)_htmlDocumentFragmentString:(NSRange)range documentAttributes:(NSDictionary *)dict subresources:(NSArray **)subresources;
66 @end
67 #elif PLATFORM(IOS)
68 SOFT_LINK_PRIVATE_FRAMEWORK(WebKitLegacy)
69 #elif PLATFORM(MAC)
70 SOFT_LINK_FRAMEWORK_IN_UMBRELLA(WebKit, WebKitLegacy)
71 #endif
72
73 #if (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED < 110000) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101300)
74 SOFT_LINK(WebKitLegacy, _WebCreateFragment, void, (WebCore::Document& document, NSAttributedString *string, WebCore::FragmentAndResources& result), (document, string, result))
75 #endif
76
77 namespace WebCore {
78
79 #if (PLATFORM(IOS) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000) || (PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300)
80
81 static NSDictionary *attributesForAttributedStringConversion()
82 {
83     // This function needs to be kept in sync with identically named one in WebKitLegacy, which is used on older OS versions.
84     RetainPtr<NSMutableArray> excludedElements = adoptNS([[NSMutableArray alloc] initWithObjects:
85         // Omit style since we want style to be inline so the fragment can be easily inserted.
86         @"style",
87         // Omit xml so the result is not XHTML.
88         @"xml",
89         // Omit tags that will get stripped when converted to a fragment anyway.
90         @"doctype", @"html", @"head", @"body",
91         // Omit deprecated tags.
92         @"applet", @"basefont", @"center", @"dir", @"font", @"menu", @"s", @"strike", @"u",
93 #if !ENABLE(ATTACHMENT_ELEMENT)
94         // Omit object so no file attachments are part of the fragment.
95         @"object",
96 #endif
97         nil]);
98
99 #if ENABLE(ATTACHMENT_ELEMENT)
100     if (!RuntimeEnabledFeatures::sharedFeatures().attachmentElementEnabled())
101         [excludedElements addObject:@"object"];
102 #endif
103
104 #if PLATFORM(IOS)
105     static NSString * const NSExcludedElementsDocumentAttribute = @"ExcludedElements";
106 #endif
107
108     return @{
109         NSExcludedElementsDocumentAttribute: excludedElements.get(),
110         @"InterchangeNewline": @YES,
111         @"CoalesceTabSpans": @YES,
112         @"OutputBaseURL": [(NSURL *)URL::fakeURLWithRelativePart(emptyString()) retain], // The value needs +1 refcount, as NSAttributedString over-releases it.
113         @"WebResourceHandler": [[WebArchiveResourceWebResourceHandler new] autorelease],
114     };
115 }
116
117 static FragmentAndResources createFragment(Frame& frame, NSAttributedString *string)
118 {
119     FragmentAndResources result;
120     Document& document = *frame.document();
121
122     NSArray *subresources = nil;
123     NSString *fragmentString = [string _htmlDocumentFragmentString:NSMakeRange(0, [string length]) documentAttributes:attributesForAttributedStringConversion() subresources:&subresources];
124     auto fragment = DocumentFragment::create(document);
125     fragment->parseHTML(fragmentString, document.body(), DisallowScriptingAndPluginContent);
126
127     result.fragment = WTFMove(fragment);
128     for (WebArchiveResourceFromNSAttributedString *resource in subresources)
129         result.resources.append(*resource->resource);
130
131     return result;
132 }
133
134 #else
135
136 static FragmentAndResources createFragment(Frame& frame, NSAttributedString *string)
137 {
138     FragmentAndResources result;
139     _WebCreateFragment(*frame.document(), string, result);
140     return result;
141 }
142
143 #endif
144
145 class DeferredLoadingScope {
146 public:
147     DeferredLoadingScope(Frame& frame)
148         : m_frame(frame)
149         , m_cachedResourceLoader(frame.document()->cachedResourceLoader())
150     {
151         if (!frame.page()->defersLoading()) {
152             frame.page()->setDefersLoading(true);
153             m_didEnabledDeferredLoading = true;
154         }
155
156         if (m_cachedResourceLoader->imagesEnabled()) {
157             m_cachedResourceLoader->setImagesEnabled(false);
158             m_didDisableImage = true;
159         }
160     }
161
162     ~DeferredLoadingScope()
163     {
164         if (m_didDisableImage)
165             m_cachedResourceLoader->setImagesEnabled(true);
166         if (m_didEnabledDeferredLoading)
167             m_frame->page()->setDefersLoading(false);
168     }
169
170 private:
171     Ref<Frame> m_frame;
172     Ref<CachedResourceLoader> m_cachedResourceLoader;
173     bool m_didEnabledDeferredLoading { false };
174     bool m_didDisableImage { false };
175 };
176
177
178 static bool shouldReplaceSubresourceURL(const URL& url)
179 {
180     return !(url.protocolIsInHTTPFamily() || url.protocolIsData());
181 }
182
183 static bool shouldReplaceRichContentWithAttachments()
184 {
185 #if ENABLE(ATTACHMENT_ELEMENT)
186     return RuntimeEnabledFeatures::sharedFeatures().attachmentElementEnabled();
187 #else
188     return false;
189 #endif
190 }
191
192 static Ref<DocumentFragment> createFragmentForImageAttachment(Document& document, Ref<Blob>&& blob)
193 {
194 #if ENABLE(ATTACHMENT_ELEMENT)
195     auto attachment = HTMLAttachmentElement::create(HTMLNames::attachmentTag, document);
196     attachment->setFile(File::create(blob, AtomicString("image")), HTMLAttachmentElement::UpdateDisplayAttributes::Yes);
197     attachment->updateDisplayMode(AttachmentDisplayMode::InPlace);
198
199     auto fragment = document.createDocumentFragment();
200     fragment->appendChild(attachment);
201
202     return fragment;
203 #else
204     UNUSED_PARAM(blob);
205     return document.createDocumentFragment();
206 #endif
207 }
208
209 static void replaceRichContentWithAttachments(DocumentFragment& fragment, const Vector<Ref<ArchiveResource>>& subresources)
210 {
211 #if ENABLE(ATTACHMENT_ELEMENT)
212     struct AttachmentReplacementInfo {
213         AttachmentDisplayMode displayMode;
214         Ref<File> file;
215         Ref<Element> elementToReplace;
216     };
217
218     ASSERT(RuntimeEnabledFeatures::sharedFeatures().attachmentElementEnabled());
219     if (subresources.isEmpty())
220         return;
221
222     // FIXME: Handle resources in subframe archives.
223     HashMap<AtomicString, Ref<Blob>> urlToBlobMap;
224     for (const Ref<ArchiveResource>& subresource : subresources) {
225         auto& url = subresource->url();
226         if (shouldReplaceSubresourceURL(url))
227             urlToBlobMap.set(url.string(), Blob::create(subresource->data(), subresource->mimeType()));
228     }
229
230     Vector<Ref<Element>> elementsToRemove;
231     Vector<AttachmentReplacementInfo> attachmentReplacementInfo;
232     for (auto& image : descendantsOfType<HTMLImageElement>(fragment)) {
233         auto resourceURLString = image.attributeWithoutSynchronization(HTMLNames::srcAttr);
234         if (resourceURLString.isEmpty())
235             continue;
236
237         auto blob = urlToBlobMap.get(resourceURLString);
238         if (!blob)
239             continue;
240
241         auto title = URLParser { resourceURLString }.result().lastPathComponent();
242         if (title.isEmpty())
243             title = AtomicString("media");
244
245         attachmentReplacementInfo.append({ AttachmentDisplayMode::InPlace, File::create(*blob, title), image });
246     }
247
248     for (auto& object : descendantsOfType<HTMLObjectElement>(fragment)) {
249         auto resourceURLString = object.attributeWithoutSynchronization(HTMLNames::dataAttr);
250         if (resourceURLString.isEmpty()) {
251             elementsToRemove.append(object);
252             continue;
253         }
254
255         auto blob = urlToBlobMap.get(resourceURLString);
256         if (!blob) {
257             elementsToRemove.append(object);
258             continue;
259         }
260
261         auto title = URLParser { resourceURLString }.result().lastPathComponent();
262         if (title.isEmpty())
263             title = AtomicString("file");
264
265         attachmentReplacementInfo.append({ AttachmentDisplayMode::AsIcon, File::create(*blob, title), object });
266     }
267
268     for (auto& info : attachmentReplacementInfo) {
269         auto file = WTFMove(info.file);
270         auto elementToReplace = WTFMove(info.elementToReplace);
271         auto parent = makeRefPtr(elementToReplace->parentNode());
272         if (!parent)
273             continue;
274
275         auto attachment = HTMLAttachmentElement::create(HTMLNames::attachmentTag, fragment.document());
276         attachment->setFile(WTFMove(file), HTMLAttachmentElement::UpdateDisplayAttributes::Yes);
277         attachment->updateDisplayMode(info.displayMode);
278         parent->replaceChild(attachment, elementToReplace);
279     }
280
281     for (auto& elementToRemove : elementsToRemove)
282         elementToRemove->remove();
283 #else
284     UNUSED_PARAM(fragment);
285     UNUSED_PARAM(subresources);
286 #endif
287 }
288
289 static void replaceSubresourceURLsWithURLsFromClient(DocumentFragment& fragment, const Vector<Ref<ArchiveResource>>& subresources, Vector<Ref<ArchiveResource>>& outUnreplacedResources)
290 {
291     ASSERT(fragment.document().frame());
292     auto& frame = *fragment.document().frame();
293     HashMap<AtomicString, AtomicString> subresourceURLToClientURLMap;
294     for (auto& subresource : subresources) {
295         auto& originalURL = subresource->url();
296         if (!shouldReplaceSubresourceURL(originalURL)) {
297             outUnreplacedResources.append(subresource.copyRef());
298             continue;
299         }
300
301         auto replacementURL = frame.editor().clientReplacementURLForResource(subresource->data(), subresource->mimeType());
302         if (replacementURL.isEmpty()) {
303             outUnreplacedResources.append(subresource.copyRef());
304             continue;
305         }
306
307         subresourceURLToClientURLMap.set(originalURL.string(), replacementURL);
308     }
309
310     if (!subresourceURLToClientURLMap.isEmpty())
311         replaceSubresourceURLs(fragment, WTFMove(subresourceURLToClientURLMap));
312 }
313
314 RefPtr<DocumentFragment> createFragmentAndAddResources(Frame& frame, NSAttributedString *string)
315 {
316     if (!frame.page() || !frame.document())
317         return nullptr;
318
319     auto& document = *frame.document();
320     if (!document.isHTMLDocument() || !string)
321         return nullptr;
322
323     DeferredLoadingScope scope(frame);
324     auto fragmentAndResources = createFragment(frame, string);
325     if (!fragmentAndResources.fragment)
326         return nullptr;
327
328     if (!DeprecatedGlobalSettings::customPasteboardDataEnabled()) {
329         if (DocumentLoader* loader = frame.loader().documentLoader()) {
330             for (auto& resource : fragmentAndResources.resources)
331                 loader->addArchiveResource(resource.copyRef());
332         }
333         return WTFMove(fragmentAndResources.fragment);
334     }
335
336     Vector<Ref<ArchiveResource>> unreplacedResources;
337     replaceSubresourceURLsWithURLsFromClient(*fragmentAndResources.fragment, fragmentAndResources.resources, unreplacedResources);
338
339     if (shouldReplaceRichContentWithAttachments()) {
340         replaceRichContentWithAttachments(*fragmentAndResources.fragment, unreplacedResources);
341         return WTFMove(fragmentAndResources.fragment);
342     }
343
344     HashMap<AtomicString, AtomicString> blobURLMap;
345     for (const Ref<ArchiveResource>& subresource : unreplacedResources) {
346         auto blob = Blob::create(subresource->data(), subresource->mimeType());
347         String blobURL = DOMURL::createObjectURL(document, blob);
348         blobURLMap.set(subresource->url().string(), blobURL);
349     }
350
351     replaceSubresourceURLs(*fragmentAndResources.fragment, WTFMove(blobURLMap));
352     return WTFMove(fragmentAndResources.fragment);
353 }
354
355 struct MarkupAndArchive {
356     String markup;
357     Ref<ArchiveResource> mainResource;
358     Ref<Archive> archive;
359 };
360
361 static std::optional<MarkupAndArchive> extractMarkupAndArchive(SharedBuffer& buffer, const std::function<bool(const String)>& canShowMIMETypeAsHTML)
362 {
363     auto archive = LegacyWebArchive::create(URL(), buffer);
364     if (!archive)
365         return std::nullopt;
366
367     RefPtr<ArchiveResource> mainResource = archive->mainResource();
368     if (!mainResource)
369         return std::nullopt;
370
371     auto type = mainResource->mimeType();
372     if (!canShowMIMETypeAsHTML(type))
373         return std::nullopt;
374
375     return MarkupAndArchive { String::fromUTF8(mainResource->data().data(), mainResource->data().size()), mainResource.releaseNonNull(), archive.releaseNonNull() };
376 }
377
378 static String markupForFragmentInDocument(Ref<DocumentFragment>&& fragment, Document& document)
379 {
380     auto* bodyElement = document.body();
381     ASSERT(bodyElement);
382     bodyElement->appendChild(WTFMove(fragment));
383
384     auto range = Range::create(document);
385     range->selectNodeContents(*bodyElement);
386     return createMarkup(range.get(), nullptr, AnnotateForInterchange, false, ResolveNonLocalURLs);
387 }
388
389 static String sanitizeMarkupWithArchive(Document& destinationDocument, MarkupAndArchive& markupAndArchive, const std::function<bool(const String)>& canShowMIMETypeAsHTML)
390 {
391     auto page = createPageForSanitizingWebContent();
392     Document* stagingDocument = page->mainFrame().document();
393     ASSERT(stagingDocument);
394     auto fragment = createFragmentFromMarkup(*stagingDocument, markupAndArchive.markup, markupAndArchive.mainResource->url(), DisallowScriptingAndPluginContent);
395
396     Vector<Ref<ArchiveResource>> unreplacedResources;
397     replaceSubresourceURLsWithURLsFromClient(fragment, markupAndArchive.archive->subresources(), unreplacedResources);
398
399     if (shouldReplaceRichContentWithAttachments()) {
400         replaceRichContentWithAttachments(fragment, unreplacedResources);
401         return markupForFragmentInDocument(WTFMove(fragment), *stagingDocument);
402     }
403
404     HashMap<AtomicString, AtomicString> blobURLMap;
405     for (const Ref<ArchiveResource>& subresource : unreplacedResources) {
406         auto& subresourceURL = subresource->url();
407         if (!shouldReplaceSubresourceURL(subresourceURL))
408             continue;
409         auto blob = Blob::create(subresource->data(), subresource->mimeType());
410         String blobURL = DOMURL::createObjectURL(destinationDocument, blob);
411         blobURLMap.set(subresourceURL.string(), blobURL);
412     }
413
414     auto contentOrigin = SecurityOrigin::create(markupAndArchive.mainResource->url());
415     for (const Ref<Archive>& subframeArchive : markupAndArchive.archive->subframeArchives()) {
416         RefPtr<ArchiveResource> subframeMainResource = subframeArchive->mainResource();
417         if (!subframeMainResource)
418             continue;
419
420         auto type = subframeMainResource->mimeType();
421         if (!canShowMIMETypeAsHTML(type))
422             continue;
423
424         auto subframeURL = subframeMainResource->url();
425         if (!shouldReplaceSubresourceURL(subframeURL))
426             continue;
427
428         MarkupAndArchive subframeContent = { String::fromUTF8(subframeMainResource->data().data(), subframeMainResource->data().size()),
429             subframeMainResource.releaseNonNull(), subframeArchive.copyRef() };
430         auto subframeMarkup = sanitizeMarkupWithArchive(destinationDocument, subframeContent, canShowMIMETypeAsHTML);
431
432         CString utf8 = subframeMarkup.utf8();
433         Vector<uint8_t> blobBuffer;
434         blobBuffer.reserveCapacity(utf8.length());
435         blobBuffer.append(reinterpret_cast<const uint8_t*>(utf8.data()), utf8.length());
436         auto blob = Blob::create(WTFMove(blobBuffer), type);
437
438         String subframeBlobURL = DOMURL::createObjectURL(destinationDocument, blob);
439         blobURLMap.set(subframeURL.string(), subframeBlobURL);
440     }
441
442     replaceSubresourceURLs(fragment.get(), WTFMove(blobURLMap));
443
444     return markupForFragmentInDocument(WTFMove(fragment), *stagingDocument);
445 }
446
447 bool WebContentReader::readWebArchive(SharedBuffer& buffer)
448 {
449     if (frame.settings().preferMIMETypeForImages() || !frame.document())
450         return false;
451
452     DeferredLoadingScope scope(frame);
453     auto result = extractMarkupAndArchive(buffer, [&] (const String& type) {
454         return frame.loader().client().canShowMIMETypeAsHTML(type);
455     });
456     if (!result)
457         return false;
458     
459     if (!DeprecatedGlobalSettings::customPasteboardDataEnabled()) {
460         fragment = createFragmentFromMarkup(*frame.document(), result->markup, result->mainResource->url(), DisallowScriptingAndPluginContent);
461         if (DocumentLoader* loader = frame.loader().documentLoader())
462             loader->addAllArchiveResources(result->archive.get());
463         return true;
464     }
465
466     if (!shouldSanitize()) {
467         fragment = createFragmentFromMarkup(*frame.document(), result->markup, result->mainResource->url(), DisallowScriptingAndPluginContent);
468         return true;
469     }
470
471     String sanitizedMarkup = sanitizeMarkupWithArchive(*frame.document(), *result, [&] (const String& type) {
472         return frame.loader().client().canShowMIMETypeAsHTML(type);
473     });
474     fragment = createFragmentFromMarkup(*frame.document(), sanitizedMarkup, blankURL(), DisallowScriptingAndPluginContent);
475
476     if (!fragment)
477         return false;
478
479     return true;
480 }
481
482 bool WebContentMarkupReader::readWebArchive(SharedBuffer& buffer)
483 {
484     if (!frame.document())
485         return false;
486
487     auto result = extractMarkupAndArchive(buffer, [&] (const String& type) {
488         return frame.loader().client().canShowMIMETypeAsHTML(type);
489     });
490     if (!result)
491         return false;
492
493     if (!shouldSanitize()) {
494         markup = result->markup;
495         return true;
496     }
497
498     markup = sanitizeMarkupWithArchive(*frame.document(), *result, [&] (const String& type) {
499         return frame.loader().client().canShowMIMETypeAsHTML(type);
500     });
501
502     return true;
503 }
504
505 static String stripMicrosoftPrefix(const String& string)
506 {
507 #if PLATFORM(MAC)
508     // This code was added to make HTML paste from Microsoft Word on Mac work, back in 2004.
509     // It's a simple-minded way to ignore the CF_HTML clipboard format, just skipping over the
510     // description part and parsing the entire context plus fragment.
511     if (string.startsWith("Version:")) {
512         size_t location = string.findIgnoringASCIICase("<html");
513         if (location != notFound)
514             return string.substring(location);
515     }
516 #endif
517     return string;
518 }
519
520 bool WebContentReader::readHTML(const String& string)
521 {
522     if (frame.settings().preferMIMETypeForImages() || !frame.document())
523         return false;
524     Document& document = *frame.document();
525
526     String stringOmittingMicrosoftPrefix = stripMicrosoftPrefix(string);
527     if (stringOmittingMicrosoftPrefix.isEmpty())
528         return false;
529
530     addFragment(createFragmentFromMarkup(document, stringOmittingMicrosoftPrefix, emptyString(), DisallowScriptingAndPluginContent));
531     return true;
532 }
533
534 bool WebContentMarkupReader::readHTML(const String& string)
535 {
536     if (!frame.document())
537         return false;
538
539     String rawHTML = stripMicrosoftPrefix(string);
540     if (shouldSanitize())
541         markup = sanitizeMarkup(rawHTML);
542     else
543         markup = rawHTML;
544
545     return !markup.isEmpty();
546 }
547
548 bool WebContentReader::readRTFD(SharedBuffer& buffer)
549 {
550     if (frame.settings().preferMIMETypeForImages() || !frame.document())
551         return false;
552
553     auto string = adoptNS([[NSAttributedString alloc] initWithRTFD:buffer.createNSData().get() documentAttributes:nullptr]);
554     auto fragment = createFragmentAndAddResources(frame, string.get());
555     if (!fragment)
556         return false;
557     addFragment(fragment.releaseNonNull());
558
559     return true;
560 }
561
562 bool WebContentMarkupReader::readRTFD(SharedBuffer& buffer)
563 {
564     if (!frame.document())
565         return false;
566     auto string = adoptNS([[NSAttributedString alloc] initWithRTFD:buffer.createNSData().get() documentAttributes:nullptr]);
567     auto fragment = createFragmentAndAddResources(frame, string.get());
568     if (!fragment)
569         return false;
570
571     markup = createMarkup(*fragment);
572     return true;
573 }
574
575 bool WebContentReader::readRTF(SharedBuffer& buffer)
576 {
577     if (frame.settings().preferMIMETypeForImages())
578         return false;
579
580     auto string = adoptNS([[NSAttributedString alloc] initWithRTF:buffer.createNSData().get() documentAttributes:nullptr]);
581     auto fragment = createFragmentAndAddResources(frame, string.get());
582     if (!fragment)
583         return false;
584     addFragment(fragment.releaseNonNull());
585
586     return true;
587 }
588
589 bool WebContentMarkupReader::readRTF(SharedBuffer& buffer)
590 {
591     if (!frame.document())
592         return false;
593     auto string = adoptNS([[NSAttributedString alloc] initWithRTF:buffer.createNSData().get() documentAttributes:nullptr]);
594     auto fragment = createFragmentAndAddResources(frame, string.get());
595     if (!fragment)
596         return false;
597     markup = createMarkup(*fragment);
598     return true;
599 }
600
601 bool WebContentReader::readPlainText(const String& text)
602 {
603     if (!allowPlainText)
604         return false;
605
606     addFragment(createFragmentFromText(context, [text precomposedStringWithCanonicalMapping]));
607
608     madeFragmentFromPlainText = true;
609     return true;
610 }
611
612 bool WebContentReader::readImage(Ref<SharedBuffer>&& buffer, const String& type)
613 {
614     ASSERT(frame.document());
615     auto& document = *frame.document();
616
617     auto replacementURL = frame.editor().clientReplacementURLForResource(buffer.copyRef(), type);
618     if (!replacementURL.isEmpty()) {
619         addFragment(createFragmentForImageAndURL(document, replacementURL));
620         return true;
621     }
622
623     auto blob = Blob::create(buffer.get(), type);
624     if (shouldReplaceRichContentWithAttachments())
625         addFragment(createFragmentForImageAttachment(document, WTFMove(blob)));
626     else
627         addFragment(createFragmentForImageAndURL(document, DOMURL::createObjectURL(document, blob)));
628
629     return fragment;
630 }
631
632 bool WebContentReader::readFilePaths(const Vector<String>& paths)
633 {
634     if (paths.isEmpty() || !frame.document())
635         return false;
636
637     auto& document = *frame.document();
638     bool readAnyFilePath = false;
639     for (auto& path : paths) {
640 #if ENABLE(ATTACHMENT_ELEMENT)
641         if (RuntimeEnabledFeatures::sharedFeatures().attachmentElementEnabled()) {
642             auto attachment = HTMLAttachmentElement::create(HTMLNames::attachmentTag, document);
643             attachment->setFile(File::create(path), HTMLAttachmentElement::UpdateDisplayAttributes::Yes);
644             ensureFragment().appendChild(attachment);
645             readAnyFilePath = true;
646             continue;
647         }
648 #endif
649 #if PLATFORM(MAC)
650         // FIXME: Does (and should) any macOS client depend on inserting file paths as plain text in web content?
651         // If not, we should just remove this.
652         auto paragraph = createDefaultParagraphElement(document);
653         paragraph->appendChild(document.createTextNode(userVisibleString([NSURL fileURLWithPath:path])));
654         ensureFragment().appendChild(paragraph);
655         readAnyFilePath = true;
656 #endif
657     }
658     return readAnyFilePath;
659 }
660
661 }