| 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 901 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 912 } | 912 } |
| 913 | 913 |
| 914 static inline HTMLElement* elementToSplitToAvoidPastingIntoInlineElementsWithSty
le(const Position& insertionPos) | 914 static inline HTMLElement* elementToSplitToAvoidPastingIntoInlineElementsWithSty
le(const Position& insertionPos) |
| 915 { | 915 { |
| 916 Element* containingBlock = enclosingBlock(insertionPos.computeContainerNode(
)); | 916 Element* containingBlock = enclosingBlock(insertionPos.computeContainerNode(
)); |
| 917 return toHTMLElement(highestEnclosingNodeOfType(insertionPos, isInlineHTMLEl
ementWithStyle, CannotCrossEditingBoundary, containingBlock)); | 917 return toHTMLElement(highestEnclosingNodeOfType(insertionPos, isInlineHTMLEl
ementWithStyle, CannotCrossEditingBoundary, containingBlock)); |
| 918 } | 918 } |
| 919 | 919 |
| 920 void ReplaceSelectionCommand::doApply() | 920 void ReplaceSelectionCommand::doApply() |
| 921 { | 921 { |
| 922 VisibleSelection selection = endingSelection(); | 922 const VisibleSelection selection = endingSelection(); |
| 923 ASSERT(selection.isCaretOrRange()); | 923 ASSERT(selection.isCaretOrRange()); |
| 924 ASSERT(selection.start().anchorNode()); | 924 ASSERT(selection.start().anchorNode()); |
| 925 if (!selection.isNonOrphanedCaretOrRange() || !selection.start().anchorNode(
)) | 925 if (!selection.isNonOrphanedCaretOrRange() || !selection.start().anchorNode(
)) |
| 926 return; | 926 return; |
| 927 | 927 |
| 928 if (!selection.rootEditableElement()) | 928 if (!selection.rootEditableElement()) |
| 929 return; | 929 return; |
| 930 | 930 |
| 931 ReplacementFragment fragment(&document(), m_documentFragment.get(), selectio
n); | 931 ReplacementFragment fragment(&document(), m_documentFragment.get(), selectio
n); |
| 932 if (performTrivialReplace(fragment)) | 932 if (performTrivialReplace(fragment)) |
| 933 return; | 933 return; |
| 934 | 934 |
| 935 // We can skip matching the style if the selection is plain text. | 935 // We can skip matching the style if the selection is plain text. |
| 936 if ((selection.start().anchorNode()->layoutObject() && selection.start().anc
horNode()->layoutObject()->style()->userModify() == READ_WRITE_PLAINTEXT_ONLY) | 936 if ((selection.start().anchorNode()->layoutObject() && selection.start().anc
horNode()->layoutObject()->style()->userModify() == READ_WRITE_PLAINTEXT_ONLY) |
| 937 && (selection.end().anchorNode()->layoutObject() && selection.end().anch
orNode()->layoutObject()->style()->userModify() == READ_WRITE_PLAINTEXT_ONLY)) | 937 && (selection.end().anchorNode()->layoutObject() && selection.end().anch
orNode()->layoutObject()->style()->userModify() == READ_WRITE_PLAINTEXT_ONLY)) |
| 938 m_matchStyle = false; | 938 m_matchStyle = false; |
| 939 | 939 |
| 940 if (m_matchStyle) { | 940 if (m_matchStyle) { |
| 941 m_insertionStyle = EditingStyle::create(selection.start()); | 941 m_insertionStyle = EditingStyle::create(selection.start()); |
| 942 m_insertionStyle->mergeTypingStyle(&document()); | 942 m_insertionStyle->mergeTypingStyle(&document()); |
| 943 } | 943 } |
| 944 | 944 |
| 945 VisiblePosition visibleStart = selection.visibleStart(); | 945 const VisiblePosition visibleStart = selection.visibleStart(); |
| 946 VisiblePosition visibleEnd = selection.visibleEnd(); | 946 const VisiblePosition visibleEnd = selection.visibleEnd(); |
| 947 | 947 |
| 948 bool selectionEndWasEndOfParagraph = isEndOfParagraph(visibleEnd); | 948 const bool selectionEndWasEndOfParagraph = isEndOfParagraph(visibleEnd); |
| 949 bool selectionStartWasStartOfParagraph = isStartOfParagraph(visibleStart); | 949 const bool selectionStartWasStartOfParagraph = isStartOfParagraph(visibleSta
rt); |
| 950 | 950 |
| 951 Element* enclosingBlockOfVisibleStart = enclosingBlock(visibleStart.deepEqui
valent().anchorNode()); | 951 Element* enclosingBlockOfVisibleStart = enclosingBlock(visibleStart.deepEqui
valent().anchorNode()); |
| 952 | 952 |
| 953 Position insertionPos = selection.start(); | 953 const bool startIsInsideMailBlockquote = enclosingNodeOfType(selection.start
(), isMailHTMLBlockquoteElement, CanCrossEditingBoundary); |
| 954 bool startIsInsideMailBlockquote = enclosingNodeOfType(insertionPos, isMailH
TMLBlockquoteElement, CanCrossEditingBoundary); | 954 const bool selectionIsPlainText = !selection.isContentRichlyEditable(); |
| 955 bool selectionIsPlainText = !selection.isContentRichlyEditable(); | |
| 956 Element* currentRoot = selection.rootEditableElement(); | 955 Element* currentRoot = selection.rootEditableElement(); |
| 957 | 956 |
| 958 if ((selectionStartWasStartOfParagraph && selectionEndWasEndOfParagraph && !
startIsInsideMailBlockquote) | 957 if ((selectionStartWasStartOfParagraph && selectionEndWasEndOfParagraph && !
startIsInsideMailBlockquote) |
| 959 || enclosingBlockOfVisibleStart == currentRoot | 958 || enclosingBlockOfVisibleStart == currentRoot |
| 960 || isListItem(enclosingBlockOfVisibleStart) | 959 || isListItem(enclosingBlockOfVisibleStart) |
| 961 || selectionIsPlainText) { | 960 || selectionIsPlainText) { |
| 962 m_preventNesting = false; | 961 m_preventNesting = false; |
| 963 } | 962 } |
| 964 | 963 |
| 965 if (selection.isRange()) { | 964 if (selection.isRange()) { |
| 966 // When the end of the selection being pasted into is at the end of a pa
ragraph, and that selection | 965 // When the end of the selection being pasted into is at the end of a pa
ragraph, and that selection |
| 967 // spans multiple blocks, not merging may leave an empty line. | 966 // spans multiple blocks, not merging may leave an empty line. |
| 968 // When the start of the selection being pasted into is at the start of
a block, not merging | 967 // When the start of the selection being pasted into is at the start of
a block, not merging |
| 969 // will leave hanging block(s). | 968 // will leave hanging block(s). |
| 970 // Merge blocks if the start of the selection was in a Mail blockquote,
since we handle | 969 // Merge blocks if the start of the selection was in a Mail blockquote,
since we handle |
| 971 // that case specially to prevent nesting. | 970 // that case specially to prevent nesting. |
| 972 bool mergeBlocksAfterDelete = startIsInsideMailBlockquote || isEndOfPara
graph(visibleEnd) || isStartOfBlock(visibleStart); | 971 bool mergeBlocksAfterDelete = startIsInsideMailBlockquote || isEndOfPara
graph(visibleEnd) || isStartOfBlock(visibleStart); |
| 973 // FIXME: We should only expand to include fully selected special elemen
ts if we are copying a | 972 // FIXME: We should only expand to include fully selected special elemen
ts if we are copying a |
| 974 // selection and pasting it on top of itself. | 973 // selection and pasting it on top of itself. |
| 975 deleteSelection(false, mergeBlocksAfterDelete, false); | 974 deleteSelection(false, mergeBlocksAfterDelete, false); |
| 976 visibleStart = endingSelection().visibleStart(); | |
| 977 if (fragment.hasInterchangeNewlineAtStart()) { | 975 if (fragment.hasInterchangeNewlineAtStart()) { |
| 978 if (isEndOfParagraph(visibleStart) && !isStartOfParagraph(visibleSta
rt)) { | 976 VisiblePosition startAfterDelete = endingSelection().visibleStart(); |
| 979 if (!isEndOfEditableOrNonEditableContent(visibleStart)) | 977 if (isEndOfParagraph(startAfterDelete) && !isStartOfParagraph(startA
fterDelete) && !isEndOfEditableOrNonEditableContent(startAfterDelete)) |
| 980 setEndingSelection(nextPositionOf(visibleStart)); | 978 setEndingSelection(nextPositionOf(startAfterDelete)); |
| 981 } else { | 979 else |
| 982 insertParagraphSeparator(); | 980 insertParagraphSeparator(); |
| 983 } | |
| 984 } | 981 } |
| 985 insertionPos = endingSelection().start(); | |
| 986 } else { | 982 } else { |
| 987 ASSERT(selection.isCaret()); | 983 ASSERT(selection.isCaret()); |
| 988 if (fragment.hasInterchangeNewlineAtStart()) { | 984 if (fragment.hasInterchangeNewlineAtStart()) { |
| 989 VisiblePosition next = nextPositionOf(visibleStart, CannotCrossEditi
ngBoundary); | 985 const VisiblePosition next = nextPositionOf(visibleStart, CannotCros
sEditingBoundary); |
| 990 if (isEndOfParagraph(visibleStart) && !isStartOfParagraph(visibleSta
rt) && next.isNotNull()) { | 986 if (isEndOfParagraph(visibleStart) && !isStartOfParagraph(visibleSta
rt) && next.isNotNull()) |
| 991 setEndingSelection(next); | 987 setEndingSelection(next); |
| 992 } else { | 988 else |
| 993 insertParagraphSeparator(); | 989 insertParagraphSeparator(); |
| 994 visibleStart = endingSelection().visibleStart(); | |
| 995 } | |
| 996 } | 990 } |
| 997 // We split the current paragraph in two to avoid nesting the blocks fro
m the fragment inside the current block. | 991 // We split the current paragraph in two to avoid nesting the blocks fro
m the fragment inside the current block. |
| 998 // For example paste <div>foo</div><div>bar</div><div>baz</div> into <di
v>x^x</div>, where ^ is the caret. | 992 // For example paste <div>foo</div><div>bar</div><div>baz</div> into <di
v>x^x</div>, where ^ is the caret. |
| 999 // As long as the div styles are the same, visually you'd expect: <div>
xbar</div><div>bar</div><div>bazx</div>, | 993 // As long as the div styles are the same, visually you'd expect: <div>
xbar</div><div>bar</div><div>bazx</div>, |
| 1000 // not <div>xbar<div>bar</div><div>bazx</div></div>. | 994 // not <div>xbar<div>bar</div><div>bazx</div></div>. |
| 1001 // Don't do this if the selection started in a Mail blockquote. | 995 // Don't do this if the selection started in a Mail blockquote. |
| 1002 if (m_preventNesting && !startIsInsideMailBlockquote && !isEndOfParagrap
h(visibleStart) && !isStartOfParagraph(visibleStart)) { | 996 if (m_preventNesting && !startIsInsideMailBlockquote && !isEndOfParagrap
h(endingSelection().visibleStart()) && !isStartOfParagraph(endingSelection().vis
ibleStart())) { |
| 1003 insertParagraphSeparator(); | 997 insertParagraphSeparator(); |
| 1004 setEndingSelection(previousPositionOf(endingSelection().visibleStart
())); | 998 setEndingSelection(previousPositionOf(endingSelection().visibleStart
())); |
| 1005 } | 999 } |
| 1006 insertionPos = endingSelection().start(); | |
| 1007 } | 1000 } |
| 1008 | 1001 |
| 1002 Position insertionPos = endingSelection().start(); |
| 1009 // We don't want any of the pasted content to end up nested in a Mail blockq
uote, so first break | 1003 // We don't want any of the pasted content to end up nested in a Mail blockq
uote, so first break |
| 1010 // out of any surrounding Mail blockquotes. Unless we're inserting in a tabl
e, in which case | 1004 // out of any surrounding Mail blockquotes. Unless we're inserting in a tabl
e, in which case |
| 1011 // breaking the blockquote will prevent the content from actually being inse
rted in the table. | 1005 // breaking the blockquote will prevent the content from actually being inse
rted in the table. |
| 1012 if (startIsInsideMailBlockquote && m_preventNesting && !(enclosingNodeOfType
(insertionPos, &isTableStructureNode))) { | 1006 if (enclosingNodeOfType(insertionPos, isMailHTMLBlockquoteElement, CanCrossE
ditingBoundary) && m_preventNesting && !(enclosingNodeOfType(insertionPos, &isTa
bleStructureNode))) { |
| 1013 applyCommandToComposite(BreakBlockquoteCommand::create(document())); | 1007 applyCommandToComposite(BreakBlockquoteCommand::create(document())); |
| 1014 // This will leave a br between the split. | 1008 // This will leave a br between the split. |
| 1015 Node* br = endingSelection().start().anchorNode(); | 1009 Node* br = endingSelection().start().anchorNode(); |
| 1016 ASSERT(isHTMLBRElement(br)); | 1010 ASSERT(isHTMLBRElement(br)); |
| 1017 // Insert content between the two blockquotes, but remove the br (since
it was just a placeholder). | 1011 // Insert content between the two blockquotes, but remove the br (since
it was just a placeholder). |
| 1018 insertionPos = positionInParentBeforeNode(*br); | 1012 insertionPos = positionInParentBeforeNode(*br); |
| 1019 removeNode(br); | 1013 removeNode(br); |
| 1020 } | 1014 } |
| 1021 | 1015 |
| 1022 // Inserting content could cause whitespace to collapse, e.g. inserting <div
>foo</div> into hello^ world. | 1016 // Inserting content could cause whitespace to collapse, e.g. inserting <div
>foo</div> into hello^ world. |
| (...skipping 526 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1549 DEFINE_TRACE(ReplaceSelectionCommand) | 1543 DEFINE_TRACE(ReplaceSelectionCommand) |
| 1550 { | 1544 { |
| 1551 visitor->trace(m_startOfInsertedContent); | 1545 visitor->trace(m_startOfInsertedContent); |
| 1552 visitor->trace(m_endOfInsertedContent); | 1546 visitor->trace(m_endOfInsertedContent); |
| 1553 visitor->trace(m_insertionStyle); | 1547 visitor->trace(m_insertionStyle); |
| 1554 visitor->trace(m_documentFragment); | 1548 visitor->trace(m_documentFragment); |
| 1555 CompositeEditCommand::trace(visitor); | 1549 CompositeEditCommand::trace(visitor); |
| 1556 } | 1550 } |
| 1557 | 1551 |
| 1558 } // namespace blink | 1552 } // namespace blink |
| OLD | NEW |