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