Index: _source/plugins/clipboard/plugin.js
===================================================================
--- _source/plugins/clipboard/plugin.js	(revision 6311)
+++ _source/plugins/clipboard/plugin.js	(revision )
@@ -406,7 +406,7 @@
 				// For improved performance, we're checking the readOnly state on selectionChange instead of hooking a key event for that.
 				editor.on( 'selectionChange', function( evt )
 				{
-					inReadOnly = evt.data.selection.getCommonAncestor().isReadOnly();
+					inReadOnly = evt.data.selection.getRanges()[ 0 ].checkReadOnly();
 				});
 
 				// If the "contextmenu" plugin is loaded, register the listeners.
@@ -414,7 +414,7 @@
 				{
 					editor.contextMenu.addListener( function( element, selection )
 						{
-							var readOnly = selection.getCommonAncestor().isReadOnly();
+							var readOnly = selection.getRanges()[ 0 ].checkReadOnly();
 							return {
 								cut : !readOnly && stateFromNamedCommand( 'Cut', editor ),
 								copy : stateFromNamedCommand( 'Copy', editor ),
Index: _source/plugins/wysiwygarea/plugin.js
===================================================================
--- _source/plugins/wysiwygarea/plugin.js	(revision 6306)
+++ _source/plugins/wysiwygarea/plugin.js	(revision )
@@ -25,15 +25,7 @@
 				|| element.isBlockBoundary() && CKEDITOR.dtd.$empty[ element.getName() ];
 	}
 
-	function checkReadOnly( selection )
-	{
-		if ( selection.getType() == CKEDITOR.SELECTION_ELEMENT )
-			return selection.getSelectedElement().isReadOnly();
-		else
-			return selection.getCommonAncestor().isReadOnly();
-	}
 
-
 	function onInsert( insertFunc )
 	{
 		return function( evt )
@@ -42,10 +34,6 @@
 			{
 				this.focus();
 
-				var selection = this.getSelection();
-				if ( checkReadOnly( selection ) )
-					return;
-
 				this.fire( 'saveSnapshot' );
 
 				insertFunc.call( this, evt.data );
@@ -67,7 +55,13 @@
 		if ( this.dataProcessor )
 			data = this.dataProcessor.toHtml( data );
 
-		var selection = this.getSelection();
+		// HTML insertion only considers the first range.
+		var selection = this.getSelection(),
+			range = selection.getRanges()[ 0 ];
+
+		if ( range.checkReadOnly() )
+			return;
+
 		if ( CKEDITOR.env.ie )
 		{
 			var selIsLocked = selection.isLocked;
@@ -86,7 +80,7 @@
 				// (#6005), we need to make some checks and eventually
 				// delete the selection first.
 
-				var range = selection.getRanges()[0],
+					range = selection.getRanges()[0],
 						endContainer = range && range.endContainer;
 
 				if ( endContainer &&
@@ -209,61 +203,67 @@
 		{
 			range = ranges[ i ];
 
+				if ( !range.checkReadOnly() )
+				{
-			// Remove the original contents.
-			range.deleteContents();
+					// Remove the original contents.
+					range.deleteContents();
 
-			clone = !i && element || element.clone( 1 );
+					clone = !i && element || element.clone( 1 );
 
-			// If we're inserting a block at dtd-violated position, split
-			// the parent blocks until we reach blockLimit.
-			var current, dtd;
-			if ( isBlock )
-			{
-				while ( ( current = range.getCommonAncestor( 0, 1 ) )
-						&& ( dtd = CKEDITOR.dtd[ current.getName() ] )
-						&& !( dtd && dtd [ elementName ] ) )
-				{
-					// Split up inline elements.
-					if ( current.getName() in CKEDITOR.dtd.span )
-						range.splitElement( current );
-					// If we're in an empty block which indicate a new paragraph,
-					// simply replace it with the inserting block.(#3664)
-					else if ( range.checkStartOfBlock()
-							&& range.checkEndOfBlock() )
-					{
-						range.setStartBefore( current );
-						range.collapse( true );
-						current.remove();
-					}
-					else
-						range.splitBlock();
-				}
-			}
+					// If we're inserting a block at dtd-violated position, split
+					// the parent blocks until we reach blockLimit.
+					var current, dtd;
+					if ( isBlock )
+					{
+						while ( ( current = range.getCommonAncestor( 0, 1 ) )
+								&& ( dtd = CKEDITOR.dtd[ current.getName() ] )
+								&& !( dtd && dtd [ elementName ] ) )
+						{
+							// Split up inline elements.
+							if ( current.getName() in CKEDITOR.dtd.span )
+								range.splitElement( current );
+							// If we're in an empty block which indicate a new paragraph,
+							// simply replace it with the inserting block.(#3664)
+							else if ( range.checkStartOfBlock()
+									&& range.checkEndOfBlock() )
+							{
+								range.setStartBefore( current );
+								range.collapse( true );
+								current.remove();
+							}
+							else
+								range.splitBlock();
+						}
+					}
 
-			// Insert the new node.
-			range.insertNode( clone );
+					// Insert the new node.
+					range.insertNode( clone );
 
-			// Save the last element reference so we can make the
-			// selection later.
-			if ( !lastElement )
-				lastElement = clone;
-		}
+					// Save the last element reference so we can make the
+					// selection later.
+					if ( !lastElement )
+						lastElement = clone;
+				}
+			}
 
+			if ( lastElement )
+			{
-		range.moveToPosition( lastElement, CKEDITOR.POSITION_AFTER_END );
+				range.moveToPosition( lastElement, CKEDITOR.POSITION_AFTER_END );
 
-		// If we're inserting a block element immediatelly followed by
-		// another block element, the selection must move there. (#3100,#5436)
-		if ( isBlock )
-		{
-			var next = lastElement.getNext( notWhitespaceEval ),
-					nextName = next && next.type == CKEDITOR.NODE_ELEMENT && next.getName();
+				// If we're inserting a block element immediatelly followed by
+				// another block element, the selection must move there. (#3100,#5436)
+				if ( isBlock )
+				{
+					var next = lastElement.getNext( notWhitespaceEval ),
+						nextName = next && next.type == CKEDITOR.NODE_ELEMENT && next.getName();
 
-			// Check if it's a block element that accepts text.
-			if ( nextName && CKEDITOR.dtd.$block[ nextName ] && CKEDITOR.dtd[ nextName ]['#'] )
-				range.moveToElementEditStart( next );
-		}
+					// Check if it's a block element that accepts text.
+					if ( nextName && CKEDITOR.dtd.$block[ nextName ] && CKEDITOR.dtd[ nextName ]['#'] )
+						range.moveToElementEditStart( next );
+				}
+			}
 
-		selection.selectRanges( [ range ] );
+			selection.selectRanges( [ range ] );
 
 		if ( selIsLocked )
 			this.getSelection().lock();
Index: _source/core/dom/range.js
===================================================================
--- _source/core/dom/range.js	(revision 6241)
+++ _source/core/dom/range.js	(revision )
@@ -1755,6 +1755,47 @@
 		},
 
 		/**
+		 * Check if elements at which the range boundaries anchor are read-only,
+		 * with respect to "contenteditable" attribute.
+		 */
+		checkReadOnly : ( function()
+		{
+			function checkNodesEditable( node, anotherEnd )
+			{
+				while( node )
+				{
+					if ( node.type == CKEDITOR.NODE_ELEMENT )
+					{
+						if ( node.getAttribute( 'contentEditable' ) == 'false'
+							&& !node.data( 'cke-editable' ) )
+						{
+							return 0;
+						}
+						// Range enclosed entirely in an editable element.
+						else if ( node.is( 'body' )
+							|| node.getAttribute( 'contentEditable' ) == 'true'
+							&& ( node.contains( anotherEnd ) || node.equals( anotherEnd ) ) )
+						{
+							break;
+						}
+					}
+					node = node.getParent();
+				}
+
+				return 1;
+			}
+
+			return function()
+			{
+				var startNode = this.startContainer,
+					endNode = this.endContainer;
+
+				// Check if elements path at both boundaries are editable.
+				return !( checkNodesEditable( startNode, endNode ) && checkNodesEditable( endNode, startNode ) )
+			};
+		})(),
+
+		/**
 		 * Moves the range boundaries to the first/end editing point inside an
 		 * element. For example, in an element tree like
 		 * "&lt;p&gt;&lt;b&gt;&lt;i&gt;&lt;/i&gt;&lt;/b&gt; Text&lt;/p&gt;", the start editing point is
Index: _source/plugins/scayt/plugin.js
===================================================================
--- _source/plugins/scayt/plugin.js	(revision 6241)
+++ _source/plugins/scayt/plugin.js	(revision )
@@ -626,7 +626,7 @@
 				editor.contextMenu.addListener( function( element, selection )
 					{
 						if ( !plugin.isScaytEnabled( editor )
-								|| selection.getCommonAncestor().isReadOnly() )
+								|| selection.getRanges()[ 0 ].checkReadOnly() )
 							return null;
 
 						var scayt_control = plugin.getScayt( editor ),
