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