| 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 113 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 124 (AppleConvertedSpace)); | 124 (AppleConvertedSpace)); |
| 125 if (!node->isHTMLElement() || | 125 if (!node->isHTMLElement() || |
| 126 toHTMLElement(node)->getAttribute(classAttr) != | 126 toHTMLElement(node)->getAttribute(classAttr) != |
| 127 convertedSpaceSpanClassString) | 127 convertedSpaceSpanClassString) |
| 128 return false; | 128 return false; |
| 129 UseCounter::count(node->document(), UseCounter::EditingAppleConvertedSpace); | 129 UseCounter::count(node->document(), UseCounter::EditingAppleConvertedSpace); |
| 130 return true; | 130 return true; |
| 131 } | 131 } |
| 132 | 132 |
| 133 static Position positionAvoidingPrecedingNodes(Position pos) { | 133 static Position positionAvoidingPrecedingNodes(Position pos) { |
| 134 // If we're already on a break, it's probably a placeholder and we shouldn't c
hange our position. | 134 // If we're already on a break, it's probably a placeholder and we shouldn't |
| 135 // change our position. |
| 135 if (editingIgnoresContent(pos.anchorNode())) | 136 if (editingIgnoresContent(pos.anchorNode())) |
| 136 return pos; | 137 return pos; |
| 137 | 138 |
| 138 // We also stop when changing block flow elements because even though the visu
al position is the | 139 // We also stop when changing block flow elements because even though the |
| 139 // same. E.g., | 140 // visual position is the same. E.g., |
| 140 // <div>foo^</div>^ | 141 // <div>foo^</div>^ |
| 141 // The two positions above are the same visual position, but we want to stay i
n the same block. | 142 // The two positions above are the same visual position, but we want to stay |
| 143 // in the same block. |
| 142 Element* enclosingBlockElement = enclosingBlock(pos.computeContainerNode()); | 144 Element* enclosingBlockElement = enclosingBlock(pos.computeContainerNode()); |
| 143 for (Position nextPosition = pos; | 145 for (Position nextPosition = pos; |
| 144 nextPosition.computeContainerNode() != enclosingBlockElement; | 146 nextPosition.computeContainerNode() != enclosingBlockElement; |
| 145 pos = nextPosition) { | 147 pos = nextPosition) { |
| 146 if (lineBreakExistsAtPosition(pos)) | 148 if (lineBreakExistsAtPosition(pos)) |
| 147 break; | 149 break; |
| 148 | 150 |
| 149 if (pos.computeContainerNode()->nonShadowBoundaryParentNode()) | 151 if (pos.computeContainerNode()->nonShadowBoundaryParentNode()) |
| 150 nextPosition = Position::inParentAfterNode(*pos.computeContainerNode()); | 152 nextPosition = Position::inParentAfterNode(*pos.computeContainerNode()); |
| 151 | 153 |
| (...skipping 26 matching lines...) Expand all Loading... |
| 178 return; | 180 return; |
| 179 | 181 |
| 180 Element* shadowAncestorElement; | 182 Element* shadowAncestorElement; |
| 181 if (editableRoot->isInShadowTree()) | 183 if (editableRoot->isInShadowTree()) |
| 182 shadowAncestorElement = editableRoot->ownerShadowHost(); | 184 shadowAncestorElement = editableRoot->ownerShadowHost(); |
| 183 else | 185 else |
| 184 shadowAncestorElement = editableRoot; | 186 shadowAncestorElement = editableRoot; |
| 185 | 187 |
| 186 if (!editableRoot->getAttributeEventListener( | 188 if (!editableRoot->getAttributeEventListener( |
| 187 EventTypeNames::webkitBeforeTextInserted) | 189 EventTypeNames::webkitBeforeTextInserted) |
| 188 // FIXME: Remove these checks once textareas and textfields actually regis
ter an event handler. | 190 // FIXME: Remove these checks once textareas and textfields actually |
| 191 // register an event handler. |
| 189 && | 192 && |
| 190 !(shadowAncestorElement && shadowAncestorElement->layoutObject() && | 193 !(shadowAncestorElement && shadowAncestorElement->layoutObject() && |
| 191 shadowAncestorElement->layoutObject()->isTextControl()) && | 194 shadowAncestorElement->layoutObject()->isTextControl()) && |
| 192 hasRichlyEditableStyle(*editableRoot)) { | 195 hasRichlyEditableStyle(*editableRoot)) { |
| 193 removeInterchangeNodes(m_fragment.get()); | 196 removeInterchangeNodes(m_fragment.get()); |
| 194 return; | 197 return; |
| 195 } | 198 } |
| 196 | 199 |
| 197 if (!hasRichlyEditableStyle(*editableRoot)) { | 200 if (!hasRichlyEditableStyle(*editableRoot)) { |
| 198 bool isPlainText = true; | 201 bool isPlainText = true; |
| 199 for (Node& node : NodeTraversal::childrenOf(*m_fragment)) { | 202 for (Node& node : NodeTraversal::childrenOf(*m_fragment)) { |
| 200 if (isInterchangeHTMLBRElement(&node) && &node == m_fragment->lastChild()) | 203 if (isInterchangeHTMLBRElement(&node) && &node == m_fragment->lastChild()) |
| 201 continue; | 204 continue; |
| 202 if (!node.isTextNode()) { | 205 if (!node.isTextNode()) { |
| 203 isPlainText = false; | 206 isPlainText = false; |
| 204 break; | 207 break; |
| 205 } | 208 } |
| 206 } | 209 } |
| 207 // We don't need TestRendering for plain-text editing + plain-text insertion
. | 210 // We don't need TestRendering for plain-text editing + plain-text |
| 211 // insertion. |
| 208 if (isPlainText) { | 212 if (isPlainText) { |
| 209 removeInterchangeNodes(m_fragment.get()); | 213 removeInterchangeNodes(m_fragment.get()); |
| 210 String originalText = m_fragment->textContent(); | 214 String originalText = m_fragment->textContent(); |
| 211 BeforeTextInsertedEvent* event = | 215 BeforeTextInsertedEvent* event = |
| 212 BeforeTextInsertedEvent::create(originalText); | 216 BeforeTextInsertedEvent::create(originalText); |
| 213 editableRoot->dispatchEvent(event); | 217 editableRoot->dispatchEvent(event); |
| 214 if (originalText != event->text()) { | 218 if (originalText != event->text()) { |
| 215 m_fragment = createFragmentFromText( | 219 m_fragment = createFragmentFromText( |
| 216 selection.toNormalizedEphemeralRange(), event->text()); | 220 selection.toNormalizedEphemeralRange(), event->text()); |
| 217 removeInterchangeNodes(m_fragment.get()); | 221 removeInterchangeNodes(m_fragment.get()); |
| (...skipping 236 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 454 if (m_movingParagraph) | 458 if (m_movingParagraph) |
| 455 return false; | 459 return false; |
| 456 | 460 |
| 457 VisiblePosition startOfInsertedContent = positionAtStartOfInsertedContent(); | 461 VisiblePosition startOfInsertedContent = positionAtStartOfInsertedContent(); |
| 458 VisiblePosition prev = | 462 VisiblePosition prev = |
| 459 previousPositionOf(startOfInsertedContent, CannotCrossEditingBoundary); | 463 previousPositionOf(startOfInsertedContent, CannotCrossEditingBoundary); |
| 460 if (prev.isNull()) | 464 if (prev.isNull()) |
| 461 return false; | 465 return false; |
| 462 | 466 |
| 463 // When we have matching quote levels, its ok to merge more frequently. | 467 // When we have matching quote levels, its ok to merge more frequently. |
| 464 // For a successful merge, we still need to make sure that the inserted conten
t starts with the beginning of a paragraph. | 468 // For a successful merge, we still need to make sure that the inserted |
| 465 // And we should only merge here if the selection start was inside a mail bloc
kquote. This prevents against removing a | 469 // content starts with the beginning of a paragraph. And we should only merge |
| 466 // blockquote from newly pasted quoted content that was pasted into an unquote
d position. If that unquoted position happens | 470 // here if the selection start was inside a mail blockquote. This prevents |
| 467 // to be right after another blockquote, we don't want to merge and risk strip
ping a valid block (and newline) from the pasted content. | 471 // against removing a blockquote from newly pasted quoted content that was |
| 472 // pasted into an unquoted position. If that unquoted position happens to be |
| 473 // right after another blockquote, we don't want to merge and risk stripping a |
| 474 // valid block (and newline) from the pasted content. |
| 468 if (isStartOfParagraphDeprecated(startOfInsertedContent) && | 475 if (isStartOfParagraphDeprecated(startOfInsertedContent) && |
| 469 selectionStartWasInsideMailBlockquote && | 476 selectionStartWasInsideMailBlockquote && |
| 470 hasMatchingQuoteLevel(prev, positionAtEndOfInsertedContent())) | 477 hasMatchingQuoteLevel(prev, positionAtEndOfInsertedContent())) |
| 471 return true; | 478 return true; |
| 472 | 479 |
| 473 return !selectionStartWasStartOfParagraph && | 480 return !selectionStartWasStartOfParagraph && |
| 474 !fragmentHasInterchangeNewlineAtStart && | 481 !fragmentHasInterchangeNewlineAtStart && |
| 475 isStartOfParagraphDeprecated(startOfInsertedContent) && | 482 isStartOfParagraphDeprecated(startOfInsertedContent) && |
| 476 !isHTMLBRElement( | 483 !isHTMLBRElement( |
| 477 *startOfInsertedContent.deepEquivalent().anchorNode()) && | 484 *startOfInsertedContent.deepEquivalent().anchorNode()) && |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 530 return !enclosingNodeOfType(source.deepEquivalent(), | 537 return !enclosingNodeOfType(source.deepEquivalent(), |
| 531 &isMailPasteAsQuotationHTMLBlockQuoteElement) && | 538 &isMailPasteAsQuotationHTMLBlockQuoteElement) && |
| 532 sourceBlock && (!sourceBlock->hasTagName(blockquoteTag) || | 539 sourceBlock && (!sourceBlock->hasTagName(blockquoteTag) || |
| 533 isMailHTMLBlockquoteElement(sourceBlock)) && | 540 isMailHTMLBlockquoteElement(sourceBlock)) && |
| 534 enclosingListChild(sourceBlock) == | 541 enclosingListChild(sourceBlock) == |
| 535 enclosingListChild(destinationNode) && | 542 enclosingListChild(destinationNode) && |
| 536 enclosingTableCell(source.deepEquivalent()) == | 543 enclosingTableCell(source.deepEquivalent()) == |
| 537 enclosingTableCell(destination.deepEquivalent()) && | 544 enclosingTableCell(destination.deepEquivalent()) && |
| 538 (!isHTMLHeaderElement(sourceBlock) || | 545 (!isHTMLHeaderElement(sourceBlock) || |
| 539 haveSameTagName(sourceBlock, destinationBlock)) | 546 haveSameTagName(sourceBlock, destinationBlock)) |
| 540 // Don't merge to or from a position before or after a block because it
would | 547 // Don't merge to or from a position before or after a block because it |
| 541 // be a no-op and cause infinite recursion. | 548 // would be a no-op and cause infinite recursion. |
| 542 && !isEnclosingBlock(sourceNode) && !isEnclosingBlock(destinationNode); | 549 && !isEnclosingBlock(sourceNode) && !isEnclosingBlock(destinationNode); |
| 543 } | 550 } |
| 544 | 551 |
| 545 // Style rules that match just inserted elements could change their appearance,
like | 552 // Style rules that match just inserted elements could change their appearance, |
| 546 // a div inserted into a document with div { display:inline; }. | 553 // like a div inserted into a document with div { display:inline; }. |
| 547 void ReplaceSelectionCommand::removeRedundantStylesAndKeepStyleSpanInline( | 554 void ReplaceSelectionCommand::removeRedundantStylesAndKeepStyleSpanInline( |
| 548 InsertedNodes& insertedNodes, | 555 InsertedNodes& insertedNodes, |
| 549 EditingState* editingState) { | 556 EditingState* editingState) { |
| 550 Node* pastEndNode = insertedNodes.pastLastLeaf(); | 557 Node* pastEndNode = insertedNodes.pastLastLeaf(); |
| 551 Node* next = nullptr; | 558 Node* next = nullptr; |
| 552 for (Node* node = insertedNodes.firstNodeInserted(); | 559 for (Node* node = insertedNodes.firstNodeInserted(); |
| 553 node && node != pastEndNode; node = next) { | 560 node && node != pastEndNode; node = next) { |
| 554 // FIXME: <rdar://problem/5371536> Style rules that match pasted content can
change it's appearance | 561 // FIXME: <rdar://problem/5371536> Style rules that match pasted content can |
| 562 // change it's appearance |
| 555 | 563 |
| 556 next = NodeTraversal::next(*node); | 564 next = NodeTraversal::next(*node); |
| 557 if (!node->isStyledElement()) | 565 if (!node->isStyledElement()) |
| 558 continue; | 566 continue; |
| 559 | 567 |
| 560 Element* element = toElement(node); | 568 Element* element = toElement(node); |
| 561 | 569 |
| 562 const StylePropertySet* inlineStyle = element->inlineStyle(); | 570 const StylePropertySet* inlineStyle = element->inlineStyle(); |
| 563 EditingStyle* newInlineStyle = EditingStyle::create(inlineStyle); | 571 EditingStyle* newInlineStyle = EditingStyle::create(inlineStyle); |
| 564 if (inlineStyle) { | 572 if (inlineStyle) { |
| 565 if (element->isHTMLElement()) { | 573 if (element->isHTMLElement()) { |
| 566 Vector<QualifiedName> attributes; | 574 Vector<QualifiedName> attributes; |
| 567 HTMLElement* htmlElement = toHTMLElement(element); | 575 HTMLElement* htmlElement = toHTMLElement(element); |
| 568 DCHECK(htmlElement); | 576 DCHECK(htmlElement); |
| 569 | 577 |
| 570 if (newInlineStyle->conflictsWithImplicitStyleOfElement(htmlElement)) { | 578 if (newInlineStyle->conflictsWithImplicitStyleOfElement(htmlElement)) { |
| 571 // e.g. <b style="font-weight: normal;"> is converted to <span style="
font-weight: normal;"> | 579 // e.g. <b style="font-weight: normal;"> is converted to <span |
| 580 // style="font-weight: normal;"> |
| 572 element = replaceElementWithSpanPreservingChildrenAndAttributes( | 581 element = replaceElementWithSpanPreservingChildrenAndAttributes( |
| 573 htmlElement); | 582 htmlElement); |
| 574 inlineStyle = element->inlineStyle(); | 583 inlineStyle = element->inlineStyle(); |
| 575 insertedNodes.didReplaceNode(*htmlElement, *element); | 584 insertedNodes.didReplaceNode(*htmlElement, *element); |
| 576 } else if (newInlineStyle->extractConflictingImplicitStyleOfAttributes( | 585 } else if (newInlineStyle->extractConflictingImplicitStyleOfAttributes( |
| 577 htmlElement, EditingStyle::PreserveWritingDirection, 0, | 586 htmlElement, EditingStyle::PreserveWritingDirection, 0, |
| 578 attributes, EditingStyle::DoNotExtractMatchingStyle)) { | 587 attributes, EditingStyle::DoNotExtractMatchingStyle)) { |
| 579 // e.g. <font size="3" style="font-size: 20px;"> is converted to <font
style="font-size: 20px;"> | 588 // e.g. <font size="3" style="font-size: 20px;"> is converted to <font |
| 589 // style="font-size: 20px;"> |
| 580 for (size_t i = 0; i < attributes.size(); i++) | 590 for (size_t i = 0; i < attributes.size(); i++) |
| 581 removeElementAttribute(htmlElement, attributes[i]); | 591 removeElementAttribute(htmlElement, attributes[i]); |
| 582 } | 592 } |
| 583 } | 593 } |
| 584 | 594 |
| 585 ContainerNode* context = element->parentNode(); | 595 ContainerNode* context = element->parentNode(); |
| 586 | 596 |
| 587 // If Mail wraps the fragment with a Paste as Quotation blockquote, or if
you're pasting into a quoted region, | 597 // If Mail wraps the fragment with a Paste as Quotation blockquote, or if |
| 588 // styles from blockquoteNode are allowed to override those from the sourc
e document, see <rdar://problem/4930986> and <rdar://problem/5089327>. | 598 // you're pasting into a quoted region, styles from blockquoteNode are |
| 599 // allowed to override those from the source document, see |
| 600 // <rdar://problem/4930986> and <rdar://problem/5089327>. |
| 589 HTMLQuoteElement* blockquoteElement = | 601 HTMLQuoteElement* blockquoteElement = |
| 590 !context || isMailPasteAsQuotationHTMLBlockQuoteElement(context) | 602 !context || isMailPasteAsQuotationHTMLBlockQuoteElement(context) |
| 591 ? toHTMLQuoteElement(context) | 603 ? toHTMLQuoteElement(context) |
| 592 : toHTMLQuoteElement(enclosingNodeOfType( | 604 : toHTMLQuoteElement(enclosingNodeOfType( |
| 593 Position::firstPositionInNode(context), | 605 Position::firstPositionInNode(context), |
| 594 isMailHTMLBlockquoteElement, CanCrossEditingBoundary)); | 606 isMailHTMLBlockquoteElement, CanCrossEditingBoundary)); |
| 595 if (blockquoteElement) | 607 if (blockquoteElement) |
| 596 newInlineStyle->removeStyleFromRulesAndContext( | 608 newInlineStyle->removeStyleFromRulesAndContext( |
| 597 element, document().documentElement()); | 609 element, document().documentElement()); |
| 598 | 610 |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 637 // WebKit used to not add display: inline and float: none on copy. | 649 // WebKit used to not add display: inline and float: none on copy. |
| 638 // Keep this code around for backward compatibility | 650 // Keep this code around for backward compatibility |
| 639 if (isLegacyAppleHTMLSpanElement(element)) { | 651 if (isLegacyAppleHTMLSpanElement(element)) { |
| 640 if (!element->hasChildren()) { | 652 if (!element->hasChildren()) { |
| 641 insertedNodes.willRemoveNodePreservingChildren(*element); | 653 insertedNodes.willRemoveNodePreservingChildren(*element); |
| 642 removeNodePreservingChildren(element, editingState); | 654 removeNodePreservingChildren(element, editingState); |
| 643 if (editingState->isAborted()) | 655 if (editingState->isAborted()) |
| 644 return; | 656 return; |
| 645 continue; | 657 continue; |
| 646 } | 658 } |
| 647 // There are other styles that style rules can give to style spans, | 659 // There are other styles that style rules can give to style spans, but |
| 648 // but these are the two important ones because they'll prevent | 660 // these are the two important ones because they'll prevent inserted |
| 649 // inserted content from appearing in the right paragraph. | 661 // content from appearing in the right paragraph. |
| 650 // FIXME: Hyatt is concerned that selectively using display:inline will gi
ve inconsistent | 662 // FIXME: Hyatt is concerned that selectively using display:inline will |
| 651 // results. We already know one issue because td elements ignore their dis
play property | 663 // give inconsistent results. We already know one issue because td |
| 652 // in quirks mode (which Mail.app is always in). We should look for an alt
ernative. | 664 // elements ignore their display property in quirks mode (which Mail.app |
| 665 // is always in). We should look for an alternative. |
| 653 | 666 |
| 654 // Mutate using the CSSOM wrapper so we get the same event behavior as a s
cript. | 667 // Mutate using the CSSOM wrapper so we get the same event behavior as a |
| 668 // script. |
| 655 if (isEnclosingBlock(element)) | 669 if (isEnclosingBlock(element)) |
| 656 element->style()->setPropertyInternal( | 670 element->style()->setPropertyInternal( |
| 657 CSSPropertyDisplay, String(), "inline", false, IGNORE_EXCEPTION); | 671 CSSPropertyDisplay, String(), "inline", false, IGNORE_EXCEPTION); |
| 658 if (element->layoutObject() && | 672 if (element->layoutObject() && |
| 659 element->layoutObject()->style()->isFloating()) | 673 element->layoutObject()->style()->isFloating()) |
| 660 element->style()->setPropertyInternal(CSSPropertyFloat, String(), | 674 element->style()->setPropertyInternal(CSSPropertyFloat, String(), |
| 661 "none", false, IGNORE_EXCEPTION); | 675 "none", false, IGNORE_EXCEPTION); |
| 662 } | 676 } |
| 663 } | 677 } |
| 664 } | 678 } |
| (...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 782 !nodeHasVisibleLayoutText(toText(*lastLeafInserted)) && | 796 !nodeHasVisibleLayoutText(toText(*lastLeafInserted)) && |
| 783 !enclosingElementWithTag(firstPositionInOrBeforeNode(lastLeafInserted), | 797 !enclosingElementWithTag(firstPositionInOrBeforeNode(lastLeafInserted), |
| 784 selectTag) && | 798 selectTag) && |
| 785 !enclosingElementWithTag(firstPositionInOrBeforeNode(lastLeafInserted), | 799 !enclosingElementWithTag(firstPositionInOrBeforeNode(lastLeafInserted), |
| 786 scriptTag)) { | 800 scriptTag)) { |
| 787 insertedNodes.willRemoveNode(*lastLeafInserted); | 801 insertedNodes.willRemoveNode(*lastLeafInserted); |
| 788 // Removing a Text node won't dispatch synchronous events. | 802 // Removing a Text node won't dispatch synchronous events. |
| 789 removeNode(lastLeafInserted, ASSERT_NO_EDITING_ABORT); | 803 removeNode(lastLeafInserted, ASSERT_NO_EDITING_ABORT); |
| 790 } | 804 } |
| 791 | 805 |
| 792 // We don't have to make sure that firstNodeInserted isn't inside a select or
script element, because | 806 // We don't have to make sure that firstNodeInserted isn't inside a select or |
| 793 // it is a top level node in the fragment and the user can't insert into those
elements. | 807 // script element, because it is a top level node in the fragment and the user |
| 808 // can't insert into those elements. |
| 794 Node* firstNodeInserted = insertedNodes.firstNodeInserted(); | 809 Node* firstNodeInserted = insertedNodes.firstNodeInserted(); |
| 795 if (firstNodeInserted && firstNodeInserted->isTextNode() && | 810 if (firstNodeInserted && firstNodeInserted->isTextNode() && |
| 796 !nodeHasVisibleLayoutText(toText(*firstNodeInserted))) { | 811 !nodeHasVisibleLayoutText(toText(*firstNodeInserted))) { |
| 797 insertedNodes.willRemoveNode(*firstNodeInserted); | 812 insertedNodes.willRemoveNode(*firstNodeInserted); |
| 798 // Removing a Text node won't dispatch synchronous events. | 813 // Removing a Text node won't dispatch synchronous events. |
| 799 removeNode(firstNodeInserted, ASSERT_NO_EDITING_ABORT); | 814 removeNode(firstNodeInserted, ASSERT_NO_EDITING_ABORT); |
| 800 } | 815 } |
| 801 } | 816 } |
| 802 | 817 |
| 803 VisiblePosition ReplaceSelectionCommand::positionAtEndOfInsertedContent() | 818 VisiblePosition ReplaceSelectionCommand::positionAtEndOfInsertedContent() |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 839 if (!node->isHTMLElement()) | 854 if (!node->isHTMLElement()) |
| 840 return false; | 855 return false; |
| 841 | 856 |
| 842 const HTMLElement& element = toHTMLElement(*node); | 857 const HTMLElement& element = toHTMLElement(*node); |
| 843 return isListItem(node) || isTableCell(node) || element.hasTagName(preTag) || | 858 return isListItem(node) || isTableCell(node) || element.hasTagName(preTag) || |
| 844 element.hasTagName(h1Tag) || element.hasTagName(h2Tag) || | 859 element.hasTagName(h1Tag) || element.hasTagName(h2Tag) || |
| 845 element.hasTagName(h3Tag) || element.hasTagName(h4Tag) || | 860 element.hasTagName(h3Tag) || element.hasTagName(h4Tag) || |
| 846 element.hasTagName(h5Tag) || element.hasTagName(h6Tag); | 861 element.hasTagName(h5Tag) || element.hasTagName(h6Tag); |
| 847 } | 862 } |
| 848 | 863 |
| 849 // Remove style spans before insertion if they are unnecessary. It's faster bec
ause we'll | 864 // Remove style spans before insertion if they are unnecessary. It's faster |
| 850 // avoid doing a layout. | 865 // because we'll avoid doing a layout. |
| 851 static bool handleStyleSpansBeforeInsertion(ReplacementFragment& fragment, | 866 static bool handleStyleSpansBeforeInsertion(ReplacementFragment& fragment, |
| 852 const Position& insertionPos) { | 867 const Position& insertionPos) { |
| 853 Node* topNode = fragment.firstChild(); | 868 Node* topNode = fragment.firstChild(); |
| 854 if (!isHTMLSpanElement(topNode)) | 869 if (!isHTMLSpanElement(topNode)) |
| 855 return false; | 870 return false; |
| 856 | 871 |
| 857 // Handling the case where we are doing Paste as Quotation or pasting into quo
ted content is more complicated (see handleStyleSpans) | 872 // Handling the case where we are doing Paste as Quotation or pasting into |
| 858 // and doesn't receive the optimization. | 873 // quoted content is more complicated (see handleStyleSpans) and doesn't |
| 874 // receive the optimization. |
| 859 if (isMailPasteAsQuotationHTMLBlockQuoteElement(topNode) || | 875 if (isMailPasteAsQuotationHTMLBlockQuoteElement(topNode) || |
| 860 enclosingNodeOfType(firstPositionInOrBeforeNode(topNode), | 876 enclosingNodeOfType(firstPositionInOrBeforeNode(topNode), |
| 861 isMailHTMLBlockquoteElement, CanCrossEditingBoundary)) | 877 isMailHTMLBlockquoteElement, CanCrossEditingBoundary)) |
| 862 return false; | 878 return false; |
| 863 | 879 |
| 864 // Remove style spans to follow the styles of parent block element when |fragm
ent| becomes a part of it. | 880 // Remove style spans to follow the styles of parent block element when |
| 865 // See bugs http://crbug.com/226941 and http://crbug.com/335955. | 881 // |fragment| becomes a part of it. See bugs http://crbug.com/226941 and |
| 882 // http://crbug.com/335955. |
| 866 HTMLSpanElement* wrappingStyleSpan = toHTMLSpanElement(topNode); | 883 HTMLSpanElement* wrappingStyleSpan = toHTMLSpanElement(topNode); |
| 867 const Node* node = insertionPos.anchorNode(); | 884 const Node* node = insertionPos.anchorNode(); |
| 868 // |node| can be an inline element like <br> under <li> | 885 // |node| can be an inline element like <br> under <li> |
| 869 // e.g.) editing/execCommand/switch-list-type.html | 886 // e.g.) editing/execCommand/switch-list-type.html |
| 870 // editing/deleting/backspace-merge-into-block.html | 887 // editing/deleting/backspace-merge-into-block.html |
| 871 if (isInline(node)) { | 888 if (isInline(node)) { |
| 872 node = enclosingBlock(insertionPos.anchorNode()); | 889 node = enclosingBlock(insertionPos.anchorNode()); |
| 873 if (!node) | 890 if (!node) |
| 874 return false; | 891 return false; |
| 875 } | 892 } |
| 876 | 893 |
| 877 if (followBlockElementStyle(node)) { | 894 if (followBlockElementStyle(node)) { |
| 878 fragment.removeNodePreservingChildren(wrappingStyleSpan); | 895 fragment.removeNodePreservingChildren(wrappingStyleSpan); |
| 879 return true; | 896 return true; |
| 880 } | 897 } |
| 881 | 898 |
| 882 // Either there are no style spans in the fragment or a WebKit client has adde
d content to the fragment | 899 // Either there are no style spans in the fragment or a WebKit client has |
| 883 // before inserting it. Look for and handle style spans after insertion. | 900 // added content to the fragment before inserting it. Look for and handle |
| 901 // style spans after insertion. |
| 884 if (!isLegacyAppleHTMLSpanElement(topNode)) | 902 if (!isLegacyAppleHTMLSpanElement(topNode)) |
| 885 return false; | 903 return false; |
| 886 | 904 |
| 887 EditingStyle* styleAtInsertionPos = | 905 EditingStyle* styleAtInsertionPos = |
| 888 EditingStyle::create(insertionPos.parentAnchoredEquivalent()); | 906 EditingStyle::create(insertionPos.parentAnchoredEquivalent()); |
| 889 String styleText = styleAtInsertionPos->style()->asText(); | 907 String styleText = styleAtInsertionPos->style()->asText(); |
| 890 | 908 |
| 891 // FIXME: This string comparison is a naive way of comparing two styles. | 909 // FIXME: This string comparison is a naive way of comparing two styles. |
| 892 // We should be taking the diff and check that the diff is empty. | 910 // We should be taking the diff and check that the diff is empty. |
| 893 if (styleText != wrappingStyleSpan->getAttribute(styleAttr)) | 911 if (styleText != wrappingStyleSpan->getAttribute(styleAttr)) |
| 894 return false; | 912 return false; |
| 895 | 913 |
| 896 fragment.removeNodePreservingChildren(wrappingStyleSpan); | 914 fragment.removeNodePreservingChildren(wrappingStyleSpan); |
| 897 return true; | 915 return true; |
| 898 } | 916 } |
| 899 | 917 |
| 900 // At copy time, WebKit wraps copied content in a span that contains the source
document's | 918 // At copy time, WebKit wraps copied content in a span that contains the source |
| 901 // default styles. If the copied Range inherits any other styles from its ances
tors, we put | 919 // document's default styles. If the copied Range inherits any other styles |
| 902 // those styles on a second span. | 920 // from its ancestors, we put those styles on a second span. This function |
| 903 // This function removes redundant styles from those spans, and removes the span
s if all their | 921 // removes redundant styles from those spans, and removes the |
| 904 // styles are redundant. | 922 // spans if all their styles are redundant. |
| 905 // We should remove the Apple-style-span class when we're done, see <rdar://prob
lem/5685600>. | 923 // We should remove the Apple-style-span class when we're done, see |
| 906 // We should remove styles from spans that are overridden by all of their childr
en, either here | 924 // <rdar://problem/5685600>. |
| 907 // or at copy time. | 925 // We should remove styles from spans that are overridden by all of their |
| 926 // children, either here or at copy time. |
| 908 void ReplaceSelectionCommand::handleStyleSpans(InsertedNodes& insertedNodes, | 927 void ReplaceSelectionCommand::handleStyleSpans(InsertedNodes& insertedNodes, |
| 909 EditingState* editingState) { | 928 EditingState* editingState) { |
| 910 if (!insertedNodes.firstNodeInserted()) | 929 if (!insertedNodes.firstNodeInserted()) |
| 911 return; | 930 return; |
| 912 | 931 |
| 913 HTMLSpanElement* wrappingStyleSpan = nullptr; | 932 HTMLSpanElement* wrappingStyleSpan = nullptr; |
| 914 // The style span that contains the source document's default style should be
at | 933 // The style span that contains the source document's default style should be |
| 915 // the top of the fragment, but Mail sometimes adds a wrapper (for Paste As Qu
otation), | 934 // at the top of the fragment, but Mail sometimes adds a wrapper (for Paste As |
| 916 // so search for the top level style span instead of assuming it's at the top. | 935 // Quotation), so search for the top level style span instead of assuming it's |
| 936 // at the top. |
| 917 | 937 |
| 918 for (Node& node : | 938 for (Node& node : |
| 919 NodeTraversal::startsAt(*insertedNodes.firstNodeInserted())) { | 939 NodeTraversal::startsAt(*insertedNodes.firstNodeInserted())) { |
| 920 if (isLegacyAppleHTMLSpanElement(&node)) { | 940 if (isLegacyAppleHTMLSpanElement(&node)) { |
| 921 wrappingStyleSpan = toHTMLSpanElement(&node); | 941 wrappingStyleSpan = toHTMLSpanElement(&node); |
| 922 break; | 942 break; |
| 923 } | 943 } |
| 924 } | 944 } |
| 925 | 945 |
| 926 // There might not be any style spans if we're pasting from another applicatio
n or if | 946 // There might not be any style spans if we're pasting from another |
| 927 // we are here because of a document.execCommand("InsertHTML", ...) call. | 947 // application or if we are here because of a |
| 948 // document.execCommand("InsertHTML", ...) call. |
| 928 if (!wrappingStyleSpan) | 949 if (!wrappingStyleSpan) |
| 929 return; | 950 return; |
| 930 | 951 |
| 931 EditingStyle* style = EditingStyle::create(wrappingStyleSpan->inlineStyle()); | 952 EditingStyle* style = EditingStyle::create(wrappingStyleSpan->inlineStyle()); |
| 932 ContainerNode* context = wrappingStyleSpan->parentNode(); | 953 ContainerNode* context = wrappingStyleSpan->parentNode(); |
| 933 | 954 |
| 934 // If Mail wraps the fragment with a Paste as Quotation blockquote, or if you'
re pasting into a quoted region, | 955 // If Mail wraps the fragment with a Paste as Quotation blockquote, or if |
| 935 // styles from blockquoteElement are allowed to override those from the source
document, see <rdar://problem/4930986> and <rdar://problem/5089327>. | 956 // you're pasting into a quoted region, styles from blockquoteElement are |
| 957 // allowed to override those from the source document, see |
| 958 // <rdar://problem/4930986> and <rdar://problem/5089327>. |
| 936 HTMLQuoteElement* blockquoteElement = | 959 HTMLQuoteElement* blockquoteElement = |
| 937 isMailPasteAsQuotationHTMLBlockQuoteElement(context) | 960 isMailPasteAsQuotationHTMLBlockQuoteElement(context) |
| 938 ? toHTMLQuoteElement(context) | 961 ? toHTMLQuoteElement(context) |
| 939 : toHTMLQuoteElement(enclosingNodeOfType( | 962 : toHTMLQuoteElement(enclosingNodeOfType( |
| 940 Position::firstPositionInNode(context), | 963 Position::firstPositionInNode(context), |
| 941 isMailHTMLBlockquoteElement, CanCrossEditingBoundary)); | 964 isMailHTMLBlockquoteElement, CanCrossEditingBoundary)); |
| 942 if (blockquoteElement) | 965 if (blockquoteElement) |
| 943 context = document().documentElement(); | 966 context = document().documentElement(); |
| 944 | 967 |
| 945 // This operation requires that only editing styles to be removed from sourceD
ocumentStyle. | 968 // This operation requires that only editing styles to be removed from |
| 969 // sourceDocumentStyle. |
| 946 style->prepareToApplyAt(Position::firstPositionInNode(context)); | 970 style->prepareToApplyAt(Position::firstPositionInNode(context)); |
| 947 | 971 |
| 948 // Remove block properties in the span's style. This prevents properties that
probably have no effect | 972 // Remove block properties in the span's style. This prevents properties that |
| 949 // currently from affecting blocks later if the style is cloned for a new bloc
k element during a future | 973 // probably have no effect currently from affecting blocks later if the style |
| 974 // is cloned for a new block element during a future |
| 950 // editing operation. | 975 // editing operation. |
| 951 // FIXME: They *can* have an effect currently if blocks beneath the style span
aren't individually marked | 976 // FIXME: They *can* have an effect currently if blocks beneath the style span |
| 952 // with block styles by the editing engine used to style them. WebKit doesn't
do this, but others might. | 977 // aren't individually marked with block styles by the editing engine used to |
| 978 // style them. WebKit doesn't do this, but others might. |
| 953 style->removeBlockProperties(); | 979 style->removeBlockProperties(); |
| 954 | 980 |
| 955 if (style->isEmpty() || !wrappingStyleSpan->hasChildren()) { | 981 if (style->isEmpty() || !wrappingStyleSpan->hasChildren()) { |
| 956 insertedNodes.willRemoveNodePreservingChildren(*wrappingStyleSpan); | 982 insertedNodes.willRemoveNodePreservingChildren(*wrappingStyleSpan); |
| 957 removeNodePreservingChildren(wrappingStyleSpan, editingState); | 983 removeNodePreservingChildren(wrappingStyleSpan, editingState); |
| 958 } else { | 984 } else { |
| 959 setNodeAttribute(wrappingStyleSpan, styleAttr, | 985 setNodeAttribute(wrappingStyleSpan, styleAttr, |
| 960 AtomicString(style->style()->asText())); | 986 AtomicString(style->style()->asText())); |
| 961 } | 987 } |
| 962 } | 988 } |
| 963 | 989 |
| 964 void ReplaceSelectionCommand::mergeEndIfNeeded(EditingState* editingState) { | 990 void ReplaceSelectionCommand::mergeEndIfNeeded(EditingState* editingState) { |
| 965 if (!m_shouldMergeEnd) | 991 if (!m_shouldMergeEnd) |
| 966 return; | 992 return; |
| 967 | 993 |
| 968 VisiblePosition startOfInsertedContent(positionAtStartOfInsertedContent()); | 994 VisiblePosition startOfInsertedContent(positionAtStartOfInsertedContent()); |
| 969 VisiblePosition endOfInsertedContent(positionAtEndOfInsertedContent()); | 995 VisiblePosition endOfInsertedContent(positionAtEndOfInsertedContent()); |
| 970 | 996 |
| 971 // Bail to avoid infinite recursion. | 997 // Bail to avoid infinite recursion. |
| 972 if (m_movingParagraph) { | 998 if (m_movingParagraph) { |
| 973 NOTREACHED(); | 999 NOTREACHED(); |
| 974 return; | 1000 return; |
| 975 } | 1001 } |
| 976 | 1002 |
| 977 // Merging two paragraphs will destroy the moved one's block styles. Always m
ove the end of inserted forward | 1003 // Merging two paragraphs will destroy the moved one's block styles. Always |
| 978 // to preserve the block style of the paragraph already in the document, unles
s the paragraph to move would | 1004 // move the end of inserted forward to preserve the block style of the |
| 979 // include the what was the start of the selection that was pasted into, so th
at we preserve that paragraph's | 1005 // paragraph already in the document, unless the paragraph to move would |
| 980 // block styles. | 1006 // include the what was the start of the selection that was pasted into, so |
| 1007 // that we preserve that paragraph's block styles. |
| 981 bool mergeForward = !( | 1008 bool mergeForward = !( |
| 982 inSameParagraphDeprecated(startOfInsertedContent, endOfInsertedContent) && | 1009 inSameParagraphDeprecated(startOfInsertedContent, endOfInsertedContent) && |
| 983 !isStartOfParagraphDeprecated(startOfInsertedContent)); | 1010 !isStartOfParagraphDeprecated(startOfInsertedContent)); |
| 984 | 1011 |
| 985 VisiblePosition destination = mergeForward | 1012 VisiblePosition destination = mergeForward |
| 986 ? nextPositionOf(endOfInsertedContent) | 1013 ? nextPositionOf(endOfInsertedContent) |
| 987 : endOfInsertedContent; | 1014 : endOfInsertedContent; |
| 988 VisiblePosition startOfParagraphToMove = | 1015 VisiblePosition startOfParagraphToMove = |
| 989 mergeForward ? startOfParagraphDeprecated(endOfInsertedContent) | 1016 mergeForward ? startOfParagraphDeprecated(endOfInsertedContent) |
| 990 : nextPositionOf(endOfInsertedContent); | 1017 : nextPositionOf(endOfInsertedContent); |
| (...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1128 Element* currentRoot = selection.rootEditableElement(); | 1155 Element* currentRoot = selection.rootEditableElement(); |
| 1129 | 1156 |
| 1130 if ((selectionStartWasStartOfParagraph && selectionEndWasEndOfParagraph && | 1157 if ((selectionStartWasStartOfParagraph && selectionEndWasEndOfParagraph && |
| 1131 !startIsInsideMailBlockquote) || | 1158 !startIsInsideMailBlockquote) || |
| 1132 enclosingBlockOfVisibleStart == currentRoot || | 1159 enclosingBlockOfVisibleStart == currentRoot || |
| 1133 isListItem(enclosingBlockOfVisibleStart) || selectionIsPlainText) { | 1160 isListItem(enclosingBlockOfVisibleStart) || selectionIsPlainText) { |
| 1134 m_preventNesting = false; | 1161 m_preventNesting = false; |
| 1135 } | 1162 } |
| 1136 | 1163 |
| 1137 if (selection.isRange()) { | 1164 if (selection.isRange()) { |
| 1138 // When the end of the selection being pasted into is at the end of a paragr
aph, and that selection | 1165 // When the end of the selection being pasted into is at the end of a |
| 1139 // spans multiple blocks, not merging may leave an empty line. | 1166 // paragraph, and that selection spans multiple blocks, not merging may |
| 1140 // When the start of the selection being pasted into is at the start of a bl
ock, not merging | 1167 // leave an empty line. |
| 1141 // will leave hanging block(s). | 1168 // When the start of the selection being pasted into is at the start of a |
| 1142 // Merge blocks if the start of the selection was in a Mail blockquote, sinc
e we handle | 1169 // block, not merging will leave hanging block(s). |
| 1143 // that case specially to prevent nesting. | 1170 // Merge blocks if the start of the selection was in a Mail blockquote, |
| 1171 // since we handle that case specially to prevent nesting. |
| 1144 bool mergeBlocksAfterDelete = startIsInsideMailBlockquote || | 1172 bool mergeBlocksAfterDelete = startIsInsideMailBlockquote || |
| 1145 isEndOfParagraphDeprecated(visibleEnd) || | 1173 isEndOfParagraphDeprecated(visibleEnd) || |
| 1146 isStartOfBlock(visibleStart); | 1174 isStartOfBlock(visibleStart); |
| 1147 // FIXME: We should only expand to include fully selected special elements i
f we are copying a | 1175 // FIXME: We should only expand to include fully selected special elements |
| 1148 // selection and pasting it on top of itself. | 1176 // if we are copying a selection and pasting it on top of itself. |
| 1149 deleteSelection(editingState, false, mergeBlocksAfterDelete, false); | 1177 deleteSelection(editingState, false, mergeBlocksAfterDelete, false); |
| 1150 if (editingState->isAborted()) | 1178 if (editingState->isAborted()) |
| 1151 return; | 1179 return; |
| 1152 if (fragment.hasInterchangeNewlineAtStart()) { | 1180 if (fragment.hasInterchangeNewlineAtStart()) { |
| 1153 VisiblePosition startAfterDelete = | 1181 VisiblePosition startAfterDelete = |
| 1154 endingSelection().visibleStartDeprecated(); | 1182 endingSelection().visibleStartDeprecated(); |
| 1155 if (isEndOfParagraphDeprecated(startAfterDelete) && | 1183 if (isEndOfParagraphDeprecated(startAfterDelete) && |
| 1156 !isStartOfParagraphDeprecated(startAfterDelete) && | 1184 !isStartOfParagraphDeprecated(startAfterDelete) && |
| 1157 !isEndOfEditableOrNonEditableContent(startAfterDelete)) | 1185 !isEndOfEditableOrNonEditableContent(startAfterDelete)) |
| 1158 setEndingSelection(nextPositionOf(startAfterDelete)); | 1186 setEndingSelection(nextPositionOf(startAfterDelete)); |
| 1159 else | 1187 else |
| 1160 insertParagraphSeparator(editingState); | 1188 insertParagraphSeparator(editingState); |
| 1161 if (editingState->isAborted()) | 1189 if (editingState->isAborted()) |
| 1162 return; | 1190 return; |
| 1163 } | 1191 } |
| 1164 } else { | 1192 } else { |
| 1165 DCHECK(selection.isCaret()); | 1193 DCHECK(selection.isCaret()); |
| 1166 if (fragment.hasInterchangeNewlineAtStart()) { | 1194 if (fragment.hasInterchangeNewlineAtStart()) { |
| 1167 const VisiblePosition next = | 1195 const VisiblePosition next = |
| 1168 nextPositionOf(visibleStart, CannotCrossEditingBoundary); | 1196 nextPositionOf(visibleStart, CannotCrossEditingBoundary); |
| 1169 if (isEndOfParagraphDeprecated(visibleStart) && | 1197 if (isEndOfParagraphDeprecated(visibleStart) && |
| 1170 !isStartOfParagraphDeprecated(visibleStart) && next.isNotNull()) | 1198 !isStartOfParagraphDeprecated(visibleStart) && next.isNotNull()) |
| 1171 setEndingSelection(next); | 1199 setEndingSelection(next); |
| 1172 else | 1200 else |
| 1173 insertParagraphSeparator(editingState); | 1201 insertParagraphSeparator(editingState); |
| 1174 if (editingState->isAborted()) | 1202 if (editingState->isAborted()) |
| 1175 return; | 1203 return; |
| 1176 } | 1204 } |
| 1177 // We split the current paragraph in two to avoid nesting the blocks from th
e fragment inside the current block. | 1205 // We split the current paragraph in two to avoid nesting the blocks from |
| 1178 // For example paste <div>foo</div><div>bar</div><div>baz</div> into <div>x^
x</div>, where ^ is the caret. | 1206 // the fragment inside the current block. |
| 1179 // As long as the div styles are the same, visually you'd expect: <div>xbar
</div><div>bar</div><div>bazx</div>, | 1207 // |
| 1180 // not <div>xbar<div>bar</div><div>bazx</div></div>. | 1208 // For example, paste |
| 1209 // <div>foo</div><div>bar</div><div>baz</div> |
| 1210 // into |
| 1211 // <div>x^x</div> |
| 1212 // where ^ is the caret. |
| 1213 // |
| 1214 // As long as the div styles are the same, visually you'd expect: |
| 1215 // <div>xbar</div><div>bar</div><div>bazx</div> |
| 1216 // not |
| 1217 // <div>xbar<div>bar</div><div>bazx</div></div> |
| 1181 // Don't do this if the selection started in a Mail blockquote. | 1218 // Don't do this if the selection started in a Mail blockquote. |
| 1182 if (m_preventNesting && !startIsInsideMailBlockquote && | 1219 if (m_preventNesting && !startIsInsideMailBlockquote && |
| 1183 !isEndOfParagraphDeprecated( | 1220 !isEndOfParagraphDeprecated( |
| 1184 endingSelection().visibleStartDeprecated()) && | 1221 endingSelection().visibleStartDeprecated()) && |
| 1185 !isStartOfParagraphDeprecated( | 1222 !isStartOfParagraphDeprecated( |
| 1186 endingSelection().visibleStartDeprecated())) { | 1223 endingSelection().visibleStartDeprecated())) { |
| 1187 insertParagraphSeparator(editingState); | 1224 insertParagraphSeparator(editingState); |
| 1188 if (editingState->isAborted()) | 1225 if (editingState->isAborted()) |
| 1189 return; | 1226 return; |
| 1190 setEndingSelection( | 1227 setEndingSelection( |
| 1191 previousPositionOf(endingSelection().visibleStartDeprecated())); | 1228 previousPositionOf(endingSelection().visibleStartDeprecated())); |
| 1192 } | 1229 } |
| 1193 } | 1230 } |
| 1194 | 1231 |
| 1195 Position insertionPos = endingSelection().start(); | 1232 Position insertionPos = endingSelection().start(); |
| 1196 // We don't want any of the pasted content to end up nested in a Mail blockquo
te, so first break | 1233 // We don't want any of the pasted content to end up nested in a Mail |
| 1197 // out of any surrounding Mail blockquotes. Unless we're inserting in a table,
in which case | 1234 // blockquote, so first break out of any surrounding Mail blockquotes. Unless |
| 1198 // breaking the blockquote will prevent the content from actually being insert
ed in the table. | 1235 // we're inserting in a table, in which case breaking the blockquote will |
| 1236 // prevent the content from actually being inserted in the table. |
| 1199 if (enclosingNodeOfType(insertionPos, isMailHTMLBlockquoteElement, | 1237 if (enclosingNodeOfType(insertionPos, isMailHTMLBlockquoteElement, |
| 1200 CanCrossEditingBoundary) && | 1238 CanCrossEditingBoundary) && |
| 1201 m_preventNesting && | 1239 m_preventNesting && |
| 1202 !(enclosingNodeOfType(insertionPos, &isTableStructureNode))) { | 1240 !(enclosingNodeOfType(insertionPos, &isTableStructureNode))) { |
| 1203 applyCommandToComposite(BreakBlockquoteCommand::create(document()), | 1241 applyCommandToComposite(BreakBlockquoteCommand::create(document()), |
| 1204 editingState); | 1242 editingState); |
| 1205 if (editingState->isAborted()) | 1243 if (editingState->isAborted()) |
| 1206 return; | 1244 return; |
| 1207 // This will leave a br between the split. | 1245 // This will leave a br between the split. |
| 1208 Node* br = endingSelection().start().anchorNode(); | 1246 Node* br = endingSelection().start().anchorNode(); |
| 1209 DCHECK(isHTMLBRElement(br)) << br; | 1247 DCHECK(isHTMLBRElement(br)) << br; |
| 1210 // Insert content between the two blockquotes, but remove the br (since it w
as just a placeholder). | 1248 // Insert content between the two blockquotes, but remove the br (since it |
| 1249 // was just a placeholder). |
| 1211 insertionPos = Position::inParentBeforeNode(*br); | 1250 insertionPos = Position::inParentBeforeNode(*br); |
| 1212 removeNode(br, editingState); | 1251 removeNode(br, editingState); |
| 1213 if (editingState->isAborted()) | 1252 if (editingState->isAborted()) |
| 1214 return; | 1253 return; |
| 1215 } | 1254 } |
| 1216 | 1255 |
| 1217 // Inserting content could cause whitespace to collapse, e.g. inserting <div>f
oo</div> into hello^ world. | 1256 // Inserting content could cause whitespace to collapse, e.g. inserting |
| 1257 // <div>foo</div> into hello^ world. |
| 1218 prepareWhitespaceAtPositionForSplit(insertionPos); | 1258 prepareWhitespaceAtPositionForSplit(insertionPos); |
| 1219 | 1259 |
| 1220 // If the downstream node has been removed there's no point in continuing. | 1260 // If the downstream node has been removed there's no point in continuing. |
| 1221 if (!mostForwardCaretPosition(insertionPos).anchorNode()) | 1261 if (!mostForwardCaretPosition(insertionPos).anchorNode()) |
| 1222 return; | 1262 return; |
| 1223 | 1263 |
| 1224 // NOTE: This would be an incorrect usage of downstream() if downstream() were
changed to mean the last position after | 1264 // NOTE: This would be an incorrect usage of downstream() if downstream() were |
| 1225 // 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 | 1265 // changed to mean the last position after p that maps to the same visible |
| 1226 // away, there are positions after the br which map to the same visible positi
on as [br, 0]). | 1266 // position as p (since in the case where a br is at the end of a block and |
| 1267 // collapsed away, there are positions after the br which map to the same |
| 1268 // visible position as [br, 0]). |
| 1227 HTMLBRElement* endBR = | 1269 HTMLBRElement* endBR = |
| 1228 isHTMLBRElement(*mostForwardCaretPosition(insertionPos).anchorNode()) | 1270 isHTMLBRElement(*mostForwardCaretPosition(insertionPos).anchorNode()) |
| 1229 ? toHTMLBRElement(mostForwardCaretPosition(insertionPos).anchorNode()) | 1271 ? toHTMLBRElement(mostForwardCaretPosition(insertionPos).anchorNode()) |
| 1230 : 0; | 1272 : 0; |
| 1231 VisiblePosition originalVisPosBeforeEndBR; | 1273 VisiblePosition originalVisPosBeforeEndBR; |
| 1232 if (endBR) | 1274 if (endBR) |
| 1233 originalVisPosBeforeEndBR = | 1275 originalVisPosBeforeEndBR = |
| 1234 previousPositionOf(VisiblePosition::beforeNode(endBR)); | 1276 previousPositionOf(VisiblePosition::beforeNode(endBR)); |
| 1235 | 1277 |
| 1236 Element* enclosingBlockOfInsertionPos = | 1278 Element* enclosingBlockOfInsertionPos = |
| (...skipping 16 matching lines...) Expand all Loading... |
| 1253 insertionPos = | 1295 insertionPos = |
| 1254 Position::inParentBeforeNode(*enclosingBlockOfInsertionPos); | 1296 Position::inParentBeforeNode(*enclosingBlockOfInsertionPos); |
| 1255 } | 1297 } |
| 1256 | 1298 |
| 1257 // Paste at start or end of link goes outside of link. | 1299 // Paste at start or end of link goes outside of link. |
| 1258 insertionPos = | 1300 insertionPos = |
| 1259 positionAvoidingSpecialElementBoundary(insertionPos, editingState); | 1301 positionAvoidingSpecialElementBoundary(insertionPos, editingState); |
| 1260 if (editingState->isAborted()) | 1302 if (editingState->isAborted()) |
| 1261 return; | 1303 return; |
| 1262 | 1304 |
| 1263 // FIXME: Can this wait until after the operation has been performed? There d
oesn't seem to be | 1305 // FIXME: Can this wait until after the operation has been performed? There |
| 1264 // any work performed after this that queries or uses the typing style. | 1306 // doesn't seem to be any work performed after this that queries or uses the |
| 1307 // typing style. |
| 1265 if (LocalFrame* frame = document().frame()) | 1308 if (LocalFrame* frame = document().frame()) |
| 1266 frame->selection().clearTypingStyle(); | 1309 frame->selection().clearTypingStyle(); |
| 1267 | 1310 |
| 1268 removeHeadContents(fragment); | 1311 removeHeadContents(fragment); |
| 1269 | 1312 |
| 1270 // We don't want the destination to end up inside nodes that weren't selected.
To avoid that, we move the | 1313 // We don't want the destination to end up inside nodes that weren't selected. |
| 1271 // position forward without changing the visible position so we're still at th
e same visible location, but | 1314 // To avoid that, we move the position forward without changing the visible |
| 1272 // outside of preceding tags. | 1315 // position so we're still at the same visible location, but outside of |
| 1316 // preceding tags. |
| 1273 insertionPos = positionAvoidingPrecedingNodes(insertionPos); | 1317 insertionPos = positionAvoidingPrecedingNodes(insertionPos); |
| 1274 | 1318 |
| 1275 // Paste into run of tabs splits the tab span. | 1319 // Paste into run of tabs splits the tab span. |
| 1276 insertionPos = positionOutsideTabSpan(insertionPos); | 1320 insertionPos = positionOutsideTabSpan(insertionPos); |
| 1277 | 1321 |
| 1278 bool handledStyleSpans = | 1322 bool handledStyleSpans = |
| 1279 handleStyleSpansBeforeInsertion(fragment, insertionPos); | 1323 handleStyleSpansBeforeInsertion(fragment, insertionPos); |
| 1280 | 1324 |
| 1281 // We're finished if there is nothing to add. | 1325 // We're finished if there is nothing to add. |
| 1282 if (fragment.isEmpty() || !fragment.firstChild()) | 1326 if (fragment.isEmpty() || !fragment.firstChild()) |
| (...skipping 23 matching lines...) Expand all Loading... |
| 1306 Node* splitStart = insertionPos.computeNodeAfterPosition(); | 1350 Node* splitStart = insertionPos.computeNodeAfterPosition(); |
| 1307 if (!splitStart) | 1351 if (!splitStart) |
| 1308 splitStart = insertionPos.computeContainerNode(); | 1352 splitStart = insertionPos.computeContainerNode(); |
| 1309 Node* nodeToSplitTo = | 1353 Node* nodeToSplitTo = |
| 1310 splitTreeToNode(splitStart, elementToSplitTo->parentNode()); | 1354 splitTreeToNode(splitStart, elementToSplitTo->parentNode()); |
| 1311 insertionPos = Position::inParentBeforeNode(*nodeToSplitTo); | 1355 insertionPos = Position::inParentBeforeNode(*nodeToSplitTo); |
| 1312 } | 1356 } |
| 1313 } | 1357 } |
| 1314 } | 1358 } |
| 1315 | 1359 |
| 1316 // FIXME: When pasting rich content we're often prevented from heading down th
e fast path by style spans. Try | 1360 // FIXME: When pasting rich content we're often prevented from heading down |
| 1317 // again here if they've been removed. | 1361 // the fast path by style spans. Try again here if they've been removed. |
| 1318 | 1362 |
| 1319 // 1) Insert the content. | 1363 // 1) Insert the content. |
| 1320 // 2) Remove redundant styles and style tags, this inner <b> for example: <b>f
oo <b>bar</b> baz</b>. | 1364 // 2) Remove redundant styles and style tags, this inner <b> for example: |
| 1321 // 3) Merge the start of the added content with the content before the positio
n being pasted into. | 1365 // <b>foo <b>bar</b> baz</b>. |
| 1322 // 4) Do one of the following: a) expand the last br if the fragment ends with
one and it collapsed, | 1366 // 3) Merge the start of the added content with the content before the |
| 1323 // b) merge the last paragraph of the incoming fragment with the paragraph tha
t contained the | 1367 // position being pasted into. |
| 1324 // end of the selection that was pasted into, or c) handle an interchange newl
ine at the end of the | 1368 // 4) Do one of the following: |
| 1325 // incoming fragment. | 1369 // a) expand the last br if the fragment ends with one and it collapsed, |
| 1370 // b) merge the last paragraph of the incoming fragment with the paragraph |
| 1371 // that contained the end of the selection that was pasted into, or |
| 1372 // c) handle an interchange newline at the end of the incoming fragment. |
| 1326 // 5) Add spaces for smart replace. | 1373 // 5) Add spaces for smart replace. |
| 1327 // 6) Select the replacement if requested, and match style if requested. | 1374 // 6) Select the replacement if requested, and match style if requested. |
| 1328 | 1375 |
| 1329 InsertedNodes insertedNodes; | 1376 InsertedNodes insertedNodes; |
| 1330 Node* refNode = fragment.firstChild(); | 1377 Node* refNode = fragment.firstChild(); |
| 1331 DCHECK(refNode); | 1378 DCHECK(refNode); |
| 1332 Node* node = refNode->nextSibling(); | 1379 Node* node = refNode->nextSibling(); |
| 1333 | 1380 |
| 1334 fragment.removeNode(refNode); | 1381 fragment.removeNode(refNode); |
| 1335 | 1382 |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1381 handleStyleSpans(insertedNodes, editingState); | 1428 handleStyleSpans(insertedNodes, editingState); |
| 1382 if (editingState->isAborted()) | 1429 if (editingState->isAborted()) |
| 1383 return; | 1430 return; |
| 1384 } | 1431 } |
| 1385 | 1432 |
| 1386 // Mutation events (bug 20161) may have already removed the inserted content | 1433 // Mutation events (bug 20161) may have already removed the inserted content |
| 1387 if (!insertedNodes.firstNodeInserted() || | 1434 if (!insertedNodes.firstNodeInserted() || |
| 1388 !insertedNodes.firstNodeInserted()->isConnected()) | 1435 !insertedNodes.firstNodeInserted()->isConnected()) |
| 1389 return; | 1436 return; |
| 1390 | 1437 |
| 1391 // Scripts specified in javascript protocol may remove |enclosingBlockOfInsert
ionPos| | 1438 // Scripts specified in javascript protocol may remove |
| 1392 // during insertion, e.g. <iframe src="javascript:..."> | 1439 // |enclosingBlockOfInsertionPos| during insertion, e.g. <iframe |
| 1440 // src="javascript:..."> |
| 1393 if (enclosingBlockOfInsertionPos && | 1441 if (enclosingBlockOfInsertionPos && |
| 1394 !enclosingBlockOfInsertionPos->isConnected()) | 1442 !enclosingBlockOfInsertionPos->isConnected()) |
| 1395 enclosingBlockOfInsertionPos = nullptr; | 1443 enclosingBlockOfInsertionPos = nullptr; |
| 1396 | 1444 |
| 1397 VisiblePosition startOfInsertedContent = createVisiblePositionDeprecated( | 1445 VisiblePosition startOfInsertedContent = createVisiblePositionDeprecated( |
| 1398 firstPositionInOrBeforeNode(insertedNodes.firstNodeInserted())); | 1446 firstPositionInOrBeforeNode(insertedNodes.firstNodeInserted())); |
| 1399 | 1447 |
| 1400 // We inserted before the enclosingBlockOfInsertionPos to prevent nesting, and
the content before the enclosingBlockOfInsertionPos wasn't in its own block and | 1448 // We inserted before the enclosingBlockOfInsertionPos to prevent nesting, and |
| 1401 // didn't have a br after it, so the inserted content ended up in the same par
agraph. | 1449 // the content before the enclosingBlockOfInsertionPos wasn't in its own block |
| 1450 // and didn't have a br after it, so the inserted content ended up in the same |
| 1451 // paragraph. |
| 1402 if (!startOfInsertedContent.isNull() && enclosingBlockOfInsertionPos && | 1452 if (!startOfInsertedContent.isNull() && enclosingBlockOfInsertionPos && |
| 1403 insertionPos.anchorNode() == enclosingBlockOfInsertionPos->parentNode() && | 1453 insertionPos.anchorNode() == enclosingBlockOfInsertionPos->parentNode() && |
| 1404 (unsigned)insertionPos.computeEditingOffset() < | 1454 (unsigned)insertionPos.computeEditingOffset() < |
| 1405 enclosingBlockOfInsertionPos->nodeIndex() && | 1455 enclosingBlockOfInsertionPos->nodeIndex() && |
| 1406 !isStartOfParagraphDeprecated(startOfInsertedContent)) { | 1456 !isStartOfParagraphDeprecated(startOfInsertedContent)) { |
| 1407 insertNodeAt(HTMLBRElement::create(document()), | 1457 insertNodeAt(HTMLBRElement::create(document()), |
| 1408 startOfInsertedContent.deepEquivalent(), editingState); | 1458 startOfInsertedContent.deepEquivalent(), editingState); |
| 1409 if (editingState->isAborted()) | 1459 if (editingState->isAborted()) |
| 1410 return; | 1460 return; |
| 1411 } | 1461 } |
| (...skipping 26 matching lines...) Expand all Loading... |
| 1438 | 1488 |
| 1439 if (m_sanitizeFragment && insertedNodes.firstNodeInserted()) { | 1489 if (m_sanitizeFragment && insertedNodes.firstNodeInserted()) { |
| 1440 applyCommandToComposite(SimplifyMarkupCommand::create( | 1490 applyCommandToComposite(SimplifyMarkupCommand::create( |
| 1441 document(), insertedNodes.firstNodeInserted(), | 1491 document(), insertedNodes.firstNodeInserted(), |
| 1442 insertedNodes.pastLastLeaf()), | 1492 insertedNodes.pastLastLeaf()), |
| 1443 editingState); | 1493 editingState); |
| 1444 if (editingState->isAborted()) | 1494 if (editingState->isAborted()) |
| 1445 return; | 1495 return; |
| 1446 } | 1496 } |
| 1447 | 1497 |
| 1448 // Setup m_startOfInsertedContent and m_endOfInsertedContent. This should be t
he last two lines of code that access insertedNodes. | 1498 // Setup m_startOfInsertedContent and m_endOfInsertedContent. This should be |
| 1499 // the last two lines of code that access insertedNodes. |
| 1449 m_startOfInsertedContent = | 1500 m_startOfInsertedContent = |
| 1450 firstPositionInOrBeforeNode(insertedNodes.firstNodeInserted()); | 1501 firstPositionInOrBeforeNode(insertedNodes.firstNodeInserted()); |
| 1451 m_endOfInsertedContent = | 1502 m_endOfInsertedContent = |
| 1452 lastPositionInOrAfterNode(insertedNodes.lastLeafInserted()); | 1503 lastPositionInOrAfterNode(insertedNodes.lastLeafInserted()); |
| 1453 | 1504 |
| 1454 // Determine whether or not we should merge the end of inserted content with w
hat's after it before we do | 1505 // Determine whether or not we should merge the end of inserted content with |
| 1455 // the start merge so that the start merge doesn't effect our decision. | 1506 // what's after it before we do the start merge so that the start merge |
| 1507 // doesn't effect our decision. |
| 1456 m_shouldMergeEnd = shouldMergeEnd(selectionEndWasEndOfParagraph); | 1508 m_shouldMergeEnd = shouldMergeEnd(selectionEndWasEndOfParagraph); |
| 1457 | 1509 |
| 1458 if (shouldMergeStart(selectionStartWasStartOfParagraph, | 1510 if (shouldMergeStart(selectionStartWasStartOfParagraph, |
| 1459 fragment.hasInterchangeNewlineAtStart(), | 1511 fragment.hasInterchangeNewlineAtStart(), |
| 1460 startIsInsideMailBlockquote)) { | 1512 startIsInsideMailBlockquote)) { |
| 1461 VisiblePosition startOfParagraphToMove = positionAtStartOfInsertedContent(); | 1513 VisiblePosition startOfParagraphToMove = positionAtStartOfInsertedContent(); |
| 1462 VisiblePosition destination = previousPositionOf(startOfParagraphToMove); | 1514 VisiblePosition destination = previousPositionOf(startOfParagraphToMove); |
| 1463 // We need to handle the case where we need to merge the end | 1515 // We need to handle the case where we need to merge the end |
| 1464 // but our destination node is inside an inline that is the last in the bloc
k. | 1516 // but our destination node is inside an inline that is the last in the |
| 1465 // We insert a placeholder before the newly inserted content to avoid being
merged into the inline. | 1517 // block. |
| 1518 // We insert a placeholder before the newly inserted content to avoid being |
| 1519 // merged into the inline. |
| 1466 Node* destinationNode = destination.deepEquivalent().anchorNode(); | 1520 Node* destinationNode = destination.deepEquivalent().anchorNode(); |
| 1467 if (m_shouldMergeEnd && | 1521 if (m_shouldMergeEnd && |
| 1468 destinationNode != enclosingInline(destinationNode) && | 1522 destinationNode != enclosingInline(destinationNode) && |
| 1469 enclosingInline(destinationNode)->nextSibling()) { | 1523 enclosingInline(destinationNode)->nextSibling()) { |
| 1470 insertNodeBefore(HTMLBRElement::create(document()), refNode, | 1524 insertNodeBefore(HTMLBRElement::create(document()), refNode, |
| 1471 editingState); | 1525 editingState); |
| 1472 if (editingState->isAborted()) | 1526 if (editingState->isAborted()) |
| 1473 return; | 1527 return; |
| 1474 } | 1528 } |
| 1475 | 1529 |
| 1476 // Merging the the first paragraph of inserted content with the content that
came | 1530 // Merging the the first paragraph of inserted content with the content that |
| 1477 // before the selection that was pasted into would also move content after | 1531 // came before the selection that was pasted into would also move content |
| 1478 // the selection that was pasted into if: only one paragraph was being paste
d, | 1532 // after the selection that was pasted into if: only one paragraph was being |
| 1479 // and it was not wrapped in a block, the selection that was pasted into end
ed | 1533 // pasted, and it was not wrapped in a block, the selection that was pasted |
| 1480 // at the end of a block and the next paragraph didn't start at the start of
a block. | 1534 // into ended at the end of a block and the next paragraph didn't start at |
| 1481 // Insert a line break just after the inserted content to separate it from w
hat | 1535 // the start of a block. |
| 1482 // comes after and prevent that from happening. | 1536 // Insert a line break just after the inserted content to separate it from |
| 1537 // what comes after and prevent that from happening. |
| 1483 VisiblePosition endOfInsertedContent = positionAtEndOfInsertedContent(); | 1538 VisiblePosition endOfInsertedContent = positionAtEndOfInsertedContent(); |
| 1484 if (startOfParagraphDeprecated(endOfInsertedContent).deepEquivalent() == | 1539 if (startOfParagraphDeprecated(endOfInsertedContent).deepEquivalent() == |
| 1485 startOfParagraphToMove.deepEquivalent()) { | 1540 startOfParagraphToMove.deepEquivalent()) { |
| 1486 insertNodeAt(HTMLBRElement::create(document()), | 1541 insertNodeAt(HTMLBRElement::create(document()), |
| 1487 endOfInsertedContent.deepEquivalent(), editingState); | 1542 endOfInsertedContent.deepEquivalent(), editingState); |
| 1488 if (editingState->isAborted()) | 1543 if (editingState->isAborted()) |
| 1489 return; | 1544 return; |
| 1490 // Mutation events (bug 22634) triggered by inserting the <br> might have
removed the content we're about to move | 1545 // Mutation events (bug 22634) triggered by inserting the <br> might have |
| 1546 // removed the content we're about to move |
| 1491 if (!startOfParagraphToMove.deepEquivalent().isConnected()) | 1547 if (!startOfParagraphToMove.deepEquivalent().isConnected()) |
| 1492 return; | 1548 return; |
| 1493 } | 1549 } |
| 1494 | 1550 |
| 1495 // FIXME: Maintain positions for the start and end of inserted content inste
ad of keeping nodes. The nodes are | 1551 // FIXME: Maintain positions for the start and end of inserted content |
| 1496 // only ever used to create positions where inserted content starts/ends. | 1552 // instead of keeping nodes. The nodes are only ever used to create |
| 1553 // positions where inserted content starts/ends. |
| 1497 moveParagraph(startOfParagraphToMove, | 1554 moveParagraph(startOfParagraphToMove, |
| 1498 endOfParagraphDeprecated(startOfParagraphToMove), destination, | 1555 endOfParagraphDeprecated(startOfParagraphToMove), destination, |
| 1499 editingState); | 1556 editingState); |
| 1500 if (editingState->isAborted()) | 1557 if (editingState->isAborted()) |
| 1501 return; | 1558 return; |
| 1502 m_startOfInsertedContent = mostForwardCaretPosition( | 1559 m_startOfInsertedContent = mostForwardCaretPosition( |
| 1503 endingSelection().visibleStartDeprecated().deepEquivalent()); | 1560 endingSelection().visibleStartDeprecated().deepEquivalent()); |
| 1504 if (m_endOfInsertedContent.isOrphan()) | 1561 if (m_endOfInsertedContent.isOrphan()) |
| 1505 m_endOfInsertedContent = mostBackwardCaretPosition( | 1562 m_endOfInsertedContent = mostBackwardCaretPosition( |
| 1506 endingSelection().visibleEndDeprecated().deepEquivalent()); | 1563 endingSelection().visibleEndDeprecated().deepEquivalent()); |
| (...skipping 24 matching lines...) Expand all Loading... |
| 1531 setEndingSelection(endOfInsertedContent); | 1588 setEndingSelection(endOfInsertedContent); |
| 1532 Element* enclosingBlockElement = | 1589 Element* enclosingBlockElement = |
| 1533 enclosingBlock(endOfInsertedContent.deepEquivalent().anchorNode()); | 1590 enclosingBlock(endOfInsertedContent.deepEquivalent().anchorNode()); |
| 1534 if (isListItem(enclosingBlockElement)) { | 1591 if (isListItem(enclosingBlockElement)) { |
| 1535 HTMLLIElement* newListItem = HTMLLIElement::create(document()); | 1592 HTMLLIElement* newListItem = HTMLLIElement::create(document()); |
| 1536 insertNodeAfter(newListItem, enclosingBlockElement, editingState); | 1593 insertNodeAfter(newListItem, enclosingBlockElement, editingState); |
| 1537 if (editingState->isAborted()) | 1594 if (editingState->isAborted()) |
| 1538 return; | 1595 return; |
| 1539 setEndingSelection(VisiblePosition::firstPositionInNode(newListItem)); | 1596 setEndingSelection(VisiblePosition::firstPositionInNode(newListItem)); |
| 1540 } else { | 1597 } else { |
| 1541 // Use a default paragraph element (a plain div) for the empty paragra
ph, using the last paragraph | 1598 // Use a default paragraph element (a plain div) for the empty |
| 1542 // block's style seems to annoy users. | 1599 // paragraph, using the last paragraph block's style seems to annoy |
| 1600 // users. |
| 1543 insertParagraphSeparator( | 1601 insertParagraphSeparator( |
| 1544 editingState, true, | 1602 editingState, true, |
| 1545 !startIsInsideMailBlockquote && | 1603 !startIsInsideMailBlockquote && |
| 1546 highestEnclosingNodeOfType( | 1604 highestEnclosingNodeOfType( |
| 1547 endOfInsertedContent.deepEquivalent(), | 1605 endOfInsertedContent.deepEquivalent(), |
| 1548 isMailHTMLBlockquoteElement, CannotCrossEditingBoundary, | 1606 isMailHTMLBlockquoteElement, CannotCrossEditingBoundary, |
| 1549 insertedNodes.firstNodeInserted()->parentNode())); | 1607 insertedNodes.firstNodeInserted()->parentNode())); |
| 1550 if (editingState->isAborted()) | 1608 if (editingState->isAborted()) |
| 1551 return; | 1609 return; |
| 1552 } | 1610 } |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1596 // Don't remove the br if nothing was inserted. | 1654 // Don't remove the br if nothing was inserted. |
| 1597 if (previousPositionOf(visiblePos).deepEquivalent() == | 1655 if (previousPositionOf(visiblePos).deepEquivalent() == |
| 1598 originalVisPosBeforeEndBR.deepEquivalent()) | 1656 originalVisPosBeforeEndBR.deepEquivalent()) |
| 1599 return false; | 1657 return false; |
| 1600 | 1658 |
| 1601 // Remove the br if it is collapsed away and so is unnecessary. | 1659 // Remove the br if it is collapsed away and so is unnecessary. |
| 1602 if (!document().inNoQuirksMode() && isEndOfBlock(visiblePos) && | 1660 if (!document().inNoQuirksMode() && isEndOfBlock(visiblePos) && |
| 1603 !isStartOfParagraphDeprecated(visiblePos)) | 1661 !isStartOfParagraphDeprecated(visiblePos)) |
| 1604 return true; | 1662 return true; |
| 1605 | 1663 |
| 1606 // A br that was originally holding a line open should be displaced by inserte
d content or turned into a line break. | 1664 // A br that was originally holding a line open should be displaced by |
| 1607 // A br that was originally acting as a line break should still be acting as a
line break, not as a placeholder. | 1665 // inserted content or turned into a line break. |
| 1666 // A br that was originally acting as a line break should still be acting as a |
| 1667 // line break, not as a placeholder. |
| 1608 return isStartOfParagraphDeprecated(visiblePos) && | 1668 return isStartOfParagraphDeprecated(visiblePos) && |
| 1609 isEndOfParagraphDeprecated(visiblePos); | 1669 isEndOfParagraphDeprecated(visiblePos); |
| 1610 } | 1670 } |
| 1611 | 1671 |
| 1612 bool ReplaceSelectionCommand::shouldPerformSmartReplace() const { | 1672 bool ReplaceSelectionCommand::shouldPerformSmartReplace() const { |
| 1613 if (!m_smartReplace) | 1673 if (!m_smartReplace) |
| 1614 return false; | 1674 return false; |
| 1615 | 1675 |
| 1616 HTMLTextFormControlElement* textControl = enclosingTextFormControl( | 1676 HTMLTextFormControlElement* textControl = enclosingTextFormControl( |
| 1617 positionAtStartOfInsertedContent().deepEquivalent()); | 1677 positionAtStartOfInsertedContent().deepEquivalent()); |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1692 if (startNode->isTextNode()) { | 1752 if (startNode->isTextNode()) { |
| 1693 insertTextIntoNode(toText(startNode), startOffset, | 1753 insertTextIntoNode(toText(startNode), startOffset, |
| 1694 collapseWhiteSpace ? nonBreakingSpaceString() : " "); | 1754 collapseWhiteSpace ? nonBreakingSpaceString() : " "); |
| 1695 if (m_endOfInsertedContent.computeContainerNode() == startNode && | 1755 if (m_endOfInsertedContent.computeContainerNode() == startNode && |
| 1696 m_endOfInsertedContent.offsetInContainerNode()) | 1756 m_endOfInsertedContent.offsetInContainerNode()) |
| 1697 m_endOfInsertedContent = Position( | 1757 m_endOfInsertedContent = Position( |
| 1698 startNode, m_endOfInsertedContent.offsetInContainerNode() + 1); | 1758 startNode, m_endOfInsertedContent.offsetInContainerNode() + 1); |
| 1699 } else { | 1759 } else { |
| 1700 Text* node = document().createEditingTextNode( | 1760 Text* node = document().createEditingTextNode( |
| 1701 collapseWhiteSpace ? nonBreakingSpaceString() : " "); | 1761 collapseWhiteSpace ? nonBreakingSpaceString() : " "); |
| 1702 // Don't updateNodesInserted. Doing so would set m_endOfInsertedContent to
be the node containing the leading space, | 1762 // Don't updateNodesInserted. Doing so would set m_endOfInsertedContent to |
| 1703 // but m_endOfInsertedContent is supposed to mark the end of pasted conten
t. | 1763 // be the node containing the leading space, but m_endOfInsertedContent is |
| 1764 // supposed to mark the end of pasted content. |
| 1704 insertNodeBefore(node, startNode, editingState); | 1765 insertNodeBefore(node, startNode, editingState); |
| 1705 if (editingState->isAborted()) | 1766 if (editingState->isAborted()) |
| 1706 return; | 1767 return; |
| 1707 m_startOfInsertedContent = Position::firstPositionInNode(node); | 1768 m_startOfInsertedContent = Position::firstPositionInNode(node); |
| 1708 } | 1769 } |
| 1709 } | 1770 } |
| 1710 } | 1771 } |
| 1711 | 1772 |
| 1712 void ReplaceSelectionCommand::completeHTMLReplacement( | 1773 void ReplaceSelectionCommand::completeHTMLReplacement( |
| 1713 const Position& lastPositionToSelect, | 1774 const Position& lastPositionToSelect, |
| 1714 EditingState* editingState) { | 1775 EditingState* editingState) { |
| 1715 Position start = positionAtStartOfInsertedContent().deepEquivalent(); | 1776 Position start = positionAtStartOfInsertedContent().deepEquivalent(); |
| 1716 Position end = positionAtEndOfInsertedContent().deepEquivalent(); | 1777 Position end = positionAtEndOfInsertedContent().deepEquivalent(); |
| 1717 | 1778 |
| 1718 // Mutation events may have deleted start or end | 1779 // Mutation events may have deleted start or end |
| 1719 if (start.isNotNull() && !start.isOrphan() && end.isNotNull() && | 1780 if (start.isNotNull() && !start.isOrphan() && end.isNotNull() && |
| 1720 !end.isOrphan()) { | 1781 !end.isOrphan()) { |
| 1721 // FIXME (11475): Remove this and require that the creator of the fragment t
o use nbsps. | 1782 // FIXME (11475): Remove this and require that the creator of the fragment |
| 1783 // to use nbsps. |
| 1722 rebalanceWhitespaceAt(start); | 1784 rebalanceWhitespaceAt(start); |
| 1723 rebalanceWhitespaceAt(end); | 1785 rebalanceWhitespaceAt(end); |
| 1724 | 1786 |
| 1725 if (m_matchStyle) { | 1787 if (m_matchStyle) { |
| 1726 DCHECK(m_insertionStyle); | 1788 DCHECK(m_insertionStyle); |
| 1727 applyStyle(m_insertionStyle.get(), start, end, editingState); | 1789 applyStyle(m_insertionStyle.get(), start, end, editingState); |
| 1728 if (editingState->isAborted()) | 1790 if (editingState->isAborted()) |
| 1729 return; | 1791 return; |
| 1730 } | 1792 } |
| 1731 | 1793 |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1834 else | 1896 else |
| 1835 updatePositionForNodeRemoval(positionOnlyToBeUpdated, *next); | 1897 updatePositionForNodeRemoval(positionOnlyToBeUpdated, *next); |
| 1836 | 1898 |
| 1837 removeNode(next, editingState); | 1899 removeNode(next, editingState); |
| 1838 if (editingState->isAborted()) | 1900 if (editingState->isAborted()) |
| 1839 return; | 1901 return; |
| 1840 } | 1902 } |
| 1841 } | 1903 } |
| 1842 | 1904 |
| 1843 InputEvent::InputType ReplaceSelectionCommand::inputType() const { | 1905 InputEvent::InputType ReplaceSelectionCommand::inputType() const { |
| 1844 // |ReplaceSelectionCommand| could be used with Paste, Drag&Drop, InsertFragme
nt and |TypingCommand|. | 1906 // |ReplaceSelectionCommand| could be used with Paste, Drag&Drop, |
| 1907 // InsertFragment and |TypingCommand|. |
| 1845 // 1. Paste, Drag&Drop, InsertFragment should rely on correct |m_inputType|. | 1908 // 1. Paste, Drag&Drop, InsertFragment should rely on correct |m_inputType|. |
| 1846 // 2. |TypingCommand| will supply the |inputType()|, so |m_inputType| could de
fault to |InputType::None|. | 1909 // 2. |TypingCommand| will supply the |inputType()|, so |m_inputType| could |
| 1910 // default to |InputType::None|. |
| 1847 return m_inputType; | 1911 return m_inputType; |
| 1848 } | 1912 } |
| 1849 | 1913 |
| 1850 // If the user is inserting a list into an existing list, instead of nesting the
list, | 1914 // If the user is inserting a list into an existing list, instead of nesting the |
| 1851 // we put the list items into the existing list. | 1915 // list, we put the list items into the existing list. |
| 1852 Node* ReplaceSelectionCommand::insertAsListItems(HTMLElement* listElement, | 1916 Node* ReplaceSelectionCommand::insertAsListItems(HTMLElement* listElement, |
| 1853 Element* insertionBlock, | 1917 Element* insertionBlock, |
| 1854 const Position& insertPos, | 1918 const Position& insertPos, |
| 1855 InsertedNodes& insertedNodes, | 1919 InsertedNodes& insertedNodes, |
| 1856 EditingState* editingState) { | 1920 EditingState* editingState) { |
| 1857 while (listElement->hasOneChild() && | 1921 while (listElement->hasOneChild() && |
| 1858 isHTMLListElement(listElement->firstChild())) | 1922 isHTMLListElement(listElement->firstChild())) |
| 1859 listElement = toHTMLElement(listElement->firstChild()); | 1923 listElement = toHTMLElement(listElement->firstChild()); |
| 1860 | 1924 |
| 1861 bool isStart = | 1925 bool isStart = |
| (...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1902 if (!node) | 1966 if (!node) |
| 1903 return; | 1967 return; |
| 1904 | 1968 |
| 1905 if (m_startOfInsertedContent.isNull()) | 1969 if (m_startOfInsertedContent.isNull()) |
| 1906 m_startOfInsertedContent = firstPositionInOrBeforeNode(node); | 1970 m_startOfInsertedContent = firstPositionInOrBeforeNode(node); |
| 1907 | 1971 |
| 1908 m_endOfInsertedContent = | 1972 m_endOfInsertedContent = |
| 1909 lastPositionInOrAfterNode(&NodeTraversal::lastWithinOrSelf(*node)); | 1973 lastPositionInOrAfterNode(&NodeTraversal::lastWithinOrSelf(*node)); |
| 1910 } | 1974 } |
| 1911 | 1975 |
| 1912 // During simple pastes, where we're just pasting a text node into a run of text
, we insert the text node | 1976 // During simple pastes, where we're just pasting a text node into a run of |
| 1913 // directly into the text node that holds the selection. This is much faster th
an the generalized code in | 1977 // text, we insert the text node directly into the text node that holds the |
| 1914 // ReplaceSelectionCommand, and works around <https://bugs.webkit.org/show_bug.c
gi?id=6148> since we don't | 1978 // selection. This is much faster than the generalized code in |
| 1915 // split text nodes. | 1979 // ReplaceSelectionCommand, and works around |
| 1980 // <https://bugs.webkit.org/show_bug.cgi?id=6148> since we don't split text |
| 1981 // nodes. |
| 1916 bool ReplaceSelectionCommand::performTrivialReplace( | 1982 bool ReplaceSelectionCommand::performTrivialReplace( |
| 1917 const ReplacementFragment& fragment, | 1983 const ReplacementFragment& fragment, |
| 1918 EditingState* editingState) { | 1984 EditingState* editingState) { |
| 1919 if (!fragment.firstChild() || fragment.firstChild() != fragment.lastChild() || | 1985 if (!fragment.firstChild() || fragment.firstChild() != fragment.lastChild() || |
| 1920 !fragment.firstChild()->isTextNode()) | 1986 !fragment.firstChild()->isTextNode()) |
| 1921 return false; | 1987 return false; |
| 1922 | 1988 |
| 1923 // FIXME: Would be nice to handle smart replace in the fast path. | 1989 // FIXME: Would be nice to handle smart replace in the fast path. |
| 1924 if (m_smartReplace || fragment.hasInterchangeNewlineAtStart() || | 1990 if (m_smartReplace || fragment.hasInterchangeNewlineAtStart() || |
| 1925 fragment.hasInterchangeNewlineAtEnd()) | 1991 fragment.hasInterchangeNewlineAtEnd()) |
| 1926 return false; | 1992 return false; |
| 1927 | 1993 |
| 1928 // e.g. when "bar" is inserted after "foo" in <div><u>foo</u></div>, "bar" sho
uld not be underlined. | 1994 // e.g. when "bar" is inserted after "foo" in <div><u>foo</u></div>, "bar" |
| 1995 // should not be underlined. |
| 1929 if (elementToSplitToAvoidPastingIntoInlineElementsWithStyle( | 1996 if (elementToSplitToAvoidPastingIntoInlineElementsWithStyle( |
| 1930 endingSelection().start())) | 1997 endingSelection().start())) |
| 1931 return false; | 1998 return false; |
| 1932 | 1999 |
| 1933 Node* nodeAfterInsertionPos = | 2000 Node* nodeAfterInsertionPos = |
| 1934 mostForwardCaretPosition(endingSelection().end()).anchorNode(); | 2001 mostForwardCaretPosition(endingSelection().end()).anchorNode(); |
| 1935 Text* textNode = toText(fragment.firstChild()); | 2002 Text* textNode = toText(fragment.firstChild()); |
| 1936 // Our fragment creation code handles tabs, spaces, and newlines, so we don't
have to worry about those here. | 2003 // Our fragment creation code handles tabs, spaces, and newlines, so we don't |
| 2004 // have to worry about those here. |
| 1937 | 2005 |
| 1938 Position start = endingSelection().start(); | 2006 Position start = endingSelection().start(); |
| 1939 Position end = replaceSelectedTextInNode(textNode->data()); | 2007 Position end = replaceSelectedTextInNode(textNode->data()); |
| 1940 if (end.isNull()) | 2008 if (end.isNull()) |
| 1941 return false; | 2009 return false; |
| 1942 | 2010 |
| 1943 if (nodeAfterInsertionPos && nodeAfterInsertionPos->parentNode() && | 2011 if (nodeAfterInsertionPos && nodeAfterInsertionPos->parentNode() && |
| 1944 isHTMLBRElement(*nodeAfterInsertionPos) && | 2012 isHTMLBRElement(*nodeAfterInsertionPos) && |
| 1945 shouldRemoveEndBR(toHTMLBRElement(nodeAfterInsertionPos), | 2013 shouldRemoveEndBR(toHTMLBRElement(nodeAfterInsertionPos), |
| 1946 VisiblePosition::beforeNode(nodeAfterInsertionPos))) { | 2014 VisiblePosition::beforeNode(nodeAfterInsertionPos))) { |
| (...skipping 25 matching lines...) Expand all Loading... |
| 1972 visitor->trace(m_startOfInsertedContent); | 2040 visitor->trace(m_startOfInsertedContent); |
| 1973 visitor->trace(m_endOfInsertedContent); | 2041 visitor->trace(m_endOfInsertedContent); |
| 1974 visitor->trace(m_insertionStyle); | 2042 visitor->trace(m_insertionStyle); |
| 1975 visitor->trace(m_documentFragment); | 2043 visitor->trace(m_documentFragment); |
| 1976 visitor->trace(m_startOfInsertedRange); | 2044 visitor->trace(m_startOfInsertedRange); |
| 1977 visitor->trace(m_endOfInsertedRange); | 2045 visitor->trace(m_endOfInsertedRange); |
| 1978 CompositeEditCommand::trace(visitor); | 2046 CompositeEditCommand::trace(visitor); |
| 1979 } | 2047 } |
| 1980 | 2048 |
| 1981 } // namespace blink | 2049 } // namespace blink |
| OLD | NEW |