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 618 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
629 embeddingRemoveStart = positionInParentAfterNode(*startUnsplitAncest
or); | 629 embeddingRemoveStart = positionInParentAfterNode(*startUnsplitAncest
or); |
630 | 630 |
631 Position embeddingRemoveEnd = end; | 631 Position embeddingRemoveEnd = end; |
632 if (endUnsplitAncestor && elementFullySelected(*endUnsplitAncestor, remo
veStart, end)) | 632 if (endUnsplitAncestor && elementFullySelected(*endUnsplitAncestor, remo
veStart, end)) |
633 embeddingRemoveEnd = mostForwardCaretPosition(positionInParentBefore
Node(*endUnsplitAncestor)); | 633 embeddingRemoveEnd = mostForwardCaretPosition(positionInParentBefore
Node(*endUnsplitAncestor)); |
634 | 634 |
635 if (embeddingRemoveEnd != removeStart || embeddingRemoveEnd != end) { | 635 if (embeddingRemoveEnd != removeStart || embeddingRemoveEnd != end) { |
636 styleWithoutEmbedding = style->copy(); | 636 styleWithoutEmbedding = style->copy(); |
637 embeddingStyle = styleWithoutEmbedding->extractAndRemoveTextDirectio
n(); | 637 embeddingStyle = styleWithoutEmbedding->extractAndRemoveTextDirectio
n(); |
638 | 638 |
639 if (comparePositions(embeddingRemoveStart, embeddingRemoveEnd) <= 0) | 639 if (comparePositions(embeddingRemoveStart, embeddingRemoveEnd) <= 0)
{ |
640 removeInlineStyle(embeddingStyle.get(), embeddingRemoveStart, em
beddingRemoveEnd); | 640 removeInlineStyle(embeddingStyle.get(), embeddingRemoveStart, em
beddingRemoveEnd, editingState); |
| 641 if (editingState->isAborted()) |
| 642 return; |
| 643 } |
641 } | 644 } |
642 } | 645 } |
643 | 646 |
644 removeInlineStyle(styleWithoutEmbedding ? styleWithoutEmbedding.get() : styl
e, removeStart, end); | 647 removeInlineStyle(styleWithoutEmbedding ? styleWithoutEmbedding.get() : styl
e, removeStart, end, editingState); |
| 648 if (editingState->isAborted()) |
| 649 return; |
645 start = startPosition(); | 650 start = startPosition(); |
646 end = endPosition(); | 651 end = endPosition(); |
647 if (start.isNull() || start.isOrphan() || end.isNull() || end.isOrphan()) | 652 if (start.isNull() || start.isOrphan() || end.isNull() || end.isOrphan()) |
648 return; | 653 return; |
649 | 654 |
650 if (splitStart && mergeStartWithPreviousIfIdentical(start, end)) { | 655 if (splitStart) { |
| 656 bool mergeResult = mergeStartWithPreviousIfIdentical(start, end, editing
State); |
| 657 if (editingState->isAborted()) |
| 658 return; |
| 659 if (splitStart && mergeResult) { |
| 660 start = startPosition(); |
| 661 end = endPosition(); |
| 662 } |
| 663 } |
| 664 |
| 665 if (splitEnd) { |
| 666 mergeEndWithNextIfIdentical(start, end, editingState); |
| 667 if (editingState->isAborted()) |
| 668 return; |
651 start = startPosition(); | 669 start = startPosition(); |
652 end = endPosition(); | 670 end = endPosition(); |
653 } | 671 } |
654 | |
655 if (splitEnd) { | |
656 mergeEndWithNextIfIdentical(start, end); | |
657 start = startPosition(); | |
658 end = endPosition(); | |
659 } | |
660 | 672 |
661 // update document layout once before running the rest of the function | 673 // update document layout once before running the rest of the function |
662 // so that we avoid the expense of updating before each and every call | 674 // so that we avoid the expense of updating before each and every call |
663 // to check a computed style | 675 // to check a computed style |
664 document().updateLayoutIgnorePendingStylesheets(); | 676 document().updateLayoutIgnorePendingStylesheets(); |
665 | 677 |
666 RefPtrWillBeRawPtr<EditingStyle> styleToApply = style; | 678 RefPtrWillBeRawPtr<EditingStyle> styleToApply = style; |
667 if (hasTextDirection) { | 679 if (hasTextDirection) { |
668 // Avoid applying the unicode-bidi and direction properties beneath ance
stors that already have them. | 680 // Avoid applying the unicode-bidi and direction properties beneath ance
stors that already have them. |
669 HTMLElement* embeddingStartElement = highestEmbeddingAncestor(start.anch
orNode(), enclosingBlock(start.anchorNode())); | 681 HTMLElement* embeddingStartElement = highestEmbeddingAncestor(start.anch
orNode(), enclosingBlock(start.anchorNode())); |
(...skipping 356 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1026 result = toHTMLElement(n); | 1038 result = toHTMLElement(n); |
1027 // Should stop at the editable root (cannot cross editing boundary) and | 1039 // Should stop at the editable root (cannot cross editing boundary) and |
1028 // also stop at the unsplittable element to be consistent with other UAs | 1040 // also stop at the unsplittable element to be consistent with other UAs |
1029 if (n == unsplittableElement) | 1041 if (n == unsplittableElement) |
1030 break; | 1042 break; |
1031 } | 1043 } |
1032 | 1044 |
1033 return result; | 1045 return result; |
1034 } | 1046 } |
1035 | 1047 |
1036 void ApplyStyleCommand::applyInlineStyleToPushDown(Node* node, EditingStyle* sty
le) | 1048 void ApplyStyleCommand::applyInlineStyleToPushDown(Node* node, EditingStyle* sty
le, EditingState* editingState) |
1037 { | 1049 { |
1038 ASSERT(node); | 1050 ASSERT(node); |
1039 | 1051 |
1040 node->document().updateLayoutTreeIfNeeded(); | 1052 node->document().updateLayoutTreeIfNeeded(); |
1041 | 1053 |
1042 if (!style || style->isEmpty() || !node->layoutObject() || isHTMLIFrameEleme
nt(*node)) | 1054 if (!style || style->isEmpty() || !node->layoutObject() || isHTMLIFrameEleme
nt(*node)) |
1043 return; | 1055 return; |
1044 | 1056 |
1045 RefPtrWillBeRawPtr<EditingStyle> newInlineStyle = style; | 1057 RefPtrWillBeRawPtr<EditingStyle> newInlineStyle = style; |
1046 if (node->isHTMLElement() && toHTMLElement(node)->inlineStyle()) { | 1058 if (node->isHTMLElement() && toHTMLElement(node)->inlineStyle()) { |
1047 newInlineStyle = style->copy(); | 1059 newInlineStyle = style->copy(); |
1048 newInlineStyle->mergeInlineStyleOfElement(toHTMLElement(node), EditingSt
yle::OverrideValues); | 1060 newInlineStyle->mergeInlineStyleOfElement(toHTMLElement(node), EditingSt
yle::OverrideValues); |
1049 } | 1061 } |
1050 | 1062 |
1051 // Since addInlineStyleIfNeeded can't add styles to block-flow layout object
s, add style attribute instead. | 1063 // Since addInlineStyleIfNeeded can't add styles to block-flow layout object
s, add style attribute instead. |
1052 // FIXME: applyInlineStyleToRange should be used here instead. | 1064 // FIXME: applyInlineStyleToRange should be used here instead. |
1053 if ((node->layoutObject()->isLayoutBlockFlow() || node->hasChildren()) && no
de->isHTMLElement()) { | 1065 if ((node->layoutObject()->isLayoutBlockFlow() || node->hasChildren()) && no
de->isHTMLElement()) { |
1054 setNodeAttribute(toHTMLElement(node), styleAttr, AtomicString(newInlineS
tyle->style()->asText())); | 1066 setNodeAttribute(toHTMLElement(node), styleAttr, AtomicString(newInlineS
tyle->style()->asText())); |
1055 return; | 1067 return; |
1056 } | 1068 } |
1057 | 1069 |
1058 if (node->layoutObject()->isText() && toLayoutText(node->layoutObject())->is
AllCollapsibleWhitespace()) | 1070 if (node->layoutObject()->isText() && toLayoutText(node->layoutObject())->is
AllCollapsibleWhitespace()) |
1059 return; | 1071 return; |
1060 | 1072 |
1061 // We can't wrap node with the styled element here because new styled elemen
t will never be removed if we did. | 1073 // We can't wrap node with the styled element here because new styled elemen
t will never be removed if we did. |
1062 // If we modified the child pointer in pushDownInlineStyleAroundNode to poin
t to new style element | 1074 // If we modified the child pointer in pushDownInlineStyleAroundNode to poin
t to new style element |
1063 // then we fall into an infinite loop where we keep removing and adding styl
ed element wrapping node. | 1075 // then we fall into an infinite loop where we keep removing and adding styl
ed element wrapping node. |
1064 addInlineStyleIfNeeded(newInlineStyle.get(), node, node, DoNotAddStyledEleme
nt); | 1076 addInlineStyleIfNeeded(newInlineStyle.get(), node, node, editingState, DoNot
AddStyledElement); |
1065 } | 1077 } |
1066 | 1078 |
1067 void ApplyStyleCommand::pushDownInlineStyleAroundNode(EditingStyle* style, Node*
targetNode) | 1079 void ApplyStyleCommand::pushDownInlineStyleAroundNode(EditingStyle* style, Node*
targetNode, EditingState* editingState) |
1068 { | 1080 { |
1069 HTMLElement* highestAncestor = highestAncestorWithConflictingInlineStyle(sty
le, targetNode); | 1081 HTMLElement* highestAncestor = highestAncestorWithConflictingInlineStyle(sty
le, targetNode); |
1070 if (!highestAncestor) | 1082 if (!highestAncestor) |
1071 return; | 1083 return; |
1072 | 1084 |
1073 // The outer loop is traversing the tree vertically from highestAncestor to
targetNode | 1085 // The outer loop is traversing the tree vertically from highestAncestor to
targetNode |
1074 RefPtrWillBeRawPtr<Node> current = highestAncestor; | 1086 RefPtrWillBeRawPtr<Node> current = highestAncestor; |
1075 // Along the way, styled elements that contain targetNode are removed and ac
cumulated into elementsToPushDown. | 1087 // Along the way, styled elements that contain targetNode are removed and ac
cumulated into elementsToPushDown. |
1076 // Each child of the removed element, exclusing ancestors of targetNode, is
then wrapped by clones of elements in elementsToPushDown. | 1088 // Each child of the removed element, exclusing ancestors of targetNode, is
then wrapped by clones of elements in elementsToPushDown. |
1077 WillBeHeapVector<RefPtrWillBeMember<Element>> elementsToPushDown; | 1089 WillBeHeapVector<RefPtrWillBeMember<Element>> elementsToPushDown; |
(...skipping 23 matching lines...) Expand all Loading... |
1101 // Delete id attribute from the second element because the s
ame id cannot be used for more than one element | 1113 // Delete id attribute from the second element because the s
ame id cannot be used for more than one element |
1102 element->removeAttribute(HTMLNames::idAttr); | 1114 element->removeAttribute(HTMLNames::idAttr); |
1103 if (isHTMLAnchorElement(element)) | 1115 if (isHTMLAnchorElement(element)) |
1104 element->removeAttribute(HTMLNames::nameAttr); | 1116 element->removeAttribute(HTMLNames::nameAttr); |
1105 surroundNodeRangeWithElement(child, child, wrapper, ASSERT_N
O_EDITING_ABORT); | 1117 surroundNodeRangeWithElement(child, child, wrapper, ASSERT_N
O_EDITING_ABORT); |
1106 } | 1118 } |
1107 } | 1119 } |
1108 | 1120 |
1109 // Apply style to all nodes containing targetNode and their siblings
but NOT to targetNode | 1121 // Apply style to all nodes containing targetNode and their siblings
but NOT to targetNode |
1110 // But if we've removed styledElement then go ahead and always apply
the style. | 1122 // But if we've removed styledElement then go ahead and always apply
the style. |
1111 if (child != targetNode || styledElement) | 1123 if (child != targetNode || styledElement) { |
1112 applyInlineStyleToPushDown(child, styleToPushDown.get()); | 1124 applyInlineStyleToPushDown(child, styleToPushDown.get(), editing
State); |
| 1125 if (editingState->isAborted()) |
| 1126 return; |
| 1127 } |
1113 | 1128 |
1114 // We found the next node for the outer loop (contains targetNode) | 1129 // We found the next node for the outer loop (contains targetNode) |
1115 // When reached targetNode, stop the outer loop upon the completion
of the current inner loop | 1130 // When reached targetNode, stop the outer loop upon the completion
of the current inner loop |
1116 if (child == targetNode || child->contains(targetNode)) | 1131 if (child == targetNode || child->contains(targetNode)) |
1117 current = child; | 1132 current = child; |
1118 } | 1133 } |
1119 } | 1134 } |
1120 } | 1135 } |
1121 | 1136 |
1122 void ApplyStyleCommand::removeInlineStyle(EditingStyle* style, const Position &s
tart, const Position &end) | 1137 void ApplyStyleCommand::removeInlineStyle(EditingStyle* style, const Position &s
tart, const Position &end, EditingState* editingState) |
1123 { | 1138 { |
1124 ASSERT(start.isNotNull()); | 1139 ASSERT(start.isNotNull()); |
1125 ASSERT(end.isNotNull()); | 1140 ASSERT(end.isNotNull()); |
1126 ASSERT(start.inDocument()); | 1141 ASSERT(start.inDocument()); |
1127 ASSERT(end.inDocument()); | 1142 ASSERT(end.inDocument()); |
1128 ASSERT(Position::commonAncestorTreeScope(start, end)); | 1143 ASSERT(Position::commonAncestorTreeScope(start, end)); |
1129 ASSERT(comparePositions(start, end) <= 0); | 1144 ASSERT(comparePositions(start, end) <= 0); |
1130 // FIXME: We should assert that start/end are not in the middle of a text no
de. | 1145 // FIXME: We should assert that start/end are not in the middle of a text no
de. |
1131 | 1146 |
1132 Position pushDownStart = mostForwardCaretPosition(start); | 1147 Position pushDownStart = mostForwardCaretPosition(start); |
1133 // If the pushDownStart is at the end of a text node, then this node is not
fully selected. | 1148 // If the pushDownStart is at the end of a text node, then this node is not
fully selected. |
1134 // Move it to the next deep quivalent position to avoid removing the style f
rom this node. | 1149 // Move it to the next deep quivalent position to avoid removing the style f
rom this node. |
1135 // e.g. if pushDownStart was at Position("hello", 5) in <b>hello<div>world</
div></b>, we want Position("world", 0) instead. | 1150 // e.g. if pushDownStart was at Position("hello", 5) in <b>hello<div>world</
div></b>, we want Position("world", 0) instead. |
1136 Node* pushDownStartContainer = pushDownStart.computeContainerNode(); | 1151 Node* pushDownStartContainer = pushDownStart.computeContainerNode(); |
1137 if (pushDownStartContainer && pushDownStartContainer->isTextNode() | 1152 if (pushDownStartContainer && pushDownStartContainer->isTextNode() |
1138 && pushDownStart.computeOffsetInContainerNode() == pushDownStartContaine
r->maxCharacterOffset()) | 1153 && pushDownStart.computeOffsetInContainerNode() == pushDownStartContaine
r->maxCharacterOffset()) |
1139 pushDownStart = nextVisuallyDistinctCandidate(pushDownStart); | 1154 pushDownStart = nextVisuallyDistinctCandidate(pushDownStart); |
1140 Position pushDownEnd = mostBackwardCaretPosition(end); | 1155 Position pushDownEnd = mostBackwardCaretPosition(end); |
1141 // If pushDownEnd is at the start of a text node, then this node is not full
y selected. | 1156 // If pushDownEnd is at the start of a text node, then this node is not full
y selected. |
1142 // Move it to the previous deep equivalent position to avoid removing the st
yle from this node. | 1157 // Move it to the previous deep equivalent position to avoid removing the st
yle from this node. |
1143 Node* pushDownEndContainer = pushDownEnd.computeContainerNode(); | 1158 Node* pushDownEndContainer = pushDownEnd.computeContainerNode(); |
1144 if (pushDownEndContainer && pushDownEndContainer->isTextNode() && !pushDownE
nd.computeOffsetInContainerNode()) | 1159 if (pushDownEndContainer && pushDownEndContainer->isTextNode() && !pushDownE
nd.computeOffsetInContainerNode()) |
1145 pushDownEnd = previousVisuallyDistinctCandidate(pushDownEnd); | 1160 pushDownEnd = previousVisuallyDistinctCandidate(pushDownEnd); |
1146 | 1161 |
1147 pushDownInlineStyleAroundNode(style, pushDownStart.anchorNode()); | 1162 pushDownInlineStyleAroundNode(style, pushDownStart.anchorNode(), editingStat
e); |
1148 pushDownInlineStyleAroundNode(style, pushDownEnd.anchorNode()); | 1163 if (editingState->isAborted()) |
| 1164 return; |
| 1165 pushDownInlineStyleAroundNode(style, pushDownEnd.anchorNode(), editingState)
; |
| 1166 if (editingState->isAborted()) |
| 1167 return; |
1149 | 1168 |
1150 // The s and e variables store the positions used to set the ending selectio
n after style removal | 1169 // The s and e variables store the positions used to set the ending selectio
n after style removal |
1151 // takes place. This will help callers to recognize when either the start no
de or the end node | 1170 // takes place. This will help callers to recognize when either the start no
de or the end node |
1152 // are removed from the document during the work of this function. | 1171 // are removed from the document during the work of this function. |
1153 // If pushDownInlineStyleAroundNode has pruned start.anchorNode() or end.anc
horNode(), | 1172 // If pushDownInlineStyleAroundNode has pruned start.anchorNode() or end.anc
horNode(), |
1154 // use pushDownStart or pushDownEnd instead, which pushDownInlineStyleAround
Node won't prune. | 1173 // use pushDownStart or pushDownEnd instead, which pushDownInlineStyleAround
Node won't prune. |
1155 Position s = start.isNull() || start.isOrphan() ? pushDownStart : start; | 1174 Position s = start.isNull() || start.isOrphan() ? pushDownStart : start; |
1156 Position e = end.isNull() || end.isOrphan() ? pushDownEnd : end; | 1175 Position e = end.isNull() || end.isOrphan() ? pushDownEnd : end; |
1157 | 1176 |
1158 // Current ending selection resetting algorithm assumes |start| and |end| | 1177 // Current ending selection resetting algorithm assumes |start| and |end| |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1191 if (e.anchorNode() == elem) { | 1210 if (e.anchorNode() == elem) { |
1192 // Since elem must have been fully selected, and it is at th
e end | 1211 // Since elem must have been fully selected, and it is at th
e end |
1193 // of the selection, it is clear we can set the new e offset
to | 1212 // of the selection, it is clear we can set the new e offset
to |
1194 // the max range offset of prev. | 1213 // the max range offset of prev. |
1195 ASSERT(s.isAfterAnchor() || !offsetIsBeforeLastNodeOffset(s.
offsetInContainerNode(), s.computeContainerNode())); | 1214 ASSERT(s.isAfterAnchor() || !offsetIsBeforeLastNodeOffset(s.
offsetInContainerNode(), s.computeContainerNode())); |
1196 e = lastPositionInOrAfterNode(prev.get()); | 1215 e = lastPositionInOrAfterNode(prev.get()); |
1197 } | 1216 } |
1198 } | 1217 } |
1199 | 1218 |
1200 if (styleToPushDown) { | 1219 if (styleToPushDown) { |
1201 for (; childNode; childNode = childNode->nextSibling()) | 1220 for (; childNode; childNode = childNode->nextSibling()) { |
1202 applyInlineStyleToPushDown(childNode.get(), styleToPushDown.
get()); | 1221 applyInlineStyleToPushDown(childNode.get(), styleToPushDown.
get(), editingState); |
| 1222 if (editingState->isAborted()) |
| 1223 return; |
| 1224 } |
1203 } | 1225 } |
1204 } | 1226 } |
1205 if (node == end.anchorNode()) | 1227 if (node == end.anchorNode()) |
1206 break; | 1228 break; |
1207 node = next; | 1229 node = next; |
1208 } | 1230 } |
1209 | 1231 |
1210 updateStartEnd(s, e); | 1232 updateStartEnd(s, e); |
1211 } | 1233 } |
1212 | 1234 |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1294 { | 1316 { |
1295 ASSERT(position.isNotNull()); | 1317 ASSERT(position.isNotNull()); |
1296 | 1318 |
1297 Node* node = position.computeContainerNode(); | 1319 Node* node = position.computeContainerNode(); |
1298 if (!position.isOffsetInAnchor() || !node->isTextNode()) | 1320 if (!position.isOffsetInAnchor() || !node->isTextNode()) |
1299 return false; | 1321 return false; |
1300 int offsetInText = position.offsetInContainerNode(); | 1322 int offsetInText = position.offsetInContainerNode(); |
1301 return offsetInText > caretMinOffset(node) && offsetInText < caretMaxOffset(
node); | 1323 return offsetInText > caretMinOffset(node) && offsetInText < caretMaxOffset(
node); |
1302 } | 1324 } |
1303 | 1325 |
1304 bool ApplyStyleCommand::mergeStartWithPreviousIfIdentical(const Position& start,
const Position& end) | 1326 bool ApplyStyleCommand::mergeStartWithPreviousIfIdentical(const Position& start,
const Position& end, EditingState* editingState) |
1305 { | 1327 { |
1306 Node* startNode = start.computeContainerNode(); | 1328 Node* startNode = start.computeContainerNode(); |
1307 int startOffset = start.computeOffsetInContainerNode(); | 1329 int startOffset = start.computeOffsetInContainerNode(); |
1308 if (startOffset) | 1330 if (startOffset) |
1309 return false; | 1331 return false; |
1310 | 1332 |
1311 if (isAtomicNode(startNode)) { | 1333 if (isAtomicNode(startNode)) { |
1312 // note: prior siblings could be unrendered elements. it's silly to miss
the | 1334 // note: prior siblings could be unrendered elements. it's silly to miss
the |
1313 // merge opportunity just for that. | 1335 // merge opportunity just for that. |
1314 if (startNode->previousSibling()) | 1336 if (startNode->previousSibling()) |
1315 return false; | 1337 return false; |
1316 | 1338 |
1317 startNode = startNode->parentNode(); | 1339 startNode = startNode->parentNode(); |
1318 } | 1340 } |
1319 | 1341 |
1320 if (!startNode->isElementNode()) | 1342 if (!startNode->isElementNode()) |
1321 return false; | 1343 return false; |
1322 | 1344 |
1323 Node* previousSibling = startNode->previousSibling(); | 1345 Node* previousSibling = startNode->previousSibling(); |
1324 | 1346 |
1325 if (previousSibling && areIdenticalElements(*startNode, *previousSibling)) { | 1347 if (previousSibling && areIdenticalElements(*startNode, *previousSibling)) { |
1326 Element* previousElement = toElement(previousSibling); | 1348 Element* previousElement = toElement(previousSibling); |
1327 Element* element = toElement(startNode); | 1349 Element* element = toElement(startNode); |
1328 Node* startChild = element->firstChild(); | 1350 Node* startChild = element->firstChild(); |
1329 ASSERT(startChild); | 1351 ASSERT(startChild); |
1330 mergeIdenticalElements(previousElement, element); | 1352 mergeIdenticalElements(previousElement, element, editingState); |
| 1353 if (editingState->isAborted()) |
| 1354 return false; |
1331 | 1355 |
1332 int startOffsetAdjustment = startChild->nodeIndex(); | 1356 int startOffsetAdjustment = startChild->nodeIndex(); |
1333 int endOffsetAdjustment = startNode == end.anchorNode() ? startOffsetAdj
ustment : 0; | 1357 int endOffsetAdjustment = startNode == end.anchorNode() ? startOffsetAdj
ustment : 0; |
1334 updateStartEnd(Position(startNode, startOffsetAdjustment), | 1358 updateStartEnd(Position(startNode, startOffsetAdjustment), |
1335 Position(end.anchorNode(), end.computeEditingOffset() + endOffsetAdj
ustment)); | 1359 Position(end.anchorNode(), end.computeEditingOffset() + endOffsetAdj
ustment)); |
1336 return true; | 1360 return true; |
1337 } | 1361 } |
1338 | 1362 |
1339 return false; | 1363 return false; |
1340 } | 1364 } |
1341 | 1365 |
1342 bool ApplyStyleCommand::mergeEndWithNextIfIdentical(const Position& start, const
Position& end) | 1366 bool ApplyStyleCommand::mergeEndWithNextIfIdentical(const Position& start, const
Position& end, EditingState* editingState) |
1343 { | 1367 { |
1344 Node* endNode = end.computeContainerNode(); | 1368 Node* endNode = end.computeContainerNode(); |
1345 | 1369 |
1346 if (isAtomicNode(endNode)) { | 1370 if (isAtomicNode(endNode)) { |
1347 int endOffset = end.computeOffsetInContainerNode(); | 1371 int endOffset = end.computeOffsetInContainerNode(); |
1348 if (offsetIsBeforeLastNodeOffset(endOffset, endNode)) | 1372 if (offsetIsBeforeLastNodeOffset(endOffset, endNode)) |
1349 return false; | 1373 return false; |
1350 | 1374 |
1351 if (end.anchorNode()->nextSibling()) | 1375 if (end.anchorNode()->nextSibling()) |
1352 return false; | 1376 return false; |
1353 | 1377 |
1354 endNode = end.anchorNode()->parentNode(); | 1378 endNode = end.anchorNode()->parentNode(); |
1355 } | 1379 } |
1356 | 1380 |
1357 if (!endNode->isElementNode() || isHTMLBRElement(*endNode)) | 1381 if (!endNode->isElementNode() || isHTMLBRElement(*endNode)) |
1358 return false; | 1382 return false; |
1359 | 1383 |
1360 Node* nextSibling = endNode->nextSibling(); | 1384 Node* nextSibling = endNode->nextSibling(); |
1361 if (nextSibling && areIdenticalElements(*endNode, *nextSibling)) { | 1385 if (nextSibling && areIdenticalElements(*endNode, *nextSibling)) { |
1362 Element* nextElement = toElement(nextSibling); | 1386 Element* nextElement = toElement(nextSibling); |
1363 Element* element = toElement(endNode); | 1387 Element* element = toElement(endNode); |
1364 Node* nextChild = nextElement->firstChild(); | 1388 Node* nextChild = nextElement->firstChild(); |
1365 | 1389 |
1366 mergeIdenticalElements(element, nextElement); | 1390 mergeIdenticalElements(element, nextElement, editingState); |
| 1391 if (editingState->isAborted()) |
| 1392 return false; |
1367 | 1393 |
1368 bool shouldUpdateStart = start.computeContainerNode() == endNode; | 1394 bool shouldUpdateStart = start.computeContainerNode() == endNode; |
1369 int endOffset = nextChild ? nextChild->nodeIndex() : nextElement->childN
odes()->length(); | 1395 int endOffset = nextChild ? nextChild->nodeIndex() : nextElement->childN
odes()->length(); |
1370 updateStartEnd(shouldUpdateStart ? Position(nextElement, start.offsetInC
ontainerNode()) : start, | 1396 updateStartEnd(shouldUpdateStart ? Position(nextElement, start.offsetInC
ontainerNode()) : start, |
1371 Position(nextElement, endOffset)); | 1397 Position(nextElement, endOffset)); |
1372 return true; | 1398 return true; |
1373 } | 1399 } |
1374 | 1400 |
1375 return false; | 1401 return false; |
1376 } | 1402 } |
(...skipping 19 matching lines...) Expand all Loading... |
1396 return; | 1422 return; |
1397 } | 1423 } |
1398 if (node == endNode) | 1424 if (node == endNode) |
1399 break; | 1425 break; |
1400 node = next; | 1426 node = next; |
1401 } | 1427 } |
1402 | 1428 |
1403 RefPtrWillBeRawPtr<Node> nextSibling = element->nextSibling(); | 1429 RefPtrWillBeRawPtr<Node> nextSibling = element->nextSibling(); |
1404 RefPtrWillBeRawPtr<Node> previousSibling = element->previousSibling(); | 1430 RefPtrWillBeRawPtr<Node> previousSibling = element->previousSibling(); |
1405 if (nextSibling && nextSibling->isElementNode() && nextSibling->hasEditableS
tyle() | 1431 if (nextSibling && nextSibling->isElementNode() && nextSibling->hasEditableS
tyle() |
1406 && areIdenticalElements(*element, toElement(*nextSibling))) | 1432 && areIdenticalElements(*element, toElement(*nextSibling))) { |
1407 mergeIdenticalElements(element.get(), toElement(nextSibling)); | 1433 mergeIdenticalElements(element.get(), toElement(nextSibling), editingSta
te); |
| 1434 if (editingState->isAborted()) |
| 1435 return; |
| 1436 } |
1408 | 1437 |
1409 if (previousSibling && previousSibling->isElementNode() && previousSibling->
hasEditableStyle()) { | 1438 if (previousSibling && previousSibling->isElementNode() && previousSibling->
hasEditableStyle()) { |
1410 Node* mergedElement = previousSibling->nextSibling(); | 1439 Node* mergedElement = previousSibling->nextSibling(); |
1411 if (mergedElement->isElementNode() && mergedElement->hasEditableStyle() | 1440 if (mergedElement->isElementNode() && mergedElement->hasEditableStyle() |
1412 && areIdenticalElements(toElement(*previousSibling), toElement(*merg
edElement))) | 1441 && areIdenticalElements(toElement(*previousSibling), toElement(*merg
edElement))) { |
1413 mergeIdenticalElements(toElement(previousSibling), toElement(mergedE
lement)); | 1442 mergeIdenticalElements(toElement(previousSibling), toElement(mergedE
lement), editingState); |
| 1443 if (editingState->isAborted()) |
| 1444 return; |
| 1445 } |
1414 } | 1446 } |
1415 | 1447 |
1416 // FIXME: We should probably call updateStartEnd if the start or end was in
the node | 1448 // FIXME: We should probably call updateStartEnd if the start or end was in
the node |
1417 // range so that the endingSelection() is canonicalized. See the comments a
t the end of | 1449 // range so that the endingSelection() is canonicalized. See the comments a
t the end of |
1418 // VisibleSelection::validate(). | 1450 // VisibleSelection::validate(). |
1419 } | 1451 } |
1420 | 1452 |
1421 void ApplyStyleCommand::addBlockStyle(const StyleChange& styleChange, HTMLElemen
t* block) | 1453 void ApplyStyleCommand::addBlockStyle(const StyleChange& styleChange, HTMLElemen
t* block) |
1422 { | 1454 { |
1423 // Do not check for legacy styles here. Those styles, like <B> and <I>, only
apply for | 1455 // Do not check for legacy styles here. Those styles, like <B> and <I>, only
apply for |
1424 // inline content. | 1456 // inline content. |
1425 if (!block) | 1457 if (!block) |
1426 return; | 1458 return; |
1427 | 1459 |
1428 String cssStyle = styleChange.cssStyle(); | 1460 String cssStyle = styleChange.cssStyle(); |
1429 StringBuilder cssText; | 1461 StringBuilder cssText; |
1430 cssText.append(cssStyle); | 1462 cssText.append(cssStyle); |
1431 if (const StylePropertySet* decl = block->inlineStyle()) { | 1463 if (const StylePropertySet* decl = block->inlineStyle()) { |
1432 if (!cssStyle.isEmpty()) | 1464 if (!cssStyle.isEmpty()) |
1433 cssText.append(' '); | 1465 cssText.append(' '); |
1434 cssText.append(decl->asText()); | 1466 cssText.append(decl->asText()); |
1435 } | 1467 } |
1436 setNodeAttribute(block, styleAttr, cssText.toAtomicString()); | 1468 setNodeAttribute(block, styleAttr, cssText.toAtomicString()); |
1437 } | 1469 } |
1438 | 1470 |
1439 void ApplyStyleCommand::addInlineStyleIfNeeded(EditingStyle* style, PassRefPtrWi
llBeRawPtr<Node> passedStart, PassRefPtrWillBeRawPtr<Node> passedEnd, EAddStyled
Element addStyledElement) | 1471 void ApplyStyleCommand::addInlineStyleIfNeeded(EditingStyle* style, PassRefPtrWi
llBeRawPtr<Node> passedStart, PassRefPtrWillBeRawPtr<Node> passedEnd, EditingSta
te* editingState, EAddStyledElement addStyledElement) |
1440 { | 1472 { |
1441 if (!passedStart || !passedEnd || !passedStart->inDocument() || !passedEnd->
inDocument()) | 1473 if (!passedStart || !passedEnd || !passedStart->inDocument() || !passedEnd->
inDocument()) |
1442 return; | 1474 return; |
1443 | 1475 |
1444 RefPtrWillBeRawPtr<Node> start = passedStart; | 1476 RefPtrWillBeRawPtr<Node> start = passedStart; |
1445 RefPtrWillBeMember<HTMLSpanElement> dummyElement = nullptr; | 1477 RefPtrWillBeMember<HTMLSpanElement> dummyElement = nullptr; |
1446 StyleChange styleChange(style, positionToComputeInlineStyleChange(start, dum
myElement)); | 1478 StyleChange styleChange(style, positionToComputeInlineStyleChange(start, dum
myElement)); |
1447 | 1479 |
1448 if (dummyElement) | 1480 if (dummyElement) { |
1449 removeNode(dummyElement); | 1481 removeNode(dummyElement, editingState); |
| 1482 if (editingState->isAborted()) |
| 1483 return; |
| 1484 } |
1450 | 1485 |
1451 applyInlineStyleChange(start, passedEnd, styleChange, addStyledElement, ASSE
RT_NO_EDITING_ABORT); | 1486 applyInlineStyleChange(start, passedEnd, styleChange, addStyledElement, edit
ingState); |
1452 } | 1487 } |
1453 | 1488 |
1454 Position ApplyStyleCommand::positionToComputeInlineStyleChange(PassRefPtrWillBeR
awPtr<Node> startNode, RefPtrWillBeMember<HTMLSpanElement>& dummyElement) | 1489 Position ApplyStyleCommand::positionToComputeInlineStyleChange(PassRefPtrWillBeR
awPtr<Node> startNode, RefPtrWillBeMember<HTMLSpanElement>& dummyElement) |
1455 { | 1490 { |
1456 // It's okay to obtain the style at the startNode because we've removed all
relevant styles from the current run. | 1491 // It's okay to obtain the style at the startNode because we've removed all
relevant styles from the current run. |
1457 if (!startNode->isElementNode()) { | 1492 if (!startNode->isElementNode()) { |
1458 dummyElement = HTMLSpanElement::create(document()); | 1493 dummyElement = HTMLSpanElement::create(document()); |
1459 insertNodeAt(dummyElement, positionBeforeNode(startNode.get())); | 1494 insertNodeAt(dummyElement, positionBeforeNode(startNode.get())); |
1460 return positionBeforeNode(dummyElement.get()); | 1495 return positionBeforeNode(dummyElement.get()); |
1461 } | 1496 } |
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1610 if (!next || !next->isTextNode()) | 1645 if (!next || !next->isTextNode()) |
1611 continue; | 1646 continue; |
1612 | 1647 |
1613 Text* nextText = toText(next); | 1648 Text* nextText = toText(next); |
1614 if (start.isOffsetInAnchor() && next == start.computeContainerNode()) | 1649 if (start.isOffsetInAnchor() && next == start.computeContainerNode()) |
1615 newStart = Position(childText, childText->length() + start.offsetInC
ontainerNode()); | 1650 newStart = Position(childText, childText->length() + start.offsetInC
ontainerNode()); |
1616 if (end.isOffsetInAnchor() && next == end.computeContainerNode()) | 1651 if (end.isOffsetInAnchor() && next == end.computeContainerNode()) |
1617 newEnd = Position(childText, childText->length() + end.offsetInConta
inerNode()); | 1652 newEnd = Position(childText, childText->length() + end.offsetInConta
inerNode()); |
1618 String textToMove = nextText->data(); | 1653 String textToMove = nextText->data(); |
1619 insertTextIntoNode(childText, childText->length(), textToMove); | 1654 insertTextIntoNode(childText, childText->length(), textToMove); |
1620 removeNode(next); | 1655 // Removing a Text node doesn't dispatch synchronous events. |
| 1656 removeNode(next, ASSERT_NO_EDITING_ABORT); |
1621 // don't move child node pointer. it may want to merge with more text no
des. | 1657 // don't move child node pointer. it may want to merge with more text no
des. |
1622 } | 1658 } |
1623 | 1659 |
1624 updateStartEnd(newStart, newEnd); | 1660 updateStartEnd(newStart, newEnd); |
1625 } | 1661 } |
1626 | 1662 |
1627 DEFINE_TRACE(ApplyStyleCommand) | 1663 DEFINE_TRACE(ApplyStyleCommand) |
1628 { | 1664 { |
1629 visitor->trace(m_style); | 1665 visitor->trace(m_style); |
1630 visitor->trace(m_start); | 1666 visitor->trace(m_start); |
1631 visitor->trace(m_end); | 1667 visitor->trace(m_end); |
1632 visitor->trace(m_styledInlineElement); | 1668 visitor->trace(m_styledInlineElement); |
1633 CompositeEditCommand::trace(visitor); | 1669 CompositeEditCommand::trace(visitor); |
1634 } | 1670 } |
1635 | 1671 |
1636 } // namespace blink | 1672 } // namespace blink |
OLD | NEW |