Index: Source/core/editing/StyledMarkupSerializer.cpp |
diff --git a/Source/core/editing/StyledMarkupSerializer.cpp b/Source/core/editing/StyledMarkupSerializer.cpp |
index 1a70e858d64a6f1bc730d0c7ee18c8867326e5a1..b11b9ebeef9ce548f2739067b4a1076c7c775a9f 100644 |
--- a/Source/core/editing/StyledMarkupSerializer.cpp |
+++ b/Source/core/editing/StyledMarkupSerializer.cpp |
@@ -34,9 +34,12 @@ |
#include "core/dom/Element.h" |
#include "core/dom/Text.h" |
#include "core/editing/EditingStyle.h" |
+#include "core/editing/VisibleSelection.h" |
+#include "core/editing/VisibleUnits.h" |
#include "core/editing/htmlediting.h" |
#include "core/editing/iterators/TextIterator.h" |
#include "core/editing/markup.h" |
+#include "core/html/HTMLBodyElement.h" |
#include "core/html/HTMLElement.h" |
#include "wtf/text/StringBuilder.h" |
@@ -212,9 +215,119 @@ bool StyledMarkupAccumulator::shouldApplyWrappingStyle(const Node& node) const |
StyledMarkupSerializer::StyledMarkupSerializer(EAbsoluteURLs shouldResolveURLs, EAnnotateForInterchange shouldAnnotate, const Position& start, const Position& end, Node* highestNodeToBeSerialized) |
: m_markupAccumulator(this, shouldResolveURLs, start, end, shouldAnnotate, highestNodeToBeSerialized) |
+ , m_start(start) |
+ , m_end(end) |
{ |
} |
+static bool needInterchangeNewlineAfter(const VisiblePosition& v) |
+{ |
+ VisiblePosition next = v.next(); |
+ Node* upstreamNode = next.deepEquivalent().upstream().deprecatedNode(); |
+ Node* downstreamNode = v.deepEquivalent().downstream().deprecatedNode(); |
+ // Add an interchange newline if a paragraph break is selected and a br won't already be added to the markup to represent it. |
+ return isEndOfParagraph(v) && isStartOfParagraph(next) && !(isHTMLBRElement(*upstreamNode) && upstreamNode == downstreamNode); |
+} |
+ |
+static bool needInterchangeNewlineAt(const VisiblePosition& v) |
+{ |
+ // FIXME: |v.previous()| works on a DOM tree. We need to fix this to work on |
+ // a composed tree. |
+ return needInterchangeNewlineAfter(v.previous()); |
+} |
+ |
+static bool areSameRanges(Node* node, const Position& startPosition, const Position& endPosition) |
+{ |
+ ASSERT(node); |
+ Position otherStartPosition; |
+ Position otherEndPosition; |
+ VisibleSelection::selectionFromContentsOfNode(node).toNormalizedPositions(otherStartPosition, otherEndPosition); |
+ return startPosition == otherStartPosition && endPosition == otherEndPosition; |
+} |
+ |
+static PassRefPtrWillBeRawPtr<EditingStyle> styleFromMatchedRulesAndInlineDecl(const HTMLElement* element) |
+{ |
+ RefPtrWillBeRawPtr<EditingStyle> style = EditingStyle::create(element->inlineStyle()); |
+ // FIXME: Having to const_cast here is ugly, but it is quite a bit of work to untangle |
+ // the non-const-ness of styleFromMatchedRulesForElement. |
+ style->mergeStyleFromRules(const_cast<HTMLElement*>(element)); |
+ return style.release(); |
+} |
+ |
+String StyledMarkupSerializer::createMarkup(bool convertBlocksToInlines, Node* specialCommonAncestor) |
+{ |
+ DEFINE_STATIC_LOCAL(const String, interchangeNewlineString, ("<br class=\"" AppleInterchangeNewline "\">")); |
+ |
+ Node* pastEnd = m_end.nodeAsRangePastLastNode(); |
+ |
+ Node* firstNode = m_start.nodeAsRangeFirstNode(); |
+ VisiblePosition visibleStart(m_start, VP_DEFAULT_AFFINITY); |
+ VisiblePosition visibleEnd(m_end, VP_DEFAULT_AFFINITY); |
+ if (m_markupAccumulator.shouldAnnotateForInterchange() && needInterchangeNewlineAfter(visibleStart)) { |
+ if (visibleStart == visibleEnd.previous()) |
+ return interchangeNewlineString; |
+ |
+ m_markupAccumulator.appendString(interchangeNewlineString); |
+ firstNode = visibleStart.next().deepEquivalent().deprecatedNode(); |
+ |
+ if (pastEnd && Position::beforeNode(firstNode).compareTo(Position::beforeNode(pastEnd)) >= 0) { |
+ // This condition hits in editing/pasteboard/copy-display-none.html. |
+ return interchangeNewlineString; |
+ } |
+ } |
+ |
+ Node* lastClosed = serializeNodes<EditingStrategy>(firstNode, pastEnd); |
+ |
+ if (specialCommonAncestor && lastClosed) { |
+ // TODO(hajimehoshi): This is calculated at createMarkupInternal too. |
+ Node* commonAncestor = NodeTraversal::commonAncestor(*m_start.containerNode(), *m_end.containerNode()); |
+ ASSERT(commonAncestor); |
+ HTMLBodyElement* body = toHTMLBodyElement(enclosingElementWithTag(firstPositionInNode(commonAncestor), bodyTag)); |
+ HTMLBodyElement* fullySelectedRoot = nullptr; |
+ // FIXME: Do this for all fully selected blocks, not just the body. |
+ if (body && areSameRanges(body, m_start, m_end)) |
+ fullySelectedRoot = body; |
+ |
+ // Also include all of the ancestors of lastClosed up to this special ancestor. |
+ // FIXME: What is ancestor? |
+ for (ContainerNode* ancestor = NodeTraversal::parent(*lastClosed); ancestor; ancestor = NodeTraversal::parent(*ancestor)) { |
+ if (ancestor == fullySelectedRoot && !convertBlocksToInlines) { |
+ RefPtrWillBeRawPtr<EditingStyle> fullySelectedRootStyle = styleFromMatchedRulesAndInlineDecl(fullySelectedRoot); |
+ |
+ // Bring the background attribute over, but not as an attribute because a background attribute on a div |
+ // appears to have no effect. |
+ if ((!fullySelectedRootStyle || !fullySelectedRootStyle->style() || !fullySelectedRootStyle->style()->getPropertyCSSValue(CSSPropertyBackgroundImage)) |
+ && fullySelectedRoot->hasAttribute(backgroundAttr)) |
+ fullySelectedRootStyle->style()->setProperty(CSSPropertyBackgroundImage, "url('" + fullySelectedRoot->getAttribute(backgroundAttr) + "')"); |
+ |
+ if (fullySelectedRootStyle->style()) { |
+ // Reset the CSS properties to avoid an assertion error in addStyleMarkup(). |
+ // This assertion is caused at least when we select all text of a <body> element whose |
+ // 'text-decoration' property is "inherit", and copy it. |
+ if (!propertyMissingOrEqualToNone(fullySelectedRootStyle->style(), CSSPropertyTextDecoration)) |
+ fullySelectedRootStyle->style()->setProperty(CSSPropertyTextDecoration, CSSValueNone); |
+ if (!propertyMissingOrEqualToNone(fullySelectedRootStyle->style(), CSSPropertyWebkitTextDecorationsInEffect)) |
+ fullySelectedRootStyle->style()->setProperty(CSSPropertyWebkitTextDecorationsInEffect, CSSValueNone); |
+ wrapWithStyleNode(fullySelectedRootStyle->style(), true); |
+ } |
+ } else { |
+ // Since this node and all the other ancestors are not in the selection we want to set RangeFullySelectsNode to DoesNotFullySelectNode |
+ // so that styles that affect the exterior of the node are not included. |
+ wrapWithNode(*ancestor, convertBlocksToInlines, StyledMarkupAccumulator::DoesNotFullySelectNode); |
+ } |
+ |
+ if (ancestor == specialCommonAncestor) |
+ break; |
+ } |
+ } |
+ |
+ // FIXME: The interchange newline should be placed in the block that it's in, not after all of the content, unconditionally. |
+ if (m_markupAccumulator.shouldAnnotateForInterchange() && needInterchangeNewlineAt(visibleEnd)) |
+ m_markupAccumulator.appendString(interchangeNewlineString); |
+ |
+ return takeResults(); |
+} |
+ |
void StyledMarkupSerializer::wrapWithNode(ContainerNode& node, bool convertBlocksToInlines, StyledMarkupAccumulator::RangeFullySelectsNode rangeFullySelectsNode) |
{ |
StringBuilder markup; |
@@ -232,7 +345,7 @@ void StyledMarkupSerializer::wrapWithStyleNode(StylePropertySet* style, bool isB |
StringBuilder openTag; |
m_markupAccumulator.appendStyleNodeOpenTag(openTag, style, isBlock); |
m_reversedPrecedingMarkup.append(openTag.toString()); |
- appendString(styleNodeCloseTag(isBlock)); |
+ m_markupAccumulator.appendString(styleNodeCloseTag(isBlock)); |
} |
String StyledMarkupSerializer::takeResults() |