1/* 2 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2013 Apple Inc. All rights reserved. 3 * Copyright (C) 2008, 2009, 2010, 2011 Google Inc. All rights reserved. 4 * Copyright (C) 2011 Igalia S.L. 5 * Copyright (C) 2011 Motorola Mobility. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY 17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR 20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 21 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 23 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 24 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include "config.h" 30#include "markup.h" 31 32#include "CDATASection.h" 33#include "CSSPrimitiveValue.h" 34#include "CSSPropertyNames.h" 35#include "CSSValue.h" 36#include "CSSValueKeywords.h" 37#include "ChildListMutationScope.h" 38#include "DocumentFragment.h" 39#include "DocumentType.h" 40#include "Editor.h" 41#include "ElementIterator.h" 42#include "ExceptionCode.h" 43#include "ExceptionCodePlaceholder.h" 44#include "Frame.h" 45#include "HTMLBodyElement.h" 46#include "HTMLElement.h" 47#include "HTMLNames.h" 48#include "HTMLTableElement.h" 49#include "HTMLTextAreaElement.h" 50#include "HTMLTextFormControlElement.h" 51#include "URL.h" 52#include "MarkupAccumulator.h" 53#include "Range.h" 54#include "RenderBlock.h" 55#include "Settings.h" 56#include "StyleProperties.h" 57#include "TextIterator.h" 58#include "VisibleSelection.h" 59#include "VisibleUnits.h" 60#include "htmlediting.h" 61#include <wtf/StdLibExtras.h> 62#include <wtf/text/StringBuilder.h> 63 64#if ENABLE(DELETION_UI) 65#include "DeleteButtonController.h" 66#endif 67 68namespace WebCore { 69 70using namespace HTMLNames; 71 72static bool propertyMissingOrEqualToNone(StyleProperties*, CSSPropertyID); 73 74class AttributeChange { 75public: 76 AttributeChange() 77 : m_name(nullAtom, nullAtom, nullAtom) 78 { 79 } 80 81 AttributeChange(PassRefPtr<Element> element, const QualifiedName& name, const String& value) 82 : m_element(element), m_name(name), m_value(value) 83 { 84 } 85 86 void apply() 87 { 88 m_element->setAttribute(m_name, m_value); 89 } 90 91private: 92 RefPtr<Element> m_element; 93 QualifiedName m_name; 94 String m_value; 95}; 96 97static void completeURLs(DocumentFragment* fragment, const String& baseURL) 98{ 99 Vector<AttributeChange> changes; 100 101 URL parsedBaseURL(ParsedURLString, baseURL); 102 103 for (auto& element : descendantsOfType<Element>(*fragment)) { 104 if (!element.hasAttributes()) 105 continue; 106 for (const Attribute& attribute : element.attributesIterator()) { 107 if (element.isURLAttribute(attribute) && !attribute.value().isEmpty()) 108 changes.append(AttributeChange(&element, attribute.name(), URL(parsedBaseURL, attribute.value()).string())); 109 } 110 } 111 112 size_t numChanges = changes.size(); 113 for (size_t i = 0; i < numChanges; ++i) 114 changes[i].apply(); 115} 116 117class StyledMarkupAccumulator final : public MarkupAccumulator { 118public: 119 enum RangeFullySelectsNode { DoesFullySelectNode, DoesNotFullySelectNode }; 120 121 StyledMarkupAccumulator(Vector<Node*>* nodes, EAbsoluteURLs, EAnnotateForInterchange, const Range*, bool needsPositionStyleConversion, Node* highestNodeToBeSerialized = 0); 122 123 Node* serializeNodes(Node* startNode, Node* pastEnd); 124 void wrapWithNode(Node&, bool convertBlocksToInlines = false, RangeFullySelectsNode = DoesFullySelectNode); 125 void wrapWithStyleNode(StyleProperties*, Document&, bool isBlock = false); 126 String takeResults(); 127 128 bool needRelativeStyleWrapper() const { return m_needRelativeStyleWrapper; } 129 bool needClearingDiv() const { return m_needClearingDiv; } 130 131 using MarkupAccumulator::appendString; 132 133private: 134 void appendStyleNodeOpenTag(StringBuilder&, StyleProperties*, Document&, bool isBlock = false); 135 const String& styleNodeCloseTag(bool isBlock = false); 136 137 String renderedText(const Node&, const Range*); 138 String stringValueForRange(const Node&, const Range*); 139 140 void appendElement(StringBuilder& out, const Element&, bool addDisplayInline, RangeFullySelectsNode); 141 142 virtual void appendText(StringBuilder& out, const Text&) override; 143 virtual void appendElement(StringBuilder& out, const Element& element, Namespaces*) override 144 { 145 appendElement(out, element, false, DoesFullySelectNode); 146 } 147 148 enum NodeTraversalMode { EmitString, DoNotEmitString }; 149 Node* traverseNodesForSerialization(Node* startNode, Node* pastEnd, NodeTraversalMode); 150 151 bool shouldAnnotate() 152 { 153 return m_shouldAnnotate == AnnotateForInterchange; 154 } 155 156 bool shouldApplyWrappingStyle(const Node& node) const 157 { 158 return m_highestNodeToBeSerialized && m_highestNodeToBeSerialized->parentNode() == node.parentNode() && m_wrappingStyle && m_wrappingStyle->style(); 159 } 160 161 Vector<String> m_reversedPrecedingMarkup; 162 const EAnnotateForInterchange m_shouldAnnotate; 163 Node* m_highestNodeToBeSerialized; 164 RefPtr<EditingStyle> m_wrappingStyle; 165 bool m_needRelativeStyleWrapper; 166 bool m_needsPositionStyleConversion; 167 bool m_needClearingDiv; 168}; 169 170inline StyledMarkupAccumulator::StyledMarkupAccumulator(Vector<Node*>* nodes, EAbsoluteURLs shouldResolveURLs, EAnnotateForInterchange shouldAnnotate, const Range* range, bool needsPositionStyleConversion, Node* highestNodeToBeSerialized) 171 : MarkupAccumulator(nodes, shouldResolveURLs, range) 172 , m_shouldAnnotate(shouldAnnotate) 173 , m_highestNodeToBeSerialized(highestNodeToBeSerialized) 174 , m_needRelativeStyleWrapper(false) 175 , m_needsPositionStyleConversion(needsPositionStyleConversion) 176 , m_needClearingDiv(false) 177{ 178} 179 180void StyledMarkupAccumulator::wrapWithNode(Node& node, bool convertBlocksToInlines, RangeFullySelectsNode rangeFullySelectsNode) 181{ 182 StringBuilder markup; 183 if (node.isElementNode()) 184 appendElement(markup, toElement(node), convertBlocksToInlines && isBlock(&node), rangeFullySelectsNode); 185 else 186 appendStartMarkup(markup, node, 0); 187 m_reversedPrecedingMarkup.append(markup.toString()); 188 appendEndTag(node); 189 if (m_nodes) 190 m_nodes->append(&node); 191} 192 193void StyledMarkupAccumulator::wrapWithStyleNode(StyleProperties* style, Document& document, bool isBlock) 194{ 195 StringBuilder openTag; 196 appendStyleNodeOpenTag(openTag, style, document, isBlock); 197 m_reversedPrecedingMarkup.append(openTag.toString()); 198 appendString(styleNodeCloseTag(isBlock)); 199} 200 201void StyledMarkupAccumulator::appendStyleNodeOpenTag(StringBuilder& out, StyleProperties* style, Document& document, bool isBlock) 202{ 203 // wrappingStyleForSerialization should have removed -webkit-text-decorations-in-effect 204 ASSERT(propertyMissingOrEqualToNone(style, CSSPropertyWebkitTextDecorationsInEffect)); 205 if (isBlock) 206 out.appendLiteral("<div style=\""); 207 else 208 out.appendLiteral("<span style=\""); 209 appendAttributeValue(out, style->asText(), document.isHTMLDocument()); 210 out.appendLiteral("\">"); 211} 212 213const String& StyledMarkupAccumulator::styleNodeCloseTag(bool isBlock) 214{ 215 DEPRECATED_DEFINE_STATIC_LOCAL(const String, divClose, (ASCIILiteral("</div>"))); 216 DEPRECATED_DEFINE_STATIC_LOCAL(const String, styleSpanClose, (ASCIILiteral("</span>"))); 217 return isBlock ? divClose : styleSpanClose; 218} 219 220String StyledMarkupAccumulator::takeResults() 221{ 222 StringBuilder result; 223 result.reserveCapacity(totalLength(m_reversedPrecedingMarkup) + length()); 224 225 for (size_t i = m_reversedPrecedingMarkup.size(); i > 0; --i) 226 result.append(m_reversedPrecedingMarkup[i - 1]); 227 228 concatenateMarkup(result); 229 230 // We remove '\0' characters because they are not visibly rendered to the user. 231 return result.toString().replaceWithLiteral('\0', ""); 232} 233 234void StyledMarkupAccumulator::appendText(StringBuilder& out, const Text& text) 235{ 236 const bool parentIsTextarea = text.parentElement() && isHTMLTextAreaElement(text.parentElement()); 237 const bool wrappingSpan = shouldApplyWrappingStyle(text) && !parentIsTextarea; 238 if (wrappingSpan) { 239 RefPtr<EditingStyle> wrappingStyle = m_wrappingStyle->copy(); 240 // FIXME: <rdar://problem/5371536> Style rules that match pasted content can change it's appearance 241 // Make sure spans are inline style in paste side e.g. span { display: block }. 242 wrappingStyle->forceInline(); 243 // FIXME: Should this be included in forceInline? 244 wrappingStyle->style()->setProperty(CSSPropertyFloat, CSSValueNone); 245 246 appendStyleNodeOpenTag(out, wrappingStyle->style(), text.document()); 247 } 248 249 if (!shouldAnnotate() || parentIsTextarea) 250 MarkupAccumulator::appendText(out, text); 251 else { 252 const bool useRenderedText = !enclosingElementWithTag(firstPositionInNode(const_cast<Text*>(&text)), selectTag); 253 String content = useRenderedText ? renderedText(text, m_range) : stringValueForRange(text, m_range); 254 StringBuilder buffer; 255 appendCharactersReplacingEntities(buffer, content, 0, content.length(), EntityMaskInPCDATA); 256 out.append(convertHTMLTextToInterchangeFormat(buffer.toString(), &text)); 257 } 258 259 if (wrappingSpan) 260 out.append(styleNodeCloseTag()); 261} 262 263String StyledMarkupAccumulator::renderedText(const Node& node, const Range* range) 264{ 265 if (!node.isTextNode()) 266 return String(); 267 268 const Text& textNode = toText(node); 269 unsigned startOffset = 0; 270 unsigned endOffset = textNode.length(); 271 272 TextIteratorBehavior behavior = TextIteratorDefaultBehavior; 273 if (range && &node == range->startContainer()) 274 startOffset = range->startOffset(); 275 if (range && &node == range->endContainer()) 276 endOffset = range->endOffset(); 277 else if (range) 278 behavior = TextIteratorBehavesAsIfNodesFollowing; 279 280 Position start = createLegacyEditingPosition(const_cast<Node*>(&node), startOffset); 281 Position end = createLegacyEditingPosition(const_cast<Node*>(&node), endOffset); 282 return plainText(Range::create(node.document(), start, end).get(), behavior); 283} 284 285String StyledMarkupAccumulator::stringValueForRange(const Node& node, const Range* range) 286{ 287 if (!range) 288 return node.nodeValue(); 289 290 String nodeValue = node.nodeValue(); 291 if (&node == range->endContainer()) 292 nodeValue.truncate(range->endOffset()); 293 if (&node == range->startContainer()) 294 nodeValue.remove(0, range->startOffset()); 295 return nodeValue; 296} 297 298void StyledMarkupAccumulator::appendElement(StringBuilder& out, const Element& element, bool addDisplayInline, RangeFullySelectsNode rangeFullySelectsNode) 299{ 300 const bool documentIsHTML = element.document().isHTMLDocument(); 301 appendOpenTag(out, element, 0); 302 303 const bool shouldAnnotateOrForceInline = element.isHTMLElement() && (shouldAnnotate() || addDisplayInline); 304 const bool shouldOverrideStyleAttr = shouldAnnotateOrForceInline || shouldApplyWrappingStyle(element); 305 if (element.hasAttributes()) { 306 for (const Attribute& attribute : element.attributesIterator()) { 307 // We'll handle the style attribute separately, below. 308 if (attribute.name() == styleAttr && shouldOverrideStyleAttr) 309 continue; 310 appendAttribute(out, element, attribute, 0); 311 } 312 } 313 314 if (shouldOverrideStyleAttr) { 315 RefPtr<EditingStyle> newInlineStyle; 316 317 if (shouldApplyWrappingStyle(element)) { 318 newInlineStyle = m_wrappingStyle->copy(); 319 newInlineStyle->removePropertiesInElementDefaultStyle(const_cast<Element*>(&element)); 320 newInlineStyle->removeStyleConflictingWithStyleOfNode(const_cast<Element*>(&element)); 321 } else 322 newInlineStyle = EditingStyle::create(); 323 324 if (element.isStyledElement() && toStyledElement(element).inlineStyle()) 325 newInlineStyle->overrideWithStyle(toStyledElement(element).inlineStyle()); 326 327 if (shouldAnnotateOrForceInline) { 328 if (shouldAnnotate()) 329 newInlineStyle->mergeStyleFromRulesForSerialization(toHTMLElement(const_cast<Element*>(&element))); 330 331 if (addDisplayInline) 332 newInlineStyle->forceInline(); 333 334 if (m_needsPositionStyleConversion) { 335 m_needRelativeStyleWrapper |= newInlineStyle->convertPositionStyle(); 336 m_needClearingDiv |= newInlineStyle->isFloating(); 337 } 338 339 // 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 340 // only the ones that affect it and the nodes within it. 341 if (rangeFullySelectsNode == DoesNotFullySelectNode && newInlineStyle->style()) 342 newInlineStyle->style()->removeProperty(CSSPropertyFloat); 343 } 344 345 if (!newInlineStyle->isEmpty()) { 346 out.appendLiteral(" style=\""); 347 appendAttributeValue(out, newInlineStyle->style()->asText(), documentIsHTML); 348 out.append('\"'); 349 } 350 } 351 352 appendCloseTag(out, element); 353} 354 355Node* StyledMarkupAccumulator::serializeNodes(Node* startNode, Node* pastEnd) 356{ 357 if (!m_highestNodeToBeSerialized) { 358 Node* lastClosed = traverseNodesForSerialization(startNode, pastEnd, DoNotEmitString); 359 m_highestNodeToBeSerialized = lastClosed; 360 } 361 362 if (m_highestNodeToBeSerialized && m_highestNodeToBeSerialized->parentNode()) 363 m_wrappingStyle = EditingStyle::wrappingStyleForSerialization(m_highestNodeToBeSerialized->parentNode(), shouldAnnotate()); 364 365 return traverseNodesForSerialization(startNode, pastEnd, EmitString); 366} 367 368Node* StyledMarkupAccumulator::traverseNodesForSerialization(Node* startNode, Node* pastEnd, NodeTraversalMode traversalMode) 369{ 370 const bool shouldEmit = traversalMode == EmitString; 371 Vector<Node*> ancestorsToClose; 372 Node* next; 373 Node* lastClosed = 0; 374 for (Node* n = startNode; n != pastEnd; n = next) { 375 // According to <rdar://problem/5730668>, it is possible for n to blow 376 // past pastEnd and become null here. This shouldn't be possible. 377 // This null check will prevent crashes (but create too much markup) 378 // and the ASSERT will hopefully lead us to understanding the problem. 379 ASSERT(n); 380 if (!n) 381 break; 382 383 next = NodeTraversal::next(n); 384 bool openedTag = false; 385 386 if (isBlock(n) && canHaveChildrenForEditing(n) && next == pastEnd) 387 // Don't write out empty block containers that aren't fully selected. 388 continue; 389 390 if (!n->renderer() && !enclosingElementWithTag(firstPositionInOrBeforeNode(n), selectTag)) { 391 next = NodeTraversal::nextSkippingChildren(n); 392 // Don't skip over pastEnd. 393 if (pastEnd && pastEnd->isDescendantOf(n)) 394 next = pastEnd; 395 } else { 396 // Add the node to the markup if we're not skipping the descendants 397 if (shouldEmit) 398 appendStartTag(*n); 399 400 // If node has no children, close the tag now. 401 if (!n->childNodeCount()) { 402 if (shouldEmit) 403 appendEndTag(*n); 404 lastClosed = n; 405 } else { 406 openedTag = true; 407 ancestorsToClose.append(n); 408 } 409 } 410 411 // If we didn't insert open tag and there's no more siblings or we're at the end of the traversal, take care of ancestors. 412 // FIXME: What happens if we just inserted open tag and reached the end? 413 if (!openedTag && (!n->nextSibling() || next == pastEnd)) { 414 // Close up the ancestors. 415 while (!ancestorsToClose.isEmpty()) { 416 Node* ancestor = ancestorsToClose.last(); 417 if (next != pastEnd && next->isDescendantOf(ancestor)) 418 break; 419 // Not at the end of the range, close ancestors up to sibling of next node. 420 if (shouldEmit) 421 appendEndTag(*ancestor); 422 lastClosed = ancestor; 423 ancestorsToClose.removeLast(); 424 } 425 426 // Surround the currently accumulated markup with markup for ancestors we never opened as we leave the subtree(s) rooted at those ancestors. 427 ContainerNode* nextParent = next ? next->parentNode() : 0; 428 if (next != pastEnd && n != nextParent) { 429 Node* lastAncestorClosedOrSelf = n->isDescendantOf(lastClosed) ? lastClosed : n; 430 for (ContainerNode* parent = lastAncestorClosedOrSelf->parentNode(); parent && parent != nextParent; parent = parent->parentNode()) { 431 // All ancestors that aren't in the ancestorsToClose list should either be a) unrendered: 432 if (!parent->renderer()) 433 continue; 434 // or b) ancestors that we never encountered during a pre-order traversal starting at startNode: 435 ASSERT(startNode->isDescendantOf(parent)); 436 if (shouldEmit) 437 wrapWithNode(*parent); 438 lastClosed = parent; 439 } 440 } 441 } 442 } 443 444 return lastClosed; 445} 446 447static Node* ancestorToRetainStructureAndAppearanceForBlock(Node* commonAncestorBlock) 448{ 449 if (!commonAncestorBlock) 450 return 0; 451 452 if (commonAncestorBlock->hasTagName(tbodyTag) || commonAncestorBlock->hasTagName(trTag)) { 453 ContainerNode* table = commonAncestorBlock->parentNode(); 454 while (table && !isHTMLTableElement(table)) 455 table = table->parentNode(); 456 457 return table; 458 } 459 460 if (isNonTableCellHTMLBlockElement(commonAncestorBlock)) 461 return commonAncestorBlock; 462 463 return 0; 464} 465 466static inline Node* ancestorToRetainStructureAndAppearance(Node* commonAncestor) 467{ 468 return ancestorToRetainStructureAndAppearanceForBlock(enclosingBlock(commonAncestor)); 469} 470 471static bool propertyMissingOrEqualToNone(StyleProperties* style, CSSPropertyID propertyID) 472{ 473 if (!style) 474 return false; 475 RefPtr<CSSValue> value = style->getPropertyCSSValue(propertyID); 476 if (!value) 477 return true; 478 if (!value->isPrimitiveValue()) 479 return false; 480 return toCSSPrimitiveValue(value.get())->getValueID() == CSSValueNone; 481} 482 483static bool needInterchangeNewlineAfter(const VisiblePosition& v) 484{ 485 VisiblePosition next = v.next(); 486 Node* upstreamNode = next.deepEquivalent().upstream().deprecatedNode(); 487 Node* downstreamNode = v.deepEquivalent().downstream().deprecatedNode(); 488 // Add an interchange newline if a paragraph break is selected and a br won't already be added to the markup to represent it. 489 return isEndOfParagraph(v) && isStartOfParagraph(next) && !(upstreamNode->hasTagName(brTag) && upstreamNode == downstreamNode); 490} 491 492static PassRefPtr<EditingStyle> styleFromMatchedRulesAndInlineDecl(const Node* node) 493{ 494 if (!node->isHTMLElement()) 495 return 0; 496 497 // FIXME: Having to const_cast here is ugly, but it is quite a bit of work to untangle 498 // the non-const-ness of styleFromMatchedRulesForElement. 499 HTMLElement* element = const_cast<HTMLElement*>(static_cast<const HTMLElement*>(node)); 500 RefPtr<EditingStyle> style = EditingStyle::create(element->inlineStyle()); 501 style->mergeStyleFromRules(element); 502 return style.release(); 503} 504 505static bool isElementPresentational(const Node* node) 506{ 507 return node->hasTagName(uTag) || node->hasTagName(sTag) || node->hasTagName(strikeTag) 508 || node->hasTagName(iTag) || node->hasTagName(emTag) || node->hasTagName(bTag) || node->hasTagName(strongTag); 509} 510 511static Node* highestAncestorToWrapMarkup(const Range* range, EAnnotateForInterchange shouldAnnotate) 512{ 513 Node* commonAncestor = range->commonAncestorContainer(IGNORE_EXCEPTION); 514 ASSERT(commonAncestor); 515 Node* specialCommonAncestor = 0; 516 if (shouldAnnotate == AnnotateForInterchange) { 517 // Include ancestors that aren't completely inside the range but are required to retain 518 // the structure and appearance of the copied markup. 519 specialCommonAncestor = ancestorToRetainStructureAndAppearance(commonAncestor); 520 521 if (Node* parentListNode = enclosingNodeOfType(firstPositionInOrBeforeNode(range->firstNode()), isListItem)) { 522 if (WebCore::areRangesEqual(VisibleSelection::selectionFromContentsOfNode(parentListNode).toNormalizedRange().get(), range)) { 523 specialCommonAncestor = parentListNode->parentNode(); 524 while (specialCommonAncestor && !isListElement(specialCommonAncestor)) 525 specialCommonAncestor = specialCommonAncestor->parentNode(); 526 } 527 } 528 529 // Retain the Mail quote level by including all ancestor mail block quotes. 530 if (Node* highestMailBlockquote = highestEnclosingNodeOfType(firstPositionInOrBeforeNode(range->firstNode()), isMailBlockquote, CanCrossEditingBoundary)) 531 specialCommonAncestor = highestMailBlockquote; 532 } 533 534 Node* checkAncestor = specialCommonAncestor ? specialCommonAncestor : commonAncestor; 535 if (checkAncestor->renderer() && checkAncestor->renderer()->containingBlock()) { 536 Node* newSpecialCommonAncestor = highestEnclosingNodeOfType(firstPositionInNode(checkAncestor), &isElementPresentational, CanCrossEditingBoundary, checkAncestor->renderer()->containingBlock()->element()); 537 if (newSpecialCommonAncestor) 538 specialCommonAncestor = newSpecialCommonAncestor; 539 } 540 541 // If a single tab is selected, commonAncestor will be a text node inside a tab span. 542 // If two or more tabs are selected, commonAncestor will be the tab span. 543 // In either case, if there is a specialCommonAncestor already, it will necessarily be above 544 // any tab span that needs to be included. 545 if (!specialCommonAncestor && isTabSpanTextNode(commonAncestor)) 546 specialCommonAncestor = commonAncestor->parentNode(); 547 if (!specialCommonAncestor && isTabSpanNode(commonAncestor)) 548 specialCommonAncestor = commonAncestor; 549 550 if (auto* enclosingAnchor = enclosingElementWithTag(firstPositionInNode(specialCommonAncestor ? specialCommonAncestor : commonAncestor), aTag)) 551 specialCommonAncestor = enclosingAnchor; 552 553 return specialCommonAncestor; 554} 555 556// FIXME: Shouldn't we omit style info when annotate == DoNotAnnotateForInterchange? 557// FIXME: At least, annotation and style info should probably not be included in range.markupString() 558static String createMarkupInternal(Document& document, const Range& range, const Range& updatedRange, Vector<Node*>* nodes, 559 EAnnotateForInterchange shouldAnnotate, bool convertBlocksToInlines, EAbsoluteURLs shouldResolveURLs) 560{ 561 DEPRECATED_DEFINE_STATIC_LOCAL(const String, interchangeNewlineString, (ASCIILiteral("<br class=\"" AppleInterchangeNewline "\">"))); 562 563 bool collapsed = updatedRange.collapsed(ASSERT_NO_EXCEPTION); 564 if (collapsed) 565 return emptyString(); 566 Node* commonAncestor = updatedRange.commonAncestorContainer(ASSERT_NO_EXCEPTION); 567 if (!commonAncestor) 568 return emptyString(); 569 570 document.updateLayoutIgnorePendingStylesheets(); 571 572 auto* body = enclosingElementWithTag(firstPositionInNode(commonAncestor), bodyTag); 573 Node* fullySelectedRoot = 0; 574 // FIXME: Do this for all fully selected blocks, not just the body. 575 if (body && VisiblePosition(firstPositionInNode(body)) == VisiblePosition(range.startPosition()) 576 && VisiblePosition(lastPositionInNode(body)) == VisiblePosition(range.endPosition())) 577 fullySelectedRoot = body; 578 Node* specialCommonAncestor = highestAncestorToWrapMarkup(&updatedRange, shouldAnnotate); 579 580 bool needsPositionStyleConversion = body && fullySelectedRoot == body 581 && document.settings() && document.settings()->shouldConvertPositionStyleOnCopy(); 582 StyledMarkupAccumulator accumulator(nodes, shouldResolveURLs, shouldAnnotate, &updatedRange, needsPositionStyleConversion, specialCommonAncestor); 583 Node* pastEnd = updatedRange.pastLastNode(); 584 585 Node* startNode = updatedRange.firstNode(); 586 VisiblePosition visibleStart(updatedRange.startPosition(), VP_DEFAULT_AFFINITY); 587 VisiblePosition visibleEnd(updatedRange.endPosition(), VP_DEFAULT_AFFINITY); 588 if (shouldAnnotate == AnnotateForInterchange && needInterchangeNewlineAfter(visibleStart)) { 589 if (visibleStart == visibleEnd.previous()) 590 return interchangeNewlineString; 591 592 accumulator.appendString(interchangeNewlineString); 593 startNode = visibleStart.next().deepEquivalent().deprecatedNode(); 594 595 if (pastEnd && Range::compareBoundaryPoints(startNode, 0, pastEnd, 0, ASSERT_NO_EXCEPTION) >= 0) 596 return interchangeNewlineString; 597 } 598 599 Node* lastClosed = accumulator.serializeNodes(startNode, pastEnd); 600 601 if (specialCommonAncestor && lastClosed) { 602 // Also include all of the ancestors of lastClosed up to this special ancestor. 603 for (ContainerNode* ancestor = lastClosed->parentNode(); ancestor; ancestor = ancestor->parentNode()) { 604 if (ancestor == fullySelectedRoot && !convertBlocksToInlines) { 605 RefPtr<EditingStyle> fullySelectedRootStyle = styleFromMatchedRulesAndInlineDecl(fullySelectedRoot); 606 607 // Bring the background attribute over, but not as an attribute because a background attribute on a div 608 // appears to have no effect. 609 if ((!fullySelectedRootStyle || !fullySelectedRootStyle->style() || !fullySelectedRootStyle->style()->getPropertyCSSValue(CSSPropertyBackgroundImage)) 610 && toElement(fullySelectedRoot)->hasAttribute(backgroundAttr)) 611 fullySelectedRootStyle->style()->setProperty(CSSPropertyBackgroundImage, "url('" + toElement(fullySelectedRoot)->getAttribute(backgroundAttr) + "')"); 612 613 if (fullySelectedRootStyle->style()) { 614 // Reset the CSS properties to avoid an assertion error in addStyleMarkup(). 615 // This assertion is caused at least when we select all text of a <body> element whose 616 // 'text-decoration' property is "inherit", and copy it. 617 if (!propertyMissingOrEqualToNone(fullySelectedRootStyle->style(), CSSPropertyTextDecoration)) 618 fullySelectedRootStyle->style()->setProperty(CSSPropertyTextDecoration, CSSValueNone); 619 if (!propertyMissingOrEqualToNone(fullySelectedRootStyle->style(), CSSPropertyWebkitTextDecorationsInEffect)) 620 fullySelectedRootStyle->style()->setProperty(CSSPropertyWebkitTextDecorationsInEffect, CSSValueNone); 621 accumulator.wrapWithStyleNode(fullySelectedRootStyle->style(), document, true); 622 } 623 } else { 624 // Since this node and all the other ancestors are not in the selection we want to set RangeFullySelectsNode to DoesNotFullySelectNode 625 // so that styles that affect the exterior of the node are not included. 626 accumulator.wrapWithNode(*ancestor, convertBlocksToInlines, StyledMarkupAccumulator::DoesNotFullySelectNode); 627 } 628 if (nodes) 629 nodes->append(ancestor); 630 631 if (ancestor == specialCommonAncestor) 632 break; 633 } 634 } 635 636 if (accumulator.needRelativeStyleWrapper() && needsPositionStyleConversion) { 637 if (accumulator.needClearingDiv()) 638 accumulator.appendString("<div style=\"clear: both;\"></div>"); 639 RefPtr<EditingStyle> positionRelativeStyle = styleFromMatchedRulesAndInlineDecl(body); 640 positionRelativeStyle->style()->setProperty(CSSPropertyPosition, CSSValueRelative); 641 accumulator.wrapWithStyleNode(positionRelativeStyle->style(), document, true); 642 } 643 644 // FIXME: The interchange newline should be placed in the block that it's in, not after all of the content, unconditionally. 645 if (shouldAnnotate == AnnotateForInterchange && needInterchangeNewlineAfter(visibleEnd.previous())) 646 accumulator.appendString(interchangeNewlineString); 647 648 return accumulator.takeResults(); 649} 650 651String createMarkup(const Range& range, Vector<Node*>* nodes, EAnnotateForInterchange shouldAnnotate, bool convertBlocksToInlines, EAbsoluteURLs shouldResolveURLs) 652{ 653 Document& document = range.ownerDocument(); 654 const Range* updatedRange = ⦥ 655 656#if ENABLE(DELETION_UI) 657 // Disable the delete button so it's elements are not serialized into the markup, 658 // but make sure neither endpoint is inside the delete user interface. 659 Frame* frame = document.frame(); 660 DeleteButtonControllerDisableScope deleteButtonControllerDisableScope(frame); 661 662 RefPtr<Range> updatedRangeRef; 663 if (frame) { 664 updatedRangeRef = frame->editor().avoidIntersectionWithDeleteButtonController(&range); 665 updatedRange = updatedRangeRef.get(); 666 if (!updatedRange) 667 return emptyString(); 668 } 669#endif 670 671 return createMarkupInternal(document, range, *updatedRange, nodes, shouldAnnotate, convertBlocksToInlines, shouldResolveURLs); 672} 673 674PassRefPtr<DocumentFragment> createFragmentFromMarkup(Document& document, const String& markup, const String& baseURL, ParserContentPolicy parserContentPolicy) 675{ 676 // We use a fake body element here to trick the HTML parser to using the InBody insertion mode. 677 RefPtr<HTMLBodyElement> fakeBody = HTMLBodyElement::create(document); 678 RefPtr<DocumentFragment> fragment = DocumentFragment::create(document); 679 680 fragment->parseHTML(markup, fakeBody.get(), parserContentPolicy); 681 682 if (!baseURL.isEmpty() && baseURL != blankURL() && baseURL != document.baseURL()) 683 completeURLs(fragment.get(), baseURL); 684 685 return fragment.release(); 686} 687 688String createMarkup(const Node& node, EChildrenOnly childrenOnly, Vector<Node*>* nodes, EAbsoluteURLs shouldResolveURLs, Vector<QualifiedName>* tagNamesToSkip, EFragmentSerialization fragmentSerialization) 689{ 690 HTMLElement* deleteButtonContainerElement = 0; 691#if ENABLE(DELETION_UI) 692 if (Frame* frame = node.document().frame()) { 693 deleteButtonContainerElement = frame->editor().deleteButtonController().containerElement(); 694 if (node.isDescendantOf(deleteButtonContainerElement)) 695 return emptyString(); 696 } 697#endif 698 699 MarkupAccumulator accumulator(nodes, shouldResolveURLs, 0, fragmentSerialization); 700 return accumulator.serializeNodes(const_cast<Node&>(node), deleteButtonContainerElement, childrenOnly, tagNamesToSkip); 701} 702 703static void fillContainerFromString(ContainerNode* paragraph, const String& string) 704{ 705 Document& document = paragraph->document(); 706 707 if (string.isEmpty()) { 708 paragraph->appendChild(createBlockPlaceholderElement(document), ASSERT_NO_EXCEPTION); 709 return; 710 } 711 712 ASSERT(string.find('\n') == notFound); 713 714 Vector<String> tabList; 715 string.split('\t', true, tabList); 716 String tabText = emptyString(); 717 bool first = true; 718 size_t numEntries = tabList.size(); 719 for (size_t i = 0; i < numEntries; ++i) { 720 const String& s = tabList[i]; 721 722 // append the non-tab textual part 723 if (!s.isEmpty()) { 724 if (!tabText.isEmpty()) { 725 paragraph->appendChild(createTabSpanElement(document, tabText), ASSERT_NO_EXCEPTION); 726 tabText = emptyString(); 727 } 728 RefPtr<Node> textNode = document.createTextNode(stringWithRebalancedWhitespace(s, first, i + 1 == numEntries)); 729 paragraph->appendChild(textNode.release(), ASSERT_NO_EXCEPTION); 730 } 731 732 // there is a tab after every entry, except the last entry 733 // (if the last character is a tab, the list gets an extra empty entry) 734 if (i + 1 != numEntries) 735 tabText.append('\t'); 736 else if (!tabText.isEmpty()) 737 paragraph->appendChild(createTabSpanElement(document, tabText), ASSERT_NO_EXCEPTION); 738 739 first = false; 740 } 741} 742 743bool isPlainTextMarkup(Node *node) 744{ 745 if (!node->isElementNode() || !node->hasTagName(divTag) || toElement(node)->hasAttributes()) 746 return false; 747 748 if (node->childNodeCount() == 1 && (node->firstChild()->isTextNode() || (node->firstChild()->firstChild()))) 749 return true; 750 751 return (node->childNodeCount() == 2 && isTabSpanTextNode(node->firstChild()->firstChild()) && node->firstChild()->nextSibling()->isTextNode()); 752} 753 754static bool contextPreservesNewline(const Range& context) 755{ 756 VisiblePosition position(context.startPosition()); 757 Node* container = position.deepEquivalent().containerNode(); 758 if (!container || !container->renderer()) 759 return false; 760 761 return container->renderer()->style().preserveNewline(); 762} 763 764PassRefPtr<DocumentFragment> createFragmentFromText(Range& context, const String& text) 765{ 766 Document& document = context.ownerDocument(); 767 RefPtr<DocumentFragment> fragment = document.createDocumentFragment(); 768 769 if (text.isEmpty()) 770 return fragment.release(); 771 772 String string = text; 773 string.replace("\r\n", "\n"); 774 string.replace('\r', '\n'); 775 776 if (contextPreservesNewline(context)) { 777 fragment->appendChild(document.createTextNode(string), ASSERT_NO_EXCEPTION); 778 if (string.endsWith('\n')) { 779 RefPtr<Element> element = createBreakElement(document); 780 element->setAttribute(classAttr, AppleInterchangeNewline); 781 fragment->appendChild(element.release(), ASSERT_NO_EXCEPTION); 782 } 783 return fragment.release(); 784 } 785 786 // A string with no newlines gets added inline, rather than being put into a paragraph. 787 if (string.find('\n') == notFound) { 788 fillContainerFromString(fragment.get(), string); 789 return fragment.release(); 790 } 791 792 // Break string into paragraphs. Extra line breaks turn into empty paragraphs. 793 Node* blockNode = enclosingBlock(context.firstNode()); 794 Element* block = toElement(blockNode); 795 bool useClonesOfEnclosingBlock = blockNode 796 && blockNode->isElementNode() 797 && !block->hasTagName(bodyTag) 798 && !block->hasTagName(htmlTag) 799 && block != editableRootForPosition(context.startPosition()); 800 bool useLineBreak = enclosingTextFormControl(context.startPosition()); 801 802 Vector<String> list; 803 string.split('\n', true, list); // true gets us empty strings in the list 804 size_t numLines = list.size(); 805 for (size_t i = 0; i < numLines; ++i) { 806 const String& s = list[i]; 807 808 RefPtr<Element> element; 809 if (s.isEmpty() && i + 1 == numLines) { 810 // For last line, use the "magic BR" rather than a P. 811 element = createBreakElement(document); 812 element->setAttribute(classAttr, AppleInterchangeNewline); 813 } else if (useLineBreak) { 814 element = createBreakElement(document); 815 fillContainerFromString(fragment.get(), s); 816 } else { 817 if (useClonesOfEnclosingBlock) 818 element = block->cloneElementWithoutChildren(); 819 else 820 element = createDefaultParagraphElement(document); 821 fillContainerFromString(element.get(), s); 822 } 823 fragment->appendChild(element.release(), ASSERT_NO_EXCEPTION); 824 } 825 return fragment.release(); 826} 827 828String documentTypeString(const Document& document) 829{ 830 DocumentType* documentType = document.doctype(); 831 if (!documentType) 832 return emptyString(); 833 return createMarkup(*documentType); 834} 835 836String createFullMarkup(const Node& node) 837{ 838 // FIXME: This is never "for interchange". Is that right? 839 String markupString = createMarkup(node, IncludeNode, 0); 840 841 Node::NodeType nodeType = node.nodeType(); 842 if (nodeType != Node::DOCUMENT_NODE && nodeType != Node::DOCUMENT_TYPE_NODE) 843 markupString = documentTypeString(node.document()) + markupString; 844 845 return markupString; 846} 847 848String createFullMarkup(const Range& range) 849{ 850 Node* node = range.startContainer(); 851 if (!node) 852 return String(); 853 854 // FIXME: This is always "for interchange". Is that right? 855 return documentTypeString(node->document()) + createMarkup(range, 0, AnnotateForInterchange); 856} 857 858String urlToMarkup(const URL& url, const String& title) 859{ 860 StringBuilder markup; 861 markup.append("<a href=\""); 862 markup.append(url.string()); 863 markup.append("\">"); 864 MarkupAccumulator::appendCharactersReplacingEntities(markup, title, 0, title.length(), EntityMaskInPCDATA); 865 markup.append("</a>"); 866 return markup.toString(); 867} 868 869PassRefPtr<DocumentFragment> createFragmentForInnerOuterHTML(const String& markup, Element* contextElement, ParserContentPolicy parserContentPolicy, ExceptionCode& ec) 870{ 871 Document* document = &contextElement->document(); 872#if ENABLE(TEMPLATE_ELEMENT) 873 if (contextElement->hasTagName(templateTag)) 874 document = document->ensureTemplateDocument(); 875#endif 876 RefPtr<DocumentFragment> fragment = DocumentFragment::create(*document); 877 878 if (document->isHTMLDocument()) { 879 fragment->parseHTML(markup, contextElement, parserContentPolicy); 880 return fragment; 881 } 882 883 bool wasValid = fragment->parseXML(markup, contextElement, parserContentPolicy); 884 if (!wasValid) { 885 ec = SYNTAX_ERR; 886 return 0; 887 } 888 return fragment.release(); 889} 890 891PassRefPtr<DocumentFragment> createFragmentForTransformToFragment(const String& sourceString, const String& sourceMIMEType, Document* outputDoc) 892{ 893 RefPtr<DocumentFragment> fragment = outputDoc->createDocumentFragment(); 894 895 if (sourceMIMEType == "text/html") { 896 // As far as I can tell, there isn't a spec for how transformToFragment is supposed to work. 897 // Based on the documentation I can find, it looks like we want to start parsing the fragment in the InBody insertion mode. 898 // Unfortunately, that's an implementation detail of the parser. 899 // We achieve that effect here by passing in a fake body element as context for the fragment. 900 RefPtr<HTMLBodyElement> fakeBody = HTMLBodyElement::create(*outputDoc); 901 fragment->parseHTML(sourceString, fakeBody.get()); 902 } else if (sourceMIMEType == "text/plain") 903 fragment->parserAppendChild(Text::create(*outputDoc, sourceString)); 904 else { 905 bool successfulParse = fragment->parseXML(sourceString, 0); 906 if (!successfulParse) 907 return 0; 908 } 909 910 // FIXME: Do we need to mess with URLs here? 911 912 return fragment.release(); 913} 914 915static Vector<Ref<HTMLElement>> collectElementsToRemoveFromFragment(ContainerNode& container) 916{ 917 Vector<Ref<HTMLElement>> toRemove; 918 for (auto& element : childrenOfType<HTMLElement>(container)) { 919 if (isHTMLHtmlElement(element)) { 920 toRemove.append(element); 921 collectElementsToRemoveFromFragment(element); 922 continue; 923 } 924 if (isHTMLHeadElement(element) || isHTMLBodyElement(element)) 925 toRemove.append(element); 926 } 927 return toRemove; 928} 929 930static void removeElementFromFragmentPreservingChildren(DocumentFragment& fragment, HTMLElement& element) 931{ 932 RefPtr<Node> nextChild; 933 for (RefPtr<Node> child = element.firstChild(); child; child = nextChild) { 934 nextChild = child->nextSibling(); 935 element.removeChild(child.get(), ASSERT_NO_EXCEPTION); 936 fragment.insertBefore(child, &element, ASSERT_NO_EXCEPTION); 937 } 938 fragment.removeChild(&element, ASSERT_NO_EXCEPTION); 939} 940 941PassRefPtr<DocumentFragment> createContextualFragment(const String& markup, HTMLElement* element, ParserContentPolicy parserContentPolicy, ExceptionCode& ec) 942{ 943 ASSERT(element); 944 if (element->ieForbidsInsertHTML()) { 945 ec = NOT_SUPPORTED_ERR; 946 return 0; 947 } 948 949 if (element->hasTagName(colTag) || element->hasTagName(colgroupTag) || element->hasTagName(framesetTag) 950 || element->hasTagName(headTag) || element->hasTagName(styleTag) || element->hasTagName(titleTag)) { 951 ec = NOT_SUPPORTED_ERR; 952 return 0; 953 } 954 955 RefPtr<DocumentFragment> fragment = createFragmentForInnerOuterHTML(markup, element, parserContentPolicy, ec); 956 if (!fragment) 957 return 0; 958 959 // We need to pop <html> and <body> elements and remove <head> to 960 // accommodate folks passing complete HTML documents to make the 961 // child of an element. 962 auto toRemove = collectElementsToRemoveFromFragment(*fragment); 963 for (unsigned i = 0; i < toRemove.size(); ++i) 964 removeElementFromFragmentPreservingChildren(*fragment, toRemove[i].get()); 965 966 return fragment.release(); 967} 968 969static inline bool hasOneChild(ContainerNode* node) 970{ 971 Node* firstChild = node->firstChild(); 972 return firstChild && !firstChild->nextSibling(); 973} 974 975static inline bool hasOneTextChild(ContainerNode* node) 976{ 977 return hasOneChild(node) && node->firstChild()->isTextNode(); 978} 979 980void replaceChildrenWithFragment(ContainerNode& container, PassRefPtr<DocumentFragment> fragment, ExceptionCode& ec) 981{ 982 Ref<ContainerNode> containerNode(container); 983 ChildListMutationScope mutation(containerNode.get()); 984 985 if (!fragment->firstChild()) { 986 containerNode->removeChildren(); 987 return; 988 } 989 990 if (hasOneTextChild(&containerNode.get()) && hasOneTextChild(fragment.get())) { 991 toText(containerNode->firstChild())->setData(toText(fragment->firstChild())->data(), ec); 992 return; 993 } 994 995 if (hasOneChild(&containerNode.get())) { 996 containerNode->replaceChild(fragment, containerNode->firstChild(), ec); 997 return; 998 } 999 1000 containerNode->removeChildren(); 1001 containerNode->appendChild(fragment, ec); 1002} 1003 1004void replaceChildrenWithText(ContainerNode& container, const String& text, ExceptionCode& ec) 1005{ 1006 Ref<ContainerNode> containerNode(container); 1007 ChildListMutationScope mutation(containerNode.get()); 1008 1009 if (hasOneTextChild(&containerNode.get())) { 1010 toText(containerNode->firstChild())->setData(text, ec); 1011 return; 1012 } 1013 1014 RefPtr<Text> textNode = Text::create(containerNode->document(), text); 1015 1016 if (hasOneChild(&containerNode.get())) { 1017 containerNode->replaceChild(textNode.release(), containerNode->firstChild(), ec); 1018 return; 1019 } 1020 1021 containerNode->removeChildren(); 1022 containerNode->appendChild(textNode.release(), ec); 1023} 1024 1025} 1026