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