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 |