| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. | 2 * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. |
| 3 * Copyright (C) 2010 Google Inc. All rights reserved. | 3 * Copyright (C) 2010 Google Inc. All rights reserved. |
| 4 * | 4 * |
| 5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
| 6 * modification, are permitted provided that the following conditions | 6 * modification, are permitted provided that the following conditions |
| 7 * are met: | 7 * are met: |
| 8 * 1. Redistributions of source code must retain the above copyright | 8 * 1. Redistributions of source code must retain the above copyright |
| 9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
| 10 * 2. Redistributions in binary form must reproduce the above copyright | 10 * 2. Redistributions in binary form must reproduce the above copyright |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 50 m_inlineStyle(inlineStyle) {} | 50 m_inlineStyle(inlineStyle) {} |
| 51 | 51 |
| 52 ApplyBlockElementCommand::ApplyBlockElementCommand(Document& document, | 52 ApplyBlockElementCommand::ApplyBlockElementCommand(Document& document, |
| 53 const QualifiedName& tagName) | 53 const QualifiedName& tagName) |
| 54 : CompositeEditCommand(document), m_tagName(tagName) {} | 54 : CompositeEditCommand(document), m_tagName(tagName) {} |
| 55 | 55 |
| 56 void ApplyBlockElementCommand::doApply(EditingState* editingState) { | 56 void ApplyBlockElementCommand::doApply(EditingState* editingState) { |
| 57 if (!endingSelection().rootEditableElement()) | 57 if (!endingSelection().rootEditableElement()) |
| 58 return; | 58 return; |
| 59 | 59 |
| 60 VisiblePosition visibleEnd = endingSelection().visibleEndDeprecated(); | 60 // ApplyBlockElementCommands are only created directly by editor commands' |
| 61 VisiblePosition visibleStart = endingSelection().visibleStartDeprecated(); | 61 // execution, which updates layout before entering doApply(). |
| 62 DCHECK(!document().needsLayoutTreeUpdate()); |
| 63 |
| 64 VisiblePosition visibleEnd = endingSelection().visibleEnd(); |
| 65 VisiblePosition visibleStart = endingSelection().visibleStart(); |
| 62 if (visibleStart.isNull() || visibleStart.isOrphan() || visibleEnd.isNull() || | 66 if (visibleStart.isNull() || visibleStart.isOrphan() || visibleEnd.isNull() || |
| 63 visibleEnd.isOrphan()) | 67 visibleEnd.isOrphan()) |
| 64 return; | 68 return; |
| 65 | 69 |
| 66 // When a selection ends at the start of a paragraph, we rarely paint | 70 // When a selection ends at the start of a paragraph, we rarely paint |
| 67 // the selection gap before that paragraph, because there often is no gap. | 71 // the selection gap before that paragraph, because there often is no gap. |
| 68 // In a case like this, it's not obvious to the user that the selection | 72 // In a case like this, it's not obvious to the user that the selection |
| 69 // ends "inside" that paragraph, so it would be confusing if Indent/Outdent | 73 // ends "inside" that paragraph, so it would be confusing if Indent/Outdent |
| 70 // operated on that paragraph. | 74 // operated on that paragraph. |
| 71 // FIXME: We paint the gap before some paragraphs that are indented with left | 75 // FIXME: We paint the gap before some paragraphs that are indented with left |
| 72 // margin/padding, but not others. We should make the gap painting more | 76 // margin/padding, but not others. We should make the gap painting more |
| 73 // consistent and then use a left margin/padding rule here. | 77 // consistent and then use a left margin/padding rule here. |
| 74 if (visibleEnd.deepEquivalent() != visibleStart.deepEquivalent() && | 78 if (visibleEnd.deepEquivalent() != visibleStart.deepEquivalent() && |
| 75 isStartOfParagraphDeprecated(visibleEnd)) { | 79 isStartOfParagraph(visibleEnd)) { |
| 76 VisibleSelection newSelection = createVisibleSelectionDeprecated( | 80 VisibleSelection newSelection = createVisibleSelection( |
| 77 visibleStart, | 81 visibleStart, |
| 78 previousPositionOf(visibleEnd, CannotCrossEditingBoundary), | 82 previousPositionOf(visibleEnd, CannotCrossEditingBoundary), |
| 79 endingSelection().isDirectional()); | 83 endingSelection().isDirectional()); |
| 80 if (newSelection.isNone()) | 84 if (newSelection.isNone()) |
| 81 return; | 85 return; |
| 82 setEndingSelection(newSelection); | 86 setEndingSelection(newSelection); |
| 83 } | 87 } |
| 84 | 88 |
| 85 VisibleSelection selection = | 89 VisibleSelection selection = |
| 86 selectionForParagraphIteration(endingSelection()); | 90 selectionForParagraphIteration(endingSelection()); |
| 87 VisiblePosition startOfSelection = selection.visibleStartDeprecated(); | 91 VisiblePosition startOfSelection = selection.visibleStart(); |
| 88 VisiblePosition endOfSelection = selection.visibleEndDeprecated(); | 92 VisiblePosition endOfSelection = selection.visibleEnd(); |
| 89 DCHECK(!startOfSelection.isNull()); | 93 DCHECK(!startOfSelection.isNull()); |
| 90 DCHECK(!endOfSelection.isNull()); | 94 DCHECK(!endOfSelection.isNull()); |
| 91 ContainerNode* startScope = nullptr; | 95 ContainerNode* startScope = nullptr; |
| 92 int startIndex = indexForVisiblePosition(startOfSelection, startScope); | 96 int startIndex = indexForVisiblePosition(startOfSelection, startScope); |
| 93 ContainerNode* endScope = nullptr; | 97 ContainerNode* endScope = nullptr; |
| 94 int endIndex = indexForVisiblePosition(endOfSelection, endScope); | 98 int endIndex = indexForVisiblePosition(endOfSelection, endScope); |
| 95 | 99 |
| 96 formatSelection(startOfSelection, endOfSelection, editingState); | 100 formatSelection(startOfSelection, endOfSelection, editingState); |
| 97 if (editingState->isAborted()) | 101 if (editingState->isAborted()) |
| 98 return; | 102 return; |
| 99 | 103 |
| 100 document().updateStyleAndLayoutIgnorePendingStylesheets(); | 104 document().updateStyleAndLayoutIgnorePendingStylesheets(); |
| 101 | 105 |
| 102 DCHECK_EQ(startScope, endScope); | 106 DCHECK_EQ(startScope, endScope); |
| 103 DCHECK_GE(startIndex, 0); | 107 DCHECK_GE(startIndex, 0); |
| 104 DCHECK_LE(startIndex, endIndex); | 108 DCHECK_LE(startIndex, endIndex); |
| 105 if (startScope == endScope && startIndex >= 0 && startIndex <= endIndex) { | 109 if (startScope == endScope && startIndex >= 0 && startIndex <= endIndex) { |
| 106 VisiblePosition start(visiblePositionForIndex(startIndex, startScope)); | 110 VisiblePosition start(visiblePositionForIndex(startIndex, startScope)); |
| 107 VisiblePosition end(visiblePositionForIndex(endIndex, endScope)); | 111 VisiblePosition end(visiblePositionForIndex(endIndex, endScope)); |
| 108 if (start.isNotNull() && end.isNotNull()) | 112 if (start.isNotNull() && end.isNotNull()) |
| 109 setEndingSelection(createVisibleSelectionDeprecated( | 113 setEndingSelection(createVisibleSelection( |
| 110 start, end, endingSelection().isDirectional())); | 114 start, end, endingSelection().isDirectional())); |
| 111 } | 115 } |
| 112 } | 116 } |
| 113 | 117 |
| 114 static bool isAtUnsplittableElement(const Position& pos) { | 118 static bool isAtUnsplittableElement(const Position& pos) { |
| 115 Node* node = pos.anchorNode(); | 119 Node* node = pos.anchorNode(); |
| 116 return node == rootEditableElementOf(pos) || | 120 return node == rootEditableElementOf(pos) || |
| 117 node == enclosingNodeOfType(pos, &isTableCell); | 121 node == enclosingNodeOfType(pos, &isTableCell); |
| 118 } | 122 } |
| 119 | 123 |
| 120 void ApplyBlockElementCommand::formatSelection( | 124 void ApplyBlockElementCommand::formatSelection( |
| 121 const VisiblePosition& startOfSelection, | 125 const VisiblePosition& startOfSelection, |
| 122 const VisiblePosition& endOfSelection, | 126 const VisiblePosition& endOfSelection, |
| 123 EditingState* editingState) { | 127 EditingState* editingState) { |
| 124 // Special case empty unsplittable elements because there's nothing to split | 128 // Special case empty unsplittable elements because there's nothing to split |
| 125 // and there's nothing to move. | 129 // and there's nothing to move. |
| 126 Position start = mostForwardCaretPosition(startOfSelection.deepEquivalent()); | 130 Position start = mostForwardCaretPosition(startOfSelection.deepEquivalent()); |
| 127 if (isAtUnsplittableElement(start)) { | 131 if (isAtUnsplittableElement(start)) { |
| 128 HTMLElement* blockquote = createBlockElement(); | 132 HTMLElement* blockquote = createBlockElement(); |
| 129 insertNodeAt(blockquote, start, editingState); | 133 insertNodeAt(blockquote, start, editingState); |
| 130 if (editingState->isAborted()) | 134 if (editingState->isAborted()) |
| 131 return; | 135 return; |
| 132 HTMLBRElement* placeholder = HTMLBRElement::create(document()); | 136 HTMLBRElement* placeholder = HTMLBRElement::create(document()); |
| 133 appendNode(placeholder, blockquote, editingState); | 137 appendNode(placeholder, blockquote, editingState); |
| 134 if (editingState->isAborted()) | 138 if (editingState->isAborted()) |
| 135 return; | 139 return; |
| 136 setEndingSelection(createVisibleSelectionDeprecated( | 140 document().updateStyleAndLayoutIgnorePendingStylesheets(); |
| 141 setEndingSelection(createVisibleSelection( |
| 137 Position::beforeNode(placeholder), TextAffinity::Downstream, | 142 Position::beforeNode(placeholder), TextAffinity::Downstream, |
| 138 endingSelection().isDirectional())); | 143 endingSelection().isDirectional())); |
| 139 return; | 144 return; |
| 140 } | 145 } |
| 141 | 146 |
| 142 HTMLElement* blockquoteForNextIndent = nullptr; | 147 HTMLElement* blockquoteForNextIndent = nullptr; |
| 143 VisiblePosition endOfCurrentParagraph = | 148 VisiblePosition endOfCurrentParagraph = endOfParagraph(startOfSelection); |
| 144 endOfParagraphDeprecated(startOfSelection); | 149 VisiblePosition endOfLastParagraph = endOfParagraph(endOfSelection); |
| 145 VisiblePosition endOfLastParagraph = endOfParagraphDeprecated(endOfSelection); | 150 Position endAfterSelection = |
| 146 VisiblePosition endAfterSelection = | 151 endOfParagraph(nextPositionOf(endOfLastParagraph)).deepEquivalent(); |
| 147 endOfParagraphDeprecated(nextPositionOf(endOfLastParagraph)); | |
| 148 m_endOfLastParagraph = endOfLastParagraph.deepEquivalent(); | 152 m_endOfLastParagraph = endOfLastParagraph.deepEquivalent(); |
| 149 | 153 |
| 150 bool atEnd = false; | 154 bool atEnd = false; |
| 151 Position end; | 155 Position end; |
| 152 while (endOfCurrentParagraph.deepEquivalent() != | 156 while (endOfCurrentParagraph.deepEquivalent() != endAfterSelection && |
| 153 endAfterSelection.deepEquivalent() && | |
| 154 !atEnd) { | 157 !atEnd) { |
| 155 if (endOfCurrentParagraph.deepEquivalent() == m_endOfLastParagraph) | 158 if (endOfCurrentParagraph.deepEquivalent() == m_endOfLastParagraph) |
| 156 atEnd = true; | 159 atEnd = true; |
| 157 | 160 |
| 158 rangeForParagraphSplittingTextNodesIfNeeded(endOfCurrentParagraph, start, | 161 rangeForParagraphSplittingTextNodesIfNeeded(endOfCurrentParagraph, start, |
| 159 end); | 162 end); |
| 160 endOfCurrentParagraph = createVisiblePositionDeprecated(end); | 163 endOfCurrentParagraph = createVisiblePosition(end); |
| 161 | 164 |
| 162 Node* enclosingCell = enclosingNodeOfType(start, &isTableCell); | 165 Node* enclosingCell = enclosingNodeOfType(start, &isTableCell); |
| 163 VisiblePosition endOfNextParagraph = | 166 PositionWithAffinity endOfNextParagraph = |
| 164 endOfNextParagrahSplittingTextNodesIfNeeded(endOfCurrentParagraph, | 167 endOfNextParagrahSplittingTextNodesIfNeeded(endOfCurrentParagraph, |
| 165 start, end); | 168 start, end) |
| 169 .toPositionWithAffinity(); |
| 166 | 170 |
| 167 formatRange(start, end, m_endOfLastParagraph, blockquoteForNextIndent, | 171 formatRange(start, end, m_endOfLastParagraph, blockquoteForNextIndent, |
| 168 editingState); | 172 editingState); |
| 169 if (editingState->isAborted()) | 173 if (editingState->isAborted()) |
| 170 return; | 174 return; |
| 171 | 175 |
| 172 // Don't put the next paragraph in the blockquote we just created for this p
aragraph unless | 176 // Don't put the next paragraph in the blockquote we just created for this p
aragraph unless |
| 173 // the next paragraph is in the same cell. | 177 // the next paragraph is in the same cell. |
| 174 if (enclosingCell && | 178 if (enclosingCell && |
| 175 enclosingCell != enclosingNodeOfType( | 179 enclosingCell != |
| 176 endOfNextParagraph.deepEquivalent(), &isTableCell)) | 180 enclosingNodeOfType(endOfNextParagraph.position(), &isTableCell)) |
| 177 blockquoteForNextIndent = nullptr; | 181 blockquoteForNextIndent = nullptr; |
| 178 | 182 |
| 179 // indentIntoBlockquote could move more than one paragraph if the paragraph | 183 // indentIntoBlockquote could move more than one paragraph if the paragraph |
| 180 // is in a list item or a table. As a result, endAfterSelection could refer
to a position | 184 // is in a list item or a table. As a result, endAfterSelection could refer
to a position |
| 181 // no longer in the document. | 185 // no longer in the document. |
| 182 if (endAfterSelection.isNotNull() && | 186 if (endAfterSelection.isNotNull() && !endAfterSelection.isConnected()) |
| 183 !endAfterSelection.deepEquivalent().isConnected()) | |
| 184 break; | 187 break; |
| 185 // Sanity check: Make sure our moveParagraph calls didn't remove endOfNextPa
ragraph.deepEquivalent().anchorNode() | 188 // Sanity check: Make sure our moveParagraph calls didn't remove endOfNextPa
ragraph.position().anchorNode() |
| 186 // If somehow, e.g. mutation event handler, we did, return to prevent crashe
s. | 189 // If somehow, e.g. mutation event handler, we did, return to prevent crashe
s. |
| 187 if (endOfNextParagraph.isNotNull() && | 190 if (endOfNextParagraph.isNotNull() && |
| 188 !endOfNextParagraph.deepEquivalent().isConnected()) | 191 !endOfNextParagraph.position().isConnected()) |
| 189 return; | 192 return; |
| 190 endOfCurrentParagraph = endOfNextParagraph; | 193 |
| 194 document().updateStyleAndLayoutIgnorePendingStylesheets(); |
| 195 endOfCurrentParagraph = createVisiblePosition(endOfNextParagraph); |
| 191 } | 196 } |
| 192 } | 197 } |
| 193 | 198 |
| 194 static bool isNewLineAtPosition(const Position& position) { | 199 static bool isNewLineAtPosition(const Position& position) { |
| 195 Node* textNode = position.computeContainerNode(); | 200 Node* textNode = position.computeContainerNode(); |
| 196 int offset = position.offsetInContainerNode(); | 201 int offset = position.offsetInContainerNode(); |
| 197 if (!textNode || !textNode->isTextNode() || offset < 0 || | 202 if (!textNode || !textNode->isTextNode() || offset < 0 || |
| 198 offset >= textNode->maxCharacterOffset()) | 203 offset >= textNode->maxCharacterOffset()) |
| 199 return false; | 204 return false; |
| 200 | 205 |
| (...skipping 11 matching lines...) Expand all Loading... |
| 212 if (!position.isOffsetInAnchor() || !position.computeContainerNode() || | 217 if (!position.isOffsetInAnchor() || !position.computeContainerNode() || |
| 213 !position.computeContainerNode()->isTextNode()) | 218 !position.computeContainerNode()->isTextNode()) |
| 214 return 0; | 219 return 0; |
| 215 return position.computeContainerNode()->computedStyle(); | 220 return position.computeContainerNode()->computedStyle(); |
| 216 } | 221 } |
| 217 | 222 |
| 218 void ApplyBlockElementCommand::rangeForParagraphSplittingTextNodesIfNeeded( | 223 void ApplyBlockElementCommand::rangeForParagraphSplittingTextNodesIfNeeded( |
| 219 const VisiblePosition& endOfCurrentParagraph, | 224 const VisiblePosition& endOfCurrentParagraph, |
| 220 Position& start, | 225 Position& start, |
| 221 Position& end) { | 226 Position& end) { |
| 222 start = startOfParagraphDeprecated(endOfCurrentParagraph).deepEquivalent(); | 227 start = startOfParagraph(endOfCurrentParagraph).deepEquivalent(); |
| 223 end = endOfCurrentParagraph.deepEquivalent(); | 228 end = endOfCurrentParagraph.deepEquivalent(); |
| 224 | 229 |
| 225 document().updateStyleAndLayoutTree(); | |
| 226 | |
| 227 bool isStartAndEndOnSameNode = false; | 230 bool isStartAndEndOnSameNode = false; |
| 228 if (const ComputedStyle* startStyle = | 231 if (const ComputedStyle* startStyle = |
| 229 computedStyleOfEnclosingTextNode(start)) { | 232 computedStyleOfEnclosingTextNode(start)) { |
| 230 isStartAndEndOnSameNode = | 233 isStartAndEndOnSameNode = |
| 231 computedStyleOfEnclosingTextNode(end) && | 234 computedStyleOfEnclosingTextNode(end) && |
| 232 start.computeContainerNode() == end.computeContainerNode(); | 235 start.computeContainerNode() == end.computeContainerNode(); |
| 233 bool isStartAndEndOfLastParagraphOnSameNode = | 236 bool isStartAndEndOfLastParagraphOnSameNode = |
| 234 computedStyleOfEnclosingTextNode(m_endOfLastParagraph) && | 237 computedStyleOfEnclosingTextNode(m_endOfLastParagraph) && |
| 235 start.computeContainerNode() == | 238 start.computeContainerNode() == |
| 236 m_endOfLastParagraph.computeContainerNode(); | 239 m_endOfLastParagraph.computeContainerNode(); |
| 237 | 240 |
| 238 // Avoid obtanining the start of next paragraph for start | 241 // Avoid obtanining the start of next paragraph for start |
| 239 // TODO(yosin) We should use |PositionMoveType::CodePoint| for | 242 // TODO(yosin) We should use |PositionMoveType::CodePoint| for |
| 240 // |previousPositionOf()|. | 243 // |previousPositionOf()|. |
| 241 if (startStyle->preserveNewline() && isNewLineAtPosition(start) && | 244 if (startStyle->preserveNewline() && isNewLineAtPosition(start) && |
| 242 !isNewLineAtPosition( | 245 !isNewLineAtPosition( |
| 243 previousPositionOf(start, PositionMoveType::CodeUnit)) && | 246 previousPositionOf(start, PositionMoveType::CodeUnit)) && |
| 244 start.offsetInContainerNode() > 0) | 247 start.offsetInContainerNode() > 0) |
| 245 start = startOfParagraphDeprecated( | 248 start = startOfParagraph(createVisiblePosition(previousPositionOf( |
| 246 createVisiblePositionDeprecated( | 249 end, PositionMoveType::CodeUnit))) |
| 247 previousPositionOf(end, PositionMoveType::CodeUnit))) | |
| 248 .deepEquivalent(); | 250 .deepEquivalent(); |
| 249 | 251 |
| 250 // If start is in the middle of a text node, split. | 252 // If start is in the middle of a text node, split. |
| 251 if (!startStyle->collapseWhiteSpace() && | 253 if (!startStyle->collapseWhiteSpace() && |
| 252 start.offsetInContainerNode() > 0) { | 254 start.offsetInContainerNode() > 0) { |
| 253 int startOffset = start.offsetInContainerNode(); | 255 int startOffset = start.offsetInContainerNode(); |
| 254 Text* startText = toText(start.computeContainerNode()); | 256 Text* startText = toText(start.computeContainerNode()); |
| 255 splitTextNode(startText, startOffset); | 257 splitTextNode(startText, startOffset); |
| 258 document().updateStyleAndLayoutTree(); |
| 259 |
| 256 start = Position::firstPositionInNode(startText); | 260 start = Position::firstPositionInNode(startText); |
| 257 if (isStartAndEndOnSameNode) { | 261 if (isStartAndEndOnSameNode) { |
| 258 DCHECK_GE(end.offsetInContainerNode(), startOffset); | 262 DCHECK_GE(end.offsetInContainerNode(), startOffset); |
| 259 end = Position(startText, end.offsetInContainerNode() - startOffset); | 263 end = Position(startText, end.offsetInContainerNode() - startOffset); |
| 260 } | 264 } |
| 261 if (isStartAndEndOfLastParagraphOnSameNode) { | 265 if (isStartAndEndOfLastParagraphOnSameNode) { |
| 262 DCHECK_GE(m_endOfLastParagraph.offsetInContainerNode(), startOffset); | 266 DCHECK_GE(m_endOfLastParagraph.offsetInContainerNode(), startOffset); |
| 263 m_endOfLastParagraph = | 267 m_endOfLastParagraph = |
| 264 Position(startText, m_endOfLastParagraph.offsetInContainerNode() - | 268 Position(startText, m_endOfLastParagraph.offsetInContainerNode() - |
| 265 startOffset); | 269 startOffset); |
| 266 } | 270 } |
| 267 } | 271 } |
| 268 } | 272 } |
| 269 | 273 |
| 270 document().updateStyleAndLayoutTree(); | |
| 271 | |
| 272 if (const ComputedStyle* endStyle = computedStyleOfEnclosingTextNode(end)) { | 274 if (const ComputedStyle* endStyle = computedStyleOfEnclosingTextNode(end)) { |
| 273 bool isEndAndEndOfLastParagraphOnSameNode = | 275 bool isEndAndEndOfLastParagraphOnSameNode = |
| 274 computedStyleOfEnclosingTextNode(m_endOfLastParagraph) && | 276 computedStyleOfEnclosingTextNode(m_endOfLastParagraph) && |
| 275 end.anchorNode() == m_endOfLastParagraph.anchorNode(); | 277 end.anchorNode() == m_endOfLastParagraph.anchorNode(); |
| 276 // Include \n at the end of line if we're at an empty paragraph | 278 // Include \n at the end of line if we're at an empty paragraph |
| 277 if (endStyle->preserveNewline() && start == end && | 279 if (endStyle->preserveNewline() && start == end && |
| 278 end.offsetInContainerNode() < | 280 end.offsetInContainerNode() < |
| 279 end.computeContainerNode()->maxCharacterOffset()) { | 281 end.computeContainerNode()->maxCharacterOffset()) { |
| 280 int endOffset = end.offsetInContainerNode(); | 282 int endOffset = end.offsetInContainerNode(); |
| 281 // TODO(yosin) We should use |PositionMoveType::CodePoint| for | 283 // TODO(yosin) We should use |PositionMoveType::CodePoint| for |
| 282 // |previousPositionOf()|. | 284 // |previousPositionOf()|. |
| 283 if (!isNewLineAtPosition( | 285 if (!isNewLineAtPosition( |
| 284 previousPositionOf(end, PositionMoveType::CodeUnit)) && | 286 previousPositionOf(end, PositionMoveType::CodeUnit)) && |
| 285 isNewLineAtPosition(end)) | 287 isNewLineAtPosition(end)) |
| 286 end = Position(end.computeContainerNode(), endOffset + 1); | 288 end = Position(end.computeContainerNode(), endOffset + 1); |
| 287 if (isEndAndEndOfLastParagraphOnSameNode && | 289 if (isEndAndEndOfLastParagraphOnSameNode && |
| 288 end.offsetInContainerNode() >= | 290 end.offsetInContainerNode() >= |
| 289 m_endOfLastParagraph.offsetInContainerNode()) | 291 m_endOfLastParagraph.offsetInContainerNode()) |
| 290 m_endOfLastParagraph = end; | 292 m_endOfLastParagraph = end; |
| 291 } | 293 } |
| 292 | 294 |
| 293 // If end is in the middle of a text node, split. | 295 // If end is in the middle of a text node, split. |
| 294 if (endStyle->userModify() != READ_ONLY && | 296 if (endStyle->userModify() != READ_ONLY && |
| 295 !endStyle->collapseWhiteSpace() && end.offsetInContainerNode() && | 297 !endStyle->collapseWhiteSpace() && end.offsetInContainerNode() && |
| 296 end.offsetInContainerNode() < | 298 end.offsetInContainerNode() < |
| 297 end.computeContainerNode()->maxCharacterOffset()) { | 299 end.computeContainerNode()->maxCharacterOffset()) { |
| 298 Text* endContainer = toText(end.computeContainerNode()); | 300 Text* endContainer = toText(end.computeContainerNode()); |
| 299 splitTextNode(endContainer, end.offsetInContainerNode()); | 301 splitTextNode(endContainer, end.offsetInContainerNode()); |
| 302 document().updateStyleAndLayoutTree(); |
| 303 |
| 300 if (isStartAndEndOnSameNode) | 304 if (isStartAndEndOnSameNode) |
| 301 start = firstPositionInOrBeforeNode(endContainer->previousSibling()); | 305 start = firstPositionInOrBeforeNode(endContainer->previousSibling()); |
| 302 if (isEndAndEndOfLastParagraphOnSameNode) { | 306 if (isEndAndEndOfLastParagraphOnSameNode) { |
| 303 if (m_endOfLastParagraph.offsetInContainerNode() == | 307 if (m_endOfLastParagraph.offsetInContainerNode() == |
| 304 end.offsetInContainerNode()) | 308 end.offsetInContainerNode()) |
| 305 m_endOfLastParagraph = | 309 m_endOfLastParagraph = |
| 306 lastPositionInOrAfterNode(endContainer->previousSibling()); | 310 lastPositionInOrAfterNode(endContainer->previousSibling()); |
| 307 else | 311 else |
| 308 m_endOfLastParagraph = Position( | 312 m_endOfLastParagraph = Position( |
| 309 endContainer, m_endOfLastParagraph.offsetInContainerNode() - | 313 endContainer, m_endOfLastParagraph.offsetInContainerNode() - |
| 310 end.offsetInContainerNode()); | 314 end.offsetInContainerNode()); |
| 311 } | 315 } |
| 312 end = Position::lastPositionInNode(endContainer->previousSibling()); | 316 end = Position::lastPositionInNode(endContainer->previousSibling()); |
| 313 } | 317 } |
| 314 } | 318 } |
| 315 } | 319 } |
| 316 | 320 |
| 317 VisiblePosition | 321 VisiblePosition |
| 318 ApplyBlockElementCommand::endOfNextParagrahSplittingTextNodesIfNeeded( | 322 ApplyBlockElementCommand::endOfNextParagrahSplittingTextNodesIfNeeded( |
| 319 VisiblePosition& endOfCurrentParagraph, | 323 VisiblePosition& endOfCurrentParagraph, |
| 320 Position& start, | 324 Position& start, |
| 321 Position& end) { | 325 Position& end) { |
| 322 VisiblePosition endOfNextParagraph = | 326 VisiblePosition endOfNextParagraph = |
| 323 endOfParagraphDeprecated(nextPositionOf(endOfCurrentParagraph)); | 327 endOfParagraph(nextPositionOf(endOfCurrentParagraph)); |
| 324 Position position = endOfNextParagraph.deepEquivalent(); | 328 Position position = endOfNextParagraph.deepEquivalent(); |
| 325 const ComputedStyle* style = computedStyleOfEnclosingTextNode(position); | 329 const ComputedStyle* style = computedStyleOfEnclosingTextNode(position); |
| 326 if (!style) | 330 if (!style) |
| 327 return endOfNextParagraph; | 331 return endOfNextParagraph; |
| 328 | 332 |
| 329 Text* text = toText(position.computeContainerNode()); | 333 Text* text = toText(position.computeContainerNode()); |
| 330 if (!style->preserveNewline() || !position.offsetInContainerNode() || | 334 if (!style->preserveNewline() || !position.offsetInContainerNode() || |
| 331 !isNewLineAtPosition(Position::firstPositionInNode(text))) | 335 !isNewLineAtPosition(Position::firstPositionInNode(text))) |
| 332 return endOfNextParagraph; | 336 return endOfNextParagraph; |
| 333 | 337 |
| 334 // \n at the beginning of the text node immediately following the current para
graph is trimmed by moveParagraphWithClones. | 338 // \n at the beginning of the text node immediately following the current para
graph is trimmed by moveParagraphWithClones. |
| 335 // If endOfNextParagraph was pointing at this same text node, endOfNextParagra
ph will be shifted by one paragraph. | 339 // If endOfNextParagraph was pointing at this same text node, endOfNextParagra
ph will be shifted by one paragraph. |
| 336 // Avoid this by splitting "\n" | 340 // Avoid this by splitting "\n" |
| 337 splitTextNode(text, 1); | 341 splitTextNode(text, 1); |
| 342 document().updateStyleAndLayoutIgnorePendingStylesheets(); |
| 338 | 343 |
| 339 if (text == start.computeContainerNode() && text->previousSibling() && | 344 if (text == start.computeContainerNode() && text->previousSibling() && |
| 340 text->previousSibling()->isTextNode()) { | 345 text->previousSibling()->isTextNode()) { |
| 341 DCHECK_LT(start.offsetInContainerNode(), position.offsetInContainerNode()); | 346 DCHECK_LT(start.offsetInContainerNode(), position.offsetInContainerNode()); |
| 342 start = Position(toText(text->previousSibling()), | 347 start = Position(toText(text->previousSibling()), |
| 343 start.offsetInContainerNode()); | 348 start.offsetInContainerNode()); |
| 344 } | 349 } |
| 345 if (text == end.computeContainerNode() && text->previousSibling() && | 350 if (text == end.computeContainerNode() && text->previousSibling() && |
| 346 text->previousSibling()->isTextNode()) { | 351 text->previousSibling()->isTextNode()) { |
| 347 DCHECK_LT(end.offsetInContainerNode(), position.offsetInContainerNode()); | 352 DCHECK_LT(end.offsetInContainerNode(), position.offsetInContainerNode()); |
| 348 end = | 353 end = |
| 349 Position(toText(text->previousSibling()), end.offsetInContainerNode()); | 354 Position(toText(text->previousSibling()), end.offsetInContainerNode()); |
| 350 } | 355 } |
| 351 if (text == m_endOfLastParagraph.computeContainerNode()) { | 356 if (text == m_endOfLastParagraph.computeContainerNode()) { |
| 352 if (m_endOfLastParagraph.offsetInContainerNode() < | 357 if (m_endOfLastParagraph.offsetInContainerNode() < |
| 353 position.offsetInContainerNode()) { | 358 position.offsetInContainerNode()) { |
| 354 // We can only fix endOfLastParagraph if the previous node was still text
and hasn't been modified by script. | 359 // We can only fix endOfLastParagraph if the previous node was still text
and hasn't been modified by script. |
| 355 if (text->previousSibling()->isTextNode() && | 360 if (text->previousSibling()->isTextNode() && |
| 356 static_cast<unsigned>(m_endOfLastParagraph.offsetInContainerNode()) <= | 361 static_cast<unsigned>(m_endOfLastParagraph.offsetInContainerNode()) <= |
| 357 toText(text->previousSibling())->length()) | 362 toText(text->previousSibling())->length()) |
| 358 m_endOfLastParagraph = | 363 m_endOfLastParagraph = |
| 359 Position(toText(text->previousSibling()), | 364 Position(toText(text->previousSibling()), |
| 360 m_endOfLastParagraph.offsetInContainerNode()); | 365 m_endOfLastParagraph.offsetInContainerNode()); |
| 361 } else { | 366 } else { |
| 362 m_endOfLastParagraph = | 367 m_endOfLastParagraph = |
| 363 Position(text, m_endOfLastParagraph.offsetInContainerNode() - 1); | 368 Position(text, m_endOfLastParagraph.offsetInContainerNode() - 1); |
| 364 } | 369 } |
| 365 } | 370 } |
| 366 | 371 |
| 367 return createVisiblePositionDeprecated( | 372 return createVisiblePosition( |
| 368 Position(text, position.offsetInContainerNode() - 1)); | 373 Position(text, position.offsetInContainerNode() - 1)); |
| 369 } | 374 } |
| 370 | 375 |
| 371 HTMLElement* ApplyBlockElementCommand::createBlockElement() const { | 376 HTMLElement* ApplyBlockElementCommand::createBlockElement() const { |
| 372 HTMLElement* element = createHTMLElement(document(), m_tagName); | 377 HTMLElement* element = createHTMLElement(document(), m_tagName); |
| 373 if (m_inlineStyle.length()) | 378 if (m_inlineStyle.length()) |
| 374 element->setAttribute(styleAttr, m_inlineStyle); | 379 element->setAttribute(styleAttr, m_inlineStyle); |
| 375 return element; | 380 return element; |
| 376 } | 381 } |
| 377 | 382 |
| 378 DEFINE_TRACE(ApplyBlockElementCommand) { | 383 DEFINE_TRACE(ApplyBlockElementCommand) { |
| 379 visitor->trace(m_endOfLastParagraph); | 384 visitor->trace(m_endOfLastParagraph); |
| 380 CompositeEditCommand::trace(visitor); | 385 CompositeEditCommand::trace(visitor); |
| 381 } | 386 } |
| 382 | 387 |
| 383 } // namespace blink | 388 } // namespace blink |
| OLD | NEW |