Changeset 8087 in webkit


Ignore:
Timestamp:
Nov 29, 2004 4:07:04 PM (19 years ago)
Author:
kocienda
Message:

Reviewed by Chris

Rewrite of paste code (specifically the ReplaceSelectionCommand class). Many more cases
are handled correctly now, including selections that span multiple blocks, and cases
where content on the pasteboard ends in newlines (or what appear to be newlines to a
user, really block ends or BRs). I also made one small, but important change in the
copy code to annotate the markup written to the pasteboard to support these selections
ending in newlines.

New header that defines a couple of constants used in copying and pasting.

  • ForwardingHeaders/editing/html_interchange.h: Added.
  • khtml/editing/html_interchange.h: Added.

Rewrite of the ReplaceSelectionCommand. There are several new helper functions, as well
as a new helper class, ReplacementFragment, which encapsulates information and functions
pertaining to a document fragment that is being inserted into a document.

  • khtml/editing/htmlediting.cpp: (khtml::ReplacementFragment::ReplacementFragment): (khtml::ReplacementFragment::~ReplacementFragment): (khtml::ReplacementFragment::firstChild): Simple accessor. (khtml::ReplacementFragment::lastChild): Ditto. (khtml::ReplacementFragment::mergeStartNode): Looks at the nodes in a fragment and determines the starting node to use for merging into the block containing the start of the selection. (khtml::ReplacementFragment::mergeEndNode): Same as above, but for the end of the selection. (khtml::ReplacementFragment::pruneEmptyNodes): Simple helper. (khtml::ReplacementFragment::isInterchangeNewlineComment): Determines if a node is the special annotation comment added in by the copy code. (khtml::ReplacementFragment::removeNode): Simple helper. (khtml::isComment): Simple helper. (khtml::isProbablyBlock): Determines if a node is of a type that is usually rendered as a block. I would like to do better than this some day, but this check will hold us until I can do better. (khtml::ReplaceSelectionCommand::ReplaceSelectionCommand): (khtml::ReplaceSelectionCommand::~ReplaceSelectionCommand): (khtml::ReplaceSelectionCommand::doApply): (khtml::ReplaceSelectionCommand::completeHTMLReplacement): Figures out the right ending selection.
  • khtml/editing/htmlediting.h: Declarations for the new ReplacementFragment class. (khtml::ReplacementFragment::root): (khtml::ReplacementFragment::type): (khtml::ReplacementFragment::isEmpty): (khtml::ReplacementFragment::isSingleTextNode): (khtml::ReplacementFragment::isTreeFragment): (khtml::ReplacementFragment::hasMoreThanOneBlock): (khtml::ReplacementFragment::hasLogicalNewlineAtEnd):

This smaller set of changes markup generation to add the newline annotation described in the
comment at the start of this entry.

  • khtml/xml/dom2_rangeimpl.cpp: (DOM::RangeImpl::addCommentToHTMLMarkup): Simple helper. (DOM::RangeImpl::toHTML): Added new EAnnotateForInterchange default argument to control whether comment annotations are added to the markup generated.
  • khtml/xml/dom2_rangeimpl.h: Add some new declarations.
  • kwq/WebCoreBridge.mm: (-[WebCoreBridge markupStringFromRange:nodes:]): Request that markup resulting from call to DOM::RangeImpl::toHTML uses annotations when generating.

New tests.

  • layout-tests/editing/pasteboard/paste-text-001-expected.txt: Added.
  • layout-tests/editing/pasteboard/paste-text-001.html: Added.
  • layout-tests/editing/pasteboard/paste-text-002-expected.txt: Added.
  • layout-tests/editing/pasteboard/paste-text-002.html: Added.
  • layout-tests/editing/pasteboard/paste-text-003-expected.txt: Added.
  • layout-tests/editing/pasteboard/paste-text-003.html: Added.
  • layout-tests/editing/pasteboard/paste-text-004-expected.txt: Added.
  • layout-tests/editing/pasteboard/paste-text-004.html: Added.
  • layout-tests/editing/pasteboard/paste-text-005-expected.txt: Added.
  • layout-tests/editing/pasteboard/paste-text-005.html: Added.
  • layout-tests/editing/pasteboard/paste-text-006-expected.txt: Added.
  • layout-tests/editing/pasteboard/paste-text-006.html: Added.
  • layout-tests/editing/pasteboard/paste-text-007-expected.txt: Added.
  • layout-tests/editing/pasteboard/paste-text-007.html: Added.
  • layout-tests/editing/pasteboard/paste-text-008-expected.txt: Added.
  • layout-tests/editing/pasteboard/paste-text-008.html: Added.
  • layout-tests/editing/pasteboard/paste-text-009-expected.txt: Added.
  • layout-tests/editing/pasteboard/paste-text-009.html: Added.
