5bb92364c1ca588c785fe822f7dc81cc38aac5bb
[WebKit-https.git] / WebCore / editing / markup.cpp
1 /*
2  * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "markup.h"
28
29 #include "CDATASection.h"
30 #include "CSSComputedStyleDeclaration.h"
31 #include "CSSPropertyNames.h"
32 #include "CSSRule.h"
33 #include "CSSRuleList.h"
34 #include "CSSStyleRule.h"
35 #include "cssstyleselector.h"
36 #include "Comment.h"
37 #include "DeleteButtonController.h"
38 #include "DeprecatedStringList.h"
39 #include "Document.h"
40 #include "DocumentFragment.h"
41 #include "DocumentType.h"
42 #include "Editor.h"
43 #include "Frame.h"
44 #include "HTMLElement.h"
45 #include "HTMLNames.h"
46 #include "InlineTextBox.h"
47 #include "KURL.h"
48 #include "Logging.h"
49 #include "ProcessingInstruction.h"
50 #include "Range.h"
51 #include "Selection.h"
52 #include "htmlediting.h"
53 #include "visible_units.h"
54 #include "TextIterator.h"
55
56 using namespace std;
57
58 namespace WebCore {
59
60 using namespace HTMLNames;
61
62 static inline bool shouldSelfClose(const Node *node);
63
64 static DeprecatedString escapeTextForMarkup(const DeprecatedString &in, bool isAttributeValue)
65 {
66     DeprecatedString s = "";
67
68     unsigned len = in.length();
69     for (unsigned i = 0; i < len; ++i) {
70         switch (in[i].unicode()) {
71             case '&':
72                 s += "&amp;";
73                 break;
74             case '<':
75                 s += "&lt;";
76                 break;
77             case '>':
78                 s += "&gt;";
79                 break;
80             case '"':
81                 if (isAttributeValue) {
82                     s += "&quot;";
83                     break;
84                 }
85                 // fall through
86             default:
87                 s += in[i];
88         }
89     }
90
91     return s;
92 }
93
94 static String stringValueForRange(const Node *node, const Range *range)
95 {
96     String str = node->nodeValue().copy();
97     if (range) {
98         ExceptionCode ec;
99         if (node == range->endContainer(ec))
100             str.truncate(range->endOffset(ec));
101         if (node == range->startContainer(ec))
102             str.remove(0, range->startOffset(ec));
103     }
104     return str;
105 }
106
107 static DeprecatedString renderedText(const Node *node, const Range *range)
108 {
109     if (!node->isTextNode())
110         return DeprecatedString();
111
112     ExceptionCode ec;
113     const Text* textNode = static_cast<const Text*>(node);
114     unsigned startOffset = 0;
115     unsigned endOffset = textNode->length();
116
117     if (range && node == range->startContainer(ec))
118         startOffset = range->startOffset(ec);
119     if (range && node == range->endContainer(ec))
120         endOffset = range->endOffset(ec);
121     
122     Position start(const_cast<Node*>(node), startOffset);
123     Position end(const_cast<Node*>(node), endOffset);
124     Range r(node->document(), start, end);
125     return plainText(&r);
126 }
127
128 static PassRefPtr<CSSMutableStyleDeclaration> styleFromMatchedRulesForElement(Element* element, bool authorOnly = true)
129 {
130     RefPtr<CSSMutableStyleDeclaration> style = new CSSMutableStyleDeclaration();
131     RefPtr<CSSRuleList> matchedRules = element->document()->styleSelector()->styleRulesForElement(element, authorOnly);
132     if (matchedRules) {
133         for (unsigned i = 0; i < matchedRules->length(); i++) {
134             if (matchedRules->item(i)->type() == CSSRule::STYLE_RULE) {
135                 RefPtr<CSSMutableStyleDeclaration> s = static_cast<CSSStyleRule*>(matchedRules->item(i))->style();
136                 style->merge(s.get(), true);
137             }
138         }
139     }
140     
141     return style.release();
142 }
143
144 static void removeEnclosingMailBlockquoteStyle(CSSMutableStyleDeclaration* style, Node* node)
145 {
146     Node* blockquote = nearestMailBlockquote(node);
147     if (!blockquote || !blockquote->parentNode())
148         return;
149             
150     RefPtr<CSSMutableStyleDeclaration> parentStyle = Position(blockquote->parentNode(), 0).computedStyle()->copyInheritableProperties();
151     RefPtr<CSSMutableStyleDeclaration> blockquoteStyle = Position(blockquote, 0).computedStyle()->copyInheritableProperties();
152     parentStyle->diff(blockquoteStyle.get());
153     blockquoteStyle->diff(style);
154 }
155
156 static DeprecatedString startMarkup(const Node *node, const Range *range, EAnnotateForInterchange annotate)
157 {
158     bool documentIsHTML = node->document()->isHTMLDocument();
159     switch (node->nodeType()) {
160         case Node::TEXT_NODE: {
161             if (Node* parent = node->parentNode()) {
162                 if (parent->hasTagName(listingTag)
163                         || parent->hasTagName(scriptTag)
164                         || parent->hasTagName(styleTag)
165                         || parent->hasTagName(textareaTag)
166                         || parent->hasTagName(xmpTag))
167                     return stringValueForRange(node, range).deprecatedString();
168             }
169             bool useRenderedText = annotate && !enclosingNodeWithTag(const_cast<Node*>(node), selectTag);
170             DeprecatedString markup = useRenderedText ? escapeTextForMarkup(renderedText(node, range), false) : escapeTextForMarkup(stringValueForRange(node, range).deprecatedString(), false);
171             return annotate ? convertHTMLTextToInterchangeFormat(markup, static_cast<const Text*>(node)) : markup;
172         }
173         case Node::COMMENT_NODE:
174             return static_cast<const Comment*>(node)->toString().deprecatedString();
175         case Node::DOCUMENT_NODE: {
176             // Documents do not normally contain a docType as a child node, force it to print here instead.
177             const DocumentType* docType = static_cast<const Document*>(node)->doctype();
178             if (docType)
179                 return docType->toString().deprecatedString();
180             return "";
181         }
182         case Node::DOCUMENT_FRAGMENT_NODE:
183             return "";
184         case Node::DOCUMENT_TYPE_NODE:
185             return static_cast<const DocumentType*>(node)->toString().deprecatedString();
186         case Node::PROCESSING_INSTRUCTION_NODE:
187             return static_cast<const ProcessingInstruction*>(node)->toString().deprecatedString();
188         case Node::ELEMENT_NODE: {
189             DeprecatedString markup = DeprecatedChar('<');
190             const Element* el = static_cast<const Element*>(node);
191             markup += el->nodeNamePreservingCase().deprecatedString();
192             String additionalStyle;
193             if (annotate && el->isHTMLElement()) {
194                 RefPtr<CSSMutableStyleDeclaration> style = styleFromMatchedRulesForElement(const_cast<Element*>(el));
195                 if (style->length() > 0)
196                     additionalStyle = style->cssText();
197             }
198             NamedAttrMap *attrs = el->attributes();
199             unsigned length = attrs->length();
200
201             for (unsigned int i = 0; i < length; i++) {
202                 Attribute *attr = attrs->attributeItem(i);
203                 String value = attr->value();
204                 if (annotate && attr->name() == styleAttr && additionalStyle.length()) {
205                     value += "; " + additionalStyle;
206                     additionalStyle = "";
207                 }
208                 // FIXME: Handle case where value has illegal characters in it, like "
209                 if (documentIsHTML)
210                     markup += " " + attr->name().localName().deprecatedString();
211                 else
212                     markup += " " + attr->name().toString().deprecatedString();
213                 markup += "=\"" + escapeTextForMarkup(value.deprecatedString(), true) + "\"";
214             }
215             
216             if (annotate && additionalStyle.length())
217                 // FIXME: Handle case where additionalStyle has illegal characters in it, like "
218                 markup += " " +  styleAttr.localName().deprecatedString() + "=\"" + additionalStyle.deprecatedString() + "\"";
219             
220             if (shouldSelfClose(el)) {
221                 if (el->isHTMLElement())
222                     markup += " "; // XHTML 1.0 <-> HTML compatibility.
223                 markup += "/>";
224             } else
225                 markup += ">";
226             
227             return markup;
228         }
229         case Node::CDATA_SECTION_NODE:
230             return static_cast<const CDATASection*>(node)->toString().deprecatedString();
231         case Node::ATTRIBUTE_NODE:
232         case Node::ENTITY_NODE:
233         case Node::ENTITY_REFERENCE_NODE:
234         case Node::NOTATION_NODE:
235         case Node::XPATH_NAMESPACE_NODE:
236             break;
237     }
238     return "";
239 }
240
241 static inline bool doesHTMLForbidEndTag(const Node *node)
242 {
243     if (node->isHTMLElement()) {
244         const HTMLElement* htmlElt = static_cast<const HTMLElement*>(node);
245         return (htmlElt->endTagRequirement() == TagStatusForbidden);
246     }
247     return false;
248 }
249
250 // Rules of self-closure
251 // 1. No elements in HTML documents use the self-closing syntax.
252 // 2. Elements w/ children never self-close because they use a separate end tag.
253 // 3. HTML elements which do not have a "forbidden" end tag will close with a separate end tag.
254 // 4. Other elements self-close.
255 static inline bool shouldSelfClose(const Node *node)
256 {
257     if (node->document()->isHTMLDocument())
258         return false;
259     if (node->hasChildNodes())
260         return false;
261     if (node->isHTMLElement() && !doesHTMLForbidEndTag(node))
262         return false;
263     return true;
264 }
265
266 static DeprecatedString endMarkup(const Node *node)
267 {
268     if (node->isElementNode() && !shouldSelfClose(node) && (node->hasChildNodes() || !doesHTMLForbidEndTag(node)))
269         return "</" + static_cast<const Element*>(node)->nodeNamePreservingCase().deprecatedString() + ">";
270     return "";
271 }
272
273 static DeprecatedString markup(Node* startNode, bool onlyIncludeChildren, bool includeSiblings, Vector<Node*> *nodes)
274 {
275     // Doesn't make sense to only include children and include siblings.
276     ASSERT(!(onlyIncludeChildren && includeSiblings));
277     DeprecatedString me = "";
278     for (Node* current = startNode; current != NULL; current = includeSiblings ? current->nextSibling() : NULL) {
279         if (!onlyIncludeChildren) {
280             if (nodes)
281                 nodes->append(current);
282             me += startMarkup(current, 0, DoNotAnnotateForInterchange);
283         }
284         // print children
285         if (Node *n = current->firstChild())
286             if (!(n->document()->isHTMLDocument() && doesHTMLForbidEndTag(current)))
287                 me += markup(n, false, true, nodes);
288         
289         // Print my ending tag
290         if (!onlyIncludeChildren)
291             me += endMarkup(current);
292     }
293     return me;
294 }
295
296 static void completeURLs(Node *node, const DeprecatedString &baseURL)
297 {
298     Node *end = node->traverseNextSibling();
299     for (Node *n = node; n != end; n = n->traverseNextNode()) {
300         if (n->isElementNode()) {
301             Element *e = static_cast<Element*>(n);
302             NamedAttrMap *attrs = e->attributes();
303             unsigned length = attrs->length();
304             for (unsigned i = 0; i < length; i++) {
305                 Attribute *attr = attrs->attributeItem(i);
306                 if (e->isURLAttribute(attr))
307                     e->setAttribute(attr->name(), KURL(baseURL, attr->value().deprecatedString()).url());
308             }
309         }
310     }
311 }
312
313 static bool needInterchangeNewlineAfter(const VisiblePosition& v)
314 {
315     VisiblePosition next = v.next();
316     return isEndOfParagraph(v) && isStartOfParagraph(next) && !next.deepEquivalent().upstream().node()->hasTagName(brTag);
317 }
318
319 // FIXME: Shouldn't we omit style info when annotate == DoNotAnnotateForInterchange? 
320 // FIXME: At least, annotation and style info should probably not be included in range.markupString()
321 DeprecatedString createMarkup(const Range *range, Vector<Node*>* nodes, EAnnotateForInterchange annotate)
322 {
323     if (!range || range->isDetached())
324         return DeprecatedString();
325
326     static const DeprecatedString interchangeNewlineString = DeprecatedString("<br class=\"") + AppleInterchangeNewline + "\">";
327
328     ExceptionCode ec = 0;
329     if (range->collapsed(ec))
330         return "";
331     ASSERT(ec == 0);
332     Node *commonAncestor = range->commonAncestorContainer(ec);
333     ASSERT(ec == 0);
334
335     Document *doc = commonAncestor->document();
336     // disable the delete button so it's elements are not serialized into the markup
337     doc->frame()->editor()->deleteButtonController()->disable();
338     doc->updateLayoutIgnorePendingStylesheets();
339
340     Node *commonAncestorBlock = 0;
341     if (commonAncestor) {
342         commonAncestorBlock = enclosingBlock(commonAncestor);
343         if (commonAncestorBlock && commonAncestorBlock->hasTagName(tbodyTag)) {
344             Node* table = commonAncestorBlock->parentNode();
345             while (table && !table->hasTagName(tableTag))
346                 table = table->parentNode();
347             if (table)
348                 commonAncestorBlock = table;
349         }
350     }
351
352     DeprecatedStringList markups;
353     Node *pastEnd = range->pastEndNode();
354     Node *lastClosed = 0;
355     Vector<Node*> ancestorsToClose;
356     
357     Node* startNode = range->startNode();
358     VisiblePosition visibleStart(range->startPosition(), VP_DEFAULT_AFFINITY);
359     VisiblePosition visibleEnd(range->endPosition(), VP_DEFAULT_AFFINITY);
360     if (annotate && needInterchangeNewlineAfter(visibleStart)) {
361         if (visibleStart == visibleEnd.previous()) {
362             doc->frame()->editor()->deleteButtonController()->enable();
363             return interchangeNewlineString;
364         }
365
366         markups.append(interchangeNewlineString);
367         startNode = visibleStart.next().deepEquivalent().node();
368     }
369
370     Node *next;
371     for (Node *n = startNode; n != pastEnd; n = next) {
372         next = n->traverseNextNode();
373         bool skipDescendants = false;
374         bool addMarkupForNode = true;
375         
376         if (!n->renderer() && !enclosingNodeWithTag(n, selectTag)) {
377             skipDescendants = true;
378             addMarkupForNode = false;
379             next = n->traverseNextSibling();
380             // Don't skip over pastEnd.
381             if (pastEnd && pastEnd->isDescendantOf(n))
382                 next = pastEnd;
383         }
384
385         if (isBlock(n) && canHaveChildrenForEditing(n) && next == pastEnd)
386             // Don't write out empty block containers that aren't fully selected.
387             continue;
388         
389         // Add the node to the markup.
390         if (addMarkupForNode) {
391             markups.append(startMarkup(n, range, annotate));
392             if (nodes)
393                 nodes->append(n);
394         }
395         
396         if (n->firstChild() == 0 || skipDescendants) {
397             // Node has no children, or we are skipping it's descendants, add its close tag now.
398             if (addMarkupForNode) {
399                 markups.append(endMarkup(n));
400                 lastClosed = n;
401             }
402             
403             // Check if the node is the last leaf of a tree.
404             if (!n->nextSibling() || next == pastEnd) {
405                 if (!ancestorsToClose.isEmpty()) {
406                     // Close up the ancestors.
407                     do {
408                         Node *ancestor = ancestorsToClose.last();
409                         if (next != pastEnd && next->isDescendantOf(ancestor))
410                             break;
411                         // Not at the end of the range, close ancestors up to sibling of next node.
412                         markups.append(endMarkup(ancestor));
413                         lastClosed = ancestor;
414                         ancestorsToClose.removeLast();
415                     } while (!ancestorsToClose.isEmpty());
416                 }
417                 
418                 // Surround the currently accumulated markup with markup for ancestors we never opened as we leave the subtree(s) rooted at those ancestors.
419                 Node* nextParent = next ? next->parentNode() : 0;
420                 if (next != pastEnd && n != nextParent) {
421                     Node* lastAncestorClosedOrSelf = n->isDescendantOf(lastClosed) ? lastClosed : n;
422                     for (Node *parent = lastAncestorClosedOrSelf->parent(); parent != 0 && parent != nextParent; parent = parent->parentNode()) {
423                         // All ancestors that aren't in the ancestorsToClose list should either be a) unrendered:
424                         if (!parent->renderer())
425                             continue;
426                         // or b) ancestors that we never encountered during a pre-order traversal starting at startNode:
427                         ASSERT(startNode->isDescendantOf(parent));
428                         markups.prepend(startMarkup(parent, range, annotate));
429                         markups.append(endMarkup(parent));
430                         if (nodes)
431                             nodes->append(parent);
432                         lastClosed = parent;
433                     }
434                 }
435             }
436         } else if (addMarkupForNode && !skipDescendants)
437             // We added markup for this node, and we're descending into it.  Set it to close eventually.
438             ancestorsToClose.append(n);
439     }
440     
441     Node* body = enclosingNodeWithTag(commonAncestor, bodyTag);
442     // FIXME: Do this for all fully selected blocks, not just the body.
443     Node* fullySelectedRoot = body && *Selection::selectionFromContentsOfNode(body).toRange() == *range ? body : 0;
444     // FIXME: Only include markup for a fully selected root (and ancestors of lastClosed up to that root) if
445     // there are styles/attributes on those nodes that need to be included to preserve the appearance of the copied markup.
446     if (fullySelectedRoot)
447         commonAncestorBlock = fullySelectedRoot;
448     // Include ancestor blocks that are required to retain the appearance of the copied markup.
449     if (annotate &&
450         (commonAncestorBlock->hasTagName(listingTag)
451             || commonAncestorBlock->hasTagName(olTag)
452             || commonAncestorBlock->hasTagName(preTag)
453             || commonAncestorBlock->hasTagName(tableTag)
454             || commonAncestorBlock->hasTagName(ulTag)
455             || commonAncestorBlock->hasTagName(xmpTag)
456             || commonAncestorBlock == fullySelectedRoot)) {
457         // Also include all of the ancestors of lastClosed up to this ancestor block.
458         for (Node* ancestor = lastClosed->parentNode(); ancestor; ancestor = ancestor->parentNode()) {
459             if (ancestor == fullySelectedRoot) {
460                 // From a fully selected root we want:
461                 // The non-inheritble styles from its matched rules (author only).
462                 RefPtr<CSSMutableStyleDeclaration> style = styleFromMatchedRulesForElement(static_cast<Element*>(fullySelectedRoot));
463                 
464                 // The non-inheritble styles from its inline style declaration.
465                 RefPtr<CSSMutableStyleDeclaration> inlineStyleDecl = static_cast<HTMLElement*>(fullySelectedRoot)->getInlineStyleDecl();
466                 style->merge(inlineStyleDecl.get());
467                 
468                 // Bring the background attribute over, but not as an attribute because a background attribute on a div
469                 // appears to have no effect.
470                 if (!style->getPropertyCSSValue(CSS_PROP_BACKGROUND_IMAGE) && static_cast<Element*>(fullySelectedRoot)->hasAttribute(backgroundAttr))
471                     style->setProperty(CSS_PROP_BACKGROUND_IMAGE, "url('" + static_cast<Element*>(fullySelectedRoot)->getAttribute(backgroundAttr) + "')");
472                 
473                 if (style->length()) {
474                     markups.prepend("<div style='" + style->cssText().deprecatedString() + "'>");
475                     markups.append("</div>");
476                 }
477             } else {
478                 markups.prepend(startMarkup(ancestor, range, annotate));
479                 markups.append(endMarkup(ancestor));
480             }
481             if (nodes)
482                 nodes->append(ancestor);
483             
484             lastClosed = ancestor;
485             
486             if (ancestor == commonAncestorBlock)
487                 break;
488         }
489     }
490     
491     // Add a wrapper span with the styles that all of the nodes in the markup inherit.
492     if (Node* parentOfLastClosed = lastClosed->parentNode()) {
493         RefPtr<CSSMutableStyleDeclaration> style = computedStyle(parentOfLastClosed)->copyInheritableProperties();
494         // Styles that Mail blockquotes contribute should only be placed on the Mail blockquote, to help
495         // us differentiate those styles from ones that the user has applied.  This helps us
496         // get the color of content pasted into blockquotes right.
497         removeEnclosingMailBlockquoteStyle(style.get(), parentOfLastClosed);
498         
499         if (style->length() > 0) {
500             // FIXME: Handle case where style->cssText() has illegal characters in it, like "
501             DeprecatedString openTag = DeprecatedString("<span class=\"") + AppleStyleSpanClass + "\" style=\"" + style->cssText().deprecatedString() + "\">";
502             markups.prepend(openTag);
503             markups.append("</span>");
504         }
505     }
506
507     if (annotate && needInterchangeNewlineAfter(visibleEnd.previous()))
508         markups.append(interchangeNewlineString);
509
510     bool selectedOneOrMoreParagraphs = startOfParagraph(visibleStart) != startOfParagraph(visibleEnd) ||
511                                        isStartOfParagraph(visibleStart) && isEndOfParagraph(visibleEnd);
512                                       
513     // Retain the Mail quote level by including all ancestor mail block quotes.
514     if (annotate && selectedOneOrMoreParagraphs) {
515         for (Node *ancestor = commonAncestorBlock; ancestor; ancestor = ancestor->parentNode()) {
516             if (isMailBlockquote(ancestor)) {
517                 markups.prepend(startMarkup(ancestor, range, annotate));
518                 markups.append(endMarkup(ancestor));
519             }
520         }
521     }
522
523     doc->frame()->editor()->deleteButtonController()->enable();
524     return markups.join("");
525 }
526
527 PassRefPtr<DocumentFragment> createFragmentFromMarkup(Document* document, const String& markup, const String& baseURL)
528 {
529     ASSERT(document->documentElement()->isHTMLElement());
530     // FIXME: What if the document element is not an HTML element?
531     HTMLElement *element = static_cast<HTMLElement*>(document->documentElement());
532
533     RefPtr<DocumentFragment> fragment = element->createContextualFragment(markup);
534
535     if (fragment && !baseURL.isEmpty() && baseURL != document->baseURL())
536         completeURLs(fragment.get(), baseURL.deprecatedString());
537
538     return fragment.release();
539 }
540
541 DeprecatedString createMarkup(const Node* node, EChildrenOnly includeChildren,
542     Vector<Node*>* nodes, EAnnotateForInterchange annotate)
543 {
544     ASSERT(annotate == DoNotAnnotateForInterchange); // annotation not yet implemented for this code path
545     // disable the delete button so it's elements are not serialized into the markup
546     if (node->document()->frame())
547         node->document()->frame()->editor()->deleteButtonController()->disable();
548     node->document()->updateLayoutIgnorePendingStylesheets();
549     DeprecatedString result(markup(const_cast<Node*>(node), includeChildren, false, nodes));
550     if (node->document()->frame())
551         node->document()->frame()->editor()->deleteButtonController()->enable();
552     return result;
553 }
554
555 static void fillContainerFromString(ContainerNode* paragraph, const DeprecatedString& string)
556 {
557     Document* document = paragraph->document();
558
559     ExceptionCode ec = 0;
560     if (string.isEmpty()) {
561         paragraph->appendChild(createBlockPlaceholderElement(document), ec);
562         ASSERT(ec == 0);
563         return;
564     }
565
566     assert(string.find('\n') == -1);
567
568     DeprecatedStringList tabList = DeprecatedStringList::split('\t', string, true);
569     DeprecatedString tabText = "";
570     bool first = true;
571     while (!tabList.isEmpty()) {
572         DeprecatedString s = tabList.first();
573         tabList.pop_front();
574
575         // append the non-tab textual part
576         if (!s.isEmpty()) {
577             if (!tabText.isEmpty()) {
578                 paragraph->appendChild(createTabSpanElement(document, tabText), ec);
579                 ASSERT(ec == 0);
580                 tabText = "";
581             }
582             RefPtr<Node> textNode = document->createTextNode(stringWithRebalancedWhitespace(s, first, tabList.isEmpty()));
583             paragraph->appendChild(textNode.release(), ec);
584             ASSERT(ec == 0);
585         }
586
587         // there is a tab after every entry, except the last entry
588         // (if the last character is a tab, the list gets an extra empty entry)
589         if (!tabList.isEmpty())
590             tabText += '\t';
591         else if (!tabText.isEmpty()) {
592             paragraph->appendChild(createTabSpanElement(document, tabText), ec);
593             ASSERT(ec == 0);
594         }
595         
596         first = false;
597     }
598 }
599
600 PassRefPtr<DocumentFragment> createFragmentFromText(Range* context, const String& text)
601 {
602     if (!context)
603         return 0;
604
605     Node* styleNode = context->startNode();
606     if (!styleNode) {
607         styleNode = context->startPosition().node();
608         if (!styleNode)
609             return 0;
610     }
611
612     Document* document = styleNode->document();
613     RefPtr<DocumentFragment> fragment = document->createDocumentFragment();
614     
615     if (text.isEmpty())
616         return fragment.release();
617
618     DeprecatedString string = text.deprecatedString();
619     string.replace("\r\n", "\n");
620     string.replace('\r', '\n');
621
622     ExceptionCode ec = 0;
623     RenderObject* renderer = styleNode->renderer();
624     if (renderer && renderer->style()->preserveNewline()) {
625         fragment->appendChild(document->createTextNode(string), ec);
626         ASSERT(ec == 0);
627         if (string.endsWith("\n")) {
628             RefPtr<Element> element;
629             element = document->createElementNS(xhtmlNamespaceURI, "br", ec);
630             ASSERT(ec == 0);
631             element->setAttribute(classAttr, AppleInterchangeNewline);            
632             fragment->appendChild(element.release(), ec);
633             ASSERT(ec == 0);
634         }
635         return fragment.release();
636     }
637
638     // A string with no newlines gets added inline, rather than being put into a paragraph.
639     if (string.find('\n') == -1) {
640         fillContainerFromString(fragment.get(), string);
641         return fragment.release();
642     }
643
644     // Break string into paragraphs. Extra line breaks turn into empty paragraphs.
645     DeprecatedStringList list = DeprecatedStringList::split('\n', string, true); // true gets us empty strings in the list
646     while (!list.isEmpty()) {
647         DeprecatedString s = list.first();
648         list.pop_front();
649
650         RefPtr<Element> element;
651         if (s.isEmpty() && list.isEmpty()) {
652             // For last line, use the "magic BR" rather than a P.
653             element = document->createElementNS(xhtmlNamespaceURI, "br", ec);
654             ASSERT(ec == 0);
655             element->setAttribute(classAttr, AppleInterchangeNewline);            
656         } else {
657             element = createDefaultParagraphElement(document);
658             fillContainerFromString(element.get(), s);
659         }
660         fragment->appendChild(element.release(), ec);
661         ASSERT(ec == 0);
662     }
663     return fragment.release();
664 }
665
666 PassRefPtr<DocumentFragment> createFragmentFromNodes(Document *document, const Vector<Node*>& nodes)
667 {
668     if (!document)
669         return 0;
670
671     // disable the delete button so it's elements are not serialized into the markup
672     if (document->frame())
673         document->frame()->editor()->deleteButtonController()->disable();
674
675     RefPtr<DocumentFragment> fragment = document->createDocumentFragment();
676
677     ExceptionCode ec = 0;
678     size_t size = nodes.size();
679     for (size_t i = 0; i < size; ++i) {
680         RefPtr<Element> element = createDefaultParagraphElement(document);
681         element->appendChild(nodes[i], ec);
682         ASSERT(ec == 0);
683         fragment->appendChild(element.release(), ec);
684         ASSERT(ec == 0);
685     }
686
687     if (document->frame())
688         document->frame()->editor()->deleteButtonController()->enable();
689
690     return fragment.release();
691 }
692
693 }