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