Changeset 96353 in webkit
- Timestamp:
- Sep 29, 2011 1:31:41 PM (13 years ago)
- Location:
- trunk/Source/WebCore
- Files:
-
- 5 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebCore/ChangeLog
r96352 r96353 1 2011-09-29 Ryosuke Niwa <rniwa@webkit.org> 2 3 Remove direct reads to m_firstNodeInserted and m_lastLeafInserted in ReplaceSelectionCommand 4 https://bugs.webkit.org/show_bug.cgi?id=69023 5 6 Reviewed by Enrica Casucci. 7 8 Converted to removeUnrenderedTextNodesAtEnds to use InsertedNodes instead of m_firstNodeInserted 9 and m_lastLeafInserted. Extracted the logic to update nodes as InsertedNodes::willRemoveNode 10 (old one was renamed to InsertedNodes::willRemoveNodePreservingChildren). 11 12 Also extracted shouldPerformSmartReplace and addSpacesForSmartReplace out of doApply, 13 and rewrote the logic to obtain endNode and startNode using startOfInsertedContent and 14 endOfInsertedContent instead of m_firstNodeInserted and m_lastLeafInserted. 15 16 Finally, replaced the nullity checks of m_firstNodeInserted and m_lastLeafInserted in 17 completeHTMLReplacement by nullity checks of start and end positions. 18 19 * dom/Node.cpp: 20 (WebCore::Node::traversePreviousSibling): Added. 21 * dom/Node.h: 22 * editing/ReplaceSelectionCommand.cpp: 23 (WebCore::ReplaceSelectionCommand::InsertedNodes::willRemoveNodePreservingChildren): Renamed from 24 willRemoveNode. 25 (WebCore::ReplaceSelectionCommand::InsertedNodes::willRemoveNode): Extracted from 26 removeUnrenderedTextNodesAtEnds. 27 (WebCore::ReplaceSelectionCommand::removeRedundantStylesAndKeepStyleSpanInline): 28 (WebCore::ReplaceSelectionCommand::removeUnrenderedTextNodesAtEnds): 29 (WebCore::ReplaceSelectionCommand::positionAtEndOfInsertedContent): Made const. 30 (WebCore::ReplaceSelectionCommand::positionAtStartOfInsertedContent): Made const. 31 (WebCore::ReplaceSelectionCommand::handleStyleSpans): Takes firstNodeInserted instead of directly 32 accessing m_firstNodeInserted. 33 (WebCore::ReplaceSelectionCommand::doApply): 34 (WebCore::ReplaceSelectionCommand::shouldPerformSmartReplace): 35 (WebCore::ReplaceSelectionCommand::addSpacesForSmartReplace): 36 (WebCore::ReplaceSelectionCommand::completeHTMLReplacement): 37 * editing/ReplaceSelectionCommand.h: 38 1 39 2011-09-29 Andreas Kling <kling@webkit.org> 2 40 -
trunk/Source/WebCore/dom/Node.cpp
r95372 r96353 1194 1194 } 1195 1195 1196 Node* Node::traversePreviousSibling(const Node* stayWithin) const 1197 { 1198 if (this == stayWithin) 1199 return 0; 1200 if (previousSibling()) 1201 return previousSibling(); 1202 const Node *n = this; 1203 while (n && !n->previousSibling() && (!stayWithin || n->parentNode() != stayWithin)) 1204 n = n->parentNode(); 1205 if (n) 1206 return n->previousSibling(); 1207 return 0; 1208 } 1209 1196 1210 Node* Node::traversePreviousNodePostOrder(const Node* stayWithin) const 1197 1211 { -
trunk/Source/WebCore/dom/Node.h
r94659 r96353 400 400 Node* traversePreviousNode(const Node* stayWithin = 0) const; 401 401 402 // Like traversePreviousNode, but skips children and starts with the next sibling. 403 Node* traversePreviousSibling(const Node* stayWithin = 0) const; 404 402 405 // Like traverseNextNode, but visits parents after their children. 403 406 Node* traverseNextNodePostOrder() const; -
trunk/Source/WebCore/editing/ReplaceSelectionCommand.cpp
r96187 r96353 360 360 } 361 361 362 inline void ReplaceSelectionCommand::InsertedNodes::willRemoveNode (Node* node)362 inline void ReplaceSelectionCommand::InsertedNodes::willRemoveNodePreservingChildren(Node* node) 363 363 { 364 364 if (m_firstNodeInserted == node) … … 366 366 if (m_lastNodeInserted == node) 367 367 m_lastNodeInserted = node->lastChild() ? node->lastChild() : node->traverseNextSibling(); 368 } 369 370 inline void ReplaceSelectionCommand::InsertedNodes::willRemoveNode(Node* node) 371 { 372 if (m_firstNodeInserted == node && m_lastNodeInserted == node) { 373 m_firstNodeInserted = 0; 374 m_lastNodeInserted = 0; 375 } else if (m_firstNodeInserted == node) 376 m_firstNodeInserted = m_firstNodeInserted->traverseNextSibling(); 377 else if (m_lastNodeInserted == node) 378 m_lastNodeInserted = m_lastNodeInserted->traversePreviousSibling(); 368 379 } 369 380 … … 526 537 if (!inlineStyle || newInlineStyle->isEmpty()) { 527 538 if (isStyleSpanOrSpanWithOnlyStyleAttribute(element)) { 528 insertedNodes.willRemoveNode (element);539 insertedNodes.willRemoveNodePreservingChildren(element); 529 540 removeNodePreservingChildren(element); 530 541 } else … … 537 548 if (isLegacyAppleStyleSpan(element)) { 538 549 if (!element->firstChild()) { 539 insertedNodes.willRemoveNode (element);550 insertedNodes.willRemoveNodePreservingChildren(element); 540 551 removeNodePreservingChildren(element); 541 552 continue; … … 560 571 } 561 572 562 void ReplaceSelectionCommand::removeUnrenderedTextNodesAtEnds( )573 void ReplaceSelectionCommand::removeUnrenderedTextNodesAtEnds(InsertedNodes& insertedNodes) 563 574 { 564 575 document()->updateLayoutIgnorePendingStylesheets(); 565 if (m_lastLeafInserted->isTextNode() && !nodeHasVisibleRenderText(static_cast<Text*>(m_lastLeafInserted.get())) 566 && !enclosingNodeWithTag(firstPositionInOrBeforeNode(m_lastLeafInserted.get()), selectTag) 567 && !enclosingNodeWithTag(firstPositionInOrBeforeNode(m_lastLeafInserted.get()), scriptTag)) { 568 if (m_firstNodeInserted == m_lastLeafInserted) { 569 removeNode(m_lastLeafInserted.get()); 570 m_lastLeafInserted = 0; 571 m_firstNodeInserted = 0; 572 return; 573 } 574 RefPtr<Node> previous = m_lastLeafInserted->traversePreviousNode(); 575 removeNode(m_lastLeafInserted.get()); 576 m_lastLeafInserted = previous; 577 } 578 576 577 Node* lastLeafInserted = insertedNodes.lastLeafInserted(); 578 if (lastLeafInserted && lastLeafInserted->isTextNode() && !nodeHasVisibleRenderText(static_cast<Text*>(lastLeafInserted)) 579 && !enclosingNodeWithTag(firstPositionInOrBeforeNode(lastLeafInserted), selectTag) 580 && !enclosingNodeWithTag(firstPositionInOrBeforeNode(lastLeafInserted), scriptTag)) { 581 insertedNodes.willRemoveNode(lastLeafInserted); 582 removeNode(lastLeafInserted); 583 } 584 579 585 // We don't have to make sure that m_firstNodeInserted isn't inside a select or script element, because 580 586 // it is a top level node in the fragment and the user can't insert into those elements. 581 if (m_firstNodeInserted->isTextNode() && !nodeHasVisibleRenderText(static_cast<Text*>(m_firstNodeInserted.get()))) { 582 if (m_firstNodeInserted == m_lastLeafInserted) { 583 removeNode(m_firstNodeInserted.get()); 584 m_firstNodeInserted = 0; 585 m_lastLeafInserted = 0; 586 return; 587 } 588 RefPtr<Node> next = m_firstNodeInserted->traverseNextSibling(); 589 removeNode(m_firstNodeInserted.get()); 590 m_firstNodeInserted = next; 591 } 592 } 593 594 void ReplaceSelectionCommand::handlePasteAsQuotationNode() 595 { 596 Node* node = m_firstNodeInserted.get(); 597 if (isMailPasteAsQuotationNode(node)) 598 removeNodeAttribute(static_cast<Element*>(node), classAttr); 599 } 600 601 VisiblePosition ReplaceSelectionCommand::positionAtEndOfInsertedContent() 587 Node* firstNodeInserted = insertedNodes.firstNodeInserted(); 588 lastLeafInserted = insertedNodes.lastLeafInserted(); 589 if (firstNodeInserted && firstNodeInserted->isTextNode() && !nodeHasVisibleRenderText(static_cast<Text*>(firstNodeInserted))) { 590 insertedNodes.willRemoveNode(firstNodeInserted); 591 removeNode(firstNodeInserted); 592 } 593 } 594 595 VisiblePosition ReplaceSelectionCommand::positionAtEndOfInsertedContent() const 602 596 { 603 597 Node* lastNode = m_lastLeafInserted.get(); … … 609 603 } 610 604 611 VisiblePosition ReplaceSelectionCommand::positionAtStartOfInsertedContent() 605 VisiblePosition ReplaceSelectionCommand::positionAtStartOfInsertedContent() const 612 606 { 613 607 return firstPositionInOrBeforeNode(m_firstNodeInserted.get()); … … 667 661 // We should remove styles from spans that are overridden by all of their children, either here 668 662 // or at copy time. 669 void ReplaceSelectionCommand::handleStyleSpans( )663 void ReplaceSelectionCommand::handleStyleSpans(Node* firstNodeInserted) 670 664 { 671 665 HTMLElement* wrappingStyleSpan = 0; … … 673 667 // the top of the fragment, but Mail sometimes adds a wrapper (for Paste As Quotation), 674 668 // so search for the top level style span instead of assuming it's at the top. 675 for (Node* node = m_firstNodeInserted.get(); node; node = node->traverseNextNode()) {669 for (Node* node = firstNodeInserted; node; node = node->traverseNextNode()) { 676 670 if (isLegacyAppleStyleSpan(node)) { 677 671 wrappingStyleSpan = toHTMLElement(node); … … 836 830 if (!selection.isNonOrphanedCaretOrRange() || !selection.start().deprecatedNode()) 837 831 return; 838 839 bool selectionIsPlainText = !selection.isContentRichlyEditable(); 840 841 Element* currentRoot = selection.rootEditableElement(); 832 842 833 ReplacementFragment fragment(document(), m_documentFragment.get(), m_matchStyle, selection); 843 844 834 if (performTrivialReplace(fragment)) 845 835 return; … … 865 855 Position insertionPos = selection.start(); 866 856 bool startIsInsideMailBlockquote = enclosingNodeOfType(insertionPos, isMailBlockquote, CanCrossEditingBoundary); 857 bool selectionIsPlainText = !selection.isContentRichlyEditable(); 858 Element* currentRoot = selection.rootEditableElement(); 867 859 868 860 if ((selectionStartWasStartOfParagraph && selectionEndWasEndOfParagraph && !startIsInsideMailBlockquote) || … … 1051 1043 removeRedundantStylesAndKeepStyleSpanInline(insertedNodes); 1052 1044 1045 removeUnrenderedTextNodesAtEnds(insertedNodes); 1046 1053 1047 m_firstNodeInserted = insertedNodes.firstNodeInserted(); 1054 1048 m_lastLeafInserted = insertedNodes.lastLeafInserted(); 1055 1049 1056 removeUnrenderedTextNodesAtEnds();1057 1058 1050 if (!handledStyleSpans) 1059 handleStyleSpans( );1060 1051 handleStyleSpans(m_firstNodeInserted.get()); 1052 1061 1053 // Mutation events (bug 20161) may have already removed the inserted content 1062 1054 if (!m_firstNodeInserted || !m_firstNodeInserted->inDocument()) … … 1070 1062 if (startBlock && insertionPos.deprecatedNode() == startBlock->parentNode() && (unsigned)insertionPos.deprecatedEditingOffset() < startBlock->nodeIndex() && !isStartOfParagraph(startOfInsertedContent)) 1071 1063 insertNodeAt(createBreakElement(document()).get(), startOfInsertedContent.deepEquivalent()); 1072 1073 Position lastPositionToSelect; 1074 1064 1075 1065 bool interchangeNewlineAtEnd = fragment.hasInterchangeNewlineAtEnd(); 1076 1066 … … 1117 1107 endOfInsertedContent = positionAtEndOfInsertedContent(); 1118 1108 startOfInsertedContent = positionAtStartOfInsertedContent(); 1119 1109 1110 Position lastPositionToSelect; 1120 1111 if (interchangeNewlineAtEnd) { 1121 1112 VisiblePosition next = endOfInsertedContent.next(CannotCrossEditingBoundary); … … 1145 1136 } else 1146 1137 mergeEndIfNeeded(); 1147 1148 handlePasteAsQuotationNode(); 1149 1150 endOfInsertedContent = positionAtEndOfInsertedContent(); 1151 startOfInsertedContent = positionAtStartOfInsertedContent(); 1152 1153 // Add spaces for smart replace. 1154 if (m_smartReplace && currentRoot) { 1155 Element* textControl = enclosingTextFormControl(firstPositionInNode(currentRoot)); 1156 if (textControl && textControl->hasTagName(inputTag) && static_cast<HTMLInputElement*>(textControl)->isPasswordField()) 1157 m_smartReplace = false; // Disable smart replace for password fields. 1158 } 1159 if (m_smartReplace) { 1160 bool needsTrailingSpace = !isEndOfParagraph(endOfInsertedContent) && 1161 !isCharacterSmartReplaceExempt(endOfInsertedContent.characterAfter(), false); 1162 if (needsTrailingSpace) { 1163 RenderObject* renderer = m_lastLeafInserted->renderer(); 1164 bool collapseWhiteSpace = !renderer || renderer->style()->collapseWhiteSpace(); 1165 Node* endNode = positionAtEndOfInsertedContent().deepEquivalent().upstream().deprecatedNode(); 1166 if (endNode->isTextNode()) { 1167 Text* text = static_cast<Text*>(endNode); 1168 insertTextIntoNode(text, text->length(), collapseWhiteSpace ? nonBreakingSpaceString() : " "); 1169 } else { 1170 RefPtr<Node> node = document()->createEditingTextNode(collapseWhiteSpace ? nonBreakingSpaceString() : " "); 1171 insertNodeAfter(node, endNode); 1172 updateNodesInserted(node.get()); 1173 } 1174 } 1175 1176 bool needsLeadingSpace = !isStartOfParagraph(startOfInsertedContent) && 1177 !isCharacterSmartReplaceExempt(startOfInsertedContent.previous().characterAfter(), true); 1178 if (needsLeadingSpace) { 1179 RenderObject* renderer = m_lastLeafInserted->renderer(); 1180 bool collapseWhiteSpace = !renderer || renderer->style()->collapseWhiteSpace(); 1181 Node* startNode = positionAtStartOfInsertedContent().deepEquivalent().downstream().deprecatedNode(); 1182 if (startNode->isTextNode()) { 1183 Text* text = static_cast<Text*>(startNode); 1184 insertTextIntoNode(text, 0, collapseWhiteSpace ? nonBreakingSpaceString() : " "); 1185 } else { 1186 RefPtr<Node> node = document()->createEditingTextNode(collapseWhiteSpace ? nonBreakingSpaceString() : " "); 1187 // Don't updateNodesInserted. Doing so would set m_lastLeafInserted to be the node containing the 1188 // leading space, but m_lastLeafInserted is supposed to mark the end of pasted content. 1189 insertNodeBefore(node, startNode); 1190 // FIXME: Use positions to track the start/end of inserted content. 1191 m_firstNodeInserted = node; 1192 } 1193 } 1194 } 1195 1138 1139 if (Node* mailBlockquote = enclosingNodeOfType(positionAtStartOfInsertedContent().deepEquivalent(), isMailPasteAsQuotationNode)) 1140 removeNodeAttribute(static_cast<Element*>(mailBlockquote), classAttr); 1141 1142 if (shouldPerformSmartReplace()) 1143 addSpacesForSmartReplace(); 1144 1196 1145 // If we are dealing with a fragment created from plain text 1197 1146 // no style matching is necessary. … … 1222 1171 } 1223 1172 1173 bool ReplaceSelectionCommand::shouldPerformSmartReplace() const 1174 { 1175 if (!m_smartReplace) 1176 return false; 1177 1178 Element* textControl = enclosingTextFormControl(positionAtStartOfInsertedContent().deepEquivalent()); 1179 if (textControl && textControl->hasTagName(inputTag) && static_cast<HTMLInputElement*>(textControl)->isPasswordField()) 1180 return false; // Disable smart replace for password fields. 1181 1182 return true; 1183 } 1184 1185 void ReplaceSelectionCommand::addSpacesForSmartReplace() 1186 { 1187 VisiblePosition startOfInsertedContent = positionAtStartOfInsertedContent(); 1188 VisiblePosition endOfInsertedContent = positionAtEndOfInsertedContent(); 1189 1190 Position endUpstream = endOfInsertedContent.deepEquivalent().upstream(); 1191 Node* endNode = endUpstream.computeNodeBeforePosition(); 1192 if (endUpstream.anchorType() == Position::PositionIsOffsetInAnchor) 1193 endNode = endUpstream.containerNode(); 1194 1195 bool needsTrailingSpace = !isEndOfParagraph(endOfInsertedContent) && !isCharacterSmartReplaceExempt(endOfInsertedContent.characterAfter(), false); 1196 if (needsTrailingSpace && endNode) { 1197 bool collapseWhiteSpace = !endNode->renderer() || endNode->renderer()->style()->collapseWhiteSpace(); 1198 if (endNode->isTextNode()) { 1199 Text* text = static_cast<Text*>(endNode); 1200 insertTextIntoNode(text, text->length(), collapseWhiteSpace ? nonBreakingSpaceString() : " "); 1201 } else { 1202 RefPtr<Node> node = document()->createEditingTextNode(collapseWhiteSpace ? nonBreakingSpaceString() : " "); 1203 insertNodeAfter(node, endNode); 1204 updateNodesInserted(node.get()); 1205 } 1206 } 1207 1208 Position startDownstream = startOfInsertedContent.deepEquivalent().downstream(); 1209 Node* startNode = startDownstream.computeNodeAfterPosition(); 1210 if (startDownstream.anchorType() == Position::PositionIsOffsetInAnchor) 1211 startNode = startDownstream.containerNode(); 1212 1213 bool needsLeadingSpace = !isStartOfParagraph(startOfInsertedContent) && !isCharacterSmartReplaceExempt(startOfInsertedContent.previous().characterAfter(), true); 1214 if (needsLeadingSpace && startNode) { 1215 bool collapseWhiteSpace = !startNode->renderer() || startNode->renderer()->style()->collapseWhiteSpace(); 1216 if (startNode->isTextNode()) { 1217 Text* text = static_cast<Text*>(startNode); 1218 insertTextIntoNode(text, 0, collapseWhiteSpace ? nonBreakingSpaceString() : " "); 1219 } else { 1220 RefPtr<Node> node = document()->createEditingTextNode(collapseWhiteSpace ? nonBreakingSpaceString() : " "); 1221 // Don't updateNodesInserted. Doing so would set m_lastLeafInserted to be the node containing the leading space, 1222 // but m_lastLeafInserted is supposed to mark the end of pasted content. 1223 insertNodeBefore(node, startNode); 1224 // FIXME: Use positions to track the start/end of inserted content. 1225 m_firstNodeInserted = node; 1226 } 1227 } 1228 } 1229 1224 1230 void ReplaceSelectionCommand::completeHTMLReplacement(const Position &lastPositionToSelect) 1225 1231 { 1226 Position start; 1227 Position end; 1228 1229 // FIXME: This should never not be the case. 1230 if (m_firstNodeInserted && m_firstNodeInserted->inDocument() && m_lastLeafInserted && m_lastLeafInserted->inDocument()) { 1231 1232 start = positionAtStartOfInsertedContent().deepEquivalent(); 1233 end = positionAtEndOfInsertedContent().deepEquivalent(); 1234 1232 Position start = positionAtStartOfInsertedContent().deepEquivalent(); 1233 Position end = positionAtEndOfInsertedContent().deepEquivalent(); 1234 1235 // Mutation events may have deleted start or end 1236 if (start.isNotNull() && !start.isOrphan() && end.isNotNull() && !end.isOrphan()) { 1235 1237 // FIXME (11475): Remove this and require that the creator of the fragment to use nbsps. 1236 1238 rebalanceWhitespaceAt(start); … … 1241 1243 applyStyle(m_insertionStyle.get(), start, end); 1242 1244 } 1243 1245 1244 1246 if (lastPositionToSelect.isNotNull()) 1245 1247 end = lastPositionToSelect; … … 1248 1250 else 1249 1251 return; 1250 1252 1251 1253 if (m_selectReplacement) 1252 1254 setEndingSelection(VisibleSelection(start, end, SEL_DEFAULT_AFFINITY, endingSelection().isDirectional())); -
trunk/Source/WebCore/editing/ReplaceSelectionCommand.h
r96084 r96353 59 59 virtual void doApply(); 60 60 virtual EditAction editingAction() const; 61 62 void completeHTMLReplacement(const Position& lastPositionToSelect);63 61 64 62 class InsertedNodes { 65 63 public: 66 void respondToNodeInsertion(Node* node); 67 void willRemoveNode(Node* node); 64 void respondToNodeInsertion(Node*); 65 void willRemoveNodePreservingChildren(Node*); 66 void willRemoveNode(Node*); 67 68 68 Node* firstNodeInserted() const { return m_firstNodeInserted.get(); } 69 69 Node* lastLeafInserted() const { return m_lastNodeInserted->lastDescendant(); } … … 86 86 void mergeEndIfNeeded(); 87 87 88 void removeUnrenderedTextNodesAtEnds( );88 void removeUnrenderedTextNodesAtEnds(InsertedNodes&); 89 89 90 90 void removeRedundantStylesAndKeepStyleSpanInline(InsertedNodes&); 91 void handleStyleSpans( );91 void handleStyleSpans(Node* firstNodeInserted); 92 92 void copyStyleToChildren(Node* parentNode, const CSSMutableStyleDeclaration* parentStyle); 93 93 void handlePasteAsQuotationNode(); … … 96 96 virtual void removeNodeAndPruneAncestors(Node*); 97 97 98 VisiblePosition positionAtStartOfInsertedContent(); 99 VisiblePosition positionAtEndOfInsertedContent(); 100 98 VisiblePosition positionAtStartOfInsertedContent() const; 99 VisiblePosition positionAtEndOfInsertedContent() const; 100 101 bool shouldPerformSmartReplace() const; 102 void addSpacesForSmartReplace(); 103 void completeHTMLReplacement(const Position& lastPositionToSelect); 104 101 105 bool performTrivialReplace(const ReplacementFragment&); 102 106
Note: See TracChangeset
for help on using the changeset viewer.