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