Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(109)

Side by Side Diff: Source/WebCore/editing/ReplaceSelectionCommand.cpp

Issue 13954003: Remove mail blockquote special case handling. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Created 7 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 360 matching lines...) Expand 10 before | Expand all | Expand 10 after
371 , m_matchStyle(options & MatchStyle) 371 , m_matchStyle(options & MatchStyle)
372 , m_documentFragment(fragment) 372 , m_documentFragment(fragment)
373 , m_preventNesting(options & PreventNesting) 373 , m_preventNesting(options & PreventNesting)
374 , m_movingParagraph(options & MovingParagraph) 374 , m_movingParagraph(options & MovingParagraph)
375 , m_editAction(editAction) 375 , m_editAction(editAction)
376 , m_sanitizeFragment(options & SanitizeFragment) 376 , m_sanitizeFragment(options & SanitizeFragment)
377 , m_shouldMergeEnd(false) 377 , m_shouldMergeEnd(false)
378 { 378 {
379 } 379 }
380 380
381 static bool hasMatchingQuoteLevel(VisiblePosition endOfExistingContent, VisibleP osition endOfInsertedContent) 381 bool ReplaceSelectionCommand::shouldMergeStart(bool selectionStartWasStartOfPara graph, bool fragmentHasInterchangeNewlineAtStart)
382 {
383 Position existing = endOfExistingContent.deepEquivalent();
384 Position inserted = endOfInsertedContent.deepEquivalent();
385 bool isInsideMailBlockquote = enclosingNodeOfType(inserted, isMailBlockquote , CanCrossEditingBoundary);
386 return isInsideMailBlockquote && (numEnclosingMailBlockquotes(existing) == n umEnclosingMailBlockquotes(inserted));
387 }
388
389 bool ReplaceSelectionCommand::shouldMergeStart(bool selectionStartWasStartOfPara graph, bool fragmentHasInterchangeNewlineAtStart, bool selectionStartWasInsideMa ilBlockquote)
390 { 382 {
391 if (m_movingParagraph) 383 if (m_movingParagraph)
392 return false; 384 return false;
393 385
394 VisiblePosition startOfInsertedContent(positionAtStartOfInsertedContent()); 386 VisiblePosition startOfInsertedContent(positionAtStartOfInsertedContent());
395 VisiblePosition prev = startOfInsertedContent.previous(CannotCrossEditingBou ndary); 387 VisiblePosition prev = startOfInsertedContent.previous(CannotCrossEditingBou ndary);
396 if (prev.isNull()) 388 if (prev.isNull())
397 return false; 389 return false;
398
399 // When we have matching quote levels, its ok to merge more frequently.
400 // For a successful merge, we still need to make sure that the inserted cont ent starts with the beginning of a paragraph.
401 // And we should only merge here if the selection start was inside a mail bl ockquote. This prevents against removing a
402 // blockquote from newly pasted quoted content that was pasted into an unquo ted position. If that unquoted position happens
403 // 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.
404 if (isStartOfParagraph(startOfInsertedContent) && selectionStartWasInsideMai lBlockquote && hasMatchingQuoteLevel(prev, positionAtEndOfInsertedContent()))
405 return true;
406 390
407 return !selectionStartWasStartOfParagraph 391 return !selectionStartWasStartOfParagraph
408 && !fragmentHasInterchangeNewlineAtStart 392 && !fragmentHasInterchangeNewlineAtStart
409 && isStartOfParagraph(startOfInsertedContent) 393 && isStartOfParagraph(startOfInsertedContent)
410 && !startOfInsertedContent.deepEquivalent().deprecatedNode()->hasTagName (brTag) 394 && !startOfInsertedContent.deepEquivalent().deprecatedNode()->hasTagName (brTag)
411 && shouldMerge(startOfInsertedContent, prev); 395 && shouldMerge(startOfInsertedContent, prev);
412 } 396 }
413 397
414 bool ReplaceSelectionCommand::shouldMergeEnd(bool selectionEndWasEndOfParagraph) 398 bool ReplaceSelectionCommand::shouldMergeEnd(bool selectionEndWasEndOfParagraph)
415 { 399 {
416 VisiblePosition endOfInsertedContent(positionAtEndOfInsertedContent()); 400 VisiblePosition endOfInsertedContent(positionAtEndOfInsertedContent());
417 VisiblePosition next = endOfInsertedContent.next(CannotCrossEditingBoundary) ; 401 VisiblePosition next = endOfInsertedContent.next(CannotCrossEditingBoundary) ;
418 if (next.isNull()) 402 if (next.isNull())
419 return false; 403 return false;
420 404
421 return !selectionEndWasEndOfParagraph 405 return !selectionEndWasEndOfParagraph
422 && isEndOfParagraph(endOfInsertedContent) 406 && isEndOfParagraph(endOfInsertedContent)
423 && !endOfInsertedContent.deepEquivalent().deprecatedNode()->hasTagName(b rTag) 407 && !endOfInsertedContent.deepEquivalent().deprecatedNode()->hasTagName(b rTag)
424 && shouldMerge(endOfInsertedContent, next); 408 && shouldMerge(endOfInsertedContent, next);
425 } 409 }
426 410
427 static bool isMailPasteAsQuotationNode(const Node* node)
428 {
429 return node && node->hasTagName(blockquoteTag) && node->isElementNode() && t oElement(node)->getAttribute(classAttr) == ApplePasteAsQuotation;
430 }
431
432 static bool isHeaderElement(const Node* a) 411 static bool isHeaderElement(const Node* a)
433 { 412 {
434 if (!a) 413 if (!a)
435 return false; 414 return false;
436 415
437 return a->hasTagName(h1Tag) 416 return a->hasTagName(h1Tag)
438 || a->hasTagName(h2Tag) 417 || a->hasTagName(h2Tag)
439 || a->hasTagName(h3Tag) 418 || a->hasTagName(h3Tag)
440 || a->hasTagName(h4Tag) 419 || a->hasTagName(h4Tag)
441 || a->hasTagName(h5Tag) 420 || a->hasTagName(h5Tag)
442 || a->hasTagName(h6Tag); 421 || a->hasTagName(h6Tag);
443 } 422 }
444 423
445 static bool haveSameTagName(Node* a, Node* b) 424 static bool haveSameTagName(Node* a, Node* b)
446 { 425 {
447 return a && b && a->isElementNode() && b->isElementNode() && toElement(a)->t agName() == toElement(b)->tagName(); 426 return a && b && a->isElementNode() && b->isElementNode() && toElement(a)->t agName() == toElement(b)->tagName();
448 } 427 }
449 428
450 bool ReplaceSelectionCommand::shouldMerge(const VisiblePosition& source, const V isiblePosition& destination) 429 bool ReplaceSelectionCommand::shouldMerge(const VisiblePosition& source, const V isiblePosition& destination)
451 { 430 {
452 if (source.isNull() || destination.isNull()) 431 if (source.isNull() || destination.isNull())
453 return false; 432 return false;
454 433
455 Node* sourceNode = source.deepEquivalent().deprecatedNode(); 434 Node* sourceNode = source.deepEquivalent().deprecatedNode();
456 Node* destinationNode = destination.deepEquivalent().deprecatedNode(); 435 Node* destinationNode = destination.deepEquivalent().deprecatedNode();
457 Node* sourceBlock = enclosingBlock(sourceNode); 436 Node* sourceBlock = enclosingBlock(sourceNode);
458 Node* destinationBlock = enclosingBlock(destinationNode); 437 Node* destinationBlock = enclosingBlock(destinationNode);
459 return !enclosingNodeOfType(source.deepEquivalent(), &isMailPasteAsQuotation Node) && 438 return sourceBlock && !sourceBlock->hasTagName(blockquoteTag) &&
460 sourceBlock && (!sourceBlock->hasTagName(blockquoteTag) || isMailBloc kquote(sourceBlock)) &&
461 enclosingListChild(sourceBlock) == enclosingListChild(destinationNode ) && 439 enclosingListChild(sourceBlock) == enclosingListChild(destinationNode ) &&
462 enclosingTableCell(source.deepEquivalent()) == enclosingTableCell(des tination.deepEquivalent()) && 440 enclosingTableCell(source.deepEquivalent()) == enclosingTableCell(des tination.deepEquivalent()) &&
463 (!isHeaderElement(sourceBlock) || haveSameTagName(sourceBlock, destin ationBlock)) && 441 (!isHeaderElement(sourceBlock) || haveSameTagName(sourceBlock, destin ationBlock)) &&
464 // Don't merge to or from a position before or after a block because it would 442 // Don't merge to or from a position before or after a block because it would
465 // be a no-op and cause infinite recursion. 443 // be a no-op and cause infinite recursion.
466 !isBlock(sourceNode) && !isBlock(destinationNode); 444 !isBlock(sourceNode) && !isBlock(destinationNode);
467 } 445 }
468 446
469 // Style rules that match just inserted elements could change their appearance, like 447 // Style rules that match just inserted elements could change their appearance, like
470 // a div inserted into a document with div { display:inline; }. 448 // a div inserted into a document with div { display:inline; }.
(...skipping 25 matching lines...) Expand all
496 } else if (newInlineStyle->extractConflictingImplicitStyleOfAttr ibutes(htmlElement, EditingStyle::PreserveWritingDirection, 0, attributes, 474 } else if (newInlineStyle->extractConflictingImplicitStyleOfAttr ibutes(htmlElement, EditingStyle::PreserveWritingDirection, 0, attributes,
497 EditingStyle::DoNotExtractMatchingStyle)) { 475 EditingStyle::DoNotExtractMatchingStyle)) {
498 // e.g. <font size="3" style="font-size: 20px;"> is converte d to <font style="font-size: 20px;"> 476 // e.g. <font size="3" style="font-size: 20px;"> is converte d to <font style="font-size: 20px;">
499 for (size_t i = 0; i < attributes.size(); i++) 477 for (size_t i = 0; i < attributes.size(); i++)
500 removeNodeAttribute(element, attributes[i]); 478 removeNodeAttribute(element, attributes[i]);
501 } 479 }
502 } 480 }
503 481
504 ContainerNode* context = element->parentNode(); 482 ContainerNode* context = element->parentNode();
505 483
506 // If Mail wraps the fragment with a Paste as Quotation blockquote, or if you're pasting into a quoted region,
507 // styles from blockquoteNode are allowed to override those from the source document, see <rdar://problem/4930986> and <rdar://problem/5089327>.
508 Node* blockquoteNode = isMailPasteAsQuotationNode(context) ? context : enclosingNodeOfType(firstPositionInNode(context), isMailBlockquote, CanCrossE ditingBoundary);
509 if (blockquoteNode)
510 newInlineStyle->removeStyleFromRulesAndContext(element, document ()->documentElement());
511
512 newInlineStyle->removeStyleFromRulesAndContext(element, context); 484 newInlineStyle->removeStyleFromRulesAndContext(element, context);
513 } 485 }
514 486
515 if (!inlineStyle || newInlineStyle->isEmpty()) { 487 if (!inlineStyle || newInlineStyle->isEmpty()) {
516 if (isStyleSpanOrSpanWithOnlyStyleAttribute(element) || isEmptyFontT ag(element, AllowNonEmptyStyleAttribute)) { 488 if (isStyleSpanOrSpanWithOnlyStyleAttribute(element) || isEmptyFontT ag(element, AllowNonEmptyStyleAttribute)) {
517 insertedNodes.willRemoveNodePreservingChildren(element); 489 insertedNodes.willRemoveNodePreservingChildren(element);
518 removeNodePreservingChildren(element); 490 removeNodePreservingChildren(element);
519 continue; 491 continue;
520 } 492 }
521 removeNodeAttribute(element, styleAttr); 493 removeNodeAttribute(element, styleAttr);
(...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after
713 next = NodeTraversal::next(node); 685 next = NodeTraversal::next(node);
714 } 686 }
715 } 687 }
716 688
717 // Remove style spans before insertion if they are unnecessary. It's faster bec ause we'll 689 // Remove style spans before insertion if they are unnecessary. It's faster bec ause we'll
718 // avoid doing a layout. 690 // avoid doing a layout.
719 static bool handleStyleSpansBeforeInsertion(ReplacementFragment& fragment, const Position& insertionPos) 691 static bool handleStyleSpansBeforeInsertion(ReplacementFragment& fragment, const Position& insertionPos)
720 { 692 {
721 Node* topNode = fragment.firstChild(); 693 Node* topNode = fragment.firstChild();
722 694
723 // Handling the case where we are doing Paste as Quotation or pasting into q uoted content is more complicated (see handleStyleSpans)
724 // and doesn't receive the optimization.
725 if (isMailPasteAsQuotationNode(topNode) || enclosingNodeOfType(firstPosition InOrBeforeNode(topNode), isMailBlockquote, CanCrossEditingBoundary))
726 return false;
727
728 // Either there are no style spans in the fragment or a WebKit client has ad ded content to the fragment 695 // Either there are no style spans in the fragment or a WebKit client has ad ded content to the fragment
729 // before inserting it. Look for and handle style spans after insertion. 696 // before inserting it. Look for and handle style spans after insertion.
730 if (!isLegacyAppleStyleSpan(topNode)) 697 if (!isLegacyAppleStyleSpan(topNode))
731 return false; 698 return false;
732 699
733 Node* wrappingStyleSpan = topNode; 700 Node* wrappingStyleSpan = topNode;
734 RefPtr<EditingStyle> styleAtInsertionPos = EditingStyle::create(insertionPos .parentAnchoredEquivalent()); 701 RefPtr<EditingStyle> styleAtInsertionPos = EditingStyle::create(insertionPos .parentAnchoredEquivalent());
735 String styleText = styleAtInsertionPos->style()->asText(); 702 String styleText = styleAtInsertionPos->style()->asText();
736 703
737 // FIXME: This string comparison is a naive way of comparing two styles. 704 // FIXME: This string comparison is a naive way of comparing two styles.
(...skipping 27 matching lines...) Expand all
765 } 732 }
766 733
767 // There might not be any style spans if we're pasting from another applicat ion or if 734 // There might not be any style spans if we're pasting from another applicat ion or if
768 // we are here because of a document.execCommand("InsertHTML", ...) call. 735 // we are here because of a document.execCommand("InsertHTML", ...) call.
769 if (!wrappingStyleSpan) 736 if (!wrappingStyleSpan)
770 return; 737 return;
771 738
772 RefPtr<EditingStyle> style = EditingStyle::create(wrappingStyleSpan->inlineS tyle()); 739 RefPtr<EditingStyle> style = EditingStyle::create(wrappingStyleSpan->inlineS tyle());
773 ContainerNode* context = wrappingStyleSpan->parentNode(); 740 ContainerNode* context = wrappingStyleSpan->parentNode();
774 741
775 // If Mail wraps the fragment with a Paste as Quotation blockquote, or if yo u're pasting into a quoted region,
776 // styles from blockquoteNode are allowed to override those from the source document, see <rdar://problem/4930986> and <rdar://problem/5089327>.
777 Node* blockquoteNode = isMailPasteAsQuotationNode(context) ? context : enclo singNodeOfType(firstPositionInNode(context), isMailBlockquote, CanCrossEditingBo undary);
778 if (blockquoteNode)
779 context = document()->documentElement();
780
781 // This operation requires that only editing styles to be removed from sourc eDocumentStyle. 742 // This operation requires that only editing styles to be removed from sourc eDocumentStyle.
782 style->prepareToApplyAt(firstPositionInNode(context)); 743 style->prepareToApplyAt(firstPositionInNode(context));
783 744
784 // Remove block properties in the span's style. This prevents properties tha t probably have no effect 745 // Remove block properties in the span's style. This prevents properties tha t probably have no effect
785 // currently from affecting blocks later if the style is cloned for a new bl ock element during a future 746 // currently from affecting blocks later if the style is cloned for a new bl ock element during a future
786 // editing operation. 747 // editing operation.
787 // FIXME: They *can* have an effect currently if blocks beneath the style sp an aren't individually marked 748 // FIXME: They *can* have an effect currently if blocks beneath the style sp an aren't individually marked
788 // with block styles by the editing engine used to style them. WebKit doesn 't do this, but others might. 749 // with block styles by the editing engine used to style them. WebKit doesn 't do this, but others might.
789 style->removeBlockProperties(); 750 style->removeBlockProperties();
790 751
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after
861 return false; 822 return false;
862 823
863 if (!node->isHTMLElement()) 824 if (!node->isHTMLElement())
864 return false; 825 return false;
865 826
866 // We can skip over elements whose class attribute is 827 // We can skip over elements whose class attribute is
867 // one of our internal classes. 828 // one of our internal classes.
868 const HTMLElement* element = static_cast<const HTMLElement*>(node); 829 const HTMLElement* element = static_cast<const HTMLElement*>(node);
869 const AtomicString& classAttributeValue = element->getAttribute(classAttr); 830 const AtomicString& classAttributeValue = element->getAttribute(classAttr);
870 if (classAttributeValue == AppleTabSpanClass 831 if (classAttributeValue == AppleTabSpanClass
871 || classAttributeValue == AppleConvertedSpace 832 || classAttributeValue == AppleConvertedSpace)
872 || classAttributeValue == ApplePasteAsQuotation)
873 return true; 833 return true;
874 834
875 return EditingStyle::elementIsStyledSpanOrHTMLEquivalent(element); 835 return EditingStyle::elementIsStyledSpanOrHTMLEquivalent(element);
876 } 836 }
877 837
878 inline Node* nodeToSplitToAvoidPastingIntoInlineNodesWithStyle(const Position& i nsertionPos) 838 inline Node* nodeToSplitToAvoidPastingIntoInlineNodesWithStyle(const Position& i nsertionPos)
879 { 839 {
880 Node* containgBlock = enclosingBlock(insertionPos.containerNode()); 840 Node* containgBlock = enclosingBlock(insertionPos.containerNode());
881 return highestEnclosingNodeOfType(insertionPos, isInlineNodeWithStyle, Canno tCrossEditingBoundary, containgBlock); 841 return highestEnclosingNodeOfType(insertionPos, isInlineNodeWithStyle, Canno tCrossEditingBoundary, containgBlock);
882 } 842 }
(...skipping 25 matching lines...) Expand all
908 868
909 VisiblePosition visibleStart = selection.visibleStart(); 869 VisiblePosition visibleStart = selection.visibleStart();
910 VisiblePosition visibleEnd = selection.visibleEnd(); 870 VisiblePosition visibleEnd = selection.visibleEnd();
911 871
912 bool selectionEndWasEndOfParagraph = isEndOfParagraph(visibleEnd); 872 bool selectionEndWasEndOfParagraph = isEndOfParagraph(visibleEnd);
913 bool selectionStartWasStartOfParagraph = isStartOfParagraph(visibleStart); 873 bool selectionStartWasStartOfParagraph = isStartOfParagraph(visibleStart);
914 874
915 Node* startBlock = enclosingBlock(visibleStart.deepEquivalent().deprecatedNo de()); 875 Node* startBlock = enclosingBlock(visibleStart.deepEquivalent().deprecatedNo de());
916 876
917 Position insertionPos = selection.start(); 877 Position insertionPos = selection.start();
918 bool startIsInsideMailBlockquote = enclosingNodeOfType(insertionPos, isMailB lockquote, CanCrossEditingBoundary);
919 bool selectionIsPlainText = !selection.isContentRichlyEditable(); 878 bool selectionIsPlainText = !selection.isContentRichlyEditable();
920 Element* currentRoot = selection.rootEditableElement(); 879 Element* currentRoot = selection.rootEditableElement();
921 880
922 if ((selectionStartWasStartOfParagraph && selectionEndWasEndOfParagraph && ! startIsInsideMailBlockquote) || 881 if ((selectionStartWasStartOfParagraph && selectionEndWasEndOfParagraph) ||
923 startBlock == currentRoot || isListItem(startBlock) || selectionIsPlainT ext) 882 startBlock == currentRoot || isListItem(startBlock) || selectionIsPlainT ext)
924 m_preventNesting = false; 883 m_preventNesting = false;
925 884
926 if (selection.isRange()) { 885 if (selection.isRange()) {
927 // When the end of the selection being pasted into is at the end of a pa ragraph, and that selection 886 // When the end of the selection being pasted into is at the end of a pa ragraph, and that selection
928 // spans multiple blocks, not merging may leave an empty line. 887 // spans multiple blocks, not merging may leave an empty line.
929 // When the start of the selection being pasted into is at the start of a block, not merging 888 // When the start of the selection being pasted into is at the start of a block, not merging
930 // will leave hanging block(s). 889 // will leave hanging block(s).
931 // Merge blocks if the start of the selection was in a Mail blockquote, since we handle 890 bool mergeBlocksAfterDelete = isEndOfParagraph(visibleEnd) || isStartOfB lock(visibleStart);
932 // that case specially to prevent nesting.
933 bool mergeBlocksAfterDelete = startIsInsideMailBlockquote || isEndOfPara graph(visibleEnd) || isStartOfBlock(visibleStart);
934 // FIXME: We should only expand to include fully selected special elemen ts if we are copying a 891 // FIXME: We should only expand to include fully selected special elemen ts if we are copying a
935 // selection and pasting it on top of itself. 892 // selection and pasting it on top of itself.
936 deleteSelection(false, mergeBlocksAfterDelete, true, false); 893 deleteSelection(false, mergeBlocksAfterDelete, true, false);
937 visibleStart = endingSelection().visibleStart(); 894 visibleStart = endingSelection().visibleStart();
938 if (fragment.hasInterchangeNewlineAtStart()) { 895 if (fragment.hasInterchangeNewlineAtStart()) {
939 if (isEndOfParagraph(visibleStart) && !isStartOfParagraph(visibleSta rt)) { 896 if (isEndOfParagraph(visibleStart) && !isStartOfParagraph(visibleSta rt)) {
940 if (!isEndOfEditableOrNonEditableContent(visibleStart)) 897 if (!isEndOfEditableOrNonEditableContent(visibleStart))
941 setEndingSelection(visibleStart.next()); 898 setEndingSelection(visibleStart.next());
942 } else 899 } else
943 insertParagraphSeparator(); 900 insertParagraphSeparator();
944 } 901 }
945 insertionPos = endingSelection().start(); 902 insertionPos = endingSelection().start();
946 } else { 903 } else {
947 ASSERT(selection.isCaret()); 904 ASSERT(selection.isCaret());
948 if (fragment.hasInterchangeNewlineAtStart()) { 905 if (fragment.hasInterchangeNewlineAtStart()) {
949 VisiblePosition next = visibleStart.next(CannotCrossEditingBoundary) ; 906 VisiblePosition next = visibleStart.next(CannotCrossEditingBoundary) ;
950 if (isEndOfParagraph(visibleStart) && !isStartOfParagraph(visibleSta rt) && next.isNotNull()) 907 if (isEndOfParagraph(visibleStart) && !isStartOfParagraph(visibleSta rt) && next.isNotNull())
951 setEndingSelection(next); 908 setEndingSelection(next);
952 else { 909 else {
953 insertParagraphSeparator(); 910 insertParagraphSeparator();
954 visibleStart = endingSelection().visibleStart(); 911 visibleStart = endingSelection().visibleStart();
955 } 912 }
956 } 913 }
957 // We split the current paragraph in two to avoid nesting the blocks fro m the fragment inside the current block. 914 // We split the current paragraph in two to avoid nesting the blocks fro m the fragment inside the current block.
958 // For example paste <div>foo</div><div>bar</div><div>baz</div> into <di v>x^x</div>, where ^ is the caret. 915 // For example paste <div>foo</div><div>bar</div><div>baz</div> into <di v>x^x</div>, where ^ is the caret.
959 // As long as the div styles are the same, visually you'd expect: <div> xbar</div><div>bar</div><div>bazx</div>, 916 // As long as the div styles are the same, visually you'd expect: <div> xbar</div><div>bar</div><div>bazx</div>,
960 // not <div>xbar<div>bar</div><div>bazx</div></div>. 917 // not <div>xbar<div>bar</div><div>bazx</div></div>.
961 // Don't do this if the selection started in a Mail blockquote. 918 if (m_preventNesting && !isEndOfParagraph(visibleStart) && !isStartOfPar agraph(visibleStart)) {
962 if (m_preventNesting && !startIsInsideMailBlockquote && !isEndOfParagrap h(visibleStart) && !isStartOfParagraph(visibleStart)) {
963 insertParagraphSeparator(); 919 insertParagraphSeparator();
964 setEndingSelection(endingSelection().visibleStart().previous()); 920 setEndingSelection(endingSelection().visibleStart().previous());
965 } 921 }
966 insertionPos = endingSelection().start(); 922 insertionPos = endingSelection().start();
967 } 923 }
968 924
969 // We don't want any of the pasted content to end up nested in a Mail blockq uote, so first break
970 // out of any surrounding Mail blockquotes. Unless we're inserting in a tabl e, in which case
971 // breaking the blockquote will prevent the content from actually being inse rted in the table.
972 if (startIsInsideMailBlockquote && m_preventNesting && !(enclosingNodeOfType (insertionPos, &isTableStructureNode))) {
973 applyCommandToComposite(BreakBlockquoteCommand::create(document()));
974 // This will leave a br between the split.
975 Node* br = endingSelection().start().deprecatedNode();
976 ASSERT(br->hasTagName(brTag));
977 // Insert content between the two blockquotes, but remove the br (since it was just a placeholder).
978 insertionPos = positionInParentBeforeNode(br);
979 removeNode(br);
980 }
981
982 // Inserting content could cause whitespace to collapse, e.g. inserting <div >foo</div> into hello^ world. 925 // Inserting content could cause whitespace to collapse, e.g. inserting <div >foo</div> into hello^ world.
983 prepareWhitespaceAtPositionForSplit(insertionPos); 926 prepareWhitespaceAtPositionForSplit(insertionPos);
984 927
985 // If the downstream node has been removed there's no point in continuing. 928 // If the downstream node has been removed there's no point in continuing.
986 if (!insertionPos.downstream().deprecatedNode()) 929 if (!insertionPos.downstream().deprecatedNode())
987 return; 930 return;
988 931
989 // NOTE: This would be an incorrect usage of downstream() if downstream() we re changed to mean the last position after 932 // NOTE: This would be an incorrect usage of downstream() if downstream() we re changed to mean the last position after
990 // 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 933 // 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
991 // away, there are positions after the br which map to the same visible posi tion as [br, 0]). 934 // away, there are positions after the br which map to the same visible posi tion as [br, 0]).
992 Node* endBR = insertionPos.downstream().deprecatedNode()->hasTagName(brTag) ? insertionPos.downstream().deprecatedNode() : 0; 935 Node* endBR = insertionPos.downstream().deprecatedNode()->hasTagName(brTag) ? insertionPos.downstream().deprecatedNode() : 0;
993 VisiblePosition originalVisPosBeforeEndBR; 936 VisiblePosition originalVisPosBeforeEndBR;
994 if (endBR) 937 if (endBR)
995 originalVisPosBeforeEndBR = VisiblePosition(positionBeforeNode(endBR), D OWNSTREAM).previous(); 938 originalVisPosBeforeEndBR = VisiblePosition(positionBeforeNode(endBR), D OWNSTREAM).previous();
996 939
997 startBlock = enclosingBlock(insertionPos.deprecatedNode()); 940 startBlock = enclosingBlock(insertionPos.deprecatedNode());
998 941
999 // Adjust insertionPos to prevent nesting. 942 // Adjust insertionPos to prevent nesting.
1000 // If the start was in a Mail blockquote, we will have already handled adjus ting insertionPos above. 943 if (m_preventNesting && startBlock && !isTableCell(startBlock)) {
1001 if (m_preventNesting && startBlock && !isTableCell(startBlock) && !startIsIn sideMailBlockquote) {
1002 ASSERT(startBlock != currentRoot); 944 ASSERT(startBlock != currentRoot);
1003 VisiblePosition visibleInsertionPos(insertionPos); 945 VisiblePosition visibleInsertionPos(insertionPos);
1004 if (isEndOfBlock(visibleInsertionPos) && !(isStartOfBlock(visibleInserti onPos) && fragment.hasInterchangeNewlineAtEnd())) 946 if (isEndOfBlock(visibleInsertionPos) && !(isStartOfBlock(visibleInserti onPos) && fragment.hasInterchangeNewlineAtEnd()))
1005 insertionPos = positionInParentAfterNode(startBlock); 947 insertionPos = positionInParentAfterNode(startBlock);
1006 else if (isStartOfBlock(visibleInsertionPos)) 948 else if (isStartOfBlock(visibleInsertionPos))
1007 insertionPos = positionInParentBeforeNode(startBlock); 949 insertionPos = positionInParentBeforeNode(startBlock);
1008 } 950 }
1009 951
1010 // Paste at start or end of link goes outside of link. 952 // Paste at start or end of link goes outside of link.
1011 insertionPos = positionAvoidingSpecialElementBoundary(insertionPos); 953 insertionPos = positionAvoidingSpecialElementBoundary(insertionPos);
(...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after
1138 applyCommandToComposite(SimplifyMarkupCommand::create(document(), insert edNodes.firstNodeInserted(), insertedNodes.pastLastLeaf())); 1080 applyCommandToComposite(SimplifyMarkupCommand::create(document(), insert edNodes.firstNodeInserted(), insertedNodes.pastLastLeaf()));
1139 1081
1140 // Setup m_startOfInsertedContent and m_endOfInsertedContent. This should be the last two lines of code that access insertedNodes. 1082 // Setup m_startOfInsertedContent and m_endOfInsertedContent. This should be the last two lines of code that access insertedNodes.
1141 m_startOfInsertedContent = firstPositionInOrBeforeNode(insertedNodes.firstNo deInserted()); 1083 m_startOfInsertedContent = firstPositionInOrBeforeNode(insertedNodes.firstNo deInserted());
1142 m_endOfInsertedContent = lastPositionInOrAfterNode(insertedNodes.lastLeafIns erted()); 1084 m_endOfInsertedContent = lastPositionInOrAfterNode(insertedNodes.lastLeafIns erted());
1143 1085
1144 // Determine whether or not we should merge the end of inserted content with what's after it before we do 1086 // Determine whether or not we should merge the end of inserted content with what's after it before we do
1145 // the start merge so that the start merge doesn't effect our decision. 1087 // the start merge so that the start merge doesn't effect our decision.
1146 m_shouldMergeEnd = shouldMergeEnd(selectionEndWasEndOfParagraph); 1088 m_shouldMergeEnd = shouldMergeEnd(selectionEndWasEndOfParagraph);
1147 1089
1148 if (shouldMergeStart(selectionStartWasStartOfParagraph, fragment.hasIntercha ngeNewlineAtStart(), startIsInsideMailBlockquote)) { 1090 if (shouldMergeStart(selectionStartWasStartOfParagraph, fragment.hasIntercha ngeNewlineAtStart())) {
1149 VisiblePosition startOfParagraphToMove = positionAtStartOfInsertedConten t(); 1091 VisiblePosition startOfParagraphToMove = positionAtStartOfInsertedConten t();
1150 VisiblePosition destination = startOfParagraphToMove.previous(); 1092 VisiblePosition destination = startOfParagraphToMove.previous();
1151 // We need to handle the case where we need to merge the end 1093 // We need to handle the case where we need to merge the end
1152 // but our destination node is inside an inline that is the last in the block. 1094 // but our destination node is inside an inline that is the last in the block.
1153 // We insert a placeholder before the newly inserted content to avoid be ing merged into the inline. 1095 // We insert a placeholder before the newly inserted content to avoid be ing merged into the inline.
1154 Node* destinationNode = destination.deepEquivalent().deprecatedNode(); 1096 Node* destinationNode = destination.deepEquivalent().deprecatedNode();
1155 if (m_shouldMergeEnd && destinationNode != enclosingInline(destinationNo de) && enclosingInline(destinationNode)->nextSibling()) 1097 if (m_shouldMergeEnd && destinationNode != enclosingInline(destinationNo de) && enclosingInline(destinationNode)->nextSibling())
1156 insertNodeBefore(createBreakElement(document()), refNode.get()); 1098 insertNodeBefore(createBreakElement(document()), refNode.get());
1157 1099
1158 // Merging the the first paragraph of inserted content with the content that came 1100 // Merging the the first paragraph of inserted content with the content that came
(...skipping 28 matching lines...) Expand all
1187 if (!isStartOfParagraph(endOfInsertedContent)) { 1129 if (!isStartOfParagraph(endOfInsertedContent)) {
1188 setEndingSelection(endOfInsertedContent); 1130 setEndingSelection(endOfInsertedContent);
1189 Node* enclosingNode = enclosingBlock(endOfInsertedContent.deepEq uivalent().deprecatedNode()); 1131 Node* enclosingNode = enclosingBlock(endOfInsertedContent.deepEq uivalent().deprecatedNode());
1190 if (isListItem(enclosingNode)) { 1132 if (isListItem(enclosingNode)) {
1191 RefPtr<Node> newListItem = createListItemElement(document()) ; 1133 RefPtr<Node> newListItem = createListItemElement(document()) ;
1192 insertNodeAfter(newListItem, enclosingNode); 1134 insertNodeAfter(newListItem, enclosingNode);
1193 setEndingSelection(VisiblePosition(firstPositionInNode(newLi stItem.get()))); 1135 setEndingSelection(VisiblePosition(firstPositionInNode(newLi stItem.get())));
1194 } else { 1136 } else {
1195 // Use a default paragraph element (a plain div) for the emp ty paragraph, using the last paragraph 1137 // Use a default paragraph element (a plain div) for the emp ty paragraph, using the last paragraph
1196 // block's style seems to annoy users. 1138 // block's style seems to annoy users.
1197 insertParagraphSeparator(true, !startIsInsideMailBlockquote && highestEnclosingNodeOfType(endOfInsertedContent.deepEquivalent(), 1139 insertParagraphSeparator(true);
1198 isMailBlockquote, CannotCrossEditingBoundary, insertedNo des.firstNodeInserted()->parentNode()));
1199 } 1140 }
1200 1141
1201 // Select up to the paragraph separator that was added. 1142 // Select up to the paragraph separator that was added.
1202 lastPositionToSelect = endingSelection().visibleStart().deepEqui valent(); 1143 lastPositionToSelect = endingSelection().visibleStart().deepEqui valent();
1203 updateNodesInserted(lastPositionToSelect.deprecatedNode()); 1144 updateNodesInserted(lastPositionToSelect.deprecatedNode());
1204 } 1145 }
1205 } else { 1146 } else {
1206 // Select up to the beginning of the next paragraph. 1147 // Select up to the beginning of the next paragraph.
1207 lastPositionToSelect = next.deepEquivalent().downstream(); 1148 lastPositionToSelect = next.deepEquivalent().downstream();
1208 } 1149 }
1209 1150
1210 } else 1151 } else
1211 mergeEndIfNeeded(); 1152 mergeEndIfNeeded();
1212 1153
1213 if (Node* mailBlockquote = enclosingNodeOfType(positionAtStartOfInsertedCont ent().deepEquivalent(), isMailPasteAsQuotationNode))
1214 removeNodeAttribute(toElement(mailBlockquote), classAttr);
1215
1216 if (shouldPerformSmartReplace()) 1154 if (shouldPerformSmartReplace())
1217 addSpacesForSmartReplace(); 1155 addSpacesForSmartReplace();
1218 1156
1219 // If we are dealing with a fragment created from plain text 1157 // If we are dealing with a fragment created from plain text
1220 // no style matching is necessary. 1158 // no style matching is necessary.
1221 if (plainTextFragment) 1159 if (plainTextFragment)
1222 m_matchStyle = false; 1160 m_matchStyle = false;
1223 1161
1224 completeHTMLReplacement(lastPositionToSelect); 1162 completeHTMLReplacement(lastPositionToSelect);
1225 } 1163 }
(...skipping 263 matching lines...) Expand 10 before | Expand all | Expand 10 after
1489 removeNodeAndPruneAncestors(nodeAfterInsertionPos.get()); 1427 removeNodeAndPruneAncestors(nodeAfterInsertionPos.get());
1490 1428
1491 VisibleSelection selectionAfterReplace(m_selectReplacement ? start : end, en d); 1429 VisibleSelection selectionAfterReplace(m_selectReplacement ? start : end, en d);
1492 1430
1493 setEndingSelection(selectionAfterReplace); 1431 setEndingSelection(selectionAfterReplace);
1494 1432
1495 return true; 1433 return true;
1496 } 1434 }
1497 1435
1498 } // namespace WebCore 1436 } // namespace WebCore
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698