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.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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.
32 #include "CDATASection.h"
33 #include "CSSPrimitiveValue.h"
34 #include "CSSPropertyNames.h"
36 #include "CSSValueKeywords.h"
37 #include "ChildListMutationScope.h"
38 #include "DocumentFragment.h"
39 #include "DocumentType.h"
41 #include "ElementIterator.h"
42 #include "ExceptionCode.h"
43 #include "ExceptionCodePlaceholder.h"
45 #include "HTMLBodyElement.h"
46 #include "HTMLDivElement.h"
47 #include "HTMLElement.h"
48 #include "HTMLNames.h"
49 #include "HTMLTableElement.h"
50 #include "HTMLTextAreaElement.h"
51 #include "HTMLTextFormControlElement.h"
53 #include "MarkupAccumulator.h"
55 #include "RenderBlock.h"
57 #include "StyleProperties.h"
58 #include "TextIterator.h"
59 #include "VisibleSelection.h"
60 #include "VisibleUnits.h"
61 #include "htmlediting.h"
62 #include <wtf/StdLibExtras.h>
63 #include <wtf/text/StringBuilder.h>
65 #if ENABLE(DELETION_UI)
66 #include "DeleteButtonController.h"
71 using namespace HTMLNames;
73 static bool propertyMissingOrEqualToNone(StyleProperties*, CSSPropertyID);
75 class AttributeChange {
78 : m_name(nullAtom, nullAtom, nullAtom)
82 AttributeChange(PassRefPtr<Element> element, const QualifiedName& name, const String& value)
83 : m_element(element), m_name(name), m_value(value)
89 m_element->setAttribute(m_name, m_value);
93 RefPtr<Element> m_element;
98 static void completeURLs(DocumentFragment* fragment, const String& baseURL)
100 Vector<AttributeChange> changes;
102 URL parsedBaseURL(ParsedURLString, baseURL);
104 for (auto& element : descendantsOfType<Element>(*fragment)) {
105 if (!element.hasAttributes())
107 for (const Attribute& attribute : element.attributesIterator()) {
108 if (element.attributeContainsURL(attribute) && !attribute.value().isEmpty())
109 changes.append(AttributeChange(&element, attribute.name(), element.completeURLsInAttributeValue(parsedBaseURL, attribute)));
113 size_t numChanges = changes.size();
114 for (size_t i = 0; i < numChanges; ++i)
118 class StyledMarkupAccumulator final : public MarkupAccumulator {
120 enum RangeFullySelectsNode { DoesFullySelectNode, DoesNotFullySelectNode };
122 StyledMarkupAccumulator(Vector<Node*>* nodes, EAbsoluteURLs, EAnnotateForInterchange, const Range*, bool needsPositionStyleConversion, Node* highestNodeToBeSerialized = 0);
124 Node* serializeNodes(Node* startNode, Node* pastEnd);
125 void wrapWithNode(Node&, bool convertBlocksToInlines = false, RangeFullySelectsNode = DoesFullySelectNode);
126 void wrapWithStyleNode(StyleProperties*, Document&, bool isBlock = false);
127 String takeResults();
129 bool needRelativeStyleWrapper() const { return m_needRelativeStyleWrapper; }
130 bool needClearingDiv() const { return m_needClearingDiv; }
132 using MarkupAccumulator::appendString;
135 void appendStyleNodeOpenTag(StringBuilder&, StyleProperties*, Document&, bool isBlock = false);
136 const String& styleNodeCloseTag(bool isBlock = false);
138 String renderedText(const Node&, const Range*);
139 String stringValueForRange(const Node&, const Range*);
141 void appendElement(StringBuilder& out, const Element&, bool addDisplayInline, RangeFullySelectsNode);
143 virtual void appendText(StringBuilder& out, const Text&) override;
144 virtual void appendElement(StringBuilder& out, const Element& element, Namespaces*) override
146 appendElement(out, element, false, DoesFullySelectNode);
149 enum NodeTraversalMode { EmitString, DoNotEmitString };
150 Node* traverseNodesForSerialization(Node* startNode, Node* pastEnd, NodeTraversalMode);
152 bool shouldAnnotate()
154 return m_shouldAnnotate == AnnotateForInterchange;
157 bool shouldApplyWrappingStyle(const Node& node) const
159 return m_highestNodeToBeSerialized && m_highestNodeToBeSerialized->parentNode() == node.parentNode() && m_wrappingStyle && m_wrappingStyle->style();
162 Vector<String> m_reversedPrecedingMarkup;
163 const EAnnotateForInterchange m_shouldAnnotate;
164 Node* m_highestNodeToBeSerialized;
165 RefPtr<EditingStyle> m_wrappingStyle;
166 bool m_needRelativeStyleWrapper;
167 bool m_needsPositionStyleConversion;
168 bool m_needClearingDiv;
171 inline StyledMarkupAccumulator::StyledMarkupAccumulator(Vector<Node*>* nodes, EAbsoluteURLs shouldResolveURLs, EAnnotateForInterchange shouldAnnotate, const Range* range, bool needsPositionStyleConversion, Node* highestNodeToBeSerialized)
172 : MarkupAccumulator(nodes, shouldResolveURLs, range)
173 , m_shouldAnnotate(shouldAnnotate)
174 , m_highestNodeToBeSerialized(highestNodeToBeSerialized)
175 , m_needRelativeStyleWrapper(false)
176 , m_needsPositionStyleConversion(needsPositionStyleConversion)
177 , m_needClearingDiv(false)
181 void StyledMarkupAccumulator::wrapWithNode(Node& node, bool convertBlocksToInlines, RangeFullySelectsNode rangeFullySelectsNode)
183 StringBuilder markup;
184 if (node.isElementNode())
185 appendElement(markup, toElement(node), convertBlocksToInlines && isBlock(&node), rangeFullySelectsNode);
187 appendStartMarkup(markup, node, 0);
188 m_reversedPrecedingMarkup.append(markup.toString());
191 m_nodes->append(&node);
194 void StyledMarkupAccumulator::wrapWithStyleNode(StyleProperties* style, Document& document, bool isBlock)
196 StringBuilder openTag;
197 appendStyleNodeOpenTag(openTag, style, document, isBlock);
198 m_reversedPrecedingMarkup.append(openTag.toString());
199 appendString(styleNodeCloseTag(isBlock));
202 void StyledMarkupAccumulator::appendStyleNodeOpenTag(StringBuilder& out, StyleProperties* style, Document& document, bool isBlock)
204 // wrappingStyleForSerialization should have removed -webkit-text-decorations-in-effect
205 ASSERT(propertyMissingOrEqualToNone(style, CSSPropertyWebkitTextDecorationsInEffect));
207 out.appendLiteral("<div style=\"");
209 out.appendLiteral("<span style=\"");
210 appendAttributeValue(out, style->asText(), document.isHTMLDocument());
211 out.appendLiteral("\">");
214 const String& StyledMarkupAccumulator::styleNodeCloseTag(bool isBlock)
216 DEPRECATED_DEFINE_STATIC_LOCAL(const String, divClose, (ASCIILiteral("</div>")));
217 DEPRECATED_DEFINE_STATIC_LOCAL(const String, styleSpanClose, (ASCIILiteral("</span>")));
218 return isBlock ? divClose : styleSpanClose;
221 String StyledMarkupAccumulator::takeResults()
223 StringBuilder result;
224 result.reserveCapacity(totalLength(m_reversedPrecedingMarkup) + length());
226 for (size_t i = m_reversedPrecedingMarkup.size(); i > 0; --i)
227 result.append(m_reversedPrecedingMarkup[i - 1]);
229 concatenateMarkup(result);
231 // We remove '\0' characters because they are not visibly rendered to the user.
232 return result.toString().replaceWithLiteral('\0', "");
235 void StyledMarkupAccumulator::appendText(StringBuilder& out, const Text& text)
237 const bool parentIsTextarea = text.parentElement() && isHTMLTextAreaElement(text.parentElement());
238 const bool wrappingSpan = shouldApplyWrappingStyle(text) && !parentIsTextarea;
240 RefPtr<EditingStyle> wrappingStyle = m_wrappingStyle->copy();
241 // FIXME: <rdar://problem/5371536> Style rules that match pasted content can change it's appearance
242 // Make sure spans are inline style in paste side e.g. span { display: block }.
243 wrappingStyle->forceInline();
244 // FIXME: Should this be included in forceInline?
245 wrappingStyle->style()->setProperty(CSSPropertyFloat, CSSValueNone);
247 appendStyleNodeOpenTag(out, wrappingStyle->style(), text.document());
250 if (!shouldAnnotate() || parentIsTextarea)
251 MarkupAccumulator::appendText(out, text);
253 const bool useRenderedText = !enclosingElementWithTag(firstPositionInNode(const_cast<Text*>(&text)), selectTag);
254 String content = useRenderedText ? renderedText(text, m_range) : stringValueForRange(text, m_range);
255 StringBuilder buffer;
256 appendCharactersReplacingEntities(buffer, content, 0, content.length(), EntityMaskInPCDATA);
257 out.append(convertHTMLTextToInterchangeFormat(buffer.toString(), &text));
261 out.append(styleNodeCloseTag());
264 String StyledMarkupAccumulator::renderedText(const Node& node, const Range* range)
266 if (!node.isTextNode())
269 const Text& textNode = toText(node);
270 unsigned startOffset = 0;
271 unsigned endOffset = textNode.length();
273 TextIteratorBehavior behavior = TextIteratorDefaultBehavior;
274 if (range && &node == range->startContainer())
275 startOffset = range->startOffset();
276 if (range && &node == range->endContainer())
277 endOffset = range->endOffset();
279 behavior = TextIteratorBehavesAsIfNodesFollowing;
281 Position start = createLegacyEditingPosition(const_cast<Node*>(&node), startOffset);
282 Position end = createLegacyEditingPosition(const_cast<Node*>(&node), endOffset);
283 return plainText(Range::create(node.document(), start, end).get(), behavior);
286 String StyledMarkupAccumulator::stringValueForRange(const Node& node, const Range* range)
289 return node.nodeValue();
291 String nodeValue = node.nodeValue();
292 if (&node == range->endContainer())
293 nodeValue.truncate(range->endOffset());
294 if (&node == range->startContainer())
295 nodeValue.remove(0, range->startOffset());
299 void StyledMarkupAccumulator::appendElement(StringBuilder& out, const Element& element, bool addDisplayInline, RangeFullySelectsNode rangeFullySelectsNode)
301 const bool documentIsHTML = element.document().isHTMLDocument();
302 appendOpenTag(out, element, 0);
304 const bool shouldAnnotateOrForceInline = element.isHTMLElement() && (shouldAnnotate() || addDisplayInline);
305 const bool shouldOverrideStyleAttr = shouldAnnotateOrForceInline || shouldApplyWrappingStyle(element);
306 if (element.hasAttributes()) {
307 for (const Attribute& attribute : element.attributesIterator()) {
308 // We'll handle the style attribute separately, below.
309 if (attribute.name() == styleAttr && shouldOverrideStyleAttr)
311 appendAttribute(out, element, attribute, 0);
315 if (shouldOverrideStyleAttr) {
316 RefPtr<EditingStyle> newInlineStyle;
318 if (shouldApplyWrappingStyle(element)) {
319 newInlineStyle = m_wrappingStyle->copy();
320 newInlineStyle->removePropertiesInElementDefaultStyle(const_cast<Element*>(&element));
321 newInlineStyle->removeStyleConflictingWithStyleOfNode(const_cast<Element*>(&element));
323 newInlineStyle = EditingStyle::create();
325 if (element.isStyledElement() && toStyledElement(element).inlineStyle())
326 newInlineStyle->overrideWithStyle(toStyledElement(element).inlineStyle());
328 if (shouldAnnotateOrForceInline) {
329 if (shouldAnnotate())
330 newInlineStyle->mergeStyleFromRulesForSerialization(toHTMLElement(const_cast<Element*>(&element)));
332 if (addDisplayInline)
333 newInlineStyle->forceInline();
335 if (m_needsPositionStyleConversion) {
336 m_needRelativeStyleWrapper |= newInlineStyle->convertPositionStyle();
337 m_needClearingDiv |= newInlineStyle->isFloating();
340 // 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
341 // only the ones that affect it and the nodes within it.
342 if (rangeFullySelectsNode == DoesNotFullySelectNode && newInlineStyle->style())
343 newInlineStyle->style()->removeProperty(CSSPropertyFloat);
346 if (!newInlineStyle->isEmpty()) {
347 out.appendLiteral(" style=\"");
348 appendAttributeValue(out, newInlineStyle->style()->asText(), documentIsHTML);
353 appendCloseTag(out, element);
356 Node* StyledMarkupAccumulator::serializeNodes(Node* startNode, Node* pastEnd)
358 if (!m_highestNodeToBeSerialized) {
359 Node* lastClosed = traverseNodesForSerialization(startNode, pastEnd, DoNotEmitString);
360 m_highestNodeToBeSerialized = lastClosed;
363 if (m_highestNodeToBeSerialized && m_highestNodeToBeSerialized->parentNode())
364 m_wrappingStyle = EditingStyle::wrappingStyleForSerialization(m_highestNodeToBeSerialized->parentNode(), shouldAnnotate());
366 return traverseNodesForSerialization(startNode, pastEnd, EmitString);
369 Node* StyledMarkupAccumulator::traverseNodesForSerialization(Node* startNode, Node* pastEnd, NodeTraversalMode traversalMode)
371 const bool shouldEmit = traversalMode == EmitString;
372 Vector<Node*> ancestorsToClose;
374 Node* lastClosed = 0;
375 for (Node* n = startNode; n != pastEnd; n = next) {
376 // According to <rdar://problem/5730668>, it is possible for n to blow
377 // past pastEnd and become null here. This shouldn't be possible.
378 // This null check will prevent crashes (but create too much markup)
379 // and the ASSERT will hopefully lead us to understanding the problem.
384 next = NodeTraversal::next(n);
385 bool openedTag = false;
387 if (isBlock(n) && canHaveChildrenForEditing(n) && next == pastEnd)
388 // Don't write out empty block containers that aren't fully selected.
391 if (!n->renderer() && !enclosingElementWithTag(firstPositionInOrBeforeNode(n), selectTag)) {
392 next = NodeTraversal::nextSkippingChildren(n);
393 // Don't skip over pastEnd.
394 if (pastEnd && pastEnd->isDescendantOf(n))
397 // Add the node to the markup if we're not skipping the descendants
401 // If node has no children, close the tag now.
402 if (!n->hasChildNodes()) {
408 ancestorsToClose.append(n);
412 // 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.
413 // FIXME: What happens if we just inserted open tag and reached the end?
414 if (!openedTag && (!n->nextSibling() || next == pastEnd)) {
415 // Close up the ancestors.
416 while (!ancestorsToClose.isEmpty()) {
417 Node* ancestor = ancestorsToClose.last();
418 if (next != pastEnd && next->isDescendantOf(ancestor))
420 // Not at the end of the range, close ancestors up to sibling of next node.
422 appendEndTag(*ancestor);
423 lastClosed = ancestor;
424 ancestorsToClose.removeLast();
427 // Surround the currently accumulated markup with markup for ancestors we never opened as we leave the subtree(s) rooted at those ancestors.
428 ContainerNode* nextParent = next ? next->parentNode() : 0;
429 if (next != pastEnd && n != nextParent) {
430 Node* lastAncestorClosedOrSelf = n->isDescendantOf(lastClosed) ? lastClosed : n;
431 for (ContainerNode* parent = lastAncestorClosedOrSelf->parentNode(); parent && parent != nextParent; parent = parent->parentNode()) {
432 // All ancestors that aren't in the ancestorsToClose list should either be a) unrendered:
433 if (!parent->renderer())
435 // or b) ancestors that we never encountered during a pre-order traversal starting at startNode:
436 ASSERT(startNode->isDescendantOf(parent));
438 wrapWithNode(*parent);
448 static Node* ancestorToRetainStructureAndAppearanceForBlock(Node* commonAncestorBlock)
450 if (!commonAncestorBlock)
453 if (commonAncestorBlock->hasTagName(tbodyTag) || commonAncestorBlock->hasTagName(trTag)) {
454 ContainerNode* table = commonAncestorBlock->parentNode();
455 while (table && !isHTMLTableElement(table))
456 table = table->parentNode();
461 if (isNonTableCellHTMLBlockElement(commonAncestorBlock))
462 return commonAncestorBlock;
467 static inline Node* ancestorToRetainStructureAndAppearance(Node* commonAncestor)
469 return ancestorToRetainStructureAndAppearanceForBlock(enclosingBlock(commonAncestor));
472 static bool propertyMissingOrEqualToNone(StyleProperties* style, CSSPropertyID propertyID)
476 RefPtr<CSSValue> value = style->getPropertyCSSValue(propertyID);
479 if (!value->isPrimitiveValue())
481 return toCSSPrimitiveValue(value.get())->getValueID() == CSSValueNone;
484 static bool needInterchangeNewlineAfter(const VisiblePosition& v)
486 VisiblePosition next = v.next();
487 Node* upstreamNode = next.deepEquivalent().upstream().deprecatedNode();
488 Node* downstreamNode = v.deepEquivalent().downstream().deprecatedNode();
489 // Add an interchange newline if a paragraph break is selected and a br won't already be added to the markup to represent it.
490 return isEndOfParagraph(v) && isStartOfParagraph(next) && !(upstreamNode->hasTagName(brTag) && upstreamNode == downstreamNode);
493 static PassRefPtr<EditingStyle> styleFromMatchedRulesAndInlineDecl(const Node* node)
495 if (!node->isHTMLElement())
498 // FIXME: Having to const_cast here is ugly, but it is quite a bit of work to untangle
499 // the non-const-ness of styleFromMatchedRulesForElement.
500 HTMLElement* element = const_cast<HTMLElement*>(static_cast<const HTMLElement*>(node));
501 RefPtr<EditingStyle> style = EditingStyle::create(element->inlineStyle());
502 style->mergeStyleFromRules(element);
503 return style.release();
506 static bool isElementPresentational(const Node* node)
508 return node->hasTagName(uTag) || node->hasTagName(sTag) || node->hasTagName(strikeTag)
509 || node->hasTagName(iTag) || node->hasTagName(emTag) || node->hasTagName(bTag) || node->hasTagName(strongTag);
512 static Node* highestAncestorToWrapMarkup(const Range* range, EAnnotateForInterchange shouldAnnotate)
514 Node* commonAncestor = range->commonAncestorContainer(IGNORE_EXCEPTION);
515 ASSERT(commonAncestor);
516 Node* specialCommonAncestor = 0;
517 if (shouldAnnotate == AnnotateForInterchange) {
518 // Include ancestors that aren't completely inside the range but are required to retain
519 // the structure and appearance of the copied markup.
520 specialCommonAncestor = ancestorToRetainStructureAndAppearance(commonAncestor);
522 if (Node* parentListNode = enclosingNodeOfType(firstPositionInOrBeforeNode(range->firstNode()), isListItem)) {
523 if (WebCore::areRangesEqual(VisibleSelection::selectionFromContentsOfNode(parentListNode).toNormalizedRange().get(), range)) {
524 specialCommonAncestor = parentListNode->parentNode();
525 while (specialCommonAncestor && !isListElement(specialCommonAncestor))
526 specialCommonAncestor = specialCommonAncestor->parentNode();
530 // Retain the Mail quote level by including all ancestor mail block quotes.
531 if (Node* highestMailBlockquote = highestEnclosingNodeOfType(firstPositionInOrBeforeNode(range->firstNode()), isMailBlockquote, CanCrossEditingBoundary))
532 specialCommonAncestor = highestMailBlockquote;
535 Node* checkAncestor = specialCommonAncestor ? specialCommonAncestor : commonAncestor;
536 if (checkAncestor->renderer() && checkAncestor->renderer()->containingBlock()) {
537 Node* newSpecialCommonAncestor = highestEnclosingNodeOfType(firstPositionInNode(checkAncestor), &isElementPresentational, CanCrossEditingBoundary, checkAncestor->renderer()->containingBlock()->element());
538 if (newSpecialCommonAncestor)
539 specialCommonAncestor = newSpecialCommonAncestor;
542 // If a single tab is selected, commonAncestor will be a text node inside a tab span.
543 // If two or more tabs are selected, commonAncestor will be the tab span.
544 // In either case, if there is a specialCommonAncestor already, it will necessarily be above
545 // any tab span that needs to be included.
546 if (!specialCommonAncestor && isTabSpanTextNode(commonAncestor))
547 specialCommonAncestor = commonAncestor->parentNode();
548 if (!specialCommonAncestor && isTabSpanNode(commonAncestor))
549 specialCommonAncestor = commonAncestor;
551 if (auto* enclosingAnchor = enclosingElementWithTag(firstPositionInNode(specialCommonAncestor ? specialCommonAncestor : commonAncestor), aTag))
552 specialCommonAncestor = enclosingAnchor;
554 return specialCommonAncestor;
557 // FIXME: Shouldn't we omit style info when annotate == DoNotAnnotateForInterchange?
558 // FIXME: At least, annotation and style info should probably not be included in range.markupString()
559 static String createMarkupInternal(Document& document, const Range& range, const Range& updatedRange, Vector<Node*>* nodes,
560 EAnnotateForInterchange shouldAnnotate, bool convertBlocksToInlines, EAbsoluteURLs shouldResolveURLs)
562 DEPRECATED_DEFINE_STATIC_LOCAL(const String, interchangeNewlineString, (ASCIILiteral("<br class=\"" AppleInterchangeNewline "\">")));
564 bool collapsed = updatedRange.collapsed(ASSERT_NO_EXCEPTION);
566 return emptyString();
567 Node* commonAncestor = updatedRange.commonAncestorContainer(ASSERT_NO_EXCEPTION);
569 return emptyString();
571 document.updateLayoutIgnorePendingStylesheets();
573 auto* body = enclosingElementWithTag(firstPositionInNode(commonAncestor), bodyTag);
574 Node* fullySelectedRoot = 0;
575 // FIXME: Do this for all fully selected blocks, not just the body.
576 if (body && VisiblePosition(firstPositionInNode(body)) == VisiblePosition(range.startPosition())
577 && VisiblePosition(lastPositionInNode(body)) == VisiblePosition(range.endPosition()))
578 fullySelectedRoot = body;
579 Node* specialCommonAncestor = highestAncestorToWrapMarkup(&updatedRange, shouldAnnotate);
581 bool needsPositionStyleConversion = body && fullySelectedRoot == body
582 && document.settings() && document.settings()->shouldConvertPositionStyleOnCopy();
583 StyledMarkupAccumulator accumulator(nodes, shouldResolveURLs, shouldAnnotate, &updatedRange, needsPositionStyleConversion, specialCommonAncestor);
584 Node* pastEnd = updatedRange.pastLastNode();
586 Node* startNode = updatedRange.firstNode();
587 VisiblePosition visibleStart(updatedRange.startPosition(), VP_DEFAULT_AFFINITY);
588 VisiblePosition visibleEnd(updatedRange.endPosition(), VP_DEFAULT_AFFINITY);
589 if (shouldAnnotate == AnnotateForInterchange && needInterchangeNewlineAfter(visibleStart)) {
590 if (visibleStart == visibleEnd.previous())
591 return interchangeNewlineString;
593 accumulator.appendString(interchangeNewlineString);
594 startNode = visibleStart.next().deepEquivalent().deprecatedNode();
596 if (pastEnd && Range::compareBoundaryPoints(startNode, 0, pastEnd, 0, ASSERT_NO_EXCEPTION) >= 0)
597 return interchangeNewlineString;
600 Node* lastClosed = accumulator.serializeNodes(startNode, pastEnd);
602 if (specialCommonAncestor && lastClosed) {
603 // Also include all of the ancestors of lastClosed up to this special ancestor.
604 for (ContainerNode* ancestor = lastClosed->parentNode(); ancestor; ancestor = ancestor->parentNode()) {
605 if (ancestor == fullySelectedRoot && !convertBlocksToInlines) {
606 RefPtr<EditingStyle> fullySelectedRootStyle = styleFromMatchedRulesAndInlineDecl(fullySelectedRoot);
608 // Bring the background attribute over, but not as an attribute because a background attribute on a div
609 // appears to have no effect.
610 if ((!fullySelectedRootStyle || !fullySelectedRootStyle->style() || !fullySelectedRootStyle->style()->getPropertyCSSValue(CSSPropertyBackgroundImage))
611 && toElement(fullySelectedRoot)->hasAttribute(backgroundAttr))
612 fullySelectedRootStyle->style()->setProperty(CSSPropertyBackgroundImage, "url('" + toElement(fullySelectedRoot)->getAttribute(backgroundAttr) + "')");
614 if (fullySelectedRootStyle->style()) {
615 // Reset the CSS properties to avoid an assertion error in addStyleMarkup().
616 // This assertion is caused at least when we select all text of a <body> element whose
617 // 'text-decoration' property is "inherit", and copy it.
618 if (!propertyMissingOrEqualToNone(fullySelectedRootStyle->style(), CSSPropertyTextDecoration))
619 fullySelectedRootStyle->style()->setProperty(CSSPropertyTextDecoration, CSSValueNone);
620 if (!propertyMissingOrEqualToNone(fullySelectedRootStyle->style(), CSSPropertyWebkitTextDecorationsInEffect))
621 fullySelectedRootStyle->style()->setProperty(CSSPropertyWebkitTextDecorationsInEffect, CSSValueNone);
622 accumulator.wrapWithStyleNode(fullySelectedRootStyle->style(), document, true);
625 // Since this node and all the other ancestors are not in the selection we want to set RangeFullySelectsNode to DoesNotFullySelectNode
626 // so that styles that affect the exterior of the node are not included.
627 accumulator.wrapWithNode(*ancestor, convertBlocksToInlines, StyledMarkupAccumulator::DoesNotFullySelectNode);
630 nodes->append(ancestor);
632 if (ancestor == specialCommonAncestor)
637 if (accumulator.needRelativeStyleWrapper() && needsPositionStyleConversion) {
638 if (accumulator.needClearingDiv())
639 accumulator.appendString("<div style=\"clear: both;\"></div>");
640 RefPtr<EditingStyle> positionRelativeStyle = styleFromMatchedRulesAndInlineDecl(body);
641 positionRelativeStyle->style()->setProperty(CSSPropertyPosition, CSSValueRelative);
642 accumulator.wrapWithStyleNode(positionRelativeStyle->style(), document, true);
645 // FIXME: The interchange newline should be placed in the block that it's in, not after all of the content, unconditionally.
646 if (shouldAnnotate == AnnotateForInterchange && needInterchangeNewlineAfter(visibleEnd.previous()))
647 accumulator.appendString(interchangeNewlineString);
649 return accumulator.takeResults();
652 String createMarkup(const Range& range, Vector<Node*>* nodes, EAnnotateForInterchange shouldAnnotate, bool convertBlocksToInlines, EAbsoluteURLs shouldResolveURLs)
654 Document& document = range.ownerDocument();
655 const Range* updatedRange = ⦥
657 #if ENABLE(DELETION_UI)
658 // Disable the delete button so it's elements are not serialized into the markup,
659 // but make sure neither endpoint is inside the delete user interface.
660 Frame* frame = document.frame();
661 DeleteButtonControllerDisableScope deleteButtonControllerDisableScope(frame);
663 RefPtr<Range> updatedRangeRef;
665 updatedRangeRef = frame->editor().avoidIntersectionWithDeleteButtonController(&range);
666 updatedRange = updatedRangeRef.get();
668 return emptyString();
672 return createMarkupInternal(document, range, *updatedRange, nodes, shouldAnnotate, convertBlocksToInlines, shouldResolveURLs);
675 PassRefPtr<DocumentFragment> createFragmentFromMarkup(Document& document, const String& markup, const String& baseURL, ParserContentPolicy parserContentPolicy)
677 // We use a fake body element here to trick the HTML parser to using the InBody insertion mode.
678 RefPtr<HTMLBodyElement> fakeBody = HTMLBodyElement::create(document);
679 RefPtr<DocumentFragment> fragment = DocumentFragment::create(document);
681 fragment->parseHTML(markup, fakeBody.get(), parserContentPolicy);
683 if (!baseURL.isEmpty() && baseURL != blankURL() && baseURL != document.baseURL())
684 completeURLs(fragment.get(), baseURL);
686 return fragment.release();
689 String createMarkup(const Node& node, EChildrenOnly childrenOnly, Vector<Node*>* nodes, EAbsoluteURLs shouldResolveURLs, Vector<QualifiedName>* tagNamesToSkip, EFragmentSerialization fragmentSerialization)
691 HTMLElement* deleteButtonContainerElement = 0;
692 #if ENABLE(DELETION_UI)
693 if (Frame* frame = node.document().frame()) {
694 deleteButtonContainerElement = frame->editor().deleteButtonController().containerElement();
695 if (node.isDescendantOf(deleteButtonContainerElement))
696 return emptyString();
700 MarkupAccumulator accumulator(nodes, shouldResolveURLs, 0, fragmentSerialization);
701 return accumulator.serializeNodes(const_cast<Node&>(node), deleteButtonContainerElement, childrenOnly, tagNamesToSkip);
704 static void fillContainerFromString(ContainerNode* paragraph, const String& string)
706 Document& document = paragraph->document();
708 if (string.isEmpty()) {
709 paragraph->appendChild(createBlockPlaceholderElement(document), ASSERT_NO_EXCEPTION);
713 ASSERT(string.find('\n') == notFound);
715 Vector<String> tabList;
716 string.split('\t', true, tabList);
717 String tabText = emptyString();
719 size_t numEntries = tabList.size();
720 for (size_t i = 0; i < numEntries; ++i) {
721 const String& s = tabList[i];
723 // append the non-tab textual part
725 if (!tabText.isEmpty()) {
726 paragraph->appendChild(createTabSpanElement(document, tabText), ASSERT_NO_EXCEPTION);
727 tabText = emptyString();
729 RefPtr<Node> textNode = document.createTextNode(stringWithRebalancedWhitespace(s, first, i + 1 == numEntries));
730 paragraph->appendChild(textNode.release(), ASSERT_NO_EXCEPTION);
733 // there is a tab after every entry, except the last entry
734 // (if the last character is a tab, the list gets an extra empty entry)
735 if (i + 1 != numEntries)
736 tabText.append('\t');
737 else if (!tabText.isEmpty())
738 paragraph->appendChild(createTabSpanElement(document, tabText), ASSERT_NO_EXCEPTION);
744 bool isPlainTextMarkup(Node* node)
746 if (!isHTMLDivElement(node))
749 HTMLDivElement& element = downcast<HTMLDivElement>(*node);
750 if (element.hasAttributes())
753 Node* firstChild = element.firstChild();
757 Node* secondChild = firstChild->nextSibling();
759 return firstChild->isTextNode() || firstChild->firstChild();
761 if (secondChild->nextSibling())
764 return isTabSpanTextNode(firstChild->firstChild()) && secondChild->isTextNode();
767 static bool contextPreservesNewline(const Range& context)
769 VisiblePosition position(context.startPosition());
770 Node* container = position.deepEquivalent().containerNode();
771 if (!container || !container->renderer())
774 return container->renderer()->style().preserveNewline();
777 PassRefPtr<DocumentFragment> createFragmentFromText(Range& context, const String& text)
779 Document& document = context.ownerDocument();
780 RefPtr<DocumentFragment> fragment = document.createDocumentFragment();
783 return fragment.release();
785 String string = text;
786 string.replace("\r\n", "\n");
787 string.replace('\r', '\n');
789 if (contextPreservesNewline(context)) {
790 fragment->appendChild(document.createTextNode(string), ASSERT_NO_EXCEPTION);
791 if (string.endsWith('\n')) {
792 RefPtr<Element> element = createBreakElement(document);
793 element->setAttribute(classAttr, AppleInterchangeNewline);
794 fragment->appendChild(element.release(), ASSERT_NO_EXCEPTION);
796 return fragment.release();
799 // A string with no newlines gets added inline, rather than being put into a paragraph.
800 if (string.find('\n') == notFound) {
801 fillContainerFromString(fragment.get(), string);
802 return fragment.release();
805 // Break string into paragraphs. Extra line breaks turn into empty paragraphs.
806 Node* blockNode = enclosingBlock(context.firstNode());
807 Element* block = toElement(blockNode);
808 bool useClonesOfEnclosingBlock = blockNode
809 && blockNode->isElementNode()
810 && !block->hasTagName(bodyTag)
811 && !block->hasTagName(htmlTag)
812 && block != editableRootForPosition(context.startPosition());
813 bool useLineBreak = enclosingTextFormControl(context.startPosition());
816 string.split('\n', true, list); // true gets us empty strings in the list
817 size_t numLines = list.size();
818 for (size_t i = 0; i < numLines; ++i) {
819 const String& s = list[i];
821 RefPtr<Element> element;
822 if (s.isEmpty() && i + 1 == numLines) {
823 // For last line, use the "magic BR" rather than a P.
824 element = createBreakElement(document);
825 element->setAttribute(classAttr, AppleInterchangeNewline);
826 } else if (useLineBreak) {
827 element = createBreakElement(document);
828 fillContainerFromString(fragment.get(), s);
830 if (useClonesOfEnclosingBlock)
831 element = block->cloneElementWithoutChildren();
833 element = createDefaultParagraphElement(document);
834 fillContainerFromString(element.get(), s);
836 fragment->appendChild(element.release(), ASSERT_NO_EXCEPTION);
838 return fragment.release();
841 String documentTypeString(const Document& document)
843 DocumentType* documentType = document.doctype();
845 return emptyString();
846 return createMarkup(*documentType);
849 String createFullMarkup(const Node& node)
851 // FIXME: This is never "for interchange". Is that right?
852 String markupString = createMarkup(node, IncludeNode, 0);
854 Node::NodeType nodeType = node.nodeType();
855 if (nodeType != Node::DOCUMENT_NODE && nodeType != Node::DOCUMENT_TYPE_NODE)
856 markupString = documentTypeString(node.document()) + markupString;
861 String createFullMarkup(const Range& range)
863 Node* node = range.startContainer();
867 // FIXME: This is always "for interchange". Is that right?
868 return documentTypeString(node->document()) + createMarkup(range, 0, AnnotateForInterchange);
871 String urlToMarkup(const URL& url, const String& title)
873 StringBuilder markup;
874 markup.appendLiteral("<a href=\"");
875 markup.append(url.string());
876 markup.appendLiteral("\">");
877 MarkupAccumulator::appendCharactersReplacingEntities(markup, title, 0, title.length(), EntityMaskInPCDATA);
878 markup.appendLiteral("</a>");
879 return markup.toString();
882 PassRefPtr<DocumentFragment> createFragmentForInnerOuterHTML(const String& markup, Element* contextElement, ParserContentPolicy parserContentPolicy, ExceptionCode& ec)
884 Document* document = &contextElement->document();
885 #if ENABLE(TEMPLATE_ELEMENT)
886 if (contextElement->hasTagName(templateTag))
887 document = document->ensureTemplateDocument();
889 RefPtr<DocumentFragment> fragment = DocumentFragment::create(*document);
891 if (document->isHTMLDocument()) {
892 fragment->parseHTML(markup, contextElement, parserContentPolicy);
896 bool wasValid = fragment->parseXML(markup, contextElement, parserContentPolicy);
901 return fragment.release();
904 PassRefPtr<DocumentFragment> createFragmentForTransformToFragment(const String& sourceString, const String& sourceMIMEType, Document* outputDoc)
906 RefPtr<DocumentFragment> fragment = outputDoc->createDocumentFragment();
908 if (sourceMIMEType == "text/html") {
909 // As far as I can tell, there isn't a spec for how transformToFragment is supposed to work.
910 // Based on the documentation I can find, it looks like we want to start parsing the fragment in the InBody insertion mode.
911 // Unfortunately, that's an implementation detail of the parser.
912 // We achieve that effect here by passing in a fake body element as context for the fragment.
913 RefPtr<HTMLBodyElement> fakeBody = HTMLBodyElement::create(*outputDoc);
914 fragment->parseHTML(sourceString, fakeBody.get());
915 } else if (sourceMIMEType == "text/plain")
916 fragment->parserAppendChild(Text::create(*outputDoc, sourceString));
918 bool successfulParse = fragment->parseXML(sourceString, 0);
919 if (!successfulParse)
923 // FIXME: Do we need to mess with URLs here?
925 return fragment.release();
928 static Vector<Ref<HTMLElement>> collectElementsToRemoveFromFragment(ContainerNode& container)
930 Vector<Ref<HTMLElement>> toRemove;
931 for (auto& element : childrenOfType<HTMLElement>(container)) {
932 if (isHTMLHtmlElement(element)) {
933 toRemove.append(element);
934 collectElementsToRemoveFromFragment(element);
937 if (isHTMLHeadElement(element) || isHTMLBodyElement(element))
938 toRemove.append(element);
943 static void removeElementFromFragmentPreservingChildren(DocumentFragment& fragment, HTMLElement& element)
945 RefPtr<Node> nextChild;
946 for (RefPtr<Node> child = element.firstChild(); child; child = nextChild) {
947 nextChild = child->nextSibling();
948 element.removeChild(child.get(), ASSERT_NO_EXCEPTION);
949 fragment.insertBefore(child, &element, ASSERT_NO_EXCEPTION);
951 fragment.removeChild(&element, ASSERT_NO_EXCEPTION);
954 PassRefPtr<DocumentFragment> createContextualFragment(const String& markup, HTMLElement* element, ParserContentPolicy parserContentPolicy, ExceptionCode& ec)
957 if (element->ieForbidsInsertHTML()) {
958 ec = NOT_SUPPORTED_ERR;
962 if (element->hasTagName(colTag) || element->hasTagName(colgroupTag) || element->hasTagName(framesetTag)
963 || element->hasTagName(headTag) || element->hasTagName(styleTag) || element->hasTagName(titleTag)) {
964 ec = NOT_SUPPORTED_ERR;
968 RefPtr<DocumentFragment> fragment = createFragmentForInnerOuterHTML(markup, element, parserContentPolicy, ec);
972 // We need to pop <html> and <body> elements and remove <head> to
973 // accommodate folks passing complete HTML documents to make the
974 // child of an element.
975 auto toRemove = collectElementsToRemoveFromFragment(*fragment);
976 for (unsigned i = 0; i < toRemove.size(); ++i)
977 removeElementFromFragmentPreservingChildren(*fragment, toRemove[i].get());
979 return fragment.release();
982 static inline bool hasOneChild(ContainerNode* node)
984 Node* firstChild = node->firstChild();
985 return firstChild && !firstChild->nextSibling();
988 static inline bool hasOneTextChild(ContainerNode* node)
990 return hasOneChild(node) && node->firstChild()->isTextNode();
993 void replaceChildrenWithFragment(ContainerNode& container, PassRefPtr<DocumentFragment> fragment, ExceptionCode& ec)
995 Ref<ContainerNode> containerNode(container);
996 ChildListMutationScope mutation(containerNode.get());
998 if (!fragment->firstChild()) {
999 containerNode->removeChildren();
1003 if (hasOneTextChild(&containerNode.get()) && hasOneTextChild(fragment.get())) {
1004 toText(containerNode->firstChild())->setData(toText(fragment->firstChild())->data(), ec);
1008 if (hasOneChild(&containerNode.get())) {
1009 containerNode->replaceChild(fragment, containerNode->firstChild(), ec);
1013 containerNode->removeChildren();
1014 containerNode->appendChild(fragment, ec);
1017 void replaceChildrenWithText(ContainerNode& container, const String& text, ExceptionCode& ec)
1019 Ref<ContainerNode> containerNode(container);
1020 ChildListMutationScope mutation(containerNode.get());
1022 if (hasOneTextChild(&containerNode.get())) {
1023 toText(containerNode->firstChild())->setData(text, ec);
1027 RefPtr<Text> textNode = Text::create(containerNode->document(), text);
1029 if (hasOneChild(&containerNode.get())) {
1030 containerNode->replaceChild(textNode.release(), containerNode->firstChild(), ec);
1034 containerNode->removeChildren();
1035 containerNode->appendChild(textNode.release(), ec);