| OLD | NEW |
| (Empty) |
| 1 /* | |
| 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. | |
| 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 COMPUTER, 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 COMPUTER, 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 "core/editing/StyledMarkupSerializer.h" | |
| 31 | |
| 32 #include "core/css/StylePropertySet.h" | |
| 33 #include "core/dom/Document.h" | |
| 34 #include "core/dom/Element.h" | |
| 35 #include "core/dom/Text.h" | |
| 36 #include "core/dom/shadow/ElementShadow.h" | |
| 37 #include "core/editing/EditingStyle.h" | |
| 38 #include "core/editing/EditingUtilities.h" | |
| 39 #include "core/editing/Serialization.h" | |
| 40 #include "core/editing/VisibleSelection.h" | |
| 41 #include "core/editing/VisibleUnits.h" | |
| 42 #include "core/editing/iterators/TextIterator.h" | |
| 43 #include "core/html/HTMLBodyElement.h" | |
| 44 #include "core/html/HTMLElement.h" | |
| 45 #include "wtf/text/StringBuilder.h" | |
| 46 | |
| 47 namespace blink { | |
| 48 | |
| 49 namespace { | |
| 50 | |
| 51 template<typename Strategy> | |
| 52 TextOffset toTextOffset(const PositionAlgorithm<Strategy>& position) | |
| 53 { | |
| 54 if (position.isNull()) | |
| 55 return TextOffset(); | |
| 56 | |
| 57 if (!position.computeContainerNode()->isTextNode()) | |
| 58 return TextOffset(); | |
| 59 | |
| 60 return TextOffset(toText(position.computeContainerNode()), position.offsetIn
ContainerNode()); | |
| 61 } | |
| 62 | |
| 63 template<typename EditingStrategy> | |
| 64 static bool handleSelectionBoundary(const Node&); | |
| 65 | |
| 66 template<> | |
| 67 bool handleSelectionBoundary<EditingStrategy>(const Node&) | |
| 68 { | |
| 69 return false; | |
| 70 } | |
| 71 | |
| 72 template<> | |
| 73 bool handleSelectionBoundary<EditingInComposedTreeStrategy>(const Node& node) | |
| 74 { | |
| 75 if (!node.isElementNode()) | |
| 76 return false; | |
| 77 ElementShadow* shadow = toElement(node).shadow(); | |
| 78 if (!shadow) | |
| 79 return false; | |
| 80 return shadow->youngestShadowRoot()->type() == ShadowRootType::UserAgent; | |
| 81 } | |
| 82 | |
| 83 } // namespace | |
| 84 | |
| 85 using namespace HTMLNames; | |
| 86 | |
| 87 template<typename Strategy> | |
| 88 class StyledMarkupTraverser { | |
| 89 WTF_MAKE_NONCOPYABLE(StyledMarkupTraverser); | |
| 90 STACK_ALLOCATED(); | |
| 91 public: | |
| 92 StyledMarkupTraverser(); | |
| 93 StyledMarkupTraverser(StyledMarkupAccumulator*, Node*); | |
| 94 | |
| 95 Node* traverse(Node*, Node*); | |
| 96 void wrapWithNode(ContainerNode&, PassRefPtrWillBeRawPtr<EditingStyle>); | |
| 97 RefPtrWillBeRawPtr<EditingStyle> createInlineStyleIfNeeded(Node&); | |
| 98 | |
| 99 private: | |
| 100 bool shouldAnnotate() const; | |
| 101 bool convertBlocksToInlines() const; | |
| 102 void appendStartMarkup(Node&); | |
| 103 void appendEndMarkup(Node&); | |
| 104 RefPtrWillBeRawPtr<EditingStyle> createInlineStyle(Element&); | |
| 105 bool needsInlineStyle(const Element&); | |
| 106 bool shouldApplyWrappingStyle(const Node&) const; | |
| 107 | |
| 108 StyledMarkupAccumulator* m_accumulator; | |
| 109 RefPtrWillBeMember<Node> m_lastClosed; | |
| 110 RefPtrWillBeMember<EditingStyle> m_wrappingStyle; | |
| 111 }; | |
| 112 | |
| 113 template<typename Strategy> | |
| 114 bool StyledMarkupTraverser<Strategy>::shouldAnnotate() const | |
| 115 { | |
| 116 return m_accumulator->shouldAnnotate(); | |
| 117 } | |
| 118 | |
| 119 template<typename Strategy> | |
| 120 bool StyledMarkupTraverser<Strategy>::convertBlocksToInlines() const | |
| 121 { | |
| 122 return m_accumulator->convertBlocksToInlines(); | |
| 123 } | |
| 124 | |
| 125 template<typename Strategy> | |
| 126 StyledMarkupSerializer<Strategy>::StyledMarkupSerializer(EAbsoluteURLs shouldRes
olveURLs, EAnnotateForInterchange shouldAnnotate, const PositionAlgorithm<Strate
gy>& start, const PositionAlgorithm<Strategy>& end, Node* highestNodeToBeSeriali
zed, ConvertBlocksToInlines convertBlocksToInlines) | |
| 127 : m_start(start) | |
| 128 , m_end(end) | |
| 129 , m_shouldResolveURLs(shouldResolveURLs) | |
| 130 , m_shouldAnnotate(shouldAnnotate) | |
| 131 , m_highestNodeToBeSerialized(highestNodeToBeSerialized) | |
| 132 , m_convertBlocksToInlines(convertBlocksToInlines) | |
| 133 , m_lastClosed(highestNodeToBeSerialized) | |
| 134 { | |
| 135 } | |
| 136 | |
| 137 static bool needInterchangeNewlineAfter(const VisiblePosition& v) | |
| 138 { | |
| 139 VisiblePosition next = v.next(); | |
| 140 Node* upstreamNode = next.deepEquivalent().upstream().anchorNode(); | |
| 141 Node* downstreamNode = v.deepEquivalent().downstream().anchorNode(); | |
| 142 // Add an interchange newline if a paragraph break is selected and a br won'
t already be added to the markup to represent it. | |
| 143 return isEndOfParagraph(v) && isStartOfParagraph(next) && !(isHTMLBRElement(
*upstreamNode) && upstreamNode == downstreamNode); | |
| 144 } | |
| 145 | |
| 146 static bool needInterchangeNewlineAt(const VisiblePosition& v) | |
| 147 { | |
| 148 // FIXME: |v.previous()| works on a DOM tree. We need to fix this to work on | |
| 149 // a composed tree. | |
| 150 return needInterchangeNewlineAfter(v.previous()); | |
| 151 } | |
| 152 | |
| 153 template<typename Strategy> | |
| 154 static bool areSameRanges(Node* node, const PositionAlgorithm<Strategy>& startPo
sition, const PositionAlgorithm<Strategy>& endPosition) | |
| 155 { | |
| 156 ASSERT(node); | |
| 157 const EphemeralRange range = VisibleSelection::selectionFromContentsOfNode(n
ode).toNormalizedEphemeralRange(); | |
| 158 return toPositionInDOMTree(startPosition) == range.startPosition() && toPosi
tionInDOMTree(endPosition) == range.endPosition(); | |
| 159 } | |
| 160 | |
| 161 static PassRefPtrWillBeRawPtr<EditingStyle> styleFromMatchedRulesAndInlineDecl(c
onst HTMLElement* element) | |
| 162 { | |
| 163 RefPtrWillBeRawPtr<EditingStyle> style = EditingStyle::create(element->inlin
eStyle()); | |
| 164 // FIXME: Having to const_cast here is ugly, but it is quite a bit of work t
o untangle | |
| 165 // the non-const-ness of styleFromMatchedRulesForElement. | |
| 166 style->mergeStyleFromRules(const_cast<HTMLElement*>(element)); | |
| 167 return style.release(); | |
| 168 } | |
| 169 | |
| 170 template<typename Strategy> | |
| 171 String StyledMarkupSerializer<Strategy>::createMarkup() | |
| 172 { | |
| 173 StyledMarkupAccumulator markupAccumulator(m_shouldResolveURLs, toTextOffset(
m_start.parentAnchoredEquivalent()), toTextOffset(m_end.parentAnchoredEquivalent
()), m_start.document(), m_shouldAnnotate, m_convertBlocksToInlines); | |
| 174 | |
| 175 Node* pastEnd = m_end.nodeAsRangePastLastNode(); | |
| 176 | |
| 177 Node* firstNode = m_start.nodeAsRangeFirstNode(); | |
| 178 VisiblePosition visibleStart(m_start); | |
| 179 VisiblePosition visibleEnd(m_end); | |
| 180 if (shouldAnnotate() && needInterchangeNewlineAfter(visibleStart)) { | |
| 181 markupAccumulator.appendInterchangeNewline(); | |
| 182 if (visibleStart.deepEquivalent() == visibleEnd.previous().deepEquivalen
t()) | |
| 183 return markupAccumulator.takeResults(); | |
| 184 | |
| 185 firstNode = visibleStart.next().deepEquivalent().anchorNode(); | |
| 186 | |
| 187 if (pastEnd && PositionAlgorithm<Strategy>::beforeNode(firstNode).compar
eTo(PositionAlgorithm<Strategy>::beforeNode(pastEnd)) >= 0) { | |
| 188 // This condition hits in editing/pasteboard/copy-display-none.html. | |
| 189 return markupAccumulator.takeResults(); | |
| 190 } | |
| 191 } | |
| 192 | |
| 193 if (!m_lastClosed) | |
| 194 m_lastClosed = StyledMarkupTraverser<Strategy>().traverse(firstNode, pas
tEnd); | |
| 195 StyledMarkupTraverser<Strategy> traverser(&markupAccumulator, m_lastClosed); | |
| 196 Node* lastClosed = traverser.traverse(firstNode, pastEnd); | |
| 197 | |
| 198 if (m_highestNodeToBeSerialized && lastClosed) { | |
| 199 // TODO(hajimehoshi): This is calculated at createMarkupInternal too. | |
| 200 Node* commonAncestor = Strategy::commonAncestor(*m_start.computeContaine
rNode(), *m_end.computeContainerNode()); | |
| 201 ASSERT(commonAncestor); | |
| 202 HTMLBodyElement* body = toHTMLBodyElement(enclosingElementWithTag(firstP
ositionInNode(commonAncestor), bodyTag)); | |
| 203 HTMLBodyElement* fullySelectedRoot = nullptr; | |
| 204 // FIXME: Do this for all fully selected blocks, not just the body. | |
| 205 if (body && areSameRanges(body, m_start, m_end)) | |
| 206 fullySelectedRoot = body; | |
| 207 | |
| 208 // Also include all of the ancestors of lastClosed up to this special an
cestor. | |
| 209 // FIXME: What is ancestor? | |
| 210 for (ContainerNode* ancestor = Strategy::parent(*lastClosed); ancestor;
ancestor = Strategy::parent(*ancestor)) { | |
| 211 if (ancestor == fullySelectedRoot && !markupAccumulator.convertBlock
sToInlines()) { | |
| 212 RefPtrWillBeRawPtr<EditingStyle> fullySelectedRootStyle = styleF
romMatchedRulesAndInlineDecl(fullySelectedRoot); | |
| 213 | |
| 214 // Bring the background attribute over, but not as an attribute
because a background attribute on a div | |
| 215 // appears to have no effect. | |
| 216 if ((!fullySelectedRootStyle || !fullySelectedRootStyle->style()
|| !fullySelectedRootStyle->style()->getPropertyCSSValue(CSSPropertyBackgroundI
mage)) | |
| 217 && fullySelectedRoot->hasAttribute(backgroundAttr)) | |
| 218 fullySelectedRootStyle->style()->setProperty(CSSPropertyBack
groundImage, "url('" + fullySelectedRoot->getAttribute(backgroundAttr) + "')"); | |
| 219 | |
| 220 if (fullySelectedRootStyle->style()) { | |
| 221 // Reset the CSS properties to avoid an assertion error in a
ddStyleMarkup(). | |
| 222 // This assertion is caused at least when we select all text
of a <body> element whose | |
| 223 // 'text-decoration' property is "inherit", and copy it. | |
| 224 if (!propertyMissingOrEqualToNone(fullySelectedRootStyle->st
yle(), CSSPropertyTextDecoration)) | |
| 225 fullySelectedRootStyle->style()->setProperty(CSSProperty
TextDecoration, CSSValueNone); | |
| 226 if (!propertyMissingOrEqualToNone(fullySelectedRootStyle->st
yle(), CSSPropertyWebkitTextDecorationsInEffect)) | |
| 227 fullySelectedRootStyle->style()->setProperty(CSSProperty
WebkitTextDecorationsInEffect, CSSValueNone); | |
| 228 markupAccumulator.wrapWithStyleNode(fullySelectedRootStyle->
style()); | |
| 229 } | |
| 230 } else { | |
| 231 RefPtrWillBeRawPtr<EditingStyle> style = traverser.createInlineS
tyleIfNeeded(*ancestor); | |
| 232 // Since this node and all the other ancestors are not in the se
lection we want | |
| 233 // styles that affect the exterior of the node not to be not inc
luded. | |
| 234 // 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 | |
| 235 // only the ones that affect it and the nodes within it. | |
| 236 if (style && style->style()) | |
| 237 style->style()->removeProperty(CSSPropertyFloat); | |
| 238 traverser.wrapWithNode(*ancestor, style); | |
| 239 } | |
| 240 | |
| 241 if (ancestor == m_highestNodeToBeSerialized) | |
| 242 break; | |
| 243 } | |
| 244 } | |
| 245 | |
| 246 // FIXME: The interchange newline should be placed in the block that it's in
, not after all of the content, unconditionally. | |
| 247 if (shouldAnnotate() && needInterchangeNewlineAt(visibleEnd)) | |
| 248 markupAccumulator.appendInterchangeNewline(); | |
| 249 | |
| 250 return markupAccumulator.takeResults(); | |
| 251 } | |
| 252 | |
| 253 template<typename Strategy> | |
| 254 StyledMarkupTraverser<Strategy>::StyledMarkupTraverser() | |
| 255 : StyledMarkupTraverser(nullptr, nullptr) | |
| 256 { | |
| 257 } | |
| 258 | |
| 259 template<typename Strategy> | |
| 260 StyledMarkupTraverser<Strategy>::StyledMarkupTraverser(StyledMarkupAccumulator*
accumulator, Node* lastClosed) | |
| 261 : m_accumulator(accumulator) | |
| 262 , m_lastClosed(lastClosed) | |
| 263 , m_wrappingStyle(nullptr) | |
| 264 { | |
| 265 if (!m_accumulator) { | |
| 266 ASSERT(!m_lastClosed); | |
| 267 return; | |
| 268 } | |
| 269 if (!m_lastClosed) | |
| 270 return; | |
| 271 ContainerNode* parent = Strategy::parent(*m_lastClosed); | |
| 272 if (!parent) | |
| 273 return; | |
| 274 if (shouldAnnotate()) { | |
| 275 m_wrappingStyle = EditingStyle::wrappingStyleForAnnotatedSerialization(p
arent); | |
| 276 return; | |
| 277 } | |
| 278 m_wrappingStyle = EditingStyle::wrappingStyleForSerialization(parent); | |
| 279 } | |
| 280 | |
| 281 template<typename Strategy> | |
| 282 Node* StyledMarkupTraverser<Strategy>::traverse(Node* startNode, Node* pastEnd) | |
| 283 { | |
| 284 WillBeHeapVector<RawPtrWillBeMember<ContainerNode>> ancestorsToClose; | |
| 285 Node* next; | |
| 286 Node* lastClosed = nullptr; | |
| 287 for (Node* n = startNode; n && n != pastEnd; n = next) { | |
| 288 // If |n| is a selection boundary such as <input>, traverse the child | |
| 289 // nodes in the DOM tree instead of the composed tree. | |
| 290 if (handleSelectionBoundary<Strategy>(*n)) { | |
| 291 lastClosed = StyledMarkupTraverser<EditingStrategy>(m_accumulator, m
_lastClosed.get()).traverse(n, EditingStrategy::nextSkippingChildren(*n)); | |
| 292 next = EditingInComposedTreeStrategy::nextSkippingChildren(*n); | |
| 293 } else { | |
| 294 next = Strategy::next(*n); | |
| 295 if (isBlock(n) && canHaveChildrenForEditing(n) && next == pastEnd) { | |
| 296 // Don't write out empty block containers that aren't fully sele
cted. | |
| 297 continue; | |
| 298 } | |
| 299 | |
| 300 if (!n->layoutObject() && !enclosingElementWithTag(firstPositionInOr
BeforeNode(n), selectTag)) { | |
| 301 next = Strategy::nextSkippingChildren(*n); | |
| 302 // Don't skip over pastEnd. | |
| 303 if (pastEnd && Strategy::isDescendantOf(*pastEnd, *n)) | |
| 304 next = pastEnd; | |
| 305 } else { | |
| 306 // Add the node to the markup if we're not skipping the descenda
nts | |
| 307 appendStartMarkup(*n); | |
| 308 | |
| 309 // If node has no children, close the tag now. | |
| 310 if (Strategy::hasChildren(*n)) { | |
| 311 ancestorsToClose.append(toContainerNode(n)); | |
| 312 continue; | |
| 313 } | |
| 314 appendEndMarkup(*n); | |
| 315 lastClosed = n; | |
| 316 } | |
| 317 } | |
| 318 | |
| 319 // 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. | |
| 320 // FIXME: What happens if we just inserted open tag and reached the end? | |
| 321 if (Strategy::nextSibling(*n) && next != pastEnd) | |
| 322 continue; | |
| 323 | |
| 324 // Close up the ancestors. | |
| 325 while (!ancestorsToClose.isEmpty()) { | |
| 326 ContainerNode* ancestor = ancestorsToClose.last(); | |
| 327 ASSERT(ancestor); | |
| 328 if (next && next != pastEnd && Strategy::isDescendantOf(*next, *ance
stor)) | |
| 329 break; | |
| 330 // Not at the end of the range, close ancestors up to sibling of nex
t node. | |
| 331 appendEndMarkup(*ancestor); | |
| 332 lastClosed = ancestor; | |
| 333 ancestorsToClose.removeLast(); | |
| 334 } | |
| 335 | |
| 336 // Surround the currently accumulated markup with markup for ancestors w
e never opened as we leave the subtree(s) rooted at those ancestors. | |
| 337 ContainerNode* nextParent = next ? Strategy::parent(*next) : nullptr; | |
| 338 if (next == pastEnd || n == nextParent) | |
| 339 continue; | |
| 340 | |
| 341 ASSERT(n); | |
| 342 Node* lastAncestorClosedOrSelf = (lastClosed && Strategy::isDescendantOf
(*n, *lastClosed)) ? lastClosed : n; | |
| 343 for (ContainerNode* parent = Strategy::parent(*lastAncestorClosedOrSelf)
; parent && parent != nextParent; parent = Strategy::parent(*parent)) { | |
| 344 // All ancestors that aren't in the ancestorsToClose list should eit
her be a) unrendered: | |
| 345 if (!parent->layoutObject()) | |
| 346 continue; | |
| 347 // or b) ancestors that we never encountered during a pre-order trav
ersal starting at startNode: | |
| 348 ASSERT(startNode); | |
| 349 ASSERT(Strategy::isDescendantOf(*startNode, *parent)); | |
| 350 RefPtrWillBeRawPtr<EditingStyle> style = createInlineStyleIfNeeded(*
parent); | |
| 351 wrapWithNode(*parent, style); | |
| 352 lastClosed = parent; | |
| 353 } | |
| 354 } | |
| 355 | |
| 356 return lastClosed; | |
| 357 } | |
| 358 | |
| 359 template<typename Strategy> | |
| 360 bool StyledMarkupTraverser<Strategy>::needsInlineStyle(const Element& element) | |
| 361 { | |
| 362 if (!element.isHTMLElement()) | |
| 363 return false; | |
| 364 if (shouldAnnotate()) | |
| 365 return true; | |
| 366 return convertBlocksToInlines() && isBlock(&element); | |
| 367 } | |
| 368 | |
| 369 template<typename Strategy> | |
| 370 void StyledMarkupTraverser<Strategy>::wrapWithNode(ContainerNode& node, PassRefP
trWillBeRawPtr<EditingStyle> style) | |
| 371 { | |
| 372 if (!m_accumulator) | |
| 373 return; | |
| 374 StringBuilder markup; | |
| 375 if (node.isDocumentNode()) { | |
| 376 MarkupFormatter::appendXMLDeclaration(markup, toDocument(node)); | |
| 377 m_accumulator->pushMarkup(markup.toString()); | |
| 378 return; | |
| 379 } | |
| 380 if (!node.isElementNode()) | |
| 381 return; | |
| 382 Element& element = toElement(node); | |
| 383 if (shouldApplyWrappingStyle(element) || needsInlineStyle(element)) | |
| 384 m_accumulator->appendElementWithInlineStyle(markup, element, style); | |
| 385 else | |
| 386 m_accumulator->appendElement(markup, element); | |
| 387 m_accumulator->pushMarkup(markup.toString()); | |
| 388 m_accumulator->appendEndTag(toElement(node)); | |
| 389 } | |
| 390 | |
| 391 template<typename Strategy> | |
| 392 RefPtrWillBeRawPtr<EditingStyle> StyledMarkupTraverser<Strategy>::createInlineSt
yleIfNeeded(Node& node) | |
| 393 { | |
| 394 if (!m_accumulator) | |
| 395 return nullptr; | |
| 396 if (!node.isElementNode()) | |
| 397 return nullptr; | |
| 398 RefPtrWillBeRawPtr<EditingStyle> inlineStyle = createInlineStyle(toElement(n
ode)); | |
| 399 if (convertBlocksToInlines() && isBlock(&node)) | |
| 400 inlineStyle->forceInline(); | |
| 401 return inlineStyle; | |
| 402 } | |
| 403 | |
| 404 template<typename Strategy> | |
| 405 void StyledMarkupTraverser<Strategy>::appendStartMarkup(Node& node) | |
| 406 { | |
| 407 if (!m_accumulator) | |
| 408 return; | |
| 409 switch (node.nodeType()) { | |
| 410 case Node::TEXT_NODE: { | |
| 411 Text& text = toText(node); | |
| 412 if (text.parentElement() && isHTMLTextAreaElement(text.parentElement()))
{ | |
| 413 m_accumulator->appendText(text); | |
| 414 break; | |
| 415 } | |
| 416 RefPtrWillBeRawPtr<EditingStyle> inlineStyle = nullptr; | |
| 417 if (shouldApplyWrappingStyle(text)) { | |
| 418 inlineStyle = m_wrappingStyle->copy(); | |
| 419 // FIXME: <rdar://problem/5371536> Style rules that match pasted con
tent can change it's appearance | |
| 420 // Make sure spans are inline style in paste side e.g. span { displa
y: block }. | |
| 421 inlineStyle->forceInline(); | |
| 422 // FIXME: Should this be included in forceInline? | |
| 423 inlineStyle->style()->setProperty(CSSPropertyFloat, CSSValueNone); | |
| 424 } | |
| 425 m_accumulator->appendTextWithInlineStyle(text, inlineStyle); | |
| 426 break; | |
| 427 } | |
| 428 case Node::ELEMENT_NODE: { | |
| 429 Element& element = toElement(node); | |
| 430 if ((element.isHTMLElement() && shouldAnnotate()) || shouldApplyWrapping
Style(element)) { | |
| 431 RefPtrWillBeRawPtr<EditingStyle> inlineStyle = createInlineStyle(ele
ment); | |
| 432 m_accumulator->appendElementWithInlineStyle(element, inlineStyle); | |
| 433 break; | |
| 434 } | |
| 435 m_accumulator->appendElement(element); | |
| 436 break; | |
| 437 } | |
| 438 default: | |
| 439 m_accumulator->appendStartMarkup(node); | |
| 440 break; | |
| 441 } | |
| 442 } | |
| 443 | |
| 444 template<typename Strategy> | |
| 445 void StyledMarkupTraverser<Strategy>::appendEndMarkup(Node& node) | |
| 446 { | |
| 447 if (!m_accumulator || !node.isElementNode()) | |
| 448 return; | |
| 449 m_accumulator->appendEndTag(toElement(node)); | |
| 450 } | |
| 451 | |
| 452 template<typename Strategy> | |
| 453 bool StyledMarkupTraverser<Strategy>::shouldApplyWrappingStyle(const Node& node)
const | |
| 454 { | |
| 455 return m_lastClosed && Strategy::parent(*m_lastClosed) == Strategy::parent(n
ode) | |
| 456 && m_wrappingStyle && m_wrappingStyle->style(); | |
| 457 } | |
| 458 | |
| 459 template<typename Strategy> | |
| 460 RefPtrWillBeRawPtr<EditingStyle> StyledMarkupTraverser<Strategy>::createInlineSt
yle(Element& element) | |
| 461 { | |
| 462 RefPtrWillBeRawPtr<EditingStyle> inlineStyle = nullptr; | |
| 463 | |
| 464 if (shouldApplyWrappingStyle(element)) { | |
| 465 inlineStyle = m_wrappingStyle->copy(); | |
| 466 inlineStyle->removePropertiesInElementDefaultStyle(&element); | |
| 467 inlineStyle->removeStyleConflictingWithStyleOfElement(&element); | |
| 468 } else { | |
| 469 inlineStyle = EditingStyle::create(); | |
| 470 } | |
| 471 | |
| 472 if (element.isStyledElement() && element.inlineStyle()) | |
| 473 inlineStyle->overrideWithStyle(element.inlineStyle()); | |
| 474 | |
| 475 if (element.isHTMLElement() && shouldAnnotate()) | |
| 476 inlineStyle->mergeStyleFromRulesForSerialization(&toHTMLElement(element)
); | |
| 477 | |
| 478 return inlineStyle; | |
| 479 } | |
| 480 | |
| 481 template class StyledMarkupSerializer<EditingStrategy>; | |
| 482 template class StyledMarkupSerializer<EditingInComposedTreeStrategy>; | |
| 483 | |
| 484 } // namespace blink | |
| OLD | NEW |