LayoutTests:
authorjusting <justing@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 25 Mar 2006 03:28:51 +0000 (03:28 +0000)
committerjusting <justing@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 25 Mar 2006 03:28:51 +0000 (03:28 +0000)
        Reviewed by harrison

        <http://bugzilla.opendarwin.org/show_bug.cgi?id=7955>
        REGRESSION: Content with an interchange newline lost when pasted at the end of the document

        * editing/pasteboard/7955-expected.checksum: Added.
        * editing/pasteboard/7955-expected.png: Added.
        * editing/pasteboard/7955-expected.txt: Added.
        * editing/pasteboard/7955.html: Added.

WebCore:

        Reviewed by harrison

        <http://bugzilla.opendarwin.org/show_bug.cgi?id=7955>
        REGRESSION: Content with an interchange newline lost when pasted at the end of the document

        There's no safe place in the document to keep the fragment while pasting, so I avoid
        isProbablyBlock by saving whether or not something was blockFlow during the test
        insertion.

        * editing/ReplaceSelectionCommand.cpp:
        (WebCore::ReplacementFragment::ReplacementFragment):
        (WebCore::ReplacementFragment::~ReplacementFragment):
        (WebCore::ReplacementFragment::firstChild):
        (WebCore::ReplacementFragment::lastChild):
        (WebCore::ReplacementFragment::mergeStartNode):
        (WebCore::ReplacementFragment::enclosingBlock):
        (WebCore::ReplacementFragment::insertFragmentForTestRendering):
        (WebCore::ReplacementFragment::restoreTestRenderingNodesToFragment):
        (WebCore::ReplacementFragment::isBlockFlow):
        (WebCore::ReplaceSelectionCommand::fixupNodeStyles):
        (WebCore::styleForNode):
        (WebCore::ReplacementFragment::saveRenderingInfo):
        (WebCore::ReplacementFragment::removeUnrenderedNodes):
        (WebCore::ReplacementFragment::renderedBlocks):
        (WebCore::ReplacementFragment::removeStyleNodes):
        (WebCore::RenderingInfo::RenderingInfo):
        (WebCore::ReplaceSelectionCommand::ReplaceSelectionCommand):
        (WebCore::ReplaceSelectionCommand::doApply):
        * editing/ReplaceSelectionCommand.h:
        (WebCore::RenderingInfo::isBlockFlow):
        (WebCore::ReplacementFragment::renderingInfo):
        (WebCore::ReplacementFragment::nodes):

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

LayoutTests/ChangeLog
LayoutTests/editing/pasteboard/7955-expected.checksum [new file with mode: 0644]
LayoutTests/editing/pasteboard/7955-expected.png [new file with mode: 0644]
LayoutTests/editing/pasteboard/7955-expected.txt [new file with mode: 0644]
LayoutTests/editing/pasteboard/7955.html [new file with mode: 0644]
WebCore/ChangeLog
WebCore/editing/ReplaceSelectionCommand.cpp
WebCore/editing/ReplaceSelectionCommand.h

index a703c813438ea4156df2f0849dd46bf0f11f0716..2c24d767072798407a0a3b5c61d91bd42b61f01b 100644 (file)
@@ -1,3 +1,15 @@
+2006-03-24  Justin Garcia  <justin.garcia@apple.com>
+
+        Reviewed by harrison
+        
+        <http://bugzilla.opendarwin.org/show_bug.cgi?id=7955>
+        REGRESSION: Content with an interchange newline lost when pasted at the end of the document
+        
+        * editing/pasteboard/7955-expected.checksum: Added.
+        * editing/pasteboard/7955-expected.png: Added.
+        * editing/pasteboard/7955-expected.txt: Added.
+        * editing/pasteboard/7955.html: Added.
+
 2006-03-24  Darin Adler  <darin@apple.com>
 
         Reviewed by Dave Harrison.
