[SCM] WebKit Debian packaging branch, debian/unstable, updated. debian/1.1.15-1-40151-g37bb677

kocienda kocienda at 268f45cc-cd09-0410-ab3c-d52691b4dbfc
Sat Sep 26 08:31:42 UTC 2009


The following commit has been merged in the debian/unstable branch:
commit 616320e91ea7c4207e66faf1fe72697d6f44930c
Author: kocienda <kocienda at 268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Date:   Thu Apr 1 00:34:07 2004 +0000

            Reviewed by Dave.
    
            Many, many editing improvements, with a concentration on getting
            caret navigation and deleting selections working correctly.
    
            * WebCore.pbproj/project.pbxproj:
            * khtml/dom/dom_position.cpp: Removed.
            * khtml/dom/dom_position.h: Removed.
            * khtml/editing/htmlediting.cpp:
            (EditCommand::isNull): Inlined.
            (EditCommand::notNull): New function.
            (EditCommand::parent): Commands now have parents. Allows for walking the tree of composite commands.
            (EditCommand::setParent): Ditto.
            (EditCommand::emptyCommand): Returns a static empty command.
            (AppendNodeCommand::AppendNodeCommand):
            (AppendNodeCommand::parentNode): Member variable name change only. parent -> parentNode.
            (DeleteCollapsibleWhitespaceCommand::DeleteCollapsibleWhitespaceCommand): New command.
            (InputTextCommand::InputTextCommand):
            (InputTextCommand::input):
            (InputTextCommand::charactersAdded):
            (JoinTextNodesCommand::JoinTextNodesCommand): Now derives directly from EditCommand.
            (RemoveNodeAndPruneCommand::RemoveNodeAndPruneCommand): New command.
            (SplitTextNodeCommand::SplitTextNodeCommand): Now derives directly from EditCommand.
            * khtml/editing/htmlediting.h:
            * khtml/editing/htmlediting_impl.cpp:
            (isNBSP): New helper.
            (isWS): New helper.
            (shouldPruneNode): New helper.
            (leadingWhitespacePosition): New helper.
            (trailingWhitespacePosition): New helper.
            (textNodesAreJoinable): New helper.
            (nonBreakingSpaceString): Returns a static DOMString containing a non-breaking space.
            (EditCommandImpl::EditCommandImpl):
            (EditCommandImpl::setStartingSelection): Now recursively sets starting selection on parents.
            (EditCommandImpl::setEndingSelection): As above, for ending selection.
            (EditCommandImpl::parent): New accessor.
            (EditCommandImpl::setParent): New accessor.
            (CompositeEditCommandImpl::doUnapply): Removed some logging.
            (CompositeEditCommandImpl::doReapply): Removed some logging.
            (CompositeEditCommandImpl::applyCommandToComposite): Sets parent.
            (CompositeEditCommandImpl::removeNodeAndPrune): New comvenience.
            (CompositeEditCommandImpl::replaceText): New comvenience.
            (CompositeEditCommandImpl::deleteSelection): New comvenience.
            (CompositeEditCommandImpl::deleteCollapsibleWhitespace): New comvenience.
            (AppendNodeCommandImpl::AppendNodeCommandImpl): Member variable name change only. parent -> parentNode.
            (AppendNodeCommandImpl::~AppendNodeCommandImpl): Ditto.
            (AppendNodeCommandImpl::doApply): Ditto.
            (AppendNodeCommandImpl::doUnapply): Ditto.
            (DeleteCollapsibleWhitespaceCommandImpl::DeleteCollapsibleWhitespaceCommandImpl): New command
            (debugPosition): New debugging aid.
            (DeleteSelectionCommandImpl::doApply): Major reworking to handle more cases correctly.
            (InputNewlineCommandImpl::doApply): Position and selection tweaks.
            (InputTextCommandImpl::InputTextCommandImpl): Handles more cases now, like typing after an image.
            (JoinTextNodesCommandImpl::JoinTextNodesCommandImpl): Now derives directly from EditCommand.
            Implements the guts of the command itself now, rather than replying on its former base class.
            (RemoveNodeAndPruneCommandImpl::RemoveNodeAndPruneCommandImpl): New command.
            (SplitTextNodeCommandImpl::SplitTextNodeCommandImpl): Now derives directly from EditCommand.
            Implements the guts of the command itself now, rather than replying on its former base class.
            (TypingCommandImpl::TypingCommandImpl): Major rework to handle more cases correctly.
            * khtml/editing/htmlediting_impl.h:
            * khtml/html/html_elementimpl.cpp:
            (HTMLElementImpl::isContentEditable): More efficient use of the style system to answer the question.
            * khtml/khtml_part.cpp:
            (KHTMLPart::setSelection): Fixes an issue where the caret would not repaint after being moved when undoing.
            (KHTMLPart::takeSelectionFrom): Ditto.
            (KHTMLPart::clearSelection): Ditto.
            (KHTMLPart::invalidateSelection): Ditto.
            (KHTMLPart::setSelectionVisible): Ditto.
            (KHTMLPart::slotClearSelection): Ditto.
            (KHTMLPart::clearCaretRectIfNeeded):  Ditto.
            (KHTMLPart::notifySelectionChanged): Ditto.
            (KHTMLPart::unappliedEditing): Now uses EditCommand::emptyCommand().
            (KHTMLPart::reappliedEditing): Ditto.
            * khtml/khtml_part.h:
            * khtml/khtml_selection.cpp:
            (KHTMLSelection::KHTMLSelection):
            (KHTMLSelection::modify): Updated to work with new DOMPosition API.
            (KHTMLSelection::paintCaret): Ditto.
            (KHTMLSelection::moveToRenderedContent): Ditto.
            (KHTMLSelection::basePosition): New convenience.
            (KHTMLSelection::extentPosition): New convenience.
            (KHTMLSelection::startPosition): New convenience.
            (KHTMLSelection::endPosition): New convenience.
            (KHTMLSelection::debugPosition): Modified debug output.
            * khtml/khtml_selection.h:
            (KHTMLSelection::isEmpty):
            (KHTMLSelection::notEmpty):
            * khtml/rendering/bidi.cpp:
            (khtml::RenderBlock::layoutInlineChildren): Fixed a crasher that happened when deleting content at the start of a line.
            * khtml/rendering/render_br.cpp:
            (RenderBR::caretMaxRenderedOffset): New function.
            (RenderBR::caretPos): Now draws the caret in the right place when a block is empty.
            * khtml/rendering/render_br.h:
            * khtml/rendering/render_flow.cpp:
            (RenderFlow::caretPos): Now draws the caret in the right place when a flow is empty.
            * khtml/rendering/render_line.cpp:
            (InlineBox::caretMaxRenderedOffset): New function.
            * khtml/rendering/render_line.h:
            * khtml/rendering/render_object.cpp:
            (RenderObject::isEditable):
            (RenderObject::caretMaxRenderedOffset): New function.
            * khtml/rendering/render_object.h:
            * khtml/rendering/render_replaced.cpp:
            (RenderReplaced::caretMaxRenderedOffset): New function.
            * khtml/rendering/render_replaced.h:
            * khtml/rendering/render_text.cpp:
            (InlineTextBox::caretMaxRenderedOffset): New function.
            (RenderText::detach):
            (RenderText::caretMaxOffset):
            (RenderText::caretMaxRenderedOffset): New function.
            * khtml/rendering/render_text.h:
            (khtml::InlineTextBox::len):
            * khtml/xml/dom_edititerator.cpp: Added.
            * khtml/xml/dom_edititerator.h: Added.
            * khtml/xml/dom_nodeimpl.cpp:
            (NodeImpl::previousEditable): Improved the correctness of this function.
            (NodeImpl::nextEditable): Ditto.
            * khtml/xml/dom_nodeimpl.h:
            * khtml/xml/dom_position.cpp: Added.
            (DOMPosition::renderedOffset): New function.
            (DOMPosition::previousCharacterPosition): New function.
            (DOMPosition::nextCharacterPosition): New function.
            (DOMPosition::equivalentUpstreamPosition): New function.
            (DOMPosition::equivalentDownstreamPosition): New function.
            (DOMPosition::validUpstreamDownstreamPosition): New function.
            (DOMPosition::inRenderedContent): New function.
            (inlineBoxForRenderer): New function.
            (renderersOnDifferentLine): New function.
            (nextRenderedEditable): New function.
            (previousRenderedEditable): New function.
            (DOMPosition::inRenderedText): New function.
            (DOMPosition::rendersOnSameLine): New function.
            (DOMPosition::rendersInDifferentPosition): New function.
            (DOMPosition::isFirstRenderedPositionOnLine): New function.
            (DOMPosition::isLastRenderedPositionOnLine): New function.
            (DOMPosition::isLastRenderedPositionInEditableBlock): New function.
            (DOMPosition::inFirstEditableInRootEditableBlock): New function.
            (DOMPosition::inLastEditableInRootEditableBlock): New function.
            (DOMPosition::inFirstEditableInContainingEditableBlock): New function.
            (DOMPosition::inLastEditableInContainingEditableBlock): New function.
            * khtml/xml/dom_position.h: Added.
            (DOM::DOMPosition::notEmpty): New function.
            * khtml/xml/dom_stringimpl.cpp:
            (DOM::DOMStringImpl::containsOnlyWhitespace): Added a version which takes an offset and length.
            * khtml/xml/dom_stringimpl.h:
            * khtml/xml/dom_textimpl.cpp:
            (CharacterDataImpl::containsOnlyWhitespace): As above
            (CharacterDataImpl::maxOffset): New function.
            (CharacterDataImpl::caretMaxRenderedOffset): New function.
            (TextImpl::TextImpl):
            (TextImpl::rendererIsNeeded): A new bit. When set, makes a text renderer unconditionally.
            * khtml/xml/dom_textimpl.h:
            (DOM::TextImpl::setRendererIsNeeded): Sets the bit.
            * kwq/KWQAssertions.m:
            (KWQLog): Change to decrease the amount of output for the Editing log level. (my preference)
    
    
    git-svn-id: http://svn.webkit.org/repository/webkit/trunk@6289 268f45cc-cd09-0410-ab3c-d52691b4dbfc

diff --git a/WebCore/ChangeLog-2005-08-23 b/WebCore/ChangeLog-2005-08-23
index 2a663d4..ad2fe87 100644
--- a/WebCore/ChangeLog-2005-08-23
+++ b/WebCore/ChangeLog-2005-08-23
@@ -1,3 +1,161 @@
+2004-03-31  Ken Kocienda  <kocienda at apple.com>
+
+        Reviewed by Dave.
+        
+        Many, many editing improvements, with a concentration on getting
+        caret navigation and deleting selections working correctly.
+
+        * WebCore.pbproj/project.pbxproj:
+        * khtml/dom/dom_position.cpp: Removed.
+        * khtml/dom/dom_position.h: Removed.
+        * khtml/editing/htmlediting.cpp:
+        (EditCommand::isNull): Inlined.
+        (EditCommand::notNull): New function.
+        (EditCommand::parent): Commands now have parents. Allows for walking the tree of composite commands.
+        (EditCommand::setParent): Ditto.
+        (EditCommand::emptyCommand): Returns a static empty command.
+        (AppendNodeCommand::AppendNodeCommand): 
+        (AppendNodeCommand::parentNode): Member variable name change only. parent -> parentNode.
+        (DeleteCollapsibleWhitespaceCommand::DeleteCollapsibleWhitespaceCommand): New command.
+        (InputTextCommand::InputTextCommand):
+        (InputTextCommand::input):
+        (InputTextCommand::charactersAdded):
+        (JoinTextNodesCommand::JoinTextNodesCommand): Now derives directly from EditCommand.
+        (RemoveNodeAndPruneCommand::RemoveNodeAndPruneCommand): New command.
+        (SplitTextNodeCommand::SplitTextNodeCommand): Now derives directly from EditCommand.
+        * khtml/editing/htmlediting.h:
+        * khtml/editing/htmlediting_impl.cpp:
+        (isNBSP): New helper.
+        (isWS): New helper.
+        (shouldPruneNode): New helper.
+        (leadingWhitespacePosition): New helper.
+        (trailingWhitespacePosition): New helper.
+        (textNodesAreJoinable): New helper.
+        (nonBreakingSpaceString): Returns a static DOMString containing a non-breaking space.
+        (EditCommandImpl::EditCommandImpl):
+        (EditCommandImpl::setStartingSelection): Now recursively sets starting selection on parents.
+        (EditCommandImpl::setEndingSelection): As above, for ending selection.
+        (EditCommandImpl::parent): New accessor.
+        (EditCommandImpl::setParent): New accessor.
+        (CompositeEditCommandImpl::doUnapply): Removed some logging.
+        (CompositeEditCommandImpl::doReapply): Removed some logging.
+        (CompositeEditCommandImpl::applyCommandToComposite): Sets parent.
+        (CompositeEditCommandImpl::removeNodeAndPrune): New comvenience.
+        (CompositeEditCommandImpl::replaceText): New comvenience.
+        (CompositeEditCommandImpl::deleteSelection): New comvenience.
+        (CompositeEditCommandImpl::deleteCollapsibleWhitespace): New comvenience.
+        (AppendNodeCommandImpl::AppendNodeCommandImpl): Member variable name change only. parent -> parentNode.
+        (AppendNodeCommandImpl::~AppendNodeCommandImpl): Ditto.
+        (AppendNodeCommandImpl::doApply): Ditto.
+        (AppendNodeCommandImpl::doUnapply): Ditto.
+        (DeleteCollapsibleWhitespaceCommandImpl::DeleteCollapsibleWhitespaceCommandImpl): New command
+        (debugPosition): New debugging aid.
+        (DeleteSelectionCommandImpl::doApply): Major reworking to handle more cases correctly.
+        (InputNewlineCommandImpl::doApply): Position and selection tweaks.
+        (InputTextCommandImpl::InputTextCommandImpl): Handles more cases now, like typing after an image.
+        (JoinTextNodesCommandImpl::JoinTextNodesCommandImpl): Now derives directly from EditCommand.
+        Implements the guts of the command itself now, rather than replying on its former base class.
+        (RemoveNodeAndPruneCommandImpl::RemoveNodeAndPruneCommandImpl): New command.
+        (SplitTextNodeCommandImpl::SplitTextNodeCommandImpl): Now derives directly from EditCommand.
+        Implements the guts of the command itself now, rather than replying on its former base class.
+        (TypingCommandImpl::TypingCommandImpl): Major rework to handle more cases correctly.
+        * khtml/editing/htmlediting_impl.h:
+        * khtml/html/html_elementimpl.cpp:
+        (HTMLElementImpl::isContentEditable): More efficient use of the style system to answer the question.
+        * khtml/khtml_part.cpp:
+        (KHTMLPart::setSelection): Fixes an issue where the caret would not repaint after being moved when undoing.
+        (KHTMLPart::takeSelectionFrom): Ditto.
+        (KHTMLPart::clearSelection): Ditto.
+        (KHTMLPart::invalidateSelection): Ditto.
+        (KHTMLPart::setSelectionVisible): Ditto.
+        (KHTMLPart::slotClearSelection): Ditto.
+        (KHTMLPart::clearCaretRectIfNeeded):  Ditto.
+        (KHTMLPart::notifySelectionChanged): Ditto.
+        (KHTMLPart::unappliedEditing): Now uses EditCommand::emptyCommand().
+        (KHTMLPart::reappliedEditing): Ditto.
+        * khtml/khtml_part.h:
+        * khtml/khtml_selection.cpp:
+        (KHTMLSelection::KHTMLSelection):
+        (KHTMLSelection::modify): Updated to work with new DOMPosition API.
+        (KHTMLSelection::paintCaret): Ditto.
+        (KHTMLSelection::moveToRenderedContent): Ditto.
+        (KHTMLSelection::basePosition): New convenience.
+        (KHTMLSelection::extentPosition): New convenience.
+        (KHTMLSelection::startPosition): New convenience.
+        (KHTMLSelection::endPosition): New convenience.
+        (KHTMLSelection::debugPosition): Modified debug output.
+        * khtml/khtml_selection.h:
+        (KHTMLSelection::isEmpty):
+        (KHTMLSelection::notEmpty):
+        * khtml/rendering/bidi.cpp:
+        (khtml::RenderBlock::layoutInlineChildren): Fixed a crasher that happened when deleting content at the start of a line.
+        * khtml/rendering/render_br.cpp:
+        (RenderBR::caretMaxRenderedOffset): New function.
+        (RenderBR::caretPos): Now draws the caret in the right place when a block is empty.
+        * khtml/rendering/render_br.h:
+        * khtml/rendering/render_flow.cpp:
+        (RenderFlow::caretPos): Now draws the caret in the right place when a flow is empty.
+        * khtml/rendering/render_line.cpp:
+        (InlineBox::caretMaxRenderedOffset): New function.
+        * khtml/rendering/render_line.h:
+        * khtml/rendering/render_object.cpp:
+        (RenderObject::isEditable):
+        (RenderObject::caretMaxRenderedOffset): New function.
+        * khtml/rendering/render_object.h:
+        * khtml/rendering/render_replaced.cpp:
+        (RenderReplaced::caretMaxRenderedOffset): New function.
+        * khtml/rendering/render_replaced.h:
+        * khtml/rendering/render_text.cpp:
+        (InlineTextBox::caretMaxRenderedOffset): New function.
+        (RenderText::detach):
+        (RenderText::caretMaxOffset):
+        (RenderText::caretMaxRenderedOffset): New function.
+        * khtml/rendering/render_text.h:
+        (khtml::InlineTextBox::len):
+        * khtml/xml/dom_edititerator.cpp: Added.
+        * khtml/xml/dom_edititerator.h: Added.
+        * khtml/xml/dom_nodeimpl.cpp:
+        (NodeImpl::previousEditable): Improved the correctness of this function.
+        (NodeImpl::nextEditable): Ditto.
+        * khtml/xml/dom_nodeimpl.h:
+        * khtml/xml/dom_position.cpp: Added.
+        (DOMPosition::renderedOffset): New function.
+        (DOMPosition::previousCharacterPosition): New function.
+        (DOMPosition::nextCharacterPosition): New function.
+        (DOMPosition::equivalentUpstreamPosition): New function.
+        (DOMPosition::equivalentDownstreamPosition): New function.
+        (DOMPosition::validUpstreamDownstreamPosition): New function.
+        (DOMPosition::inRenderedContent): New function.
+        (inlineBoxForRenderer): New function.
+        (renderersOnDifferentLine): New function.
+        (nextRenderedEditable): New function.
+        (previousRenderedEditable): New function.
+        (DOMPosition::inRenderedText): New function.
+        (DOMPosition::rendersOnSameLine): New function.
+        (DOMPosition::rendersInDifferentPosition): New function.
+        (DOMPosition::isFirstRenderedPositionOnLine): New function.
+        (DOMPosition::isLastRenderedPositionOnLine): New function.
+        (DOMPosition::isLastRenderedPositionInEditableBlock): New function.
+        (DOMPosition::inFirstEditableInRootEditableBlock): New function.
+        (DOMPosition::inLastEditableInRootEditableBlock): New function.
+        (DOMPosition::inFirstEditableInContainingEditableBlock): New function.
+        (DOMPosition::inLastEditableInContainingEditableBlock): New function.
+        * khtml/xml/dom_position.h: Added.
+        (DOM::DOMPosition::notEmpty): New function.
+        * khtml/xml/dom_stringimpl.cpp:
+        (DOM::DOMStringImpl::containsOnlyWhitespace): Added a version which takes an offset and length.
+        * khtml/xml/dom_stringimpl.h:
+        * khtml/xml/dom_textimpl.cpp:
+        (CharacterDataImpl::containsOnlyWhitespace): As above
+        (CharacterDataImpl::maxOffset): New function.
+        (CharacterDataImpl::caretMaxRenderedOffset): New function.
+        (TextImpl::TextImpl):
+        (TextImpl::rendererIsNeeded): A new bit. When set, makes a text renderer unconditionally.
+        * khtml/xml/dom_textimpl.h:
+        (DOM::TextImpl::setRendererIsNeeded): Sets the bit.
+        * kwq/KWQAssertions.m:
+        (KWQLog): Change to decrease the amount of output for the Editing log level. (my preference)
+
 2004-03-31  David Hyatt  <hyatt at apple.com>
 
 	Fix for 3601834, make sure that textareas do a layout when their rows/cols/wrap attributes are dynamically
diff --git a/WebCore/ForwardingHeaders/xml/dom_edititerator.h b/WebCore/ForwardingHeaders/xml/dom_edititerator.h
new file mode 100644
index 0000000..3acedfc
--- /dev/null
+++ b/WebCore/ForwardingHeaders/xml/dom_edititerator.h
@@ -0,0 +1 @@
+#include <dom_edititerator.h>
diff --git a/WebCore/WebCore.pbproj/project.pbxproj b/WebCore/WebCore.pbproj/project.pbxproj
index 82e80bb..b94a0d5 100644
--- a/WebCore/WebCore.pbproj/project.pbxproj
+++ b/WebCore/WebCore.pbproj/project.pbxproj
@@ -510,7 +510,6 @@
 				BCBDB03A0597B36E00B83B92,
 				BCBDB096059A28B100B83B92,
 				BEB1DD3205C1980700DD1F43,
-				BEB1DD3E05C1982000DD1F43,
 				BC7FDE3405C1D9AB0070A902,
 				BC7E782205C5EB700088A50F,
 				BC3B364905C9D5E200E42902,
@@ -531,6 +530,8 @@
 				9321275B0606724900B62302,
 				9321275C0606724900B62302,
 				9321275D0606724900B62302,
+				BE91FC8D06133666005E3790,
+				BE91FC9206133697005E3790,
 			);
 			isa = PBXHeadersBuildPhase;
 			runOnlyForDeploymentPostprocessing = 0;
@@ -798,7 +799,6 @@
 				BCBDB0390597B36E00B83B92,
 				BCBDB095059A28B100B83B92,
 				BEB1DD3105C1980700DD1F43,
-				BEB1DD3D05C1982000DD1F43,
 				BC7FDE3305C1D9AB0070A902,
 				BC3B364805C9D5E200E42902,
 				BC433AD005D3046F003A5A14,
@@ -810,6 +810,8 @@
 				BE9CB65A05F9546800514D9C,
 				BEF7EEA305FF8F0D009717EE,
 				845563E606014A9800609194,
+				BE91FC8E06133666005E3790,
+				BE91FC9106133697005E3790,
 			);
 			isa = PBXSourcesBuildPhase;
 			runOnlyForDeploymentPostprocessing = 0;
@@ -2741,6 +2743,62 @@
 			settings = {
 			};
 		};
+		BE91FC8B06133666005E3790 = {
+			fileEncoding = 30;
+			isa = PBXFileReference;
+			lastKnownFileType = sourcecode.c.h;
+			path = dom_position.h;
+			refType = 4;
+			sourceTree = "<group>";
+		};
+		BE91FC8C06133666005E3790 = {
+			fileEncoding = 30;
+			isa = PBXFileReference;
+			lastKnownFileType = sourcecode.cpp.cpp;
+			path = dom_position.cpp;
+			refType = 4;
+			sourceTree = "<group>";
+		};
+		BE91FC8D06133666005E3790 = {
+			fileRef = BE91FC8B06133666005E3790;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		BE91FC8E06133666005E3790 = {
+			fileRef = BE91FC8C06133666005E3790;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		BE91FC8F06133697005E3790 = {
+			fileEncoding = 30;
+			isa = PBXFileReference;
+			lastKnownFileType = sourcecode.cpp.cpp;
+			path = dom_edititerator.cpp;
+			refType = 4;
+			sourceTree = "<group>";
+		};
+		BE91FC9006133697005E3790 = {
+			fileEncoding = 30;
+			isa = PBXFileReference;
+			lastKnownFileType = sourcecode.c.h;
+			path = dom_edititerator.h;
+			refType = 4;
+			sourceTree = "<group>";
+		};
+		BE91FC9106133697005E3790 = {
+			fileRef = BE91FC8F06133697005E3790;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
+		BE91FC9206133697005E3790 = {
+			fileRef = BE91FC9006133697005E3790;
+			isa = PBXBuildFile;
+			settings = {
+			};
+		};
 		BE94EB6305EFFE6B0032DCB5 = {
 			fileEncoding = 30;
 			isa = PBXFileReference;
@@ -2896,34 +2954,6 @@
 			settings = {
 			};
 		};
-		BEB1DD3B05C1982000DD1F43 = {
-			fileEncoding = 30;
-			isa = PBXFileReference;
-			lastKnownFileType = sourcecode.cpp.cpp;
-			path = dom_position.cpp;
-			refType = 4;
-			sourceTree = "<group>";
-		};
-		BEB1DD3C05C1982000DD1F43 = {
-			fileEncoding = 30;
-			isa = PBXFileReference;
-			lastKnownFileType = sourcecode.c.h;
-			path = dom_position.h;
-			refType = 4;
-			sourceTree = "<group>";
-		};
-		BEB1DD3D05C1982000DD1F43 = {
-			fileRef = BEB1DD3B05C1982000DD1F43;
-			isa = PBXBuildFile;
-			settings = {
-			};
-		};
-		BEB1DD3E05C1982000DD1F43 = {
-			fileRef = BEB1DD3C05C1982000DD1F43;
-			isa = PBXBuildFile;
-			settings = {
-			};
-		};
 		BEF7EEA005FF8F0D009717EE = {
 			fileEncoding = 30;
 			isa = PBXFileReference;
@@ -4222,8 +4252,6 @@
 				F523D19302DE4322018635CA,
 				F523D19402DE4322018635CA,
 				F523D19502DE4322018635CA,
-				BEB1DD3B05C1982000DD1F43,
-				BEB1DD3C05C1982000DD1F43,
 				F523D19602DE4322018635CA,
 				F523D19702DE4322018635CA,
 				F523D19802DE4322018635CA,
@@ -6293,11 +6321,15 @@
 				BC3B364705C9D5E200E42902,
 				F523D2F402DE4476018635CA,
 				F523D2F502DE4476018635CA,
+				BE91FC8F06133697005E3790,
+				BE91FC9006133697005E3790,
 				F523D2F702DE4476018635CA,
 				F523D2F802DE4476018635CA,
 				BC7E782005C5EB700088A50F,
 				F523D2F902DE4476018635CA,
 				F523D2FA02DE4476018635CA,
+				BE91FC8B06133666005E3790,
+				BE91FC8C06133666005E3790,
 				F523D2FB02DE4476018635CA,
 				F523D2FC02DE4476018635CA,
 				F523D2FD02DE4476018635CA,
diff --git a/WebCore/khtml/dom/dom_position.cpp b/WebCore/khtml/dom/dom_position.cpp
deleted file mode 100644
index b6b5e32..0000000
--- a/WebCore/khtml/dom/dom_position.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2003 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 "dom_position.h"
-#include "xml/dom_nodeimpl.h"
-
-using DOM::DOMPosition;
-
-DOMPosition::DOMPosition(NodeImpl *node, long offset) 
-    : m_node(0), m_offset(offset) 
-{ 
-    if (node) {
-        m_node = node;
-        m_node->ref();
-    }
-};
-
-DOMPosition::DOMPosition(const DOMPosition &o)
-    : m_node(0), m_offset(o.offset()) 
-{
-    if (o.node()) {
-        m_node = o.node();
-        m_node->ref();
-    }
-}
-
-DOMPosition::~DOMPosition() {
-    if (m_node) {
-        m_node->deref();
-    }
-}
-
-DOMPosition &DOMPosition::operator=(const DOMPosition &o)
-{
-    if (m_node) {
-        m_node->deref();
-    }
-    m_node = o.node();
-    if (m_node) {
-        m_node->ref();
-    }
-
-    m_offset = o.offset();
-    
-    return *this;
-}
diff --git a/WebCore/khtml/editing/SelectionController.cpp b/WebCore/khtml/editing/SelectionController.cpp
index e5d20f5..46cb6ed 100644
--- a/WebCore/khtml/editing/SelectionController.cpp
+++ b/WebCore/khtml/editing/SelectionController.cpp
@@ -25,6 +25,7 @@
   
 #include "khtml_selection.h"
 
+#include "htmltags.h"
 #include "khtml_part.h"
 #include "khtmlview.h"
 #include "qevent.h"
@@ -38,6 +39,7 @@
 #include "rendering/render_style.h"
 #include "rendering/render_text.h"
 #include "xml/dom_docimpl.h"
+#include "xml/dom_edititerator.h"
 #include "xml/dom_elementimpl.h"
 #include "xml/dom_nodeimpl.h"
 #include "xml/dom_textimpl.h"
@@ -52,6 +54,7 @@
 using DOM::DocumentImpl;
 using DOM::DOMPosition;
 using DOM::DOMString;
+using DOM::EditIterator;
 using DOM::ElementImpl;
 using DOM::Node;
 using DOM::NodeImpl;
@@ -86,7 +89,7 @@ KHTMLSelection::KHTMLSelection(NodeImpl *node, long offset)
     validate();
 }
 
-KHTMLSelection::KHTMLSelection(const DOM::DOMPosition &pos)
+KHTMLSelection::KHTMLSelection(const DOMPosition &pos)
 {
     init();
 
@@ -98,6 +101,18 @@ KHTMLSelection::KHTMLSelection(const DOM::DOMPosition &pos)
     validate();
 }
 
+KHTMLSelection::KHTMLSelection(const DOMPosition &base, const DOMPosition &extent)
+{
+    init();
+
+	setBaseNode(base.node());
+	setExtentNode(extent.node());
+	setBaseOffset(base.offset());
+	setExtentOffset(extent.offset());
+
+    validate();
+}
+
 KHTMLSelection::KHTMLSelection(NodeImpl *baseNode, long baseOffset, NodeImpl *endNode, long endOffset)
 {
     init();
@@ -253,14 +268,14 @@ bool KHTMLSelection::modify(EAlter alter, EDirection dir, ETextGranularity elem)
                             setExtentNode(endNode());
                             setExtentOffset(endOffset());
                         }
-                        pos = nextCharacterPosition(DOMPosition(extentNode(), extentOffset()));
+                        pos = extentPosition().nextCharacterPosition();
                     }
                     else {
                         m_modifyBiasSet = false;
                         if (state() == RANGE)
-                            pos = DOMPosition(endNode(), endOffset());
+                            pos = endPosition();
                         else
-                            pos = nextCharacterPosition();
+                            pos = endPosition().nextCharacterPosition();
                     }
                     break;
                 case WORD:
@@ -284,14 +299,14 @@ bool KHTMLSelection::modify(EAlter alter, EDirection dir, ETextGranularity elem)
                             setExtentNode(startNode());
                             setExtentOffset(startOffset());
                         }
-                        pos = previousCharacterPosition(DOMPosition(extentNode(), extentOffset()));
+                        pos = extentPosition().previousCharacterPosition();
                     }
                     else {
                         m_modifyBiasSet = false;
                         if (state() == RANGE)
-                            pos = DOMPosition(startNode(), startOffset());
+                            pos = startPosition();
                         else
-                            pos = previousCharacterPosition();
+                            pos = startPosition().previousCharacterPosition();
                     }
                     break;
                 case WORD:
