Highlighting password field then making a Sticky Note via Safari Services exposes...
[WebKit-https.git] / Source / WebCore / editing / mac / EditorMac.mm
1 /*
2  * Copyright (C) 2006, 2007, 2008, 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 COMPUTER, 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 COMPUTER, 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 "Editor.h"
28
29 #import "CachedResourceLoader.h"
30 #import "Clipboard.h"
31 #import "ColorMac.h"
32 #import "DOMRangeInternal.h"
33 #import "DocumentFragment.h"
34 #import "DocumentLoader.h"
35 #import "Editor.h"
36 #import "EditorClient.h"
37 #import "Font.h"
38 #import "Frame.h"
39 #import "FrameLoaderClient.h"
40 #import "FrameView.h"
41 #import "HTMLConverter.h"
42 #import "HTMLElement.h"
43 #import "HTMLNames.h"
44 #import "LegacyWebArchive.h"
45 #import "MIMETypeRegistry.h"
46 #import "NodeTraversal.h"
47 #import "Page.h"
48 #import "Pasteboard.h"
49 #import "PasteboardStrategy.h"
50 #import "PlatformStrategies.h"
51 #import "Range.h"
52 #import "RenderBlock.h"
53 #import "RenderImage.h"
54 #import "ResourceBuffer.h"
55 #import "RuntimeApplicationChecks.h"
56 #import "Sound.h"
57 #import "StyleProperties.h"
58 #import "Text.h"
59 #import "TypingCommand.h"
60 #import "UUID.h"
61 #import "WebNSAttributedStringExtras.h"
62 #import "htmlediting.h"
63 #import "markup.h"
64
65 namespace WebCore {
66
67 using namespace HTMLNames;
68
69 void Editor::showFontPanel()
70 {
71     [[NSFontManager sharedFontManager] orderFrontFontPanel:nil];
72 }
73
74 void Editor::showStylesPanel()
75 {
76     [[NSFontManager sharedFontManager] orderFrontStylesPanel:nil];
77 }
78
79 void Editor::showColorPanel()
80 {
81     [[NSApplication sharedApplication] orderFrontColorPanel:nil];
82 }
83
84 void Editor::pasteWithPasteboard(Pasteboard* pasteboard, bool allowPlainText)
85 {
86     RefPtr<Range> range = selectedRange();
87
88     // FIXME: How can this hard-coded pasteboard name be right, given that the passed-in pasteboard has a name?
89     client()->setInsertionPasteboard(NSGeneralPboard);
90
91     bool chosePlainText;
92     RefPtr<DocumentFragment> fragment = webContentFromPasteboard(*pasteboard, *range, allowPlainText, chosePlainText);
93
94     if (fragment && shouldInsertFragment(fragment, range, EditorInsertActionPasted))
95         pasteAsFragment(fragment, canSmartReplaceWithPasteboard(*pasteboard), false);
96
97     client()->setInsertionPasteboard(String());
98 }
99
100 bool Editor::insertParagraphSeparatorInQuotedContent()
101 {
102     // FIXME: Why is this missing calls to canEdit, canEditRichly, etc.?
103     TypingCommand::insertParagraphSeparatorInQuotedContent(document());
104     revealSelectionAfterEditingOperation();
105     return true;
106 }
107
108 static RenderStyle* styleForSelectionStart(Frame* frame, Node *&nodeToRemove)
109 {
110     nodeToRemove = 0;
111
112     if (frame->selection().isNone())
113         return 0;
114
115     Position position = frame->selection().selection().visibleStart().deepEquivalent();
116     if (!position.isCandidate() || position.isNull())
117         return 0;
118
119     RefPtr<EditingStyle> typingStyle = frame->selection().typingStyle();
120     if (!typingStyle || !typingStyle->style())
121         return &position.deprecatedNode()->renderer()->style();
122
123     RefPtr<Element> styleElement = frame->document()->createElement(spanTag, false);
124
125     String styleText = typingStyle->style()->asText() + " display: inline";
126     styleElement->setAttribute(styleAttr, styleText);
127
128     styleElement->appendChild(frame->document()->createEditingTextNode(""), ASSERT_NO_EXCEPTION);
129
130     position.deprecatedNode()->parentNode()->appendChild(styleElement, ASSERT_NO_EXCEPTION);
131
132     nodeToRemove = styleElement.get();
133     return styleElement->renderer() ? &styleElement->renderer()->style() : 0;
134 }
135
136 const SimpleFontData* Editor::fontForSelection(bool& hasMultipleFonts) const
137 {
138     hasMultipleFonts = false;
139
140     if (!m_frame.selection().isRange()) {
141         Node* nodeToRemove;
142         RenderStyle* style = styleForSelectionStart(&m_frame, nodeToRemove); // sets nodeToRemove
143
144         const SimpleFontData* result = 0;
145         if (style)
146             result = style->font().primaryFont();
147
148         if (nodeToRemove)
149             nodeToRemove->remove(ASSERT_NO_EXCEPTION);
150
151         return result;
152     }
153
154     const SimpleFontData* font = 0;
155     RefPtr<Range> range = m_frame.selection().toNormalizedRange();
156     Node* startNode = adjustedSelectionStartForStyleComputation(m_frame.selection().selection()).deprecatedNode();
157     if (range && startNode) {
158         Node* pastEnd = range->pastLastNode();
159         // In the loop below, n should eventually match pastEnd and not become nil, but we've seen at least one
160         // unreproducible case where this didn't happen, so check for null also.
161         for (Node* node = startNode; node && node != pastEnd; node = NodeTraversal::next(node)) {
162             auto renderer = node->renderer();
163             if (!renderer)
164                 continue;
165             // FIXME: Are there any node types that have renderers, but that we should be skipping?
166             const SimpleFontData* primaryFont = renderer->style().font().primaryFont();
167             if (!font)
168                 font = primaryFont;
169             else if (font != primaryFont) {
170                 hasMultipleFonts = true;
171                 break;
172             }
173         }
174     }
175
176     return font;
177 }
178
179 NSDictionary* Editor::fontAttributesForSelectionStart() const
180 {
181     Node* nodeToRemove;
182     RenderStyle* style = styleForSelectionStart(&m_frame, nodeToRemove);
183     if (!style)
184         return nil;
185
186     NSMutableDictionary* result = [NSMutableDictionary dictionary];
187
188     if (style->visitedDependentColor(CSSPropertyBackgroundColor).isValid() && style->visitedDependentColor(CSSPropertyBackgroundColor).alpha() != 0)
189         [result setObject:nsColor(style->visitedDependentColor(CSSPropertyBackgroundColor)) forKey:NSBackgroundColorAttributeName];
190
191     if (style->font().primaryFont()->getNSFont())
192         [result setObject:style->font().primaryFont()->getNSFont() forKey:NSFontAttributeName];
193
194     if (style->visitedDependentColor(CSSPropertyColor).isValid() && style->visitedDependentColor(CSSPropertyColor) != Color::black)
195         [result setObject:nsColor(style->visitedDependentColor(CSSPropertyColor)) forKey:NSForegroundColorAttributeName];
196
197     const ShadowData* shadow = style->textShadow();
198     if (shadow) {
199         RetainPtr<NSShadow> s = adoptNS([[NSShadow alloc] init]);
200         [s.get() setShadowOffset:NSMakeSize(shadow->x(), shadow->y())];
201         [s.get() setShadowBlurRadius:shadow->radius()];
202         [s.get() setShadowColor:nsColor(shadow->color())];
203         [result setObject:s.get() forKey:NSShadowAttributeName];
204     }
205
206     int decoration = style->textDecorationsInEffect();
207     if (decoration & TextDecorationLineThrough)
208         [result setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle] forKey:NSStrikethroughStyleAttributeName];
209
210     int superscriptInt = 0;
211     switch (style->verticalAlign()) {
212         case BASELINE:
213         case BOTTOM:
214         case BASELINE_MIDDLE:
215         case LENGTH:
216         case MIDDLE:
217         case TEXT_BOTTOM:
218         case TEXT_TOP:
219         case TOP:
220             break;
221         case SUB:
222             superscriptInt = -1;
223             break;
224         case SUPER:
225             superscriptInt = 1;
226             break;
227     }
228     if (superscriptInt)
229         [result setObject:[NSNumber numberWithInt:superscriptInt] forKey:NSSuperscriptAttributeName];
230
231     if (decoration & TextDecorationUnderline)
232         [result setObject:[NSNumber numberWithInt:NSUnderlineStyleSingle] forKey:NSUnderlineStyleAttributeName];
233
234     if (nodeToRemove)
235         nodeToRemove->remove(ASSERT_NO_EXCEPTION);
236
237     return result;
238 }
239
240 bool Editor::canCopyExcludingStandaloneImages()
241 {
242     FrameSelection& selection = m_frame.selection();
243     return selection.isRange() && !selection.isInPasswordField();
244 }
245
246 void Editor::takeFindStringFromSelection()
247 {
248     if (!canCopyExcludingStandaloneImages()) {
249         systemBeep();
250         return;
251     }
252
253     Vector<String> types;
254     types.append(String(NSStringPboardType));
255     platformStrategies()->pasteboardStrategy()->setTypes(types, NSFindPboard);
256     platformStrategies()->pasteboardStrategy()->setStringForType(m_frame.displayStringModifiedByEncoding(selectedTextForClipboard()), NSStringPboardType, NSFindPboard);
257 }
258
259 void Editor::readSelectionFromPasteboard(const String& pasteboardName)
260 {
261     Pasteboard pasteboard(pasteboardName);
262     if (m_frame.selection().isContentRichlyEditable())
263         pasteWithPasteboard(&pasteboard, true);
264     else
265         pasteAsPlainTextWithPasteboard(pasteboard);
266 }
267
268 // FIXME: Makes no sense that selectedTextForClipboard always includes alt text, but stringSelectionForPasteboard does not.
269 // This was left in a bad state when selectedTextForClipboard was added. Need to look over clients and fix this.
270 String Editor::stringSelectionForPasteboard()
271 {
272     if (!canCopy())
273         return "";
274     String text = selectedText();
275     text.replace(noBreakSpace, ' ');
276     return text;
277 }
278
279 String Editor::stringSelectionForPasteboardWithImageAltText()
280 {
281     if (!canCopy())
282         return "";
283     String text = selectedTextForClipboard();
284     text.replace(noBreakSpace, ' ');
285     return text;
286 }
287
288 PassRefPtr<SharedBuffer> Editor::selectionInWebArchiveFormat()
289 {
290     RefPtr<LegacyWebArchive> archive = LegacyWebArchive::createFromSelection(&m_frame);
291     return archive ? SharedBuffer::wrapCFData(archive->rawDataRepresentation().get()) : 0;
292 }
293
294 PassRefPtr<Range> Editor::adjustedSelectionRange()
295 {
296     // FIXME: Why do we need to adjust the selection to include the anchor tag it's in?
297     // Whoever wrote this code originally forgot to leave us a comment explaining the rationale.
298     RefPtr<Range> range = selectedRange();
299     Node* commonAncestor = range->commonAncestorContainer(IGNORE_EXCEPTION);
300     ASSERT(commonAncestor);
301     Node* enclosingAnchor = enclosingNodeWithTag(firstPositionInNode(commonAncestor), HTMLNames::aTag);
302     if (enclosingAnchor && comparePositions(firstPositionInOrBeforeNode(range->startPosition().anchorNode()), range->startPosition()) >= 0)
303         range->setStart(enclosingAnchor, 0, IGNORE_EXCEPTION);
304     return range;
305 }
306
307 static NSAttributedString *attributedStringForRange(Range& range)
308 {
309     return [adoptNS([[WebHTMLConverter alloc] initWithDOMRange:kit(&range)]) attributedString];
310 }
311
312 static PassRefPtr<SharedBuffer> dataInRTFDFormat(NSAttributedString *string)
313 {
314     NSUInteger length = [string length];
315     return length ? SharedBuffer::wrapNSData([string RTFDFromRange:NSMakeRange(0, length) documentAttributes:nil]) : 0;
316 }
317
318 static PassRefPtr<SharedBuffer> dataInRTFFormat(NSAttributedString *string)
319 {
320     NSUInteger length = [string length];
321     return length ? SharedBuffer::wrapNSData([string RTFFromRange:NSMakeRange(0, length) documentAttributes:nil]) : 0;
322 }
323
324 PassRefPtr<SharedBuffer> Editor::dataSelectionForPasteboard(const String& pasteboardType)
325 {
326     // FIXME: The interface to this function is awkward. We'd probably be better off with three separate functions.
327     // As of this writing, this is only used in WebKit2 to implement the method -[WKView writeSelectionToPasteboard:types:],
328     // which is only used to support OS X services.
329
330     // FIXME: Does this function really need to use adjustedSelectionRange()? Because writeSelectionToPasteboard() just uses selectedRange().
331     if (!canCopy())
332         return nullptr;
333
334     if (pasteboardType == WebArchivePboardType)
335         return selectionInWebArchiveFormat();
336
337     if (pasteboardType == String(NSRTFDPboardType))
338        return dataInRTFDFormat(attributedStringForRange(*adjustedSelectionRange()));
339
340     if (pasteboardType == String(NSRTFPboardType)) {
341         NSAttributedString* attributedString = attributedStringForRange(*adjustedSelectionRange());
342         // FIXME: Why is this attachment character stripping needed here, but not needed in writeSelectionToPasteboard?
343         if ([attributedString containsAttachments])
344             attributedString = attributedStringByStrippingAttachmentCharacters(attributedString);
345         return dataInRTFFormat(attributedString);
346     }
347
348     return 0;
349 }
350
351 void Editor::writeSelectionToPasteboard(Pasteboard& pasteboard)
352 {
353     NSAttributedString *attributedString = attributedStringForRange(*selectedRange());
354
355     PasteboardWebContent content;
356     content.canSmartCopyOrDelete = canSmartCopyOrDelete();
357     content.dataInWebArchiveFormat = selectionInWebArchiveFormat();
358     content.dataInRTFDFormat = [attributedString containsAttachments] ? dataInRTFDFormat(attributedString) : 0;
359     content.dataInRTFFormat = dataInRTFFormat(attributedString);
360     content.dataInStringFormat = stringSelectionForPasteboardWithImageAltText();
361     client()->getClientPasteboardDataForRange(selectedRange().get(), content.clientTypes, content.clientData);
362
363     pasteboard.write(content);
364 }
365
366 static void getImage(Element& imageElement, RefPtr<Image>& image, CachedImage*& cachedImage)
367 {
368     auto renderer = imageElement.renderer();
369     if (!renderer || !renderer->isImage())
370         return;
371
372     CachedImage* tentativeCachedImage = toRenderImage(renderer)->cachedImage();
373     if (!tentativeCachedImage || tentativeCachedImage->errorOccurred()) {
374         tentativeCachedImage = 0;
375         return;
376     }
377
378     image = tentativeCachedImage->imageForRenderer(renderer);
379     if (!image)
380         return;
381
382     cachedImage = tentativeCachedImage;
383 }
384
385 void Editor::fillInUserVisibleForm(PasteboardURL& pasteboardURL)
386 {
387     pasteboardURL.userVisibleForm = client()->userVisibleString(pasteboardURL.url);
388 }
389
390 String Editor::plainTextFromPasteboard(const PasteboardPlainText& text)
391 {
392     String string = text.text;
393
394     // FIXME: It's not clear this is 100% correct since we know -[NSURL URLWithString:] does not handle
395     // all the same cases we handle well in the URL code for creating an NSURL.
396     if (text.isURL)
397         string = client()->userVisibleString([NSURL URLWithString:string]);
398
399     // FIXME: WTF should offer a non-Mac-specific way to convert string to precomposed form so we can do it for all platforms.
400     return [(NSString *)string precomposedStringWithCanonicalMapping];
401 }
402
403 void Editor::writeImageToPasteboard(Pasteboard& pasteboard, Element& imageElement, const URL& url, const String& title)
404 {
405     PasteboardImage pasteboardImage;
406
407     CachedImage* cachedImage;
408     getImage(imageElement, pasteboardImage.image, cachedImage);
409     if (!pasteboardImage.image)
410         return;
411     ASSERT(cachedImage);
412
413     pasteboardImage.url.url = url;
414     pasteboardImage.url.title = title;
415     pasteboardImage.url.userVisibleForm = client()->userVisibleString(pasteboardImage.url.url);
416     pasteboardImage.resourceData = cachedImage->resourceBuffer()->sharedBuffer();
417     pasteboardImage.resourceMIMEType = cachedImage->response().mimeType();
418
419     pasteboard.write(pasteboardImage);
420 }
421
422 class Editor::WebContentReader FINAL : public PasteboardWebContentReader {
423 public:
424     Frame& frame;
425     Range& context;
426     const bool allowPlainText;
427
428     RefPtr<DocumentFragment> fragment;
429     bool madeFragmentFromPlainText;
430
431     WebContentReader(Frame& frame, Range& context, bool allowPlainText)
432         : frame(frame)
433         , context(context)
434         , allowPlainText(allowPlainText)
435         , madeFragmentFromPlainText(false)
436     {
437     }
438
439 private:
440     virtual bool readWebArchive(PassRefPtr<SharedBuffer>) override;
441     virtual bool readFilenames(const Vector<String>&) override;
442     virtual bool readHTML(const String&) override;
443     virtual bool readRTFD(PassRefPtr<SharedBuffer>) override;
444     virtual bool readRTF(PassRefPtr<SharedBuffer>) override;
445     virtual bool readImage(PassRefPtr<SharedBuffer>, const String& type) override;
446     virtual bool readURL(const URL&, const String& title) override;
447     virtual bool readPlainText(const String&) override;
448 };
449
450 bool Editor::WebContentReader::readWebArchive(PassRefPtr<SharedBuffer> buffer)
451 {
452     if (!frame.document())
453         return false;
454
455     RefPtr<LegacyWebArchive> archive = LegacyWebArchive::create(URL(), buffer.get());
456     if (!archive)
457         return false;
458
459     RefPtr<ArchiveResource> mainResource = archive->mainResource();
460     if (!mainResource)
461         return false;
462
463     const String& type = mainResource->mimeType();
464
465     if (frame.loader().client().canShowMIMETypeAsHTML(type)) {
466         // FIXME: The code in createFragmentAndAddResources calls setDefersLoading(true). Don't we need that here?
467         if (DocumentLoader* loader = frame.loader().documentLoader())
468             loader->addAllArchiveResources(archive.get());
469
470         String markupString = String::fromUTF8(mainResource->data()->data(), mainResource->data()->size());
471         fragment = createFragmentFromMarkup(*frame.document(), markupString, mainResource->url(), DisallowScriptingAndPluginContent);
472         return true;
473     }
474
475     if (MIMETypeRegistry::isSupportedImageMIMEType(type)) {
476         fragment = frame.editor().createFragmentForImageResourceAndAddResource(mainResource.release());
477         return true;
478     }
479
480     return false;
481 }
482
483 bool Editor::WebContentReader::readFilenames(const Vector<String>& paths)
484 {
485     size_t size = paths.size();
486     if (!size)
487         return false;
488
489     if (!frame.document())
490         return false;
491     Document& document = *frame.document();
492
493     fragment = document.createDocumentFragment();
494
495     for (size_t i = 0; i < size; i++) {
496         String text = paths[i];
497         text = frame.editor().client()->userVisibleString([NSURL fileURLWithPath:text]);
498
499         RefPtr<HTMLElement> paragraph = createDefaultParagraphElement(document);
500         paragraph->appendChild(document.createTextNode(text));
501         fragment->appendChild(paragraph.release());
502     }
503
504     return true;
505 }
506
507 bool Editor::WebContentReader::readHTML(const String& string)
508 {
509     String stringOmittingMicrosoftPrefix = string;
510
511     // This code was added to make HTML paste from Microsoft Word on Mac work, back in 2004.
512     // It's a simple-minded way to ignore the CF_HTML clipboard format, just skipping over the
513     // description part and parsing the entire context plus fragment.
514     if (string.startsWith("Version:")) {
515         size_t location = string.findIgnoringCase("<html");
516         if (location != notFound)
517             stringOmittingMicrosoftPrefix = string.substring(location);
518     }
519
520     if (stringOmittingMicrosoftPrefix.isEmpty())
521         return false;
522
523     if (!frame.document())
524         return false;
525     Document& document = *frame.document();
526
527     fragment = createFragmentFromMarkup(document, stringOmittingMicrosoftPrefix, emptyString(), DisallowScriptingAndPluginContent);
528     return fragment;
529 }
530
531 bool Editor::WebContentReader::readRTFD(PassRefPtr<SharedBuffer> buffer)
532 {
533     fragment = frame.editor().createFragmentAndAddResources(adoptNS([[NSAttributedString alloc] initWithRTFD:buffer->createNSData().get() documentAttributes:nullptr]).get());
534     return fragment;
535 }
536
537 bool Editor::WebContentReader::readRTF(PassRefPtr<SharedBuffer> buffer)
538 {
539     fragment = frame.editor().createFragmentAndAddResources(adoptNS([[NSAttributedString alloc] initWithRTF:buffer->createNSData().get() documentAttributes:nullptr]).get());
540     return fragment;
541 }
542
543 bool Editor::WebContentReader::readImage(PassRefPtr<SharedBuffer> buffer, const String& type)
544 {
545     ASSERT(type.contains('/'));
546     String typeAsFilenameWithExtension = type;
547     typeAsFilenameWithExtension.replace('/', '.');
548     URL imageURL = URL(URL(), "webkit-fake-url://" + createCanonicalUUIDString() + '/' + typeAsFilenameWithExtension);
549
550     fragment = frame.editor().createFragmentForImageResourceAndAddResource(ArchiveResource::create(buffer, imageURL, type, emptyString(), emptyString()));
551     return fragment;
552 }
553
554 bool Editor::WebContentReader::readURL(const URL& url, const String& title)
555 {
556     if (url.string().isEmpty())
557         return false;
558
559     RefPtr<Element> anchor = frame.document()->createElement(HTMLNames::aTag, false);
560     anchor->setAttribute(HTMLNames::hrefAttr, url.string());
561     anchor->appendChild(frame.document()->createTextNode([title precomposedStringWithCanonicalMapping]));
562
563     fragment = frame.document()->createDocumentFragment();
564     fragment->appendChild(anchor.release());
565     return true;
566 }
567
568 bool Editor::WebContentReader::readPlainText(const String& text)
569 {
570     if (!allowPlainText)
571         return false;
572
573     fragment = createFragmentFromText(context, [text precomposedStringWithCanonicalMapping]);
574     if (!fragment)
575         return false;
576
577     madeFragmentFromPlainText = true;
578     return true;
579 }
580
581 // FIXME: Should give this function a name that makes it clear it adds resources to the document loader as a side effect.
582 // Or refactor so it does not do that.
583 PassRefPtr<DocumentFragment> Editor::webContentFromPasteboard(Pasteboard& pasteboard, Range& context, bool allowPlainText, bool& chosePlainText)
584 {
585     WebContentReader reader(m_frame, context, allowPlainText);
586     pasteboard.read(reader);
587     chosePlainText = reader.madeFragmentFromPlainText;
588     return reader.fragment.release();
589 }
590
591 PassRefPtr<DocumentFragment> Editor::createFragmentForImageResourceAndAddResource(PassRefPtr<ArchiveResource> resource)
592 {
593     if (!resource)
594         return nullptr;
595
596     RefPtr<Element> imageElement = document().createElement(HTMLNames::imgTag, false);
597     imageElement->setAttribute(HTMLNames::srcAttr, resource->url().string());
598
599     RefPtr<DocumentFragment> fragment = document().createDocumentFragment();
600     fragment->appendChild(imageElement.release());
601
602     // FIXME: The code in createFragmentAndAddResources calls setDefersLoading(true). Don't we need that here?
603     if (DocumentLoader* loader = m_frame.loader().documentLoader())
604         loader->addArchiveResource(resource.get());
605
606     return fragment.release();
607 }
608
609 PassRefPtr<DocumentFragment> Editor::createFragmentAndAddResources(NSAttributedString *string)
610 {
611     if (!m_frame.page() || !document().isHTMLDocument())
612         return nullptr;
613
614     if (!string)
615         return nullptr;
616
617     bool wasDeferringCallbacks = m_frame.page()->defersLoading();
618     if (!wasDeferringCallbacks)
619         m_frame.page()->setDefersLoading(true);
620
621     Vector<RefPtr<ArchiveResource>> resources;
622     RefPtr<DocumentFragment> fragment = client()->documentFragmentFromAttributedString(string, resources);
623
624     if (DocumentLoader* loader = m_frame.loader().documentLoader()) {
625         for (size_t i = 0, size = resources.size(); i < size; ++i)
626             loader->addArchiveResource(resources[i]);
627     }
628
629     if (!wasDeferringCallbacks)
630         m_frame.page()->setDefersLoading(false);
631
632     return fragment.release();
633 }
634
635 } // namespace WebCore