diff --git a/LayoutTests/editing/pasteboard/7955-expected.checksum b/LayoutTests/editing/pasteboard/7955-expected.checksum
new file mode 100644 (file)
index 0000000..1be59c0
--- /dev/null
@@ -0,0 +1 @@
+1b9e4ed2024567e5699a634cc8aeacf8
\ No newline at end of file
diff --git a/LayoutTests/editing/pasteboard/7955-expected.png b/LayoutTests/editing/pasteboard/7955-expected.png
new file mode 100644 (file)
index 0000000..57c2094
Binary files /dev/null and b/LayoutTests/editing/pasteboard/7955-expected.png differ
diff --git a/LayoutTests/editing/pasteboard/7955-expected.txt b/LayoutTests/editing/pasteboard/7955-expected.txt
new file mode 100644 (file)
index 0000000..c46fa41
--- /dev/null
@@ -0,0 +1,12 @@
+layer at (0,0) size 800x600
+  RenderCanvas 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 784x36
+        RenderBlock {DIV} at (0,0) size 784x18
+          RenderText {TEXT} at (0,0) size 21x18
+            text run at (0,0) width 21: "foo"
+        RenderBlock (anonymous) at (0,18) size 784x18
+          RenderText {TEXT} at (0,0) size 20x18
+            text run at (0,0) width 20: "bar"
diff --git a/LayoutTests/editing/pasteboard/7955.html b/LayoutTests/editing/pasteboard/7955.html
new file mode 100644 (file)
index 0000000..869d3ac
--- /dev/null
@@ -0,0 +1 @@
+<div contenteditable><div>foo</div>bar</div>
\ No newline at end of file
index ac7b32784ebdd3c0121c56bdfed6f506557e05f9..2579d24908ec9d618397136754988420f699f51f 100644 (file)
@@ -1,9 +1,47 @@
+<<<<<<< .mine
+2006-03-24  Justin Garcia  <justin.garcia@apple.com>
+
+        Reviewed by harrison
+        
+        <http://bugzilla.opendarwin.org/show_bug.cgi?id=7955>
+        REGRESSION: Content with an interchange newline lost when pasted at the end of the document
+        
+        There's no safe place in the document to keep the fragment while pasting, so I avoid
+        isProbablyBlock by saving whether or not something was blockFlow during the test 
+        insertion.
+
+        * editing/ReplaceSelectionCommand.cpp:
+        (WebCore::ReplacementFragment::ReplacementFragment):
+        (WebCore::ReplacementFragment::~ReplacementFragment):
+        (WebCore::ReplacementFragment::firstChild):
+        (WebCore::ReplacementFragment::lastChild):
+        (WebCore::ReplacementFragment::mergeStartNode):
+        (WebCore::ReplacementFragment::enclosingBlock):
+        (WebCore::ReplacementFragment::insertFragmentForTestRendering):
+        (WebCore::ReplacementFragment::restoreTestRenderingNodesToFragment):
+        (WebCore::ReplacementFragment::isBlockFlow):
+        (WebCore::ReplaceSelectionCommand::fixupNodeStyles):
+        (WebCore::styleForNode):
+        (WebCore::ReplacementFragment::saveRenderingInfo):
+        (WebCore::ReplacementFragment::removeUnrenderedNodes):
+        (WebCore::ReplacementFragment::renderedBlocks):
+        (WebCore::ReplacementFragment::removeStyleNodes):
+        (WebCore::RenderingInfo::RenderingInfo):
+        (WebCore::ReplaceSelectionCommand::ReplaceSelectionCommand):
+        (WebCore::ReplaceSelectionCommand::doApply):
+        * editing/ReplaceSelectionCommand.h:
+        (WebCore::RenderingInfo::isBlockFlow):
+        (WebCore::ReplacementFragment::renderingInfo):
+        (WebCore::ReplacementFragment::nodes):
+
+=======
 2006-03-24  Eric Seidel  <eseidel@apple.com>
 
         Reviewed by justing.
 
         * generate-derived-sources: fix clean builds on mac.
 
+>>>>>>> .r13474
 2006-03-24  Darin Adler  <darin@apple.com>
 
         Reviewed by Dave Harrison.
index c7f58d2a51ded799606a471a9182ad02da66f84b..48a884e7e812e2ff77f04900754d824711cff2e9 100644 (file)
@@ -55,6 +55,7 @@ using namespace HTMLNames;
 
 ReplacementFragment::ReplacementFragment(Document *document, DocumentFragment *fragment, bool matchStyle)
     : m_document(document),
+      m_fragment(fragment),
       m_matchStyle(matchStyle), 
       m_hasInterchangeNewlineAtStart(false), 
       m_hasInterchangeNewlineAtEnd(false), 
