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 |