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