| 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 |