@@ -348,11 +363,6 @@ void KHTMLSelection::setNeedsLayout(bool flag)
     m_needsCaretLayout = flag;
 }
 
-bool KHTMLSelection::isEmpty() const
-{
-    return m_baseNode == 0 && m_extentNode == 0;
-}
-
 Range KHTMLSelection::toRange() const
 {
     if (isEmpty())
@@ -423,7 +433,7 @@ void KHTMLSelection::paintCaret(QPainter *p, const QRect &rect)
 
     if (m_needsCaretLayout) {
         DOMPosition pos = DOMPosition(startNode(), startOffset());
-        if (!inRenderedContent(pos)) {
+        if (!pos.inRenderedContent()) {
             moveToRenderedContent();
         }
         layoutCaret();
@@ -656,171 +666,6 @@ void KHTMLSelection::validate(ETextGranularity expandTo)
 #endif
 }
 
-DOMPosition KHTMLSelection::previousCharacterPosition() const
-{
-    return previousCharacterPosition(DOMPosition(startNode(), startOffset()));
-}
-
-DOMPosition KHTMLSelection::previousCharacterPosition(const DOMPosition &from)
-{
-    if (!from.node())
-        return from;
-
-	NodeImpl *node = from.node();
-	long offset = from.offset() - 1;
-
-    //
-    // Look in this renderer
-    //
-    RenderObject *renderer = node->renderer();
-    if (renderer->isText()) {
-        if (!renderer->isBR()) {
-            RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
-            for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
-                long start = box->m_start;
-                long end = box->m_start + box->m_len;
-                if (offset > end) {
-                    // Skip this node.
-                    // It is too early in the text runs to be involved.
-                    continue;
-                }
-                else if (offset >= start) {
-                    // Offset is in this node, return the start
-                    return DOMPosition(node, offset);
-                }
-            }
-        }
-    }
-    else {
-        // Offset is in this node, if:
-        // 1. greater than the min offset and less than or equal to the max caret offset
-        // 2. at the start of the editable content of the document
-        if ((offset > renderer->caretMinOffset() && offset <= renderer->caretMaxOffset()) || 
-            (offset == renderer->caretMinOffset() && !renderer->previousEditable()))
-            return DOMPosition(node, offset);
-    }
-
-    //
-    // Look in previous renderer(s)
-    //
-    renderer = renderer->previousEditable();
-    while (renderer) {
-        // Offset is in this node, if:
-        // 1. it is a BR which follows a line break
-        // 2. it is an element with content
-    	if (renderer->isBR()) {
-			if (renderer->followsLineBreak())
-				return DOMPosition(renderer->element(), renderer->caretMinOffset());
-    	}
-    	else {
-    		if (renderer->isText()) {
-    			 RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
-    			 if (!textRenderer->lastTextBox()) 
-    			 	continue;
-    		}
-            offset = renderer->caretMaxOffset();
-            if (renderer->nextEditable() && !renderer->precedesLineBreak())
-                offset--;
-            assert(offset >= 0);
-            return DOMPosition(renderer->element(), offset);
-    	}
-        renderer = renderer->previousEditable();
-    }
-
-    // can't move the position
-    return from;
-}
-
-
-DOMPosition KHTMLSelection::nextCharacterPosition() const
-{
-    return nextCharacterPosition(DOMPosition(endNode(), endOffset()));
-}
-
-DOMPosition KHTMLSelection::nextCharacterPosition(const DOMPosition &from)
-{
-    if (!from.node())
-        return DOMPosition();
-
- 	NodeImpl *node = from.node();
- 	long offset = from.offset() + 1;
-
-    //
-    // Look in this renderer
-    //
-    RenderObject *renderer = node->renderer();
-    if (renderer->isText()) {
-        if (!renderer->isBR()) {
-            RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
-            for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
-                long start = box->m_start;
-                long end = box->m_start + box->m_len;
-                if (offset > end) {
-                    // Skip this node.
-                    // It is too early in the text runs to be involved.
-                    continue;
-                }
-                else if (offset >= start) {
-                    // Offset is in this node, if:
-                    // Either it is:
-                    // 1. at or after the start and before, but not at the end
-                    // 2. at the end of a text run and is immediately followed by a line break
-                    // 3. at the end of the editable content of the document
-                    if (offset < end)
-                        return DOMPosition(node, offset);
-                    else if (offset == end && renderer->precedesLineBreak())
-                        return DOMPosition(node, offset);
-                    else if (offset == end && !box->nextTextBox() && !renderer->nextEditable())
-                        return DOMPosition(node, offset);
-                }
-                else if (offset < start) {
-                    // The offset we're looking for is before this node
-                    // this means the offset must be in content that is
-                    // not rendered. Just return the start of the node.
-                    return DOMPosition(node, start);
-                }
-            }
-        }
-    }
-    else {
-        // Offset is in this node, if:
-        // 1. before the max caret offset
-        // 2. equal to the max caret offset and is immediately preceded by a line break
-        // 3. at the end of the editable content of the document
-        if (offset < renderer->caretMaxOffset() ||
-            (offset == renderer->caretMaxOffset() && renderer->precedesLineBreak()) ||
-            (offset == renderer->caretMaxOffset() && !renderer->nextEditable()))
-                return DOMPosition(node, offset);
-    }
-
-    //
-    // Look in next renderer(s)
-    //
-    renderer = renderer->nextEditable();
-    while (renderer) {
-		// Offset is in this node, if:
-		// 1. it is a BR which follows a line break
-		// 2. it is a text element with content
-        // 3. it is a non-text element with content
-		if (renderer->isBR()) {
-			if (renderer->followsLineBreak())
-				return DOMPosition(renderer->element(), renderer->caretMinOffset());
-		}
-		else if (renderer->isText()) {
-            RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
-            if (textRenderer->firstTextBox())
-                return DOMPosition(renderer->element(), textRenderer->firstTextBox()->m_start);
-        }
-        else {
-            return DOMPosition(renderer->element(), renderer->caretMinOffset());
-        }
-        renderer = renderer->nextEditable();
-    }
-
-    // can't move the position
-    return from;
-}
-
 bool KHTMLSelection::moveToRenderedContent()
 {
     if (isEmpty())
@@ -830,58 +675,46 @@ bool KHTMLSelection::moveToRenderedContent()
         return false;
 
     DOMPosition pos = DOMPosition(startNode(), startOffset());
-    if (inRenderedContent(pos))
+    if (pos.inRenderedContent())
         return true;
         
-    // not currently rendered, try moving to next
-    DOMPosition next = nextCharacterPosition(pos);
-    if (next != pos) {
-        moveTo(next);
+    // not currently rendered, try moving to prev
+    DOMPosition prev = pos.previousCharacterPosition();
+    if (prev != pos && prev.node()->inSameContainingEditableBlock(pos.node())) {
+        moveTo(prev);
         return true;
     }
 
-    // could not be moved to next, try prev
-    DOMPosition prev = previousCharacterPosition(pos);
-    if (prev != pos) {
-        moveTo(prev);
+    // could not be moved to prev, try next
+    DOMPosition next = pos.nextCharacterPosition();
+    if (next != pos && next.node()->inSameContainingEditableBlock(pos.node())) {
+        moveTo(next);
         return true;
     }
     
     return false;
 }
 
-bool KHTMLSelection::inRenderedContent(const DOMPosition &pos)
+DOMPosition KHTMLSelection::basePosition() const
 {
-    if (pos.isEmpty())
-        return false;
-        
- 	long offset = pos.offset();
+    return DOMPosition(baseNode(), baseOffset());
+}
 
-    RenderObject *renderer = pos.node()->renderer();
-    if (!renderer)
-        return false;
-    
-    if (renderer->isText() && !renderer->isBR()) {
-        RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
-        for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
-            if (offset >= box->m_start && offset <= box->m_start + box->m_len) {
-                return true;
-            }
-            else if (offset < box->m_start) {
-                // The offset we're looking for is before this node
-                // this means the offset must be in content that is
-                // not rendered. Return false.
-                return false;
-            }
-        }
-    }
-    else if (offset >= renderer->caretMinOffset() && offset <= renderer->caretMaxOffset()) {
-        return true;
-    }
-    
-    return false;
+DOMPosition KHTMLSelection::extentPosition() const
+{
+    return DOMPosition(extentNode(), extentOffset());
 }
- 
+
+DOMPosition KHTMLSelection::startPosition() const
+{
+    return DOMPosition(startNode(), startOffset());
+}
+
+DOMPosition KHTMLSelection::endPosition() const
+{
+    return DOMPosition(endNode(), endOffset());
+}
+
 bool KHTMLSelection::nodeIsBeforeNode(NodeImpl *n1, NodeImpl *n2) 
 {
 	if (!n1 || !n2) 
@@ -1164,12 +997,38 @@ void KHTMLSelection::debugPosition() const
     if (!startNode())
         return;
 
-    static int context = 5;
+    //static int context = 5;
     
-    RenderObject *r = 0;
+    //RenderObject *r = 0;
 
     fprintf(stderr, "KHTMLSelection =================\n");
-    
+
+    if (startPosition() == endPosition()) {
+        DOMPosition pos = startPosition();
+        DOMPosition upstream = pos.equivalentUpstreamPosition();
+        DOMPosition downstream = pos.equivalentDownstreamPosition();
+        fprintf(stderr, "upstream:   %s %p:%d\n", getTagName(upstream.node()->id()).string().latin1(), upstream.node(), upstream.offset());
+        fprintf(stderr, "pos:        %s %p:%d\n", getTagName(pos.node()->id()).string().latin1(), pos.node(), pos.offset());
+        fprintf(stderr, "downstream: %s %p:%d\n", getTagName(downstream.node()->id()).string().latin1(), downstream.node(), downstream.offset());
+    }
+    else {
+        DOMPosition pos = endPosition();
+        DOMPosition upstream = pos.equivalentUpstreamPosition();
+        DOMPosition downstream = pos.equivalentDownstreamPosition();
+        fprintf(stderr, "upstream:   %s %p:%d\n", getTagName(upstream.node()->id()).string().latin1(), upstream.node(), upstream.offset());
+        fprintf(stderr, "start:      %s %p:%d\n", getTagName(pos.node()->id()).string().latin1(), pos.node(), pos.offset());
+        fprintf(stderr, "downstream: %s %p:%d\n", getTagName(downstream.node()->id()).string().latin1(), downstream.node(), downstream.offset());
+        fprintf(stderr, "-----------------------------------\n");
+        pos = startPosition();
+        upstream = pos.equivalentUpstreamPosition();
+        downstream = pos.equivalentDownstreamPosition();
+        fprintf(stderr, "upstream:   %s %p:%d\n", getTagName(upstream.node()->id()).string().latin1(), upstream.node(), upstream.offset());
+        fprintf(stderr, "end:        %s %p:%d\n", getTagName(pos.node()->id()).string().latin1(), pos.node(), pos.offset());
+        fprintf(stderr, "downstream: %s %p:%d\n", getTagName(downstream.node()->id()).string().latin1(), downstream.node(), downstream.offset());
+        fprintf(stderr, "-----------------------------------\n");
+    }
+          
+#if 0
     int back = 0;
     r = startNode()->renderer();
     for (int i = 0; i < context; i++, back++) {
@@ -1203,6 +1062,7 @@ void KHTMLSelection::debugPosition() const
         else
             break;
     }
+#endif
 
     fprintf(stderr, "================================\n");
 }
diff --git a/WebCore/khtml/editing/SelectionController.h b/WebCore/khtml/editing/SelectionController.h
index 2864eae..a52f542 100644
--- a/WebCore/khtml/editing/SelectionController.h
+++ b/WebCore/khtml/editing/SelectionController.h
@@ -46,6 +46,7 @@ public:
     KHTMLSelection();
     KHTMLSelection(DOM::NodeImpl *node, long offset);
     KHTMLSelection(const DOM::DOMPosition &);
+    KHTMLSelection(const DOM::DOMPosition &, const DOM::DOMPosition &);
     KHTMLSelection(DOM::NodeImpl *startNode, long startOffset, DOM::NodeImpl *endNode, long endOffset);
     KHTMLSelection(const KHTMLSelection &);
     ~KHTMLSelection();
@@ -83,15 +84,16 @@ public:
     DOM::NodeImpl *endNode() const { return m_endNode; }
     long endOffset() const { return m_endOffset; }
 
-    DOM::DOMPosition previousCharacterPosition() const;
-    static DOM::DOMPosition previousCharacterPosition(const DOM::DOMPosition &from);
-    DOM::DOMPosition nextCharacterPosition() const;
-    static DOM::DOMPosition nextCharacterPosition(const DOM::DOMPosition &from);
-        
+    DOM::DOMPosition basePosition() const;
+    DOM::DOMPosition extentPosition() const;
+    DOM::DOMPosition startPosition() const;
+    DOM::DOMPosition endPosition() const;
+
     void setNeedsLayout(bool flag=true);
     void clearModifyBias() { m_modifyBiasSet = false; }
     
-    bool isEmpty() const;
+    bool isEmpty() const { return state() == NONE; }
+    bool notEmpty() const { return !isEmpty(); }
     DOM::Range toRange() const;
 
     
@@ -124,7 +126,6 @@ private:
 	void setEndNode(DOM::NodeImpl *);
 	void setEndOffset(long);
 
-    bool inRenderedContent(const DOM::DOMPosition &);
     bool nodeIsBeforeNode(DOM::NodeImpl *n1, DOM::NodeImpl *n2);
 
     void calculateStartAndEnd(ETextGranularity select=CHARACTER);
diff --git a/WebCore/khtml/editing/htmlediting.cpp b/WebCore/khtml/editing/htmlediting.cpp
index 6497337..5a6c182 100644
--- a/WebCore/khtml/editing/htmlediting.cpp
+++ b/WebCore/khtml/editing/htmlediting.cpp
@@ -48,8 +48,8 @@ using khtml::AppendNodeCommand;
 using khtml::AppendNodeCommandImpl;
 using khtml::CompositeEditCommand;
 using khtml::CompositeEditCommandImpl;
-using khtml::DeleteKeyCommand;
-using khtml::DeleteKeyCommandImpl;
+using khtml::DeleteCollapsibleWhitespaceCommand;
+using khtml::DeleteCollapsibleWhitespaceCommandImpl;
 using khtml::DeleteSelectionCommand;
 using khtml::DeleteSelectionCommandImpl;
 using khtml::DeleteTextCommand;
@@ -66,10 +66,10 @@ using khtml::InsertTextCommand;
 using khtml::InsertTextCommandImpl;
 using khtml::JoinTextNodesCommand;
 using khtml::JoinTextNodesCommandImpl;
-using khtml::ModifyTextNodeCommand;
-using khtml::ModifyTextNodeCommandImpl;
 using khtml::RemoveNodeCommand;
 using khtml::RemoveNodeCommandImpl;
+using khtml::RemoveNodeAndPruneCommand;
+using khtml::RemoveNodeAndPruneCommandImpl;
 using khtml::PasteHTMLCommand;
 using khtml::PasteHTMLCommandImpl;
 using khtml::PasteImageCommand;
@@ -88,11 +88,11 @@ using khtml::TypingCommandImpl;
 #endif
 
 #define IF_IMPL_NULL_RETURN_ARG(arg) do { \
-        if (isNull()) { LOG(Editing, "impl is null"); return arg; } \
+        if (isNull()) { return arg; } \
     } while (0)
         
 #define IF_IMPL_NULL_RETURN do { \
-        if (isNull()) { LOG(Editing, "impl is null"); return; } \
+        if (isNull()) { return; } \
     } while (0)
         
 //------------------------------------------------------------------------------------------
@@ -126,10 +126,14 @@ bool EditCommand::isCompositeStep() const
     return get()->isCompositeStep();
 }
 
-void EditCommand::setIsCompositeStep(bool flag)
+bool EditCommand::isNull() const
 {
-    IF_IMPL_NULL_RETURN;
-    get()->setIsCompositeStep(flag);
+    return get() == 0;
+}
+
+bool EditCommand::notNull() const
+{
+    return !isNull();
 }
 
 void EditCommand::apply()
@@ -198,11 +202,29 @@ void EditCommand::moveToEndingSelection()
     get()->moveToEndingSelection();
 }
 
+EditCommand EditCommand::parent() const
+{
+    IF_IMPL_NULL_RETURN_ARG(0);
+    return get()->parent();
+}
+
+void EditCommand::setParent(const EditCommand &cmd)
+{
+    IF_IMPL_NULL_RETURN;
+    get()->setParent(cmd);
+}
+
 EditCommandImpl *EditCommand::handle() const
 {
     return static_cast<EditCommandImpl *>(get());
 }
 
+EditCommand &EditCommand::emptyCommand()
+{
+    static EditCommand m_emptyCommand;
+    return m_emptyCommand;
+}
+
 //------------------------------------------------------------------------------------------
 // CompositeEditCommand
 
@@ -228,24 +250,13 @@ CompositeEditCommandImpl *CompositeEditCommand::impl() const
     return static_cast<CompositeEditCommandImpl *>(get());
 }
 
-//------------------------------------------------------------------------------------------
-// ModifyTextNodeCommand
-
-ModifyTextNodeCommand::ModifyTextNodeCommand(ModifyTextNodeCommandImpl *impl) : EditCommand(impl) 
-{
-}
-
-ModifyTextNodeCommand::~ModifyTextNodeCommand()
-{
-}
-
 //==========================================================================================
 // Concrete commands
 //------------------------------------------------------------------------------------------
 // AppendNodeCommand
 
-AppendNodeCommand::AppendNodeCommand(DocumentImpl *document, NodeImpl *parent, NodeImpl *appendChild)
-    : EditCommand(new AppendNodeCommandImpl(document, parent, appendChild))
+AppendNodeCommand::AppendNodeCommand(DocumentImpl *document, NodeImpl *parentNode, NodeImpl *appendChild)
+    : EditCommand(new AppendNodeCommandImpl(document, parentNode, appendChild))
 {
 }
 
@@ -258,10 +269,10 @@ AppendNodeCommandImpl *AppendNodeCommand::impl() const
     return static_cast<AppendNodeCommandImpl *>(get());
 }
 
-NodeImpl *AppendNodeCommand::parent() const
+NodeImpl *AppendNodeCommand::parentNode() const
 {
     IF_IMPL_NULL_RETURN_ARG(0);
-    return impl()->parent();
+    return impl()->parentNode();
 }
 
 NodeImpl *AppendNodeCommand::appendChild() const
@@ -271,30 +282,40 @@ NodeImpl *AppendNodeCommand::appendChild() const
 }
 
 //------------------------------------------------------------------------------------------
-// DeleteKeyCommand
+// DeleteCollapsibleWhitespaceCommand
 
-DeleteKeyCommand::DeleteKeyCommand(DocumentImpl *document) 
-    : CompositeEditCommand(new DeleteKeyCommandImpl(document))
+DeleteCollapsibleWhitespaceCommand::DeleteCollapsibleWhitespaceCommand(DocumentImpl *document)
+    : CompositeEditCommand(new DeleteCollapsibleWhitespaceCommandImpl(document))
 {
 }
 
-DeleteKeyCommand::~DeleteKeyCommand() 
+DeleteCollapsibleWhitespaceCommand::DeleteCollapsibleWhitespaceCommand(DocumentImpl *document, const KHTMLSelection &selection)
+    : CompositeEditCommand(new DeleteCollapsibleWhitespaceCommandImpl(document, selection))
 {
 }
 
-DeleteKeyCommandImpl *DeleteKeyCommand::impl() const
+DeleteCollapsibleWhitespaceCommand::~DeleteCollapsibleWhitespaceCommand()
+{
+}
+	
+DeleteCollapsibleWhitespaceCommandImpl *DeleteCollapsibleWhitespaceCommand::impl() const
 {
-    return static_cast<DeleteKeyCommandImpl *>(get());
+    return static_cast<DeleteCollapsibleWhitespaceCommandImpl *>(get());
 }
 
 //------------------------------------------------------------------------------------------
 // DeleteSelectionCommand
 
-DeleteSelectionCommand::DeleteSelectionCommand(DOM::DocumentImpl *document)
+DeleteSelectionCommand::DeleteSelectionCommand(DocumentImpl *document)
     : CompositeEditCommand(new DeleteSelectionCommandImpl(document))
 {
 }
 
+DeleteSelectionCommand::DeleteSelectionCommand(DocumentImpl *document, const KHTMLSelection &selection)
+    : CompositeEditCommand(new DeleteSelectionCommandImpl(document, selection))
+{
+}
+
 DeleteSelectionCommand::~DeleteSelectionCommand()
 {
 }
@@ -312,6 +333,11 @@ DeleteTextCommand::DeleteTextCommand(DocumentImpl *document, TextImpl *node, lon
 {
 }
 
+DeleteTextCommand::DeleteTextCommand(const DeleteTextCommand &o)
+    : EditCommand(o.impl())
+{
+}
+
 DeleteTextCommand::~DeleteTextCommand()
 {
 }
@@ -359,8 +385,8 @@ InputNewlineCommandImpl *InputNewlineCommand::impl() const
 //------------------------------------------------------------------------------------------
 // InputTextCommand
 
-InputTextCommand::InputTextCommand(DocumentImpl *document, const DOMString &text) 
-    : CompositeEditCommand(new InputTextCommandImpl(document, text))
+InputTextCommand::InputTextCommand(DocumentImpl *document) 
+    : CompositeEditCommand(new InputTextCommandImpl(document))
 {
 }
 
@@ -373,22 +399,22 @@ InputTextCommandImpl *InputTextCommand::impl() const
     return static_cast<InputTextCommandImpl *>(get());
 }
 
-DOMString InputTextCommand::text() const
-{
-    IF_IMPL_NULL_RETURN_ARG(DOMString());
-    return impl()->text();
-}
-
 void InputTextCommand::deleteCharacter()
 {
     IF_IMPL_NULL_RETURN;
     impl()->deleteCharacter();
 }
 
-void InputTextCommand::coalesce(const DOM::DOMString &text)
+void InputTextCommand::input(const DOM::DOMString &text)
 {
     IF_IMPL_NULL_RETURN;
-    impl()->coalesce(text);
+    impl()->input(text);
+}
+
+unsigned long InputTextCommand::charactersAdded() const
+{
+    IF_IMPL_NULL_RETURN_ARG(0);
+    return impl()->charactersAdded();
 }
 
 //------------------------------------------------------------------------------------------
@@ -468,7 +494,7 @@ DOMString InsertTextCommand::text() const
 // JoinTextNodesCommand
 
 JoinTextNodesCommand::JoinTextNodesCommand(DocumentImpl *document, TextImpl *text1, TextImpl *text2)
-    : ModifyTextNodeCommand(new JoinTextNodesCommandImpl(document, text1, text2))
+    : EditCommand(new JoinTextNodesCommandImpl(document, text1, text2))
 {
 }
 
@@ -557,10 +583,33 @@ NodeImpl *RemoveNodeCommand::node() const
 }
 
 //------------------------------------------------------------------------------------------
+// RemoveNodeAndPruneCommand
+
+RemoveNodeAndPruneCommand::RemoveNodeAndPruneCommand(DocumentImpl *document, NodeImpl *node)
+    : CompositeEditCommand(new RemoveNodeAndPruneCommandImpl(document, node))
+{
+}
+
+RemoveNodeAndPruneCommand::~RemoveNodeAndPruneCommand()
+{
+}
+
+RemoveNodeAndPruneCommandImpl *RemoveNodeAndPruneCommand::impl() const
+{
+    return static_cast<RemoveNodeAndPruneCommandImpl *>(get());
+}
+
+NodeImpl *RemoveNodeAndPruneCommand::node() const
+{
+    IF_IMPL_NULL_RETURN_ARG(0);
+    return impl()->node();
+}
+
+//------------------------------------------------------------------------------------------
 // SplitTextNodeCommand
 
 SplitTextNodeCommand::SplitTextNodeCommand(DocumentImpl *document, TextImpl *text, long offset)
-    : ModifyTextNodeCommand(new SplitTextNodeCommandImpl(document, text, offset))
+    : EditCommand(new SplitTextNodeCommandImpl(document, text, offset))
 {
 }
 
diff --git a/WebCore/khtml/editing/htmlediting.h b/WebCore/khtml/editing/htmlediting.h
index c39a07f..19cfb30 100644
--- a/WebCore/khtml/editing/htmlediting.h
+++ b/WebCore/khtml/editing/htmlediting.h
@@ -45,20 +45,20 @@ namespace khtml {
 
 class AppendNodeCommandImpl;
 class CompositeEditCommandImpl;
-class DeleteKeyCommandImpl;
+class DeleteCollapsibleWhitespaceCommandImpl;
 class DeleteSelectionCommandImpl;
 class DeleteTextCommandImpl;
+class EditCommand;
 class EditCommandImpl;
 class InputNewlineCommandImpl;
 class InputTextCommandImpl;
 class InsertNodeBeforeCommandImpl;
 class InsertTextCommandImpl;
 class JoinTextNodesCommandImpl;
-class ModifyTextNodeCommand;
-class ModifyTextNodeCommandImpl;
 class PasteHTMLCommandImpl;
 class PasteImageCommandImpl;
 class RemoveNodeCommandImpl;
+class RemoveNodeAndPruneCommandImpl;
 class SplitTextNodeCommandImpl;
 class TypingCommandImpl;
 
@@ -69,7 +69,7 @@ enum ECommandID {
     EditCommandID, // leave the base class first, others in alpha order
     AppendNodeCommandID,
     CompositeEditCommandID,
-    DeleteKeyCommandID,
+    DeleteCollapsibleWhitespaceCommandID,
     DeleteSelectionCommandID,
     DeleteTextCommandID,
     InputNewlineCommandID,
@@ -77,10 +77,10 @@ enum ECommandID {
     InsertNodeBeforeCommandID,
     InsertTextCommandID,
     JoinTextNodesCommandID,
-    ModifyTextNodeCommandID,
     PasteHTMLCommandID,
     PasteImageCommandID,
     RemoveNodeCommandID,
+    RemoveNodeAndPruneCommandID,
     SplitTextNodeCommandID,
     TypingCommandID,
 };
@@ -96,7 +96,6 @@ public:
 
     virtual int commandID() const = 0;
     virtual bool isCompositeStep() const = 0;
-    virtual void setIsCompositeStep(bool flag=true) = 0;
 
 	virtual void apply() = 0;	
 	virtual void unapply() = 0;
@@ -113,6 +112,9 @@ public:
 
     virtual void moveToStartingSelection() = 0;
     virtual void moveToEndingSelection() = 0;
+
+    virtual EditCommand parent() const = 0;
+    virtual void setParent(const EditCommand &) = 0;
 };
 
 //------------------------------------------------------------------------------------------
@@ -128,7 +130,8 @@ public:
 
     int commandID() const;
     bool isCompositeStep() const;
-    void setIsCompositeStep(bool flag=true);
+    bool isNull() const;
+    bool notNull() const;
 
 	void apply();	
 	void unapply();
@@ -146,7 +149,12 @@ public:
     void moveToStartingSelection();
     void moveToEndingSelection();
 
+    EditCommand parent() const;
+    void setParent(const EditCommand &);
+
     EditCommandImpl *handle() const;
+    
+    static EditCommand &emptyCommand();
 };
 
 //------------------------------------------------------------------------------------------
@@ -164,16 +172,6 @@ private:
     inline CompositeEditCommandImpl *impl() const;
 };
 
-//------------------------------------------------------------------------------------------
-// ModifyTextNodeCommand
-
-class ModifyTextNodeCommand : public EditCommand
-{
-public:
-    ModifyTextNodeCommand(ModifyTextNodeCommandImpl *);
-	virtual ~ModifyTextNodeCommand();
-};
-
 //==========================================================================================
 // Concrete commands
 //------------------------------------------------------------------------------------------
@@ -182,10 +180,10 @@ public:
 class AppendNodeCommand : public EditCommand
 {
 public:
-    AppendNodeCommand(DOM::DocumentImpl *, DOM::NodeImpl *parent, DOM::NodeImpl *appendChild);
+    AppendNodeCommand(DOM::DocumentImpl *, DOM::NodeImpl *parentNode, DOM::NodeImpl *appendChild);
 	virtual ~AppendNodeCommand();
 
-    DOM::NodeImpl *parent() const;
+    DOM::NodeImpl *parentNode() const;
     DOM::NodeImpl *appendChild() const;
     
 private:
@@ -193,16 +191,18 @@ private:
 };
 
 //------------------------------------------------------------------------------------------
-// DeleteKeyCommand
+// DeleteCollapsibleWhitespaceCommand
 
