Reviewed by Kevin.
authormjs <mjs@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 12 May 2005 04:00:20 +0000 (04:00 +0000)
committermjs <mjs@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 12 May 2005 04:00:20 +0000 (04:00 +0000)
- split some more individual classes out of htmlediting.cpp
(CompositeEditcommand, AppendnodeCommand, ApplyStyleCommand)

Also moves StyleChange directly into ApplyStyleCommand
implementation file, it doesn't need to be in a header at all.

        * WebCore.pbproj/project.pbxproj:
        * khtml/editing/append_node_command.cpp: Added.
        * khtml/editing/append_node_command.h: Added.
        * khtml/editing/apply_style_command.cpp: Added.
        * khtml/editing/apply_style_command.h: Added.
        * khtml/editing/composite_edit_command.cpp: Added.
        * khtml/editing/composite_edit_command.h: Added.
        * khtml/editing/edit_command.cpp: Minor clean-ups.
        * khtml/editing/edit_command.h:
        * khtml/editing/htmlediting.cpp:
        * khtml/editing/htmlediting.h:

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

12 files changed:
WebCore/ChangeLog-2005-08-23
WebCore/WebCore.pbproj/project.pbxproj
WebCore/khtml/editing/append_node_command.cpp [new file with mode: 0644]
WebCore/khtml/editing/append_node_command.h [new file with mode: 0644]
WebCore/khtml/editing/apply_style_command.cpp [new file with mode: 0644]
WebCore/khtml/editing/apply_style_command.h [new file with mode: 0644]
WebCore/khtml/editing/composite_edit_command.cpp [new file with mode: 0644]
WebCore/khtml/editing/composite_edit_command.h [new file with mode: 0644]
WebCore/khtml/editing/edit_command.cpp
WebCore/khtml/editing/edit_command.h
WebCore/khtml/editing/htmlediting.cpp
WebCore/khtml/editing/htmlediting.h

index 4844d38f0057798ae7f33afbffa2182991e703a4..f8112686a70a0fc98d4a12aa51088bfaf3b781b4 100644 (file)
@@ -1,3 +1,25 @@
+2005-05-11  Maciej Stachowiak  <mjs@apple.com>
+
+        Reviewed by Kevin.
+
+       - split some more individual classes out of htmlediting.cpp
+       (CompositeEditcommand, AppendnodeCommand, ApplyStyleCommand)
+
+       Also moves StyleChange directly into ApplyStyleCommand
+       implementation file, it doesn't need to be in a header at all.
+       
+        * WebCore.pbproj/project.pbxproj:
+        * khtml/editing/append_node_command.cpp: Added.
+        * khtml/editing/append_node_command.h: Added.
+        * khtml/editing/apply_style_command.cpp: Added.
+        * khtml/editing/apply_style_command.h: Added.
+        * khtml/editing/composite_edit_command.cpp: Added.
+        * khtml/editing/composite_edit_command.h: Added.
+        * khtml/editing/edit_command.cpp: Minor clean-ups.
+        * khtml/editing/edit_command.h:
+        * khtml/editing/htmlediting.cpp:
+        * khtml/editing/htmlediting.h:
+
 2005-05-11  Adele Peterson  <adele@apple.com>
 
         Reviewed by Darin.
index 482f04bdbdaf7946532d996d0f5a5a04ab73effb..c427776d9b3e975c470742364c238d47c32283bc 100644 (file)
                        refType = 4;
                        sourceTree = "<group>";
                };
+               65AC799F0831ED6D009385CE = {
+                       fileEncoding = 30;
+                       isa = PBXFileReference;
+                       lastKnownFileType = sourcecode.cpp.cpp;
+                       name = append_node_command.cpp;
+                       path = editing/append_node_command.cpp;
+                       refType = 4;
+                       sourceTree = "<group>";
+               };
+               65AC79A00831ED6D009385CE = {
+                       fileEncoding = 30;
+                       isa = PBXFileReference;
+                       lastKnownFileType = sourcecode.c.h;
+                       name = append_node_command.h;
+                       path = editing/append_node_command.h;
+                       refType = 4;
+                       sourceTree = "<group>";
+               };
+               65AC79A10831ED6D009385CE = {
+                       fileRef = 65AC799F0831ED6D009385CE;
+                       isa = PBXBuildFile;
+                       settings = {
+                       };
+               };
+               65AC79A20831ED6D009385CE = {
+                       fileRef = 65AC79A00831ED6D009385CE;
+                       isa = PBXBuildFile;
+                       settings = {
+                       };
+               };
+               65AC79A70831F006009385CE = {
+                       fileEncoding = 30;
+                       isa = PBXFileReference;
+                       lastKnownFileType = sourcecode.cpp.cpp;
+                       name = apply_style_command.cpp;
+                       path = editing/apply_style_command.cpp;
+                       refType = 4;
+                       sourceTree = "<group>";
+               };
+               65AC79A80831F006009385CE = {
+                       fileEncoding = 30;
+                       isa = PBXFileReference;
+                       lastKnownFileType = sourcecode.c.h;
+                       name = apply_style_command.h;
+                       path = editing/apply_style_command.h;
+                       refType = 4;
+                       sourceTree = "<group>";
+               };
+               65AC79A90831F006009385CE = {
+                       fileRef = 65AC79A70831F006009385CE;
+                       isa = PBXBuildFile;
+                       settings = {
+                       };
+               };
+               65AC79AA0831F006009385CE = {
+                       fileRef = 65AC79A80831F006009385CE;
+                       isa = PBXBuildFile;
+                       settings = {
+                       };
+               };
+               65DC16C20831DD6F0022744E = {
+                       fileEncoding = 30;
+                       isa = PBXFileReference;
+                       lastKnownFileType = sourcecode.cpp.cpp;
+                       name = composite_edit_command.cpp;
+                       path = editing/composite_edit_command.cpp;
+                       refType = 4;
+                       sourceTree = "<group>";
+               };
+               65DC16C30831DD6F0022744E = {
+                       fileEncoding = 30;
+                       isa = PBXFileReference;
+                       lastKnownFileType = sourcecode.c.h;
+                       name = composite_edit_command.h;
+                       path = editing/composite_edit_command.h;
+                       refType = 4;
+                       sourceTree = "<group>";
+               };
+               65DC16C40831DD6F0022744E = {
+                       fileRef = 65DC16C20831DD6F0022744E;
+                       isa = PBXBuildFile;
+                       settings = {
+                       };
+               };
+               65DC16C50831DD6F0022744E = {
+                       fileRef = 65DC16C30831DD6F0022744E;
+                       isa = PBXBuildFile;
+                       settings = {
+                       };
+               };
                65F80697054D9F86008BF776 = {
                        fileEncoding = 30;
                        isa = PBXFileReference;
                                93F199FB08245E59001E9ABC,
                                93F199FC08245E59001E9ABC,
                                654D87B80831973B0082DCA1,
+                               65DC16C50831DD6F0022744E,
+                               65AC79A20831ED6D009385CE,
+                               65AC79AA0831F006009385CE,
                        );
                        isa = PBXHeadersBuildPhase;
                        runOnlyForDeploymentPostprocessing = 0;
                                93F19B1108245E59001E9ABC,
                                93276B4F0826F80F002E46CE,
                                654D87B70831973B0082DCA1,
