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