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