OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2005, 2006, 2008, 2009 Apple Inc. All rights reserved. | 2 * Copyright (C) 2005, 2006, 2008, 2009 Apple Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
6 * are met: | 6 * are met: |
7 * 1. Redistributions of source code must retain the above copyright | 7 * 1. Redistributions of source code must retain the above copyright |
8 * notice, this list of conditions and the following disclaimer. | 8 * notice, this list of conditions and the following disclaimer. |
9 * 2. Redistributions in binary form must reproduce the above copyright | 9 * 2. Redistributions in binary form must reproduce the above copyright |
10 * notice, this list of conditions and the following disclaimer in the | 10 * notice, this list of conditions and the following disclaimer in the |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
121 PassRefPtrWillBeRawPtr<HTMLSpanElement> createStyleSpanElement(Document& documen
t) | 121 PassRefPtrWillBeRawPtr<HTMLSpanElement> createStyleSpanElement(Document& documen
t) |
122 { | 122 { |
123 return toHTMLSpanElement(createHTMLElement(document, spanTag).get()); | 123 return toHTMLSpanElement(createHTMLElement(document, spanTag).get()); |
124 } | 124 } |
125 | 125 |
126 ApplyStyleCommand::ApplyStyleCommand(Document& document, const EditingStyle* sty
le, EditAction editingAction, EPropertyLevel propertyLevel) | 126 ApplyStyleCommand::ApplyStyleCommand(Document& document, const EditingStyle* sty
le, EditAction editingAction, EPropertyLevel propertyLevel) |
127 : CompositeEditCommand(document) | 127 : CompositeEditCommand(document) |
128 , m_style(style->copy()) | 128 , m_style(style->copy()) |
129 , m_editingAction(editingAction) | 129 , m_editingAction(editingAction) |
130 , m_propertyLevel(propertyLevel) | 130 , m_propertyLevel(propertyLevel) |
131 , m_start(endingSelection().start().downstream()) | 131 , m_start(mostForwardCaretPosition(endingSelection().start())) |
132 , m_end(endingSelection().end().upstream()) | 132 , m_end(mostBackwardCaretPosition(endingSelection().end())) |
133 , m_useEndingSelection(true) | 133 , m_useEndingSelection(true) |
134 , m_styledInlineElement(nullptr) | 134 , m_styledInlineElement(nullptr) |
135 , m_removeOnly(false) | 135 , m_removeOnly(false) |
136 , m_isInlineElementToRemoveFunction(0) | 136 , m_isInlineElementToRemoveFunction(0) |
137 { | 137 { |
138 } | 138 } |
139 | 139 |
140 ApplyStyleCommand::ApplyStyleCommand(Document& document, const EditingStyle* sty
le, const Position& start, const Position& end, EditAction editingAction, EPrope
rtyLevel propertyLevel) | 140 ApplyStyleCommand::ApplyStyleCommand(Document& document, const EditingStyle* sty
le, const Position& start, const Position& end, EditAction editingAction, EPrope
rtyLevel propertyLevel) |
141 : CompositeEditCommand(document) | 141 : CompositeEditCommand(document) |
142 , m_style(style->copy()) | 142 , m_style(style->copy()) |
143 , m_editingAction(editingAction) | 143 , m_editingAction(editingAction) |
144 , m_propertyLevel(propertyLevel) | 144 , m_propertyLevel(propertyLevel) |
145 , m_start(start) | 145 , m_start(start) |
146 , m_end(end) | 146 , m_end(end) |
147 , m_useEndingSelection(false) | 147 , m_useEndingSelection(false) |
148 , m_styledInlineElement(nullptr) | 148 , m_styledInlineElement(nullptr) |
149 , m_removeOnly(false) | 149 , m_removeOnly(false) |
150 , m_isInlineElementToRemoveFunction(0) | 150 , m_isInlineElementToRemoveFunction(0) |
151 { | 151 { |
152 } | 152 } |
153 | 153 |
154 ApplyStyleCommand::ApplyStyleCommand(PassRefPtrWillBeRawPtr<Element> element, bo
ol removeOnly, EditAction editingAction) | 154 ApplyStyleCommand::ApplyStyleCommand(PassRefPtrWillBeRawPtr<Element> element, bo
ol removeOnly, EditAction editingAction) |
155 : CompositeEditCommand(element->document()) | 155 : CompositeEditCommand(element->document()) |
156 , m_style(EditingStyle::create()) | 156 , m_style(EditingStyle::create()) |
157 , m_editingAction(editingAction) | 157 , m_editingAction(editingAction) |
158 , m_propertyLevel(PropertyDefault) | 158 , m_propertyLevel(PropertyDefault) |
159 , m_start(endingSelection().start().downstream()) | 159 , m_start(mostForwardCaretPosition(endingSelection().start())) |
160 , m_end(endingSelection().end().upstream()) | 160 , m_end(mostBackwardCaretPosition(endingSelection().end())) |
161 , m_useEndingSelection(true) | 161 , m_useEndingSelection(true) |
162 , m_styledInlineElement(element) | 162 , m_styledInlineElement(element) |
163 , m_removeOnly(removeOnly) | 163 , m_removeOnly(removeOnly) |
164 , m_isInlineElementToRemoveFunction(0) | 164 , m_isInlineElementToRemoveFunction(0) |
165 { | 165 { |
166 } | 166 } |
167 | 167 |
168 ApplyStyleCommand::ApplyStyleCommand(Document& document, const EditingStyle* sty
le, IsInlineElementToRemoveFunction isInlineElementToRemoveFunction, EditAction
editingAction) | 168 ApplyStyleCommand::ApplyStyleCommand(Document& document, const EditingStyle* sty
le, IsInlineElementToRemoveFunction isInlineElementToRemoveFunction, EditAction
editingAction) |
169 : CompositeEditCommand(document) | 169 : CompositeEditCommand(document) |
170 , m_style(style->copy()) | 170 , m_style(style->copy()) |
171 , m_editingAction(editingAction) | 171 , m_editingAction(editingAction) |
172 , m_propertyLevel(PropertyDefault) | 172 , m_propertyLevel(PropertyDefault) |
173 , m_start(endingSelection().start().downstream()) | 173 , m_start(mostForwardCaretPosition(endingSelection().start())) |
174 , m_end(endingSelection().end().upstream()) | 174 , m_end(mostBackwardCaretPosition(endingSelection().end())) |
175 , m_useEndingSelection(true) | 175 , m_useEndingSelection(true) |
176 , m_styledInlineElement(nullptr) | 176 , m_styledInlineElement(nullptr) |
177 , m_removeOnly(true) | 177 , m_removeOnly(true) |
178 , m_isInlineElementToRemoveFunction(isInlineElementToRemoveFunction) | 178 , m_isInlineElementToRemoveFunction(isInlineElementToRemoveFunction) |
179 { | 179 { |
180 } | 180 } |
181 | 181 |
182 void ApplyStyleCommand::updateStartEnd(const Position& newStart, const Position&
newEnd) | 182 void ApplyStyleCommand::updateStartEnd(const Position& newStart, const Position&
newEnd) |
183 { | 183 { |
184 ASSERT(comparePositions(newEnd, newStart) >= 0); | 184 ASSERT(comparePositions(newEnd, newStart) >= 0); |
(...skipping 174 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
359 // If the end node is before the start node (can only happen if the end node
is | 359 // If the end node is before the start node (can only happen if the end node
is |
360 // an ancestor of the start node), we gather nodes up to the next sibling of
the end node | 360 // an ancestor of the start node), we gather nodes up to the next sibling of
the end node |
361 Node* beyondEnd; | 361 Node* beyondEnd; |
362 ASSERT(start.anchorNode()); | 362 ASSERT(start.anchorNode()); |
363 ASSERT(end.anchorNode()); | 363 ASSERT(end.anchorNode()); |
364 if (start.anchorNode()->isDescendantOf(end.anchorNode())) | 364 if (start.anchorNode()->isDescendantOf(end.anchorNode())) |
365 beyondEnd = NodeTraversal::nextSkippingChildren(*end.anchorNode()); | 365 beyondEnd = NodeTraversal::nextSkippingChildren(*end.anchorNode()); |
366 else | 366 else |
367 beyondEnd = NodeTraversal::next(*end.anchorNode()); | 367 beyondEnd = NodeTraversal::next(*end.anchorNode()); |
368 | 368 |
369 start = start.upstream(); // Move upstream to ensure we do not add redundant
spans. | 369 start = mostBackwardCaretPosition(start); // Move upstream to ensure we do n
ot add redundant spans. |
370 Node* startNode = start.anchorNode(); | 370 Node* startNode = start.anchorNode(); |
371 ASSERT(startNode); | 371 ASSERT(startNode); |
372 | 372 |
373 // Make sure we're not already at the end or the next NodeTraversal::next()
will traverse | 373 // Make sure we're not already at the end or the next NodeTraversal::next()
will traverse |
374 // past it. | 374 // past it. |
375 if (startNode == beyondEnd) | 375 if (startNode == beyondEnd) |
376 return; | 376 return; |
377 | 377 |
378 if (startNode->isTextNode() && start.computeOffsetInContainerNode() >= caret
MaxOffset(startNode)) { | 378 if (startNode->isTextNode() && start.computeOffsetInContainerNode() >= caret
MaxOffset(startNode)) { |
379 // Move out of text node if range does not include its characters. | 379 // Move out of text node if range does not include its characters. |
(...skipping 226 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
606 if (start.isNull() || end.isNull()) | 606 if (start.isNull() || end.isNull()) |
607 return; | 607 return; |
608 endDummySpanAncestor = dummySpanAncestorForNode(end.anchorNode()); | 608 endDummySpanAncestor = dummySpanAncestorForNode(end.anchorNode()); |
609 } | 609 } |
610 | 610 |
611 // Remove style from the selection. | 611 // Remove style from the selection. |
612 // Use the upstream position of the start for removing style. | 612 // Use the upstream position of the start for removing style. |
613 // This will ensure we remove all traces of the relevant styles from the sel
ection | 613 // This will ensure we remove all traces of the relevant styles from the sel
ection |
614 // and prevent us from adding redundant ones, as described in: | 614 // and prevent us from adding redundant ones, as described in: |
615 // <rdar://problem/3724344> Bolding and unbolding creates extraneous tags | 615 // <rdar://problem/3724344> Bolding and unbolding creates extraneous tags |
616 Position removeStart = start.upstream(); | 616 Position removeStart = mostBackwardCaretPosition(start); |
617 WritingDirection textDirection = NaturalWritingDirection; | 617 WritingDirection textDirection = NaturalWritingDirection; |
618 bool hasTextDirection = style->textDirection(textDirection); | 618 bool hasTextDirection = style->textDirection(textDirection); |
619 RefPtrWillBeRawPtr<EditingStyle> styleWithoutEmbedding = nullptr; | 619 RefPtrWillBeRawPtr<EditingStyle> styleWithoutEmbedding = nullptr; |
620 RefPtrWillBeRawPtr<EditingStyle> embeddingStyle = nullptr; | 620 RefPtrWillBeRawPtr<EditingStyle> embeddingStyle = nullptr; |
621 if (hasTextDirection) { | 621 if (hasTextDirection) { |
622 // Leave alone an ancestor that provides the desired single level embedd
ing, if there is one. | 622 // Leave alone an ancestor that provides the desired single level embedd
ing, if there is one. |
623 HTMLElement* startUnsplitAncestor = splitAncestorsWithUnicodeBidi(start.
anchorNode(), true, textDirection); | 623 HTMLElement* startUnsplitAncestor = splitAncestorsWithUnicodeBidi(start.
anchorNode(), true, textDirection); |
624 HTMLElement* endUnsplitAncestor = splitAncestorsWithUnicodeBidi(end.anch
orNode(), false, textDirection); | 624 HTMLElement* endUnsplitAncestor = splitAncestorsWithUnicodeBidi(end.anch
orNode(), false, textDirection); |
625 removeEmbeddingUpToEnclosingBlock(start.anchorNode(), startUnsplitAncest
or); | 625 removeEmbeddingUpToEnclosingBlock(start.anchorNode(), startUnsplitAncest
or); |
626 removeEmbeddingUpToEnclosingBlock(end.anchorNode(), endUnsplitAncestor); | 626 removeEmbeddingUpToEnclosingBlock(end.anchorNode(), endUnsplitAncestor); |
627 | 627 |
628 // Avoid removing the dir attribute and the unicode-bidi and direction p
roperties from the unsplit ancestors. | 628 // Avoid removing the dir attribute and the unicode-bidi and direction p
roperties from the unsplit ancestors. |
629 Position embeddingRemoveStart = removeStart; | 629 Position embeddingRemoveStart = removeStart; |
630 if (startUnsplitAncestor && elementFullySelected(*startUnsplitAncestor,
removeStart, end)) | 630 if (startUnsplitAncestor && elementFullySelected(*startUnsplitAncestor,
removeStart, end)) |
631 embeddingRemoveStart = positionInParentAfterNode(*startUnsplitAncest
or); | 631 embeddingRemoveStart = positionInParentAfterNode(*startUnsplitAncest
or); |
632 | 632 |
633 Position embeddingRemoveEnd = end; | 633 Position embeddingRemoveEnd = end; |
634 if (endUnsplitAncestor && elementFullySelected(*endUnsplitAncestor, remo
veStart, end)) | 634 if (endUnsplitAncestor && elementFullySelected(*endUnsplitAncestor, remo
veStart, end)) |
635 embeddingRemoveEnd = positionInParentBeforeNode(*endUnsplitAncestor)
.downstream(); | 635 embeddingRemoveEnd = mostForwardCaretPosition(positionInParentBefore
Node(*endUnsplitAncestor)); |
636 | 636 |
637 if (embeddingRemoveEnd != removeStart || embeddingRemoveEnd != end) { | 637 if (embeddingRemoveEnd != removeStart || embeddingRemoveEnd != end) { |
638 styleWithoutEmbedding = style->copy(); | 638 styleWithoutEmbedding = style->copy(); |
639 embeddingStyle = styleWithoutEmbedding->extractAndRemoveTextDirectio
n(); | 639 embeddingStyle = styleWithoutEmbedding->extractAndRemoveTextDirectio
n(); |
640 | 640 |
641 if (comparePositions(embeddingRemoveStart, embeddingRemoveEnd) <= 0) | 641 if (comparePositions(embeddingRemoveStart, embeddingRemoveEnd) <= 0) |
642 removeInlineStyle(embeddingStyle.get(), embeddingRemoveStart, em
beddingRemoveEnd); | 642 removeInlineStyle(embeddingStyle.get(), embeddingRemoveStart, em
beddingRemoveEnd); |
643 } | 643 } |
644 } | 644 } |
645 | 645 |
(...skipping 468 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1114 void ApplyStyleCommand::removeInlineStyle(EditingStyle* style, const Position &s
tart, const Position &end) | 1114 void ApplyStyleCommand::removeInlineStyle(EditingStyle* style, const Position &s
tart, const Position &end) |
1115 { | 1115 { |
1116 ASSERT(start.isNotNull()); | 1116 ASSERT(start.isNotNull()); |
1117 ASSERT(end.isNotNull()); | 1117 ASSERT(end.isNotNull()); |
1118 ASSERT(start.inDocument()); | 1118 ASSERT(start.inDocument()); |
1119 ASSERT(end.inDocument()); | 1119 ASSERT(end.inDocument()); |
1120 ASSERT(Position::commonAncestorTreeScope(start, end)); | 1120 ASSERT(Position::commonAncestorTreeScope(start, end)); |
1121 ASSERT(comparePositions(start, end) <= 0); | 1121 ASSERT(comparePositions(start, end) <= 0); |
1122 // FIXME: We should assert that start/end are not in the middle of a text no
de. | 1122 // FIXME: We should assert that start/end are not in the middle of a text no
de. |
1123 | 1123 |
1124 Position pushDownStart = start.downstream(); | 1124 Position pushDownStart = mostForwardCaretPosition(start); |
1125 // If the pushDownStart is at the end of a text node, then this node is not
fully selected. | 1125 // If the pushDownStart is at the end of a text node, then this node is not
fully selected. |
1126 // Move it to the next deep quivalent position to avoid removing the style f
rom this node. | 1126 // Move it to the next deep quivalent position to avoid removing the style f
rom this node. |
1127 // e.g. if pushDownStart was at Position("hello", 5) in <b>hello<div>world</
div></b>, we want Position("world", 0) instead. | 1127 // e.g. if pushDownStart was at Position("hello", 5) in <b>hello<div>world</
div></b>, we want Position("world", 0) instead. |
1128 Node* pushDownStartContainer = pushDownStart.computeContainerNode(); | 1128 Node* pushDownStartContainer = pushDownStart.computeContainerNode(); |
1129 if (pushDownStartContainer && pushDownStartContainer->isTextNode() | 1129 if (pushDownStartContainer && pushDownStartContainer->isTextNode() |
1130 && pushDownStart.computeOffsetInContainerNode() == pushDownStartContaine
r->maxCharacterOffset()) | 1130 && pushDownStart.computeOffsetInContainerNode() == pushDownStartContaine
r->maxCharacterOffset()) |
1131 pushDownStart = nextVisuallyDistinctCandidate(pushDownStart); | 1131 pushDownStart = nextVisuallyDistinctCandidate(pushDownStart); |
1132 Position pushDownEnd = end.upstream(); | 1132 Position pushDownEnd = mostBackwardCaretPosition(end); |
1133 // If pushDownEnd is at the start of a text node, then this node is not full
y selected. | 1133 // If pushDownEnd is at the start of a text node, then this node is not full
y selected. |
1134 // Move it to the previous deep equivalent position to avoid removing the st
yle from this node. | 1134 // Move it to the previous deep equivalent position to avoid removing the st
yle from this node. |
1135 Node* pushDownEndContainer = pushDownEnd.computeContainerNode(); | 1135 Node* pushDownEndContainer = pushDownEnd.computeContainerNode(); |
1136 if (pushDownEndContainer && pushDownEndContainer->isTextNode() && !pushDownE
nd.computeOffsetInContainerNode()) | 1136 if (pushDownEndContainer && pushDownEndContainer->isTextNode() && !pushDownE
nd.computeOffsetInContainerNode()) |
1137 pushDownEnd = previousVisuallyDistinctCandidate(pushDownEnd); | 1137 pushDownEnd = previousVisuallyDistinctCandidate(pushDownEnd); |
1138 | 1138 |
1139 pushDownInlineStyleAroundNode(style, pushDownStart.anchorNode()); | 1139 pushDownInlineStyleAroundNode(style, pushDownStart.anchorNode()); |
1140 pushDownInlineStyleAroundNode(style, pushDownEnd.anchorNode()); | 1140 pushDownInlineStyleAroundNode(style, pushDownEnd.anchorNode()); |
1141 | 1141 |
1142 // The s and e variables store the positions used to set the ending selectio
n after style removal | 1142 // The s and e variables store the positions used to set the ending selectio
n after style removal |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1201 | 1201 |
1202 updateStartEnd(s, e); | 1202 updateStartEnd(s, e); |
1203 } | 1203 } |
1204 | 1204 |
1205 bool ApplyStyleCommand::elementFullySelected(HTMLElement& element, const Positio
n& start, const Position& end) const | 1205 bool ApplyStyleCommand::elementFullySelected(HTMLElement& element, const Positio
n& start, const Position& end) const |
1206 { | 1206 { |
1207 // The tree may have changed and Position::upstream() relies on an up-to-dat
e layout. | 1207 // The tree may have changed and Position::upstream() relies on an up-to-dat
e layout. |
1208 element.document().updateLayoutIgnorePendingStylesheets(); | 1208 element.document().updateLayoutIgnorePendingStylesheets(); |
1209 | 1209 |
1210 return comparePositions(firstPositionInOrBeforeNode(&element), start) >= 0 | 1210 return comparePositions(firstPositionInOrBeforeNode(&element), start) >= 0 |
1211 && comparePositions(lastPositionInOrAfterNode(&element).upstream(), end)
<= 0; | 1211 && comparePositions(mostBackwardCaretPosition(lastPositionInOrAfterNode(
&element)), end) <= 0; |
1212 } | 1212 } |
1213 | 1213 |
1214 void ApplyStyleCommand::splitTextAtStart(const Position& start, const Position&
end) | 1214 void ApplyStyleCommand::splitTextAtStart(const Position& start, const Position&
end) |
1215 { | 1215 { |
1216 ASSERT(start.computeContainerNode()->isTextNode()); | 1216 ASSERT(start.computeContainerNode()->isTextNode()); |
1217 | 1217 |
1218 Position newEnd; | 1218 Position newEnd; |
1219 if (end.isOffsetInAnchor() && start.computeContainerNode() == end.computeCon
tainerNode()) | 1219 if (end.isOffsetInAnchor() && start.computeContainerNode() == end.computeCon
tainerNode()) |
1220 newEnd = Position(end.computeContainerNode(), end.offsetInContainerNode(
) - start.offsetInContainerNode()); | 1220 newEnd = Position(end.computeContainerNode(), end.offsetInContainerNode(
) - start.offsetInContainerNode()); |
1221 else | 1221 else |
(...skipping 372 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1594 DEFINE_TRACE(ApplyStyleCommand) | 1594 DEFINE_TRACE(ApplyStyleCommand) |
1595 { | 1595 { |
1596 visitor->trace(m_style); | 1596 visitor->trace(m_style); |
1597 visitor->trace(m_start); | 1597 visitor->trace(m_start); |
1598 visitor->trace(m_end); | 1598 visitor->trace(m_end); |
1599 visitor->trace(m_styledInlineElement); | 1599 visitor->trace(m_styledInlineElement); |
1600 CompositeEditCommand::trace(visitor); | 1600 CompositeEditCommand::trace(visitor); |
1601 } | 1601 } |
1602 | 1602 |
1603 } | 1603 } |
OLD | NEW |