WebCore:
[WebKit-https.git] / WebCore / editing / CompositeEditCommand.cpp
index 0843c4e..976d0f8 100644 (file)
 #include "config.h"
 #include "CompositeEditCommand.h"
 
-#include "Document.h"
-#include "InlineTextBox.h"
 #include "AppendNodeCommand.h"
 #include "ApplyStyleCommand.h"
+#include "CSSComputedStyleDeclaration.h"
+#include "CSSMutableStyleDeclaration.h"
+#include "CharacterNames.h"
 #include "DeleteFromTextNodeCommand.h"
 #include "DeleteSelectionCommand.h"
-#include "Range.h"
-#include "htmlediting.h"
-#include "htmlnames.h"
+#include "Document.h"
+#include "DocumentFragment.h"
+#include "EditorInsertAction.h"
+#include "Element.h"
+#include "HTMLNames.h"
+#include "InlineTextBox.h"
 #include "InsertIntoTextNodeCommand.h"
 #include "InsertNodeBeforeCommand.h"
 #include "InsertParagraphSeparatorCommand.h"
 #include "InsertTextCommand.h"
 #include "JoinTextNodesCommand.h"
 #include "MergeIdenticalElementsCommand.h"
-#include "RebalanceWhitespaceCommand.h"
+#include "Range.h"
 #include "RemoveCSSPropertyCommand.h"
 #include "RemoveNodeAttributeCommand.h"
 #include "RemoveNodeCommand.h"
 #include "RemoveNodePreservingChildrenCommand.h"
+#include "ReplaceSelectionCommand.h"
 #include "SetNodeAttributeCommand.h"
 #include "SplitElementCommand.h"
 #include "SplitTextNodeCommand.h"
 #include "SplitTextNodeContainingElementCommand.h"
-#include "VisiblePosition.h"
-#include "visible_units.h"
+#include "Text.h"
+#include "TextIterator.h"
 #include "WrapContentsInDummySpanCommand.h"
