Turn avoidIntersectionWithNode into Editor member functions to encapsulate delete...
[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     // 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 #if ENABLE(DELETION_UI)
645     Frame* frame = document->frame();
646     if (DeleteButtonController* deleteButton = frame ? frame->editor()->deleteButtonController() : 0) {
647         RefPtr<Range> updatedRange = frame->editor()->avoidIntersectionWithDeleteButtonController(range);
648         if (!updatedRange)
649             return emptyString();
650
651         deleteButton->disable();
652
653         String result = createMarkupInternal(document, range, updatedRange.get(), nodes, shouldAnnotate, convertBlocksToInlines, shouldResolveURLs);
654
655         deleteButton->enable();
656
657         return result;
658     }
659 #endif
660     return createMarkupInternal(document, range, range, nodes, shouldAnnotate, convertBlocksToInlines, shouldResolveURLs);
661 }
662
663 PassRefPtr<DocumentFragment> createFragmentFromMarkup(Document* document, const String& markup, const String& baseURL, FragmentScriptingPermission scriptingPermission)
664 {
665     // We use a fake body element here to trick the HTML parser to using the InBody insertion mode.
666     RefPtr<HTMLBodyElement> fakeBody = HTMLBodyElement::create(document);
667     RefPtr<DocumentFragment> fragment = DocumentFragment::create(document);
668
669     if (scriptingPermission == DisallowScriptingAndPluginContentIfNeeded && (!document->settings() || document->settings()->unsafePluginPastingEnabled()))
670         scriptingPermission = DisallowScriptingContent;
671
672     fragment->parseHTML(markup, fakeBody.get(), scriptingPermission);
673
674     if (!baseURL.isEmpty() && baseURL != blankURL() && baseURL != document->baseURL())
675         completeURLs(fragment.get(), baseURL);
676
677     return fragment.release();
678 }
679
680 static const char fragmentMarkerTag[] = "webkit-fragment-marker";
681
682 static bool findNodesSurroundingContext(Document* document, RefPtr<Node>& nodeBeforeContext, RefPtr<Node>& nodeAfterContext)
683 {
684     for (Node* node = document->firstChild(); node; node = NodeTraversal::next(node)) {
685         if (node->nodeType() == Node::COMMENT_NODE && static_cast<CharacterData*>(node)->data() == fragmentMarkerTag) {
686             if (!nodeBeforeContext)
687                 nodeBeforeContext = node;
688             else {
689                 nodeAfterContext = node;
690                 return true;
691             }
692         }
693     }
694     return false;
695 }
696
697 static void trimFragment(DocumentFragment* fragment, Node* nodeBeforeContext, Node* nodeAfterContext)
698 {
699     RefPtr<Node> next;
700     for (RefPtr<Node> node = fragment->firstChild(); node; node = next) {
701         if (nodeBeforeContext->isDescendantOf(node.get())) {
702             next = NodeTraversal::next(node.get());
703             continue;
704         }
705         next = NodeTraversal::nextSkippingChildren(node.get());
706         ASSERT(!node->contains(nodeAfterContext));
707         node->parentNode()->removeChild(node.get(), ASSERT_NO_EXCEPTION);
708         if (nodeBeforeContext == node)
709             break;
710     }
711
712     ASSERT(nodeAfterContext->parentNode());
713     for (RefPtr<Node> node = nodeAfterContext; node; node = next) {
714         next = NodeTraversal::nextSkippingChildren(node.get());
715         node->parentNode()->removeChild(node.get(), ASSERT_NO_EXCEPTION);
716     }
717 }
718
719 PassRefPtr<DocumentFragment> createFragmentFromMarkupWithContext(Document* document, const String& markup, unsigned fragmentStart, unsigned fragmentEnd,
720     const String& baseURL, FragmentScriptingPermission scriptingPermission)
721 {
722     // FIXME: Need to handle the case where the markup already contains these markers.
723
724     StringBuilder taggedMarkup;
725     taggedMarkup.append(markup.left(fragmentStart));
726     MarkupAccumulator::appendComment(taggedMarkup, fragmentMarkerTag);
727     taggedMarkup.append(markup.substring(fragmentStart, fragmentEnd - fragmentStart));
728     MarkupAccumulator::appendComment(taggedMarkup, fragmentMarkerTag);
729     taggedMarkup.append(markup.substring(fragmentEnd));
730
731     RefPtr<DocumentFragment> taggedFragment = createFragmentFromMarkup(document, taggedMarkup.toString(), baseURL, scriptingPermission);
732     RefPtr<Document> taggedDocument = Document::create(0, KURL());
733     taggedDocument->setContextFeatures(document->contextFeatures());
734     taggedDocument->takeAllChildrenFrom(taggedFragment.get());
735
736     RefPtr<Node> nodeBeforeContext;
737     RefPtr<Node> nodeAfterContext;
738     if (!findNodesSurroundingContext(taggedDocument.get(), nodeBeforeContext, nodeAfterContext))
739         return 0;
740
741     RefPtr<Range> range = Range::create(taggedDocument.get(),
742         positionAfterNode(nodeBeforeContext.get()).parentAnchoredEquivalent(),
743         positionBeforeNode(nodeAfterContext.get()).parentAnchoredEquivalent());
744
745     Node* commonAncestor = range->commonAncestorContainer(ASSERT_NO_EXCEPTION);
746     Node* specialCommonAncestor = ancestorToRetainStructureAndAppearanceWithNoRenderer(commonAncestor);
747
748     // When there's a special common ancestor outside of the fragment, we must include it as well to
749     // preserve the structure and appearance of the fragment. For example, if the fragment contains
750     // TD, we need to include the enclosing TABLE tag as well.
751     RefPtr<DocumentFragment> fragment = DocumentFragment::create(document);
752     if (specialCommonAncestor)
753         fragment->appendChild(specialCommonAncestor, ASSERT_NO_EXCEPTION);
754     else
755         fragment->takeAllChildrenFrom(static_cast<ContainerNode*>(commonAncestor));
756
757     trimFragment(fragment.get(), nodeBeforeContext.get(), nodeAfterContext.get());
758
759     return fragment;
760 }
761
762 String createMarkup(const Node* node, EChildrenOnly childrenOnly, Vector<Node*>* nodes, EAbsoluteURLs shouldResolveURLs, Vector<QualifiedName>* tagNamesToSkip)
763 {
764     if (!node)
765         return "";
766
767     HTMLElement* deleteButtonContainerElement = 0;
768 #if ENABLE(DELETION_UI)
769     if (Frame* frame = node->document()->frame()) {
770         deleteButtonContainerElement = frame->editor()->deleteButtonController()->containerElement();
771         if (node->isDescendantOf(deleteButtonContainerElement))
772             return "";
773     }
774 #endif
775     MarkupAccumulator accumulator(nodes, shouldResolveURLs);
776     return accumulator.serializeNodes(const_cast<Node*>(node), deleteButtonContainerElement, childrenOnly, tagNamesToSkip);
777 }
778
779 static void fillContainerFromString(ContainerNode* paragraph, const String& string)
780 {
781     Document* document = paragraph->document();
782
783     if (string.isEmpty()) {
784         paragraph->appendChild(createBlockPlaceholderElement(document), ASSERT_NO_EXCEPTION);
785         return;
786     }
787
788     ASSERT(string.find('\n') == notFound);
789
790     Vector<String> tabList;
791     string.split('\t', true, tabList);
792     String tabText = emptyString();
793     bool first = true;
794     size_t numEntries = tabList.size();
795     for (size_t i = 0; i < numEntries; ++i) {
796         const String& s = tabList[i];
797
798         // append the non-tab textual part
799         if (!s.isEmpty()) {
800             if (!tabText.isEmpty()) {
801                 paragraph->appendChild(createTabSpanElement(document, tabText), ASSERT_NO_EXCEPTION);
802                 tabText = emptyString();
803             }
804             RefPtr<Node> textNode = document->createTextNode(stringWithRebalancedWhitespace(s, first, i + 1 == numEntries));
805             paragraph->appendChild(textNode.release(), ASSERT_NO_EXCEPTION);
806         }
807
808         // there is a tab after every entry, except the last entry
809         // (if the last character is a tab, the list gets an extra empty entry)
810         if (i + 1 != numEntries)
811             tabText.append('\t');
812         else if (!tabText.isEmpty())
813             paragraph->appendChild(createTabSpanElement(document, tabText), ASSERT_NO_EXCEPTION);
814
815         first = false;
816     }
817 }
818
819 bool isPlainTextMarkup(Node *node)
820 {
821     if (!node->isElementNode() || !node->hasTagName(divTag) || static_cast<Element*>(node)->hasAttributes())
822         return false;
823     
824     if (node->childNodeCount() == 1 && (node->firstChild()->isTextNode() || (node->firstChild()->firstChild())))
825         return true;
826     
827     return (node->childNodeCount() == 2 && isTabSpanTextNode(node->firstChild()->firstChild()) && node->firstChild()->nextSibling()->isTextNode());
828 }
829
830 PassRefPtr<DocumentFragment> createFragmentFromText(Range* context, const String& text)
831 {
832     if (!context)
833         return 0;
834
835     Node* styleNode = context->firstNode();
836     if (!styleNode) {
837         styleNode = context->startPosition().deprecatedNode();
838         if (!styleNode)
839             return 0;
840     }
841
842     Document* document = styleNode->document();
843     RefPtr<DocumentFragment> fragment = document->createDocumentFragment();
844     
845     if (text.isEmpty())
846         return fragment.release();
847
848     String string = text;
849     string.replace("\r\n", "\n");
850     string.replace('\r', '\n');
851
852     RenderObject* renderer = styleNode->renderer();
853     if (renderer && renderer->style()->preserveNewline()) {
854         fragment->appendChild(document->createTextNode(string), ASSERT_NO_EXCEPTION);
855         if (string.endsWith('\n')) {
856             RefPtr<Element> element = createBreakElement(document);
857             element->setAttribute(classAttr, AppleInterchangeNewline);            
858             fragment->appendChild(element.release(), ASSERT_NO_EXCEPTION);
859         }
860         return fragment.release();
861     }
862
863     // A string with no newlines gets added inline, rather than being put into a paragraph.
864     if (string.find('\n') == notFound) {
865         fillContainerFromString(fragment.get(), string);
866         return fragment.release();
867     }
868
869     // Break string into paragraphs. Extra line breaks turn into empty paragraphs.
870     Node* blockNode = enclosingBlock(context->firstNode());
871     Element* block = static_cast<Element*>(blockNode);
872     bool useClonesOfEnclosingBlock = blockNode
873         && blockNode->isElementNode()
874         && !block->hasTagName(bodyTag)
875         && !block->hasTagName(htmlTag)
876         && block != editableRootForPosition(context->startPosition());
877     bool useLineBreak = enclosingTextFormControl(context->startPosition());
878
879     Vector<String> list;
880     string.split('\n', true, list); // true gets us empty strings in the list
881     size_t numLines = list.size();
882     for (size_t i = 0; i < numLines; ++i) {
883         const String& s = list[i];
884
885         RefPtr<Element> element;
886         if (s.isEmpty() && i + 1 == numLines) {
887             // For last line, use the "magic BR" rather than a P.
888             element = createBreakElement(document);
889             element->setAttribute(classAttr, AppleInterchangeNewline);
890         } else if (useLineBreak) {
891             element = createBreakElement(document);
892             fillContainerFromString(fragment.get(), s);
893         } else {
894             if (useClonesOfEnclosingBlock)
895                 element = block->cloneElementWithoutChildren();
896             else
897                 element = createDefaultParagraphElement(document);
898             fillContainerFromString(element.get(), s);
899         }
900         fragment->appendChild(element.release(), ASSERT_NO_EXCEPTION);
901     }
902     return fragment.release();
903 }
904
905 PassRefPtr<DocumentFragment> createFragmentFromNodes(Document *document, const Vector<Node*>& nodes)
906 {
907     if (!document)
908         return 0;
909
910 #if ENABLE(DELETION_UI)
911     // disable the delete button so it's elements are not serialized into the markup
912     if (document->frame())
913         document->frame()->editor()->deleteButtonController()->disable();
914 #endif
915     RefPtr<DocumentFragment> fragment = document->createDocumentFragment();
916
917     size_t size = nodes.size();
918     for (size_t i = 0; i < size; ++i) {
919         RefPtr<Element> element = createDefaultParagraphElement(document);
920         element->appendChild(nodes[i], ASSERT_NO_EXCEPTION);
921         fragment->appendChild(element.release(), ASSERT_NO_EXCEPTION);
922     }
923
924 #if ENABLE(DELETION_UI)
925     if (document->frame())
926         document->frame()->editor()->deleteButtonController()->enable();
927 #endif
928     return fragment.release();
929 }
930
931 String createFullMarkup(const Node* node)
932 {
933     if (!node)
934         return String();
935         
936     Document* document = node->document();
937     if (!document)
938         return String();
939         
940     Frame* frame = document->frame();
941     if (!frame)
942         return String();
943
944     // FIXME: This is never "for interchange". Is that right?    
945     String markupString = createMarkup(node, IncludeNode, 0);
946     Node::NodeType nodeType = node->nodeType();
947     if (nodeType != Node::DOCUMENT_NODE && nodeType != Node::DOCUMENT_TYPE_NODE)
948         markupString = frame->documentTypeString() + markupString;
949
950     return markupString;
951 }
952
953 String createFullMarkup(const Range* range)
954 {
955     if (!range)
956         return String();
957
958     Node* node = range->startContainer();
959     if (!node)
960         return String();
961         
962     Document* document = node->document();
963     if (!document)
964         return String();
965         
966     Frame* frame = document->frame();
967     if (!frame)
968         return String();
969
970     // FIXME: This is always "for interchange". Is that right? See the previous method.
971     return frame->documentTypeString() + createMarkup(range, 0, AnnotateForInterchange);        
972 }
973
974 String urlToMarkup(const KURL& url, const String& title)
975 {
976     StringBuilder markup;
977     markup.append("<a href=\"");
978     markup.append(url.string());
979     markup.append("\">");
980     MarkupAccumulator::appendCharactersReplacingEntities(markup, title, 0, title.length(), EntityMaskInPCDATA);
981     markup.append("</a>");
982     return markup.toString();
983 }
984
985 PassRefPtr<DocumentFragment> createFragmentForInnerOuterHTML(const String& markup, Element* contextElement, FragmentScriptingPermission scriptingPermission, ExceptionCode& ec)
986 {
987     Document* document = contextElement->document();
988 #if ENABLE(TEMPLATE_ELEMENT)
989     if (contextElement->hasTagName(templateTag))
990         document = document->ensureTemplateDocument();
991 #endif
992     RefPtr<DocumentFragment> fragment = DocumentFragment::create(document);
993
994     if (document->isHTMLDocument()) {
995         fragment->parseHTML(markup, contextElement, scriptingPermission);
996         return fragment;
997     }
998
999     bool wasValid = fragment->parseXML(markup, contextElement, scriptingPermission);
1000     if (!wasValid) {
1001         ec = SYNTAX_ERR;
1002         return 0;
1003     }
1004     return fragment.release();
1005 }
1006
1007 PassRefPtr<DocumentFragment> createFragmentForTransformToFragment(const String& sourceString, const String& sourceMIMEType, Document* outputDoc)
1008 {
1009     RefPtr<DocumentFragment> fragment = outputDoc->createDocumentFragment();
1010     
1011     if (sourceMIMEType == "text/html") {
1012         // As far as I can tell, there isn't a spec for how transformToFragment is supposed to work.
1013         // Based on the documentation I can find, it looks like we want to start parsing the fragment in the InBody insertion mode.
1014         // Unfortunately, that's an implementation detail of the parser.
1015         // We achieve that effect here by passing in a fake body element as context for the fragment.
1016         RefPtr<HTMLBodyElement> fakeBody = HTMLBodyElement::create(outputDoc);
1017         fragment->parseHTML(sourceString, fakeBody.get());
1018     } else if (sourceMIMEType == "text/plain")
1019         fragment->parserAppendChild(Text::create(outputDoc, sourceString));
1020     else {
1021         bool successfulParse = fragment->parseXML(sourceString, 0);
1022         if (!successfulParse)
1023             return 0;
1024     }
1025     
1026     // FIXME: Do we need to mess with URLs here?
1027     
1028     return fragment.release();
1029 }
1030
1031 static inline void removeElementPreservingChildren(PassRefPtr<DocumentFragment> fragment, HTMLElement* element)
1032 {
1033     RefPtr<Node> nextChild;
1034     for (RefPtr<Node> child = element->firstChild(); child; child = nextChild) {
1035         nextChild = child->nextSibling();
1036         element->removeChild(child.get(), ASSERT_NO_EXCEPTION);
1037         fragment->insertBefore(child, element, ASSERT_NO_EXCEPTION);
1038     }
1039     fragment->removeChild(element, ASSERT_NO_EXCEPTION);
1040 }
1041
1042 PassRefPtr<DocumentFragment> createContextualFragment(const String& markup, HTMLElement* element, FragmentScriptingPermission scriptingPermission, ExceptionCode& ec)
1043 {
1044     ASSERT(element);
1045     if (element->ieForbidsInsertHTML()) {
1046         ec = NOT_SUPPORTED_ERR;
1047         return 0;
1048     }
1049
1050     if (element->hasLocalName(colTag) || element->hasLocalName(colgroupTag) || element->hasLocalName(framesetTag)
1051         || element->hasLocalName(headTag) || element->hasLocalName(styleTag) || element->hasLocalName(titleTag)) {
1052         ec = NOT_SUPPORTED_ERR;
1053         return 0;
1054     }
1055
1056     RefPtr<DocumentFragment> fragment = createFragmentForInnerOuterHTML(markup, element, scriptingPermission, ec);
1057     if (!fragment)
1058         return 0;
1059
1060     // We need to pop <html> and <body> elements and remove <head> to
1061     // accommodate folks passing complete HTML documents to make the
1062     // child of an element.
1063
1064     RefPtr<Node> nextNode;
1065     for (RefPtr<Node> node = fragment->firstChild(); node; node = nextNode) {
1066         nextNode = node->nextSibling();
1067         if (node->hasTagName(htmlTag) || node->hasTagName(headTag) || node->hasTagName(bodyTag)) {
1068             HTMLElement* element = toHTMLElement(node.get());
1069             if (Node* firstChild = element->firstChild())
1070                 nextNode = firstChild;
1071             removeElementPreservingChildren(fragment, element);
1072         }
1073     }
1074     return fragment.release();
1075 }
1076
1077 static inline bool hasOneChild(ContainerNode* node)
1078 {
1079     Node* firstChild = node->firstChild();
1080     return firstChild && !firstChild->nextSibling();
1081 }
1082
1083 static inline bool hasOneTextChild(ContainerNode* node)
1084 {
1085     return hasOneChild(node) && node->firstChild()->isTextNode();
1086 }
1087
1088 void replaceChildrenWithFragment(ContainerNode* container, PassRefPtr<DocumentFragment> fragment, ExceptionCode& ec)
1089 {
1090     RefPtr<ContainerNode> containerNode(container);
1091
1092     ChildListMutationScope mutation(containerNode.get());
1093
1094     if (!fragment->firstChild()) {
1095         containerNode->removeChildren();
1096         return;
1097     }
1098
1099     if (hasOneTextChild(containerNode.get()) && hasOneTextChild(fragment.get())) {
1100         toText(containerNode->firstChild())->setData(toText(fragment->firstChild())->data(), ec);
1101         return;
1102     }
1103
1104     if (hasOneChild(containerNode.get())) {
1105         containerNode->replaceChild(fragment, containerNode->firstChild(), ec);
1106         return;
1107     }
1108
1109     containerNode->removeChildren();
1110     containerNode->appendChild(fragment, ec);
1111 }
1112
1113 void replaceChildrenWithText(ContainerNode* container, const String& text, ExceptionCode& ec)
1114 {
1115     RefPtr<ContainerNode> containerNode(container);
1116
1117     ChildListMutationScope mutation(containerNode.get());
1118
1119     if (hasOneTextChild(containerNode.get())) {
1120         toText(containerNode->firstChild())->setData(text, ec);
1121         return;
1122     }
1123
1124     RefPtr<Text> textNode = Text::create(containerNode->document(), text);
1125
1126     if (hasOneChild(containerNode.get())) {
1127         containerNode->replaceChild(textNode.release(), containerNode->firstChild(), ec);
1128         return;
1129     }
1130
1131     containerNode->removeChildren();
1132     containerNode->appendChild(textNode.release(), ec);
1133 }
1134
1135 }