+                               65DC16C40831DD6F0022744E,
+                               65AC79A10831ED6D009385CE,
+                               65AC79A90831F006009385CE,
                        );
                        isa = PBXSourcesBuildPhase;
                        runOnlyForDeploymentPostprocessing = 0;
                };
                BEB1DD0805C197DF00DD1F43 = {
                        children = (
+                               65AC79A00831ED6D009385CE,
+                               65AC799F0831ED6D009385CE,
+                               65AC79A80831F006009385CE,
+                               65AC79A70831F006009385CE,
+                               65DC16C30831DD6F0022744E,
+                               65DC16C20831DD6F0022744E,
                                EDA4AC97076FB89100DD23EC,
-                               654D87B50831973B0082DCA1,
                                654D87B60831973B0082DCA1,
+                               654D87B50831973B0082DCA1,
                                BE9185DD05EE59B80081354D,
                                BE9185E005EE59B80081354D,
                                BEA5DBDA075CEDA00098A432,
diff --git a/WebCore/khtml/editing/append_node_command.cpp b/WebCore/khtml/editing/append_node_command.cpp
new file mode 100644 (file)
index 0000000..3581c6c
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2005 Apple Computer, Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "append_node_command.h"
+
+#include "xml/dom_nodeimpl.h"
+
+#if APPLE_CHANGES
+#include "KWQAssertions.h"
+#else
+#define ASSERT(assertion) assert(assertion)
+#endif
+
+using DOM::DocumentImpl;
+using DOM::NodeImpl;
+
+namespace khtml {
+
+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);
+}
+
+} // namespace khtml
diff --git a/WebCore/khtml/editing/append_node_command.h b/WebCore/khtml/editing/append_node_command.h
new file mode 100644 (file)
index 0000000..bac2073
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2005 Apple Computer, Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef __append_node_command_h__
+#define __append_node_command_h__
+
+#include "edit_command.h"
+
+namespace DOM {
+    class NodeImpl;
+}
+
+namespace khtml {
+
+class AppendNodeCommand : public EditCommand
+{
+public:
+    AppendNodeCommand(DOM::DocumentImpl *, DOM::NodeImpl *appendChild, DOM::NodeImpl *parentNode);
+    virtual ~AppendNodeCommand();
+
+    virtual void doApply();
+    virtual void doUnapply();
+
+    DOM::NodeImpl *appendChild() const { return m_appendChild; }
+    DOM::NodeImpl *parentNode() const { return m_parentNode; }
+
+private:
+    DOM::NodeImpl *m_appendChild;
+    DOM::NodeImpl *m_parentNode;    
+};
+
+} // namespace khtml
+
+#endif // __append_node_command_h__
+
diff --git a/WebCore/khtml/editing/apply_style_command.cpp b/WebCore/khtml/editing/apply_style_command.cpp
new file mode 100644 (file)
index 0000000..71f0654
--- /dev/null
@@ -0,0 +1,1352 @@
+/*
+ * Copyright (C) 2005 Apple Computer, Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "apply_style_command.h"
+
+#include "html_interchange.h"
+
+#include "css/css_valueimpl.h"
+#include "css/css_computedstyle.h"
+#include "css/cssparser.h"
+#include "css/cssproperties.h"
+#include "dom/dom_string.h"
+#include "html/html_elementimpl.h"
+#include "misc/htmltags.h"
+#include "misc/htmlattrs.h"
+#include "rendering/render_object.h"
+#include "xml/dom_docimpl.h"
+#include "xml/dom_textimpl.h"
+#include "xml/dom2_rangeimpl.h"
+
+#if APPLE_CHANGES
+#include "KWQAssertions.h"
+#else
+#define ASSERT(assertion) assert(assertion)
+#endif
+
+using DOM::CSSComputedStyleDeclarationImpl;
+using DOM::CSSMutableStyleDeclarationImpl;
+using DOM::CSSParser;
+using DOM::CSSPrimitiveValue;
+using DOM::CSSPrimitiveValueImpl;
+using DOM::CSSProperty;
+using DOM::CSSStyleDeclarationImpl;
+using DOM::CSSValue;
+using DOM::CSSValueImpl;
+using DOM::DOMString;
+using DOM::DoNotUpdateLayout;
+using DOM::DocumentImpl;
+using DOM::ElementImpl;
+using DOM::HTMLElementImpl;
+using DOM::NamedAttrMapImpl;
+using DOM::NodeImpl;
+using DOM::Position;
+using DOM::RangeImpl;
+using DOM::TextImpl;
+
+namespace khtml {
+
+class StyleChange {
+public:
+    enum ELegacyHTMLStyles { DoNotUseLegacyHTMLStyles, UseLegacyHTMLStyles };
+
+    explicit StyleChange(CSSStyleDeclarationImpl *, ELegacyHTMLStyles usesLegacyStyles=UseLegacyHTMLStyles);
+    StyleChange(CSSStyleDeclarationImpl *, const Position &, ELegacyHTMLStyles usesLegacyStyles=UseLegacyHTMLStyles);
+
+    static ELegacyHTMLStyles styleModeForParseMode(bool);
+
+    DOMString cssStyle() const { return m_cssStyle; }
+    bool applyBold() const { return m_applyBold; }
+    bool applyItalic() const { return m_applyItalic; }
+    bool applyFontColor() const { return m_applyFontColor.length() > 0; }
+    bool applyFontFace() const { return m_applyFontFace.length() > 0; }
+    bool applyFontSize() const { return m_applyFontSize.length() > 0; }
+
+    DOMString fontColor() { return m_applyFontColor; }
+    DOMString fontFace() { return m_applyFontFace; }
+    DOMString fontSize() { return m_applyFontSize; }
+
+    bool usesLegacyStyles() const { return m_usesLegacyStyles; }
+
+private:
+    void init(CSSStyleDeclarationImpl *, const Position &);
+    bool checkForLegacyHTMLStyleChange(const CSSProperty *);
+    static bool currentlyHasStyle(const Position &, const CSSProperty *);
+    
+    DOMString m_cssStyle;
+    bool m_applyBold;
+    bool m_applyItalic;
+    DOMString m_applyFontColor;
+    DOMString m_applyFontFace;
+    DOMString m_applyFontSize;
+    bool m_usesLegacyStyles;
+};
+
+
+
+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;
+}
+
+static DOMString &styleSpanClassString()
+{
+    static DOMString styleSpanClassString = AppleStyleSpanClass;
+    return styleSpanClassString;
+}
+
+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 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 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 ElementImpl *createFontElement(DocumentImpl *document)
+{
+    int exceptionCode = 0;
+    ElementImpl *fontNode = document->createHTMLElement("font", exceptionCode);
+    ASSERT(exceptionCode == 0);
+    fontNode->setAttribute(ATTR_CLASS, styleSpanClassString());
+    return fontNode;
+}
+
+ElementImpl *createStyleSpanElement(DocumentImpl *document)
+{
+    int exceptionCode = 0;
+    ElementImpl *styleElement = document->createHTMLElement("SPAN", exceptionCode);
+    ASSERT(exceptionCode == 0);
+    styleElement->setAttribute(ATTR_CLASS, styleSpanClassString());
+    return styleElement;
+}
+
+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);
+}
+
+static int maxRangeOffset(NodeImpl *n)
+{
+    if (DOM::offsetInCharacters(n->nodeType()))
+        return n->maxOffset();
+
+    if (n->isElementNode())
+        return n->childNodeCount();
+
+    return 1;
+}
+
+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
+{
+    ASSERT(node);
+    ASSERT(node->isElementNode());
+
+    Position pos = Position(node, node->childNodeCount()).upstream();
+    return RangeImpl::compareBoundaryPoints(node, 0, start.node(), start.offset()) >= 0 &&
+        RangeImpl::compareBoundaryPoints(pos, end) <= 0;
+}
+
+bool ApplyStyleCommand::nodeFullyUnselected(NodeImpl *node, const Position &start, const Position &end) const
+{
+    ASSERT(node);
+    ASSERT(node->isElementNode());
+
+    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;
+
+    return isFullyBeforeStart || isFullyAfterEnd;
+}
+
+
+//------------------------------------------------------------------------------------------
+// ApplyStyleCommand: style-application helpers
+
+bool ApplyStyleCommand::splitTextAtStartIfNeeded(const Position &start, const Position &end)
+{
+    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;
+}
+
+bool ApplyStyleCommand::splitTextAtEndIfNeeded(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());
+        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;
+}
+
+bool ApplyStyleCommand::splitTextElementAtStartIfNeeded(const Position &start, const Position &end)
+{
+    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());
+
+        setEndingSelection(Selection(Position(start.node()->parentNode(), start.node()->nodeIndex()), SEL_DEFAULT_AFFINITY, Position(end.node(), end.offset() - endOffsetAdjustment), SEL_DEFAULT_AFFINITY));
+        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));
+        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;
+
+    ElementImpl *firstElement = static_cast<ElementImpl *>(first);
+    ElementImpl *secondElement = static_cast<ElementImpl *>(second);
+    
+    if (firstElement->id() != secondElement->id())
+        return false;
+
+    NamedAttrMapImpl *firstMap = firstElement->attributes();
+    NamedAttrMapImpl *secondMap = secondElement->attributes();
+
+    unsigned firstLength = firstMap->length();
+
+    if (firstLength != secondMap->length())
+        return false;
+
+    for (unsigned i = 0; i < firstLength; i++) {
+        DOM::AttributeImpl *attribute = firstMap->attributeItem(i);
+        DOM::AttributeImpl *secondAttribute = secondMap->getAttributeItem(attribute->id());
+
+        if (!secondAttribute || attribute->value() != secondAttribute->value())
+            return false;
+    }
+    
+    return true;
+}
+
+bool ApplyStyleCommand::mergeStartWithPreviousIfIdentical(const Position &start, const Position &end)
+{
+    NodeImpl *startNode = start.node();
+    long startOffset = start.offset();
+
+    if (start.node()->isAtomicNode()) {
+        if (start.offset() != 0)
+            return false;
+
+        if (start.node()->previousSibling())
+            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;
+    }
+
+    return false;
+}
+
+bool ApplyStyleCommand::mergeEndWithNextIfIdentical(const Position &start, const Position &end)
+{
+    NodeImpl *endNode = end.node();
+    int endOffset = end.offset();
+
+    if (endNode->isAtomicNode()) {
+        if (endOffset < endNode->caretMaxOffset())
+            return false;
+
+        unsigned parentLastOffset = end.node()->parent()->childNodes()->length() - 1;
+        if (end.node()->nextSibling())
+            return false;
+
+        endNode = end.node()->parent();
+        endOffset = parentLastOffset;
+    }
+
+    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);
+
+        NodeImpl *startNode = start.node() == endNode ? nextElement : start.node();
+        ASSERT(startNode);
+
+        int endOffset = nextChild ? nextChild->nodeIndex() : nextElement->childNodes()->length();
+
+        setEndingSelection(Selection(Position(startNode, start.offset()), SEL_DEFAULT_AFFINITY, 
+                                     Position(nextElement, endOffset), SEL_DEFAULT_AFFINITY));
+        return true;
+    }
+
+    return false;
+}
+
+void ApplyStyleCommand::cleanUpEmptyStyleSpans(const Position &start, const Position &end)
+{
+    NodeImpl *node;
+    for (node = start.node(); node && !node->previousSibling(); node = node->parentNode()) {
+    }
+
+    if (node && isEmptyStyleSpan(node->previousSibling())) {
+        removeNodePreservingChildren(node->previousSibling());
+    }
+
+    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;
+                }
+            }
+
+        }
+    } 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;
+                }
+            }
+        }
+
+        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());
+    }
+}
+
+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)
+            break;
+        node = next;
+    }
+}
+
+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;
+    
+    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);
+    }
+}
+
+void ApplyStyleCommand::addInlineStyleIfNeeded(CSSMutableStyleDeclarationImpl *style, NodeImpl *startNode, NodeImpl *endNode)
+{
+    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 (styleChange.applyItalic()) {
+        ElementImpl *italicElement = document()->createHTMLElement("I", exceptionCode);
+        ASSERT(exceptionCode == 0);
+        insertNodeBefore(italicElement, startNode);
+        surroundNodeRangeWithElement(startNode, endNode, italicElement);
+    }
+}
+
+float ApplyStyleCommand::computedFontSize(const NodeImpl *node)
+{
+    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();
+    }
+
+    computedStyle->deref();
+    return size;
+}
+
+void ApplyStyleCommand::joinChildTextNodes(NodeImpl *node, const Position &start, const Position &end)
+{
+    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();
+        }
+    }
+
+    setEndingSelection(Selection(newStart, SEL_DEFAULT_AFFINITY, newEnd, SEL_DEFAULT_AFFINITY));
+}
+
+} // namespace khtml
diff --git a/WebCore/khtml/editing/apply_style_command.h b/WebCore/khtml/editing/apply_style_command.h
new file mode 100644 (file)
index 0000000..b88ae98
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2005 Apple Computer, Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef __apply_style_command_h__
+#define __apply_style_command_h__
+
+#include "composite_edit_command.h"
+
+namespace DOM {
+    class HTMLElementImpl;
+}
+
+namespace khtml {
+
+class ApplyStyleCommand : public CompositeEditCommand
+{
+public:
+    enum EPropertyLevel { PropertyDefault, ForceBlockProperties };
+
+    ApplyStyleCommand(DOM::DocumentImpl *, DOM::CSSStyleDeclarationImpl *style, EditAction editingAction=EditActionChangeAttributes, EPropertyLevel=PropertyDefault);
+    virtual ~ApplyStyleCommand();
+       
+    virtual void doApply();
+    virtual EditAction editingAction() const;
+
+    DOM::CSSMutableStyleDeclarationImpl *style() const { return m_style; }
+
+private:
+    // style-removal helpers
+    bool isHTMLStyleNode(DOM::CSSMutableStyleDeclarationImpl *, DOM::HTMLElementImpl *);
+    void removeHTMLStyleNode(DOM::HTMLElementImpl *);
+    void removeHTMLFontStyle(DOM::CSSMutableStyleDeclarationImpl *, DOM::HTMLElementImpl *);
+    void removeCSSStyle(DOM::CSSMutableStyleDeclarationImpl *, DOM::HTMLElementImpl *);
+    void removeBlockStyle(DOM::CSSMutableStyleDeclarationImpl *, const DOM::Position &start, const DOM::Position &end);
+    void removeInlineStyle(DOM::CSSMutableStyleDeclarationImpl *, const DOM::Position &start, const DOM::Position &end);
+    bool nodeFullySelected(DOM::NodeImpl *, const DOM::Position &start, const DOM::Position &end) const;
+    bool nodeFullyUnselected(DOM::NodeImpl *node, const DOM::Position &start, const DOM::Position &end) const;
+    DOM::CSSMutableStyleDeclarationImpl *extractTextDecorationStyle(DOM::NodeImpl *node);
+    DOM::CSSMutableStyleDeclarationImpl *extractAndNegateTextDecorationStyle(DOM::NodeImpl *node);
+    void applyTextDecorationStyle(DOM::NodeImpl *node, DOM::CSSMutableStyleDeclarationImpl *style);
+    void pushDownTextDecorationStyleAroundNode(DOM::NodeImpl *node, const DOM::Position &start, const DOM::Position &end, bool force);
+    void pushDownTextDecorationStyleAtBoundaries(const DOM::Position &start, const DOM::Position &end);
+    
+    // style-application helpers
+    void applyBlockStyle(DOM::CSSMutableStyleDeclarationImpl *);
+    void applyRelativeFontStyleChange(DOM::CSSMutableStyleDeclarationImpl *);
+    void applyInlineStyle(DOM::CSSMutableStyleDeclarationImpl *);
+    void addBlockStyleIfNeeded(DOM::CSSMutableStyleDeclarationImpl *, DOM::NodeImpl *);
+    void addInlineStyleIfNeeded(DOM::CSSMutableStyleDeclarationImpl *, DOM::NodeImpl *start, DOM::NodeImpl *end);
+    bool splitTextAtStartIfNeeded(const DOM::Position &start, const DOM::Position &end);
+    bool splitTextAtEndIfNeeded(const DOM::Position &start, const DOM::Position &end);
+    bool splitTextElementAtStartIfNeeded(const DOM::Position &start, const DOM::Position &end);
+    bool splitTextElementAtEndIfNeeded(const DOM::Position &start, const DOM::Position &end);
+    bool mergeStartWithPreviousIfIdentical(const DOM::Position &start, const DOM::Position &end);
+    bool mergeEndWithNextIfIdentical(const DOM::Position &start, const DOM::Position &end);
+    void cleanUpEmptyStyleSpans(const DOM::Position &start, const DOM::Position &end);
+
+    void surroundNodeRangeWithElement(DOM::NodeImpl *start, DOM::NodeImpl *end, DOM::ElementImpl *element);
+    float computedFontSize(const DOM::NodeImpl *);
+    void joinChildTextNodes(DOM::NodeImpl *, const DOM::Position &start, const DOM::Position &end);
+    
+    DOM::CSSMutableStyleDeclarationImpl *m_style;
+    EditAction m_editingAction;
+    EPropertyLevel m_propertyLevel;
+};
+
+bool isStyleSpan(const DOM::NodeImpl *node);
+DOM::ElementImpl *createStyleSpanElement(DOM::DocumentImpl *document);
+
+} // namespace khtml
+
+#endif // __apply_style_command_h__
diff --git a/WebCore/khtml/editing/composite_edit_command.cpp b/WebCore/khtml/editing/composite_edit_command.cpp
new file mode 100644 (file)
index 0000000..28f74f8
--- /dev/null
@@ -0,0 +1,579 @@
+/*
+ * Copyright (C) 2005 Apple Computer, Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "composite_edit_command.h"
+
+#include "append_node_command.h"
+#include "htmlediting.h"
+#include "visible_units.h"
+
+#include "misc/htmlattrs.h"
+#include "misc/htmltags.h"
+#include "rendering/render_text.h"
+#include "xml/dom2_rangeimpl.h"
+#include "xml/dom_textimpl.h"
+
+#if APPLE_CHANGES
+#include "KWQAssertions.h"
+#else
+#define ASSERT(assertion) assert(assertion)
+#endif
+
+using DOM::CSSStyleDeclarationImpl;
+using DOM::DocumentImpl;
+using DOM::DOMString;
+using DOM::DOMStringImpl;
+using DOM::ElementImpl;
+using DOM::NodeImpl;
+using DOM::Position;
+using DOM::RangeImpl;
+using DOM::TextImpl;
+
+namespace khtml {
+
+static const DOMString &blockPlaceholderClassString();
+
+//------------------------------------------------------------------------------------------
+// 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;
+    }
+}
+
+ElementImpl *createBlockPlaceholderElement(DocumentImpl *document)
+{
+    int exceptionCode = 0;
+    ElementImpl *breakNode = document->createHTMLElement("br", exceptionCode);
+    ASSERT(exceptionCode == 0);
+    breakNode->setAttribute(ATTR_CLASS, blockPlaceholderClassString());
+    return breakNode;
+}
+
+static const DOMString &blockPlaceholderClassString()
+{
+    static DOMString blockPlaceholderClassString = "khtml-block-placeholder";
+    return blockPlaceholderClassString;
+}
+
+} // namespace khtml
diff --git a/WebCore/khtml/editing/composite_edit_command.h b/WebCore/khtml/editing/composite_edit_command.h
new file mode 100644 (file)
index 0000000..1af0e61
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2005 Apple Computer, Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef __composite_edit_command_h__
+#define __composite_edit_command_h__
+
+#include "edit_command.h"
+#include "qvaluelist.h"
+
+namespace DOM {
+    class CSSStyleDeclarationImpl;
+    class DOMString;
+    class TextImpl;
+}
+
+namespace khtml {
+
+class CompositeEditCommand : public EditCommand
+{
+public:
+    CompositeEditCommand(DOM::DocumentImpl *);
+       
+    virtual void doUnapply();
+    virtual void doReapply();
+
+protected:
+    //
+    // sugary-sweet convenience functions to help create and apply edit commands in composite commands
+    //
+    void appendNode(DOM::NodeImpl *appendChild, DOM::NodeImpl *parentNode);
+    void applyCommandToComposite(EditCommandPtr &);
+    void applyStyle(DOM::CSSStyleDeclarationImpl *style, EditAction editingAction=EditActionChangeAttributes);
+    void deleteKeyPressed();
+    void deleteSelection(bool smartDelete=false, bool mergeBlocksAfterDelete=true);
+    void deleteSelection(const Selection &selection, bool smartDelete=false, bool mergeBlocksAfterDelete=true);
+    void deleteTextFromNode(DOM::TextImpl *node, long offset, long count);
+    void inputText(const DOM::DOMString &text, bool selectInsertedText = false);
+    void insertNodeAfter(DOM::NodeImpl *insertChild, DOM::NodeImpl *refChild);
+    void insertNodeAt(DOM::NodeImpl *insertChild, DOM::NodeImpl *refChild, long offset);
+    void insertNodeBefore(DOM::NodeImpl *insertChild, DOM::NodeImpl *refChild);
+    void insertParagraphSeparator();
+    void insertTextIntoNode(DOM::TextImpl *node, long offset, const DOM::DOMString &text);
+    void joinTextNodes(DOM::TextImpl *text1, DOM::TextImpl *text2);
+    void rebalanceWhitespace();
+    void removeCSSProperty(DOM::CSSStyleDeclarationImpl *, int property);
+    void removeFullySelectedNode(DOM::NodeImpl *node);
+    void removeNodeAttribute(DOM::ElementImpl *, int attribute);
+    void removeChildrenInRange(DOM::NodeImpl *node, int from, int to);
+    void removeNode(DOM::NodeImpl *removeChild);
+    void removeNodePreservingChildren(DOM::NodeImpl *node);
+    void replaceTextInNode(DOM::TextImpl *node, long offset, long count, const DOM::DOMString &replacementText);
+    void setNodeAttribute(DOM::ElementImpl *, int attribute, const DOM::DOMString &);
+    void splitTextNode(DOM::TextImpl *text, long offset);
+    void splitElement(DOM::ElementImpl *element, DOM::NodeImpl *atChild);
+    void mergeIdenticalElements(DOM::ElementImpl *first, DOM::ElementImpl *second);
+    void wrapContentsInDummySpan(DOM::ElementImpl *element);
+    void splitTextNodeContainingElement(DOM::TextImpl *text, long offset);
+
+    void deleteInsignificantText(DOM::TextImpl *, int start, int end);
+    void deleteInsignificantText(const DOM::Position &start, const DOM::Position &end);
+    void deleteInsignificantTextDownstream(const DOM::Position &);
+
+    DOM::NodeImpl *appendBlockPlaceholder(DOM::NodeImpl *);
+    DOM::NodeImpl *insertBlockPlaceholder(const DOM::Position &pos);
+    DOM::NodeImpl *addBlockPlaceholderIfNeeded(DOM::NodeImpl *);
+    bool removeBlockPlaceholder(DOM::NodeImpl *);
+    DOM::NodeImpl *findBlockPlaceholder(DOM::NodeImpl *);
+
+    void moveParagraphContentsToNewBlockIfNecessary(const DOM::Position &);
+
+    QValueList<EditCommandPtr> m_cmds;
+};
+
+} // namespace khtml
+
+#endif // __composite_edit_command_h__
index cca3005e8c48983cbee8f5b57f5210039669b871..7796f7f7f4fe8331ed1182d40078a1042dbc0101 100644 (file)
@@ -24,6 +24,8 @@
  */
 
 #include "edit_command.h"
