Reviewed by John
authorkocienda <kocienda@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 1 Feb 2005 21:23:02 +0000 (21:23 +0000)
committerkocienda <kocienda@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 1 Feb 2005 21:23:02 +0000 (21:23 +0000)
        Fix for this bug:

        <rdar://problem/3985160> Deficiencies in pasting architecture blocking progress on other bugs

        * khtml/editing/html_interchange.h: Move style span text used to mark element added to
        add style to this header.
        * khtml/editing/htmlediting.cpp:
        (khtml::styleSpanClassString): Change to use constant moved to html_interchange.h.
        (khtml::isStyleSpan): New helper function. Checks if this is a span we added to apply style.
        (khtml::CompositeEditCommand::insertNodeBefore): Added an assert to check that the node
        we are inserting before is not the body.
        (khtml::CompositeEditCommand::insertNodeAfter): Ditto, but check is for after.
        (khtml::ReplacementFragment::ReplacementFragment): Added code to process the "default style"
        that is added by the copy code.
        (khtml::ReplaceSelectionCommand::ReplaceSelectionCommand): first and last nodes inserted are
        now member variables instead of function locals. Initialize them here.
        (khtml::ReplaceSelectionCommand::~ReplaceSelectionCommand): Deref first and last nodes inserted
        if necessary.
        (khtml::ReplaceSelectionCommand::doApply): Change design to fix the bug. Major change is to
        separate out the code that inserts nodes into the tree so additional styling checks can
        be done in a centralized way. Also got rid of the notion of "merging into the end block." That
        concept was just wrong.
        (khtml::ReplaceSelectionCommand::completeHTMLReplacement): Tweak interface now that first and
        last nodes inserted are member variables.
        (khtml::ReplaceSelectionCommand::insertNodeAfterAndUpdateNodesInserted): New helper used
        by replace code to do the stated DOM operation and update state internal to the command.
        This will also be a catch point to handle the kinds of additional style checks needed to
        make paste work right.
        (khtml::ReplaceSelectionCommand::insertNodeAtAndUpdateNodesInserted): Ditto.
        (khtml::ReplaceSelectionCommand::insertNodeBeforeAndUpdateNodesInserted): Ditto.
        (khtml::ReplaceSelectionCommand::updateNodesInserted): Ditto.
        * khtml/editing/htmlediting.h: Update declarations as needed.
        * khtml/editing/markup.cpp:
        (khtml::createMarkup): Adds a "default style" span to the content written to the pasteboard.
        This will help us to fix some of the bugs blocked by the bug above.
        * khtml/xml/dom_nodeimpl.cpp:
        (NodeImpl::lastDescendent): New helper.
        * khtml/xml/dom_nodeimpl.h: Ditto.
        * khtml/xml/dom_position.cpp:
        (DOM::Position::upstream): Fixed a bug which would allow the upstream position returned to be
        in unrendered content.
        (DOM::Position::downstream): Ditto.

        * layout-tests/editing/deleting/delete-3775172-fix-expected.txt: Ending positions tweaked due to
        changes in upstream() and downstream() functions.
        * layout-tests/editing/inserting/insert-3851164-fix-expected.txt: Ditto
        * layout-tests/editing/inserting/insert-3907422-fix-expected.txt: Ditto
        * layout-tests/editing/selection/extend-by-character-006-expected.txt: Ditto

        * layout-tests/editing/pasteboard/paste-text-003-expected.txt: Changed what we expect, given
        new behavior of paste code.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@8492 268f45cc-cd09-0410-ab3c-d52691b4dbfc

13 files changed:
LayoutTests/editing/deleting/delete-3775172-fix-expected.txt
LayoutTests/editing/inserting/insert-3851164-fix-expected.txt
LayoutTests/editing/inserting/insert-3907422-fix-expected.txt
LayoutTests/editing/pasteboard/paste-text-003-expected.txt
LayoutTests/editing/selection/extend-by-character-006-expected.txt
WebCore/ChangeLog-2005-08-23
WebCore/khtml/editing/html_interchange.h
WebCore/khtml/editing/htmlediting.cpp
WebCore/khtml/editing/htmlediting.h
WebCore/khtml/editing/markup.cpp
WebCore/khtml/xml/dom_nodeimpl.cpp
WebCore/khtml/xml/dom_nodeimpl.h
WebCore/khtml/xml/dom_position.cpp

index adea112..b94c3f9 100644 (file)
@@ -6,4 +6,4 @@ layer at (0,0) size 800x600
 selection is CARET:
 start:      position 0 of child 2 {BODY} of child 1 {HTML} of root {}
 upstream:   position 0 of child 2 {BODY} of child 1 {HTML} of root {}
-downstream: position 1 of child 2 {TEXT} of child 2 {BODY} of child 1 {HTML} of root {}
+downstream: position 0 of child 2 {BODY} of child 1 {HTML} of root {}
index 8186cb0..eb1f3fe 100644 (file)
@@ -12,4 +12,4 @@ layer at (0,0) size 800x600
 selection is CARET:
 start:      position 1 of child 1 {TEXT} of child 1 {SPAN} of root {BODY}
 upstream:   position 1 of child 1 {TEXT} of child 1 {SPAN} of root {BODY}
