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