Reviewed by Kevin.
[WebKit-https.git] / WebCore / khtml / editing / htmlediting.cpp
index f3fafb83165686964e97e654cc45c531bd75f4f4..3707381b2fa764d343ddf30484d701b9c1f4bd87 100644 (file)
@@ -120,7 +120,7 @@ static inline bool nextCharacterIsCollapsibleWhitespace(const Position &pos)
 
 static const int spacesPerTab = 4;
 
-static bool isTableStructureNode(const NodeImpl *node)
+bool isTableStructureNode(const NodeImpl *node)
 {
     RenderObject *r = node->renderer();
     return (r && (r->isTableCell() || r->isTableRow() || r->isTableSection() || r->isTableCol()));
@@ -142,47 +142,6 @@ static DOMString &nonBreakingSpaceString()
     return nonBreakingSpaceString;
 }
 
-static DOMString &styleSpanClassString()
-{
-    static DOMString styleSpanClassString = AppleStyleSpanClass;
-    return styleSpanClassString;
-}
-
-static bool isEmptyStyleSpan(const NodeImpl *node)
-{
-    if (!node || !node->isHTMLElement() || node->id() != ID_SPAN)
-        return false;
-
-    const HTMLElementImpl *elem = static_cast<const HTMLElementImpl *>(node);
-    CSSMutableStyleDeclarationImpl *inlineStyleDecl = elem->inlineStyleDecl();
-    return (!inlineStyleDecl || inlineStyleDecl->length() == 0) && elem->getAttribute(ATTR_CLASS) == styleSpanClassString();
-}
-
-static bool isStyleSpan(const NodeImpl *node)
-{
-    if (!node || !node->isHTMLElement())
-        return false;
-
-    const HTMLElementImpl *elem = static_cast<const HTMLElementImpl *>(node);
-    return elem->id() == ID_SPAN && elem->getAttribute(ATTR_CLASS) == styleSpanClassString();
-}
-
-static bool isEmptyFontTag(const NodeImpl *node)
-{
-    if (!node || node->id() != ID_FONT)
-        return false;
-
-    const ElementImpl *elem = static_cast<const ElementImpl *>(node);
-    NamedAttrMapImpl *map = elem->attributes(true); // true for read-only
-    return (!map || map->length() == 1) && elem->getAttribute(ATTR_CLASS) == styleSpanClassString();
-}
-
-static DOMString &blockPlaceholderClassString()
-{
-    static DOMString blockPlaceholderClassString = "khtml-block-placeholder";
-    return blockPlaceholderClassString;
-}
-
 static DOMString &matchNearestBlockquoteColorString()
 {
     static DOMString matchNearestBlockquoteColorString = "match";
@@ -195,1953 +154,217 @@ static void derefNodesInList(QPtrList<NodeImpl> &list)
         it.current()->deref();
 }
 
