| 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 |