| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. | 2 * Copyright (C) 2005, 2006, 2007, 2008 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 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 103 bool EditCommandComposition::belongsTo(const LocalFrame& frame) const { | 103 bool EditCommandComposition::belongsTo(const LocalFrame& frame) const { |
| 104 DCHECK(m_document); | 104 DCHECK(m_document); |
| 105 return m_document->frame() == &frame; | 105 return m_document->frame() == &frame; |
| 106 } | 106 } |
| 107 | 107 |
| 108 void EditCommandComposition::unapply() { | 108 void EditCommandComposition::unapply() { |
| 109 DCHECK(m_document); | 109 DCHECK(m_document); |
| 110 LocalFrame* frame = m_document->frame(); | 110 LocalFrame* frame = m_document->frame(); |
| 111 DCHECK(frame); | 111 DCHECK(frame); |
| 112 | 112 |
| 113 // Changes to the document may have been made since the last editing operation
that require a layout, as in <rdar://problem/5658603>. | 113 // Changes to the document may have been made since the last editing operation |
| 114 // Low level operations, like RemoveNodeCommand, don't require a layout becaus
e the high level operations that use them perform one | 114 // that require a layout, as in <rdar://problem/5658603>. Low level |
| 115 // if one is necessary (like for the creation of VisiblePositions). | 115 // operations, like RemoveNodeCommand, don't require a layout because the high |
| 116 // level operations that use them perform one if one is necessary (like for |
| 117 // the creation of VisiblePositions). |
| 116 m_document->updateStyleAndLayoutIgnorePendingStylesheets(); | 118 m_document->updateStyleAndLayoutIgnorePendingStylesheets(); |
| 117 | 119 |
| 118 { | 120 { |
| 119 size_t size = m_commands.size(); | 121 size_t size = m_commands.size(); |
| 120 for (size_t i = size; i; --i) | 122 for (size_t i = size; i; --i) |
| 121 m_commands[i - 1]->doUnapply(); | 123 m_commands[i - 1]->doUnapply(); |
| 122 } | 124 } |
| 123 | 125 |
| 124 frame->editor().unappliedEditing(this); | 126 frame->editor().unappliedEditing(this); |
| 125 } | 127 } |
| 126 | 128 |
| 127 void EditCommandComposition::reapply() { | 129 void EditCommandComposition::reapply() { |
| 128 DCHECK(m_document); | 130 DCHECK(m_document); |
| 129 LocalFrame* frame = m_document->frame(); | 131 LocalFrame* frame = m_document->frame(); |
| 130 DCHECK(frame); | 132 DCHECK(frame); |
| 131 | 133 |
| 132 // Changes to the document may have been made since the last editing operation
that require a layout, as in <rdar://problem/5658603>. | 134 // Changes to the document may have been made since the last editing operation |
| 133 // Low level operations, like RemoveNodeCommand, don't require a layout becaus
e the high level operations that use them perform one | 135 // that require a layout, as in <rdar://problem/5658603>. Low level |
| 134 // if one is necessary (like for the creation of VisiblePositions). | 136 // operations, like RemoveNodeCommand, don't require a layout because the high |
| 137 // level operations that use them perform one if one is necessary (like for |
| 138 // the creation of VisiblePositions). |
| 135 m_document->updateStyleAndLayoutIgnorePendingStylesheets(); | 139 m_document->updateStyleAndLayoutIgnorePendingStylesheets(); |
| 136 | 140 |
| 137 { | 141 { |
| 138 for (const auto& command : m_commands) | 142 for (const auto& command : m_commands) |
| 139 command->doReapply(); | 143 command->doReapply(); |
| 140 } | 144 } |
| 141 | 145 |
| 142 frame->editor().reappliedEditing(this); | 146 frame->editor().reappliedEditing(this); |
| 143 } | 147 } |
| 144 | 148 |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 205 case InputEvent::InputType::SetWritingDirection: | 209 case InputEvent::InputType::SetWritingDirection: |
| 206 case InputEvent::InputType::None: | 210 case InputEvent::InputType::None: |
| 207 break; | 211 break; |
| 208 default: | 212 default: |
| 209 NOTREACHED(); | 213 NOTREACHED(); |
| 210 return false; | 214 return false; |
| 211 } | 215 } |
| 212 } | 216 } |
| 213 ensureComposition(); | 217 ensureComposition(); |
| 214 | 218 |
| 215 // Changes to the document may have been made since the last editing operation
that require a layout, as in <rdar://problem/5658603>. | 219 // Changes to the document may have been made since the last editing operation |
| 216 // Low level operations, like RemoveNodeCommand, don't require a layout becaus
e the high level operations that use them perform one | 220 // that require a layout, as in <rdar://problem/5658603>. Low level |
| 217 // if one is necessary (like for the creation of VisiblePositions). | 221 // operations, like RemoveNodeCommand, don't require a layout because the high |
| 222 // level operations that use them perform one if one is necessary (like for |
| 223 // the creation of VisiblePositions). |
| 218 document().updateStyleAndLayoutIgnorePendingStylesheets(); | 224 document().updateStyleAndLayoutIgnorePendingStylesheets(); |
| 219 | 225 |
| 220 LocalFrame* frame = document().frame(); | 226 LocalFrame* frame = document().frame(); |
| 221 DCHECK(frame); | 227 DCHECK(frame); |
| 222 EditingState editingState; | 228 EditingState editingState; |
| 223 { | 229 { |
| 224 EventQueueScope eventQueueScope; | 230 EventQueueScope eventQueueScope; |
| 225 doApply(&editingState); | 231 doApply(&editingState); |
| 226 } | 232 } |
| 227 | 233 |
| 228 // Only need to call appliedEditing for top-level commands, | 234 // Only need to call appliedEditing for top-level commands, and TypingCommands |
| 229 // and TypingCommands do it on their own (see TypingCommand::typingAddedToOpen
Command). | 235 // do it on their own (see TypingCommand::typingAddedToOpenCommand). |
| 230 if (!isTypingCommand()) | 236 if (!isTypingCommand()) |
| 231 frame->editor().appliedEditing(this); | 237 frame->editor().appliedEditing(this); |
| 232 setShouldRetainAutocorrectionIndicator(false); | 238 setShouldRetainAutocorrectionIndicator(false); |
| 233 return !editingState.isAborted(); | 239 return !editingState.isAborted(); |
| 234 } | 240 } |
| 235 | 241 |
| 236 EditCommandComposition* CompositeEditCommand::ensureComposition() { | 242 EditCommandComposition* CompositeEditCommand::ensureComposition() { |
| 237 CompositeEditCommand* command = this; | 243 CompositeEditCommand* command = this; |
| 238 while (command && command->parent()) | 244 while (command && command->parent()) |
| 239 command = command->parent(); | 245 command = command->parent(); |
| (...skipping 19 matching lines...) Expand all Loading... |
| 259 return false; | 265 return false; |
| 260 } | 266 } |
| 261 | 267 |
| 262 bool CompositeEditCommand::isReplaceSelectionCommand() const { | 268 bool CompositeEditCommand::isReplaceSelectionCommand() const { |
| 263 return false; | 269 return false; |
| 264 } | 270 } |
| 265 | 271 |
| 266 void CompositeEditCommand::setShouldRetainAutocorrectionIndicator(bool) {} | 272 void CompositeEditCommand::setShouldRetainAutocorrectionIndicator(bool) {} |
| 267 | 273 |
| 268 // | 274 // |
| 269 // sugary-sweet convenience functions to help create and apply edit commands in
composite commands | 275 // sugary-sweet convenience functions to help create and apply edit commands in |
| 276 // composite commands |
| 270 // | 277 // |
| 271 void CompositeEditCommand::applyCommandToComposite(EditCommand* command, | 278 void CompositeEditCommand::applyCommandToComposite(EditCommand* command, |
| 272 EditingState* editingState) { | 279 EditingState* editingState) { |
| 273 command->setParent(this); | 280 command->setParent(this); |
| 274 command->doApply(editingState); | 281 command->doApply(editingState); |
| 275 if (editingState->isAborted()) { | 282 if (editingState->isAborted()) { |
| 276 command->setParent(nullptr); | 283 command->setParent(nullptr); |
| 277 return; | 284 return; |
| 278 } | 285 } |
| 279 if (command->isSimpleEditCommand()) { | 286 if (command->isSimpleEditCommand()) { |
| (...skipping 127 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 407 child = child->nextSibling(); | 414 child = child->nextSibling(); |
| 408 if (child) | 415 if (child) |
| 409 insertNodeBefore(insertChild, child, editingState); | 416 insertNodeBefore(insertChild, child, editingState); |
| 410 else | 417 else |
| 411 appendNode(insertChild, toContainerNode(refChild), editingState); | 418 appendNode(insertChild, toContainerNode(refChild), editingState); |
| 412 } else if (caretMinOffset(refChild) >= offset) { | 419 } else if (caretMinOffset(refChild) >= offset) { |
| 413 insertNodeBefore(insertChild, refChild, editingState); | 420 insertNodeBefore(insertChild, refChild, editingState); |
| 414 } else if (refChild->isTextNode() && caretMaxOffset(refChild) > offset) { | 421 } else if (refChild->isTextNode() && caretMaxOffset(refChild) > offset) { |
| 415 splitTextNode(toText(refChild), offset); | 422 splitTextNode(toText(refChild), offset); |
| 416 | 423 |
| 417 // Mutation events (bug 22634) from the text node insertion may have removed
the refChild | 424 // Mutation events (bug 22634) from the text node insertion may have removed |
| 425 // the refChild |
| 418 if (!refChild->isConnected()) | 426 if (!refChild->isConnected()) |
| 419 return; | 427 return; |
| 420 insertNodeBefore(insertChild, refChild, editingState); | 428 insertNodeBefore(insertChild, refChild, editingState); |
| 421 } else { | 429 } else { |
| 422 insertNodeAfter(insertChild, refChild, editingState); | 430 insertNodeAfter(insertChild, refChild, editingState); |
| 423 } | 431 } |
| 424 } | 432 } |
| 425 | 433 |
| 426 void CompositeEditCommand::appendNode(Node* node, | 434 void CompositeEditCommand::appendNode(Node* node, |
| 427 ContainerNode* parent, | 435 ContainerNode* parent, |
| (...skipping 371 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 799 if (textNode->length() == 0) | 807 if (textNode->length() == 0) |
| 800 return false; | 808 return false; |
| 801 | 809 |
| 802 LayoutText* layoutText = textNode->layoutObject(); | 810 LayoutText* layoutText = textNode->layoutObject(); |
| 803 if (layoutText && !layoutText->style()->collapseWhiteSpace()) | 811 if (layoutText && !layoutText->style()->collapseWhiteSpace()) |
| 804 return false; | 812 return false; |
| 805 | 813 |
| 806 return true; | 814 return true; |
| 807 } | 815 } |
| 808 | 816 |
| 809 // FIXME: Doesn't go into text nodes that contribute adjacent text (siblings, co
usins, etc). | 817 // FIXME: Doesn't go into text nodes that contribute adjacent text (siblings, |
| 818 // cousins, etc). |
| 810 void CompositeEditCommand::rebalanceWhitespaceAt(const Position& position) { | 819 void CompositeEditCommand::rebalanceWhitespaceAt(const Position& position) { |
| 811 Node* node = position.computeContainerNode(); | 820 Node* node = position.computeContainerNode(); |
| 812 if (!canRebalance(position)) | 821 if (!canRebalance(position)) |
| 813 return; | 822 return; |
| 814 | 823 |
| 815 // If the rebalance is for the single offset, and neither text[offset] nor tex
t[offset - 1] are some form of whitespace, do nothing. | 824 // If the rebalance is for the single offset, and neither text[offset] nor |
| 825 // text[offset - 1] are some form of whitespace, do nothing. |
| 816 int offset = position.computeOffsetInContainerNode(); | 826 int offset = position.computeOffsetInContainerNode(); |
| 817 String text = toText(node)->data(); | 827 String text = toText(node)->data(); |
| 818 if (!isWhitespace(text[offset])) { | 828 if (!isWhitespace(text[offset])) { |
| 819 offset--; | 829 offset--; |
| 820 if (offset < 0 || !isWhitespace(text[offset])) | 830 if (offset < 0 || !isWhitespace(text[offset])) |
| 821 return; | 831 return; |
| 822 } | 832 } |
| 823 | 833 |
| 824 rebalanceWhitespaceOnTextSubstring(toText(node), | 834 rebalanceWhitespaceOnTextSubstring(toText(node), |
| 825 position.offsetInContainerNode(), | 835 position.offsetInContainerNode(), |
| 826 position.offsetInContainerNode()); | 836 position.offsetInContainerNode()); |
| 827 } | 837 } |
| 828 | 838 |
| 829 void CompositeEditCommand::rebalanceWhitespaceOnTextSubstring(Text* textNode, | 839 void CompositeEditCommand::rebalanceWhitespaceOnTextSubstring(Text* textNode, |
| 830 int startOffset, | 840 int startOffset, |
| 831 int endOffset) { | 841 int endOffset) { |
| 832 String text = textNode->data(); | 842 String text = textNode->data(); |
| 833 DCHECK(!text.isEmpty()); | 843 DCHECK(!text.isEmpty()); |
| 834 | 844 |
| 835 // Set upstream and downstream to define the extent of the whitespace surround
ing text[offset]. | 845 // Set upstream and downstream to define the extent of the whitespace |
| 846 // surrounding text[offset]. |
| 836 int upstream = startOffset; | 847 int upstream = startOffset; |
| 837 while (upstream > 0 && isWhitespace(text[upstream - 1])) | 848 while (upstream > 0 && isWhitespace(text[upstream - 1])) |
| 838 upstream--; | 849 upstream--; |
| 839 | 850 |
| 840 int downstream = endOffset; | 851 int downstream = endOffset; |
| 841 while ((unsigned)downstream < text.length() && isWhitespace(text[downstream])) | 852 while ((unsigned)downstream < text.length() && isWhitespace(text[downstream])) |
| 842 downstream++; | 853 downstream++; |
| 843 | 854 |
| 844 int length = downstream - upstream; | 855 int length = downstream - upstream; |
| 845 if (!length) | 856 if (!length) |
| (...skipping 203 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1049 } | 1060 } |
| 1050 | 1061 |
| 1051 HTMLBRElement* CompositeEditCommand::appendBlockPlaceholder( | 1062 HTMLBRElement* CompositeEditCommand::appendBlockPlaceholder( |
| 1052 Element* container, | 1063 Element* container, |
| 1053 EditingState* editingState) { | 1064 EditingState* editingState) { |
| 1054 if (!container) | 1065 if (!container) |
| 1055 return nullptr; | 1066 return nullptr; |
| 1056 | 1067 |
| 1057 document().updateStyleAndLayoutIgnorePendingStylesheets(); | 1068 document().updateStyleAndLayoutIgnorePendingStylesheets(); |
| 1058 | 1069 |
| 1059 // Should assert isLayoutBlockFlow || isInlineFlow when deletion improves. See
4244964. | 1070 // Should assert isLayoutBlockFlow || isInlineFlow when deletion improves. See |
| 1071 // 4244964. |
| 1060 DCHECK(container->layoutObject()) << container; | 1072 DCHECK(container->layoutObject()) << container; |
| 1061 | 1073 |
| 1062 HTMLBRElement* placeholder = HTMLBRElement::create(document()); | 1074 HTMLBRElement* placeholder = HTMLBRElement::create(document()); |
| 1063 appendNode(placeholder, container, editingState); | 1075 appendNode(placeholder, container, editingState); |
| 1064 if (editingState->isAborted()) | 1076 if (editingState->isAborted()) |
| 1065 return nullptr; | 1077 return nullptr; |
| 1066 return placeholder; | 1078 return placeholder; |
| 1067 } | 1079 } |
| 1068 | 1080 |
| 1069 HTMLBRElement* CompositeEditCommand::insertBlockPlaceholder( | 1081 HTMLBRElement* CompositeEditCommand::insertBlockPlaceholder( |
| 1070 const Position& pos, | 1082 const Position& pos, |
| 1071 EditingState* editingState) { | 1083 EditingState* editingState) { |
| 1072 if (pos.isNull()) | 1084 if (pos.isNull()) |
| 1073 return nullptr; | 1085 return nullptr; |
| 1074 | 1086 |
| 1075 // Should assert isLayoutBlockFlow || isInlineFlow when deletion improves. See
4244964. | 1087 // Should assert isLayoutBlockFlow || isInlineFlow when deletion improves. See |
| 1088 // 4244964. |
| 1076 DCHECK(pos.anchorNode()->layoutObject()) << pos; | 1089 DCHECK(pos.anchorNode()->layoutObject()) << pos; |
| 1077 | 1090 |
| 1078 HTMLBRElement* placeholder = HTMLBRElement::create(document()); | 1091 HTMLBRElement* placeholder = HTMLBRElement::create(document()); |
| 1079 insertNodeAt(placeholder, pos, editingState); | 1092 insertNodeAt(placeholder, pos, editingState); |
| 1080 if (editingState->isAborted()) | 1093 if (editingState->isAborted()) |
| 1081 return nullptr; | 1094 return nullptr; |
| 1082 return placeholder; | 1095 return placeholder; |
| 1083 } | 1096 } |
| 1084 | 1097 |
| 1085 HTMLBRElement* CompositeEditCommand::addBlockPlaceholderIfNeeded( | 1098 HTMLBRElement* CompositeEditCommand::addBlockPlaceholderIfNeeded( |
| (...skipping 11 matching lines...) Expand all Loading... |
| 1097 // append the placeholder to make sure it follows | 1110 // append the placeholder to make sure it follows |
| 1098 // any unrendered blocks | 1111 // any unrendered blocks |
| 1099 LayoutBlockFlow* block = toLayoutBlockFlow(layoutObject); | 1112 LayoutBlockFlow* block = toLayoutBlockFlow(layoutObject); |
| 1100 if (block->size().height() == 0 || | 1113 if (block->size().height() == 0 || |
| 1101 (block->isListItem() && toLayoutListItem(block)->isEmpty())) | 1114 (block->isListItem() && toLayoutListItem(block)->isEmpty())) |
| 1102 return appendBlockPlaceholder(container, editingState); | 1115 return appendBlockPlaceholder(container, editingState); |
| 1103 | 1116 |
| 1104 return nullptr; | 1117 return nullptr; |
| 1105 } | 1118 } |
| 1106 | 1119 |
| 1107 // Assumes that the position is at a placeholder and does the removal without mu
ch checking. | 1120 // Assumes that the position is at a placeholder and does the removal without |
| 1121 // much checking. |
| 1108 void CompositeEditCommand::removePlaceholderAt(const Position& p) { | 1122 void CompositeEditCommand::removePlaceholderAt(const Position& p) { |
| 1109 DCHECK(lineBreakExistsAtPosition(p)) << p; | 1123 DCHECK(lineBreakExistsAtPosition(p)) << p; |
| 1110 | 1124 |
| 1111 // We are certain that the position is at a line break, but it may be a br or
a preserved newline. | 1125 // We are certain that the position is at a line break, but it may be a br or |
| 1126 // a preserved newline. |
| 1112 if (isHTMLBRElement(*p.anchorNode())) { | 1127 if (isHTMLBRElement(*p.anchorNode())) { |
| 1113 // Removing a BR element won't dispatch synchronous events. | 1128 // Removing a BR element won't dispatch synchronous events. |
| 1114 removeNode(p.anchorNode(), ASSERT_NO_EDITING_ABORT); | 1129 removeNode(p.anchorNode(), ASSERT_NO_EDITING_ABORT); |
| 1115 return; | 1130 return; |
| 1116 } | 1131 } |
| 1117 | 1132 |
| 1118 deleteTextFromNode(toText(p.anchorNode()), p.offsetInContainerNode(), 1); | 1133 deleteTextFromNode(toText(p.anchorNode()), p.offsetInContainerNode(), 1); |
| 1119 } | 1134 } |
| 1120 | 1135 |
| 1121 HTMLElement* CompositeEditCommand::insertNewDefaultParagraphElementAt( | 1136 HTMLElement* CompositeEditCommand::insertNewDefaultParagraphElementAt( |
| 1122 const Position& position, | 1137 const Position& position, |
| 1123 EditingState* editingState) { | 1138 EditingState* editingState) { |
| 1124 HTMLElement* paragraphElement = createDefaultParagraphElement(document()); | 1139 HTMLElement* paragraphElement = createDefaultParagraphElement(document()); |
| 1125 paragraphElement->appendChild(HTMLBRElement::create(document())); | 1140 paragraphElement->appendChild(HTMLBRElement::create(document())); |
| 1126 insertNodeAt(paragraphElement, position, editingState); | 1141 insertNodeAt(paragraphElement, position, editingState); |
| 1127 if (editingState->isAborted()) | 1142 if (editingState->isAborted()) |
| 1128 return nullptr; | 1143 return nullptr; |
| 1129 return paragraphElement; | 1144 return paragraphElement; |
| 1130 } | 1145 } |
| 1131 | 1146 |
| 1132 // If the paragraph is not entirely within it's own block, create one and move t
he paragraph into | 1147 // If the paragraph is not entirely within it's own block, create one and move |
| 1133 // it, and return that block. Otherwise return 0. | 1148 // the paragraph into it, and return that block. Otherwise return 0. |
| 1134 HTMLElement* CompositeEditCommand::moveParagraphContentsToNewBlockIfNecessary( | 1149 HTMLElement* CompositeEditCommand::moveParagraphContentsToNewBlockIfNecessary( |
| 1135 const Position& pos, | 1150 const Position& pos, |
| 1136 EditingState* editingState) { | 1151 EditingState* editingState) { |
| 1137 DCHECK(isEditablePosition(pos)) << pos; | 1152 DCHECK(isEditablePosition(pos)) << pos; |
| 1138 | 1153 |
| 1139 // It's strange that this function is responsible for verifying that pos has n
ot been invalidated | 1154 // It's strange that this function is responsible for verifying that pos has |
| 1140 // by an earlier call to this function. The caller, applyBlockStyle, should d
o this. | 1155 // not been invalidated by an earlier call to this function. The caller, |
| 1156 // applyBlockStyle, should do this. |
| 1141 VisiblePosition visiblePos = | 1157 VisiblePosition visiblePos = |
| 1142 createVisiblePositionDeprecated(pos, VP_DEFAULT_AFFINITY); | 1158 createVisiblePositionDeprecated(pos, VP_DEFAULT_AFFINITY); |
| 1143 VisiblePosition visibleParagraphStart = | 1159 VisiblePosition visibleParagraphStart = |
| 1144 startOfParagraphDeprecated(visiblePos); | 1160 startOfParagraphDeprecated(visiblePos); |
| 1145 VisiblePosition visibleParagraphEnd = endOfParagraphDeprecated(visiblePos); | 1161 VisiblePosition visibleParagraphEnd = endOfParagraphDeprecated(visiblePos); |
| 1146 VisiblePosition next = nextPositionOf(visibleParagraphEnd); | 1162 VisiblePosition next = nextPositionOf(visibleParagraphEnd); |
| 1147 VisiblePosition visibleEnd = next.isNotNull() ? next : visibleParagraphEnd; | 1163 VisiblePosition visibleEnd = next.isNotNull() ? next : visibleParagraphEnd; |
| 1148 | 1164 |
| 1149 Position upstreamStart = | 1165 Position upstreamStart = |
| 1150 mostBackwardCaretPosition(visibleParagraphStart.deepEquivalent()); | 1166 mostBackwardCaretPosition(visibleParagraphStart.deepEquivalent()); |
| 1151 Position upstreamEnd = mostBackwardCaretPosition(visibleEnd.deepEquivalent()); | 1167 Position upstreamEnd = mostBackwardCaretPosition(visibleEnd.deepEquivalent()); |
| 1152 | 1168 |
| 1153 // If there are no VisiblePositions in the same block as pos then | 1169 // If there are no VisiblePositions in the same block as pos then |
| 1154 // upstreamStart will be outside the paragraph | 1170 // upstreamStart will be outside the paragraph |
| 1155 if (comparePositions(pos, upstreamStart) < 0) | 1171 if (comparePositions(pos, upstreamStart) < 0) |
| 1156 return nullptr; | 1172 return nullptr; |
| 1157 | 1173 |
| 1158 // Perform some checks to see if we need to perform work in this function. | 1174 // Perform some checks to see if we need to perform work in this function. |
| 1159 if (isEnclosingBlock(upstreamStart.anchorNode())) { | 1175 if (isEnclosingBlock(upstreamStart.anchorNode())) { |
| 1160 // If the block is the root editable element, always move content to a new b
lock, | 1176 // If the block is the root editable element, always move content to a new |
| 1161 // since it is illegal to modify attributes on the root editable element for
editing. | 1177 // block, since it is illegal to modify attributes on the root editable |
| 1178 // element for editing. |
| 1162 if (upstreamStart.anchorNode() == rootEditableElementOf(upstreamStart)) { | 1179 if (upstreamStart.anchorNode() == rootEditableElementOf(upstreamStart)) { |
| 1163 // If the block is the root editable element and it contains no visible co
ntent, create a new | 1180 // If the block is the root editable element and it contains no visible |
| 1164 // block but don't try and move content into it, since there's nothing for
moveParagraphs to move. | 1181 // content, create a new block but don't try and move content into it, |
| 1182 // since there's nothing for moveParagraphs to move. |
| 1165 if (!hasRenderedNonAnonymousDescendantsWithHeight( | 1183 if (!hasRenderedNonAnonymousDescendantsWithHeight( |
| 1166 upstreamStart.anchorNode()->layoutObject())) | 1184 upstreamStart.anchorNode()->layoutObject())) |
| 1167 return insertNewDefaultParagraphElementAt(upstreamStart, editingState); | 1185 return insertNewDefaultParagraphElementAt(upstreamStart, editingState); |
| 1168 } else if (isEnclosingBlock(upstreamEnd.anchorNode())) { | 1186 } else if (isEnclosingBlock(upstreamEnd.anchorNode())) { |
| 1169 if (!upstreamEnd.anchorNode()->isDescendantOf( | 1187 if (!upstreamEnd.anchorNode()->isDescendantOf( |
| 1170 upstreamStart.anchorNode())) { | 1188 upstreamStart.anchorNode())) { |
| 1171 // If the paragraph end is a descendant of paragraph start, then we need
to run | 1189 // If the paragraph end is a descendant of paragraph start, then we need |
| 1172 // the rest of this function. If not, we can bail here. | 1190 // to run the rest of this function. If not, we can bail here. |
| 1173 return nullptr; | 1191 return nullptr; |
| 1174 } | 1192 } |
| 1175 } else if (enclosingBlock(upstreamEnd.anchorNode()) != | 1193 } else if (enclosingBlock(upstreamEnd.anchorNode()) != |
| 1176 upstreamStart.anchorNode()) { | 1194 upstreamStart.anchorNode()) { |
| 1177 // It should be an ancestor of the paragraph start. | 1195 // It should be an ancestor of the paragraph start. |
| 1178 // We can bail as we have a full block to work with. | 1196 // We can bail as we have a full block to work with. |
| 1179 return nullptr; | 1197 return nullptr; |
| 1180 } else if (isEndOfEditableOrNonEditableContent(visibleEnd)) { | 1198 } else if (isEndOfEditableOrNonEditableContent(visibleEnd)) { |
| 1181 // At the end of the editable region. We can bail here as well. | 1199 // At the end of the editable region. We can bail here as well. |
| 1182 return nullptr; | 1200 return nullptr; |
| (...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1297 outerNode = outerNode->parentNode(); | 1315 outerNode = outerNode->parentNode(); |
| 1298 } | 1316 } |
| 1299 | 1317 |
| 1300 if (!outerNode) | 1318 if (!outerNode) |
| 1301 return; | 1319 return; |
| 1302 | 1320 |
| 1303 Node* startNode = start.anchorNode(); | 1321 Node* startNode = start.anchorNode(); |
| 1304 for (Node* node = | 1322 for (Node* node = |
| 1305 NodeTraversal::nextSkippingChildren(*startNode, outerNode); | 1323 NodeTraversal::nextSkippingChildren(*startNode, outerNode); |
| 1306 node; node = NodeTraversal::nextSkippingChildren(*node, outerNode)) { | 1324 node; node = NodeTraversal::nextSkippingChildren(*node, outerNode)) { |
| 1307 // Move lastNode up in the tree as much as node was moved up in the | 1325 // Move lastNode up in the tree as much as node was moved up in the tree |
| 1308 // tree by NodeTraversal::nextSkippingChildren, so that the relative depth
between | 1326 // by NodeTraversal::nextSkippingChildren, so that the relative depth |
| 1309 // node and the original start node is maintained in the clone. | 1327 // between node and the original start node is maintained in the clone. |
| 1310 while (startNode && lastNode && | 1328 while (startNode && lastNode && |
| 1311 startNode->parentNode() != node->parentNode()) { | 1329 startNode->parentNode() != node->parentNode()) { |
| 1312 startNode = startNode->parentNode(); | 1330 startNode = startNode->parentNode(); |
| 1313 lastNode = lastNode->parentNode(); | 1331 lastNode = lastNode->parentNode(); |
| 1314 } | 1332 } |
| 1315 | 1333 |
| 1316 if (!lastNode || !lastNode->parentNode()) | 1334 if (!lastNode || !lastNode->parentNode()) |
| 1317 return; | 1335 return; |
| 1318 | 1336 |
| 1319 Node* clonedNode = node->cloneNode(true); | 1337 Node* clonedNode = node->cloneNode(true); |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1371 Text* textNode = toText(node); | 1389 Text* textNode = toText(node); |
| 1372 if (textNode->length() == 1) | 1390 if (textNode->length() == 1) |
| 1373 removeNodeAndPruneAncestors(node, editingState, destinationNode); | 1391 removeNodeAndPruneAncestors(node, editingState, destinationNode); |
| 1374 else | 1392 else |
| 1375 deleteTextFromNode(textNode, position.computeOffsetInContainerNode(), | 1393 deleteTextFromNode(textNode, position.computeOffsetInContainerNode(), |
| 1376 1); | 1394 1); |
| 1377 } | 1395 } |
| 1378 } | 1396 } |
| 1379 } | 1397 } |
| 1380 | 1398 |
| 1381 // This is a version of moveParagraph that preserves style by keeping the origin
al markup | 1399 // This is a version of moveParagraph that preserves style by keeping the |
| 1382 // It is currently used only by IndentOutdentCommand but it is meant to be used
in the | 1400 // original markup. It is currently used only by IndentOutdentCommand but it is |
| 1383 // future by several other commands such as InsertList and the align commands. | 1401 // meant to be used in the future by several other commands such as InsertList |
| 1384 // The blockElement parameter is the element to move the paragraph to, | 1402 // and the align commands. |
| 1385 // outerNode is the top element of the paragraph hierarchy. | 1403 // The blockElement parameter is the element to move the paragraph to, outerNode |
| 1404 // is the top element of the paragraph hierarchy. |
| 1386 | 1405 |
| 1387 void CompositeEditCommand::moveParagraphWithClones( | 1406 void CompositeEditCommand::moveParagraphWithClones( |
| 1388 const VisiblePosition& startOfParagraphToMove, | 1407 const VisiblePosition& startOfParagraphToMove, |
| 1389 const VisiblePosition& endOfParagraphToMove, | 1408 const VisiblePosition& endOfParagraphToMove, |
| 1390 HTMLElement* blockElement, | 1409 HTMLElement* blockElement, |
| 1391 Node* outerNode, | 1410 Node* outerNode, |
| 1392 EditingState* editingState) { | 1411 EditingState* editingState) { |
| 1393 DCHECK(outerNode); | 1412 DCHECK(outerNode); |
| 1394 DCHECK(blockElement); | 1413 DCHECK(blockElement); |
| 1395 | 1414 |
| 1396 VisiblePosition beforeParagraph = previousPositionOf(startOfParagraphToMove); | 1415 VisiblePosition beforeParagraph = previousPositionOf(startOfParagraphToMove); |
| 1397 VisiblePosition afterParagraph = nextPositionOf(endOfParagraphToMove); | 1416 VisiblePosition afterParagraph = nextPositionOf(endOfParagraphToMove); |
| 1398 | 1417 |
| 1399 // We upstream() the end and downstream() the start so that we don't include c
ollapsed whitespace in the move. | 1418 // We upstream() the end and downstream() the start so that we don't include |
| 1400 // When we paste a fragment, spaces after the end and before the start are tre
ated as though they were rendered. | 1419 // collapsed whitespace in the move. When we paste a fragment, spaces after |
| 1420 // the end and before the start are treated as though they were rendered. |
| 1401 Position start = | 1421 Position start = |
| 1402 mostForwardCaretPosition(startOfParagraphToMove.deepEquivalent()); | 1422 mostForwardCaretPosition(startOfParagraphToMove.deepEquivalent()); |
| 1403 Position end = | 1423 Position end = |
| 1404 startOfParagraphToMove.deepEquivalent() == | 1424 startOfParagraphToMove.deepEquivalent() == |
| 1405 endOfParagraphToMove.deepEquivalent() | 1425 endOfParagraphToMove.deepEquivalent() |
| 1406 ? start | 1426 ? start |
| 1407 : mostBackwardCaretPosition(endOfParagraphToMove.deepEquivalent()); | 1427 : mostBackwardCaretPosition(endOfParagraphToMove.deepEquivalent()); |
| 1408 if (comparePositions(start, end) > 0) | 1428 if (comparePositions(start, end) > 0) |
| 1409 end = start; | 1429 end = start; |
| 1410 | 1430 |
| 1411 cloneParagraphUnderNewElement(start, end, outerNode, blockElement, | 1431 cloneParagraphUnderNewElement(start, end, outerNode, blockElement, |
| 1412 editingState); | 1432 editingState); |
| 1413 | 1433 |
| 1414 setEndingSelection(createVisibleSelectionDeprecated(start, end)); | 1434 setEndingSelection(createVisibleSelectionDeprecated(start, end)); |
| 1415 deleteSelection(editingState, false, false, false); | 1435 deleteSelection(editingState, false, false, false); |
| 1416 if (editingState->isAborted()) | 1436 if (editingState->isAborted()) |
| 1417 return; | 1437 return; |
| 1418 | 1438 |
| 1419 // There are bugs in deletion when it removes a fully selected table/list. | 1439 // There are bugs in deletion when it removes a fully selected table/list. |
| 1420 // It expands and removes the entire table/list, but will let content | 1440 // It expands and removes the entire table/list, but will let content |
| 1421 // before and after the table/list collapse onto one line. | 1441 // before and after the table/list collapse onto one line. |
| 1422 | 1442 |
| 1423 cleanupAfterDeletion(editingState); | 1443 cleanupAfterDeletion(editingState); |
| 1424 if (editingState->isAborted()) | 1444 if (editingState->isAborted()) |
| 1425 return; | 1445 return; |
| 1426 | 1446 |
| 1427 // Add a br if pruning an empty block level element caused a collapse. For ex
ample: | 1447 // Add a br if pruning an empty block level element caused a collapse. For |
| 1448 // example: |
| 1428 // foo^ | 1449 // foo^ |
| 1429 // <div>bar</div> | 1450 // <div>bar</div> |
| 1430 // baz | 1451 // baz |
| 1431 // Imagine moving 'bar' to ^. 'bar' will be deleted and its div pruned. That
would | 1452 // Imagine moving 'bar' to ^. 'bar' will be deleted and its div pruned. That |
| 1432 // cause 'baz' to collapse onto the line with 'foobar' unless we insert a br. | 1453 // would cause 'baz' to collapse onto the line with 'foobar' unless we insert |
| 1433 // Must recononicalize these two VisiblePositions after the pruning above. | 1454 // a br. Must recononicalize these two VisiblePositions after the pruning |
| 1455 // above. |
| 1434 // TODO(yosin): We should abort when |beforeParagraph| is a orphan when | 1456 // TODO(yosin): We should abort when |beforeParagraph| is a orphan when |
| 1435 // we have a sample. | 1457 // we have a sample. |
| 1436 beforeParagraph = | 1458 beforeParagraph = |
| 1437 createVisiblePositionDeprecated(beforeParagraph.deepEquivalent()); | 1459 createVisiblePositionDeprecated(beforeParagraph.deepEquivalent()); |
| 1438 if (afterParagraph.isOrphan()) { | 1460 if (afterParagraph.isOrphan()) { |
| 1439 editingState->abort(); | 1461 editingState->abort(); |
| 1440 return; | 1462 return; |
| 1441 } | 1463 } |
| 1442 afterParagraph = | 1464 afterParagraph = |
| 1443 createVisiblePositionDeprecated(afterParagraph.deepEquivalent()); | 1465 createVisiblePositionDeprecated(afterParagraph.deepEquivalent()); |
| 1444 | 1466 |
| 1445 if (beforeParagraph.isNotNull() && | 1467 if (beforeParagraph.isNotNull() && |
| 1446 !isDisplayInsideTable(beforeParagraph.deepEquivalent().anchorNode()) && | 1468 !isDisplayInsideTable(beforeParagraph.deepEquivalent().anchorNode()) && |
| 1447 ((!isEndOfParagraphDeprecated(beforeParagraph) && | 1469 ((!isEndOfParagraphDeprecated(beforeParagraph) && |
| 1448 !isStartOfParagraphDeprecated(beforeParagraph)) || | 1470 !isStartOfParagraphDeprecated(beforeParagraph)) || |
| 1449 beforeParagraph.deepEquivalent() == afterParagraph.deepEquivalent())) { | 1471 beforeParagraph.deepEquivalent() == afterParagraph.deepEquivalent())) { |
| 1450 // FIXME: Trim text between beforeParagraph and afterParagraph if they aren'
t equal. | 1472 // FIXME: Trim text between beforeParagraph and afterParagraph if they |
| 1473 // aren't equal. |
| 1451 insertNodeAt(HTMLBRElement::create(document()), | 1474 insertNodeAt(HTMLBRElement::create(document()), |
| 1452 beforeParagraph.deepEquivalent(), editingState); | 1475 beforeParagraph.deepEquivalent(), editingState); |
| 1453 } | 1476 } |
| 1454 } | 1477 } |
| 1455 | 1478 |
| 1456 void CompositeEditCommand::moveParagraph( | 1479 void CompositeEditCommand::moveParagraph( |
| 1457 const VisiblePosition& startOfParagraphToMove, | 1480 const VisiblePosition& startOfParagraphToMove, |
| 1458 const VisiblePosition& endOfParagraphToMove, | 1481 const VisiblePosition& endOfParagraphToMove, |
| 1459 const VisiblePosition& destination, | 1482 const VisiblePosition& destination, |
| 1460 EditingState* editingState, | 1483 EditingState* editingState, |
| (...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1520 } | 1543 } |
| 1521 | 1544 |
| 1522 RelocatablePosition beforeParagraphPosition( | 1545 RelocatablePosition beforeParagraphPosition( |
| 1523 previousPositionOfDeprecated(startOfParagraphToMove, | 1546 previousPositionOfDeprecated(startOfParagraphToMove, |
| 1524 CannotCrossEditingBoundary) | 1547 CannotCrossEditingBoundary) |
| 1525 .deepEquivalent()); | 1548 .deepEquivalent()); |
| 1526 RelocatablePosition afterParagraphPosition( | 1549 RelocatablePosition afterParagraphPosition( |
| 1527 nextPositionOfDeprecated(endOfParagraphToMove, CannotCrossEditingBoundary) | 1550 nextPositionOfDeprecated(endOfParagraphToMove, CannotCrossEditingBoundary) |
| 1528 .deepEquivalent()); | 1551 .deepEquivalent()); |
| 1529 | 1552 |
| 1530 // We upstream() the end and downstream() the start so that we don't include c
ollapsed whitespace in the move. | 1553 // We upstream() the end and downstream() the start so that we don't include |
| 1531 // When we paste a fragment, spaces after the end and before the start are tre
ated as though they were rendered. | 1554 // collapsed whitespace in the move. When we paste a fragment, spaces after |
| 1555 // the end and before the start are treated as though they were rendered. |
| 1532 Position start = | 1556 Position start = |
| 1533 mostForwardCaretPosition(startOfParagraphToMove.deepEquivalent()); | 1557 mostForwardCaretPosition(startOfParagraphToMove.deepEquivalent()); |
| 1534 Position end = | 1558 Position end = |
| 1535 mostBackwardCaretPosition(endOfParagraphToMove.deepEquivalent()); | 1559 mostBackwardCaretPosition(endOfParagraphToMove.deepEquivalent()); |
| 1536 | 1560 |
| 1537 // FIXME: This is an inefficient way to preserve style on nodes in the paragra
ph to move. It | 1561 // FIXME: This is an inefficient way to preserve style on nodes in the |
| 1538 // shouldn't matter though, since moved paragraphs will usually be quite small
. | 1562 // paragraph to move. It shouldn't matter though, since moved paragraphs will |
| 1563 // usually be quite small. |
| 1539 DocumentFragment* fragment = | 1564 DocumentFragment* fragment = |
| 1540 startOfParagraphToMove.deepEquivalent() != | 1565 startOfParagraphToMove.deepEquivalent() != |
| 1541 endOfParagraphToMove.deepEquivalent() | 1566 endOfParagraphToMove.deepEquivalent() |
| 1542 ? createFragmentFromMarkup( | 1567 ? createFragmentFromMarkup( |
| 1543 document(), | 1568 document(), |
| 1544 createMarkup(start.parentAnchoredEquivalent(), | 1569 createMarkup(start.parentAnchoredEquivalent(), |
| 1545 end.parentAnchoredEquivalent(), | 1570 end.parentAnchoredEquivalent(), |
| 1546 DoNotAnnotateForInterchange, | 1571 DoNotAnnotateForInterchange, |
| 1547 ConvertBlocksToInlines::Convert, DoNotResolveURLs, | 1572 ConvertBlocksToInlines::Convert, DoNotResolveURLs, |
| 1548 constrainingAncestor), | 1573 constrainingAncestor), |
| 1549 "") | 1574 "") |
| 1550 : nullptr; | 1575 : nullptr; |
| 1551 | 1576 |
| 1552 // A non-empty paragraph's style is moved when we copy and move it. We don't
move | 1577 // A non-empty paragraph's style is moved when we copy and move it. We don't |
| 1553 // anything if we're given an empty paragraph, but an empty paragraph can have
style | 1578 // move anything if we're given an empty paragraph, but an empty paragraph can |
| 1554 // too, <div><b><br></b></div> for example. Save it so that we can preserve i
t later. | 1579 // have style too, <div><b><br></b></div> for example. Save it so that we can |
| 1580 // preserve it later. |
| 1555 EditingStyle* styleInEmptyParagraph = nullptr; | 1581 EditingStyle* styleInEmptyParagraph = nullptr; |
| 1556 if (startOfParagraphToMove.deepEquivalent() == | 1582 if (startOfParagraphToMove.deepEquivalent() == |
| 1557 endOfParagraphToMove.deepEquivalent() && | 1583 endOfParagraphToMove.deepEquivalent() && |
| 1558 shouldPreserveStyle == PreserveStyle) { | 1584 shouldPreserveStyle == PreserveStyle) { |
| 1559 styleInEmptyParagraph = | 1585 styleInEmptyParagraph = |
| 1560 EditingStyle::create(startOfParagraphToMove.deepEquivalent()); | 1586 EditingStyle::create(startOfParagraphToMove.deepEquivalent()); |
| 1561 styleInEmptyParagraph->mergeTypingStyle(&document()); | 1587 styleInEmptyParagraph->mergeTypingStyle(&document()); |
| 1562 // The moved paragraph should assume the block style of the destination. | 1588 // The moved paragraph should assume the block style of the destination. |
| 1563 styleInEmptyParagraph->removeBlockProperties(); | 1589 styleInEmptyParagraph->removeBlockProperties(); |
| 1564 } | 1590 } |
| 1565 | 1591 |
| 1566 // FIXME (5098931): We should add a new insert action "WebViewInsertActionMove
d" and call shouldInsertFragment here. | 1592 // FIXME (5098931): We should add a new insert action |
| 1593 // "WebViewInsertActionMoved" and call shouldInsertFragment here. |
| 1567 | 1594 |
| 1568 setEndingSelection(createVisibleSelectionDeprecated(start, end)); | 1595 setEndingSelection(createVisibleSelectionDeprecated(start, end)); |
| 1569 document() | 1596 document() |
| 1570 .frame() | 1597 .frame() |
| 1571 ->spellChecker() | 1598 ->spellChecker() |
| 1572 .clearMisspellingsAndBadGrammarForMovingParagraphs(endingSelection()); | 1599 .clearMisspellingsAndBadGrammarForMovingParagraphs(endingSelection()); |
| 1573 deleteSelection(editingState, false, false, false); | 1600 deleteSelection(editingState, false, false, false); |
| 1574 if (editingState->isAborted()) | 1601 if (editingState->isAborted()) |
| 1575 return; | 1602 return; |
| 1576 | 1603 |
| 1577 DCHECK(destination.deepEquivalent().isConnected()) << destination; | 1604 DCHECK(destination.deepEquivalent().isConnected()) << destination; |
| 1578 cleanupAfterDeletion(editingState, destination); | 1605 cleanupAfterDeletion(editingState, destination); |
| 1579 if (editingState->isAborted()) | 1606 if (editingState->isAborted()) |
| 1580 return; | 1607 return; |
| 1581 DCHECK(destination.deepEquivalent().isConnected()) << destination; | 1608 DCHECK(destination.deepEquivalent().isConnected()) << destination; |
| 1582 | 1609 |
| 1583 // Add a br if pruning an empty block level element caused a collapse. For exa
mple: | 1610 // Add a br if pruning an empty block level element caused a collapse. For |
| 1611 // example: |
| 1584 // foo^ | 1612 // foo^ |
| 1585 // <div>bar</div> | 1613 // <div>bar</div> |
| 1586 // baz | 1614 // baz |
| 1587 // Imagine moving 'bar' to ^. 'bar' will be deleted and its div pruned. That w
ould | 1615 // Imagine moving 'bar' to ^. 'bar' will be deleted and its div pruned. That |
| 1588 // cause 'baz' to collapse onto the line with 'foobar' unless we insert a br. | 1616 // would cause 'baz' to collapse onto the line with 'foobar' unless we insert |
| 1589 // Must recononicalize these two VisiblePositions after the pruning above. | 1617 // a br. Must recononicalize these two VisiblePositions after the pruning |
| 1618 // above. |
| 1590 VisiblePosition beforeParagraph = | 1619 VisiblePosition beforeParagraph = |
| 1591 createVisiblePositionDeprecated(beforeParagraphPosition.position()); | 1620 createVisiblePositionDeprecated(beforeParagraphPosition.position()); |
| 1592 VisiblePosition afterParagraph = | 1621 VisiblePosition afterParagraph = |
| 1593 createVisiblePositionDeprecated(afterParagraphPosition.position()); | 1622 createVisiblePositionDeprecated(afterParagraphPosition.position()); |
| 1594 if (beforeParagraph.isNotNull() && | 1623 if (beforeParagraph.isNotNull() && |
| 1595 (!isEndOfParagraphDeprecated(beforeParagraph) || | 1624 (!isEndOfParagraphDeprecated(beforeParagraph) || |
| 1596 beforeParagraph.deepEquivalent() == afterParagraph.deepEquivalent())) { | 1625 beforeParagraph.deepEquivalent() == afterParagraph.deepEquivalent())) { |
| 1597 // FIXME: Trim text between beforeParagraph and afterParagraph if they aren'
t equal. | 1626 // FIXME: Trim text between beforeParagraph and afterParagraph if they |
| 1627 // aren't equal. |
| 1598 insertNodeAt(HTMLBRElement::create(document()), | 1628 insertNodeAt(HTMLBRElement::create(document()), |
| 1599 beforeParagraph.deepEquivalent(), editingState); | 1629 beforeParagraph.deepEquivalent(), editingState); |
| 1600 if (editingState->isAborted()) | 1630 if (editingState->isAborted()) |
| 1601 return; | 1631 return; |
| 1602 // Need an updateLayout here in case inserting the br has split a text node. | 1632 // Need an updateLayout here in case inserting the br has split a text node. |
| 1603 document().updateStyleAndLayoutIgnorePendingStylesheets(); | 1633 document().updateStyleAndLayoutIgnorePendingStylesheets(); |
| 1604 } | 1634 } |
| 1605 | 1635 |
| 1606 destinationIndex = TextIterator::rangeLength( | 1636 destinationIndex = TextIterator::rangeLength( |
| 1607 Position::firstPositionInNode(document().documentElement()), | 1637 Position::firstPositionInNode(document().documentElement()), |
| (...skipping 16 matching lines...) Expand all Loading... |
| 1624 ReplaceSelectionCommand::create(document(), fragment, options), | 1654 ReplaceSelectionCommand::create(document(), fragment, options), |
| 1625 editingState); | 1655 editingState); |
| 1626 if (editingState->isAborted()) | 1656 if (editingState->isAborted()) |
| 1627 return; | 1657 return; |
| 1628 | 1658 |
| 1629 document() | 1659 document() |
| 1630 .frame() | 1660 .frame() |
| 1631 ->spellChecker() | 1661 ->spellChecker() |
| 1632 .markMisspellingsAndBadGrammarForMovingParagraphs(endingSelection()); | 1662 .markMisspellingsAndBadGrammarForMovingParagraphs(endingSelection()); |
| 1633 | 1663 |
| 1634 // If the selection is in an empty paragraph, restore styles from the old empt
y paragraph to the new empty paragraph. | 1664 // If the selection is in an empty paragraph, restore styles from the old |
| 1665 // empty paragraph to the new empty paragraph. |
| 1635 bool selectionIsEmptyParagraph = | 1666 bool selectionIsEmptyParagraph = |
| 1636 endingSelection().isCaret() && | 1667 endingSelection().isCaret() && |
| 1637 isStartOfParagraphDeprecated( | 1668 isStartOfParagraphDeprecated( |
| 1638 endingSelection().visibleStartDeprecated()) && | 1669 endingSelection().visibleStartDeprecated()) && |
| 1639 isEndOfParagraphDeprecated(endingSelection().visibleStartDeprecated()); | 1670 isEndOfParagraphDeprecated(endingSelection().visibleStartDeprecated()); |
| 1640 if (styleInEmptyParagraph && selectionIsEmptyParagraph) { | 1671 if (styleInEmptyParagraph && selectionIsEmptyParagraph) { |
| 1641 applyStyle(styleInEmptyParagraph, editingState); | 1672 applyStyle(styleInEmptyParagraph, editingState); |
| 1642 if (editingState->isAborted()) | 1673 if (editingState->isAborted()) |
| 1643 return; | 1674 return; |
| 1644 } | 1675 } |
| (...skipping 29 matching lines...) Expand all Loading... |
| 1674 bool CompositeEditCommand::breakOutOfEmptyListItem(EditingState* editingState) { | 1705 bool CompositeEditCommand::breakOutOfEmptyListItem(EditingState* editingState) { |
| 1675 Node* emptyListItem = | 1706 Node* emptyListItem = |
| 1676 enclosingEmptyListItem(endingSelection().visibleStartDeprecated()); | 1707 enclosingEmptyListItem(endingSelection().visibleStartDeprecated()); |
| 1677 if (!emptyListItem) | 1708 if (!emptyListItem) |
| 1678 return false; | 1709 return false; |
| 1679 | 1710 |
| 1680 EditingStyle* style = EditingStyle::create(endingSelection().start()); | 1711 EditingStyle* style = EditingStyle::create(endingSelection().start()); |
| 1681 style->mergeTypingStyle(&document()); | 1712 style->mergeTypingStyle(&document()); |
| 1682 | 1713 |
| 1683 ContainerNode* listNode = emptyListItem->parentNode(); | 1714 ContainerNode* listNode = emptyListItem->parentNode(); |
| 1684 // FIXME: Can't we do something better when the immediate parent wasn't a list
node? | 1715 // FIXME: Can't we do something better when the immediate parent wasn't a list |
| 1716 // node? |
| 1685 if (!listNode || | 1717 if (!listNode || |
| 1686 (!isHTMLUListElement(*listNode) && !isHTMLOListElement(*listNode)) || | 1718 (!isHTMLUListElement(*listNode) && !isHTMLOListElement(*listNode)) || |
| 1687 !hasEditableStyle(*listNode) || | 1719 !hasEditableStyle(*listNode) || |
| 1688 listNode == rootEditableElement(*emptyListItem)) | 1720 listNode == rootEditableElement(*emptyListItem)) |
| 1689 return false; | 1721 return false; |
| 1690 | 1722 |
| 1691 HTMLElement* newBlock = nullptr; | 1723 HTMLElement* newBlock = nullptr; |
| 1692 if (ContainerNode* blockEnclosingList = listNode->parentNode()) { | 1724 if (ContainerNode* blockEnclosingList = listNode->parentNode()) { |
| 1693 if (isHTMLLIElement( | 1725 if (isHTMLLIElement( |
| 1694 *blockEnclosingList)) { // listNode is inside another list item | 1726 *blockEnclosingList)) { // listNode is inside another list item |
| 1695 if (visiblePositionAfterNode(*blockEnclosingList).deepEquivalent() == | 1727 if (visiblePositionAfterNode(*blockEnclosingList).deepEquivalent() == |
| 1696 visiblePositionAfterNode(*listNode).deepEquivalent()) { | 1728 visiblePositionAfterNode(*listNode).deepEquivalent()) { |
| 1697 // If listNode appears at the end of the outer list item, then move list
Node outside of this list item | 1729 // If listNode appears at the end of the outer list item, then move |
| 1698 // e.g. <ul><li>hello <ul><li><br></li></ul> </li></ul> should become <u
l><li>hello</li> <ul><li><br></li></ul> </ul> after this section | 1730 // listNode outside of this list item, e.g. |
| 1699 // If listNode does NOT appear at the end, then we should consider it as
a regular paragraph. | 1731 // <ul><li>hello <ul><li><br></li></ul> </li></ul> |
| 1700 // e.g. <ul><li> <ul><li><br></li></ul> hello</li></ul> should become <u
l><li> <div><br></div> hello</li></ul> at the end | 1732 // should become |
| 1733 // <ul><li>hello</li> <ul><li><br></li></ul> </ul> |
| 1734 // after this section. |
| 1735 // |
| 1736 // If listNode does NOT appear at the end, then we should consider it as |
| 1737 // a regular paragraph, e.g. |
| 1738 // <ul><li> <ul><li><br></li></ul> hello</li></ul> |
| 1739 // should become |
| 1740 // <ul><li> <div><br></div> hello</li></ul> |
| 1741 // at the end |
| 1701 splitElement(toElement(blockEnclosingList), listNode); | 1742 splitElement(toElement(blockEnclosingList), listNode); |
| 1702 removeNodePreservingChildren(listNode->parentNode(), editingState); | 1743 removeNodePreservingChildren(listNode->parentNode(), editingState); |
| 1703 if (editingState->isAborted()) | 1744 if (editingState->isAborted()) |
| 1704 return false; | 1745 return false; |
| 1705 newBlock = HTMLLIElement::create(document()); | 1746 newBlock = HTMLLIElement::create(document()); |
| 1706 } | 1747 } |
| 1707 // If listNode does NOT appear at the end of the outer list item, then beh
ave as if in a regular paragraph. | 1748 // If listNode does NOT appear at the end of the outer list item, then |
| 1749 // behave as if in a regular paragraph. |
| 1708 } else if (isHTMLOListElement(*blockEnclosingList) || | 1750 } else if (isHTMLOListElement(*blockEnclosingList) || |
| 1709 isHTMLUListElement(*blockEnclosingList)) { | 1751 isHTMLUListElement(*blockEnclosingList)) { |
| 1710 newBlock = HTMLLIElement::create(document()); | 1752 newBlock = HTMLLIElement::create(document()); |
| 1711 } | 1753 } |
| 1712 } | 1754 } |
| 1713 if (!newBlock) | 1755 if (!newBlock) |
| 1714 newBlock = createDefaultParagraphElement(document()); | 1756 newBlock = createDefaultParagraphElement(document()); |
| 1715 | 1757 |
| 1716 Node* previousListNode = | 1758 Node* previousListNode = |
| 1717 emptyListItem->isElementNode() | 1759 emptyListItem->isElementNode() |
| 1718 ? ElementTraversal::previousSibling(*emptyListItem) | 1760 ? ElementTraversal::previousSibling(*emptyListItem) |
| 1719 : emptyListItem->previousSibling(); | 1761 : emptyListItem->previousSibling(); |
| 1720 Node* nextListNode = emptyListItem->isElementNode() | 1762 Node* nextListNode = emptyListItem->isElementNode() |
| 1721 ? ElementTraversal::nextSibling(*emptyListItem) | 1763 ? ElementTraversal::nextSibling(*emptyListItem) |
| 1722 : emptyListItem->nextSibling(); | 1764 : emptyListItem->nextSibling(); |
| 1723 if (isListItem(nextListNode) || isHTMLListElement(nextListNode)) { | 1765 if (isListItem(nextListNode) || isHTMLListElement(nextListNode)) { |
| 1724 // If emptyListItem follows another list item or nested list, split the list
node. | 1766 // If emptyListItem follows another list item or nested list, split the list |
| 1767 // node. |
| 1725 if (isListItem(previousListNode) || isHTMLListElement(previousListNode)) | 1768 if (isListItem(previousListNode) || isHTMLListElement(previousListNode)) |
| 1726 splitElement(toElement(listNode), emptyListItem); | 1769 splitElement(toElement(listNode), emptyListItem); |
| 1727 | 1770 |
| 1728 // If emptyListItem is followed by other list item or nested list, then inse
rt newBlock before the list node. | 1771 // If emptyListItem is followed by other list item or nested list, then |
| 1729 // Because we have splitted the element, emptyListItem is the first element
in the list node. | 1772 // insert newBlock before the list node. Because we have splitted the |
| 1773 // element, emptyListItem is the first element in the list node. |
| 1730 // i.e. insert newBlock before ul or ol whose first element is emptyListItem | 1774 // i.e. insert newBlock before ul or ol whose first element is emptyListItem |
| 1731 insertNodeBefore(newBlock, listNode, editingState); | 1775 insertNodeBefore(newBlock, listNode, editingState); |
| 1732 if (editingState->isAborted()) | 1776 if (editingState->isAborted()) |
| 1733 return false; | 1777 return false; |
| 1734 removeNode(emptyListItem, editingState); | 1778 removeNode(emptyListItem, editingState); |
| 1735 if (editingState->isAborted()) | 1779 if (editingState->isAborted()) |
| 1736 return false; | 1780 return false; |
| 1737 } else { | 1781 } else { |
| 1738 // When emptyListItem does not follow any list item or nested list, insert n
ewBlock after the enclosing list node. | 1782 // When emptyListItem does not follow any list item or nested list, insert |
| 1739 // Remove the enclosing node if emptyListItem is the only child; otherwise j
ust remove emptyListItem. | 1783 // newBlock after the enclosing list node. Remove the enclosing node if |
| 1784 // emptyListItem is the only child; otherwise just remove emptyListItem. |
| 1740 insertNodeAfter(newBlock, listNode, editingState); | 1785 insertNodeAfter(newBlock, listNode, editingState); |
| 1741 if (editingState->isAborted()) | 1786 if (editingState->isAborted()) |
| 1742 return false; | 1787 return false; |
| 1743 removeNode( | 1788 removeNode( |
| 1744 isListItem(previousListNode) || isHTMLListElement(previousListNode) | 1789 isListItem(previousListNode) || isHTMLListElement(previousListNode) |
| 1745 ? emptyListItem | 1790 ? emptyListItem |
| 1746 : listNode, | 1791 : listNode, |
| 1747 editingState); | 1792 editingState); |
| 1748 if (editingState->isAborted()) | 1793 if (editingState->isAborted()) |
| 1749 return false; | 1794 return false; |
| 1750 } | 1795 } |
| 1751 | 1796 |
| 1752 appendBlockPlaceholder(newBlock, editingState); | 1797 appendBlockPlaceholder(newBlock, editingState); |
| 1753 if (editingState->isAborted()) | 1798 if (editingState->isAborted()) |
| 1754 return false; | 1799 return false; |
| 1755 setEndingSelection(createVisibleSelectionDeprecated( | 1800 setEndingSelection(createVisibleSelectionDeprecated( |
| 1756 Position::firstPositionInNode(newBlock), TextAffinity::Downstream, | 1801 Position::firstPositionInNode(newBlock), TextAffinity::Downstream, |
| 1757 endingSelection().isDirectional())); | 1802 endingSelection().isDirectional())); |
| 1758 | 1803 |
| 1759 style->prepareToApplyAt(endingSelection().start()); | 1804 style->prepareToApplyAt(endingSelection().start()); |
| 1760 if (!style->isEmpty()) { | 1805 if (!style->isEmpty()) { |
| 1761 applyStyle(style, editingState); | 1806 applyStyle(style, editingState); |
| 1762 if (editingState->isAborted()) | 1807 if (editingState->isAborted()) |
| 1763 return false; | 1808 return false; |
| 1764 } | 1809 } |
| 1765 | 1810 |
| 1766 return true; | 1811 return true; |
| 1767 } | 1812 } |
| 1768 | 1813 |
| 1769 // If the caret is in an empty quoted paragraph, and either there is nothing bef
ore that | 1814 // If the caret is in an empty quoted paragraph, and either there is nothing |
| 1770 // paragraph, or what is before is unquoted, and the user presses delete, unquot
e that paragraph. | 1815 // before that paragraph, or what is before is unquoted, and the user presses |
| 1816 // delete, unquote that paragraph. |
| 1771 bool CompositeEditCommand::breakOutOfEmptyMailBlockquotedParagraph( | 1817 bool CompositeEditCommand::breakOutOfEmptyMailBlockquotedParagraph( |
| 1772 EditingState* editingState) { | 1818 EditingState* editingState) { |
| 1773 if (!endingSelection().isCaret()) | 1819 if (!endingSelection().isCaret()) |
| 1774 return false; | 1820 return false; |
| 1775 | 1821 |
| 1776 VisiblePosition caret = endingSelection().visibleStartDeprecated(); | 1822 VisiblePosition caret = endingSelection().visibleStartDeprecated(); |
| 1777 HTMLQuoteElement* highestBlockquote = | 1823 HTMLQuoteElement* highestBlockquote = |
| 1778 toHTMLQuoteElement(highestEnclosingNodeOfType( | 1824 toHTMLQuoteElement(highestEnclosingNodeOfType( |
| 1779 caret.deepEquivalent(), &isMailHTMLBlockquoteElement)); | 1825 caret.deepEquivalent(), &isMailHTMLBlockquoteElement)); |
| 1780 if (!highestBlockquote) | 1826 if (!highestBlockquote) |
| 1781 return false; | 1827 return false; |
| 1782 | 1828 |
| 1783 if (!isStartOfParagraphDeprecated(caret) || | 1829 if (!isStartOfParagraphDeprecated(caret) || |
| 1784 !isEndOfParagraphDeprecated(caret)) | 1830 !isEndOfParagraphDeprecated(caret)) |
| 1785 return false; | 1831 return false; |
| 1786 | 1832 |
| 1787 VisiblePosition previous = | 1833 VisiblePosition previous = |
| 1788 previousPositionOf(caret, CannotCrossEditingBoundary); | 1834 previousPositionOf(caret, CannotCrossEditingBoundary); |
| 1789 // Only move forward if there's nothing before the caret, or if there's unquot
ed content before it. | 1835 // Only move forward if there's nothing before the caret, or if there's |
| 1836 // unquoted content before it. |
| 1790 if (enclosingNodeOfType(previous.deepEquivalent(), | 1837 if (enclosingNodeOfType(previous.deepEquivalent(), |
| 1791 &isMailHTMLBlockquoteElement)) | 1838 &isMailHTMLBlockquoteElement)) |
| 1792 return false; | 1839 return false; |
| 1793 | 1840 |
| 1794 HTMLBRElement* br = HTMLBRElement::create(document()); | 1841 HTMLBRElement* br = HTMLBRElement::create(document()); |
| 1795 // We want to replace this quoted paragraph with an unquoted one, so insert a
br | 1842 // We want to replace this quoted paragraph with an unquoted one, so insert a |
| 1796 // to hold the caret before the highest blockquote. | 1843 // br to hold the caret before the highest blockquote. |
| 1797 insertNodeBefore(br, highestBlockquote, editingState); | 1844 insertNodeBefore(br, highestBlockquote, editingState); |
| 1798 if (editingState->isAborted()) | 1845 if (editingState->isAborted()) |
| 1799 return false; | 1846 return false; |
| 1800 VisiblePosition atBR = VisiblePosition::beforeNode(br); | 1847 VisiblePosition atBR = VisiblePosition::beforeNode(br); |
| 1801 // If the br we inserted collapsed, for example foo<br><blockquote>...</blockq
uote>, insert | 1848 // If the br we inserted collapsed, for example: |
| 1802 // a second one. | 1849 // foo<br><blockquote>...</blockquote> |
| 1850 // insert a second one. |
| 1803 if (!isStartOfParagraphDeprecated(atBR)) { | 1851 if (!isStartOfParagraphDeprecated(atBR)) { |
| 1804 insertNodeBefore(HTMLBRElement::create(document()), br, editingState); | 1852 insertNodeBefore(HTMLBRElement::create(document()), br, editingState); |
| 1805 if (editingState->isAborted()) | 1853 if (editingState->isAborted()) |
| 1806 return false; | 1854 return false; |
| 1807 } | 1855 } |
| 1808 setEndingSelection(createVisibleSelectionDeprecated( | 1856 setEndingSelection(createVisibleSelectionDeprecated( |
| 1809 atBR, endingSelection().isDirectional())); | 1857 atBR, endingSelection().isDirectional())); |
| 1810 | 1858 |
| 1811 // If this is an empty paragraph there must be a line break here. | 1859 // If this is an empty paragraph there must be a line break here. |
| 1812 if (!lineBreakExistsAtVisiblePosition(caret)) | 1860 if (!lineBreakExistsAtVisiblePosition(caret)) |
| 1813 return false; | 1861 return false; |
| 1814 | 1862 |
| 1815 Position caretPos(mostForwardCaretPosition(caret.deepEquivalent())); | 1863 Position caretPos(mostForwardCaretPosition(caret.deepEquivalent())); |
| 1816 // A line break is either a br or a preserved newline. | 1864 // A line break is either a br or a preserved newline. |
| 1817 DCHECK(isHTMLBRElement(caretPos.anchorNode()) || | 1865 DCHECK(isHTMLBRElement(caretPos.anchorNode()) || |
| 1818 (caretPos.anchorNode()->isTextNode() && | 1866 (caretPos.anchorNode()->isTextNode() && |
| 1819 caretPos.anchorNode()->layoutObject()->style()->preserveNewline())) | 1867 caretPos.anchorNode()->layoutObject()->style()->preserveNewline())) |
| 1820 << caretPos; | 1868 << caretPos; |
| 1821 | 1869 |
| 1822 if (isHTMLBRElement(*caretPos.anchorNode())) { | 1870 if (isHTMLBRElement(*caretPos.anchorNode())) { |
| 1823 removeNodeAndPruneAncestors(caretPos.anchorNode(), editingState); | 1871 removeNodeAndPruneAncestors(caretPos.anchorNode(), editingState); |
| 1824 if (editingState->isAborted()) | 1872 if (editingState->isAborted()) |
| 1825 return false; | 1873 return false; |
| 1826 } else if (caretPos.anchorNode()->isTextNode()) { | 1874 } else if (caretPos.anchorNode()->isTextNode()) { |
| 1827 DCHECK_EQ(caretPos.computeOffsetInContainerNode(), 0); | 1875 DCHECK_EQ(caretPos.computeOffsetInContainerNode(), 0); |
| 1828 Text* textNode = toText(caretPos.anchorNode()); | 1876 Text* textNode = toText(caretPos.anchorNode()); |
| 1829 ContainerNode* parentNode = textNode->parentNode(); | 1877 ContainerNode* parentNode = textNode->parentNode(); |
| 1830 // The preserved newline must be the first thing in the node, since otherwis
e the previous | 1878 // The preserved newline must be the first thing in the node, since |
| 1831 // paragraph would be quoted, and we verified that it wasn't above. | 1879 // otherwise the previous paragraph would be quoted, and we verified that it |
| 1880 // wasn't above. |
| 1832 deleteTextFromNode(textNode, 0, 1); | 1881 deleteTextFromNode(textNode, 0, 1); |
| 1833 prune(parentNode, editingState); | 1882 prune(parentNode, editingState); |
| 1834 if (editingState->isAborted()) | 1883 if (editingState->isAborted()) |
| 1835 return false; | 1884 return false; |
| 1836 } | 1885 } |
| 1837 | 1886 |
| 1838 return true; | 1887 return true; |
| 1839 } | 1888 } |
| 1840 | 1889 |
| 1841 // Operations use this function to avoid inserting content into an anchor when a
t the start or the end of | 1890 // Operations use this function to avoid inserting content into an anchor when |
| 1842 // that anchor, as in NSTextView. | 1891 // at the start or the end of that anchor, as in NSTextView. |
| 1843 // FIXME: This is only an approximation of NSTextViews insertion behavior, which
varies depending on how | 1892 // FIXME: This is only an approximation of NSTextViews insertion behavior, which |
| 1844 // the caret was made. | 1893 // varies depending on how the caret was made. |
| 1845 Position CompositeEditCommand::positionAvoidingSpecialElementBoundary( | 1894 Position CompositeEditCommand::positionAvoidingSpecialElementBoundary( |
| 1846 const Position& original, | 1895 const Position& original, |
| 1847 EditingState* editingState) { | 1896 EditingState* editingState) { |
| 1848 if (original.isNull()) | 1897 if (original.isNull()) |
| 1849 return original; | 1898 return original; |
| 1850 | 1899 |
| 1851 VisiblePosition visiblePos = createVisiblePositionDeprecated(original); | 1900 VisiblePosition visiblePos = createVisiblePositionDeprecated(original); |
| 1852 Element* enclosingAnchor = enclosingAnchorElement(original); | 1901 Element* enclosingAnchor = enclosingAnchorElement(original); |
| 1853 Position result = original; | 1902 Position result = original; |
| 1854 | 1903 |
| 1855 if (!enclosingAnchor) | 1904 if (!enclosingAnchor) |
| 1856 return result; | 1905 return result; |
| 1857 | 1906 |
| 1858 // Don't avoid block level anchors, because that would insert content into the
wrong paragraph. | 1907 // Don't avoid block level anchors, because that would insert content into the |
| 1908 // wrong paragraph. |
| 1859 if (enclosingAnchor && !isEnclosingBlock(enclosingAnchor)) { | 1909 if (enclosingAnchor && !isEnclosingBlock(enclosingAnchor)) { |
| 1860 VisiblePosition firstInAnchor = | 1910 VisiblePosition firstInAnchor = |
| 1861 VisiblePosition::firstPositionInNode(enclosingAnchor); | 1911 VisiblePosition::firstPositionInNode(enclosingAnchor); |
| 1862 VisiblePosition lastInAnchor = | 1912 VisiblePosition lastInAnchor = |
| 1863 VisiblePosition::lastPositionInNode(enclosingAnchor); | 1913 VisiblePosition::lastPositionInNode(enclosingAnchor); |
| 1864 // If visually just after the anchor, insert *inside* the anchor unless it's
the last | 1914 // If visually just after the anchor, insert *inside* the anchor unless it's |
| 1865 // VisiblePosition in the document, to match NSTextView. | 1915 // the last VisiblePosition in the document, to match NSTextView. |
| 1866 if (visiblePos.deepEquivalent() == lastInAnchor.deepEquivalent()) { | 1916 if (visiblePos.deepEquivalent() == lastInAnchor.deepEquivalent()) { |
| 1867 // Make sure anchors are pushed down before avoiding them so that we don't | 1917 // Make sure anchors are pushed down before avoiding them so that we don't |
| 1868 // also avoid structural elements like lists and blocks (5142012). | 1918 // also avoid structural elements like lists and blocks (5142012). |
| 1869 if (original.anchorNode() != enclosingAnchor && | 1919 if (original.anchorNode() != enclosingAnchor && |
| 1870 original.anchorNode()->parentNode() != enclosingAnchor) { | 1920 original.anchorNode()->parentNode() != enclosingAnchor) { |
| 1871 pushAnchorElementDown(enclosingAnchor, editingState); | 1921 pushAnchorElementDown(enclosingAnchor, editingState); |
| 1872 if (editingState->isAborted()) | 1922 if (editingState->isAborted()) |
| 1873 return original; | 1923 return original; |
| 1874 enclosingAnchor = enclosingAnchorElement(original); | 1924 enclosingAnchor = enclosingAnchorElement(original); |
| 1875 if (!enclosingAnchor) | 1925 if (!enclosingAnchor) |
| 1876 return original; | 1926 return original; |
| 1877 } | 1927 } |
| 1878 // Don't insert outside an anchor if doing so would skip over a line break
. It would | 1928 // Don't insert outside an anchor if doing so would skip over a line |
| 1879 // probably be safe to move the line break so that we could still avoid th
e anchor here. | 1929 // break. It would probably be safe to move the line break so that we |
| 1930 // could still avoid the anchor here. |
| 1880 Position downstream( | 1931 Position downstream( |
| 1881 mostForwardCaretPosition(visiblePos.deepEquivalent())); | 1932 mostForwardCaretPosition(visiblePos.deepEquivalent())); |
| 1882 if (lineBreakExistsAtVisiblePosition(visiblePos) && | 1933 if (lineBreakExistsAtVisiblePosition(visiblePos) && |
| 1883 downstream.anchorNode()->isDescendantOf(enclosingAnchor)) | 1934 downstream.anchorNode()->isDescendantOf(enclosingAnchor)) |
| 1884 return original; | 1935 return original; |
| 1885 | 1936 |
| 1886 result = Position::inParentAfterNode(*enclosingAnchor); | 1937 result = Position::inParentAfterNode(*enclosingAnchor); |
| 1887 } | 1938 } |
| 1888 // If visually just before an anchor, insert *outside* the anchor unless it'
s the first | 1939 // If visually just before an anchor, insert *outside* the anchor unless |
| 1889 // VisiblePosition in a paragraph, to match NSTextView. | 1940 // it's the first VisiblePosition in a paragraph, to match NSTextView. |
| 1890 if (visiblePos.deepEquivalent() == firstInAnchor.deepEquivalent()) { | 1941 if (visiblePos.deepEquivalent() == firstInAnchor.deepEquivalent()) { |
| 1891 // Make sure anchors are pushed down before avoiding them so that we don't | 1942 // Make sure anchors are pushed down before avoiding them so that we don't |
| 1892 // also avoid structural elements like lists and blocks (5142012). | 1943 // also avoid structural elements like lists and blocks (5142012). |
| 1893 if (original.anchorNode() != enclosingAnchor && | 1944 if (original.anchorNode() != enclosingAnchor && |
| 1894 original.anchorNode()->parentNode() != enclosingAnchor) { | 1945 original.anchorNode()->parentNode() != enclosingAnchor) { |
| 1895 pushAnchorElementDown(enclosingAnchor, editingState); | 1946 pushAnchorElementDown(enclosingAnchor, editingState); |
| 1896 if (editingState->isAborted()) | 1947 if (editingState->isAborted()) |
| 1897 return original; | 1948 return original; |
| 1898 enclosingAnchor = enclosingAnchorElement(original); | 1949 enclosingAnchor = enclosingAnchorElement(original); |
| 1899 } | 1950 } |
| 1900 if (!enclosingAnchor) | 1951 if (!enclosingAnchor) |
| 1901 return original; | 1952 return original; |
| 1902 | 1953 |
| 1903 result = Position::inParentBeforeNode(*enclosingAnchor); | 1954 result = Position::inParentBeforeNode(*enclosingAnchor); |
| 1904 } | 1955 } |
| 1905 } | 1956 } |
| 1906 | 1957 |
| 1907 if (result.isNull() || !rootEditableElementOf(result)) | 1958 if (result.isNull() || !rootEditableElementOf(result)) |
| 1908 result = original; | 1959 result = original; |
| 1909 | 1960 |
| 1910 return result; | 1961 return result; |
| 1911 } | 1962 } |
| 1912 | 1963 |
| 1913 // Splits the tree parent by parent until we reach the specified ancestor. We us
e VisiblePositions | 1964 // Splits the tree parent by parent until we reach the specified ancestor. We |
| 1914 // to determine if the split is necessary. Returns the last split node. | 1965 // use VisiblePositions to determine if the split is necessary. Returns the last |
| 1966 // split node. |
| 1915 Node* CompositeEditCommand::splitTreeToNode(Node* start, | 1967 Node* CompositeEditCommand::splitTreeToNode(Node* start, |
| 1916 Node* end, | 1968 Node* end, |
| 1917 bool shouldSplitAncestor) { | 1969 bool shouldSplitAncestor) { |
| 1918 DCHECK(start); | 1970 DCHECK(start); |
| 1919 DCHECK(end); | 1971 DCHECK(end); |
| 1920 DCHECK_NE(start, end); | 1972 DCHECK_NE(start, end); |
| 1921 | 1973 |
| 1922 if (shouldSplitAncestor && end->parentNode()) | 1974 if (shouldSplitAncestor && end->parentNode()) |
| 1923 end = end->parentNode(); | 1975 end = end->parentNode(); |
| 1924 if (!start->isDescendantOf(end)) | 1976 if (!start->isDescendantOf(end)) |
| (...skipping 17 matching lines...) Expand all Loading... |
| 1942 return node; | 1994 return node; |
| 1943 } | 1995 } |
| 1944 | 1996 |
| 1945 DEFINE_TRACE(CompositeEditCommand) { | 1997 DEFINE_TRACE(CompositeEditCommand) { |
| 1946 visitor->trace(m_commands); | 1998 visitor->trace(m_commands); |
| 1947 visitor->trace(m_composition); | 1999 visitor->trace(m_composition); |
| 1948 EditCommand::trace(visitor); | 2000 EditCommand::trace(visitor); |
| 1949 } | 2001 } |
| 1950 | 2002 |
| 1951 } // namespace blink | 2003 } // namespace blink |
| OLD | NEW |