Use is<>() / downcast<>() for Table render objects
[WebKit-https.git] / Source / WebCore / editing / DeleteSelectionCommand.cpp
index ebcd5e4..a107e69 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2005 Apple Computer, Inc.  All rights reserved.
+ * Copyright (C) 2005 Apple Inc.  All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
  *
- * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 #include "DeleteSelectionCommand.h"
 
 #include "Document.h"
-#include "DocumentFragment.h"
 #include "DocumentMarkerController.h"
-#include "EditingBoundary.h"
 #include "Editor.h"
 #include "EditorClient.h"
 #include "Element.h"
 #include "Frame.h"
 #include "htmlediting.h"
 #include "HTMLInputElement.h"
+#include "HTMLLinkElement.h"
 #include "HTMLNames.h"
+#include "HTMLStyleElement.h"
+#include "HTMLTableElement.h"
+#include "NodeTraversal.h"
 #include "RenderTableCell.h"
+#include "RenderText.h"
 #include "Text.h"
-#include "visible_units.h"
+#include "VisibleUnits.h"
 
 namespace WebCore {
 
@@ -68,7 +71,7 @@ static bool isTableRowEmpty(Node* row)
     return true;
 }
 
-DeleteSelectionCommand::DeleteSelectionCommand(Document *document, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements)
+DeleteSelectionCommand::DeleteSelectionCommand(Document& document, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements, bool sanitizeMarkup)
     : CompositeEditCommand(document)
     , m_hasSelectionToDelete(false)
     , m_smartDelete(smartDelete)
