Index: CKEditor/trunk/CHANGES.html
===================================================================
--- CKEditor/trunk/CHANGES.html	(revision 4542)
+++ CKEditor/trunk/CHANGES.html	(revision 4546)
@@ -81,4 +81,5 @@
 		<li><a href="http://dev.fckeditor.net/ticket/4455">#4455</a> : Fixed unable to start editing when image inside document not loaded.</li>
 		<li><a href="http://dev.fckeditor.net/ticket/4517">#4517</a> : Fixed 'dialog_backgroundCoverColor' doesn't work on IE6.</li>
+		<li><a href="http://dev.fckeditor.net/ticket/3165">#3165</a> : Fixed enter key in empty list item before nested one result in collapsed line.</li>
 	<h3>
 		CKEditor 3.0.1</h3>
Index: CKEditor/trunk/_source/core/dom/element.js
===================================================================
--- CKEditor/trunk/_source/core/dom/element.js	(revision 4542)
+++ CKEditor/trunk/_source/core/dom/element.js	(revision 4546)
@@ -628,4 +628,5 @@
 		/**
 		 * Gets the first child node of this element.
+		 * @param {Function} evaluator Filtering the result node.
 		 * @returns {CKEDITOR.dom.node} The first child node or null if not
 		 *		available.
@@ -635,8 +636,12 @@
 		 * alert( first.getName() );  // "b"
 		 */
-		getFirst : function()
-		{
-			var $ = this.$.firstChild;
-			return $ ? new CKEDITOR.dom.node( $ ) : null;
+		getFirst : function( evaluator )
+		{
+			var first = this.$.firstChild,
+				retval = first && new CKEDITOR.dom.node( first );
+			if ( retval && evaluator && !evaluator( retval ) )
+				retval = retval.getNext( evaluator );
+
+			return retval;
 		},
 
Index: CKEditor/trunk/_source/core/dom/walker.js
===================================================================
--- CKEditor/trunk/_source/core/dom/walker.js	(revision 4542)
+++ CKEditor/trunk/_source/core/dom/walker.js	(revision 4546)
@@ -409,3 +409,23 @@
 		};
 	};
+
+	/**
+	 * Whether the node is invisible in wysiwyg mode.
+	 * @param isReject
+	 */
+	CKEDITOR.dom.walker.invisible = function( isReject )
+	{
+		var whitespace = CKEDITOR.dom.walker.whitespaces();
+		return function( node )
+		{
+			// Nodes that take no spaces in wysiwyg:
+			// 1. White-spaces but not including NBSP;
+			// 2. Empty inline elements, e.g. <b></b> we're checking here
+			// 'offsetHeight' instead of 'offsetWidth' for properly excluding
+			// all sorts of empty paragraph, e.g. <br />.
+			var isInvisible = whitespace( node ) || node.is && !node.$.offsetHeight;
+			return isReject ^ isInvisible;
+		};
+	};
+
 })();
Index: CKEditor/trunk/_source/plugins/enterkey/plugin.js
===================================================================
--- CKEditor/trunk/_source/plugins/enterkey/plugin.js	(revision 4542)
+++ CKEditor/trunk/_source/plugins/enterkey/plugin.js	(revision 4546)
@@ -102,9 +102,10 @@
 		{
 			// If the next block is an <li> with another list tree as the first
-			// child, we'll need to append a placeholder or the list item
+			// child, we'll need to append a filler (<br>/NBSP) or the list item
 			// wouldn't be editable. (#1420)
-			if ( nextBlock.is( 'li' ) && ( node = nextBlock.getFirst() )
-					&& node.is && node.is( 'ul', 'ol') )
-				nextBlock.insertBefore( doc.createText( '\xa0' ), node );
+			if ( nextBlock.is( 'li' )
+				 && ( node = nextBlock.getFirst( CKEDITOR.dom.walker.invisible( true ) ) )
+				 && node.is && node.is( 'ul', 'ol' ) )
+				( CKEDITOR.env.ie ? doc.createText( '\xa0' ) : doc.createElement( 'br' ) ).insertBefore( node );
 
 			// Move the selection to the end block.
Index: CKEditor/trunk/_source/plugins/list/plugin.js
===================================================================
--- CKEditor/trunk/_source/plugins/list/plugin.js	(revision 4542)
+++ CKEditor/trunk/_source/plugins/list/plugin.js	(revision 4546)
@@ -540,4 +540,63 @@
 	};
 
