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