f0e0d865c8bc7dcdb135814c07d2445a9da7ccf4
[WebKit-https.git] / Source / WebCore / editing / markup.cpp
1 /*
2  * Copyright (C) 2004-2017 Apple Inc. All rights reserved.
3  * Copyright (C) 2008, 2009, 2010, 2011 Google Inc. All rights reserved.
4  * Copyright (C) 2011 Igalia S.L.
5  * Copyright (C) 2011 Motorola Mobility. All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
20  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
24  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
27  */
28
29 #include "config.h"
30 #include "markup.h"
31
32 #include "ArchiveResource.h"
33 #include "CSSPrimitiveValue.h"
34 #include "CSSPropertyNames.h"
35 #include "CSSValue.h"
36 #include "CSSValueKeywords.h"
37 #include "CacheStorageProvider.h"
38 #include "ChildListMutationScope.h"
39 #include "Comment.h"
40 #include "DocumentFragment.h"
41 #include "DocumentLoader.h"
42 #include "DocumentType.h"
43 #include "Editing.h"
44 #include "Editor.h"
45 #include "EditorClient.h"
46 #include "ElementIterator.h"
47 #include "EmptyClients.h"
48 #include "File.h"
49 #include "Frame.h"
50 #include "FrameLoader.h"
51 #include "HTMLAttachmentElement.h"
52 #include "HTMLBRElement.h"
53 #include "HTMLBodyElement.h"
54 #include "HTMLDivElement.h"
55 #include "HTMLHeadElement.h"
56 #include "HTMLHtmlElement.h"
57 #include "HTMLImageElement.h"
58 #include "HTMLNames.h"
59 #include "HTMLStyleElement.h"
60 #include "HTMLTableElement.h"
61 #include "HTMLTextAreaElement.h"
62 #include "HTMLTextFormControlElement.h"
63 #include "LibWebRTCProvider.h"
64 #include "MarkupAccumulator.h"
65 #include "NodeList.h"
66 #include "Page.h"
67 #include "PageConfiguration.h"
68 #include "Range.h"
69 #include "RenderBlock.h"
70 #include "RuntimeEnabledFeatures.h"
71 #include "Settings.h"
72 #include "SocketProvider.h"
73 #include "StyleProperties.h"
74 #include "TextIterator.h"
75 #include "TypedElementDescendantIterator.h"
76 #include "URL.h"
77 #include "URLParser.h"
78 #include "VisibleSelection.h"
79 #include "VisibleUnits.h"
80 #include <wtf/StdLibExtras.h>
81 #include <wtf/text/StringBuilder.h>
82
83 namespace WebCore {
84
85 using namespace HTMLNames;
86
87 static bool propertyMissingOrEqualToNone(StyleProperties*, CSSPropertyID);
88
89 class AttributeChange {
90 public:
91     AttributeChange()
92         : m_name(nullAtom(), nullAtom(), nullAtom())
93     {
94     }
95
96     AttributeChange(Element* element, const QualifiedName& name, const String& value)
97         : m_element(element), m_name(name), m_value(value)
98     {
99     }
100
101     void apply()
102     {
103         m_element->setAttribute(m_name, m_value);
104     }
105
106 private:
107     RefPtr<Element> m_element;
108     QualifiedName m_name;
109     String m_value;
110 };
111
112 static void completeURLs(DocumentFragment* fragment, const String& baseURL)
113 {
114     Vector<AttributeChange> changes;
115
116     URL parsedBaseURL({ }, baseURL);
117
118     for (auto& element : descendantsOfType<Element>(*fragment)) {
119         if (!element.hasAttributes())
120             continue;
121         for (const Attribute& attribute : element.attributesIterator()) {
122             if (element.attributeContainsURL(attribute) && !attribute.value().isEmpty())
123                 changes.append(AttributeChange(&element, attribute.name(), element.completeURLsInAttributeValue(parsedBaseURL, attribute)));
124         }
125     }
126
127     for (auto& change : changes)
128         change.apply();
129 }
130
131 void replaceSubresourceURLs(Ref<DocumentFragment>&& fragment, HashMap<AtomicString, AtomicString>&& replacementMap)
132 {
133     Vector<AttributeChange> changes;
134     for (auto& element : descendantsOfType<Element>(fragment)) {
135         if (!element.hasAttributes())
136             continue;
137         for (const Attribute& attribute : element.attributesIterator()) {
138             // FIXME: This won't work for srcset.
139             if (element.attributeContainsURL(attribute) && !attribute.value().isEmpty()) {
140                 auto replacement = replacementMap.get(attribute.value());
141                 if (!replacement.isNull())
142                     changes.append({ &element, attribute.name(), replacement });
143             }
144         }
145     }
146     for (auto& change : changes)
147         change.apply();
148 }
149
150 struct ElementAttribute {
151     Ref<Element> element;
152     QualifiedName attributeName;
153 };
154
155 void removeSubresourceURLAttributes(Ref<DocumentFragment>&& fragment, WTF::Function<bool(const URL&)> shouldRemoveURL)
156 {
157     Vector<ElementAttribute> attributesToRemove;
158     for (auto& element : descendantsOfType<Element>(fragment)) {
159         if (!element.hasAttributes())
160             continue;
161         for (const Attribute& attribute : element.attributesIterator()) {
162             // FIXME: This won't work for srcset.
163             if (element.attributeContainsURL(attribute) && !attribute.value().isEmpty()) {
164                 URL url = URLParser { attribute.value() }.result();
165                 if (shouldRemoveURL(url))
166                     attributesToRemove.append({ element, attribute.name() });
167             }
168         }
169     }
170     for (auto& item : attributesToRemove)
171         item.element->removeAttribute(item.attributeName);
172 }
173
174 std::unique_ptr<Page> createPageForSanitizingWebContent()
175 {
176     PageConfiguration pageConfiguration(createEmptyEditorClient(), SocketProvider::create(), LibWebRTCProvider::create(), CacheStorageProvider::create());
177
178     fillWithEmptyClients(pageConfiguration);
179     
180     auto page = std::make_unique<Page>(WTFMove(pageConfiguration));
181     page->settings().setMediaEnabled(false);
182     page->settings().setScriptEnabled(false);
183     page->settings().setPluginsEnabled(false);
184     page->settings().setAcceleratedCompositingEnabled(false);
185
186     Frame& frame = page->mainFrame();
187     frame.setView(FrameView::create(frame));
188     frame.init();
189
190     FrameLoader& loader = frame.loader();
191     static char markup[] = "<!DOCTYPE html><html><body></body></html>";
192     ASSERT(loader.activeDocumentLoader());
193     auto& writer = loader.activeDocumentLoader()->writer();
194     writer.setMIMEType("text/html");
195     writer.begin();
196     writer.insertDataSynchronously(String(markup));
197     writer.end();
198     RELEASE_ASSERT(page->mainFrame().document()->body());
199
200     return page;
201 }
202
203 String sanitizeMarkup(const String& rawHTML, MSOListQuirks msoListQuirks, std::optional<WTF::Function<void(DocumentFragment&)>> fragmentSanitizer)
204 {
205     auto page = createPageForSanitizingWebContent();
206     Document* stagingDocument = page->mainFrame().document();
207     ASSERT(stagingDocument);
208
209     auto fragment = createFragmentFromMarkup(*stagingDocument, rawHTML, emptyString(), DisallowScriptingAndPluginContent);
210
211     if (fragmentSanitizer)
212         (*fragmentSanitizer)(fragment);
213
214     return sanitizedMarkupForFragmentInDocument(WTFMove(fragment), *stagingDocument, msoListQuirks, rawHTML);
215 }
216
217 enum class MSOListMode { Preserve, DoNotPreserve };
218 class StyledMarkupAccumulator final : public MarkupAccumulator {
219 public:
220     enum RangeFullySelectsNode { DoesFullySelectNode, DoesNotFullySelectNode };
221
222     StyledMarkupAccumulator(const Position& start, const Position& end, Vector<Node*>* nodes,
223         ResolveURLs, AnnotateForInterchange, MSOListMode, bool needsPositionStyleConversion, Node* highestNodeToBeSerialized = nullptr);
224
225     Node* serializeNodes(const Position& start, const Position& end);
226     void wrapWithNode(Node&, bool convertBlocksToInlines = false, RangeFullySelectsNode = DoesFullySelectNode);
227     void wrapWithStyleNode(StyleProperties*, Document&, bool isBlock = false);
228     String takeResults();
229     
230     bool needRelativeStyleWrapper() const { return m_needRelativeStyleWrapper; }
231     bool needClearingDiv() const { return m_needClearingDiv; }
232
233     using MarkupAccumulator::appendString;
234
235 private:
236     void appendStyleNodeOpenTag(StringBuilder&, StyleProperties*, Document&, bool isBlock = false);
237     const String& styleNodeCloseTag(bool isBlock = false);
238
239     String renderedTextRespectingRange(const Text&);
240     String textContentRespectingRange(const Text&);
241
242     bool shouldPreserveMSOListStyleForElement(const Element&);
243
244     void appendElement(StringBuilder& out, const Element&, bool addDisplayInline, RangeFullySelectsNode);
245     void appendCustomAttributes(StringBuilder&, const Element&, Namespaces*) override;
246
247     void appendText(StringBuilder& out, const Text&) override;
248     void appendElement(StringBuilder& out, const Element& element, Namespaces*) override
249     {
250         appendElement(out, element, false, DoesFullySelectNode);
251     }
252
253     enum class NodeTraversalMode { EmitString, DoNotEmitString };
254     Node* traverseNodesForSerialization(Node* startNode, Node* pastEnd, NodeTraversalMode);
255
256     bool appendNodeToPreserveMSOList(Node&);
257
258     bool shouldAnnotate()
259     {
260         return m_annotate == AnnotateForInterchange::Yes;
261     }
262
263     bool shouldApplyWrappingStyle(const Node& node) const
264     {
265         return m_highestNodeToBeSerialized && m_highestNodeToBeSerialized->parentNode() == node.parentNode() && m_wrappingStyle && m_wrappingStyle->style();
266     }
267
268     Position m_start;
269     Position m_end;
270     Vector<String> m_reversedPrecedingMarkup;
271     const AnnotateForInterchange m_annotate;
272     RefPtr<Node> m_highestNodeToBeSerialized;
273     RefPtr<EditingStyle> m_wrappingStyle;
274     bool m_needsPositionStyleConversion;
275     bool m_needRelativeStyleWrapper { false };
276     bool m_needClearingDiv { false };
277     bool m_shouldPreserveMSOList;
278     bool m_inMSOList { false };
279 };
280
281 inline StyledMarkupAccumulator::StyledMarkupAccumulator(const Position& start, const Position& end, Vector<Node*>* nodes,
282     ResolveURLs urlsToResolve, AnnotateForInterchange annotate, MSOListMode msoListMode, bool needsPositionStyleConversion, Node* highestNodeToBeSerialized)
283     : MarkupAccumulator(nodes, urlsToResolve)
284     , m_start(start)
285     , m_end(end)
286     , m_annotate(annotate)
287     , m_highestNodeToBeSerialized(highestNodeToBeSerialized)
288     , m_needsPositionStyleConversion(needsPositionStyleConversion)
289     , m_shouldPreserveMSOList(msoListMode == MSOListMode::Preserve)
290 {
291 }
292
293 void StyledMarkupAccumulator::wrapWithNode(Node& node, bool convertBlocksToInlines, RangeFullySelectsNode rangeFullySelectsNode)
294 {
295     StringBuilder markup;
296     if (is<Element>(node))
297         appendElement(markup, downcast<Element>(node), convertBlocksToInlines && isBlock(&node), rangeFullySelectsNode);
298     else
299         appendStartMarkup(markup, node, nullptr);
300     m_reversedPrecedingMarkup.append(markup.toString());
301     appendEndTag(node);
302     if (m_nodes)
303         m_nodes->append(&node);
304 }
305
306 void StyledMarkupAccumulator::wrapWithStyleNode(StyleProperties* style, Document& document, bool isBlock)
307 {
308     StringBuilder openTag;
309     appendStyleNodeOpenTag(openTag, style, document, isBlock);
310     m_reversedPrecedingMarkup.append(openTag.toString());
311     appendString(styleNodeCloseTag(isBlock));
312 }
313
314 void StyledMarkupAccumulator::appendStyleNodeOpenTag(StringBuilder& out, StyleProperties* style, Document& document, bool isBlock)
315 {
316     // wrappingStyleForSerialization should have removed -webkit-text-decorations-in-effect
317     ASSERT(propertyMissingOrEqualToNone(style, CSSPropertyWebkitTextDecorationsInEffect));
318     if (isBlock)
319         out.appendLiteral("<div style=\"");
320     else
321         out.appendLiteral("<span style=\"");
322     appendAttributeValue(out, style->asText(), document.isHTMLDocument());
323     out.appendLiteral("\">");
324 }
325
326 const String& StyledMarkupAccumulator::styleNodeCloseTag(bool isBlock)
327 {
328     static NeverDestroyed<const String> divClose(MAKE_STATIC_STRING_IMPL("</div>"));
329     static NeverDestroyed<const String> styleSpanClose(MAKE_STATIC_STRING_IMPL("</span>"));
330     return isBlock ? divClose : styleSpanClose;
331 }
332
333 String StyledMarkupAccumulator::takeResults()
334 {
335     StringBuilder result;
336     result.reserveCapacity(totalLength(m_reversedPrecedingMarkup) + length());
337
338     for (size_t i = m_reversedPrecedingMarkup.size(); i > 0; --i)
339         result.append(m_reversedPrecedingMarkup[i - 1]);
340
341     concatenateMarkup(result);
342
343     // We remove '\0' characters because they are not visibly rendered to the user.
344     return result.toString().replaceWithLiteral('\0', "");
345 }
346
347 void StyledMarkupAccumulator::appendText(StringBuilder& out, const Text& text)
348 {    
349     const bool parentIsTextarea = is<HTMLTextAreaElement>(text.parentElement());
350     const bool wrappingSpan = shouldApplyWrappingStyle(text) && !parentIsTextarea;
351     if (wrappingSpan) {
352         RefPtr<EditingStyle> wrappingStyle = m_wrappingStyle->copy();
353         // FIXME: <rdar://problem/5371536> Style rules that match pasted content can change it's appearance
354         // Make sure spans are inline style in paste side e.g. span { display: block }.
355         wrappingStyle->forceInline();
356         // FIXME: Should this be included in forceInline?
357         wrappingStyle->style()->setProperty(CSSPropertyFloat, CSSValueNone);
358
359         appendStyleNodeOpenTag(out, wrappingStyle->style(), text.document());
360     }
361
362     if (!shouldAnnotate() || parentIsTextarea) {
363         auto content = textContentRespectingRange(text);
364         appendCharactersReplacingEntities(out, content, 0, content.length(), entityMaskForText(text));
365     } else {
366         const bool useRenderedText = !enclosingElementWithTag(firstPositionInNode(const_cast<Text*>(&text)), selectTag);
367         String content = useRenderedText ? renderedTextRespectingRange(text) : textContentRespectingRange(text);
368         StringBuilder buffer;
369         appendCharactersReplacingEntities(buffer, content, 0, content.length(), EntityMaskInPCDATA);
370         out.append(convertHTMLTextToInterchangeFormat(buffer.toString(), &text));
371     }
372
373     if (wrappingSpan)
374         out.append(styleNodeCloseTag());
375 }
376     
377 String StyledMarkupAccumulator::renderedTextRespectingRange(const Text& text)
378 {
379     TextIteratorBehavior behavior = TextIteratorDefaultBehavior;
380     Position start = &text == m_start.containerNode() ? m_start : firstPositionInNode(const_cast<Text*>(&text));
381     Position end;
382     if (&text == m_end.containerNode())
383         end = m_end;
384     else {
385         end = lastPositionInNode(const_cast<Text*>(&text));
386         if (!m_end.isNull())
387             behavior = TextIteratorBehavesAsIfNodesFollowing;
388     }
389
390     return plainText(Range::create(text.document(), start, end).ptr(), behavior);
391 }
392
393 String StyledMarkupAccumulator::textContentRespectingRange(const Text& text)
394 {
395     if (m_start.isNull() && m_end.isNull())
396         return text.data();
397
398     unsigned start = 0;
399     unsigned end = std::numeric_limits<unsigned>::max();
400     if (&text == m_start.containerNode())
401         start = m_start.offsetInContainerNode();
402     if (&text == m_end.containerNode())
403         end = m_end.offsetInContainerNode();
404     ASSERT(start < end);
405     return text.data().substring(start, end - start);
406 }
407
408 void StyledMarkupAccumulator::appendCustomAttributes(StringBuilder& out, const Element& element, Namespaces* namespaces)
409 {
410 #if ENABLE(ATTACHMENT_ELEMENT)
411     if (!RuntimeEnabledFeatures::sharedFeatures().attachmentElementEnabled())
412         return;
413     
414     if (is<HTMLAttachmentElement>(element)) {
415         auto& attachment = downcast<HTMLAttachmentElement>(element);
416         appendAttribute(out, element, { webkitattachmentidAttr, attachment.uniqueIdentifier() }, namespaces);
417         if (auto* file = attachment.file()) {
418             // These attributes are only intended for File deserialization, and are removed from the generated attachment
419             // element after we've deserialized and set its backing File, in restoreAttachmentElementsInFragment.
420             appendAttribute(out, element, { webkitattachmentpathAttr, file->path() }, namespaces);
421             appendAttribute(out, element, { webkitattachmentbloburlAttr, file->url().string() }, namespaces);
422         }
423     } else if (is<HTMLImageElement>(element)) {
424         if (auto attachment = downcast<HTMLImageElement>(element).attachmentElement())
425             appendAttribute(out, element, { webkitattachmentidAttr, attachment->uniqueIdentifier() }, namespaces);
426     }
427 #else
428     UNUSED_PARAM(out);
429     UNUSED_PARAM(element);
430     UNUSED_PARAM(namespaces);
431 #endif
432 }
433
434 bool StyledMarkupAccumulator::shouldPreserveMSOListStyleForElement(const Element& element)
435 {
436     if (m_inMSOList)
437         return true;
438     if (m_shouldPreserveMSOList) {
439         auto style = element.getAttribute(styleAttr);
440         return style.startsWith("mso-list:") || style.contains(";mso-list:") || style.contains("\nmso-list:");
441     }
442     return false;
443 }
444
445 void StyledMarkupAccumulator::appendElement(StringBuilder& out, const Element& element, bool addDisplayInline, RangeFullySelectsNode rangeFullySelectsNode)
446 {
447     const bool documentIsHTML = element.document().isHTMLDocument();
448     appendOpenTag(out, element, 0);
449
450     appendCustomAttributes(out, element, nullptr);
451
452     const bool shouldAnnotateOrForceInline = element.isHTMLElement() && (shouldAnnotate() || addDisplayInline);
453     bool shouldOverrideStyleAttr = (shouldAnnotateOrForceInline || shouldApplyWrappingStyle(element)) && !shouldPreserveMSOListStyleForElement(element);
454     if (element.hasAttributes()) {
455         for (const Attribute& attribute : element.attributesIterator()) {
456             // We'll handle the style attribute separately, below.
457             if (attribute.name() == styleAttr && shouldOverrideStyleAttr)
458                 continue;
459             if (element.isEventHandlerAttribute(attribute) || element.isJavaScriptURLAttribute(attribute))
460                 continue;
461             appendAttribute(out, element, attribute, 0);
462         }
463     }
464
465     if (shouldOverrideStyleAttr) {
466         RefPtr<EditingStyle> newInlineStyle;
467
468         if (shouldApplyWrappingStyle(element)) {
469             newInlineStyle = m_wrappingStyle->copy();
470             newInlineStyle->removePropertiesInElementDefaultStyle(*const_cast<Element*>(&element));
471             newInlineStyle->removeStyleConflictingWithStyleOfNode(*const_cast<Element*>(&element));
472         } else
473             newInlineStyle = EditingStyle::create();
474
475         if (is<StyledElement>(element) && downcast<StyledElement>(element).inlineStyle())
476             newInlineStyle->overrideWithStyle(*downcast<StyledElement>(element).inlineStyle());
477
478         if (shouldAnnotateOrForceInline) {
479             if (shouldAnnotate())
480                 newInlineStyle->mergeStyleFromRulesForSerialization(downcast<HTMLElement>(*const_cast<Element*>(&element)));
481
482             if (addDisplayInline)
483                 newInlineStyle->forceInline();
484             
485             if (m_needsPositionStyleConversion) {
486                 m_needRelativeStyleWrapper |= newInlineStyle->convertPositionStyle();
487                 m_needClearingDiv |= newInlineStyle->isFloating();
488             }
489
490             // If the node is not fully selected by the range, then we don't want to keep styles that affect its relationship to the nodes around it
491             // only the ones that affect it and the nodes within it.
492             if (rangeFullySelectsNode == DoesNotFullySelectNode && newInlineStyle->style())
493                 newInlineStyle->style()->removeProperty(CSSPropertyFloat);
494         }
495
496         if (!newInlineStyle->isEmpty()) {
497             out.appendLiteral(" style=\"");
498             appendAttributeValue(out, newInlineStyle->style()->asText(), documentIsHTML);
499             out.append('\"');
500         }
501     }
502
503     appendCloseTag(out, element);
504 }
505
506 Node* StyledMarkupAccumulator::serializeNodes(const Position& start, const Position& end)
507 {
508     ASSERT(comparePositions(start, end) <= 0);
509     auto startNode = start.firstNode();
510     Node* pastEnd = end.computeNodeAfterPosition();
511     if (!pastEnd)
512         pastEnd = NodeTraversal::nextSkippingChildren(*end.containerNode());
513
514     if (!m_highestNodeToBeSerialized) {
515         Node* lastClosed = traverseNodesForSerialization(startNode.get(), pastEnd, NodeTraversalMode::DoNotEmitString);
516         m_highestNodeToBeSerialized = lastClosed;
517     }
518
519     if (m_highestNodeToBeSerialized && m_highestNodeToBeSerialized->parentNode())
520         m_wrappingStyle = EditingStyle::wrappingStyleForSerialization(*m_highestNodeToBeSerialized->parentNode(), shouldAnnotate());
521
522     return traverseNodesForSerialization(startNode.get(), pastEnd, NodeTraversalMode::EmitString);
523 }
524
525 Node* StyledMarkupAccumulator::traverseNodesForSerialization(Node* startNode, Node* pastEnd, NodeTraversalMode traversalMode)
526 {
527     const bool shouldEmit = traversalMode == NodeTraversalMode::EmitString;
528
529     m_inMSOList = false;
530
531     unsigned depth = 0;
532     auto enterNode = [&] (Node& node) {
533         if (UNLIKELY(m_shouldPreserveMSOList) && shouldEmit) {
534             if (appendNodeToPreserveMSOList(node))
535                 return false;
536         }
537
538         if (!node.renderer() && !enclosingElementWithTag(firstPositionInOrBeforeNode(&node), selectTag))
539             return false;
540
541         ++depth;
542         if (shouldEmit)
543             appendStartTag(node);
544
545         return true;
546     };
547
548     Node* lastClosed = nullptr;
549     auto exitNode = [&] (Node& node) {
550         bool closing = depth;
551         if (depth)
552             --depth;
553         if (shouldEmit) {
554             if (closing)
555                 appendEndTag(node);
556             else
557                 wrapWithNode(node);
558         }
559         lastClosed = &node;
560     };
561
562     Node* lastNode = nullptr;
563     Node* next = nullptr;
564     for (Node* n = startNode; n != pastEnd; n = next) {
565         lastNode = n;
566
567         Vector<Node*, 8> exitedAncestors;
568         next = nullptr;
569         if (auto* firstChild = n->firstChild())
570             next = firstChild;
571         else if (auto* nextSibling = n->nextSibling())
572             next = nextSibling;
573         else {
574             for (auto* ancestor = n->parentNode(); ancestor; ancestor = ancestor->parentNode()) {
575                 exitedAncestors.append(ancestor);
576                 if (auto* nextSibling = ancestor->nextSibling()) {
577                     next = nextSibling;
578                     break;
579                 }
580             }
581         }
582
583         if (isBlock(n) && canHaveChildrenForEditing(*n) && next == pastEnd) {
584             // Don't write out empty block containers that aren't fully selected.
585             continue;
586         }
587
588         if (!enterNode(*n)) {
589             next = NodeTraversal::nextSkippingChildren(*n);
590             // Don't skip over pastEnd.
591             if (pastEnd && pastEnd->isDescendantOf(*n))
592                 next = pastEnd;
593         } else {
594             if (!n->hasChildNodes())
595                 exitNode(*n);
596         }
597
598         for (auto* ancestor : exitedAncestors) {
599             if (!depth && next == pastEnd)
600                 break;
601             exitNode(*ancestor);
602         }
603     }
604
605     for (auto* ancestor = (pastEnd ? pastEnd : lastNode)->parentNode(); ancestor && depth; ancestor = ancestor->parentNode())
606         exitNode(*ancestor);
607
608     return lastClosed;
609 }
610
611 bool StyledMarkupAccumulator::appendNodeToPreserveMSOList(Node& node)
612 {
613     if (is<Comment>(node)) {
614         auto& commentNode = downcast<Comment>(node);
615         if (!m_inMSOList && commentNode.data() == "[if !supportLists]")
616             m_inMSOList = true;
617         else if (m_inMSOList && commentNode.data() == "[endif]")
618             m_inMSOList = false;
619         else
620             return false;
621         appendStartTag(commentNode);
622         return true;
623     }
624     if (is<HTMLStyleElement>(node)) {
625         auto* firstChild = node.firstChild();
626         if (!is<Text>(firstChild))
627             return false;
628
629         auto& textChild = downcast<Text>(*firstChild);
630         auto& styleContent = textChild.data();
631
632         const auto msoStyleDefinitionsStart = styleContent.find("/* Style Definitions */");
633         const auto msoListDefinitionsStart = styleContent.find("/* List Definitions */");
634         const auto lastListItem = styleContent.reverseFind("\n@list");
635         if (msoListDefinitionsStart == notFound || lastListItem == notFound)
636             return false;
637         const auto start = msoStyleDefinitionsStart != notFound && msoStyleDefinitionsStart < msoListDefinitionsStart ? msoStyleDefinitionsStart : msoListDefinitionsStart;
638
639         const auto msoListDefinitionsEnd = styleContent.find(";}\n", lastListItem);
640         if (msoListDefinitionsEnd == notFound || start >= msoListDefinitionsEnd)
641             return false;
642
643         appendString("<head><style class=\"" WebKitMSOListQuirksStyle "\">\n<!--\n");
644         appendTextSubstring(textChild, start, msoListDefinitionsEnd - start + 3);
645         appendString("\n-->\n</style></head>");
646
647         return true;
648     }
649     return false;
650 }
651
652 static Node* ancestorToRetainStructureAndAppearanceForBlock(Node* commonAncestorBlock)
653 {
654     if (!commonAncestorBlock)
655         return nullptr;
656
657     if (commonAncestorBlock->hasTagName(tbodyTag) || commonAncestorBlock->hasTagName(trTag)) {
658         ContainerNode* table = commonAncestorBlock->parentNode();
659         while (table && !is<HTMLTableElement>(*table))
660             table = table->parentNode();
661
662         return table;
663     }
664
665     if (isNonTableCellHTMLBlockElement(commonAncestorBlock))
666         return commonAncestorBlock;
667
668     return nullptr;
669 }
670
671 static inline Node* ancestorToRetainStructureAndAppearance(Node* commonAncestor)
672 {
673     return ancestorToRetainStructureAndAppearanceForBlock(enclosingBlock(commonAncestor));
674 }
675
676 static bool propertyMissingOrEqualToNone(StyleProperties* style, CSSPropertyID propertyID)
677 {
678     if (!style)
679         return false;
680     RefPtr<CSSValue> value = style->getPropertyCSSValue(propertyID);
681     if (!value)
682         return true;
683     if (!is<CSSPrimitiveValue>(*value))
684         return false;
685     return downcast<CSSPrimitiveValue>(*value).valueID() == CSSValueNone;
686 }
687
688 static bool needInterchangeNewlineAfter(const VisiblePosition& v)
689 {
690     VisiblePosition next = v.next();
691     Node* upstreamNode = next.deepEquivalent().upstream().deprecatedNode();
692     Node* downstreamNode = v.deepEquivalent().downstream().deprecatedNode();
693     // Add an interchange newline if a paragraph break is selected and a br won't already be added to the markup to represent it.
694     return isEndOfParagraph(v) && isStartOfParagraph(next) && !(upstreamNode->hasTagName(brTag) && upstreamNode == downstreamNode);
695 }
696
697 static RefPtr<EditingStyle> styleFromMatchedRulesAndInlineDecl(Node& node)
698 {
699     if (!is<HTMLElement>(node))
700         return nullptr;
701
702     auto& element = downcast<HTMLElement>(node);
703     RefPtr<EditingStyle> style = EditingStyle::create(element.inlineStyle());
704     style->mergeStyleFromRules(element);
705     return style;
706 }
707
708 static bool isElementPresentational(const Node* node)
709 {
710     return node->hasTagName(uTag) || node->hasTagName(sTag) || node->hasTagName(strikeTag)
711         || node->hasTagName(iTag) || node->hasTagName(emTag) || node->hasTagName(bTag) || node->hasTagName(strongTag);
712 }
713
714 static Node* highestAncestorToWrapMarkup(const Position& start, const Position& end, Node& commonAncestor, AnnotateForInterchange annotate)
715 {
716     Node* specialCommonAncestor = nullptr;
717     if (annotate == AnnotateForInterchange::Yes) {
718         // Include ancestors that aren't completely inside the range but are required to retain 
719         // the structure and appearance of the copied markup.
720         specialCommonAncestor = ancestorToRetainStructureAndAppearance(&commonAncestor);
721
722         if (auto* parentListNode = enclosingNodeOfType(start, isListItem)) {
723             if (!editingIgnoresContent(*parentListNode) && VisibleSelection::selectionFromContentsOfNode(parentListNode) == VisibleSelection(start, end)) {
724                 specialCommonAncestor = parentListNode->parentNode();
725                 while (specialCommonAncestor && !isListHTMLElement(specialCommonAncestor))
726                     specialCommonAncestor = specialCommonAncestor->parentNode();
727             }
728         }
729
730         // Retain the Mail quote level by including all ancestor mail block quotes.
731         if (Node* highestMailBlockquote = highestEnclosingNodeOfType(start, isMailBlockquote, CanCrossEditingBoundary))
732             specialCommonAncestor = highestMailBlockquote;
733     }
734
735     auto* checkAncestor = specialCommonAncestor ? specialCommonAncestor : &commonAncestor;
736     if (checkAncestor->renderer() && checkAncestor->renderer()->containingBlock()) {
737         Node* newSpecialCommonAncestor = highestEnclosingNodeOfType(firstPositionInNode(checkAncestor), &isElementPresentational, CanCrossEditingBoundary, checkAncestor->renderer()->containingBlock()->element());
738         if (newSpecialCommonAncestor)
739             specialCommonAncestor = newSpecialCommonAncestor;
740     }
741
742     // If a single tab is selected, commonAncestor will be a text node inside a tab span.
743     // If two or more tabs are selected, commonAncestor will be the tab span.
744     // In either case, if there is a specialCommonAncestor already, it will necessarily be above 
745     // any tab span that needs to be included.
746     if (!specialCommonAncestor && isTabSpanTextNode(&commonAncestor))
747         specialCommonAncestor = commonAncestor.parentNode();
748     if (!specialCommonAncestor && isTabSpanNode(&commonAncestor))
749         specialCommonAncestor = &commonAncestor;
750
751     if (auto* enclosingAnchor = enclosingElementWithTag(firstPositionInNode(specialCommonAncestor ? specialCommonAncestor : &commonAncestor), aTag))
752         specialCommonAncestor = enclosingAnchor;
753
754     return specialCommonAncestor;
755 }
756
757 static String serializePreservingVisualAppearanceInternal(const Position& start, const Position& end, Vector<Node*>* nodes,
758     AnnotateForInterchange annotate, ConvertBlocksToInlines convertBlocksToInlines, ResolveURLs urlsToResolve, MSOListMode msoListMode)
759 {
760     static NeverDestroyed<const String> interchangeNewlineString(MAKE_STATIC_STRING_IMPL("<br class=\"" AppleInterchangeNewline "\">"));
761
762     if (!comparePositions(start, end))
763         return emptyString();
764
765     RefPtr<Node> commonAncestor = Range::commonAncestorContainer(start.containerNode(), end.containerNode());
766     if (!commonAncestor)
767         return emptyString();
768
769     auto& document = *start.document();
770     document.updateLayoutIgnorePendingStylesheets();
771
772     VisiblePosition visibleStart { start };
773     VisiblePosition visibleEnd { end };
774
775     auto body = makeRefPtr(enclosingElementWithTag(firstPositionInNode(commonAncestor.get()), bodyTag));
776     RefPtr<Element> fullySelectedRoot;
777     // FIXME: Do this for all fully selected blocks, not just the body.
778     if (body && VisiblePosition(firstPositionInNode(body.get())) == visibleStart && VisiblePosition(lastPositionInNode(body.get())) == visibleEnd)
779         fullySelectedRoot = body;
780     bool needsPositionStyleConversion = body && fullySelectedRoot == body && document.settings().shouldConvertPositionStyleOnCopy();
781
782     Node* specialCommonAncestor = highestAncestorToWrapMarkup(start, end, *commonAncestor, annotate);
783
784     StyledMarkupAccumulator accumulator(start, end, nodes, urlsToResolve, annotate, msoListMode, needsPositionStyleConversion, specialCommonAncestor);
785
786     Position startAdjustedForInterchangeNewline = start;
787     if (annotate == AnnotateForInterchange::Yes && needInterchangeNewlineAfter(visibleStart)) {
788         if (visibleStart == visibleEnd.previous())
789             return interchangeNewlineString;
790
791         accumulator.appendString(interchangeNewlineString);
792         startAdjustedForInterchangeNewline = visibleStart.next().deepEquivalent();
793
794         if (comparePositions(startAdjustedForInterchangeNewline, end) >= 0)
795             return interchangeNewlineString;
796     }
797
798     Node* lastClosed = accumulator.serializeNodes(startAdjustedForInterchangeNewline, end);
799
800     if (specialCommonAncestor && lastClosed) {
801         // Also include all of the ancestors of lastClosed up to this special ancestor.
802         for (ContainerNode* ancestor = lastClosed->parentNode(); ancestor; ancestor = ancestor->parentNode()) {
803             if (ancestor == fullySelectedRoot && convertBlocksToInlines == ConvertBlocksToInlines::No) {
804                 RefPtr<EditingStyle> fullySelectedRootStyle = styleFromMatchedRulesAndInlineDecl(*fullySelectedRoot);
805
806                 // Bring the background attribute over, but not as an attribute because a background attribute on a div
807                 // appears to have no effect.
808                 if ((!fullySelectedRootStyle || !fullySelectedRootStyle->style() || !fullySelectedRootStyle->style()->getPropertyCSSValue(CSSPropertyBackgroundImage))
809                     && fullySelectedRoot->hasAttributeWithoutSynchronization(backgroundAttr))
810                     fullySelectedRootStyle->style()->setProperty(CSSPropertyBackgroundImage, "url('" + fullySelectedRoot->getAttribute(backgroundAttr) + "')");
811
812                 if (fullySelectedRootStyle->style()) {
813                     // Reset the CSS properties to avoid an assertion error in addStyleMarkup().
814                     // This assertion is caused at least when we select all text of a <body> element whose
815                     // 'text-decoration' property is "inherit", and copy it.
816                     if (!propertyMissingOrEqualToNone(fullySelectedRootStyle->style(), CSSPropertyTextDecoration))
817                         fullySelectedRootStyle->style()->setProperty(CSSPropertyTextDecoration, CSSValueNone);
818                     if (!propertyMissingOrEqualToNone(fullySelectedRootStyle->style(), CSSPropertyWebkitTextDecorationsInEffect))
819                         fullySelectedRootStyle->style()->setProperty(CSSPropertyWebkitTextDecorationsInEffect, CSSValueNone);
820                     accumulator.wrapWithStyleNode(fullySelectedRootStyle->style(), document, true);
821                 }
822             } else {
823                 // Since this node and all the other ancestors are not in the selection we want to set RangeFullySelectsNode to DoesNotFullySelectNode
824                 // so that styles that affect the exterior of the node are not included.
825                 accumulator.wrapWithNode(*ancestor, convertBlocksToInlines == ConvertBlocksToInlines::Yes, StyledMarkupAccumulator::DoesNotFullySelectNode);
826             }
827             if (nodes)
828                 nodes->append(ancestor);
829             
830             if (ancestor == specialCommonAncestor)
831                 break;
832         }
833     }
834     
835     if (accumulator.needRelativeStyleWrapper() && needsPositionStyleConversion) {
836         if (accumulator.needClearingDiv())
837             accumulator.appendString("<div style=\"clear: both;\"></div>");
838         RefPtr<EditingStyle> positionRelativeStyle = styleFromMatchedRulesAndInlineDecl(*body);
839         positionRelativeStyle->style()->setProperty(CSSPropertyPosition, CSSValueRelative);
840         accumulator.wrapWithStyleNode(positionRelativeStyle->style(), document, true);
841     }
842
843     // FIXME: The interchange newline should be placed in the block that it's in, not after all of the content, unconditionally.
844     if (annotate == AnnotateForInterchange::Yes && needInterchangeNewlineAfter(visibleEnd.previous()))
845         accumulator.appendString(interchangeNewlineString);
846
847     return accumulator.takeResults();
848 }
849
850 String serializePreservingVisualAppearance(const Range& range, Vector<Node*>* nodes, AnnotateForInterchange annotate, ConvertBlocksToInlines convertBlocksToInlines, ResolveURLs urlsToReslve)
851 {
852     return serializePreservingVisualAppearanceInternal(range.startPosition(), range.endPosition(), nodes, annotate, convertBlocksToInlines, urlsToReslve, MSOListMode::DoNotPreserve);
853 }
854
855 String serializePreservingVisualAppearance(const VisibleSelection& selection, ResolveURLs resolveURLs, Vector<Node*>* nodes)
856 {
857     return serializePreservingVisualAppearanceInternal(selection.start(), selection.end(), nodes,
858         AnnotateForInterchange::Yes, ConvertBlocksToInlines::No, resolveURLs, MSOListMode::DoNotPreserve);
859 }
860
861
862 static bool shouldPreserveMSOLists(const String& markup)
863 {
864     if (!markup.startsWith("<html xmlns:"))
865         return false;
866     auto tagClose = markup.find('>');
867     if (tagClose == notFound)
868         return false;
869     auto htmlTag = markup.substring(0, tagClose);
870     return htmlTag.contains("xmlns:o=\"urn:schemas-microsoft-com:office:office\"")
871         && htmlTag.contains("xmlns:w=\"urn:schemas-microsoft-com:office:word\"");
872 }
873
874 String sanitizedMarkupForFragmentInDocument(Ref<DocumentFragment>&& fragment, Document& document, MSOListQuirks msoListQuirks, const String& originalMarkup)
875 {
876     MSOListMode msoListMode = msoListQuirks == MSOListQuirks::CheckIfNeeded && shouldPreserveMSOLists(originalMarkup)
877         ? MSOListMode::Preserve : MSOListMode::DoNotPreserve;
878
879     auto bodyElement = makeRefPtr(document.body());
880     ASSERT(bodyElement);
881     bodyElement->appendChild(fragment.get());
882
883     auto result = serializePreservingVisualAppearanceInternal(firstPositionInNode(bodyElement.get()), lastPositionInNode(bodyElement.get()), nullptr,
884         AnnotateForInterchange::Yes, ConvertBlocksToInlines::No, ResolveURLs::YesExcludingLocalFileURLsForPrivacy, msoListMode);
885
886     if (msoListMode == MSOListMode::Preserve) {
887         StringBuilder builder;
888         builder.appendLiteral("<html xmlns:o=\"urn:schemas-microsoft-com:office:office\"\n"
889             "xmlns:w=\"urn:schemas-microsoft-com:office:word\"\n"
890             "xmlns:m=\"http://schemas.microsoft.com/office/2004/12/omml\"\n"
891             "xmlns=\"http://www.w3.org/TR/REC-html40\">");
892         builder.append(result);
893         builder.appendLiteral("</html>");
894         return builder.toString();
895     }
896
897     return result;
898 }
899
900 static void restoreAttachmentElementsInFragment(DocumentFragment& fragment)
901 {
902 #if ENABLE(ATTACHMENT_ELEMENT)
903     if (!RuntimeEnabledFeatures::sharedFeatures().attachmentElementEnabled())
904         return;
905
906     // When creating a fragment we must strip the webkit-attachment-path attribute after restoring the File object.
907     Vector<Ref<HTMLAttachmentElement>> attachments;
908     for (auto& attachment : descendantsOfType<HTMLAttachmentElement>(fragment))
909         attachments.append(attachment);
910
911     for (auto& attachment : attachments) {
912         attachment->setUniqueIdentifier(attachment->attributeWithoutSynchronization(webkitattachmentidAttr));
913
914         auto attachmentPath = attachment->attachmentPath();
915         auto blobURL = attachment->blobURL();
916         if (!attachmentPath.isEmpty())
917             attachment->setFile(File::create(attachmentPath));
918         else if (!blobURL.isEmpty())
919             attachment->setFile(File::deserialize({ }, blobURL, attachment->attachmentType(), attachment->attachmentTitle()));
920
921         // Remove temporary attributes that were previously added in StyledMarkupAccumulator::appendCustomAttributes.
922         attachment->removeAttribute(webkitattachmentidAttr);
923         attachment->removeAttribute(webkitattachmentpathAttr);
924         attachment->removeAttribute(webkitattachmentbloburlAttr);
925     }
926
927     Vector<Ref<HTMLImageElement>> images;
928     for (auto& image : descendantsOfType<HTMLImageElement>(fragment))
929         images.append(image);
930
931     for (auto& image : images) {
932         auto attachmentIdentifier = image->attributeWithoutSynchronization(webkitattachmentidAttr);
933         if (attachmentIdentifier.isEmpty())
934             continue;
935
936         auto attachment = HTMLAttachmentElement::create(HTMLNames::attachmentTag, *fragment.ownerDocument());
937         attachment->setUniqueIdentifier(attachmentIdentifier);
938         image->setAttachmentElement(WTFMove(attachment));
939         image->removeAttribute(webkitattachmentidAttr);
940     }
941 #else
942     UNUSED_PARAM(fragment);
943 #endif
944 }
945
946 Ref<DocumentFragment> createFragmentFromMarkup(Document& document, const String& markup, const String& baseURL, ParserContentPolicy parserContentPolicy)
947 {
948     // We use a fake body element here to trick the HTML parser into using the InBody insertion mode.
949     auto fakeBody = HTMLBodyElement::create(document);
950     auto fragment = DocumentFragment::create(document);
951
952     fragment->parseHTML(markup, fakeBody.ptr(), parserContentPolicy);
953     restoreAttachmentElementsInFragment(fragment);
954     if (!baseURL.isEmpty() && baseURL != blankURL() && baseURL != document.baseURL())
955         completeURLs(fragment.ptr(), baseURL);
956
957     return fragment;
958 }
959
960 String serializeFragment(const Node& node, SerializedNodes root, Vector<Node*>* nodes, ResolveURLs urlsToResolve, Vector<QualifiedName>* tagNamesToSkip, SerializationSyntax serializationSyntax)
961 {
962     MarkupAccumulator accumulator(nodes, urlsToResolve, serializationSyntax);
963     return accumulator.serializeNodes(const_cast<Node&>(node), root, tagNamesToSkip);
964 }
965
966 static void fillContainerFromString(ContainerNode& paragraph, const String& string)
967 {
968     Document& document = paragraph.document();
969
970     if (string.isEmpty()) {
971         paragraph.appendChild(createBlockPlaceholderElement(document));
972         return;
973     }
974
975     ASSERT(string.find('\n') == notFound);
976
977     Vector<String> tabList = string.splitAllowingEmptyEntries('\t');
978     String tabText = emptyString();
979     bool first = true;
980     size_t numEntries = tabList.size();
981     for (size_t i = 0; i < numEntries; ++i) {
982         const String& s = tabList[i];
983
984         // append the non-tab textual part
985         if (!s.isEmpty()) {
986             if (!tabText.isEmpty()) {
987                 paragraph.appendChild(createTabSpanElement(document, tabText));
988                 tabText = emptyString();
989             }
990             Ref<Node> textNode = document.createTextNode(stringWithRebalancedWhitespace(s, first, i + 1 == numEntries));
991             paragraph.appendChild(textNode);
992         }
993
994         // there is a tab after every entry, except the last entry
995         // (if the last character is a tab, the list gets an extra empty entry)
996         if (i + 1 != numEntries)
997             tabText.append('\t');
998         else if (!tabText.isEmpty())
999             paragraph.appendChild(createTabSpanElement(document, tabText));
1000
1001         first = false;
1002     }
1003 }
1004
1005 bool isPlainTextMarkup(Node* node)
1006 {
1007     ASSERT(node);
1008     if (!is<HTMLDivElement>(*node))
1009         return false;
1010
1011     HTMLDivElement& element = downcast<HTMLDivElement>(*node);
1012     if (element.hasAttributes())
1013         return false;
1014
1015     Node* firstChild = element.firstChild();
1016     if (!firstChild)
1017         return false;
1018
1019     Node* secondChild = firstChild->nextSibling();
1020     if (!secondChild)
1021         return firstChild->isTextNode() || firstChild->firstChild();
1022     
1023     if (secondChild->nextSibling())
1024         return false;
1025     
1026     return isTabSpanTextNode(firstChild->firstChild()) && secondChild->isTextNode();
1027 }
1028
1029 static bool contextPreservesNewline(const Range& context)
1030 {
1031     VisiblePosition position(context.startPosition());
1032     Node* container = position.deepEquivalent().containerNode();
1033     if (!container || !container->renderer())
1034         return false;
1035
1036     return container->renderer()->style().preserveNewline();
1037 }
1038
1039 Ref<DocumentFragment> createFragmentFromText(Range& context, const String& text)
1040 {
1041     Document& document = context.ownerDocument();
1042     Ref<DocumentFragment> fragment = document.createDocumentFragment();
1043     
1044     if (text.isEmpty())
1045         return fragment;
1046
1047     String string = text;
1048     string.replace("\r\n", "\n");
1049     string.replace('\r', '\n');
1050
1051     if (contextPreservesNewline(context)) {
1052         fragment->appendChild(document.createTextNode(string));
1053         if (string.endsWith('\n')) {
1054             auto element = HTMLBRElement::create(document);
1055             element->setAttributeWithoutSynchronization(classAttr, AppleInterchangeNewline);
1056             fragment->appendChild(element);
1057         }
1058         return fragment;
1059     }
1060
1061     // A string with no newlines gets added inline, rather than being put into a paragraph.
1062     if (string.find('\n') == notFound) {
1063         fillContainerFromString(fragment, string);
1064         return fragment;
1065     }
1066
1067     // Break string into paragraphs. Extra line breaks turn into empty paragraphs.
1068     Node* blockNode = enclosingBlock(context.firstNode());
1069     Element* block = downcast<Element>(blockNode);
1070     bool useClonesOfEnclosingBlock = blockNode
1071         && blockNode->isElementNode()
1072         && !block->hasTagName(bodyTag)
1073         && !block->hasTagName(htmlTag)
1074         && block != editableRootForPosition(context.startPosition());
1075     bool useLineBreak = enclosingTextFormControl(context.startPosition());
1076
1077     Vector<String> list = string.splitAllowingEmptyEntries('\n');
1078     size_t numLines = list.size();
1079     for (size_t i = 0; i < numLines; ++i) {
1080         const String& s = list[i];
1081
1082         RefPtr<Element> element;
1083         if (s.isEmpty() && i + 1 == numLines) {
1084             // For last line, use the "magic BR" rather than a P.
1085             element = HTMLBRElement::create(document);
1086             element->setAttributeWithoutSynchronization(classAttr, AppleInterchangeNewline);
1087         } else if (useLineBreak) {
1088             element = HTMLBRElement::create(document);
1089             fillContainerFromString(fragment, s);
1090         } else {
1091             if (useClonesOfEnclosingBlock)
1092                 element = block->cloneElementWithoutChildren(document);
1093             else
1094                 element = createDefaultParagraphElement(document);
1095             fillContainerFromString(*element, s);
1096         }
1097         fragment->appendChild(*element);
1098     }
1099     return fragment;
1100 }
1101
1102 String documentTypeString(const Document& document)
1103 {
1104     DocumentType* documentType = document.doctype();
1105     if (!documentType)
1106         return emptyString();
1107     return serializeFragment(*documentType, SerializedNodes::SubtreeIncludingNode);
1108 }
1109
1110 String urlToMarkup(const URL& url, const String& title)
1111 {
1112     StringBuilder markup;
1113     markup.appendLiteral("<a href=\"");
1114     markup.append(url.string());
1115     markup.appendLiteral("\">");
1116     MarkupAccumulator::appendCharactersReplacingEntities(markup, title, 0, title.length(), EntityMaskInPCDATA);
1117     markup.appendLiteral("</a>");
1118     return markup.toString();
1119 }
1120
1121 ExceptionOr<Ref<DocumentFragment>> createFragmentForInnerOuterHTML(Element& contextElement, const String& markup, ParserContentPolicy parserContentPolicy)
1122 {
1123     auto* document = &contextElement.document();
1124     if (contextElement.hasTagName(templateTag))
1125         document = &document->ensureTemplateDocument();
1126     auto fragment = DocumentFragment::create(*document);
1127
1128     if (document->isHTMLDocument()) {
1129         fragment->parseHTML(markup, &contextElement, parserContentPolicy);
1130         return WTFMove(fragment);
1131     }
1132
1133     bool wasValid = fragment->parseXML(markup, &contextElement, parserContentPolicy);
1134     if (!wasValid)
1135         return Exception { SyntaxError };
1136     return WTFMove(fragment);
1137 }
1138
1139 RefPtr<DocumentFragment> createFragmentForTransformToFragment(Document& outputDoc, const String& sourceString, const String& sourceMIMEType)
1140 {
1141     RefPtr<DocumentFragment> fragment = outputDoc.createDocumentFragment();
1142     
1143     if (sourceMIMEType == "text/html") {
1144         // As far as I can tell, there isn't a spec for how transformToFragment is supposed to work.
1145         // Based on the documentation I can find, it looks like we want to start parsing the fragment in the InBody insertion mode.
1146         // Unfortunately, that's an implementation detail of the parser.
1147         // We achieve that effect here by passing in a fake body element as context for the fragment.
1148         RefPtr<HTMLBodyElement> fakeBody = HTMLBodyElement::create(outputDoc);
1149         fragment->parseHTML(sourceString, fakeBody.get());
1150     } else if (sourceMIMEType == "text/plain")
1151         fragment->parserAppendChild(Text::create(outputDoc, sourceString));
1152     else {
1153         bool successfulParse = fragment->parseXML(sourceString, 0);
1154         if (!successfulParse)
1155             return nullptr;
1156     }
1157     
1158     // FIXME: Do we need to mess with URLs here?
1159     
1160     return fragment;
1161 }
1162
1163 Ref<DocumentFragment> createFragmentForImageAndURL(Document& document, const String& url)
1164 {
1165     auto imageElement = HTMLImageElement::create(document);
1166     imageElement->setAttributeWithoutSynchronization(HTMLNames::srcAttr, url);
1167
1168     auto fragment = document.createDocumentFragment();
1169     fragment->appendChild(imageElement);
1170
1171     return fragment;
1172 }
1173
1174 static Vector<Ref<HTMLElement>> collectElementsToRemoveFromFragment(ContainerNode& container)
1175 {
1176     Vector<Ref<HTMLElement>> toRemove;
1177     for (auto& element : childrenOfType<HTMLElement>(container)) {
1178         if (is<HTMLHtmlElement>(element)) {
1179             toRemove.append(element);
1180             collectElementsToRemoveFromFragment(element);
1181             continue;
1182         }
1183         if (is<HTMLHeadElement>(element) || is<HTMLBodyElement>(element))
1184             toRemove.append(element);
1185     }
1186     return toRemove;
1187 }
1188
1189 static void removeElementFromFragmentPreservingChildren(DocumentFragment& fragment, HTMLElement& element)
1190 {
1191     RefPtr<Node> nextChild;
1192     for (RefPtr<Node> child = element.firstChild(); child; child = nextChild) {
1193         nextChild = child->nextSibling();
1194         element.removeChild(*child);
1195         fragment.insertBefore(*child, &element);
1196     }
1197     fragment.removeChild(element);
1198 }
1199
1200 ExceptionOr<Ref<DocumentFragment>> createContextualFragment(Element& element, const String& markup, ParserContentPolicy parserContentPolicy)
1201 {
1202     auto result = createFragmentForInnerOuterHTML(element, markup, parserContentPolicy);
1203     if (result.hasException())
1204         return result.releaseException();
1205
1206     auto fragment = result.releaseReturnValue();
1207
1208     // We need to pop <html> and <body> elements and remove <head> to
1209     // accommodate folks passing complete HTML documents to make the
1210     // child of an element.
1211     auto toRemove = collectElementsToRemoveFromFragment(fragment);
1212     for (auto& element : toRemove)
1213         removeElementFromFragmentPreservingChildren(fragment, element);
1214
1215     return WTFMove(fragment);
1216 }
1217
1218 static inline bool hasOneChild(ContainerNode& node)
1219 {
1220     Node* firstChild = node.firstChild();
1221     return firstChild && !firstChild->nextSibling();
1222 }
1223
1224 static inline bool hasOneTextChild(ContainerNode& node)
1225 {
1226     return hasOneChild(node) && node.firstChild()->isTextNode();
1227 }
1228
1229 static inline bool hasMutationEventListeners(const Document& document)
1230 {
1231     return document.hasListenerType(Document::DOMSUBTREEMODIFIED_LISTENER)
1232         || document.hasListenerType(Document::DOMNODEINSERTED_LISTENER)
1233         || document.hasListenerType(Document::DOMNODEREMOVED_LISTENER)
1234         || document.hasListenerType(Document::DOMNODEREMOVEDFROMDOCUMENT_LISTENER)
1235         || document.hasListenerType(Document::DOMCHARACTERDATAMODIFIED_LISTENER);
1236 }
1237
1238 // We can use setData instead of replacing Text node as long as script can't observe the difference.
1239 static inline bool canUseSetDataOptimization(const Text& containerChild, const ChildListMutationScope& mutationScope)
1240 {
1241     bool authorScriptMayHaveReference = containerChild.refCount();
1242     return !authorScriptMayHaveReference && !mutationScope.canObserve() && !hasMutationEventListeners(containerChild.document());
1243 }
1244
1245 ExceptionOr<void> replaceChildrenWithFragment(ContainerNode& container, Ref<DocumentFragment>&& fragment)
1246 {
1247     Ref<ContainerNode> containerNode(container);
1248     ChildListMutationScope mutation(containerNode);
1249
1250     if (!fragment->firstChild()) {
1251         containerNode->removeChildren();
1252         return { };
1253     }
1254
1255     auto* containerChild = containerNode->firstChild();
1256     if (containerChild && !containerChild->nextSibling()) {
1257         if (is<Text>(*containerChild) && hasOneTextChild(fragment) && canUseSetDataOptimization(downcast<Text>(*containerChild), mutation)) {
1258             ASSERT(!fragment->firstChild()->refCount());
1259             downcast<Text>(*containerChild).setData(downcast<Text>(*fragment->firstChild()).data());
1260             return { };
1261         }
1262
1263         return containerNode->replaceChild(fragment, *containerChild);
1264     }
1265
1266     containerNode->removeChildren();
1267     return containerNode->appendChild(fragment);
1268 }
1269
1270 }