Index: /CKEditor/trunk/CHANGES.html
===================================================================
--- /CKEditor/trunk/CHANGES.html	(revision 6560)
+++ /CKEditor/trunk/CHANGES.html	(revision 6561)
@@ -76,4 +76,5 @@
 		<li><a href="http://dev.ckeditor.com/ticket/6236">#6236</a> : [IE] Fixing malformed nested list structure which was introduced by Backspace key.</li>
 		<li><a href="http://dev.ckeditor.com/ticket/6649">#6649</a> : [IE] Fully table selection is not working sometimes.</li>
+		<li><a href="http://dev.ckeditor.com/ticket/6946">#6946</a> : Html parser is now able to fix orphan list items.</li>
 		<li>Updated the following language files:<ul>
 			<li><a href="http://dev.ckeditor.com/ticket/7124">#7124</a> : Czech;</li>
Index: /CKEditor/trunk/_source/core/htmlparser/fragment.js
===================================================================
--- /CKEditor/trunk/_source/core/htmlparser/fragment.js	(revision 6560)
+++ /CKEditor/trunk/_source/core/htmlparser/fragment.js	(revision 6561)
@@ -38,14 +38,12 @@
 (function()
 {
-	// Elements which the end tag is marked as optional in the HTML 4.01 DTD
-	// (expect empty elements).
-	var optionalClose = {colgroup:1,dd:1,dt:1,li:1,option:1,p:1,td:1,tfoot:1,th:1,thead:1,tr:1};
-
 	// Block-level elements whose internal structure should be respected during
 	// parser fixing.
-	var nonBreakingBlocks = CKEDITOR.tools.extend(
-			{table:1,ul:1,ol:1,dl:1},
-			CKEDITOR.dtd.table, CKEDITOR.dtd.ul, CKEDITOR.dtd.ol, CKEDITOR.dtd.dl ),
-		listBlocks = CKEDITOR.dtd.$list, listItems = CKEDITOR.dtd.$listItem;
+	var nonBreakingBlocks = CKEDITOR.tools.extend( { table:1,ul:1,ol:1,dl:1 }, CKEDITOR.dtd.table, CKEDITOR.dtd.ul, CKEDITOR.dtd.ol, CKEDITOR.dtd.dl );
+
+	var listBlocks = { ol:1, ul:1 };
+
+	// Dtd of the fragment element, basically it accept anything except for intermediate structure, e.g. orphan <li>.
+	var rootDtd = CKEDITOR.tools.extend( {}, { html: 1 }, CKEDITOR.dtd.html, CKEDITOR.dtd.body, CKEDITOR.dtd.head, { style:1,script:1 } );
 
 	/**
@@ -68,6 +66,5 @@
 			currentNode = fragment,
 		    // Indicate we're inside a <pre> element, spaces should be touched differently.
-			inPre = false,
-			returnPoint;
+			inPre = false;
 
 		function checkPending( newTagName )
@@ -115,11 +112,27 @@
 		}
 
-		function addElement( element, target, enforceCurrent )
-		{
+		/*
+		* Beside of simply append specified element to target, it also takes
+		* care of other dirty lifts like forcing block in body, trimming spaces at
+		* the block boundaries etc.
+		*
+		* Note: This function should NOT change the "currentNode" global unless
+		* there's a return point node specified on the element.
+		 */
+		function addElement( element, target )
+		{
+			// Ignore any element that has already been added.
+			if ( element.previous !== undefined )
+				return;
+
 			target = target || currentNode || fragment;
+
+			// Current element might be mangled by fix body below,
+			// save it for restore later.
+			var savedCurrent = currentNode;
 
 			// If the target is the fragment and this inline element can't go inside
 			// body (if fixForBody).
-			if ( fixForBody && !target.type )
+			if ( fixForBody && ( !target.type || target.name == 'body' ) )
 			{
 				var elementName, realElementName;
@@ -131,8 +144,6 @@
 					elementName =  element.name;
 
-				if ( elementName && elementName in CKEDITOR.dtd.$inline )
-				{
-					var savedCurrent = currentNode;
-
+				if ( elementName && !( elementName in CKEDITOR.dtd.$body || elementName == 'body' || element.isOrphan ) )
+				{
 					// Create a <p> in the fragment.
 					currentNode = target;
@@ -141,7 +152,4 @@
 					// The new target now is the <p>.
 					target = currentNode;
-
-					if ( enforceCurrent )
-						currentNode = savedCurrent;
 				}
 			}
@@ -171,4 +179,6 @@
 				delete element.returnPoint;
 			}
+			else
+				currentNode = savedCurrent;
 		}
 
@@ -202,79 +212,67 @@
 			}
 
-			var currentName = currentNode.name;
-
-			var currentDtd = currentName
-				&& ( CKEDITOR.dtd[ currentName ]
-					|| ( currentNode._.isBlockLike ? CKEDITOR.dtd.div : CKEDITOR.dtd.span ) );
-
-			// If the element cannot be child of the current element.
-			if ( currentDtd   // Fragment could receive any elements.
-				 && !element.isUnknown && !currentNode.isUnknown && !currentDtd[ tagName ] )
-			{
-
-				var reApply = false,
-					addPoint;   // New position to start adding nodes.
-
-				// Fixing malformed nested lists by moving it into a previous list item. (#3828)
-				if ( tagName in listBlocks
-					&& currentName in listBlocks )
-				{
-					var children = currentNode.children,
-						lastChild = children[ children.length - 1 ];
-
-					// Establish the list item if it's not existed.
-					if ( !( lastChild && lastChild.name in listItems ) )
-						addElement( ( lastChild = new CKEDITOR.htmlParser.element( 'li' ) ), currentNode );
-
-					returnPoint = currentNode, addPoint = lastChild;
-				}
-				// If the element name is the same as the current element name,
-				// then just close the current one and append the new one to the
-				// parent. This situation usually happens with <p>, <li>, <dt> and
-				// <dd>, specially in IE. Do not enter in this if block in this case.
-				else if ( tagName == currentName )
-				{
-					addElement( currentNode, currentNode.parent );
-				}
-				else if ( tagName in CKEDITOR.dtd.$listItem )
-				{
-					parser.onTagOpen( 'ul', {} );
-					addPoint = currentNode;
-					reApply = true;
-				}
-				else
-				{
-					if ( nonBreakingBlocks[ currentName ] )
+			while( 1 )
+			{
+				var currentName = currentNode.name;
+
+				var currentDtd = currentName ? ( CKEDITOR.dtd[ currentName ]
+						|| ( currentNode._.isBlockLike ? CKEDITOR.dtd.div : CKEDITOR.dtd.span ) )
+						: rootDtd;
+
+				// If the element cannot be child of the current element.
+				if ( !element.isUnknown && !currentNode.isUnknown && !currentDtd[ tagName ] )
+				{
+					// Fixing malformed nested lists by moving it into a previous list item. (#3828)
+					if ( tagName in listBlocks
+						&& currentName in listBlocks )
 					{
-						if ( !returnPoint )
-							returnPoint = currentNode;
+						var children = currentNode.children,
+							lastChild = children[ children.length - 1 ];
+
+						// Establish the list item if it's not existed.
+						if ( !( lastChild && lastChild.name == 'li' ) )
+							addElement( ( lastChild = new CKEDITOR.htmlParser.element( 'li' ) ), currentNode );
+
+						currentNode = lastChild;
+					}
+					// Establish new list root for orphan list items.
+					else if ( tagName in CKEDITOR.dtd.$listItem && currentName != tagName )
+						parser.onTagOpen( tagName == 'li' ? 'ul' : 'dl', {} );
+					// We're inside a structural block like table and list, AND the incoming element
+					// is not of the same type (e.g. <td>td1<td>td2</td>), we simply add this new one before it,
+					// and most importantly, return back to here once this element is added,
+					// e.g. <table><tr><td>td1</td><p>p1</p><td>td2</td></tr></table>
+					else if ( currentName in nonBreakingBlocks && currentName != tagName )
+					{
+						!element.returnPoint && ( element.returnPoint = currentNode );
+						currentNode = currentNode.parent;
 					}
 					else
 					{
-						addElement( currentNode, currentNode.parent, true );
-
-						if ( !optionalClose[ currentName ] )
+						// The current element is an inline element, which
+						// need to be continued even after the close, so put
+						// it in the pending list.
+						if ( currentName in CKEDITOR.dtd.$inline )
+							pendingInline.unshift( currentNode );
+
+						// The most common case where we just need to close the
+						// current one and append the new one to the parent.
+						if ( currentNode.parent )
 						{
-							// The current element is an inline element, which
-							// cannot hold the new one. Put it in the pending list,
-							// and try adding the new one after it.
-							pendingInline.unshift( currentNode );
+							addElement( currentNode, currentNode.parent );
+							currentNode = currentNode.parent;
+						}
+						// We've tried our best to fix the embarrassment here, while
+						// this element still doesn't find it's parent, mark it as
+						// orphan and show our tolerance to it.
+						else
+						{
+							element.isOrphan = 1;
+							break;
 						}
 					}
-
-					reApply = true;
-				}
-
-				if ( addPoint )
-					currentNode = addPoint;
-				// Try adding it to the return point, or the parent element.
+				}
 				else
-					currentNode = currentNode.returnPoint || currentNode.parent;
-
-				if ( reApply )
-				{
-					parser.onTagOpen.apply( this, arguments );
-					return;
-				}
+					break;
 			}
 
@@ -283,6 +281,4 @@
 
 			element.parent = currentNode;
-			element.returnPoint = returnPoint;
-			returnPoint = 0;
 
 			if ( element.isEmpty )
@@ -322,5 +318,6 @@
 				pendingAdd.push( candidate );
 
-				candidate = candidate.parent;
+				// Make sure return point is properly restored.
+				candidate = candidate.returnPoint || candidate.parent;
 			}
 
@@ -406,18 +403,8 @@
 		while ( currentNode != fragment )
 		{
-			var parent = currentNode.parent,
-				node = currentNode;
-
-			if ( fixForBody
-				 && ( !parent.type || parent.name == 'body' )
-				 && !CKEDITOR.dtd.$body[ node.name ] )
-			{
-				currentNode = parent;
-				parser.onTagOpen( fixForBody, {} );
-				parent = currentNode;
-			}
-
-			parent.add( node );
-			currentNode = parent;
+			// Make sure return point is properly restored.
+			var returnPoint = currentNode.returnPoint;
+			addElement( currentNode, currentNode.parent );
+			currentNode = returnPoint || currentNode.parent;
 		}
 