Location:
trunk
Files:
20 added
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/WebCore/ChangeLog-2005-08-23

    r8085 r8087  
     12004-11-29  Ken Kocienda  <kocienda@apple.com>
     2
     3        Reviewed by Chris
     4   
     5        Rewrite of paste code (specifically the ReplaceSelectionCommand class). Many more cases
     6        are handled correctly now, including selections that span multiple blocks, and cases
     7        where content on the pasteboard ends in newlines (or what appear to be newlines to a
     8        user, really block ends or BRs). I also made one small, but important change in the
     9        copy code to annotate the markup written to the pasteboard to support these selections
     10        ending in newlines.
     11
     12        New header that defines a couple of constants used in copying and pasting.
     13
     14        * ForwardingHeaders/editing/html_interchange.h: Added.
     15        * khtml/editing/html_interchange.h: Added.
     16       
     17        Rewrite of the ReplaceSelectionCommand. There are several new helper functions, as well
     18        as a new helper class, ReplacementFragment, which encapsulates information and functions
     19        pertaining to a document fragment that is being inserted into a document.
     20       
     21        * khtml/editing/htmlediting.cpp:
     22        (khtml::ReplacementFragment::ReplacementFragment):
     23        (khtml::ReplacementFragment::~ReplacementFragment):
     24        (khtml::ReplacementFragment::firstChild): Simple accessor.
     25        (khtml::ReplacementFragment::lastChild): Ditto.
     26        (khtml::ReplacementFragment::mergeStartNode): Looks at the nodes in a fragment and determines
     27        the starting node to use for merging into the block containing the start of the selection.
     28        (khtml::ReplacementFragment::mergeEndNode): Same as above, but for the end of the selection.
     29        (khtml::ReplacementFragment::pruneEmptyNodes): Simple helper.
     30        (khtml::ReplacementFragment::isInterchangeNewlineComment): Determines if a node is the
     31        special annotation comment added in by the copy code.
     32        (khtml::ReplacementFragment::removeNode): Simple helper.
     33        (khtml::isComment): Simple helper.
     34        (khtml::isProbablyBlock): Determines if a node is of a type that is usually rendered as a block.
     35        I would like to do better than this some day, but this check will hold us until I can do better.
     36        (khtml::ReplaceSelectionCommand::ReplaceSelectionCommand):
     37        (khtml::ReplaceSelectionCommand::~ReplaceSelectionCommand):
     38        (khtml::ReplaceSelectionCommand::doApply):
     39        (khtml::ReplaceSelectionCommand::completeHTMLReplacement): Figures out the right ending selection.
     40        * khtml/editing/htmlediting.h: Declarations for the new ReplacementFragment class.
     41        (khtml::ReplacementFragment::root):
     42        (khtml::ReplacementFragment::type):
     43        (khtml::ReplacementFragment::isEmpty):
     44        (khtml::ReplacementFragment::isSingleTextNode):
     45        (khtml::ReplacementFragment::isTreeFragment):
     46        (khtml::ReplacementFragment::hasMoreThanOneBlock):
     47        (khtml::ReplacementFragment::hasLogicalNewlineAtEnd):
     48       
     49        This smaller set of changes markup generation to add the newline annotation described in the
     50        comment at the start of this entry.
     51       
     52        * khtml/xml/dom2_rangeimpl.cpp:
     53        (DOM::RangeImpl::addCommentToHTMLMarkup): Simple helper.
     54        (DOM::RangeImpl::toHTML): Added new EAnnotateForInterchange default argument to control whether
     55        comment annotations are added to the markup generated.
     56        * khtml/xml/dom2_rangeimpl.h: Add some new declarations.
     57        * kwq/WebCoreBridge.mm:
     58        (-[WebCoreBridge markupStringFromRange:nodes:]): Request that markup resulting from call to
     59        DOM::RangeImpl::toHTML uses annotations when generating.
     60       
     61        New tests.       
     62
     63        * layout-tests/editing/pasteboard/paste-text-001-expected.txt: Added.
     64        * layout-tests/editing/pasteboard/paste-text-001.html: Added.
     65        * layout-tests/editing/pasteboard/paste-text-002-expected.txt: Added.
     66        * layout-tests/editing/pasteboard/paste-text-002.html: Added.
     67        * layout-tests/editing/pasteboard/paste-text-003-expected.txt: Added.
     68        * layout-tests/editing/pasteboard/paste-text-003.html: Added.
     69        * layout-tests/editing/pasteboard/paste-text-004-expected.txt: Added.
     70        * layout-tests/editing/pasteboard/paste-text-004.html: Added.
     71        * layout-tests/editing/pasteboard/paste-text-005-expected.txt: Added.
     72        * layout-tests/editing/pasteboard/paste-text-005.html: Added.
     73        * layout-tests/editing/pasteboard/paste-text-006-expected.txt: Added.
     74        * layout-tests/editing/pasteboard/paste-text-006.html: Added.
     75        * layout-tests/editing/pasteboard/paste-text-007-expected.txt: Added.
     76        * layout-tests/editing/pasteboard/paste-text-007.html: Added.
     77        * layout-tests/editing/pasteboard/paste-text-008-expected.txt: Added.
     78        * layout-tests/editing/pasteboard/paste-text-008.html: Added.
     79        * layout-tests/editing/pasteboard/paste-text-009-expected.txt: Added.
     80        * layout-tests/editing/pasteboard/paste-text-009.html: Added.
     81
    1822004-11-29  Ken Kocienda  <kocienda@apple.com>
    283
  • trunk/WebCore/khtml/editing/htmlediting.cpp

    r8081 r8087  
    4242#include "html_elementimpl.h"
    4343#include "html_imageimpl.h"
     44#include "html_interchange.h"
    4445#include "htmlattrs.h"
    4546#include "htmltags.h"
     
    6667using DOM::DOMString;
    6768using DOM::DOMStringImpl;
     69using DOM::DoNotStayInBlock;
    6870using DOM::DoNotUpdateLayout;
    6971using DOM::EditingTextImpl;
    7072using DOM::ElementImpl;
     73using DOM::EStayInBlock;
    7174using DOM::HTMLElementImpl;
    7275using DOM::HTMLImageElementImpl;
     
    26712674// ReplaceSelectionCommand
    26722675
    2673 ReplaceSelectionCommand::ReplaceSelectionCommand(DocumentImpl *document, DocumentFragmentImpl *fragment, bool selectReplacement, bool smartReplace)
    2674     : CompositeEditCommand(document), m_fragment(fragment), m_selectReplacement(selectReplacement), m_smartReplace(smartReplace)
    2675 {
    2676     ASSERT(m_fragment);
     2676ReplacementFragment::ReplacementFragment(DocumentFragmentImpl *fragment)
     2677    : m_fragment(fragment), m_hasInterchangeNewlineComment(false), m_hasMoreThanOneBlock(false)
     2678{
     2679    if (!m_fragment) {
     2680        m_type = EmptyFragment;
     2681        return;
     2682    }
     2683
    26772684    m_fragment->ref();
    2678 }
    2679 
    2680 ReplaceSelectionCommand::~ReplaceSelectionCommand()
    2681 {
    2682     ASSERT(m_fragment);
    2683     m_fragment->deref();
    2684 }
    2685 
    2686 void ReplaceSelectionCommand::doApply()
    2687 {
     2685
    26882686    NodeImpl *firstChild = m_fragment->firstChild();
    26892687    NodeImpl *lastChild = m_fragment->lastChild();
    26902688
     2689    if (!firstChild) {
     2690        m_type = EmptyFragment;
     2691        return;
     2692    }
     2693
     2694    if (firstChild == lastChild && firstChild->isTextNode()) {
     2695        m_type = SingleTextNodeFragment;
     2696        return;
     2697    }
     2698   
     2699    m_type = TreeFragment;
     2700
     2701    NodeImpl *node = firstChild;
     2702    int blockCount = 0;
     2703    NodeImpl *commentToDelete = 0;
     2704    while (node) {
     2705        NodeImpl *next = node->traverseNextNode();
     2706        if (isInterchangeNewlineComment(node)) {
     2707            m_hasInterchangeNewlineComment = true;
     2708            commentToDelete = node;
     2709        }
     2710        else if (isProbablyBlock(node))
     2711            blockCount++;   
     2712        node = next;
     2713     }
     2714
     2715     if (commentToDelete)
     2716        removeNode(commentToDelete);
     2717
     2718    firstChild = m_fragment->firstChild();
     2719    lastChild = m_fragment->lastChild();
     2720    if (!isProbablyBlock(firstChild))
     2721        blockCount++;
     2722    if (!isProbablyBlock(lastChild) && firstChild != lastChild)
     2723        blockCount++;
     2724
     2725     if (blockCount > 1)
     2726        m_hasMoreThanOneBlock = true;
     2727}
     2728
     2729ReplacementFragment::~ReplacementFragment()
     2730{
     2731    if (m_fragment)
     2732        m_fragment->deref();
     2733}
     2734
     2735NodeImpl *ReplacementFragment::firstChild() const
     2736{
     2737    return m_fragment->firstChild();
     2738}
     2739
     2740NodeImpl *ReplacementFragment::lastChild() const
     2741{
     2742    return  m_fragment->lastChild();
     2743}
     2744
     2745NodeImpl *ReplacementFragment::mergeStartNode() const
     2746{
     2747    NodeImpl *node = m_fragment->firstChild();
     2748    while (node) {
     2749        NodeImpl *next = node->traverseNextNode();
     2750        if (!isProbablyBlock(node))
     2751            return node;
     2752        node = next;
     2753     }
     2754     return 0;
     2755}
     2756
     2757NodeImpl *ReplacementFragment::mergeEndNode() const
     2758{
     2759    NodeImpl *node = m_fragment->lastChild();
     2760    while (node && node->lastChild())
     2761        node = node->lastChild();
     2762    while (node) {
     2763        NodeImpl *prev = node->traversePreviousNode();
     2764        if (!isProbablyBlock(node)) {
     2765            NodeImpl *previousSibling = node->previousSibling();
     2766            while (1) {
     2767                if (!previousSibling || isProbablyBlock(previousSibling))
     2768                    return node;
     2769                node = previousSibling;
     2770            }
     2771        }
     2772        node = prev;
     2773    }
     2774    return 0;
     2775}
     2776
     2777void ReplacementFragment::pruneEmptyNodes()
     2778{
     2779    bool run = true;
     2780    while (run) {
     2781        run = false;
     2782        NodeImpl *node = m_fragment->firstChild();
     2783        while (node) {
     2784            if ((node->isTextNode() && static_cast<TextImpl *>(node)->length() == 0) ||
     2785                (isProbablyBlock(node) && node->childNodeCount() == 0)) {
     2786                NodeImpl *next = node->traverseNextSibling();
     2787                removeNode(node);
     2788                node = next;
     2789                run = true;
     2790            }
     2791            else {
     2792                node = node->traverseNextNode();
     2793            }
     2794         }
     2795    }
     2796}
     2797
     2798bool ReplacementFragment::isInterchangeNewlineComment(const NodeImpl *node)
     2799{
     2800    return isComment(node) && node->nodeValue() == KHTMLInterchangeNewline;
     2801}
     2802
     2803void ReplacementFragment::removeNode(NodeImpl *node)
     2804{
     2805    if (!node)
     2806        return;
     2807       
     2808    NodeImpl *parent = node->parentNode();
     2809    if (!parent)
     2810        return;
     2811       
     2812    int exceptionCode = 0;
     2813    parent->removeChild(node, exceptionCode);
     2814    ASSERT(exceptionCode == 0);
     2815 }
     2816
     2817bool isComment(const NodeImpl *node)
     2818{
     2819    return node && node->nodeType() == Node::COMMENT_NODE;
     2820}
     2821
     2822bool isProbablyBlock(const NodeImpl *node)
     2823{
     2824    if (!node)
     2825        return false;
     2826   
     2827    switch (node->id()) {
     2828        case ID_BLOCKQUOTE:
     2829        case ID_DD:
     2830        case ID_DIV:
     2831        case ID_DL:
     2832        case ID_DT:
     2833        case ID_H1:
     2834        case ID_H2:
     2835        case ID_H3:
     2836        case ID_H4:
     2837        case ID_H5:
     2838        case ID_H6:
     2839        case ID_HR:
     2840        case ID_LI:
     2841        case ID_OL:
     2842        case ID_P:
     2843        case ID_PRE:
     2844        case ID_TD:
     2845        case ID_TH:
     2846        case ID_TR:
     2847        case ID_UL:
     2848            return true;
     2849    }
     2850   
     2851    return false;
     2852}
     2853
     2854ReplaceSelectionCommand::ReplaceSelectionCommand(DocumentImpl *document, DocumentFragmentImpl *fragment, bool selectReplacement, bool smartReplace)
     2855    : CompositeEditCommand(document),
     2856      m_fragment(fragment),
     2857      m_selectReplacement(selectReplacement),
     2858      m_smartReplace(smartReplace)
     2859{
     2860}
     2861
     2862ReplaceSelectionCommand::~ReplaceSelectionCommand()
     2863{
     2864}
     2865
     2866void ReplaceSelectionCommand::doApply()
     2867{
    26912868    Selection selection = endingSelection();
     2869    VisiblePosition visibleStart(selection.start());
     2870    VisiblePosition visibleEnd(selection.end());
     2871    bool startAtStartOfLine = isFirstVisiblePositionOnLine(visibleStart);
     2872    bool startAtStartOfBlock = isFirstVisiblePositionInBlock(visibleStart);
     2873    bool startAtEndOfBlock = isLastVisiblePositionInBlock(visibleStart);
     2874    bool startAtBlockBoundary = startAtStartOfBlock || startAtEndOfBlock;
     2875    NodeImpl *startBlock = selection.start().node()->enclosingBlockFlowElement();
     2876    NodeImpl *endBlock = selection.end().node()->enclosingBlockFlowElement();
     2877
     2878    bool mergeStart = !(startAtStartOfLine && (m_fragment.hasInterchangeNewlineComment() || m_fragment.hasMoreThanOneBlock()));
     2879    bool mergeEnd = !m_fragment.hasInterchangeNewlineComment() && m_fragment.hasMoreThanOneBlock();
     2880    Position startPos = Position(selection.start().node()->enclosingBlockFlowElement(), 0);
     2881    Position endPos;
     2882    EStayInBlock upstreamStayInBlock = StayInBlock;
    26922883
    26932884    // Delete the current selection, or collapse whitespace, as needed
    2694     if (selection.isRange())
    2695         deleteSelection();
    2696    
    2697     KHTMLPart *part = document()->part();
    2698     ASSERT(part);
     2885    if (selection.isRange()) {
     2886        deleteSelection(false, !(m_fragment.hasInterchangeNewlineComment() || m_fragment.hasMoreThanOneBlock()));
     2887    }
     2888    else if (selection.isCaret() && mergeEnd && !startAtBlockBoundary) {
     2889        // The start and the end need to wind up in separate blocks.
     2890        // Insert a paragraph separator to make that happen.
     2891        insertParagraphSeparator();
     2892        upstreamStayInBlock = DoNotStayInBlock;
     2893    }
     2894   
     2895    selection = endingSelection();
     2896    if (!startAtBlockBoundary || !startPos.node()->inDocument())
     2897        startPos = selection.start().upstream(upstreamStayInBlock);
     2898    endPos = selection.end().downstream();
    26992899   
    27002900    // This command does not use any typing style that is set as a residual effect of
     
    27022902    // FIXME: Improve typing style.
    27032903    // See this bug: <rdar://problem/3769899> Implementation of typing style needs improvement
     2904    KHTMLPart *part = document()->part();
    27042905    part->clearTypingStyle();
    27052906    setTypingStyle(0);
    2706    
    2707     selection = endingSelection();
    2708     ASSERT(selection.isCaret());
    2709 
     2907
     2908    if (!m_fragment.firstChild())
     2909        return;
     2910   
    27102911    // Now that we are about to add content, check to see if a placeholder element
    27112912    // can be removed.
    2712     Position pos = selection.start();
    2713     NodeImpl *block = pos.node()->enclosingBlockFlowElement();
     2913    NodeImpl *block = startPos.node()->enclosingBlockFlowElement();
    27142914    if (removeBlockPlaceholderIfNeeded(block)) {
    2715         pos = Position(block, 0);
     2915        startPos = Position(block, 0);
    27162916    }
    27172917   
     
    27192919    bool addTrailingSpace = false;
    27202920    if (m_smartReplace) {
    2721         addLeadingSpace = pos.leadingWhitespacePosition().isNull();
    2722         addTrailingSpace = pos.trailingWhitespacePosition().isNull();
     2921        addLeadingSpace = startPos.leadingWhitespacePosition().isNull();
     2922        addTrailingSpace = endPos.trailingWhitespacePosition().isNull();
    27232923    }
    27242924
    27252925#if APPLE_CHANGES
    27262926    if (addLeadingSpace) {
    2727         QChar previousChar = VisiblePosition(pos).previous().character();
     2927        QChar previousChar = VisiblePosition(startPos).previous().character();
    27282928        if (!previousChar.isNull()) {
    27292929            addLeadingSpace = !KWQ(part)->isCharacterSmartReplaceExempt(previousChar, true);
     
    27312931    }
    27322932    if (addTrailingSpace) {
    2733         QChar thisChar = VisiblePosition(pos).character();
     2933        QChar thisChar = VisiblePosition(endPos).character();
    27342934        if (!thisChar.isNull()) {
    27352935            addTrailingSpace = !KWQ(part)->isCharacterSmartReplaceExempt(thisChar, false);
     
    27372937    }
    27382938#endif
    2739    
    2740     if (!firstChild) {
    2741         // Pasting something that didn't parse or was empty.
    2742         ASSERT(!lastChild);
    2743     } else if (firstChild == lastChild && firstChild->isTextNode()) {
    2744         // FIXME: HTML fragment case needs to be improved to the point
    2745         // where we can remove this separate case.
    2746        
    2747         // Simple text paste. Treat as if the text were typed.
    2748         Position upstreamStart(pos.upstream(StayInBlock));
    2749         DOMString text = static_cast<TextImpl *>(firstChild)->data();
    2750         if (addLeadingSpace) {
    2751             text = " " + text;
    2752         }
    2753         if (addTrailingSpace) {
    2754             text += " ";
    2755         }
    2756         inputText(text, m_selectReplacement);
     2939
     2940    document()->updateLayout();
     2941
     2942    NodeImpl *refBlock = startPos.node()->enclosingBlockFlowElement();
     2943    Position insertionPos = startPos;
     2944    bool insertBlocksBefore = true;
     2945
     2946    NodeImpl *firstNodeInserted = 0;
     2947    NodeImpl *lastNodeInserted = 0;
     2948    bool lastNodeInsertedInMergeEnd = false;
     2949
     2950    // prune empty nodes from fragment
     2951    m_fragment.pruneEmptyNodes();
     2952
     2953    // Merge content into the end block, if necessary.
     2954    if (mergeEnd) {
     2955        NodeImpl *node = m_fragment.mergeEndNode();
     2956        if (node) {
     2957            NodeImpl *refNode = node;
     2958            NodeImpl *node = refNode ? refNode->nextSibling() : 0;
     2959            insertNodeAt(refNode, endPos.node(), endPos.offset());
     2960            firstNodeInserted = refNode;
     2961            lastNodeInserted = refNode;
     2962            while (node && !isProbablyBlock(node)) {
     2963                NodeImpl *next = node->nextSibling();
     2964                insertNodeAfter(node, refNode);
     2965                lastNodeInserted = node;
     2966                refNode = node;
     2967                node = next;
     2968            }
     2969            lastNodeInsertedInMergeEnd = true;
     2970        }
     2971    }
     2972   
     2973    // prune empty nodes from fragment
     2974    m_fragment.pruneEmptyNodes();
     2975
     2976    // Merge content into the start block, if necessary.
     2977    if (mergeStart) {
     2978        NodeImpl *node = m_fragment.mergeStartNode();
     2979        NodeImpl *insertionNode = 0;
     2980        if (node) {
     2981            NodeImpl *refNode = node;
     2982            NodeImpl *node = refNode ? refNode->nextSibling() : 0;
     2983            insertNodeAt(refNode, startPos.node(), startPos.offset());
     2984            firstNodeInserted = refNode;
     2985            if (!lastNodeInsertedInMergeEnd)
     2986                lastNodeInserted = refNode;
     2987            insertionNode = refNode;
     2988            while (node && !isProbablyBlock(node)) {
     2989                NodeImpl *next = node->nextSibling();
     2990                insertNodeAfter(node, refNode);
     2991                if (!lastNodeInsertedInMergeEnd)
     2992                    lastNodeInserted = node;
     2993                insertionNode = node;
     2994                refNode = node;
     2995                node = next;
     2996            }
     2997        }
     2998        if (insertionNode)
     2999            insertionPos = Position(insertionNode, insertionNode->caretMaxOffset());
     3000        insertBlocksBefore = false;
     3001    }
     3002
     3003    // prune empty nodes from fragment
     3004    m_fragment.pruneEmptyNodes();
     3005   
     3006    // Merge everything remaining.
     3007    NodeImpl *node = m_fragment.firstChild();
     3008    if (node) {
     3009        NodeImpl *refNode = node;
     3010        NodeImpl *node = refNode ? refNode->nextSibling() : 0;
     3011        if (isProbablyBlock(refNode) && (insertBlocksBefore || startAtStartOfBlock)) {
     3012            insertNodeBefore(refNode, refBlock);
     3013        }
     3014        else if (isProbablyBlock(refNode) && startAtEndOfBlock) {
     3015            insertNodeAfter(refNode, refBlock);
     3016        }
     3017        else {
     3018            insertNodeAt(refNode, insertionPos.node(), insertionPos.offset());
     3019        }
     3020        if (!firstNodeInserted)
     3021            firstNodeInserted = refNode;
     3022        if (!lastNodeInsertedInMergeEnd)
     3023            lastNodeInserted = refNode;
     3024        while (node) {
     3025            NodeImpl *next = node->nextSibling();
     3026            insertNodeAfter(node, refNode);
     3027            if (!lastNodeInsertedInMergeEnd)
     3028                lastNodeInserted = node;
     3029            refNode = node;
     3030            node = next;
     3031        }
     3032        insertionPos = Position(lastNodeInserted, lastNodeInserted->caretMaxOffset());
     3033    }
     3034
     3035    if (m_fragment.hasInterchangeNewlineComment()) {
     3036        if (startBlock == endBlock && !isProbablyBlock(lastNodeInserted)) {
     3037            setEndingSelection(insertionPos);
     3038            insertParagraphSeparator();
     3039            endPos = endingSelection().end().downstream();
     3040        }
     3041        completeHTMLReplacement(startPos, endPos);
     3042    }
     3043    else {
     3044        completeHTMLReplacement(firstNodeInserted, lastNodeInserted);
     3045    }
     3046}
     3047
     3048void ReplaceSelectionCommand::completeHTMLReplacement(const Position &start, const Position &end)
     3049 {
     3050    if (start.isNull() || !start.node()->inDocument() || end.isNull() || !end.node()->inDocument())
     3051        return;
     3052    m_selectReplacement ? setEndingSelection(Selection(start, end)) : setEndingSelection(end);
     3053}
     3054
     3055void ReplaceSelectionCommand::completeHTMLReplacement(NodeImpl *firstNodeInserted, NodeImpl *lastNodeInserted)
     3056{
     3057    if (!firstNodeInserted || !firstNodeInserted->inDocument() ||
     3058        !lastNodeInserted || !lastNodeInserted->inDocument())
     3059        return;
     3060
     3061    // Find the last leaf.
     3062    NodeImpl *lastLeaf = lastNodeInserted;
     3063    while (1) {
     3064        NodeImpl *nextChild = lastLeaf->lastChild();
     3065        if (!nextChild)
     3066            break;
     3067        lastLeaf = nextChild;
     3068    }
     3069
     3070    // Find the first leaf.
     3071    NodeImpl *firstLeaf = firstNodeInserted;
     3072    while (1) {
     3073        NodeImpl *nextChild = firstLeaf->firstChild();
     3074        if (!nextChild)
     3075            break;
     3076        firstLeaf = nextChild;
     3077    }
     3078   
     3079    Position start(firstLeaf, firstLeaf->caretMinOffset());
     3080    Position end(lastLeaf, lastLeaf->caretMaxOffset());
     3081    Selection replacementSelection(start, end);
     3082    if (m_selectReplacement) {
     3083        // Select what was inserted.
     3084        setEndingSelection(replacementSelection);
    27573085    }
    27583086    else {
    2759         // HTML fragment paste.
    2760        
    2761         // FIXME: Add leading and trailing spaces to the fragment?
    2762         // Or just insert them as we insert it?
    2763        
    2764         NodeImpl *beforeNode = firstChild;
    2765         NodeImpl *node = firstChild->nextSibling();
    2766        
    2767         insertNodeAt(firstChild, pos.node(), pos.offset());
    2768        
    2769         // Insert the nodes from the fragment
    2770         while (node) {
    2771             NodeImpl *next = node->nextSibling();
    2772             insertNodeAfter(node, beforeNode);
    2773             beforeNode = node;
    2774             node = next;
    2775         }
    2776         ASSERT(beforeNode);
    2777        
    2778         // Find the last leaf.
    2779         NodeImpl *lastLeaf = lastChild;
    2780         while (1) {
    2781             NodeImpl *nextChild = lastLeaf->lastChild();
    2782             if (!nextChild)
    2783                 break;
    2784             lastLeaf = nextChild;
    2785         }
    2786 
    2787         // Find the first leaf.
    2788         NodeImpl *firstLeaf = firstChild;
    2789         while (1) {
    2790             NodeImpl *nextChild = firstLeaf->firstChild();
    2791             if (!nextChild)
    2792                 break;
    2793             firstLeaf = nextChild;
    2794         }
    2795        
    2796         Selection replacementSelection(Position(firstLeaf, firstLeaf->caretMinOffset()), Position(lastLeaf, lastLeaf->caretMaxOffset()));
    2797         if (m_selectReplacement) {
    2798             // Select what was inserted.
    2799             setEndingSelection(replacementSelection);
    2800         }
    2801         else {
    2802             // Place the cursor after what was inserted, and mark misspellings in the inserted content.
    2803             selection = Selection(Position(lastLeaf, lastLeaf->caretMaxOffset()));
    2804             setEndingSelection(selection);
    2805         }
     3087        // Place the cursor after what was inserted, and mark misspellings in the inserted content.
     3088        setEndingSelection(end);
    28063089    }
    28073090}
  • trunk/WebCore/khtml/editing/htmlediting.h

    r8080 r8087  
    482482// ReplaceSelectionCommand
    483483
     484// --- ReplacementFragment helper class
     485
     486class ReplacementFragment
     487{
     488public:
     489    ReplacementFragment(DOM::DocumentFragmentImpl *fragment);
     490    ~ReplacementFragment();
     491
     492    enum EFragmentType { EmptyFragment, SingleTextNodeFragment, TreeFragment };
     493
     494    DOM::DocumentFragmentImpl *root() const { return m_fragment; }
     495    DOM::NodeImpl *firstChild() const;
     496    DOM::NodeImpl *lastChild() const;
     497
     498    DOM::NodeImpl *mergeStartNode() const;
     499    DOM::NodeImpl *mergeEndNode() const;
     500   
     501    void pruneEmptyNodes();
     502
     503    EFragmentType type() const { return m_type; }
     504    bool isEmpty() const { return m_type == EmptyFragment; }
     505    bool isSingleTextNode() const { return m_type == SingleTextNodeFragment; }
     506    bool isTreeFragment() const { return m_type == TreeFragment; }
     507
     508    bool hasMoreThanOneBlock() const { return m_hasMoreThanOneBlock; }
     509    bool hasInterchangeNewlineComment() const { return m_hasInterchangeNewlineComment; }
     510
     511private:
     512    // no copy construction or assignment
     513    ReplacementFragment(const ReplacementFragment &);
     514    ReplacementFragment &operator=(const ReplacementFragment &);
     515
     516    static bool isInterchangeNewlineComment(const DOM::NodeImpl *);
     517    void removeNode(DOM::NodeImpl *);
     518
     519    EFragmentType m_type;
     520    DOM::DocumentFragmentImpl *m_fragment;
     521    bool m_hasInterchangeNewlineComment;
     522    bool m_hasMoreThanOneBlock;
     523};
     524
     525// free-floating helper functions
     526bool isProbablyBlock(const DOM::NodeImpl *);
     527bool isComment(const DOM::NodeImpl *);
     528
    484529class ReplaceSelectionCommand : public CompositeEditCommand
    485530{
     
    491536
    492537private:
    493     DOM::DocumentFragmentImpl *m_fragment;
     538    void completeHTMLReplacement(const DOM::Position &, const DOM::Position &);
     539    void completeHTMLReplacement(DOM::NodeImpl *, DOM::NodeImpl *);
     540   
     541    ReplacementFragment m_fragment;
    494542    bool m_selectReplacement;
    495543    bool m_smartReplace;
  • trunk/WebCore/khtml/xml/dom2_rangeimpl.cpp

    r7981 r8087  
    3131#include "html/html_elementimpl.h"
    3232#include "misc/htmltags.h"
     33#include "editing/visible_position.h"
    3334#include "editing/visible_text.h"
    3435#include "xml/dom_position.h"
     
    827828}
    828829
    829 DOMString RangeImpl::toHTML(QPtrList<NodeImpl> *nodes) const
     830void RangeImpl::addCommentToHTMLMarkup(const DOMString &comment, QStringList &markups, EAddToMarkup appendOrPrepend) const
     831 {
     832    if (!m_ownerDocument)
     833        return;
     834       
     835    CommentImpl *n = new CommentImpl(m_ownerDocument, comment);
     836    n->ref();
     837    switch (appendOrPrepend) {
     838        case PrependToMarkup:
     839            markups.prepend(n->startMarkup(this));
     840            break;
     841        case AppendToMarkup:
     842            markups.append(n->startMarkup(this));
     843            break;
     844    }
     845    n->deref();
     846}
     847
     848DOMString RangeImpl::toHTML(QPtrList<NodeImpl> *nodes, EAnnotateForInterchange annotate) const
    830849{
    831850    int exceptionCode;
     
    848867    for (NodeImpl *n = startNode(); n != pastEnd; n = next) {
    849868        next = n->traverseNextNode();
     869        if (!n->renderer())
     870            continue;
     871
     872        if (n->isBlockFlow() && next == pastEnd) {
     873            // Don't write out an empty block.
     874            continue;
     875        }
    850876       
    851877        // Add the node to the markup.
     
    861887           
    862888            // Check if the node is the last leaf of a tree.
    863             if (n->nextSibling() == 0) {
     889            if (n->nextSibling() == 0 || next == pastEnd) {
    864890                if (!ancestorsToClose.isEmpty()) {
    865891                    // Close up the ancestors.
     
    918944    }
    919945   
     946    if (annotate) {
     947        Position pos(m_endContainer, m_endOffset);
     948        NodeImpl *block = pos.node()->enclosingBlockFlowElement();
     949        NodeImpl *upstreamBlock = pos.upstream().node()->enclosingBlockFlowElement();
     950        if (block != upstreamBlock) {
     951            addCommentToHTMLMarkup(KHTMLInterchangeNewline, markups, AppendToMarkup);   
     952        }
     953    }
     954       
    920955    return markups.join("");
    921956}
  • trunk/WebCore/khtml/xml/dom2_rangeimpl.h

    r7981 r8087  
    3030#include <qptrlist.h>
    3131#include "dom/dom2_range.h"
     32#include "editing/html_interchange.h"
    3233#include "misc/shared.h"
     34
     35class QStringList;
    3336
    3437namespace DOM {
     
    7073    void insertNode( NodeImpl *newNode, int &exceptioncode );
    7174    DOMString toString ( int &exceptioncode ) const;
    72     DOMString toHTML(QPtrList<NodeImpl> *nodes=NULL) const;
     75    DOMString toHTML(QPtrList<NodeImpl> *nodes=NULL, EAnnotateForInterchange annotate=DoNotAnnotateForInterchange) const;
    7376    DOMString text() const;
    7477
     
    120123    void checkDeleteExtract(int &exceptioncode);
    121124    bool containedByReadOnly() const;
     125   
     126    enum EAddToMarkup { PrependToMarkup, AppendToMarkup };
     127    void addCommentToHTMLMarkup(const DOMString &, QStringList &, EAddToMarkup) const;
    122128};
    123129
  • trunk/WebCore/kwq/WebCoreBridge.mm

    r8085 r8087  
    552552        nodeList = new QPtrList<NodeImpl>;
    553553    }
    554     NSString *markupString = [range _rangeImpl]->toHTML(nodeList).string().getNSString();
     554    NSString *markupString = [range _rangeImpl]->toHTML(nodeList, AnnotateForInterchange).string().getNSString();
    555555    if (nodes) {
    556556        *nodes = [self nodesFromList:nodeList];
Note: See TracChangeset for help on using the changeset viewer.