| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserv
ed. | 2 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserv
ed. |
| 3 * Copyright (C) 2008, 2009, 2010, 2011 Google Inc. All rights reserved. | 3 * Copyright (C) 2008, 2009, 2010, 2011 Google Inc. All rights reserved. |
| 4 * Copyright (C) 2011 Igalia S.L. | 4 * Copyright (C) 2011 Igalia S.L. |
| 5 * Copyright (C) 2011 Motorola Mobility. All rights reserved. | 5 * Copyright (C) 2011 Motorola Mobility. All rights reserved. |
| 6 * | 6 * |
| 7 * Redistribution and use in source and binary forms, with or without | 7 * Redistribution and use in source and binary forms, with or without |
| 8 * modification, are permitted provided that the following conditions | 8 * modification, are permitted provided that the following conditions |
| 9 * are met: | 9 * are met: |
| 10 * 1. Redistributions of source code must retain the above copyright | 10 * 1. Redistributions of source code must retain the above copyright |
| (...skipping 16 matching lines...) Expand all Loading... |
| 27 */ | 27 */ |
| 28 | 28 |
| 29 #include "config.h" | 29 #include "config.h" |
| 30 #include "core/editing/StyledMarkupSerializer.h" | 30 #include "core/editing/StyledMarkupSerializer.h" |
| 31 | 31 |
| 32 #include "core/css/StylePropertySet.h" | 32 #include "core/css/StylePropertySet.h" |
| 33 #include "core/dom/Document.h" | 33 #include "core/dom/Document.h" |
| 34 #include "core/dom/Element.h" | 34 #include "core/dom/Element.h" |
| 35 #include "core/dom/Text.h" | 35 #include "core/dom/Text.h" |
| 36 #include "core/editing/EditingStyle.h" | 36 #include "core/editing/EditingStyle.h" |
| 37 #include "core/editing/VisibleSelection.h" |
| 38 #include "core/editing/VisibleUnits.h" |
| 37 #include "core/editing/htmlediting.h" | 39 #include "core/editing/htmlediting.h" |
| 38 #include "core/editing/iterators/TextIterator.h" | 40 #include "core/editing/iterators/TextIterator.h" |
| 39 #include "core/editing/markup.h" | 41 #include "core/editing/markup.h" |
| 42 #include "core/html/HTMLBodyElement.h" |
| 40 #include "core/html/HTMLElement.h" | 43 #include "core/html/HTMLElement.h" |
| 41 #include "wtf/text/StringBuilder.h" | 44 #include "wtf/text/StringBuilder.h" |
| 42 | 45 |
| 43 namespace blink { | 46 namespace blink { |
| 44 | 47 |
| 45 namespace { | 48 namespace { |
| 46 | 49 |
| 47 const String& styleNodeCloseTag(bool isBlock) | 50 const String& styleNodeCloseTag(bool isBlock) |
| 48 { | 51 { |
| 49 DEFINE_STATIC_LOCAL(const String, divClose, ("</div>")); | 52 DEFINE_STATIC_LOCAL(const String, divClose, ("</div>")); |
| (...skipping 155 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 205 } | 208 } |
| 206 | 209 |
| 207 bool StyledMarkupAccumulator::shouldApplyWrappingStyle(const Node& node) const | 210 bool StyledMarkupAccumulator::shouldApplyWrappingStyle(const Node& node) const |
| 208 { | 211 { |
| 209 return m_highestNodeToBeSerialized && m_highestNodeToBeSerialized->parentNod
e() == node.parentNode() | 212 return m_highestNodeToBeSerialized && m_highestNodeToBeSerialized->parentNod
e() == node.parentNode() |
| 210 && m_serializer->wrappingStyle() && m_serializer->wrappingStyle()->style
(); | 213 && m_serializer->wrappingStyle() && m_serializer->wrappingStyle()->style
(); |
| 211 } | 214 } |
| 212 | 215 |
| 213 StyledMarkupSerializer::StyledMarkupSerializer(EAbsoluteURLs shouldResolveURLs,
EAnnotateForInterchange shouldAnnotate, const Position& start, const Position& e
nd, Node* highestNodeToBeSerialized) | 216 StyledMarkupSerializer::StyledMarkupSerializer(EAbsoluteURLs shouldResolveURLs,
EAnnotateForInterchange shouldAnnotate, const Position& start, const Position& e
nd, Node* highestNodeToBeSerialized) |
| 214 : m_markupAccumulator(this, shouldResolveURLs, start, end, shouldAnnotate, h
ighestNodeToBeSerialized) | 217 : m_markupAccumulator(this, shouldResolveURLs, start, end, shouldAnnotate, h
ighestNodeToBeSerialized) |
| 218 , m_start(start) |
| 219 , m_end(end) |
| 215 { | 220 { |
| 216 } | 221 } |
| 217 | 222 |
| 223 static bool needInterchangeNewlineAfter(const VisiblePosition& v) |
| 224 { |
| 225 VisiblePosition next = v.next(); |
| 226 Node* upstreamNode = next.deepEquivalent().upstream().deprecatedNode(); |
| 227 Node* downstreamNode = v.deepEquivalent().downstream().deprecatedNode(); |
| 228 // Add an interchange newline if a paragraph break is selected and a br won'
t already be added to the markup to represent it. |
| 229 return isEndOfParagraph(v) && isStartOfParagraph(next) && !(isHTMLBRElement(
*upstreamNode) && upstreamNode == downstreamNode); |
| 230 } |
| 231 |
| 232 static bool needInterchangeNewlineAt(const VisiblePosition& v) |
| 233 { |
| 234 // FIXME: |v.previous()| works on a DOM tree. We need to fix this to work on |
| 235 // a composed tree. |
| 236 return needInterchangeNewlineAfter(v.previous()); |
| 237 } |
| 238 |
| 239 static bool areSameRanges(Node* node, const Position& startPosition, const Posit
ion& endPosition) |
| 240 { |
| 241 ASSERT(node); |
| 242 Position otherStartPosition; |
| 243 Position otherEndPosition; |
| 244 VisibleSelection::selectionFromContentsOfNode(node).toNormalizedPositions(ot
herStartPosition, otherEndPosition); |
| 245 return startPosition == otherStartPosition && endPosition == otherEndPositio
n; |
| 246 } |
| 247 |
| 248 static PassRefPtrWillBeRawPtr<EditingStyle> styleFromMatchedRulesAndInlineDecl(c
onst HTMLElement* element) |
| 249 { |
| 250 RefPtrWillBeRawPtr<EditingStyle> style = EditingStyle::create(element->inlin
eStyle()); |
| 251 // FIXME: Having to const_cast here is ugly, but it is quite a bit of work t
o untangle |
| 252 // the non-const-ness of styleFromMatchedRulesForElement. |
| 253 style->mergeStyleFromRules(const_cast<HTMLElement*>(element)); |
| 254 return style.release(); |
| 255 } |
| 256 |
| 257 String StyledMarkupSerializer::createMarkup(bool convertBlocksToInlines, Node* s
pecialCommonAncestor) |
| 258 { |
| 259 DEFINE_STATIC_LOCAL(const String, interchangeNewlineString, ("<br class=\""
AppleInterchangeNewline "\">")); |
| 260 |
| 261 Node* pastEnd = m_end.nodeAsRangePastLastNode(); |
| 262 |
| 263 Node* firstNode = m_start.nodeAsRangeFirstNode(); |
| 264 VisiblePosition visibleStart(m_start, VP_DEFAULT_AFFINITY); |
| 265 VisiblePosition visibleEnd(m_end, VP_DEFAULT_AFFINITY); |
| 266 if (m_markupAccumulator.shouldAnnotateForInterchange() && needInterchangeNew
lineAfter(visibleStart)) { |
| 267 if (visibleStart == visibleEnd.previous()) |
| 268 return interchangeNewlineString; |
| 269 |
| 270 m_markupAccumulator.appendString(interchangeNewlineString); |
| 271 firstNode = visibleStart.next().deepEquivalent().deprecatedNode(); |
| 272 |
| 273 if (pastEnd && Position::beforeNode(firstNode).compareTo(Position::befor
eNode(pastEnd)) >= 0) { |
| 274 // This condition hits in editing/pasteboard/copy-display-none.html. |
| 275 return interchangeNewlineString; |
| 276 } |
| 277 } |
| 278 |
| 279 Node* lastClosed = serializeNodes<EditingStrategy>(firstNode, pastEnd); |
| 280 |
| 281 if (specialCommonAncestor && lastClosed) { |
| 282 // TODO(hajimehoshi): This is calculated at createMarkupInternal too. |
| 283 Node* commonAncestor = NodeTraversal::commonAncestor(*m_start.containerN
ode(), *m_end.containerNode()); |
| 284 ASSERT(commonAncestor); |
| 285 HTMLBodyElement* body = toHTMLBodyElement(enclosingElementWithTag(firstP
ositionInNode(commonAncestor), bodyTag)); |
| 286 HTMLBodyElement* fullySelectedRoot = nullptr; |
| 287 // FIXME: Do this for all fully selected blocks, not just the body. |
| 288 if (body && areSameRanges(body, m_start, m_end)) |
| 289 fullySelectedRoot = body; |
| 290 |
| 291 // Also include all of the ancestors of lastClosed up to this special an
cestor. |
| 292 // FIXME: What is ancestor? |
| 293 for (ContainerNode* ancestor = NodeTraversal::parent(*lastClosed); ances
tor; ancestor = NodeTraversal::parent(*ancestor)) { |
| 294 if (ancestor == fullySelectedRoot && !convertBlocksToInlines) { |
| 295 RefPtrWillBeRawPtr<EditingStyle> fullySelectedRootStyle = styleF
romMatchedRulesAndInlineDecl(fullySelectedRoot); |
| 296 |
| 297 // Bring the background attribute over, but not as an attribute
because a background attribute on a div |
| 298 // appears to have no effect. |
| 299 if ((!fullySelectedRootStyle || !fullySelectedRootStyle->style()
|| !fullySelectedRootStyle->style()->getPropertyCSSValue(CSSPropertyBackgroundI
mage)) |
| 300 && fullySelectedRoot->hasAttribute(backgroundAttr)) |
| 301 fullySelectedRootStyle->style()->setProperty(CSSPropertyBack
groundImage, "url('" + fullySelectedRoot->getAttribute(backgroundAttr) + "')"); |
| 302 |
| 303 if (fullySelectedRootStyle->style()) { |
| 304 // Reset the CSS properties to avoid an assertion error in a
ddStyleMarkup(). |
| 305 // This assertion is caused at least when we select all text
of a <body> element whose |
| 306 // 'text-decoration' property is "inherit", and copy it. |
| 307 if (!propertyMissingOrEqualToNone(fullySelectedRootStyle->st
yle(), CSSPropertyTextDecoration)) |
| 308 fullySelectedRootStyle->style()->setProperty(CSSProperty
TextDecoration, CSSValueNone); |
| 309 if (!propertyMissingOrEqualToNone(fullySelectedRootStyle->st
yle(), CSSPropertyWebkitTextDecorationsInEffect)) |
| 310 fullySelectedRootStyle->style()->setProperty(CSSProperty
WebkitTextDecorationsInEffect, CSSValueNone); |
| 311 wrapWithStyleNode(fullySelectedRootStyle->style(), true); |
| 312 } |
| 313 } else { |
| 314 // Since this node and all the other ancestors are not in the se
lection we want to set RangeFullySelectsNode to DoesNotFullySelectNode |
| 315 // so that styles that affect the exterior of the node are not i
ncluded. |
| 316 wrapWithNode(*ancestor, convertBlocksToInlines, StyledMarkupAccu
mulator::DoesNotFullySelectNode); |
| 317 } |
| 318 |
| 319 if (ancestor == specialCommonAncestor) |
| 320 break; |
| 321 } |
| 322 } |
| 323 |
| 324 // FIXME: The interchange newline should be placed in the block that it's in
, not after all of the content, unconditionally. |
| 325 if (m_markupAccumulator.shouldAnnotateForInterchange() && needInterchangeNew
lineAt(visibleEnd)) |
| 326 m_markupAccumulator.appendString(interchangeNewlineString); |
| 327 |
| 328 return takeResults(); |
| 329 } |
| 330 |
| 218 void StyledMarkupSerializer::wrapWithNode(ContainerNode& node, bool convertBlock
sToInlines, StyledMarkupAccumulator::RangeFullySelectsNode rangeFullySelectsNode
) | 331 void StyledMarkupSerializer::wrapWithNode(ContainerNode& node, bool convertBlock
sToInlines, StyledMarkupAccumulator::RangeFullySelectsNode rangeFullySelectsNode
) |
| 219 { | 332 { |
| 220 StringBuilder markup; | 333 StringBuilder markup; |
| 221 if (node.isElementNode()) | 334 if (node.isElementNode()) |
| 222 m_markupAccumulator.appendElement(markup, toElement(node), convertBlocks
ToInlines && isBlock(&node), rangeFullySelectsNode); | 335 m_markupAccumulator.appendElement(markup, toElement(node), convertBlocks
ToInlines && isBlock(&node), rangeFullySelectsNode); |
| 223 else | 336 else |
| 224 m_markupAccumulator.appendStartMarkup(markup, node, 0); | 337 m_markupAccumulator.appendStartMarkup(markup, node, 0); |
| 225 m_reversedPrecedingMarkup.append(markup.toString()); | 338 m_reversedPrecedingMarkup.append(markup.toString()); |
| 226 if (node.isElementNode()) | 339 if (node.isElementNode()) |
| 227 m_markupAccumulator.appendEndTag(toElement(node)); | 340 m_markupAccumulator.appendEndTag(toElement(node)); |
| 228 } | 341 } |
| 229 | 342 |
| 230 void StyledMarkupSerializer::wrapWithStyleNode(StylePropertySet* style, bool isB
lock) | 343 void StyledMarkupSerializer::wrapWithStyleNode(StylePropertySet* style, bool isB
lock) |
| 231 { | 344 { |
| 232 StringBuilder openTag; | 345 StringBuilder openTag; |
| 233 m_markupAccumulator.appendStyleNodeOpenTag(openTag, style, isBlock); | 346 m_markupAccumulator.appendStyleNodeOpenTag(openTag, style, isBlock); |
| 234 m_reversedPrecedingMarkup.append(openTag.toString()); | 347 m_reversedPrecedingMarkup.append(openTag.toString()); |
| 235 appendString(styleNodeCloseTag(isBlock)); | 348 m_markupAccumulator.appendString(styleNodeCloseTag(isBlock)); |
| 236 } | 349 } |
| 237 | 350 |
| 238 String StyledMarkupSerializer::takeResults() | 351 String StyledMarkupSerializer::takeResults() |
| 239 { | 352 { |
| 240 StringBuilder result; | 353 StringBuilder result; |
| 241 result.reserveCapacity(MarkupAccumulator::totalLength(m_reversedPrecedingMar
kup) + m_markupAccumulator.length()); | 354 result.reserveCapacity(MarkupAccumulator::totalLength(m_reversedPrecedingMar
kup) + m_markupAccumulator.length()); |
| 242 | 355 |
| 243 for (size_t i = m_reversedPrecedingMarkup.size(); i > 0; --i) | 356 for (size_t i = m_reversedPrecedingMarkup.size(); i > 0; --i) |
| 244 result.append(m_reversedPrecedingMarkup[i - 1]); | 357 result.append(m_reversedPrecedingMarkup[i - 1]); |
| 245 | 358 |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 348 } | 461 } |
| 349 } | 462 } |
| 350 } | 463 } |
| 351 | 464 |
| 352 return lastClosed; | 465 return lastClosed; |
| 353 } | 466 } |
| 354 | 467 |
| 355 template Node* StyledMarkupSerializer::serializeNodes<EditingStrategy>(Node* sta
rtNode, Node* endNode); | 468 template Node* StyledMarkupSerializer::serializeNodes<EditingStrategy>(Node* sta
rtNode, Node* endNode); |
| 356 | 469 |
| 357 } // namespace blink | 470 } // namespace blink |
| OLD | NEW |