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