-#include <kxmlcore/Assertions.h>
+#include "htmlediting.h"
+#include "markup.h"
+#include "visible_units.h"
+
+using namespace std;
 
 namespace WebCore {
 
 using namespace HTMLNames;
 
-static const String &blockPlaceholderClassString();
-
 CompositeEditCommand::CompositeEditCommand(Document *document) 
     : EditCommand(document)
 {
@@ -68,213 +75,212 @@ CompositeEditCommand::CompositeEditCommand(Document *document)
 
 void CompositeEditCommand::doUnapply()
 {
-    if (m_cmds.count() == 0)
-        return;
-    
-    DeprecatedValueList<EditCommandPtr>::ConstIterator end;
-    for (DeprecatedValueList<EditCommandPtr>::ConstIterator it = m_cmds.fromLast(); it != end; --it)
-        (*it)->unapply();
-
-    setState(NotApplied);
+    size_t size = m_commands.size();
+    for (size_t i = size; i != 0; --i)
+        m_commands[i - 1]->unapply();
 }
 
 void CompositeEditCommand::doReapply()
 {
-    if (m_cmds.count() == 0)
-        return;
-
-    for (DeprecatedValueList<EditCommandPtr>::ConstIterator it = m_cmds.begin(); it != m_cmds.end(); ++it)
-        (*it)->reapply();
-
-    setState(Applied);
+    size_t size = m_commands.size();
+    for (size_t i = 0; i != size; ++i)
+        m_commands[i]->reapply();
 }
 
 //
 // sugary-sweet convenience functions to help create and apply edit commands in composite commands
 //
-void CompositeEditCommand::applyCommandToComposite(EditCommandPtr &cmd)
+void CompositeEditCommand::applyCommandToComposite(PassRefPtr<EditCommand> cmd)
+{
+    cmd->setParent(this);
+    cmd->apply();
+    m_commands.append(cmd);
+}
+
+void CompositeEditCommand::applyStyle(CSSStyleDeclaration* style, EditAction editingAction)
+{
+    applyCommandToComposite(new ApplyStyleCommand(document(), style, editingAction));
+}
+
+void CompositeEditCommand::applyStyle(CSSStyleDeclaration* style, const Position& start, const Position& end, EditAction editingAction)
 {
-    cmd.setStartingSelection(endingSelection());
-    cmd.setEndingSelection(endingSelection());
-    cmd.setParent(this);
-    cmd.apply();
-    m_cmds.append(cmd);
+    applyCommandToComposite(new ApplyStyleCommand(document(), style, start, end, editingAction));
 }
 
-void CompositeEditCommand::applyStyle(CSSStyleDeclaration *style, EditAction editingAction)
+void CompositeEditCommand::applyStyledElement(Element* element)
 {
-    EditCommandPtr cmd(new ApplyStyleCommand(document(), style, 0, editingAction));
-    applyCommandToComposite(cmd);
+    applyCommandToComposite(new ApplyStyleCommand(element, false));
 }
 
-void CompositeEditCommand::applyStyle(CSSStyleDeclaration *style, Position start, Position end, EditAction editingAction)
+void CompositeEditCommand::removeStyledElement(Element* element)
 {
-    EditCommandPtr cmd(new ApplyStyleCommand(document(), style, 0, start, end, editingAction));
-    applyCommandToComposite(cmd);
+    applyCommandToComposite(new ApplyStyleCommand(element, true));
 }
 
-void CompositeEditCommand::insertParagraphSeparator()
+void CompositeEditCommand::insertParagraphSeparator(bool useDefaultParagraphElement)
 {
-    EditCommandPtr cmd(new InsertParagraphSeparatorCommand(document()));
-    applyCommandToComposite(cmd);
+    applyCommandToComposite(new InsertParagraphSeparatorCommand(document(), useDefaultParagraphElement));
 }
 
-void CompositeEditCommand::insertNodeBefore(Node *insertChild, Node *refChild)
+void CompositeEditCommand::insertNodeBefore(Node* insertChild, Node* refChild)
 {
     ASSERT(!refChild->hasTagName(bodyTag));
-    EditCommandPtr cmd(new InsertNodeBeforeCommand(document(), insertChild, refChild));
-    applyCommandToComposite(cmd);
+    applyCommandToComposite(new InsertNodeBeforeCommand(insertChild, refChild));
 }
 
-void CompositeEditCommand::insertNodeAfter(Node *insertChild, Node *refChild)
+void CompositeEditCommand::insertNodeAfter(Node* insertChild, Node* refChild)
 {
     ASSERT(!refChild->hasTagName(bodyTag));
-    if (refChild->parentNode()->lastChild() == refChild) {
+    if (refChild->parentNode()->lastChild() == refChild)
         appendNode(insertChild, refChild->parentNode());
-    }
     else {
         ASSERT(refChild->nextSibling());
         insertNodeBefore(insertChild, refChild->nextSibling());
     }
 }
 
-void CompositeEditCommand::insertNodeAt(Node *insertChild, Node *refChild, int offset)
+void CompositeEditCommand::insertNodeAt(Node* insertChild, const Position& editingPosition)
 {
-    if (refChild->hasChildNodes() || (refChild->renderer() && refChild->renderer()->isBlockFlow())) {
-        Node *child = refChild->firstChild();
+    ASSERT(isEditablePosition(editingPosition));
+    // For editing positions like [table, 0], insert before the table,
+    // likewise for replaced elements, brs, etc.
+    Position p = rangeCompliantEquivalent(editingPosition);
+    Node* refChild = p.node();
+    int offset = p.offset();
+    
+    if (canHaveChildrenForEditing(refChild)) {
+        Node* child = refChild->firstChild();
         for (int i = 0; child && i < offset; i++)
             child = child->nextSibling();
         if (child)
             insertNodeBefore(insertChild, child);
         else
             appendNode(insertChild, refChild);
-    } 
-    else if (refChild->caretMinOffset() >= offset) {
+    } else if (refChild->caretMinOffset() >= offset) {
         insertNodeBefore(insertChild, refChild);
-    } 
-    else if (refChild->isTextNode() && refChild->caretMaxOffset() > offset) {
+    } else if (refChild->isTextNode() && refChild->caretMaxOffset() > offset) {
         splitTextNode(static_cast<Text *>(refChild), offset);
         insertNodeBefore(insertChild, refChild);
-    } 
-    else {
+    } else {
         insertNodeAfter(insertChild, refChild);
     }
 }
 
-void CompositeEditCommand::appendNode(Node *appendChild, Node *parent)
-{
-    EditCommandPtr cmd(new AppendNodeCommand(document(), appendChild, parent));
-    applyCommandToComposite(cmd);
-}
-
-void CompositeEditCommand::removeFullySelectedNode(Node *node)
+void CompositeEditCommand::appendNode(Node* newChild, Node* parent)
 {
-    if (isTableStructureNode(node) || node == node->rootEditableElement()) {
-        // 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();
-            removeFullySelectedNode(remove);
-        }
-        
-        // make sure empty cell has some height
-        updateLayout();
-        RenderObject *r = node->renderer();
-        if (r && r->isTableCell() && r->contentHeight() <= 0)
-            insertBlockPlaceholder(Position(node,0));
-        return;
-    }
-    
-    removeNode(node);
+    ASSERT(canHaveChildrenForEditing(parent));
+    applyCommandToComposite(new AppendNodeCommand(parent, newChild));
 }
 
-void CompositeEditCommand::removeChildrenInRange(Node *node, int from, int to)
+void CompositeEditCommand::removeChildrenInRange(Nodenode, int from, int to)
 {
-    Node *nodeToRemove = node->childNode(from);
+    NodenodeToRemove = node->childNode(from);
     for (int i = from; i < to; i++) {
         ASSERT(nodeToRemove);
-        Node *next = nodeToRemove->nextSibling();
+        Nodenext = nodeToRemove->nextSibling();
         removeNode(nodeToRemove);
         nodeToRemove = next;
     }
 }
 
-void CompositeEditCommand::removeNode(Node *removeChild)
+void CompositeEditCommand::removeNode(Node* removeChild)
+{
+    applyCommandToComposite(new RemoveNodeCommand(removeChild));
+}
+
+void CompositeEditCommand::removeNodePreservingChildren(Node* removeChild)
+{
+    applyCommandToComposite(new RemoveNodePreservingChildrenCommand(removeChild));
+}
+
+void CompositeEditCommand::removeNodeAndPruneAncestors(Node* node)
 {
-    EditCommandPtr cmd(new RemoveNodeCommand(document(), removeChild));
-    applyCommandToComposite(cmd);
+    RefPtr<Node> parent = node->parentNode();
+    removeNode(node);
+    prune(parent);
 }
 
-void CompositeEditCommand::removeNodePreservingChildren(Node *removeChild)
+bool hasARenderedDescendant(Node* node)
 {
-    EditCommandPtr cmd(new RemoveNodePreservingChildrenCommand(document(), removeChild));
-    applyCommandToComposite(cmd);
+    Node* n = node->firstChild();
+    while (n) {
+        if (n->renderer())
+            return true;
+        n = n->traverseNextNode(node);
+    }
+    return false;
+}
+
+void CompositeEditCommand::prune(PassRefPtr<Node> node)
+{
+    while (node) {
+        // If you change this rule you may have to add an updateLayout() here.
+        RenderObject* renderer = node->renderer();
+        if (renderer && (!renderer->canHaveChildren() || hasARenderedDescendant(node.get()) || node->rootEditableElement() == node))
+            return;
+            
+        RefPtr<Node> next = node->parentNode();
+        removeNode(node.get());
+        node = next;
+    }
 }
 
 void CompositeEditCommand::splitTextNode(Text *text, int offset)
 {
-    EditCommandPtr cmd(new SplitTextNodeCommand(document(), text, offset));
-    applyCommandToComposite(cmd);
+    applyCommandToComposite(new SplitTextNodeCommand(text, offset));
 }
 
-void CompositeEditCommand::splitElement(Element *element, Node *atChild)
+void CompositeEditCommand::splitElement(Element* element, Node* atChild)
 {
-    EditCommandPtr cmd(new SplitElementCommand(document(), element, atChild));
-    applyCommandToComposite(cmd);
+    applyCommandToComposite(new SplitElementCommand(element, atChild));
 }
 
-void CompositeEditCommand::mergeIdenticalElements(WebCore::Element *first, WebCore::Element *second)
+void CompositeEditCommand::mergeIdenticalElements(Element* first, Element* second)
 {
-    EditCommandPtr cmd(new MergeIdenticalElementsCommand(document(), first, second));
-    applyCommandToComposite(cmd);
+    ASSERT(!first->isDescendantOf(second) && second != first);
+    if (first->nextSibling() != second) {
+        removeNode(second);
+        insertNodeAfter(second, first);
+    }
+    applyCommandToComposite(new MergeIdenticalElementsCommand(first, second));
 }
 
-void CompositeEditCommand::wrapContentsInDummySpan(WebCore::Element *element)
+void CompositeEditCommand::wrapContentsInDummySpan(Element* element)
 {
-    EditCommandPtr cmd(new WrapContentsInDummySpanCommand(document(), element));
-    applyCommandToComposite(cmd);
+    applyCommandToComposite(new WrapContentsInDummySpanCommand(element));
 }
 
-void CompositeEditCommand::splitTextNodeContainingElement(WebCore::Text *text, int offset)
+void CompositeEditCommand::splitTextNodeContainingElement(Text *text, int offset)
 {
-    EditCommandPtr cmd(new SplitTextNodeContainingElementCommand(document(), text, offset));
-    applyCommandToComposite(cmd);
+    applyCommandToComposite(new SplitTextNodeContainingElementCommand(text, offset));
 }
 
 void CompositeEditCommand::joinTextNodes(Text *text1, Text *text2)
 {
-    EditCommandPtr cmd(new JoinTextNodesCommand(document(), text1, text2));
-    applyCommandToComposite(cmd);
+    applyCommandToComposite(new JoinTextNodesCommand(text1, text2));
 }
 
 void CompositeEditCommand::inputText(const String &text, bool selectInsertedText)
 {
-    InsertTextCommand *impl = new InsertTextCommand(document());
-    EditCommandPtr cmd(impl);
-    applyCommandToComposite(cmd);
-    impl->input(text, selectInsertedText);
+    RefPtr<InsertTextCommand> command = new InsertTextCommand(document());
+    applyCommandToComposite(command);
+    command->input(text, selectInsertedText);
 }
 
 void CompositeEditCommand::insertTextIntoNode(Text *node, int offset, const String &text)
 {
-    EditCommandPtr cmd(new InsertIntoTextNodeCommand(document(), node, offset, text));
-    applyCommandToComposite(cmd);
+    applyCommandToComposite(new InsertIntoTextNodeCommand(node, offset, text));
 }
 
 void CompositeEditCommand::deleteTextFromNode(Text *node, int offset, int count)
 {
-    EditCommandPtr cmd(new DeleteFromTextNodeCommand(document(), node, offset, count));
-    applyCommandToComposite(cmd);
+    applyCommandToComposite(new DeleteFromTextNodeCommand(node, offset, count));
 }
 
 void CompositeEditCommand::replaceTextInNode(Text *node, int offset, int count, const String &replacementText)
 {
-    EditCommandPtr deleteCommand(new DeleteFromTextNodeCommand(document(), node, offset, count));
-    applyCommandToComposite(deleteCommand);
-    EditCommandPtr insertCommand(new InsertIntoTextNodeCommand(document(), node, offset, replacementText));
-    applyCommandToComposite(insertCommand);
+    applyCommandToComposite(new DeleteFromTextNodeCommand(node, offset, count));
+    applyCommandToComposite(new InsertIntoTextNodeCommand(node, offset, replacementText));
 }
 
 Position CompositeEditCommand::positionOutsideTabSpan(const Position& pos)
@@ -282,7 +288,7 @@ Position CompositeEditCommand::positionOutsideTabSpan(const Position& pos)
     if (!isTabSpanTextNode(pos.node()))
         return pos;
     
-    Node *tabSpan = tabSpanNode(pos.node());
+    NodetabSpan = tabSpanNode(pos.node());
     
     if (pos.offset() <= pos.node()->caretMinOffset())
         return positionBeforeNode(tabSpan);
@@ -294,76 +300,144 @@ Position CompositeEditCommand::positionOutsideTabSpan(const Position& pos)
     return positionBeforeNode(tabSpan);
 }
 
-void CompositeEditCommand::insertNodeAtTabSpanPosition(Node *node, const Position& pos)
+void CompositeEditCommand::insertNodeAtTabSpanPosition(Nodenode, const Position& pos)
 {
     // insert node before, after, or at split of tab span
     Position insertPos = positionOutsideTabSpan(pos);
-    insertNodeAt(node, insertPos.node(), insertPos.offset());
+    insertNodeAt(node, insertPos);
 }
 
-void CompositeEditCommand::deleteSelection(bool smartDelete, bool mergeBlocksAfterDelete)
+void CompositeEditCommand::deleteSelection(bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements)
 {
-    if (endingSelection().isRange()) {
-        EditCommandPtr cmd(new DeleteSelectionCommand(document(), smartDelete, mergeBlocksAfterDelete));
-        applyCommandToComposite(cmd);
-    }
+    if (endingSelection().isRange())
+        applyCommandToComposite(new DeleteSelectionCommand(document(), smartDelete, mergeBlocksAfterDelete, replace, expandForSpecialElements));
 }
 
-void CompositeEditCommand::deleteSelection(const Selection &selection, bool smartDelete, bool mergeBlocksAfterDelete)
+void CompositeEditCommand::deleteSelection(const Selection &selection, bool smartDelete, bool mergeBlocksAfterDelete, bool replace, bool expandForSpecialElements)
 {
-    if (selection.isRange()) {
-        EditCommandPtr cmd(new DeleteSelectionCommand(document(), selection, smartDelete, mergeBlocksAfterDelete));
-        applyCommandToComposite(cmd);
-    }
+    if (selection.isRange())
+        applyCommandToComposite(new DeleteSelectionCommand(selection, smartDelete, mergeBlocksAfterDelete, replace, expandForSpecialElements));
 }
 
 void CompositeEditCommand::removeCSSProperty(CSSStyleDeclaration *decl, int property)
 {
-    EditCommandPtr cmd(new RemoveCSSPropertyCommand(document(), decl, property));
-    applyCommandToComposite(cmd);
+    applyCommandToComposite(new RemoveCSSPropertyCommand(document(), decl, property));
 }
 
-void CompositeEditCommand::removeNodeAttribute(Element *element, const QualifiedName& attribute)
+void CompositeEditCommand::removeNodeAttribute(Elementelement, const QualifiedName& attribute)
 {
-    String value = element->getAttribute(attribute);
-    if (value.isEmpty())
+    if (element->getAttribute(attribute).isNull())
         return;
-    EditCommandPtr cmd(new RemoveNodeAttributeCommand(document(), element, attribute));
-    applyCommandToComposite(cmd);
+    applyCommandToComposite(new RemoveNodeAttributeCommand(element, attribute));
+}
+
+void CompositeEditCommand::setNodeAttribute(Element* element, const QualifiedName& attribute, const String &value)
+{
+    applyCommandToComposite(new SetNodeAttributeCommand(element, attribute, value));
 }
 
-void CompositeEditCommand::setNodeAttribute(Element *element, const QualifiedName& attribute, const String &value)
+static inline bool isWhitespace(UChar c)
 {
-    EditCommandPtr cmd(new SetNodeAttributeCommand(document(), element, attribute, value));
-    applyCommandToComposite(cmd);
+    return c == noBreakSpace || c == ' ' || c == '\n' || c == '\t';
+}
+
+// FIXME: Doesn't go into text nodes that contribute adjacent text (siblings, cousins, etc).
+void CompositeEditCommand::rebalanceWhitespaceAt(const Position& position)
+{
+    Node* node = position.node();
+    if (!node || !node->isTextNode())
+        return;
+    Text* textNode = static_cast<Text*>(node);    
+    
+    if (textNode->length() == 0)
+        return;
+    RenderObject* renderer = textNode->renderer();
+    if (renderer && !renderer->style()->collapseWhiteSpace())
+        return;
+        
+    String text = textNode->data();
+    ASSERT(!text.isEmpty());
+
+    int offset = position.offset();
+    // If neither text[offset] nor text[offset - 1] are some form of whitespace, do nothing.
+    if (!isWhitespace(text[offset])) {
+        offset--;
+        if (offset < 0 || !isWhitespace(text[offset]))
+            return;
+    }
+    
+    // Set upstream and downstream to define the extent of the whitespace surrounding text[offset].
+    int upstream = offset;
+    while (upstream > 0 && isWhitespace(text[upstream - 1]))
+        upstream--;
+    
+    int downstream = offset;
+    while ((unsigned)downstream + 1 < text.length() && isWhitespace(text[downstream + 1]))
+        downstream++;
+    
+    int length = downstream - upstream + 1;
+    ASSERT(length > 0);
+    
+    VisiblePosition visibleUpstreamPos(Position(position.node(), upstream));
+    VisiblePosition visibleDownstreamPos(Position(position.node(), downstream + 1));
+    
+    String string = text.substring(upstream, length);
+    String rebalancedString = stringWithRebalancedWhitespace(string,
+    // FIXME: Because of the problem mentioned at the top of this function, we must also use nbsps at the start/end of the string because
+    // this function doesn't get all surrounding whitespace, just the whitespace in the current text node.
+                                                             isStartOfParagraph(visibleUpstreamPos) || upstream == 0, 
+                                                             isEndOfParagraph(visibleDownstreamPos) || (unsigned)downstream == text.length() - 1);
+    
+    if (string != rebalancedString)
+        replaceTextInNode(textNode, upstream, length, rebalancedString);
 }
 
-void CompositeEditCommand::rebalanceWhitespaceAt(const Position &position)
+void CompositeEditCommand::prepareWhitespaceAtPositionForSplit(Position& position)
 {
-    EditCommandPtr cmd(new RebalanceWhitespaceCommand(document(), position));
-    applyCommandToComposite(cmd);    
+    Node* node = position.node();
+    if (!node || !node->isTextNode())
+        return;
+    Text* textNode = static_cast<Text*>(node);    
+    
+    if (textNode->length() == 0)
+        return;
+    RenderObject* renderer = textNode->renderer();
+    if (renderer && !renderer->style()->collapseWhiteSpace())
+        return;
+
+    // Delete collapsed whitespace so that inserting nbsps doesn't uncollapse it.
+    Position upstreamPos = position.upstream();
+    deleteInsignificantText(position.upstream(), position.downstream());
+    position = upstreamPos.downstream();
+
+    VisiblePosition visiblePos(position);
+    VisiblePosition previousVisiblePos(visiblePos.next());
+    Position previous(previousVisiblePos.deepEquivalent());
+    
+    if (isCollapsibleWhitespace(previousVisiblePos.characterAfter()) && previous.node()->isTextNode() && !previous.node()->hasTagName(brTag))
+        replaceTextInNode(static_cast<Text*>(previous.node()), previous.offset(), 1, nonBreakingSpaceString());
+    if (isCollapsibleWhitespace(visiblePos.characterAfter()) && position.node()->isTextNode() && !position.node()->hasTagName(brTag))
+        replaceTextInNode(static_cast<Text*>(position.node()), position.offset(), 1, nonBreakingSpaceString());
 }
 
 void CompositeEditCommand::rebalanceWhitespace()
 {
     Selection selection = endingSelection();
-    if (selection.isCaretOrRange()) {
-        EditCommandPtr startCmd(new RebalanceWhitespaceCommand(document(), endingSelection().start()));
-        applyCommandToComposite(startCmd);
-        if (selection.isRange()) {
-            EditCommandPtr endCmd(new RebalanceWhitespaceCommand(document(), endingSelection().end()));
-            applyCommandToComposite(endCmd);
-        }
-    }
+    if (selection.isNone())
+        return;
+        
+    rebalanceWhitespaceAt(selection.start());
+    if (selection.isRange())
+        rebalanceWhitespaceAt(selection.end());
 }
 
-void CompositeEditCommand::deleteInsignificantText(Text *textNode, int start, int end)
+void CompositeEditCommand::deleteInsignificantText(TexttextNode, int start, int end)
 {
     if (!textNode || !textNode->renderer() || start >= end)
         return;
 
-    RenderText *textRenderer = static_cast<RenderText *>(textNode->renderer());
-    InlineTextBox *box = textRenderer->firstTextBox();
+    RenderText* textRenderer = static_cast<RenderText*>(textNode->renderer());
+    InlineTextBoxbox = textRenderer->firstTextBox();
     if (!box) {
         // whole text node is empty
         removeNode(textNode);
@@ -375,7 +449,7 @@ void CompositeEditCommand::deleteInsignificantText(Text *textNode, int start, in
         return;
 
     int removed = 0;
-    InlineTextBox *prevBox = 0;
+    InlineTextBoxprevBox = 0;
     RefPtr<StringImpl> str;
 
     // This loop structure works to process all gaps preceding a box,
@@ -390,8 +464,8 @@ void CompositeEditCommand::deleteInsignificantText(Text *textNode, int start, in
         bool indicesIntersect = start <= gapEnd && end >= gapStart;
         int gapLen = gapEnd - gapStart;
         if (indicesIntersect && gapLen > 0) {
-            gapStart = kMax(gapStart, start);
-            gapEnd = kMin(gapEnd, end);
+            gapStart = max(gapStart, start);
+            gapEnd = min(gapEnd, end);
             if (!str)
                 str = textNode->string()->substring(start, end - start);
             // remove text in the gap
@@ -418,7 +492,7 @@ void CompositeEditCommand::deleteInsignificantText(Text *textNode, int start, in
     }
 }
 
-void CompositeEditCommand::deleteInsignificantText(const Position &start, const Position &end)
+void CompositeEditCommand::deleteInsignificantText(const Position& start, const Position& end)
 {
     if (start.isNull() || end.isNull())
         return;
@@ -426,35 +500,30 @@ void CompositeEditCommand::deleteInsignificantText(const Position &start, const
     if (Range::compareBoundaryPoints(start, end) >= 0)
         return;
 
-    Node *node = start.node();
-    while (node) {
-        Node *next = node->traverseNextNode();
-    
+    Node* next;
+    for (Node* node = start.node(); node; node = next) {
+        next = node->traverseNextNode();
         if (node->isTextNode()) {
-            Text *textNode = static_cast<Text *>(node);
-            bool isStartNode = node == start.node();
-            bool isEndNode = node == end.node();
-            int startOffset = isStartNode ? start.offset() : 0;
-            int endOffset = isEndNode ? end.offset() : textNode->length();
+            Text* textNode = static_cast<Text*>(node);
+            int startOffset = node == start.node() ? start.offset() : 0;
+            int endOffset = node == end.node() ? end.offset() : textNode->length();
             deleteInsignificantText(textNode, startOffset, endOffset);
         }
-            
         if (node == end.node())
             break;
-        node = next;
     }
 }
 
-void CompositeEditCommand::deleteInsignificantTextDownstream(const WebCore::Position &pos)
+void CompositeEditCommand::deleteInsignificantTextDownstream(const Position& pos)
 {
     Position end = VisiblePosition(pos, VP_DEFAULT_AFFINITY).next().deepEquivalent().downstream();
     deleteInsignificantText(pos, end);
 }
 
-Node *CompositeEditCommand::appendBlockPlaceholder(Node *node)
+Node* CompositeEditCommand::appendBlockPlaceholder(Node* node)
 {
     if (!node)
-        return NULL;
+        return 0;
     
     // Should assert isBlockFlow || isInlineFlow when deletion improves.  See 4244964.
     ASSERT(node->renderer());
@@ -464,76 +533,62 @@ Node *CompositeEditCommand::appendBlockPlaceholder(Node *node)
     return placeholder.get();
 }
 
-Node *CompositeEditCommand::insertBlockPlaceholder(const Position &pos)
+Node* CompositeEditCommand::insertBlockPlaceholder(const Position& pos)
 {
     if (pos.isNull())
-        return NULL;
+        return 0;
 
     // Should assert isBlockFlow || isInlineFlow when deletion improves.  See 4244964.
     ASSERT(pos.node()->renderer());
 
     RefPtr<Node> placeholder = createBlockPlaceholderElement(document());
-    insertNodeAt(placeholder.get(), pos.node(), pos.offset());
+    insertNodeAt(placeholder.get(), pos);
     return placeholder.get();
 }
 
-Node *CompositeEditCommand::addBlockPlaceholderIfNeeded(Node *node)
+Node* CompositeEditCommand::addBlockPlaceholderIfNeeded(Node* node)
 {
     if (!node)
-        return false;
+        return 0;
 
     updateLayout();
 
     RenderObject *renderer = node->renderer();
     if (!renderer || !renderer->isBlockFlow())
-        return false;
+        return 0;
     
     // append the placeholder to make sure it follows
     // any unrendered blocks
     if (renderer->height() == 0 || (renderer->isListItem() && renderer->isEmpty()))
         return appendBlockPlaceholder(node);
 
-    return NULL;
-}
-
-bool CompositeEditCommand::removeBlockPlaceholder(Node *node)
-{
-    Node *placeholder = findBlockPlaceholder(node);
-    if (placeholder) {
-        removeNode(placeholder);
-        return true;
-    }
-    return false;
+    return 0;
 }
 
-Node *CompositeEditCommand::findBlockPlaceholder(Node *node)
+// Removes '\n's and brs that will collapse when content is inserted just before them.
+// FIXME: We shouldn't really have to remove placeholders, but removing them is a workaround for 9661.
+void CompositeEditCommand::removePlaceholderAt(const VisiblePosition& visiblePosition)
 {
-    if (!node)
-        return 0;
-
-    updateLayout();
-
-    RenderObject *renderer = node->renderer();
-    if (!renderer || !renderer->isBlockFlow())
-        return 0;
-
-    for (Node *checkMe = node; checkMe; checkMe = checkMe->traverseNextNode(node)) {
-        if (checkMe->isElementNode()) {
-            Element *element = static_cast<Element *>(checkMe);
-            if (element->enclosingBlockFlowElement() == node && 
-                element->getAttribute(classAttr) == blockPlaceholderClassString()) {
-                return element;
-            }
-        }
+    if (visiblePosition.isNull())
+        return;
+        
+    Position p = visiblePosition.deepEquivalent().downstream();
+    // If a br or '\n' is at the end of a block and not at the start of a paragraph,
+    // then it is superfluous, so adding content before a br or '\n' that is at
+    // the start of a paragraph will render it superfluous.
+    // FIXME: This doesn't remove placeholders at the end of anonymous blocks.
+    if (isEndOfBlock(visiblePosition) && isStartOfParagraph(visiblePosition)) {
+        if (p.node()->hasTagName(brTag) && p.offset() == 0)
+            removeNode(p.node());
+        else if (lineBreakExistsAtPosition(visiblePosition))
+            deleteTextFromNode(static_cast<Text*>(p.node()), p.offset(), 1);
     }
-    
-    return 0;
 }
 
-void CompositeEditCommand::moveParagraphContentsToNewBlockIfNecessary(const Position &pos)
+Node* CompositeEditCommand::moveParagraphContentsToNewBlockIfNecessary(const Position& pos)
 {
     if (pos.isNull())
-        return;
+        return 0;
     
     updateLayout();
     
@@ -545,45 +600,289 @@ void CompositeEditCommand::moveParagraphContentsToNewBlockIfNecessary(const Posi
     
     Position paragraphStart = visibleParagraphStart.deepEquivalent().upstream();
     Position end = visibleEnd.deepEquivalent().upstream();
-    
+
+    // If there are no VisiblePositions in the same block as pos then 
+    // paragraphStart will be outside the paragraph
+    if (Range::compareBoundaryPoints(pos, paragraphStart) < 0)
+        return 0;
+
     // Perform some checks to see if we need to perform work in this function.
-    if (paragraphStart.node()->isBlockFlow()) {
-        if (end.node()->isBlockFlow()) {
-            if (!end.node()->isAncestor(paragraphStart.node())) {
+    if (isBlock(paragraphStart.node())) {
+        if (isBlock(end.node())) {
+            if (!end.node()->isDescendantOf(paragraphStart.node())) {
                 // If the paragraph end is a descendant of paragraph start, then we need to run
                 // the rest of this function. If not, we can bail here.
-                return;
+                return 0;
             }
         }
-        else if (end.node()->enclosingBlockFlowElement() != paragraphStart.node()) {
-            // The paragraph end is in another block that is an ancestor of the paragraph start.
+        else if (enclosingBlock(end.node()) != paragraphStart.node()) {
+            // The visibleEnd.  It must be an ancestor of the paragraph start.
             // We can bail as we have a full block to work with.
-            ASSERT(paragraphStart.node()->isAncestor(end.node()->enclosingBlockFlowElement()));
-            return;
+            ASSERT(paragraphStart.node()->isDescendantOf(enclosingBlock(end.node())));
+            return 0;
         }
         else if (isEndOfDocument(visibleEnd)) {
             // At the end of the document. We can bail here as well.
-            return;
+            return 0;
         }
     }
 
     RefPtr<Node> newBlock = createDefaultParagraphElement(document());
+    appendNode(createBreakElement(document()).get(), newBlock.get());
+    insertNodeAt(newBlock.get(), paragraphStart);
+    
+    moveParagraphs(visibleParagraphStart, visibleParagraphEnd, VisiblePosition(Position(newBlock.get(), 0)));
+    
+    return newBlock.get();
+}
 
-    Node *moveNode = paragraphStart.node();
-    if (paragraphStart.offset() >= paragraphStart.node()->caretMaxOffset())
-        moveNode = moveNode->traverseNextNode();
-    Node *endNode = end.node();
+void CompositeEditCommand::pushAnchorElementDown(Node* anchorNode)
+{
+    if (!anchorNode)
+        return;
+    
+    ASSERT(anchorNode->isLink());
     
-    insertNodeAt(newBlock.get(), paragraphStart.node(), paragraphStart.offset());
+    setEndingSelection(Selection::selectionFromContentsOfNode(anchorNode));
+    applyStyledElement(static_cast<Element*>(anchorNode));
+    // Clones of anchorNode have been pushed down, now remove it.
+    if (anchorNode->inDocument())
+        removeNodePreservingChildren(anchorNode);
+}
+
+// We must push partially selected anchors down before creating or removing
+// links from a selection to create fully selected chunks that can be removed.
+// ApplyStyleCommand doesn't do this for us because styles can be nested.
+// Anchors cannot be nested.
+void CompositeEditCommand::pushPartiallySelectedAnchorElementsDown()
+{
+    Selection originalSelection = endingSelection();
+    VisiblePosition visibleStart(originalSelection.start());
+    VisiblePosition visibleEnd(originalSelection.end());
+    
+    Node* startAnchor = enclosingAnchorElement(originalSelection.start());
+    VisiblePosition startOfStartAnchor(Position(startAnchor, 0));
+    if (startAnchor && startOfStartAnchor != visibleStart)
+        pushAnchorElementDown(startAnchor);
 
-    while (moveNode && !moveNode->isBlockFlow()) {
-        Node *next = moveNode->traverseNextSibling();
-        removeNode(moveNode);
-        appendNode(moveNode, newBlock.get());
-        if (moveNode == endNode)
-            break;
-        moveNode = next;
+    Node* endAnchor = enclosingAnchorElement(originalSelection.end());
+    VisiblePosition endOfEndAnchor(Position(endAnchor, 0));
+    if (endAnchor && endOfEndAnchor != visibleEnd)
+        pushAnchorElementDown(endAnchor);
+
+    ASSERT(originalSelection.start().node()->inDocument() && originalSelection.end().node()->inDocument());
+    setEndingSelection(originalSelection);
+}
+
+// This moves a paragraph preserving its style.
+void CompositeEditCommand::moveParagraph(const VisiblePosition& startOfParagraphToMove, const VisiblePosition& endOfParagraphToMove, const VisiblePosition& destination, bool preserveSelection, bool preserveStyle)
+{
+    ASSERT(isStartOfParagraph(startOfParagraphToMove));
+    ASSERT(isEndOfParagraph(endOfParagraphToMove));
+    moveParagraphs(startOfParagraphToMove, endOfParagraphToMove, destination, preserveSelection, preserveStyle);
+}
+
+void CompositeEditCommand::moveParagraphs(const VisiblePosition& startOfParagraphToMove, const VisiblePosition& endOfParagraphToMove, const VisiblePosition& destination, bool preserveSelection, bool preserveStyle)
+{
+    if (startOfParagraphToMove == destination)
+        return;
+    
+    int startIndex = -1;
+    int endIndex = -1;
+    int destinationIndex = -1;
+    if (preserveSelection && !endingSelection().isNone()) {
+        VisiblePosition visibleStart = endingSelection().visibleStart();
+        VisiblePosition visibleEnd = endingSelection().visibleEnd();
+        
+        bool startAfterParagraph = Range::compareBoundaryPoints(visibleStart.deepEquivalent(), endOfParagraphToMove.deepEquivalent()) > 0;
+        bool endBeforeParagraph = Range::compareBoundaryPoints(visibleEnd.deepEquivalent(), startOfParagraphToMove.deepEquivalent()) < 0;
+        
+        if (!startAfterParagraph && !endBeforeParagraph) {
+            bool startInParagraph = Range::compareBoundaryPoints(visibleStart.deepEquivalent(), startOfParagraphToMove.deepEquivalent()) >= 0;
+            bool endInParagraph = Range::compareBoundaryPoints(visibleEnd.deepEquivalent(), endOfParagraphToMove.deepEquivalent()) <= 0;
+            
+            startIndex = 0;
+            if (startInParagraph) {
+                RefPtr<Range> startRange = new Range(document(), startOfParagraphToMove.deepEquivalent(), visibleStart.deepEquivalent());
+                startIndex = TextIterator::rangeLength(startRange.get(), true);
+            }
+
+            endIndex = 0;
+            if (endInParagraph) {
+                RefPtr<Range> endRange = new Range(document(), startOfParagraphToMove.deepEquivalent(), visibleEnd.deepEquivalent());
+                endIndex = TextIterator::rangeLength(endRange.get(), true);
+            }
+        }
+    }
+    
+    VisiblePosition beforeParagraph = startOfParagraphToMove.previous();
+    VisiblePosition afterParagraph(endOfParagraphToMove.next());
+
+    // We upstream() the end and downstream() the start so that we don't include collapsed whitespace in the move.
+    // When we paste a fragment, spaces after the end and before the start are treated as though they were rendered.    
+    Position start = startOfParagraphToMove.deepEquivalent().downstream();
+    Position end = endOfParagraphToMove.deepEquivalent().upstream();
+    
+    // start and end can't be used directly to create a Range; they are "editing positions"
+    Position startRangeCompliant = rangeCompliantEquivalent(start);
+    Position endRangeCompliant = rangeCompliantEquivalent(end);
+    RefPtr<Range> range = new Range(document(), startRangeCompliant.node(), startRangeCompliant.offset(), endRangeCompliant.node(), endRangeCompliant.offset());
+
+    // FIXME: This is an inefficient way to preserve style on nodes in the paragraph to move.  It 
+    // shouldn't matter though, since moved paragraphs will usually be quite small.
+    RefPtr<DocumentFragment> fragment = startOfParagraphToMove != endOfParagraphToMove ? createFragmentFromMarkup(document(), createMarkup(range.get(), 0, DoNotAnnotateForInterchange, true), "") : 0;
+    
+    // FIXME (5098931): We should add a new insert action "WebViewInsertActionMoved" and call shouldInsertFragment here.
+    
+    setEndingSelection(Selection(start, end, DOWNSTREAM));
+    deleteSelection(false, false, false, false);
+
+    ASSERT(destination.deepEquivalent().node()->inDocument());
+    
+    // There are bugs in deletion when it removes a fully selected table/list.  
+    // It expands and removes the entire table/list, but will let content
+    // before and after the table/list collapse onto one line.
+    
+    // Deleting a paragraph will leave a placeholder.  Remove it (and prune
+    // empty or unrendered parents).
+    VisiblePosition caretAfterDelete = endingSelection().visibleStart();
+    if (isStartOfParagraph(caretAfterDelete) && isEndOfParagraph(caretAfterDelete)) {
+        // Note: We want the rightmost candidate.
+        Position position = caretAfterDelete.deepEquivalent().downstream();
+        Node* node = position.node();
+        // Normally deletion will leave a br as a placeholder.
+        if (node->hasTagName(brTag))
+            removeNodeAndPruneAncestors(node);
+        // If the selection to move was empty and in an empty block that 
+        // doesn't require a placeholder to prop itself open (like a bordered 
+        // div or an li), remove it during the move (the list removal code 
+        // expects this behavior).
+        else if (isBlock(node))
+            removeNodeAndPruneAncestors(node);
+        else if (lineBreakExistsAtPosition(caretAfterDelete))
+            deleteTextFromNode(static_cast<Text*>(node), position.offset(), 1);
+    }
+
+    // Add a br if pruning an empty block level element caused a collapse.  For example:
+    // foo^
+    // <div>bar</div>
+    // baz
+    // Imagine moving 'bar' to ^.  'bar' will be deleted and its div pruned.  That would
+    // cause 'baz' to collapse onto the line with 'foobar' unless we insert a br.
+    // Must recononicalize these two VisiblePositions after the pruning above.
+    beforeParagraph = VisiblePosition(beforeParagraph.deepEquivalent());
+    afterParagraph = VisiblePosition(afterParagraph.deepEquivalent());
+    if (beforeParagraph.isNotNull() && (!isEndOfParagraph(beforeParagraph) || beforeParagraph == afterParagraph)) {
+        // FIXME: Trim text between beforeParagraph and afterParagraph if they aren't equal.
+        insertNodeAt(createBreakElement(document()).get(), beforeParagraph.deepEquivalent());
+        // Need an updateLayout here in case inserting the br has split a text node.
+        updateLayout();
+    }
+        
+    RefPtr<Range> startToDestinationRange(new Range(document(), Position(document(), 0), rangeCompliantEquivalent(destination.deepEquivalent())));
+    destinationIndex = TextIterator::rangeLength(startToDestinationRange.get(), true);
+    
+    setEndingSelection(destination);
+    applyCommandToComposite(new ReplaceSelectionCommand(document(), fragment.get(), true, false, !preserveStyle, false, true));
+    
+    if (preserveSelection && startIndex != -1) {
+        // Fragment creation (using createMarkup) incorrectly uses regular
+        // spaces instead of nbsps for some spaces that were rendered (11475), which
+        // causes spaces to be collapsed during the move operation.  This results
+        // in a call to rangeFromLocationAndLength with a location past the end
+        // of the document (which will return null).
+        RefPtr<Range> start = TextIterator::rangeFromLocationAndLength(document()->documentElement(), destinationIndex + startIndex, 0, true);
+        RefPtr<Range> end = TextIterator::rangeFromLocationAndLength(document()->documentElement(), destinationIndex + endIndex, 0, true);
+        if (start && end)
+            setEndingSelection(Selection(start->startPosition(), end->startPosition(), DOWNSTREAM));
+    }
+}
+
+// FIXME: Send an appropriate shouldDeleteRange call.
+bool CompositeEditCommand::breakOutOfEmptyListItem()
+{
+    Node* emptyListItem = enclosingEmptyListItem(endingSelection().visibleStart());
+    if (!emptyListItem)
+        return false;
+        
+    RefPtr<CSSMutableStyleDeclaration> style = styleAtPosition(endingSelection().start());
+
+    Node* listNode = emptyListItem->parentNode();
+    RefPtr<Node> newBlock = isListElement(listNode->parentNode()) ? createListItemElement(document()) : createDefaultParagraphElement(document());
+    
+    if (emptyListItem->renderer()->nextSibling()) {
+        if (emptyListItem->renderer()->previousSibling())
+            splitElement(static_cast<Element*>(listNode), emptyListItem);
+        insertNodeBefore(newBlock.get(), listNode);
+        removeNode(emptyListItem);
+    } else {
+        insertNodeAfter(newBlock.get(), listNode);
+        removeNode(emptyListItem->renderer()->previousSibling() ? emptyListItem : listNode);
+    }
+    
+    appendBlockPlaceholder(newBlock.get());
+    setEndingSelection(Selection(Position(newBlock.get(), 0), DOWNSTREAM));
+    
+    CSSComputedStyleDeclaration endingStyle(endingSelection().start().node());
+    endingStyle.diff(style.get());
+    if (style->length() > 0)
+        applyStyle(style.get());
+    
+    return true;
+}
+
+// Operations use this function to avoid inserting content into an anchor when at the start or the end of 
+// that anchor, as in NSTextView.
+// FIXME: This is only an approximation of NSTextViews insertion behavior, which varies depending on how
+// the caret was made. 
+Position CompositeEditCommand::positionAvoidingSpecialElementBoundary(const Position& original, bool alwaysAvoidAnchors)
+{
+    if (original.isNull())
+        return original;
+        
+    VisiblePosition visiblePos(original);
+    Node* enclosingAnchor = enclosingAnchorElement(original);
+    Position result = original;
+    // Don't avoid block level anchors, because that would insert content into the wrong paragraph.
+    if (enclosingAnchor && !isBlock(enclosingAnchor)) {
+        VisiblePosition firstInAnchor(Position(enclosingAnchor, 0));
+        VisiblePosition lastInAnchor(Position(enclosingAnchor, maxDeepOffset(enclosingAnchor)));
+        // If visually just after the anchor, insert *inside* the anchor unless it's the last 
+        // VisiblePosition in the document, to match NSTextView.
+        if (visiblePos == lastInAnchor && (isEndOfDocument(visiblePos) || alwaysAvoidAnchors)) {
+            // Make sure anchors are pushed down before avoiding them so that we don't
+            // also avoid structural elements like lists and blocks (5142012).
+            if (original.node() != enclosingAnchor && original.node()->parentNode() != enclosingAnchor) {
+                pushAnchorElementDown(enclosingAnchor);
+                enclosingAnchor = enclosingAnchorElement(original);
+                if (!enclosingAnchor)
+                    return original;
+            }
+            // Don't insert outside an anchor if doing so would skip over a line break.  It would
+            // probably be safe to move the line break so that we could still avoid the anchor here.
+            Position downstream(visiblePos.deepEquivalent().downstream());
+            if (lineBreakExistsAtPosition(visiblePos) && downstream.node()->isDescendantOf(enclosingAnchor))
+                return original;
+            
+            result = positionAfterNode(enclosingAnchor);
+        }
+        // If visually just before an anchor, insert *outside* the anchor unless it's the first
+        // VisiblePosition in a paragraph, to match NSTextView.
+        if (visiblePos == firstInAnchor && (!isStartOfParagraph(visiblePos) || alwaysAvoidAnchors)) {
+            // Make sure anchors are pushed down before avoiding them so that we don't
+            // also avoid structural elements like lists and blocks (5142012).
+            if (original.node() != enclosingAnchor && original.node()->parentNode() != enclosingAnchor) {
+                pushAnchorElementDown(enclosingAnchor);
+                enclosingAnchor = enclosingAnchorElement(original);
+            }
+            result = positionBeforeNode(enclosingAnchor);
+        }
     }
+        
+    if (result.isNull() || !editableRootForPosition(result))
+        result = original;
+    
+    return result;
 }
 
 PassRefPtr<Element> createBlockPlaceholderElement(Document* document)
@@ -591,14 +890,9 @@ PassRefPtr<Element> createBlockPlaceholderElement(Document* document)
     ExceptionCode ec = 0;
     RefPtr<Element> breakNode = document->createElementNS(xhtmlNamespaceURI, "br", ec);
     ASSERT(ec == 0);
-    breakNode->setAttribute(classAttr, blockPlaceholderClassString());
+    static String classString = "webkit-block-placeholder";
+    breakNode->setAttribute(classAttr, classString);
     return breakNode.release();
 }
 
-static const String &blockPlaceholderClassString()
-{
-    static String blockPlaceholderClassString = "khtml-block-placeholder";
-    return blockPlaceholderClassString;
-}
-
 } // namespace WebCore