OLD | NEW |
1 /* | 1 /* |
2 * Copyright (C) 2005, 2006, 2008 Apple Inc. All rights reserved. | 2 * Copyright (C) 2005, 2006, 2008 Apple Inc. All rights reserved. |
3 * Copyright (C) 2009, 2010, 2011 Google Inc. All rights reserved. | 3 * Copyright (C) 2009, 2010, 2011 Google Inc. All rights reserved. |
4 * | 4 * |
5 * Redistribution and use in source and binary forms, with or without | 5 * Redistribution and use in source and binary forms, with or without |
6 * modification, are permitted provided that the following conditions | 6 * modification, are permitted provided that the following conditions |
7 * are met: | 7 * are met: |
8 * 1. Redistributions of source code must retain the above copyright | 8 * 1. Redistributions of source code must retain the above copyright |
9 * notice, this list of conditions and the following disclaimer. | 9 * notice, this list of conditions and the following disclaimer. |
10 * 2. Redistributions in binary form must reproduce the above copyright | 10 * 2. Redistributions in binary form must reproduce the above copyright |
(...skipping 1005 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1016 ASSERT(isHTMLBRElement(br)); | 1016 ASSERT(isHTMLBRElement(br)); |
1017 // Insert content between the two blockquotes, but remove the br (since
it was just a placeholder). | 1017 // Insert content between the two blockquotes, but remove the br (since
it was just a placeholder). |
1018 insertionPos = positionInParentBeforeNode(*br); | 1018 insertionPos = positionInParentBeforeNode(*br); |
1019 removeNode(br); | 1019 removeNode(br); |
1020 } | 1020 } |
1021 | 1021 |
1022 // Inserting content could cause whitespace to collapse, e.g. inserting <div
>foo</div> into hello^ world. | 1022 // Inserting content could cause whitespace to collapse, e.g. inserting <div
>foo</div> into hello^ world. |
1023 prepareWhitespaceAtPositionForSplit(insertionPos); | 1023 prepareWhitespaceAtPositionForSplit(insertionPos); |
1024 | 1024 |
1025 // If the downstream node has been removed there's no point in continuing. | 1025 // If the downstream node has been removed there's no point in continuing. |
1026 if (!insertionPos.downstream().anchorNode()) | 1026 if (!mostForwardCaretPosition(insertionPos).anchorNode()) |
1027 return; | 1027 return; |
1028 | 1028 |
1029 // NOTE: This would be an incorrect usage of downstream() if downstream() we
re changed to mean the last position after | 1029 // NOTE: This would be an incorrect usage of downstream() if downstream() we
re changed to mean the last position after |
1030 // p that maps to the same visible position as p (since in the case where a
br is at the end of a block and collapsed | 1030 // p that maps to the same visible position as p (since in the case where a
br is at the end of a block and collapsed |
1031 // away, there are positions after the br which map to the same visible posi
tion as [br, 0]). | 1031 // away, there are positions after the br which map to the same visible posi
tion as [br, 0]). |
1032 HTMLBRElement* endBR = isHTMLBRElement(*insertionPos.downstream().anchorNode
()) ? toHTMLBRElement(insertionPos.downstream().anchorNode()) : 0; | 1032 HTMLBRElement* endBR = isHTMLBRElement(*mostForwardCaretPosition(insertionPo
s).anchorNode()) ? toHTMLBRElement(mostForwardCaretPosition(insertionPos).anchor
Node()) : 0; |
1033 VisiblePosition originalVisPosBeforeEndBR; | 1033 VisiblePosition originalVisPosBeforeEndBR; |
1034 if (endBR) | 1034 if (endBR) |
1035 originalVisPosBeforeEndBR = VisiblePosition(positionBeforeNode(endBR)).p
revious(); | 1035 originalVisPosBeforeEndBR = VisiblePosition(positionBeforeNode(endBR)).p
revious(); |
1036 | 1036 |
1037 RefPtrWillBeRawPtr<Element> enclosingBlockOfInsertionPos = enclosingBlock(in
sertionPos.anchorNode()); | 1037 RefPtrWillBeRawPtr<Element> enclosingBlockOfInsertionPos = enclosingBlock(in
sertionPos.anchorNode()); |
1038 | 1038 |
1039 // Adjust insertionPos to prevent nesting. | 1039 // Adjust insertionPos to prevent nesting. |
1040 // If the start was in a Mail blockquote, we will have already handled adjus
ting insertionPos above. | 1040 // If the start was in a Mail blockquote, we will have already handled adjus
ting insertionPos above. |
1041 if (m_preventNesting && enclosingBlockOfInsertionPos && !isTableCell(enclosi
ngBlockOfInsertionPos.get()) && !startIsInsideMailBlockquote) { | 1041 if (m_preventNesting && enclosingBlockOfInsertionPos && !isTableCell(enclosi
ngBlockOfInsertionPos.get()) && !startIsInsideMailBlockquote) { |
1042 ASSERT(enclosingBlockOfInsertionPos != currentRoot); | 1042 ASSERT(enclosingBlockOfInsertionPos != currentRoot); |
(...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1212 if (startOfParagraph(endOfInsertedContent).deepEquivalent() == startOfPa
ragraphToMove.deepEquivalent()) { | 1212 if (startOfParagraph(endOfInsertedContent).deepEquivalent() == startOfPa
ragraphToMove.deepEquivalent()) { |
1213 insertNodeAt(createBreakElement(document()).get(), endOfInsertedCont
ent.deepEquivalent()); | 1213 insertNodeAt(createBreakElement(document()).get(), endOfInsertedCont
ent.deepEquivalent()); |
1214 // Mutation events (bug 22634) triggered by inserting the <br> might
have removed the content we're about to move | 1214 // Mutation events (bug 22634) triggered by inserting the <br> might
have removed the content we're about to move |
1215 if (!startOfParagraphToMove.deepEquivalent().inDocument()) | 1215 if (!startOfParagraphToMove.deepEquivalent().inDocument()) |
1216 return; | 1216 return; |
1217 } | 1217 } |
1218 | 1218 |
1219 // FIXME: Maintain positions for the start and end of inserted content i
nstead of keeping nodes. The nodes are | 1219 // FIXME: Maintain positions for the start and end of inserted content i
nstead of keeping nodes. The nodes are |
1220 // only ever used to create positions where inserted content starts/ends
. | 1220 // only ever used to create positions where inserted content starts/ends
. |
1221 moveParagraph(startOfParagraphToMove, endOfParagraph(startOfParagraphToM
ove), destination); | 1221 moveParagraph(startOfParagraphToMove, endOfParagraph(startOfParagraphToM
ove), destination); |
1222 m_startOfInsertedContent = endingSelection().visibleStart().deepEquivale
nt().downstream(); | 1222 m_startOfInsertedContent = mostForwardCaretPosition(endingSelection().vi
sibleStart().deepEquivalent()); |
1223 if (m_endOfInsertedContent.isOrphan()) | 1223 if (m_endOfInsertedContent.isOrphan()) |
1224 m_endOfInsertedContent = endingSelection().visibleEnd().deepEquivale
nt().upstream(); | 1224 m_endOfInsertedContent = mostBackwardCaretPosition(endingSelection()
.visibleEnd().deepEquivalent()); |
1225 } | 1225 } |
1226 | 1226 |
1227 Position lastPositionToSelect; | 1227 Position lastPositionToSelect; |
1228 if (fragment.hasInterchangeNewlineAtEnd()) { | 1228 if (fragment.hasInterchangeNewlineAtEnd()) { |
1229 VisiblePosition endOfInsertedContent = positionAtEndOfInsertedContent(); | 1229 VisiblePosition endOfInsertedContent = positionAtEndOfInsertedContent(); |
1230 VisiblePosition next = endOfInsertedContent.next(CannotCrossEditingBound
ary); | 1230 VisiblePosition next = endOfInsertedContent.next(CannotCrossEditingBound
ary); |
1231 | 1231 |
1232 if (selectionEndWasEndOfParagraph || !isEndOfParagraph(endOfInsertedCont
ent) || next.isNull()) { | 1232 if (selectionEndWasEndOfParagraph || !isEndOfParagraph(endOfInsertedCont
ent) || next.isNull()) { |
1233 if (!isStartOfParagraph(endOfInsertedContent)) { | 1233 if (!isStartOfParagraph(endOfInsertedContent)) { |
1234 setEndingSelection(endOfInsertedContent); | 1234 setEndingSelection(endOfInsertedContent); |
1235 Element* enclosingBlockElement = enclosingBlock(endOfInsertedCon
tent.deepEquivalent().anchorNode()); | 1235 Element* enclosingBlockElement = enclosingBlock(endOfInsertedCon
tent.deepEquivalent().anchorNode()); |
1236 if (isListItem(enclosingBlockElement)) { | 1236 if (isListItem(enclosingBlockElement)) { |
1237 RefPtrWillBeRawPtr<HTMLLIElement> newListItem = createListIt
emElement(document()); | 1237 RefPtrWillBeRawPtr<HTMLLIElement> newListItem = createListIt
emElement(document()); |
1238 insertNodeAfter(newListItem, enclosingBlockElement); | 1238 insertNodeAfter(newListItem, enclosingBlockElement); |
1239 setEndingSelection(VisiblePosition(firstPositionInNode(newLi
stItem.get()))); | 1239 setEndingSelection(VisiblePosition(firstPositionInNode(newLi
stItem.get()))); |
1240 } else { | 1240 } else { |
1241 // Use a default paragraph element (a plain div) for the emp
ty paragraph, using the last paragraph | 1241 // Use a default paragraph element (a plain div) for the emp
ty paragraph, using the last paragraph |
1242 // block's style seems to annoy users. | 1242 // block's style seems to annoy users. |
1243 insertParagraphSeparator(true, !startIsInsideMailBlockquote
&& highestEnclosingNodeOfType(endOfInsertedContent.deepEquivalent(), | 1243 insertParagraphSeparator(true, !startIsInsideMailBlockquote
&& highestEnclosingNodeOfType(endOfInsertedContent.deepEquivalent(), |
1244 isMailHTMLBlockquoteElement, CannotCrossEditingBoundary,
insertedNodes.firstNodeInserted()->parentNode())); | 1244 isMailHTMLBlockquoteElement, CannotCrossEditingBoundary,
insertedNodes.firstNodeInserted()->parentNode())); |
1245 } | 1245 } |
1246 | 1246 |
1247 // Select up to the paragraph separator that was added. | 1247 // Select up to the paragraph separator that was added. |
1248 lastPositionToSelect = endingSelection().visibleStart().deepEqui
valent(); | 1248 lastPositionToSelect = endingSelection().visibleStart().deepEqui
valent(); |
1249 updateNodesInserted(lastPositionToSelect.anchorNode()); | 1249 updateNodesInserted(lastPositionToSelect.anchorNode()); |
1250 } | 1250 } |
1251 } else { | 1251 } else { |
1252 // Select up to the beginning of the next paragraph. | 1252 // Select up to the beginning of the next paragraph. |
1253 lastPositionToSelect = next.deepEquivalent().downstream(); | 1253 lastPositionToSelect = mostForwardCaretPosition(next.deepEquivalent(
)); |
1254 } | 1254 } |
1255 } else { | 1255 } else { |
1256 mergeEndIfNeeded(); | 1256 mergeEndIfNeeded(); |
1257 } | 1257 } |
1258 | 1258 |
1259 if (HTMLQuoteElement* mailBlockquote = toHTMLQuoteElement(enclosingNodeOfTyp
e(positionAtStartOfInsertedContent().deepEquivalent(), isMailPasteAsQuotationHTM
LBlockQuoteElement))) | 1259 if (HTMLQuoteElement* mailBlockquote = toHTMLQuoteElement(enclosingNodeOfTyp
e(positionAtStartOfInsertedContent().deepEquivalent(), isMailPasteAsQuotationHTM
LBlockQuoteElement))) |
1260 removeElementAttribute(mailBlockquote, classAttr); | 1260 removeElementAttribute(mailBlockquote, classAttr); |
1261 | 1261 |
1262 if (shouldPerformSmartReplace()) | 1262 if (shouldPerformSmartReplace()) |
1263 addSpacesForSmartReplace(); | 1263 addSpacesForSmartReplace(); |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1305 static bool isCharacterSmartReplaceExemptConsideringNonBreakingSpace(UChar32 cha
racter, bool previousCharacter) | 1305 static bool isCharacterSmartReplaceExemptConsideringNonBreakingSpace(UChar32 cha
racter, bool previousCharacter) |
1306 { | 1306 { |
1307 return isCharacterSmartReplaceExempt(character == noBreakSpaceCharacter ? '
' : character, previousCharacter); | 1307 return isCharacterSmartReplaceExempt(character == noBreakSpaceCharacter ? '
' : character, previousCharacter); |
1308 } | 1308 } |
1309 | 1309 |
1310 void ReplaceSelectionCommand::addSpacesForSmartReplace() | 1310 void ReplaceSelectionCommand::addSpacesForSmartReplace() |
1311 { | 1311 { |
1312 VisiblePosition startOfInsertedContent = positionAtStartOfInsertedContent(); | 1312 VisiblePosition startOfInsertedContent = positionAtStartOfInsertedContent(); |
1313 VisiblePosition endOfInsertedContent = positionAtEndOfInsertedContent(); | 1313 VisiblePosition endOfInsertedContent = positionAtEndOfInsertedContent(); |
1314 | 1314 |
1315 Position endUpstream = endOfInsertedContent.deepEquivalent().upstream(); | 1315 Position endUpstream = mostBackwardCaretPosition(endOfInsertedContent.deepEq
uivalent()); |
1316 Node* endNode = endUpstream.computeNodeBeforePosition(); | 1316 Node* endNode = endUpstream.computeNodeBeforePosition(); |
1317 int endOffset = endNode && endNode->isTextNode() ? toText(endNode)->length()
: 0; | 1317 int endOffset = endNode && endNode->isTextNode() ? toText(endNode)->length()
: 0; |
1318 if (endUpstream.isOffsetInAnchor()) { | 1318 if (endUpstream.isOffsetInAnchor()) { |
1319 endNode = endUpstream.computeContainerNode(); | 1319 endNode = endUpstream.computeContainerNode(); |
1320 endOffset = endUpstream.offsetInContainerNode(); | 1320 endOffset = endUpstream.offsetInContainerNode(); |
1321 } | 1321 } |
1322 | 1322 |
1323 bool needsTrailingSpace = !isEndOfParagraph(endOfInsertedContent) && !isChar
acterSmartReplaceExemptConsideringNonBreakingSpace(endOfInsertedContent.characte
rAfter(), false); | 1323 bool needsTrailingSpace = !isEndOfParagraph(endOfInsertedContent) && !isChar
acterSmartReplaceExemptConsideringNonBreakingSpace(endOfInsertedContent.characte
rAfter(), false); |
1324 if (needsTrailingSpace && endNode) { | 1324 if (needsTrailingSpace && endNode) { |
1325 bool collapseWhiteSpace = !endNode->layoutObject() || endNode->layoutObj
ect()->style()->collapseWhiteSpace(); | 1325 bool collapseWhiteSpace = !endNode->layoutObject() || endNode->layoutObj
ect()->style()->collapseWhiteSpace(); |
1326 if (endNode->isTextNode()) { | 1326 if (endNode->isTextNode()) { |
1327 insertTextIntoNode(toText(endNode), endOffset, collapseWhiteSpace ?
nonBreakingSpaceString() : " "); | 1327 insertTextIntoNode(toText(endNode), endOffset, collapseWhiteSpace ?
nonBreakingSpaceString() : " "); |
1328 if (m_endOfInsertedContent.computeContainerNode() == endNode) | 1328 if (m_endOfInsertedContent.computeContainerNode() == endNode) |
1329 m_endOfInsertedContent = Position(endNode, m_endOfInsertedConten
t.offsetInContainerNode() + 1); | 1329 m_endOfInsertedContent = Position(endNode, m_endOfInsertedConten
t.offsetInContainerNode() + 1); |
1330 } else { | 1330 } else { |
1331 RefPtrWillBeRawPtr<Text> node = document().createEditingTextNode(col
lapseWhiteSpace ? nonBreakingSpaceString() : " "); | 1331 RefPtrWillBeRawPtr<Text> node = document().createEditingTextNode(col
lapseWhiteSpace ? nonBreakingSpaceString() : " "); |
1332 insertNodeAfter(node, endNode); | 1332 insertNodeAfter(node, endNode); |
1333 updateNodesInserted(node.get()); | 1333 updateNodesInserted(node.get()); |
1334 } | 1334 } |
1335 } | 1335 } |
1336 | 1336 |
1337 document().updateLayout(); | 1337 document().updateLayout(); |
1338 | 1338 |
1339 Position startDownstream = startOfInsertedContent.deepEquivalent().downstrea
m(); | 1339 Position startDownstream = mostForwardCaretPosition(startOfInsertedContent.d
eepEquivalent()); |
1340 Node* startNode = startDownstream.computeNodeAfterPosition(); | 1340 Node* startNode = startDownstream.computeNodeAfterPosition(); |
1341 unsigned startOffset = 0; | 1341 unsigned startOffset = 0; |
1342 if (startDownstream.isOffsetInAnchor()) { | 1342 if (startDownstream.isOffsetInAnchor()) { |
1343 startNode = startDownstream.computeContainerNode(); | 1343 startNode = startDownstream.computeContainerNode(); |
1344 startOffset = startDownstream.offsetInContainerNode(); | 1344 startOffset = startDownstream.offsetInContainerNode(); |
1345 } | 1345 } |
1346 | 1346 |
1347 bool needsLeadingSpace = !isStartOfParagraph(startOfInsertedContent) && !isC
haracterSmartReplaceExemptConsideringNonBreakingSpace(startOfInsertedContent.pre
vious().characterAfter(), true); | 1347 bool needsLeadingSpace = !isStartOfParagraph(startOfInsertedContent) && !isC
haracterSmartReplaceExemptConsideringNonBreakingSpace(startOfInsertedContent.pre
vious().characterAfter(), true); |
1348 if (needsLeadingSpace && startNode) { | 1348 if (needsLeadingSpace && startNode) { |
1349 bool collapseWhiteSpace = !startNode->layoutObject() || startNode->layou
tObject()->style()->collapseWhiteSpace(); | 1349 bool collapseWhiteSpace = !startNode->layoutObject() || startNode->layou
tObject()->style()->collapseWhiteSpace(); |
(...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1519 return false; | 1519 return false; |
1520 | 1520 |
1521 // FIXME: Would be nice to handle smart replace in the fast path. | 1521 // FIXME: Would be nice to handle smart replace in the fast path. |
1522 if (m_smartReplace || fragment.hasInterchangeNewlineAtStart() || fragment.ha
sInterchangeNewlineAtEnd()) | 1522 if (m_smartReplace || fragment.hasInterchangeNewlineAtStart() || fragment.ha
sInterchangeNewlineAtEnd()) |
1523 return false; | 1523 return false; |
1524 | 1524 |
1525 // e.g. when "bar" is inserted after "foo" in <div><u>foo</u></div>, "bar" s
hould not be underlined. | 1525 // e.g. when "bar" is inserted after "foo" in <div><u>foo</u></div>, "bar" s
hould not be underlined. |
1526 if (elementToSplitToAvoidPastingIntoInlineElementsWithStyle(endingSelection(
).start())) | 1526 if (elementToSplitToAvoidPastingIntoInlineElementsWithStyle(endingSelection(
).start())) |
1527 return false; | 1527 return false; |
1528 | 1528 |
1529 RefPtrWillBeRawPtr<Node> nodeAfterInsertionPos = endingSelection().end().dow
nstream().anchorNode(); | 1529 RefPtrWillBeRawPtr<Node> nodeAfterInsertionPos = mostForwardCaretPosition(en
dingSelection().end()).anchorNode(); |
1530 Text* textNode = toText(fragment.firstChild()); | 1530 Text* textNode = toText(fragment.firstChild()); |
1531 // Our fragment creation code handles tabs, spaces, and newlines, so we don'
t have to worry about those here. | 1531 // Our fragment creation code handles tabs, spaces, and newlines, so we don'
t have to worry about those here. |
1532 | 1532 |
1533 Position start = endingSelection().start(); | 1533 Position start = endingSelection().start(); |
1534 Position end = replaceSelectedTextInNode(textNode->data()); | 1534 Position end = replaceSelectedTextInNode(textNode->data()); |
1535 if (end.isNull()) | 1535 if (end.isNull()) |
1536 return false; | 1536 return false; |
1537 | 1537 |
1538 if (nodeAfterInsertionPos && nodeAfterInsertionPos->parentNode() && isHTMLBR
Element(*nodeAfterInsertionPos) | 1538 if (nodeAfterInsertionPos && nodeAfterInsertionPos->parentNode() && isHTMLBR
Element(*nodeAfterInsertionPos) |
1539 && shouldRemoveEndBR(toHTMLBRElement(nodeAfterInsertionPos.get()), Visib
lePosition(positionBeforeNode(nodeAfterInsertionPos.get())))) | 1539 && shouldRemoveEndBR(toHTMLBRElement(nodeAfterInsertionPos.get()), Visib
lePosition(positionBeforeNode(nodeAfterInsertionPos.get())))) |
1540 removeNodeAndPruneAncestors(nodeAfterInsertionPos.get()); | 1540 removeNodeAndPruneAncestors(nodeAfterInsertionPos.get()); |
1541 | 1541 |
1542 VisibleSelection selectionAfterReplace(m_selectReplacement ? start : end, en
d); | 1542 VisibleSelection selectionAfterReplace(m_selectReplacement ? start : end, en
d); |
1543 | 1543 |
1544 setEndingSelection(selectionAfterReplace); | 1544 setEndingSelection(selectionAfterReplace); |
1545 | 1545 |
1546 return true; | 1546 return true; |
1547 } | 1547 } |
1548 | 1548 |
1549 DEFINE_TRACE(ReplaceSelectionCommand) | 1549 DEFINE_TRACE(ReplaceSelectionCommand) |
1550 { | 1550 { |
1551 visitor->trace(m_startOfInsertedContent); | 1551 visitor->trace(m_startOfInsertedContent); |
1552 visitor->trace(m_endOfInsertedContent); | 1552 visitor->trace(m_endOfInsertedContent); |
1553 visitor->trace(m_insertionStyle); | 1553 visitor->trace(m_insertionStyle); |
1554 visitor->trace(m_documentFragment); | 1554 visitor->trace(m_documentFragment); |
1555 CompositeEditCommand::trace(visitor); | 1555 CompositeEditCommand::trace(visitor); |
1556 } | 1556 } |
1557 | 1557 |
1558 } // namespace blink | 1558 } // namespace blink |
OLD | NEW |