Index: /CKEditor/trunk/_source/plugins/htmldataprocessor/plugin.js
===================================================================
--- /CKEditor/trunk/_source/plugins/htmldataprocessor/plugin.js	(revision 3659)
+++ /CKEditor/trunk/_source/plugins/htmldataprocessor/plugin.js	(revision 3660)
@@ -6,4 +6,65 @@
 (function()
 {
+	// Regex to scan for &nbsp; at the end of blocks, which are actually placeholders.
+	var tailNbspRegex = /^[\t\r\n ]*&nbsp;$/;
+
+	function trimFillers( block, fromSource )
+	{
+		// If the current node is a block, and if we're converting from source or
+		// we're not in IE then search for and remove any tailing BR node.
+		//
+		// Also, any &nbsp; at the end of blocks are fillers, remove them as well.
+		// (#2886)
+		var children = block.children;
+		var lastChild = children[ children.length - 1 ];
+		if ( lastChild )
+		{
+			if ( ( fromSource || !CKEDITOR.env.ie ) && lastChild.type == CKEDITOR.NODE_ELEMENT && lastChild.name == 'br' )
+				children.pop();
+			if ( lastChild.type == CKEDITOR.NODE_TEXT && tailNbspRegex.test( lastChild.value ) )
+				children.pop();
+		}
+	}
+
+	function blockNeedsExtension( block )
+	{
+		if ( block.children.length < 1 )
+			return true;
+
+		var lastChild = block.children[ block.children.length - 1 ];
+		return lastChild.type == CKEDITOR.NODE_ELEMENT && lastChild.name == 'br';
+	}
+
+	function extendBlockForDisplay( block )
+	{
+		trimFillers( block, true );
+
+		if ( blockNeedsExtension( block ) )
+		{
+			if ( CKEDITOR.env.ie )
+				block.children.push( new CKEDITOR.htmlParser.text( '\xa0' ) );
+			else
+				block.children.push( new CKEDITOR.htmlParser.element( 'br', {} ) );
+		}
+	}
+
+	function extendBlockForOutput( block )
+	{
+		trimFillers( block );
+
+		if ( blockNeedsExtension( block ) )
+			block.children.push( new CKEDITOR.htmlParser.text( '\xa0' ) );
+	}
+
+	var dtd = CKEDITOR.dtd;
+	
+	// Find out the list of block-like tags that can contain <br>.
+	var blockLikeTags = CKEDITOR.tools.extend( {}, dtd.$block, dtd.$listItem, dtd.$tableContent );
+	for ( var i in blockLikeTags )
+	{
+		if ( ! ( 'br' in dtd[i] ) )
+			delete blockLikeTags[i];
+	}
+
 	var defaultDataFilterRules =
 	{
@@ -21,4 +82,9 @@
 		]
 	};
+
+	var defaultDataBlockFilterRules = { elements : {} };
+
+	for ( var i in blockLikeTags )
+		defaultDataBlockFilterRules.elements[ i ] = extendBlockForDisplay;
 
 	/**
@@ -104,4 +170,9 @@
 		};
 
+	var defaultHtmlBlockFilterRules = { elements : {} };
+	
+	for ( var i in blockLikeTags )
+		defaultHtmlBlockFilterRules.elements[ i ] = extendBlockForOutput;
+
 	if ( CKEDITOR.env.ie )
 	{
@@ -132,5 +203,7 @@
 
 			dataProcessor.dataFilter.addRules( defaultDataFilterRules );
+			dataProcessor.dataFilter.addRules( defaultDataBlockFilterRules );
 			dataProcessor.htmlFilter.addRules( defaultHtmlFilterRules );
+			dataProcessor.htmlFilter.addRules( defaultHtmlBlockFilterRules );
 		}
 	});
Index: /CKEditor/trunk/_source/tests/plugins/htmldataprocessor/htmldataprocessor.html
===================================================================
--- /CKEditor/trunk/_source/tests/plugins/htmldataprocessor/htmldataprocessor.html	(revision 3659)
+++ /CKEditor/trunk/_source/tests/plugins/htmldataprocessor/htmldataprocessor.html	(revision 3660)
@@ -4,5 +4,7 @@
 	<title>Plugin: htmldataprocessor</title>
 	<link rel="stylesheet" type="text/css" href="../../test.css" />
-	<script type="text/javascript" src="../../../../ckeditor_source.js"></script>
+	<script type="text/javascript" src="../../../../ckeditor_source.js"></script> <!-- %REMOVE_LINE%
+	<script type="text/javascript" src="../../../ckeditor.js"></script>
+	%REMOVE_LINE% -->
 	<script type="text/javascript" src="../../test.js"></script>
 	<script type="text/javascript">
@@ -103,4 +105,117 @@
 		},
 
+		test_toDataFormat_ticket_2886_1 : function()
+		{
+			var editor = CKEDITOR.instances.editor1;
+			var test = this;
+			var isReady = !!editor.dataProcessor;
+
+			if ( !isReady )
+			{
+				editor.on( 'instanceReady', function()
+				{
+					isReady = true;
+				} );
+			}
+
+			this.wait( function()
+				{
+					if ( !isReady )
+					{
+						test.wait( arguments.callee, 100 );
+						return;
+					}
+
+					assert.areSame( '<p>\n\t&nbsp;</p>\n',
+						editor.dataProcessor.toDataFormat( '<p></p>' ) );
+				}, 100 );
+		},
+
+		test_toDataFormat_ticket_2886_2 : function()
+		{
+			var dataProcessor = CKEDITOR.instances.editor1.dataProcessor;
+
+			var source = '<p>Some text<br><br><br></p>';
+			if ( CKEDITOR.env.ie )
+				source = '<p>Some text<br><br></p>';
+			assert.areSame( '<p>\n\tSome text<br />\n\t<br />\n\t&nbsp;</p>\n',
+				dataProcessor.toDataFormat( source ) );
+		},
+
+		test_toDataFormat_ticket_2886_3 : function()
+		{
+			var dataProcessor = CKEDITOR.instances.editor1.dataProcessor;
+
+			assert.areSame( '<p>\n\tSome text<br />\n\t<br />\n\t<br />\n\tSome more text</p>\n',
+				dataProcessor.toDataFormat( '<p>Some text<br><br><br>Some more text</p>' ) );
+		},
+
+		test_toDataFormat_ticket_2886_4 : function()
+		{
+			var dataProcessor = CKEDITOR.instances.editor1.dataProcessor;
+
+			assert.areSame( '<p>\n\tSome text<br />\n\t<br />\n\t&nbsp;</p>\n',
+				dataProcessor.toDataFormat( '<p>Some text<br><br>&nbsp;</p>' ) );
+		},
+
+		test_toDataFormat_ticket_2886_5 : function()
+		{
+			if ( CKEDITOR.env.ie )
+				return;
+
+			var dataProcessor = CKEDITOR.instances.editor1.dataProcessor;
+
+			assert.areSame( '<p>\n\t&nbsp;</p>\n',
+				dataProcessor.toDataFormat( '<p><br></p>' ) );
+		},
+
+		test_toDataFormat_ticket_2886_6 : function()
+		{
+			var dataProcessor = CKEDITOR.instances.editor1.dataProcessor;
+
+			var source = '<p><br><br></p>';
+			if ( CKEDITOR.env.ie )
+				source = '<p><br></p>';
+
+			assert.areSame( '<p>\n\t<br />\n\t&nbsp;</p>\n',
+				dataProcessor.toDataFormat( source ) );
+		},
+
+		test_toHtml_ticket_2886_1 : function()
+		{
+			var dataProcessor = CKEDITOR.instances.editor1.dataProcessor;
+
+			var expected = '<p><br /></p>';
+			if ( CKEDITOR.env.ie )
+				expected = '<p>\xa0</p>';
+			assert.areSame( expected, dataProcessor.toHtml( '<p></p>' ) );
+		},
+
+		test_toHtml_ticket_2886_2 : function()
+		{
+			var dataProcessor = CKEDITOR.instances.editor1.dataProcessor;
+
+			var expected = '<p>Some text<br />Some other text</p>';
+			assert.areSame( expected, dataProcessor.toHtml( '<p>Some text<br>Some other text</p>' ) );
+		},
+
+		test_toHtml_ticket_2886_3 : function()
+		{
+			var dataProcessor = CKEDITOR.instances.editor1.dataProcessor;
+
+			var expected = '<p>Some text<br /><br /></p>';
+			if ( CKEDITOR.env.ie )
+				expected = '<p>Some text<br />\xa0</p>';
+			assert.areSame( expected, dataProcessor.toHtml( '<p>Some text<br>&nbsp;</p>' ) );
+		},
+
+		test_toHtml_ticket_2886_4 : function()
+		{
+			var dataProcessor = CKEDITOR.instances.editor1.dataProcessor;
+
+			var expected = '<p>Some text</p>';
+			assert.areSame( expected, dataProcessor.toHtml( '<p>Some text<br></p>' ));
+		},
+
 		name : document.title
 	};
@@ -116,4 +231,5 @@
 </head>
 <body>
+	<textarea id="editor1" class="ckeditor" cols="80" rows="10"></textarea>
 </body>
 </html>