-downstream: position 1 of child 3 {TEXT} of root {BODY}
+downstream: position 0 of child 3 {TEXT} of root {BODY}
index f36f88f..4ddf41c 100644 (file)
@@ -25,4 +25,4 @@ layer at (0,0) size 800x600
 selection is CARET:
 start:      position 3 of child 6 {TEXT} of root {BODY}
 upstream:   position 3 of child 6 {TEXT} of root {BODY}
-downstream: position 1 of child 8 {TEXT} of root {BODY}
+downstream: position 0 of child 8 {TEXT} of root {BODY}
index 0fd311a..649106d 100644 (file)
@@ -3,36 +3,35 @@ layer at (0,0) size 800x600
 layer at (0,0) size 800x600
   RenderBlock {HTML} at (0,0) size 800x600
     RenderBody {BODY} at (8,8) size 784x584
-      RenderBlock {DIV} at (0,0) size 784x364 [border: (2px solid #FF0000)]
+      RenderBlock {DIV} at (0,0) size 784x336 [border: (2px solid #FF0000)]
         RenderBlock (anonymous) at (14,14) size 756x28
           RenderText {TEXT} at (0,0) size 63x28
             text run at (0,0) width 63: "There "
           RenderText {TEXT} at (63,0) size 285x28
             text run at (63,0) width 285: "is a tide in the affairs of men,"
-        RenderBlock {DIV} at (14,42) size 756x56 [border: (2px solid #FF0000)]
-          RenderText {TEXT} at (14,14) size 434x28
-            text run at (14,14) width 434: "Which taken at the flood leads on to fortune."
-        RenderBlock {DIV} at (14,98) size 756x252 [border: (2px solid #FF0000)]
-          RenderBlock (anonymous) at (14,14) size 728x0
-          RenderBlock {DIV} at (14,14) size 728x112 [border: (2px solid #FF0000)]
+        RenderBlock {DIV} at (14,42) size 756x280 [border: (2px solid #FF0000)]
+          RenderBlock (anonymous) at (14,14) size 728x28
+            RenderText {TEXT} at (0,0) size 434x28
+              text run at (0,0) width 434: "Which taken at the flood leads on to fortune."
+          RenderBlock {DIV} at (14,42) size 728x56 [border: (2px solid #FF0000)]
+            RenderText {TEXT} at (14,14) size 80x28
+              text run at (14,14) width 80: "Omitted"
+            RenderText {TEXT} at (94,14) size 285x28
+              text run at (94,14) width 285: "is a tide in the affairs of men,"
+          RenderBlock {DIV} at (14,98) size 728x168 [border: (2px solid #FF0000)]
             RenderBlock (anonymous) at (14,14) size 700x28
-              RenderText {TEXT} at (0,0) size 80x28
-                text run at (0,0) width 80: "Omitted"
-              RenderText {TEXT} at (80,0) size 285x28
-                text run at (80,0) width 285: "is a tide in the affairs of men,"
-            RenderBlock {DIV} at (14,42) size 700x56 [border: (2px solid #FF0000)]
-              RenderText {TEXT} at (14,14) size 434x28
-                text run at (14,14) width 434: "Which taken at the flood leads on to fortune."
-          RenderBlock {DIV} at (14,126) size 728x112 [border: (2px solid #FF0000)]
-            RenderBlock (anonymous) at (14,14) size 700x28
-              RenderText {TEXT} at (0,0) size 80x28
-                text run at (0,0) width 80: "Omitted"
-              RenderText {TEXT} at (80,0) size 271x28
-                text run at (80,0) width 271: ", all the voyage of their life,"
-            RenderBlock {DIV} at (14,42) size 700x56 [border: (2px solid #FF0000)]
-              RenderText {TEXT} at (14,14) size 357x28
-                text run at (14,14) width 357: "Is bound in shallows and in miseries."
+              RenderText {TEXT} at (0,0) size 434x28
+                text run at (0,0) width 434: "Which taken at the flood leads on to fortune."
+            RenderBlock {DIV} at (14,42) size 700x112 [border: (2px solid #FF0000)]
+              RenderBlock (anonymous) at (14,14) size 672x28
+                RenderText {TEXT} at (0,0) size 80x28
+                  text run at (0,0) width 80: "Omitted"
+                RenderText {TEXT} at (80,0) size 271x28
+                  text run at (80,0) width 271: ", all the voyage of their life,"
+              RenderBlock {DIV} at (14,42) size 672x56 [border: (2px solid #FF0000)]
+                RenderText {TEXT} at (14,14) size 357x28
+                  text run at (14,14) width 357: "Is bound in shallows and in miseries."
 selection is CARET:
-start:      position 7 of child 1 {TEXT} of child 2 {DIV} of child 4 {DIV} of child 1 {DIV} of root {BODY}
-upstream:   position 7 of child 1 {TEXT} of child 2 {DIV} of child 4 {DIV} of child 1 {DIV} of root {BODY}
-downstream: position 0 of child 2 {TEXT} of child 2 {DIV} of child 4 {DIV} of child 1 {DIV} of root {BODY}
+start:      position 7 of child 1 {TEXT} of child 2 {DIV} of child 3 {DIV} of child 3 {DIV} of child 1 {DIV} of root {BODY}
+upstream:   position 7 of child 1 {TEXT} of child 2 {DIV} of child 3 {DIV} of child 3 {DIV} of child 1 {DIV} of root {BODY}
+downstream: position 0 of child 2 {TEXT} of child 2 {DIV} of child 3 {DIV} of child 3 {DIV} of child 1 {DIV} of root {BODY}
index f275653..5c5d06f 100644 (file)
@@ -16,4 +16,4 @@ upstream:   position 0 of  of root {DIV}
 downstream: position 1 of child 1 {TEXT} of child 1 {SPAN} of root {DIV}
 end:        position 4 of child 1 {TEXT} of child 1 {SPAN} of root {DIV}
 upstream:   position 4 of child 1 {TEXT} of child 1 {SPAN} of root {DIV}
-downstream: position 1 of child 5 {TEXT} of root {DIV}
+downstream: position 0 of child 5 {TEXT} of root {DIV}
index 21208da..f1fed22 100644 (file)
@@ -1,3 +1,59 @@
+2005-02-01  Ken Kocienda  <kocienda@apple.com>
+
+        Reviewed by John
+
+        Fix for this bug:
+        
+        <rdar://problem/3985160> Deficiencies in pasting architecture blocking progress on other bugs
+
+        * khtml/editing/html_interchange.h: Move style span text used to mark element added to 
+        add style to this header.
+        * khtml/editing/htmlediting.cpp:
+        (khtml::styleSpanClassString): Change to use constant moved to html_interchange.h.
+        (khtml::isStyleSpan): New helper function. Checks if this is a span we added to apply style.
+        (khtml::CompositeEditCommand::insertNodeBefore): Added an assert to check that the node
+        we are inserting before is not the body.
+        (khtml::CompositeEditCommand::insertNodeAfter): Ditto, but check is for after.
+        (khtml::ReplacementFragment::ReplacementFragment): Added code to process the "default style"
+        that is added by the copy code.
+        (khtml::ReplaceSelectionCommand::ReplaceSelectionCommand): first and last nodes inserted are
+        now member variables instead of function locals. Initialize them here.
+        (khtml::ReplaceSelectionCommand::~ReplaceSelectionCommand): Deref first and last nodes inserted
+        if necessary.
+        (khtml::ReplaceSelectionCommand::doApply): Change design to fix the bug. Major change is to
+        separate out the code that inserts nodes into the tree so additional styling checks can
+        be done in a centralized way. Also got rid of the notion of "merging into the end block." That
+        concept was just wrong.
+        (khtml::ReplaceSelectionCommand::completeHTMLReplacement): Tweak interface now that first and 
+        last nodes inserted are member variables.
+        (khtml::ReplaceSelectionCommand::insertNodeAfterAndUpdateNodesInserted): New helper used
+        by replace code to do the stated DOM operation and update state internal to the command.
+        This will also be a catch point to handle the kinds of additional style checks needed to
+        make paste work right.
+        (khtml::ReplaceSelectionCommand::insertNodeAtAndUpdateNodesInserted): Ditto.
+        (khtml::ReplaceSelectionCommand::insertNodeBeforeAndUpdateNodesInserted): Ditto.
+        (khtml::ReplaceSelectionCommand::updateNodesInserted): Ditto.
+        * khtml/editing/htmlediting.h: Update declarations as needed.
+        * khtml/editing/markup.cpp:
+        (khtml::createMarkup): Adds a "default style" span to the content written to the pasteboard.
+        This will help us to fix some of the bugs blocked by the bug above.
+        * khtml/xml/dom_nodeimpl.cpp:
+        (NodeImpl::lastDescendent): New helper.
+        * khtml/xml/dom_nodeimpl.h: Ditto.
+        * khtml/xml/dom_position.cpp:
+        (DOM::Position::upstream): Fixed a bug which would allow the upstream position returned to be
+        in unrendered content.
+        (DOM::Position::downstream): Ditto.
+
+        * layout-tests/editing/deleting/delete-3775172-fix-expected.txt: Ending positions tweaked due to 
+        changes in upstream() and downstream() functions.
+        * layout-tests/editing/inserting/insert-3851164-fix-expected.txt: Ditto
+        * layout-tests/editing/inserting/insert-3907422-fix-expected.txt: Ditto
+        * layout-tests/editing/selection/extend-by-character-006-expected.txt: Ditto
+
+        * layout-tests/editing/pasteboard/paste-text-003-expected.txt: Changed what we expect, given
+        new behavior of paste code.
+
 2005-01-31  John Sullivan  <sullivan@apple.com>
 
         Reviewed by Dave Hyatt.
index 76d46fe..116e562 100644 (file)
@@ -30,6 +30,7 @@ class QString;
 
 #define AppleInterchangeNewline   "Apple-interchange-newline"
 #define AppleConvertedSpace       "Apple-converted-space"
+#define AppleStyleSpanClass       "Apple-style-span"
 
 enum EAnnotateForInterchange { DoNotAnnotateForInterchange, AnnotateForInterchange };
 
index 71ad0b4..224088f 100644 (file)
@@ -28,6 +28,7 @@
 #include "css_computedstyle.h"
 #include "css_value.h"
 #include "css_valueimpl.h"
+#include "cssparser.h"
 #include "cssproperties.h"
 #include "dom_doc.h"
 #include "dom_docimpl.h"
@@ -57,6 +58,7 @@
 using DOM::AttrImpl;
 using DOM::CSSComputedStyleDeclarationImpl;
 using DOM::CSSMutableStyleDeclarationImpl;
+using DOM::CSSParser;
 using DOM::CSSPrimitiveValue;
 using DOM::CSSPrimitiveValueImpl;
 using DOM::CSSProperty;
@@ -170,7 +172,7 @@ static DOMString &nonBreakingSpaceString()
 
 static DOMString &styleSpanClassString()
 {
-    static DOMString styleSpanClassString = "khtml-style-span";
+    static DOMString styleSpanClassString = AppleStyleSpanClass;
     return styleSpanClassString;
 }
 
@@ -190,6 +192,15 @@ static bool isEmptyStyleSpan(const NodeImpl *node)
     return false;
 }
 
+static bool isStyleSpan(const NodeImpl *node)
+{
+    if (!node || !node->isHTMLElement())
+        return false;
+
+    const HTMLElementImpl *elem = static_cast<const HTMLElementImpl *>(node);
+    return elem->getAttribute(ATTR_CLASS) == styleSpanClassString();
+}
+
 static DOMString &blockPlaceholderClassString()
 {
     static DOMString blockPlaceholderClassString = "khtml-block-placeholder";
@@ -656,12 +667,14 @@ void CompositeEditCommand::insertParagraphSeparator()
 
 void CompositeEditCommand::insertNodeBefore(NodeImpl *insertChild, NodeImpl *refChild)
 {
+    ASSERT(refChild->id() != ID_BODY);
     EditCommandPtr cmd(new InsertNodeBeforeCommand(document(), insertChild, refChild));
     applyCommandToComposite(cmd);
 }
 
 void CompositeEditCommand::insertNodeAfter(NodeImpl *insertChild, NodeImpl *refChild)
 {
+    ASSERT(refChild->id() != ID_BODY);
     if (refChild->parentNode()->lastChild() == refChild) {
         appendNode(insertChild, refChild->parentNode());
     }
@@ -3893,7 +3906,31 @@ ReplacementFragment::ReplacementFragment(DocumentFragmentImpl *fragment)
     
     m_type = TreeFragment;
 
-    NodeImpl *node = firstChild;
+    // look for default style
+    if (firstChild == lastChild && isStyleSpan(firstChild)) {
+        NodeImpl *styleSpan = firstChild;
+        DOMString styleString = static_cast<ElementImpl *>(styleSpan)->getAttribute(ATTR_STYLE);
+        if (styleString.length() > 0) {
+            CSSParser parser(true);
+            m_defaultStyle = new CSSMutableStyleDeclarationImpl;
+            if (!parser.parseDeclaration(m_defaultStyle, styleString)) {
+                m_defaultStyle->deref();
+                m_defaultStyle = 0;
+            }
+        }
+        NodeImpl *n = styleSpan->firstChild();
+        while (n) {
+            NodeImpl *next = n->traverseNextSibling();
+            n->ref();
+            removeNode(n);
+            insertNodeBefore(n, styleSpan);
+            n->deref();
+            n = next;
+        }
+        removeNode(styleSpan);
+    }
+
+    NodeImpl *node = m_fragment->firstChild();
     int realBlockCount = 0;
     NodeImpl *nodeToDelete = 0;
     while (node) {
@@ -3960,27 +3997,6 @@ NodeImpl *ReplacementFragment::mergeStartNode() const
     return node->firstChild();
 }
 
-NodeImpl *ReplacementFragment::mergeEndNode() const
-{
-    NodeImpl *node = m_fragment->lastChild();
-    while (node && node->lastChild())
-        node = node->lastChild();
-    
-    if (isProbablyBlock(node))
-        return 0;
-        
-    NodeImpl *startingBlock = enclosingBlock(node);
-    ASSERT(startingBlock != node);
-    while (node) {
-        NodeImpl *prev = node->traversePreviousNode();
-        if (prev == m_fragment || prev == startingBlock || enclosingBlock(prev) != startingBlock)
-            return node;
-        node = prev;
-    }
-    
-    return 0;
-}
-
 void ReplacementFragment::pruneEmptyNodes()
 {
     bool run = true;
@@ -4084,6 +4100,8 @@ bool isProbablyBlock(const NodeImpl *node)
 ReplaceSelectionCommand::ReplaceSelectionCommand(DocumentImpl *document, DocumentFragmentImpl *fragment, bool selectReplacement, bool smartReplace) 
     : CompositeEditCommand(document), 
       m_fragment(fragment),
+      m_firstNodeInserted(0),
+      m_lastNodeInserted(0),
       m_selectReplacement(selectReplacement), 
       m_smartReplace(smartReplace)
 {
@@ -4091,6 +4109,10 @@ ReplaceSelectionCommand::ReplaceSelectionCommand(DocumentImpl *document, Documen
 
 ReplaceSelectionCommand::~ReplaceSelectionCommand()
 {
+    if (m_firstNodeInserted)
+        m_firstNodeInserted->deref();
+    if (m_lastNodeInserted)
+        m_lastNodeInserted->deref();
 }
 
 void ReplaceSelectionCommand::doApply()
@@ -4105,16 +4127,16 @@ void ReplaceSelectionCommand::doApply()
     NodeImpl *startBlock = selection.start().node()->enclosingBlockFlowElement();
     NodeImpl *endBlock = selection.end().node()->enclosingBlockFlowElement();
     bool mergeStart = false;
-    bool mergeEnd = false;
     if (startBlock == startBlock->rootEditableElement() && startAtStartOfBlock && startAtEndOfBlock) {
         // Empty document. Merge neither start nor end.
-        mergeStart = mergeEnd = false;
+        mergeStart = false;
     }
     else {
         mergeStart = !isStartOfParagraph(visibleStart) || (!m_fragment.hasInterchangeNewline() && !m_fragment.hasMoreThanOneBlock());
-        mergeEnd = !m_fragment.hasInterchangeNewline() && m_fragment.hasMoreThanOneBlock() && !isEndOfParagraph(visibleEnd);
     }
     
+    bool moveNodesAfterEnd = !m_fragment.hasInterchangeNewline() && (startBlock != endBlock || m_fragment.hasMoreThanOneBlock());
+    
     Position startPos = Position(selection.start().node()->enclosingBlockFlowElement(), 0);
     Position endPos; 
     EStayInBlock upstreamStayInBlock = StayInBlock;
@@ -4123,7 +4145,8 @@ void ReplaceSelectionCommand::doApply()
     if (selection.isRange()) {
         deleteSelection(false, !(m_fragment.hasInterchangeNewline() || m_fragment.hasMoreThanOneBlock()));
     }
-    else if (selection.isCaret() && mergeEnd && !startAtBlockBoundary) {
+    else if (selection.isCaret() && !startAtBlockBoundary &&
+             !m_fragment.hasInterchangeNewline() && m_fragment.hasMoreThanOneBlock() && !isEndOfParagraph(visibleEnd)) {
         // The start and the end need to wind up in separate blocks.
         // Insert a paragraph separator to make that happen.
         insertParagraphSeparator();
@@ -4181,68 +4204,25 @@ void ReplaceSelectionCommand::doApply()
         }
     }
 
+    // Merge content into the start block, if necessary.
     document()->updateLayout();
-
     Position insertionPos = startPos;
-    NodeImpl *firstNodeInserted = 0;
-    NodeImpl *lastNodeInserted = 0;
-    bool lastNodeInsertedInMergeEnd = false;
-
-    // prune empty nodes from fragment
-    m_fragment.pruneEmptyNodes();
-
-    // Merge content into the end block, if necessary.
-    if (mergeEnd) {
-        NodeImpl *node = m_fragment.mergeEndNode();
-        if (node) {
-            NodeImpl *refNode = node;
-            NodeImpl *node = refNode ? refNode->nextSibling() : 0;
-            insertNodeAt(refNode, endPos.node(), endPos.offset());
-            firstNodeInserted = refNode;
-            lastNodeInserted = refNode;
-            while (node && !isProbablyBlock(node)) {
-                NodeImpl *next = node->nextSibling();
-                insertNodeAfter(node, refNode);
-                lastNodeInserted = node;
-                refNode = node;
-                node = next;
-            }
-            lastNodeInsertedInMergeEnd = true;
-        }
-    }
-    
-    // prune empty nodes from fragment
-    m_fragment.pruneEmptyNodes();
-
-    // Merge content into the start block, if necessary.
     if (mergeStart) {
         NodeImpl *node = m_fragment.mergeStartNode();
-        NodeImpl *insertionNode = 0;
         if (node) {
             NodeImpl *refNode = node;
             NodeImpl *node = refNode ? refNode->nextSibling() : 0;
-            insertNodeAt(refNode, startPos.node(), startPos.offset());
-            firstNodeInserted = refNode;
-            if (!lastNodeInsertedInMergeEnd)
-                lastNodeInserted = refNode;
-            insertionNode = refNode;
+            insertNodeAtAndUpdateNodesInserted(refNode, startPos.node(), startPos.offset());
             while (node && !isProbablyBlock(node)) {
                 NodeImpl *next = node->nextSibling();
-                insertNodeAfter(node, refNode);
-                if (!lastNodeInsertedInMergeEnd)
-                    lastNodeInserted = node;
-                insertionNode = node;
+                insertNodeAfterAndUpdateNodesInserted(node, refNode);
                 refNode = node;
                 node = next;
             }
         }
-        if (insertionNode) {
-            if (insertionNode->isTextNode())
-                insertionPos = Position(insertionNode, insertionNode->caretMaxOffset());
-            else if (insertionNode->childNodeCount() > 0)
-                insertionPos = Position(insertionNode, insertionNode->childNodeCount());
-            else
-                insertionPos = Position(insertionNode->parentNode(), insertionNode->nodeIndex() + 1);
+        if (m_lastNodeInserted) {
+            document()->updateLayout();
+            insertionPos = Position(m_lastNodeInserted, m_lastNodeInserted->caretMaxOffset());
         }
     }
 
@@ -4254,70 +4234,56 @@ void ReplaceSelectionCommand::doApply()
     if (node) {
         NodeImpl *refNode = node;
         NodeImpl *node = refNode ? refNode->nextSibling() : 0;
+        NodeImpl *insertionBlock = insertionPos.node()->enclosingBlockFlowElement();
+        bool insertionBlockIsBody = insertionBlock->id() == ID_BODY;
         VisiblePosition visiblePos(insertionPos);
-        bool insertionNodeIsBody = insertionPos.node()->id() == ID_BODY;
-        if (!mergeStart && !insertionNodeIsBody && isProbablyBlock(refNode) && isStartOfParagraph(visiblePos)) {
-            Position pos = insertionPos;
-            if (!insertionPos.node()->isTextNode() && !insertionPos.node()->isBlockFlow() && insertionPos.offset() > 0)
-                pos = insertionPos.downstream(StayInBlock);
-            insertNodeBefore(refNode, pos.node());
+        if (!insertionBlockIsBody && isProbablyBlock(refNode) && isFirstVisiblePositionInBlock(visiblePos))
+            insertNodeBeforeAndUpdateNodesInserted(refNode, insertionBlock);
+        else if (!insertionBlockIsBody && isProbablyBlock(refNode) && isLastVisiblePositionInBlock(visiblePos)) {
+            insertNodeAfterAndUpdateNodesInserted(refNode, insertionBlock);
+        }
+        else if (mergeStart && !isProbablyBlock(refNode)){
+            Position pos = insertionPos.downstream();
+            insertNodeAtAndUpdateNodesInserted(refNode, pos.node(), pos.offset());
         }
-        else if (!mergeEnd && !insertionNodeIsBody && isProbablyBlock(refNode) && isEndOfParagraph(visiblePos))
-            insertNodeAfter(refNode, insertionPos.node());
         else {
-            insertNodeAt(refNode, insertionPos.node(), insertionPos.offset());
-            if (insertionPos.node() == endPos.node() && insertionPos.offset() <= endPos.offset())
-                endPos = Position(endPos.node(), endPos.offset() + 1);
+            insertNodeAtAndUpdateNodesInserted(refNode, insertionPos.node(), insertionPos.offset());
         }
-        if (!firstNodeInserted)
-            firstNodeInserted = refNode;
-        if (!lastNodeInsertedInMergeEnd)
-            lastNodeInserted = refNode;
         while (node) {
             NodeImpl *next = node->nextSibling();
-            insertNodeAfter(node, refNode);
-            if (!lastNodeInsertedInMergeEnd)
-                lastNodeInserted = node;
+            insertNodeAfterAndUpdateNodesInserted(node, refNode);
             refNode = node;
             node = next;
         }
         document()->updateLayout();
-        if (lastNodeInserted->isTextNode())
-            insertionPos = Position(lastNodeInserted, lastNodeInserted->caretMaxOffset());
-        else if (lastNodeInserted->childNodeCount() > 0)
-            insertionPos = Position(lastNodeInserted, lastNodeInserted->childNodeCount());
-        else
-            insertionPos = Position(lastNodeInserted->parentNode(), lastNodeInserted->nodeIndex() + 1);
+        insertionPos = Position(m_lastNodeInserted, m_lastNodeInserted->caretMaxOffset());
     }
 
     // Handle "smart replace" whitespace
-    if (addTrailingSpace && lastNodeInserted) {
-        if (lastNodeInserted->isTextNode()) {
-            TextImpl *text = static_cast<TextImpl *>(lastNodeInserted);
+    if (addTrailingSpace && m_lastNodeInserted) {
+        if (m_lastNodeInserted->isTextNode()) {
+            TextImpl *text = static_cast<TextImpl *>(m_lastNodeInserted);
             insertTextIntoNode(text, text->length(), nonBreakingSpaceString());
             insertionPos = Position(text, text->length());
         }
         else {
             NodeImpl *node = document()->createEditingTextNode(nonBreakingSpaceString());
-            insertNodeAfter(node, lastNodeInserted);
-            if (!firstNodeInserted)
-                firstNodeInserted = node;
-            lastNodeInserted = node;
+            insertNodeAfter(node, m_lastNodeInserted);
+            if (!m_firstNodeInserted)
+                m_firstNodeInserted = node;
+            m_lastNodeInserted = node;
             insertionPos = Position(node, 1);
         }
     }
 
-    if (addLeadingSpace && firstNodeInserted) {
-        if (firstNodeInserted->isTextNode()) {
-            TextImpl *text = static_cast<TextImpl *>(firstNodeInserted);
+    if (addLeadingSpace && m_firstNodeInserted) {
+        if (m_firstNodeInserted->isTextNode()) {
+            TextImpl *text = static_cast<TextImpl *>(m_firstNodeInserted);
             insertTextIntoNode(text, 0, nonBreakingSpaceString());
         }
         else {
             NodeImpl *node = document()->createEditingTextNode(nonBreakingSpaceString());
-            insertNodeBefore(node, firstNodeInserted);
-            firstNodeInserted = node;
-            if (!lastNodeInsertedInMergeEnd)
-                lastNodeInserted = node;
+            insertNodeBefore(node, m_firstNodeInserted);
         }
     }
 
@@ -4331,19 +4297,50 @@ void ReplaceSelectionCommand::doApply()
         completeHTMLReplacement(startPos, endPos);
     }
     else {
-        if (lastNodeInserted && lastNodeInserted->id() == ID_BR && !document()->inStrictMode()) {
+        if (m_lastNodeInserted && m_lastNodeInserted->id() == ID_BR && !document()->inStrictMode()) {
             document()->updateLayout();
-            VisiblePosition pos(Position(lastNodeInserted, 0));
+            VisiblePosition pos(Position(m_lastNodeInserted, 0));
             if (isLastVisiblePositionInBlock(pos)) {
-                NodeImpl *next = lastNodeInserted->traverseNextNode();
-                bool hasTrailingBR = next && next->id() == ID_BR && lastNodeInserted->enclosingBlockFlowElement() == next->enclosingBlockFlowElement();
+                NodeImpl *next = m_lastNodeInserted->traverseNextNode();
+                bool hasTrailingBR = next && next->id() == ID_BR && m_lastNodeInserted->enclosingBlockFlowElement() == next->enclosingBlockFlowElement();
                 if (!hasTrailingBR) {
                     // Insert an "extra" BR at the end of the block. 
-                    insertNodeBefore(createBreakElement(document()), lastNodeInserted);
+                    insertNodeBefore(createBreakElement(document()), m_lastNodeInserted);
                 }
             }
         }
-        completeHTMLReplacement(firstNodeInserted, lastNodeInserted);
+
+        if (moveNodesAfterEnd) {
+            QPtrList<NodeImpl> blocks;
+            NodeImpl *node = Position(m_lastNodeInserted, m_lastNodeInserted->caretMaxOffset()).downstream().node();
+            if (node != m_lastNodeInserted) {
+                NodeImpl *refNode = m_lastNodeInserted;
+                while (node) {
+                    NodeImpl *next = node->nextSibling();
+                    blocks.append(node->enclosingBlockFlowElement());
+                    removeNode(node);
+                    // Call the base class insert after code here.
+                    // No need to update inserted node variables.
+                    insertNodeAfter(node, refNode);
+                    refNode = node;
+                    node = next;
+                }
+            }
+            document()->updateLayout();
+            for (QPtrListIterator<NodeImpl> it(blocks); it.current(); ++it) {
+                NodeImpl *blockToRemove = it.current();
+                if (!blockToRemove->inDocument())
+                    continue;
+                if (!blockToRemove->renderer() || !blockToRemove->renderer()->firstChild()) {
+                    if (blockToRemove->parentNode())
+                        blocks.append(blockToRemove->parentNode()->enclosingBlockFlowElement());
+                    removeNode(blockToRemove);
+                    document()->updateLayout();
+                }
+            }
+        }
+    
+        completeHTMLReplacement();
     }
     
     if (placeholderBlock) {
@@ -4361,14 +4358,14 @@ void ReplaceSelectionCommand::completeHTMLReplacement(const Position &start, con
     rebalanceWhitespace();
 }
 
-void ReplaceSelectionCommand::completeHTMLReplacement(NodeImpl *firstNodeInserted, NodeImpl *lastNodeInserted)
+void ReplaceSelectionCommand::completeHTMLReplacement()
 {
-    if (!firstNodeInserted || !firstNodeInserted->inDocument() ||
-        !lastNodeInserted || !lastNodeInserted->inDocument())
+    if (!m_firstNodeInserted || !m_firstNodeInserted->inDocument() ||
+        !m_lastNodeInserted || !m_lastNodeInserted->inDocument())
         return;
 
     // Find the last leaf.
-    NodeImpl *lastLeaf = lastNodeInserted;
+    NodeImpl *lastLeaf = m_lastNodeInserted;
     while (1) {
         NodeImpl *nextChild = lastLeaf->lastChild();
         if (!nextChild)
@@ -4377,7 +4374,7 @@ void ReplaceSelectionCommand::completeHTMLReplacement(NodeImpl *firstNodeInserte
     }
 
     // Find the first leaf.
-    NodeImpl *firstLeaf = firstNodeInserted;
+    NodeImpl *firstLeaf = m_firstNodeInserted;
     while (1) {
         NodeImpl *nextChild = firstLeaf->firstChild();
         if (!nextChild)
@@ -4404,6 +4401,44 @@ EditAction ReplaceSelectionCommand::editingAction() const
     return EditActionPaste;
 }
 
+void ReplaceSelectionCommand::insertNodeAfterAndUpdateNodesInserted(NodeImpl *insertChild, NodeImpl *refChild)
+{
+    insertNodeAfter(insertChild, refChild);
+    updateNodesInserted(insertChild);
+}
+
+void ReplaceSelectionCommand::insertNodeAtAndUpdateNodesInserted(NodeImpl *insertChild, NodeImpl *refChild, long offset)
+{
+    insertNodeAt(insertChild, refChild, offset);
+    updateNodesInserted(insertChild);
+}
+
+void ReplaceSelectionCommand::insertNodeBeforeAndUpdateNodesInserted(NodeImpl *insertChild, NodeImpl *refChild)
+{
+    insertNodeBefore(insertChild, refChild);
+    updateNodesInserted(insertChild);
+}
+
+void ReplaceSelectionCommand::updateNodesInserted(NodeImpl *node)
+{
+    if (!node)
+        return;
+
+    if (!m_firstNodeInserted) {
+        m_firstNodeInserted = node;
+        m_firstNodeInserted->ref();
+    }
+    
+    if (node == m_lastNodeInserted)
+        return;
+        
+    NodeImpl *old = m_lastNodeInserted;
+    m_lastNodeInserted = node->lastDescendent();
+    m_lastNodeInserted->ref();
+    if (old)
+        old->deref();
+}
+
 //------------------------------------------------------------------------------------------
 // SetNodeAttributeCommand
 
index 4d0b3c4..5b2883f 100644 (file)
@@ -657,7 +657,6 @@ public:
     DOM::NodeImpl *lastChild() const;
 
     DOM::NodeImpl *mergeStartNode() const;
-    DOM::NodeImpl *mergeEndNode() const;
     
     void pruneEmptyNodes();
 
@@ -683,6 +682,7 @@ private:
     void insertNodeBefore(DOM::NodeImpl *node, DOM::NodeImpl *refNode);
 
     EFragmentType m_type;
+    DOM::CSSMutableStyleDeclarationImpl *m_defaultStyle;
     DOM::DocumentFragmentImpl *m_fragment;
     bool m_hasInterchangeNewline;
     bool m_hasMoreThanOneBlock;
@@ -702,9 +702,17 @@ public:
 
 private:
     void completeHTMLReplacement(const DOM::Position &, const DOM::Position &);
-    void completeHTMLReplacement(DOM::NodeImpl *, DOM::NodeImpl *);
+    void completeHTMLReplacement();
+
+    void insertNodeAfterAndUpdateNodesInserted(DOM::NodeImpl *insertChild, DOM::NodeImpl *refChild);
+    void insertNodeAtAndUpdateNodesInserted(DOM::NodeImpl *insertChild, DOM::NodeImpl *refChild, long offset);
+    void insertNodeBeforeAndUpdateNodesInserted(DOM::NodeImpl *insertChild, DOM::NodeImpl *refChild);
+
+    void updateNodesInserted(DOM::NodeImpl *);
     
     ReplacementFragment m_fragment;
+    DOM::NodeImpl *m_firstNodeInserted;
+    DOM::NodeImpl *m_lastNodeInserted;
     bool m_selectReplacement;
     bool m_smartReplace;
 };
index a0654ba..dcd0cf0 100644 (file)
@@ -27,6 +27,8 @@
 
 #include "htmlediting.h"
 
+#include "css/css_computedstyle.h"
+#include "css/css_valueimpl.h"
 #include "html/html_elementimpl.h"
 #include "xml/dom_position.h"
 #include "xml/dom2_rangeimpl.h"
@@ -37,6 +39,8 @@
 
 using DOM::AttributeImpl;
 using DOM::CommentImpl;
+using DOM::CSSComputedStyleDeclarationImpl;
+using DOM::CSSMutableStyleDeclarationImpl;
 using DOM::DocumentFragmentImpl;
 using DOM::DocumentImpl;
 using DOM::DOMString;
@@ -352,6 +356,15 @@ QString createMarkup(const RangeImpl *range, QPtrList<NodeImpl> *nodes, EAnnotat
         }
     }
 
+    // add in the "default style" for this markup
+    Position pos(commonAncestor->getDocument()->documentElement(), 0);
+    CSSMutableStyleDeclarationImpl *style = pos.computedStyle()->copyInheritableProperties();
+    style->ref();
+    QString openTag = QString("<span class=\"") + AppleStyleSpanClass + "\" style=\"" + style->cssText().string() + "\">";
+    markups.prepend(openTag);
+    markups.append("</span>");
+    style->deref();
+
     return markups.join("");
 }
 
index a955653..31fa637 100644 (file)
@@ -161,6 +161,14 @@ NodeImpl *NodeImpl::lastChild() const
   return 0;
 }
 
+NodeImpl *NodeImpl::lastDescendent() const
+{
+    NodeImpl *n = const_cast<NodeImpl *>(this);
+    while (n && n->lastChild())
+        n = n->lastChild();
+    return n;
+}
+
 NodeImpl *NodeImpl::insertBefore( NodeImpl *newChild, NodeImpl *, int &exceptioncode )
 {
     newChild->ref();
index 8b5b944..6547d18 100644 (file)
@@ -117,6 +117,8 @@ public:
     virtual void setPrefix(const DOMString &_prefix, int &exceptioncode );
     void normalize ();
 
+    NodeImpl *lastDescendent() const;
+
     // Other methods (not part of DOM)
     virtual bool isElementNode() const { return false; }
     virtual bool isHTMLElement() const { return false; }
index 681be7a..7f1b2fa 100644 (file)
@@ -365,6 +365,7 @@ Position Position::upstream(EStayInBlock stayInBlock) const
         return Position();
 
     NodeImpl *block = startNode->enclosingBlockFlowElement();
+    Position lastVisible;
     
     PositionIterator it(start);
     for (; !it.atStart(); it.previous()) {
@@ -383,6 +384,8 @@ Position Position::upstream(EStayInBlock stayInBlock) const
         if (renderer->style()->visibility() != VISIBLE)
             continue;
 
+        lastVisible = it.current();
+
         if (renderer->isReplaced() || renderer->isBR()) {
             if (it.current().offset() >= renderer->caretMaxOffset())
                 return Position(currentNode, renderer->caretMaxOffset());
@@ -410,7 +413,7 @@ Position Position::upstream(EStayInBlock stayInBlock) const
         }
     }
     
-    return it.current();
+    return lastVisible.isNotNull() ? lastVisible : *this;
 }
 
 Position Position::downstream(EStayInBlock stayInBlock) const
@@ -421,6 +424,7 @@ Position Position::downstream(EStayInBlock stayInBlock) const
         return Position();
 
     NodeImpl *block = startNode->enclosingBlockFlowElement();
+    Position lastVisible;
     
     PositionIterator it(start);            
     for (; !it.atEnd(); it.next()) {   
@@ -439,6 +443,8 @@ Position Position::downstream(EStayInBlock stayInBlock) const
         if (renderer->style()->visibility() != VISIBLE)
             continue;
 
+        lastVisible = it.current();
+
         if (currentNode != startNode && renderer->isBlockFlow()) {
             if (it.current().offset() == 0) {
                 // If no first child, or first visible child is a not a block, return; otherwise continue.
@@ -484,7 +490,7 @@ Position Position::downstream(EStayInBlock stayInBlock) const
         }
     }
     
-    return it.current();
+    return lastVisible.isNotNull() ? lastVisible : *this;
 }
 
 Position Position::equivalentRangeCompliantPosition() const