Index: /CKEditor/branches/prototype/_source/core/dom/range.js
===================================================================
--- /CKEditor/branches/prototype/_source/core/dom/range.js	(revision 2806)
+++ /CKEditor/branches/prototype/_source/core/dom/range.js	(revision 2807)
@@ -274,4 +274,95 @@
 	};
 
+	var inlineChildReqElements = { abbr:1,acronym:1,b:1,bdo:1,big:1,cite:1,code:1,del:1,dfn:1,em:1,font:1,i:1,ins:1,label:1,kbd:1,q:1,samp:1,small:1,span:1,strike:1,strong:1,sub:1,sup:1,tt:1,u:1,'var':1 };
+
+	var getBoundaryNodes = function()
+	{
+		var startNode = this.startContainer,
+			endNode = this.endContainer,
+			startOffset = this.startOffset,
+			endOffset = this.endOffset;
+
+		if ( startNode.type == CKEDITOR.NODE_ELEMENT )
+		{
+			var childCount = startNode.getChildCount();
+			if ( childCount > startOffset )
+				startNode = startNode.getChild( startOffset );
+			else if ( childCount < 1 )
+				startNode = startNode.getPreviousSourceNode();
+			else		// startOffset > childCount but childCount is not 0
+			{
+				// Try to take the node just after the current position.
+				startNode = startNode.$;
+				while ( startNode.lastChild )
+					startNode = startNode.lastChild;
+				startNode = new CKEDITOR.dom.node( startNode );
+
+				// Normally we should take the next node in DFS order. But it
+				// is also possible that we've already reached the end of
+				// document.
+				startNode = startNode.getNextSourceNode() || startNode;
+			}
+		}
+		if ( endNode.type == CKEDITOR.NODE_ELEMENT )
+		{
+			var childCount = endNode.getChildCount();
+			if ( childCount > endOffset )
+				endNode = endNode.getChild( endOffset ).getPreviousSourceNode();
+			else if ( childCount < 1 )
+				endNode = endNode.getPreviousSourceNode();
+			else		// endOffset > childCount but childCount is not 0
+			{
+				// Try to take the node just before the current position.
+				endNode = endNode.$;
+				while ( endNode.lastChild )
+					endNode = endNode.lastChild;
+				endNode = new CKEDITOR.dom.node( endNode );
+			}
+		}
+
+		return { startNode : startNode, endNode : endNode };
+	};
+
+	// Check every node between the block boundary and the startNode or endNode.
+	var getCheckStartEndBlockFunction = function( isStart )
+	{
+		return function( evt )
+		{
+			// Don't check the block boundary itself.
+			if ( this.stopped() || !evt.data.node )
+				return;
+
+			var node = evt.data.node,
+				hadBr = false;
+			if ( node.type == CKEDITOR.NODE_ELEMENT )
+			{
+				// If there are non-empty inline elements (e.g. <img />), then we're not
+				// at the start.
+				if ( !inlineChildReqElements[ node.getName() ] )
+				{
+					// If we're working at the end-of-block, forgive the first <br />.
+					if ( !isStart && node.getName() == 'br' && !hadBr )
+						hadBr = true;
+					else
+					{
+						this.checkFailed = true;
+						this.stop();
+					}
+				}
+			}
+			else if ( node.type == CKEDITOR.NODE_TEXT )
+			{
+				// If there's any visible text, then we're not at the start.
+				var visibleText = CKEDITOR.tools.trim( node.getText() );
+				if ( visibleText.length > 0 )
+				{
+					this.checkFailed = true;
+					this.stop();
+				}
+			}
+		};
+	};
+
+
 	CKEDITOR.dom.range.prototype =
 	{
@@ -535,5 +626,5 @@
 		},
 