+#include "selection.h"
+#include "khtml_part.h"
 
 #include "xml/dom_position.h"
 #include "xml/dom_docimpl.h"
 
 #if APPLE_CHANGES
 #include "KWQAssertions.h"
-#include "KWQLogging.h"
-#include "KWQKHTMLPart.h"
 #else
-#define ASSERT(assertion) ((void)0)
-#define ASSERT_WITH_MESSAGE(assertion, formatAndArgs...) ((void)0)
-#define ASSERT_NOT_REACHED() ((void)0)
-#define LOG(channel, formatAndArgs...) ((void)0)
-#define ERROR(formatAndArgs...) ((void)0)
 #define ASSERT(assertion) assert(assertion)
-#if LOG_DISABLED
-#define debugPosition(a,b) ((void)0)
-#define debugNode(a,b) ((void)0)
-#endif
 #endif
 
 using DOM::DocumentImpl;
index d066f3b9eaad06f14b0d9f193c4d783058976115..7a00656a6ff199f2a367083c75de4e7e7462a810 100644 (file)
@@ -37,53 +37,6 @@ namespace DOM {
 
 namespace khtml {
 
-class EditCommand;
-//------------------------------------------------------------------------------------------
-// EditCommandPtr
-
-class EditCommandPtr : public SharedPtr<EditCommand>
-{
-public:
-    EditCommandPtr();
-    EditCommandPtr(EditCommand *);
-    EditCommandPtr(const EditCommandPtr &);
-    ~EditCommandPtr();
-
-    EditCommandPtr &operator=(const EditCommandPtr &);
-
-    bool isCompositeStep() const;
-
-    void apply() const;
-    void unapply() const;
-    void reapply() const;
-
-    EditAction editingAction() const;
-
-    DOM::DocumentImpl * const document() const;
-
-    Selection startingSelection() const;
-    Selection endingSelection() const;
-
-    void setStartingSelection(const Selection &s) const;
-    void setStartingSelection(const VisiblePosition &p) const;
-    void setStartingSelection(const DOM::Position &p, EAffinity affinity) const;
-    void setEndingSelection(const Selection &s) const;
-    void setEndingSelection(const VisiblePosition &p) const;
-    void setEndingSelection(const DOM::Position &p, EAffinity affinity) const;
-
-    DOM::CSSMutableStyleDeclarationImpl *typingStyle() const;
-    void setTypingStyle(DOM::CSSMutableStyleDeclarationImpl *) const;
-
-    EditCommandPtr parent() const;
-    void setParent(const EditCommandPtr &) const;
-
-    bool isInsertTextCommand() const;
-    bool isInsertLineBreakCommand() const;
-    bool isTypingCommand() const;
-
-    static EditCommandPtr &emptyCommand();
-};
-
 //------------------------------------------------------------------------------------------
 // EditCommand
 
@@ -147,6 +100,49 @@ private:
     EditCommand *m_parent;
 };
 
+class EditCommandPtr : public SharedPtr<EditCommand>
+{
+public:
+    EditCommandPtr();
+    EditCommandPtr(EditCommand *);
+    EditCommandPtr(const EditCommandPtr &);
+    ~EditCommandPtr();
+
+    EditCommandPtr &operator=(const EditCommandPtr &);
+
+    bool isCompositeStep() const;
+
+    void apply() const;
+    void unapply() const;
+    void reapply() const;
+
+    EditAction editingAction() const;
+
+    DOM::DocumentImpl * const document() const;
+
+    Selection startingSelection() const;
+    Selection endingSelection() const;
+
+    void setStartingSelection(const Selection &s) const;
+    void setStartingSelection(const VisiblePosition &p) const;
+    void setStartingSelection(const DOM::Position &p, EAffinity affinity) const;
+    void setEndingSelection(const Selection &s) const;
+    void setEndingSelection(const VisiblePosition &p) const;
+    void setEndingSelection(const DOM::Position &p, EAffinity affinity) const;
+
+    DOM::CSSMutableStyleDeclarationImpl *typingStyle() const;
+    void setTypingStyle(DOM::CSSMutableStyleDeclarationImpl *) const;
+
+    EditCommandPtr parent() const;
+    void setParent(const EditCommandPtr &) const;
+
+    bool isInsertTextCommand() const;
+    bool isInsertLineBreakCommand() const;
+    bool isTypingCommand() const;
+
+    static EditCommandPtr &emptyCommand();
+};
+
 } // namespace khtml
 
 #endif // __edit_command_h__
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)
index f0a6410127c1e7aceb5d4508bbf3607a1c4c8f70..e1b05df385808e242d7e6859a8156d0726cdb943 100644 (file)
@@ -27,6 +27,9 @@
 #define __htmlediting_h__
 
 #include "edit_command.h"
