Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(16)

Side by Side Diff: third_party/WebKit/Source/core/editing/commands/DeleteSelectionCommand.cpp

Issue 2397963002: Reflow comments in //third_party/WebKit/Source/core/editing/commands (Closed)
Patch Set: . Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. 2 * Copyright (C) 2005 Apple Computer, Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions 5 * modification, are permitted provided that the following conditions
6 * are met: 6 * are met:
7 * 1. Redistributions of source code must retain the above copyright 7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer. 8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright 9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the 10 * notice, this list of conditions and the following disclaimer in the
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after
111 m_deleteIntoBlockquoteStyle(nullptr) {} 111 m_deleteIntoBlockquoteStyle(nullptr) {}
112 112
113 void DeleteSelectionCommand::initializeStartEnd(Position& start, 113 void DeleteSelectionCommand::initializeStartEnd(Position& start,
114 Position& end) { 114 Position& end) {
115 HTMLElement* startSpecialContainer = nullptr; 115 HTMLElement* startSpecialContainer = nullptr;
116 HTMLElement* endSpecialContainer = nullptr; 116 HTMLElement* endSpecialContainer = nullptr;
117 117
118 start = m_selectionToDelete.start(); 118 start = m_selectionToDelete.start();
119 end = m_selectionToDelete.end(); 119 end = m_selectionToDelete.end();
120 120
121 // For HRs, we'll get a position at (HR,1) when hitting delete from the beginn ing of the previous line, or (HR,0) when forward deleting, 121 // For HRs, we'll get a position at (HR,1) when hitting delete from the
122 // but in these cases, we want to delete it, so manually expand the selection 122 // beginning of the previous line, or (HR,0) when forward deleting, but in
123 // these cases, we want to delete it, so manually expand the selection
123 if (isHTMLHRElement(*start.anchorNode())) 124 if (isHTMLHRElement(*start.anchorNode()))
124 start = Position::beforeNode(start.anchorNode()); 125 start = Position::beforeNode(start.anchorNode());
125 else if (isHTMLHRElement(*end.anchorNode())) 126 else if (isHTMLHRElement(*end.anchorNode()))
126 end = Position::afterNode(end.anchorNode()); 127 end = Position::afterNode(end.anchorNode());
127 128
128 // FIXME: This is only used so that moveParagraphs can avoid the bugs in speci al element expansion. 129 // FIXME: This is only used so that moveParagraphs can avoid the bugs in
130 // special element expansion.
129 if (!m_expandForSpecialElements) 131 if (!m_expandForSpecialElements)
130 return; 132 return;
131 133
132 while (1) { 134 while (1) {
133 startSpecialContainer = 0; 135 startSpecialContainer = 0;
134 endSpecialContainer = 0; 136 endSpecialContainer = 0;
135 137
136 Position s = 138 Position s =
137 positionBeforeContainingSpecialElement(start, &startSpecialContainer); 139 positionBeforeContainingSpecialElement(start, &startSpecialContainer);
138 Position e = 140 Position e =
139 positionAfterContainingSpecialElement(end, &endSpecialContainer); 141 positionAfterContainingSpecialElement(end, &endSpecialContainer);
140 142
141 if (!startSpecialContainer && !endSpecialContainer) 143 if (!startSpecialContainer && !endSpecialContainer)
142 break; 144 break;
143 145
144 if (createVisiblePositionDeprecated(start).deepEquivalent() != 146 if (createVisiblePositionDeprecated(start).deepEquivalent() !=
145 m_selectionToDelete.visibleStartDeprecated().deepEquivalent() || 147 m_selectionToDelete.visibleStartDeprecated().deepEquivalent() ||
146 createVisiblePositionDeprecated(end).deepEquivalent() != 148 createVisiblePositionDeprecated(end).deepEquivalent() !=
147 m_selectionToDelete.visibleEndDeprecated().deepEquivalent()) 149 m_selectionToDelete.visibleEndDeprecated().deepEquivalent())
148 break; 150 break;
149 151
150 // If we're going to expand to include the startSpecialContainer, it must be fully selected. 152 // If we're going to expand to include the startSpecialContainer, it must be
153 // fully selected.
151 if (startSpecialContainer && !endSpecialContainer && 154 if (startSpecialContainer && !endSpecialContainer &&
152 comparePositions(Position::inParentAfterNode(*startSpecialContainer), 155 comparePositions(Position::inParentAfterNode(*startSpecialContainer),
153 end) > -1) 156 end) > -1)
154 break; 157 break;
155 158
156 // If we're going to expand to include the endSpecialContainer, it must be f ully selected. 159 // If we're going to expand to include the endSpecialContainer, it must be
160 // fully selected.
157 if (endSpecialContainer && !startSpecialContainer && 161 if (endSpecialContainer && !startSpecialContainer &&
158 comparePositions( 162 comparePositions(
159 start, Position::inParentBeforeNode(*endSpecialContainer)) > -1) 163 start, Position::inParentBeforeNode(*endSpecialContainer)) > -1)
160 break; 164 break;
161 165
162 if (startSpecialContainer && 166 if (startSpecialContainer &&
163 startSpecialContainer->isDescendantOf(endSpecialContainer)) { 167 startSpecialContainer->isDescendantOf(endSpecialContainer)) {
164 // Don't adjust the end yet, it is the end of a special element that conta ins the start 168 // Don't adjust the end yet, it is the end of a special element that
165 // special element (which may or may not be fully selected). 169 // contains the start special element (which may or may not be fully
170 // selected).
166 start = s; 171 start = s;
167 } else if (endSpecialContainer && 172 } else if (endSpecialContainer &&
168 endSpecialContainer->isDescendantOf(startSpecialContainer)) { 173 endSpecialContainer->isDescendantOf(startSpecialContainer)) {
169 // Don't adjust the start yet, it is the start of a special element that c ontains the end 174 // Don't adjust the start yet, it is the start of a special element that
170 // special element (which may or may not be fully selected). 175 // contains the end special element (which may or may not be fully
176 // selected).
171 end = e; 177 end = e;
172 } else { 178 } else {
173 start = s; 179 start = s;
174 end = e; 180 end = e;
175 } 181 }
176 } 182 }
177 } 183 }
178 184
179 void DeleteSelectionCommand::setStartingSelectionOnSmartDelete( 185 void DeleteSelectionCommand::setStartingSelectionOnSmartDelete(
180 const Position& start, 186 const Position& start,
(...skipping 30 matching lines...) Expand all
211 217
212 m_startRoot = rootEditableElementOf(start); 218 m_startRoot = rootEditableElementOf(start);
213 m_endRoot = rootEditableElementOf(end); 219 m_endRoot = rootEditableElementOf(end);
214 220
215 m_startTableRow = 221 m_startTableRow =
216 toHTMLTableRowElement(enclosingNodeOfType(start, &isHTMLTableRowElement)); 222 toHTMLTableRowElement(enclosingNodeOfType(start, &isHTMLTableRowElement));
217 m_endTableRow = 223 m_endTableRow =
218 toHTMLTableRowElement(enclosingNodeOfType(end, &isHTMLTableRowElement)); 224 toHTMLTableRowElement(enclosingNodeOfType(end, &isHTMLTableRowElement));
219 225
220 // Don't move content out of a table cell. 226 // Don't move content out of a table cell.
221 // If the cell is non-editable, enclosingNodeOfType won't return it by default , so 227 // If the cell is non-editable, enclosingNodeOfType won't return it by
222 // tell that function that we don't care if it returns non-editable nodes. 228 // default, so tell that function that we don't care if it returns
229 // non-editable nodes.
223 Node* startCell = enclosingNodeOfType(m_upstreamStart, &isTableCell, 230 Node* startCell = enclosingNodeOfType(m_upstreamStart, &isTableCell,
224 CanCrossEditingBoundary); 231 CanCrossEditingBoundary);
225 Node* endCell = enclosingNodeOfType(m_downstreamEnd, &isTableCell, 232 Node* endCell = enclosingNodeOfType(m_downstreamEnd, &isTableCell,
226 CanCrossEditingBoundary); 233 CanCrossEditingBoundary);
227 // FIXME: This isn't right. A borderless table with two rows and a single col umn would appear as two paragraphs. 234 // FIXME: This isn't right. A borderless table with two rows and a single
235 // column would appear as two paragraphs.
228 if (endCell && endCell != startCell) 236 if (endCell && endCell != startCell)
229 m_mergeBlocksAfterDelete = false; 237 m_mergeBlocksAfterDelete = false;
230 238
231 // Usually the start and the end of the selection to delete are pulled togethe r as a result of the deletion. 239 // Usually the start and the end of the selection to delete are pulled
232 // Sometimes they aren't (like when no merge is requested), so we must choose one position to hold the caret 240 // together as a result of the deletion. Sometimes they aren't (like when no
241 // merge is requested), so we must choose one position to hold the caret
233 // and receive the placeholder after deletion. 242 // and receive the placeholder after deletion.
234 VisiblePosition visibleEnd = createVisiblePositionDeprecated(m_downstreamEnd); 243 VisiblePosition visibleEnd = createVisiblePositionDeprecated(m_downstreamEnd);
235 if (m_mergeBlocksAfterDelete && !isEndOfParagraphDeprecated(visibleEnd)) 244 if (m_mergeBlocksAfterDelete && !isEndOfParagraphDeprecated(visibleEnd))
236 m_endingPosition = m_downstreamEnd; 245 m_endingPosition = m_downstreamEnd;
237 else 246 else
238 m_endingPosition = m_downstreamStart; 247 m_endingPosition = m_downstreamStart;
239 248
240 // We don't want to merge into a block if it will mean changing the quote leve l of content after deleting 249 // We don't want to merge into a block if it will mean changing the quote
241 // selections that contain a whole number paragraphs plus a line break, since it is unclear to most users 250 // level of content after deleting selections that contain a whole number
242 // that such a selection actually ends at the start of the next paragraph. Thi s matches TextEdit behavior 251 // paragraphs plus a line break, since it is unclear to most users that such a
243 // for indented paragraphs. 252 // selection actually ends at the start of the next paragraph. This matches
244 // Only apply this rule if the endingSelection is a range selection. If it is a caret, then other operations have created 253 // TextEdit behavior for indented paragraphs.
245 // the selection we're deleting (like the process of creating a selection to d elete during a backspace), and the user isn't in the situation described above. 254 // Only apply this rule if the endingSelection is a range selection. If it is
255 // a caret, then other operations have created the selection we're deleting
256 // (like the process of creating a selection to delete during a backspace),
257 // and the user isn't in the situation described above.
246 if (numEnclosingMailBlockquotes(start) != numEnclosingMailBlockquotes(end) && 258 if (numEnclosingMailBlockquotes(start) != numEnclosingMailBlockquotes(end) &&
247 isStartOfParagraphDeprecated(visibleEnd) && 259 isStartOfParagraphDeprecated(visibleEnd) &&
248 isStartOfParagraph(createVisiblePositionDeprecated(start)) && 260 isStartOfParagraph(createVisiblePositionDeprecated(start)) &&
249 endingSelection().isRange()) { 261 endingSelection().isRange()) {
250 m_mergeBlocksAfterDelete = false; 262 m_mergeBlocksAfterDelete = false;
251 m_pruneStartBlockIfNecessary = true; 263 m_pruneStartBlockIfNecessary = true;
252 } 264 }
253 265
254 // Handle leading and trailing whitespace, as well as smart delete adjustments to the selection 266 // Handle leading and trailing whitespace, as well as smart delete adjustments
267 // to the selection
255 m_leadingWhitespace = leadingWhitespacePosition( 268 m_leadingWhitespace = leadingWhitespacePosition(
256 m_upstreamStart, m_selectionToDelete.affinity()); 269 m_upstreamStart, m_selectionToDelete.affinity());
257 m_trailingWhitespace = 270 m_trailingWhitespace =
258 trailingWhitespacePosition(m_downstreamEnd, VP_DEFAULT_AFFINITY); 271 trailingWhitespacePosition(m_downstreamEnd, VP_DEFAULT_AFFINITY);
259 272
260 if (m_smartDelete) { 273 if (m_smartDelete) {
261 // skip smart delete if the selection to delete already starts or ends with whitespace 274 // skip smart delete if the selection to delete already starts or ends with
275 // whitespace
262 Position pos = createVisiblePositionDeprecated( 276 Position pos = createVisiblePositionDeprecated(
263 m_upstreamStart, m_selectionToDelete.affinity()) 277 m_upstreamStart, m_selectionToDelete.affinity())
264 .deepEquivalent(); 278 .deepEquivalent();
265 bool skipSmartDelete = 279 bool skipSmartDelete =
266 trailingWhitespacePosition(pos, VP_DEFAULT_AFFINITY, 280 trailingWhitespacePosition(pos, VP_DEFAULT_AFFINITY,
267 ConsiderNonCollapsibleWhitespace) 281 ConsiderNonCollapsibleWhitespace)
268 .isNotNull(); 282 .isNotNull();
269 if (!skipSmartDelete) 283 if (!skipSmartDelete)
270 skipSmartDelete = 284 skipSmartDelete =
271 leadingWhitespacePosition(m_downstreamEnd, VP_DEFAULT_AFFINITY, 285 leadingWhitespacePosition(m_downstreamEnd, VP_DEFAULT_AFFINITY,
(...skipping 14 matching lines...) Expand all
286 // Expand out one character upstream for smart delete and recalculate 300 // Expand out one character upstream for smart delete and recalculate
287 // positions based on this change. 301 // positions based on this change.
288 m_upstreamStart = mostBackwardCaretPosition(pos); 302 m_upstreamStart = mostBackwardCaretPosition(pos);
289 m_downstreamStart = mostForwardCaretPosition(pos); 303 m_downstreamStart = mostForwardCaretPosition(pos);
290 m_leadingWhitespace = 304 m_leadingWhitespace =
291 leadingWhitespacePosition(m_upstreamStart, visiblePos.affinity()); 305 leadingWhitespacePosition(m_upstreamStart, visiblePos.affinity());
292 306
293 setStartingSelectionOnSmartDelete(m_upstreamStart, m_upstreamEnd); 307 setStartingSelectionOnSmartDelete(m_upstreamStart, m_upstreamEnd);
294 } 308 }
295 309
296 // trailing whitespace is only considered for smart delete if there is no le ading 310 // trailing whitespace is only considered for smart delete if there is no
297 // whitespace, as in the case where you double-click the first word of a par agraph. 311 // leading whitespace, as in the case where you double-click the first word
312 // of a paragraph.
298 if (!skipSmartDelete && !hasLeadingWhitespaceBeforeAdjustment && 313 if (!skipSmartDelete && !hasLeadingWhitespaceBeforeAdjustment &&
299 trailingWhitespacePosition(m_downstreamEnd, VP_DEFAULT_AFFINITY, 314 trailingWhitespacePosition(m_downstreamEnd, VP_DEFAULT_AFFINITY,
300 ConsiderNonCollapsibleWhitespace) 315 ConsiderNonCollapsibleWhitespace)
301 .isNotNull()) { 316 .isNotNull()) {
302 // Expand out one character downstream for smart delete and recalculate 317 // Expand out one character downstream for smart delete and recalculate
303 // positions based on this change. 318 // positions based on this change.
304 pos = nextPositionOf(createVisiblePositionDeprecated(m_downstreamEnd, 319 pos = nextPositionOf(createVisiblePositionDeprecated(m_downstreamEnd,
305 VP_DEFAULT_AFFINITY)) 320 VP_DEFAULT_AFFINITY))
306 .deepEquivalent(); 321 .deepEquivalent();
307 m_upstreamEnd = mostBackwardCaretPosition(pos); 322 m_upstreamEnd = mostBackwardCaretPosition(pos);
308 m_downstreamEnd = mostForwardCaretPosition(pos); 323 m_downstreamEnd = mostForwardCaretPosition(pos);
309 m_trailingWhitespace = 324 m_trailingWhitespace =
310 trailingWhitespacePosition(m_downstreamEnd, VP_DEFAULT_AFFINITY); 325 trailingWhitespacePosition(m_downstreamEnd, VP_DEFAULT_AFFINITY);
311 326
312 setStartingSelectionOnSmartDelete(m_downstreamStart, m_downstreamEnd); 327 setStartingSelectionOnSmartDelete(m_downstreamStart, m_downstreamEnd);
313 } 328 }
314 } 329 }
315 330
316 // We must pass call parentAnchoredEquivalent on the positions since some edit ing positions 331 // We must pass call parentAnchoredEquivalent on the positions since some
317 // that appear inside their nodes aren't really inside them. [hr, 0] is one e xample. 332 // editing positions that appear inside their nodes aren't really inside them.
318 // FIXME: parentAnchoredEquivalent should eventually be moved into enclosing e lement getters 333 // [hr, 0] is one example.
319 // like the one below, since editing functions should obviously accept editing positions. 334 // FIXME: parentAnchoredEquivalent should eventually be moved into enclosing
320 // FIXME: Passing false to enclosingNodeOfType tells it that it's OK to return a non-editable 335 // element getters like the one below, since editing functions should
321 // node. This was done to match existing behavior, but it seems wrong. 336 // obviously accept editing positions.
337 // FIXME: Passing false to enclosingNodeOfType tells it that it's OK to return
338 // a non-editable node. This was done to match existing behavior, but it
339 // seems wrong.
322 m_startBlock = 340 m_startBlock =
323 enclosingNodeOfType(m_downstreamStart.parentAnchoredEquivalent(), 341 enclosingNodeOfType(m_downstreamStart.parentAnchoredEquivalent(),
324 &isEnclosingBlock, CanCrossEditingBoundary); 342 &isEnclosingBlock, CanCrossEditingBoundary);
325 m_endBlock = enclosingNodeOfType(m_upstreamEnd.parentAnchoredEquivalent(), 343 m_endBlock = enclosingNodeOfType(m_upstreamEnd.parentAnchoredEquivalent(),
326 &isEnclosingBlock, CanCrossEditingBoundary); 344 &isEnclosingBlock, CanCrossEditingBoundary);
327 } 345 }
328 346
329 // We don't want to inherit style from an element which can't have contents. 347 // We don't want to inherit style from an element which can't have contents.
330 static bool shouldNotInheritStyleFrom(const Node& node) { 348 static bool shouldNotInheritStyleFrom(const Node& node) {
331 return !node.canContainRangeEndPoint(); 349 return !node.canContainRangeEndPoint();
332 } 350 }
333 351
334 void DeleteSelectionCommand::saveTypingStyleState() { 352 void DeleteSelectionCommand::saveTypingStyleState() {
335 // A common case is deleting characters that are all from the same text node. In 353 // A common case is deleting characters that are all from the same text node.
336 // that case, the style at the start of the selection before deletion will be the 354 // In that case, the style at the start of the selection before deletion will
337 // same as the style at the start of the selection after deletion (since those 355 // be the same as the style at the start of the selection after deletion
338 // two positions will be identical). Therefore there is no need to save the 356 // (since those two positions will be identical). Therefore there is no need
339 // typing style at the start of the selection, nor is there a reason to 357 // to save the typing style at the start of the selection, nor is there a
340 // compute the style at the start of the selection after deletion (see the 358 // reason to compute the style at the start of the selection after deletion
341 // early return in calculateTypingStyleAfterDelete). 359 // (see the early return in calculateTypingStyleAfterDelete).
342 if (m_upstreamStart.anchorNode() == m_downstreamEnd.anchorNode() && 360 if (m_upstreamStart.anchorNode() == m_downstreamEnd.anchorNode() &&
343 m_upstreamStart.anchorNode()->isTextNode()) 361 m_upstreamStart.anchorNode()->isTextNode())
344 return; 362 return;
345 363
346 if (shouldNotInheritStyleFrom(*m_selectionToDelete.start().anchorNode())) 364 if (shouldNotInheritStyleFrom(*m_selectionToDelete.start().anchorNode()))
347 return; 365 return;
348 366
349 // Figure out the typing style in effect before the delete is done. 367 // Figure out the typing style in effect before the delete is done.
350 m_typingStyle = EditingStyle::create(m_selectionToDelete.start(), 368 m_typingStyle = EditingStyle::create(m_selectionToDelete.start(),
351 EditingStyle::EditingPropertiesInEffect); 369 EditingStyle::EditingPropertiesInEffect);
352 m_typingStyle->removeStyleAddedByElement( 370 m_typingStyle->removeStyleAddedByElement(
353 enclosingAnchorElement(m_selectionToDelete.start())); 371 enclosingAnchorElement(m_selectionToDelete.start()));
354 372
355 // If we're deleting into a Mail blockquote, save the style at end() instead o f start() 373 // If we're deleting into a Mail blockquote, save the style at end() instead
356 // We'll use this later in computeTypingStyleAfterDelete if we end up outside of a Mail blockquote 374 // of start(). We'll use this later in computeTypingStyleAfterDelete if we end
375 // up outside of a Mail blockquote
357 if (enclosingNodeOfType(m_selectionToDelete.start(), 376 if (enclosingNodeOfType(m_selectionToDelete.start(),
358 isMailHTMLBlockquoteElement)) 377 isMailHTMLBlockquoteElement))
359 m_deleteIntoBlockquoteStyle = 378 m_deleteIntoBlockquoteStyle =
360 EditingStyle::create(m_selectionToDelete.end()); 379 EditingStyle::create(m_selectionToDelete.end());
361 else 380 else
362 m_deleteIntoBlockquoteStyle = nullptr; 381 m_deleteIntoBlockquoteStyle = nullptr;
363 } 382 }
364 383
365 bool DeleteSelectionCommand::handleSpecialCaseBRDelete( 384 bool DeleteSelectionCommand::handleSpecialCaseBRDelete(
366 EditingState* editingState) { 385 EditingState* editingState) {
367 Node* nodeAfterUpstreamStart = m_upstreamStart.computeNodeAfterPosition(); 386 Node* nodeAfterUpstreamStart = m_upstreamStart.computeNodeAfterPosition();
368 Node* nodeAfterDownstreamStart = m_downstreamStart.computeNodeAfterPosition(); 387 Node* nodeAfterDownstreamStart = m_downstreamStart.computeNodeAfterPosition();
369 // Upstream end will appear before BR due to canonicalization 388 // Upstream end will appear before BR due to canonicalization
370 Node* nodeAfterUpstreamEnd = m_upstreamEnd.computeNodeAfterPosition(); 389 Node* nodeAfterUpstreamEnd = m_upstreamEnd.computeNodeAfterPosition();
371 390
372 if (!nodeAfterUpstreamStart || !nodeAfterDownstreamStart) 391 if (!nodeAfterUpstreamStart || !nodeAfterDownstreamStart)
373 return false; 392 return false;
374 393
375 // Check for special-case where the selection contains only a BR on a line by itself after another BR. 394 // Check for special-case where the selection contains only a BR on a line by
395 // itself after another BR.
376 bool upstreamStartIsBR = isHTMLBRElement(*nodeAfterUpstreamStart); 396 bool upstreamStartIsBR = isHTMLBRElement(*nodeAfterUpstreamStart);
377 bool downstreamStartIsBR = isHTMLBRElement(*nodeAfterDownstreamStart); 397 bool downstreamStartIsBR = isHTMLBRElement(*nodeAfterDownstreamStart);
378 bool isBROnLineByItself = upstreamStartIsBR && downstreamStartIsBR && 398 bool isBROnLineByItself = upstreamStartIsBR && downstreamStartIsBR &&
379 nodeAfterDownstreamStart == nodeAfterUpstreamEnd; 399 nodeAfterDownstreamStart == nodeAfterUpstreamEnd;
380 if (isBROnLineByItself) { 400 if (isBROnLineByItself) {
381 removeNode(nodeAfterDownstreamStart, editingState); 401 removeNode(nodeAfterDownstreamStart, editingState);
382 return true; 402 return true;
383 } 403 }
384 404
385 // FIXME: This code doesn't belong in here. 405 // FIXME: This code doesn't belong in here.
386 // We detect the case where the start is an empty line consisting of BR not wr apped in a block element. 406 // We detect the case where the start is an empty line consisting of BR not
407 // wrapped in a block element.
387 if (upstreamStartIsBR && downstreamStartIsBR && 408 if (upstreamStartIsBR && downstreamStartIsBR &&
388 !(isStartOfBlock(VisiblePosition::beforeNode(nodeAfterUpstreamStart)) && 409 !(isStartOfBlock(VisiblePosition::beforeNode(nodeAfterUpstreamStart)) &&
389 isEndOfBlock(VisiblePosition::afterNode(nodeAfterUpstreamStart)))) { 410 isEndOfBlock(VisiblePosition::afterNode(nodeAfterUpstreamStart)))) {
390 m_startsAtEmptyLine = true; 411 m_startsAtEmptyLine = true;
391 m_endingPosition = m_downstreamEnd; 412 m_endingPosition = m_downstreamEnd;
392 } 413 }
393 414
394 return false; 415 return false;
395 } 416 }
396 417
397 static Position firstEditablePositionInNode(Node* node) { 418 static Position firstEditablePositionInNode(Node* node) {
398 DCHECK(node); 419 DCHECK(node);
399 Node* next = node; 420 Node* next = node;
400 while (next && !hasEditableStyle(*next)) 421 while (next && !hasEditableStyle(*next))
401 next = NodeTraversal::next(*next, node); 422 next = NodeTraversal::next(*next, node);
402 return next ? firstPositionInOrBeforeNode(next) : Position(); 423 return next ? firstPositionInOrBeforeNode(next) : Position();
403 } 424 }
404 425
405 void DeleteSelectionCommand::removeNode( 426 void DeleteSelectionCommand::removeNode(
406 Node* node, 427 Node* node,
407 EditingState* editingState, 428 EditingState* editingState,
408 ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable) { 429 ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable) {
409 if (!node) 430 if (!node)
410 return; 431 return;
411 432
412 if (m_startRoot != m_endRoot && 433 if (m_startRoot != m_endRoot &&
413 !(node->isDescendantOf(m_startRoot.get()) && 434 !(node->isDescendantOf(m_startRoot.get()) &&
414 node->isDescendantOf(m_endRoot.get()))) { 435 node->isDescendantOf(m_endRoot.get()))) {
415 // If a node is not in both the start and end editable roots, remove it only if its inside an editable region. 436 // If a node is not in both the start and end editable roots, remove it only
437 // if its inside an editable region.
416 if (!hasEditableStyle(*node->parentNode())) { 438 if (!hasEditableStyle(*node->parentNode())) {
417 // Don't remove non-editable atomic nodes. 439 // Don't remove non-editable atomic nodes.
418 if (!node->hasChildren()) 440 if (!node->hasChildren())
419 return; 441 return;
420 // Search this non-editable region for editable regions to empty. 442 // Search this non-editable region for editable regions to empty.
421 Node* child = node->firstChild(); 443 Node* child = node->firstChild();
422 while (child) { 444 while (child) {
423 Node* nextChild = child->nextSibling(); 445 Node* nextChild = child->nextSibling();
424 removeNode(child, editingState, shouldAssumeContentIsAlwaysEditable); 446 removeNode(child, editingState, shouldAssumeContentIsAlwaysEditable);
425 if (editingState->isAborted()) 447 if (editingState->isAborted())
426 return; 448 return;
427 // Bail if nextChild is no longer node's child. 449 // Bail if nextChild is no longer node's child.
428 if (nextChild && nextChild->parentNode() != node) 450 if (nextChild && nextChild->parentNode() != node)
429 return; 451 return;
430 child = nextChild; 452 child = nextChild;
431 } 453 }
432 454
433 // Don't remove editable regions that are inside non-editable ones, just c lear them. 455 // Don't remove editable regions that are inside non-editable ones, just
456 // clear them.
434 return; 457 return;
435 } 458 }
436 } 459 }
437 460
438 if (isTableStructureNode(node) || isRootEditableElement(*node)) { 461 if (isTableStructureNode(node) || isRootEditableElement(*node)) {
439 // Do not remove an element of table structure; remove its contents. 462 // Do not remove an element of table structure; remove its contents.
440 // Likewise for the root editable element. 463 // Likewise for the root editable element.
441 Node* child = node->firstChild(); 464 Node* child = node->firstChild();
442 while (child) { 465 while (child) {
443 Node* remove = child; 466 Node* remove = child;
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after
535 558
536 int startOffset = m_upstreamStart.computeEditingOffset(); 559 int startOffset = m_upstreamStart.computeEditingOffset();
537 Node* startNode = m_upstreamStart.anchorNode(); 560 Node* startNode = m_upstreamStart.anchorNode();
538 DCHECK(startNode); 561 DCHECK(startNode);
539 562
540 makeStylingElementsDirectChildrenOfEditableRootToPreventStyleLoss( 563 makeStylingElementsDirectChildrenOfEditableRootToPreventStyleLoss(
541 editingState); 564 editingState);
542 if (editingState->isAborted()) 565 if (editingState->isAborted())
543 return; 566 return;
544 567
545 // Never remove the start block unless it's a table, in which case we won't me rge content in. 568 // Never remove the start block unless it's a table, in which case we won't
569 // merge content in.
546 if (startNode == m_startBlock.get() && !startOffset && 570 if (startNode == m_startBlock.get() && !startOffset &&
547 canHaveChildrenForEditing(startNode) && !isHTMLTableElement(*startNode)) { 571 canHaveChildrenForEditing(startNode) && !isHTMLTableElement(*startNode)) {
548 startOffset = 0; 572 startOffset = 0;
549 startNode = NodeTraversal::next(*startNode); 573 startNode = NodeTraversal::next(*startNode);
550 if (!startNode) 574 if (!startNode)
551 return; 575 return;
552 } 576 }
553 577
554 if (startOffset >= caretMaxOffset(startNode) && startNode->isTextNode()) { 578 if (startOffset >= caretMaxOffset(startNode) && startNode->isTextNode()) {
555 Text* text = toText(startNode); 579 Text* text = toText(startNode);
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
611 } else if (startNode == m_upstreamEnd.anchorNode() && 635 } else if (startNode == m_upstreamEnd.anchorNode() &&
612 startNode->isTextNode()) { 636 startNode->isTextNode()) {
613 Text* text = toText(m_upstreamEnd.anchorNode()); 637 Text* text = toText(m_upstreamEnd.anchorNode());
614 deleteTextFromNode(text, 0, m_upstreamEnd.computeOffsetInContainerNode()); 638 deleteTextFromNode(text, 0, m_upstreamEnd.computeOffsetInContainerNode());
615 } 639 }
616 640
617 // handle deleting all nodes that are completely selected 641 // handle deleting all nodes that are completely selected
618 while (node && node != m_downstreamEnd.anchorNode()) { 642 while (node && node != m_downstreamEnd.anchorNode()) {
619 if (comparePositions(firstPositionInOrBeforeNode(node), 643 if (comparePositions(firstPositionInOrBeforeNode(node),
620 m_downstreamEnd) >= 0) { 644 m_downstreamEnd) >= 0) {
621 // NodeTraversal::nextSkippingChildren just blew past the end position, so stop deleting 645 // NodeTraversal::nextSkippingChildren just blew past the end position,
646 // so stop deleting
622 node = nullptr; 647 node = nullptr;
623 } else if (!m_downstreamEnd.anchorNode()->isDescendantOf(node)) { 648 } else if (!m_downstreamEnd.anchorNode()->isDescendantOf(node)) {
624 Node* nextNode = NodeTraversal::nextSkippingChildren(*node); 649 Node* nextNode = NodeTraversal::nextSkippingChildren(*node);
625 // if we just removed a node from the end container, update end position so the 650 // if we just removed a node from the end container, update end position
626 // check above will work 651 // so the check above will work
627 updatePositionForNodeRemoval(m_downstreamEnd, *node); 652 updatePositionForNodeRemoval(m_downstreamEnd, *node);
628 removeNode(node, editingState); 653 removeNode(node, editingState);
629 if (editingState->isAborted()) 654 if (editingState->isAborted())
630 return; 655 return;
631 node = nextNode; 656 node = nextNode;
632 } else { 657 } else {
633 Node& n = NodeTraversal::lastWithinOrSelf(*node); 658 Node& n = NodeTraversal::lastWithinOrSelf(*node);
634 if (m_downstreamEnd.anchorNode() == n && 659 if (m_downstreamEnd.anchorNode() == n &&
635 m_downstreamEnd.computeEditingOffset() >= caretMaxOffset(&n)) { 660 m_downstreamEnd.computeEditingOffset() >= caretMaxOffset(&n)) {
636 removeNode(node, editingState); 661 removeNode(node, editingState);
(...skipping 16 matching lines...) Expand all
653 !canHaveChildrenForEditing(m_downstreamEnd.anchorNode())) { 678 !canHaveChildrenForEditing(m_downstreamEnd.anchorNode())) {
654 // The node itself is fully selected, not just its contents. Delete it. 679 // The node itself is fully selected, not just its contents. Delete it.
655 removeNode(m_downstreamEnd.anchorNode(), editingState); 680 removeNode(m_downstreamEnd.anchorNode(), editingState);
656 } else { 681 } else {
657 if (m_downstreamEnd.anchorNode()->isTextNode()) { 682 if (m_downstreamEnd.anchorNode()->isTextNode()) {
658 // in a text node that needs to be trimmed 683 // in a text node that needs to be trimmed
659 Text* text = toText(m_downstreamEnd.anchorNode()); 684 Text* text = toText(m_downstreamEnd.anchorNode());
660 if (m_downstreamEnd.computeEditingOffset() > 0) { 685 if (m_downstreamEnd.computeEditingOffset() > 0) {
661 deleteTextFromNode(text, 0, m_downstreamEnd.computeEditingOffset()); 686 deleteTextFromNode(text, 0, m_downstreamEnd.computeEditingOffset());
662 } 687 }
663 // Remove children of m_downstreamEnd.anchorNode() that come after m_u pstreamStart. 688 // Remove children of m_downstreamEnd.anchorNode() that come after
664 // Don't try to remove children if m_upstreamStart was inside m_downst reamEnd.anchorNode() 689 // m_upstreamStart. Don't try to remove children if m_upstreamStart
665 // and m_upstreamStart has been removed from the document, because the n we don't 690 // was inside m_downstreamEnd.anchorNode() and m_upstreamStart has
666 // know how many children to remove. 691 // been removed from the document, because then we don't know how many
667 // FIXME: Make m_upstreamStart a position we update as we remove conte nt, then we can 692 // children to remove.
668 // always know which children to remove. 693 // FIXME: Make m_upstreamStart a position we update as we remove
694 // content, then we can always know which children to remove.
669 } else if (!(startNodeWasDescendantOfEndNode && 695 } else if (!(startNodeWasDescendantOfEndNode &&
670 !m_upstreamStart.isConnected())) { 696 !m_upstreamStart.isConnected())) {
671 int offset = 0; 697 int offset = 0;
672 if (m_upstreamStart.anchorNode()->isDescendantOf( 698 if (m_upstreamStart.anchorNode()->isDescendantOf(
673 m_downstreamEnd.anchorNode())) { 699 m_downstreamEnd.anchorNode())) {
674 Node* n = m_upstreamStart.anchorNode(); 700 Node* n = m_upstreamStart.anchorNode();
675 while (n && n->parentNode() != m_downstreamEnd.anchorNode()) 701 while (n && n->parentNode() != m_downstreamEnd.anchorNode())
676 n = n->parentNode(); 702 n = n->parentNode();
677 if (n) 703 if (n)
678 offset = n->nodeIndex() + 1; 704 offset = n->nodeIndex() + 1;
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
712 Text* textNode = toText(m_trailingWhitespace.anchorNode()); 738 Text* textNode = toText(m_trailingWhitespace.anchorNode());
713 DCHECK(!textNode->layoutObject() || 739 DCHECK(!textNode->layoutObject() ||
714 textNode->layoutObject()->style()->collapseWhiteSpace()) 740 textNode->layoutObject()->style()->collapseWhiteSpace())
715 << textNode; 741 << textNode;
716 replaceTextInNodePreservingMarkers( 742 replaceTextInNodePreservingMarkers(
717 textNode, m_trailingWhitespace.computeOffsetInContainerNode(), 1, 743 textNode, m_trailingWhitespace.computeOffsetInContainerNode(), 1,
718 nonBreakingSpaceString()); 744 nonBreakingSpaceString());
719 } 745 }
720 } 746 }
721 747
722 // If a selection starts in one block and ends in another, we have to merge to b ring content before the 748 // If a selection starts in one block and ends in another, we have to merge to
723 // start together with content after the end. 749 // bring content before the start together with content after the end.
724 void DeleteSelectionCommand::mergeParagraphs(EditingState* editingState) { 750 void DeleteSelectionCommand::mergeParagraphs(EditingState* editingState) {
725 if (!m_mergeBlocksAfterDelete) { 751 if (!m_mergeBlocksAfterDelete) {
726 if (m_pruneStartBlockIfNecessary) { 752 if (m_pruneStartBlockIfNecessary) {
727 // We aren't going to merge into the start block, so remove it if it's emp ty. 753 // We aren't going to merge into the start block, so remove it if it's
754 // empty.
728 prune(m_startBlock, editingState); 755 prune(m_startBlock, editingState);
729 if (editingState->isAborted()) 756 if (editingState->isAborted())
730 return; 757 return;
731 // Removing the start block during a deletion is usually an indication tha t we need 758 // Removing the start block during a deletion is usually an indication
732 // a placeholder, but not in this case. 759 // that we need a placeholder, but not in this case.
733 m_needPlaceholder = false; 760 m_needPlaceholder = false;
734 } 761 }
735 return; 762 return;
736 } 763 }
737 764
738 // It shouldn't have been asked to both try and merge content into the start b lock and prune it. 765 // It shouldn't have been asked to both try and merge content into the start
766 // block and prune it.
739 DCHECK(!m_pruneStartBlockIfNecessary); 767 DCHECK(!m_pruneStartBlockIfNecessary);
740 768
741 // FIXME: Deletion should adjust selection endpoints as it removes nodes so th at we never get into this state (4099839). 769 // FIXME: Deletion should adjust selection endpoints as it removes nodes so
770 // that we never get into this state (4099839).
742 if (!m_downstreamEnd.isConnected() || !m_upstreamStart.isConnected()) 771 if (!m_downstreamEnd.isConnected() || !m_upstreamStart.isConnected())
743 return; 772 return;
744 773
745 // FIXME: The deletion algorithm shouldn't let this happen. 774 // FIXME: The deletion algorithm shouldn't let this happen.
746 if (comparePositions(m_upstreamStart, m_downstreamEnd) > 0) 775 if (comparePositions(m_upstreamStart, m_downstreamEnd) > 0)
747 return; 776 return;
748 777
749 // There's nothing to merge. 778 // There's nothing to merge.
750 if (m_upstreamStart == m_downstreamEnd) 779 if (m_upstreamStart == m_downstreamEnd)
751 return; 780 return;
752 781
753 VisiblePosition startOfParagraphToMove = 782 VisiblePosition startOfParagraphToMove =
754 createVisiblePositionDeprecated(m_downstreamEnd); 783 createVisiblePositionDeprecated(m_downstreamEnd);
755 VisiblePosition mergeDestination = 784 VisiblePosition mergeDestination =
756 createVisiblePositionDeprecated(m_upstreamStart); 785 createVisiblePositionDeprecated(m_upstreamStart);
757 786
758 // m_downstreamEnd's block has been emptied out by deletion. There is no cont ent inside of it to 787 // m_downstreamEnd's block has been emptied out by deletion. There is no
759 // move, so just remove it. 788 // content inside of it to move, so just remove it.
760 Element* endBlock = enclosingBlock(m_downstreamEnd.anchorNode()); 789 Element* endBlock = enclosingBlock(m_downstreamEnd.anchorNode());
761 if (!endBlock || 790 if (!endBlock ||
762 !endBlock->contains( 791 !endBlock->contains(
763 startOfParagraphToMove.deepEquivalent().anchorNode()) || 792 startOfParagraphToMove.deepEquivalent().anchorNode()) ||
764 !startOfParagraphToMove.deepEquivalent().anchorNode()) { 793 !startOfParagraphToMove.deepEquivalent().anchorNode()) {
765 removeNode(enclosingBlock(m_downstreamEnd.anchorNode()), editingState); 794 removeNode(enclosingBlock(m_downstreamEnd.anchorNode()), editingState);
766 return; 795 return;
767 } 796 }
768 797
769 // We need to merge into m_upstreamStart's block, but it's been emptied out an d collapsed by deletion. 798 // We need to merge into m_upstreamStart's block, but it's been emptied out
799 // and collapsed by deletion.
770 if (!mergeDestination.deepEquivalent().anchorNode() || 800 if (!mergeDestination.deepEquivalent().anchorNode() ||
771 (!mergeDestination.deepEquivalent().anchorNode()->isDescendantOf( 801 (!mergeDestination.deepEquivalent().anchorNode()->isDescendantOf(
772 enclosingBlock(m_upstreamStart.computeContainerNode())) && 802 enclosingBlock(m_upstreamStart.computeContainerNode())) &&
773 (!mergeDestination.deepEquivalent().anchorNode()->hasChildren() || 803 (!mergeDestination.deepEquivalent().anchorNode()->hasChildren() ||
774 !m_upstreamStart.computeContainerNode()->hasChildren())) || 804 !m_upstreamStart.computeContainerNode()->hasChildren())) ||
775 (m_startsAtEmptyLine && 805 (m_startsAtEmptyLine &&
776 mergeDestination.deepEquivalent() != 806 mergeDestination.deepEquivalent() !=
777 startOfParagraphToMove.deepEquivalent())) { 807 startOfParagraphToMove.deepEquivalent())) {
778 PositionWithAffinity storedStartOfParagraphToMove = 808 PositionWithAffinity storedStartOfParagraphToMove =
779 startOfParagraphToMove.toPositionWithAffinity(); 809 startOfParagraphToMove.toPositionWithAffinity();
(...skipping 11 matching lines...) Expand all
791 startOfParagraphToMove.deepEquivalent()) 821 startOfParagraphToMove.deepEquivalent())
792 return; 822 return;
793 823
794 VisiblePosition endOfParagraphToMove = endOfParagraphDeprecated( 824 VisiblePosition endOfParagraphToMove = endOfParagraphDeprecated(
795 startOfParagraphToMove, CanSkipOverEditingBoundary); 825 startOfParagraphToMove, CanSkipOverEditingBoundary);
796 826
797 if (mergeDestination.deepEquivalent() == 827 if (mergeDestination.deepEquivalent() ==
798 endOfParagraphToMove.deepEquivalent()) 828 endOfParagraphToMove.deepEquivalent())
799 return; 829 return;
800 830
801 // If the merge destination and source to be moved are both list items of diff erent lists, merge them into single list. 831 // If the merge destination and source to be moved are both list items of
832 // different lists, merge them into single list.
802 Node* listItemInFirstParagraph = 833 Node* listItemInFirstParagraph =
803 enclosingNodeOfType(m_upstreamStart, isListItem); 834 enclosingNodeOfType(m_upstreamStart, isListItem);
804 Node* listItemInSecondParagraph = 835 Node* listItemInSecondParagraph =
805 enclosingNodeOfType(m_downstreamEnd, isListItem); 836 enclosingNodeOfType(m_downstreamEnd, isListItem);
806 if (listItemInFirstParagraph && listItemInSecondParagraph && 837 if (listItemInFirstParagraph && listItemInSecondParagraph &&
807 listItemInFirstParagraph->parentElement() != 838 listItemInFirstParagraph->parentElement() !=
808 listItemInSecondParagraph->parentElement() && 839 listItemInSecondParagraph->parentElement() &&
809 canMergeLists(listItemInFirstParagraph->parentElement(), 840 canMergeLists(listItemInFirstParagraph->parentElement(),
810 listItemInSecondParagraph->parentElement())) { 841 listItemInSecondParagraph->parentElement())) {
811 mergeIdenticalElements(listItemInFirstParagraph->parentElement(), 842 mergeIdenticalElements(listItemInFirstParagraph->parentElement(),
812 listItemInSecondParagraph->parentElement(), 843 listItemInSecondParagraph->parentElement(),
813 editingState); 844 editingState);
814 if (editingState->isAborted()) 845 if (editingState->isAborted())
815 return; 846 return;
816 m_endingPosition = mergeDestination.deepEquivalent(); 847 m_endingPosition = mergeDestination.deepEquivalent();
817 return; 848 return;
818 } 849 }
819 850
820 // The rule for merging into an empty block is: only do so if its farther to t he right. 851 // The rule for merging into an empty block is: only do so if its farther to
852 // the right.
821 // FIXME: Consider RTL. 853 // FIXME: Consider RTL.
822 if (!m_startsAtEmptyLine && isStartOfParagraphDeprecated(mergeDestination) && 854 if (!m_startsAtEmptyLine && isStartOfParagraphDeprecated(mergeDestination) &&
823 absoluteCaretBoundsOf(startOfParagraphToMove).x() > 855 absoluteCaretBoundsOf(startOfParagraphToMove).x() >
824 absoluteCaretBoundsOf(mergeDestination).x()) { 856 absoluteCaretBoundsOf(mergeDestination).x()) {
825 if (isHTMLBRElement( 857 if (isHTMLBRElement(
826 *mostForwardCaretPosition(mergeDestination.deepEquivalent()) 858 *mostForwardCaretPosition(mergeDestination.deepEquivalent())
827 .anchorNode())) { 859 .anchorNode())) {
828 removeNodeAndPruneAncestors( 860 removeNodeAndPruneAncestors(
829 mostForwardCaretPosition(mergeDestination.deepEquivalent()) 861 mostForwardCaretPosition(mergeDestination.deepEquivalent())
830 .anchorNode(), 862 .anchorNode(),
831 editingState); 863 editingState);
832 if (editingState->isAborted()) 864 if (editingState->isAborted())
833 return; 865 return;
834 m_endingPosition = startOfParagraphToMove.deepEquivalent(); 866 m_endingPosition = startOfParagraphToMove.deepEquivalent();
835 return; 867 return;
836 } 868 }
837 } 869 }
838 870
839 // Block images, tables and horizontal rules cannot be made inline with conten t at mergeDestination. If there is 871 // Block images, tables and horizontal rules cannot be made inline with
840 // any (!isStartOfParagraphDeprecated(mergeDestination)), don't merge, just mo ve the caret to just before the selection we deleted. 872 // content at mergeDestination. If there is any
841 // See https://bugs.webkit.org/show_bug.cgi?id=25439 873 // (!isStartOfParagraphDeprecated(mergeDestination)), don't merge, just move
874 // the caret to just before the selection we deleted. See
875 // https://bugs.webkit.org/show_bug.cgi?id=25439
842 if (isRenderedAsNonInlineTableImageOrHR( 876 if (isRenderedAsNonInlineTableImageOrHR(
843 startOfParagraphToMove.deepEquivalent().anchorNode()) && 877 startOfParagraphToMove.deepEquivalent().anchorNode()) &&
844 !isStartOfParagraphDeprecated(mergeDestination)) { 878 !isStartOfParagraphDeprecated(mergeDestination)) {
845 m_endingPosition = m_upstreamStart; 879 m_endingPosition = m_upstreamStart;
846 return; 880 return;
847 } 881 }
848 882
849 // moveParagraphs will insert placeholders if it removes blocks that would req uire their use, don't let block 883 // moveParagraphs will insert placeholders if it removes blocks that would
850 // removals that it does cause the insertion of *another* placeholder. 884 // require their use, don't let block removals that it does cause the
885 // insertion of *another* placeholder.
851 bool needPlaceholder = m_needPlaceholder; 886 bool needPlaceholder = m_needPlaceholder;
852 bool paragraphToMergeIsEmpty = startOfParagraphToMove.deepEquivalent() == 887 bool paragraphToMergeIsEmpty = startOfParagraphToMove.deepEquivalent() ==
853 endOfParagraphToMove.deepEquivalent(); 888 endOfParagraphToMove.deepEquivalent();
854 moveParagraph(startOfParagraphToMove, endOfParagraphToMove, mergeDestination, 889 moveParagraph(startOfParagraphToMove, endOfParagraphToMove, mergeDestination,
855 editingState, DoNotPreserveSelection, 890 editingState, DoNotPreserveSelection,
856 paragraphToMergeIsEmpty ? DoNotPreserveStyle : PreserveStyle); 891 paragraphToMergeIsEmpty ? DoNotPreserveStyle : PreserveStyle);
857 if (editingState->isAborted()) 892 if (editingState->isAborted())
858 return; 893 return;
859 m_needPlaceholder = needPlaceholder; 894 m_needPlaceholder = needPlaceholder;
860 // The endingPosition was likely clobbered by the move, so recompute it (moveP aragraph selects the moved paragraph). 895 // The endingPosition was likely clobbered by the move, so recompute it
896 // (moveParagraph selects the moved paragraph).
861 m_endingPosition = endingSelection().start(); 897 m_endingPosition = endingSelection().start();
862 } 898 }
863 899
864 void DeleteSelectionCommand::removePreviouslySelectedEmptyTableRows( 900 void DeleteSelectionCommand::removePreviouslySelectedEmptyTableRows(
865 EditingState* editingState) { 901 EditingState* editingState) {
866 if (m_endTableRow && m_endTableRow->isConnected() && 902 if (m_endTableRow && m_endTableRow->isConnected() &&
867 m_endTableRow != m_startTableRow) { 903 m_endTableRow != m_startTableRow) {
868 Node* row = m_endTableRow->previousSibling(); 904 Node* row = m_endTableRow->previousSibling();
869 while (row && row != m_startTableRow) { 905 while (row && row != m_startTableRow) {
870 Node* previousRow = row->previousSibling(); 906 Node* previousRow = row->previousSibling();
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
913 } 949 }
914 } 950 }
915 951
916 void DeleteSelectionCommand::calculateTypingStyleAfterDelete() { 952 void DeleteSelectionCommand::calculateTypingStyleAfterDelete() {
917 // Clearing any previously set typing style and doing an early return. 953 // Clearing any previously set typing style and doing an early return.
918 if (!m_typingStyle) { 954 if (!m_typingStyle) {
919 document().frame()->selection().clearTypingStyle(); 955 document().frame()->selection().clearTypingStyle();
920 return; 956 return;
921 } 957 }
922 958
923 // Compute the difference between the style before the delete and the style no w 959 // Compute the difference between the style before the delete and the style
924 // after the delete has been done. Set this style on the frame, so other editi ng 960 // now after the delete has been done. Set this style on the frame, so other
925 // commands being composed with this one will work, and also cache it on the c ommand, 961 // editing commands being composed with this one will work, and also cache it
926 // so the LocalFrame::appliedEditing can set it after the whole composite comm and 962 // on the command, so the LocalFrame::appliedEditing can set it after the
927 // has completed. 963 // whole composite command has completed.
928 964
929 // If we deleted into a blockquote, but are now no longer in a blockquote, use the alternate typing style 965 // If we deleted into a blockquote, but are now no longer in a blockquote, use
966 // the alternate typing style
930 if (m_deleteIntoBlockquoteStyle && 967 if (m_deleteIntoBlockquoteStyle &&
931 !enclosingNodeOfType(m_endingPosition, isMailHTMLBlockquoteElement, 968 !enclosingNodeOfType(m_endingPosition, isMailHTMLBlockquoteElement,
932 CanCrossEditingBoundary)) 969 CanCrossEditingBoundary))
933 m_typingStyle = m_deleteIntoBlockquoteStyle; 970 m_typingStyle = m_deleteIntoBlockquoteStyle;
934 m_deleteIntoBlockquoteStyle = nullptr; 971 m_deleteIntoBlockquoteStyle = nullptr;
935 972
936 m_typingStyle->prepareToApplyAt(m_endingPosition); 973 m_typingStyle->prepareToApplyAt(m_endingPosition);
937 if (m_typingStyle->isEmpty()) 974 if (m_typingStyle->isEmpty())
938 m_typingStyle = nullptr; 975 m_typingStyle = nullptr;
939 // This is where we've deleted all traces of a style but not a whole paragraph (that's handled above). 976 // This is where we've deleted all traces of a style but not a whole paragraph
940 // In this case if we start typing, the new characters should have the same st yle as the just deleted ones, 977 // (that's handled above). In this case if we start typing, the new characters
941 // but, if we change the selection, come back and start typing that style shou ld be lost. Also see 978 // should have the same style as the just deleted ones, but, if we change the
979 // selection, come back and start typing that style should be lost. Also see
942 // preserveTypingStyle() below. 980 // preserveTypingStyle() below.
943 document().frame()->selection().setTypingStyle(m_typingStyle); 981 document().frame()->selection().setTypingStyle(m_typingStyle);
944 } 982 }
945 983
946 void DeleteSelectionCommand::clearTransientState() { 984 void DeleteSelectionCommand::clearTransientState() {
947 m_selectionToDelete = VisibleSelection(); 985 m_selectionToDelete = VisibleSelection();
948 m_upstreamStart = Position(); 986 m_upstreamStart = Position();
949 m_downstreamStart = Position(); 987 m_downstreamStart = Position();
950 m_upstreamEnd = Position(); 988 m_upstreamEnd = Position();
951 m_downstreamEnd = Position(); 989 m_downstreamEnd = Position();
952 m_endingPosition = Position(); 990 m_endingPosition = Position();
953 m_leadingWhitespace = Position(); 991 m_leadingWhitespace = Position();
954 m_trailingWhitespace = Position(); 992 m_trailingWhitespace = Position();
955 m_referenceMovePosition = Position(); 993 m_referenceMovePosition = Position();
956 } 994 }
957 995
958 // This method removes div elements with no attributes that have only one child or no children at all. 996 // This method removes div elements with no attributes that have only one child
997 // or no children at all.
959 void DeleteSelectionCommand::removeRedundantBlocks(EditingState* editingState) { 998 void DeleteSelectionCommand::removeRedundantBlocks(EditingState* editingState) {
960 Node* node = m_endingPosition.computeContainerNode(); 999 Node* node = m_endingPosition.computeContainerNode();
961 Element* rootElement = rootEditableElement(*node); 1000 Element* rootElement = rootEditableElement(*node);
962 1001
963 while (node != rootElement) { 1002 while (node != rootElement) {
964 if (isRemovableBlock(node)) { 1003 if (isRemovableBlock(node)) {
965 if (node == m_endingPosition.anchorNode()) 1004 if (node == m_endingPosition.anchorNode())
966 updatePositionForNodeRemovalPreservingChildren(m_endingPosition, *node); 1005 updatePositionForNodeRemovalPreservingChildren(m_endingPosition, *node);
967 1006
968 CompositeEditCommand::removeNodePreservingChildren(node, editingState); 1007 CompositeEditCommand::removeNodePreservingChildren(node, editingState);
969 if (editingState->isAborted()) 1008 if (editingState->isAborted())
970 return; 1009 return;
971 node = m_endingPosition.anchorNode(); 1010 node = m_endingPosition.anchorNode();
972 } else { 1011 } else {
973 node = node->parentNode(); 1012 node = node->parentNode();
974 } 1013 }
975 } 1014 }
976 } 1015 }
977 1016
978 void DeleteSelectionCommand::doApply(EditingState* editingState) { 1017 void DeleteSelectionCommand::doApply(EditingState* editingState) {
979 // If selection has not been set to a custom selection when the command was cr eated, 1018 // If selection has not been set to a custom selection when the command was
980 // use the current ending selection. 1019 // created, use the current ending selection.
981 if (!m_hasSelectionToDelete) 1020 if (!m_hasSelectionToDelete)
982 m_selectionToDelete = endingSelection(); 1021 m_selectionToDelete = endingSelection();
983 1022
984 if (!m_selectionToDelete.isNonOrphanedRange() || 1023 if (!m_selectionToDelete.isNonOrphanedRange() ||
985 !m_selectionToDelete.isContentEditable()) 1024 !m_selectionToDelete.isContentEditable())
986 return; 1025 return;
987 1026
988 RelocatablePosition relocatableReferencePosition(m_referenceMovePosition); 1027 RelocatablePosition relocatableReferencePosition(m_referenceMovePosition);
989 1028
990 // save this to later make the selection with 1029 // save this to later make the selection with
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
1092 calculateTypingStyleAfterDelete(); 1131 calculateTypingStyleAfterDelete();
1093 1132
1094 setEndingSelection(createVisibleSelectionDeprecated( 1133 setEndingSelection(createVisibleSelectionDeprecated(
1095 m_endingPosition, affinity, endingSelection().isDirectional())); 1134 m_endingPosition, affinity, endingSelection().isDirectional()));
1096 1135
1097 if (relocatableReferencePosition.position().isNull()) { 1136 if (relocatableReferencePosition.position().isNull()) {
1098 clearTransientState(); 1137 clearTransientState();
1099 return; 1138 return;
1100 } 1139 }
1101 1140
1102 // This deletion command is part of a move operation, we need to cleanup after deletion. 1141 // This deletion command is part of a move operation, we need to cleanup after
1142 // deletion.
1103 m_referenceMovePosition = relocatableReferencePosition.position(); 1143 m_referenceMovePosition = relocatableReferencePosition.position();
1104 // If the node for the destination has been removed as a result of the deletio n, 1144 // If the node for the destination has been removed as a result of the
1105 // set the destination to the ending point after the deletion. 1145 // deletion, set the destination to the ending point after the deletion.
1106 // Fixes: <rdar://problem/3910425> REGRESSION (Mail): Crash in ReplaceSelectio nCommand; 1146 // Fixes: <rdar://problem/3910425> REGRESSION (Mail): Crash in
1107 // selection is empty, leading to null deref 1147 // ReplaceSelectionCommand; selection is empty, leading to null deref
1108 if (!m_referenceMovePosition.isConnected()) 1148 if (!m_referenceMovePosition.isConnected())
1109 m_referenceMovePosition = endingSelection().start(); 1149 m_referenceMovePosition = endingSelection().start();
1110 1150
1111 // Move selection shouldn't left empty <li> block. 1151 // Move selection shouldn't left empty <li> block.
1112 cleanupAfterDeletion( 1152 cleanupAfterDeletion(
1113 editingState, createVisiblePositionDeprecated(m_referenceMovePosition)); 1153 editingState, createVisiblePositionDeprecated(m_referenceMovePosition));
1114 if (editingState->isAborted()) 1154 if (editingState->isAborted())
1115 return; 1155 return;
1116 1156
1117 clearTransientState(); 1157 clearTransientState();
1118 } 1158 }
1119 1159
1120 InputEvent::InputType DeleteSelectionCommand::inputType() const { 1160 InputEvent::InputType DeleteSelectionCommand::inputType() const {
1121 // |DeleteSelectionCommand| could be used with Cut, Menu Bar deletion and |Typ ingCommand|. 1161 // |DeleteSelectionCommand| could be used with Cut, Menu Bar deletion and
1162 // |TypingCommand|.
1122 // 1. Cut and Menu Bar deletion should rely on correct |m_inputType|. 1163 // 1. Cut and Menu Bar deletion should rely on correct |m_inputType|.
1123 // 2. |TypingCommand| will supply the |inputType()|, so |m_inputType| could de fault to |InputType::None|. 1164 // 2. |TypingCommand| will supply the |inputType()|, so |m_inputType| could
1165 // default to |InputType::None|.
1124 return m_inputType; 1166 return m_inputType;
1125 } 1167 }
1126 1168
1127 // Normally deletion doesn't preserve the typing style that was present before i t. For example, 1169 // Normally deletion doesn't preserve the typing style that was present before
1128 // type a character, Bold, then delete the character and start typing. The Bold typing style shouldn't 1170 // it. For example, type a character, Bold, then delete the character and start
1129 // stick around. Deletion should preserve a typing style that *it* sets, howeve r. 1171 // typing. The Bold typing style shouldn't stick around. Deletion should
1172 // preserve a typing style that *it* sets, however.
1130 bool DeleteSelectionCommand::preservesTypingStyle() const { 1173 bool DeleteSelectionCommand::preservesTypingStyle() const {
1131 return m_typingStyle; 1174 return m_typingStyle;
1132 } 1175 }
1133 1176
1134 DEFINE_TRACE(DeleteSelectionCommand) { 1177 DEFINE_TRACE(DeleteSelectionCommand) {
1135 visitor->trace(m_selectionToDelete); 1178 visitor->trace(m_selectionToDelete);
1136 visitor->trace(m_upstreamStart); 1179 visitor->trace(m_upstreamStart);
1137 visitor->trace(m_downstreamStart); 1180 visitor->trace(m_downstreamStart);
1138 visitor->trace(m_upstreamEnd); 1181 visitor->trace(m_upstreamEnd);
1139 visitor->trace(m_downstreamEnd); 1182 visitor->trace(m_downstreamEnd);
1140 visitor->trace(m_endingPosition); 1183 visitor->trace(m_endingPosition);
1141 visitor->trace(m_leadingWhitespace); 1184 visitor->trace(m_leadingWhitespace);
1142 visitor->trace(m_trailingWhitespace); 1185 visitor->trace(m_trailingWhitespace);
1143 visitor->trace(m_referenceMovePosition); 1186 visitor->trace(m_referenceMovePosition);
1144 visitor->trace(m_startBlock); 1187 visitor->trace(m_startBlock);
1145 visitor->trace(m_endBlock); 1188 visitor->trace(m_endBlock);
1146 visitor->trace(m_typingStyle); 1189 visitor->trace(m_typingStyle);
1147 visitor->trace(m_deleteIntoBlockquoteStyle); 1190 visitor->trace(m_deleteIntoBlockquoteStyle);
1148 visitor->trace(m_startRoot); 1191 visitor->trace(m_startRoot);
1149 visitor->trace(m_endRoot); 1192 visitor->trace(m_endRoot);
1150 visitor->trace(m_startTableRow); 1193 visitor->trace(m_startTableRow);
1151 visitor->trace(m_endTableRow); 1194 visitor->trace(m_endTableRow);
1152 visitor->trace(m_temporaryPlaceholder); 1195 visitor->trace(m_temporaryPlaceholder);
1153 CompositeEditCommand::trace(visitor); 1196 CompositeEditCommand::trace(visitor);
1154 } 1197 }
1155 1198
1156 } // namespace blink 1199 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698