2009-03-17 Darin Adler <darin@apple.com>
[WebKit-https.git] / WebCore / editing / markup.cpp
1 /*
2  * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple 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 "CharacterNames.h"
31 #include "Comment.h"
32 #include "CSSComputedStyleDeclaration.h"
33 #include "CSSPrimitiveValue.h"
34 #include "CSSProperty.h"
35 #include "CSSPropertyNames.h"
36 #include "CSSRule.h"
37 #include "CSSRuleList.h"
38 #include "CSSStyleRule.h"
39 #include "CSSStyleSelector.h"
40 #include "CSSValue.h"
41 #include "CSSValueKeywords.h"
42 #include "DeleteButtonController.h"
43 #include "Document.h"
44 #include "DocumentFragment.h"
45 #include "DocumentType.h"
46 #include "Editor.h"
47 #include "Frame.h"
48 #include "HTMLElement.h"
49 #include "HTMLNames.h"
50 #include "InlineTextBox.h"
51 #include "Logging.h"
52 #include "ProcessingInstruction.h"
53 #include "QualifiedName.h"
54 #include "Range.h"
55 #include "VisibleSelection.h"
56 #include "TextIterator.h"
57 #include "htmlediting.h"
58 #include "visible_units.h"
59 #include <wtf/StdLibExtras.h>
60
61 using namespace std;
62
63 namespace WebCore {
64
65 using namespace HTMLNames;
66
67 static inline bool shouldSelfClose(const Node *node);
68
69 class AttributeChange {
70 public:
71     AttributeChange()
72         : m_name(nullAtom, nullAtom, nullAtom)
73     {
74     }
75
76     AttributeChange(PassRefPtr<Element> element, const QualifiedName& name, const String& value)
77         : m_element(element), m_name(name), m_value(value)
78     {
79     }
80
81     void apply()
82     {
83         m_element->setAttribute(m_name, m_value);
84     }
85
86 private:
87     RefPtr<Element> m_element;
88     QualifiedName m_name;
89     String m_value;
90 };
91
92 static void appendAttributeValue(Vector<UChar>& result, const String& attr, bool escapeNBSP)
93 {
94     const UChar* uchars = attr.characters();
95     unsigned len = attr.length();
96     unsigned lastCopiedFrom = 0;
97
98     DEFINE_STATIC_LOCAL(const String, ampEntity, ("&amp;"));
99     DEFINE_STATIC_LOCAL(const String, gtEntity, ("&gt;"));
100     DEFINE_STATIC_LOCAL(const String, ltEntity, ("&lt;"));
101     DEFINE_STATIC_LOCAL(const String, quotEntity, ("&quot;"));
102     DEFINE_STATIC_LOCAL(const String, nbspEntity, ("&nbsp;"));
103     
104     for (unsigned i = 0; i < len; ++i) {
105         UChar c = uchars[i];
106         switch (c) {
107             case '&':
108                 result.append(uchars + lastCopiedFrom, i - lastCopiedFrom);
109                 append(result, ampEntity);
110                 lastCopiedFrom = i + 1;
111                 break;
112             case '<':
113                 result.append(uchars + lastCopiedFrom, i - lastCopiedFrom);
114                 append(result, ltEntity);
115                 lastCopiedFrom = i + 1;
116                 break;
117             case '>':
118                 result.append(uchars + lastCopiedFrom, i - lastCopiedFrom);
119                 append(result, gtEntity);
120                 lastCopiedFrom = i + 1;
121                 break;
122             case '"':
123                 result.append(uchars + lastCopiedFrom, i - lastCopiedFrom);
124                 append(result, quotEntity);
125                 lastCopiedFrom = i + 1;
126                 break;
127             case noBreakSpace:
128                 if (escapeNBSP) {
129                     result.append(uchars + lastCopiedFrom, i - lastCopiedFrom);
130                     append(result, nbspEntity);
131                     lastCopiedFrom = i + 1;
132                 }
133                 break;
134         }
135     }
136     
137     result.append(uchars + lastCopiedFrom, len - lastCopiedFrom);
138 }
139
140 static void appendEscapedContent(Vector<UChar>& result, pair<const UChar*, size_t> range, bool escapeNBSP)
141 {
142     const UChar* uchars = range.first;
143     unsigned len = range.second;
144     unsigned lastCopiedFrom = 0;
145     
146     DEFINE_STATIC_LOCAL(const String, ampEntity, ("&amp;"));
147     DEFINE_STATIC_LOCAL(const String, gtEntity, ("&gt;"));
148     DEFINE_STATIC_LOCAL(const String, ltEntity, ("&lt;"));
149     DEFINE_STATIC_LOCAL(const String, nbspEntity, ("&nbsp;"));
150
151     for (unsigned i = 0; i < len; ++i) {
152         UChar c = uchars[i];
153         switch (c) {
154             case '&':
155                 result.append(uchars + lastCopiedFrom, i - lastCopiedFrom);
156                 append(result, ampEntity);
157                 lastCopiedFrom = i + 1;
158                 break;
159             case '<':
160                 result.append(uchars + lastCopiedFrom, i - lastCopiedFrom);
161                 append(result, ltEntity);
162                 lastCopiedFrom = i + 1;
163                 break;
164             case '>':
165                 result.append(uchars + lastCopiedFrom, i - lastCopiedFrom);
166                 append(result, gtEntity);
167                 lastCopiedFrom = i + 1;
168                 break;
169             case noBreakSpace:
170                 if (escapeNBSP) {
171                     result.append(uchars + lastCopiedFrom, i - lastCopiedFrom);
172                     append(result, nbspEntity);
173                     lastCopiedFrom = i + 1;
174                 }
175                 break;
176         }
177     }
178     
179     result.append(uchars + lastCopiedFrom, len - lastCopiedFrom);
180 }    
181
182 static String escapeContentText(const String& in, bool escapeNBSP)
183 {
184     Vector<UChar> buffer;
185     appendEscapedContent(buffer, make_pair(in.characters(), in.length()), escapeNBSP);
186     return String::adopt(buffer);
187 }
188     
189 static void appendQuotedURLAttributeValue(Vector<UChar>& result, const String& urlString)
190 {
191     UChar quoteChar = '\"';
192     String strippedURLString = urlString.stripWhiteSpace();
193     if (protocolIs(strippedURLString, "javascript")) {
194         // minimal escaping for javascript urls
195         if (strippedURLString.contains('"')) {
196             if (strippedURLString.contains('\''))
197                 strippedURLString.replace('\"', "&quot;");
198             else
199                 quoteChar = '\'';
200         }
201         result.append(quoteChar);
202         append(result, strippedURLString);
203         result.append(quoteChar);
204         return;
205     }
206
207     // FIXME: This does not fully match other browsers. Firefox percent-escapes non-ASCII characters for innerHTML.
208     result.append(quoteChar);
209     appendAttributeValue(result, urlString, false);
210     result.append(quoteChar);    
211 }
212     
213 static String stringValueForRange(const Node* node, const Range* range)
214 {
215     if (!range)
216         return node->nodeValue();
217
218     String str = node->nodeValue();
219     ExceptionCode ec;
220     if (node == range->endContainer(ec))
221         str.truncate(range->endOffset(ec));
222     if (node == range->startContainer(ec))
223         str.remove(0, range->startOffset(ec));
224     return str;
225 }
226
227 static inline pair<const UChar*, size_t> ucharRange(const Node *node, const Range *range)
228 {
229     String str = node->nodeValue();
230     const UChar* characters = str.characters();
231     size_t length = str.length();
232
233     if (range) {
234         ExceptionCode ec;
235         if (node == range->endContainer(ec))
236             length = range->endOffset(ec);
237         if (node == range->startContainer(ec)) {
238             size_t start = range->startOffset(ec);
239             characters += start;
240             length -= start;
241         }
242     }
243     
244     return make_pair(characters, length);
245 }
246     
247 static inline void appendUCharRange(Vector<UChar>& result, const pair<const UChar*, size_t> range)
248 {
249     result.append(range.first, range.second);
250 }
251     
252 static String renderedText(const Node* node, const Range* range)
253 {
254     if (!node->isTextNode())
255         return String();
256
257     ExceptionCode ec;
258     const Text* textNode = static_cast<const Text*>(node);
259     unsigned startOffset = 0;
260     unsigned endOffset = textNode->length();
261
262     if (range && node == range->startContainer(ec))
263         startOffset = range->startOffset(ec);
264     if (range && node == range->endContainer(ec))
265         endOffset = range->endOffset(ec);
266     
267     Position start(const_cast<Node*>(node), startOffset);
268     Position end(const_cast<Node*>(node), endOffset);
269     return plainText(Range::create(node->document(), start, end).get());
270 }
271
272 static PassRefPtr<CSSMutableStyleDeclaration> styleFromMatchedRulesForElement(Element* element, bool authorOnly = true)
273 {
274     RefPtr<CSSMutableStyleDeclaration> style = CSSMutableStyleDeclaration::create();
275     RefPtr<CSSRuleList> matchedRules = element->document()->styleSelector()->styleRulesForElement(element, authorOnly);
276     if (matchedRules) {
277         for (unsigned i = 0; i < matchedRules->length(); i++) {
278             if (matchedRules->item(i)->type() == CSSRule::STYLE_RULE) {
279                 RefPtr<CSSMutableStyleDeclaration> s = static_cast<CSSStyleRule*>(matchedRules->item(i))->style();
280                 style->merge(s.get(), true);
281             }
282         }
283     }
284     
285     return style.release();
286 }
287
288 static void removeEnclosingMailBlockquoteStyle(CSSMutableStyleDeclaration* style, Node* node)
289 {
290     Node* blockquote = nearestMailBlockquote(node);
291     if (!blockquote || !blockquote->parentNode())
292         return;
293             
294     RefPtr<CSSMutableStyleDeclaration> parentStyle = Position(blockquote->parentNode(), 0).computedStyle()->copyInheritableProperties();
295     RefPtr<CSSMutableStyleDeclaration> blockquoteStyle = Position(blockquote, 0).computedStyle()->copyInheritableProperties();
296     parentStyle->diff(blockquoteStyle.get());
297     blockquoteStyle->diff(style);
298 }
299
300 static void removeDefaultStyles(CSSMutableStyleDeclaration* style, Document* document)
301 {
302     if (!document || !document->documentElement())
303         return;
304             
305     RefPtr<CSSMutableStyleDeclaration> documentStyle = computedStyle(document->documentElement())->copyInheritableProperties();
306     documentStyle->diff(style);
307 }
308
309 static bool shouldAddNamespaceElem(const Element* elem)
310 {
311     // Don't add namespace attribute if it is already defined for this elem.
312     const AtomicString& prefix = elem->prefix();
313     AtomicString attr = !prefix.isEmpty() ? "xmlns:" + prefix : "xmlns";
314     return !elem->hasAttribute(attr);
315 }
316
317 static bool shouldAddNamespaceAttr(const Attribute* attr, HashMap<AtomicStringImpl*, AtomicStringImpl*>& namespaces)
318 {
319     // Don't add namespace attributes twice
320     DEFINE_STATIC_LOCAL(const AtomicString, xmlnsURI, ("http://www.w3.org/2000/xmlns/"));
321     DEFINE_STATIC_LOCAL(const QualifiedName, xmlnsAttr, (nullAtom, "xmlns", xmlnsURI));
322     if (attr->name() == xmlnsAttr) {
323         namespaces.set(emptyAtom.impl(), attr->value().impl());
324         return false;
325     }
326     
327     QualifiedName xmlnsPrefixAttr("xmlns", attr->localName(), xmlnsURI);
328     if (attr->name() == xmlnsPrefixAttr) {
329         namespaces.set(attr->localName().impl(), attr->value().impl());
330         return false;
331     }
332     
333     return true;
334 }
335
336 static void appendNamespace(Vector<UChar>& result, const AtomicString& prefix, const AtomicString& ns, HashMap<AtomicStringImpl*, AtomicStringImpl*>& namespaces)
337 {
338     if (ns.isEmpty())
339         return;
340         
341     // Use emptyAtoms's impl() for both null and empty strings since the HashMap can't handle 0 as a key
342     AtomicStringImpl* pre = prefix.isEmpty() ? emptyAtom.impl() : prefix.impl();
343     AtomicStringImpl* foundNS = namespaces.get(pre);
344     if (foundNS != ns.impl()) {
345         namespaces.set(pre, ns.impl());
346         DEFINE_STATIC_LOCAL(const String, xmlns, ("xmlns"));
347         result.append(' ');
348         append(result, xmlns);
349         if (!prefix.isEmpty()) {
350             result.append(':');
351             append(result, prefix);
352         }
353
354         result.append('=');
355         result.append('"');
356         appendAttributeValue(result, ns, false);
357         result.append('"');
358     }
359 }
360
361 static void appendDocumentType(Vector<UChar>& result, const DocumentType* n)
362 {
363     if (n->name().isEmpty())
364         return;
365
366     append(result, "<!DOCTYPE ");
367     append(result, n->name());
368     if (!n->publicId().isEmpty()) {
369         append(result, " PUBLIC \"");
370         append(result, n->publicId());
371         append(result, "\"");
372         if (!n->systemId().isEmpty()) {
373             append(result, " \"");
374             append(result, n->systemId());
375             append(result, "\"");
376         }
377     } else if (!n->systemId().isEmpty()) {
378         append(result, " SYSTEM \"");
379         append(result, n->systemId());
380         append(result, "\"");
381     }
382     if (!n->internalSubset().isEmpty()) {
383         append(result, " [");
384         append(result, n->internalSubset());
385         append(result, "]");
386     }
387     append(result, ">");
388 }
389
390 static void appendStartMarkup(Vector<UChar>& result, const Node *node, const Range *range, EAnnotateForInterchange annotate, bool convertBlocksToInlines = false, HashMap<AtomicStringImpl*, AtomicStringImpl*>* namespaces = 0)
391 {
392     bool documentIsHTML = node->document()->isHTMLDocument();
393     switch (node->nodeType()) {
394         case Node::TEXT_NODE: {
395             if (Node* parent = node->parentNode()) {
396                 if (parent->hasTagName(scriptTag)
397                     || parent->hasTagName(styleTag)
398                     || parent->hasTagName(textareaTag)
399                     || parent->hasTagName(xmpTag)) {
400                     appendUCharRange(result, ucharRange(node, range));
401                     break;
402                 }
403             }
404             if (!annotate) {
405                 appendEscapedContent(result, ucharRange(node, range), documentIsHTML);
406                 break;
407             }
408             
409             bool useRenderedText = !enclosingNodeWithTag(Position(const_cast<Node*>(node), 0), selectTag);
410             String markup = escapeContentText(useRenderedText ? renderedText(node, range) : stringValueForRange(node, range), false);
411             if (annotate)
412                 markup = convertHTMLTextToInterchangeFormat(markup, static_cast<const Text*>(node));
413             append(result, markup);
414             break;
415         }
416         case Node::COMMENT_NODE:
417             // FIXME: Comment content is not escaped, but XMLSerializer (and possibly other callers) should raise an exception if it includes "-->".
418             append(result, "<!--");
419             append(result, static_cast<const Comment*>(node)->nodeValue());
420             append(result, "-->");
421             break;
422         case Node::DOCUMENT_NODE:
423         case Node::DOCUMENT_FRAGMENT_NODE:
424             break;
425         case Node::DOCUMENT_TYPE_NODE:
426             appendDocumentType(result, static_cast<const DocumentType*>(node));
427             break;
428         case Node::PROCESSING_INSTRUCTION_NODE: {
429             // FIXME: PI data is not escaped, but XMLSerializer (and possibly other callers) this should raise an exception if it includes "?>".
430             const ProcessingInstruction* n = static_cast<const ProcessingInstruction*>(node);
431             append(result, "<?");
432             append(result, n->target());
433             append(result, " ");
434             append(result, n->data());
435             append(result, "?>");
436             break;
437         }
438         case Node::ELEMENT_NODE: {
439             result.append('<');
440             const Element* el = static_cast<const Element*>(node);
441             bool convert = convertBlocksToInlines && isBlock(const_cast<Node*>(node));
442             append(result, el->nodeNamePreservingCase());
443             NamedAttrMap *attrs = el->attributes();
444             unsigned length = attrs->length();
445             if (!documentIsHTML && namespaces && shouldAddNamespaceElem(el))
446                 appendNamespace(result, el->prefix(), el->namespaceURI(), *namespaces);
447
448             for (unsigned int i = 0; i < length; i++) {
449                 Attribute *attr = attrs->attributeItem(i);
450                 // We'll handle the style attribute separately, below.
451                 if (attr->name() == styleAttr && el->isHTMLElement() && (annotate || convert))
452                     continue;
453                 result.append(' ');
454
455                 if (documentIsHTML)
456                     append(result, attr->name().localName());
457                 else
458                     append(result, attr->name().toString());
459
460                 result.append('=');
461
462                 if (el->isURLAttribute(attr))
463                     appendQuotedURLAttributeValue(result, attr->value());
464                 else {
465                     result.append('\"');
466                     appendAttributeValue(result, attr->value(), documentIsHTML);
467                     result.append('\"');
468                 }
469
470                 if (!documentIsHTML && namespaces && shouldAddNamespaceAttr(attr, *namespaces))
471                     appendNamespace(result, attr->prefix(), attr->namespaceURI(), *namespaces);
472             }
473             
474             if (el->isHTMLElement() && (annotate || convert)) {
475                 Element* element = const_cast<Element*>(el);
476                 RefPtr<CSSMutableStyleDeclaration> style = static_cast<HTMLElement*>(element)->getInlineStyleDecl()->copy();
477                 if (annotate) {
478                     RefPtr<CSSMutableStyleDeclaration> styleFromMatchedRules = styleFromMatchedRulesForElement(const_cast<Element*>(el));
479                     // Styles from the inline style declaration, held in the variable "style", take precedence 
480                     // over those from matched rules.
481                     styleFromMatchedRules->merge(style.get());
482                     style = styleFromMatchedRules;
483                     
484                     RefPtr<CSSComputedStyleDeclaration> computedStyleForElement = computedStyle(element);
485                     RefPtr<CSSMutableStyleDeclaration> fromComputedStyle = CSSMutableStyleDeclaration::create();
486                     
487                     {
488                         CSSMutableStyleDeclaration::const_iterator end = style->end();
489                         for (CSSMutableStyleDeclaration::const_iterator it = style->begin(); it != end; ++it) {
490                             const CSSProperty& property = *it;
491                             CSSValue* value = property.value();
492                             // The property value, if it's a percentage, may not reflect the actual computed value.  
493                             // For example: style="height: 1%; overflow: visible;" in quirksmode
494                             // FIXME: There are others like this, see <rdar://problem/5195123> Slashdot copy/paste fidelity problem
495                             if (value->cssValueType() == CSSValue::CSS_PRIMITIVE_VALUE)
496                                 if (static_cast<CSSPrimitiveValue*>(value)->primitiveType() == CSSPrimitiveValue::CSS_PERCENTAGE)
497                                     if (RefPtr<CSSValue> computedPropertyValue = computedStyleForElement->getPropertyCSSValue(property.id()))
498                                         fromComputedStyle->addParsedProperty(CSSProperty(property.id(), computedPropertyValue));
499                         }
500                     }
501                     
502                     style->merge(fromComputedStyle.get());
503                 }
504                 if (convert)
505                     style->setProperty(CSSPropertyDisplay, CSSValueInline, true);
506                 if (style->length() > 0) {
507                     DEFINE_STATIC_LOCAL(const String, stylePrefix, (" style=\""));
508                     append(result, stylePrefix);
509                     appendAttributeValue(result, style->cssText(), documentIsHTML);
510                     result.append('\"');
511                 }
512             }
513             
514             if (shouldSelfClose(el)) {
515                 if (el->isHTMLElement())
516                     result.append(' '); // XHTML 1.0 <-> HTML compatibility.
517                 result.append('/');
518             }
519             result.append('>');
520             break;
521         }
522         case Node::CDATA_SECTION_NODE: {
523             // FIXME: CDATA content is not escaped, but XMLSerializer (and possibly other callers) should raise an exception if it includes "]]>".
524             const CDATASection* n = static_cast<const CDATASection*>(node);
525             append(result, "<![CDATA[");
526             append(result, n->data());
527             append(result, "]]>");
528             break;
529         }
530         case Node::ATTRIBUTE_NODE:
531         case Node::ENTITY_NODE:
532         case Node::ENTITY_REFERENCE_NODE:
533         case Node::NOTATION_NODE:
534         case Node::XPATH_NAMESPACE_NODE:
535             ASSERT_NOT_REACHED();
536             break;
537     }
538 }
539
540 static String getStartMarkup(const Node *node, const Range *range, EAnnotateForInterchange annotate, bool convertBlocksToInlines = false, HashMap<AtomicStringImpl*, AtomicStringImpl*>* namespaces = 0)
541 {
542     Vector<UChar> result;
543     appendStartMarkup(result, node, range, annotate, convertBlocksToInlines, namespaces);
544     return String::adopt(result);
545 }
546
547 static inline bool doesHTMLForbidEndTag(const Node *node)
548 {
549     if (node->isHTMLElement()) {
550         const HTMLElement* htmlElt = static_cast<const HTMLElement*>(node);
551         return (htmlElt->endTagRequirement() == TagStatusForbidden);
552     }
553     return false;
554 }
555
556 // Rules of self-closure
557 // 1. No elements in HTML documents use the self-closing syntax.
558 // 2. Elements w/ children never self-close because they use a separate end tag.
559 // 3. HTML elements which do not have a "forbidden" end tag will close with a separate end tag.
560 // 4. Other elements self-close.
561 static inline bool shouldSelfClose(const Node *node)
562 {
563     if (node->document()->isHTMLDocument())
564         return false;
565     if (node->hasChildNodes())
566         return false;
567     if (node->isHTMLElement() && !doesHTMLForbidEndTag(node))
568         return false;
569     return true;
570 }
571
572 static void appendEndMarkup(Vector<UChar>& result, const Node* node)
573 {
574     if (!node->isElementNode() || shouldSelfClose(node) || (!node->hasChildNodes() && doesHTMLForbidEndTag(node)))
575         return;
576
577     result.append('<');
578     result.append('/');
579     append(result, static_cast<const Element*>(node)->nodeNamePreservingCase());
580     result.append('>');
581 }
582
583 static String getEndMarkup(const Node *node)
584 {
585     Vector<UChar> result;
586     appendEndMarkup(result, node);
587     return String::adopt(result);
588 }
589
590 class MarkupAccumulator {
591 public:
592     MarkupAccumulator(Node* nodeToSkip, Vector<Node*>* nodes)
593         : m_nodeToSkip(nodeToSkip)
594         , m_nodes(nodes)
595     {
596     }
597
598     void appendMarkup(Node* startNode, EChildrenOnly, const HashMap<AtomicStringImpl*, AtomicStringImpl*>* namespaces = 0);
599
600     String takeResult() { return String::adopt(m_result); }
601
602 private:
603     Vector<UChar> m_result;
604     Node* m_nodeToSkip;
605     Vector<Node*>* m_nodes;
606 };
607
608 // FIXME: Would be nice to do this in a non-recursive way.
609 void MarkupAccumulator::appendMarkup(Node* startNode, EChildrenOnly childrenOnly, const HashMap<AtomicStringImpl*, AtomicStringImpl*>* namespaces)
610 {
611     if (startNode == m_nodeToSkip)
612         return;
613
614     HashMap<AtomicStringImpl*, AtomicStringImpl*> namespaceHash;
615     if (namespaces)
616         namespaceHash = *namespaces;
617
618     // start tag
619     if (!childrenOnly) {
620         if (m_nodes)
621             m_nodes->append(startNode);
622         appendStartMarkup(m_result, startNode, 0, DoNotAnnotateForInterchange, false, &namespaceHash);
623     }
624
625     // children
626     if (!(startNode->document()->isHTMLDocument() && doesHTMLForbidEndTag(startNode))) {
627         for (Node* current = startNode->firstChild(); current; current = current->nextSibling())
628             appendMarkup(current, IncludeNode, &namespaceHash);
629     }
630
631     // end tag
632     if (!childrenOnly)
633         appendEndMarkup(m_result, startNode);
634 }
635
636 static void completeURLs(Node* node, const String& baseURL)
637 {
638     Vector<AttributeChange> changes;
639
640     KURL parsedBaseURL(baseURL);
641
642     Node* end = node->traverseNextSibling();
643     for (Node* n = node; n != end; n = n->traverseNextNode()) {
644         if (n->isElementNode()) {
645             Element* e = static_cast<Element*>(n);
646             NamedAttrMap* attrs = e->attributes();
647             unsigned length = attrs->length();
648             for (unsigned i = 0; i < length; i++) {
649                 Attribute* attr = attrs->attributeItem(i);
650                 if (e->isURLAttribute(attr))
651                     changes.append(AttributeChange(e, attr->name(), KURL(parsedBaseURL, attr->value()).string()));
652             }
653         }
654     }
655
656     size_t numChanges = changes.size();
657     for (size_t i = 0; i < numChanges; ++i)
658         changes[i].apply();
659 }
660
661 static bool needInterchangeNewlineAfter(const VisiblePosition& v)
662 {
663     VisiblePosition next = v.next();
664     Node* upstreamNode = next.deepEquivalent().upstream().node();
665     Node* downstreamNode = v.deepEquivalent().downstream().node();
666     // Add an interchange newline if a paragraph break is selected and a br won't already be added to the markup to represent it.
667     return isEndOfParagraph(v) && isStartOfParagraph(next) && !(upstreamNode->hasTagName(brTag) && upstreamNode == downstreamNode);
668 }
669
670 static PassRefPtr<CSSMutableStyleDeclaration> styleFromMatchedRulesAndInlineDecl(const Node* node)
671 {
672     if (!node->isHTMLElement())
673         return 0;
674     
675     // FIXME: Having to const_cast here is ugly, but it is quite a bit of work to untangle
676     // the non-const-ness of styleFromMatchedRulesForElement.
677     HTMLElement* element = const_cast<HTMLElement*>(static_cast<const HTMLElement*>(node));
678     RefPtr<CSSMutableStyleDeclaration> style = styleFromMatchedRulesForElement(element);
679     RefPtr<CSSMutableStyleDeclaration> inlineStyleDecl = element->getInlineStyleDecl();
680     style->merge(inlineStyleDecl.get());
681     return style.release();
682 }
683
684 static bool propertyMissingOrEqualToNone(CSSMutableStyleDeclaration* style, int propertyID)
685 {
686     if (!style)
687         return false;
688     RefPtr<CSSValue> value = style->getPropertyCSSValue(propertyID);
689     if (!value)
690         return true;
691     if (!value->isPrimitiveValue())
692         return false;
693     return static_cast<CSSPrimitiveValue*>(value.get())->getIdent() == CSSValueNone;
694 }
695
696 static bool elementHasTextDecorationProperty(const Node* node)
697 {
698     RefPtr<CSSMutableStyleDeclaration> style = styleFromMatchedRulesAndInlineDecl(node);
699     if (!style)
700         return false;
701     return !propertyMissingOrEqualToNone(style.get(), CSSPropertyTextDecoration);
702 }
703
704 static String joinMarkups(const Vector<String>& preMarkups, const Vector<String>& postMarkups)
705 {
706     size_t length = 0;
707
708     size_t preCount = preMarkups.size();
709     for (size_t i = 0; i < preCount; ++i)
710         length += preMarkups[i].length();
711
712     size_t postCount = postMarkups.size();
713     for (size_t i = 0; i < postCount; ++i)
714         length += postMarkups[i].length();
715
716     Vector<UChar> result;
717     result.reserveInitialCapacity(length);
718
719     for (size_t i = preCount; i > 0; --i)
720         append(result, preMarkups[i - 1]);
721
722     for (size_t i = 0; i < postCount; ++i)
723         append(result, postMarkups[i]);
724
725     return String::adopt(result);
726 }
727
728 static bool isSpecialAncestorBlock(Node* node)
729 {
730     if (!node || !isBlock(node))
731         return false;
732         
733     return node->hasTagName(listingTag) ||
734            node->hasTagName(olTag) ||
735            node->hasTagName(preTag) ||
736            node->hasTagName(tableTag) ||
737            node->hasTagName(ulTag) ||
738            node->hasTagName(xmpTag) ||
739            node->hasTagName(h1Tag) ||
740            node->hasTagName(h2Tag) ||
741            node->hasTagName(h3Tag) ||
742            node->hasTagName(h4Tag) ||
743            node->hasTagName(h5Tag);
744 }
745
746 // FIXME: Shouldn't we omit style info when annotate == DoNotAnnotateForInterchange? 
747 // FIXME: At least, annotation and style info should probably not be included in range.markupString()
748 String createMarkup(const Range* range, Vector<Node*>* nodes, EAnnotateForInterchange annotate, bool convertBlocksToInlines)
749 {
750     DEFINE_STATIC_LOCAL(const String, interchangeNewlineString, ("<br class=\"" AppleInterchangeNewline "\">"));
751
752     if (!range)
753         return "";
754
755     Document* document = range->ownerDocument();
756     if (!document)
757         return "";
758
759     bool documentIsHTML = document->isHTMLDocument();
760
761     // Disable the delete button so it's elements are not serialized into the markup,
762     // but make sure neither endpoint is inside the delete user interface.
763     Frame* frame = document->frame();
764     DeleteButtonController* deleteButton = frame ? frame->editor()->deleteButtonController() : 0;
765     RefPtr<Range> updatedRange = avoidIntersectionWithNode(range, deleteButton ? deleteButton->containerElement() : 0);
766     if (!updatedRange)
767         return "";
768
769     if (deleteButton)
770         deleteButton->disable();
771
772     ExceptionCode ec = 0;
773     bool collapsed = updatedRange->collapsed(ec);
774     ASSERT(ec == 0);
775     if (collapsed)
776         return "";
777     Node* commonAncestor = updatedRange->commonAncestorContainer(ec);
778     ASSERT(ec == 0);
779     if (!commonAncestor)
780         return "";
781
782     document->updateLayoutIgnorePendingStylesheets();
783
784     Vector<String> markups;
785     Vector<String> preMarkups;
786     Node* pastEnd = updatedRange->pastLastNode();
787     Node* lastClosed = 0;
788     Vector<Node*> ancestorsToClose;
789     
790     Node* startNode = updatedRange->firstNode();
791     VisiblePosition visibleStart(updatedRange->startPosition(), VP_DEFAULT_AFFINITY);
792     VisiblePosition visibleEnd(updatedRange->endPosition(), VP_DEFAULT_AFFINITY);
793     if (annotate && needInterchangeNewlineAfter(visibleStart)) {
794         if (visibleStart == visibleEnd.previous()) {
795             if (deleteButton)
796                 deleteButton->enable();
797             return interchangeNewlineString;
798         }
799
800         markups.append(interchangeNewlineString);
801         startNode = visibleStart.next().deepEquivalent().node();
802     }
803
804     Node* next;
805     for (Node* n = startNode; n != pastEnd; n = next) {
806     
807         // According to <rdar://problem/5730668>, it is possible for n to blow past pastEnd and become null here.  This 
808         // shouldn't be possible.  This null check will prevent crashes (but create too much markup) and the ASSERT will 
809         // hopefully lead us to understanding the problem.
810         ASSERT(n);
811         if (!n)
812             break;
813     
814         next = n->traverseNextNode();
815         bool skipDescendants = false;
816         bool addMarkupForNode = true;
817         
818         if (!n->renderer() && !enclosingNodeWithTag(Position(n, 0), selectTag)) {
819             skipDescendants = true;
820             addMarkupForNode = false;
821             next = n->traverseNextSibling();
822             // Don't skip over pastEnd.
823             if (pastEnd && pastEnd->isDescendantOf(n))
824                 next = pastEnd;
825         }
826
827         if (isBlock(n) && canHaveChildrenForEditing(n) && next == pastEnd)
828             // Don't write out empty block containers that aren't fully selected.
829             continue;
830         
831         // Add the node to the markup.
832         if (addMarkupForNode) {
833             markups.append(getStartMarkup(n, updatedRange.get(), annotate));
834             if (nodes)
835                 nodes->append(n);
836         }
837         
838         if (n->firstChild() == 0 || skipDescendants) {
839             // Node has no children, or we are skipping it's descendants, add its close tag now.
840             if (addMarkupForNode) {
841                 markups.append(getEndMarkup(n));
842                 lastClosed = n;
843             }
844             
845             // Check if the node is the last leaf of a tree.
846             if (!n->nextSibling() || next == pastEnd) {
847                 if (!ancestorsToClose.isEmpty()) {
848                     // Close up the ancestors.
849                     do {
850                         Node *ancestor = ancestorsToClose.last();
851                         if (next != pastEnd && next->isDescendantOf(ancestor))
852                             break;
853                         // Not at the end of the range, close ancestors up to sibling of next node.
854                         markups.append(getEndMarkup(ancestor));
855                         lastClosed = ancestor;
856                         ancestorsToClose.removeLast();
857                     } while (!ancestorsToClose.isEmpty());
858                 }
859                 
860                 // Surround the currently accumulated markup with markup for ancestors we never opened as we leave the subtree(s) rooted at those ancestors.
861                 Node* nextParent = next ? next->parentNode() : 0;
862                 if (next != pastEnd && n != nextParent) {
863                     Node* lastAncestorClosedOrSelf = n->isDescendantOf(lastClosed) ? lastClosed : n;
864                     for (Node *parent = lastAncestorClosedOrSelf->parent(); parent != 0 && parent != nextParent; parent = parent->parentNode()) {
865                         // All ancestors that aren't in the ancestorsToClose list should either be a) unrendered:
866                         if (!parent->renderer())
867                             continue;
868                         // or b) ancestors that we never encountered during a pre-order traversal starting at startNode:
869                         ASSERT(startNode->isDescendantOf(parent));
870                         preMarkups.append(getStartMarkup(parent, updatedRange.get(), annotate));
871                         markups.append(getEndMarkup(parent));
872                         if (nodes)
873                             nodes->append(parent);
874                         lastClosed = parent;
875                     }
876                 }
877             }
878         } else if (addMarkupForNode && !skipDescendants)
879             // We added markup for this node, and we're descending into it.  Set it to close eventually.
880             ancestorsToClose.append(n);
881     }
882     
883     // Include ancestors that aren't completely inside the range but are required to retain 
884     // the structure and appearance of the copied markup.
885     Node* specialCommonAncestor = 0;
886     Node* commonAncestorBlock = commonAncestor ? enclosingBlock(commonAncestor) : 0;
887     if (annotate && commonAncestorBlock) {
888         if (commonAncestorBlock->hasTagName(tbodyTag) || commonAncestorBlock->hasTagName(trTag)) {
889             Node* table = commonAncestorBlock->parentNode();
890             while (table && !table->hasTagName(tableTag))
891                 table = table->parentNode();
892             if (table)
893                 specialCommonAncestor = table;
894         } else if (isSpecialAncestorBlock(commonAncestorBlock))
895             specialCommonAncestor = commonAncestorBlock;
896     }
897                                       
898     // Retain the Mail quote level by including all ancestor mail block quotes.
899     if (lastClosed && annotate) {
900         for (Node *ancestor = lastClosed->parentNode(); ancestor; ancestor = ancestor->parentNode())
901             if (isMailBlockquote(ancestor))
902                 specialCommonAncestor = ancestor;
903     }
904     
905     Node* checkAncestor = specialCommonAncestor ? specialCommonAncestor : commonAncestor;
906     if (checkAncestor->renderer()) {
907         RefPtr<CSSMutableStyleDeclaration> checkAncestorStyle = computedStyle(checkAncestor)->copyInheritableProperties();
908         if (!propertyMissingOrEqualToNone(checkAncestorStyle.get(), CSSPropertyWebkitTextDecorationsInEffect))
909             specialCommonAncestor = enclosingNodeOfType(Position(checkAncestor, 0), &elementHasTextDecorationProperty);
910     }
911     
912     // If a single tab is selected, commonAncestor will be a text node inside a tab span.
913     // If two or more tabs are selected, commonAncestor will be the tab span.
914     // In either case, if there is a specialCommonAncestor already, it will necessarily be above 
915     // any tab span that needs to be included.
916     if (!specialCommonAncestor && isTabSpanTextNode(commonAncestor))
917         specialCommonAncestor = commonAncestor->parentNode();
918     if (!specialCommonAncestor && isTabSpanNode(commonAncestor))
919         specialCommonAncestor = commonAncestor;
920         
921     if (Node *enclosingAnchor = enclosingNodeWithTag(Position(specialCommonAncestor ? specialCommonAncestor : commonAncestor, 0), aTag))
922         specialCommonAncestor = enclosingAnchor;
923     
924     Node* body = enclosingNodeWithTag(Position(commonAncestor, 0), bodyTag);
925     // FIXME: Only include markup for a fully selected root (and ancestors of lastClosed up to that root) if
926     // there are styles/attributes on those nodes that need to be included to preserve the appearance of the copied markup.
927     // FIXME: Do this for all fully selected blocks, not just the body.
928     Node* fullySelectedRoot = body && *VisibleSelection::selectionFromContentsOfNode(body).toNormalizedRange() == *updatedRange ? body : 0;
929     if (annotate && fullySelectedRoot)
930         specialCommonAncestor = fullySelectedRoot;
931         
932     if (specialCommonAncestor && lastClosed) {
933         // Also include all of the ancestors of lastClosed up to this special ancestor.
934         for (Node* ancestor = lastClosed->parentNode(); ancestor; ancestor = ancestor->parentNode()) {
935             if (ancestor == fullySelectedRoot && !convertBlocksToInlines) {
936                 RefPtr<CSSMutableStyleDeclaration> style = styleFromMatchedRulesAndInlineDecl(fullySelectedRoot);
937                 
938                 // Bring the background attribute over, but not as an attribute because a background attribute on a div
939                 // appears to have no effect.
940                 if (!style->getPropertyCSSValue(CSSPropertyBackgroundImage) && static_cast<Element*>(fullySelectedRoot)->hasAttribute(backgroundAttr))
941                     style->setProperty(CSSPropertyBackgroundImage, "url('" + static_cast<Element*>(fullySelectedRoot)->getAttribute(backgroundAttr) + "')");
942                 
943                 if (style->length()) {
944                     Vector<UChar> openTag;
945                     DEFINE_STATIC_LOCAL(const String, divStyle, ("<div style=\""));
946                     append(openTag, divStyle);
947                     appendAttributeValue(openTag, style->cssText(), documentIsHTML);
948                     openTag.append('\"');
949                     openTag.append('>');
950                     preMarkups.append(String::adopt(openTag));
951
952                     DEFINE_STATIC_LOCAL(const String, divCloseTag, ("</div>"));
953                     markups.append(divCloseTag);
954                 }
955             } else {
956                 preMarkups.append(getStartMarkup(ancestor, updatedRange.get(), annotate, convertBlocksToInlines));
957                 markups.append(getEndMarkup(ancestor));
958             }
959             if (nodes)
960                 nodes->append(ancestor);
961             
962             lastClosed = ancestor;
963             
964             if (ancestor == specialCommonAncestor)
965                 break;
966         }
967     }
968     
969     DEFINE_STATIC_LOCAL(const String, styleSpanOpen, ("<span class=\"" AppleStyleSpanClass "\" style=\""));
970     DEFINE_STATIC_LOCAL(const String, styleSpanClose, ("</span>"));
971     
972     // Add a wrapper span with the styles that all of the nodes in the markup inherit.
973     Node* parentOfLastClosed = lastClosed ? lastClosed->parentNode() : 0;
974     if (parentOfLastClosed && parentOfLastClosed->renderer()) {
975         RefPtr<CSSMutableStyleDeclaration> style = computedStyle(parentOfLastClosed)->copyInheritableProperties();
976
977         // Styles that Mail blockquotes contribute should only be placed on the Mail blockquote, to help
978         // us differentiate those styles from ones that the user has applied.  This helps us
979         // get the color of content pasted into blockquotes right.
980         removeEnclosingMailBlockquoteStyle(style.get(), parentOfLastClosed);
981         
982         // Document default styles will be added on another wrapper span.
983         removeDefaultStyles(style.get(), document);
984         
985         // Since we are converting blocks to inlines, remove any inherited block properties that are in the style.
986         // This cuts out meaningless properties and prevents properties from magically affecting blocks later
987         // if the style is cloned for a new block element during a future editing operation.
988         if (convertBlocksToInlines)
989             style->removeBlockProperties();
990
991         if (style->length() > 0) {
992             Vector<UChar> openTag;
993             append(openTag, styleSpanOpen);
994             appendAttributeValue(openTag, style->cssText(), documentIsHTML);
995             openTag.append('\"');
996             openTag.append('>');
997             preMarkups.append(String::adopt(openTag));
998             
999             markups.append(styleSpanClose);
1000         }
1001     }
1002     
1003     if (lastClosed && lastClosed != document->documentElement()) {
1004         // Add a style span with the document's default styles.  We add these in a separate
1005         // span so that at paste time we can differentiate between document defaults and user
1006         // applied styles.
1007         RefPtr<CSSMutableStyleDeclaration> defaultStyle = computedStyle(document->documentElement())->copyInheritableProperties();
1008         
1009         if (defaultStyle->length() > 0) {
1010             Vector<UChar> openTag;
1011             append(openTag, styleSpanOpen);
1012             appendAttributeValue(openTag, defaultStyle->cssText(), documentIsHTML);
1013             openTag.append('\"');
1014             openTag.append('>');
1015             preMarkups.append(String::adopt(openTag));
1016             markups.append(styleSpanClose);
1017         }
1018     }
1019
1020     // FIXME: The interchange newline should be placed in the block that it's in, not after all of the content, unconditionally.
1021     if (annotate && needInterchangeNewlineAfter(visibleEnd.previous()))
1022         markups.append(interchangeNewlineString);
1023     
1024     if (deleteButton)
1025         deleteButton->enable();
1026
1027     return joinMarkups(preMarkups, markups);
1028 }
1029
1030 PassRefPtr<DocumentFragment> createFragmentFromMarkup(Document* document, const String& markup, const String& baseURL)
1031 {
1032     ASSERT(document->documentElement()->isHTMLElement());
1033     // FIXME: What if the document element is not an HTML element?
1034     HTMLElement *element = static_cast<HTMLElement*>(document->documentElement());
1035
1036     RefPtr<DocumentFragment> fragment = element->createContextualFragment(markup);
1037
1038     if (fragment && !baseURL.isEmpty() && baseURL != blankURL() && baseURL != document->baseURL())
1039         completeURLs(fragment.get(), baseURL);
1040
1041     return fragment.release();
1042 }
1043
1044 String createMarkup(const Node* node, EChildrenOnly childrenOnly, Vector<Node*>* nodes)
1045 {
1046     if (!node)
1047         return "";
1048
1049     HTMLElement* deleteButtonContainerElement = 0;
1050     if (Frame* frame = node->document()->frame()) {
1051         deleteButtonContainerElement = frame->editor()->deleteButtonController()->containerElement();
1052         if (node->isDescendantOf(deleteButtonContainerElement))
1053             return "";
1054     }
1055
1056     MarkupAccumulator accumulator(deleteButtonContainerElement, nodes);
1057     accumulator.appendMarkup(const_cast<Node*>(node), childrenOnly);
1058     return accumulator.takeResult();
1059 }
1060
1061 static void fillContainerFromString(ContainerNode* paragraph, const String& string)
1062 {
1063     Document* document = paragraph->document();
1064
1065     ExceptionCode ec = 0;
1066     if (string.isEmpty()) {
1067         paragraph->appendChild(createBlockPlaceholderElement(document), ec);
1068         ASSERT(ec == 0);
1069         return;
1070     }
1071
1072     ASSERT(string.find('\n') == -1);
1073
1074     Vector<String> tabList;
1075     string.split('\t', true, tabList);
1076     String tabText = "";
1077     bool first = true;
1078     size_t numEntries = tabList.size();
1079     for (size_t i = 0; i < numEntries; ++i) {
1080         const String& s = tabList[i];
1081
1082         // append the non-tab textual part
1083         if (!s.isEmpty()) {
1084             if (!tabText.isEmpty()) {
1085                 paragraph->appendChild(createTabSpanElement(document, tabText), ec);
1086                 ASSERT(ec == 0);
1087                 tabText = "";
1088             }
1089             RefPtr<Node> textNode = document->createTextNode(stringWithRebalancedWhitespace(s, first, i + 1 == numEntries));
1090             paragraph->appendChild(textNode.release(), ec);
1091             ASSERT(ec == 0);
1092         }
1093
1094         // there is a tab after every entry, except the last entry
1095         // (if the last character is a tab, the list gets an extra empty entry)
1096         if (i + 1 != numEntries)
1097             tabText.append('\t');
1098         else if (!tabText.isEmpty()) {
1099             paragraph->appendChild(createTabSpanElement(document, tabText), ec);
1100             ASSERT(ec == 0);
1101         }
1102         
1103         first = false;
1104     }
1105 }
1106
1107 PassRefPtr<DocumentFragment> createFragmentFromText(Range* context, const String& text)
1108 {
1109     if (!context)
1110         return 0;
1111
1112     Node* styleNode = context->firstNode();
1113     if (!styleNode) {
1114         styleNode = context->startPosition().node();
1115         if (!styleNode)
1116             return 0;
1117     }
1118
1119     Document* document = styleNode->document();
1120     RefPtr<DocumentFragment> fragment = document->createDocumentFragment();
1121     
1122     if (text.isEmpty())
1123         return fragment.release();
1124
1125     String string = text;
1126     string.replace("\r\n", "\n");
1127     string.replace('\r', '\n');
1128
1129     ExceptionCode ec = 0;
1130     RenderObject* renderer = styleNode->renderer();
1131     if (renderer && renderer->style()->preserveNewline()) {
1132         fragment->appendChild(document->createTextNode(string), ec);
1133         ASSERT(ec == 0);
1134         if (string.endsWith("\n")) {
1135             RefPtr<Element> element = createBreakElement(document);
1136             element->setAttribute(classAttr, AppleInterchangeNewline);            
1137             fragment->appendChild(element.release(), ec);
1138             ASSERT(ec == 0);
1139         }
1140         return fragment.release();
1141     }
1142
1143     // A string with no newlines gets added inline, rather than being put into a paragraph.
1144     if (string.find('\n') == -1) {
1145         fillContainerFromString(fragment.get(), string);
1146         return fragment.release();
1147     }
1148
1149     // Break string into paragraphs. Extra line breaks turn into empty paragraphs.
1150     Node* blockNode = enclosingBlock(context->firstNode());
1151     Element* block = static_cast<Element*>(blockNode);
1152     bool useClonesOfEnclosingBlock = blockNode
1153         && blockNode->isElementNode()
1154         && !block->hasTagName(bodyTag)
1155         && !block->hasTagName(htmlTag)
1156         && block != editableRootForPosition(context->startPosition());
1157     
1158     Vector<String> list;
1159     string.split('\n', true, list); // true gets us empty strings in the list
1160     size_t numLines = list.size();
1161     for (size_t i = 0; i < numLines; ++i) {
1162         const String& s = list[i];
1163
1164         RefPtr<Element> element;
1165         if (s.isEmpty() && i + 1 == numLines) {
1166             // For last line, use the "magic BR" rather than a P.
1167             element = createBreakElement(document);
1168             element->setAttribute(classAttr, AppleInterchangeNewline);            
1169         } else {
1170             if (useClonesOfEnclosingBlock)
1171                 element = block->cloneElementWithoutChildren();
1172             else
1173                 element = createDefaultParagraphElement(document);
1174             fillContainerFromString(element.get(), s);
1175         }
1176         fragment->appendChild(element.release(), ec);
1177         ASSERT(ec == 0);
1178     }
1179     return fragment.release();
1180 }
1181
1182 PassRefPtr<DocumentFragment> createFragmentFromNodes(Document *document, const Vector<Node*>& nodes)
1183 {
1184     if (!document)
1185         return 0;
1186
1187     // disable the delete button so it's elements are not serialized into the markup
1188     if (document->frame())
1189         document->frame()->editor()->deleteButtonController()->disable();
1190
1191     RefPtr<DocumentFragment> fragment = document->createDocumentFragment();
1192
1193     ExceptionCode ec = 0;
1194     size_t size = nodes.size();
1195     for (size_t i = 0; i < size; ++i) {
1196         RefPtr<Element> element = createDefaultParagraphElement(document);
1197         element->appendChild(nodes[i], ec);
1198         ASSERT(ec == 0);
1199         fragment->appendChild(element.release(), ec);
1200         ASSERT(ec == 0);
1201     }
1202
1203     if (document->frame())
1204         document->frame()->editor()->deleteButtonController()->enable();
1205
1206     return fragment.release();
1207 }
1208
1209 String createFullMarkup(const Node* node)
1210 {
1211     if (!node)
1212         return String();
1213         
1214     Document* document = node->document();
1215     if (!document)
1216         return String();
1217         
1218     Frame* frame = document->frame();
1219     if (!frame)
1220         return String();
1221
1222     // FIXME: This is never "for interchange". Is that right?    
1223     String markupString = createMarkup(node, IncludeNode, 0);
1224     Node::NodeType nodeType = node->nodeType();
1225     if (nodeType != Node::DOCUMENT_NODE && nodeType != Node::DOCUMENT_TYPE_NODE)
1226         markupString = frame->documentTypeString() + markupString;
1227
1228     return markupString;
1229 }
1230
1231 String createFullMarkup(const Range* range)
1232 {
1233     if (!range)
1234         return String();
1235
1236     Node* node = range->startContainer();
1237     if (!node)
1238         return String();
1239         
1240     Document* document = node->document();
1241     if (!document)
1242         return String();
1243         
1244     Frame* frame = document->frame();
1245     if (!frame)
1246         return String();
1247
1248     // FIXME: This is always "for interchange". Is that right? See the previous method.
1249     return frame->documentTypeString() + createMarkup(range, 0, AnnotateForInterchange);        
1250 }
1251
1252 }