@@ -78,6 +81,7 @@ DeleteSelectionCommand::DeleteSelectionCommand(Document *document, bool smartDel
     , m_expandForSpecialElements(expandForSpecialElements)
     , m_pruneStartBlockIfNecessary(false)
     , m_startsAtEmptyLine(false)
+    , m_sanitizeMarkup(sanitizeMarkup)
     , m_startBlock(0)
     , m_endBlock(0)
     , m_typingStyle(0)
@@ -85,7 +89,7 @@ DeleteSelectionCommand::DeleteSelectionCommand(Document *document, bool smartDel
 {
 }
 
-DeleteSelectionCommand::DeleteSelectionCommand(const VisibleSelection& selection, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements)
+DeleteSelectionCommand::DeleteSelectionCommand(const VisibleSelection& selection, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements, bool sanitizeMarkup)
     : CompositeEditCommand(selection.start().anchorNode()->document())
     , m_hasSelectionToDelete(true)
     , m_smartDelete(smartDelete)
@@ -95,6 +99,7 @@ DeleteSelectionCommand::DeleteSelectionCommand(const VisibleSelection& selection
     , m_expandForSpecialElements(expandForSpecialElements)
     , m_pruneStartBlockIfNecessary(false)
     , m_startsAtEmptyLine(false)
+    , m_sanitizeMarkup(sanitizeMarkup)
     , m_selectionToDelete(selection)
     , m_startBlock(0)
     , m_endBlock(0)
@@ -131,7 +136,9 @@ void DeleteSelectionCommand::initializeStartEnd(Position& start, Position& end)
         
         if (!startSpecialContainer && !endSpecialContainer)
             break;
-            
+
+        m_mergeBlocksAfterDelete = false;
+
         if (VisiblePosition(start) != m_selectionToDelete.visibleStart() || VisiblePosition(end) != m_selectionToDelete.visibleEnd())
             break;
 
@@ -169,7 +176,7 @@ void DeleteSelectionCommand::setStartingSelectionOnSmartDelete(const Position& s
         newBase = end;
         newExtent = start;        
     }
-    setStartingSelection(VisibleSelection(newBase, newExtent));            
+    setStartingSelection(VisibleSelection(newBase, newExtent, startingSelection().isDirectional())); 
 }
     
 void DeleteSelectionCommand::initializePositionData()
@@ -278,11 +285,15 @@ void DeleteSelectionCommand::saveTypingStyleState()
     // typing style at the start of the selection, nor is there a reason to 
     // compute the style at the start of the selection after deletion (see the 
     // early return in calculateTypingStyleAfterDelete).
-    if (m_upstreamStart.deprecatedNode() == m_downstreamEnd.deprecatedNode() && m_upstreamStart.deprecatedNode()->isTextNode())
+    // However, if typing style was previously set from another text node at the previous
+    // position (now deleted), we need to clear that style as well.
+    if (m_upstreamStart.deprecatedNode() == m_downstreamEnd.deprecatedNode() && m_upstreamStart.deprecatedNode()->isTextNode()) {
+        frame().selection().clearTypingStyle();
         return;
+    }
 
     // Figure out the typing style in effect before the delete is done.
-    m_typingStyle = EditingStyle::create(m_selectionToDelete.start());
+    m_typingStyle = EditingStyle::create(m_selectionToDelete.start(), EditingStyle::EditingPropertiesInEffect);
     m_typingStyle->removeStyleAddedByNode(enclosingAnchorElement(m_selectionToDelete.start()));
 
     // If we're deleting into a Mail blockquote, save the style at end() instead of start()
@@ -295,18 +306,31 @@ void DeleteSelectionCommand::saveTypingStyleState()
 
 bool DeleteSelectionCommand::handleSpecialCaseBRDelete()
 {
+    Node* nodeAfterUpstreamStart = m_upstreamStart.computeNodeAfterPosition();
+    Node* nodeAfterDownstreamStart = m_downstreamStart.computeNodeAfterPosition();
+    // Upstream end will appear before BR due to canonicalization
+    Node* nodeAfterUpstreamEnd = m_upstreamEnd.computeNodeAfterPosition();
+
+    if (!nodeAfterUpstreamStart || !nodeAfterDownstreamStart)
+        return false;
+
     // Check for special-case where the selection contains only a BR on a line by itself after another BR.
-    bool upstreamStartIsBR = m_upstreamStart.deprecatedNode()->hasTagName(brTag);
-    bool downstreamStartIsBR = m_downstreamStart.deprecatedNode()->hasTagName(brTag);
-    bool isBROnLineByItself = upstreamStartIsBR && downstreamStartIsBR && m_downstreamStart.deprecatedNode() == m_upstreamEnd.deprecatedNode();
+    bool upstreamStartIsBR = nodeAfterUpstreamStart->hasTagName(brTag);
+    bool downstreamStartIsBR = nodeAfterDownstreamStart->hasTagName(brTag);
+    // We should consider that the BR is on a line by itself also when we have <br><br>. This test should be true only
+    // when the two elements are siblings and should be false in a case like <div><br></div><br>.
+    bool isBROnLineByItself = upstreamStartIsBR && downstreamStartIsBR && ((nodeAfterDownstreamStart == nodeAfterUpstreamEnd) || (nodeAfterUpstreamEnd && nodeAfterUpstreamEnd->hasTagName(brTag) && nodeAfterUpstreamStart->nextSibling() == nodeAfterUpstreamEnd));
+
     if (isBROnLineByItself) {
-        removeNode(m_downstreamStart.deprecatedNode());
+        removeNode(nodeAfterDownstreamStart);
         return true;
     }
 
-    // Not a special-case delete per se, but we can detect that the merging of content between blocks
-    // should not be done.
-    if (upstreamStartIsBR && downstreamStartIsBR) {
+    // FIXME: This code doesn't belong in here.
+    // We detect the case where the start is an empty line consisting of BR not wrapped in a block element.
+    if (upstreamStartIsBR && downstreamStartIsBR
+        && !(isStartOfBlock(positionBeforeNode(nodeAfterUpstreamStart)) && isEndOfBlock(positionAfterNode(nodeAfterDownstreamStart)))
+        && (!nodeAfterUpstreamEnd || nodeAfterUpstreamEnd->hasTagName(brTag) || nodeAfterUpstreamEnd->previousSibling() != nodeAfterUpstreamStart)) {
         m_startsAtEmptyLine = true;
         m_endingPosition = m_downstreamEnd;
     }
@@ -314,53 +338,23 @@ bool DeleteSelectionCommand::handleSpecialCaseBRDelete()
     return false;
 }
 
-static void updatePositionForNodeRemoval(Node* node, Position& position)
-{
-    if (position.isNull())
-        return;
-    switch (position.anchorType()) {
-    case Position::PositionIsBeforeChildren:
-        if (position.containerNode() == node)
-            position = positionInParentBeforeNode(node);
-        break;
-    case Position::PositionIsAfterChildren:
-        if (position.containerNode() == node)
-            position = positionInParentAfterNode(node);
-        break;
-    case Position::PositionIsOffsetInAnchor:
-        if (position.containerNode() == node->parentNode() && static_cast<unsigned>(position.offsetInContainerNode()) > node->nodeIndex())
-            position.moveToOffset(position.offsetInContainerNode() - 1);
-        else if (node->contains(position.containerNode()))
-            position = positionInParentBeforeNode(node);
-        break;
-    case Position::PositionIsAfterAnchor:
-        if (node->contains(position.anchorNode()))
-            position = positionInParentAfterNode(node);
-        break;
-    case Position::PositionIsBeforeAnchor:
-        if (node->contains(position.anchorNode()))
-            position = positionInParentBeforeNode(node);
-        break;
-    }
-}
-
 static Position firstEditablePositionInNode(Node* node)
 {
     ASSERT(node);
     Node* next = node;
-    while (next && !next->rendererIsEditable())
-        next = next->traverseNextNode(node);
+    while (next && !next->hasEditableStyle())
+        next = NodeTraversal::next(next, node);
     return next ? firstPositionInOrBeforeNode(next) : Position();
 }
 
-void DeleteSelectionCommand::removeNode(PassRefPtr<Node> node)
+void DeleteSelectionCommand::removeNode(PassRefPtr<Node> node, ShouldAssumeContentIsAlwaysEditable shouldAssumeContentIsAlwaysEditable)
 {
     if (!node)
         return;
         
     if (m_startRoot != m_endRoot && !(node->isDescendantOf(m_startRoot.get()) && node->isDescendantOf(m_endRoot.get()))) {
         // If a node is not in both the start and end editable roots, remove it only if its inside an editable region.
-        if (!node->parentNode()->rendererIsEditable()) {
+        if (!node->parentNode()->hasEditableStyle()) {
             // Don't remove non-editable atomic nodes.
             if (!node->firstChild())
                 return;
@@ -368,7 +362,7 @@ void DeleteSelectionCommand::removeNode(PassRefPtr<Node> node)
             RefPtr<Node> child = node->firstChild();
             while (child) {
                 RefPtr<Node> nextChild = child->nextSibling();
-                removeNode(child.get());
+                removeNode(child.get(), shouldAssumeContentIsAlwaysEditable);
                 // Bail if nextChild is no longer node's child.
                 if (nextChild && nextChild->parentNode() != node)
                     return;
@@ -380,20 +374,20 @@ void DeleteSelectionCommand::removeNode(PassRefPtr<Node> node)
         }
     }
     
-    if (isTableStructureNode(node.get()) || node == node->rootEditableElement()) {
+    if (isTableStructureNode(node.get()) || node->isRootEditableElement()) {
         // Do not remove an element of table structure; remove its contents.
         // Likewise for the root editable element.
         Node* child = node->firstChild();
         while (child) {
             Node* remove = child;
             child = child->nextSibling();
-            removeNode(remove);
+            removeNode(remove, shouldAssumeContentIsAlwaysEditable);
         }
         
         // Make sure empty cell has some height, if a placeholder can be inserted.
-        updateLayout();
-        RenderObject *r = node->renderer();
-        if (r && r->isTableCell() && toRenderTableCell(r)->contentHeight() <= 0) {
+        document().updateLayoutIgnorePendingStylesheets();
+        RenderObject* renderer = node->renderer();
+        if (is<RenderTableCell>(renderer) && downcast<RenderTableCell>(*renderer).contentHeight() <= 0) {
             Position firstEditablePosition = firstEditablePositionInNode(node.get());
             if (firstEditablePosition.isNotNull())
                 insertBlockPlaceholder(firstEditablePosition);
@@ -407,11 +401,11 @@ void DeleteSelectionCommand::removeNode(PassRefPtr<Node> node)
         m_needPlaceholder = true;
     
     // FIXME: Update the endpoints of the range being deleted.
-    updatePositionForNodeRemoval(node.get(), m_endingPosition);
-    updatePositionForNodeRemoval(node.get(), m_leadingWhitespace);
-    updatePositionForNodeRemoval(node.get(), m_trailingWhitespace);
+    updatePositionForNodeRemoval(m_endingPosition, node.get());
+    updatePositionForNodeRemoval(m_leadingWhitespace, node.get());
+    updatePositionForNodeRemoval(m_trailingWhitespace, node.get());
     
-    CompositeEditCommand::removeNode(node);
+    CompositeEditCommand::removeNode(node, shouldAssumeContentIsAlwaysEditable);
 }
 
 static void updatePositionForTextRemoval(Node* node, int offset, int count, Position& position)
@@ -436,25 +430,50 @@ void DeleteSelectionCommand::deleteTextFromNode(PassRefPtr<Text> node, unsigned
     CompositeEditCommand::deleteTextFromNode(node, offset, count);
 }
 
+void DeleteSelectionCommand::makeStylingElementsDirectChildrenOfEditableRootToPreventStyleLoss()
+{
+    RefPtr<Range> range = m_selectionToDelete.toNormalizedRange();
+    RefPtr<Node> node = range->firstNode();
+    while (node && node != range->pastLastNode()) {
+        RefPtr<Node> nextNode = NodeTraversal::next(node.get());
+        if ((is<HTMLStyleElement>(*node) && !downcast<HTMLStyleElement>(*node).hasAttribute(scopedAttr)) || is<HTMLLinkElement>(*node)) {
+            nextNode = NodeTraversal::nextSkippingChildren(node.get());
+            RefPtr<ContainerNode> rootEditableElement = node->rootEditableElement();
+            if (rootEditableElement) {
+                removeNode(node);
+                appendNode(node, rootEditableElement);
+            }
+        }
+        node = nextNode;
+    }
+}
+
 void DeleteSelectionCommand::handleGeneralDelete()
 {
+    if (m_upstreamStart.isNull())
+        return;
+
     int startOffset = m_upstreamStart.deprecatedEditingOffset();
     Node* startNode = m_upstreamStart.deprecatedNode();
     
+    makeStylingElementsDirectChildrenOfEditableRootToPreventStyleLoss();
+
     // Never remove the start block unless it's a table, in which case we won't merge content in.
-    if (startNode == m_startBlock && startOffset == 0 && canHaveChildrenForEditing(startNode) && !startNode->hasTagName(tableTag)) {
+    if (startNode == m_startBlock && !startOffset && canHaveChildrenForEditing(startNode) && !is<HTMLTableElement>(*startNode)) {
         startOffset = 0;
-        startNode = startNode->traverseNextNode();
+        startNode = NodeTraversal::next(startNode);
+        if (!startNode)
+            return;
     }
 
-    if (startOffset >= caretMaxOffset(startNode) && startNode->isTextNode()) {
-        Text *text = static_cast<Text *>(startNode);
-        if (text->length() > (unsigned)caretMaxOffset(startNode))
-            deleteTextFromNode(text, caretMaxOffset(startNode), text->length() - caretMaxOffset(startNode));
+    if (startOffset >= caretMaxOffset(startNode) && is<Text>(*startNode)) {
+        Text& text = downcast<Text>(*startNode);
+        if (text.length() > static_cast<unsigned>(caretMaxOffset(startNode)))
+            deleteTextFromNode(&text, caretMaxOffset(startNode), text.length() - caretMaxOffset(startNode));
     }
 
     if (startOffset >= lastOffsetForEditing(startNode)) {
-        startNode = startNode->traverseNextSibling();
+        startNode = NodeTraversal::nextSkippingChildren(startNode);
         startOffset = 0;
     }
 
@@ -464,10 +483,10 @@ void DeleteSelectionCommand::handleGeneralDelete()
 
     if (startNode == m_downstreamEnd.deprecatedNode()) {
         if (m_downstreamEnd.deprecatedEditingOffset() - startOffset > 0) {
-            if (startNode->isTextNode()) {
+            if (is<Text>(*startNode)) {
                 // in a text node that needs to be trimmed
-                Text* text = static_cast<Text*>(startNode);
-                deleteTextFromNode(text, startOffset, m_downstreamEnd.deprecatedEditingOffset() - startOffset);
+                Text& text = downcast<Text>(*startNode);
+                deleteTextFromNode(&text, startOffset, m_downstreamEnd.deprecatedEditingOffset() - startOffset);
             } else {
                 removeChildrenInRange(startNode, startOffset, m_downstreamEnd.deprecatedEditingOffset());
                 m_endingPosition = m_upstreamStart;
@@ -484,29 +503,29 @@ void DeleteSelectionCommand::handleGeneralDelete()
         RefPtr<Node> node(startNode);
         
         if (startOffset > 0) {
-            if (startNode->isTextNode()) {
+            if (is<Text>(*startNode)) {
                 // in a text node that needs to be trimmed
-                Text *text = static_cast<Text *>(node.get());
-                deleteTextFromNode(text, startOffset, text->length() - startOffset);
-                node = node->traverseNextNode();
+                Text& text = downcast<Text>(*node);
+                deleteTextFromNode(&text, startOffset, text.length() - startOffset);
+                node = NodeTraversal::next(node.get());
             } else {
-                node = startNode->childNode(startOffset);
+                node = startNode->traverseToChildAt(startOffset);
             }
-        } else if (startNode == m_upstreamEnd.deprecatedNode() && startNode->isTextNode()) {
-            Text* text = static_cast<Text*>(m_upstreamEnd.deprecatedNode());
-            deleteTextFromNode(text, 0, m_upstreamEnd.deprecatedEditingOffset());
+        } else if (startNode == m_upstreamEnd.deprecatedNode() && is<Text>(*startNode)) {
+            Text& text = downcast<Text>(*m_upstreamEnd.deprecatedNode());
+            deleteTextFromNode(&text, 0, m_upstreamEnd.deprecatedEditingOffset());
         }
         
         // handle deleting all nodes that are completely selected
         while (node && node != m_downstreamEnd.deprecatedNode()) {
             if (comparePositions(firstPositionInOrBeforeNode(node.get()), m_downstreamEnd) >= 0) {
-                // traverseNextSibling just blew past the end position, so stop deleting
+                // NodeTraversal::nextSkippingChildren just blew past the end position, so stop deleting
                 node = 0;
             } else if (!m_downstreamEnd.deprecatedNode()->isDescendantOf(node.get())) {
-                RefPtr<Node> nextNode = node->traverseNextSibling();
+                RefPtr<Node> nextNode = NodeTraversal::nextSkippingChildren(node.get());
                 // if we just removed a node from the end container, update end position so the
                 // check above will work
-                updatePositionForNodeRemoval(node.get(), m_downstreamEnd);
+                updatePositionForNodeRemoval(m_downstreamEnd, node.get());
                 removeNode(node.get());
                 node = nextNode.get();
             } else {
@@ -515,7 +534,7 @@ void DeleteSelectionCommand::handleGeneralDelete()
                     removeNode(node.get());
                     node = 0;
                 } else
-                    node = node->traverseNextNode();
+                    node = NodeTraversal::next(node.get());
             }
         }
         
@@ -524,11 +543,11 @@ void DeleteSelectionCommand::handleGeneralDelete()
                 // The node itself is fully selected, not just its contents.  Delete it.
                 removeNode(m_downstreamEnd.deprecatedNode());
             } else {
-                if (m_downstreamEnd.deprecatedNode()->isTextNode()) {
+                if (is<Text>(*m_downstreamEnd.deprecatedNode())) {
                     // in a text node that needs to be trimmed
-                    Text* text = static_cast<Text*>(m_downstreamEnd.deprecatedNode());
+                    Text& text = downcast<Text>(*m_downstreamEnd.deprecatedNode());
                     if (m_downstreamEnd.deprecatedEditingOffset() > 0) {
-                        deleteTextFromNode(text, 0, m_downstreamEnd.deprecatedEditingOffset());
+                        deleteTextFromNode(&text, 0, m_downstreamEnd.deprecatedEditingOffset());
                     }
                 // Remove children of m_downstreamEnd.deprecatedNode() that come after m_upstreamStart.
                 // Don't try to remove children if m_upstreamStart was inside m_downstreamEnd.deprecatedNode()
@@ -537,13 +556,13 @@ void DeleteSelectionCommand::handleGeneralDelete()
                 // FIXME: Make m_upstreamStart a position we update as we remove content, then we can
                 // always know which children to remove.
                 } else if (!(startNodeWasDescendantOfEndNode && !m_upstreamStart.anchorNode()->inDocument())) {
-                    int offset = 0;
+                    unsigned offset = 0;
                     if (m_upstreamStart.deprecatedNode()->isDescendantOf(m_downstreamEnd.deprecatedNode())) {
                         Node* n = m_upstreamStart.deprecatedNode();
                         while (n && n->parentNode() != m_downstreamEnd.deprecatedNode())
                             n = n->parentNode();
                         if (n)
-                            offset = n->nodeIndex() + 1;
+                            offset = n->computeNodeIndex() + 1;
                     }
                     removeChildrenInRange(m_downstreamEnd.deprecatedNode(), offset, m_downstreamEnd.deprecatedEditingOffset());
                     m_downstreamEnd = createLegacyEditingPosition(m_downstreamEnd.deprecatedNode(), offset);
@@ -555,17 +574,17 @@ void DeleteSelectionCommand::handleGeneralDelete()
 
 void DeleteSelectionCommand::fixupWhitespace()
 {
-    updateLayout();
+    document().updateLayoutIgnorePendingStylesheets();
     // FIXME: isRenderedCharacter should be removed, and we should use VisiblePosition::characterAfter and VisiblePosition::characterBefore
-    if (m_leadingWhitespace.isNotNull() && !m_leadingWhitespace.isRenderedCharacter() && m_leadingWhitespace.deprecatedNode()->isTextNode()) {
-        Text* textNode = static_cast<Text*>(m_leadingWhitespace.deprecatedNode());
-        ASSERT(!textNode->renderer() || textNode->renderer()->style()->collapseWhiteSpace());
-        replaceTextInNodePreservingMarkers(textNode, m_leadingWhitespace.deprecatedEditingOffset(), 1, nonBreakingSpaceString());
+    if (m_leadingWhitespace.isNotNull() && !m_leadingWhitespace.isRenderedCharacter() && is<Text>(*m_leadingWhitespace.deprecatedNode())) {
+        Text& textNode = downcast<Text>(*m_leadingWhitespace.deprecatedNode());
+        ASSERT(!textNode.renderer() || textNode.renderer()->style().collapseWhiteSpace());
+        replaceTextInNodePreservingMarkers(&textNode, m_leadingWhitespace.deprecatedEditingOffset(), 1, nonBreakingSpaceString());
     }
-    if (m_trailingWhitespace.isNotNull() && !m_trailingWhitespace.isRenderedCharacter() && m_trailingWhitespace.deprecatedNode()->isTextNode()) {
-        Text* textNode = static_cast<Text*>(m_trailingWhitespace.deprecatedNode());
-        ASSERT(!textNode->renderer() ||textNode->renderer()->style()->collapseWhiteSpace());
-        replaceTextInNodePreservingMarkers(textNode, m_trailingWhitespace.deprecatedEditingOffset(), 1, nonBreakingSpaceString());
+    if (m_trailingWhitespace.isNotNull() && !m_trailingWhitespace.isRenderedCharacter() && is<Text>(*m_trailingWhitespace.deprecatedNode())) {
+        Text& textNode = downcast<Text>(*m_trailingWhitespace.deprecatedNode());
+        ASSERT(!textNode.renderer() || textNode.renderer()->style().collapseWhiteSpace());
+        replaceTextInNodePreservingMarkers(&textNode, m_trailingWhitespace.deprecatedEditingOffset(), 1, nonBreakingSpaceString());
     }
 }
 
@@ -604,8 +623,8 @@ void DeleteSelectionCommand::mergeParagraphs()
     
     // m_downstreamEnd's block has been emptied out by deletion.  There is no content inside of it to
     // move, so just remove it.
-    Element* endBlock = static_cast<Element*>(enclosingBlock(m_downstreamEnd.deprecatedNode()));
-    if (!startOfParagraphToMove.deepEquivalent().deprecatedNode() || !endBlock->contains(startOfParagraphToMove.deepEquivalent().deprecatedNode())) {
+    Element* endBlock = enclosingBlock(m_downstreamEnd.deprecatedNode());
+    if (!endBlock || !endBlock->contains(startOfParagraphToMove.deepEquivalent().deprecatedNode()) || !startOfParagraphToMove.deepEquivalent().deprecatedNode()) {
         removeNode(enclosingBlock(m_downstreamEnd.deprecatedNode()));
         return;
     }
@@ -619,7 +638,7 @@ void DeleteSelectionCommand::mergeParagraphs()
     if (mergeDestination == startOfParagraphToMove)
         return;
         
-    VisiblePosition endOfParagraphToMove = endOfParagraph(startOfParagraphToMove);
+    VisiblePosition endOfParagraphToMove = endOfParagraph(startOfParagraphToMove, CanSkipOverEditingBoundary);
     
     if (mergeDestination == endOfParagraphToMove)
         return;
@@ -644,7 +663,7 @@ void DeleteSelectionCommand::mergeParagraphs()
     
     RefPtr<Range> range = Range::create(document(), startOfParagraphToMove.deepEquivalent().parentAnchoredEquivalent(), endOfParagraphToMove.deepEquivalent().parentAnchoredEquivalent());
     RefPtr<Range> rangeToBeReplaced = Range::create(document(), mergeDestination.deepEquivalent().parentAnchoredEquivalent(), mergeDestination.deepEquivalent().parentAnchoredEquivalent());
-    if (!document()->frame()->editor()->client()->shouldMoveRangeAfterDelete(range.get(), rangeToBeReplaced.get()))
+    if (!frame().editor().client()->shouldMoveRangeAfterDelete(range.get(), rangeToBeReplaced.get()))
         return;
     
     // moveParagraphs will insert placeholders if it removes blocks that would require their use, don't let block
@@ -713,28 +732,11 @@ void DeleteSelectionCommand::calculateTypingStyleAfterDelete()
     m_typingStyle->prepareToApplyAt(m_endingPosition);
     if (m_typingStyle->isEmpty())
         m_typingStyle = 0;
-    VisiblePosition visibleEnd(m_endingPosition);
-    if (m_typingStyle && 
-        isStartOfParagraph(visibleEnd) &&
-        isEndOfParagraph(visibleEnd) &&
-        lineBreakExistsAtVisiblePosition(visibleEnd)) {
-        // Apply style to the placeholder that is now holding open the empty paragraph. 
-        // This makes sure that the paragraph has the right height, and that the paragraph 
-        // takes on the right style and retains it even if you move the selection away and
-        // then move it back (which will clear typing style).
-
-        setEndingSelection(visibleEnd);
-        applyStyle(m_typingStyle.get(), EditActionUnspecified);
-        // applyStyle can destroy the placeholder that was at m_endingPosition if it needs to 
-        // move it, but it will set an endingSelection() at [movedPlaceholder, 0] if it does so.
-        m_endingPosition = endingSelection().start();
-        m_typingStyle = 0;
-    }
     // This is where we've deleted all traces of a style but not a whole paragraph (that's handled above).
     // In this case if we start typing, the new characters should have the same style as the just deleted ones,
     // but, if we change the selection, come back and start typing that style should be lost.  Also see 
     // preserveTypingStyle() below.
-    document()->frame()->selection()->setTypingStyle(m_typingStyle);
+    frame().selection().setTypingStyle(m_typingStyle);
 }
 
 void DeleteSelectionCommand::clearTransientState()
@@ -763,7 +765,7 @@ String DeleteSelectionCommand::originalStringForAutocorrectionAtBeginningOfSelec
         return String();
 
     RefPtr<Range> rangeOfFirstCharacter = Range::create(document(), startOfSelection.deepEquivalent(), nextPosition.deepEquivalent());
-    Vector<DocumentMarker*> markers = document()->markers()->markersInRange(rangeOfFirstCharacter.get(), DocumentMarker::Autocorrected);
+    Vector<DocumentMarker*> markers = document().markers().markersInRange(rangeOfFirstCharacter.get(), DocumentMarker::Autocorrected);
     for (size_t i = 0; i < markers.size(); ++i) {
         const DocumentMarker* marker = markers[i];
         int startOffset = marker->startOffset();
@@ -773,6 +775,24 @@ String DeleteSelectionCommand::originalStringForAutocorrectionAtBeginningOfSelec
     return String();
 }
 
+// This method removes div elements with no attributes that have only one child or no children at all.
+void DeleteSelectionCommand::removeRedundantBlocks()
+{
+    Node* node = m_endingPosition.containerNode();
+    Node* rootNode = node->rootEditableElement();
+   
+    while (node != rootNode) {
+        if (isRemovableBlock(node)) {
+            if (node == m_endingPosition.anchorNode())
+                updatePositionForNodeRemovalPreservingChildren(m_endingPosition, node);
+            
+            CompositeEditCommand::removeNodePreservingChildren(node);
+            node = m_endingPosition.anchorNode();
+        } else
+            node = node->parentNode();
+    }
+}
+
 void DeleteSelectionCommand::doApply()
 {
     // If selection has not been set to a custom selection when the command was created,
@@ -787,12 +807,9 @@ void DeleteSelectionCommand::doApply()
 
     // If the deletion is occurring in a text field, and we're not deleting to replace the selection, then let the frame call across the bridge to notify the form delegate. 
     if (!m_replace) {
-        Node* startNode = m_selectionToDelete.start().deprecatedNode();
-        Node* ancestorNode = startNode ? startNode->shadowAncestorNode() : 0;
-        if (ancestorNode && ancestorNode->hasTagName(inputTag)
-                && static_cast<HTMLInputElement*>(ancestorNode)->isTextField()
-                && ancestorNode->focused())
-            document()->frame()->editor()->textWillBeDeletedInTextField(static_cast<Element*>(ancestorNode));
+        Element* textControl = enclosingTextFormControl(m_selectionToDelete.start());
+        if (textControl && textControl->focused())
+            frame().editor().textWillBeDeletedInTextField(textControl);
     }
 
     // save this to later make the selection with
@@ -824,7 +841,7 @@ void DeleteSelectionCommand::doApply()
     // want to replace it with a placeholder BR!
     if (handleSpecialCaseBRDelete()) {
         calculateTypingStyleAfterDelete();
-        setEndingSelection(VisibleSelection(m_endingPosition, affinity));
+        setEndingSelection(VisibleSelection(m_endingPosition, affinity, endingSelection().isDirectional()));
         clearTransientState();
         rebalanceWhitespace();
         return;
@@ -840,19 +857,30 @@ void DeleteSelectionCommand::doApply()
     
     RefPtr<Node> placeholder = m_needPlaceholder ? createBreakElement(document()).get() : 0;
     
-    if (placeholder)
+    if (placeholder) {
+        if (m_sanitizeMarkup)
+            removeRedundantBlocks();
         insertNodeAt(placeholder.get(), m_endingPosition);
+    }
 
-    rebalanceWhitespaceAt(m_endingPosition);
+    bool shouldRebalaceWhiteSpace = true;
+    if (!frame().editor().behavior().shouldRebalanceWhiteSpacesInSecureField()) {
+        Node* node = m_endingPosition.deprecatedNode();
+        if (is<Text>(node)) {
+            Text& textNode = downcast<Text>(*node);
+            if (textNode.length())
+                shouldRebalaceWhiteSpace = textNode.renderer()->style().textSecurity() == TSNONE;
+        }        
+    }
+    if (shouldRebalaceWhiteSpace)
+        rebalanceWhitespaceAt(m_endingPosition);
 
     calculateTypingStyleAfterDelete();
 
-    if (!originalString.isEmpty()) {
-        if (Frame* frame = document()->frame())
-            frame->editor()->deletedAutocorrectionAtPosition(m_endingPosition, originalString);
-    }
+    if (!originalString.isEmpty())
+        frame().editor().deletedAutocorrectionAtPosition(m_endingPosition, originalString);
 
-    setEndingSelection(VisibleSelection(m_endingPosition, affinity));
+    setEndingSelection(VisibleSelection(m_endingPosition, affinity, endingSelection().isDirectional()));
     clearTransientState();
 }