Align the innerText setter with the HTML spec and Gecko
[WebKit-https.git] / Source / WebCore / editing / markup.cpp
1 /*
2  * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2013 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 "CSSPrimitiveValue.h"
33 #include "CSSPropertyNames.h"
34 #include "CSSValue.h"
35 #include "CSSValueKeywords.h"
36 #include "ChildListMutationScope.h"
37 #include "DocumentFragment.h"
38 #include "DocumentType.h"
39 #include "Editor.h"
40 #include "ElementIterator.h"
41 #include "ExceptionCode.h"
42 #include "File.h"
43 #include "Frame.h"
44 #include "HTMLAttachmentElement.h"
45 #include "HTMLBRElement.h"
46 #include "HTMLBodyElement.h"
47 #include "HTMLDivElement.h"
48 #include "HTMLHeadElement.h"
49 #include "HTMLHtmlElement.h"
50 #include "HTMLNames.h"
51 #include "HTMLTableElement.h"
52 #include "HTMLTextAreaElement.h"
53 #include "HTMLTextFormControlElement.h"
54 #include "URL.h"
55 #include "MarkupAccumulator.h"
56 #include "NodeList.h"
57 #include "Range.h"
58 #include "RenderBlock.h"
59 #include "Settings.h"
60 #include "StyleProperties.h"
61 #include "TextIterator.h"
62 #include "TypedElementDescendantIterator.h"
63 #include "VisibleSelection.h"
64 #include "VisibleUnits.h"
65 #include "htmlediting.h"
66 #include <wtf/StdLibExtras.h>
67 #include <wtf/text/StringBuilder.h>
68
69 namespace WebCore {
70
71 using namespace HTMLNames;
72
73 static bool propertyMissingOrEqualToNone(StyleProperties*, CSSPropertyID);
74
75 class AttributeChange {
76 public:
77     AttributeChange()
78         : m_name(nullAtom, nullAtom, nullAtom)
79     {
80     }
81
82     AttributeChange(Element* element, const QualifiedName& name, const String& value)
83         : m_element(element), m_name(name), m_value(value)
84     {
85     }
86
87     void apply()
88     {
89         m_element->setAttribute(m_name, m_value);
90     }
91
92 private:
93     RefPtr<Element> m_element;
94     QualifiedName m_name;
95     String m_value;
96 };
97
98 static void completeURLs(DocumentFragment* fragment, const String& baseURL)
99 {
100     Vector<AttributeChange> changes;
101
102     URL parsedBaseURL(ParsedURLString, baseURL);
103
104     for (auto& element : descendantsOfType<Element>(*fragment)) {
105         if (!element.hasAttributes())
106             continue;
107         for (const Attribute& attribute : element.attributesIterator()) {
108             if (element.attributeContainsURL(attribute) && !attribute.value().isEmpty())
109                 changes.append(AttributeChange(&element, attribute.name(), element.completeURLsInAttributeValue(parsedBaseURL, attribute)));
110         }
111     }
112
113     for (auto& change : changes)
114         change.apply();
115 }
116     
117 class StyledMarkupAccumulator final : public MarkupAccumulator {
118 public:
119     enum RangeFullySelectsNode { DoesFullySelectNode, DoesNotFullySelectNode };
120
121     StyledMarkupAccumulator(Vector<Node*>* nodes, EAbsoluteURLs, EAnnotateForInterchange, const Range*, bool needsPositionStyleConversion, Node* highestNodeToBeSerialized = nullptr);
122
123     Node* serializeNodes(Node* startNode, Node* pastEnd);
124     void wrapWithNode(Node&, bool convertBlocksToInlines = false, RangeFullySelectsNode = DoesFullySelectNode);
125     void wrapWithStyleNode(StyleProperties*, Document&, bool isBlock = false);
126     String takeResults();
127     
128     bool needRelativeStyleWrapper() const { return m_needRelativeStyleWrapper; }
129     bool needClearingDiv() const { return m_needClearingDiv; }
130
131     using MarkupAccumulator::appendString;
132
133 private:
134     void appendStyleNodeOpenTag(StringBuilder&, StyleProperties*, Document&, bool isBlock = false);
135     const String& styleNodeCloseTag(bool isBlock = false);
136
137     String renderedText(const Node&, const Range*);
138     String stringValueForRange(const Node&, const Range*);
139
140     void appendElement(StringBuilder& out, const Element&, bool addDisplayInline, RangeFullySelectsNode);
141     void appendCustomAttributes(StringBuilder&, const Element&, Namespaces*) override;
142
143     void appendText(StringBuilder& out, const Text&) override;
144     void appendElement(StringBuilder& out, const Element& element, Namespaces*) override
145     {
146         appendElement(out, element, false, DoesFullySelectNode);
147     }
148
149     enum NodeTraversalMode { EmitString, DoNotEmitString };
150     Node* traverseNodesForSerialization(Node* startNode, Node* pastEnd, NodeTraversalMode);
151
152     bool shouldAnnotate()
153     {
154         return m_shouldAnnotate == AnnotateForInterchange;
155     }
156
157     bool shouldApplyWrappingStyle(const Node& node) const
158     {
159         return m_highestNodeToBeSerialized && m_highestNodeToBeSerialized->parentNode() == node.parentNode() && m_wrappingStyle && m_wrappingStyle->style();
160     }
161
162     Vector<String> m_reversedPrecedingMarkup;
163     const EAnnotateForInterchange m_shouldAnnotate;
164     Node* m_highestNodeToBeSerialized;
165     RefPtr<EditingStyle> m_wrappingStyle;
166     bool m_needRelativeStyleWrapper;
167     bool m_needsPositionStyleConversion;
168     bool m_needClearingDiv;
169 };
170
171 inline StyledMarkupAccumulator::StyledMarkupAccumulator(Vector<Node*>* nodes, EAbsoluteURLs shouldResolveURLs, EAnnotateForInterchange shouldAnnotate, const Range* range, bool needsPositionStyleConversion, Node* highestNodeToBeSerialized)
172     : MarkupAccumulator(nodes, shouldResolveURLs, range)
173     , m_shouldAnnotate(shouldAnnotate)
174     , m_highestNodeToBeSerialized(highestNodeToBeSerialized)
175     , m_needRelativeStyleWrapper(false)
176     , m_needsPositionStyleConversion(needsPositionStyleConversion)
177     , m_needClearingDiv(false)
178 {
179 }
180
181 void StyledMarkupAccumulator::wrapWithNode(Node& node, bool convertBlocksToInlines, RangeFullySelectsNode rangeFullySelectsNode)
182 {
183     StringBuilder markup;
184     if (is<Element>(node))
185         appendElement(markup, downcast<Element>(node), convertBlocksToInlines && isBlock(&node), rangeFullySelectsNode);
186     else
187         appendStartMarkup(markup, node, nullptr);
188     m_reversedPrecedingMarkup.append(markup.toString());
189     appendEndTag(node);
190     if (m_nodes)
191         m_nodes->append(&node);
192 }
193
194 void StyledMarkupAccumulator::wrapWithStyleNode(StyleProperties* style, Document& document, bool isBlock)
195 {
196     StringBuilder openTag;
197     appendStyleNodeOpenTag(openTag, style, document, isBlock);
198     m_reversedPrecedingMarkup.append(openTag.toString());
199     appendString(styleNodeCloseTag(isBlock));
200 }
201
202 void StyledMarkupAccumulator::appendStyleNodeOpenTag(StringBuilder& out, StyleProperties* style, Document& document, bool isBlock)
203 {
204     // wrappingStyleForSerialization should have removed -webkit-text-decorations-in-effect
205     ASSERT(propertyMissingOrEqualToNone(style, CSSPropertyWebkitTextDecorationsInEffect));
206     if (isBlock)
207         out.appendLiteral("<div style=\"");
208     else
209         out.appendLiteral("<span style=\"");
210     appendAttributeValue(out, style->asText(), document.isHTMLDocument());
211     out.appendLiteral("\">");
212 }
213
214 const String& StyledMarkupAccumulator::styleNodeCloseTag(bool isBlock)
215 {
216     static NeverDestroyed<const String> divClose(ASCIILiteral("</div>"));
217     static NeverDestroyed<const String> styleSpanClose(ASCIILiteral("</span>"));
218     return isBlock ? divClose : styleSpanClose;
219 }
220
221 String StyledMarkupAccumulator::takeResults()
222 {
223     StringBuilder result;
224     result.reserveCapacity(totalLength(m_reversedPrecedingMarkup) + length());
225
226     for (size_t i = m_reversedPrecedingMarkup.size(); i > 0; --i)
227         result.append(m_reversedPrecedingMarkup[i - 1]);
228
229     concatenateMarkup(result);
230
231     // We remove '\0' characters because they are not visibly rendered to the user.
232     return result.toString().replaceWithLiteral('\0', "");
233 }
234
235 void StyledMarkupAccumulator::appendText(StringBuilder& out, const Text& text)
236 {    
237     const bool parentIsTextarea = is<HTMLTextAreaElement>(text.parentElement());
238     const bool wrappingSpan = shouldApplyWrappingStyle(text) && !parentIsTextarea;
239     if (wrappingSpan) {
240         RefPtr<EditingStyle> wrappingStyle = m_wrappingStyle->copy();
241         // FIXME: <rdar://problem/5371536> Style rules that match pasted content can change it's appearance
242         // Make sure spans are inline style in paste side e.g. span { display: block }.
243         wrappingStyle->forceInline();
244         // FIXME: Should this be included in forceInline?
245         wrappingStyle->style()->setProperty(CSSPropertyFloat, CSSValueNone);
246
247         appendStyleNodeOpenTag(out, wrappingStyle->style(), text.document());
248     }
249
250     if (!shouldAnnotate() || parentIsTextarea)
251         MarkupAccumulator::appendText(out, text);
252     else {
253         const bool useRenderedText = !enclosingElementWithTag(firstPositionInNode(const_cast<Text*>(&text)), selectTag);
254         String content = useRenderedText ? renderedText(text, m_range) : stringValueForRange(text, m_range);
255         StringBuilder buffer;
256         appendCharactersReplacingEntities(buffer, content, 0, content.length(), EntityMaskInPCDATA);
257         out.append(convertHTMLTextToInterchangeFormat(buffer.toString(), &text));
258     }
259
260     if (wrappingSpan)
261         out.append(styleNodeCloseTag());
262 }
263     
264 String StyledMarkupAccumulator::renderedText(const Node& node, const Range* range)
265 {
266     if (!is<Text>(node))
267         return String();
268
269     const Text& textNode = downcast<Text>(node);
270     unsigned startOffset = 0;
271     unsigned endOffset = textNode.length();
272
273     TextIteratorBehavior behavior = TextIteratorDefaultBehavior;
274     if (range && &node == &range->startContainer())
275         startOffset = range->startOffset();
276     if (range && &node == &range->endContainer())
277         endOffset = range->endOffset();
278     else if (range)
279         behavior = TextIteratorBehavesAsIfNodesFollowing;
280
281     Position start = createLegacyEditingPosition(const_cast<Node*>(&node), startOffset);
282     Position end = createLegacyEditingPosition(const_cast<Node*>(&node), endOffset);
283     return plainText(Range::create(node.document(), start, end).ptr(), behavior);
284 }
285
286 String StyledMarkupAccumulator::stringValueForRange(const Node& node, const Range* range)
287 {
288     if (!range)
289         return node.nodeValue();
290
291     String nodeValue = node.nodeValue();
292     if (&node == &range->endContainer())
293         nodeValue.truncate(range->endOffset());
294     if (&node == &range->startContainer())
295         nodeValue.remove(0, range->startOffset());
296     return nodeValue;
297 }
298
299 void StyledMarkupAccumulator::appendCustomAttributes(StringBuilder& out, const Element&element, Namespaces* namespaces)
300 {
301 #if ENABLE(ATTACHMENT_ELEMENT)
302     if (!is<HTMLAttachmentElement>(element))
303         return;
304     
305     const HTMLAttachmentElement& attachment = downcast<HTMLAttachmentElement>(element);
306     if (attachment.file())
307         appendAttribute(out, element, Attribute(webkitattachmentpathAttr, attachment.file()->path()), namespaces);
308 #else
309     UNUSED_PARAM(out);
310     UNUSED_PARAM(element);
311     UNUSED_PARAM(namespaces);
312 #endif
313 }
314
315 void StyledMarkupAccumulator::appendElement(StringBuilder& out, const Element& element, bool addDisplayInline, RangeFullySelectsNode rangeFullySelectsNode)
316 {
317     const bool documentIsHTML = element.document().isHTMLDocument();
318     appendOpenTag(out, element, 0);
319
320     appendCustomAttributes(out, element, nullptr);
321
322     const bool shouldAnnotateOrForceInline = element.isHTMLElement() && (shouldAnnotate() || addDisplayInline);
323     const bool shouldOverrideStyleAttr = shouldAnnotateOrForceInline || shouldApplyWrappingStyle(element);
324     if (element.hasAttributes()) {
325         for (const Attribute& attribute : element.attributesIterator()) {
326             // We'll handle the style attribute separately, below.
327             if (attribute.name() == styleAttr && shouldOverrideStyleAttr)
328                 continue;
329             appendAttribute(out, element, attribute, 0);
330         }
331     }
332
333     if (shouldOverrideStyleAttr) {
334         RefPtr<EditingStyle> newInlineStyle;
335
336         if (shouldApplyWrappingStyle(element)) {
337             newInlineStyle = m_wrappingStyle->copy();
338             newInlineStyle->removePropertiesInElementDefaultStyle(const_cast<Element*>(&element));
339             newInlineStyle->removeStyleConflictingWithStyleOfNode(const_cast<Element*>(&element));
340         } else
341             newInlineStyle = EditingStyle::create();
342
343         if (is<StyledElement>(element) && downcast<StyledElement>(element).inlineStyle())
344             newInlineStyle->overrideWithStyle(downcast<StyledElement>(element).inlineStyle());
345
346         if (shouldAnnotateOrForceInline) {
347             if (shouldAnnotate())
348                 newInlineStyle->mergeStyleFromRulesForSerialization(downcast<HTMLElement>(const_cast<Element*>(&element)));
349
350             if (addDisplayInline)
351                 newInlineStyle->forceInline();
352             
353             if (m_needsPositionStyleConversion) {
354                 m_needRelativeStyleWrapper |= newInlineStyle->convertPositionStyle();
355                 m_needClearingDiv |= newInlineStyle->isFloating();
356             }
357
358             // 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
359             // only the ones that affect it and the nodes within it.
360             if (rangeFullySelectsNode == DoesNotFullySelectNode && newInlineStyle->style())
361                 newInlineStyle->style()->removeProperty(CSSPropertyFloat);
362         }
363
364         if (!newInlineStyle->isEmpty()) {
365             out.appendLiteral(" style=\"");
366             appendAttributeValue(out, newInlineStyle->style()->asText(), documentIsHTML);
367             out.append('\"');
368         }
369     }
370
371     appendCloseTag(out, element);
372 }
373
374 Node* StyledMarkupAccumulator::serializeNodes(Node* startNode, Node* pastEnd)
375 {
376     if (!m_highestNodeToBeSerialized) {
377         Node* lastClosed = traverseNodesForSerialization(startNode, pastEnd, DoNotEmitString);
378         m_highestNodeToBeSerialized = lastClosed;
379     }
380
381     if (m_highestNodeToBeSerialized && m_highestNodeToBeSerialized->parentNode())
382         m_wrappingStyle = EditingStyle::wrappingStyleForSerialization(m_highestNodeToBeSerialized->parentNode(), shouldAnnotate());
383
384     return traverseNodesForSerialization(startNode, pastEnd, EmitString);
385 }
386
387 Node* StyledMarkupAccumulator::traverseNodesForSerialization(Node* startNode, Node* pastEnd, NodeTraversalMode traversalMode)
388 {
389     const bool shouldEmit = traversalMode == EmitString;
390     Vector<Node*> ancestorsToClose;
391     Node* next;
392     Node* lastClosed = nullptr;
393     for (Node* n = startNode; n != pastEnd; n = next) {
394         // According to <rdar://problem/5730668>, it is possible for n to blow
395         // past pastEnd and become null here. This shouldn't be possible.
396         // This null check will prevent crashes (but create too much markup)
397         // and the ASSERT will hopefully lead us to understanding the problem.
398         ASSERT(n);
399         if (!n)
400             break;
401         
402         next = NodeTraversal::next(*n);
403         bool openedTag = false;
404
405         if (isBlock(n) && canHaveChildrenForEditing(*n) && next == pastEnd) {
406             // Don't write out empty block containers that aren't fully selected.
407             continue;
408         }
409
410         if (!n->renderer() && !enclosingElementWithTag(firstPositionInOrBeforeNode(n), selectTag)) {
411             next = NodeTraversal::nextSkippingChildren(*n);
412             // Don't skip over pastEnd.
413             if (pastEnd && pastEnd->isDescendantOf(*n))
414                 next = pastEnd;
415         } else {
416             // Add the node to the markup if we're not skipping the descendants
417             if (shouldEmit)
418                 appendStartTag(*n);
419
420             // If node has no children, close the tag now.
421             if (!n->hasChildNodes()) {
422                 if (shouldEmit)
423                     appendEndTag(*n);
424                 lastClosed = n;
425             } else {
426                 openedTag = true;
427                 ancestorsToClose.append(n);
428             }
429         }
430
431         // If we didn't insert open tag and there's no more siblings or we're at the end of the traversal, take care of ancestors.
432         // FIXME: What happens if we just inserted open tag and reached the end?
433         if (!openedTag && (!n->nextSibling() || next == pastEnd)) {
434             // Close up the ancestors.
435             while (!ancestorsToClose.isEmpty()) {
436                 Node* ancestor = ancestorsToClose.last();
437                 if (next != pastEnd && next->isDescendantOf(ancestor))
438                     break;
439                 // Not at the end of the range, close ancestors up to sibling of next node.
440                 if (shouldEmit)
441                     appendEndTag(*ancestor);
442                 lastClosed = ancestor;
443                 ancestorsToClose.removeLast();
444             }
445
446             // Surround the currently accumulated markup with markup for ancestors we never opened as we leave the subtree(s) rooted at those ancestors.
447             ContainerNode* nextParent = next ? next->parentNode() : 0;
448             if (next != pastEnd && n != nextParent) {
449                 Node* lastAncestorClosedOrSelf = n->isDescendantOf(lastClosed) ? lastClosed : n;
450                 for (ContainerNode* parent = lastAncestorClosedOrSelf->parentNode(); parent && parent != nextParent; parent = parent->parentNode()) {
451                     // All ancestors that aren't in the ancestorsToClose list should either be a) unrendered:
452                     if (!parent->renderer())
453                         continue;
454                     // or b) ancestors that we never encountered during a pre-order traversal starting at startNode:
455                     ASSERT(startNode->isDescendantOf(*parent));
456                     if (shouldEmit)
457                         wrapWithNode(*parent);
458                     lastClosed = parent;
459                 }
460             }
461         }
462     }
463
464     return lastClosed;
465 }
466
467 static Node* ancestorToRetainStructureAndAppearanceForBlock(Node* commonAncestorBlock)
468 {
469     if (!commonAncestorBlock)
470         return nullptr;
471
472     if (commonAncestorBlock->hasTagName(tbodyTag) || commonAncestorBlock->hasTagName(trTag)) {
473         ContainerNode* table = commonAncestorBlock->parentNode();
474         while (table && !is<HTMLTableElement>(*table))
475             table = table->parentNode();
476
477         return table;
478     }
479
480     if (isNonTableCellHTMLBlockElement(commonAncestorBlock))
481         return commonAncestorBlock;
482
483     return nullptr;
484 }
485
486 static inline Node* ancestorToRetainStructureAndAppearance(Node* commonAncestor)
487 {
488     return ancestorToRetainStructureAndAppearanceForBlock(enclosingBlock(commonAncestor));
489 }
490
491 static bool propertyMissingOrEqualToNone(StyleProperties* style, CSSPropertyID propertyID)
492 {
493     if (!style)
494         return false;
495     RefPtr<CSSValue> value = style->getPropertyCSSValue(propertyID);
496     if (!value)
497         return true;
498     if (!is<CSSPrimitiveValue>(*value))
499         return false;
500     return downcast<CSSPrimitiveValue>(*value).valueID() == CSSValueNone;
501 }
502
503 static bool needInterchangeNewlineAfter(const VisiblePosition& v)
504 {
505     VisiblePosition next = v.next();
506     Node* upstreamNode = next.deepEquivalent().upstream().deprecatedNode();
507     Node* downstreamNode = v.deepEquivalent().downstream().deprecatedNode();
508     // Add an interchange newline if a paragraph break is selected and a br won't already be added to the markup to represent it.
509     return isEndOfParagraph(v) && isStartOfParagraph(next) && !(upstreamNode->hasTagName(brTag) && upstreamNode == downstreamNode);
510 }
511
512 static RefPtr<EditingStyle> styleFromMatchedRulesAndInlineDecl(const Node* node)
513 {
514     if (!node->isHTMLElement())
515         return nullptr;
516
517     // FIXME: Having to const_cast here is ugly, but it is quite a bit of work to untangle
518     // the non-const-ness of styleFromMatchedRulesForElement.
519     HTMLElement* element = const_cast<HTMLElement*>(static_cast<const HTMLElement*>(node));
520     RefPtr<EditingStyle> style = EditingStyle::create(element->inlineStyle());
521     style->mergeStyleFromRules(element);
522     return style;
523 }
524
525 static bool isElementPresentational(const Node* node)
526 {
527     return node->hasTagName(uTag) || node->hasTagName(sTag) || node->hasTagName(strikeTag)
528         || node->hasTagName(iTag) || node->hasTagName(emTag) || node->hasTagName(bTag) || node->hasTagName(strongTag);
529 }
530
531 static Node* highestAncestorToWrapMarkup(const Range* range, EAnnotateForInterchange shouldAnnotate)
532 {
533     auto* commonAncestor = range->commonAncestorContainer();
534     ASSERT(commonAncestor);
535     Node* specialCommonAncestor = nullptr;
536     if (shouldAnnotate == AnnotateForInterchange) {
537         // Include ancestors that aren't completely inside the range but are required to retain 
538         // the structure and appearance of the copied markup.
539         specialCommonAncestor = ancestorToRetainStructureAndAppearance(commonAncestor);
540
541         if (auto* parentListNode = enclosingNodeOfType(firstPositionInOrBeforeNode(range->firstNode()), isListItem)) {
542             if (!editingIgnoresContent(*parentListNode) && WebCore::areRangesEqual(VisibleSelection::selectionFromContentsOfNode(parentListNode).toNormalizedRange().get(), range)) {
543                 specialCommonAncestor = parentListNode->parentNode();
544                 while (specialCommonAncestor && !isListHTMLElement(specialCommonAncestor))
545                     specialCommonAncestor = specialCommonAncestor->parentNode();
546             }
547         }
548
549         // Retain the Mail quote level by including all ancestor mail block quotes.
550         if (Node* highestMailBlockquote = highestEnclosingNodeOfType(firstPositionInOrBeforeNode(range->firstNode()), isMailBlockquote, CanCrossEditingBoundary))
551             specialCommonAncestor = highestMailBlockquote;
552     }
553
554     auto* checkAncestor = specialCommonAncestor ? specialCommonAncestor : commonAncestor;
555     if (checkAncestor->renderer() && checkAncestor->renderer()->containingBlock()) {
556         Node* newSpecialCommonAncestor = highestEnclosingNodeOfType(firstPositionInNode(checkAncestor), &isElementPresentational, CanCrossEditingBoundary, checkAncestor->renderer()->containingBlock()->element());
557         if (newSpecialCommonAncestor)
558             specialCommonAncestor = newSpecialCommonAncestor;
559     }
560
561     // If a single tab is selected, commonAncestor will be a text node inside a tab span.
562     // If two or more tabs are selected, commonAncestor will be the tab span.
563     // In either case, if there is a specialCommonAncestor already, it will necessarily be above 
564     // any tab span that needs to be included.
565     if (!specialCommonAncestor && isTabSpanTextNode(commonAncestor))
566         specialCommonAncestor = commonAncestor->parentNode();
567     if (!specialCommonAncestor && isTabSpanNode(commonAncestor))
568         specialCommonAncestor = commonAncestor;
569
570     if (auto* enclosingAnchor = enclosingElementWithTag(firstPositionInNode(specialCommonAncestor ? specialCommonAncestor : commonAncestor), aTag))
571         specialCommonAncestor = enclosingAnchor;
572
573     return specialCommonAncestor;
574 }
575
576 // FIXME: Shouldn't we omit style info when annotate == DoNotAnnotateForInterchange? 
577 // FIXME: At least, annotation and style info should probably not be included in range.markupString()
578 static String createMarkupInternal(Document& document, const Range& range, Vector<Node*>* nodes,
579     EAnnotateForInterchange shouldAnnotate, bool convertBlocksToInlines, EAbsoluteURLs shouldResolveURLs)
580 {
581     static NeverDestroyed<const String> interchangeNewlineString(ASCIILiteral("<br class=\"" AppleInterchangeNewline "\">"));
582
583     bool collapsed = range.collapsed();
584     if (collapsed)
585         return emptyString();
586     Node* commonAncestor = range.commonAncestorContainer();
587     if (!commonAncestor)
588         return emptyString();
589
590     document.updateLayoutIgnorePendingStylesheets();
591
592     auto* body = enclosingElementWithTag(firstPositionInNode(commonAncestor), bodyTag);
593     Element* fullySelectedRoot = nullptr;
594     // FIXME: Do this for all fully selected blocks, not just the body.
595     if (body && VisiblePosition(firstPositionInNode(body)) == VisiblePosition(range.startPosition())
596         && VisiblePosition(lastPositionInNode(body)) == VisiblePosition(range.endPosition()))
597         fullySelectedRoot = body;
598     Node* specialCommonAncestor = highestAncestorToWrapMarkup(&range, shouldAnnotate);
599
600     bool needsPositionStyleConversion = body && fullySelectedRoot == body
601         && document.settings() && document.settings()->shouldConvertPositionStyleOnCopy();
602     StyledMarkupAccumulator accumulator(nodes, shouldResolveURLs, shouldAnnotate, &range, needsPositionStyleConversion, specialCommonAncestor);
603     Node* pastEnd = range.pastLastNode();
604
605     Node* startNode = range.firstNode();
606     VisiblePosition visibleStart(range.startPosition(), VP_DEFAULT_AFFINITY);
607     VisiblePosition visibleEnd(range.endPosition(), VP_DEFAULT_AFFINITY);
608     if (shouldAnnotate == AnnotateForInterchange && needInterchangeNewlineAfter(visibleStart)) {
609         if (visibleStart == visibleEnd.previous())
610             return interchangeNewlineString;
611
612         accumulator.appendString(interchangeNewlineString);
613         startNode = visibleStart.next().deepEquivalent().deprecatedNode();
614
615         if (pastEnd && Range::compareBoundaryPoints(startNode, 0, pastEnd, 0).releaseReturnValue() >= 0)
616             return interchangeNewlineString;
617     }
618
619     Node* lastClosed = accumulator.serializeNodes(startNode, pastEnd);
620
621     if (specialCommonAncestor && lastClosed) {
622         // Also include all of the ancestors of lastClosed up to this special ancestor.
623         for (ContainerNode* ancestor = lastClosed->parentNode(); ancestor; ancestor = ancestor->parentNode()) {
624             if (ancestor == fullySelectedRoot && !convertBlocksToInlines) {
625                 RefPtr<EditingStyle> fullySelectedRootStyle = styleFromMatchedRulesAndInlineDecl(fullySelectedRoot);
626
627                 // Bring the background attribute over, but not as an attribute because a background attribute on a div
628                 // appears to have no effect.
629                 if ((!fullySelectedRootStyle || !fullySelectedRootStyle->style() || !fullySelectedRootStyle->style()->getPropertyCSSValue(CSSPropertyBackgroundImage))
630                     && fullySelectedRoot->hasAttributeWithoutSynchronization(backgroundAttr))
631                     fullySelectedRootStyle->style()->setProperty(CSSPropertyBackgroundImage, "url('" + fullySelectedRoot->getAttribute(backgroundAttr) + "')");
632
633                 if (fullySelectedRootStyle->style()) {
634                     // Reset the CSS properties to avoid an assertion error in addStyleMarkup().
635                     // This assertion is caused at least when we select all text of a <body> element whose
636                     // 'text-decoration' property is "inherit", and copy it.
637                     if (!propertyMissingOrEqualToNone(fullySelectedRootStyle->style(), CSSPropertyTextDecoration))
638                         fullySelectedRootStyle->style()->setProperty(CSSPropertyTextDecoration, CSSValueNone);
639                     if (!propertyMissingOrEqualToNone(fullySelectedRootStyle->style(), CSSPropertyWebkitTextDecorationsInEffect))
640                         fullySelectedRootStyle->style()->setProperty(CSSPropertyWebkitTextDecorationsInEffect, CSSValueNone);
641                     accumulator.wrapWithStyleNode(fullySelectedRootStyle->style(), document, true);
642                 }
643             } else {
644                 // Since this node and all the other ancestors are not in the selection we want to set RangeFullySelectsNode to DoesNotFullySelectNode
645                 // so that styles that affect the exterior of the node are not included.
646                 accumulator.wrapWithNode(*ancestor, convertBlocksToInlines, StyledMarkupAccumulator::DoesNotFullySelectNode);
647             }
648             if (nodes)
649                 nodes->append(ancestor);
650             
651             if (ancestor == specialCommonAncestor)
652                 break;
653         }
654     }
655     
656     if (accumulator.needRelativeStyleWrapper() && needsPositionStyleConversion) {
657         if (accumulator.needClearingDiv())
658             accumulator.appendString("<div style=\"clear: both;\"></div>");
659         RefPtr<EditingStyle> positionRelativeStyle = styleFromMatchedRulesAndInlineDecl(body);
660         positionRelativeStyle->style()->setProperty(CSSPropertyPosition, CSSValueRelative);
661         accumulator.wrapWithStyleNode(positionRelativeStyle->style(), document, true);
662     }
663
664     // FIXME: The interchange newline should be placed in the block that it's in, not after all of the content, unconditionally.
665     if (shouldAnnotate == AnnotateForInterchange && needInterchangeNewlineAfter(visibleEnd.previous()))
666         accumulator.appendString(interchangeNewlineString);
667
668     return accumulator.takeResults();
669 }
670
671 String createMarkup(const Range& range, Vector<Node*>* nodes, EAnnotateForInterchange shouldAnnotate, bool convertBlocksToInlines, EAbsoluteURLs shouldResolveURLs)
672 {
673     return createMarkupInternal(range.ownerDocument(), range, nodes, shouldAnnotate, convertBlocksToInlines, shouldResolveURLs);
674 }
675
676 Ref<DocumentFragment> createFragmentFromMarkup(Document& document, const String& markup, const String& baseURL, ParserContentPolicy parserContentPolicy)
677 {
678     // We use a fake body element here to trick the HTML parser to using the InBody insertion mode.
679     auto fakeBody = HTMLBodyElement::create(document);
680     auto fragment = DocumentFragment::create(document);
681
682     fragment->parseHTML(markup, fakeBody.ptr(), parserContentPolicy);
683
684 #if ENABLE(ATTACHMENT_ELEMENT)
685     // When creating a fragment we must strip the webkit-attachment-path attribute after restoring the File object.
686     Vector<Ref<HTMLAttachmentElement>> attachments;
687     for (auto& attachment : descendantsOfType<HTMLAttachmentElement>(fragment))
688         attachments.append(attachment);
689
690     for (auto& attachment : attachments) {
691         attachment->setFile(File::create(attachment->attributeWithoutSynchronization(webkitattachmentpathAttr)).ptr());
692         attachment->removeAttribute(webkitattachmentpathAttr);
693     }
694 #endif
695     if (!baseURL.isEmpty() && baseURL != blankURL() && baseURL != document.baseURL())
696         completeURLs(fragment.ptr(), baseURL);
697
698     return fragment;
699 }
700
701 String createMarkup(const Node& node, EChildrenOnly childrenOnly, Vector<Node*>* nodes, EAbsoluteURLs shouldResolveURLs, Vector<QualifiedName>* tagNamesToSkip, EFragmentSerialization fragmentSerialization)
702 {
703     MarkupAccumulator accumulator(nodes, shouldResolveURLs, 0, fragmentSerialization);
704     return accumulator.serializeNodes(const_cast<Node&>(node), childrenOnly, tagNamesToSkip);
705 }
706
707 static void fillContainerFromString(ContainerNode& paragraph, const String& string)
708 {
709     Document& document = paragraph.document();
710
711     if (string.isEmpty()) {
712         paragraph.appendChild(createBlockPlaceholderElement(document));
713         return;
714     }
715
716     ASSERT(string.find('\n') == notFound);
717
718     Vector<String> tabList;
719     string.split('\t', true, tabList);
720     String tabText = emptyString();
721     bool first = true;
722     size_t numEntries = tabList.size();
723     for (size_t i = 0; i < numEntries; ++i) {
724         const String& s = tabList[i];
725
726         // append the non-tab textual part
727         if (!s.isEmpty()) {
728             if (!tabText.isEmpty()) {
729                 paragraph.appendChild(createTabSpanElement(document, tabText));
730                 tabText = emptyString();
731             }
732             Ref<Node> textNode = document.createTextNode(stringWithRebalancedWhitespace(s, first, i + 1 == numEntries));
733             paragraph.appendChild(textNode);
734         }
735
736         // there is a tab after every entry, except the last entry
737         // (if the last character is a tab, the list gets an extra empty entry)
738         if (i + 1 != numEntries)
739             tabText.append('\t');
740         else if (!tabText.isEmpty())
741             paragraph.appendChild(createTabSpanElement(document, tabText));
742
743         first = false;
744     }
745 }
746
747 bool isPlainTextMarkup(Node* node)
748 {
749     ASSERT(node);
750     if (!is<HTMLDivElement>(*node))
751         return false;
752
753     HTMLDivElement& element = downcast<HTMLDivElement>(*node);
754     if (element.hasAttributes())
755         return false;
756
757     Node* firstChild = element.firstChild();
758     if (!firstChild)
759         return false;
760
761     Node* secondChild = firstChild->nextSibling();
762     if (!secondChild)
763         return firstChild->isTextNode() || firstChild->firstChild();
764     
765     if (secondChild->nextSibling())
766         return false;
767     
768     return isTabSpanTextNode(firstChild->firstChild()) && secondChild->isTextNode();
769 }
770
771 static bool contextPreservesNewline(const Range& context)
772 {
773     VisiblePosition position(context.startPosition());
774     Node* container = position.deepEquivalent().containerNode();
775     if (!container || !container->renderer())
776         return false;
777
778     return container->renderer()->style().preserveNewline();
779 }
780
781 Ref<DocumentFragment> createFragmentFromText(Range& context, const String& text)
782 {
783     Document& document = context.ownerDocument();
784     Ref<DocumentFragment> fragment = document.createDocumentFragment();
785     
786     if (text.isEmpty())
787         return fragment;
788
789     String string = text;
790     string.replace("\r\n", "\n");
791     string.replace('\r', '\n');
792
793     if (contextPreservesNewline(context)) {
794         fragment->appendChild(document.createTextNode(string));
795         if (string.endsWith('\n')) {
796             auto element = HTMLBRElement::create(document);
797             element->setAttributeWithoutSynchronization(classAttr, AppleInterchangeNewline);
798             fragment->appendChild(element);
799         }
800         return fragment;
801     }
802
803     // A string with no newlines gets added inline, rather than being put into a paragraph.
804     if (string.find('\n') == notFound) {
805         fillContainerFromString(fragment, string);
806         return fragment;
807     }
808
809     // Break string into paragraphs. Extra line breaks turn into empty paragraphs.
810     Node* blockNode = enclosingBlock(context.firstNode());
811     Element* block = downcast<Element>(blockNode);
812     bool useClonesOfEnclosingBlock = blockNode
813         && blockNode->isElementNode()
814         && !block->hasTagName(bodyTag)
815         && !block->hasTagName(htmlTag)
816         && block != editableRootForPosition(context.startPosition());
817     bool useLineBreak = enclosingTextFormControl(context.startPosition());
818
819     Vector<String> list;
820     string.split('\n', true, list); // true gets us empty strings in the list
821     size_t numLines = list.size();
822     for (size_t i = 0; i < numLines; ++i) {
823         const String& s = list[i];
824
825         RefPtr<Element> element;
826         if (s.isEmpty() && i + 1 == numLines) {
827             // For last line, use the "magic BR" rather than a P.
828             element = HTMLBRElement::create(document);
829             element->setAttributeWithoutSynchronization(classAttr, AppleInterchangeNewline);
830         } else if (useLineBreak) {
831             element = HTMLBRElement::create(document);
832             fillContainerFromString(fragment, s);
833         } else {
834             if (useClonesOfEnclosingBlock)
835                 element = block->cloneElementWithoutChildren(document);
836             else
837                 element = createDefaultParagraphElement(document);
838             fillContainerFromString(*element, s);
839         }
840         fragment->appendChild(*element);
841     }
842     return fragment;
843 }
844
845 String documentTypeString(const Document& document)
846 {
847     DocumentType* documentType = document.doctype();
848     if (!documentType)
849         return emptyString();
850     return createMarkup(*documentType);
851 }
852
853 String createFullMarkup(const Node& node)
854 {
855     // FIXME: This is never "for interchange". Is that right?
856     String markupString = createMarkup(node, IncludeNode, 0);
857
858     Node::NodeType nodeType = node.nodeType();
859     if (nodeType != Node::DOCUMENT_NODE && nodeType != Node::DOCUMENT_TYPE_NODE)
860         markupString = documentTypeString(node.document()) + markupString;
861
862     return markupString;
863 }
864
865 String createFullMarkup(const Range& range)
866 {
867     // FIXME: This is always "for interchange". Is that right?
868     return documentTypeString(range.startContainer().document()) + createMarkup(range, 0, AnnotateForInterchange);
869 }
870
871 String urlToMarkup(const URL& url, const String& title)
872 {
873     StringBuilder markup;
874     markup.appendLiteral("<a href=\"");
875     markup.append(url.string());
876     markup.appendLiteral("\">");
877     MarkupAccumulator::appendCharactersReplacingEntities(markup, title, 0, title.length(), EntityMaskInPCDATA);
878     markup.appendLiteral("</a>");
879     return markup.toString();
880 }
881
882 ExceptionOr<Ref<DocumentFragment>> createFragmentForInnerOuterHTML(Element& contextElement, const String& markup, ParserContentPolicy parserContentPolicy)
883 {
884     auto* document = &contextElement.document();
885     if (contextElement.hasTagName(templateTag))
886         document = &document->ensureTemplateDocument();
887     auto fragment = DocumentFragment::create(*document);
888
889     if (document->isHTMLDocument()) {
890         fragment->parseHTML(markup, &contextElement, parserContentPolicy);
891         return WTFMove(fragment);
892     }
893
894     bool wasValid = fragment->parseXML(markup, &contextElement, parserContentPolicy);
895     if (!wasValid)
896         return Exception { SYNTAX_ERR };
897     return WTFMove(fragment);
898 }
899
900 RefPtr<DocumentFragment> createFragmentForTransformToFragment(Document& outputDoc, const String& sourceString, const String& sourceMIMEType)
901 {
902     RefPtr<DocumentFragment> fragment = outputDoc.createDocumentFragment();
903     
904     if (sourceMIMEType == "text/html") {
905         // As far as I can tell, there isn't a spec for how transformToFragment is supposed to work.
906         // Based on the documentation I can find, it looks like we want to start parsing the fragment in the InBody insertion mode.
907         // Unfortunately, that's an implementation detail of the parser.
908         // We achieve that effect here by passing in a fake body element as context for the fragment.
909         RefPtr<HTMLBodyElement> fakeBody = HTMLBodyElement::create(outputDoc);
910         fragment->parseHTML(sourceString, fakeBody.get());
911     } else if (sourceMIMEType == "text/plain")
912         fragment->parserAppendChild(Text::create(outputDoc, sourceString));
913     else {
914         bool successfulParse = fragment->parseXML(sourceString, 0);
915         if (!successfulParse)
916             return nullptr;
917     }
918     
919     // FIXME: Do we need to mess with URLs here?
920     
921     return fragment;
922 }
923
924 static Vector<Ref<HTMLElement>> collectElementsToRemoveFromFragment(ContainerNode& container)
925 {
926     Vector<Ref<HTMLElement>> toRemove;
927     for (auto& element : childrenOfType<HTMLElement>(container)) {
928         if (is<HTMLHtmlElement>(element)) {
929             toRemove.append(element);
930             collectElementsToRemoveFromFragment(element);
931             continue;
932         }
933         if (is<HTMLHeadElement>(element) || is<HTMLBodyElement>(element))
934             toRemove.append(element);
935     }
936     return toRemove;
937 }
938
939 static void removeElementFromFragmentPreservingChildren(DocumentFragment& fragment, HTMLElement& element)
940 {
941     RefPtr<Node> nextChild;
942     for (RefPtr<Node> child = element.firstChild(); child; child = nextChild) {
943         nextChild = child->nextSibling();
944         element.removeChild(*child);
945         fragment.insertBefore(*child, &element);
946     }
947     fragment.removeChild(element);
948 }
949
950 ExceptionOr<Ref<DocumentFragment>> createContextualFragment(Element& element, const String& markup, ParserContentPolicy parserContentPolicy)
951 {
952     auto result = createFragmentForInnerOuterHTML(element, markup, parserContentPolicy);
953     if (result.hasException())
954         return result.releaseException();
955
956     auto fragment = result.releaseReturnValue();
957
958     // We need to pop <html> and <body> elements and remove <head> to
959     // accommodate folks passing complete HTML documents to make the
960     // child of an element.
961     auto toRemove = collectElementsToRemoveFromFragment(fragment);
962     for (auto& element : toRemove)
963         removeElementFromFragmentPreservingChildren(fragment, element);
964
965     return WTFMove(fragment);
966 }
967
968 static inline bool hasOneChild(ContainerNode& node)
969 {
970     Node* firstChild = node.firstChild();
971     return firstChild && !firstChild->nextSibling();
972 }
973
974 static inline bool hasOneTextChild(ContainerNode& node)
975 {
976     return hasOneChild(node) && node.firstChild()->isTextNode();
977 }
978
979 static inline bool hasMutationEventListeners(const Document& document)
980 {
981     return document.hasListenerType(Document::DOMSUBTREEMODIFIED_LISTENER)
982         || document.hasListenerType(Document::DOMNODEINSERTED_LISTENER)
983         || document.hasListenerType(Document::DOMNODEREMOVED_LISTENER)
984         || document.hasListenerType(Document::DOMNODEREMOVEDFROMDOCUMENT_LISTENER)
985         || document.hasListenerType(Document::DOMCHARACTERDATAMODIFIED_LISTENER);
986 }
987
988 // We can use setData instead of replacing Text node as long as script can't observe the difference.
989 static inline bool canUseSetDataOptimization(const Text& containerChild, const ChildListMutationScope& mutationScope)
990 {
991     bool authorScriptMayHaveReference = containerChild.refCount();
992     return !authorScriptMayHaveReference && !mutationScope.canObserve() && !hasMutationEventListeners(containerChild.document());
993 }
994
995 ExceptionOr<void> replaceChildrenWithFragment(ContainerNode& container, Ref<DocumentFragment>&& fragment)
996 {
997     Ref<ContainerNode> containerNode(container);
998     ChildListMutationScope mutation(containerNode);
999
1000     if (!fragment->firstChild()) {
1001         containerNode->removeChildren();
1002         return { };
1003     }
1004
1005     auto* containerChild = containerNode->firstChild();
1006     if (containerChild && !containerChild->nextSibling()) {
1007         if (is<Text>(*containerChild) && hasOneTextChild(fragment) && canUseSetDataOptimization(downcast<Text>(*containerChild), mutation)) {
1008             ASSERT(!fragment->firstChild()->refCount());
1009             downcast<Text>(*containerChild).setData(downcast<Text>(*fragment->firstChild()).data());
1010             return { };
1011         }
1012
1013         return containerNode->replaceChild(fragment, *containerChild);
1014     }
1015
1016     containerNode->removeChildren();
1017     return containerNode->appendChild(fragment);
1018 }
1019
1020 ExceptionOr<void> replaceChildrenWithText(ContainerNode& container, const String& text)
1021 {
1022     Ref<ContainerNode> containerNode(container);
1023     ChildListMutationScope mutation(containerNode);
1024
1025     if (hasOneTextChild(containerNode)) {
1026         downcast<Text>(*containerNode->firstChild()).setData(text);
1027         return { };
1028     }
1029
1030     auto textNode = Text::create(containerNode->document(), text);
1031
1032     if (hasOneChild(containerNode))
1033         return containerNode->replaceChild(textNode, *containerNode->firstChild());
1034
1035     containerNode->removeChildren();
1036     return containerNode->appendChild(textNode);
1037 }
1038
1039 }