@@ -115,49 +116,50 @@ ReplacementFragment::ReplacementFragment(Document *document, DocumentFragment *f
         removeNode(newlineAtStartNode);
     if (newlineAtEndNode)
         removeNode(newlineAtEndNode);
-    
-    insertFragmentForTestRendering(fragment);
+        
+    RefPtr<Node> holder = insertFragmentForTestRendering();
     
     // Send khtmlBeforeTextInsertedEvent.  The event handler will update text if necessary.
     if (m_document->frame()) {
         Node* selectionStartNode = m_document->frame()->selection().start().node();
         if (selectionStartNode && selectionStartNode->rootEditableElement()) {
-            RefPtr<Range> range = new Range(m_holder->getDocument());
+            RefPtr<Range> range = new Range(holder->getDocument());
             ExceptionCode ec = 0;
-            range->selectNodeContents(m_holder.get(), ec);
+            range->selectNodeContents(holder.get(), ec);
             String text = plainText(range.get());
             String newText = text.copy();
             RefPtr<Event> evt = new BeforeTextInsertedEvent(newText);
             selectionStartNode->rootEditableElement()->dispatchEvent(evt, ec, true);
             if (text != newText) {
-                fragment->removeChildren();
-                fragment->appendChild(new Text(m_document.get(), newText), ec);
-                insertFragmentForTestRendering(fragment);
+                // If the event handler has changed the text, create a new holder node for test rendering
+                m_fragment->removeChildren();
+                m_fragment->appendChild(new Text(m_document.get(), newText), ec);
+                removeNode(holder);
+                holder = insertFragmentForTestRendering();
             }
         }
     }
     
-    if (!m_matchStyle)
-        computeAndStoreNodeStyles();
-    removeUnrenderedNodes();
-    m_hasMoreThanOneBlock = renderedBlocks() > 1;
+    saveRenderingInfo(holder.get());
+    removeUnrenderedNodes(holder.get());
+    m_hasMoreThanOneBlock = renderedBlocks(holder.get()) > 1;
+    restoreTestRenderingNodesToFragment(holder.get());
+    removeNode(holder);
     removeStyleNodes();
 }
 
 ReplacementFragment::~ReplacementFragment()
 {
-    if (m_holder && m_holder->inDocument())
-        removeNode(m_holder);
 }
 
 Node *ReplacementFragment::firstChild() const 
 { 
-    return m_holder->firstChild(); 
+    return m_fragment->firstChild(); 
 }
 
 Node *ReplacementFragment::lastChild() const 
 { 
-    return m_holder->lastChild(); 
+    return m_fragment->lastChild(); 
 }
 
 static bool isMailPasteAsQuotationNode(const Node *node)
@@ -167,8 +169,8 @@ static bool isMailPasteAsQuotationNode(const Node *node)
 
 Node *ReplacementFragment::mergeStartNode() const
 {
-    Node *node = m_holder->firstChild();
-    while (node && node->isBlockFlow() && !isMailPasteAsQuotationNode(node))
+    Node *node = m_fragment->firstChild();
+    while (node && isBlockFlow(node) && !isMailPasteAsQuotationNode(node))
         node = node->traverseNextNode();
     return node;
 }
@@ -189,9 +191,9 @@ bool ReplacementFragment::isInterchangeConvertedSpaceSpan(const Node *node)
 
 Node *ReplacementFragment::enclosingBlock(Node *node) const
 {
-    while (node && !node->isBlockFlow())
+    while (node && !isBlockFlow(node))
         node = node->parentNode();    
-    return node ? node : m_holder.get();
+    return node ? node : m_fragment.get();
 }
 
 void ReplacementFragment::removeNodePreservingChildren(Node *node)
@@ -234,25 +236,48 @@ void ReplacementFragment::insertNodeBefore(Node *node, Node *refNode)
     ASSERT(ec == 0);
 }
 
-void ReplacementFragment::insertFragmentForTestRendering(DocumentFragment* fragment)
+PassRefPtr<Node> ReplacementFragment::insertFragmentForTestRendering()
 {
     Node *body = m_document->body();
     if (!body)
-        return;
-
-    if (m_holder && m_holder->inDocument())
-        removeNode(m_holder);
+        return 0;
 
-    m_holder = createDefaultParagraphElement(m_document.get());
+    RefPtr<Node> holder = createDefaultParagraphElement(m_document.get());
     
     ExceptionCode ec = 0;
-    m_holder->appendChild(fragment, ec);
+    holder->appendChild(m_fragment, ec);
     ASSERT(ec == 0);
     
-    body->appendChild(m_holder.get(), ec);
+    body->appendChild(holder.get(), ec);
     ASSERT(ec == 0);
     
     m_document->updateLayoutIgnorePendingStylesheets();
+    
+    return holder.release();
+}
+
+void ReplacementFragment::restoreTestRenderingNodesToFragment(Node *holder)
+{
+    if (!holder)
+        return;
+    
+    ExceptionCode ec = 0;
+    while (RefPtr<Node> node = holder->firstChild()) {
+        holder->removeChild(node.get(), ec);
+        ASSERT(ec == 0);
+        m_fragment->appendChild(node.get(), ec);
+        ASSERT(ec == 0);
+    }
+}
+
+bool ReplacementFragment::isBlockFlow(Node* node) const
+{
+    RefPtr<RenderingInfo> info = m_renderingInfo.get(node);
+    ASSERT(info);
+    if (!info)
+        return false;
+    
+    return info->isBlockFlow();
 }
 
 static String &matchNearestBlockquoteColorString()
