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