-		enlarge : function( unit, editor )
+		enlarge : function( unit )
 		{
 			switch ( unit )
@@ -893,27 +984,9 @@
 				case CKEDITOR.ENLARGE_BLOCK_CONTENTS:
 				case CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS:
-					var startNode = this.startContainer,
-						endNode = this.endContainer,
-						startOffset = this.startOffset,
-						endOffset = this.endOffset;
-
-					if ( startNode.type == CKEDITOR.NODE_ELEMENT )
-						startNode = startNode.getChild( startOffset );
-					if ( endNode.type == CKEDITOR.NODE_ELEMENT )
-					{
-						var childCount = endNode.getChildCount();
-						if ( childCount > endOffset )
-							endNode = endNode.getChild( endOffset ).getPreviousSourceNode();
-						else if ( childCount < 1 )
-							endNode = endNode.getPreviousSourceNode();
-						else
-						{
-							while ( endNode.lastChild )
-								endNode = endNode.lastChild;
-						}
-					}
-
-					var guardFunction = ( unit == CKEDITOR.ENLARGE_BLOCK_CONTENTS ? 
-							CKEDITOR.domWalker.blockBoundary( editor ) :
+					var boundaryNodes = getBoundaryNodes.apply( this ),
+						startNode = boundaryNodes.startNode,
+						endNode = boundaryNodes.endNode,
+						guardFunction = ( unit == CKEDITOR.ENLARGE_BLOCK_CONTENTS ? 
+							CKEDITOR.domWalker.blockBoundary() :
 							CKEDITOR.domWalker.listItemBoundary() ),
 						walker = new CKEDITOR.domWalker( startNode ),
@@ -1094,4 +1167,89 @@
 
 			updateCollapsed( this );
+		},
+
+		// TODO: The fixed block isn't trimmed, does not work for <pre>.
+		// TODO: Does not add bogus <br> to empty fixed blocks.
+		fixBlock : function( isStart, blockTag )
+		{
+			var bookmark = this.createBookmark(),
+				fixedBlock = new CKEDITOR.dom.element( blockTag, this.document );
+			this.collapse( isStart );
+			this.enlarge( CKEDITOR.ENLARGE_BLOCK_CONTENTS );
+			this.extractContents().appendTo( fixedBlock );
+			this.insertNode( fixedBlock );
+			this.moveToBookmark( bookmark );
+			return fixedBlock;
+		},
+
+		splitBlock : function( blockTag )
+		{
+			var startPath = new CKEDITOR.dom.elementPath( this.startContainer ),
+				endPath = new CKEDITOR.dom.elementPath( this.endContainer ),
+				startBlockLimit = startPath.blockLimit,
+				endBlockLimit = endPath.blockLimit;
+
+			if ( startBlockLimit != endBlockLimit )
+				return null;
+
+			if ( blockTag != 'br' )
+			{
+				if ( !startPath.block )
+				{
+					this.fixBlock( true, blockTag );
+					endPath = new CKEDITOR.dom.elementPath( this.endContainer );
+				}
+
+				if ( !endPath.block )
+					this.fixBlock( false, blockTag );
+			}
+		},
+
+		checkStartOfBlock : function()
+		{
+			var startContainer = this.startContainer,
+				startOffset = this.startOffset;
+
+			// If the starting node is a text node, and non-empty before the offset,
+			// then we're surely not at the start of block.
+			if ( startContainer.type == CKEDITOR.NODE_TEXT )
+			{
+				var textBefore = CKEDITOR.tools.ltrim( startContainer.getText().substr( 0, startOffset ) );
+				if ( textBefore.length > 0 )
+					return false;
+			}
+
+			var startNode = getBoundaryNodes.apply( this ).startNode,
+				walker = new CKEDITOR.domWalker( startNode );
+
+			// DFS backwards until the block boundary, with the checker function.
+			walker.on( 'step', getCheckStartEndBlockFunction( true ), null, null, 20 );
+			walker.reverse( CKEDITOR.domWalker.blockBoundary() );
+
+			return !walker.checkFailed;
+		},
+
+		checkEndOfBlock : function()
+		{
+			var endContainer = this.endContainer,
+				endOffset = this.endOffset;
+
+			// If the ending node is a text node, and non-empty after the offset,
+			// then we're surely not at the end of block.
+			if ( endContainer.type == CKEDITOR.NODE_TEXT )
+			{
+				var textAfter = CKEDITOR.tools.rtrim( endContainer.getText().substr( endOffset ) );
+				if ( textAfter.length > 0 )
+					return false;
+			}
+
+			var endNode = getBoundaryNodes.apply( this ).endNode,
+				walker = new CKEDITOR.domWalker( endNode );
+
+			// DFS forward until the block boundary, with the checker function.
+			walker.on( 'step', getCheckStartEndBlockFunction( false ), null, null, 20 );
+			walker.forward( CKEDITOR.domWalker.blockBoundary() );
+
+			return !walker.checkFailed;
 		}
 	};
Index: /CKEditor/branches/prototype/_source/plugins/domwalker/plugin.js
===================================================================
--- /CKEditor/branches/prototype/_source/plugins/domwalker/plugin.js	(revision 2806)
+++ /CKEditor/branches/prototype/_source/plugins/domwalker/plugin.js	(revision 2807)
@@ -148,5 +148,8 @@
 			this.on( 'down', guardFunc );
 			while( ( !retval || retval.node ) && !this._.stopFlag )
+			{
 				retval = this.next();
+				this.fire( 'step', retval );
+			}
 			this.removeListener( 'sibling', guardFunc );
 			this.removeListener( 'up', guardFunc );
@@ -167,5 +170,8 @@
 			this.on( 'down', guardFunc );
 			while( ( !retval || retval.node ) && !this._.stopFlag )
+			{
 				retval = this.back();
+				this.fire( 'step', retval );
+			}
 			this.removeListener( 'sibling', guardFunc );
 			this.removeListener( 'up', guardFunc );
@@ -180,4 +186,9 @@
 		},
 
+		stopped : function()
+		{
+			return this._.stopFlag;
+		},
+
 		setNode : function( node )
 		{
@@ -187,5 +198,5 @@
 	};
 
-	CKEDITOR.domWalker.blockBoundary = function( editor )
+	CKEDITOR.domWalker.blockBoundary = function( customNodeNames )
 	{
 		return function( evt )
@@ -200,5 +211,5 @@
 				'table-footer-group' : 1, 'table-row' : 1, 'table-column-group' : 1, 'table-column' : 1, 'table-cell' : 1,
 				'table-caption' : 1 },
-				nodeNameMatches = { hr : 1, br : editor && editor.config.enterMode == 'br' },
+				nodeNameMatches = CKEDITOR.tools.extend( { hr : 1 }, customNodeNames || {} ),
 				to = evt.data.to,
 				from = evt.data.from;
@@ -206,10 +217,17 @@
 			{
 				if ( displayMatches[ to.getComputedStyle( 'display' ) ] || nodeNameMatches[ to.getName() ] )
+				{
+					evt.stop();
 					this.stop();
+					return;
+				}
 			}
 			if ( ( evt.data.type == 'up' || evt.data.type == 'sibling' ) && from && from.type == CKEDITOR.NODE_ELEMENT )
 			{
 				if ( displayMatches[ from.getComputedStyle( 'display' ) ] || nodeNameMatches[ from.getName() ] )
+				{
+					evt.stop();
 					this.stop();
+				}
 			}
 		};
@@ -218,5 +236,5 @@
 	CKEDITOR.domWalker.listItemBoundary = function()
 	{
-		return CKEDITOR.domWalker.blockBoundary( { config : { enterMode : 'br' } } );
+		return CKEDITOR.domWalker.blockBoundary( { br : 1 } );
 	};
 })();