@@ -276,17 +301,21 @@ void ReplaceSelectionCommand::removeNodeAndPruneAncestors(Node* node)
     }
 }
 
-void ReplaceSelectionCommand::fixupNodeStyles(const DeprecatedValueList<NodeDesiredStyle> &list)
+void ReplaceSelectionCommand::fixupNodeStyles(const NodeVector& nodes, const RenderingInfoMap& renderingInfo)
 {
     // This function uses the mapped "desired style" to apply the additional style needed, if any,
     // to make the node have the desired style.
 
     updateLayout();
-
-    DeprecatedValueListConstIterator<NodeDesiredStyle> it;
-    for (it = list.begin(); it != list.end(); ++it) {
-        Node *node = (*it).node();
-        CSSMutableStyleDeclaration *desiredStyle = (*it).style();
+    
+    NodeVector::const_iterator e = nodes.end();
+    for (NodeVector::const_iterator it = nodes.begin(); it != e; ++it) {
+        Node *node = (*it).get();
+        RefPtr<RenderingInfo> info = renderingInfo.get(node);
+        ASSERT(info);
+        if (!info)
+            continue;
+        CSSMutableStyleDeclaration *desiredStyle = info->style();
         ASSERT(desiredStyle);
 
         if (!node->inDocument())
@@ -325,14 +354,13 @@ void ReplaceSelectionCommand::fixupNodeStyles(const DeprecatedValueList<NodeDesi
     }
 }
 
-static void computeAndStoreNodeDesiredStyle(WebCore::Node *node, DeprecatedValueList<NodeDesiredStyle> &list)
+static PassRefPtr<CSSMutableStyleDeclaration> styleForNode(Node *node)
 {
     if (!node || !node->inDocument())
-        return;
+        return 0;
         
     RefPtr<CSSComputedStyleDeclaration> computedStyle = Position(node, 0).computedStyle();
     RefPtr<CSSMutableStyleDeclaration> style = computedStyle->copyInheritableProperties();
-    list.append(NodeDesiredStyle(node, style));
 
     // In either of the color-matching tests below, set the color to a pseudo-color that will
     // make the content take on the color of the nearest-enclosing blockquote (if any) after
@@ -342,7 +370,7 @@ static void computeAndStoreNodeDesiredStyle(WebCore::Node *node, DeprecatedValue
         bool match = (blockquoteStyle->getPropertyValue(CSS_PROP_COLOR) == style->getPropertyValue(CSS_PROP_COLOR));
         if (match) {
             style->setProperty(CSS_PROP__KHTML_MATCH_NEAREST_MAIL_BLOCKQUOTE_COLOR, matchNearestBlockquoteColorString());
-            return;
+            return style.release();
         }
     }
     Node *documentElement = node->getDocument()->documentElement();
@@ -350,25 +378,31 @@ static void computeAndStoreNodeDesiredStyle(WebCore::Node *node, DeprecatedValue
     bool match = (documentStyle->getPropertyValue(CSS_PROP_COLOR) == style->getPropertyValue(CSS_PROP_COLOR));
     if (match)
         style->setProperty(CSS_PROP__KHTML_MATCH_NEAREST_MAIL_BLOCKQUOTE_COLOR, matchNearestBlockquoteColorString());
+        
+    return style.release();
 }
 
-void ReplacementFragment::computeAndStoreNodeStyles()
+void ReplacementFragment::saveRenderingInfo(Node *holder)
 {
-    ASSERT(m_holder && m_holder->inDocument());
-
     m_document->updateLayoutIgnorePendingStylesheets();
-
-    for (Node *node = m_holder->firstChild(); node; node = node->traverseNextNode(m_holder.get()))
-        computeAndStoreNodeDesiredStyle(node, m_styles);
+    
+    if (m_matchStyle) {
+        // No style restoration will be done, so we don't need to save styles or keep a node vector.
+        for (Node *node = holder->firstChild(); node; node = node->traverseNextNode(holder))
+            m_renderingInfo.add(node, new RenderingInfo(0, node->isBlockFlow()));
+    } else {
+        for (Node *node = holder->firstChild(); node; node = node->traverseNextNode(holder)) {
+            m_renderingInfo.add(node, new RenderingInfo(styleForNode(node), node->isBlockFlow()));
+            m_nodes.append(node);
+        }
+    }
 }
 
-void ReplacementFragment::removeUnrenderedNodes()
+void ReplacementFragment::removeUnrenderedNodes(Node *holder)
 {
-    ASSERT(m_holder && m_holder->inDocument());
-
     DeprecatedPtrList<Node> unrendered;
 
-    for (Node *node = m_holder->firstChild(); node; node = node->traverseNextNode(m_holder.get())) {
+    for (Node *node = holder->firstChild(); node; node = node->traverseNextNode(holder)) {
         if (!isNodeRendered(node) && !isTableStructureNode(node))
             unrendered.append(node);
     }
@@ -377,13 +411,11 @@ void ReplacementFragment::removeUnrenderedNodes()
         removeNode(it.current());
 }
 
-int ReplacementFragment::renderedBlocks()
+int ReplacementFragment::renderedBlocks(Node *holder)
 {
-    ASSERT(m_holder && m_holder->inDocument());
-    
     int count = 0;
     Node *prev = 0;
-    for (Node *node = m_holder->firstChild(); node; node = node->traverseNextNode(m_holder.get())) {
+    for (Node *node = holder->firstChild(); node; node = node->traverseNextNode(holder)) {
         if (node->isBlockFlow()) {
             if (!prev) {
                 count++;
@@ -406,7 +438,7 @@ void ReplacementFragment::removeStyleNodes()
     // Since style information has been computed and cached away in
     // computeStylesUsingTestRendering(), these style nodes can be removed, since
     // the correct styles will be added back in fixupNodeStyles().
-    Node *node = m_holder->firstChild();
+    Node *node = m_fragment->firstChild();
     while (node) {
         Node *next = node->traverseNextNode();
         // This list of tags change the appearance of content
@@ -441,14 +473,14 @@ void ReplacementFragment::removeStyleNodes()
     }
 }
 
-NodeDesiredStyle::NodeDesiredStyle(PassRefPtr<Node> node, PassRefPtr<CSSMutableStyleDeclaration> style) 
-    : m_node(node), m_style(style)
+RenderingInfo::RenderingInfo(PassRefPtr<CSSMutableStyleDeclaration> style, bool isBlockFlow = false)
+    : m_style(style), m_isBlockFlow(isBlockFlow)
 {
 }
 
 ReplaceSelectionCommand::ReplaceSelectionCommand(Document *document, DocumentFragment *fragment, bool selectReplacement, bool smartReplace, bool matchStyle) 
     : CompositeEditCommand(document), 
-      m_documentFragment(fragment),
+      m_fragment(document, fragment, matchStyle),
       m_selectReplacement(selectReplacement), 
       m_smartReplace(smartReplace),
       m_matchStyle(matchStyle)
@@ -461,8 +493,6 @@ ReplaceSelectionCommand::~ReplaceSelectionCommand()
 
 void ReplaceSelectionCommand::doApply()
 {
-    ReplacementFragment fragment(document(), m_documentFragment.get(), m_matchStyle);
-
     // collect information about the current selection, prior to deleting the selection
     Selection selection = endingSelection();
     ASSERT(selection.isCaretOrRange());
@@ -487,14 +517,14 @@ void ReplaceSelectionCommand::doApply()
         mergeStart = false;
     } else {
         // merge if current selection starts inside a paragraph, or there is only one block and no interchange newline to add
-        mergeStart = !fragment.hasInterchangeNewlineAtStart() && 
-            (!isStartOfParagraph(visibleStart) || (!fragment.hasInterchangeNewlineAtEnd() && !fragment.hasMoreThanOneBlock()));
+        mergeStart = !m_fragment.hasInterchangeNewlineAtStart() && 
+            (!isStartOfParagraph(visibleStart) || (!m_fragment.hasInterchangeNewlineAtEnd() && !m_fragment.hasMoreThanOneBlock()));
         
         // This is a workaround for this bug:
         // <rdar://problem/4013642> Copied quoted word does not paste as a quote if pasted at the start of a line
         // We need more powerful logic in this whole mergeStart code for this case to come out right without
         // breaking other cases.
-        if (isStartOfParagraph(visibleStart) && isMailBlockquote(fragment.firstChild()))
+        if (isStartOfParagraph(visibleStart) && isMailBlockquote(m_fragment.firstChild()))
             mergeStart = false;
         
         // prevent first list item from getting merged into target, thereby pulled out of list
@@ -503,7 +533,7 @@ void ReplaceSelectionCommand::doApply()
         // assume that the mergeStartNode() contains the first visible content to paste.
         // Any better ideas?
         if (mergeStart) {
-            for (Node *n = fragment.mergeStartNode(); n; n = n->parentNode()) {
+            for (Node *n = m_fragment.mergeStartNode(); n; n = n->parentNode()) {
                 if (isListElement(n)) {
                     mergeStart = false;
                     break;
@@ -514,19 +544,19 @@ void ReplaceSelectionCommand::doApply()
     
     // decide whether to later append nodes to the end
     Node *beyondEndNode = 0;
-    if (!isEndOfParagraph(visibleEnd) && !fragment.hasInterchangeNewlineAtEnd() &&
-       (startBlock != endBlock || fragment.hasMoreThanOneBlock()))
+    if (!isEndOfParagraph(visibleEnd) && !m_fragment.hasInterchangeNewlineAtEnd() &&
+       (startBlock != endBlock || m_fragment.hasMoreThanOneBlock()))
         beyondEndNode = selection.end().downstream().node();
 
     Position startPos = selection.start();
     
     // delete the current range selection, or insert paragraph for caret selection, as needed
     if (selection.isRange()) {
-        bool mergeBlocksAfterDelete = !(fragment.hasInterchangeNewlineAtStart() || fragment.hasInterchangeNewlineAtEnd() || fragment.hasMoreThanOneBlock());
+        bool mergeBlocksAfterDelete = !(m_fragment.hasInterchangeNewlineAtStart() || m_fragment.hasInterchangeNewlineAtEnd() || m_fragment.hasMoreThanOneBlock());
         deleteSelection(false, mergeBlocksAfterDelete);
         updateLayout();
         visibleStart = VisiblePosition(endingSelection().start(), VP_DEFAULT_AFFINITY);
-        if (fragment.hasInterchangeNewlineAtStart()) {
+        if (m_fragment.hasInterchangeNewlineAtStart()) {
             if (isEndOfParagraph(visibleStart) && !isStartOfParagraph(visibleStart)) {
                 if (!isEndOfDocument(visibleStart))
                     setEndingSelection(visibleStart.next());
@@ -539,7 +569,7 @@ void ReplaceSelectionCommand::doApply()
     } 
     else {
         ASSERT(selection.isCaret());
-        if (fragment.hasInterchangeNewlineAtStart()) {
+        if (m_fragment.hasInterchangeNewlineAtStart()) {
             if (isEndOfParagraph(visibleStart) && !isStartOfParagraph(visibleStart)) {
                 if (!isEndOfDocument(visibleStart))
                     setEndingSelection(visibleStart.next());
@@ -548,7 +578,7 @@ void ReplaceSelectionCommand::doApply()
                 setEndingSelection(VisiblePosition(endingSelection().start(), VP_DEFAULT_AFFINITY));
             }
         }
-        if (!fragment.hasInterchangeNewlineAtEnd() && fragment.hasMoreThanOneBlock() && 
+        if (!m_fragment.hasInterchangeNewlineAtEnd() && m_fragment.hasMoreThanOneBlock() && 
             !startAtBlockBoundary && !isEndOfParagraph(visibleEnd)) {
             // The start and the end need to wind up in separate blocks.
             // Insert a paragraph separator to make that happen.
@@ -575,7 +605,7 @@ void ReplaceSelectionCommand::doApply()
     setTypingStyle(0);    
     
     // done if there is nothing to add
-    if (!fragment.firstChild())
+    if (!m_fragment.firstChild())
         return;
     
     // check for a line placeholder, and store it away for possible removal later.
@@ -588,7 +618,7 @@ void ReplaceSelectionCommand::doApply()
         // now a "second deepest position"
         downstream = positionAvoidingSpecialElementBoundary(downstream);
         if (downstream.node()->hasTagName(brTag) && downstream.offset() == 0 && 
-            fragment.hasInterchangeNewlineAtEnd() &&
+            m_fragment.hasInterchangeNewlineAtEnd() &&
             isStartOfLine(VisiblePosition(downstream, VP_DEFAULT_AFFINITY)))
             linePlaceholder = downstream.node();
     }
@@ -626,12 +656,12 @@ void ReplaceSelectionCommand::doApply()
 
     // step 1: merge content into the start block
     if (mergeStart) {
-        Node *refNode = fragment.mergeStartNode();
+        Node *refNode = m_fragment.mergeStartNode();
         if (refNode) {
             Node *parent = refNode->parentNode();
             Node *node = refNode->nextSibling();
             insertNodeAtAndUpdateNodesInserted(refNode, startPos.node(), startPos.offset());
-            while (node && !node->isBlockFlow()) {
+            while (node && !m_fragment.isBlockFlow(node)) {
                 Node *next = node->nextSibling();
                 insertNodeAfterAndUpdateNodesInserted(node, refNode);
                 refNode = node;
@@ -654,17 +684,17 @@ void ReplaceSelectionCommand::doApply()
     }
     
     // step 2 : merge everything remaining in the fragment
-    if (fragment.firstChild()) {
-        Node *refNode = fragment.firstChild();
+    if (m_fragment.firstChild()) {
+        Node *refNode = m_fragment.firstChild();
         Node *node = refNode ? refNode->nextSibling() : 0;
         Node *insertionBlock = insertionPos.node()->enclosingBlockFlowElement();
         bool insertionBlockIsRoot = insertionBlock == insertionBlock->rootEditableElement();
         VisiblePosition visiblePos(insertionPos, DOWNSTREAM);
-        if (!insertionBlockIsRoot && refNode->isBlockFlow() && isStartOfBlock(visiblePos))
+        if (!insertionBlockIsRoot && m_fragment.isBlockFlow(refNode) && isStartOfBlock(visiblePos))
             insertNodeBeforeAndUpdateNodesInserted(refNode, insertionBlock);
-        else if (!insertionBlockIsRoot && refNode->isBlockFlow() && isEndOfBlock(visiblePos)) {
+        else if (!insertionBlockIsRoot && m_fragment.isBlockFlow(refNode) && isEndOfBlock(visiblePos)) {
             insertNodeAfterAndUpdateNodesInserted(refNode, insertionBlock);
-        } else if (m_lastNodeInserted && !refNode->isBlockFlow()) {
+        } else if (m_lastNodeInserted && !m_fragment.isBlockFlow(refNode)) {
             Position pos = visiblePos.next().deepEquivalent().downstream();
             insertNodeAtAndUpdateNodesInserted(refNode, pos.node(), pos.offset());
         } else {
@@ -718,7 +748,7 @@ void ReplaceSelectionCommand::doApply()
     Position lastPositionToSelect;
 
     // step 4 : handle trailing newline
-    if (fragment.hasInterchangeNewlineAtEnd()) {
+    if (m_fragment.hasInterchangeNewlineAtEnd()) {
         removeLinePlaceholderIfNeeded(linePlaceholder);
 
         if (!m_lastNodeInserted) {
@@ -727,8 +757,8 @@ void ReplaceSelectionCommand::doApply()
         else {
             bool insertParagraph = false;
             VisiblePosition pos(insertionPos, VP_DEFAULT_AFFINITY);
-
-            if (startBlock == endBlock && !m_lastTopNodeInserted.get()->isBlockFlow()) {
+            
+            if (startBlock == endBlock && !m_fragment.isBlockFlow(m_lastTopNodeInserted.get())) {
                 insertParagraph = true;
             } else {
                 // Handle end-of-document case.
@@ -766,7 +796,8 @@ void ReplaceSelectionCommand::doApply()
 
         if (beyondEndNode) {
             updateLayout();
-            DeprecatedValueList<NodeDesiredStyle> styles;
+            RenderingInfoMap renderingInfo;
+            NodeVector nodes;
             Node* node = beyondEndNode->enclosingInlineElement();
             Node* refNode = m_lastNodeInserted.get();
             
@@ -775,7 +806,8 @@ void ReplaceSelectionCommand::doApply()
                     break;
                     
                 Node *next = node->nextSibling();
-                computeAndStoreNodeDesiredStyle(node, styles);
+                nodes.append(node);
+                renderingInfo.add(node, new RenderingInfo(styleForNode(node)));
                 removeNodeAndPruneAncestors(node);
                 // No need to update inserted node variables.
                 insertNodeAfter(node, refNode);
@@ -786,12 +818,12 @@ void ReplaceSelectionCommand::doApply()
                 node = next;
             }
 
-            fixupNodeStyles(styles);
+            fixupNodeStyles(nodes, renderingInfo);
         }
     }
     
     if (!m_matchStyle)
-        fixupNodeStyles(fragment.desiredStyles());
+        fixupNodeStyles(m_fragment.nodes(), m_fragment.renderingInfo());
     completeHTMLReplacement(lastPositionToSelect);
     
     // step 5 : mop up
index 182c6e3574f042631806e47f84741b4060d56046..16df6e9e1c55ec1a95baa0702bb33be3a3b17b25 100644 (file)
 
 #include "CompositeEditCommand.h"
 #include <kxmlcore/PassRefPtr.h>
+#include <kxmlcore/HashMap.h>
+#include <kxmlcore/Vector.h>
 
 namespace WebCore {
 
 class DocumentFragment;
 
-class NodeDesiredStyle {
+class RenderingInfo : public Shared<RenderingInfo> {
 public:
-    NodeDesiredStyle(PassRefPtr<Node>, PassRefPtr<CSSMutableStyleDeclaration>);
+    RenderingInfo(PassRefPtr<CSSMutableStyleDeclaration>, bool);
     
-    Node* node() const { return m_node.get(); }
     CSSMutableStyleDeclaration* style() const { return m_style.get(); }
-
+    bool isBlockFlow() const { return m_isBlockFlow; }
 private:
-    RefPtr<Node> m_node;
     RefPtr<CSSMutableStyleDeclaration> m_style;
+    bool m_isBlockFlow;
 };
 
+typedef Vector<RefPtr<Node> > NodeVector;
+typedef HashMap<Node*, RefPtr<RenderingInfo> > RenderingInfoMap;
+
 // --- ReplacementFragment helper class
 
 class ReplacementFragment
@@ -55,13 +59,13 @@ public:
 
     enum EFragmentType { EmptyFragment, SingleTextNodeFragment, TreeFragment };
 
-    Node* root() const { return m_holder.get(); }
     Node *firstChild() const;
     Node *lastChild() const;
 
     Node *mergeStartNode() const;
 
-    const DeprecatedValueList<NodeDesiredStyle> &desiredStyles() { return m_styles; }
+    const RenderingInfoMap& renderingInfo() const { return m_renderingInfo; }
+    const NodeVector& nodes() const { return m_nodes; }
 
     EFragmentType type() const { return m_type; }
     bool isEmpty() const { return m_type == EmptyFragment; }
@@ -71,6 +75,8 @@ public:
     bool hasMoreThanOneBlock() const { return m_hasMoreThanOneBlock; }
     bool hasInterchangeNewlineAtStart() const { return m_hasInterchangeNewlineAtStart; }
     bool hasInterchangeNewlineAtEnd() const { return m_hasInterchangeNewlineAtEnd; }
+    
+    bool isBlockFlow(Node*) const;
 
 private:
     // no copy construction or assignment
@@ -79,11 +85,13 @@ private:
     
     static bool isInterchangeNewlineNode(const Node *);
     static bool isInterchangeConvertedSpaceSpan(const Node *);
-
-    void insertFragmentForTestRendering(DocumentFragment*);
-    void computeAndStoreNodeStyles();
-    void removeUnrenderedNodes();
-    int renderedBlocks();
+    
+    PassRefPtr<Node> insertFragmentForTestRendering();
+    void saveRenderingInfo(Node*);
+    void computeStylesUsingTestRendering(Node*);
+    void removeUnrenderedNodes(Node*);
+    void restoreTestRenderingNodesToFragment(Node*);
+    int renderedBlocks(Node*);
     void removeStyleNodes();
 
     // A couple simple DOM helpers
@@ -94,8 +102,9 @@ private:
 
     EFragmentType m_type;
     RefPtr<Document> m_document;
-    RefPtr<Node> m_holder;
-    DeprecatedValueList<NodeDesiredStyle> m_styles;
+    RefPtr<DocumentFragment> m_fragment;
+    RenderingInfoMap m_renderingInfo;
+    NodeVector m_nodes;
     bool m_matchStyle;
     bool m_hasInterchangeNewlineAtStart;
     bool m_hasInterchangeNewlineAtEnd;
@@ -119,11 +128,11 @@ private:
     void insertNodeBeforeAndUpdateNodesInserted(Node *insertChild, Node *refChild);
 
     void updateNodesInserted(Node *);
-    void fixupNodeStyles(const DeprecatedValueList<NodeDesiredStyle> &);
+    void fixupNodeStyles(const NodeVector&, const RenderingInfoMap&);
     void removeLinePlaceholderIfNeeded(Node *);
     void removeNodeAndPruneAncestors(Node*);
 
-    RefPtr<DocumentFragment> m_documentFragment;
+    ReplacementFragment m_fragment;
     RefPtr<Node> m_firstNodeInserted;
     RefPtr<Node> m_lastNodeInserted;
     RefPtr<Node> m_lastTopNodeInserted;