-class DeleteKeyCommand : public CompositeEditCommand
+class DeleteCollapsibleWhitespaceCommand : public CompositeEditCommand
 {
 public:
-    DeleteKeyCommand(DOM::DocumentImpl *document);
-    virtual ~DeleteKeyCommand();
+	DeleteCollapsibleWhitespaceCommand(DOM::DocumentImpl *document);
+	DeleteCollapsibleWhitespaceCommand(DOM::DocumentImpl *document, const KHTMLSelection &selection);
+    
+	virtual ~DeleteCollapsibleWhitespaceCommand();
 
 private:
-    inline DeleteKeyCommandImpl *impl() const;
+    inline DeleteCollapsibleWhitespaceCommandImpl *impl() const;
 };
 
 //------------------------------------------------------------------------------------------
@@ -212,6 +212,7 @@ class DeleteSelectionCommand : public CompositeEditCommand
 {
 public:
 	DeleteSelectionCommand(DOM::DocumentImpl *document);
+	DeleteSelectionCommand(DOM::DocumentImpl *document, const KHTMLSelection &selection);
 	virtual ~DeleteSelectionCommand();
 
 private:
@@ -225,6 +226,7 @@ class DeleteTextCommand : public EditCommand
 {
 public:
 	DeleteTextCommand(DOM::DocumentImpl *document, DOM::TextImpl *, long offset, long count);
+	DeleteTextCommand(const DeleteTextCommand &);
 	virtual ~DeleteTextCommand();
 
     DOM::TextImpl *node() const;
@@ -254,13 +256,13 @@ private:
 class InputTextCommand : public CompositeEditCommand
 {
 public:
-    InputTextCommand(DOM::DocumentImpl *document, const DOM::DOMString &text);
+    InputTextCommand(DOM::DocumentImpl *document);
     virtual ~InputTextCommand();
 
-    DOM::DOMString text() const;
-
     void deleteCharacter();
-    void coalesce(const DOM::DOMString &text);
+    void input(const DOM::DOMString &text);
+
+    unsigned long charactersAdded() const;
 
 private:
     inline InputTextCommandImpl *impl() const;
@@ -304,7 +306,7 @@ private:
 //------------------------------------------------------------------------------------------
 // JoinTextNodesCommand
 
-class JoinTextNodesCommand : public ModifyTextNodeCommand
+class JoinTextNodesCommand : public EditCommand
 {
 public:
 	JoinTextNodesCommand(DOM::DocumentImpl *, DOM::TextImpl *, DOM::TextImpl *);
@@ -361,9 +363,24 @@ private:
 };
 
 //------------------------------------------------------------------------------------------
+// RemoveNodeAndPruneCommand
+
+class RemoveNodeAndPruneCommand : public CompositeEditCommand
+{
+public:
+	RemoveNodeAndPruneCommand(DOM::DocumentImpl *, DOM::NodeImpl *);
+	virtual ~RemoveNodeAndPruneCommand();
+
+    DOM::NodeImpl *node() const;
+    
+private:
+    inline RemoveNodeAndPruneCommandImpl *impl() const;
+};
+
+//------------------------------------------------------------------------------------------
 // SplitTextNodeCommand
 
-class SplitTextNodeCommand : public ModifyTextNodeCommand
+class SplitTextNodeCommand : public EditCommand
 {
 public:
 	SplitTextNodeCommand(DOM::DocumentImpl *, DOM::TextImpl *, long);
diff --git a/WebCore/khtml/editing/htmlediting_impl.cpp b/WebCore/khtml/editing/htmlediting_impl.cpp
index 10c1e77..8883984 100644
--- a/WebCore/khtml/editing/htmlediting_impl.cpp
+++ b/WebCore/khtml/editing/htmlediting_impl.cpp
@@ -29,12 +29,16 @@
 #include "html/html_elementimpl.h"
 #include "html/html_imageimpl.h"
 #include "htmlattrs.h"
+#include "htmltags.h"
 #include "khtml_part.h"
 #include "khtml_selection.h"
 #include "khtmlview.h"
 #include "rendering/render_object.h"
+#include "rendering/render_style.h"
+#include "rendering/render_text.h"
 #include "xml/dom_docimpl.h"
 #include "xml/dom_elementimpl.h"
+#include "xml/dom_edititerator.h"
 #include "xml/dom_nodeimpl.h"
 #include "xml/dom_stringimpl.h"
 #include "xml/dom_textimpl.h"
@@ -50,6 +54,7 @@ using DOM::DocumentImpl;
 using DOM::DOMPosition;
 using DOM::DOMString;
 using DOM::DOMStringImpl;
+using DOM::EditIterator;
 using DOM::ElementImpl;
 using DOM::HTMLElementImpl;
 using DOM::HTMLImageElementImpl;
@@ -64,14 +69,15 @@ using khtml::AppendNodeCommand;
 using khtml::AppendNodeCommandImpl;
 using khtml::CompositeEditCommand;
 using khtml::CompositeEditCommandImpl;
-using khtml::DeleteKeyCommand;
-using khtml::DeleteKeyCommandImpl;
+using khtml::DeleteCollapsibleWhitespaceCommand;
+using khtml::DeleteCollapsibleWhitespaceCommandImpl;
 using khtml::DeleteSelectionCommand;
 using khtml::DeleteSelectionCommandImpl;
 using khtml::DeleteTextCommand;
 using khtml::DeleteTextCommandImpl;
 using khtml::EditCommand;
 using khtml::EditCommandImpl;
+using khtml::InlineTextBox;
 using khtml::InputNewlineCommand;
 using khtml::InputNewlineCommandImpl;
 using khtml::InputTextCommand;
@@ -82,10 +88,13 @@ using khtml::InsertTextCommand;
 using khtml::InsertTextCommandImpl;
 using khtml::JoinTextNodesCommand;
 using khtml::JoinTextNodesCommandImpl;
-using khtml::ModifyTextNodeCommand;
-using khtml::ModifyTextNodeCommandImpl;
 using khtml::RemoveNodeCommand;
 using khtml::RemoveNodeCommandImpl;
+using khtml::RemoveNodeAndPruneCommand;
+using khtml::RemoveNodeAndPruneCommandImpl;
+using khtml::RenderObject;
+using khtml::RenderStyle;
+using khtml::RenderText;
 using khtml::PasteHTMLCommand;
 using khtml::PasteHTMLCommandImpl;
 using khtml::PasteImageCommand;
@@ -101,13 +110,133 @@ using khtml::TypingCommandImpl;
 #define ASSERT_NOT_REACHED() ((void)0)
 #define LOG(channel, formatAndArgs...) ((void)0)
 #define ERROR(formatAndArgs...) ((void)0)
+#if LOG_DISABLED
+#define debugPosition(a,b) ((void)0)
 #endif
+#endif
+
+static inline bool isNBSP(const QChar &c)
+{
+    return c == QChar(0xa0);
+}
+
+static inline bool isWS(const QChar &c)
+{
+    return c.isSpace() && c != QChar(0xa0);
+}
+
+static inline bool isWS(const DOMString &text)
+{
+    if (text.length() != 1)
+        return false;
+    
+    return isWS(text[0]);
+}
+
+static inline bool isWS(const DOMPosition &pos)
+{
+    if (!pos.node())
+        return false;
+        
+    if (!pos.node()->isTextNode())
+        return false;
+
+    const DOMString &string = static_cast<TextImpl *>(pos.node())->data();
+    return isWS(string[pos.offset()]);
+}
+
+static bool shouldPruneNode(NodeImpl *node)
+{
+    if (!node)
+        return false;
+
+    RenderObject *renderer = node->renderer();
+    if (!renderer)
+        return true;
+
+    if (node->hasChildNodes())
+        return false;
+        
+    if (renderer->isBR() || renderer->isBlockFlow() || renderer->isReplaced())
+        return false;
+        
+    if (node->isTextNode()) {
+        TextImpl *text = static_cast<TextImpl *>(node);
+        if (text->length() == 0)
+            return true;
+        return false;
+    }
+    
+    if (!node->isHTMLElement() && !node->isXMLElementNode())
+        return false;
+    
+    if (node->id() == ID_BODY)
+        return false;
+            
+    if (!node->isContentEditable())
+        return false;
+            
+    return true;
+}
+
+static DOMPosition leadingWhitespacePosition(const DOMPosition &pos)
+{
+    ASSERT(pos.notEmpty());
+
+    KHTMLSelection selection(pos);
+    DOMPosition prev = pos.previousCharacterPosition();
+    if (prev != pos && prev.node()->inSameContainingEditableBlock(pos.node()) && prev.node()->isTextNode()) {
+        DOMString string = static_cast<TextImpl *>(prev.node())->data();
+        if (isWS(string[prev.offset()]))
+            return prev;
+    }
+
+    return DOMPosition();
+}
+
+static DOMPosition trailingWhitespacePosition(const DOMPosition &pos)
+{
+    ASSERT(pos.notEmpty());
+
+    if (pos.node()->isTextNode()) {
+        TextImpl *textNode = static_cast<TextImpl *>(pos.node());
+        if (pos.offset() >= (long)textNode->length()) {
+            DOMPosition next = pos.nextCharacterPosition();
+            if (next != pos && next.node()->inSameContainingEditableBlock(pos.node()) && next.node()->isTextNode()) {
+                DOMString string = static_cast<TextImpl *>(next.node())->data();
+                if (isWS(string[0]))
+                    return next;
+            }
+        }
+        else {
+            DOMString string = static_cast<TextImpl *>(pos.node())->data();
+            if (isWS(string[pos.offset()]))
+                return pos;
+        }
+    }
+
+    return DOMPosition();
+}
+
+static bool textNodesAreJoinable(TextImpl *text1, TextImpl *text2)
+{
+    ASSERT(text1);
+    ASSERT(text2);
+    
+    return (text1->nextSibling() == text2);
+}
+
+static DOMString &nonBreakingSpaceString()
+{
+    static DOMString nonBreakingSpaceString = QString(QChar(0xa0));
+    return nonBreakingSpaceString;
+}
 
 //------------------------------------------------------------------------------------------
 // EditCommandImpl
 
 EditCommandImpl::EditCommandImpl(DocumentImpl *document) 
-    : SharedCommandImpl(), m_document(document), m_state(NotApplied), m_isCompositeStep(false)
+    : SharedCommandImpl(), m_document(document), m_state(NotApplied), m_parent(0)
 {
     ASSERT(m_document);
     ASSERT(m_document->part());
@@ -190,12 +319,22 @@ KHTMLSelection EditCommandImpl::currentSelection() const
 void EditCommandImpl::setStartingSelection(const KHTMLSelection &s)
 {
     m_startingSelection = s;
+    EditCommand cmd = parent();
+    while (cmd.notNull()) {
+        cmd.setStartingSelection(s);
+        cmd = cmd.parent();
+    }
     moveToStartingSelection();
 }
 
 void EditCommandImpl::setEndingSelection(const KHTMLSelection &s)
 {
     m_endingSelection = s;
+    EditCommand cmd = parent();
+    while (cmd.notNull()) {
+        cmd.setEndingSelection(s);
+        cmd = cmd.parent();
+    }
     moveToEndingSelection();
 }
 
@@ -213,6 +352,16 @@ void EditCommandImpl::moveToEndingSelection()
     m_document->part()->takeSelectionFrom(this, true);
 }
 
+EditCommand EditCommandImpl::parent() const
+{
+    return m_parent;
+}
+
+void EditCommandImpl::setParent(const EditCommand &cmd)
+{
+    m_parent = cmd;
+}
+
 //------------------------------------------------------------------------------------------
 // CompositeEditCommandImpl
 
@@ -233,7 +382,6 @@ int CompositeEditCommandImpl::commandID() const
 void CompositeEditCommandImpl::doUnapply()
 {
     if (m_cmds.count() == 0) {
-        ERROR("Unapplying composite command containing zero steps");
         return;
     }
     
@@ -247,7 +395,6 @@ void CompositeEditCommandImpl::doUnapply()
 void CompositeEditCommandImpl::doReapply()
 {
     if (m_cmds.count() == 0) {
-        ERROR("Reapplying composite command containing zero steps");
         return;
     }
 
@@ -263,9 +410,8 @@ void CompositeEditCommandImpl::doReapply()
 //
 void CompositeEditCommandImpl::applyCommandToComposite(EditCommand &cmd)
 {
-    cmd.setIsCompositeStep();
+    cmd.setParent(this);
     cmd.apply();
-    setEndingSelection(cmd.endingSelection());
     m_cmds.append(cmd);
 }
 
@@ -312,6 +458,12 @@ void CompositeEditCommandImpl::removeNode(DOM::NodeImpl *removeChild)
     applyCommandToComposite(cmd);
 }
 
+void CompositeEditCommandImpl::removeNodeAndPrune(DOM::NodeImpl *removeChild)
+{
+    RemoveNodeAndPruneCommand cmd(document(), removeChild);
+    applyCommandToComposite(cmd);
+}
+
 void CompositeEditCommandImpl::splitTextNode(DOM::TextImpl *text, long offset)
 {
     SplitTextNodeCommand cmd(document(), text, offset);
@@ -336,6 +488,14 @@ void CompositeEditCommandImpl::deleteText(DOM::TextImpl *node, long offset, long
     applyCommandToComposite(cmd);
 }
 
+void CompositeEditCommandImpl::replaceText(DOM::TextImpl *node, long offset, long count, const DOM::DOMString &replacementText)
+{
+    DeleteTextCommand deleteCommand(document(), node, offset, count);
+    applyCommandToComposite(deleteCommand);
+    InsertTextCommand insertCommand(document(), node, offset, replacementText);
+    applyCommandToComposite(insertCommand);
+}
+
 void CompositeEditCommandImpl::deleteSelection()
 {
     if (currentSelection().state() == KHTMLSelection::RANGE) {
@@ -344,88 +504,24 @@ void CompositeEditCommandImpl::deleteSelection()
     }
 }
 
-//------------------------------------------------------------------------------------------
-// ModifyTextNodeCommandImpl
-
-ModifyTextNodeCommandImpl::ModifyTextNodeCommandImpl(DocumentImpl *document, TextImpl *text, long offset)
-    : EditCommandImpl(document), m_text1(0), m_text2(text), m_offset(offset)
+void CompositeEditCommandImpl::deleteSelection(const KHTMLSelection &selection)
 {
-    ASSERT(m_text2);
-    ASSERT(m_text2->length() > 0);
-    ASSERT(m_offset >= 0);
-
-    m_text2->ref();
-}
-
-ModifyTextNodeCommandImpl::ModifyTextNodeCommandImpl(DocumentImpl *document, TextImpl *text1, TextImpl *text2)
-    : EditCommandImpl(document), m_text1(text1), m_text2(text2), m_offset(0)
-{
-    ASSERT(m_text1);
-    ASSERT(m_text2);
-    ASSERT(m_text1->nextSibling() == m_text2);
-    ASSERT(m_text1->length() > 0);
-    ASSERT(m_text2->length() > 0);
-
-    m_text1->ref();
-    m_text2->ref();
-}
-
-ModifyTextNodeCommandImpl::~ModifyTextNodeCommandImpl()
-{
-    if (m_text2)
-        m_text2->deref();
-    if (m_text1)
-        m_text1->deref();
-}
-
-int ModifyTextNodeCommandImpl::commandID() const
-{
-    return ModifyTextNodeCommandID;
+    if (selection.state() == KHTMLSelection::RANGE) {
+        DeleteSelectionCommand cmd(document(), selection);
+        applyCommandToComposite(cmd);
+    }
 }
 
-void ModifyTextNodeCommandImpl::splitTextNode()
+void CompositeEditCommandImpl::deleteCollapsibleWhitespace()
 {
-    ASSERT(m_text2);
-    ASSERT(m_text1 == 0);
-    ASSERT(m_offset > 0);
-    ASSERT(state() == splitState());
-
-    ASSERT(m_offset >= m_text2->caretMinOffset() && m_offset <= m_text2->caretMaxOffset());
-
-    int exceptionCode = 0;
-    m_text1 = document()->createTextNode(m_text2->substringData(0, m_offset, exceptionCode));
-    ASSERT(exceptionCode == 0);
-    ASSERT(m_text1);
-    m_text1->ref();
-
-    m_text2->deleteData(0, m_offset, exceptionCode);
-    ASSERT(exceptionCode == 0);
-
-    m_text2->parentNode()->insertBefore(m_text1, m_text2, exceptionCode);
-    ASSERT(exceptionCode == 0);
-        
-    ASSERT(m_text2->previousSibling()->isTextNode());
-    m_text1 = static_cast<TextImpl *>(m_text2->previousSibling());
+    DeleteCollapsibleWhitespaceCommand cmd(document());
+    applyCommandToComposite(cmd);
 }
 
-void ModifyTextNodeCommandImpl::joinTextNodes()
+void CompositeEditCommandImpl::deleteCollapsibleWhitespace(const KHTMLSelection &selection)
 {
-    ASSERT(m_text1);
-    ASSERT(m_text2);
-    ASSERT(state() == joinState());
-    
-    ASSERT(m_text1->nextSibling() == m_text2);
-
-    int exceptionCode = 0;
-    m_text2->insertData(0, m_text1->data(), exceptionCode);
-    ASSERT(exceptionCode == 0);
-
-    m_text2->parent()->removeChild(m_text1, exceptionCode);
-    ASSERT(exceptionCode == 0);
-
-    m_offset = m_text1->length();
-    m_text1->deref();
-    m_text1 = 0;
+    DeleteCollapsibleWhitespaceCommand cmd(document(), selection);
+    applyCommandToComposite(cmd);
 }
 
 //==========================================================================================
@@ -433,11 +529,11 @@ void ModifyTextNodeCommandImpl::joinTextNodes()
 //------------------------------------------------------------------------------------------
 // AppendNodeCommandImpl
 
-AppendNodeCommandImpl::AppendNodeCommandImpl(DocumentImpl *document, NodeImpl *parent, NodeImpl *appendChild)
-    : EditCommandImpl(document), m_parent(parent), m_appendChild(appendChild)
+AppendNodeCommandImpl::AppendNodeCommandImpl(DocumentImpl *document, NodeImpl *parentNode, NodeImpl *appendChild)
+    : EditCommandImpl(document), m_parentNode(parentNode), m_appendChild(appendChild)
 {
-    ASSERT(m_parent);
-    m_parent->ref();
+    ASSERT(m_parentNode);
+    m_parentNode->ref();
 
     ASSERT(m_appendChild);
     m_appendChild->ref();
@@ -445,8 +541,8 @@ AppendNodeCommandImpl::AppendNodeCommandImpl(DocumentImpl *document, NodeImpl *p
 
 AppendNodeCommandImpl::~AppendNodeCommandImpl()
 {
-    if (m_parent)
-        m_parent->deref();
+    if (m_parentNode)
+        m_parentNode->deref();
     if (m_appendChild)
         m_appendChild->deref();
 }
@@ -458,87 +554,175 @@ int AppendNodeCommandImpl::commandID() const
 
 void AppendNodeCommandImpl::doApply()
 {
-    ASSERT(m_parent);
+    ASSERT(m_parentNode);
     ASSERT(m_appendChild);
 
     int exceptionCode = 0;
-    m_parent->appendChild(m_appendChild, exceptionCode);
+    m_parentNode->appendChild(m_appendChild, exceptionCode);
     ASSERT(exceptionCode == 0);
 }
 
 void AppendNodeCommandImpl::doUnapply()
 {
-    ASSERT(m_parent);
+    ASSERT(m_parentNode);
     ASSERT(m_appendChild);
     ASSERT(state() == Applied);
 
     int exceptionCode = 0;
-    m_parent->removeChild(m_appendChild, exceptionCode);
+    m_parentNode->removeChild(m_appendChild, exceptionCode);
     ASSERT(exceptionCode == 0);
 }
 
 //------------------------------------------------------------------------------------------
-// DeleteKeyCommandImpl
+// DeleteCollapsibleWhitespaceCommandImpl
 
-DeleteKeyCommandImpl::DeleteKeyCommandImpl(DocumentImpl *document) 
-    : CompositeEditCommandImpl(document)
+DeleteCollapsibleWhitespaceCommandImpl::DeleteCollapsibleWhitespaceCommandImpl(DocumentImpl *document)
+    : CompositeEditCommandImpl(document), m_selectionToCollapse(currentSelection()), m_charactersDeleted(0)
 {
 }
 
-DeleteKeyCommandImpl::~DeleteKeyCommandImpl() 
+DeleteCollapsibleWhitespaceCommandImpl::DeleteCollapsibleWhitespaceCommandImpl(DocumentImpl *document, const KHTMLSelection &selection)
+    : CompositeEditCommandImpl(document), m_selectionToCollapse(selection), m_charactersDeleted(0)
 {
 }
 
-int DeleteKeyCommandImpl::commandID() const
+DeleteCollapsibleWhitespaceCommandImpl::~DeleteCollapsibleWhitespaceCommandImpl()
 {
-    return DeleteKeyCommandID;
 }
 
-void DeleteKeyCommandImpl::doApply()
+int DeleteCollapsibleWhitespaceCommandImpl::commandID() const
 {
-    KHTMLPart *part = document()->part();
-    ASSERT(part);
+    return DeleteCollapsibleWhitespaceCommandID;
+}
 
-    KHTMLSelection selection = part->selection();
-    ASSERT(!selection.isEmpty());
+static bool shouldDeleteUpstreamPosition(const DOMPosition &pos)
+{
+    if (!pos.node()->isTextNode())
+        return false;
+        
+    RenderObject *renderer = pos.node()->renderer();
+    if (!renderer)
+        return true;
+        
+    TextImpl *textNode = static_cast<TextImpl *>(pos.node());
+    if (pos.offset() >= (long)textNode->length())
+        return false;
 
-    // Delete the current selection
-    if (selection.state() == KHTMLSelection::RANGE) {
-        deleteSelection();
-        setEndingSelection(currentSelection());
-        return;
+    if (pos.isLastRenderedPositionInEditableBlock())
+        return false;
+
+    if (pos.isFirstRenderedPositionOnLine())
+        return false;
+
+    RenderText *textRenderer = static_cast<RenderText *>(renderer);
+
+    for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
+        if (pos.offset() < box->m_start) {
+            return true;
+        }
+        if (pos.offset() >= box->m_start && pos.offset() < box->m_start + box->m_len)
+            return false;
     }
+    
+    return true;
+}
 
-    NodeImpl *caretNode = selection.startNode();
+DOMPosition DeleteCollapsibleWhitespaceCommandImpl::deleteWhitespace(const DOMPosition &pos)
+{
+    DOMPosition upstream = pos.equivalentUpstreamPosition();
+    DOMPosition downstream = pos.equivalentDownstreamPosition();
+    
+    bool del = shouldDeleteUpstreamPosition(upstream);
 
-    if (caretNode->isTextNode()) {
-        // Check if we can delete character at cursor
-        int offset = selection.startOffset() - 1;
-        if (offset >= caretNode->caretMinOffset()) {
-            TextImpl *textNode = static_cast<TextImpl *>(caretNode);
-            deleteText(textNode, offset, 1);
-            selection = KHTMLSelection(textNode, offset);
-            setEndingSelection(selection);
-            return;
+    LOG(Editing, "pos:        %s [%p:%d]\n", getTagName(pos.node()->id()).string().latin1(), pos.node(), pos.offset());
+    if (upstream == downstream) {
+        LOG(Editing, "same:       %s [%p:%d]\n", getTagName(upstream.node()->id()).string().latin1(), upstream.node(), upstream.offset());
+    }
+    else {
+        LOG(Editing, "upstream:   %s %s [%p:%d]\n", del ? "DELETE" : "SKIP", getTagName(upstream.node()->id()).string().latin1(), upstream.node(), upstream.offset());
+        EditIterator it(upstream);
+        for (it.next(); it.current() != downstream; it.next()) {
+            if (it.current().node()->isTextNode() && (long)static_cast<TextImpl *>(it.current().node())->length() == it.current().offset())
+                LOG(Editing, "   node:    AT END %s [%p:%d]\n", getTagName(it.current().node()->id()).string().latin1(), it.current().node(), it.current().offset());
+            else
+                LOG(Editing, "   node:    DELETE %s [%p:%d]\n", getTagName(it.current().node()->id()).string().latin1(), it.current().node(), it.current().offset());
         }
+        LOG(Editing, "downstream: %s [%p:%d]\n", getTagName(downstream.node()->id()).string().latin1(), downstream.node(), downstream.offset());
+    }
+
+    if (upstream == downstream)
+        return upstream;
         
-        // Check if previous sibling is a BR element
-        NodeImpl *previousSibling = caretNode->previousSibling();
-        if (previousSibling && previousSibling->renderer() && previousSibling->renderer()->isBR()) {
-            removeNode(previousSibling);
-            return;
+    EditIterator it(upstream);
+    DOMPosition deleteStart = upstream;
+    if (!del) {
+        deleteStart = it.peekNext();
+        if (deleteStart == downstream)
+            return upstream;
+    }
+    
+    DOMPosition endingPosition = upstream;
+    
+    while (it.current() != downstream) {
+
+        DOMPosition next = it.peekNext();
+        if (next.node() != deleteStart.node()) {
+            ASSERT(deleteStart.node()->isTextNode());
+            TextImpl *textNode = static_cast<TextImpl *>(deleteStart.node());
+            unsigned long count = it.current().offset() - deleteStart.offset();
+            if (count == textNode->length()) {
+                LOG(Editing, "   removeNodeAndPrune 1: [%p]\n", textNode);
+                removeNodeAndPrune(textNode);
+            }
+            else {
+                LOG(Editing, "   deleteText 1: [%p:%d:%d:%d]\n", textNode, textNode->length(), deleteStart.offset(), it.current().offset() - deleteStart.offset());
+                deleteText(textNode, deleteStart.offset(), count);
+            }
+            deleteStart = next;
+        }
+        else if (next == downstream) {
+            ASSERT(deleteStart.node() == downstream.node());
+            ASSERT(downstream.node()->isTextNode());
+            TextImpl *textNode = static_cast<TextImpl *>(deleteStart.node());
+            unsigned long count = downstream.offset() - deleteStart.offset();
+            ASSERT(count <= textNode->length());
+            if (count == textNode->length()) {
+                LOG(Editing, "   removeNodeAndPrune 2: [%p]\n", textNode);
+                removeNodeAndPrune(textNode);
+            }
+            else {
+                LOG(Editing, "   deleteText 2: [%p:%d:%d:%d]\n", textNode, textNode->length(), deleteStart.offset(), count);
+                deleteText(textNode, deleteStart.offset(), count);
+                m_charactersDeleted = count;
+                endingPosition = DOMPosition(downstream.node(), downstream.offset() - m_charactersDeleted);
+            }
         }
         
-        // Check if previous leaf node is a text node
-        NodeImpl *previousLeafNode = caretNode->previousLeafNode();
-        if (previousLeafNode && previousLeafNode->isTextNode()) {
-            TextImpl *textNode = static_cast<TextImpl *>(previousLeafNode);
-            offset = previousLeafNode->caretMaxOffset() - 1;
-            deleteText(textNode, offset, 1);
-            selection = KHTMLSelection(textNode, offset);
-            setEndingSelection(selection);
-            return;
+        it.setPosition(next);
+    }
+    
+    return endingPosition;
+}
+
+void DeleteCollapsibleWhitespaceCommandImpl::doApply()
+{
+    int state = m_selectionToCollapse.state();
+    if (state == KHTMLSelection::CARET) {
+        DOMPosition endPosition = deleteWhitespace(m_selectionToCollapse.startPosition());
+        setEndingSelection(endPosition);
+        LOG(Editing, "-----------------------------------------------------\n");
+    }
+    else if (state == KHTMLSelection::RANGE) {
+        DOMPosition startPosition = deleteWhitespace(m_selectionToCollapse.startPosition());
+        LOG(Editing, "-----------------------------------------------------\n");
+        DOMPosition endPosition = m_selectionToCollapse.endPosition();
+        if (m_charactersDeleted > 0 && startPosition.node() == endPosition.node()) {
+            LOG(Editing, "adjust end position by %d\n", m_charactersDeleted);
+            endPosition = DOMPosition(endPosition.node(), endPosition.offset() - m_charactersDeleted);
         }
+        endPosition = deleteWhitespace(endPosition);
+        setEndingSelection(KHTMLSelection(startPosition, endPosition));
+        LOG(Editing, "=====================================================\n");
     }
 }
 
@@ -548,6 +732,13 @@ void DeleteKeyCommandImpl::doApply()
 DeleteSelectionCommandImpl::DeleteSelectionCommandImpl(DOM::DocumentImpl *document)
     : CompositeEditCommandImpl(document)
 {
+    m_selectionToDelete = startingSelection();
+}
+
+DeleteSelectionCommandImpl::DeleteSelectionCommandImpl(DOM::DocumentImpl *document, const KHTMLSelection &selection)
+    : CompositeEditCommandImpl(document)
+{
+    m_selectionToDelete = selection;
 }
 
 DeleteSelectionCommandImpl::~DeleteSelectionCommandImpl()
@@ -559,124 +750,211 @@ int DeleteSelectionCommandImpl::commandID() const
     return DeleteSelectionCommandID;
 }
 
-void DeleteSelectionCommandImpl::doApply()
+void DeleteSelectionCommandImpl::joinTextNodesWithSameStyle()
 {
-    if (startingSelection().isEmpty()) {
+    KHTMLSelection selection = currentSelection();
+
+    if (selection.state() != KHTMLSelection::CARET)
         return;
-    }
 
-    KHTMLSelection selection = startingSelection();
+    DOMPosition pos = selection.startPosition();
+    
+    if (!pos.node()->isTextNode())
+        return;
 
-    //
-    // Figure out where to place the caret after doing the delete:
-    //
-    // 1. If the start node is not completely selected, use the start 
-    //    node and start offset for the new position; else
-    // 2. If the start and end nodes are completely selected:
-    //       a. If there is an editable node following the end node, 
-    //          place the caret in the min caret offset of that node; else
-    //       b. If there is an editable node before the start node, 
-    //          place the caret in the max caret offset of that node; else
-    //       c. There is no more editable content in the document.
-    //          EDIT FIXME: We do not handle this case now
-    // 3. If the start node is completely selected and the end node is 
-    //    different than the start node and it is not completely selected,
-    //    use the end node and the end node min caret for the new position; else
-    //
+    TextImpl *textNode = static_cast<TextImpl *>(pos.node());
+    
+    if (pos.offset() == 0) {
+        EditIterator it(pos);
+        DOMPosition prev = it.previous();
+        if (prev == pos)
+            return;
+        if (prev.node()->isTextNode()) {
+            TextImpl *prevTextNode = static_cast<TextImpl *>(prev.node());
+            if (textNodesAreJoinable(prevTextNode, textNode)) {
+                joinTextNodes(prevTextNode, textNode);
+                setEndingSelection(DOMPosition(textNode, prevTextNode->length()));
+                LOG(Editing, "joinTextNodesWithSameStyle [1]\n");
+            }
+        }
+    }
+    else if (pos.offset() == (long)textNode->length()) {
+        EditIterator it(pos);
+        DOMPosition next = it.next();
+        if (next == pos)
+            return;
+        if (next.node()->isTextNode()) {
+            TextImpl *nextTextNode = static_cast<TextImpl *>(next.node());
+            if (textNodesAreJoinable(textNode, nextTextNode)) {
+                joinTextNodes(textNode, nextTextNode);
+                setEndingSelection(DOMPosition(nextTextNode, pos.offset()));
+                LOG(Editing, "joinTextNodesWithSameStyle [2]\n");
+            }
+        }
+    }
+}
+
+static void debugPosition(const char *prefix, const DOMPosition &pos)
+{
+    LOG(Editing, "%s%s %p : %d", prefix, getTagName(pos.node()->id()).string().latin1(), pos.node(), pos.offset());
+}
 
-    DOMPosition deleteStart = DOMPosition(selection.startNode(), selection.startOffset());
-    DOMPosition deleteEnd = DOMPosition(selection.endNode(), selection.endOffset());
+enum { NoPositionModification, MoveDownstreamPositionModification, MoveToNextCharacterModification };
 
-    bool startIsCompletelySelected = 
-        deleteStart.offset() == deleteStart.node()->caretMinOffset() &&
-        ((deleteStart.node() != deleteEnd.node()) ||
-         (deleteEnd.offset() == deleteEnd.node()->caretMaxOffset()));
+void DeleteSelectionCommandImpl::doApply()
+{
+    if (m_selectionToDelete.state() != KHTMLSelection::RANGE)
+        return;
 
-    bool endIsCompletelySelected = 
-        deleteEnd.offset() == deleteEnd.node()->caretMaxOffset() &&
-        ((deleteStart.node() != deleteEnd.node()) ||
-         (deleteStart.offset() == deleteStart.node()->caretMinOffset()));
+    KHTMLSelection selection = m_selectionToDelete;
 
     DOMPosition endingPosition;
+    bool adjustEndingPositionDownstream = false;
+
+    DOMPosition upstreamStart = selection.startPosition().equivalentUpstreamPosition();
+    DOMPosition downstreamStart = selection.startPosition().equivalentDownstreamPosition();
+    DOMPosition upstreamEnd = selection.endPosition().equivalentUpstreamPosition();
+    DOMPosition downstreamEnd = selection.endPosition().equivalentDownstreamPosition();
+
+    bool startCompletelySelected = 
+        downstreamStart.offset() <= downstreamStart.node()->caretMinOffset() &&
+        ((downstreamStart.node() != upstreamEnd.node()) ||
+         (upstreamEnd.offset() >= upstreamEnd.node()->caretMaxOffset()));
 
-    if (!startIsCompletelySelected) {
-        // Case 1
-        endingPosition = DOMPosition(deleteStart.node(), deleteStart.offset());
+    bool endCompletelySelected = 
+        upstreamEnd.offset() >= upstreamEnd.node()->caretMaxOffset() &&
+        ((downstreamStart.node() != upstreamEnd.node()) ||
+         (downstreamStart.offset() <= downstreamStart.node()->caretMinOffset()));
+
+    unsigned long startRenderedOffset = downstreamStart.renderedOffset();
+    //unsigned long endRenderedOffset = upstreamEnd.renderedOffset();
+    
+    bool startAtStartOfRootEditableBlock = startRenderedOffset == 0 && downstreamStart.inFirstEditableInRootEditableBlock();
+    bool startAtStartOfBlock = startAtStartOfRootEditableBlock || 
+        (startRenderedOffset == 0 && downstreamStart.inFirstEditableInContainingEditableBlock());
+    bool endAtEndOfBlock = downstreamEnd.isLastRenderedPositionInEditableBlock();
+
+    debugPosition("upstreamStart:       ", upstreamStart);
+    debugPosition("downstreamStart:     ", downstreamStart);
+    debugPosition("upstreamEnd:         ", upstreamEnd);
+    debugPosition("downstreamEnd:       ", downstreamEnd);
+    LOG(Editing,  "start selected:      %s", startCompletelySelected ? "YES" : "NO");
+    LOG(Editing,  "at start block:      %s", startAtStartOfBlock ? "YES" : "NO");
+    LOG(Editing,  "at start root block: %s", startAtStartOfRootEditableBlock ? "YES" : "NO");
+    LOG(Editing,  "at end block:        %s", endAtEndOfBlock ? "YES" : "NO");
+
+    // Start is not completely selected
+    if (startAtStartOfBlock) {
+        LOG(Editing,  "ending position case 1");
+        endingPosition = DOMPosition(downstreamStart.node()->containingEditableBlock(), 1);
+        adjustEndingPositionDownstream = true;
+    }
+    else if (!startCompletelySelected) {
+        LOG(Editing,  "ending position case 2");
+        endingPosition = upstreamStart;
+        if (upstreamStart.node()->id() == ID_BR && upstreamStart.offset() == 1)
+            adjustEndingPositionDownstream = true;
     }
-    else if (endIsCompletelySelected) {
-        DOMPosition pos = selection.nextCharacterPosition(deleteEnd);
-        if (pos != deleteEnd) {
-            // Case 2a
-            endingPosition = DOMPosition(pos.node(), pos.node()->caretMinOffset());
+    else if (upstreamStart != downstreamStart) {
+        LOG(Editing,  "ending position case 3");
+        endingPosition = upstreamStart;
+        if (downstreamStart.node()->id() == ID_BR && downstreamStart.offset() == 0)
+            adjustEndingPositionDownstream = true;
+    }
+   
+    //
+    // Figure out the whitespace conversions to do
+    //
+    if (startAtStartOfBlock && !endAtEndOfBlock) {
+        // convert trailing whitespace
+        DOMPosition trailing = trailingWhitespacePosition(downstreamEnd.equivalentDownstreamPosition());
+        if (trailing.notEmpty()) {
+            debugPosition("convertTrailingWhitespace: ", trailing);
+            DOMPosition collapse = trailing.nextCharacterPosition();
+            if (collapse != trailing)
+                deleteCollapsibleWhitespace(collapse);
+            TextImpl *textNode = static_cast<TextImpl *>(trailing.node());
+            replaceText(textNode, trailing.offset(), 1, nonBreakingSpaceString());
         }
-        else {
-            pos = selection.previousCharacterPosition(deleteStart);
-            if (pos != deleteStart) {
-                // Case 2b
-                endingPosition = DOMPosition(pos.node(), pos.node()->caretMaxOffset());
-            }
-            else {
-                // Case 2c
-                // EDIT FIXME
-                endingPosition = DOMPosition();
-            }
+    }
+    else if (!startAtStartOfBlock && endAtEndOfBlock) {
+        // convert leading whitespace
+        DOMPosition leading = leadingWhitespacePosition(upstreamStart.equivalentUpstreamPosition());
+        if (leading.notEmpty()) {
+            debugPosition("convertLeadingWhitespace:  ", leading);
+            TextImpl *textNode = static_cast<TextImpl *>(leading.node());
+            replaceText(textNode, leading.offset(), 1, nonBreakingSpaceString());
         }
     }
-    else {
-        // Case 3
-        endingPosition = DOMPosition(deleteEnd.node(), deleteEnd.node()->caretMinOffset());
+    else if (!startAtStartOfBlock && !endAtEndOfBlock) {
+        // convert contiguous whitespace
+        DOMPosition leading = leadingWhitespacePosition(upstreamStart.equivalentUpstreamPosition());
+        DOMPosition trailing = trailingWhitespacePosition(downstreamEnd.equivalentDownstreamPosition());
+        if (leading.notEmpty() && trailing.notEmpty()) {
+            debugPosition("convertLeadingWhitespace [contiguous]:  ", leading);
+            TextImpl *textNode = static_cast<TextImpl *>(leading.node());
+            replaceText(textNode, leading.offset(), 1, nonBreakingSpaceString());
+        }
     }
-
+        
     //
     // Do the delete
     //
-    NodeImpl *n = deleteStart.node()->nextLeafNode();
+    NodeImpl *n = downstreamStart.node()->traverseNextNode();
 
     // work on start node
-    if (startIsCompletelySelected) {
-        removeNode(deleteStart.node());
+    if (startCompletelySelected) {
+        removeNodeAndPrune(downstreamStart.node());
     }
-    else if (deleteStart.node()->isTextNode()) {
-        TextImpl *text = static_cast<TextImpl *>(deleteStart.node());
-        int endOffset = text == deleteEnd.node() ? deleteEnd.offset() : text->length();
-        if (endOffset > deleteStart.offset()) {
-            deleteText(text, deleteStart.offset(), endOffset - deleteStart.offset());
+    else if (downstreamStart.node()->isTextNode()) {
+        TextImpl *text = static_cast<TextImpl *>(downstreamStart.node());
+        int endOffset = text == upstreamEnd.node() ? upstreamEnd.offset() : text->length();
+        if (endOffset > downstreamStart.offset()) {
+            deleteText(text, downstreamStart.offset(), endOffset - downstreamStart.offset());
         }
     }
     else {
-        ASSERT_NOT_REACHED();
+        // we have clipped the end of a non-text element
+        // the offset must be 1 here. if it is, do nothing and move on.
+        ASSERT(downstreamStart.offset() == 1);
     }
 
-    if (deleteStart.node() != deleteEnd.node()) {
+    if (downstreamStart.node() != upstreamEnd.node()) {
         // work on intermediate nodes
-        while (n != deleteEnd.node()) {
+        while (n != upstreamEnd.node()) {
             NodeImpl *d = n;
-            n = n->nextLeafNode();
-            removeNode(d);
+            n = n->traverseNextNode();
+            if (d->renderer() && d->renderer()->isEditable())
+                removeNodeAndPrune(d);
         }
         
         // work on end node
-        ASSERT(n == deleteEnd.node());
-        if (endIsCompletelySelected) {
-            removeNode(deleteEnd.node());
+        ASSERT(n == upstreamEnd.node());
+        if (endCompletelySelected) {
+            removeNodeAndPrune(upstreamEnd.node());
         }
-        else if (deleteEnd.node()->isTextNode()) {
-            if (deleteEnd.offset() > 0) {
-                TextImpl *text = static_cast<TextImpl *>(deleteEnd.node());
-                deleteText(text, 0, deleteEnd.offset());
+        else if (upstreamEnd.node()->isTextNode()) {
+            if (upstreamEnd.offset() > 0) {
+                TextImpl *text = static_cast<TextImpl *>(upstreamEnd.node());
+                deleteText(text, 0, upstreamEnd.offset());
             }
         }
         else {
-            ASSERT_NOT_REACHED();
+            // we have clipped the beginning of a non-text element
+            // the offset must be 0 here. if it is, do nothing and move on.
+            ASSERT(downstreamStart.offset() == 0);
         }
     }
 
-    //
-    // set the ending selection
-    //
-    selection.moveTo(endingPosition);
-    selection.moveToRenderedContent();
-    setEndingSelection(selection);
+    if (adjustEndingPositionDownstream) {
+        LOG(Editing,  "adjust ending position downstream");
+        endingPosition = endingPosition.equivalentDownstreamPosition();
+    }
+
+    debugPosition("ending position:     ", endingPosition);
+    setEndingSelection(endingPosition);
+
+    LOG(Editing, "-----------------------------------------------------\n");
 }
 
 //------------------------------------------------------------------------------------------
@@ -752,8 +1030,12 @@ void InputNewlineCommandImpl::doApply()
     // Delete the current selection
     deleteSelection();
     
+    // reset the current selection since it may have changed due to the delete
+    selection = currentSelection();
+
     int exceptionCode = 0;
     ElementImpl *breakNode = document()->createHTMLElement("BR", exceptionCode);
+    ASSERT(exceptionCode == 0);
 
     TextImpl *textNode = static_cast<TextImpl *>(selection.startNode());
     bool atStart = selection.startOffset() == textNode->renderer()->caretMinOffset();
@@ -767,8 +1049,8 @@ void InputNewlineCommandImpl::doApply()
     else if (atEnd) {
         insertNodeAfter(breakNode, textNode);
         // Set the cursor at the beginning of the the BR.
-        selection = selection.nextCharacterPosition();
-        setEndingSelection(selection);
+        DOMPosition pos(breakNode, 0);
+        setEndingSelection(pos);
     }
     else {
         TextImpl *textBeforeNode = document()->createTextNode(textNode->substringData(0, selection.startOffset(), exceptionCode));
@@ -787,16 +1069,15 @@ void InputNewlineCommandImpl::doApply()
 //------------------------------------------------------------------------------------------
 // InputTextCommandImpl
 
-InputTextCommandImpl::InputTextCommandImpl(DocumentImpl *document, const DOMString &text) 
-    : CompositeEditCommandImpl(document)
+InputTextCommandImpl::InputTextCommandImpl(DocumentImpl *document) 
+    : CompositeEditCommandImpl(document), m_insertedTextNode(0), m_charactersAdded(0)
 {
-    ASSERT(!text.isEmpty());
-    m_text = text; 
-    nbsp = DOMString(QChar(0xa0));
 }
 
 InputTextCommandImpl::~InputTextCommandImpl() 
 {
+    if (m_insertedTextNode)
+        m_insertedTextNode->deref();
 }
 
 int InputTextCommandImpl::commandID() const
@@ -806,20 +1087,16 @@ int InputTextCommandImpl::commandID() const
 
 void InputTextCommandImpl::doApply()
 {
-    execute(m_text);
 }
 
-void InputTextCommandImpl::coalesce(const DOMString &text)
+void InputTextCommandImpl::input(const DOMString &text)
 {
-    ASSERT(state() == Applied);
     execute(text);
-    m_text += text;
 }
 
 void InputTextCommandImpl::deleteCharacter()
 {
     ASSERT(state() == Applied);
-    ASSERT(m_text.length() > 0);
 
     KHTMLSelection selection = currentSelection();
 
@@ -834,24 +1111,111 @@ void InputTextCommandImpl::deleteCharacter()
         ASSERT(exceptionCode == 0);
         selection = KHTMLSelection(textNode, offset);
         setEndingSelection(selection);
-        m_text = m_text.string().left(m_text.length() - 1);
+        m_charactersAdded--;
     }
 }
 
-void InputTextCommandImpl::execute(const DOMString &text)
+DOMPosition InputTextCommandImpl::prepareForTextInsertion()
 {
+    // Prepare for text input by looking at the current position.
+    // It may be necessary to insert a text node to receive characters.
     KHTMLSelection selection = currentSelection();
+    ASSERT(selection.state() == KHTMLSelection::CARET);
+    
+    DOMPosition pos = selection.startPosition().equivalentUpstreamPosition();
+    if (!pos.node()->inSameContainingEditableBlock(selection.startNode()))
+        pos = selection.startPosition();
+    
+    if (!pos.node()->isTextNode()) {
+        if (!m_insertedTextNode) {
+            m_insertedTextNode = document()->createTextNode("");
+            m_insertedTextNode->setRendererIsNeeded();
+            m_insertedTextNode->ref();
+        }
+        
+        if (pos.node()->isEditableBlock())
+            appendNode(pos.node(), m_insertedTextNode);
+        else if (pos.node()->id() == ID_BR || pos.offset() == 1)
+            insertNodeAfter(m_insertedTextNode, pos.node());
+        else {
+            ASSERT(pos.offset() == 0);
+            insertNodeBefore(m_insertedTextNode, pos.node());
+        }
+        
+        pos = DOMPosition(m_insertedTextNode, 0);
+    }
+    
+    return pos;
+}
 
-    if (!selection.startNode()->isTextNode())
-        return;
+void InputTextCommandImpl::execute(const DOMString &text)
+{
+    KHTMLSelection selection = currentSelection();
 
     // Delete the current selection
     deleteSelection();
     
-    TextImpl *textNode = static_cast<TextImpl *>(selection.startNode());
-    insertText(textNode, selection.startOffset(), text);
-    selection = KHTMLSelection(selection.startNode(), selection.startOffset() + text.length());
+    // Make sure the document is set up to receive text
+    DOMPosition pos = prepareForTextInsertion();
+    
+    TextImpl *textNode = static_cast<TextImpl *>(pos.node());
+    long offset = pos.offset();
+    
+    // This is a temporary implementation for inserting adjoining spaces
+    // into a document. We are working on a CSS-related whitespace solution
+    // that will replace this some day.
+    if (isWS(text))
+        insertSpace(textNode, offset);
+    else
+        insertText(textNode, offset, text);
+    selection = KHTMLSelection(textNode, offset + text.length());
     setEndingSelection(selection);
+    m_charactersAdded += text.length();
+}
+
+void InputTextCommandImpl::insertSpace(TextImpl *textNode, unsigned long offset)
+{
+    ASSERT(textNode);
+
+    DOMString text(textNode->data());
+
+    // count up all spaces and newlines in front of the caret
+    // delete all collapsed ones
+    // this will work out OK since the offset we have been passed has been upstream-ized 
+    int count = 0;
+    for (unsigned int i = offset; i < text.length(); i++) {
+        if (isWS(text[i]))
+            count++;
+        else 
+            break;
+    }
+    if (count > 0) {
+        // By checking the character at the downstream position, we can
+        // check if there is a rendered WS at the caret
+        DOMPosition pos(textNode, offset);
+        DOMPosition downstream = pos.equivalentDownstreamPosition();
+        if (downstream.offset() < (long)text.length() && isWS(text[downstream.offset()]))
+            count--; // leave this WS in
+        if (count > 0)
+            deleteText(textNode, offset, count);
+    }
+
+    if (offset > 0 && offset <= text.length() - 1 && !isWS(text[offset]) && !isWS(text[offset - 1])) {
+        // insert a "regular" space
+        insertText(textNode, offset, " ");
+        return;
+    }
+
+    if (text.length() >= 2 && offset >= 2 && isNBSP(text[offset - 2]) && isNBSP(text[offset - 1])) {
+        // DOM looks like this:
+        // nbsp nbsp caret
+        // insert a space between the two nbsps
+        insertText(textNode, offset - 1, " ");
+        return;
+    }
+
+    // insert an nbsp
+    insertText(textNode, offset, nonBreakingSpaceString());
 }
 
 //------------------------------------------------------------------------------------------
@@ -951,12 +1315,24 @@ void InsertTextCommandImpl::doUnapply()
 // JoinTextNodesCommandImpl
 
 JoinTextNodesCommandImpl::JoinTextNodesCommandImpl(DocumentImpl *document, TextImpl *text1, TextImpl *text2)
-    : ModifyTextNodeCommandImpl(document, text1, text2)
+    : EditCommandImpl(document), m_text1(text1), m_text2(text2)
 {
+    ASSERT(m_text1);
+    ASSERT(m_text2);
+    ASSERT(m_text1->nextSibling() == m_text2);
+    ASSERT(m_text1->length() > 0);
+    ASSERT(m_text2->length() > 0);
+
+    m_text1->ref();
+    m_text2->ref();
 }
 
 JoinTextNodesCommandImpl::~JoinTextNodesCommandImpl()
 {
+    if (m_text1)
+        m_text1->deref();
+    if (m_text2)
+        m_text2->deref();
 }
 
 int JoinTextNodesCommandImpl::commandID() const
@@ -966,12 +1342,35 @@ int JoinTextNodesCommandImpl::commandID() const
 
 void JoinTextNodesCommandImpl::doApply()
 {
-    joinTextNodes();
+    ASSERT(m_text1);
+    ASSERT(m_text2);
+    ASSERT(m_text1->nextSibling() == m_text2);
+
+    int exceptionCode = 0;
+    m_text2->insertData(0, m_text1->data(), exceptionCode);
+    ASSERT(exceptionCode == 0);
+
+    m_text2->parent()->removeChild(m_text1, exceptionCode);
+    ASSERT(exceptionCode == 0);
+
+    m_offset = m_text1->length();
 }
 
 void JoinTextNodesCommandImpl::doUnapply()
 {
-    splitTextNode();
+    ASSERT(m_text2);
+    ASSERT(m_offset > 0);
+
+    int exceptionCode = 0;
+
+    m_text2->deleteData(0, m_offset, exceptionCode);
+    ASSERT(exceptionCode == 0);
+
+    m_text2->parentNode()->insertBefore(m_text1, m_text2, exceptionCode);
+    ASSERT(exceptionCode == 0);
+        
+    ASSERT(m_text2->previousSibling()->isTextNode());
+    ASSERT(m_text2->previousSibling() == m_text1);
 }
 
 //------------------------------------------------------------------------------------------
@@ -1152,15 +1551,55 @@ void RemoveNodeCommandImpl::doUnapply()
 }
 
 //------------------------------------------------------------------------------------------
+// RemoveNodeAndPruneCommandImpl
+
+RemoveNodeAndPruneCommandImpl::RemoveNodeAndPruneCommandImpl(DocumentImpl *document, NodeImpl *removeChild)
+    : CompositeEditCommandImpl(document), m_removeChild(removeChild)
+{
+    ASSERT(m_removeChild);
+    m_removeChild->ref();
+}
+
+RemoveNodeAndPruneCommandImpl::~RemoveNodeAndPruneCommandImpl()
+{
+    if (m_removeChild)
+        m_removeChild->deref();
+}
+
+int RemoveNodeAndPruneCommandImpl::commandID() const
+{
+    return RemoveNodeAndPruneCommandID;
+}
+
+void RemoveNodeAndPruneCommandImpl::doApply()
+{
+    NodeImpl *editableBlock = m_removeChild->containingEditableBlock();
+    NodeImpl *pruneNode = m_removeChild;
+    NodeImpl *node = pruneNode->traversePreviousNode();
+    removeNode(pruneNode);
+    while (1) {
+        if (editableBlock != node->containingEditableBlock() || !shouldPruneNode(node))
+            break;
+        pruneNode = node;
+        node = node->traversePreviousNode();
+        removeNode(pruneNode);
+    }
+}
+
+//------------------------------------------------------------------------------------------
 // SplitTextNodeCommandImpl
 
 SplitTextNodeCommandImpl::SplitTextNodeCommandImpl(DocumentImpl *document, TextImpl *text, long offset)
-    : ModifyTextNodeCommandImpl(document, text, offset)
+    : EditCommandImpl(document), m_text2(text), m_offset(offset)
 {
 }
 
 SplitTextNodeCommandImpl::~SplitTextNodeCommandImpl()
 {
+    if (m_text1)
+        m_text1->deref();
+    if (m_text2)
+        m_text2->deref();
 }
 
 int SplitTextNodeCommandImpl::commandID() const
@@ -1170,19 +1609,56 @@ int SplitTextNodeCommandImpl::commandID() const
 
 void SplitTextNodeCommandImpl::doApply()
 {
-    splitTextNode();
+    ASSERT(m_text2);
+    ASSERT(m_offset > 0);
+
+    int exceptionCode = 0;
+
+    // EDIT FIXME: This should use better smarts for figuring out which portion
+    // of the split to copy (based on their comparitive sizes). We should also
+    // just use the DOM's splitText function.
+    
+    if (!m_text1) {
+        // create only if needed.
+        // if reapplying, this object will already exist.
+        m_text1 = document()->createTextNode(m_text2->substringData(0, m_offset, exceptionCode));
+        ASSERT(exceptionCode == 0);
+        ASSERT(m_text1);
+        m_text1->ref();
+    }
+
+    m_text2->deleteData(0, m_offset, exceptionCode);
+    ASSERT(exceptionCode == 0);
+
+    m_text2->parentNode()->insertBefore(m_text1, m_text2, exceptionCode);
+    ASSERT(exceptionCode == 0);
+        
+    ASSERT(m_text2->previousSibling()->isTextNode());
+    ASSERT(m_text2->previousSibling() == m_text1);
 }
 
 void SplitTextNodeCommandImpl::doUnapply()
 {
-    joinTextNodes();
+    ASSERT(m_text1);
+    ASSERT(m_text2);
+    
+    ASSERT(m_text1->nextSibling() == m_text2);
+
+    int exceptionCode = 0;
+    m_text2->insertData(0, m_text1->data(), exceptionCode);
+    ASSERT(exceptionCode == 0);
+
+    m_text2->parent()->removeChild(m_text1, exceptionCode);
+    ASSERT(exceptionCode == 0);
+
+    m_offset = m_text1->length();
 }
 
 //------------------------------------------------------------------------------------------
 // TypingCommandImpl
 
 TypingCommandImpl::TypingCommandImpl(DocumentImpl *document)
-    : CompositeEditCommandImpl(document)
+    : CompositeEditCommandImpl(document), m_openForMoreTyping(true)
 {
 }
 
@@ -1202,18 +1678,22 @@ void TypingCommandImpl::doApply()
 void TypingCommandImpl::insertText(const DOM::DOMString &text)
 {
     if (m_cmds.count() == 0) {
-        InputTextCommand cmd(document(), text);
+        InputTextCommand cmd(document());
         applyCommandToComposite(cmd);
+        cmd.input(text);
+        setEndingSelection(cmd.endingSelection());
     }
     else {
         EditCommand lastCommand = m_cmds.last();
         if (lastCommand.commandID() == InputTextCommandID) {
-            static_cast<InputTextCommand &>(lastCommand).coalesce(text);
+            static_cast<InputTextCommand &>(lastCommand).input(text);
             setEndingSelection(lastCommand.endingSelection());
         }
         else {
-            InputTextCommand cmd(document(), text);
+            InputTextCommand cmd(document());
             applyCommandToComposite(cmd);
+            cmd.input(text);
+            setEndingSelection(cmd.endingSelection());
         }
     }
 }
@@ -1224,29 +1704,47 @@ void TypingCommandImpl::insertNewline()
     applyCommandToComposite(cmd);
 }
 
+void TypingCommandImpl::issueCommandForDeleteKey()
+{
+    KHTMLSelection selection = currentSelection();
+    ASSERT(selection.state() != KHTMLSelection::NONE);
+    
+    if (selection.state() == KHTMLSelection::CARET) {
+        KHTMLSelection selectionToDelete(selection.startPosition().previousCharacterPosition(), selection.startPosition());
+        setEndingSelection(selectionToDelete);
+        deleteCollapsibleWhitespace();
+        selection = currentSelection();
+        deleteSelection(selection);
+    }
+    else { // selection.state() == KHTMLSelection::RANGE
+        deleteCollapsibleWhitespace();
+        deleteSelection();
+    }
+}
+
 void TypingCommandImpl::deleteKeyPressed()
 {
     if (m_cmds.count() == 0) {
-        DeleteKeyCommand cmd(document());
-        applyCommandToComposite(cmd);
+        issueCommandForDeleteKey();
     }
     else {
         EditCommand lastCommand = m_cmds.last();
         if (lastCommand.commandID() == InputTextCommandID) {
             InputTextCommand cmd = static_cast<InputTextCommand &>(lastCommand);
             cmd.deleteCharacter();
-            if (cmd.text().length() == 0)
+            if (cmd.charactersAdded() == 0) {
                 removeCommand(cmd);
-            else
+            }
+            else {
                 setEndingSelection(cmd.endingSelection());
+            }
         }
         else if (lastCommand.commandID() == InputNewlineCommandID) {
             lastCommand.unapply();
             removeCommand(lastCommand);
         }
         else {
-            DeleteKeyCommand cmd(document());
-            applyCommandToComposite(cmd);
+            issueCommandForDeleteKey();
         }
     }
 }
diff --git a/WebCore/khtml/editing/htmlediting_impl.h b/WebCore/khtml/editing/htmlediting_impl.h
index 5517fd7..39ef1c1 100644
--- a/WebCore/khtml/editing/htmlediting_impl.h
+++ b/WebCore/khtml/editing/htmlediting_impl.h
@@ -40,6 +40,7 @@ namespace DOM {
     class DocumentImpl;
     class DOMPosition;
     class DOMString;
+    class ElementImpl;
     class NodeImpl;
     class TextImpl;
 };
@@ -56,8 +57,9 @@ public:
 	virtual ~EditCommandImpl();
 
     virtual int commandID() const;
-    bool isCompositeStep() const { return m_isCompositeStep; }
-    void setIsCompositeStep(bool flag=true) { m_isCompositeStep = flag; }
+    bool isCompositeStep() const { return parent().notNull(); }
+    EditCommand parent() const;
+    void setParent(const EditCommand &);
 
     enum ECommandState { NotApplied, Applied };
     
@@ -89,7 +91,7 @@ private:
     ECommandState m_state;
     KHTMLSelection m_startingSelection;
     KHTMLSelection m_endingSelection;
-    bool m_isCompositeStep;
+    EditCommand m_parent;
 };
 
 //------------------------------------------------------------------------------------------
@@ -117,42 +119,21 @@ protected:
     void insertNodeAt(DOM::NodeImpl *insertChild, DOM::NodeImpl *refChild, long offset);
     void appendNode(DOM::NodeImpl *parent, DOM::NodeImpl *appendChild);
     void removeNode(DOM::NodeImpl *removeChild);
+    void removeNodeAndPrune(DOM::NodeImpl *removeChild);
     void splitTextNode(DOM::TextImpl *text, long offset);
     void joinTextNodes(DOM::TextImpl *text1, DOM::TextImpl *text2);
     void insertText(DOM::TextImpl *node, long offset, const DOM::DOMString &text);
     void deleteText(DOM::TextImpl *node, long offset, long count);
+    void replaceText(DOM::TextImpl *node, long offset, long count, const DOM::DOMString &replacementText);
     void deleteSelection();
+    void deleteSelection(const KHTMLSelection &selection);
     void deleteKeyPressed();
+    void deleteCollapsibleWhitespace();
+    void deleteCollapsibleWhitespace(const KHTMLSelection &selection);
 
     QValueList<EditCommand> m_cmds;
 };
 
-//------------------------------------------------------------------------------------------
-// ModifyTextNodeCommandImpl
-
-class ModifyTextNodeCommandImpl : public EditCommandImpl
-{
-public:
-    // used by SplitTextNodeCommandImpl derived class
-	ModifyTextNodeCommandImpl(DOM::DocumentImpl *, DOM::TextImpl *, long); 
-    // used by JoinTextNodesCommandImpl derived class
-    ModifyTextNodeCommandImpl(DOM::DocumentImpl *, DOM::TextImpl *, DOM::TextImpl *);
-	virtual ~ModifyTextNodeCommandImpl();
-	
-    virtual int commandID() const;
-
-protected:
-    void splitTextNode();
-    void joinTextNodes();
-
-    virtual ECommandState joinState() = 0;
-    virtual ECommandState splitState() = 0;
-
-    DOM::TextImpl *m_text1;
-    DOM::TextImpl *m_text2;
-    long m_offset;
-};
-
 //==========================================================================================
 // Concrete commands
 //------------------------------------------------------------------------------------------
@@ -161,7 +142,7 @@ protected:
 class AppendNodeCommandImpl : public EditCommandImpl
 {
 public:
-    AppendNodeCommandImpl(DOM::DocumentImpl *, DOM::NodeImpl *parent, DOM::NodeImpl *appendChild);
+    AppendNodeCommandImpl(DOM::DocumentImpl *, DOM::NodeImpl *parentNode, DOM::NodeImpl *appendChild);
 	virtual ~AppendNodeCommandImpl();
 
     virtual int commandID() const;
@@ -169,40 +150,56 @@ public:
 	virtual void doApply();
 	virtual void doUnapply();
 
-    DOM::NodeImpl *parent() const { return m_parent; }
+    DOM::NodeImpl *parentNode() const { return m_parentNode; }
     DOM::NodeImpl *appendChild() const { return m_appendChild; }
 
 private:
-    DOM::NodeImpl *m_parent;    
+    DOM::NodeImpl *m_parentNode;    
     DOM::NodeImpl *m_appendChild;
 };
 
 //------------------------------------------------------------------------------------------
-// DeleteKeyCommandImpl
+// DeleteCollapsibleWhitespaceCommandImpl
 
-class DeleteKeyCommandImpl : public CompositeEditCommandImpl
-{
+class DeleteCollapsibleWhitespaceCommandImpl : public CompositeEditCommandImpl
+{ 
 public:
-    DeleteKeyCommandImpl(DOM::DocumentImpl *document);
-    virtual ~DeleteKeyCommandImpl();
+	DeleteCollapsibleWhitespaceCommandImpl(DOM::DocumentImpl *document);
+	DeleteCollapsibleWhitespaceCommandImpl(DOM::DocumentImpl *document, const KHTMLSelection &selection);
     
+	virtual ~DeleteCollapsibleWhitespaceCommandImpl();
+	
     virtual int commandID() const;
 
-    virtual void doApply();
+	virtual void doApply();
+
+private:
+    DOM::DOMPosition deleteWhitespace(const DOM::DOMPosition &pos);
+
+    KHTMLSelection m_selectionToCollapse;
+    unsigned long m_charactersDeleted;
 };
 
 //------------------------------------------------------------------------------------------
 // DeleteSelectionCommandImpl
 
 class DeleteSelectionCommandImpl : public CompositeEditCommandImpl
-{
+{ 
 public:
 	DeleteSelectionCommandImpl(DOM::DocumentImpl *document);
+	DeleteSelectionCommandImpl(DOM::DocumentImpl *document, const KHTMLSelection &selection);
+    
 	virtual ~DeleteSelectionCommandImpl();
 	
     virtual int commandID() const;
 
 	virtual void doApply();
+    
+private:
+    void deleteDownstreamWS(const DOM::DOMPosition &start);
+    void joinTextNodesWithSameStyle();
+
+    KHTMLSelection m_selectionToDelete;
 };
 
 //------------------------------------------------------------------------------------------
@@ -250,23 +247,25 @@ public:
 class InputTextCommandImpl : public CompositeEditCommandImpl
 {
 public:
-    InputTextCommandImpl(DOM::DocumentImpl *document, const DOM::DOMString &text);
+    InputTextCommandImpl(DOM::DocumentImpl *document);
     virtual ~InputTextCommandImpl();
 
     virtual int commandID() const;
 
     virtual void doApply();
 
-    DOM::DOMString text() const { return m_text; }
-
     void deleteCharacter();
-    void coalesce(const DOM::DOMString &text);
+    void input(const DOM::DOMString &text);
+    
+    unsigned long charactersAdded() const { return m_charactersAdded; }
     
 private:
+    DOM::DOMPosition prepareForTextInsertion();
     void execute(const DOM::DOMString &text);
+    void insertSpace(DOM::TextImpl *textNode, unsigned long offset);
 
-    DOM::DOMString m_text;
-    DOM::DOMString nbsp;
+    DOM::TextImpl *m_insertedTextNode;
+    unsigned long m_charactersAdded;
 };
 
 //------------------------------------------------------------------------------------------
@@ -318,7 +317,7 @@ private:
 //------------------------------------------------------------------------------------------
 // JoinTextNodesCommandImpl
 
-class JoinTextNodesCommandImpl : public ModifyTextNodeCommandImpl
+class JoinTextNodesCommandImpl : public EditCommandImpl
 {
 public:
 	JoinTextNodesCommandImpl(DOM::DocumentImpl *, DOM::TextImpl *, DOM::TextImpl *);
@@ -329,11 +328,13 @@ public:
 	virtual void doApply();
 	virtual void doUnapply();
 
-    virtual ECommandState joinState() { return NotApplied; }
-    virtual ECommandState splitState() { return Applied; }
-
     DOM::TextImpl *firstNode() const { return m_text1; }
     DOM::TextImpl *secondNode() const { return m_text2; }
+
+private:
+    DOM::TextImpl *m_text1;
+    DOM::TextImpl *m_text2;
+    unsigned long m_offset;
 };
 
 //------------------------------------------------------------------------------------------
@@ -395,9 +396,28 @@ private:
 };
 
 //------------------------------------------------------------------------------------------
+// RemoveNodeAndPruneCommandImpl
+
+class RemoveNodeAndPruneCommandImpl : public CompositeEditCommandImpl
+{
+public:
+	RemoveNodeAndPruneCommandImpl(DOM::DocumentImpl *, DOM::NodeImpl *);
+	virtual ~RemoveNodeAndPruneCommandImpl();
+	
+    virtual int commandID() const;
+
+	virtual void doApply();
+
+    DOM::NodeImpl *node() const { return m_removeChild; }
+
+private:
+    DOM::NodeImpl *m_removeChild;
+};
+
+//------------------------------------------------------------------------------------------
 // SplitTextNodeCommandImpl
 
-class SplitTextNodeCommandImpl : public ModifyTextNodeCommandImpl
+class SplitTextNodeCommandImpl : public EditCommandImpl
 {
 public:
 	SplitTextNodeCommandImpl(DOM::DocumentImpl *, DOM::TextImpl *, long);
@@ -408,11 +428,13 @@ public:
 	virtual void doApply();
 	virtual void doUnapply();
 
-    virtual ECommandState joinState() { return Applied; }
-    virtual ECommandState splitState() { return NotApplied; }
-
     DOM::TextImpl *node() const { return m_text2; }
     long offset() const { return m_offset; }
+
+private:
+    DOM::TextImpl *m_text1;
+    DOM::TextImpl *m_text2;
+    unsigned long m_offset;
 };
 
 //------------------------------------------------------------------------------------------
@@ -436,6 +458,7 @@ public:
     void deleteKeyPressed();
 
 private:
+    void issueCommandForDeleteKey();
     void removeCommand(const EditCommand &);
     
     bool m_openForMoreTyping;
diff --git a/WebCore/khtml/editing/selection.cpp b/WebCore/khtml/editing/selection.cpp
index e5d20f5..46cb6ed 100644
--- a/WebCore/khtml/editing/selection.cpp
+++ b/WebCore/khtml/editing/selection.cpp
@@ -25,6 +25,7 @@
   
 #include "khtml_selection.h"
 
+#include "htmltags.h"
 #include "khtml_part.h"
 #include "khtmlview.h"
 #include "qevent.h"
@@ -38,6 +39,7 @@
 #include "rendering/render_style.h"
 #include "rendering/render_text.h"
 #include "xml/dom_docimpl.h"
+#include "xml/dom_edititerator.h"
 #include "xml/dom_elementimpl.h"
 #include "xml/dom_nodeimpl.h"
 #include "xml/dom_textimpl.h"
@@ -52,6 +54,7 @@
 using DOM::DocumentImpl;
 using DOM::DOMPosition;
 using DOM::DOMString;
+using DOM::EditIterator;
 using DOM::ElementImpl;
 using DOM::Node;
 using DOM::NodeImpl;
@@ -86,7 +89,7 @@ KHTMLSelection::KHTMLSelection(NodeImpl *node, long offset)
     validate();
 }
 
-KHTMLSelection::KHTMLSelection(const DOM::DOMPosition &pos)
+KHTMLSelection::KHTMLSelection(const DOMPosition &pos)
 {
     init();
 
@@ -98,6 +101,18 @@ KHTMLSelection::KHTMLSelection(const DOM::DOMPosition &pos)
     validate();
 }
 
+KHTMLSelection::KHTMLSelection(const DOMPosition &base, const DOMPosition &extent)
+{
+    init();
+
+	setBaseNode(base.node());
+	setExtentNode(extent.node());
+	setBaseOffset(base.offset());
+	setExtentOffset(extent.offset());
+
+    validate();
+}
+
 KHTMLSelection::KHTMLSelection(NodeImpl *baseNode, long baseOffset, NodeImpl *endNode, long endOffset)
 {
     init();
@@ -253,14 +268,14 @@ bool KHTMLSelection::modify(EAlter alter, EDirection dir, ETextGranularity elem)
                             setExtentNode(endNode());
                             setExtentOffset(endOffset());
                         }
-                        pos = nextCharacterPosition(DOMPosition(extentNode(), extentOffset()));
+                        pos = extentPosition().nextCharacterPosition();
                     }
                     else {
                         m_modifyBiasSet = false;
                         if (state() == RANGE)
-                            pos = DOMPosition(endNode(), endOffset());
+                            pos = endPosition();
                         else
-                            pos = nextCharacterPosition();
+                            pos = endPosition().nextCharacterPosition();
                     }
                     break;
                 case WORD:
@@ -284,14 +299,14 @@ bool KHTMLSelection::modify(EAlter alter, EDirection dir, ETextGranularity elem)
                             setExtentNode(startNode());
                             setExtentOffset(startOffset());
                         }
-                        pos = previousCharacterPosition(DOMPosition(extentNode(), extentOffset()));
+                        pos = extentPosition().previousCharacterPosition();
                     }
                     else {
                         m_modifyBiasSet = false;
                         if (state() == RANGE)
-                            pos = DOMPosition(startNode(), startOffset());
+                            pos = startPosition();
                         else
-                            pos = previousCharacterPosition();
+                            pos = startPosition().previousCharacterPosition();
                     }
                     break;
                 case WORD:
@@ -348,11 +363,6 @@ void KHTMLSelection::setNeedsLayout(bool flag)
     m_needsCaretLayout = flag;
 }
 
-bool KHTMLSelection::isEmpty() const
-{
-    return m_baseNode == 0 && m_extentNode == 0;
-}
-
 Range KHTMLSelection::toRange() const
 {
     if (isEmpty())
@@ -423,7 +433,7 @@ void KHTMLSelection::paintCaret(QPainter *p, const QRect &rect)
 
     if (m_needsCaretLayout) {
         DOMPosition pos = DOMPosition(startNode(), startOffset());
-        if (!inRenderedContent(pos)) {
+        if (!pos.inRenderedContent()) {
             moveToRenderedContent();
         }
         layoutCaret();
@@ -656,171 +666,6 @@ void KHTMLSelection::validate(ETextGranularity expandTo)
 #endif
 }
 
-DOMPosition KHTMLSelection::previousCharacterPosition() const
-{
-    return previousCharacterPosition(DOMPosition(startNode(), startOffset()));
-}
-
-DOMPosition KHTMLSelection::previousCharacterPosition(const DOMPosition &from)
-{
-    if (!from.node())
-        return from;
-
-	NodeImpl *node = from.node();
-	long offset = from.offset() - 1;
-
-    //
-    // Look in this renderer
-    //
-    RenderObject *renderer = node->renderer();
-    if (renderer->isText()) {
-        if (!renderer->isBR()) {
-            RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
-            for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
-                long start = box->m_start;
-                long end = box->m_start + box->m_len;
-                if (offset > end) {
-                    // Skip this node.
-                    // It is too early in the text runs to be involved.
-                    continue;
-                }
-                else if (offset >= start) {
-                    // Offset is in this node, return the start
-                    return DOMPosition(node, offset);
-                }
-            }
-        }
-    }
-    else {
-        // Offset is in this node, if:
-        // 1. greater than the min offset and less than or equal to the max caret offset
-        // 2. at the start of the editable content of the document
-        if ((offset > renderer->caretMinOffset() && offset <= renderer->caretMaxOffset()) || 
-            (offset == renderer->caretMinOffset() && !renderer->previousEditable()))
-            return DOMPosition(node, offset);
-    }
-
-    //
-    // Look in previous renderer(s)
-    //
-    renderer = renderer->previousEditable();
-    while (renderer) {
-        // Offset is in this node, if:
-        // 1. it is a BR which follows a line break
-        // 2. it is an element with content
-    	if (renderer->isBR()) {
-			if (renderer->followsLineBreak())
-				return DOMPosition(renderer->element(), renderer->caretMinOffset());
-    	}
-    	else {
-    		if (renderer->isText()) {
-    			 RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
-    			 if (!textRenderer->lastTextBox()) 
-    			 	continue;
-    		}
-            offset = renderer->caretMaxOffset();
-            if (renderer->nextEditable() && !renderer->precedesLineBreak())
-                offset--;
-            assert(offset >= 0);
-            return DOMPosition(renderer->element(), offset);
-    	}
-        renderer = renderer->previousEditable();
-    }
-
-    // can't move the position
-    return from;
-}
-
-
-DOMPosition KHTMLSelection::nextCharacterPosition() const
-{
-    return nextCharacterPosition(DOMPosition(endNode(), endOffset()));
-}
-
-DOMPosition KHTMLSelection::nextCharacterPosition(const DOMPosition &from)
-{
-    if (!from.node())
-        return DOMPosition();
-
- 	NodeImpl *node = from.node();
- 	long offset = from.offset() + 1;
-
-    //
-    // Look in this renderer
-    //
-    RenderObject *renderer = node->renderer();
-    if (renderer->isText()) {
-        if (!renderer->isBR()) {
-            RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
-            for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
-                long start = box->m_start;
-                long end = box->m_start + box->m_len;
-                if (offset > end) {
-                    // Skip this node.
-                    // It is too early in the text runs to be involved.
-                    continue;
-                }
-                else if (offset >= start) {
-                    // Offset is in this node, if:
-                    // Either it is:
-                    // 1. at or after the start and before, but not at the end
-                    // 2. at the end of a text run and is immediately followed by a line break
-                    // 3. at the end of the editable content of the document
-                    if (offset < end)
-                        return DOMPosition(node, offset);
-                    else if (offset == end && renderer->precedesLineBreak())
-                        return DOMPosition(node, offset);
-                    else if (offset == end && !box->nextTextBox() && !renderer->nextEditable())
-                        return DOMPosition(node, offset);
-                }
-                else if (offset < start) {
-                    // The offset we're looking for is before this node
-                    // this means the offset must be in content that is
-                    // not rendered. Just return the start of the node.
-                    return DOMPosition(node, start);
-                }
-            }
-        }
-    }
-    else {
-        // Offset is in this node, if:
-        // 1. before the max caret offset
-        // 2. equal to the max caret offset and is immediately preceded by a line break
-        // 3. at the end of the editable content of the document
-        if (offset < renderer->caretMaxOffset() ||
-            (offset == renderer->caretMaxOffset() && renderer->precedesLineBreak()) ||
-            (offset == renderer->caretMaxOffset() && !renderer->nextEditable()))
-                return DOMPosition(node, offset);
-    }
-
-    //
-    // Look in next renderer(s)
-    //
-    renderer = renderer->nextEditable();
-    while (renderer) {
-		// Offset is in this node, if:
-		// 1. it is a BR which follows a line break
-		// 2. it is a text element with content
-        // 3. it is a non-text element with content
-		if (renderer->isBR()) {
-			if (renderer->followsLineBreak())
-				return DOMPosition(renderer->element(), renderer->caretMinOffset());
-		}
-		else if (renderer->isText()) {
-            RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
-            if (textRenderer->firstTextBox())
-                return DOMPosition(renderer->element(), textRenderer->firstTextBox()->m_start);
-        }
-        else {
-            return DOMPosition(renderer->element(), renderer->caretMinOffset());
-        }
-        renderer = renderer->nextEditable();
-    }
-
-    // can't move the position
-    return from;
-}
-
 bool KHTMLSelection::moveToRenderedContent()
 {
     if (isEmpty())
@@ -830,58 +675,46 @@ bool KHTMLSelection::moveToRenderedContent()
         return false;
 
     DOMPosition pos = DOMPosition(startNode(), startOffset());
-    if (inRenderedContent(pos))
+    if (pos.inRenderedContent())
         return true;
         
-    // not currently rendered, try moving to next
-    DOMPosition next = nextCharacterPosition(pos);
-    if (next != pos) {
-        moveTo(next);
+    // not currently rendered, try moving to prev
+    DOMPosition prev = pos.previousCharacterPosition();
+    if (prev != pos && prev.node()->inSameContainingEditableBlock(pos.node())) {
+        moveTo(prev);
         return true;
     }
 
-    // could not be moved to next, try prev
-    DOMPosition prev = previousCharacterPosition(pos);
-    if (prev != pos) {
-        moveTo(prev);
+    // could not be moved to prev, try next
+    DOMPosition next = pos.nextCharacterPosition();
+    if (next != pos && next.node()->inSameContainingEditableBlock(pos.node())) {
+        moveTo(next);
         return true;
     }
     
     return false;
 }
 
-bool KHTMLSelection::inRenderedContent(const DOMPosition &pos)
+DOMPosition KHTMLSelection::basePosition() const
 {
-    if (pos.isEmpty())
-        return false;
-        
- 	long offset = pos.offset();
+    return DOMPosition(baseNode(), baseOffset());
+}
 
-    RenderObject *renderer = pos.node()->renderer();
-    if (!renderer)
-        return false;
-    
-    if (renderer->isText() && !renderer->isBR()) {
-        RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
-        for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
-            if (offset >= box->m_start && offset <= box->m_start + box->m_len) {
-                return true;
-            }
-            else if (offset < box->m_start) {
-                // The offset we're looking for is before this node
-                // this means the offset must be in content that is
-                // not rendered. Return false.
-                return false;
-            }
-        }
-    }
-    else if (offset >= renderer->caretMinOffset() && offset <= renderer->caretMaxOffset()) {
-        return true;
-    }
-    
-    return false;
+DOMPosition KHTMLSelection::extentPosition() const
+{
+    return DOMPosition(extentNode(), extentOffset());
 }
- 
+
+DOMPosition KHTMLSelection::startPosition() const
+{
+    return DOMPosition(startNode(), startOffset());
+}
+
+DOMPosition KHTMLSelection::endPosition() const
+{
+    return DOMPosition(endNode(), endOffset());
+}
+
 bool KHTMLSelection::nodeIsBeforeNode(NodeImpl *n1, NodeImpl *n2) 
 {
 	if (!n1 || !n2) 
@@ -1164,12 +997,38 @@ void KHTMLSelection::debugPosition() const
     if (!startNode())
         return;
 
-    static int context = 5;
+    //static int context = 5;
     
-    RenderObject *r = 0;
+    //RenderObject *r = 0;
 
     fprintf(stderr, "KHTMLSelection =================\n");
-    
+
+    if (startPosition() == endPosition()) {
+        DOMPosition pos = startPosition();
+        DOMPosition upstream = pos.equivalentUpstreamPosition();
+        DOMPosition downstream = pos.equivalentDownstreamPosition();
+        fprintf(stderr, "upstream:   %s %p:%d\n", getTagName(upstream.node()->id()).string().latin1(), upstream.node(), upstream.offset());
+        fprintf(stderr, "pos:        %s %p:%d\n", getTagName(pos.node()->id()).string().latin1(), pos.node(), pos.offset());
+        fprintf(stderr, "downstream: %s %p:%d\n", getTagName(downstream.node()->id()).string().latin1(), downstream.node(), downstream.offset());
+    }
+    else {
+        DOMPosition pos = endPosition();
+        DOMPosition upstream = pos.equivalentUpstreamPosition();
+        DOMPosition downstream = pos.equivalentDownstreamPosition();
+        fprintf(stderr, "upstream:   %s %p:%d\n", getTagName(upstream.node()->id()).string().latin1(), upstream.node(), upstream.offset());
+        fprintf(stderr, "start:      %s %p:%d\n", getTagName(pos.node()->id()).string().latin1(), pos.node(), pos.offset());
+        fprintf(stderr, "downstream: %s %p:%d\n", getTagName(downstream.node()->id()).string().latin1(), downstream.node(), downstream.offset());
+        fprintf(stderr, "-----------------------------------\n");
+        pos = startPosition();
+        upstream = pos.equivalentUpstreamPosition();
+        downstream = pos.equivalentDownstreamPosition();
+        fprintf(stderr, "upstream:   %s %p:%d\n", getTagName(upstream.node()->id()).string().latin1(), upstream.node(), upstream.offset());
+        fprintf(stderr, "end:        %s %p:%d\n", getTagName(pos.node()->id()).string().latin1(), pos.node(), pos.offset());
+        fprintf(stderr, "downstream: %s %p:%d\n", getTagName(downstream.node()->id()).string().latin1(), downstream.node(), downstream.offset());
+        fprintf(stderr, "-----------------------------------\n");
+    }
+          
+#if 0
     int back = 0;
     r = startNode()->renderer();
     for (int i = 0; i < context; i++, back++) {
@@ -1203,6 +1062,7 @@ void KHTMLSelection::debugPosition() const
         else
             break;
     }
+#endif
 
     fprintf(stderr, "================================\n");
 }
diff --git a/WebCore/khtml/editing/selection.h b/WebCore/khtml/editing/selection.h
index 2864eae..a52f542 100644
--- a/WebCore/khtml/editing/selection.h
+++ b/WebCore/khtml/editing/selection.h
@@ -46,6 +46,7 @@ public:
     KHTMLSelection();
     KHTMLSelection(DOM::NodeImpl *node, long offset);
     KHTMLSelection(const DOM::DOMPosition &);
+    KHTMLSelection(const DOM::DOMPosition &, const DOM::DOMPosition &);
     KHTMLSelection(DOM::NodeImpl *startNode, long startOffset, DOM::NodeImpl *endNode, long endOffset);
     KHTMLSelection(const KHTMLSelection &);
     ~KHTMLSelection();
@@ -83,15 +84,16 @@ public:
     DOM::NodeImpl *endNode() const { return m_endNode; }
     long endOffset() const { return m_endOffset; }
 
-    DOM::DOMPosition previousCharacterPosition() const;
-    static DOM::DOMPosition previousCharacterPosition(const DOM::DOMPosition &from);
-    DOM::DOMPosition nextCharacterPosition() const;
-    static DOM::DOMPosition nextCharacterPosition(const DOM::DOMPosition &from);
-        
+    DOM::DOMPosition basePosition() const;
+    DOM::DOMPosition extentPosition() const;
+    DOM::DOMPosition startPosition() const;
+    DOM::DOMPosition endPosition() const;
+
     void setNeedsLayout(bool flag=true);
     void clearModifyBias() { m_modifyBiasSet = false; }
     
-    bool isEmpty() const;
+    bool isEmpty() const { return state() == NONE; }
+    bool notEmpty() const { return !isEmpty(); }
     DOM::Range toRange() const;
 
     
@@ -124,7 +126,6 @@ private:
 	void setEndNode(DOM::NodeImpl *);
 	void setEndOffset(long);
 
-    bool inRenderedContent(const DOM::DOMPosition &);
     bool nodeIsBeforeNode(DOM::NodeImpl *n1, DOM::NodeImpl *n2);
 
     void calculateStartAndEnd(ETextGranularity select=CHARACTER);
diff --git a/WebCore/khtml/html/html_elementimpl.cpp b/WebCore/khtml/html/html_elementimpl.cpp
index aa756a6..a0476ea 100644
--- a/WebCore/khtml/html/html_elementimpl.cpp
+++ b/WebCore/khtml/html/html_elementimpl.cpp
@@ -819,8 +819,16 @@ bool HTMLElementImpl::isFocusable() const
     return isContentEditable() && !parent()->isContentEditable();
 }
 
-bool HTMLElementImpl::isContentEditable() const {
-    return contentEditable() == "true";
+bool HTMLElementImpl::isContentEditable() const 
+{
+    if (!renderer()) {
+        if (parentNode())
+            return parentNode()->isContentEditable();
+        else
+            return false;
+    }
+    
+    return renderer()->style()->userModify() == READ_WRITE;
 }
 
 DOMString HTMLElementImpl::contentEditable() const {
diff --git a/WebCore/khtml/khtml_part.cpp b/WebCore/khtml/khtml_part.cpp
index d3708e8..695c9e0 100644
--- a/WebCore/khtml/khtml_part.cpp
+++ b/WebCore/khtml/khtml_part.cpp
@@ -99,7 +99,6 @@ using namespace DOM;
 #endif
 
 using khtml::Decoder;
-using khtml::DeleteKeyCommand;
 using khtml::DeleteSelectionCommand;
 using khtml::EditCommand;
 using khtml::InlineTextBox;
@@ -2472,6 +2471,7 @@ const KHTMLSelection &KHTMLPart::selection() const
 void KHTMLPart::setSelection(const KHTMLSelection &s)
 {
     if (d->m_selection != s) {
+        clearCaretRectIfNeeded(); 
         d->m_selection = s;
         notifySelectionChanged();
     }
@@ -2486,6 +2486,7 @@ void KHTMLPart::takeSelectionFrom(const EditCommand &cmd, bool useEndingSelectio
         s = cmd.startingSelection();
     
     if (d->m_selection != s) {
+        clearCaretRectIfNeeded();        
         d->m_selection = s;
         notifySelectionChanged(false);
     }
@@ -2493,6 +2494,7 @@ void KHTMLPart::takeSelectionFrom(const EditCommand &cmd, bool useEndingSelectio
 
 void KHTMLPart::clearSelection()
 {
+    clearCaretRectIfNeeded();
     d->m_selection = KHTMLSelection();
     notifySelectionChanged();
 }
@@ -2505,6 +2507,7 @@ void KHTMLPart::deleteSelection()
 
 void KHTMLPart::invalidateSelection()
 {
+    clearCaretRectIfNeeded();
     d->m_selection.setNeedsLayout();
     notifySelectionChanged(false);
 }
@@ -2513,19 +2516,29 @@ void KHTMLPart::setSelectionVisible(bool flag)
 {
     if (d->m_caretVisible == flag)
         return;
-        
+
+    clearCaretRectIfNeeded();
     d->m_caretVisible = flag;
     notifySelectionChanged();
 }
 
 void KHTMLPart::slotClearSelection()
 {
+    clearCaretRectIfNeeded();
     bool hadSelection = hasSelection();
     d->m_selection.clear();
     if (hadSelection)
         notifySelectionChanged();
 }
 
+void KHTMLPart::clearCaretRectIfNeeded()
+{
+    if (d->m_caretPaint) {
+        d->m_caretPaint = false;
+        d->m_selection.needsCaretRepaint();
+    }        
+}
+
 void KHTMLPart::notifySelectionChanged(bool endTyping)
 {
     // kill any caret blink timer now running
@@ -2541,10 +2554,6 @@ void KHTMLPart::notifySelectionChanged(bool endTyping)
         d->m_caretPaint = true;
         d->m_selection.needsCaretRepaint();
     }
-    else if (d->m_caretPaint) {
-        d->m_caretPaint = false;
-        d->m_selection.needsCaretRepaint();
-    }
 
     if (d->m_doc)
         d->m_doc->updateSelection();
@@ -5101,10 +5110,12 @@ void KHTMLPart::appliedEditing(EditCommand &cmd)
 
 void KHTMLPart::unappliedEditing(EditCommand &cmd)
 {
+    TypingCommand::closeTyping(lastEditCommand());
+
 #if APPLE_CHANGES
     KWQ(this)->registerCommandForRedo(cmd);
 #endif
-    d->m_lastEditCommand = EditCommand();
+    d->m_lastEditCommand = EditCommand::emptyCommand();
 }
 
 void KHTMLPart::reappliedEditing(EditCommand &cmd)
@@ -5112,7 +5123,7 @@ void KHTMLPart::reappliedEditing(EditCommand &cmd)
 #if APPLE_CHANGES
     KWQ(this)->registerCommandForUndo(cmd);
 #endif
-    d->m_lastEditCommand = EditCommand();
+    d->m_lastEditCommand = EditCommand::emptyCommand();
 }
 
 void KHTMLPart::pasteHTMLString(const QString &HTMLString)
diff --git a/WebCore/khtml/khtml_part.h b/WebCore/khtml/khtml_part.h
index 4d55874..22678e5 100644
--- a/WebCore/khtml/khtml_part.h
+++ b/WebCore/khtml/khtml_part.h
@@ -1096,6 +1096,11 @@ private:
   /**
    * @internal
    */
+  void clearCaretRectIfNeeded();
+
+  /**
+   * @internal
+   */
   void notifySelectionChanged(bool endTyping=true);
 
   /**
diff --git a/WebCore/khtml/khtml_selection.cpp b/WebCore/khtml/khtml_selection.cpp
index e5d20f5..46cb6ed 100644
--- a/WebCore/khtml/khtml_selection.cpp
+++ b/WebCore/khtml/khtml_selection.cpp
@@ -25,6 +25,7 @@
   
 #include "khtml_selection.h"
 
+#include "htmltags.h"
 #include "khtml_part.h"
 #include "khtmlview.h"
 #include "qevent.h"
@@ -38,6 +39,7 @@
 #include "rendering/render_style.h"
 #include "rendering/render_text.h"
 #include "xml/dom_docimpl.h"
+#include "xml/dom_edititerator.h"
 #include "xml/dom_elementimpl.h"
 #include "xml/dom_nodeimpl.h"
 #include "xml/dom_textimpl.h"
@@ -52,6 +54,7 @@
 using DOM::DocumentImpl;
 using DOM::DOMPosition;
 using DOM::DOMString;
+using DOM::EditIterator;
 using DOM::ElementImpl;
 using DOM::Node;
 using DOM::NodeImpl;
@@ -86,7 +89,7 @@ KHTMLSelection::KHTMLSelection(NodeImpl *node, long offset)
     validate();
 }
 
-KHTMLSelection::KHTMLSelection(const DOM::DOMPosition &pos)
+KHTMLSelection::KHTMLSelection(const DOMPosition &pos)
 {
     init();
 
@@ -98,6 +101,18 @@ KHTMLSelection::KHTMLSelection(const DOM::DOMPosition &pos)
     validate();
 }
 
+KHTMLSelection::KHTMLSelection(const DOMPosition &base, const DOMPosition &extent)
+{
+    init();
+
+	setBaseNode(base.node());
+	setExtentNode(extent.node());
+	setBaseOffset(base.offset());
+	setExtentOffset(extent.offset());
+
+    validate();
+}
+
 KHTMLSelection::KHTMLSelection(NodeImpl *baseNode, long baseOffset, NodeImpl *endNode, long endOffset)
 {
     init();
@@ -253,14 +268,14 @@ bool KHTMLSelection::modify(EAlter alter, EDirection dir, ETextGranularity elem)
                             setExtentNode(endNode());
                             setExtentOffset(endOffset());
                         }
-                        pos = nextCharacterPosition(DOMPosition(extentNode(), extentOffset()));
+                        pos = extentPosition().nextCharacterPosition();
                     }
                     else {
                         m_modifyBiasSet = false;
                         if (state() == RANGE)
-                            pos = DOMPosition(endNode(), endOffset());
+                            pos = endPosition();
                         else
-                            pos = nextCharacterPosition();
+                            pos = endPosition().nextCharacterPosition();
                     }
                     break;
                 case WORD:
@@ -284,14 +299,14 @@ bool KHTMLSelection::modify(EAlter alter, EDirection dir, ETextGranularity elem)
                             setExtentNode(startNode());
                             setExtentOffset(startOffset());
                         }
-                        pos = previousCharacterPosition(DOMPosition(extentNode(), extentOffset()));
+                        pos = extentPosition().previousCharacterPosition();
                     }
                     else {
                         m_modifyBiasSet = false;
                         if (state() == RANGE)
-                            pos = DOMPosition(startNode(), startOffset());
+                            pos = startPosition();
                         else
-                            pos = previousCharacterPosition();
+                            pos = startPosition().previousCharacterPosition();
                     }
                     break;
                 case WORD:
@@ -348,11 +363,6 @@ void KHTMLSelection::setNeedsLayout(bool flag)
     m_needsCaretLayout = flag;
 }
 
-bool KHTMLSelection::isEmpty() const
-{
-    return m_baseNode == 0 && m_extentNode == 0;
-}
-
 Range KHTMLSelection::toRange() const
 {
     if (isEmpty())
@@ -423,7 +433,7 @@ void KHTMLSelection::paintCaret(QPainter *p, const QRect &rect)
 
     if (m_needsCaretLayout) {
         DOMPosition pos = DOMPosition(startNode(), startOffset());
-        if (!inRenderedContent(pos)) {
+        if (!pos.inRenderedContent()) {
             moveToRenderedContent();
         }
         layoutCaret();
@@ -656,171 +666,6 @@ void KHTMLSelection::validate(ETextGranularity expandTo)
 #endif
 }
 
-DOMPosition KHTMLSelection::previousCharacterPosition() const
-{
-    return previousCharacterPosition(DOMPosition(startNode(), startOffset()));
-}
-
-DOMPosition KHTMLSelection::previousCharacterPosition(const DOMPosition &from)
-{
-    if (!from.node())
-        return from;
-
-	NodeImpl *node = from.node();
-	long offset = from.offset() - 1;
-
-    //
-    // Look in this renderer
-    //
-    RenderObject *renderer = node->renderer();
-    if (renderer->isText()) {
-        if (!renderer->isBR()) {
-            RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
-            for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
-                long start = box->m_start;
-                long end = box->m_start + box->m_len;
-                if (offset > end) {
-                    // Skip this node.
-                    // It is too early in the text runs to be involved.
-                    continue;
-                }
-                else if (offset >= start) {
-                    // Offset is in this node, return the start
-                    return DOMPosition(node, offset);
-                }
-            }
-        }
-    }
-    else {
-        // Offset is in this node, if:
-        // 1. greater than the min offset and less than or equal to the max caret offset
-        // 2. at the start of the editable content of the document
-        if ((offset > renderer->caretMinOffset() && offset <= renderer->caretMaxOffset()) || 
-            (offset == renderer->caretMinOffset() && !renderer->previousEditable()))
-            return DOMPosition(node, offset);
-    }
-
-    //
-    // Look in previous renderer(s)
-    //
-    renderer = renderer->previousEditable();
-    while (renderer) {
-        // Offset is in this node, if:
-        // 1. it is a BR which follows a line break
-        // 2. it is an element with content
-    	if (renderer->isBR()) {
-			if (renderer->followsLineBreak())
-				return DOMPosition(renderer->element(), renderer->caretMinOffset());
-    	}
-    	else {
-    		if (renderer->isText()) {
-    			 RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
-    			 if (!textRenderer->lastTextBox()) 
-    			 	continue;
-    		}
-            offset = renderer->caretMaxOffset();
-            if (renderer->nextEditable() && !renderer->precedesLineBreak())
-                offset--;
-            assert(offset >= 0);
-            return DOMPosition(renderer->element(), offset);
-    	}
-        renderer = renderer->previousEditable();
-    }
-
-    // can't move the position
-    return from;
-}
-
-
-DOMPosition KHTMLSelection::nextCharacterPosition() const
-{
-    return nextCharacterPosition(DOMPosition(endNode(), endOffset()));
-}
-
-DOMPosition KHTMLSelection::nextCharacterPosition(const DOMPosition &from)
-{
-    if (!from.node())
-        return DOMPosition();
-
- 	NodeImpl *node = from.node();
- 	long offset = from.offset() + 1;
-
-    //
-    // Look in this renderer
-    //
-    RenderObject *renderer = node->renderer();
-    if (renderer->isText()) {
-        if (!renderer->isBR()) {
-            RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
-            for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
-                long start = box->m_start;
-                long end = box->m_start + box->m_len;
-                if (offset > end) {
-                    // Skip this node.
-                    // It is too early in the text runs to be involved.
-                    continue;
-                }
-                else if (offset >= start) {
-                    // Offset is in this node, if:
-                    // Either it is:
-                    // 1. at or after the start and before, but not at the end
-                    // 2. at the end of a text run and is immediately followed by a line break
-                    // 3. at the end of the editable content of the document
-                    if (offset < end)
-                        return DOMPosition(node, offset);
-                    else if (offset == end && renderer->precedesLineBreak())
-                        return DOMPosition(node, offset);
-                    else if (offset == end && !box->nextTextBox() && !renderer->nextEditable())
-                        return DOMPosition(node, offset);
-                }
-                else if (offset < start) {
-                    // The offset we're looking for is before this node
-                    // this means the offset must be in content that is
-                    // not rendered. Just return the start of the node.
-                    return DOMPosition(node, start);
-                }
-            }
-        }
-    }
-    else {
-        // Offset is in this node, if:
-        // 1. before the max caret offset
-        // 2. equal to the max caret offset and is immediately preceded by a line break
-        // 3. at the end of the editable content of the document
-        if (offset < renderer->caretMaxOffset() ||
-            (offset == renderer->caretMaxOffset() && renderer->precedesLineBreak()) ||
-            (offset == renderer->caretMaxOffset() && !renderer->nextEditable()))
-                return DOMPosition(node, offset);
-    }
-
-    //
-    // Look in next renderer(s)
-    //
-    renderer = renderer->nextEditable();
-    while (renderer) {
-		// Offset is in this node, if:
-		// 1. it is a BR which follows a line break
-		// 2. it is a text element with content
-        // 3. it is a non-text element with content
-		if (renderer->isBR()) {
-			if (renderer->followsLineBreak())
-				return DOMPosition(renderer->element(), renderer->caretMinOffset());
-		}
-		else if (renderer->isText()) {
-            RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
-            if (textRenderer->firstTextBox())
-                return DOMPosition(renderer->element(), textRenderer->firstTextBox()->m_start);
-        }
-        else {
-            return DOMPosition(renderer->element(), renderer->caretMinOffset());
-        }
-        renderer = renderer->nextEditable();
-    }
-
-    // can't move the position
-    return from;
-}
-
 bool KHTMLSelection::moveToRenderedContent()
 {
     if (isEmpty())
@@ -830,58 +675,46 @@ bool KHTMLSelection::moveToRenderedContent()
         return false;
 
     DOMPosition pos = DOMPosition(startNode(), startOffset());
-    if (inRenderedContent(pos))
+    if (pos.inRenderedContent())
         return true;
         
-    // not currently rendered, try moving to next
-    DOMPosition next = nextCharacterPosition(pos);
-    if (next != pos) {
-        moveTo(next);
+    // not currently rendered, try moving to prev
+    DOMPosition prev = pos.previousCharacterPosition();
+    if (prev != pos && prev.node()->inSameContainingEditableBlock(pos.node())) {
+        moveTo(prev);
         return true;
     }
 
-    // could not be moved to next, try prev
-    DOMPosition prev = previousCharacterPosition(pos);
-    if (prev != pos) {
-        moveTo(prev);
+    // could not be moved to prev, try next
+    DOMPosition next = pos.nextCharacterPosition();
+    if (next != pos && next.node()->inSameContainingEditableBlock(pos.node())) {
+        moveTo(next);
         return true;
     }
     
     return false;
 }
 
-bool KHTMLSelection::inRenderedContent(const DOMPosition &pos)
+DOMPosition KHTMLSelection::basePosition() const
 {
-    if (pos.isEmpty())
-        return false;
-        
- 	long offset = pos.offset();
+    return DOMPosition(baseNode(), baseOffset());
+}
 
-    RenderObject *renderer = pos.node()->renderer();
-    if (!renderer)
-        return false;
-    
-    if (renderer->isText() && !renderer->isBR()) {
-        RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
-        for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
-            if (offset >= box->m_start && offset <= box->m_start + box->m_len) {
-                return true;
-            }
-            else if (offset < box->m_start) {
-                // The offset we're looking for is before this node
-                // this means the offset must be in content that is
-                // not rendered. Return false.
-                return false;
-            }
-        }
-    }
-    else if (offset >= renderer->caretMinOffset() && offset <= renderer->caretMaxOffset()) {
-        return true;
-    }
-    
-    return false;
+DOMPosition KHTMLSelection::extentPosition() const
+{
+    return DOMPosition(extentNode(), extentOffset());
 }
- 
+
+DOMPosition KHTMLSelection::startPosition() const
+{
+    return DOMPosition(startNode(), startOffset());
+}
+
+DOMPosition KHTMLSelection::endPosition() const
+{
+    return DOMPosition(endNode(), endOffset());
+}
+
 bool KHTMLSelection::nodeIsBeforeNode(NodeImpl *n1, NodeImpl *n2) 
 {
 	if (!n1 || !n2) 
@@ -1164,12 +997,38 @@ void KHTMLSelection::debugPosition() const
     if (!startNode())
         return;
 
-    static int context = 5;
+    //static int context = 5;
     
-    RenderObject *r = 0;
+    //RenderObject *r = 0;
 
     fprintf(stderr, "KHTMLSelection =================\n");
-    
+
+    if (startPosition() == endPosition()) {
+        DOMPosition pos = startPosition();
+        DOMPosition upstream = pos.equivalentUpstreamPosition();
+        DOMPosition downstream = pos.equivalentDownstreamPosition();
+        fprintf(stderr, "upstream:   %s %p:%d\n", getTagName(upstream.node()->id()).string().latin1(), upstream.node(), upstream.offset());
+        fprintf(stderr, "pos:        %s %p:%d\n", getTagName(pos.node()->id()).string().latin1(), pos.node(), pos.offset());
+        fprintf(stderr, "downstream: %s %p:%d\n", getTagName(downstream.node()->id()).string().latin1(), downstream.node(), downstream.offset());
+    }
+    else {
+        DOMPosition pos = endPosition();
+        DOMPosition upstream = pos.equivalentUpstreamPosition();
+        DOMPosition downstream = pos.equivalentDownstreamPosition();
+        fprintf(stderr, "upstream:   %s %p:%d\n", getTagName(upstream.node()->id()).string().latin1(), upstream.node(), upstream.offset());
+        fprintf(stderr, "start:      %s %p:%d\n", getTagName(pos.node()->id()).string().latin1(), pos.node(), pos.offset());
+        fprintf(stderr, "downstream: %s %p:%d\n", getTagName(downstream.node()->id()).string().latin1(), downstream.node(), downstream.offset());
+        fprintf(stderr, "-----------------------------------\n");
+        pos = startPosition();
+        upstream = pos.equivalentUpstreamPosition();
+        downstream = pos.equivalentDownstreamPosition();
+        fprintf(stderr, "upstream:   %s %p:%d\n", getTagName(upstream.node()->id()).string().latin1(), upstream.node(), upstream.offset());
+        fprintf(stderr, "end:        %s %p:%d\n", getTagName(pos.node()->id()).string().latin1(), pos.node(), pos.offset());
+        fprintf(stderr, "downstream: %s %p:%d\n", getTagName(downstream.node()->id()).string().latin1(), downstream.node(), downstream.offset());
+        fprintf(stderr, "-----------------------------------\n");
+    }
+          
+#if 0
     int back = 0;
     r = startNode()->renderer();
     for (int i = 0; i < context; i++, back++) {
@@ -1203,6 +1062,7 @@ void KHTMLSelection::debugPosition() const
         else
             break;
     }
+#endif
 
     fprintf(stderr, "================================\n");
 }
diff --git a/WebCore/khtml/khtml_selection.h b/WebCore/khtml/khtml_selection.h
index 2864eae..a52f542 100644
--- a/WebCore/khtml/khtml_selection.h
+++ b/WebCore/khtml/khtml_selection.h
@@ -46,6 +46,7 @@ public:
     KHTMLSelection();
     KHTMLSelection(DOM::NodeImpl *node, long offset);
     KHTMLSelection(const DOM::DOMPosition &);
+    KHTMLSelection(const DOM::DOMPosition &, const DOM::DOMPosition &);
     KHTMLSelection(DOM::NodeImpl *startNode, long startOffset, DOM::NodeImpl *endNode, long endOffset);
     KHTMLSelection(const KHTMLSelection &);
     ~KHTMLSelection();
@@ -83,15 +84,16 @@ public:
     DOM::NodeImpl *endNode() const { return m_endNode; }
     long endOffset() const { return m_endOffset; }
 
-    DOM::DOMPosition previousCharacterPosition() const;
-    static DOM::DOMPosition previousCharacterPosition(const DOM::DOMPosition &from);
-    DOM::DOMPosition nextCharacterPosition() const;
-    static DOM::DOMPosition nextCharacterPosition(const DOM::DOMPosition &from);
-        
+    DOM::DOMPosition basePosition() const;
+    DOM::DOMPosition extentPosition() const;
+    DOM::DOMPosition startPosition() const;
+    DOM::DOMPosition endPosition() const;
+
     void setNeedsLayout(bool flag=true);
     void clearModifyBias() { m_modifyBiasSet = false; }
     
-    bool isEmpty() const;
+    bool isEmpty() const { return state() == NONE; }
+    bool notEmpty() const { return !isEmpty(); }
     DOM::Range toRange() const;
 
     
@@ -124,7 +126,6 @@ private:
 	void setEndNode(DOM::NodeImpl *);
 	void setEndOffset(long);
 
-    bool inRenderedContent(const DOM::DOMPosition &);
     bool nodeIsBeforeNode(DOM::NodeImpl *n1, DOM::NodeImpl *n2);
 
     void calculateStartAndEnd(ETextGranularity select=CHARACTER);
diff --git a/WebCore/khtml/rendering/bidi.cpp b/WebCore/khtml/rendering/bidi.cpp
index c4ad942..2235232 100644
--- a/WebCore/khtml/rendering/bidi.cpp
+++ b/WebCore/khtml/rendering/bidi.cpp
@@ -1542,6 +1542,10 @@ QRect RenderBlock::layoutInlineChildren(bool relayoutChildren)
     
     setLinesAppended(false);
     
+    if (!firstLineBox() && element() && element()->containingEditableBlock() == element()) {
+        m_height += lineHeight(true);
+    }
+    
     return repaintRect;
 
 #if BIDI_DEBUG > 1
diff --git a/WebCore/khtml/rendering/render_br.cpp b/WebCore/khtml/rendering/render_br.cpp
index e6fb2a0..aa0d9a6 100644
--- a/WebCore/khtml/rendering/render_br.cpp
+++ b/WebCore/khtml/rendering/render_br.cpp
@@ -107,11 +107,16 @@ long RenderBR::caretMaxOffset() const
     return 1; 
 }
 
+unsigned long RenderBR::caretMaxRenderedOffset() const
+{
+    return 1;
+}
+
 void RenderBR::caretPos(int offset, bool override, int &_x, int &_y, int &_w, int &_h)
 {
     // EDIT FIXME: This does not work yet. Some other changes are need before
     // an accurate position can be determined.
-    _h = height();
+    _h = lineHeight(false);
     _x = xPos();
     _y = yPos();
 
diff --git a/WebCore/khtml/rendering/render_br.h b/WebCore/khtml/rendering/render_br.h
index 2426821..998451f 100644
--- a/WebCore/khtml/rendering/render_br.h
+++ b/WebCore/khtml/rendering/render_br.h
@@ -61,6 +61,7 @@ public:
 
     virtual long caretMinOffset() const;
     virtual long caretMaxOffset() const;
+    virtual unsigned long caretMaxRenderedOffset() const;
     
     virtual void caretPos(int offset, bool override, int &_x, int &_y, int &_w, int &_h);
     
diff --git a/WebCore/khtml/rendering/render_flow.cpp b/WebCore/khtml/rendering/render_flow.cpp
index 4a7713c..e6fa455 100644
--- a/WebCore/khtml/rendering/render_flow.cpp
+++ b/WebCore/khtml/rendering/render_flow.cpp
@@ -476,7 +476,7 @@ int RenderFlow::leftmostPosition(bool includeOverflowInterior, bool includeSelf)
 
 void RenderFlow::caretPos(int offset, bool override, int &_x, int &_y, int &width, int &height)
 {
-    if (firstChild() || style()->display() == INLINE) { 
+    if (firstChild() || style()->display() == INLINE) {
         // Do the normal calculation
         RenderBox::caretPos(offset, override, _x, _y, width, height);
         return;
@@ -492,7 +492,8 @@ void RenderFlow::caretPos(int offset, bool override, int &_x, int &_y, int &widt
     // the caret size of an empty :first-line'd block is wrong, but I think we
     // can live with that.
     RenderStyle *currentStyle = style(true);
-    height = currentStyle->fontMetrics().height();
+    //height = currentStyle->fontMetrics().height();
+    height = lineHeight(true);
     width = 1;
 
     // EDIT FIXME: This needs to account for text direction
@@ -518,6 +519,6 @@ void RenderFlow::caretPos(int offset, bool override, int &_x, int &_y, int &widt
     
     int absx, absy;
     absolutePosition(absx, absy, false);
-    _x += absx;
-    _y += absy;
+    _x += absx + paddingLeft() + borderLeft();
+    _y += absy + paddingTop() + borderTop();
 }
diff --git a/WebCore/khtml/rendering/render_line.cpp b/WebCore/khtml/rendering/render_line.cpp
index 6e9c6dc..63fc074 100644
--- a/WebCore/khtml/rendering/render_line.cpp
+++ b/WebCore/khtml/rendering/render_line.cpp
@@ -90,6 +90,11 @@ long InlineBox::caretMaxOffset() const
     return 1; 
 }
 
+unsigned long InlineBox::caretMaxRenderedOffset() const 
+{ 
+    return 1; 
+}
+
 void InlineBox::dirtyLineBoxes()
 {
     markDirty();
diff --git a/WebCore/khtml/rendering/render_line.h b/WebCore/khtml/rendering/render_line.h
index b85c4a0..3b89d9d 100644
--- a/WebCore/khtml/rendering/render_line.h
+++ b/WebCore/khtml/rendering/render_line.h
@@ -119,6 +119,7 @@ public:
 
     virtual long caretMinOffset() const;
     virtual long caretMaxOffset() const;
+    virtual unsigned long caretMaxRenderedOffset() const;
     
     bool isDirty() const { return m_dirty; }
     void markDirty(bool dirty=true) { m_dirty = dirty; }
diff --git a/WebCore/khtml/rendering/render_object.cpp b/WebCore/khtml/rendering/render_object.cpp
index 7afaa14..8a2dff6 100644
--- a/WebCore/khtml/rendering/render_object.cpp
+++ b/WebCore/khtml/rendering/render_object.cpp
@@ -217,34 +217,6 @@ void RenderObject::insertChildNode(RenderObject*, RenderObject*)
     KHTMLAssert(0);
 }
 
-bool RenderObject::precedesLineBreak() const
-{
-    RenderObject *r = nextRenderer();
-    while (r) {
-        if (r->isBR() || r->isRenderBlock())
-            return true;
-        if (r->isText() || r->isReplaced())
-            return false;
-        r = r->nextRenderer();
-    }
-    
-    return false;
-}
-
-bool RenderObject::followsLineBreak() const
-{
-    RenderObject *r = previousRenderer();
-    while (r) {
-        if (r->isBR() || r->isRenderBlock())
-            return true;
-        if (r->isText() || r->isReplaced())
-            return false;
-        r = r->previousRenderer();
-    }
-    
-    return false;
-}
-
 RenderObject *RenderObject::nextRenderer() const
 {
     if (firstChild())
@@ -286,7 +258,10 @@ bool RenderObject::isEditable() const
 
     return style()->visibility() == VISIBLE && 
         element() && element()->isContentEditable() &&
-        (isReplaced() || isBR() || (textRenderer && textRenderer->firstTextBox()));
+        ((isBlockFlow() && !firstChild()) || 
+        isReplaced() || 
+        isBR() || 
+        (textRenderer && textRenderer->firstTextBox()));
 }
 
 RenderObject *RenderObject::nextEditable() const
@@ -2180,3 +2155,18 @@ int RenderObject::maximalOutlineSize(PaintAction p) const
         return 0;
     return static_cast<RenderCanvas*>(document()->renderer())->maximalOutlineSize();
 }
+
+long RenderObject::caretMinOffset() const
+{
+    return 0;
+}
+
+long RenderObject::caretMaxOffset() const
+{
+    return 0;
+}
+
+unsigned long RenderObject::caretMaxRenderedOffset() const
+{
+    return 0;
+}
diff --git a/WebCore/khtml/rendering/render_object.h b/WebCore/khtml/rendering/render_object.h
index 5ac2664..3bdc19e 100644
--- a/WebCore/khtml/rendering/render_object.h
+++ b/WebCore/khtml/rendering/render_object.h
@@ -717,12 +717,10 @@ public:
     // Convenience, to avoid repeating the code to dig down to get this.
     QChar backslashAsCurrencySymbol() const;
 
-    virtual long caretMinOffset() const { return 0; }
-    virtual long caretMaxOffset() const { return 0; }
+    virtual long caretMinOffset() const;
+    virtual long caretMaxOffset() const;
+    virtual unsigned long caretMaxRenderedOffset() const;
 
-    bool precedesLineBreak() const;
-    bool followsLineBreak() const;
-            
     virtual void setPixmap(const QPixmap&, const QRect&, CachedImage *);
 
 protected:
diff --git a/WebCore/khtml/rendering/render_replaced.cpp b/WebCore/khtml/rendering/render_replaced.cpp
index 9546550..bdb7385 100644
--- a/WebCore/khtml/rendering/render_replaced.cpp
+++ b/WebCore/khtml/rendering/render_replaced.cpp
@@ -121,6 +121,11 @@ long RenderReplaced::caretMaxOffset() const
     return 1; 
 }
 
+unsigned long RenderReplaced::caretMaxRenderedOffset() const
+{
+    return 1; 
+}
+
 // -----------------------------------------------------------------------------
 
 RenderWidget::RenderWidget(DOM::NodeImpl* node)
diff --git a/WebCore/khtml/rendering/render_replaced.h b/WebCore/khtml/rendering/render_replaced.h
index 0d44853..b01bbd3 100644
--- a/WebCore/khtml/rendering/render_replaced.h
+++ b/WebCore/khtml/rendering/render_replaced.h
@@ -54,7 +54,8 @@ public:
 
     virtual long caretMinOffset() const;
     virtual long caretMaxOffset() const;
-
+    virtual unsigned long caretMaxRenderedOffset() const;
+    
 private:
     int m_intrinsicWidth;
     int m_intrinsicHeight;
diff --git a/WebCore/khtml/rendering/render_text.cpp b/WebCore/khtml/rendering/render_text.cpp
index 8e647b0..3cae19a 100644
--- a/WebCore/khtml/rendering/render_text.cpp
+++ b/WebCore/khtml/rendering/render_text.cpp
@@ -195,6 +195,11 @@ long InlineTextBox::caretMaxOffset() const
     return m_start + m_len;
 }
 
+unsigned long InlineTextBox::caretMaxRenderedOffset() const
+{
+    return m_start + m_len;
+}
+
 #define LOCAL_WIDTH_BUF_SIZE	1024
 
 FindSelectionResult InlineTextBox::checkSelectionPoint(int _x, int _y, int _tx, int _ty, const Font *f, RenderText *text, int & offset, short lineHeight)
@@ -319,11 +324,11 @@ RenderText::~RenderText()
 void RenderText::detach()
 {
     if (!documentBeingDestroyed()) {
+        if (parent() && isBR())
+            parent()->dirtyLinesFromChangedChild(this);
         if (firstTextBox())
             for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
                 box->remove();
-        else if (parent() && isBR())
-            parent()->dirtyLinesFromChangedChild(this);
     }
     deleteTextBoxes();
     RenderObject::detach();
@@ -1453,6 +1458,14 @@ long RenderText::caretMaxOffset() const
     return lastTextBox()->m_start + lastTextBox()->m_len;
 }
 
+unsigned long RenderText::caretMaxRenderedOffset() const
+{
+    int l = 0;
+    for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
+        l += box->m_len;
+    return l;
+}
+
 RenderTextFragment::RenderTextFragment(DOM::NodeImpl* _node, DOM::DOMStringImpl* _str,
                                        int startOffset, int endOffset)
 :RenderText(_node, _str->substring(startOffset, endOffset)), 
diff --git a/WebCore/khtml/rendering/render_text.h b/WebCore/khtml/rendering/render_text.h
index 9b62137..440003a 100644
--- a/WebCore/khtml/rendering/render_text.h
+++ b/WebCore/khtml/rendering/render_text.h
@@ -61,6 +61,7 @@ public:
     
     uint start() const { return m_start; }
     uint end() const { return m_len ? m_start+m_len-1 : m_start; }
+    uint len() const { return m_len; }
 
     void offsetRun(int d) { m_start += d; }
 
@@ -92,6 +93,7 @@ public:
 
     virtual long caretMinOffset() const;
     virtual long caretMaxOffset() const;
+    virtual unsigned long caretMaxRenderedOffset() const;
     
     // Return before, after (offset set to max), or inside the text, at @p offset
     FindSelectionResult checkSelectionPoint(int _x, int _y, int _tx, int _ty, const Font *f, RenderText *text, int & offset, short lineheight);
@@ -224,6 +226,7 @@ public:
 
     virtual long caretMinOffset() const;
     virtual long caretMaxOffset() const;
+    virtual unsigned long caretMaxRenderedOffset() const;
     
 #if APPLE_CHANGES
 public:
diff --git a/WebCore/kwq/KWQComboBox.h b/WebCore/khtml/xml/dom_edititerator.cpp
similarity index 52%
copy from WebCore/kwq/KWQComboBox.h
copy to WebCore/khtml/xml/dom_edititerator.cpp
index b3712b4..22f7668 100644
--- a/WebCore/kwq/KWQComboBox.h
+++ b/WebCore/khtml/xml/dom_edititerator.cpp
@@ -23,62 +23,66 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
  */
 
-#ifndef QCOMBOBOX_H_
-#define QCOMBOBOX_H_
+#include "dom_edititerator.h"
 
-#include "KWQWidget.h"
-#include "KWQStringList.h"
+#include "dom_nodeimpl.h"
 
-class QListBox;
+namespace DOM {
 
-#ifdef __OBJC__
- at class KWQComboBoxAdapter;
-#else
-class KWQComboBoxAdapter;
-#endif
-
-class QComboBox : public QWidget {
-public:
-    QComboBox();
-    ~QComboBox();
+DOMPosition EditIterator::peekPrevious() const
+{
+    DOMPosition pos = m_current;
     
-    void clear();
-    void appendItem(const QString &text);
-
-    int currentItem() const { return _currentItem; }
-    void setCurrentItem(int);
-
-    QListBox *listBox() const { return 0; }
-    void popup() { }
+    if (pos.isEmpty())
+        return pos;
     
-    QSize sizeHint() const;
-    QRect frameGeometry() const;
-    void setFrameGeometry(const QRect &);
-    int baselinePosition(int height) const;
-    void setFont(const QFont &);
-
-    void itemSelected();
+    if (pos.offset() <= 0) {
+        NodeImpl *prevNode = pos.node()->previousEditable();
+        if (prevNode)
+            pos = DOMPosition(prevNode, prevNode->maxOffset());
+    }
+    else {
+        pos = DOMPosition(pos.node(), pos.offset() - 1);
+    }
     
-    virtual FocusPolicy focusPolicy() const;
+    return pos;
+}
 
-    void setWritingDirection(QPainter::TextDirection);
-
-    virtual void populate();
-    void populateMenu();
+DOMPosition EditIterator::peekNext() const
+{
+    DOMPosition pos = m_current;
+    
+    if (pos.isEmpty())
+        return pos;
     
-private:
-    const int *dimensions() const;
+    if (pos.offset() >= pos.node()->maxOffset()) {
+        NodeImpl *nextNode = pos.node()->nextEditable();
+        if (nextNode)
+            pos = DOMPosition(nextNode, 0);
+    }
+    else {
+        pos = DOMPosition(pos.node(), pos.offset() + 1);
+    }
     
-    mutable int _width;
-    mutable bool _widthGood;
+    return pos;
+}
+
+bool EditIterator::atStart() const
+{
+    if (m_current.isEmpty())
+        return true;
 
-    mutable int _currentItem;
+    return m_current.offset() == 0 && 
+        m_current.node()->previousEditable() == 0;
+}
 
-    // A vector<QString> or QValueVector<QString> may be more efficient for large menus.
-    QStringList _items;
-    mutable bool _menuPopulated;
+bool EditIterator::atEnd() const
+{
+    if (m_current.isEmpty())
+        return true;
 
-    KWQSignal _activated;
-};
+    return m_current.offset() == m_current.node()->maxOffset() && 
+        m_current.node()->nextEditable() == 0;
+}
 
-#endif
+} // namespace DOM
diff --git a/WebCore/kwq/KWQEditCommand.mm b/WebCore/khtml/xml/dom_edititerator.h
similarity index 62%
copy from WebCore/kwq/KWQEditCommand.mm
copy to WebCore/khtml/xml/dom_edititerator.h
index fffbe24..60a3928 100644
--- a/WebCore/kwq/KWQEditCommand.mm
+++ b/WebCore/khtml/xml/dom_edititerator.h
@@ -23,39 +23,39 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
  */
 
-#import "KWQEditCommand.h"
+#ifndef _EditIterator_h_
+#define _EditIterator_h_
 
-#import "KWQAssertions.h"
-#import "htmlediting_impl.h"
+#include "dom_position.h"
 
-using khtml::EditCommandImpl;
+namespace DOM {
 
- at implementation KWQEditCommand
+class DOMPosition;
+class NodeImpl;
 
-- (id)initWithEditCommandImpl:(EditCommandImpl *)impl
+class EditIterator
 {
-    ASSERT(impl);
-    [super init];
-    m_impl = impl;
-    impl->ref();
-    return self;
-}
-
-- (void)dealloc
-{
-    if (m_impl)
-        m_impl->deref();
-    [super dealloc];
-}
+public:
+    EditIterator() : m_current() {}
+    EditIterator(NodeImpl *node, long offset) : m_current(node, offset) {}
+    EditIterator(const DOMPosition &o) : m_current(o) {}
 
-+ (KWQEditCommand *)commandWithEditCommandImpl:(EditCommandImpl *)impl
-{
-    return [[[KWQEditCommand alloc] initWithEditCommandImpl:impl] autorelease];
-}
+    DOMPosition current() const { return m_current; }
+    DOMPosition previous() { return m_current = peekPrevious(); }
+    DOMPosition next() { return m_current = peekNext(); }
+    DOMPosition peekPrevious() const;
+    DOMPosition peekNext() const;
 
-- (EditCommandImpl *)impl
-{
-    return m_impl;
-}
+    void setPosition(const DOMPosition &pos) { m_current = pos; }
+
+    bool atStart() const;
+    bool atEnd() const;
+    bool isEmpty() const { return m_current.isEmpty(); }
+
+private:
+    DOMPosition m_current;
+};
+
+} // namespace DOM
 
- at end
\ No newline at end of file
+#endif // _EditIterator_h_
diff --git a/WebCore/khtml/xml/dom_nodeimpl.cpp b/WebCore/khtml/xml/dom_nodeimpl.cpp
index 710b851..6493a1e 100644
--- a/WebCore/khtml/xml/dom_nodeimpl.cpp
+++ b/WebCore/khtml/xml/dom_nodeimpl.cpp
@@ -37,12 +37,14 @@
 #include <kglobal.h>
 #include <kdebug.h>
 
+#include "rendering/render_object.h"
 #include "rendering/render_text.h"
 
 #include "ecma/kjs_binding.h"
 #include "ecma/kjs_proxy.h"
 #include "khtmlview.h"
 #include "khtml_part.h"
+#include "khtml_selection.h"
 
 #include "html/dtd.h"
 
@@ -1148,6 +1150,32 @@ bool NodeImpl::isReadOnly()
     return false;
 }
 
+NodeImpl *NodeImpl::previousEditable() const
+{
+    NodeImpl *node = traversePreviousNode();
+    while (node) {
+        if (!node->isContentEditable())
+            return 0;
+        if (node->hasChildNodes() == 0)
+            return node;
+        node = node->traversePreviousNode();
+    }
+    return 0;
+}
+
+NodeImpl *NodeImpl::nextEditable() const
+{
+    NodeImpl *node = traverseNextNode();
+    while (node) {
+        if (!node->isContentEditable())
+            return 0;
+        if (node->hasChildNodes() == 0)
+            return node;
+        node = node->traverseNextNode();
+    }
+    return 0;
+}
+
 RenderObject * NodeImpl::previousRenderer()
 {
     for (NodeImpl *n = previousSibling(); n; n = n->previousSibling()) {
@@ -1294,6 +1322,11 @@ RenderObject *NodeImpl::createRenderer(RenderArena *arena, RenderStyle *style)
     return 0;
 }
 
+long NodeImpl::maxOffset() const
+{
+    return 1;
+}
+
 long NodeImpl::caretMinOffset() const
 {
     return renderer() ? renderer()->caretMinOffset() : 0;
@@ -1304,6 +1337,71 @@ long NodeImpl::caretMaxOffset() const
     return renderer() ? renderer()->caretMaxOffset() : 1;
 }
 
+unsigned long NodeImpl::caretMaxRenderedOffset() const
+{
+    return renderer() ? renderer()->caretMaxRenderedOffset() : 1;
+}
+
+bool NodeImpl::isBlockFlow() const
+{
+    return renderer() && renderer()->isBlockFlow();
+}
+
+bool NodeImpl::isEditableBlock() const
+{
+    return isContentEditable() && isBlockFlow();
+}
+
+NodeImpl *NodeImpl::containingEditableBlock() const
+{
+    if (!isContentEditable())
+        return 0;
+
+    NodeImpl *n = const_cast<NodeImpl *>(this);
+    if (isEditableBlock())
+        return n;
+
+    while (1) {
+        n = n->parentNode();
+        if (!n || !n->isContentEditable())
+            break;
+        if (n->isBlockFlow() || n->id() == ID_BODY)
+            return n;
+    }
+    return 0;
+}
+
+NodeImpl *NodeImpl::rootEditableBlock() const
+{
+    if (!isContentEditable())
+        return 0;
+
+    NodeImpl *n = const_cast<NodeImpl *>(this);
+    NodeImpl *result = n->isEditableBlock() ? n : 0;
+    while (1) {
+        n = n->parentNode();
+        if (!n || !n->isContentEditable())
+            break;
+        if (n->id() == ID_BODY) {
+            result = n;
+            break;
+        }
+        if (n->isBlockFlow())
+            result = n;
+    }
+    return result;
+}
+
+bool NodeImpl::inSameRootEditableBlock(NodeImpl *n)
+{
+    return n ? rootEditableBlock() == n->rootEditableBlock() : false;
+}
+
+bool NodeImpl::inSameContainingEditableBlock(NodeImpl *n)
+{
+    return n ? containingEditableBlock() == n->containingEditableBlock() : false;
+}
+
 //-------------------------------------------------------------------------
 
 NodeBaseImpl::NodeBaseImpl(DocumentPtr *doc)
@@ -1938,6 +2036,12 @@ void NodeBaseImpl::setFocus(bool received)
 
     NodeImpl::setFocus(received);
 
+    if (received && isEditableBlock() && !hasChildNodes()) {
+        KHTMLPart *part = getDocument()->part();
+        part->setSelection(KHTMLSelection(this, 0));
+        fprintf(stderr, "place caret in me\n");
+    }
+
     // note that we need to recalc the style
     setChanged();
 }
diff --git a/WebCore/khtml/xml/dom_nodeimpl.h b/WebCore/khtml/xml/dom_nodeimpl.h
index ed8b8f7..b4692fc 100644
--- a/WebCore/khtml/xml/dom_nodeimpl.h
+++ b/WebCore/khtml/xml/dom_nodeimpl.h
@@ -125,6 +125,7 @@ public:
     virtual bool isTextNode() const { return false; }
     virtual bool isDocumentNode() const { return false; }
     virtual bool isXMLElementNode() const { return false; }
+    bool isBlockFlow() const;
     
     // Used by <form> elements to indicate a malformed state of some kind, typically
     // used to keep from applying the bottom margin of the form.
@@ -159,6 +160,13 @@ public:
      */
     NodeImpl *previousLeafNode() const;
 
+    bool isEditableBlock() const;
+    NodeImpl *containingEditableBlock() const;
+    NodeImpl *rootEditableBlock() const;
+    
+    bool inSameRootEditableBlock(NodeImpl *);
+    bool inSameContainingEditableBlock(NodeImpl *);
+
     // used by the parser. Doesn't do as many error checkings as
     // appendChild(), and returns the node into which will be parsed next.
     virtual NodeImpl *addChild(NodeImpl *newChild);
@@ -314,6 +322,10 @@ public:
 
     DocumentPtr *docPtr() const { return document; }
 
+    NodeImpl *previousEditable() const;
+    NodeImpl *nextEditable() const;
+    //bool isEditable() const;
+
     khtml::RenderObject *renderer() const { return m_render; }
     khtml::RenderObject *nextRenderer();
     khtml::RenderObject *previousRenderer();
@@ -324,8 +336,10 @@ public:
     bool isAncestor( NodeImpl *other );
     virtual bool childAllowed( NodeImpl *newChild );
 
+    virtual long maxOffset() const;
     virtual long caretMinOffset() const;
     virtual long caretMaxOffset() const;
+    virtual unsigned long caretMaxRenderedOffset() const;
 
 #ifndef NDEBUG
     virtual void dump(QTextStream *stream, QString ind = "") const;
diff --git a/WebCore/khtml/xml/dom_position.cpp b/WebCore/khtml/xml/dom_position.cpp
new file mode 100644
index 0000000..cee8535
--- /dev/null
+++ b/WebCore/khtml/xml/dom_position.cpp
@@ -0,0 +1,646 @@
+/*
+ * Copyright (C) 2004 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 "dom_position.h"
+
+#include "htmltags.h"
+#include "rendering/render_line.h"
+#include "rendering/render_object.h"
+#include "rendering/render_style.h"
+#include "rendering/render_text.h"
+#include "xml/dom_edititerator.h"
+#include "xml/dom_nodeimpl.h"
+
+#if APPLE_CHANGES
+#include "KWQAssertions.h"
+#include "KWQLogging.h"
+#endif
+
+using DOM::DOMPosition;
+using DOM::EditIterator;
+using DOM::NodeImpl;
+using khtml::InlineBox;
+using khtml::InlineFlowBox;
+using khtml::InlineTextBox;
+using khtml::RenderObject;
+using khtml::RenderText;
+
+#if !APPLE_CHANGES
+#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)
+#endif
+
+DOMPosition::DOMPosition(NodeImpl *node, long offset) 
+    : m_node(0), m_offset(offset) 
+{ 
+    if (node) {
+        m_node = node;
+        m_node->ref();
+    }
+};
+
+DOMPosition::DOMPosition(const DOMPosition &o)
+    : m_node(0), m_offset(o.offset()) 
+{
+    if (o.node()) {
+        m_node = o.node();
+        m_node->ref();
+    }
+}
+
+DOMPosition::~DOMPosition() {
+    if (m_node) {
+        m_node->deref();
+    }
+}
+
+DOMPosition &DOMPosition::operator=(const DOMPosition &o)
+{
+    if (m_node) {
+        m_node->deref();
+    }
+    m_node = o.node();
+    if (m_node) {
+        m_node->ref();
+    }
+
+    m_offset = o.offset();
+    
+    return *this;
+}
+
+long DOMPosition::renderedOffset() const
+{
+    if (!node()->isTextNode())
+        return offset();
+   
+    if (!node()->renderer())
+        return offset();
+                    
+    long result = 0;
+    RenderText *textRenderer = static_cast<RenderText *>(node()->renderer());
+    for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
+        long start = box->m_start;
+        long end = box->m_start + box->m_len;
+        if (offset() < start)
+            return result;
+        if (offset() <= end) {
+            result += offset() - start;
+            return result;
+        }
+        result += box->m_len;
+    }
+    return result;
+}
+
+DOMPosition DOMPosition::previousCharacterPosition() const
+{
+    if (isEmpty())
+        return DOMPosition();
+
+    NodeImpl *fromRootEditableBlock = node()->rootEditableBlock();
+    EditIterator it(*this);
+
+    bool atStartOfLine = isFirstRenderedPositionOnLine();
+    
+    while (!it.atStart()) {
+        DOMPosition pos = it.previous();
+
+        if (pos.node()->rootEditableBlock() != fromRootEditableBlock)
+            return *this;
+
+        if (atStartOfLine) {
+            if (pos.inRenderedContent())
+                return pos;
+        }
+        else if (rendersInDifferentPosition(pos))
+            return pos;
+    }
+    
+    return *this;
+}
+
+DOMPosition DOMPosition::nextCharacterPosition() const
+{
+    if (isEmpty())
+        return DOMPosition();
+
+    NodeImpl *fromRootEditableBlock = node()->rootEditableBlock();
+    EditIterator it(*this);
+
+    bool atEndOfLine = isLastRenderedPositionOnLine();
+    
+    while (!it.atEnd()) {
+        DOMPosition pos = it.next();
+
+        if (pos.node()->rootEditableBlock() != fromRootEditableBlock)
+            return *this;
+
+        if (atEndOfLine) {
+            if (pos.inRenderedContent())
+                return pos;
+        }
+        else if (rendersInDifferentPosition(pos))
+            return pos;
+    }
+    
+    return *this;
+}
+
+DOMPosition DOMPosition::equivalentUpstreamPosition() const
+{
+    if (!node())
+        return DOMPosition();
+
+    if (!node()->isTextNode() && offset() > node()->caretMinOffset())
+        return *this;
+    
+    NodeImpl *block = node()->containingEditableBlock();
+                
+    EditIterator it(*this);
+    DOMPosition prev = it.peekPrevious();
+    if (validUpstreamDownstreamPosition() && prev.validUpstreamDownstreamPosition()) {
+        if (node() == prev.node())
+            return *this;
+        else
+            return prev;
+    }
+    while (!it.atStart()) {
+        it.previous();
+        if (it.current().validUpstreamDownstreamPosition())
+            return it.current();
+        if (block != it.current().node()->containingEditableBlock())
+            return it.next();
+    }
+    return *this;
+}
+
+DOMPosition DOMPosition::equivalentDownstreamPosition() const
+{
+    if (!node())
+        return DOMPosition();
+
+    if (!node()->isTextNode() && offset() < node()->caretMaxOffset())
+        return *this;
+
+    NodeImpl *block = node()->containingEditableBlock();
+        
+    EditIterator it(*this);
+    DOMPosition next = it.peekNext();
+    if (validUpstreamDownstreamPosition() && next.validUpstreamDownstreamPosition()) {
+        if (node() == next.node())
+            return *this;
+        else
+            return next;
+    }
+    while (!it.atEnd()) {
+        if (it.next().validUpstreamDownstreamPosition())
+            return it.current();
+        if (block != it.current().node()->containingEditableBlock())
+            return it.previous();
+    }
+    return *this;
+}
+
+bool DOMPosition::validUpstreamDownstreamPosition() const
+{
+    if (isEmpty())
+        return false;
+        
+    RenderObject *renderer = node()->renderer();
+    if (!renderer || !renderer->isEditable())
+        return false;
+
+    if (renderer->style()->visibility() != khtml::VISIBLE)
+        return false;
+
+    if (renderer->isBR() || renderer->isBlockFlow())
+        return true;
+    
+    if (renderer->isText()) {
+        RenderText *textRenderer = static_cast<RenderText *>(renderer);
+        InlineTextBox *lastTextBox = textRenderer->lastTextBox();
+        for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
+            if (offset() >= box->m_start) {
+                if (box == lastTextBox) {
+                    if (offset() <= box->m_start + box->m_len)
+                        return true;
+                }
+                else if (offset() < box->m_start + box->m_len)
+                    return true;
+            }
+            else if (offset() < box->m_start) {
+                // The offset we're looking for is before this node
+                // this means the offset must be in content that is
+                // not rendered. Return false.
+                return false;
+            }
+        }
+        return false;
+    }
+    
+    if (offset() >= renderer->caretMinOffset() && offset() <= renderer->caretMaxOffset())
+        return true;
+    
+    return false;
+}
+
+bool DOMPosition::inRenderedContent() const
+{
+    if (isEmpty())
+        return false;
+        
+    RenderObject *renderer = node()->renderer();
+    if (!renderer || !renderer->isEditable())
+        return false;
+    
+    if (renderer->style()->visibility() != khtml::VISIBLE)
+        return false;
+
+    if (renderer->isBR() && static_cast<RenderText *>(renderer)->firstTextBox()) {
+        return offset() == 0;
+    }
+    else if (renderer->isText()) {
+        RenderText *textRenderer = static_cast<RenderText *>(renderer);
+        for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
+            if (offset() >= box->m_start && offset() <= box->m_start + box->m_len) {
+                return true;
+            }
+            else if (offset() < box->m_start) {
+                // The offset we're looking for is before this node
+                // this means the offset must be in content that is
+                // not rendered. Return false.
+                return false;
+            }
+        }
+    }
+    else if (offset() >= renderer->caretMinOffset() && offset() <= renderer->caretMaxOffset()) {
+        // don't return containing editable blocks unless they are empty
+        if (node()->containingEditableBlock() == node() && node()->firstChild())
+            return false;
+        return true;
+    }
+    
+    return false;
+}
+
+
+static InlineBox *inlineBoxForRenderer(RenderObject *renderer, long offset)
+{
+    if (!renderer)
+        return 0;
+
+    if (renderer->isBR() && static_cast<RenderText *>(renderer)->firstTextBox())
+        return static_cast<RenderText *>(renderer)->firstTextBox();
+    
+    if (renderer->isText()) {
+        RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer); 
+        if (textRenderer->isBR() && textRenderer->firstTextBox())
+            return textRenderer->firstTextBox();
+        
+        for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
+            if (offset >= box->m_start && offset <= box->m_start + box->m_len) {
+                return box;
+            }
+            else if (offset < box->m_start) {
+                // The offset we're looking for is before this node
+                // this means the offset must be in content that is
+                // not rendered.
+                return box->prevTextBox() ? box->prevTextBox() : textRenderer->firstTextBox();
+            }
+        }
+    }
+    else {
+        return renderer->inlineBoxWrapper();
+    } 
+    
+    return 0;
+}
+
+static bool renderersOnDifferentLine(RenderObject *r1, long o1, RenderObject *r2, long o2)
+{
+    InlineBox *b1 = inlineBoxForRenderer(r1, o1);
+    InlineBox *b2 = inlineBoxForRenderer(r2, o2);
+
+    if (b1 && b2 && b1->root() != b2->root())
+        return true;
+    
+    return false;
+}
+
+static NodeImpl *nextRenderedEditable(NodeImpl *node)
+{
+    while (1) {
+        node = node->nextEditable();
+        if (!node)
+            return 0;
+        if (inlineBoxForRenderer(node->renderer(), 0))
+            return node;
+    }
+    return 0;
+}
+
+static NodeImpl *previousRenderedEditable(NodeImpl *node)
+{
+    while (1) {
+        node = node->previousEditable();
+        if (!node)
+            return 0;
+        if (inlineBoxForRenderer(node->renderer(), 0))
+            return node;
+    }
+    return 0;
+}
+
+bool DOMPosition::inRenderedText() const
+{
+    if (!node()->isTextNode())
+        return false;
+        
+    RenderObject *renderer = node()->renderer();
+    if (!renderer)
+        return false;
+    
+    RenderText *textRenderer = static_cast<RenderText *>(renderer);
+    for (InlineTextBox *box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
+        if (offset() < box->m_start) {
+            // The offset we're looking for is before this node
+            // this means the offset must be in content that is
+            // not rendered. Return false.
+            return false;
+        }
+        if (offset() >= box->m_start && offset() <= box->m_start + box->m_len)
+            return true;
+    }
+    
+    return false;
+}
+
+bool DOMPosition::rendersOnSameLine(const DOMPosition &pos) const
+{
+    if (isEmpty() || pos.isEmpty())
+        return false;
+
+    if (node() == pos.node() && offset() == pos.offset())
+        return true;
+
+    if (node()->containingEditableBlock() != pos.node()->containingEditableBlock())
+        return false;
+
+    RenderObject *renderer = node()->renderer();
+    if (!renderer)
+        return false;
+    
+    RenderObject *posRenderer = pos.node()->renderer();
+    if (!posRenderer)
+        return false;
+
+    if (renderer->style()->visibility() != khtml::VISIBLE ||
+        posRenderer->style()->visibility() != khtml::VISIBLE)
+        return false;
+
+    return renderersOnDifferentLine(renderer, offset(), posRenderer, pos.offset());
+}
+
+bool DOMPosition::rendersInDifferentPosition(const DOMPosition &pos) const
+{
+    if (isEmpty() || pos.isEmpty())
+        return false;
+
+    RenderObject *renderer = node()->renderer();
+    if (!renderer)
+        return false;
+    
+    RenderObject *posRenderer = pos.node()->renderer();
+    if (!posRenderer)
+        return false;
+
+    if (renderer->style()->visibility() != khtml::VISIBLE ||
+        posRenderer->style()->visibility() != khtml::VISIBLE)
+        return false;
+    
+    if (node() == pos.node()) {
+        if (node()->id() == ID_BR)
+            return false;
+
+        if (offset() == pos.offset())
+            return false;
+            
+        if (!node()->isTextNode() && !pos.node()->isTextNode()) {
+            if (offset() != pos.offset())
+                return true;
+        }
+    }
+    
+    if (node()->id() == ID_BR && pos.inRenderedContent())
+        return true;
+                
+    if (pos.node()->id() == ID_BR && inRenderedContent())
+        return true;
+                
+    if (node()->containingEditableBlock() != pos.node()->containingEditableBlock())
+        return true;
+
+    if (node()->isTextNode() && !inRenderedText())
+        return false;
+
+    if (pos.node()->isTextNode() && !pos.inRenderedText())
+        return false;
+
+    long thisRenderedOffset = renderedOffset();
+    long posRenderedOffset = pos.renderedOffset();
+
+    if (renderer == posRenderer && thisRenderedOffset == posRenderedOffset)
+        return false;
+
+    LOG(Editing, "onDifferentLine:        %s\n", renderersOnDifferentLine(renderer, offset(), posRenderer, pos.offset()) ? "YES" : "NO");
+    LOG(Editing, "renderer:               %p [%p]\n", renderer, inlineBoxForRenderer(renderer, offset()));
+    LOG(Editing, "thisRenderedOffset:         %d\n", thisRenderedOffset);
+    LOG(Editing, "posRenderer:            %p [%p]\n", posRenderer, inlineBoxForRenderer(posRenderer, pos.offset()));
+    LOG(Editing, "posRenderedOffset:      %d\n", posRenderedOffset);
+    LOG(Editing, "node min/max:           %d:%d\n", node()->caretMinOffset(), node()->caretMaxRenderedOffset());
+    LOG(Editing, "pos node min/max:       %d:%d\n", pos.node()->caretMinOffset(), pos.node()->caretMaxRenderedOffset());
+    LOG(Editing, "----------------------------------------------------------------------\n");
+
+    InlineBox *b1 = inlineBoxForRenderer(renderer, offset());
+    InlineBox *b2 = inlineBoxForRenderer(posRenderer, pos.offset());
+
+    if (!b1 || !b2) {
+        return false;
+    }
+
+    if (b1->root() != b2->root()) {
+        return true;
+    }
+
+    if (nextRenderedEditable(node()) == pos.node() && 
+        thisRenderedOffset == (long)node()->caretMaxRenderedOffset() && posRenderedOffset == 0) {
+        return false;
+    }
+    
+    if (previousRenderedEditable(node()) == pos.node() && 
+        thisRenderedOffset == 0 && posRenderedOffset == (long)pos.node()->caretMaxRenderedOffset()) {
+        return false;
+    }
+
+    return true;
+}
+
+bool DOMPosition::isFirstRenderedPositionOnLine() const
+{
+    if (isEmpty())
+        return false;
+
+    RenderObject *renderer = node()->renderer();
+    if (!renderer)
+        return false;
+
+    if (renderer->style()->visibility() != khtml::VISIBLE)
+        return false;
+    
+    DOMPosition pos(node(), offset());
+    EditIterator it(pos);
+    while (!it.atStart()) {
+        it.previous();
+        if (it.current().inRenderedContent())
+            return renderersOnDifferentLine(renderer, offset(), it.current().node()->renderer(), it.current().offset());
+    }
+    
+    return true;
+}
+
+bool DOMPosition::isLastRenderedPositionOnLine() const
+{
+    if (isEmpty())
+        return false;
+
+    RenderObject *renderer = node()->renderer();
+    if (!renderer)
+        return false;
+
+    if (renderer->style()->visibility() != khtml::VISIBLE)
+        return false;
+    
+    if (node()->id() == ID_BR)
+        return true;
+    
+    DOMPosition pos(node(), offset());
+    EditIterator it(pos);
+    while (!it.atEnd()) {
+        it.next();
+        if (it.current().inRenderedContent())
+            return renderersOnDifferentLine(renderer, offset(), it.current().node()->renderer(), it.current().offset());
+    }
+    
+    return true;
+}
+
+bool DOMPosition::isLastRenderedPositionInEditableBlock() const
+{
+    if (isEmpty())
+        return false;
+
+    RenderObject *renderer = node()->renderer();
+    if (!renderer)
+        return false;
+
+    if (renderer->style()->visibility() != khtml::VISIBLE)
+        return false;
+
+    if (renderedOffset() != (long)node()->caretMaxRenderedOffset())
+        return false;
+
+    NodeImpl *next = node()->nextEditable();
+    return !next || !node()->inSameContainingEditableBlock(next);
+}
+
+bool DOMPosition::inFirstEditableInRootEditableBlock() const
+{
+    if (isEmpty() || !inRenderedContent())
+        return false;
+
+    EditIterator it(node(), offset());
+    while (!it.atStart()) {
+        if (it.previous().inRenderedContent())
+            return false;
+    }
+
+    return true;
+}
+
+bool DOMPosition::inLastEditableInRootEditableBlock() const
+{
+    if (isEmpty() || !inRenderedContent())
+        return false;
+
+    EditIterator it(node(), offset());
+    while (!it.atEnd()) {
+        if (it.next().inRenderedContent())
+            return false;
+    }
+
+    return true;
+}
+
+bool DOMPosition::inFirstEditableInContainingEditableBlock() const
+{
+    if (isEmpty() || !inRenderedContent())
+        return false;
+    
+    NodeImpl *block = node()->containingEditableBlock();
+
+    EditIterator it(node(), offset());
+    while (!it.atStart()) {
+        it.previous();
+        if (!it.current().inRenderedContent())
+            continue;
+        return block != it.current().node()->containingEditableBlock();
+    }
+
+    return true;
+}
+
+bool DOMPosition::inLastEditableInContainingEditableBlock() const
+{
+    if (isEmpty() || !inRenderedContent())
+        return false;
+    
+    NodeImpl *block = node()->containingEditableBlock();
+
+    EditIterator it(node(), offset());
+    while (!it.atEnd()) {
+        it.next();
+        if (!it.current().inRenderedContent())
+            continue;
+        return block != it.current().node()->containingEditableBlock();
+    }
+
+    return true;
+}
+
+
diff --git a/WebCore/khtml/dom/dom_position.h b/WebCore/khtml/xml/dom_position.h
similarity index 68%
rename from WebCore/khtml/dom/dom_position.h
rename to WebCore/khtml/xml/dom_position.h
index b38ac9c..8e700ee 100644
--- a/WebCore/khtml/dom/dom_position.h
+++ b/WebCore/khtml/xml/dom_position.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2003 Apple Computer, Inc.  All rights reserved.
+ * Copyright (C) 2004 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
@@ -41,8 +41,28 @@ public:
     NodeImpl *node() const { return m_node; }
     long offset() const { return m_offset; }
 
-    bool isEmpty() const { return m_node == 0; }
+    long renderedOffset() const;
 
+    bool isEmpty() const { return m_node == 0; }
+    bool notEmpty() const { return m_node != 0; }
+    
+    DOMPosition previousCharacterPosition() const;
+    DOMPosition nextCharacterPosition() const;
+    DOMPosition equivalentUpstreamPosition() const;
+    DOMPosition equivalentDownstreamPosition() const;
+    bool validUpstreamDownstreamPosition() const;
+    bool inRenderedContent() const;
+    bool inRenderedText() const;
+    bool rendersOnSameLine(const DOMPosition &pos) const;
+    bool rendersInDifferentPosition(const DOMPosition &pos) const;
+    bool isFirstRenderedPositionOnLine() const;
+    bool isLastRenderedPositionOnLine() const;
+    bool isLastRenderedPositionInEditableBlock() const;
+    bool inFirstEditableInRootEditableBlock() const;
+    bool inLastEditableInRootEditableBlock() const;
+    bool inFirstEditableInContainingEditableBlock() const;
+    bool inLastEditableInContainingEditableBlock() const;
+    
     DOMPosition &operator=(const DOMPosition &o);
     
     friend bool operator==(const DOMPosition &a, const DOMPosition &b);
diff --git a/WebCore/kwq/KWQComboBox.h b/WebCore/khtml/xml/dom_positioniterator.cpp
similarity index 52%
copy from WebCore/kwq/KWQComboBox.h
copy to WebCore/khtml/xml/dom_positioniterator.cpp
index b3712b4..22f7668 100644
--- a/WebCore/kwq/KWQComboBox.h
+++ b/WebCore/khtml/xml/dom_positioniterator.cpp
@@ -23,62 +23,66 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
  */
 
-#ifndef QCOMBOBOX_H_
-#define QCOMBOBOX_H_
+#include "dom_edititerator.h"
 
-#include "KWQWidget.h"
-#include "KWQStringList.h"
+#include "dom_nodeimpl.h"
 
-class QListBox;
+namespace DOM {
 
-#ifdef __OBJC__
- at class KWQComboBoxAdapter;
-#else
-class KWQComboBoxAdapter;
-#endif
-
-class QComboBox : public QWidget {
-public:
-    QComboBox();
-    ~QComboBox();
+DOMPosition EditIterator::peekPrevious() const
+{
+    DOMPosition pos = m_current;
     
-    void clear();
-    void appendItem(const QString &text);
-
-    int currentItem() const { return _currentItem; }
-    void setCurrentItem(int);
-
-    QListBox *listBox() const { return 0; }
-    void popup() { }
+    if (pos.isEmpty())
+        return pos;
     
-    QSize sizeHint() const;
-    QRect frameGeometry() const;
-    void setFrameGeometry(const QRect &);
-    int baselinePosition(int height) const;
-    void setFont(const QFont &);
-
-    void itemSelected();
+    if (pos.offset() <= 0) {
+        NodeImpl *prevNode = pos.node()->previousEditable();
+        if (prevNode)
+            pos = DOMPosition(prevNode, prevNode->maxOffset());
+    }
+    else {
+        pos = DOMPosition(pos.node(), pos.offset() - 1);
+    }
     
-    virtual FocusPolicy focusPolicy() const;
+    return pos;
+}
 
-    void setWritingDirection(QPainter::TextDirection);
-
-    virtual void populate();
-    void populateMenu();
+DOMPosition EditIterator::peekNext() const
+{
+    DOMPosition pos = m_current;
+    
+    if (pos.isEmpty())
+        return pos;
     
-private:
-    const int *dimensions() const;
+    if (pos.offset() >= pos.node()->maxOffset()) {
+        NodeImpl *nextNode = pos.node()->nextEditable();
+        if (nextNode)
+            pos = DOMPosition(nextNode, 0);
+    }
+    else {
+        pos = DOMPosition(pos.node(), pos.offset() + 1);
+    }
     
-    mutable int _width;
-    mutable bool _widthGood;
+    return pos;
+}
+
+bool EditIterator::atStart() const
+{
+    if (m_current.isEmpty())
+        return true;
 
-    mutable int _currentItem;
+    return m_current.offset() == 0 && 
+        m_current.node()->previousEditable() == 0;
+}
 
-    // A vector<QString> or QValueVector<QString> may be more efficient for large menus.
-    QStringList _items;
-    mutable bool _menuPopulated;
+bool EditIterator::atEnd() const
+{
+    if (m_current.isEmpty())
+        return true;
 
-    KWQSignal _activated;
-};
+    return m_current.offset() == m_current.node()->maxOffset() && 
+        m_current.node()->nextEditable() == 0;
+}
 
-#endif
+} // namespace DOM
diff --git a/WebCore/kwq/KWQEditCommand.mm b/WebCore/khtml/xml/dom_positioniterator.h
similarity index 62%
copy from WebCore/kwq/KWQEditCommand.mm
copy to WebCore/khtml/xml/dom_positioniterator.h
index fffbe24..60a3928 100644
--- a/WebCore/kwq/KWQEditCommand.mm
+++ b/WebCore/khtml/xml/dom_positioniterator.h
@@ -23,39 +23,39 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
  */
 
-#import "KWQEditCommand.h"
+#ifndef _EditIterator_h_
+#define _EditIterator_h_
 
-#import "KWQAssertions.h"
-#import "htmlediting_impl.h"
+#include "dom_position.h"
 
-using khtml::EditCommandImpl;
+namespace DOM {
 
- at implementation KWQEditCommand
+class DOMPosition;
+class NodeImpl;
 
-- (id)initWithEditCommandImpl:(EditCommandImpl *)impl
+class EditIterator
 {
-    ASSERT(impl);
-    [super init];
-    m_impl = impl;
-    impl->ref();
-    return self;
-}
-
-- (void)dealloc
-{
-    if (m_impl)
-        m_impl->deref();
-    [super dealloc];
-}
+public:
+    EditIterator() : m_current() {}
+    EditIterator(NodeImpl *node, long offset) : m_current(node, offset) {}
+    EditIterator(const DOMPosition &o) : m_current(o) {}
 
-+ (KWQEditCommand *)commandWithEditCommandImpl:(EditCommandImpl *)impl
-{
-    return [[[KWQEditCommand alloc] initWithEditCommandImpl:impl] autorelease];
-}
+    DOMPosition current() const { return m_current; }
+    DOMPosition previous() { return m_current = peekPrevious(); }
+    DOMPosition next() { return m_current = peekNext(); }
+    DOMPosition peekPrevious() const;
+    DOMPosition peekNext() const;
 
-- (EditCommandImpl *)impl
-{
-    return m_impl;
-}
+    void setPosition(const DOMPosition &pos) { m_current = pos; }
+
+    bool atStart() const;
+    bool atEnd() const;
+    bool isEmpty() const { return m_current.isEmpty(); }
+
+private:
+    DOMPosition m_current;
+};
+
+} // namespace DOM
 
- at end
\ No newline at end of file
+#endif // _EditIterator_h_
diff --git a/WebCore/khtml/xml/dom_selection.cpp b/WebCore/khtml/xml/dom_selection.cpp
index e5d20f5..46cb6ed 100644
--- a/WebCore/khtml/xml/dom_selection.cpp
+++ b/WebCore/khtml/xml/dom_selection.cpp
@@ -25,6 +25,7 @@
   
 #include "khtml_selection.h"
 
+#include "htmltags.h"
 #include "khtml_part.h"
 #include "khtmlview.h"
 #include "qevent.h"
@@ -38,6 +39,7 @@
 #include "rendering/render_style.h"
 #include "rendering/render_text.h"
 #include "xml/dom_docimpl.h"
+#include "xml/dom_edititerator.h"
 #include "xml/dom_elementimpl.h"
 #include "xml/dom_nodeimpl.h"
 #include "xml/dom_textimpl.h"
@@ -52,6 +54,7 @@
 using DOM::DocumentImpl;
 using DOM::DOMPosition;
 using DOM::DOMString;
+using DOM::EditIterator;
 using DOM::ElementImpl;
 using DOM::Node;
 using DOM::NodeImpl;
@@ -86,7 +89,7 @@ KHTMLSelection::KHTMLSelection(NodeImpl *node, long offset)
     validate();
 }
 
-KHTMLSelection::KHTMLSelection(const DOM::DOMPosition &pos)
+KHTMLSelection::KHTMLSelection(const DOMPosition &pos)
 {
     init();
 
@@ -98,6 +101,18 @@ KHTMLSelection::KHTMLSelection(const DOM::DOMPosition &pos)
     validate();
 }
 
+KHTMLSelection::KHTMLSelection(const DOMPosition &base, const DOMPosition &extent)
+{
+    init();
+
+	setBaseNode(base.node());
+	setExtentNode(extent.node());
+	setBaseOffset(base.offset());
+	setExtentOffset(extent.offset());
+
+    validate();
+}
+
 KHTMLSelection::KHTMLSelection(NodeImpl *baseNode, long baseOffset, NodeImpl *endNode, long endOffset)
 {
     init();
@@ -253,14 +268,14 @@ bool KHTMLSelection::modify(EAlter alter, EDirection dir, ETextGranularity elem)
                             setExtentNode(endNode());
                             setExtentOffset(endOffset());
                         }
-                        pos = nextCharacterPosition(DOMPosition(extentNode(), extentOffset()));
+                        pos = extentPosition().nextCharacterPosition();
                     }
                     else {
                         m_modifyBiasSet = false;
                         if (state() == RANGE)
-                            pos = DOMPosition(endNode(), endOffset());
+                            pos = endPosition();
                         else
-                            pos = nextCharacterPosition();
+                            pos = endPosition().nextCharacterPosition();
                     }
                     break;
                 case WORD:
@@ -284,14 +299,14 @@ bool KHTMLSelection::modify(EAlter alter, EDirection dir, ETextGranularity elem)
                             setExtentNode(startNode());
                             setExtentOffset(startOffset());
                         }
-                        pos = previousCharacterPosition(DOMPosition(extentNode(), extentOffset()));
+                        pos = extentPosition().previousCharacterPosition();
                     }
                     else {
                         m_modifyBiasSet = false;
                         if (state() == RANGE)
-                            pos = DOMPosition(startNode(), startOffset());
+                            pos = startPosition();
                         else
-                            pos = previousCharacterPosition();
+                            pos = startPosition().previousCharacterPosition();
                     }
                     break;
                 case WORD:
@@ -348,11 +363,6 @@ void KHTMLSelection::setNeedsLayout(bool flag)
     m_needsCaretLayout = flag;
 }
 
-bool KHTMLSelection::isEmpty() const
-{
-    return m_baseNode == 0 && m_extentNode == 0;
-}
-
 Range KHTMLSelection::toRange() const
 {
     if (isEmpty())
@@ -423,7 +433,7 @@ void KHTMLSelection::paintCaret(QPainter *p, const QRect &rect)
 
     if (m_needsCaretLayout) {
         DOMPosition pos = DOMPosition(startNode(), startOffset());
-        if (!inRenderedContent(pos)) {
+        if (!pos.inRenderedContent()) {
             moveToRenderedContent();
         }
         layoutCaret();
@@ -656,171 +666,6 @@ void KHTMLSelection::validate(ETextGranularity expandTo)
 #endif
 }
 
-DOMPosition KHTMLSelection::previousCharacterPosition() const
-{
-    return previousCharacterPosition(DOMPosition(startNode(), startOffset()));
-}
-
-DOMPosition KHTMLSelection::previousCharacterPosition(const DOMPosition &from)
-{
-    if (!from.node())
-        return from;
-
-	NodeImpl *node = from.node();
-	long offset = from.offset() - 1;
-
-    //
-    // Look in this renderer
-    //
-    RenderObject *renderer = node->renderer();
-    if (renderer->isText()) {
-        if (!renderer->isBR()) {
-            RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
-            for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
-                long start = box->m_start;
-                long end = box->m_start + box->m_len;
-                if (offset > end) {
-                    // Skip this node.
-                    // It is too early in the text runs to be involved.
-                    continue;
-                }
-                else if (offset >= start) {
-                    // Offset is in this node, return the start
-                    return DOMPosition(node, offset);
-                }
-            }
-        }
-    }
-    else {
-        // Offset is in this node, if:
-        // 1. greater than the min offset and less than or equal to the max caret offset
-        // 2. at the start of the editable content of the document
-        if ((offset > renderer->caretMinOffset() && offset <= renderer->caretMaxOffset()) || 
-            (offset == renderer->caretMinOffset() && !renderer->previousEditable()))
-            return DOMPosition(node, offset);
-    }
-
-    //
-    // Look in previous renderer(s)
-    //
-    renderer = renderer->previousEditable();
-    while (renderer) {
-        // Offset is in this node, if:
-        // 1. it is a BR which follows a line break
-        // 2. it is an element with content
-    	if (renderer->isBR()) {
-			if (renderer->followsLineBreak())
-				return DOMPosition(renderer->element(), renderer->caretMinOffset());
-    	}
-    	else {
-    		if (renderer->isText()) {
-    			 RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
-    			 if (!textRenderer->lastTextBox()) 
-    			 	continue;
-    		}
-            offset = renderer->caretMaxOffset();
-            if (renderer->nextEditable() && !renderer->precedesLineBreak())
-                offset--;
-            assert(offset >= 0);
-            return DOMPosition(renderer->element(), offset);
-    	}
-        renderer = renderer->previousEditable();
-    }
-
-    // can't move the position
-    return from;
-}
-
-
-DOMPosition KHTMLSelection::nextCharacterPosition() const
-{
-    return nextCharacterPosition(DOMPosition(endNode(), endOffset()));
-}
-
-DOMPosition KHTMLSelection::nextCharacterPosition(const DOMPosition &from)
-{
-    if (!from.node())
-        return DOMPosition();
-
- 	NodeImpl *node = from.node();
- 	long offset = from.offset() + 1;
-
-    //
-    // Look in this renderer
-    //
-    RenderObject *renderer = node->renderer();
-    if (renderer->isText()) {
-        if (!renderer->isBR()) {
-            RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
-            for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
-                long start = box->m_start;
-                long end = box->m_start + box->m_len;
-                if (offset > end) {
-                    // Skip this node.
-                    // It is too early in the text runs to be involved.
-                    continue;
-                }
-                else if (offset >= start) {
-                    // Offset is in this node, if:
-                    // Either it is:
-                    // 1. at or after the start and before, but not at the end
-                    // 2. at the end of a text run and is immediately followed by a line break
-                    // 3. at the end of the editable content of the document
-                    if (offset < end)
-                        return DOMPosition(node, offset);
-                    else if (offset == end && renderer->precedesLineBreak())
-                        return DOMPosition(node, offset);
-                    else if (offset == end && !box->nextTextBox() && !renderer->nextEditable())
-                        return DOMPosition(node, offset);
-                }
-                else if (offset < start) {
-                    // The offset we're looking for is before this node
-                    // this means the offset must be in content that is
-                    // not rendered. Just return the start of the node.
-                    return DOMPosition(node, start);
-                }
-            }
-        }
-    }
-    else {
-        // Offset is in this node, if:
-        // 1. before the max caret offset
-        // 2. equal to the max caret offset and is immediately preceded by a line break
-        // 3. at the end of the editable content of the document
-        if (offset < renderer->caretMaxOffset() ||
-            (offset == renderer->caretMaxOffset() && renderer->precedesLineBreak()) ||
-            (offset == renderer->caretMaxOffset() && !renderer->nextEditable()))
-                return DOMPosition(node, offset);
-    }
-
-    //
-    // Look in next renderer(s)
-    //
-    renderer = renderer->nextEditable();
-    while (renderer) {
-		// Offset is in this node, if:
-		// 1. it is a BR which follows a line break
-		// 2. it is a text element with content
-        // 3. it is a non-text element with content
-		if (renderer->isBR()) {
-			if (renderer->followsLineBreak())
-				return DOMPosition(renderer->element(), renderer->caretMinOffset());
-		}
-		else if (renderer->isText()) {
-            RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
-            if (textRenderer->firstTextBox())
-                return DOMPosition(renderer->element(), textRenderer->firstTextBox()->m_start);
-        }
-        else {
-            return DOMPosition(renderer->element(), renderer->caretMinOffset());
-        }
-        renderer = renderer->nextEditable();
-    }
-
-    // can't move the position
-    return from;
-}
-
 bool KHTMLSelection::moveToRenderedContent()
 {
     if (isEmpty())
@@ -830,58 +675,46 @@ bool KHTMLSelection::moveToRenderedContent()
         return false;
 
     DOMPosition pos = DOMPosition(startNode(), startOffset());
-    if (inRenderedContent(pos))
+    if (pos.inRenderedContent())
         return true;
         
-    // not currently rendered, try moving to next
-    DOMPosition next = nextCharacterPosition(pos);
-    if (next != pos) {
-        moveTo(next);
+    // not currently rendered, try moving to prev
+    DOMPosition prev = pos.previousCharacterPosition();
+    if (prev != pos && prev.node()->inSameContainingEditableBlock(pos.node())) {
+        moveTo(prev);
         return true;
     }
 
-    // could not be moved to next, try prev
-    DOMPosition prev = previousCharacterPosition(pos);
-    if (prev != pos) {
-        moveTo(prev);
+    // could not be moved to prev, try next
+    DOMPosition next = pos.nextCharacterPosition();
+    if (next != pos && next.node()->inSameContainingEditableBlock(pos.node())) {
+        moveTo(next);
         return true;
     }
     
     return false;
 }
 
-bool KHTMLSelection::inRenderedContent(const DOMPosition &pos)
+DOMPosition KHTMLSelection::basePosition() const
 {
-    if (pos.isEmpty())
-        return false;
-        
- 	long offset = pos.offset();
+    return DOMPosition(baseNode(), baseOffset());
+}
 
-    RenderObject *renderer = pos.node()->renderer();
-    if (!renderer)
-        return false;
-    
-    if (renderer->isText() && !renderer->isBR()) {
-        RenderText *textRenderer = static_cast<khtml::RenderText *>(renderer);
-        for (InlineTextBox* box = textRenderer->firstTextBox(); box; box = box->nextTextBox()) {
-            if (offset >= box->m_start && offset <= box->m_start + box->m_len) {
-                return true;
-            }
-            else if (offset < box->m_start) {
-                // The offset we're looking for is before this node
-                // this means the offset must be in content that is
-                // not rendered. Return false.
-                return false;
-            }
-        }
-    }
-    else if (offset >= renderer->caretMinOffset() && offset <= renderer->caretMaxOffset()) {
-        return true;
-    }
-    
-    return false;
+DOMPosition KHTMLSelection::extentPosition() const
+{
+    return DOMPosition(extentNode(), extentOffset());
 }
- 
+
+DOMPosition KHTMLSelection::startPosition() const
+{
+    return DOMPosition(startNode(), startOffset());
+}
+
+DOMPosition KHTMLSelection::endPosition() const
+{
+    return DOMPosition(endNode(), endOffset());
+}
+
 bool KHTMLSelection::nodeIsBeforeNode(NodeImpl *n1, NodeImpl *n2) 
 {
 	if (!n1 || !n2) 
@@ -1164,12 +997,38 @@ void KHTMLSelection::debugPosition() const
     if (!startNode())
         return;
 
-    static int context = 5;
+    //static int context = 5;
     
-    RenderObject *r = 0;
+    //RenderObject *r = 0;
 
     fprintf(stderr, "KHTMLSelection =================\n");
-    
+
+    if (startPosition() == endPosition()) {
+        DOMPosition pos = startPosition();
+        DOMPosition upstream = pos.equivalentUpstreamPosition();
+        DOMPosition downstream = pos.equivalentDownstreamPosition();
+        fprintf(stderr, "upstream:   %s %p:%d\n", getTagName(upstream.node()->id()).string().latin1(), upstream.node(), upstream.offset());
+        fprintf(stderr, "pos:        %s %p:%d\n", getTagName(pos.node()->id()).string().latin1(), pos.node(), pos.offset());
+        fprintf(stderr, "downstream: %s %p:%d\n", getTagName(downstream.node()->id()).string().latin1(), downstream.node(), downstream.offset());
+    }
+    else {
+        DOMPosition pos = endPosition();
+        DOMPosition upstream = pos.equivalentUpstreamPosition();
+        DOMPosition downstream = pos.equivalentDownstreamPosition();
+        fprintf(stderr, "upstream:   %s %p:%d\n", getTagName(upstream.node()->id()).string().latin1(), upstream.node(), upstream.offset());
+        fprintf(stderr, "start:      %s %p:%d\n", getTagName(pos.node()->id()).string().latin1(), pos.node(), pos.offset());
+        fprintf(stderr, "downstream: %s %p:%d\n", getTagName(downstream.node()->id()).string().latin1(), downstream.node(), downstream.offset());
+        fprintf(stderr, "-----------------------------------\n");
+        pos = startPosition();
+        upstream = pos.equivalentUpstreamPosition();
+        downstream = pos.equivalentDownstreamPosition();
+        fprintf(stderr, "upstream:   %s %p:%d\n", getTagName(upstream.node()->id()).string().latin1(), upstream.node(), upstream.offset());
+        fprintf(stderr, "end:        %s %p:%d\n", getTagName(pos.node()->id()).string().latin1(), pos.node(), pos.offset());
+        fprintf(stderr, "downstream: %s %p:%d\n", getTagName(downstream.node()->id()).string().latin1(), downstream.node(), downstream.offset());
+        fprintf(stderr, "-----------------------------------\n");
+    }
+          
+#if 0
     int back = 0;
     r = startNode()->renderer();
     for (int i = 0; i < context; i++, back++) {
@@ -1203,6 +1062,7 @@ void KHTMLSelection::debugPosition() const
         else
             break;
     }
+#endif
 
     fprintf(stderr, "================================\n");
 }
diff --git a/WebCore/khtml/xml/dom_selection.h b/WebCore/khtml/xml/dom_selection.h
index 2864eae..a52f542 100644
--- a/WebCore/khtml/xml/dom_selection.h
+++ b/WebCore/khtml/xml/dom_selection.h
@@ -46,6 +46,7 @@ public:
     KHTMLSelection();
     KHTMLSelection(DOM::NodeImpl *node, long offset);
     KHTMLSelection(const DOM::DOMPosition &);
+    KHTMLSelection(const DOM::DOMPosition &, const DOM::DOMPosition &);
     KHTMLSelection(DOM::NodeImpl *startNode, long startOffset, DOM::NodeImpl *endNode, long endOffset);
     KHTMLSelection(const KHTMLSelection &);
     ~KHTMLSelection();
@@ -83,15 +84,16 @@ public:
     DOM::NodeImpl *endNode() const { return m_endNode; }
     long endOffset() const { return m_endOffset; }
 
-    DOM::DOMPosition previousCharacterPosition() const;
-    static DOM::DOMPosition previousCharacterPosition(const DOM::DOMPosition &from);
-    DOM::DOMPosition nextCharacterPosition() const;
-    static DOM::DOMPosition nextCharacterPosition(const DOM::DOMPosition &from);
-        
+    DOM::DOMPosition basePosition() const;
+    DOM::DOMPosition extentPosition() const;
+    DOM::DOMPosition startPosition() const;
+    DOM::DOMPosition endPosition() const;
+
     void setNeedsLayout(bool flag=true);
     void clearModifyBias() { m_modifyBiasSet = false; }
     
-    bool isEmpty() const;
+    bool isEmpty() const { return state() == NONE; }
+    bool notEmpty() const { return !isEmpty(); }
     DOM::Range toRange() const;
 
     
@@ -124,7 +126,6 @@ private:
 	void setEndNode(DOM::NodeImpl *);
 	void setEndOffset(long);
 
-    bool inRenderedContent(const DOM::DOMPosition &);
     bool nodeIsBeforeNode(DOM::NodeImpl *n1, DOM::NodeImpl *n2);
 
     void calculateStartAndEnd(ETextGranularity select=CHARACTER);
diff --git a/WebCore/khtml/xml/dom_stringimpl.cpp b/WebCore/khtml/xml/dom_stringimpl.cpp
index c8f67a7..a2c860b 100644
--- a/WebCore/khtml/xml/dom_stringimpl.cpp
+++ b/WebCore/khtml/xml/dom_stringimpl.cpp
@@ -191,10 +191,15 @@ DOMStringImpl *DOMStringImpl::split(uint pos)
 
 bool DOMStringImpl::containsOnlyWhitespace() const
 {
+    return containsOnlyWhitespace(0, l);
+}
+
+bool DOMStringImpl::containsOnlyWhitespace(unsigned int from, unsigned int len) const
+{
     if (!s)
         return true;
     
-    for (uint i = 0; i < l; i++) {
+    for (uint i = from; i < len; i++) {
         QChar c = s[i];
         if (c.unicode() <= 0x7F) {
             if (!isspace(c.unicode()))
diff --git a/WebCore/khtml/xml/dom_stringimpl.h b/WebCore/khtml/xml/dom_stringimpl.h
index af4eaab..ed9cfde 100644
--- a/WebCore/khtml/xml/dom_stringimpl.h
+++ b/WebCore/khtml/xml/dom_stringimpl.h
@@ -69,6 +69,7 @@ public:
     khtml::Length toLength() const;
     
     bool containsOnlyWhitespace() const;
+    bool containsOnlyWhitespace(unsigned int from, unsigned int len) const;
     
     // ignores trailing garbage, unlike QString
     int toInt(bool* ok=0) const;
diff --git a/WebCore/khtml/xml/dom_textimpl.cpp b/WebCore/khtml/xml/dom_textimpl.cpp
index 19bd751..344c6e6 100644
--- a/WebCore/khtml/xml/dom_textimpl.cpp
+++ b/WebCore/khtml/xml/dom_textimpl.cpp
@@ -180,6 +180,13 @@ DOMString CharacterDataImpl::nodeValue() const
     return str;
 }
 
+bool CharacterDataImpl::containsOnlyWhitespace(unsigned int from, unsigned int len) const
+{
+    if (str)
+        return str->containsOnlyWhitespace(from, len);
+    return true;
+}
+
 bool CharacterDataImpl::containsOnlyWhitespace() const
 {
     if (str)
@@ -227,6 +234,11 @@ void CharacterDataImpl::checkCharDataOperation( const unsigned long offset, int
     }
 }
 
+long CharacterDataImpl::maxOffset() const 
+{
+    return (long)length();
+}
+
 long CharacterDataImpl::caretMinOffset() const 
 {
     RenderText *r = static_cast<RenderText *>(renderer());
@@ -239,6 +251,12 @@ long CharacterDataImpl::caretMaxOffset() const
     return r && r->isText() ? r->caretMaxOffset() : (long)length();
 }
 
+unsigned long CharacterDataImpl::caretMaxRenderedOffset() const 
+{
+    RenderText *r = static_cast<RenderText *>(renderer());
+    return r ? r->caretMaxRenderedOffset() : length();
+}
+
 #ifndef NDEBUG
 void CharacterDataImpl::dump(QTextStream *stream, QString ind) const
 {
@@ -301,12 +319,12 @@ DOMString CommentImpl::toString() const
 // ### allow having children in text nodes for entities, comments etc.
 
 TextImpl::TextImpl(DocumentPtr *doc, const DOMString &_text)
-    : CharacterDataImpl(doc, _text)
+    : CharacterDataImpl(doc, _text), m_rendererIsNeeded(false)
 {
 }
 
 TextImpl::TextImpl(DocumentPtr *doc)
-    : CharacterDataImpl(doc)
+    : CharacterDataImpl(doc), m_rendererIsNeeded(false)
 {
 }
 
@@ -371,6 +389,11 @@ NodeImpl *TextImpl::cloneNode(bool /*deep*/)
 
 bool TextImpl::rendererIsNeeded(RenderStyle *style)
 {
+    if (m_rendererIsNeeded) {
+        m_rendererIsNeeded = false;
+        return true;
+    }
+
     if (!CharacterDataImpl::rendererIsNeeded(style)) {
         return false;
     }
@@ -378,7 +401,7 @@ bool TextImpl::rendererIsNeeded(RenderStyle *style)
     if (!onlyWS) {
         return true;
     }
-    
+
     RenderObject *par = parentNode()->renderer();
     
     if (par->isTable() || par->isTableRow() || par->isTableSection()) {
diff --git a/WebCore/khtml/xml/dom_textimpl.h b/WebCore/khtml/xml/dom_textimpl.h
index 6abbe30..e6f2469 100644
--- a/WebCore/khtml/xml/dom_textimpl.h
+++ b/WebCore/khtml/xml/dom_textimpl.h
@@ -54,6 +54,7 @@ public:
     virtual void replaceData ( const unsigned long offset, const unsigned long count, const DOMString &arg, int &exceptioncode );
 
     virtual bool containsOnlyWhitespace() const;
+    bool containsOnlyWhitespace(unsigned int from, unsigned int len) const;
     
     // DOM methods overridden from  parent classes
 
@@ -65,9 +66,11 @@ public:
     DOMStringImpl *string() { return str; }
     virtual void checkCharDataOperation( const unsigned long offset, int &exceptioncode );
 
+    virtual long maxOffset() const;
     virtual long caretMinOffset() const;
     virtual long caretMaxOffset() const;
-
+    virtual unsigned long caretMaxRenderedOffset() const;
+    
 #ifndef NDEBUG
     virtual void dump(QTextStream *stream, QString ind = "") const;
 #endif
@@ -135,6 +138,8 @@ public:
     virtual bool childTypeAllowed( unsigned short type );
 
     virtual DOMString toString() const;
+    
+    void setRendererIsNeeded(bool flag=true) { m_rendererIsNeeded = flag; }
 
 #if APPLE_CHANGES
     static Text createInstance(TextImpl *impl);
@@ -142,6 +147,9 @@ public:
 
 protected:
     virtual TextImpl *createNew(DOMStringImpl *_str);
+    
+private:
+    bool m_rendererIsNeeded;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/WebCore/kwq/KWQAssertions.m b/WebCore/kwq/KWQAssertions.m
index 4c802b8..d1dbd36 100644
--- a/WebCore/kwq/KWQAssertions.m
+++ b/WebCore/kwq/KWQAssertions.m
@@ -100,7 +100,8 @@ void KWQLog(const char *file, int line, const char *function, KWQLogChannel *cha
         return;
     }
     
-    fprintf(stderr, "- %s:%d %s - ", file, line, function);
+    if (channel->mask != 0x100) // kocienda does not want this output when logging editing
+        fprintf(stderr, "- %s:%d %s - ", file, line, function);
     va_list args;
     va_start(args, format);
     vprintf_stderr_objc(format, args);

-- 
WebKit Debian packaging



More information about the Pkg-webkit-commits mailing list