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