b65dcfc2bab572c299407f150d8969ea3cb0ed84
[WebKit-https.git] / Source / WebCore / editing / ios / EditorIOS.mm
1 /*
2  * Copyright (C) 2006, 2007, 2013 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. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "Editor.h"
28
29 #include "CachedImage.h"
30 #include "Clipboard.h"
31 #include "CSSComputedStyleDeclaration.h"
32 #include "CSSPrimitiveValueMappings.h"
33 #include "DOMRangeInternal.h"
34 #include "DocumentFragment.h"
35 #include "DocumentLoader.h"
36 #include "EditorClient.h"
37 #include "Font.h"
38 #include "Frame.h"
39 #include "FrameLoaderClient.h"
40 #include "HTMLConverter.h"
41 #include "HTMLInputElement.h"
42 #include "HTMLNames.h"
43 #include "HTMLParserIdioms.h"
44 #include "HTMLTextAreaElement.h"
45 #include "LegacyWebArchive.h"
46 #include "NodeTraversal.h"
47 #include "Page.h"
48 #include "Pasteboard.h"
49 #include "RenderBlock.h"
50 #include "RenderImage.h"
51 #include "SharedBuffer.h"
52 #include "SoftLinking.h"
53 #include "StyleProperties.h"
54 #include "Text.h"
55 #include "TypingCommand.h"
56 #include "WAKAppKitStubs.h"
57 #include "htmlediting.h"
58 #include "markup.h"
59
60 #if PLATFORM(IOS)
61
62 SOFT_LINK_FRAMEWORK(AppSupport)
63 SOFT_LINK(AppSupport, CPSharedResourcesDirectory, CFStringRef, (void), ())
64
65 SOFT_LINK_FRAMEWORK(MobileCoreServices)
66
67 SOFT_LINK(MobileCoreServices, UTTypeConformsTo, Boolean, (CFStringRef inUTI, CFStringRef inConformsToUTI), (inUTI, inConformsToUTI))
68 SOFT_LINK(MobileCoreServices, UTTypeCreatePreferredIdentifierForTag, CFStringRef, (CFStringRef inTagClass, CFStringRef inTag, CFStringRef inConformingToUTI), (inTagClass, inTag, inConformingToUTI))
69 SOFT_LINK(MobileCoreServices, UTTypeCopyPreferredTagWithClass, CFStringRef, (CFStringRef inUTI, CFStringRef inTagClass), (inUTI, inTagClass))
70
71 SOFT_LINK_CONSTANT(MobileCoreServices, kUTTypePNG, CFStringRef)
72 SOFT_LINK_CONSTANT(MobileCoreServices, kUTTypeJPEG, CFStringRef)
73 SOFT_LINK_CONSTANT(MobileCoreServices, kUTTagClassFilenameExtension, CFStringRef)
74 SOFT_LINK_CONSTANT(MobileCoreServices, kUTTagClassMIMEType, CFStringRef)
75
76 #define kUTTypePNG  getkUTTypePNG()
77 #define kUTTypeJPEG getkUTTypeJPEG()
78 #define kUTTagClassFilenameExtension getkUTTagClassFilenameExtension()
79 #define kUTTagClassMIMEType getkUTTagClassMIMEType()
80
81 @interface NSAttributedString (NSAttributedStringKitAdditions)
82 - (id)initWithRTF:(NSData *)data documentAttributes:(NSDictionary **)dict;
83 - (id)initWithRTFD:(NSData *)data documentAttributes:(NSDictionary **)dict;
84 - (NSData *)RTFFromRange:(NSRange)range documentAttributes:(NSDictionary *)dict;
85 - (NSData *)RTFDFromRange:(NSRange)range documentAttributes:(NSDictionary *)dict;
86 - (BOOL)containsAttachments;
87 @end
88
89 namespace WebCore {
90
91 using namespace HTMLNames;
92
93 void Editor::showFontPanel()
94 {
95 }
96
97 void Editor::showStylesPanel()
98 {
99 }
100
101 void Editor::showColorPanel()
102 {
103 }
104
105 void Editor::setTextAlignmentForChangedBaseWritingDirection(WritingDirection direction)
106 {
107     // Note that the passed-in argument is the direction that has been changed to by
108     // some code or user interaction outside the scope of this function. The former
109     // direction is not known, nor is it required for the kind of text alignment
110     // changes done by this function.
111     //
112     // Rules:
113     // When text has no explicit alignment, set to alignment to match the writing direction.
114     // If the text has left or right alignment, flip left->right and right->left. 
115     // Otherwise, do nothing.
116
117     RefPtr<EditingStyle> selectionStyle = EditingStyle::styleAtSelectionStart(m_frame.selection().selection());
118     if (!selectionStyle || !selectionStyle->style())
119          return;
120
121     RefPtr<CSSPrimitiveValue> value = static_pointer_cast<CSSPrimitiveValue>(selectionStyle->style()->getPropertyCSSValue(CSSPropertyTextAlign));
122     if (!value)
123         return;
124         
125     const char *newValue = NULL;
126     ETextAlign textAlign = *value;
127     switch (textAlign) {
128         case TASTART:
129         case TAEND:
130         {
131             switch (direction) {
132                 case NaturalWritingDirection:
133                     // no-op
134                     break;
135                 case LeftToRightWritingDirection:
136                     newValue = "left";
137                     break;
138                 case RightToLeftWritingDirection:
139                     newValue = "right";
140                     break;
141             }
142             break;
143         }
144         case LEFT:
145         case WEBKIT_LEFT:
146             newValue = "right";
147             break;
148         case RIGHT:
149         case WEBKIT_RIGHT:
150             newValue = "left";
151             break;
152         case CENTER:
153         case WEBKIT_CENTER:
154         case JUSTIFY:
155             // no-op
156             break;
157     }
158
159     if (!newValue)
160         return;
161
162     Element* focusedElement = m_frame.document()->focusedElement();
163     if (focusedElement && (focusedElement->hasTagName(textareaTag) || (focusedElement->hasTagName(inputTag) &&
164         (toHTMLInputElement(focusedElement)->isTextField() ||
165          toHTMLInputElement(focusedElement)->isSearchField())))) {
166         if (direction == NaturalWritingDirection)
167             return;
168         toHTMLElement(focusedElement)->setAttribute(alignAttr, newValue);
169         m_frame.document()->updateStyleIfNeeded();
170         return;
171     }
172
173     RefPtr<MutableStyleProperties> style = MutableStyleProperties::create();
174     style->setProperty(CSSPropertyTextAlign, newValue);
175     applyParagraphStyle(style.get());
176 }
177
178 bool Editor::insertParagraphSeparatorInQuotedContent()
179 {
180     // FIXME: Why is this missing calls to canEdit, canEditRichly, etc...
181     TypingCommand::insertParagraphSeparatorInQuotedContent(*m_frame.document());
182     revealSelectionAfterEditingOperation();
183     return true;
184 }
185
186 // FIXME: Copied from EditorMac. This should be shared between the two so that
187 // the implementation does not differ.
188 static RenderStyle* styleForSelectionStart(Frame* frame, Node *&nodeToRemove)
189 {
190     nodeToRemove = 0;
191
192     if (frame->selection().isNone())
193         return 0;
194
195     Position position = frame->selection().selection().visibleStart().deepEquivalent();
196     if (!position.isCandidate() || position.isNull())
197         return 0;
198
199     RefPtr<EditingStyle> typingStyle = frame->selection().typingStyle();
200     if (!typingStyle || !typingStyle->style())
201         return &position.deprecatedNode()->renderer()->style();
202
203     RefPtr<Element> styleElement = frame->document()->createElement(spanTag, false);
204
205     String styleText = typingStyle->style()->asText() + " display: inline";
206     styleElement->setAttribute(styleAttr, styleText.impl());
207
208     ExceptionCode ec = 0;
209     styleElement->appendChild(frame->document()->createEditingTextNode(""), ec);
210     ASSERT(!ec);
211
212     position.deprecatedNode()->parentNode()->appendChild(styleElement, ec);
213     ASSERT(!ec);
214
215     nodeToRemove = styleElement.get();
216     return styleElement->renderer() ? &styleElement->renderer()->style() : 0;
217 }
218
219 const SimpleFontData* Editor::fontForSelection(bool& hasMultipleFonts) const
220 {
221     hasMultipleFonts = false;
222
223     if (!m_frame.selection().isRange()) {
224         Node* nodeToRemove;
225         RenderStyle* style = styleForSelectionStart(&m_frame, nodeToRemove); // sets nodeToRemove
226
227         const SimpleFontData* result = 0;
228         if (style)
229             result = style->font().primaryFont();
230
231         if (nodeToRemove) {
232             ExceptionCode ec;
233             nodeToRemove->remove(ec);
234             ASSERT(!ec);
235         }
236
237         return result;
238     }
239
240     const SimpleFontData* font = 0;
241     RefPtr<Range> range = m_frame.selection().toNormalizedRange();
242     if (Node* startNode = adjustedSelectionStartForStyleComputation(m_frame.selection().selection()).deprecatedNode()) {
243         Node* pastEnd = range->pastLastNode();
244         // In the loop below, n should eventually match pastEnd and not become nil, but we've seen at least one
245         // unreproducible case where this didn't happen, so check for null also.
246         for (Node* node = startNode; node && node != pastEnd; node = NodeTraversal::next(node)) {
247             auto renderer = node->renderer();
248             if (!renderer)
249                 continue;
250             // FIXME: Are there any node types that have renderers, but that we should be skipping?
251             const SimpleFontData* primaryFont = renderer->style().font().primaryFont();
252             if (!font)
253                 font = primaryFont;
254             else if (font != primaryFont) {
255                 hasMultipleFonts = true;
256                 break;
257             }
258         }
259     }
260
261     return font;
262 }
263
264 NSDictionary* Editor::fontAttributesForSelectionStart() const
265 {
266     Node* nodeToRemove;
267     RenderStyle* style = styleForSelectionStart(&m_frame, nodeToRemove);
268     if (!style)
269         return nil;
270
271     NSMutableDictionary* result = [NSMutableDictionary dictionary];
272     return result;
273 }
274
275 void Editor::removeUnchangeableStyles()
276 {
277     // This function removes styles that the user cannot modify by applying their default values.
278     
279     RefPtr<EditingStyle> editingStyle = EditingStyle::create(m_frame.document()->body());
280     RefPtr<MutableStyleProperties> defaultStyle = editingStyle.get()->style()->mutableCopy();
281     
282     // Text widgets implement background color via the UIView property. Their body element will not have one.
283     defaultStyle->setProperty(CSSPropertyBackgroundColor, "rgba(255, 255, 255, 0.0)");
284     
285     // Remove properties that the user can modify, like font-weight. 
286     // Also remove font-family, per HI spec.
287     // FIXME: it'd be nice if knowledge about which styles were unchangeable was not hard-coded here.
288     defaultStyle->removeProperty(CSSPropertyFontWeight);
289     defaultStyle->removeProperty(CSSPropertyFontStyle);
290     defaultStyle->removeProperty(CSSPropertyFontVariant);
291     // FIXME: we should handle also pasted quoted text, strikethrough, etc. <rdar://problem/9255115>
292     defaultStyle->removeProperty(CSSPropertyTextDecoration);
293     defaultStyle->removeProperty(CSSPropertyWebkitTextDecorationsInEffect); // implements underline
294
295     // FIXME add EditActionMatchStlye <rdar://problem/9156507> Undo rich text's paste & match style should say "Undo Match Style"
296     applyStyleToSelection(defaultStyle.get(), EditActionChangeAttributes);
297 }
298
299 // FIXME: the following fuctions should be shared between Mac and iOS.
300 static NSAttributedString *attributedStringForRange(Range& range)
301 {
302     return [adoptNS([[WebHTMLConverter alloc] initWithDOMRange:kit(&range)]) attributedString];
303 }
304
305 static PassRefPtr<SharedBuffer> dataInRTFDFormat(NSAttributedString *string)
306 {
307     NSUInteger length = [string length];
308     return length ? SharedBuffer::wrapNSData([string RTFDFromRange:NSMakeRange(0, length) documentAttributes:nil]) : nullptr;
309 }
310
311 static PassRefPtr<SharedBuffer> dataInRTFFormat(NSAttributedString *string)
312 {
313     NSUInteger length = [string length];
314     return length ? SharedBuffer::wrapNSData([string RTFFromRange:NSMakeRange(0, length) documentAttributes:nil]) : nullptr;
315 }    
316
317 String Editor::stringSelectionForPasteboardWithImageAltText()
318 {
319     String text = selectedTextForClipboard();
320     text.replace(noBreakSpace, ' ');
321     return text;
322 }
323
324 PassRefPtr<SharedBuffer> Editor::selectionInWebArchiveFormat()
325 {
326     RefPtr<LegacyWebArchive> archive = LegacyWebArchive::createFromSelection(&m_frame);
327     return archive ? SharedBuffer::wrapCFData(archive->rawDataRepresentation().get()) : nullptr;
328 }
329
330 void Editor::writeSelectionToPasteboard(Pasteboard& pasteboard)
331 {
332     NSAttributedString *attributedString = attributedStringForRange(*selectedRange());
333
334     PasteboardWebContent content;
335     content.canSmartCopyOrDelete = canSmartCopyOrDelete();
336     content.dataInWebArchiveFormat = selectionInWebArchiveFormat();
337     content.dataInRTFDFormat = [attributedString containsAttachments] ? dataInRTFDFormat(attributedString) : 0;
338     content.dataInRTFFormat = dataInRTFFormat(attributedString);
339     content.dataInStringFormat = stringSelectionForPasteboardWithImageAltText();
340     client()->getClientPasteboardDataForRange(selectedRange().get(), content.clientTypes, content.clientData);
341
342     pasteboard.write(content);
343 }
344
345 static void getImage(Element& imageElement, RefPtr<Image>& image, CachedImage*& cachedImage)
346 {
347     auto renderer = imageElement.renderer();
348     if (!renderer || !renderer->isImage())
349         return;
350
351     CachedImage* tentativeCachedImage = toRenderImage(renderer)->cachedImage();
352     if (!tentativeCachedImage || tentativeCachedImage->errorOccurred()) {
353         tentativeCachedImage = 0;
354         return;
355     }
356
357     image = tentativeCachedImage->imageForRenderer(renderer);
358     if (!image)
359         return;
360     
361     cachedImage = tentativeCachedImage;
362 }
363
364 void Editor::writeImageToPasteboard(Pasteboard& pasteboard, Element& imageElement, const URL&, const String& title)
365 {
366     PasteboardImage pasteboardImage;
367
368     CachedImage* cachedImage;
369     getImage(imageElement, pasteboardImage.image, cachedImage);
370     if (!pasteboardImage.image)
371         return;
372     ASSERT(cachedImage);
373
374     pasteboardImage.url.url = imageElement.document().completeURL(stripLeadingAndTrailingHTMLSpaces(imageElement.imageSourceURL()));
375     pasteboardImage.url.title = title;
376     pasteboardImage.resourceMIMEType = pasteboard.resourceMIMEType(cachedImage->response().mimeType());
377
378     pasteboard.write(pasteboardImage);
379 }
380
381 class Editor::WebContentReader final : public PasteboardWebContentReader {
382 public:
383     WebContentReader(Frame& frame, Range& context, bool allowPlainText)
384         : frame(frame)
385         , context(context)
386         , allowPlainText(allowPlainText)
387         , madeFragmentFromPlainText(false)
388     {
389     }
390
391     Frame& frame;
392     Range& context;
393     const bool allowPlainText;
394
395     RefPtr<DocumentFragment> fragment;
396     bool madeFragmentFromPlainText;
397
398 private:
399     virtual bool readWebArchive(PassRefPtr<SharedBuffer>) override;
400     virtual bool readFilenames(const Vector<String>&) override;
401     virtual bool readHTML(const String&) override;
402     virtual bool readRTFD(PassRefPtr<SharedBuffer>) override;
403     virtual bool readRTF(PassRefPtr<SharedBuffer>) override;
404     virtual bool readImage(PassRefPtr<SharedBuffer>, const String& type) override;
405     virtual bool readURL(const URL&, const String& title) override;
406     virtual bool readPlainText(const String&) override;
407     void addFragment(PassRefPtr<DocumentFragment>);
408 };
409
410 void Editor::WebContentReader::addFragment(PassRefPtr<DocumentFragment> newFragment)
411 {
412     if (fragment) {
413         if (newFragment && newFragment->firstChild()) {
414             ExceptionCode ec;
415             fragment->appendChild(newFragment->firstChild(), ec);
416         }
417     } else
418         fragment = newFragment;
419 }
420
421 bool Editor::WebContentReader::readWebArchive(PassRefPtr<SharedBuffer> buffer)
422 {
423     if (!frame.document())
424         return false;
425
426     RefPtr<LegacyWebArchive> archive = LegacyWebArchive::create(URL(), buffer.get());
427     if (!archive)
428         return false;
429
430     RefPtr<ArchiveResource> mainResource = archive->mainResource();
431     if (!mainResource)
432         return false;
433
434     const String& type = mainResource->mimeType();
435
436     if (frame.loader().client().canShowMIMETypeAsHTML(type)) {
437         // FIXME: The code in createFragmentAndAddResources calls setDefersLoading(true). Don't we need that here?
438         if (DocumentLoader* loader = frame.loader().documentLoader())
439             loader->addAllArchiveResources(archive.get());
440
441         String markupString = String::fromUTF8(mainResource->data()->data(), mainResource->data()->size());
442         addFragment(createFragmentFromMarkup(*frame.document(), markupString, mainResource->url(), DisallowScriptingAndPluginContent));
443         return true;
444     }
445
446     return false;
447 }
448
449 bool Editor::WebContentReader::readFilenames(const Vector<String>&)
450 {
451     return false;
452 }
453
454 bool Editor::WebContentReader::readHTML(const String&)
455 {
456     return false;
457 }
458
459 bool Editor::WebContentReader::readRTFD(PassRefPtr<SharedBuffer> buffer)
460 {
461     addFragment(frame.editor().createFragmentAndAddResources(adoptNS([[NSAttributedString alloc] initWithRTFD:buffer->createNSData().get() documentAttributes:nullptr]).get()));
462     return fragment;
463 }
464
465 bool Editor::WebContentReader::readRTF(PassRefPtr<SharedBuffer> buffer)
466 {
467     addFragment(frame.editor().createFragmentAndAddResources(adoptNS([[NSAttributedString alloc] initWithRTF:buffer->createNSData().get() documentAttributes:nullptr]).get()));
468     return fragment;
469 }
470
471 static NSURL* uniqueURLWithRelativePart(NSString *relativePart)
472 {
473     RetainPtr<CFUUIDRef> UUIDRef = adoptCF(CFUUIDCreate(kCFAllocatorDefault));
474     RetainPtr<NSString> UUIDString = adoptNS((NSString *)CFUUIDCreateString(kCFAllocatorDefault, UUIDRef.get()));
475
476     return [NSURL URLWithString:[NSString stringWithFormat:@"%@://%@/%@", @"webkit-fake-url", UUIDString.get(), relativePart]];
477 }
478
479 bool Editor::WebContentReader::readImage(PassRefPtr<SharedBuffer> buffer, const String& type)
480 {
481     RetainPtr<CFStringRef> stringType = type.createCFString();
482     RetainPtr<NSString> filenameExtension = adoptNS((NSString *)UTTypeCopyPreferredTagWithClass(stringType.get(), kUTTagClassFilenameExtension));
483     NSString *relativeURLPart = [@"image" stringByAppendingString:filenameExtension.get()];
484     RetainPtr<NSString> mimeType = adoptNS((NSString *)UTTypeCopyPreferredTagWithClass(stringType.get(), kUTTagClassMIMEType));
485
486     addFragment(frame.editor().createFragmentForImageResourceAndAddResource(ArchiveResource::create(buffer, uniqueURLWithRelativePart(relativeURLPart), mimeType.get(), emptyString(), emptyString())));
487     return fragment;
488 }
489
490 bool Editor::WebContentReader::readURL(const URL& url, const String&)
491 {
492     if (url.isEmpty())
493         return false;
494
495     if (!frame.editor().client()->hasRichlyEditableSelection()) {
496         if (readPlainText([(NSURL *)url absoluteString]))
497             return true;
498     }
499
500     if ([(NSURL *)url isFileURL]) {
501         NSString *localPath = [(NSURL *)url relativePath];
502         // Only allow url attachments from ~/Media for now.
503         if (![localPath hasPrefix:[(NSString *)CPSharedResourcesDirectory() stringByAppendingString:@"/Media/DCIM/"]])
504             return false;
505
506         RetainPtr<NSString> fileType = adoptNS((NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (CFStringRef)[localPath pathExtension], NULL));
507         NSData *data = [NSData dataWithContentsOfFile:localPath];
508         if (UTTypeConformsTo((CFStringRef)fileType.get(), kUTTypePNG)) {
509             addFragment(frame.editor().createFragmentForImageResourceAndAddResource(ArchiveResource::create(SharedBuffer::wrapNSData([[data copy] autorelease]), uniqueURLWithRelativePart(@"image.png"), @"image/png", emptyString(), emptyString())));
510             return fragment;
511         } else if (UTTypeConformsTo((CFStringRef)fileType.get(), kUTTypeJPEG)) {
512             addFragment(frame.editor().createFragmentForImageResourceAndAddResource(ArchiveResource::create(SharedBuffer::wrapNSData([[data copy] autorelease]), uniqueURLWithRelativePart(@"image.jpg"), @"image/jpg", emptyString(), emptyString())));
513             return fragment;
514         }
515     } else {
516         RefPtr<Element> anchor = frame.document()->createElement(HTMLNames::aTag, false);
517         anchor->setAttribute(HTMLNames::hrefAttr, url.string());
518         anchor->appendChild(frame.document()->createTextNode([[(NSURL *)url absoluteString] precomposedStringWithCanonicalMapping]));
519
520         RefPtr<DocumentFragment> newFragment = frame.document()->createDocumentFragment();
521         newFragment->appendChild(anchor.release());
522         addFragment(newFragment);
523         return true;
524     }
525     return false;
526 }
527
528 bool Editor::WebContentReader::readPlainText(const String& text)
529 {
530     if (!allowPlainText)
531         return false;
532
533     addFragment(createFragmentFromText(context, [text precomposedStringWithCanonicalMapping]));
534     if (!fragment)
535         return false;
536
537     madeFragmentFromPlainText = true;
538     return true;
539 }
540
541 // FIXME: Should give this function a name that makes it clear it adds resources to the document loader as a side effect.
542 // Or refactor so it does not do that.
543 PassRefPtr<DocumentFragment> Editor::webContentFromPasteboard(Pasteboard& pasteboard, Range& context, bool allowPlainText, bool& chosePlainText)
544 {
545     WebContentReader reader(m_frame, context, allowPlainText);
546     pasteboard.read(reader);
547     chosePlainText = reader.madeFragmentFromPlainText;
548     return reader.fragment.release();
549 }
550
551 void Editor::pasteWithPasteboard(Pasteboard* pasteboard, bool allowPlainText)
552 {
553     RefPtr<Range> range = selectedRange();
554
555     bool chosePlainText;
556     RefPtr<DocumentFragment> fragment = client()->documentFragmentFromDelegate(0);
557     if (!fragment)
558         fragment = webContentFromPasteboard(*pasteboard, *range, allowPlainText, chosePlainText);
559
560     if (fragment && shouldInsertFragment(fragment, range, EditorInsertActionPasted))
561         pasteAsFragment(fragment, canSmartReplaceWithPasteboard(*pasteboard), false);
562 }
563
564 PassRefPtr<DocumentFragment> Editor::createFragmentAndAddResources(NSAttributedString *string)
565 {
566     if (!m_frame.page() || !m_frame.document() || !m_frame.document()->isHTMLDocument())
567         return nullptr;
568
569     if (!string)
570         return nullptr;
571
572     bool wasDeferringCallbacks = m_frame.page()->defersLoading();
573     if (!wasDeferringCallbacks)
574         m_frame.page()->setDefersLoading(true);
575
576     Vector<RefPtr<ArchiveResource>> resources;
577     RefPtr<DocumentFragment> fragment = client()->documentFragmentFromAttributedString(string, resources);
578
579     if (DocumentLoader* loader = m_frame.loader().documentLoader()) {
580         for (size_t i = 0, size = resources.size(); i < size; ++i)
581             loader->addArchiveResource(resources[i]);
582     }
583
584     if (!wasDeferringCallbacks)
585         m_frame.page()->setDefersLoading(false);
586     
587     return fragment.release();
588 }
589
590 PassRefPtr<DocumentFragment> Editor::createFragmentForImageResourceAndAddResource(PassRefPtr<ArchiveResource> resource)
591 {
592     if (!resource)
593         return nullptr;
594
595     RefPtr<Element> imageElement = m_frame.document()->createElement(HTMLNames::imgTag, false);
596     // FIXME: The code in createFragmentAndAddResources calls setDefersLoading(true). Don't we need that here?
597     if (DocumentLoader* loader = m_frame.loader().documentLoader())
598         loader->addArchiveResource(resource.get());
599
600     NSURL *URL = resource->url();
601     imageElement->setAttribute(HTMLNames::srcAttr, [URL isFileURL] ? [URL absoluteString] : resource->url());
602
603     RefPtr<DocumentFragment> fragment = m_frame.document()->createDocumentFragment();
604     fragment->appendChild(imageElement.release());
605
606     return fragment.release();
607 }
608
609 } // namespace WebCore
610
611 #endif // PLATFORM(IOS)