Changeset 96553 in webkit
- Timestamp:
- Oct 3, 2011 4:05:39 PM (13 years ago)
- Location:
- trunk/Source/WebCore
- Files:
-
- 6 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebCore/ChangeLog
r96552 r96553 1 2011-10-03 Ryosuke Niwa <rniwa@webkit.org> 2 3 Replace m_firstNodeInserted and m_lastLeafInserted in ReplaceSelectionCommand by positions 4 https://bugs.webkit.org/show_bug.cgi?id=68874 5 6 Reviewed by Enrica Casucci. 7 8 Replaced m_firstNodeInserted and m_lastLeafInserted by m_startOfInsertedContent and m_endOfInsertedContent 9 respectively. Also removed removeNodePreservingChildren and removeNodeAndPruneAncestors in ReplaceSelectionCommand 10 because they were not virtual in CompositeEditCommand and implicitly overriding the functions was confusing. 11 Since each of these two functions is used at exactly one place, just update positions and insertedNodes explicitly. 12 13 * editing/CompositeEditCommand.cpp: 14 * editing/ReplaceSelectionCommand.cpp: 15 (WebCore::ReplaceSelectionCommand::removeUnrenderedTextNodesAtEnds): 16 (WebCore::ReplaceSelectionCommand::positionAtEndOfInsertedContent): 17 (WebCore::ReplaceSelectionCommand::positionAtStartOfInsertedContent): 18 (WebCore::ReplaceSelectionCommand::handleStyleSpans): 19 (WebCore::ReplaceSelectionCommand::mergeEndIfNeeded): Update m_endOfInsertedContent by endingSelection().visibleEnd() 20 instead of m_lastLeafInserted with destination.previous() because moveParagraph could have removed leading whitespace in 21 the text node referenced by destination. This is tested by an existing layout test. 22 (WebCore::ReplaceSelectionCommand::doApply): 23 (WebCore::ReplaceSelectionCommand::addSpacesForSmartReplace): Update positions as needed. All changes are tested 24 by the existing layout tests in editing/pasteboard. 25 (WebCore::ReplaceSelectionCommand::updateNodesInserted): 26 * editing/ReplaceSelectionCommand.h: 27 * editing/htmlediting.cpp: 28 (WebCore::hasARenderedDescendant): Moved from CompositeEditCommand.cpp. 29 (WebCore::highestNodeToRemoveInPruning): Ditto. 30 * editing/htmlediting.h: 31 1 32 2011-10-03 Ryosuke Niwa <rniwa@webkit.org> 2 33 -
trunk/Source/WebCore/editing/CompositeEditCommand.cpp
r96150 r96553 248 248 } 249 249 250 static bool hasARenderedDescendant(Node* node, Node* excludedNode)251 {252 for (Node* n = node->firstChild(); n;) {253 if (n == excludedNode) {254 n = n->traverseNextSibling(node);255 continue;256 }257 if (n->renderer())258 return true;259 n = n->traverseNextNode(node);260 }261 return false;262 }263 264 static Node* highestNodeToRemoveInPruning(Node* node)265 {266 Node* previousNode = 0;267 Node* rootEditableElement = node ? node->rootEditableElement() : 0;268 for (; node; node = node->parentNode()) {269 if (RenderObject* renderer = node->renderer()) {270 if (!renderer->canHaveChildren() || hasARenderedDescendant(node, previousNode) || rootEditableElement == node)271 return previousNode;272 }273 previousNode = node;274 }275 return 0;276 }277 278 250 void CompositeEditCommand::prune(PassRefPtr<Node> node) 279 251 { -
trunk/Source/WebCore/editing/ReplaceSelectionCommand.cpp
r96410 r96553 443 443 } 444 444 445 // Wrap CompositeEditCommand::removeNodePreservingChildren() so we can update the nodes we track446 void ReplaceSelectionCommand::removeNodePreservingChildren(Node* node)447 {448 if (m_firstNodeInserted == node)449 m_firstNodeInserted = node->traverseNextNode();450 if (m_lastLeafInserted == node)451 m_lastLeafInserted = node->lastChild() ? node->lastChild() : node->traverseNextSibling();452 CompositeEditCommand::removeNodePreservingChildren(node);453 }454 455 // Wrap CompositeEditCommand::removeNodeAndPruneAncestors() so we can update the nodes we track456 void ReplaceSelectionCommand::removeNodeAndPruneAncestors(Node* node)457 {458 // prepare in case m_firstNodeInserted and/or m_lastLeafInserted get removed459 // FIXME: shouldn't m_lastLeafInserted be adjusted using traversePreviousNode()?460 Node* afterFirst = m_firstNodeInserted ? m_firstNodeInserted->traverseNextSibling() : 0;461 Node* afterLast = m_lastLeafInserted ? m_lastLeafInserted->traverseNextSibling() : 0;462 463 CompositeEditCommand::removeNodeAndPruneAncestors(node);464 465 // adjust m_firstNodeInserted and m_lastLeafInserted since either or both may have been removed466 if (m_lastLeafInserted && !m_lastLeafInserted->inDocument())467 m_lastLeafInserted = afterLast;468 if (m_firstNodeInserted && !m_firstNodeInserted->inDocument())469 m_firstNodeInserted = m_lastLeafInserted && m_lastLeafInserted->inDocument() ? afterFirst : 0;470 }471 472 445 static bool isHeaderElement(Node* a) 473 446 { … … 583 556 } 584 557 585 // We don't have to make sure that m_firstNodeInserted isn't inside a select or script element, because558 // We don't have to make sure that firstNodeInserted isn't inside a select or script element, because 586 559 // it is a top level node in the fragment and the user can't insert into those elements. 587 560 Node* firstNodeInserted = insertedNodes.firstNodeInserted(); … … 595 568 VisiblePosition ReplaceSelectionCommand::positionAtEndOfInsertedContent() const 596 569 { 597 Node* lastNode = m_lastLeafInserted.get();598 570 // FIXME: Why is this hack here? What's special about <select> tags? 599 Node* enclosingSelect = enclosingNodeWithTag(firstPositionInOrBeforeNode(lastNode), selectTag); 600 if (enclosingSelect) 601 lastNode = enclosingSelect; 602 return lastPositionInOrAfterNode(lastNode); 571 Node* enclosingSelect = enclosingNodeWithTag(m_endOfInsertedContent, selectTag); 572 return enclosingSelect ? lastPositionInOrAfterNode(enclosingSelect) : m_endOfInsertedContent; 603 573 } 604 574 605 575 VisiblePosition ReplaceSelectionCommand::positionAtStartOfInsertedContent() const 606 576 { 607 return firstPositionInOrBeforeNode(m_firstNodeInserted.get());577 return m_startOfInsertedContent; 608 578 } 609 579 … … 661 631 // We should remove styles from spans that are overridden by all of their children, either here 662 632 // or at copy time. 663 void ReplaceSelectionCommand::handleStyleSpans( Node* firstNodeInserted)633 void ReplaceSelectionCommand::handleStyleSpans(InsertedNodes& insertedNodes) 664 634 { 665 635 HTMLElement* wrappingStyleSpan = 0; … … 667 637 // the top of the fragment, but Mail sometimes adds a wrapper (for Paste As Quotation), 668 638 // so search for the top level style span instead of assuming it's at the top. 669 for (Node* node = firstNodeInserted; node; node = node->traverseNextNode()) {639 for (Node* node = insertedNodes.firstNodeInserted(); node; node = node->traverseNextNode()) { 670 640 if (isLegacyAppleStyleSpan(node)) { 671 641 wrappingStyleSpan = toHTMLElement(node); … … 698 668 style->removeBlockProperties(); 699 669 700 if (style->isEmpty() || !wrappingStyleSpan->firstChild()) 670 if (style->isEmpty() || !wrappingStyleSpan->firstChild()) { 671 insertedNodes.willRemoveNodePreservingChildren(wrappingStyleSpan); 701 672 removeNodePreservingChildren(wrappingStyleSpan); 702 else673 } else 703 674 setNodeAttribute(wrappingStyleSpan, styleAttr, style->style()->cssText()); 704 675 } … … 737 708 moveParagraph(startOfParagraphToMove, endOfParagraph(startOfParagraphToMove), destination); 738 709 739 // Merging forward will remove m_lastLeafInserted from the document. 740 // FIXME: Maintain positions for the start and end of inserted content instead of keeping nodes. The nodes are 741 // only ever used to create positions where inserted content starts/ends. Also, we sometimes insert content 742 // directly into text nodes already in the document, in which case tracking inserted nodes is inadequate. 710 // Merging forward will remove m_endOfInsertedContent from the document. 743 711 if (mergeForward) { 744 m_lastLeafInserted = destination.previous().deepEquivalent().deprecatedNode(); 745 if (!m_firstNodeInserted->inDocument()) 746 m_firstNodeInserted = endingSelection().visibleStart().deepEquivalent().deprecatedNode(); 747 // If we merged text nodes, m_lastLeafInserted could be null. If this is the case, 748 // we use m_firstNodeInserted. 749 if (!m_lastLeafInserted) 750 m_lastLeafInserted = m_firstNodeInserted; 712 if (m_startOfInsertedContent.isOrphan()) 713 m_startOfInsertedContent = endingSelection().visibleStart().deepEquivalent(); 714 m_endOfInsertedContent = endingSelection().visibleEnd().deepEquivalent(); 715 // If we merged text nodes, m_endOfInsertedContent could be null. If this is the case, we use m_startOfInsertedContent. 716 if (m_endOfInsertedContent.isNull()) 717 m_endOfInsertedContent = m_startOfInsertedContent; 751 718 } 752 719 } … … 966 933 // 5) Add spaces for smart replace. 967 934 // 6) Select the replacement if requested, and match style if requested. 968 969 VisiblePosition startOfInsertedContent, endOfInsertedContent;970 935 971 936 InsertedNodes insertedNodes; … … 1009 974 1010 975 removeUnrenderedTextNodesAtEnds(insertedNodes); 1011 1012 m_firstNodeInserted = insertedNodes.firstNodeInserted();1013 m_lastLeafInserted = insertedNodes.lastLeafInserted();1014 976 1015 977 if (!handledStyleSpans) 1016 handleStyleSpans( m_firstNodeInserted.get());978 handleStyleSpans(insertedNodes); 1017 979 1018 980 // Mutation events (bug 20161) may have already removed the inserted content 1019 if (!m_firstNodeInserted || !m_firstNodeInserted->inDocument()) 1020 return; 1021 1022 endOfInsertedContent = positionAtEndOfInsertedContent(); 1023 startOfInsertedContent = positionAtStartOfInsertedContent(); 1024 981 if (!insertedNodes.firstNodeInserted() || !insertedNodes.firstNodeInserted()->inDocument()) 982 return; 983 984 VisiblePosition startOfInsertedContent = firstPositionInOrBeforeNode(insertedNodes.firstNodeInserted()); 985 1025 986 // We inserted before the startBlock to prevent nesting, and the content before the startBlock wasn't in its own block and 1026 987 // didn't have a br after it, so the inserted content ended up in the same paragraph. … … 1028 989 insertNodeAt(createBreakElement(document()).get(), startOfInsertedContent.deepEquivalent()); 1029 990 1030 bool interchangeNewlineAtEnd = fragment.hasInterchangeNewlineAtEnd(); 1031 1032 if (endBR && (plainTextFragment || shouldRemoveEndBR(endBR, originalVisPosBeforeEndBR))) 1033 removeNodeAndPruneAncestors(endBR); 1034 991 if (endBR && (plainTextFragment || shouldRemoveEndBR(endBR, originalVisPosBeforeEndBR))) { 992 RefPtr<Node> parent = endBR->parentNode(); 993 insertedNodes.willRemoveNode(endBR); 994 removeNode(endBR); 995 if (Node* nodeToRemove = highestNodeToRemoveInPruning(parent.get())) { 996 insertedNodes.willRemoveNode(nodeToRemove); 997 removeNode(nodeToRemove); 998 } 999 } 1000 1001 // Setup m_startOfInsertedContent and m_endOfInsertedContent. This should be the last two lines of code that access insertedNodes. 1002 m_startOfInsertedContent = firstPositionInOrBeforeNode(insertedNodes.firstNodeInserted()); 1003 m_endOfInsertedContent = lastPositionInOrAfterNode(insertedNodes.lastLeafInserted()); 1004 1035 1005 // Determine whether or not we should merge the end of inserted content with what's after it before we do 1036 1006 // the start merge so that the start merge doesn't effect our decision. … … 1038 1008 1039 1009 if (shouldMergeStart(selectionStartWasStartOfParagraph, fragment.hasInterchangeNewlineAtStart(), startIsInsideMailBlockquote)) { 1040 VisiblePosition destination = startOfInsertedContent.previous();1041 VisiblePosition startOfParagraphToMove = startOfInsertedContent;1010 VisiblePosition startOfParagraphToMove = positionAtStartOfInsertedContent(); 1011 VisiblePosition destination = startOfParagraphToMove.previous(); 1042 1012 // We need to handle the case where we need to merge the end 1043 1013 // but our destination node is inside an inline that is the last in the block. … … 1065 1035 // only ever used to create positions where inserted content starts/ends. 1066 1036 moveParagraph(startOfParagraphToMove, endOfParagraph(startOfParagraphToMove), destination); 1067 m_firstNodeInserted = endingSelection().visibleStart().deepEquivalent().downstream().deprecatedNode(); 1068 if (!m_lastLeafInserted->inDocument()) 1069 m_lastLeafInserted = endingSelection().visibleEnd().deepEquivalent().upstream().deprecatedNode(); 1070 } 1071 1072 endOfInsertedContent = positionAtEndOfInsertedContent(); 1073 startOfInsertedContent = positionAtStartOfInsertedContent(); 1037 m_startOfInsertedContent = endingSelection().visibleStart().deepEquivalent().downstream(); 1038 if (m_endOfInsertedContent.isOrphan()) 1039 m_endOfInsertedContent = endingSelection().visibleEnd().deepEquivalent().upstream(); 1040 } 1074 1041 1075 1042 Position lastPositionToSelect; 1076 if (interchangeNewlineAtEnd) { 1043 if (fragment.hasInterchangeNewlineAtEnd()) { 1044 VisiblePosition endOfInsertedContent = positionAtEndOfInsertedContent(); 1077 1045 VisiblePosition next = endOfInsertedContent.next(CannotCrossEditingBoundary); 1078 1046 … … 1163 1131 if (endNode->isTextNode()) { 1164 1132 Text* text = static_cast<Text*>(endNode); 1133 // FIXME: we shouldn't always be inserting the space at the end 1165 1134 insertTextIntoNode(text, text->length(), collapseWhiteSpace ? nonBreakingSpaceString() : " "); 1135 if (m_endOfInsertedContent.containerNode() == text) 1136 m_endOfInsertedContent.moveToOffset(m_endOfInsertedContent.offsetInContainerNode() + 1); 1166 1137 } else { 1167 1138 RefPtr<Node> node = document()->createEditingTextNode(collapseWhiteSpace ? nonBreakingSpaceString() : " "); … … 1181 1152 if (startNode->isTextNode()) { 1182 1153 Text* text = static_cast<Text*>(startNode); 1154 // FIXME: we shouldn't always be inserting the space at the beginning 1183 1155 insertTextIntoNode(text, 0, collapseWhiteSpace ? nonBreakingSpaceString() : " "); 1156 if (m_endOfInsertedContent.containerNode() == text && m_endOfInsertedContent.offsetInContainerNode()) 1157 m_endOfInsertedContent.moveToOffset(m_endOfInsertedContent.offsetInContainerNode() + 1); 1184 1158 } else { 1185 1159 RefPtr<Node> node = document()->createEditingTextNode(collapseWhiteSpace ? nonBreakingSpaceString() : " "); 1186 // Don't updateNodesInserted. Doing so would set m_ lastLeafInsertedto be the node containing the leading space,1187 // but m_ lastLeafInsertedis supposed to mark the end of pasted content.1160 // Don't updateNodesInserted. Doing so would set m_endOfInsertedContent to be the node containing the leading space, 1161 // but m_endOfInsertedContent is supposed to mark the end of pasted content. 1188 1162 insertNodeBefore(node, startNode); 1189 // FIXME: Use positions to track the start/end of inserted content. 1190 m_firstNodeInserted = node; 1163 m_startOfInsertedContent = firstPositionInNode(node.get()); 1191 1164 } 1192 1165 } … … 1276 1249 return; 1277 1250 1278 if (!m_firstNodeInserted) 1279 m_firstNodeInserted = node; 1280 1281 if (node == m_lastLeafInserted) 1282 return; 1283 1284 m_lastLeafInserted = node->lastDescendant(); 1251 if (m_startOfInsertedContent.isNull()) 1252 m_startOfInsertedContent = firstPositionInOrBeforeNode(node); 1253 1254 m_endOfInsertedContent = lastPositionInOrAfterNode(node->lastDescendant()); 1285 1255 } 1286 1256 -
trunk/Source/WebCore/editing/ReplaceSelectionCommand.h
r96410 r96553 89 89 90 90 void removeRedundantStylesAndKeepStyleSpanInline(InsertedNodes&); 91 void handleStyleSpans( Node* firstNodeInserted);91 void handleStyleSpans(InsertedNodes&); 92 92 void handlePasteAsQuotationNode(); 93 94 virtual void removeNodePreservingChildren(Node*);95 virtual void removeNodeAndPruneAncestors(Node*);96 93 97 94 VisiblePosition positionAtStartOfInsertedContent() const; … … 104 101 bool performTrivialReplace(const ReplacementFragment&); 105 102 106 RefPtr<Node> m_firstNodeInserted;107 RefPtr<Node> m_lastLeafInserted;103 Position m_startOfInsertedContent; 104 Position m_endOfInsertedContent; 108 105 RefPtr<EditingStyle> m_insertionStyle; 109 106 bool m_selectReplacement; -
trunk/Source/WebCore/editing/htmlediting.cpp
r96257 r96553 622 622 623 623 return highest; 624 } 625 626 static bool hasARenderedDescendant(Node* node, Node* excludedNode) 627 { 628 for (Node* n = node->firstChild(); n;) { 629 if (n == excludedNode) { 630 n = n->traverseNextSibling(node); 631 continue; 632 } 633 if (n->renderer()) 634 return true; 635 n = n->traverseNextNode(node); 636 } 637 return false; 638 } 639 640 Node* highestNodeToRemoveInPruning(Node* node) 641 { 642 Node* previousNode = 0; 643 Node* rootEditableElement = node ? node->rootEditableElement() : 0; 644 for (; node; node = node->parentNode()) { 645 if (RenderObject* renderer = node->renderer()) { 646 if (!renderer->canHaveChildren() || hasARenderedDescendant(node, previousNode) || rootEditableElement == node) 647 return previousNode; 648 } 649 previousNode = node; 650 } 651 return 0; 624 652 } 625 653 -
trunk/Source/WebCore/editing/htmlediting.h
r96084 r96553 58 58 Node* highestEnclosingNodeOfType(const Position&, bool (*nodeIsOfType)(const Node*), 59 59 EditingBoundaryCrossingRule = CannotCrossEditingBoundary, Node* stayWithin = 0); 60 Node* lowestEditableAncestor(Node*); 60 Node* highestNodeToRemoveInPruning(Node*); 61 Node* lowestEditableAncestor(Node*); 61 62 62 63 Node* enclosingBlock(Node*, EditingBoundaryCrossingRule = CannotCrossEditingBoundary);
Note: See TracChangeset
for help on using the changeset viewer.