Index: _source/plugins/htmlwriter/plugin.js
===================================================================
--- _source/plugins/htmlwriter/plugin.js	(revision 3676)
+++ _source/plugins/htmlwriter/plugin.js	Fri Oct 23 02:17:14 CST 2009
@@ -67,7 +67,7 @@
 
 		var dtd = CKEDITOR.dtd;
 
-		for ( var e in CKEDITOR.tools.extend( {}, dtd.$block, dtd.$listItem, dtd.$tableContent ) )
+		for ( var e in CKEDITOR.tools.extend( {}, dtd.$noneBodyContent, dtd.$block, dtd.$listItem, dtd.$tableContent ) )
 		{
 			this.setRules( e,
 				{
Index: _source/core/htmlparser/element.js
===================================================================
--- _source/core/htmlparser/element.js	(revision 3654)
+++ _source/core/htmlparser/element.js	Fri Oct 23 02:09:12 CST 2009
@@ -35,7 +35,7 @@
 	this.children = [];
 
 	var dtd			= CKEDITOR.dtd,
-		isBlockLike	= !!( dtd.$block[ name ] || dtd.$listItem[ name ] || dtd.$tableContent[ name ] ),
+		isBlockLike	= !!( dtd.$noneBodyContent[ name ] || dtd.$block[ name ] || dtd.$listItem[ name ] || dtd.$tableContent[ name ] ),
 		isEmpty		= !!dtd.$empty[ name ];
 
 	this.isEmpty	= isEmpty;
Index: _source/plugins/wysiwygarea/plugin.js
===================================================================
--- _source/plugins/wysiwygarea/plugin.js	(revision 4371)
+++ _source/plugins/wysiwygarea/plugin.js	Fri Oct 23 02:37:56 CST 2009
@@ -550,30 +550,11 @@
 							{
 								isLoadingData = true;
 
-								// Get the HTML version of the data.
+								// Get the full page HTML version of the data.
 								if ( editor.dataProcessor )
-								{
-									data = editor.dataProcessor.toHtml( data, fixForBody );
-								}
+									data = editor.dataProcessor.toHtml( data, fixForBody, true );
 
-								data =
-									editor.config.docType +
-									'<html dir="' + editor.config.contentsLangDirection + '">' +
-									'<head>' +
-										'<link type="text/css" rel="stylesheet" href="' +
-										[].concat( editor.config.contentsCss ).join( '"><link type="text/css" rel="stylesheet" href="' ) +
-										'">' +
-										'<style type="text/css" _fcktemp="true">' +
-											editor._.styles.join( '\n' ) +
-										'</style>'+
-									'</head>' +
-									'<body>' +
-										data +
-									'</body>' +
-									'</html>' +
-									activationScript;
-
-								window[ '_cke_htmlToLoad_' + editor.name ] = data;
+								window[ '_cke_htmlToLoad_' + editor.name ] = data + activationScript;
 								CKEDITOR._[ 'contentDomReady' + editor.name ] = contentDomReady;
 								createIFrame();
 
@@ -589,7 +570,9 @@
 
 							getData : function()
 							{
-								var data = iframe.getFrameDocument().getBody().getHtml();
+								var documentElement = iframe.getFrameDocument().getDocumentElement(),
+									docTypeAttr = documentElement.getAttribute( 'cke_docType' ),
+									data = ( docTypeAttr ? decodeURIComponent( docTypeAttr ) : '' ) + documentElement.getOuterHtml();
 
 								if ( editor.dataProcessor )
 									data = editor.dataProcessor.toDataFormat( data, fixForBody );
Index: _source/core/dom/element.js
===================================================================
--- _source/core/dom/element.js	(revision 4196)
+++ _source/core/dom/element.js	Thu Oct 22 14:18:32 CST 2009
@@ -328,7 +328,10 @@
 		 */
 		getHtml : function()
 		{
-			return this.$.innerHTML;
+			var retval = this.$.innerHTML;
+			// Strip <?xml:namespace> tag in the output HTML of
+			// namespaced element in IE(#3341).
+			return CKEDITOR.env.ie ? retval.replace( /<\?[^>]*>/g, '' ) : retval;
 		},
 
 		getOuterHtml : function()
Index: _source/core/htmlparser/filter.js
===================================================================
--- _source/core/htmlparser/filter.js	(revision 3309)
+++ _source/core/htmlparser/filter.js	Thu Oct 22 14:18:34 CST 2009
@@ -127,6 +127,9 @@
 
 	function addItemsToList( list, items, priority )
 	{
+		if( typeof items == 'function' )
+			items = [ items ];
+
 		var i, j,
 			listLength = list.length,
 			itemsLength = items && items.length;
Index: _source/core/dtd.js
===================================================================
--- _source/core/dtd.js	(revision 4320)
+++ _source/core/dtd.js	Thu Oct 22 19:16:32 CST 2009
@@ -51,23 +51,39 @@
 		N = {'#':1},
 		O = X({param:1},K),
 		P = X({form:1},A,D,E,I),
-		Q = {li:1};
+		Q = {li:1},
+        R = {style:1,script:1},
+        S = {base:1,link:1,meta:1,title:1},
+		T = X(S,R),
+		U = {head:1,body:1},
+		V = {html:1};
 
-	var block = {address:1,blockquote:1,center:1,dir:1,div:1,dl:1,fieldset:1,form:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1,hr:1,isindex:1,menu:1,noframes:1,ol:1,p:1,pre:1,table:1,ul:1};
+	var block = {address:1,blockquote:1,center:1,dir:1,div:1,dl:1,fieldset:1,form:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1,hr:1,isindex:1,menu:1,noframes:1,ol:1,p:1,pre:1,table:1,ul:1},
+		inline = {a:1,abbr:1,acronym:1,b:1,basefont:1,bdo:1,big:1,br:1,cite:1,code:1,dfn:1,em:1,font:1,i:1,img:1,input:1,kbd:1,label:1,q:1,s:1,samp:1,select:1,small:1,span:1,strike:1,strong:1,sub:1,sup:1,textarea:1,tt:1,u:1,'var':1,applet:1,button:1,del:1,iframe:1,ins:1,map:1,object:1,script:1};
 
     return /** @lends CKEDITOR.dtd */ {
 
 		// The "$" items have been added manually.
 
-		/**
+	    /**
+	     * The document element. 
+	     */
+	    $ : V,
+
+	    // List of elements won't appear under body.
+	    $noneBodyContent: X(V,U,S),
+
+	    /**
 		 * List of block elements, like "p" or "div".
 		 * @type Object
 		 * @example
 		 */
 		$block : block,
 
-		$body : X({script:1}, block),
+	    $inline : inline,
 
+		$body : X({script:1,style:1}, block),
+
 		$cdata : {script:1,style:1},
 
 		/**
@@ -120,7 +136,12 @@
 		 */
 		$tableContent : {caption:1,col:1,colgroup:1,tbody:1,td:1,tfoot:1,th:1,thead:1,tr:1},
 
+        html: U,
+	    head: T,
+	    style: N,
+	    script: N,
+	    body: X(inline, block),
-        col : {},
+	    col : {},
         tr : {td:1,th:1},
         img : {},
         colgroup : {col:1},
@@ -200,7 +221,11 @@
         pre : X(G,C),
         p : L,
         em : L,
-        dfn : L
+        dfn : L,
+	    base: {},
+		link: {},
+	    meta: {},
+	    title: N
     };
 })();
 
Index: _source/core/htmlparser/basicwriter.js
===================================================================
--- _source/core/htmlparser/basicwriter.js	(revision 3308)
+++ _source/core/htmlparser/basicwriter.js	Thu Oct 22 23:34:52 CST 2009
@@ -15,6 +15,11 @@
 
 	proto :
 	{
+		docType : function( docType )
+		{
+			this._.output.push( docType );
+		},
+
 		/**
 		 * Writes the tag opening part for a opener tag.
 		 * @param {String} tagName The element name for this tag.
@@ -117,6 +122,7 @@
 		reset : function()
 		{
 			this._.output = [];
+			this._.indent = false;
 		},
 
 		/**
Index: _source/core/htmlparser/fragment.js
===================================================================
--- _source/core/htmlparser/fragment.js	(revision 4373)
+++ _source/core/htmlparser/fragment.js	Fri Oct 23 02:17:14 CST 2009
@@ -18,6 +18,7 @@
 	 * alert( fragment.children.length );  "2"
 	 */
 	this.children = [];
+	this.attributes = {};
 
 	/**
 	 * Get the fragment parent. Should always be null.
@@ -30,6 +31,7 @@
 	/** @private */
 	this._ =
 	{
+		docType : '',
 		isBlockLike : true,
 		hasInlineStarted : false
 	};
@@ -114,7 +116,9 @@
 					elementName = realElementName;
 				else
 					elementName =  element.name;
-				if ( !( elementName in CKEDITOR.dtd.$body ) )
+				if ( elementName
+						&& !( elementName in CKEDITOR.dtd.$body )
+						&& !( elementName in CKEDITOR.dtd.$noneBodyContent )  )
 				{
 					var savedCurrent = currentNode;
 
@@ -156,6 +160,11 @@
 			}
 		}
 
+		parser.onDocType = function ( docType )
+		{
+			fragment._.docType = docType;
+		};
+
 		parser.onTagOpen = function( tagName, attributes, selfClosing )
 		{
 			var element = new CKEDITOR.htmlParser.element( tagName, attributes );
@@ -180,15 +189,14 @@
 			}
 
 			var currentName = currentNode.name,
-				currentDtd = ( currentName && CKEDITOR.dtd[ currentName ] ) || ( currentNode._.isBlockLike ? CKEDITOR.dtd.div : CKEDITOR.dtd.span );
+				currentDtd = currentName &&
+				             ( CKEDITOR.dtd[ currentName ]
+						       || ( currentNode._.isBlockLike ? CKEDITOR.dtd.div : CKEDITOR.dtd.span ) );
 
 			// If the element cannot be child of the current element.
-			if ( !element.isUnknown && !currentNode.isUnknown && !currentDtd[ tagName ] )
+			if ( currentDtd   // Fragment could receive any elements.
+				 && !element.isUnknown && !currentNode.isUnknown && !currentDtd[ tagName ] )
 			{
-				// If this is the fragment node, just ignore this tag and add
-				// its children.
-				if ( !currentName )
-					return;
 
 				var reApply = false,
 					addPoint;   // New position to start adding nodes.
@@ -330,6 +338,9 @@
 					index--;
 				}
 			}
+
+			if( tagName == 'body' )
+				fixForBody = false;
 		};
 
 		parser.onText = function( text )
@@ -345,7 +356,9 @@
 
 			checkPending();
 
-			if ( fixForBody && !currentNode.type )
+			if ( fixForBody
+				 && CKEDITOR.tools.trim( text )
+				 && ( !currentNode.type || currentNode.name == 'body' ) )
 				this.onTagOpen( fixForBody, {} );
 
 			// Shrinking consequential spaces into one single for all elements
@@ -375,7 +388,9 @@
 			var parent = currentNode.parent,
 				node = currentNode;
 
-			if ( fixForBody && !parent.type && !CKEDITOR.dtd.$body[ node.name ] )
+			if ( fixForBody
+				 && ( !parent.type || parent.name == 'body' )
+				 && !CKEDITOR.dtd.$body[ node.name ] )
 			{
 				currentNode = parent;
 				parser.onTagOpen( fixForBody, {} );
@@ -444,6 +459,13 @@
 		 */
 		writeHtml : function( writer, filter )
 		{
+			// Call element filter on fragment as well as write doc-type.
+			if( !this.name )
+			{
+				filter && filter.onElement( this );
+				this._.docType && writer.docType( this._.docType );
+			}
+
 			for ( var i = 0, len = this.children.length ; i < len ; i++ )
 				this.children[i].writeHtml( writer, filter );
 		}
Index: _source/plugins/htmldataprocessor/plugin.js
===================================================================
--- _source/plugins/htmldataprocessor/plugin.js	(revision 4373)
+++ _source/plugins/htmldataprocessor/plugin.js	Fri Oct 23 02:34:56 CST 2009
@@ -11,6 +11,17 @@
 
 	var protectedSourceMarker = '{cke_protected}';
 
+	function hasChild( element, tagName )
+	{
+		var children = element.children,
+			child;
+		for ( var i = 0; i < children.length; i++ )
+		{
+			child = children[ i ];
+			if( child.name && child.name == tagName )
+				return true;
+		}
+	}
 
 	// Return the last non-space child node of the block (#4344).
 	function lastNoneSpaceChild( block )
@@ -78,6 +89,20 @@
 	// We just avoid filler in <pre> right now.
 	// TODO: Support filler for <pre>, line break is also occupy line height.
 	delete blockLikeTags.pre;
+
+	function addRoot( element, tagName )
+	{
+		var childrens = element.children,
+			root = new CKEDITOR.htmlParser.element( tagName, {} );
+		element.children = [];
+		element.add( root );
+		for ( var i = 0; i < childrens.length; i++ )
+		{
+			root.add( childrens[ i ] );
+		}
+		return root;
+	}
+
 	var defaultDataFilterRules =
 	{
 		attributeNames :
@@ -199,6 +224,68 @@
 		};
 	}
 
+	function dropElement( element )
+	{
+		return false;
+	}
+
+	function removeElement( element )
+	{
+		delete element.name;
+	};
+
+	function cleanup( element )
+	{
+		var children = element.children,
+			child;
+		for ( var i = 0; i < children.length; i++ )
+		{
+			child = children[ i ];
+			// 1. Remove any white-spaces preserve by some browsers around and inside <head>.
+			// 2. Remove any 'cke_temp' elements.
+			if( child.type == CKEDITOR.NODE_TEXT && !CKEDITOR.tools.trim( child.value )
+				|| child.attributes && child.attributes.cke_temp )
+				children.splice( i--, 1 );
+		}
+	}
+
+	var nonFullPageHtmlFilterRules =
+	{
+		elements :
+		{
+			'html' : removeElement,
+			'body' : removeElement,
+			'head' : dropElement
+		}
+	};
+
+	var fullPageHtmlFilterRules =
+	{
+		attributeNames :
+		[
+			[ 'spellcheck', '' ],
+			[ 'cke_doctype', '' ],
+			[ 'contenteditable', '' ],
+			[ 'hidefocus', '' ]
+		],
+
+		attributes :
+		{
+			'spellcheck' : function( value, element )
+			{
+				if ( element.name == 'body' )
+					return false;
+			}
+		},
+
+		elements :
+		{
+			head : cleanup,
+			html : cleanup,
+			title : cleanup
+		}
+	};
+
 	var protectAttributeRegex = /<(?:a|area|img|input).*?\s((?:href|src|name)\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|(?:[^ "'>]+)))/gi;
 
 	function protectAttributes( html )
@@ -206,19 +293,34 @@
 		return html.replace( protectAttributeRegex, '$& _cke_saved_$1' );
 	}
 
-	var protectStyleTagsRegex = /<(style)(?=[ >])[^>]*>[^<]*<\/\1>/gi;
+	var protectStyleTagsRegex = /<(style)(?=[ >])[^>]*>[^<]*<\/\1>/gi,
+		protectFullPageElementsRegex = /<(:?link|meta|base).*?>/;
 	var encodedTagsRegex = /<cke:encoded>([^<]*)<\/cke:encoded>/gi;
 	var protectElementNamesRegex = /(<\/?)((?:object|embed|param).*?>)/gi;
+	var shelvefullPageElementsRegex = /(<\/?)((?:html|body|head|title).*?>)/gi,
+		shelvedElementsRegex = /(<\/?)cke_fullpage:([^>]*>)/gi;
 	var protectSelfClosingRegex = /<cke:param(.*?)\/>/gi;
 
-	function protectStyleTagsMatch( match )
+	function encodeMatched( match )
 	{
 		return '<cke:encoded>' + encodeURIComponent( match ) + '</cke:encoded>';
 	}
 
+	function shelveTags( html, isUnShelve )
+	{
+		if ( isUnShelve )
+			return unprotectEncodedTags( html.replace( shelvedElementsRegex, '$1$2' ) );
+		else
+		{
+			return html
+					.replace( shelvefullPageElementsRegex, '$1cke_fullpage:$2' )
+					.replace( protectFullPageElementsRegex, encodeMatched );
+		}
+	}
+
 	function protectStyleTags( html )
 	{
-		return html.replace( protectStyleTagsRegex, protectStyleTagsMatch );
+		return html.replace( protectStyleTagsRegex, encodeMatched );
 	}
 	function protectElementsNames( html )
 	{
@@ -291,10 +393,98 @@
 
 			dataProcessor.writer.forceSimpleAmpersand = editor.config.forceSimpleAmpersand;
 
-			dataProcessor.dataFilter.addRules( defaultDataFilterRules );
+
+			dataProcessor.dataFilter.addRules( CKEDITOR.tools.extend( defaultDataFilterRules,
+			{
+				// Full page support. (#4067)
+				elements :
+				{
+					$ : function( element )
+					{
+						// Fix full page at the root (fragment element).
+						if( ( element._.fullPage !== false ) && !element.name )
+						{
+							var firstChild = element.children[ 0 ];
+							// Full document.
+							if( firstChild && firstChild.name == 'html' )
+								return;
+							// Missing <html>.
+							if ( !firstChild || firstChild.name in dtd.html )
+								addRoot( element, 'html' );
+							// Missing <html> and <body>/<head>.
+							else
+							{
+								addRoot( addRoot( element, 'html' ),
+										firstChild.name in dtd.head ? 'head' : 'body' );
+							}
+						}
+					},
+
+					html : function ( element )
+					{
+						// Both <head> and <body> are mandatory.
+						if ( !hasChild( element, 'head' ) )
+							element.children.splice( 0, 0, new CKEDITOR.htmlParser.element( 'head', {} ) );
+						if ( !hasChild( element, 'body' ) )
+							element.children.splice( element.children.length, 0, new CKEDITOR.htmlParser.element( 'body', {} ) );
+
+						// 1. Save doc-type as a custom attribute;
+						// 2. Adding missing xml namespace attribute;
+						// 3. Adding missing language direction attribute.
+						var attrs = element.attributes,
+							docType = element.parent._.docType,
+							configDir = editor.config.contentsLangDirection;
+						if( docType )
+						{
+							attrs.cke_docType = encodeURIComponent( docType );
+							docType.match( /xhtml/i )
+									&& !attrs.xmlns
+									&& ( attrs.xmlns = 'http://www.w3.org/1999/xhtml' );
+						}
+						!attrs.dir && configDir && ( attrs.dir = configDir );
+					},
+
+					head : function ( element )
+					{
+						// <title> is mandatory.
+						if ( !hasChild( element, 'title' ) )
+							element.children.splice( 0, 0, new CKEDITOR.htmlParser.element( 'title', {} ) );
+
+						// Adding default styles.
+						var styleLinks = [].concat( editor.config.contentsCss ),
+							styleText = editor._.styles.join( '\n' );
+
+						if( styleLinks )
+						{
+							for ( var i = 0; i < styleLinks.length; i++ )
+							{
+								var link = new CKEDITOR.htmlParser.element( 'link',
+										{
+										  type: 'text/css' ,
+										  rel : 'stylesheet',
+										  href : styleLinks[ i ],
+										  cke_temp : 1
+										} );
+								element.add( link );
+							}
+						}
+						if( styleText )
+						{
+							var style = new CKEDITOR.htmlParser.element( 'style',
+									{
+									  type: 'text/css',
+									  cke_temp : 1
+									} );
+							style.add( new CKEDITOR.htmlParser.text( styleText ) );
+							element.add( style );
+						}
+					}
+				}
+			} ) );
 			dataProcessor.dataFilter.addRules( defaultDataBlockFilterRules );
 			dataProcessor.htmlFilter.addRules( defaultHtmlFilterRules );
 			dataProcessor.htmlFilter.addRules( defaultHtmlBlockFilterRules );
+			dataProcessor.htmlFilter.addRules( editor.config.fullPage ? fullPageHtmlFilterRules : nonFullPageHtmlFilterRules );
 		}
 	});
 
@@ -309,7 +499,7 @@
 
 	CKEDITOR.htmlDataProcessor.prototype =
 	{
-		toHtml : function( data, fixForBody )
+		toHtml : function( data, fixForBody, fullPage )
 		{
 			// The source data is already HTML, but we need to clean
 			// it up and apply the filter.
@@ -333,13 +523,16 @@
 			// protecting them into open-close.(#3591)
 			data = protectSelfClosingElements( data );
 
+			data = shelveTags( data );
 			// Call the browser to help us fixing a possibly invalid HTML
 			// structure.
-			var div = document.createElement( 'div' );
+			var div = new CKEDITOR.dom.element( 'div' );
 			// Add fake character to workaround IE comments bug. (#3801)
-			div.innerHTML = 'a' + data;
-			data = div.innerHTML.substr( 1 );
+			div.setHtml( 'a' + data );
+			data = div.getHtml().substr( 1 );
 
+			data = shelveTags( data, true );
+
 			if ( CKEDITOR.env.ie )
 				data = unprotectEncodedTags( data );
 
@@ -348,6 +541,14 @@
 			var fragment = CKEDITOR.htmlParser.fragment.fromHtml( data, fixForBody ),
 				writer = new CKEDITOR.htmlParser.basicWriter();
 
+			var configDocType = this.editor.config.docType;
+			// Is docType missing and been explicitly defined in configuration?
+			configDocType && !fragment._.docType && ( fragment._.docType = configDocType );
+
+			// Is Html snippet mode instead of full page?
+			( fullPage !== true )
+				&& ( fragment._.fullPage = false, delete fragment._.docType );
+
 			fragment.writeHtml( writer, this.dataFilter );
 
 			return writer.getHtml( true );
@@ -358,8 +559,11 @@
 			var writer = this.writer,
 				fragment = CKEDITOR.htmlParser.fragment.fromHtml( html, fixForBody );
 
-			writer.reset();
+			// Ignore doctype if not in full page output mode.
+			if ( !this.editor.config.fullPage && fragment._.docType )
+				delete fragment._.docType;
 
+			writer.reset();
 			fragment.writeHtml( writer, this.htmlFilter );
 
 			return writer.getHtml( true );
Index: _source/core/htmlparser.js
===================================================================
--- _source/core/htmlparser.js	(revision 4349)
+++ _source/core/htmlparser.js	Thu Oct 22 22:51:36 CST 2009
@@ -12,7 +12,8 @@
 {
 	this._ =
 	{
-		htmlPartsRegex : new RegExp( '<(?:(?:\\/([^>]+)>)|(?:!--([\\S|\\s]*?)-->)|(?:([^\\s>]+)\\s*((?:(?:[^"\'>]+)|(?:"[^"]*")|(?:\'[^\']*\'))*)\\/?>))', 'g' )
+		htmlPartsRegex : new RegExp( '<(?:(?:\\/([^>]+)>)|(?:!--([\\S|\\s]*?)-->)|(?:([^\\s>]+)\\s*((?:(?:[^"\'>]+)|(?:"[^"]*")|(?:\'[^\']*\'))*)\\/?>))', 'g' ),
+		docTypeRegex : /<!DOCTYPE[^>]*>/i
 	};
 };
 
@@ -24,6 +25,13 @@
 	CKEDITOR.htmlParser.prototype =
 	{
 		/**
+		 * Function to be fired when a docType defintion is found. This function
+		 * should be overriden when using this class.
+		 * @param {String} docType The declaration text.
+		 */
+		onDocType : function() {},
+
+		/**
 		 * Function to be fired when a tag opener is found. This function
 		 * should be overriden when using this class.
 		 * @param {String} tagName The tag name. The name is guarantted to be
@@ -113,6 +121,14 @@
 		 */
 		parse : function( html )
 		{
+			// Remove docType declaration at first.
+			var self = this;
+			html = html.replace( this._.docTypeRegex, function( match )
+			{
+				self.onDocType( match );
+				return '';
+			} );
+
 			var parts,
 				tagName,
 				nextIndex = 0,

