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

Side by Side Diff: Source/core/editing/DeleteSelectionCommand.cpp

Issue 24278008: [oilpan] Handlify Nodes in htmlediting (Closed) Base URL: svn://svn.chromium.org/blink/branches/oilpan
Patch Set: Created 7 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 | Annotate | Revision Log
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 29 matching lines...) Expand all
40 #include "core/html/HTMLInputElement.h" 40 #include "core/html/HTMLInputElement.h"
41 #include "core/html/HTMLTextAreaElement.h" 41 #include "core/html/HTMLTextAreaElement.h"
42 #include "core/page/EditorClient.h" 42 #include "core/page/EditorClient.h"
43 #include "core/page/Frame.h" 43 #include "core/page/Frame.h"
44 #include "core/rendering/RenderTableCell.h" 44 #include "core/rendering/RenderTableCell.h"
45 45
46 namespace WebCore { 46 namespace WebCore {
47 47
48 using namespace HTMLNames; 48 using namespace HTMLNames;
49 49
50 static bool isTableRow(const Node* node) 50 static bool isTableRow(const Handle<const Node>& node)
51 { 51 {
52 return node && node->hasTagName(trTag); 52 return node && node->hasTagName(trTag);
53 } 53 }
54 54
55 static bool isTableCellEmpty(const Handle<Node>& cell) 55 static bool isTableCellEmpty(const Handle<Node>& cell)
56 { 56 {
57 ASSERT(isTableCell(cell.raw())); 57 ASSERT(isTableCell(cell));
58 return VisiblePosition(firstPositionInNode(cell)) == VisiblePosition(lastPos itionInNode(cell)); 58 return VisiblePosition(firstPositionInNode(cell)) == VisiblePosition(lastPos itionInNode(cell));
59 } 59 }
60 60
61 static bool isTableRowEmpty(const Handle<Node>& row) 61 static bool isTableRowEmpty(const Handle<Node>& row)
62 { 62 {
63 if (!isTableRow(row.raw())) 63 if (!isTableRow(row))
64 return false; 64 return false;
65 65
66 for (Handle<Node> child = row->firstChild(); child; child = child->nextSibli ng()) { 66 for (Handle<Node> child = row->firstChild(); child; child = child->nextSibli ng()) {
67 HandleScope scope; 67 HandleScope scope;
68 if (isTableCell(child.raw()) && !isTableCellEmpty(child)) 68 if (isTableCell(child) && !isTableCellEmpty(child))
69 return false; 69 return false;
70 } 70 }
71 71
72 return true; 72 return true;
73 } 73 }
74 74
75 DeleteSelectionCommand::DeleteSelectionCommand(const Handle<Document>& document, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpec ialElements, bool sanitizeMarkup) 75 DeleteSelectionCommand::DeleteSelectionCommand(const Handle<Document>& document, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpec ialElements, bool sanitizeMarkup)
76 : CompositeEditCommand(document) 76 : CompositeEditCommand(document)
77 , m_hasSelectionToDelete(false) 77 , m_hasSelectionToDelete(false)
78 , m_smartDelete(smartDelete) 78 , m_smartDelete(smartDelete)
(...skipping 25 matching lines...) Expand all
104 , m_selectionToDelete(selection) 104 , m_selectionToDelete(selection)
105 , m_startBlock(0) 105 , m_startBlock(0)
106 , m_endBlock(0) 106 , m_endBlock(0)
107 , m_typingStyle(0) 107 , m_typingStyle(0)
108 , m_deleteIntoBlockquoteStyle(0) 108 , m_deleteIntoBlockquoteStyle(0)
109 { 109 {
110 } 110 }
111 111
112 void DeleteSelectionCommand::initializeStartEnd(Position& start, Position& end) 112 void DeleteSelectionCommand::initializeStartEnd(Position& start, Position& end)
113 { 113 {
114 Node* startSpecialContainer = 0; 114 Handle<Node> startSpecialContainer;
115 Node* endSpecialContainer = 0; 115 Handle<Node> endSpecialContainer;
116 116
117 start = m_selectionToDelete.start(); 117 start = m_selectionToDelete.start();
118 end = m_selectionToDelete.end(); 118 end = m_selectionToDelete.end();
119 119
120 // For HRs, we'll get a position at (HR,1) when hitting delete from the begi nning of the previous line, or (HR,0) when forward deleting, 120 // For HRs, we'll get a position at (HR,1) when hitting delete from the begi nning of the previous line, or (HR,0) when forward deleting,
121 // but in these cases, we want to delete it, so manually expand the selectio n 121 // but in these cases, we want to delete it, so manually expand the selectio n
122 if (start.deprecatedNode()->hasTagName(hrTag)) 122 if (start.deprecatedNode()->hasTagName(hrTag))
123 start = positionBeforeNode(start.deprecatedNode()); 123 start = positionBeforeNode(start.deprecatedNode());
124 else if (end.deprecatedNode()->hasTagName(hrTag)) 124 else if (end.deprecatedNode()->hasTagName(hrTag))
125 end = positionAfterNode(end.deprecatedNode()); 125 end = positionAfterNode(end.deprecatedNode());
126 126
127 // FIXME: This is only used so that moveParagraphs can avoid the bugs in spe cial element expansion. 127 // FIXME: This is only used so that moveParagraphs can avoid the bugs in spe cial element expansion.
128 if (!m_expandForSpecialElements) 128 if (!m_expandForSpecialElements)
129 return; 129 return;
130 130
131 while (1) { 131 while (1) {
132 HandleScope scope; 132 HandleScope scope;
133 startSpecialContainer = 0; 133 startSpecialContainer = nullptr;
134 endSpecialContainer = 0; 134 endSpecialContainer = nullptr;
135 135
136 Position s = positionBeforeContainingSpecialElement(start, &startSpecial Container); 136 Position s = positionBeforeContainingSpecialElement(start, &startSpecial Container);
137 Position e = positionAfterContainingSpecialElement(end, &endSpecialConta iner); 137 Position e = positionAfterContainingSpecialElement(end, &endSpecialConta iner);
138 138
139 if (!startSpecialContainer && !endSpecialContainer) 139 if (!startSpecialContainer && !endSpecialContainer)
140 break; 140 break;
141 141
142 if (VisiblePosition(start) != m_selectionToDelete.visibleStart() || Visi blePosition(end) != m_selectionToDelete.visibleEnd()) 142 if (VisiblePosition(start) != m_selectionToDelete.visibleStart() || Visi blePosition(end) != m_selectionToDelete.visibleEnd())
143 break; 143 break;
144 144
145 // If we're going to expand to include the startSpecialContainer, it mus t be fully selected. 145 // If we're going to expand to include the startSpecialContainer, it mus t be fully selected.
146 if (startSpecialContainer && !endSpecialContainer && comparePositions(po sitionInParentAfterNode(adoptRawResult(startSpecialContainer)), end) > -1) 146 if (startSpecialContainer && !endSpecialContainer && comparePositions(po sitionInParentAfterNode(startSpecialContainer), end) > -1)
147 break; 147 break;
148 148
149 // If we're going to expand to include the endSpecialContainer, it must be fully selected. 149 // If we're going to expand to include the endSpecialContainer, it must be fully selected.
150 if (endSpecialContainer && !startSpecialContainer && comparePositions(st art, positionInParentBeforeNode(adoptRawResult(endSpecialContainer))) > -1) 150 if (endSpecialContainer && !startSpecialContainer && comparePositions(st art, positionInParentBeforeNode(endSpecialContainer)) > -1)
151 break; 151 break;
152 152
153 if (startSpecialContainer && startSpecialContainer->isDescendantOf(endSp ecialContainer)) 153 if (startSpecialContainer && startSpecialContainer->isDescendantOf(endSp ecialContainer.raw()))
154 // Don't adjust the end yet, it is the end of a special element that contains the start 154 // Don't adjust the end yet, it is the end of a special element that contains the start
155 // special element (which may or may not be fully selected). 155 // special element (which may or may not be fully selected).
156 start = s; 156 start = s;
157 else if (endSpecialContainer && endSpecialContainer->isDescendantOf(star tSpecialContainer)) 157 else if (endSpecialContainer && endSpecialContainer->isDescendantOf(star tSpecialContainer.raw()))
158 // Don't adjust the start yet, it is the start of a special element that contains the end 158 // Don't adjust the start yet, it is the start of a special element that contains the end
159 // special element (which may or may not be fully selected). 159 // special element (which may or may not be fully selected).
160 end = e; 160 end = e;
161 else { 161 else {
162 start = s; 162 start = s;
163 end = e; 163 end = e;
164 } 164 }
165 } 165 }
166 } 166 }
167 167
(...skipping 17 matching lines...) Expand all
185 initializeStartEnd(start, end); 185 initializeStartEnd(start, end);
186 186
187 m_upstreamStart = start.upstream(); 187 m_upstreamStart = start.upstream();
188 m_downstreamStart = start.downstream(); 188 m_downstreamStart = start.downstream();
189 m_upstreamEnd = end.upstream(); 189 m_upstreamEnd = end.upstream();
190 m_downstreamEnd = end.downstream(); 190 m_downstreamEnd = end.downstream();
191 191
192 m_startRoot = editableRootForPosition(start); 192 m_startRoot = editableRootForPosition(start);
193 m_endRoot = editableRootForPosition(end); 193 m_endRoot = editableRootForPosition(end);
194 194
195 m_startTableRow = adoptRawResult(enclosingNodeOfType(start, &isTableRow)); 195 m_startTableRow = enclosingNodeOfType(start, &isTableRow);
196 m_endTableRow = adoptRawResult(enclosingNodeOfType(end, &isTableRow)); 196 m_endTableRow = enclosingNodeOfType(end, &isTableRow);
197 197
198 // Don't move content out of a table cell. 198 // Don't move content out of a table cell.
199 // If the cell is non-editable, enclosingNodeOfType won't return it by defau lt, so 199 // If the cell is non-editable, enclosingNodeOfType won't return it by defau lt, so
200 // tell that function that we don't care if it returns non-editable nodes. 200 // tell that function that we don't care if it returns non-editable nodes.
201 Handle<Node> startCell = adoptRawResult(enclosingNodeOfType(m_upstreamStart, &isTableCell, CanCrossEditingBoundary)); 201 Handle<Node> startCell = enclosingNodeOfType(m_upstreamStart, &isTableCell, CanCrossEditingBoundary);
202 Handle<Node> endCell = adoptRawResult(enclosingNodeOfType(m_downstreamEnd, & isTableCell, CanCrossEditingBoundary)); 202 Handle<Node> endCell = enclosingNodeOfType(m_downstreamEnd, &isTableCell, Ca nCrossEditingBoundary);
203 // FIXME: This isn't right. A borderless table with two rows and a single c olumn would appear as two paragraphs. 203 // FIXME: This isn't right. A borderless table with two rows and a single c olumn would appear as two paragraphs.
204 if (endCell && endCell != startCell) 204 if (endCell && endCell != startCell)
205 m_mergeBlocksAfterDelete = false; 205 m_mergeBlocksAfterDelete = false;
206 206
207 // Usually the start and the end of the selection to delete are pulled toget her as a result of the deletion. 207 // Usually the start and the end of the selection to delete are pulled toget her as a result of the deletion.
208 // Sometimes they aren't (like when no merge is requested), so we must choos e one position to hold the caret 208 // Sometimes they aren't (like when no merge is requested), so we must choos e one position to hold the caret
209 // and receive the placeholder after deletion. 209 // and receive the placeholder after deletion.
210 VisiblePosition visibleEnd(m_downstreamEnd); 210 VisiblePosition visibleEnd(m_downstreamEnd);
211 if (m_mergeBlocksAfterDelete && !isEndOfParagraph(visibleEnd)) 211 if (m_mergeBlocksAfterDelete && !isEndOfParagraph(visibleEnd))
212 m_endingPosition = m_downstreamEnd; 212 m_endingPosition = m_downstreamEnd;
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
265 setStartingSelectionOnSmartDelete(m_downstreamStart, m_downstreamEnd ); 265 setStartingSelectionOnSmartDelete(m_downstreamStart, m_downstreamEnd );
266 } 266 }
267 } 267 }
268 268
269 // We must pass call parentAnchoredEquivalent on the positions since some ed iting positions 269 // We must pass call parentAnchoredEquivalent on the positions since some ed iting positions
270 // that appear inside their nodes aren't really inside them. [hr, 0] is one example. 270 // that appear inside their nodes aren't really inside them. [hr, 0] is one example.
271 // FIXME: parentAnchoredEquivalent should eventually be moved into enclosing element getters 271 // FIXME: parentAnchoredEquivalent should eventually be moved into enclosing element getters
272 // like the one below, since editing functions should obviously accept editi ng positions. 272 // like the one below, since editing functions should obviously accept editi ng positions.
273 // FIXME: Passing false to enclosingNodeOfType tells it that it's OK to retu rn a non-editable 273 // FIXME: Passing false to enclosingNodeOfType tells it that it's OK to retu rn a non-editable
274 // node. This was done to match existing behavior, but it seems wrong. 274 // node. This was done to match existing behavior, but it seems wrong.
275 m_startBlock = adoptRawResult(enclosingNodeOfType(m_downstreamStart.parentAn choredEquivalent(), &isBlock, CanCrossEditingBoundary)); 275 m_startBlock = enclosingNodeOfType(m_downstreamStart.parentAnchoredEquivalen t(), &isBlock, CanCrossEditingBoundary);
276 m_endBlock = adoptRawResult(enclosingNodeOfType(m_upstreamEnd.parentAnchored Equivalent(), &isBlock, CanCrossEditingBoundary)); 276 m_endBlock = enclosingNodeOfType(m_upstreamEnd.parentAnchoredEquivalent(), & isBlock, CanCrossEditingBoundary);
277 } 277 }
278 278
279 void DeleteSelectionCommand::saveTypingStyleState() 279 void DeleteSelectionCommand::saveTypingStyleState()
280 { 280 {
281 // A common case is deleting characters that are all from the same text node . In 281 // A common case is deleting characters that are all from the same text node . In
282 // that case, the style at the start of the selection before deletion will b e the 282 // that case, the style at the start of the selection before deletion will b e the
283 // same as the style at the start of the selection after deletion (since tho se 283 // same as the style at the start of the selection after deletion (since tho se
284 // two positions will be identical). Therefore there is no need to save the 284 // two positions will be identical). Therefore there is no need to save the
285 // typing style at the start of the selection, nor is there a reason to 285 // typing style at the start of the selection, nor is there a reason to
286 // compute the style at the start of the selection after deletion (see the 286 // compute the style at the start of the selection after deletion (see the
287 // early return in calculateTypingStyleAfterDelete). 287 // early return in calculateTypingStyleAfterDelete).
288 if (m_upstreamStart.deprecatedNode() == m_downstreamEnd.deprecatedNode() && m_upstreamStart.deprecatedNode()->isTextNode()) 288 if (m_upstreamStart.deprecatedNode() == m_downstreamEnd.deprecatedNode() && m_upstreamStart.deprecatedNode()->isTextNode())
289 return; 289 return;
290 290
291 // Figure out the typing style in effect before the delete is done. 291 // Figure out the typing style in effect before the delete is done.
292 m_typingStyle = EditingStyle::create(m_selectionToDelete.start()); 292 m_typingStyle = EditingStyle::create(m_selectionToDelete.start());
293 m_typingStyle->removeStyleAddedByNode(adoptRawResult(enclosingAnchorElement( m_selectionToDelete.start()))); 293 m_typingStyle->removeStyleAddedByNode(enclosingAnchorElement(m_selectionToDe lete.start()));
294 294
295 // If we're deleting into a Mail blockquote, save the style at end() instead of start() 295 // If we're deleting into a Mail blockquote, save the style at end() instead of start()
296 // We'll use this later in computeTypingStyleAfterDelete if we end up outsid e of a Mail blockquote 296 // We'll use this later in computeTypingStyleAfterDelete if we end up outsid e of a Mail blockquote
297 if (enclosingNodeOfType(m_selectionToDelete.start(), isMailBlockquote)) 297 if (enclosingNodeOfType(m_selectionToDelete.start(), isMailBlockquote))
298 m_deleteIntoBlockquoteStyle = EditingStyle::create(m_selectionToDelete.e nd()); 298 m_deleteIntoBlockquoteStyle = EditingStyle::create(m_selectionToDelete.e nd());
299 else 299 else
300 m_deleteIntoBlockquoteStyle = 0; 300 m_deleteIntoBlockquoteStyle = 0;
301 } 301 }
302 302
303 bool DeleteSelectionCommand::handleSpecialCaseBRDelete() 303 bool DeleteSelectionCommand::handleSpecialCaseBRDelete()
(...skipping 26 matching lines...) Expand all
330 } 330 }
331 331
332 static Position firstEditablePositionInNode(const Handle<Node>& node) 332 static Position firstEditablePositionInNode(const Handle<Node>& node)
333 { 333 {
334 ASSERT(node); 334 ASSERT(node);
335 Handle<Node> next = node; 335 Handle<Node> next = node;
336 while (next && !next->rendererIsEditable()) { 336 while (next && !next->rendererIsEditable()) {
337 HandleScope scope; 337 HandleScope scope;
338 next = NodeTraversal::next(next, node); 338 next = NodeTraversal::next(next, node);
339 } 339 }
340 return next ? firstPositionInOrBeforeNode(next.raw()) : Position(); 340 return next ? firstPositionInOrBeforeNode(next) : Position();
341 } 341 }
342 342
343 void DeleteSelectionCommand::removeNode(const Handle<Node>& node, ShouldAssumeCo ntentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable) 343 void DeleteSelectionCommand::removeNode(const Handle<Node>& node, ShouldAssumeCo ntentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)
344 { 344 {
345 if (!node) 345 if (!node)
346 return; 346 return;
347 347
348 if (m_startRoot != m_endRoot && !(node->isDescendantOf(m_startRoot.raw()) && node->isDescendantOf(m_endRoot.raw()))) { 348 if (m_startRoot != m_endRoot && !(node->isDescendantOf(m_startRoot.raw()) && node->isDescendantOf(m_endRoot.raw()))) {
349 // If a node is not in both the start and end editable roots, remove it only if its inside an editable region. 349 // If a node is not in both the start and end editable roots, remove it only if its inside an editable region.
350 if (!node->parentNode()->rendererIsEditable()) { 350 if (!node->parentNode()->rendererIsEditable()) {
(...skipping 10 matching lines...) Expand all
361 if (nextChild && nextChild->parentNode() != node) 361 if (nextChild && nextChild->parentNode() != node)
362 return; 362 return;
363 child = nextChild; 363 child = nextChild;
364 } 364 }
365 365
366 // Don't remove editable regions that are inside non-editable ones, just clear them. 366 // Don't remove editable regions that are inside non-editable ones, just clear them.
367 return; 367 return;
368 } 368 }
369 } 369 }
370 370
371 if (isTableStructureNode(node.raw()) || node->isRootEditableElement()) { 371 if (isTableStructureNode(node) || node->isRootEditableElement()) {
372 // Do not remove an element of table structure; remove its contents. 372 // Do not remove an element of table structure; remove its contents.
373 // Likewise for the root editable element. 373 // Likewise for the root editable element.
374 for (Handle<Node> child = node->firstChild(); child; ) { 374 for (Handle<Node> child = node->firstChild(); child; ) {
375 HandleScope scope; 375 HandleScope scope;
376 Handle<Node> remove = child; 376 Handle<Node> remove = child;
377 child = child->nextSibling(); 377 child = child->nextSibling();
378 removeNode(remove, shouldAssumeContentIsAlwaysEditable); 378 removeNode(remove, shouldAssumeContentIsAlwaysEditable);
379 } 379 }
380 380
381 // Make sure empty cell has some height, if a placeholder can be inserte d. 381 // Make sure empty cell has some height, if a placeholder can be inserte d.
382 document()->updateLayoutIgnorePendingStylesheets(); 382 document()->updateLayoutIgnorePendingStylesheets();
383 RenderObject *r = node->renderer(); 383 RenderObject *r = node->renderer();
384 if (r && r->isTableCell() && toRenderTableCell(r)->contentHeight() <= 0) { 384 if (r && r->isTableCell() && toRenderTableCell(r)->contentHeight() <= 0) {
385 Position firstEditablePosition = firstEditablePositionInNode(node); 385 Position firstEditablePosition = firstEditablePositionInNode(node);
386 if (firstEditablePosition.isNotNull()) 386 if (firstEditablePosition.isNotNull())
387 insertBlockPlaceholder(firstEditablePosition); 387 insertBlockPlaceholder(firstEditablePosition);
388 } 388 }
389 return; 389 return;
390 } 390 }
391 391
392 if (node == m_startBlock && !isEndOfBlock(VisiblePosition(firstPositionInNod e(m_startBlock)).previous())) 392 if (node == m_startBlock && !isEndOfBlock(VisiblePosition(firstPositionInNod e(m_startBlock)).previous()))
393 m_needPlaceholder = true; 393 m_needPlaceholder = true;
394 else if (node == m_endBlock && !isStartOfBlock(VisiblePosition(lastPositionI nNode(m_startBlock)).next())) 394 else if (node == m_endBlock && !isStartOfBlock(VisiblePosition(lastPositionI nNode(m_startBlock)).next()))
395 m_needPlaceholder = true; 395 m_needPlaceholder = true;
396 396
397 // FIXME: Update the endpoints of the range being deleted. 397 // FIXME: Update the endpoints of the range being deleted.
398 updatePositionForNodeRemoval(m_endingPosition, node.raw()); 398 updatePositionForNodeRemoval(m_endingPosition, node);
399 updatePositionForNodeRemoval(m_leadingWhitespace, node.raw()); 399 updatePositionForNodeRemoval(m_leadingWhitespace, node);
400 updatePositionForNodeRemoval(m_trailingWhitespace, node.raw()); 400 updatePositionForNodeRemoval(m_trailingWhitespace, node);
401 401
402 CompositeEditCommand::removeNode(node, shouldAssumeContentIsAlwaysEditable); 402 CompositeEditCommand::removeNode(node, shouldAssumeContentIsAlwaysEditable);
403 } 403 }
404 404
405 static void updatePositionForTextRemoval(const Handle<Node>& node, int offset, i nt count, Position& position) 405 static void updatePositionForTextRemoval(const Handle<Node>& node, int offset, i nt count, Position& position)
406 { 406 {
407 if (position.anchorType() != Position::PositionIsOffsetInAnchor || position. containerNode() != node) 407 if (position.anchorType() != Position::PositionIsOffsetInAnchor || position. containerNode() != node)
408 return; 408 return;
409 409
410 if (position.offsetInContainerNode() > offset + count) 410 if (position.offsetInContainerNode() > offset + count)
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
445 { 445 {
446 if (m_upstreamStart.isNull()) 446 if (m_upstreamStart.isNull())
447 return; 447 return;
448 448
449 int startOffset = m_upstreamStart.deprecatedEditingOffset(); 449 int startOffset = m_upstreamStart.deprecatedEditingOffset();
450 Handle<Node> startNode = m_upstreamStart.deprecatedNode(); 450 Handle<Node> startNode = m_upstreamStart.deprecatedNode();
451 451
452 makeStylingElementsDirectChildrenOfEditableRootToPreventStyleLoss(); 452 makeStylingElementsDirectChildrenOfEditableRootToPreventStyleLoss();
453 453
454 // Never remove the start block unless it's a table, in which case we won't merge content in. 454 // Never remove the start block unless it's a table, in which case we won't merge content in.
455 if (startNode == m_startBlock && startOffset == 0 && canHaveChildrenForEditi ng(startNode.raw()) && !startNode->hasTagName(tableTag)) { 455 if (startNode == m_startBlock && startOffset == 0 && canHaveChildrenForEditi ng(startNode) && !startNode->hasTagName(tableTag)) {
456 startOffset = 0; 456 startOffset = 0;
457 startNode = NodeTraversal::next(startNode); 457 startNode = NodeTraversal::next(startNode);
458 if (!startNode) 458 if (!startNode)
459 return; 459 return;
460 } 460 }
461 461
462 if (startOffset >= caretMaxOffset(startNode.raw()) && startNode->isTextNode( )) { 462 if (startOffset >= caretMaxOffset(startNode) && startNode->isTextNode()) {
463 Handle<Text> text = toText(startNode); 463 Handle<Text> text = toText(startNode);
464 if (text->length() > (unsigned)caretMaxOffset(startNode.raw())) 464 if (text->length() > (unsigned)caretMaxOffset(startNode))
465 deleteTextFromNode(text, caretMaxOffset(startNode.raw()), text->leng th() - caretMaxOffset(startNode.raw())); 465 deleteTextFromNode(text, caretMaxOffset(startNode), text->length() - caretMaxOffset(startNode));
466 } 466 }
467 467
468 if (startOffset >= lastOffsetForEditing(startNode.raw())) { 468 if (startOffset >= lastOffsetForEditing(startNode)) {
469 startNode = NodeTraversal::nextSkippingChildren(startNode); 469 startNode = NodeTraversal::nextSkippingChildren(startNode);
470 startOffset = 0; 470 startOffset = 0;
471 } 471 }
472 472
473 // Done adjusting the start. See if we're all done. 473 // Done adjusting the start. See if we're all done.
474 if (!startNode) 474 if (!startNode)
475 return; 475 return;
476 476
477 if (startNode == m_downstreamEnd.deprecatedNode()) { 477 if (startNode == m_downstreamEnd.deprecatedNode()) {
478 if (m_downstreamEnd.deprecatedEditingOffset() - startOffset > 0) { 478 if (m_downstreamEnd.deprecatedEditingOffset() - startOffset > 0) {
(...skipping 26 matching lines...) Expand all
505 node = startNode->childNode(startOffset); 505 node = startNode->childNode(startOffset);
506 } 506 }
507 } else if (startNode == m_upstreamEnd.deprecatedNode() && startNode->isT extNode()) { 507 } else if (startNode == m_upstreamEnd.deprecatedNode() && startNode->isT extNode()) {
508 Handle<Text> text = toText(m_upstreamEnd.deprecatedNode()); 508 Handle<Text> text = toText(m_upstreamEnd.deprecatedNode());
509 deleteTextFromNode(text, 0, m_upstreamEnd.deprecatedEditingOffset()) ; 509 deleteTextFromNode(text, 0, m_upstreamEnd.deprecatedEditingOffset()) ;
510 } 510 }
511 511
512 // handle deleting all nodes that are completely selected 512 // handle deleting all nodes that are completely selected
513 while (node && node != m_downstreamEnd.deprecatedNode()) { 513 while (node && node != m_downstreamEnd.deprecatedNode()) {
514 HandleScope scope; 514 HandleScope scope;
515 if (comparePositions(firstPositionInOrBeforeNode(node.raw()), m_down streamEnd) >= 0) { 515 if (comparePositions(firstPositionInOrBeforeNode(node), m_downstream End) >= 0) {
516 // NodeTraversal::nextSkippingChildren just blew past the end po sition, so stop deleting 516 // NodeTraversal::nextSkippingChildren just blew past the end po sition, so stop deleting
517 node = nullptr; 517 node = nullptr;
518 } else if (!m_downstreamEnd.deprecatedNode()->isDescendantOf(node.ra w())) { 518 } else if (!m_downstreamEnd.deprecatedNode()->isDescendantOf(node.ra w())) {
519 Handle<Node> nextNode = NodeTraversal::nextSkippingChildren(node ); 519 Handle<Node> nextNode = NodeTraversal::nextSkippingChildren(node );
520 // if we just removed a node from the end container, update end position so the 520 // if we just removed a node from the end container, update end position so the
521 // check above will work 521 // check above will work
522 updatePositionForNodeRemoval(m_downstreamEnd, node.raw()); 522 updatePositionForNodeRemoval(m_downstreamEnd, node);
523 removeNode(node); 523 removeNode(node);
524 node = nextNode; 524 node = nextNode;
525 } else { 525 } else {
526 Handle<Node> n = node->lastDescendant(); 526 Handle<Node> n = node->lastDescendant();
527 if (m_downstreamEnd.deprecatedNode() == n && m_downstreamEnd.dep recatedEditingOffset() >= caretMaxOffset(n.raw())) { 527 if (m_downstreamEnd.deprecatedNode() == n && m_downstreamEnd.dep recatedEditingOffset() >= caretMaxOffset(n)) {
528 removeNode(node); 528 removeNode(node);
529 node = nullptr; 529 node = nullptr;
530 } else 530 } else
531 node = NodeTraversal::next(node); 531 node = NodeTraversal::next(node);
532 } 532 }
533 } 533 }
534 534
535 if (m_downstreamEnd.deprecatedNode() != startNode && !m_upstreamStart.de precatedNode()->isDescendantOf(m_downstreamEnd.deprecatedNode().handle().raw()) && m_downstreamEnd.anchorNode()->inDocument() && m_downstreamEnd.deprecatedEditi ngOffset() >= caretMinOffset(m_downstreamEnd.deprecatedNode().handle().raw())) { 535 if (m_downstreamEnd.deprecatedNode() != startNode && !m_upstreamStart.de precatedNode()->isDescendantOf(m_downstreamEnd.deprecatedNode().handle().raw()) && m_downstreamEnd.anchorNode()->inDocument() && m_downstreamEnd.deprecatedEditi ngOffset() >= caretMinOffset(m_downstreamEnd.deprecatedNode())) {
536 if (m_downstreamEnd.atLastEditingPositionForNode() && !canHaveChildr enForEditing(m_downstreamEnd.deprecatedNode().handle().raw())) { 536 if (m_downstreamEnd.atLastEditingPositionForNode() && !canHaveChildr enForEditing(m_downstreamEnd.deprecatedNode())) {
537 // The node itself is fully selected, not just its contents. De lete it. 537 // The node itself is fully selected, not just its contents. De lete it.
538 removeNode(m_downstreamEnd.deprecatedNode()); 538 removeNode(m_downstreamEnd.deprecatedNode());
539 } else { 539 } else {
540 if (m_downstreamEnd.deprecatedNode()->isTextNode()) { 540 if (m_downstreamEnd.deprecatedNode()->isTextNode()) {
541 // in a text node that needs to be trimmed 541 // in a text node that needs to be trimmed
542 Handle<Text> text = toText(m_downstreamEnd.deprecatedNode()) ; 542 Handle<Text> text = toText(m_downstreamEnd.deprecatedNode()) ;
543 if (m_downstreamEnd.deprecatedEditingOffset() > 0) { 543 if (m_downstreamEnd.deprecatedEditingOffset() > 0) {
544 deleteTextFromNode(text, 0, m_downstreamEnd.deprecatedEd itingOffset()); 544 deleteTextFromNode(text, 0, m_downstreamEnd.deprecatedEd itingOffset());
545 } 545 }
546 // Remove children of m_downstreamEnd.deprecatedNode() that come after m_upstreamStart. 546 // Remove children of m_downstreamEnd.deprecatedNode() that come after m_upstreamStart.
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
612 612
613 // There's nothing to merge. 613 // There's nothing to merge.
614 if (m_upstreamStart == m_downstreamEnd) 614 if (m_upstreamStart == m_downstreamEnd)
615 return; 615 return;
616 616
617 VisiblePosition startOfParagraphToMove(m_downstreamEnd); 617 VisiblePosition startOfParagraphToMove(m_downstreamEnd);
618 VisiblePosition mergeDestination(m_upstreamStart); 618 VisiblePosition mergeDestination(m_upstreamStart);
619 619
620 // m_downstreamEnd's block has been emptied out by deletion. There is no co ntent inside of it to 620 // m_downstreamEnd's block has been emptied out by deletion. There is no co ntent inside of it to
621 // move, so just remove it. 621 // move, so just remove it.
622 Handle<Element> endBlock = enclosingBlock(m_downstreamEnd.deprecatedNode().h andle().raw()); 622 Handle<Element> endBlock = enclosingBlock(m_downstreamEnd.deprecatedNode());
623 if (!endBlock || !endBlock->contains(startOfParagraphToMove.deepEquivalent() .deprecatedNode()) || !startOfParagraphToMove.deepEquivalent().deprecatedNode()) { 623 if (!endBlock || !endBlock->contains(startOfParagraphToMove.deepEquivalent() .deprecatedNode()) || !startOfParagraphToMove.deepEquivalent().deprecatedNode()) {
624 removeNode(enclosingBlock(m_downstreamEnd.deprecatedNode().handle().raw( ))); 624 removeNode(enclosingBlock(m_downstreamEnd.deprecatedNode()));
625 return; 625 return;
626 } 626 }
627 627
628 // We need to merge into m_upstreamStart's block, but it's been emptied out and collapsed by deletion. 628 // We need to merge into m_upstreamStart's block, but it's been emptied out and collapsed by deletion.
629 if (!mergeDestination.deepEquivalent().deprecatedNode() || !mergeDestination .deepEquivalent().deprecatedNode()->isDescendantOf(enclosingBlock(m_upstreamStar t.containerNode().handle().raw()).handle().raw()) || m_startsAtEmptyLine) { 629 if (!mergeDestination.deepEquivalent().deprecatedNode() || !mergeDestination .deepEquivalent().deprecatedNode()->isDescendantOf(enclosingBlock(m_upstreamStar t.containerNode()).handle().raw()) || m_startsAtEmptyLine) {
630 insertNodeAt(createBreakElement(document()), m_upstreamStart); 630 insertNodeAt(createBreakElement(document()), m_upstreamStart);
631 mergeDestination = VisiblePosition(m_upstreamStart); 631 mergeDestination = VisiblePosition(m_upstreamStart);
632 } 632 }
633 633
634 if (mergeDestination == startOfParagraphToMove) 634 if (mergeDestination == startOfParagraphToMove)
635 return; 635 return;
636 636
637 VisiblePosition endOfParagraphToMove = endOfParagraph(startOfParagraphToMove ); 637 VisiblePosition endOfParagraphToMove = endOfParagraph(startOfParagraphToMove );
638 638
639 if (mergeDestination == endOfParagraphToMove) 639 if (mergeDestination == endOfParagraphToMove)
640 return; 640 return;
641 641
642 // The rule for merging into an empty block is: only do so if its farther to the right. 642 // The rule for merging into an empty block is: only do so if its farther to the right.
643 // FIXME: Consider RTL. 643 // FIXME: Consider RTL.
644 if (!m_startsAtEmptyLine && isStartOfParagraph(mergeDestination) && startOfP aragraphToMove.absoluteCaretBounds().x() > mergeDestination.absoluteCaretBounds( ).x()) { 644 if (!m_startsAtEmptyLine && isStartOfParagraph(mergeDestination) && startOfP aragraphToMove.absoluteCaretBounds().x() > mergeDestination.absoluteCaretBounds( ).x()) {
645 if (mergeDestination.deepEquivalent().downstream().deprecatedNode()->has TagName(brTag)) { 645 if (mergeDestination.deepEquivalent().downstream().deprecatedNode()->has TagName(brTag)) {
646 removeNodeAndPruneAncestors(mergeDestination.deepEquivalent().downst ream().deprecatedNode()); 646 removeNodeAndPruneAncestors(mergeDestination.deepEquivalent().downst ream().deprecatedNode());
647 m_endingPosition = startOfParagraphToMove.deepEquivalent(); 647 m_endingPosition = startOfParagraphToMove.deepEquivalent();
648 return; 648 return;
649 } 649 }
650 } 650 }
651 651
652 // Block images, tables and horizontal rules cannot be made inline with cont ent at mergeDestination. If there is 652 // Block images, tables and horizontal rules cannot be made inline with cont ent at mergeDestination. If there is
653 // any (!isStartOfParagraph(mergeDestination)), don't merge, just move the c aret to just before the selection we deleted. 653 // any (!isStartOfParagraph(mergeDestination)), don't merge, just move the c aret to just before the selection we deleted.
654 // See https://bugs.webkit.org/show_bug.cgi?id=25439 654 // See https://bugs.webkit.org/show_bug.cgi?id=25439
655 if (isRenderedAsNonInlineTableImageOrHR(startOfParagraphToMove.deepEquivalen t().deprecatedNode().handle().raw()) && !isStartOfParagraph(mergeDestination)) { 655 if (isRenderedAsNonInlineTableImageOrHR(startOfParagraphToMove.deepEquivalen t().deprecatedNode()) && !isStartOfParagraph(mergeDestination)) {
656 m_endingPosition = m_upstreamStart; 656 m_endingPosition = m_upstreamStart;
657 return; 657 return;
658 } 658 }
659 659
660 Handle<Range> range = Range::create(document(), startOfParagraphToMove.deepE quivalent().parentAnchoredEquivalent(), endOfParagraphToMove.deepEquivalent().pa rentAnchoredEquivalent()); 660 Handle<Range> range = Range::create(document(), startOfParagraphToMove.deepE quivalent().parentAnchoredEquivalent(), endOfParagraphToMove.deepEquivalent().pa rentAnchoredEquivalent());
661 Handle<Range> rangeToBeReplaced = Range::create(document(), mergeDestination .deepEquivalent().parentAnchoredEquivalent(), mergeDestination.deepEquivalent(). parentAnchoredEquivalent()); 661 Handle<Range> rangeToBeReplaced = Range::create(document(), mergeDestination .deepEquivalent().parentAnchoredEquivalent(), mergeDestination.deepEquivalent(). parentAnchoredEquivalent());
662 if (!document()->frame()->editor()->client()->shouldMoveRangeAfterDelete(ran ge, rangeToBeReplaced)) 662 if (!document()->frame()->editor()->client()->shouldMoveRangeAfterDelete(ran ge, rangeToBeReplaced))
663 return; 663 return;
664 664
665 // moveParagraphs will insert placeholders if it removes blocks that would r equire their use, don't let block 665 // moveParagraphs will insert placeholders if it removes blocks that would r equire their use, don't let block
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after
815 EAffinity affinity = m_selectionToDelete.affinity(); 815 EAffinity affinity = m_selectionToDelete.affinity();
816 816
817 Position downstreamEnd = m_selectionToDelete.end().downstream(); 817 Position downstreamEnd = m_selectionToDelete.end().downstream();
818 m_needPlaceholder = isStartOfParagraph(m_selectionToDelete.visibleStart(), C anCrossEditingBoundary) 818 m_needPlaceholder = isStartOfParagraph(m_selectionToDelete.visibleStart(), C anCrossEditingBoundary)
819 && isEndOfParagraph(m_selectionToDelete.visibleEnd(), CanCrossEditin gBoundary) 819 && isEndOfParagraph(m_selectionToDelete.visibleEnd(), CanCrossEditin gBoundary)
820 && !lineBreakExistsAtVisiblePosition(m_selectionToDelete.visibleEnd( )); 820 && !lineBreakExistsAtVisiblePosition(m_selectionToDelete.visibleEnd( ));
821 if (m_needPlaceholder) { 821 if (m_needPlaceholder) {
822 // Don't need a placeholder when deleting a selection that starts just b efore a table 822 // Don't need a placeholder when deleting a selection that starts just b efore a table
823 // and ends inside it (we do need placeholders to hold open empty cells, but that's 823 // and ends inside it (we do need placeholders to hold open empty cells, but that's
824 // handled elsewhere). 824 // handled elsewhere).
825 if (Handle<Node> table = adoptRawResult(isLastPositionBeforeTable(m_sele ctionToDelete.visibleStart()))) 825 if (Handle<Node> table = isLastPositionBeforeTable(m_selectionToDelete.v isibleStart()))
826 if (m_selectionToDelete.end().deprecatedNode()->isDescendantOf(table .raw())) 826 if (m_selectionToDelete.end().deprecatedNode()->isDescendantOf(table .raw()))
827 m_needPlaceholder = false; 827 m_needPlaceholder = false;
828 } 828 }
829 829
830 830
831 // set up our state 831 // set up our state
832 initializePositionData(); 832 initializePositionData();
833 833
834 // Delete any text that may hinder our ability to fixup whitespace after the delete 834 // Delete any text that may hinder our ability to fixup whitespace after the delete
835 deleteInsignificantTextDownstream(m_trailingWhitespace); 835 deleteInsignificantTextDownstream(m_trailingWhitespace);
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
885 885
886 // Normally deletion doesn't preserve the typing style that was present before i t. For example, 886 // Normally deletion doesn't preserve the typing style that was present before i t. For example,
887 // type a character, Bold, then delete the character and start typing. The Bold typing style shouldn't 887 // type a character, Bold, then delete the character and start typing. The Bold typing style shouldn't
888 // stick around. Deletion should preserve a typing style that *it* sets, howeve r. 888 // stick around. Deletion should preserve a typing style that *it* sets, howeve r.
889 bool DeleteSelectionCommand::preservesTypingStyle() const 889 bool DeleteSelectionCommand::preservesTypingStyle() const
890 { 890 {
891 return m_typingStyle; 891 return m_typingStyle;
892 } 892 }
893 893
894 } // namespace WebCore 894 } // namespace WebCore
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698