OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. | 2 * Copyright (C) 2005 Apple Computer, Inc. All rights reserved. |
3 * | 3 * |
4 * Redistribution and use in source and binary forms, with or without | 4 * Redistribution and use in source and binary forms, with or without |
5 * modification, are permitted provided that the following conditions | 5 * modification, are permitted provided that the following conditions |
6 * are met: | 6 * are met: |
7 * 1. Redistributions of source code must retain the above copyright | 7 * 1. Redistributions of source code must retain the above copyright |
8 * notice, this list of conditions and the following disclaimer. | 8 * notice, this list of conditions and the following disclaimer. |
9 * 2. Redistributions in binary form must reproduce the above copyright | 9 * 2. Redistributions in binary form must reproduce the above copyright |
10 * notice, this list of conditions and the following disclaimer in the | 10 * notice, this list of conditions and the following disclaimer in the |
(...skipping 25 matching lines...) Expand all Loading... |
36 #include "core/editing/VisibleUnits.h" | 36 #include "core/editing/VisibleUnits.h" |
37 #include "core/editing/htmlediting.h" | 37 #include "core/editing/htmlediting.h" |
38 #include "core/frame/LocalFrame.h" | 38 #include "core/frame/LocalFrame.h" |
39 #include "core/html/HTMLInputElement.h" | 39 #include "core/html/HTMLInputElement.h" |
40 #include "core/rendering/RenderTableCell.h" | 40 #include "core/rendering/RenderTableCell.h" |
41 | 41 |
42 namespace WebCore { | 42 namespace WebCore { |
43 | 43 |
44 using namespace HTMLNames; | 44 using namespace HTMLNames; |
45 | 45 |
46 static bool isTableRow(const Node* node) | |
47 { | |
48 return node && node->hasTagName(trTag); | |
49 } | |
50 | |
51 static bool isTableCellEmpty(Node* cell) | 46 static bool isTableCellEmpty(Node* cell) |
52 { | 47 { |
53 ASSERT(isTableCell(cell)); | 48 ASSERT(isTableCell(cell)); |
54 return VisiblePosition(firstPositionInNode(cell)) == VisiblePosition(lastPos
itionInNode(cell)); | 49 return VisiblePosition(firstPositionInNode(cell)) == VisiblePosition(lastPos
itionInNode(cell)); |
55 } | 50 } |
56 | 51 |
57 static bool isTableRowEmpty(Node* row) | 52 static bool isTableRowEmpty(Node* row) |
58 { | 53 { |
59 if (!isTableRow(row)) | 54 if (!isHTMLTableRowElement(row)) |
60 return false; | 55 return false; |
61 | 56 |
62 for (Node* child = row->firstChild(); child; child = child->nextSibling()) | 57 for (Node* child = row->firstChild(); child; child = child->nextSibling()) |
63 if (isTableCell(child) && !isTableCellEmpty(child)) | 58 if (isTableCell(child) && !isTableCellEmpty(child)) |
64 return false; | 59 return false; |
65 | 60 |
66 return true; | 61 return true; |
67 } | 62 } |
68 | 63 |
69 DeleteSelectionCommand::DeleteSelectionCommand(Document& document, bool smartDel
ete, bool mergeBlocksAfterDelete, bool expandForSpecialElements, bool sanitizeMa
rkup) | 64 DeleteSelectionCommand::DeleteSelectionCommand(Document& document, bool smartDel
ete, bool mergeBlocksAfterDelete, bool expandForSpecialElements, bool sanitizeMa
rkup) |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
104 void DeleteSelectionCommand::initializeStartEnd(Position& start, Position& end) | 99 void DeleteSelectionCommand::initializeStartEnd(Position& start, Position& end) |
105 { | 100 { |
106 Node* startSpecialContainer = 0; | 101 Node* startSpecialContainer = 0; |
107 Node* endSpecialContainer = 0; | 102 Node* endSpecialContainer = 0; |
108 | 103 |
109 start = m_selectionToDelete.start(); | 104 start = m_selectionToDelete.start(); |
110 end = m_selectionToDelete.end(); | 105 end = m_selectionToDelete.end(); |
111 | 106 |
112 // 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, | 107 // 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, |
113 // but in these cases, we want to delete it, so manually expand the selectio
n | 108 // but in these cases, we want to delete it, so manually expand the selectio
n |
114 if (start.deprecatedNode()->hasTagName(hrTag)) | 109 if (isHTMLHRElement(*start.deprecatedNode())) |
115 start = positionBeforeNode(start.deprecatedNode()); | 110 start = positionBeforeNode(start.deprecatedNode()); |
116 else if (end.deprecatedNode()->hasTagName(hrTag)) | 111 else if (isHTMLHRElement(*end.deprecatedNode())) |
117 end = positionAfterNode(end.deprecatedNode()); | 112 end = positionAfterNode(end.deprecatedNode()); |
118 | 113 |
119 // FIXME: This is only used so that moveParagraphs can avoid the bugs in spe
cial element expansion. | 114 // FIXME: This is only used so that moveParagraphs can avoid the bugs in spe
cial element expansion. |
120 if (!m_expandForSpecialElements) | 115 if (!m_expandForSpecialElements) |
121 return; | 116 return; |
122 | 117 |
123 while (1) { | 118 while (1) { |
124 startSpecialContainer = 0; | 119 startSpecialContainer = 0; |
125 endSpecialContainer = 0; | 120 endSpecialContainer = 0; |
126 | 121 |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
170 initializeStartEnd(start, end); | 165 initializeStartEnd(start, end); |
171 | 166 |
172 m_upstreamStart = start.upstream(); | 167 m_upstreamStart = start.upstream(); |
173 m_downstreamStart = start.downstream(); | 168 m_downstreamStart = start.downstream(); |
174 m_upstreamEnd = end.upstream(); | 169 m_upstreamEnd = end.upstream(); |
175 m_downstreamEnd = end.downstream(); | 170 m_downstreamEnd = end.downstream(); |
176 | 171 |
177 m_startRoot = editableRootForPosition(start); | 172 m_startRoot = editableRootForPosition(start); |
178 m_endRoot = editableRootForPosition(end); | 173 m_endRoot = editableRootForPosition(end); |
179 | 174 |
180 m_startTableRow = enclosingNodeOfType(start, &isTableRow); | 175 m_startTableRow = enclosingNodeOfType(start, &isHTMLTableRowElement); |
181 m_endTableRow = enclosingNodeOfType(end, &isTableRow); | 176 m_endTableRow = enclosingNodeOfType(end, &isHTMLTableRowElement); |
182 | 177 |
183 // Don't move content out of a table cell. | 178 // Don't move content out of a table cell. |
184 // If the cell is non-editable, enclosingNodeOfType won't return it by defau
lt, so | 179 // If the cell is non-editable, enclosingNodeOfType won't return it by defau
lt, so |
185 // tell that function that we don't care if it returns non-editable nodes. | 180 // tell that function that we don't care if it returns non-editable nodes. |
186 Node* startCell = enclosingNodeOfType(m_upstreamStart, &isTableCell, CanCros
sEditingBoundary); | 181 Node* startCell = enclosingNodeOfType(m_upstreamStart, &isTableCell, CanCros
sEditingBoundary); |
187 Node* endCell = enclosingNodeOfType(m_downstreamEnd, &isTableCell, CanCrossE
ditingBoundary); | 182 Node* endCell = enclosingNodeOfType(m_downstreamEnd, &isTableCell, CanCrossE
ditingBoundary); |
188 // FIXME: This isn't right. A borderless table with two rows and a single c
olumn would appear as two paragraphs. | 183 // FIXME: This isn't right. A borderless table with two rows and a single c
olumn would appear as two paragraphs. |
189 if (endCell && endCell != startCell) | 184 if (endCell && endCell != startCell) |
190 m_mergeBlocksAfterDelete = false; | 185 m_mergeBlocksAfterDelete = false; |
191 | 186 |
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
298 { | 293 { |
299 Node* nodeAfterUpstreamStart = m_upstreamStart.computeNodeAfterPosition(); | 294 Node* nodeAfterUpstreamStart = m_upstreamStart.computeNodeAfterPosition(); |
300 Node* nodeAfterDownstreamStart = m_downstreamStart.computeNodeAfterPosition(
); | 295 Node* nodeAfterDownstreamStart = m_downstreamStart.computeNodeAfterPosition(
); |
301 // Upstream end will appear before BR due to canonicalization | 296 // Upstream end will appear before BR due to canonicalization |
302 Node* nodeAfterUpstreamEnd = m_upstreamEnd.computeNodeAfterPosition(); | 297 Node* nodeAfterUpstreamEnd = m_upstreamEnd.computeNodeAfterPosition(); |
303 | 298 |
304 if (!nodeAfterUpstreamStart || !nodeAfterDownstreamStart) | 299 if (!nodeAfterUpstreamStart || !nodeAfterDownstreamStart) |
305 return false; | 300 return false; |
306 | 301 |
307 // Check for special-case where the selection contains only a BR on a line b
y itself after another BR. | 302 // Check for special-case where the selection contains only a BR on a line b
y itself after another BR. |
308 bool upstreamStartIsBR = nodeAfterUpstreamStart->hasTagName(brTag); | 303 bool upstreamStartIsBR = isHTMLBRElement(*nodeAfterUpstreamStart); |
309 bool downstreamStartIsBR = nodeAfterDownstreamStart->hasTagName(brTag); | 304 bool downstreamStartIsBR = isHTMLBRElement(*nodeAfterDownstreamStart); |
310 bool isBROnLineByItself = upstreamStartIsBR && downstreamStartIsBR && nodeAf
terDownstreamStart == nodeAfterUpstreamEnd; | 305 bool isBROnLineByItself = upstreamStartIsBR && downstreamStartIsBR && nodeAf
terDownstreamStart == nodeAfterUpstreamEnd; |
311 if (isBROnLineByItself) { | 306 if (isBROnLineByItself) { |
312 removeNode(nodeAfterDownstreamStart); | 307 removeNode(nodeAfterDownstreamStart); |
313 return true; | 308 return true; |
314 } | 309 } |
315 | 310 |
316 // FIXME: This code doesn't belong in here. | 311 // FIXME: This code doesn't belong in here. |
317 // We detect the case where the start is an empty line consisting of BR not
wrapped in a block element. | 312 // We detect the case where the start is an empty line consisting of BR not
wrapped in a block element. |
318 if (upstreamStartIsBR && downstreamStartIsBR && !(isStartOfBlock(VisiblePosi
tion(positionBeforeNode(nodeAfterUpstreamStart))) && isEndOfBlock(VisiblePositio
n(positionAfterNode(nodeAfterUpstreamStart))))) { | 313 if (upstreamStartIsBR && downstreamStartIsBR && !(isStartOfBlock(VisiblePosi
tion(positionBeforeNode(nodeAfterUpstreamStart))) && isEndOfBlock(VisiblePositio
n(positionAfterNode(nodeAfterUpstreamStart))))) { |
319 m_startsAtEmptyLine = true; | 314 m_startsAtEmptyLine = true; |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
414 | 409 |
415 CompositeEditCommand::deleteTextFromNode(node, offset, count); | 410 CompositeEditCommand::deleteTextFromNode(node, offset, count); |
416 } | 411 } |
417 | 412 |
418 void DeleteSelectionCommand::makeStylingElementsDirectChildrenOfEditableRootToPr
eventStyleLoss() | 413 void DeleteSelectionCommand::makeStylingElementsDirectChildrenOfEditableRootToPr
eventStyleLoss() |
419 { | 414 { |
420 RefPtr<Range> range = m_selectionToDelete.toNormalizedRange(); | 415 RefPtr<Range> range = m_selectionToDelete.toNormalizedRange(); |
421 RefPtr<Node> node = range->firstNode(); | 416 RefPtr<Node> node = range->firstNode(); |
422 while (node && node != range->pastLastNode()) { | 417 while (node && node != range->pastLastNode()) { |
423 RefPtr<Node> nextNode = NodeTraversal::next(*node); | 418 RefPtr<Node> nextNode = NodeTraversal::next(*node); |
424 if ((node->hasTagName(styleTag) && !(toElement(node)->hasAttribute(scope
dAttr))) || node->hasTagName(linkTag)) { | 419 if ((isHTMLStyleElement(*node) && !(toElement(node)->hasAttribute(scoped
Attr))) || isHTMLLinkElement(*node)) { |
425 nextNode = NodeTraversal::nextSkippingChildren(*node); | 420 nextNode = NodeTraversal::nextSkippingChildren(*node); |
426 RefPtr<ContainerNode> rootEditableElement = node->rootEditableElemen
t(); | 421 RefPtr<ContainerNode> rootEditableElement = node->rootEditableElemen
t(); |
427 if (rootEditableElement.get()) { | 422 if (rootEditableElement.get()) { |
428 removeNode(node); | 423 removeNode(node); |
429 appendNode(node, rootEditableElement); | 424 appendNode(node, rootEditableElement); |
430 } | 425 } |
431 } | 426 } |
432 node = nextNode; | 427 node = nextNode; |
433 } | 428 } |
434 } | 429 } |
435 | 430 |
436 void DeleteSelectionCommand::handleGeneralDelete() | 431 void DeleteSelectionCommand::handleGeneralDelete() |
437 { | 432 { |
438 if (m_upstreamStart.isNull()) | 433 if (m_upstreamStart.isNull()) |
439 return; | 434 return; |
440 | 435 |
441 int startOffset = m_upstreamStart.deprecatedEditingOffset(); | 436 int startOffset = m_upstreamStart.deprecatedEditingOffset(); |
442 Node* startNode = m_upstreamStart.deprecatedNode(); | 437 Node* startNode = m_upstreamStart.deprecatedNode(); |
443 ASSERT(startNode); | 438 ASSERT(startNode); |
444 | 439 |
445 makeStylingElementsDirectChildrenOfEditableRootToPreventStyleLoss(); | 440 makeStylingElementsDirectChildrenOfEditableRootToPreventStyleLoss(); |
446 | 441 |
447 // Never remove the start block unless it's a table, in which case we won't
merge content in. | 442 // Never remove the start block unless it's a table, in which case we won't
merge content in. |
448 if (startNode->isSameNode(m_startBlock.get()) && !startOffset && canHaveChil
drenForEditing(startNode) && !startNode->hasTagName(tableTag)) { | 443 if (startNode->isSameNode(m_startBlock.get()) && !startOffset && canHaveChil
drenForEditing(startNode) && !isHTMLTableElement(*startNode)) { |
449 startOffset = 0; | 444 startOffset = 0; |
450 startNode = NodeTraversal::next(*startNode); | 445 startNode = NodeTraversal::next(*startNode); |
451 if (!startNode) | 446 if (!startNode) |
452 return; | 447 return; |
453 } | 448 } |
454 | 449 |
455 if (startOffset >= caretMaxOffset(startNode) && startNode->isTextNode()) { | 450 if (startOffset >= caretMaxOffset(startNode) && startNode->isTextNode()) { |
456 Text* text = toText(startNode); | 451 Text* text = toText(startNode); |
457 if (text->length() > (unsigned)caretMaxOffset(startNode)) | 452 if (text->length() > (unsigned)caretMaxOffset(startNode)) |
458 deleteTextFromNode(text, caretMaxOffset(startNode), text->length() -
caretMaxOffset(startNode)); | 453 deleteTextFromNode(text, caretMaxOffset(startNode), text->length() -
caretMaxOffset(startNode)); |
(...skipping 177 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
636 if (listItemInFirstParagraph && listItemInSecondParagraph | 631 if (listItemInFirstParagraph && listItemInSecondParagraph |
637 && canMergeLists(listItemInFirstParagraph->parentElement(), listItemInSe
condParagraph->parentElement())) { | 632 && canMergeLists(listItemInFirstParagraph->parentElement(), listItemInSe
condParagraph->parentElement())) { |
638 mergeIdenticalElements(listItemInFirstParagraph->parentElement(), listIt
emInSecondParagraph->parentElement()); | 633 mergeIdenticalElements(listItemInFirstParagraph->parentElement(), listIt
emInSecondParagraph->parentElement()); |
639 m_endingPosition = mergeDestination.deepEquivalent(); | 634 m_endingPosition = mergeDestination.deepEquivalent(); |
640 return; | 635 return; |
641 } | 636 } |
642 | 637 |
643 // The rule for merging into an empty block is: only do so if its farther to
the right. | 638 // The rule for merging into an empty block is: only do so if its farther to
the right. |
644 // FIXME: Consider RTL. | 639 // FIXME: Consider RTL. |
645 if (!m_startsAtEmptyLine && isStartOfParagraph(mergeDestination) && startOfP
aragraphToMove.absoluteCaretBounds().x() > mergeDestination.absoluteCaretBounds(
).x()) { | 640 if (!m_startsAtEmptyLine && isStartOfParagraph(mergeDestination) && startOfP
aragraphToMove.absoluteCaretBounds().x() > mergeDestination.absoluteCaretBounds(
).x()) { |
646 if (mergeDestination.deepEquivalent().downstream().deprecatedNode()->has
TagName(brTag)) { | 641 if (isHTMLBRElement(*mergeDestination.deepEquivalent().downstream().depr
ecatedNode())) { |
647 removeNodeAndPruneAncestors(mergeDestination.deepEquivalent().downst
ream().deprecatedNode()); | 642 removeNodeAndPruneAncestors(mergeDestination.deepEquivalent().downst
ream().deprecatedNode()); |
648 m_endingPosition = startOfParagraphToMove.deepEquivalent(); | 643 m_endingPosition = startOfParagraphToMove.deepEquivalent(); |
649 return; | 644 return; |
650 } | 645 } |
651 } | 646 } |
652 | 647 |
653 // Block images, tables and horizontal rules cannot be made inline with cont
ent at mergeDestination. If there is | 648 // Block images, tables and horizontal rules cannot be made inline with cont
ent at mergeDestination. If there is |
654 // any (!isStartOfParagraph(mergeDestination)), don't merge, just move the c
aret to just before the selection we deleted. | 649 // any (!isStartOfParagraph(mergeDestination)), don't merge, just move the c
aret to just before the selection we deleted. |
655 // See https://bugs.webkit.org/show_bug.cgi?id=25439 | 650 // See https://bugs.webkit.org/show_bug.cgi?id=25439 |
656 if (isRenderedAsNonInlineTableImageOrHR(startOfParagraphToMove.deepEquivalen
t().deprecatedNode()) && !isStartOfParagraph(mergeDestination)) { | 651 if (isRenderedAsNonInlineTableImageOrHR(startOfParagraphToMove.deepEquivalen
t().deprecatedNode()) && !isStartOfParagraph(mergeDestination)) { |
(...skipping 199 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
856 | 851 |
857 // Normally deletion doesn't preserve the typing style that was present before i
t. For example, | 852 // Normally deletion doesn't preserve the typing style that was present before i
t. For example, |
858 // type a character, Bold, then delete the character and start typing. The Bold
typing style shouldn't | 853 // type a character, Bold, then delete the character and start typing. The Bold
typing style shouldn't |
859 // stick around. Deletion should preserve a typing style that *it* sets, howeve
r. | 854 // stick around. Deletion should preserve a typing style that *it* sets, howeve
r. |
860 bool DeleteSelectionCommand::preservesTypingStyle() const | 855 bool DeleteSelectionCommand::preservesTypingStyle() const |
861 { | 856 { |
862 return m_typingStyle; | 857 return m_typingStyle; |
863 } | 858 } |
864 | 859 |
865 } // namespace WebCore | 860 } // namespace WebCore |
OLD | NEW |