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 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
90 | 90 |
91 RefPtr<Document> m_document; | 91 RefPtr<Document> m_document; |
92 RefPtr<DocumentFragment> m_fragment; | 92 RefPtr<DocumentFragment> m_fragment; |
93 bool m_hasInterchangeNewlineAtStart; | 93 bool m_hasInterchangeNewlineAtStart; |
94 bool m_hasInterchangeNewlineAtEnd; | 94 bool m_hasInterchangeNewlineAtEnd; |
95 }; | 95 }; |
96 | 96 |
97 static bool isInterchangeNewlineNode(const Node *node) | 97 static bool isInterchangeNewlineNode(const Node *node) |
98 { | 98 { |
99 DEFINE_STATIC_LOCAL(String, interchangeNewlineClassString, (AppleInterchange
Newline)); | 99 DEFINE_STATIC_LOCAL(String, interchangeNewlineClassString, (AppleInterchange
Newline)); |
100 return node && node->hasTagName(brTag) && toElement(node)->getAttribute(clas
sAttr) == interchangeNewlineClassString; | 100 return isHTMLBRElement(node) && toElement(node)->getAttribute(classAttr) ==
interchangeNewlineClassString; |
101 } | 101 } |
102 | 102 |
103 static bool isInterchangeConvertedSpaceSpan(const Node *node) | 103 static bool isInterchangeConvertedSpaceSpan(const Node *node) |
104 { | 104 { |
105 DEFINE_STATIC_LOCAL(String, convertedSpaceSpanClassString, (AppleConvertedSp
ace)); | 105 DEFINE_STATIC_LOCAL(String, convertedSpaceSpanClassString, (AppleConvertedSp
ace)); |
106 return node->isHTMLElement() && toHTMLElement(node)->getAttribute(classAttr)
== convertedSpaceSpanClassString; | 106 return node->isHTMLElement() && toHTMLElement(node)->getAttribute(classAttr)
== convertedSpaceSpanClassString; |
107 } | 107 } |
108 | 108 |
109 static Position positionAvoidingPrecedingNodes(Position pos) | 109 static Position positionAvoidingPrecedingNodes(Position pos) |
110 { | 110 { |
(...skipping 281 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
392 // For a successful merge, we still need to make sure that the inserted cont
ent starts with the beginning of a paragraph. | 392 // For a successful merge, we still need to make sure that the inserted cont
ent starts with the beginning of a paragraph. |
393 // And we should only merge here if the selection start was inside a mail bl
ockquote. This prevents against removing a | 393 // And we should only merge here if the selection start was inside a mail bl
ockquote. This prevents against removing a |
394 // blockquote from newly pasted quoted content that was pasted into an unquo
ted position. If that unquoted position happens | 394 // blockquote from newly pasted quoted content that was pasted into an unquo
ted position. If that unquoted position happens |
395 // to be right after another blockquote, we don't want to merge and risk str
ipping a valid block (and newline) from the pasted content. | 395 // to be right after another blockquote, we don't want to merge and risk str
ipping a valid block (and newline) from the pasted content. |
396 if (isStartOfParagraph(startOfInsertedContent) && selectionStartWasInsideMai
lBlockquote && hasMatchingQuoteLevel(prev, positionAtEndOfInsertedContent())) | 396 if (isStartOfParagraph(startOfInsertedContent) && selectionStartWasInsideMai
lBlockquote && hasMatchingQuoteLevel(prev, positionAtEndOfInsertedContent())) |
397 return true; | 397 return true; |
398 | 398 |
399 return !selectionStartWasStartOfParagraph | 399 return !selectionStartWasStartOfParagraph |
400 && !fragmentHasInterchangeNewlineAtStart | 400 && !fragmentHasInterchangeNewlineAtStart |
401 && isStartOfParagraph(startOfInsertedContent) | 401 && isStartOfParagraph(startOfInsertedContent) |
402 && !startOfInsertedContent.deepEquivalent().deprecatedNode()->hasTagName
(brTag) | 402 && !isHTMLBRElement(*startOfInsertedContent.deepEquivalent().deprecatedN
ode()) |
403 && shouldMerge(startOfInsertedContent, prev); | 403 && shouldMerge(startOfInsertedContent, prev); |
404 } | 404 } |
405 | 405 |
406 bool ReplaceSelectionCommand::shouldMergeEnd(bool selectionEndWasEndOfParagraph) | 406 bool ReplaceSelectionCommand::shouldMergeEnd(bool selectionEndWasEndOfParagraph) |
407 { | 407 { |
408 VisiblePosition endOfInsertedContent(positionAtEndOfInsertedContent()); | 408 VisiblePosition endOfInsertedContent(positionAtEndOfInsertedContent()); |
409 VisiblePosition next = endOfInsertedContent.next(CannotCrossEditingBoundary)
; | 409 VisiblePosition next = endOfInsertedContent.next(CannotCrossEditingBoundary)
; |
410 if (next.isNull()) | 410 if (next.isNull()) |
411 return false; | 411 return false; |
412 | 412 |
413 return !selectionEndWasEndOfParagraph | 413 return !selectionEndWasEndOfParagraph |
414 && isEndOfParagraph(endOfInsertedContent) | 414 && isEndOfParagraph(endOfInsertedContent) |
415 && !endOfInsertedContent.deepEquivalent().deprecatedNode()->hasTagName(b
rTag) | 415 && !isHTMLBRElement(*endOfInsertedContent.deepEquivalent().deprecatedNod
e()) |
416 && shouldMerge(endOfInsertedContent, next); | 416 && shouldMerge(endOfInsertedContent, next); |
417 } | 417 } |
418 | 418 |
419 static bool isMailPasteAsQuotationNode(const Node* node) | 419 static bool isMailPasteAsQuotationNode(const Node* node) |
420 { | 420 { |
421 return node && node->hasTagName(blockquoteTag) && node->isElementNode() && t
oElement(node)->getAttribute(classAttr) == ApplePasteAsQuotation; | 421 return node && node->hasTagName(blockquoteTag) && toElement(node)->getAttrib
ute(classAttr) == ApplePasteAsQuotation; |
422 } | 422 } |
423 | 423 |
424 static bool isHeaderElement(const Node* a) | 424 static bool isHeaderElement(const Node* a) |
425 { | 425 { |
426 if (!a) | 426 if (!a) |
427 return false; | 427 return false; |
428 | 428 |
429 return a->hasTagName(h1Tag) | 429 return a->hasTagName(h1Tag) |
430 || a->hasTagName(h2Tag) | 430 || a->hasTagName(h2Tag) |
431 || a->hasTagName(h3Tag) | 431 || a->hasTagName(h3Tag) |
(...skipping 259 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
691 | 691 |
692 VisiblePosition ReplaceSelectionCommand::positionAtStartOfInsertedContent() cons
t | 692 VisiblePosition ReplaceSelectionCommand::positionAtStartOfInsertedContent() cons
t |
693 { | 693 { |
694 return VisiblePosition(m_startOfInsertedContent); | 694 return VisiblePosition(m_startOfInsertedContent); |
695 } | 695 } |
696 | 696 |
697 static void removeHeadContents(ReplacementFragment& fragment) | 697 static void removeHeadContents(ReplacementFragment& fragment) |
698 { | 698 { |
699 Node* next = 0; | 699 Node* next = 0; |
700 for (Node* node = fragment.firstChild(); node; node = next) { | 700 for (Node* node = fragment.firstChild(); node; node = next) { |
701 if (node->hasTagName(baseTag) | 701 if (isHTMLBaseElement(*node) |
702 || node->hasTagName(linkTag) | 702 || isHTMLLinkElement(*node) |
703 || node->hasTagName(metaTag) | 703 || isHTMLMetaElement(*node) |
704 || node->hasTagName(styleTag) | 704 || isHTMLStyleElement(*node) |
705 || node->hasTagName(titleTag)) { | 705 || isHTMLTitleElement(*node)) { |
706 next = NodeTraversal::nextSkippingChildren(*node); | 706 next = NodeTraversal::nextSkippingChildren(*node); |
707 fragment.removeNode(node); | 707 fragment.removeNode(node); |
708 } else { | 708 } else { |
709 next = NodeTraversal::next(*node); | 709 next = NodeTraversal::next(*node); |
710 } | 710 } |
711 } | 711 } |
712 } | 712 } |
713 | 713 |
714 // Remove style spans before insertion if they are unnecessary. It's faster bec
ause we'll | 714 // Remove style spans before insertion if they are unnecessary. It's faster bec
ause we'll |
715 // avoid doing a layout. | 715 // avoid doing a layout. |
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
833 m_endOfInsertedContent = endingSelection().visibleEnd().deepEquivalent(
); | 833 m_endOfInsertedContent = endingSelection().visibleEnd().deepEquivalent(
); |
834 // If we merged text nodes, m_endOfInsertedContent could be null. If thi
s is the case, we use m_startOfInsertedContent. | 834 // If we merged text nodes, m_endOfInsertedContent could be null. If thi
s is the case, we use m_startOfInsertedContent. |
835 if (m_endOfInsertedContent.isNull()) | 835 if (m_endOfInsertedContent.isNull()) |
836 m_endOfInsertedContent = m_startOfInsertedContent; | 836 m_endOfInsertedContent = m_startOfInsertedContent; |
837 } | 837 } |
838 } | 838 } |
839 | 839 |
840 static Node* enclosingInline(Node* node) | 840 static Node* enclosingInline(Node* node) |
841 { | 841 { |
842 while (ContainerNode* parent = node->parentNode()) { | 842 while (ContainerNode* parent = node->parentNode()) { |
843 if (parent->isBlockFlowElement() || parent->hasTagName(bodyTag)) | 843 if (parent->isBlockFlowElement() || isHTMLBodyElement(*parent)) |
844 return node; | 844 return node; |
845 // Stop if any previous sibling is a block. | 845 // Stop if any previous sibling is a block. |
846 for (Node* sibling = node->previousSibling(); sibling; sibling = sibling
->previousSibling()) { | 846 for (Node* sibling = node->previousSibling(); sibling; sibling = sibling
->previousSibling()) { |
847 if (sibling->isBlockFlowElement()) | 847 if (sibling->isBlockFlowElement()) |
848 return node; | 848 return node; |
849 } | 849 } |
850 node = parent; | 850 node = parent; |
851 } | 851 } |
852 return node; | 852 return node; |
853 } | 853 } |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
964 insertionPos = endingSelection().start(); | 964 insertionPos = endingSelection().start(); |
965 } | 965 } |
966 | 966 |
967 // We don't want any of the pasted content to end up nested in a Mail blockq
uote, so first break | 967 // We don't want any of the pasted content to end up nested in a Mail blockq
uote, so first break |
968 // out of any surrounding Mail blockquotes. Unless we're inserting in a tabl
e, in which case | 968 // out of any surrounding Mail blockquotes. Unless we're inserting in a tabl
e, in which case |
969 // breaking the blockquote will prevent the content from actually being inse
rted in the table. | 969 // breaking the blockquote will prevent the content from actually being inse
rted in the table. |
970 if (startIsInsideMailBlockquote && m_preventNesting && !(enclosingNodeOfType
(insertionPos, &isTableStructureNode))) { | 970 if (startIsInsideMailBlockquote && m_preventNesting && !(enclosingNodeOfType
(insertionPos, &isTableStructureNode))) { |
971 applyCommandToComposite(BreakBlockquoteCommand::create(document())); | 971 applyCommandToComposite(BreakBlockquoteCommand::create(document())); |
972 // This will leave a br between the split. | 972 // This will leave a br between the split. |
973 Node* br = endingSelection().start().deprecatedNode(); | 973 Node* br = endingSelection().start().deprecatedNode(); |
974 ASSERT(br->hasTagName(brTag)); | 974 ASSERT(isHTMLBRElement(br)); |
975 // Insert content between the two blockquotes, but remove the br (since
it was just a placeholder). | 975 // Insert content between the two blockquotes, but remove the br (since
it was just a placeholder). |
976 insertionPos = positionInParentBeforeNode(*br); | 976 insertionPos = positionInParentBeforeNode(*br); |
977 removeNode(br); | 977 removeNode(br); |
978 } | 978 } |
979 | 979 |
980 // Inserting content could cause whitespace to collapse, e.g. inserting <div
>foo</div> into hello^ world. | 980 // Inserting content could cause whitespace to collapse, e.g. inserting <div
>foo</div> into hello^ world. |
981 prepareWhitespaceAtPositionForSplit(insertionPos); | 981 prepareWhitespaceAtPositionForSplit(insertionPos); |
982 | 982 |
983 // If the downstream node has been removed there's no point in continuing. | 983 // If the downstream node has been removed there's no point in continuing. |
984 if (!insertionPos.downstream().deprecatedNode()) | 984 if (!insertionPos.downstream().deprecatedNode()) |
985 return; | 985 return; |
986 | 986 |
987 // NOTE: This would be an incorrect usage of downstream() if downstream() we
re changed to mean the last position after | 987 // NOTE: This would be an incorrect usage of downstream() if downstream() we
re changed to mean the last position after |
988 // 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 | 988 // 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 |
989 // away, there are positions after the br which map to the same visible posi
tion as [br, 0]). | 989 // away, there are positions after the br which map to the same visible posi
tion as [br, 0]). |
990 Node* endBR = insertionPos.downstream().deprecatedNode()->hasTagName(brTag)
? insertionPos.downstream().deprecatedNode() : 0; | 990 Node* endBR = isHTMLBRElement(*insertionPos.downstream().deprecatedNode()) ?
insertionPos.downstream().deprecatedNode() : 0; |
991 VisiblePosition originalVisPosBeforeEndBR; | 991 VisiblePosition originalVisPosBeforeEndBR; |
992 if (endBR) | 992 if (endBR) |
993 originalVisPosBeforeEndBR = VisiblePosition(positionBeforeNode(endBR), D
OWNSTREAM).previous(); | 993 originalVisPosBeforeEndBR = VisiblePosition(positionBeforeNode(endBR), D
OWNSTREAM).previous(); |
994 | 994 |
995 RefPtr<Node> insertionBlock = enclosingBlock(insertionPos.deprecatedNode()); | 995 RefPtr<Node> insertionBlock = enclosingBlock(insertionPos.deprecatedNode()); |
996 | 996 |
997 // Adjust insertionPos to prevent nesting. | 997 // Adjust insertionPos to prevent nesting. |
998 // If the start was in a Mail blockquote, we will have already handled adjus
ting insertionPos above. | 998 // If the start was in a Mail blockquote, we will have already handled adjus
ting insertionPos above. |
999 if (m_preventNesting && insertionBlock && !isTableCell(insertionBlock.get())
&& !startIsInsideMailBlockquote) { | 999 if (m_preventNesting && insertionBlock && !isTableCell(insertionBlock.get())
&& !startIsInsideMailBlockquote) { |
1000 ASSERT(insertionBlock != currentRoot); | 1000 ASSERT(insertionBlock != currentRoot); |
(...skipping 246 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1247 // A br that was originally acting as a line break should still be acting as
a line break, not as a placeholder. | 1247 // A br that was originally acting as a line break should still be acting as
a line break, not as a placeholder. |
1248 return isStartOfParagraph(visiblePos) && isEndOfParagraph(visiblePos); | 1248 return isStartOfParagraph(visiblePos) && isEndOfParagraph(visiblePos); |
1249 } | 1249 } |
1250 | 1250 |
1251 bool ReplaceSelectionCommand::shouldPerformSmartReplace() const | 1251 bool ReplaceSelectionCommand::shouldPerformSmartReplace() const |
1252 { | 1252 { |
1253 if (!m_smartReplace) | 1253 if (!m_smartReplace) |
1254 return false; | 1254 return false; |
1255 | 1255 |
1256 Element* textControl = enclosingTextFormControl(positionAtStartOfInsertedCon
tent().deepEquivalent()); | 1256 Element* textControl = enclosingTextFormControl(positionAtStartOfInsertedCon
tent().deepEquivalent()); |
1257 if (textControl && textControl->hasTagName(inputTag) && toHTMLInputElement(t
extControl)->isPasswordField()) | 1257 if (isHTMLInputElement(textControl) && toHTMLInputElement(textControl)->isPa
sswordField()) |
1258 return false; // Disable smart replace for password fields. | 1258 return false; // Disable smart replace for password fields. |
1259 | 1259 |
1260 return true; | 1260 return true; |
1261 } | 1261 } |
1262 | 1262 |
1263 static bool isCharacterSmartReplaceExemptConsideringNonBreakingSpace(UChar32 cha
racter, bool previousCharacter) | 1263 static bool isCharacterSmartReplaceExemptConsideringNonBreakingSpace(UChar32 cha
racter, bool previousCharacter) |
1264 { | 1264 { |
1265 return isCharacterSmartReplaceExempt(character == noBreakSpace ? ' ' : chara
cter, previousCharacter); | 1265 return isCharacterSmartReplaceExempt(character == noBreakSpace ? ' ' : chara
cter, previousCharacter); |
1266 } | 1266 } |
1267 | 1267 |
(...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1482 | 1482 |
1483 RefPtr<Node> nodeAfterInsertionPos = endingSelection().end().downstream().an
chorNode(); | 1483 RefPtr<Node> nodeAfterInsertionPos = endingSelection().end().downstream().an
chorNode(); |
1484 Text* textNode = toText(fragment.firstChild()); | 1484 Text* textNode = toText(fragment.firstChild()); |
1485 // Our fragment creation code handles tabs, spaces, and newlines, so we don'
t have to worry about those here. | 1485 // Our fragment creation code handles tabs, spaces, and newlines, so we don'
t have to worry about those here. |
1486 | 1486 |
1487 Position start = endingSelection().start(); | 1487 Position start = endingSelection().start(); |
1488 Position end = replaceSelectedTextInNode(textNode->data()); | 1488 Position end = replaceSelectedTextInNode(textNode->data()); |
1489 if (end.isNull()) | 1489 if (end.isNull()) |
1490 return false; | 1490 return false; |
1491 | 1491 |
1492 if (nodeAfterInsertionPos && nodeAfterInsertionPos->parentNode() && nodeAfte
rInsertionPos->hasTagName(brTag) | 1492 if (nodeAfterInsertionPos && nodeAfterInsertionPos->parentNode() && isHTMLBR
Element(*nodeAfterInsertionPos) |
1493 && shouldRemoveEndBR(nodeAfterInsertionPos.get(), VisiblePosition(positi
onBeforeNode(nodeAfterInsertionPos.get())))) | 1493 && shouldRemoveEndBR(nodeAfterInsertionPos.get(), VisiblePosition(positi
onBeforeNode(nodeAfterInsertionPos.get())))) |
1494 removeNodeAndPruneAncestors(nodeAfterInsertionPos.get()); | 1494 removeNodeAndPruneAncestors(nodeAfterInsertionPos.get()); |
1495 | 1495 |
1496 VisibleSelection selectionAfterReplace(m_selectReplacement ? start : end, en
d); | 1496 VisibleSelection selectionAfterReplace(m_selectReplacement ? start : end, en
d); |
1497 | 1497 |
1498 setEndingSelection(selectionAfterReplace); | 1498 setEndingSelection(selectionAfterReplace); |
1499 | 1499 |
1500 return true; | 1500 return true; |
1501 } | 1501 } |
1502 | 1502 |
1503 } // namespace WebCore | 1503 } // namespace WebCore |
OLD | NEW |