+#include "composite_edit_command.h"
+#include "apply_style_command.h"
+
 #include "dom_nodeimpl.h"
 #include "editing/edit_actions.h"
 #include "qmap.h"
@@ -46,185 +49,9 @@ namespace DOM {
 
 namespace khtml {
 
-class EditCommand;
 class Selection;
 class VisiblePosition;
 
-//------------------------------------------------------------------------------------------
-// StyleChange
-
-class StyleChange {
-public:
-    enum ELegacyHTMLStyles { DoNotUseLegacyHTMLStyles, UseLegacyHTMLStyles };
-
-    explicit StyleChange(DOM::CSSStyleDeclarationImpl *, ELegacyHTMLStyles usesLegacyStyles=UseLegacyHTMLStyles);
-    StyleChange(DOM::CSSStyleDeclarationImpl *, const DOM::Position &, ELegacyHTMLStyles usesLegacyStyles=UseLegacyHTMLStyles);
-
-    static ELegacyHTMLStyles styleModeForParseMode(bool);
-
-    DOM::DOMString cssStyle() const { return m_cssStyle; }
-    bool applyBold() const { return m_applyBold; }
-    bool applyItalic() const { return m_applyItalic; }
-    bool applyFontColor() const { return m_applyFontColor.length() > 0; }
-    bool applyFontFace() const { return m_applyFontFace.length() > 0; }
-    bool applyFontSize() const { return m_applyFontSize.length() > 0; }
-
-    DOM::DOMString fontColor() { return m_applyFontColor; }
-    DOM::DOMString fontFace() { return m_applyFontFace; }
-    DOM::DOMString fontSize() { return m_applyFontSize; }
-
-    bool usesLegacyStyles() const { return m_usesLegacyStyles; }
-
-private:
-    void init(DOM::CSSStyleDeclarationImpl *, const DOM::Position &);
-    bool checkForLegacyHTMLStyleChange(const DOM::CSSProperty *);
-    static bool currentlyHasStyle(const DOM::Position &, const DOM::CSSProperty *);
-    
-    DOM::DOMString m_cssStyle;
-    bool m_applyBold;
-    bool m_applyItalic;
-    DOM::DOMString m_applyFontColor;
-    DOM::DOMString m_applyFontFace;
-    DOM::DOMString m_applyFontSize;
-    bool m_usesLegacyStyles;
-};
-
-//------------------------------------------------------------------------------------------
-// CompositeEditCommand
-
-class CompositeEditCommand : public EditCommand
-{
-public:
-    CompositeEditCommand(DOM::DocumentImpl *);
-       
-    virtual void doUnapply();
-    virtual void doReapply();
-
-protected:
-    //
-    // sugary-sweet convenience functions to help create and apply edit commands in composite commands
-    //
-    void appendNode(DOM::NodeImpl *appendChild, DOM::NodeImpl *parentNode);
-    void applyCommandToComposite(EditCommandPtr &);
-    void applyStyle(DOM::CSSStyleDeclarationImpl *style, EditAction editingAction=EditActionChangeAttributes);
-    void deleteKeyPressed();
-    void deleteSelection(bool smartDelete=false, bool mergeBlocksAfterDelete=true);
-    void deleteSelection(const Selection &selection, bool smartDelete=false, bool mergeBlocksAfterDelete=true);
-    void deleteTextFromNode(DOM::TextImpl *node, long offset, long count);
-    void inputText(const DOM::DOMString &text, bool selectInsertedText = false);
-    void insertNodeAfter(DOM::NodeImpl *insertChild, DOM::NodeImpl *refChild);
-    void insertNodeAt(DOM::NodeImpl *insertChild, DOM::NodeImpl *refChild, long offset);
-    void insertNodeBefore(DOM::NodeImpl *insertChild, DOM::NodeImpl *refChild);
-    void insertParagraphSeparator();
-    void insertTextIntoNode(DOM::TextImpl *node, long offset, const DOM::DOMString &text);
-    void joinTextNodes(DOM::TextImpl *text1, DOM::TextImpl *text2);
-    void rebalanceWhitespace();
-    void removeCSSProperty(DOM::CSSStyleDeclarationImpl *, int property);
-    void removeFullySelectedNode(DOM::NodeImpl *node);
-    void removeNodeAttribute(DOM::ElementImpl *, int attribute);
-    void removeChildrenInRange(DOM::NodeImpl *node, int from, int to);
-    void removeNode(DOM::NodeImpl *removeChild);
-    void removeNodePreservingChildren(DOM::NodeImpl *node);
-    void replaceTextInNode(DOM::TextImpl *node, long offset, long count, const DOM::DOMString &replacementText);
-    void setNodeAttribute(DOM::ElementImpl *, int attribute, const DOM::DOMString &);
-    void splitTextNode(DOM::TextImpl *text, long offset);
-    void splitElement(DOM::ElementImpl *element, DOM::NodeImpl *atChild);
-    void mergeIdenticalElements(DOM::ElementImpl *first, DOM::ElementImpl *second);
-    void wrapContentsInDummySpan(DOM::ElementImpl *element);
-    void splitTextNodeContainingElement(DOM::TextImpl *text, long offset);
-
-    void deleteInsignificantText(DOM::TextImpl *, int start, int end);
-    void deleteInsignificantText(const DOM::Position &start, const DOM::Position &end);
-    void deleteInsignificantTextDownstream(const DOM::Position &);
-
-    DOM::NodeImpl *appendBlockPlaceholder(DOM::NodeImpl *);
-    DOM::NodeImpl *insertBlockPlaceholder(const DOM::Position &pos);
-    DOM::NodeImpl *addBlockPlaceholderIfNeeded(DOM::NodeImpl *);
-    bool removeBlockPlaceholder(DOM::NodeImpl *);
-    DOM::NodeImpl *findBlockPlaceholder(DOM::NodeImpl *);
-
-    void moveParagraphContentsToNewBlockIfNecessary(const DOM::Position &);
-
-    QValueList<EditCommandPtr> m_cmds;
-};
-
-//==========================================================================================
-// Concrete commands
-//------------------------------------------------------------------------------------------
-// AppendNodeCommand
-
-class AppendNodeCommand : public EditCommand
-{
-public:
-    AppendNodeCommand(DOM::DocumentImpl *, DOM::NodeImpl *appendChild, DOM::NodeImpl *parentNode);
-    virtual ~AppendNodeCommand();
-
-    virtual void doApply();
-    virtual void doUnapply();
-
-    DOM::NodeImpl *appendChild() const { return m_appendChild; }
-    DOM::NodeImpl *parentNode() const { return m_parentNode; }
-
-private:
-    DOM::NodeImpl *m_appendChild;
-    DOM::NodeImpl *m_parentNode;    
-};
-
-//------------------------------------------------------------------------------------------
-// ApplyStyleCommand
-
-class ApplyStyleCommand : public CompositeEditCommand
-{
-public:
-    enum EPropertyLevel { PropertyDefault, ForceBlockProperties };
-
-    ApplyStyleCommand(DOM::DocumentImpl *, DOM::CSSStyleDeclarationImpl *style, EditAction editingAction=EditActionChangeAttributes, EPropertyLevel=PropertyDefault);
-    virtual ~ApplyStyleCommand();
-       
-    virtual void doApply();
-    virtual EditAction editingAction() const;
-
-    DOM::CSSMutableStyleDeclarationImpl *style() const { return m_style; }
-
-private:
-    // style-removal helpers
-    bool isHTMLStyleNode(DOM::CSSMutableStyleDeclarationImpl *, DOM::HTMLElementImpl *);
-    void removeHTMLStyleNode(DOM::HTMLElementImpl *);
-    void removeHTMLFontStyle(DOM::CSSMutableStyleDeclarationImpl *, DOM::HTMLElementImpl *);
-    void removeCSSStyle(DOM::CSSMutableStyleDeclarationImpl *, DOM::HTMLElementImpl *);
-    void removeBlockStyle(DOM::CSSMutableStyleDeclarationImpl *, const DOM::Position &start, const DOM::Position &end);
-    void removeInlineStyle(DOM::CSSMutableStyleDeclarationImpl *, const DOM::Position &start, const DOM::Position &end);
-    bool nodeFullySelected(DOM::NodeImpl *, const DOM::Position &start, const DOM::Position &end) const;
-    bool nodeFullyUnselected(DOM::NodeImpl *node, const DOM::Position &start, const DOM::Position &end) const;
-    DOM::CSSMutableStyleDeclarationImpl *extractTextDecorationStyle(DOM::NodeImpl *node);
-    DOM::CSSMutableStyleDeclarationImpl *extractAndNegateTextDecorationStyle(DOM::NodeImpl *node);
-    void applyTextDecorationStyle(DOM::NodeImpl *node, DOM::CSSMutableStyleDeclarationImpl *style);
-    void pushDownTextDecorationStyleAroundNode(DOM::NodeImpl *node, const DOM::Position &start, const DOM::Position &end, bool force);
-    void pushDownTextDecorationStyleAtBoundaries(const DOM::Position &start, const DOM::Position &end);
-    
-    // style-application helpers
-    void applyBlockStyle(DOM::CSSMutableStyleDeclarationImpl *);
-    void applyRelativeFontStyleChange(DOM::CSSMutableStyleDeclarationImpl *);
-    void applyInlineStyle(DOM::CSSMutableStyleDeclarationImpl *);
-    void addBlockStyleIfNeeded(DOM::CSSMutableStyleDeclarationImpl *, DOM::NodeImpl *);
-    void addInlineStyleIfNeeded(DOM::CSSMutableStyleDeclarationImpl *, DOM::NodeImpl *start, DOM::NodeImpl *end);
-    bool splitTextAtStartIfNeeded(const DOM::Position &start, const DOM::Position &end);
-    bool splitTextAtEndIfNeeded(const DOM::Position &start, const DOM::Position &end);
-    bool splitTextElementAtStartIfNeeded(const DOM::Position &start, const DOM::Position &end);
-    bool splitTextElementAtEndIfNeeded(const DOM::Position &start, const DOM::Position &end);
-    bool mergeStartWithPreviousIfIdentical(const DOM::Position &start, const DOM::Position &end);
-    bool mergeEndWithNextIfIdentical(const DOM::Position &start, const DOM::Position &end);
-    void cleanUpEmptyStyleSpans(const DOM::Position &start, const DOM::Position &end);
-
-    void surroundNodeRangeWithElement(DOM::NodeImpl *start, DOM::NodeImpl *end, DOM::ElementImpl *element);
-    float computedFontSize(const DOM::NodeImpl *);
-    void joinChildTextNodes(DOM::NodeImpl *, const DOM::Position &start, const DOM::Position &end);
-    
-    DOM::CSSMutableStyleDeclarationImpl *m_style;
-    EditAction m_editingAction;
-    EPropertyLevel m_propertyLevel;
-};
-
 //------------------------------------------------------------------------------------------
 // DeleteFromTextNodeCommand
 
@@ -849,12 +676,8 @@ private:
 
 //------------------------------------------------------------------------------------------
 
-DOM::ElementImpl *floatRefdElement(DOM::ElementImpl *element);
 DOM::ElementImpl *createDefaultParagraphElement(DOM::DocumentImpl *document);
-DOM::ElementImpl *createBlockPlaceholderElement(DOM::DocumentImpl *document);
 DOM::ElementImpl *createBreakElement(DOM::DocumentImpl *document);
-DOM::ElementImpl *createFontElement(DOM::DocumentImpl *document);
-DOM::ElementImpl *createStyleSpanElement(DOM::DocumentImpl *document);
 
 bool isNodeRendered(const DOM::NodeImpl *);
 bool isProbablyBlock(const DOM::NodeImpl *);
@@ -865,6 +688,9 @@ bool isMailPasteAsQuotationNode(const DOM::NodeImpl *node);
 
 //------------------------------------------------------------------------------------------
 
+bool isTableStructureNode(const DOM::NodeImpl *node);
+DOM::ElementImpl *createBlockPlaceholderElement(DOM::DocumentImpl *document);
+
 } // end namespace khtml
 
 #endif