Index: /CKEditor/branches/prototype/_source/core/dom/range.js
===================================================================
--- /CKEditor/branches/prototype/_source/core/dom/range.js	(revision 2766)
+++ /CKEditor/branches/prototype/_source/core/dom/range.js	(revision 2767)
@@ -403,5 +403,5 @@
 		},
 
-		getCommonAncestor : function()
+		getCommonAncestor : function( includeSelf )
 		{
 			var start = this.startContainer;
@@ -409,5 +409,9 @@
 
 			if ( start.equals( end ) )
+			{
+				if ( includeSelf && start.type == CKEDITOR.NODE_ELEMENT && this.startOffset == this.endOffset - 1 )
+					return start.getChild( this.startOffset );
 				return start;
+			}
 
 			if ( end.type == CKEDITOR.NODE_ELEMENT && end.contains( start ) )
Index: /CKEditor/branches/prototype/_source/plugins/dialog/plugin.js
===================================================================
--- /CKEditor/branches/prototype/_source/plugins/dialog/plugin.js	(revision 2766)
+++ /CKEditor/branches/prototype/_source/plugins/dialog/plugin.js	(revision 2767)
@@ -2181,6 +2181,6 @@
 		numberRegex = /^\d*(?:\.\d+)?$/;
 
-	CKEDITOR.dialog.VALIDATE_OR = 1;
-	CKEDITOR.dialog.VALIDATE_AND = 2;
+	CKEDITOR.VALIDATE_OR = 1;
+	CKEDITOR.VALIDATE_AND = 2;
 
 	CKEDITOR.dialog.validate =
@@ -2199,5 +2199,5 @@
 
 				var msg = undefined,
-					relation = CKEDITOR.dialog.VALIDATE_AND,
+					relation = CKEDITOR.VALIDATE_AND,
 					functions = [], i;
 
@@ -2219,8 +2219,8 @@
 					relation = arguments[i];
 
-				var passed = ( relation == CKEDITOR.dialog.VALIDATE_AND ? true : false );
+				var passed = ( relation == CKEDITOR.VALIDATE_AND ? true : false );
 				for ( i = 0 ; i < functions.length ; i++ )
 				{
-					if ( relation == CKEDITOR.dialog.VALIDATE_AND )
+					if ( relation == CKEDITOR.VALIDATE_AND )
 						passed = passed && functions[i]( value );
 					else
Index: /CKEditor/branches/prototype/_source/plugins/link/dialogs/link.js
===================================================================
--- /CKEditor/branches/prototype/_source/plugins/link/dialogs/link.js	(revision 2766)
+++ /CKEditor/branches/prototype/_source/plugins/link/dialogs/link.js	(revision 2767)
@@ -668,12 +668,6 @@
 				ranges[0].enlarge( CKEDITOR.ENLARGE_ELEMENT );
 
-				var rangeRoot = ranges[0].startContainer.getChild( ranges[0].startOffset ), element;
-				if ( ranges[0].startContainer.equals( ranges[0].endContainer )
-						&& ranges[0].startContainer.type == CKEDITOR.NODE_ELEMENT 
-						&& ranges[0].startOffset == ranges[0].endOffset - 1 )
-					rangeRoot = ranges[0].startContainer.getChild( ranges[0].startOffset );
-				else
-					rangeRoot = ranges[0].getCommonAncestor();
-				element = rangeRoot.getAscendant( 'a', true );
+				var rangeRoot = ranges[0].getCommonAncestor( true ),
+					element = rangeRoot.getAscendant( 'a', true );
 				if ( element && element.getAttribute( 'href' ) )
 				{
Index: /CKEditor/branches/prototype/_source/plugins/link/plugin.js
===================================================================
--- /CKEditor/branches/prototype/_source/plugins/link/plugin.js	(revision 2766)
+++ /CKEditor/branches/prototype/_source/plugins/link/plugin.js	(revision 2767)
@@ -42,7 +42,11 @@
 		editor.on( 'selectionChange', function( evt )
 			{
+				/*
+				 * Despite our initial hope, document.queryCommandEnabled() does not work
+				 * for this in Firefox. So we must detect the state by element paths.
+				 */
 				var command = editor.getCommand( 'unlink' ),
-					element = evt.data.path.lastElement;
-				if ( element.getName() == 'a' && element.getAttribute( 'href' ) )
+					element = evt.data.path.lastElement.getAscendant( 'a', true );
+				if ( element && element.getName() == 'a' && element.getAttribute( 'href' ) )
 					command.state = CKEDITOR.TRISTATE_OFF;
 				else
@@ -59,5 +63,27 @@
 	exec : function( editor )
 	{
-		// TODO: There seems to be no way to remove the <a> tag via style system yet.
+		/*
+		 * execCommand( 'unlink', ... ) in Firefox leaves behind <span> tags at where
+		 * the <a> was, so again we have to remove the link ourselves. (See #430)
+		 *
+		 * TODO: Use the style system when it's complete. Let's use execCommand()
+		 * as a stopgap solution for now.
+		 */
+		var selection = editor.getSelection(),
+			ranges = selection.getRanges(),
+			rangeRoot,
+			element;
+
+		for ( var i = 0 ; i < ranges.length ; i++ )
+		{
+			rangeRoot = ranges[i].getCommonAncestor( true );
+			element = rangeRoot.getAscendant( 'a' );
+			if ( !element )
+				continue;
+			ranges[i].selectNodeContents( element );
+		}
+
+		selection.selectRanges( ranges );
+		editor.document.$.execCommand( 'unlink', false, null );
 	}
 };
