Reviewed by John
[WebKit-https.git] / WebCore / khtml / editing / htmlediting.cpp
index ca197022b7f147c0a5144421c0767126baba1cfe..1f99dff511c32e2e223f44a7c216008f95e6660f 100644 (file)
@@ -168,6 +168,12 @@ static DOMString &styleSpanClassString()
     return styleSpanClassString;
 }
 
+static DOMString &blockPlaceholderClassString()
+{
+    static DOMString blockPlaceholderClassString = "khtml-block-placeholder";
+    return blockPlaceholderClassString;
+}
+
 static void debugPosition(const char *prefix, const Position &pos)
 {
     if (!prefix)
@@ -798,6 +804,44 @@ void CompositeEditCommand::deleteUnrenderedText(const Position &pos)
         setEndingSelection(block);
 }
 
+void CompositeEditCommand::insertBlockPlaceholderIfNeeded(NodeImpl *node)
+{
+    document()->updateLayout();
+
+    RenderObject *renderer = node->renderer();
+    if (!renderer->isBlockFlow())
+        return;
+    
+    if (renderer->height() > 0)
+        return;
+
+    int exceptionCode = 0;
+    ElementImpl *breakNode = document()->createHTMLElement("BR", exceptionCode);
+    ASSERT(exceptionCode == 0);
+    breakNode->setAttribute(ATTR_CLASS, blockPlaceholderClassString());
+    appendNode(breakNode, node);
+}
+
+void CompositeEditCommand::removeBlockPlaceholderIfNeeded(NodeImpl *node)
+{
+    document()->updateLayout();
+
+    RenderObject *renderer = node->renderer();
+    if (!renderer->isBlockFlow())
+        return;
+
+    // This code will remove a block placeholder if it still is at the end
+    // of a block, where we placed it in insertBlockPlaceholderIfNeeded().
+    // Of course, a person who hand-edits an HTML file could move a 
+    // placeholder around, but it seems OK to be unconcerned about that case.
+    NodeImpl *last = node->lastChild();
+    if (last->isHTMLElement()) {
+        ElementImpl *element = static_cast<ElementImpl *>(last);
+        if (element->getAttribute(ATTR_CLASS) == blockPlaceholderClassString())
+            removeNode(element);
+    }
+}
+
 //==========================================================================================
 // Concrete commands
 //------------------------------------------------------------------------------------------
@@ -1169,15 +1213,12 @@ void DeleteSelectionCommand::moveNodesAfterNode(NodeImpl *startNode, NodeImpl *d
         // Do not move content between parts of a table
         return;
 
+    // Now that we are about to add content, check to see if a placeholder element
+    // can be removed.
+    removeBlockPlaceholderIfNeeded(startBlock);
+
     NodeImpl *node = startNode == startBlock ? startBlock->firstChild() : startNode;
 
-    // Only do the move if node is a text node.
-    // This is done to duplicate the behavior in NSText, particularly to
-    // mimic the behavior in Mail where deletions are done between blocks
-    // that are quoted.
-    if (!node || !node->isTextNode())
-        return;
-    
     // Do the move.
     NodeImpl *refNode = dstNode;
     while (node && node->isAncestor(startBlock)) {
@@ -1200,9 +1241,9 @@ void DeleteSelectionCommand::moveNodesAfterNode(NodeImpl *startNode, NodeImpl *d
     // This will have the side effect of moving 'Three' on to the same line as 'One'
     // and 'Two'. This is undesirable. We fix this up by adding a BR before the 'Three'.
     // This may not be ideal, but it is better than nothing.
-    if (!startBlock->firstChild()) {
+    document()->updateLayout();
+    if (startBlock->renderer() && startBlock->renderer()->height() == 0) {
         removeNode(startBlock);
-        document()->updateLayout();
         if (refNode->renderer() && refNode->renderer()->inlineBox() && refNode->renderer()->inlineBox()->nextOnLineExists()) {
             int exceptionCode = 0;
             ElementImpl *breakNode = document()->createHTMLElement("BR", exceptionCode);
@@ -1417,6 +1458,10 @@ void DeleteSelectionCommand::doApply()
         }
     }
 
+    // If the delete emptied a block, add in a placeholder so the block does not
+    // seem to disappear.
+    insertBlockPlaceholderIfNeeded(endingPosition.node());
+
     // Compute the difference between the style before the delete and the style now
     // after the delete has been done. Set this style on the part, so other editing
     // commands being composed with this one will work, and also cache it on the command,
@@ -1735,6 +1780,10 @@ void InputTextCommand::input(const DOMString &text, bool selectInsertedText)
     
     TextImpl *textNode = static_cast<TextImpl *>(pos.node());
     long offset = pos.offset();
+
+    // Now that we are about to add content, check to see if a placeholder element
+    // can be removed.
+    removeBlockPlaceholderIfNeeded(textNode->enclosingBlockFlowElement());
     
     // These are temporary implementations for inserting adjoining spaces
     // into a document. We are working on a CSS-related whitespace solution
@@ -2013,6 +2062,10 @@ void ReplaceSelectionCommand::doApply()
     
     selection = endingSelection();
     ASSERT(selection.isCaret());
+
+    // Now that we are about to add content, check to see if a placeholder element
+    // can be removed.
+    removeBlockPlaceholderIfNeeded(selection.start().node()->enclosingBlockFlowElement());
     
     bool addLeadingSpace = false;
     bool addTrailingSpace = false;