-static int maxRangeOffset(NodeImpl *n)
-{
-    if (DOM::offsetInCharacters(n->nodeType()))
-        return n->maxOffset();
-
-    if (n->isElementNode())
-        return n->childNodeCount();
-
-    return 1;
-}
-
-static int maxDeepOffset(NodeImpl *n)
-{
-    if (n->isAtomicNode())
-        return n->caretMaxOffset();
-
-    if (n->isElementNode())
-        return n->childNodeCount();
-
-    return 1;
-}
-
-static void debugPosition(const char *prefix, const Position &pos)
-{
-    if (!prefix)
-        prefix = "";
-    if (pos.isNull())
-        LOG(Editing, "%s <null>", prefix);
-    else
-        LOG(Editing, "%s%s %p : %d", prefix, pos.node()->nodeName().string().latin1(), pos.node(), pos.offset());
-}
-
-static void debugNode(const char *prefix, const NodeImpl *node)
-{
-    if (!prefix)
-        prefix = "";
-    if (!node)
-        LOG(Editing, "%s <null>", prefix);
-    else
-        LOG(Editing, "%s%s %p", prefix, node->nodeName().string().latin1(), node);
-}
-
-//------------------------------------------------------------------------------------------
-// StyleChange
-
-StyleChange::StyleChange(CSSStyleDeclarationImpl *style, ELegacyHTMLStyles usesLegacyStyles)
-    : m_applyBold(false), m_applyItalic(false), m_usesLegacyStyles(usesLegacyStyles)
-{
-    init(style, Position());
-}
-
-StyleChange::StyleChange(CSSStyleDeclarationImpl *style, const Position &position, ELegacyHTMLStyles usesLegacyStyles)
-    : m_applyBold(false), m_applyItalic(false), m_usesLegacyStyles(usesLegacyStyles)
-{
-    init(style, position);
-}
-
-void StyleChange::init(CSSStyleDeclarationImpl *style, const Position &position)
-{
-    style->ref();
-    CSSMutableStyleDeclarationImpl *mutableStyle = style->makeMutable();
-    mutableStyle->ref();
-    style->deref();
-    
-    QString styleText("");
-
-    QValueListConstIterator<CSSProperty> end;
-    for (QValueListConstIterator<CSSProperty> it = mutableStyle->valuesIterator(); it != end; ++it) {
-        const CSSProperty *property = &*it;
-
-        // If position is empty or the position passed in already has the 
-        // style, just move on.
-        if (position.isNotNull() && currentlyHasStyle(position, property))
-            continue;
-        
-        // If needed, figure out if this change is a legacy HTML style change.
-        if (m_usesLegacyStyles && checkForLegacyHTMLStyleChange(property))
-            continue;
-
-        // Add this property
-
-        if (property->id() == CSS_PROP__KHTML_TEXT_DECORATIONS_IN_EFFECT) {
-            // we have to special-case text decorations
-            CSSProperty alteredProperty = CSSProperty(CSS_PROP_TEXT_DECORATION, property->value(), property->isImportant());
-            styleText += alteredProperty.cssText().string();
-        } else {
-            styleText += property->cssText().string();
-        }
-    }
-
-    mutableStyle->deref();
-
-    // Save the result for later
-    m_cssStyle = styleText.stripWhiteSpace();
-}
-
-StyleChange::ELegacyHTMLStyles StyleChange::styleModeForParseMode(bool isQuirksMode)
-{
-    return isQuirksMode ? UseLegacyHTMLStyles : DoNotUseLegacyHTMLStyles;
-}
-
-bool StyleChange::checkForLegacyHTMLStyleChange(const CSSProperty *property)
-{
-    if (!property || !property->value()) {
-        return false;
-    }
-    
-    DOMString valueText(property->value()->cssText());
-    switch (property->id()) {
-        case CSS_PROP_FONT_WEIGHT:
-            if (strcasecmp(valueText, "bold") == 0) {
-                m_applyBold = true;
-                return true;
-            }
-            break;
-        case CSS_PROP_FONT_STYLE:
-            if (strcasecmp(valueText, "italic") == 0 || strcasecmp(valueText, "oblique") == 0) {
-                m_applyItalic = true;
-                return true;
-            }
-            break;
-        case CSS_PROP_COLOR: {
-            QColor color(CSSParser::parseColor(valueText));
-            m_applyFontColor = color.name();
-            return true;
-        }
-        case CSS_PROP_FONT_FAMILY:
-            m_applyFontFace = valueText;
-            return true;
-        case CSS_PROP_FONT_SIZE:
-            if (property->value()->cssValueType() == CSSValue::CSS_PRIMITIVE_VALUE) {
-                CSSPrimitiveValueImpl *value = static_cast<CSSPrimitiveValueImpl *>(property->value());
-                float number = value->getFloatValue(CSSPrimitiveValue::CSS_PX);
-                if (number <= 9)
-                    m_applyFontSize = "1";
-                else if (number <= 10)
-                    m_applyFontSize = "2";
-                else if (number <= 13)
-                    m_applyFontSize = "3";
-                else if (number <= 16)
-                    m_applyFontSize = "4";
-                else if (number <= 18)
-                    m_applyFontSize = "5";
-                else if (number <= 24)
-                    m_applyFontSize = "6";
-                else
-                    m_applyFontSize = "7";
-                // Huge quirk in Microsft Entourage is that they understand CSS font-size, but also write 
-                // out legacy 1-7 values in font tags (I guess for mailers that are not CSS-savvy at all, 
-                // like Eudora). Yes, they write out *both*. We need to write out both as well. Return false.
-                return false; 
-            }
-            else {
-                // Can't make sense of the number. Put no font size.
-                return true;
-            }
-    }
-    return false;
-}
-
-bool StyleChange::currentlyHasStyle(const Position &pos, const CSSProperty *property)
-{
-    ASSERT(pos.isNotNull());
-    CSSComputedStyleDeclarationImpl *style = pos.computedStyle();
-    ASSERT(style);
-    style->ref();
-    CSSValueImpl *value = style->getPropertyCSSValue(property->id(), DoNotUpdateLayout);
-    style->deref();
-    if (!value)
-        return false;
-    value->ref();
-    bool result = strcasecmp(value->cssText(), property->value()->cssText()) == 0;
-    value->deref();
-    return result;
-}
-
-//------------------------------------------------------------------------------------------
-// CompositeEditCommand
-
-CompositeEditCommand::CompositeEditCommand(DocumentImpl *document) 
-    : EditCommand(document)
-{
-}
-
-void CompositeEditCommand::doUnapply()
-{
-    if (m_cmds.count() == 0) {
-        return;
-    }
-    
-    for (int i = m_cmds.count() - 1; i >= 0; --i)
-        m_cmds[i]->unapply();
-
-    setState(NotApplied);
-}
-
-void CompositeEditCommand::doReapply()
-{
-    if (m_cmds.count() == 0) {
-        return;
-    }
-
-    for (QValueList<EditCommandPtr>::ConstIterator it = m_cmds.begin(); it != m_cmds.end(); ++it)
-        (*it)->reapply();
-
-    setState(Applied);
-}
-
-//
-// sugary-sweet convenience functions to help create and apply edit commands in composite commands
-//
-void CompositeEditCommand::applyCommandToComposite(EditCommandPtr &cmd)
-{
-    cmd.setStartingSelection(endingSelection());
-    cmd.setEndingSelection(endingSelection());
-    cmd.setParent(this);
-    cmd.apply();
-    m_cmds.append(cmd);
-}
-
-void CompositeEditCommand::applyStyle(CSSStyleDeclarationImpl *style, EditAction editingAction)
-{
-    EditCommandPtr cmd(new ApplyStyleCommand(document(), style, editingAction));
-    applyCommandToComposite(cmd);
-}
-
-void CompositeEditCommand::insertParagraphSeparator()
-{
-    EditCommandPtr cmd(new InsertParagraphSeparatorCommand(document()));
-    applyCommandToComposite(cmd);
-}
-
-void CompositeEditCommand::insertNodeBefore(NodeImpl *insertChild, NodeImpl *refChild)
-{
-    ASSERT(refChild->id() != ID_BODY);
-    EditCommandPtr cmd(new InsertNodeBeforeCommand(document(), insertChild, refChild));
-    applyCommandToComposite(cmd);
-}
-
-void CompositeEditCommand::insertNodeAfter(NodeImpl *insertChild, NodeImpl *refChild)
-{
-    ASSERT(refChild->id() != ID_BODY);
-    if (refChild->parentNode()->lastChild() == refChild) {
-        appendNode(insertChild, refChild->parentNode());
-    }
-    else {
-        ASSERT(refChild->nextSibling());
-        insertNodeBefore(insertChild, refChild->nextSibling());
-    }
-}
-
-void CompositeEditCommand::insertNodeAt(NodeImpl *insertChild, NodeImpl *refChild, long offset)
-{
-    if (refChild->hasChildNodes() || (refChild->renderer() && refChild->renderer()->isBlockFlow())) {
-        NodeImpl *child = refChild->firstChild();
-        for (long i = 0; child && i < offset; i++)
-            child = child->nextSibling();
-        if (child)
-            insertNodeBefore(insertChild, child);
-        else
-            appendNode(insertChild, refChild);
-    } 
-    else if (refChild->caretMinOffset() >= offset) {
-        insertNodeBefore(insertChild, refChild);
-    } 
-    else if (refChild->isTextNode() && refChild->caretMaxOffset() > offset) {
-        splitTextNode(static_cast<TextImpl *>(refChild), offset);
-        insertNodeBefore(insertChild, refChild);
-    } 
-    else {
-        insertNodeAfter(insertChild, refChild);
-    }
-}
-
-void CompositeEditCommand::appendNode(NodeImpl *appendChild, NodeImpl *parent)
-{
-    EditCommandPtr cmd(new AppendNodeCommand(document(), appendChild, parent));
-    applyCommandToComposite(cmd);
-}
-
-void CompositeEditCommand::removeFullySelectedNode(NodeImpl *node)
-{
-    if (isTableStructureNode(node) || node == node->rootEditableElement()) {
-        // Do not remove an element of table structure; remove its contents.
-        // Likewise for the root editable element.
-        NodeImpl *child = node->firstChild();
-        while (child) {
-            NodeImpl *remove = child;
-            child = child->nextSibling();
-            removeFullySelectedNode(remove);
-        }
-    }
-    else {
-        removeNode(node);
-    }
-}
-
-void CompositeEditCommand::removeChildrenInRange(NodeImpl *node, int from, int to)
-{
-    NodeImpl *nodeToRemove = node->childNode(from);
-    for (int i = from; i < to; i++) {
-        ASSERT(nodeToRemove);
-        NodeImpl *next = nodeToRemove->nextSibling();
-        removeNode(nodeToRemove);
-        nodeToRemove = next;
-    }
-}
-
-void CompositeEditCommand::removeNode(NodeImpl *removeChild)
-{
-    EditCommandPtr cmd(new RemoveNodeCommand(document(), removeChild));
-    applyCommandToComposite(cmd);
-}
-
-void CompositeEditCommand::removeNodePreservingChildren(NodeImpl *removeChild)
-{
-    EditCommandPtr cmd(new RemoveNodePreservingChildrenCommand(document(), removeChild));
-    applyCommandToComposite(cmd);
-}
-
-void CompositeEditCommand::splitTextNode(TextImpl *text, long offset)
-{
-    EditCommandPtr cmd(new SplitTextNodeCommand(document(), text, offset));
-    applyCommandToComposite(cmd);
-}
-
-void CompositeEditCommand::splitElement(ElementImpl *element, NodeImpl *atChild)
-{
-    EditCommandPtr cmd(new SplitElementCommand(document(), element, atChild));
-    applyCommandToComposite(cmd);
-}
-
-void CompositeEditCommand::mergeIdenticalElements(DOM::ElementImpl *first, DOM::ElementImpl *second)
-{
-    EditCommandPtr cmd(new MergeIdenticalElementsCommand(document(), first, second));
-    applyCommandToComposite(cmd);
-}
-
-void CompositeEditCommand::wrapContentsInDummySpan(DOM::ElementImpl *element)
-{
-    EditCommandPtr cmd(new WrapContentsInDummySpanCommand(document(), element));
-    applyCommandToComposite(cmd);
-}
-
-void CompositeEditCommand::splitTextNodeContainingElement(DOM::TextImpl *text, long offset)
-{
-    EditCommandPtr cmd(new SplitTextNodeContainingElementCommand(document(), text, offset));
-    applyCommandToComposite(cmd);
-}
-
-void CompositeEditCommand::joinTextNodes(TextImpl *text1, TextImpl *text2)
-{
-    EditCommandPtr cmd(new JoinTextNodesCommand(document(), text1, text2));
-    applyCommandToComposite(cmd);
-}
-
-void CompositeEditCommand::inputText(const DOMString &text, bool selectInsertedText)
-{
-    InsertTextCommand *impl = new InsertTextCommand(document());
-    EditCommandPtr cmd(impl);
-    applyCommandToComposite(cmd);
-    impl->input(text, selectInsertedText);
-}
-
-void CompositeEditCommand::insertTextIntoNode(TextImpl *node, long offset, const DOMString &text)
-{
-    EditCommandPtr cmd(new InsertIntoTextNode(document(), node, offset, text));
-    applyCommandToComposite(cmd);
-}
-
-void CompositeEditCommand::deleteTextFromNode(TextImpl *node, long offset, long count)
-{
-    EditCommandPtr cmd(new DeleteFromTextNodeCommand(document(), node, offset, count));
-    applyCommandToComposite(cmd);
-}
-
-void CompositeEditCommand::replaceTextInNode(TextImpl *node, long offset, long count, const DOMString &replacementText)
-{
-    EditCommandPtr deleteCommand(new DeleteFromTextNodeCommand(document(), node, offset, count));
-    applyCommandToComposite(deleteCommand);
-    EditCommandPtr insertCommand(new InsertIntoTextNode(document(), node, offset, replacementText));
-    applyCommandToComposite(insertCommand);
-}
-
-void CompositeEditCommand::deleteSelection(bool smartDelete, bool mergeBlocksAfterDelete)
-{
-    if (endingSelection().isRange()) {
-        EditCommandPtr cmd(new DeleteSelectionCommand(document(), smartDelete, mergeBlocksAfterDelete));
-        applyCommandToComposite(cmd);
-    }
-}
-
-void CompositeEditCommand::deleteSelection(const Selection &selection, bool smartDelete, bool mergeBlocksAfterDelete)
-{
-    if (selection.isRange()) {
-        EditCommandPtr cmd(new DeleteSelectionCommand(document(), selection, smartDelete, mergeBlocksAfterDelete));
-        applyCommandToComposite(cmd);
-    }
-}
-
-void CompositeEditCommand::removeCSSProperty(CSSStyleDeclarationImpl *decl, int property)
-{
-    EditCommandPtr cmd(new RemoveCSSPropertyCommand(document(), decl, property));
-    applyCommandToComposite(cmd);
-}
-
-void CompositeEditCommand::removeNodeAttribute(ElementImpl *element, int attribute)
-{
-    DOMString value = element->getAttribute(attribute);
-    if (value.isEmpty())
-        return;
-    EditCommandPtr cmd(new RemoveNodeAttributeCommand(document(), element, attribute));
-    applyCommandToComposite(cmd);
-}
-
-void CompositeEditCommand::setNodeAttribute(ElementImpl *element, int attribute, const DOMString &value)
-{
-    EditCommandPtr cmd(new SetNodeAttributeCommand(document(), element, attribute, value));
-    applyCommandToComposite(cmd);
-}
-
-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);
-        }
-    }
-}
-
-void CompositeEditCommand::deleteInsignificantText(TextImpl *textNode, int start, int end)
-{
-    if (!textNode || !textNode->renderer() || start >= end)
-        return;
-
-    RenderText *textRenderer = static_cast<RenderText *>(textNode->renderer());
-    InlineTextBox *box = textRenderer->firstTextBox();
-    if (!box) {
-        // whole text node is empty
-        removeNode(textNode);
-        return;    
-    }
-    
-    long length = textNode->length();
-    if (start >= length || end > length)
-        return;
-
-    int removed = 0;
-    InlineTextBox *prevBox = 0;
-    DOMStringImpl *str = 0;
-
-    // This loop structure works to process all gaps preceding a box,
-    // and also will look at the gap after the last box.
-    while (prevBox || box) {
-        int gapStart = prevBox ? prevBox->m_start + prevBox->m_len : 0;
-        if (end < gapStart)
-            // No more chance for any intersections
-            break;
-
-        int gapEnd = box ? box->m_start : length;
-        bool indicesIntersect = start <= gapEnd && end >= gapStart;
-        int gapLen = gapEnd - gapStart;
-        if (indicesIntersect && gapLen > 0) {
-            gapStart = kMax(gapStart, start);
-            gapEnd = kMin(gapEnd, end);
-            if (!str) {
-                str = textNode->string()->substring(start, end - start);
-                str->ref();
-            }    
-            // remove text in the gap
-            str->remove(gapStart - start - removed, gapLen);
-            removed += gapLen;
-        }
-        
-        prevBox = box;
-        if (box)
-            box = box->nextTextBox();
-    }
-
-    if (str) {
-        // Replace the text between start and end with our pruned version.
-        if (str->l > 0) {
-            replaceTextInNode(textNode, start, end - start, str);
-        }
-        else {
-            // Assert that we are not going to delete all of the text in the node.
-            // If we were, that should have been done above with the call to 
-            // removeNode and return.
-            ASSERT(start > 0 || (unsigned long)end - start < textNode->length());
-            deleteTextFromNode(textNode, start, end - start);
-        }
-        str->deref();
-    }
-}
-
-void CompositeEditCommand::deleteInsignificantText(const Position &start, const Position &end)
-{
-    if (start.isNull() || end.isNull())
-        return;
-
-    if (RangeImpl::compareBoundaryPoints(start, end) >= 0)
-        return;
-
-    NodeImpl *node = start.node();
-    while (node) {
-        NodeImpl *next = node->traverseNextNode();
-    
-        if (node->isTextNode()) {
-            TextImpl *textNode = static_cast<TextImpl *>(node);
-            bool isStartNode = node == start.node();
-            bool isEndNode = node == end.node();
-            int startOffset = isStartNode ? start.offset() : 0;
-            int endOffset = isEndNode ? end.offset() : textNode->length();
-            deleteInsignificantText(textNode, startOffset, endOffset);
-        }
-            
-        if (node == end.node())
-            break;
-        node = next;
-    }
-}
-
-void CompositeEditCommand::deleteInsignificantTextDownstream(const DOM::Position &pos)
-{
-    Position end = VisiblePosition(pos, VP_DEFAULT_AFFINITY).next().deepEquivalent().downstream();
-    deleteInsignificantText(pos, end);
-}
-
-NodeImpl *CompositeEditCommand::appendBlockPlaceholder(NodeImpl *node)
-{
-    if (!node)
-        return NULL;
-
-    ASSERT(node->renderer() && node->renderer()->isBlockFlow());
-
-    NodeImpl *placeholder = createBlockPlaceholderElement(document());
-    appendNode(placeholder, node);
-    return placeholder;
-}
-
-NodeImpl *CompositeEditCommand::insertBlockPlaceholder(const Position &pos)
-{
-    if (pos.isNull())
-        return NULL;
-
-    ASSERT(pos.node()->renderer() && pos.node()->renderer()->isBlockFlow());
-
-    NodeImpl *placeholder = createBlockPlaceholderElement(document());
-    insertNodeAt(placeholder, pos.node(), pos.offset());
-    return placeholder;
-}
-
-NodeImpl *CompositeEditCommand::addBlockPlaceholderIfNeeded(NodeImpl *node)
-{
-    if (!node)
-        return false;
-
-    document()->updateLayout();
-
-    RenderObject *renderer = node->renderer();
-    if (!renderer || !renderer->isBlockFlow())
-        return false;
-    
-    // append the placeholder to make sure it follows
-    // any unrendered blocks
-    if (renderer->height() == 0) {
-        return appendBlockPlaceholder(node);
-    }
-
-    return NULL;
-}
-
-bool CompositeEditCommand::removeBlockPlaceholder(NodeImpl *node)
-{
-    NodeImpl *placeholder = findBlockPlaceholder(node);
-    if (placeholder) {
-        removeNode(placeholder);
-        return true;
-    }
-    return false;
-}
-
-NodeImpl *CompositeEditCommand::findBlockPlaceholder(NodeImpl *node)
-{
-    if (!node)
-        return 0;
-
-    document()->updateLayout();
-
-    RenderObject *renderer = node->renderer();
-    if (!renderer || !renderer->isBlockFlow())
-        return 0;
-
-    for (NodeImpl *checkMe = node; checkMe; checkMe = checkMe->traverseNextNode(node)) {
-        if (checkMe->isElementNode()) {
-            ElementImpl *element = static_cast<ElementImpl *>(checkMe);
-            if (element->enclosingBlockFlowElement() == node && 
-                element->getAttribute(ATTR_CLASS) == blockPlaceholderClassString()) {
-                return element;
-            }
-        }
-    }
-    
-    return 0;
-}
-
-void CompositeEditCommand::moveParagraphContentsToNewBlockIfNecessary(const Position &pos)
-{
-    if (pos.isNull())
-        return;
-    
-    document()->updateLayout();
-    
-    VisiblePosition visiblePos(pos, VP_DEFAULT_AFFINITY);
-    VisiblePosition visibleParagraphStart(startOfParagraph(visiblePos));
-    VisiblePosition visibleParagraphEnd(endOfParagraph(visiblePos, IncludeLineBreak));
-    Position paragraphStart = visibleParagraphStart.deepEquivalent().upstream();
-    Position paragraphEnd = visibleParagraphEnd.deepEquivalent().upstream();
-    
-    // Perform some checks to see if we need to perform work in this function.
-    if (paragraphStart.node()->isBlockFlow()) {
-        if (paragraphEnd.node()->isBlockFlow()) {
-            if (!paragraphEnd.node()->isAncestor(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;
-            }
-        }
-        else if (paragraphEnd.node()->enclosingBlockFlowElement() != paragraphStart.node()) {
-            // The paragraph end is in another block that is an ancestor of the paragraph start.
-            // We can bail as we have a full block to work with.
-            ASSERT(paragraphStart.node()->isAncestor(paragraphEnd.node()->enclosingBlockFlowElement()));
-            return;
-        }
-        else if (isEndOfDocument(visibleParagraphEnd)) {
-            // At the end of the document. We can bail here as well.
-            return;
-        }
-    }
-
-    // Create the block to insert. Most times, this will be a shallow clone of the block containing
-    // the start of the selection (the start block), except for two cases:
-    //    1) When the start block is a body element.
-    //    2) When the start block is a mail blockquote and we are not in a position to insert
-    //       the new block as a peer of the start block. This prevents creating an unwanted 
-    //       additional level of quoting.
-    NodeImpl *startBlock = paragraphStart.node()->enclosingBlockFlowElement();
-    NodeImpl *newBlock = 0;
-    if (startBlock->id() == ID_BODY || (isMailBlockquote(startBlock) && paragraphStart.node() != startBlock))
-        newBlock = createDefaultParagraphElement(document());
-    else
-        newBlock = startBlock->cloneNode(false);
-
-    NodeImpl *moveNode = paragraphStart.node();
-    if (paragraphStart.offset() >= paragraphStart.node()->caretMaxOffset())
-        moveNode = moveNode->traverseNextNode();
-    NodeImpl *endNode = paragraphEnd.node();
-
-    if (paragraphStart.node()->id() == ID_BODY) {
-        insertNodeAt(newBlock, paragraphStart.node(), 0);
-    }
-    else if (paragraphStart.node()->id() == ID_BR) {
-        insertNodeAfter(newBlock, paragraphStart.node());
-    }
-    else {
-        insertNodeBefore(newBlock, paragraphStart.upstream().node());
-    }
-
-    while (moveNode && !moveNode->isBlockFlow()) {
-        NodeImpl *next = moveNode->traverseNextSibling();
-        removeNode(moveNode);
-        appendNode(moveNode, newBlock);
-        if (moveNode == endNode)
-            break;
-        moveNode = next;
-    }
-}
-
-static bool isSpecialElement(NodeImpl *n)
-{
-    if (!n->isHTMLElement())
-        return false;
-
-    if (n->id() == ID_A && n->isLink())
-        return true;
-
-    if (n->id() == ID_UL || n->id() == ID_OL || n->id() == ID_DL)
-        return true;
-
-    RenderObject *renderer = n->renderer();
-
-    if (renderer && (renderer->style()->display() == TABLE || renderer->style()->display() == INLINE_TABLE))
-        return true;
-
-    if (renderer && renderer->style()->isFloating())
-        return true;
-
-    if (renderer && renderer->style()->position() != STATIC)
-        return true;
-
-    return false;
-}
-
-// This version of the function is meant to be called on positions in a document fragment,
-// so it does not check for a root editable element, it is assumed these nodes will be put
-// somewhere editable in the future
-static bool isFirstVisiblePositionInSpecialElementInFragment(const Position& pos)
-{
-    VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
-
-    for (NodeImpl *n = pos.node(); n; n = n->parentNode()) {
-        if (VisiblePosition(n, 0, DOWNSTREAM) != vPos)
-            return false;
-        if (isSpecialElement(n))
-            return true;
-    }
-
-    return false;
-}
-
-static bool isFirstVisiblePositionInSpecialElement(const Position& pos)
-{
-    VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
-
-    for (NodeImpl *n = pos.node(); n; n = n->parentNode()) {
-        if (VisiblePosition(n, 0, DOWNSTREAM) != vPos)
-            return false;
-        if (n->rootEditableElement() == NULL)
-            return false;
-        if (isSpecialElement(n))
-            return true;
-    }
-
-    return false;
-}
-
-static Position positionBeforeNode(NodeImpl *node)
-{
-    return Position(node->parentNode(), node->nodeIndex());
-}
-
-static Position positionBeforeContainingSpecialElement(const Position& pos)
-{
-    ASSERT(isFirstVisiblePositionInSpecialElement(pos));
-
-    VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
-    
-    NodeImpl *outermostSpecialElement = NULL;
-
-    for (NodeImpl *n = pos.node(); n; n = n->parentNode()) {
-        if (VisiblePosition(n, 0, DOWNSTREAM) != vPos)
-            break;
-        if (n->rootEditableElement() == NULL)
-            break;
-        if (isSpecialElement(n))
-            outermostSpecialElement = n;
-    }
-    
-    ASSERT(outermostSpecialElement);
-
-    Position result = positionBeforeNode(outermostSpecialElement);
-    if (result.isNull() || !result.node()->rootEditableElement())
-        return pos;
-    
-    return result;
-}
-
-static bool isLastVisiblePositionInSpecialElement(const Position& pos)
-{
-    // make sure to get a range-compliant version of the position
-    Position rangePos = VisiblePosition(pos, DOWNSTREAM).position();
-
-    VisiblePosition vPos = VisiblePosition(rangePos, DOWNSTREAM);
-
-    for (NodeImpl *n = rangePos.node(); n; n = n->parentNode()) {
-        if (VisiblePosition(n, maxRangeOffset(n), DOWNSTREAM) != vPos)
-            return false;
-        if (n->rootEditableElement() == NULL)
-            return false;
-        if (isSpecialElement(n))
-            return true;
-    }
-
-    return false;
-}
-
-static Position positionAfterNode(NodeImpl *node)
-{
-    return Position(node->parentNode(), node->nodeIndex() + 1);
-}
-
-static Position positionAfterContainingSpecialElement(const Position& pos)
-{
-    ASSERT(isLastVisiblePositionInSpecialElement(pos));
-
-    // make sure to get a range-compliant version of the position
-    Position rangePos = VisiblePosition(pos, DOWNSTREAM).position();
-
-    VisiblePosition vPos = VisiblePosition(rangePos, DOWNSTREAM);
-
-    NodeImpl *outermostSpecialElement = NULL;
-
-    for (NodeImpl *n = rangePos.node(); n; n = n->parentNode()) {
-        if (VisiblePosition(n, maxRangeOffset(n), DOWNSTREAM) != vPos)
-            break;
-        if (n->rootEditableElement() == NULL)
-            break;
-        if (isSpecialElement(n))
-            outermostSpecialElement = n;
-    }
-    
-    ASSERT(outermostSpecialElement);
-
-    Position result = positionAfterNode(outermostSpecialElement);
-    if (result.isNull() || !result.node()->rootEditableElement())
-        return pos;
-    
-    return result;
-}
-
-static Position positionOutsideContainingSpecialElement(const Position &pos)
-{
-    if (isFirstVisiblePositionInSpecialElement(pos)) {
-        return positionBeforeContainingSpecialElement(pos);
-    } else if (isLastVisiblePositionInSpecialElement(pos)) {
-        return positionAfterContainingSpecialElement(pos);
-    }
-
-    return pos;
-}
-
-static Position positionBeforePossibleContainingSpecialElement(const Position &pos)
-{
-    if (isFirstVisiblePositionInSpecialElement(pos)) {
-        return positionBeforeContainingSpecialElement(pos);
-    } 
-
-    return pos;
-}
-
-static Position positionAfterPossibleContainingSpecialElement(const Position &pos)
-{
-    if (isLastVisiblePositionInSpecialElement(pos)) {
-        return positionAfterContainingSpecialElement(pos);
-    }
-
-    return pos;
-}
-
-//==========================================================================================
-// Concrete commands
-//------------------------------------------------------------------------------------------
-// AppendNodeCommand
-
-AppendNodeCommand::AppendNodeCommand(DocumentImpl *document, NodeImpl *appendChild, NodeImpl *parentNode)
-    : EditCommand(document), m_appendChild(appendChild), m_parentNode(parentNode)
-{
-    ASSERT(m_appendChild);
-    m_appendChild->ref();
-
-    ASSERT(m_parentNode);
-    m_parentNode->ref();
-}
-
-AppendNodeCommand::~AppendNodeCommand()
-{
-    ASSERT(m_appendChild);
-    m_appendChild->deref();
-
-    ASSERT(m_parentNode);
-    m_parentNode->deref();
-}
-
-void AppendNodeCommand::doApply()
-{
-    ASSERT(m_appendChild);
-    ASSERT(m_parentNode);
-
-    int exceptionCode = 0;
-    m_parentNode->appendChild(m_appendChild, exceptionCode);
-    ASSERT(exceptionCode == 0);
-}
-
-void AppendNodeCommand::doUnapply()
-{
-    ASSERT(m_appendChild);
-    ASSERT(m_parentNode);
-    ASSERT(state() == Applied);
-
-    int exceptionCode = 0;
-    m_parentNode->removeChild(m_appendChild, exceptionCode);
-    ASSERT(exceptionCode == 0);
-}
-
-//------------------------------------------------------------------------------------------
-// ApplyStyleCommand
-
-ApplyStyleCommand::ApplyStyleCommand(DocumentImpl *document, CSSStyleDeclarationImpl *style, EditAction editingAction, EPropertyLevel propertyLevel)
-    : CompositeEditCommand(document), m_style(style->makeMutable()), m_editingAction(editingAction), m_propertyLevel(propertyLevel)
-{   
-    ASSERT(m_style);
-    m_style->ref();
-}
-
-ApplyStyleCommand::~ApplyStyleCommand()
-{
-    ASSERT(m_style);
-    m_style->deref();
-}
-
-void ApplyStyleCommand::doApply()
-{
-    switch (m_propertyLevel) {
-        case PropertyDefault: {
-            // apply the block-centric properties of the style
-            CSSMutableStyleDeclarationImpl *blockStyle = m_style->copyBlockProperties();
-            blockStyle->ref();
-            applyBlockStyle(blockStyle);
-            // apply any remaining styles to the inline elements
-            // NOTE: hopefully, this string comparison is the same as checking for a non-null diff
-            if (blockStyle->length() < m_style->length()) {
-                CSSMutableStyleDeclarationImpl *inlineStyle = m_style->copy();
-                inlineStyle->ref();
-                applyRelativeFontStyleChange(inlineStyle);
-                blockStyle->diff(inlineStyle);
-                applyInlineStyle(inlineStyle);
-                inlineStyle->deref();
-            }
-            blockStyle->deref();
-            break;
-        }
-        case ForceBlockProperties:
-            // Force all properties to be applied as block styles.
-            applyBlockStyle(m_style);
-            break;
-    }
-   
-    setEndingSelectionNeedsLayout();
-}
-
-EditAction ApplyStyleCommand::editingAction() const
-{
-    return m_editingAction;
-}
-
-void ApplyStyleCommand::applyBlockStyle(CSSMutableStyleDeclarationImpl *style)
-{
-    // update document layout once before removing styles
-    // so that we avoid the expense of updating before each and every call
-    // to check a computed style
-    document()->updateLayout();
-
-    // get positions we want to use for applying style
-    Position start(endingSelection().start());
-    Position end(endingSelection().end());
-    
-    // remove current values, if any, of the specified styles from the blocks
-    // NOTE: tracks the previous block to avoid repeated processing
-    // Also, gather up all the nodes we want to process in a QPtrList before
-    // doing anything. This averts any bugs iterating over these nodes
-    // once you start removing and applying style.
-    NodeImpl *beyondEnd = end.node()->traverseNextNode();
-    QPtrList<NodeImpl> nodes;
-    for (NodeImpl *node = start.node(); node != beyondEnd; node = node->traverseNextNode())
-        nodes.append(node);
-        
-    NodeImpl *prevBlock = 0;
-    for (QPtrListIterator<NodeImpl> it(nodes); it.current(); ++it) {
-        NodeImpl *block = it.current()->enclosingBlockFlowElement();
-        if (block != prevBlock && block->isHTMLElement()) {
-            removeCSSStyle(style, static_cast<HTMLElementImpl *>(block));
-            prevBlock = block;
-        }
-    }
-    
-    // apply specified styles to the block flow elements in the selected range
-    prevBlock = 0;
-    for (QPtrListIterator<NodeImpl> it(nodes); it.current(); ++it) {
-        NodeImpl *node = it.current();
-        if (node->renderer()) {
-            NodeImpl *block = node->enclosingBlockFlowElement();
-            if (block != prevBlock) {
-                addBlockStyleIfNeeded(style, node);
-                prevBlock = block;
-            }
-        }
-    }
-}
-
-#define NoFontDelta (0.0f)
-#define MinimumFontSize (0.1f)
-
-void ApplyStyleCommand::applyRelativeFontStyleChange(CSSMutableStyleDeclarationImpl *style)
-{
-    if (style->getPropertyCSSValue(CSS_PROP_FONT_SIZE)) {
-        // Explicit font size overrides any delta.
-        style->removeProperty(CSS_PROP__KHTML_FONT_SIZE_DELTA);
-        return;
-    }
-
-    // Get the adjustment amount out of the style.
-    CSSValueImpl *value = style->getPropertyCSSValue(CSS_PROP__KHTML_FONT_SIZE_DELTA);
-    if (!value)
-        return;
-    value->ref();
-    float adjustment = NoFontDelta;
-    if (value->cssValueType() == CSSValue::CSS_PRIMITIVE_VALUE) {
-        CSSPrimitiveValueImpl *primitiveValue = static_cast<CSSPrimitiveValueImpl *>(value);
-        if (primitiveValue->primitiveType() == CSSPrimitiveValue::CSS_PX) {
-            // Only PX handled now. If we handle more types in the future, perhaps
-            // a switch statement here would be more appropriate.
-            adjustment = primitiveValue->getFloatValue(CSSPrimitiveValue::CSS_PX);
-        }
-    }
-    style->removeProperty(CSS_PROP__KHTML_FONT_SIZE_DELTA);
-    value->deref();
-    if (adjustment == NoFontDelta)
-        return;
-    
-    // Adjust to the positions we want to use for applying style.
-    Selection selection = endingSelection();
-    Position start(selection.start().downstream());
-    Position end(selection.end().upstream());
-    if (RangeImpl::compareBoundaryPoints(end, start) < 0) {
-        Position swap = start;
-        start = end;
-        end = swap;
-    }
-
-    // Join up any adjacent text nodes.
-    if (start.node()->isTextNode()) {
-        joinChildTextNodes(start.node()->parentNode(), start, end);
-        selection = endingSelection();
-        start = selection.start();
-        end = selection.end();
-    }
-    if (end.node()->isTextNode() && start.node()->parentNode() != end.node()->parentNode()) {
-        joinChildTextNodes(end.node()->parentNode(), start, end);
-        selection = endingSelection();
-        start = selection.start();
-        end = selection.end();
-    }
-
-    // Split the start text nodes if needed to apply style.
-    bool splitStart = splitTextAtStartIfNeeded(start, end); 
-    if (splitStart) {
-        start = endingSelection().start();
-        end = endingSelection().end();
-    }
-    bool splitEnd = splitTextAtEndIfNeeded(start, end);
-    if (splitEnd) {
-        start = endingSelection().start();
-        end = endingSelection().end();
-    }
-
-    NodeImpl *beyondEnd = end.node()->traverseNextNode(); // Calculate loop end point.
-    start = start.upstream(); // Move upstream to ensure we do not add redundant spans.
-    NodeImpl *startNode = start.node();
-    if (startNode->isTextNode() && start.offset() >= startNode->caretMaxOffset()) // Move out of text node if range does not include its characters.
-        startNode = startNode->traverseNextNode();
-
-    // Store away font size before making any changes to the document.
-    // This ensures that changes to one node won't effect another.
-    QMap<const NodeImpl *,float> startingFontSizes;
-    for (const NodeImpl *node = startNode; node != beyondEnd; node = node->traverseNextNode())
-        startingFontSizes.insert(node, computedFontSize(node));
-
-    // These spans were added by us. If empty after font size changes, they can be removed.
-    QPtrList<NodeImpl> emptySpans;
-    
-    NodeImpl *lastStyledNode = 0;
-    for (NodeImpl *node = startNode; node != beyondEnd; node = node->traverseNextNode()) {
-        HTMLElementImpl *elem = 0;
-        if (node->isHTMLElement()) {
-            // Only work on fully selected nodes.
-            if (!nodeFullySelected(node, start, end))
-                continue;
-            elem = static_cast<HTMLElementImpl *>(node);
-        }
-        else if (node->isTextNode() && node->parentNode() != lastStyledNode) {
-            // Last styled node was not parent node of this text node, but we wish to style this
-            // text node. To make this possible, add a style span to surround this text node.
-            elem = static_cast<HTMLElementImpl *>(createStyleSpanElement(document()));
-            insertNodeBefore(elem, node);
-            surroundNodeRangeWithElement(node, node, elem);
-        }
-        else {
-            // Only handle HTML elements and text nodes.
-            continue;
-        }
-        lastStyledNode = node;
-        
-        CSSMutableStyleDeclarationImpl *inlineStyleDecl = elem->getInlineStyleDecl();
-        float currentFontSize = computedFontSize(node);
-        float desiredFontSize = kMax(MinimumFontSize, startingFontSizes[node] + adjustment);
-        if (inlineStyleDecl->getPropertyCSSValue(CSS_PROP_FONT_SIZE)) {
-            inlineStyleDecl->removeProperty(CSS_PROP_FONT_SIZE, true);
-            currentFontSize = computedFontSize(node);
-        }
-        if (currentFontSize != desiredFontSize) {
-            QString desiredFontSizeString = QString::number(desiredFontSize);
-            desiredFontSizeString += "px";
-            inlineStyleDecl->setProperty(CSS_PROP_FONT_SIZE, desiredFontSizeString, false, false);
-            setNodeAttribute(elem, ATTR_STYLE, inlineStyleDecl->cssText());
-        }
-        if (inlineStyleDecl->length() == 0) {
-            removeNodeAttribute(elem, ATTR_STYLE);
-            if (isEmptyStyleSpan(elem))
-                emptySpans.append(elem);
-        }
-    }
-
-    for (QPtrListIterator<NodeImpl> it(emptySpans); it.current(); ++it)
-        removeNodePreservingChildren(it.current());
-}
-
-#undef NoFontDelta
-#undef MinimumFontSize
-
-void ApplyStyleCommand::applyInlineStyle(CSSMutableStyleDeclarationImpl *style)
-{
-    // adjust to the positions we want to use for applying style
-    Position start(endingSelection().start().downstream().equivalentRangeCompliantPosition());
-    Position end(endingSelection().end().upstream());
-
-    if (RangeImpl::compareBoundaryPoints(end, start) < 0) {
-        Position swap = start;
-        start = end;
-        end = swap;
-    }
-
-    // update document layout once before removing styles
-    // so that we avoid the expense of updating before each and every call
-    // to check a computed style
-    document()->updateLayout();
-
-    // split the start node and containing element if the selection starts inside of it
-    bool splitStart = splitTextElementAtStartIfNeeded(start, end); 
-    if (splitStart) {
-        start = endingSelection().start();
-        end = endingSelection().end();
-    }
-
-    // split the end node and containing element if the selection ends inside of it
-    bool splitEnd = splitTextElementAtEndIfNeeded(start, end);
-    start = endingSelection().start();
-    end = endingSelection().end();
-
-    // Remove style from the selection.
-    // Use the upstream position of the start for removing style.
-    // This will ensure we remove all traces of the relevant styles from the selection
-    // and prevent us from adding redundant ones, as described in:
-    // <rdar://problem/3724344> Bolding and unbolding creates extraneous tags
-    removeInlineStyle(style, start.upstream(), end);
-    start = endingSelection().start();
-    end = endingSelection().end();
-
-    if (splitStart) {
-        bool mergedStart = mergeStartWithPreviousIfIdentical(start, end);
-        if (mergedStart) {
-            start = endingSelection().start();
-            end = endingSelection().end();
-        }
-    }
-
-    if (splitEnd) {
-        mergeEndWithNextIfIdentical(start, end);
-        start = endingSelection().start();
-        end = endingSelection().end();
-    }
-
-    // update document layout once before running the rest of the function
-    // so that we avoid the expense of updating before each and every call
-    // to check a computed style
-    document()->updateLayout();
-    
-    if (start.node() == end.node()) {
-        // simple case...start and end are the same node
-        addInlineStyleIfNeeded(style, start.node(), end.node());
-    }
-    else {
-        NodeImpl *node = start.node();
-        if (start.offset() >= start.node()->caretMaxOffset())
-            node = node->traverseNextNode();
-        while (1) {
-            if (node->childNodeCount() == 0 && node->renderer() && node->renderer()->isInline()) {
-                NodeImpl *runStart = node;
-                while (1) {
-                    NodeImpl *next = node->traverseNextNode();
-                    // Break if node is the end node, or if the next node does not fit in with
-                    // the current group.
-                    if (node == end.node() || 
-                        runStart->parentNode() != next->parentNode() || 
-                        (next->isHTMLElement() && next->id() != ID_BR) || 
-                        (next->renderer() && !next->renderer()->isInline()))
-                        break;
-                    node = next;
-                }
-                // Now apply style to the run we found.
-                addInlineStyleIfNeeded(style, runStart, node);
-            }
-            if (node == end.node())
-                break;
-            node = node->traverseNextNode();
-        }
-    }
-
-    if (splitStart || splitEnd) {
-        cleanUpEmptyStyleSpans(start, end);
-    }
-}
-
-//------------------------------------------------------------------------------------------
-// ApplyStyleCommand: style-removal helpers
-
-bool ApplyStyleCommand::isHTMLStyleNode(CSSMutableStyleDeclarationImpl *style, HTMLElementImpl *elem)
-{
-    QValueListConstIterator<CSSProperty> end;
-    for (QValueListConstIterator<CSSProperty> it = style->valuesIterator(); it != end; ++it) {
-        switch ((*it).id()) {
-            case CSS_PROP_FONT_WEIGHT:
-                if (elem->id() == ID_B)
-                    return true;
-                break;
-            case CSS_PROP_FONT_STYLE:
-                if (elem->id() == ID_I)
-                    return true;
-        }
-    }
-
-    return false;
-}
-
-void ApplyStyleCommand::removeHTMLStyleNode(HTMLElementImpl *elem)
-{
-    // This node can be removed.
-    // EDIT FIXME: This does not handle the case where the node
-    // has attributes. But how often do people add attributes to <B> tags? 
-    // Not so often I think.
-    ASSERT(elem);
-    removeNodePreservingChildren(elem);
-}
-
-void ApplyStyleCommand::removeHTMLFontStyle(CSSMutableStyleDeclarationImpl *style, HTMLElementImpl *elem)
-{
-    ASSERT(style);
-    ASSERT(elem);
-
-    if (elem->id() != ID_FONT)
-        return;
-
-    int exceptionCode = 0;
-    QValueListConstIterator<CSSProperty> end;
-    for (QValueListConstIterator<CSSProperty> it = style->valuesIterator(); it != end; ++it) {
-        switch ((*it).id()) {
-            case CSS_PROP_COLOR:
-                elem->removeAttribute(ATTR_COLOR, exceptionCode);
-                ASSERT(exceptionCode == 0);
-                break;
-            case CSS_PROP_FONT_FAMILY:
-                elem->removeAttribute(ATTR_FACE, exceptionCode);
-                ASSERT(exceptionCode == 0);
-                break;
-            case CSS_PROP_FONT_SIZE:
-                elem->removeAttribute(ATTR_SIZE, exceptionCode);
-                ASSERT(exceptionCode == 0);
-                break;
-        }
-    }
-
-    if (isEmptyFontTag(elem))
-        removeNodePreservingChildren(elem);
-}
-
-void ApplyStyleCommand::removeCSSStyle(CSSMutableStyleDeclarationImpl *style, HTMLElementImpl *elem)
-{
-    ASSERT(style);
-    ASSERT(elem);
-
-    CSSMutableStyleDeclarationImpl *decl = elem->inlineStyleDecl();
-    if (!decl)
-        return;
-
-    QValueListConstIterator<CSSProperty> end;
-    for (QValueListConstIterator<CSSProperty> it = style->valuesIterator(); it != end; ++it) {
-        int propertyID = (*it).id();
-        CSSValueImpl *value = decl->getPropertyCSSValue(propertyID);
-        if (value) {
-            value->ref();
-            removeCSSProperty(decl, propertyID);
-            value->deref();
-        }
-    }
-
-    if (isEmptyStyleSpan(elem))
-        removeNodePreservingChildren(elem);
-}
-
-void ApplyStyleCommand::removeBlockStyle(CSSMutableStyleDeclarationImpl *style, const Position &start, const Position &end)
-{
-    ASSERT(start.isNotNull());
-    ASSERT(end.isNotNull());
-    ASSERT(start.node()->inDocument());
-    ASSERT(end.node()->inDocument());
-    ASSERT(RangeImpl::compareBoundaryPoints(start, end) <= 0);
-    
-}
-
-static bool hasTextDecorationProperty(NodeImpl *node)
-{
-    if (!node->isElementNode())
-        return false;
-
-    ElementImpl *element = static_cast<ElementImpl *>(node);
-    CSSComputedStyleDeclarationImpl style(element);
-
-    CSSValueImpl *value = style.getPropertyCSSValue(CSS_PROP_TEXT_DECORATION, DoNotUpdateLayout);
-
-    if (value) {
-        value->ref();
-        DOMString valueText(value->cssText());
-        value->deref();
-        if (strcasecmp(valueText,"none") != 0)
-            return true;
-    }
-
-    return false;
-}
-
-static NodeImpl* highestAncestorWithTextDecoration(NodeImpl *node)
-{
-    NodeImpl *result = NULL;
-
-    for (NodeImpl *n = node; n; n = n->parentNode()) {
-        if (hasTextDecorationProperty(n))
-            result = n;
-    }
-
-    return result;
-}
-
-CSSMutableStyleDeclarationImpl *ApplyStyleCommand::extractTextDecorationStyle(NodeImpl *node)
-{
-    ASSERT(node);
-    ASSERT(node->isElementNode());
-    
-    // non-html elements not handled yet
-    if (!node->isHTMLElement())
-        return 0;
-
-    HTMLElementImpl *element = static_cast<HTMLElementImpl *>(node);
-    CSSMutableStyleDeclarationImpl *style = element->inlineStyleDecl();
-    if (!style)
-        return 0;
-
-    style->ref();
-    int properties[1] = { CSS_PROP_TEXT_DECORATION };
-    CSSMutableStyleDeclarationImpl *textDecorationStyle = style->copyPropertiesInSet(properties, 1);
-
-    CSSValueImpl *property = style->getPropertyCSSValue(CSS_PROP_TEXT_DECORATION);
-    if (property && strcasecmp(property->cssText(), "none") != 0) {
-        removeCSSProperty(style, CSS_PROP_TEXT_DECORATION);
-    }
-
-    style->deref();
-
-    return textDecorationStyle;
-}
-
-CSSMutableStyleDeclarationImpl *ApplyStyleCommand::extractAndNegateTextDecorationStyle(NodeImpl *node)
-{
-    ASSERT(node);
-    ASSERT(node->isElementNode());
-    
-    // non-html elements not handled yet
-    if (!node->isHTMLElement())
-        return 0;
-
-    HTMLElementImpl *element = static_cast<HTMLElementImpl *>(node);
-    CSSComputedStyleDeclarationImpl *computedStyle = new CSSComputedStyleDeclarationImpl(element);
-    ASSERT(computedStyle);
-
-    computedStyle->ref();
-
-    int properties[1] = { CSS_PROP_TEXT_DECORATION };
-    CSSMutableStyleDeclarationImpl *textDecorationStyle = computedStyle->copyPropertiesInSet(properties, 1);
-    
-
-    CSSValueImpl *property = computedStyle->getPropertyCSSValue(CSS_PROP_TEXT_DECORATION);
-    if (property && strcasecmp(property->cssText(), "none") != 0) {
-        property->ref();
-        CSSMutableStyleDeclarationImpl *newStyle = textDecorationStyle->copy();
-
-        newStyle->ref();
-        newStyle->setProperty(CSS_PROP_TEXT_DECORATION, "none");
-        applyTextDecorationStyle(node, newStyle);
-        newStyle->deref();
-
-        property->deref();
-    }
-
-    computedStyle->deref();
-
-    return textDecorationStyle;
-}
-
-void ApplyStyleCommand::applyTextDecorationStyle(NodeImpl *node, CSSMutableStyleDeclarationImpl *style)
-{
-    ASSERT(node);
-
-    if (!style || !style->cssText().length())
-        return;
-
-    if (node->isTextNode()) {
-        HTMLElementImpl *styleSpan = static_cast<HTMLElementImpl *>(createStyleSpanElement(document()));
-        insertNodeBefore(styleSpan, node);
-        surroundNodeRangeWithElement(node, node, styleSpan);
-        node = styleSpan;
-    }
-
-    if (!node->isElementNode())
-        return;
-
-    HTMLElementImpl *element = static_cast<HTMLElementImpl *>(node);
-        
-    StyleChange styleChange(style, Position(element, 0), StyleChange::styleModeForParseMode(document()->inCompatMode()));
-    if (styleChange.cssStyle().length() > 0) {
-        DOMString cssText = styleChange.cssStyle();
-        CSSMutableStyleDeclarationImpl *decl = element->inlineStyleDecl();
-        if (decl)
-            cssText += decl->cssText();
-        setNodeAttribute(element, ATTR_STYLE, cssText);
-    }
-}
-
-void ApplyStyleCommand::pushDownTextDecorationStyleAroundNode(NodeImpl *node, const Position &start, const Position &end, bool force)
-{
-    NodeImpl *highestAncestor = highestAncestorWithTextDecoration(node);
-    
-    if (highestAncestor) {
-        NodeImpl *nextCurrent;
-        NodeImpl *nextChild;
-        for (NodeImpl *current = highestAncestor; current != node; current = nextCurrent) {
-            ASSERT(current);
-            
-            nextCurrent = NULL;
-            
-            CSSMutableStyleDeclarationImpl *decoration = force ? extractAndNegateTextDecorationStyle(current) : extractTextDecorationStyle(current);
-            if (decoration)
-                decoration->ref();
-
-            for (NodeImpl *child = current->firstChild(); child; child = nextChild) {
-                nextChild = child->nextSibling();
-
-                if (node == child) {
-                    nextCurrent = child;
-                } else if (node->isAncestor(child)) {
-                    applyTextDecorationStyle(child, decoration);
-                    nextCurrent = child;
-                } else {
-                    applyTextDecorationStyle(child, decoration);
-                }
-            }
-
-            if (decoration)
-                decoration->deref();
-        }
-    }
-}
-
-void ApplyStyleCommand::pushDownTextDecorationStyleAtBoundaries(const Position &start, const Position &end)
-{
-    // We need to work in two passes. First we push down any inline
-    // styles that set text decoration. Then we look for any remaining
-    // styles (caused by stylesheets) and explicitly negate text
-    // decoration while pushing down.
-
-    pushDownTextDecorationStyleAroundNode(start.node(), start, end, false);
-    document()->updateLayout();
-    pushDownTextDecorationStyleAroundNode(start.node(), start, end, true);
-
-    pushDownTextDecorationStyleAroundNode(end.node(), start, end, false);
-    document()->updateLayout();
-    pushDownTextDecorationStyleAroundNode(end.node(), start, end, true);
-}
-
-void ApplyStyleCommand::removeInlineStyle(CSSMutableStyleDeclarationImpl *style, const Position &start, const Position &end)
-{
-    ASSERT(start.isNotNull());
-    ASSERT(end.isNotNull());
-    ASSERT(start.node()->inDocument());
-    ASSERT(end.node()->inDocument());
-    ASSERT(RangeImpl::compareBoundaryPoints(start, end) < 0);
-    
-    CSSValueImpl *textDecorationSpecialProperty = style->getPropertyCSSValue(CSS_PROP__KHTML_TEXT_DECORATIONS_IN_EFFECT);
-
-    if (textDecorationSpecialProperty) {
-        pushDownTextDecorationStyleAtBoundaries(start.downstream(), end.upstream());
-        style = style->copy();
-        style->setProperty(CSS_PROP_TEXT_DECORATION, textDecorationSpecialProperty->cssText(), style->getPropertyPriority(CSS_PROP__KHTML_TEXT_DECORATIONS_IN_EFFECT));
-    }
-
-    // The s and e variables store the positions used to set the ending selection after style removal
-    // takes place. This will help callers to recognize when either the start node or the end node
-    // are removed from the document during the work of this function.
-    Position s = start;
-    Position e = end;
-
-    NodeImpl *node = start.node();
-    while (node) {
-        NodeImpl *next = node->traverseNextNode();
-        if (node->isHTMLElement() && nodeFullySelected(node, start, end)) {
-            HTMLElementImpl *elem = static_cast<HTMLElementImpl *>(node);
-            NodeImpl *prev = elem->traversePreviousNodePostOrder();
-            NodeImpl *next = elem->traverseNextNode();
-            if (isHTMLStyleNode(style, elem)) {
-                removeHTMLStyleNode(elem);
-            }
-            else {
-                removeHTMLFontStyle(style, elem);
-                removeCSSStyle(style, elem);
-            }
-            if (!elem->inDocument()) {
-                if (s.node() == elem) {
-                    // Since elem must have been fully selected, and it is at the start
-                    // of the selection, it is clear we can set the new s offset to 0.
-                    ASSERT(s.offset() <= s.node()->caretMinOffset());
-                    s = Position(next, 0);
-                }
-                if (e.node() == elem) {
-                    // Since elem must have been fully selected, and it is at the end
-                    // of the selection, it is clear we can set the new e offset to
-                    // the max range offset of prev.
-                    ASSERT(e.offset() >= maxRangeOffset(e.node()));
-                    e = Position(prev, maxRangeOffset(prev));
-                }
-            }
-        }
-        if (node == end.node())
-            break;
-        node = next;
-    }
-
-
-    if (textDecorationSpecialProperty) {
-        style->deref();
-    }
-    
-    ASSERT(s.node()->inDocument());
-    ASSERT(e.node()->inDocument());
-    setEndingSelection(Selection(s, VP_DEFAULT_AFFINITY, e, VP_DEFAULT_AFFINITY));
-}
-
-bool ApplyStyleCommand::nodeFullySelected(NodeImpl *node, const Position &start, const Position &end) const
+static int maxRangeOffset(NodeImpl *n)
 {
-    ASSERT(node);
-    ASSERT(node->isElementNode());
+    if (DOM::offsetInCharacters(n->nodeType()))
+        return n->maxOffset();
+
+    if (n->isElementNode())
+        return n->childNodeCount();
 
-    Position pos = Position(node, node->childNodeCount()).upstream();
-    return RangeImpl::compareBoundaryPoints(node, 0, start.node(), start.offset()) >= 0 &&
-        RangeImpl::compareBoundaryPoints(pos, end) <= 0;
+    return 1;
 }
 
-bool ApplyStyleCommand::nodeFullyUnselected(NodeImpl *node, const Position &start, const Position &end) const
+static int maxDeepOffset(NodeImpl *n)
 {
-    ASSERT(node);
-    ASSERT(node->isElementNode());
+    if (n->isAtomicNode())
+        return n->caretMaxOffset();
 
-    Position pos = Position(node, node->childNodeCount()).upstream();
-    bool isFullyBeforeStart = RangeImpl::compareBoundaryPoints(pos, start) < 0;
-    bool isFullyAfterEnd = RangeImpl::compareBoundaryPoints(node, 0, end.node(), end.offset()) > 0;
+    if (n->isElementNode())
+        return n->childNodeCount();
 
-    return isFullyBeforeStart || isFullyAfterEnd;
+    return 1;
 }
 
-
-//------------------------------------------------------------------------------------------
-// ApplyStyleCommand: style-application helpers
-
-bool ApplyStyleCommand::splitTextAtStartIfNeeded(const Position &start, const Position &end)
+static void debugPosition(const char *prefix, const Position &pos)
 {
-    if (start.node()->isTextNode() && start.offset() > start.node()->caretMinOffset() && start.offset() < start.node()->caretMaxOffset()) {
-        long endOffsetAdjustment = start.node() == end.node() ? start.offset() : 0;
-        TextImpl *text = static_cast<TextImpl *>(start.node());
-        splitTextNode(text, start.offset());
-        setEndingSelection(Selection(Position(start.node(), 0), SEL_DEFAULT_AFFINITY, Position(end.node(), end.offset() - endOffsetAdjustment), SEL_DEFAULT_AFFINITY));
-        return true;
-    }
-    return false;
+    if (!prefix)
+        prefix = "";
+    if (pos.isNull())
+        LOG(Editing, "%s <null>", prefix);
+    else
+        LOG(Editing, "%s%s %p : %d", prefix, pos.node()->nodeName().string().latin1(), pos.node(), pos.offset());
 }
 
-bool ApplyStyleCommand::splitTextAtEndIfNeeded(const Position &start, const Position &end)
+static void debugNode(const char *prefix, const NodeImpl *node)
 {
-    if (end.node()->isTextNode() && end.offset() > end.node()->caretMinOffset() && end.offset() < end.node()->caretMaxOffset()) {
-        TextImpl *text = static_cast<TextImpl *>(end.node());
-        splitTextNode(text, end.offset());
-        
-        NodeImpl *prevNode = text->previousSibling();
-        ASSERT(prevNode);
-        NodeImpl *startNode = start.node() == end.node() ? prevNode : start.node();
-        ASSERT(startNode);
-        setEndingSelection(Selection(Position(startNode, start.offset()), SEL_DEFAULT_AFFINITY, Position(prevNode, prevNode->caretMaxOffset()), SEL_DEFAULT_AFFINITY));
-        return true;
-    }
-    return false;
+    if (!prefix)
+        prefix = "";
+    if (!node)
+        LOG(Editing, "%s <null>", prefix);
+    else
+        LOG(Editing, "%s%s %p", prefix, node->nodeName().string().latin1(), node);
 }
 
-bool ApplyStyleCommand::splitTextElementAtStartIfNeeded(const Position &start, const Position &end)
+static bool isSpecialElement(NodeImpl *n)
 {
-    if (start.node()->isTextNode() && start.offset() > start.node()->caretMinOffset() && start.offset() < start.node()->caretMaxOffset()) {
-        long endOffsetAdjustment = start.node() == end.node() ? start.offset() : 0;
-        TextImpl *text = static_cast<TextImpl *>(start.node());
-        splitTextNodeContainingElement(text, start.offset());
+    if (!n->isHTMLElement())
+        return false;
 
-        setEndingSelection(Selection(Position(start.node()->parentNode(), start.node()->nodeIndex()), SEL_DEFAULT_AFFINITY, Position(end.node(), end.offset() - endOffsetAdjustment), SEL_DEFAULT_AFFINITY));
+    if (n->id() == ID_A && n->isLink())
         return true;
-    }
-    return false;
-}
 
-bool ApplyStyleCommand::splitTextElementAtEndIfNeeded(const Position &start, const Position &end)
-{
-    if (end.node()->isTextNode() && end.offset() > end.node()->caretMinOffset() && end.offset() < end.node()->caretMaxOffset()) {
-        TextImpl *text = static_cast<TextImpl *>(end.node());
-        splitTextNodeContainingElement(text, end.offset());
-
-        NodeImpl *prevNode = text->parent()->previousSibling()->lastChild();
-        ASSERT(prevNode);
-        NodeImpl *startNode = start.node() == end.node() ? prevNode : start.node();
-        ASSERT(startNode);
-        setEndingSelection(Selection(Position(startNode, start.offset()), SEL_DEFAULT_AFFINITY, Position(prevNode->parent(), prevNode->nodeIndex() + 1), SEL_DEFAULT_AFFINITY));
+    if (n->id() == ID_UL || n->id() == ID_OL || n->id() == ID_DL)
         return true;
-    }
-    return false;
-}
-
-static bool areIdenticalElements(NodeImpl *first, NodeImpl *second)
-{
-    // check that tag name and all attribute names and values are identical
 
-    if (!first->isElementNode())
-        return false;
-    
-    if (!second->isElementNode())
-        return false;
+    RenderObject *renderer = n->renderer();
 
-    ElementImpl *firstElement = static_cast<ElementImpl *>(first);
-    ElementImpl *secondElement = static_cast<ElementImpl *>(second);
-    
-    if (firstElement->id() != secondElement->id())
-        return false;
+    if (renderer && (renderer->style()->display() == TABLE || renderer->style()->display() == INLINE_TABLE))
+        return true;
 
-    NamedAttrMapImpl *firstMap = firstElement->attributes();
-    NamedAttrMapImpl *secondMap = secondElement->attributes();
+    if (renderer && renderer->style()->isFloating())
+        return true;
 
-    unsigned firstLength = firstMap->length();
+    if (renderer && renderer->style()->position() != STATIC)
+        return true;
 
-    if (firstLength != secondMap->length())
-        return false;
+    return false;
+}
 
-    for (unsigned i = 0; i < firstLength; i++) {
-        DOM::AttributeImpl *attribute = firstMap->attributeItem(i);
-        DOM::AttributeImpl *secondAttribute = secondMap->getAttributeItem(attribute->id());
+// This version of the function is meant to be called on positions in a document fragment,
+// so it does not check for a root editable element, it is assumed these nodes will be put
+// somewhere editable in the future
+static bool isFirstVisiblePositionInSpecialElementInFragment(const Position& pos)
+{
+    VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
 
-        if (!secondAttribute || attribute->value() != secondAttribute->value())
+    for (NodeImpl *n = pos.node(); n; n = n->parentNode()) {
+        if (VisiblePosition(n, 0, DOWNSTREAM) != vPos)
             return false;
+        if (isSpecialElement(n))
+            return true;
     }
-    
-    return true;
+
+    return false;
 }
 
-bool ApplyStyleCommand::mergeStartWithPreviousIfIdentical(const Position &start, const Position &end)
+static bool isFirstVisiblePositionInSpecialElement(const Position& pos)
 {
-    NodeImpl *startNode = start.node();
-    long startOffset = start.offset();
+    VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
 
-    if (start.node()->isAtomicNode()) {
-        if (start.offset() != 0)
+    for (NodeImpl *n = pos.node(); n; n = n->parentNode()) {
+        if (VisiblePosition(n, 0, DOWNSTREAM) != vPos)
             return false;
-
-        if (start.node()->previousSibling())
+        if (n->rootEditableElement() == NULL)
             return false;
-
-        startNode = start.node()->parent();
-        startOffset = 0;
-    }
-
-    if (!startNode->isElementNode())
-        return false;
-
-    if (startOffset != 0)
-        return false;
-
-    NodeImpl *previousSibling = startNode->previousSibling();
-
-    if (previousSibling && areIdenticalElements(startNode, previousSibling)) {
-        ElementImpl *previousElement = static_cast<ElementImpl *>(previousSibling);
-        ElementImpl *element = static_cast<ElementImpl *>(startNode);
-        NodeImpl *startChild = element->firstChild();
-        ASSERT(startChild);
-        mergeIdenticalElements(previousElement, element);
-
-        long startOffsetAdjustment = startChild->nodeIndex();
-        long endOffsetAdjustment = startNode == end.node() ? startOffsetAdjustment : 0;
-
-        setEndingSelection(Selection(Position(startNode, startOffsetAdjustment), SEL_DEFAULT_AFFINITY,
-                                     Position(end.node(), end.offset() + endOffsetAdjustment), SEL_DEFAULT_AFFINITY)); 
-
-        return true;
+        if (isSpecialElement(n))
+            return true;
     }
 
     return false;
 }
 
-bool ApplyStyleCommand::mergeEndWithNextIfIdentical(const Position &start, const Position &end)
+static Position positionBeforeNode(NodeImpl *node)
 {
-    NodeImpl *endNode = end.node();
-    int endOffset = end.offset();
+    return Position(node->parentNode(), node->nodeIndex());
+}
 
-    if (endNode->isAtomicNode()) {
-        if (endOffset < endNode->caretMaxOffset())
-            return false;
+static Position positionBeforeContainingSpecialElement(const Position& pos)
+{
+    ASSERT(isFirstVisiblePositionInSpecialElement(pos));
 
-        unsigned parentLastOffset = end.node()->parent()->childNodes()->length() - 1;
-        if (end.node()->nextSibling())
-            return false;
+    VisiblePosition vPos = VisiblePosition(pos, DOWNSTREAM);
+    
+    NodeImpl *outermostSpecialElement = NULL;
 
-        endNode = end.node()->parent();
-        endOffset = parentLastOffset;
+    for (NodeImpl *n = pos.node(); n; n = n->parentNode()) {
+        if (VisiblePosition(n, 0, DOWNSTREAM) != vPos)
+            break;
+        if (n->rootEditableElement() == NULL)
+            break;
+        if (isSpecialElement(n))
+            outermostSpecialElement = n;
     }
+    
+    ASSERT(outermostSpecialElement);
 
-    if (!endNode->isElementNode() || endNode->id() == ID_BR)
-        return false;
-
-    NodeImpl *nextSibling = endNode->nextSibling();
-
-    if (nextSibling && areIdenticalElements(endNode, nextSibling)) {
-        ElementImpl *nextElement = static_cast<ElementImpl *>(nextSibling);
-        ElementImpl *element = static_cast<ElementImpl *>(endNode);
-        NodeImpl *nextChild = nextElement->firstChild();
-
-        mergeIdenticalElements(element, nextElement);
+    Position result = positionBeforeNode(outermostSpecialElement);
+    if (result.isNull() || !result.node()->rootEditableElement())
+        return pos;
+    
+    return result;
+}
 
-        NodeImpl *startNode = start.node() == endNode ? nextElement : start.node();
-        ASSERT(startNode);
+static bool isLastVisiblePositionInSpecialElement(const Position& pos)
+{
+    // make sure to get a range-compliant version of the position
+    Position rangePos = VisiblePosition(pos, DOWNSTREAM).position();
 
-        int endOffset = nextChild ? nextChild->nodeIndex() : nextElement->childNodes()->length();
+    VisiblePosition vPos = VisiblePosition(rangePos, DOWNSTREAM);
 
-        setEndingSelection(Selection(Position(startNode, start.offset()), SEL_DEFAULT_AFFINITY, 
-                                     Position(nextElement, endOffset), SEL_DEFAULT_AFFINITY));
-        return true;
+    for (NodeImpl *n = rangePos.node(); n; n = n->parentNode()) {
+        if (VisiblePosition(n, maxRangeOffset(n), DOWNSTREAM) != vPos)
+            return false;
+        if (n->rootEditableElement() == NULL)
+            return false;
+        if (isSpecialElement(n))
+            return true;
     }
 
     return false;
 }
 
-void ApplyStyleCommand::cleanUpEmptyStyleSpans(const Position &start, const Position &end)
+static Position positionAfterNode(NodeImpl *node)
 {
-    NodeImpl *node;
-    for (node = start.node(); node && !node->previousSibling(); node = node->parentNode()) {
-    }
+    return Position(node->parentNode(), node->nodeIndex() + 1);
+}
 
-    if (node && isEmptyStyleSpan(node->previousSibling())) {
-        removeNodePreservingChildren(node->previousSibling());
-    }
+static Position positionAfterContainingSpecialElement(const Position& pos)
+{
+    ASSERT(isLastVisiblePositionInSpecialElement(pos));
 
-    if (start.node() == end.node()) {
-        if (start.node()->isTextNode()) {
-            for (NodeImpl *last = start.node(), *cur = last->parentNode(); cur && !last->previousSibling() && !last->nextSibling(); last = cur, cur = cur->parentNode()) {
-                if (isEmptyStyleSpan(cur)) {
-                    removeNodePreservingChildren(cur);
-                    break;
-                }
-            }
+    // make sure to get a range-compliant version of the position
+    Position rangePos = VisiblePosition(pos, DOWNSTREAM).position();
 
-        }
-    } else {
-        if (start.node()->isTextNode()) {
-            for (NodeImpl *last = start.node(), *cur = last->parentNode(); cur && !last->previousSibling(); last = cur, cur = cur->parentNode()) {
-                if (isEmptyStyleSpan(cur)) {
-                    removeNodePreservingChildren(cur);
-                    break;
-                }
-            }
-        }
+    VisiblePosition vPos = VisiblePosition(rangePos, DOWNSTREAM);
 
-        if (end.node()->isTextNode()) {
-            for (NodeImpl *last = end.node(), *cur = last->parentNode(); cur && !last->nextSibling(); last = cur, cur = cur->parentNode()) {
-                if (isEmptyStyleSpan(cur)) {
-                    removeNodePreservingChildren(cur);
-                    break;
-                }
-            }
-        }
-    }
-    
-    for (node = end.node(); node && !node->nextSibling(); node = node->parentNode()) {
-    }
-    if (node && isEmptyStyleSpan(node->nextSibling())) {
-        removeNodePreservingChildren(node->nextSibling());
-    }
-}
+    NodeImpl *outermostSpecialElement = NULL;
 
-void ApplyStyleCommand::surroundNodeRangeWithElement(NodeImpl *startNode, NodeImpl *endNode, ElementImpl *element)
-{
-    ASSERT(startNode);
-    ASSERT(endNode);
-    ASSERT(element);
-    
-    NodeImpl *node = startNode;
-    while (1) {
-        NodeImpl *next = node->traverseNextNode();
-        if (node->childNodeCount() == 0 && node->renderer() && node->renderer()->isInline()) {
-            removeNode(node);
-            appendNode(node, element);
-        }
-        if (node == endNode)
+    for (NodeImpl *n = rangePos.node(); n; n = n->parentNode()) {
+        if (VisiblePosition(n, maxRangeOffset(n), DOWNSTREAM) != vPos)
             break;
-        node = next;
+        if (n->rootEditableElement() == NULL)
+            break;
+        if (isSpecialElement(n))
+            outermostSpecialElement = n;
     }
-}
+    
+    ASSERT(outermostSpecialElement);
 
-void ApplyStyleCommand::addBlockStyleIfNeeded(CSSMutableStyleDeclarationImpl *style, NodeImpl *node)
-{
-    // Do not check for legacy styles here. Those styles, like <B> and <I>, only apply for
-    // inline content.
-    if (!node)
-        return;
+    Position result = positionAfterNode(outermostSpecialElement);
+    if (result.isNull() || !result.node()->rootEditableElement())
+        return pos;
     
-    HTMLElementImpl *block = static_cast<HTMLElementImpl *>(node->enclosingBlockFlowElement());
-    if (!block)
-        return;
-        
-    StyleChange styleChange(style, Position(block, 0), StyleChange::styleModeForParseMode(document()->inCompatMode()));
-    if (styleChange.cssStyle().length() > 0) {
-        moveParagraphContentsToNewBlockIfNecessary(Position(node, 0));
-        block = static_cast<HTMLElementImpl *>(node->enclosingBlockFlowElement());
-        DOMString cssText = styleChange.cssStyle();
-        CSSMutableStyleDeclarationImpl *decl = block->inlineStyleDecl();
-        if (decl)
-            cssText += decl->cssText();
-        setNodeAttribute(block, ATTR_STYLE, cssText);
-    }
+    return result;
 }
 
-void ApplyStyleCommand::addInlineStyleIfNeeded(CSSMutableStyleDeclarationImpl *style, NodeImpl *startNode, NodeImpl *endNode)
+static Position positionOutsideContainingSpecialElement(const Position &pos)
 {
-    StyleChange styleChange(style, Position(startNode, 0), StyleChange::styleModeForParseMode(document()->inCompatMode()));
-    int exceptionCode = 0;
-    
-    //
-    // Font tags need to go outside of CSS so that CSS font sizes override leagcy font sizes.
-    //
-    if (styleChange.applyFontColor() || styleChange.applyFontFace() || styleChange.applyFontSize()) {
-        ElementImpl *fontElement = createFontElement(document());
-        ASSERT(exceptionCode == 0);
-        insertNodeBefore(fontElement, startNode);
-        if (styleChange.applyFontColor())
-            fontElement->setAttribute(ATTR_COLOR, styleChange.fontColor());
-        if (styleChange.applyFontFace())
-            fontElement->setAttribute(ATTR_FACE, styleChange.fontFace());
-        if (styleChange.applyFontSize())
-            fontElement->setAttribute(ATTR_SIZE, styleChange.fontSize());
-        surroundNodeRangeWithElement(startNode, endNode, fontElement);
-    }
-
-    if (styleChange.cssStyle().length() > 0) {
-        ElementImpl *styleElement = createStyleSpanElement(document());
-        styleElement->ref();
-        styleElement->setAttribute(ATTR_STYLE, styleChange.cssStyle());
-        insertNodeBefore(styleElement, startNode);
-        styleElement->deref();
-        surroundNodeRangeWithElement(startNode, endNode, styleElement);
-    }
-
-    if (styleChange.applyBold()) {
-        ElementImpl *boldElement = document()->createHTMLElement("B", exceptionCode);
-        ASSERT(exceptionCode == 0);
-        insertNodeBefore(boldElement, startNode);
-        surroundNodeRangeWithElement(startNode, endNode, boldElement);
+    if (isFirstVisiblePositionInSpecialElement(pos)) {
+        return positionBeforeContainingSpecialElement(pos);
+    } else if (isLastVisiblePositionInSpecialElement(pos)) {
+        return positionAfterContainingSpecialElement(pos);
     }
 
-    if (styleChange.applyItalic()) {
-        ElementImpl *italicElement = document()->createHTMLElement("I", exceptionCode);
-        ASSERT(exceptionCode == 0);
-        insertNodeBefore(italicElement, startNode);
-        surroundNodeRangeWithElement(startNode, endNode, italicElement);
-    }
+    return pos;
 }
 
-float ApplyStyleCommand::computedFontSize(const NodeImpl *node)
+static Position positionBeforePossibleContainingSpecialElement(const Position &pos)
 {
-    float size = 0.0f;
-    
-    if (!node)
-        return size;
-    
-    Position pos(const_cast<NodeImpl *>(node), 0);
-    CSSComputedStyleDeclarationImpl *computedStyle = pos.computedStyle();
-    if (!computedStyle)
-        return size;
-    computedStyle->ref();
-
-    CSSPrimitiveValueImpl *value = static_cast<CSSPrimitiveValueImpl *>(computedStyle->getPropertyCSSValue(CSS_PROP_FONT_SIZE));
-    if (value) {
-        value->ref();
-        size = value->getFloatValue(CSSPrimitiveValue::CSS_PX);
-        value->deref();
-    }
+    if (isFirstVisiblePositionInSpecialElement(pos)) {
+        return positionBeforeContainingSpecialElement(pos);
+    } 
 
-    computedStyle->deref();
-    return size;
+    return pos;
 }
 
-void ApplyStyleCommand::joinChildTextNodes(NodeImpl *node, const Position &start, const Position &end)
+static Position positionAfterPossibleContainingSpecialElement(const Position &pos)
 {
-    if (!node)
-        return;
-
-    Position newStart = start;
-    Position newEnd = end;
-    
-    NodeImpl *child = node->firstChild();
-    while (child) {
-        NodeImpl *next = child->nextSibling();
-        if (child->isTextNode() && next && next->isTextNode()) {
-            TextImpl *childText = static_cast<TextImpl *>(child);
-            TextImpl *nextText = static_cast<TextImpl *>(next);
-            if (next == start.node())
-                newStart = Position(childText, childText->length() + start.offset());
-            if (next == end.node())
-                newEnd = Position(childText, childText->length() + end.offset());
-            DOMString textToMove = nextText->data();
-            insertTextIntoNode(childText, childText->length(), textToMove);
-            removeNode(next);
-            // don't move child node pointer. it may want to merge with more text nodes.
-        }
-        else {
-            child = child->nextSibling();
-        }
+    if (isLastVisiblePositionInSpecialElement(pos)) {
+        return positionAfterContainingSpecialElement(pos);
     }
 
-    setEndingSelection(Selection(newStart, SEL_DEFAULT_AFFINITY, newEnd, SEL_DEFAULT_AFFINITY));
+    return pos;
 }
 
 //------------------------------------------------------------------------------------------
@@ -5661,15 +3884,6 @@ bool TypingCommand::isTypingCommand() const
     return true;
 }
 
-ElementImpl *floatRefdElement(ElementImpl *element)
-{
-    assert(!element->parentNode());
-    element->setParent(element->getDocument());
-    element->deref();
-    element->setParent(0);
-    return element;
-}    
-
 ElementImpl *createDefaultParagraphElement(DocumentImpl *document)
 {
     // We would need this margin-zeroing code back if we ever return to using <p> elements for default paragraphs.
@@ -5680,16 +3894,6 @@ ElementImpl *createDefaultParagraphElement(DocumentImpl *document)
     return element;
 }
 
-ElementImpl *createBlockPlaceholderElement(DocumentImpl *document)
-{
-    int exceptionCode = 0;
-    ElementImpl *breakNode = document->createHTMLElement("br", exceptionCode);
-    ASSERT(exceptionCode == 0);
-    breakNode->ref();
-    breakNode->setAttribute(ATTR_CLASS, blockPlaceholderClassString());
-    return floatRefdElement(breakNode);
-}
-
 ElementImpl *createBreakElement(DocumentImpl *document)
 {
     int exceptionCode = 0;
@@ -5698,26 +3902,6 @@ ElementImpl *createBreakElement(DocumentImpl *document)
     return breakNode;
 }
 
-ElementImpl *createFontElement(DocumentImpl *document)
-{
-    int exceptionCode = 0;
-    ElementImpl *fontNode = document->createHTMLElement("font", exceptionCode);
-    ASSERT(exceptionCode == 0);
-    fontNode->ref();
-    fontNode->setAttribute(ATTR_CLASS, styleSpanClassString());
-    return floatRefdElement(fontNode);
-}
-
-ElementImpl *createStyleSpanElement(DocumentImpl *document)
-{
-    int exceptionCode = 0;
-    ElementImpl *styleElement = document->createHTMLElement("SPAN", exceptionCode);
-    ASSERT(exceptionCode == 0);
-    styleElement->ref();
-    styleElement->setAttribute(ATTR_CLASS, styleSpanClassString());
-    return floatRefdElement(styleElement);
-}
-
 bool isNodeRendered(const NodeImpl *node)
 {
     if (!node)