+	var dtd = CKEDITOR.dtd;
+	var tailNbspRegex = /[\t\r\n ]*(?:&nbsp;|\xa0)$/;
+	
+	function indexOfFirstChildElement( element, tagNameList )
+	{
+		var children = element.children,
+			child,
+			length = children.length;
+		for ( var i = 0; i < length; i++ )
+		{
+			child = children[ i ];
+			if( child.name && ( child.name in tagNameList ) )
+				return i;
+		}
+	}
+
+	function getExtendNestedListFilter( isHtmlFilter )
+	{
+		// An element filter function that corrects nested list start in an empty
+		// list item for better displaying/outputting. (#3165)
+		return function( listItem )
+		{
+			var children = listItem.children,
+				firstNestedListIndex = indexOfFirstChildElement( listItem, dtd.$list ),
+				firstNestedList = children[ firstNestedListIndex ],
+				nodeBefore = firstNestedList && firstNestedList.previous,
+				tailNbspmatch;
+
+			if( nodeBefore
+				&& ( nodeBefore.name && nodeBefore.name == 'br'
+					|| nodeBefore.value && ( tailNbspmatch = nodeBefore.value.match( tailNbspRegex ) ) ) )
+			{
+				var fillerNode = nodeBefore;
+
+				// Always use 'nbsp' as filler node if we found a nested list appear
+				// in front of a list item.
+				if ( !( tailNbspmatch && tailNbspmatch.index ) && fillerNode == children[ 0 ] )
+					children[ 0 ] = ( isHtmlFilter || CKEDITOR.env.ie ) ?
+					                 new CKEDITOR.htmlParser.text( '\xa0' ) :
+									 new CKEDITOR.htmlParser.element( 'br', {} );
+
+				// Otherwise the filler is not needed anymore.
+				else if ( fillerNode.name == 'br' )
+					children.splice( firstNestedListIndex - 1, 1 );
+				else
+					fillerNode.value = fillerNode.value.replace( tailNbspRegex, '' );
+			}
+
+		};
+	}
+
+	var defaultListDataFilterRules = { elements : {} };
+	for( var i in dtd.$listItem )
+		defaultListDataFilterRules.elements[ i ] = getExtendNestedListFilter();
+
+	var defaultListHtmlFilterRules = { elements : {} };
+	for( i in dtd.$listItem )
+		defaultListHtmlFilterRules.elements[ i ] = getExtendNestedListFilter( true );
+
 	CKEDITOR.plugins.add( 'list',
 	{
@@ -567,4 +626,14 @@
 		},
 
+		afterInit : function ( editor )
+		{
+			var dataProcessor = editor.dataProcessor;
+			if( dataProcessor )
+			{
+				dataProcessor.dataFilter.addRules( defaultListDataFilterRules );
+				dataProcessor.htmlFilter.addRules( defaultListHtmlFilterRules );
+			}
+		},
+
 		requires : [ 'domiterator' ]
 	} );
Index: CKEditor/trunk/_source/plugins/selection/plugin.js
===================================================================
--- CKEditor/trunk/_source/plugins/selection/plugin.js	(revision 4542)
+++ CKEditor/trunk/_source/plugins/selection/plugin.js	(revision 4546)
@@ -953,4 +953,8 @@
 	};
 })();
+( function()
+{
+var notWhitespaces = CKEDITOR.dom.walker.whitespaces( true );
+var fillerTextRegex = /\ufeff|\u00a0/;
 
 CKEDITOR.dom.range.prototype.select =
@@ -997,5 +1001,7 @@
 				// Actually, we are using this flag just to avoid using this hack in all
 				// situations, but just on those needed.
-				isStartMarkerAlone = forceExpand || !startNode.hasPrevious() || ( startNode.getPrevious().is && startNode.getPrevious().is( 'br' ) );
+				var next = startNode.getNext( notWhitespaces );
+				isStartMarkerAlone = ( !( next && next.getText && next.getText().match( fillerTextRegex ) )     // already a filler there?
+									  && ( forceExpand || !startNode.hasPrevious() || ( startNode.getPrevious().is && startNode.getPrevious().is( 'br' ) ) ) );
 
 				// Append a temporary <span>&#65279;</span> before the selection.
@@ -1081,2 +1087,3 @@
 			selection.addRange( nativeRange );
 		};
+} )();
