OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. | 2 * Copyright (C) 2005 Apple Computer, 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 15 matching lines...) Expand all Loading... |
26 #include "core/editing/commands/DeleteSelectionCommand.h" | 26 #include "core/editing/commands/DeleteSelectionCommand.h" |
27 | 27 |
28 #include "core/HTMLNames.h" | 28 #include "core/HTMLNames.h" |
29 #include "core/dom/Document.h" | 29 #include "core/dom/Document.h" |
30 #include "core/dom/Element.h" | 30 #include "core/dom/Element.h" |
31 #include "core/dom/NodeTraversal.h" | 31 #include "core/dom/NodeTraversal.h" |
32 #include "core/dom/Text.h" | 32 #include "core/dom/Text.h" |
33 #include "core/editing/EditingBoundary.h" | 33 #include "core/editing/EditingBoundary.h" |
34 #include "core/editing/EditingUtilities.h" | 34 #include "core/editing/EditingUtilities.h" |
35 #include "core/editing/Editor.h" | 35 #include "core/editing/Editor.h" |
| 36 #include "core/editing/RelocatablePosition.h" |
36 #include "core/editing/VisibleUnits.h" | 37 #include "core/editing/VisibleUnits.h" |
37 #include "core/frame/LocalFrame.h" | 38 #include "core/frame/LocalFrame.h" |
38 #include "core/html/HTMLBRElement.h" | 39 #include "core/html/HTMLBRElement.h" |
39 #include "core/html/HTMLInputElement.h" | 40 #include "core/html/HTMLInputElement.h" |
40 #include "core/html/HTMLStyleElement.h" | 41 #include "core/html/HTMLStyleElement.h" |
41 #include "core/html/HTMLTableRowElement.h" | 42 #include "core/html/HTMLTableRowElement.h" |
42 #include "core/layout/LayoutTableCell.h" | 43 #include "core/layout/LayoutTableCell.h" |
43 | 44 |
44 namespace blink { | 45 namespace blink { |
45 | 46 |
46 using namespace HTMLNames; | 47 using namespace HTMLNames; |
47 | 48 |
48 static bool isTableCellEmpty(Node* cell) { | 49 static bool isTableCellEmpty(Node* cell) { |
49 DCHECK(isTableCell(cell)) << cell; | 50 DCHECK(isTableCell(cell)) << cell; |
50 return VisiblePosition::firstPositionInNode(cell).deepEquivalent() == | 51 return VisiblePosition::firstPositionInNode(cell).deepEquivalent() == |
51 VisiblePosition::lastPositionInNode(cell).deepEquivalent(); | 52 VisiblePosition::lastPositionInNode(cell).deepEquivalent(); |
52 } | 53 } |
53 | 54 |
54 static bool isTableRowEmpty(Node* row) { | 55 static bool isTableRowEmpty(Node* row) { |
55 if (!isHTMLTableRowElement(row)) | 56 if (!isHTMLTableRowElement(row)) |
56 return false; | 57 return false; |
57 | 58 |
58 for (Node* child = row->firstChild(); child; child = child->nextSibling()) { | 59 for (Node* child = row->firstChild(); child; child = child->nextSibling()) { |
59 if (isTableCell(child) && !isTableCellEmpty(child)) | 60 if (isTableCell(child) && !isTableCellEmpty(child)) |
60 return false; | 61 return false; |
61 } | 62 } |
62 return true; | 63 return true; |
63 } | 64 } |
64 | 65 |
65 DeleteSelectionCommand::DeleteSelectionCommand(Document& document, | 66 DeleteSelectionCommand::DeleteSelectionCommand( |
66 bool smartDelete, | 67 Document& document, |
67 bool mergeBlocksAfterDelete, | 68 bool smartDelete, |
68 bool expandForSpecialElements, | 69 bool mergeBlocksAfterDelete, |
69 bool sanitizeMarkup, | 70 bool expandForSpecialElements, |
70 InputEvent::InputType inputType) | 71 bool sanitizeMarkup, |
| 72 InputEvent::InputType inputType, |
| 73 const Position& referenceMovePosition) |
71 : CompositeEditCommand(document), | 74 : CompositeEditCommand(document), |
72 m_hasSelectionToDelete(false), | 75 m_hasSelectionToDelete(false), |
73 m_smartDelete(smartDelete), | 76 m_smartDelete(smartDelete), |
74 m_mergeBlocksAfterDelete(mergeBlocksAfterDelete), | 77 m_mergeBlocksAfterDelete(mergeBlocksAfterDelete), |
75 m_needPlaceholder(false), | 78 m_needPlaceholder(false), |
76 m_expandForSpecialElements(expandForSpecialElements), | 79 m_expandForSpecialElements(expandForSpecialElements), |
77 m_pruneStartBlockIfNecessary(false), | 80 m_pruneStartBlockIfNecessary(false), |
78 m_startsAtEmptyLine(false), | 81 m_startsAtEmptyLine(false), |
79 m_sanitizeMarkup(sanitizeMarkup), | 82 m_sanitizeMarkup(sanitizeMarkup), |
80 m_inputType(inputType), | 83 m_inputType(inputType), |
| 84 m_referenceMovePosition(referenceMovePosition), |
81 m_startBlock(nullptr), | 85 m_startBlock(nullptr), |
82 m_endBlock(nullptr), | 86 m_endBlock(nullptr), |
83 m_typingStyle(nullptr), | 87 m_typingStyle(nullptr), |
84 m_deleteIntoBlockquoteStyle(nullptr) {} | 88 m_deleteIntoBlockquoteStyle(nullptr) {} |
85 | 89 |
86 DeleteSelectionCommand::DeleteSelectionCommand( | 90 DeleteSelectionCommand::DeleteSelectionCommand( |
87 const VisibleSelection& selection, | 91 const VisibleSelection& selection, |
88 bool smartDelete, | 92 bool smartDelete, |
89 bool mergeBlocksAfterDelete, | 93 bool mergeBlocksAfterDelete, |
90 bool expandForSpecialElements, | 94 bool expandForSpecialElements, |
(...skipping 850 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
941 | 945 |
942 void DeleteSelectionCommand::clearTransientState() { | 946 void DeleteSelectionCommand::clearTransientState() { |
943 m_selectionToDelete = VisibleSelection(); | 947 m_selectionToDelete = VisibleSelection(); |
944 m_upstreamStart = Position(); | 948 m_upstreamStart = Position(); |
945 m_downstreamStart = Position(); | 949 m_downstreamStart = Position(); |
946 m_upstreamEnd = Position(); | 950 m_upstreamEnd = Position(); |
947 m_downstreamEnd = Position(); | 951 m_downstreamEnd = Position(); |
948 m_endingPosition = Position(); | 952 m_endingPosition = Position(); |
949 m_leadingWhitespace = Position(); | 953 m_leadingWhitespace = Position(); |
950 m_trailingWhitespace = Position(); | 954 m_trailingWhitespace = Position(); |
| 955 m_referenceMovePosition = Position(); |
951 } | 956 } |
952 | 957 |
953 // This method removes div elements with no attributes that have only one child
or no children at all. | 958 // This method removes div elements with no attributes that have only one child
or no children at all. |
954 void DeleteSelectionCommand::removeRedundantBlocks(EditingState* editingState) { | 959 void DeleteSelectionCommand::removeRedundantBlocks(EditingState* editingState) { |
955 Node* node = m_endingPosition.computeContainerNode(); | 960 Node* node = m_endingPosition.computeContainerNode(); |
956 Element* rootElement = rootEditableElement(*node); | 961 Element* rootElement = rootEditableElement(*node); |
957 | 962 |
958 while (node != rootElement) { | 963 while (node != rootElement) { |
959 if (isRemovableBlock(node)) { | 964 if (isRemovableBlock(node)) { |
960 if (node == m_endingPosition.anchorNode()) | 965 if (node == m_endingPosition.anchorNode()) |
(...skipping 12 matching lines...) Expand all Loading... |
973 void DeleteSelectionCommand::doApply(EditingState* editingState) { | 978 void DeleteSelectionCommand::doApply(EditingState* editingState) { |
974 // If selection has not been set to a custom selection when the command was cr
eated, | 979 // If selection has not been set to a custom selection when the command was cr
eated, |
975 // use the current ending selection. | 980 // use the current ending selection. |
976 if (!m_hasSelectionToDelete) | 981 if (!m_hasSelectionToDelete) |
977 m_selectionToDelete = endingSelection(); | 982 m_selectionToDelete = endingSelection(); |
978 | 983 |
979 if (!m_selectionToDelete.isNonOrphanedRange() || | 984 if (!m_selectionToDelete.isNonOrphanedRange() || |
980 !m_selectionToDelete.isContentEditable()) | 985 !m_selectionToDelete.isContentEditable()) |
981 return; | 986 return; |
982 | 987 |
| 988 RelocatablePosition relocatableReferencePosition(m_referenceMovePosition); |
| 989 |
983 // save this to later make the selection with | 990 // save this to later make the selection with |
984 TextAffinity affinity = m_selectionToDelete.affinity(); | 991 TextAffinity affinity = m_selectionToDelete.affinity(); |
985 | 992 |
986 Position downstreamEnd = mostForwardCaretPosition(m_selectionToDelete.end()); | 993 Position downstreamEnd = mostForwardCaretPosition(m_selectionToDelete.end()); |
987 bool rootWillStayOpenWithoutPlaceholder = | 994 bool rootWillStayOpenWithoutPlaceholder = |
988 downstreamEnd.computeContainerNode() == | 995 downstreamEnd.computeContainerNode() == |
989 rootEditableElement(*downstreamEnd.computeContainerNode()) || | 996 rootEditableElement(*downstreamEnd.computeContainerNode()) || |
990 (downstreamEnd.computeContainerNode()->isTextNode() && | 997 (downstreamEnd.computeContainerNode()->isTextNode() && |
991 downstreamEnd.computeContainerNode()->parentNode() == | 998 downstreamEnd.computeContainerNode()->parentNode() == |
992 rootEditableElement(*downstreamEnd.computeContainerNode())); | 999 rootEditableElement(*downstreamEnd.computeContainerNode())); |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1079 return; | 1086 return; |
1080 } | 1087 } |
1081 } | 1088 } |
1082 | 1089 |
1083 rebalanceWhitespaceAt(m_endingPosition); | 1090 rebalanceWhitespaceAt(m_endingPosition); |
1084 | 1091 |
1085 calculateTypingStyleAfterDelete(); | 1092 calculateTypingStyleAfterDelete(); |
1086 | 1093 |
1087 setEndingSelection(createVisibleSelectionDeprecated( | 1094 setEndingSelection(createVisibleSelectionDeprecated( |
1088 m_endingPosition, affinity, endingSelection().isDirectional())); | 1095 m_endingPosition, affinity, endingSelection().isDirectional())); |
| 1096 |
| 1097 if (relocatableReferencePosition.position().isNull()) { |
| 1098 clearTransientState(); |
| 1099 return; |
| 1100 } |
| 1101 |
| 1102 // This deletion command is part of a move operation, we need to cleanup after
deletion. |
| 1103 m_referenceMovePosition = relocatableReferencePosition.position(); |
| 1104 // If the node for the destination has been removed as a result of the deletio
n, |
| 1105 // set the destination to the ending point after the deletion. |
| 1106 // Fixes: <rdar://problem/3910425> REGRESSION (Mail): Crash in ReplaceSelectio
nCommand; |
| 1107 // selection is empty, leading to null deref |
| 1108 if (!m_referenceMovePosition.isConnected()) |
| 1109 m_referenceMovePosition = endingSelection().start(); |
| 1110 |
| 1111 // Move selection shouldn't left empty <li> block. |
| 1112 cleanupAfterDeletion( |
| 1113 editingState, createVisiblePositionDeprecated(m_referenceMovePosition)); |
| 1114 if (editingState->isAborted()) |
| 1115 return; |
| 1116 |
1089 clearTransientState(); | 1117 clearTransientState(); |
1090 } | 1118 } |
1091 | 1119 |
1092 InputEvent::InputType DeleteSelectionCommand::inputType() const { | 1120 InputEvent::InputType DeleteSelectionCommand::inputType() const { |
1093 // |DeleteSelectionCommand| could be used with Cut, Menu Bar deletion and |Typ
ingCommand|. | 1121 // |DeleteSelectionCommand| could be used with Cut, Menu Bar deletion and |Typ
ingCommand|. |
1094 // 1. Cut and Menu Bar deletion should rely on correct |m_inputType|. | 1122 // 1. Cut and Menu Bar deletion should rely on correct |m_inputType|. |
1095 // 2. |TypingCommand| will supply the |inputType()|, so |m_inputType| could de
fault to |InputType::None|. | 1123 // 2. |TypingCommand| will supply the |inputType()|, so |m_inputType| could de
fault to |InputType::None|. |
1096 return m_inputType; | 1124 return m_inputType; |
1097 } | 1125 } |
1098 | 1126 |
1099 // Normally deletion doesn't preserve the typing style that was present before i
t. For example, | 1127 // Normally deletion doesn't preserve the typing style that was present before i
t. For example, |
1100 // type a character, Bold, then delete the character and start typing. The Bold
typing style shouldn't | 1128 // type a character, Bold, then delete the character and start typing. The Bold
typing style shouldn't |
1101 // stick around. Deletion should preserve a typing style that *it* sets, howeve
r. | 1129 // stick around. Deletion should preserve a typing style that *it* sets, howeve
r. |
1102 bool DeleteSelectionCommand::preservesTypingStyle() const { | 1130 bool DeleteSelectionCommand::preservesTypingStyle() const { |
1103 return m_typingStyle; | 1131 return m_typingStyle; |
1104 } | 1132 } |
1105 | 1133 |
1106 DEFINE_TRACE(DeleteSelectionCommand) { | 1134 DEFINE_TRACE(DeleteSelectionCommand) { |
1107 visitor->trace(m_selectionToDelete); | 1135 visitor->trace(m_selectionToDelete); |
1108 visitor->trace(m_upstreamStart); | 1136 visitor->trace(m_upstreamStart); |
1109 visitor->trace(m_downstreamStart); | 1137 visitor->trace(m_downstreamStart); |
1110 visitor->trace(m_upstreamEnd); | 1138 visitor->trace(m_upstreamEnd); |
1111 visitor->trace(m_downstreamEnd); | 1139 visitor->trace(m_downstreamEnd); |
1112 visitor->trace(m_endingPosition); | 1140 visitor->trace(m_endingPosition); |
1113 visitor->trace(m_leadingWhitespace); | 1141 visitor->trace(m_leadingWhitespace); |
1114 visitor->trace(m_trailingWhitespace); | 1142 visitor->trace(m_trailingWhitespace); |
| 1143 visitor->trace(m_referenceMovePosition); |
1115 visitor->trace(m_startBlock); | 1144 visitor->trace(m_startBlock); |
1116 visitor->trace(m_endBlock); | 1145 visitor->trace(m_endBlock); |
1117 visitor->trace(m_typingStyle); | 1146 visitor->trace(m_typingStyle); |
1118 visitor->trace(m_deleteIntoBlockquoteStyle); | 1147 visitor->trace(m_deleteIntoBlockquoteStyle); |
1119 visitor->trace(m_startRoot); | 1148 visitor->trace(m_startRoot); |
1120 visitor->trace(m_endRoot); | 1149 visitor->trace(m_endRoot); |
1121 visitor->trace(m_startTableRow); | 1150 visitor->trace(m_startTableRow); |
1122 visitor->trace(m_endTableRow); | 1151 visitor->trace(m_endTableRow); |
1123 visitor->trace(m_temporaryPlaceholder); | 1152 visitor->trace(m_temporaryPlaceholder); |
1124 CompositeEditCommand::trace(visitor); | 1153 CompositeEditCommand::trace(visitor); |
1125 } | 1154 } |
1126 | 1155 |
1127 } // namespace blink | 1156 } // namespace blink |
OLD | NEW |