Index: wp-admin/includes/ajax-actions.php
===================================================================
--- wp-admin/includes/ajax-actions.php	(revision 22966)
+++ wp-admin/includes/ajax-actions.php	(working copy)
@@ -1674,23 +1674,31 @@
 }
 
 function wp_ajax_set_post_thumbnail() {
+	$json = ! empty( $_REQUEST['json'] );
+
 	$post_ID = intval( $_POST['post_id'] );
-	if ( !current_user_can( 'edit_post', $post_ID ) )
-		wp_die( -1 );
+	if ( !current_user_can( 'edit_post', $post_ID ) ) {
+		$json ? wp_send_json_error() : wp_die( -1 );
+	}
 	$thumbnail_id = intval( $_POST['thumbnail_id'] );
 
 	check_ajax_referer( "set_post_thumbnail-$post_ID" );
 
 	if ( $thumbnail_id == '-1' ) {
-		if ( delete_post_thumbnail( $post_ID ) )
-			wp_die( _wp_post_thumbnail_html( null, $post_ID ) );
-		else
-			wp_die( 0 );
+		if ( delete_post_thumbnail( $post_ID ) ) {
+			$return = _wp_post_thumbnail_html( null, $post_ID );
+			$json ? wp_send_json_success( $return ) : wp_die( $return );
+		} else {
+			$json ? wp_send_json_error() : wp_die( 0 );
+		}
 	}
 
-	if ( set_post_thumbnail( $post_ID, $thumbnail_id ) )
-		wp_die( _wp_post_thumbnail_html( $thumbnail_id, $post_ID ) );
-	wp_die( 0 );
+	if ( set_post_thumbnail( $post_ID, $thumbnail_id ) ) {
+		$return = _wp_post_thumbnail_html( $thumbnail_id, $post_ID );
+		$json ? wp_send_json_success( $return ) : wp_die( $return );
+	}
+
+	$json ? wp_send_json_error() : wp_die( 0 );
 }
 
 function wp_ajax_date_format() {
Index: wp-admin/includes/meta-boxes.php
===================================================================
--- wp-admin/includes/meta-boxes.php	(revision 22966)
+++ wp-admin/includes/meta-boxes.php	(working copy)
@@ -1001,119 +1001,6 @@
  * @since 2.9.0
  */
 function post_thumbnail_meta_box( $post ) {
-	global $_wp_additional_image_sizes;
-
-	?><script type="text/javascript">
-	jQuery( function($) {
-		var $element     = $('#select-featured-image'),
-			$thumbnailId = $element.find('input[name="thumbnail_id"]'),
-			title        = '<?php _e( "Choose a Featured Image" ); ?>',
-			update       = '<?php _e( "Update Featured Image" ); ?>',
-			Attachment   = wp.media.model.Attachment,
-			frame, setFeaturedImage;
-
-		setFeaturedImage = function( thumbnailId ) {
-			var selection;
-
-			$element.find('img').remove();
-			$element.toggleClass( 'has-featured-image', -1 != thumbnailId );
-			$thumbnailId.val( thumbnailId );
-
-			if ( frame ) {
-				selection = frame.state('library').get('selection');
-
-				if ( -1 === thumbnailId )
-					selection.clear();
-				else
-					selection.add( Attachment.get( thumbnailId ) );
-			}
-		};
-
-		$element.on( 'click', '.choose, img', function( event ) {
-			var options, thumbnailId, attachment;
-
-			event.preventDefault();
-
-			if ( frame ) {
-				frame.open();
-				return;
-			}
-
-			options = {
-				title:   title,
-				library: {
-					type: 'image'
-				}
-			};
-
-			thumbnailId = $thumbnailId.val();
-			if ( '' !== thumbnailId && -1 !== thumbnailId ) {
-				attachment = Attachment.get( thumbnailId );
-				attachment.fetch();
-				options.selection = [ attachment ];
-			}
-
-			frame = wp.media( options );
-
-			frame.state('library').set( 'filterable', 'uploaded' );
-
-			frame.toolbar.on( 'activate:select', function() {
-				frame.toolbar.view().set({
-					select: {
-						style: 'primary',
-						text:  update,
-
-						click: function() {
-							var selection = frame.state().get('selection'),
-								model = selection.first(),
-								sizes = model.get('sizes'),
-								size;
-
-							setFeaturedImage( model.id );
-
-							// @todo: might need a size hierarchy equivalent.
-							if ( sizes )
-								size = sizes['post-thumbnail'] || sizes.medium;
-
-							// @todo: Need a better way of accessing full size
-							// data besides just calling toJSON().
-							size = size || model.toJSON();
-
-							frame.close();
-
-							$( '<img />', {
-								src:    size.url,
-								width:  size.width
-							}).prependTo( $element );
-						}
-					}
-				});
-			});
-
-			frame.toolbar.mode('select');
-		});
-
-		$element.on( 'click', '.remove', function( event ) {
-			event.preventDefault();
-			setFeaturedImage( -1 );
-		});
-	});
-	</script>
-
-	<?php
-	$thumbnail_id   = get_post_meta( $post->ID, '_thumbnail_id', true );
-	$thumbnail_size = isset( $_wp_additional_image_sizes['post-thumbnail'] ) ? 'post-thumbnail' : 'medium';
-	$thumbnail_html = wp_get_attachment_image( $thumbnail_id, $thumbnail_size );
-
-	$classes = empty( $thumbnail_id ) ? '' : 'has-featured-image';
-
-	?><div id="select-featured-image"
-		class="<?php echo esc_attr( $classes ); ?>"
-		data-post-id="<?php echo esc_attr( $post->ID ); ?>">
-		<?php echo $thumbnail_html; ?>
-		<input type="hidden" name="thumbnail_id" value="<?php echo esc_attr( $thumbnail_id ); ?>" />
-		<a href="#" class="choose button-secondary"><?php _e( 'Choose a Featured Image' ); ?></a>
-		<a href="#" class="remove"><?php _e( 'Remove Featured Image' ); ?></a>
-	</div>
-	<?php
+	$thumbnail_id = get_post_meta( $post->ID, '_thumbnail_id', true );
+	echo _wp_post_thumbnail_html( $thumbnail_id, $post->ID );
 }
\ No newline at end of file
Index: wp-admin/includes/post.php
===================================================================
--- wp-admin/includes/post.php	(revision 22966)
+++ wp-admin/includes/post.php	(working copy)
@@ -199,14 +199,6 @@
 			set_post_format( $post_ID, false );
 	}
 
-	// Featured Images
-	if ( isset( $post_data['thumbnail_id'] ) ) {
-		if ( '-1' == $post_data['thumbnail_id'] )
-			delete_post_thumbnail( $post_ID );
-		else
-			set_post_thumbnail( $post_ID, $post_data['thumbnail_id'] );
-	}
-
 	// Meta Stuff
 	if ( isset($post_data['meta']) && $post_data['meta'] ) {
 		foreach ( $post_data['meta'] as $key => $value ) {
Index: wp-admin/js/custom-background.js
===================================================================
--- wp-admin/js/custom-background.js	(revision 22966)
+++ wp-admin/js/custom-background.js	(working copy)
@@ -57,7 +57,7 @@
 				});
 			});
 
-			frame.setState('library');
+			frame.setState('library').open();
 		});
 	});
 })(jQuery);
\ No newline at end of file
Index: wp-admin/js/custom-header.js
===================================================================
--- wp-admin/js/custom-header.js	(revision 22966)
+++ wp-admin/js/custom-header.js	(working copy)
@@ -40,7 +40,7 @@
 				});
 			});
 
-			frame.setState('library');
+			frame.setState('library').open();
 		});
 	});
 }(jQuery));
Index: wp-includes/css/media-views.css
===================================================================
--- wp-includes/css/media-views.css	(revision 22966)
+++ wp-includes/css/media-views.css	(working copy)
@@ -403,6 +403,11 @@
  */
 .media-frame {
 	overflow: hidden;
+	position: absolute;
+	top: 0;
+	left: 0;
+	right: 0;
+	bottom: 0;
 }
 
 .media-frame .region-content {
Index: wp-includes/js/media-editor.js
===================================================================
--- wp-includes/js/media-editor.js	(revision 22966)
+++ wp-includes/js/media-editor.js	(working copy)
@@ -375,6 +375,7 @@
 
 			workflow = workflows[ id ] = wp.media( _.defaults( options || {}, {
 				frame:    'post',
+				state:    'upload',
 				title:    wp.media.view.l10n.addMedia,
 				multiple: true
 			} ) );
@@ -427,14 +428,52 @@
 				}
 			}, this );
 
+			workflow.state('featured-image').on( 'select', function() {
+				var settings = wp.media.view.settings,
+					featuredImage = settings.featuredImage,
+					selection = this.get('selection').single();
+
+				if ( ! featuredImage )
+					return;
+
+				featuredImage.id = selection ? selection.id : -1;
+				wp.media.post( 'set-post-thumbnail', {
+					json:         true,
+					post_id:      settings.postId,
+					thumbnail_id: featuredImage.id,
+					_wpnonce:     featuredImage.nonce
+				}).done( function( html ) {
+					$( '.inside', '#postimagediv' ).html( html );
+				});
+			});
+
+			workflow.setState( workflow.options.state );
 			return workflow;
 		},
 
+		id: function( id ) {
+			if ( id )
+				return id;
+
+			// If an empty `id` is provided, default to `wpActiveEditor`.
+			id = wpActiveEditor;
+
+			// If that doesn't work, fall back to `tinymce.activeEditor.id`.
+			if ( ! id && typeof tinymce !== 'undefined' && tinymce.activeEditor )
+				id = tinymce.activeEditor.id;
+
+			// Last but not least, fall back to the empty string.
+			id = id || '';
+			return id;
+		},
+
 		get: function( id ) {
+			id = this.id( id );
 			return workflows[ id ];
 		},
 
 		remove: function( id ) {
+			id = this.id( id );
 			delete workflows[ id ];
 		},
 
@@ -497,6 +536,30 @@
 			}
 		},
 
+		open: function( id ) {
+			var workflow, editor;
+
+			id = this.id( id );
+
+			// Save a bookmark of the caret position in IE.
+			if ( typeof tinymce !== 'undefined' ) {
+				editor = tinymce.get( id );
+
+				if ( tinymce.isIE && editor && ! editor.isHidden() ) {
+					editor.focus();
+					editor.windowManager.insertimagebookmark = editor.selection.getBookmark();
+				}
+			}
+
+			workflow = this.get( id );
+
+			// Initialize the editor's workflow if we haven't yet.
+			if ( ! workflow )
+				workflow = this.add( id );
+
+			return workflow.open();
+		},
+
 		init: function() {
 			$(document.body).on( 'click', '.insert-media', function( event ) {
 				var $this = $(this),
@@ -513,45 +576,40 @@
 
 				wp.media.editor.open( editor );
 			});
-		},
 
-		open: function( id ) {
-			var workflow, editor;
+			// Open the content media manager to the 'featured image' tab when
+			// the post thumbnail is clicked.
+			$('#postimagediv').on( 'click', '#set-post-thumbnail', function( event ) {
+				event.preventDefault();
+				// Stop propagation to prevent thickbox from activating.
+				event.stopPropagation();
 
-			// If an empty `id` is provided, default to `wpActiveEditor`.
-			id = id || wpActiveEditor;
+				// Always get the 'content' frame, since this is tailored to post.php.
+				var frame = wp.media.editor.add('content'),
+					initialState = frame.state().id,
+					escape;
 
-			if ( typeof tinymce !== 'undefined' && tinymce.activeEditor ) {
-				// If that doesn't work, fall back to `tinymce.activeEditor`.
-				if ( ! id ) {
-					editor = tinymce.activeEditor;
-					id = id || editor.id;
-				} else {
-					editor = tinymce.get( id );
-				}
+				escape = function() {
+					// Only run this event once.
+					this.off( 'escape', escape );
 
-				// Save a bookmark of the caret position, needed for IE
-				if ( tinymce.isIE && editor && ! editor.isHidden() ) {
-					editor.focus();
-					editor.windowManager.insertimagebookmark = editor.selection.getBookmark();
-				}
-			}
+					// If we're still on the 'featured-image' state, restore
+					// the initial state.
+					if ( 'featured-image' === this.state().id )
+						this.setState( initialState );
+				};
 
-			// Last but not least, fall back to the empty string.
-			id = id || '';
+				frame.on( 'escape', escape, frame );
 
-			workflow = wp.media.editor.get( id );
+				frame.setState('featured-image').open();
 
-			// If the workflow exists, open it.
-			// Initialize the editor's workflow if we haven't yet.
-			if ( workflow )
-				workflow.open();
-			else
-				workflow = wp.media.editor.add( id );
-
-			return workflow;
+			// Update the featured image id when the 'remove' link is clicked.
+			}).on( 'click', '#remove-post-thumbnail', function() {
+				wp.media.view.settings.featuredImage.id = -1;
+			});
 		}
 	};
 
+	_.bindAll( wp.media.editor, 'open' );
 	$( wp.media.editor.init );
 }(jQuery));
Index: wp-includes/js/media-models.js
===================================================================
--- wp-includes/js/media-models.js	(revision 22966)
+++ wp-includes/js/media-models.js	(working copy)
@@ -30,10 +30,8 @@
 			frame = new MediaFrame.Post( attributes );
 
 		delete attributes.frame;
-		// Set the default state.
-		frame.setState( frame.options.state );
-		// Render, attach, and open the frame.
-		return frame.render().attach().open();
+
+		return frame;
 	};
 
 	_.extend( media, { model: {}, view: {}, controller: {} });
@@ -235,6 +233,9 @@
 
 			// Overload the `update` request so properties can be saved.
 			} else if ( 'update' === method ) {
+				if ( ! this.get('nonces') )
+					return $.Deferred().resolveWith( this ).promise();
+
 				options = options || {};
 				options.context = this;
 
Index: wp-includes/js/media-views.js
===================================================================
--- wp-includes/js/media-views.js	(revision 22966)
+++ wp-includes/js/media-views.js	(working copy)
@@ -169,7 +169,7 @@
 			// created the `states` collection, or are trying to select a state
 			// that does not exist.
 			if ( ( previous && id === previous.id ) || ! this.states || ! this.states.get( id ) )
-				return;
+				return this;
 
 			if ( previous ) {
 				previous.trigger('deactivate');
@@ -178,6 +178,8 @@
 
 			this._state = id;
 			this.state().trigger('activate');
+
+			return this;
 		},
 
 		// Returns the previous active state.
@@ -547,7 +549,40 @@
 		}
 	});
 
+	// wp.media.controller.FeaturedImage
+	// ---------------------------------
+	media.controller.FeaturedImage = media.controller.Library.extend({
+		defaults: _.defaults({
+			id:         'featured-image',
+			filterable: 'uploaded',
+			multiple:   false,
+			menu:       'main',
+			toolbar:    'featured-image'
+		}, media.controller.Library.prototype.defaults ),
 
+		initialize: function() {
+			// If we haven't been provided a `library`, create a `Selection`.
+			if ( ! this.get('library') )
+				this.set( 'library', media.query({ type: 'image' }) );
+
+			media.controller.Library.prototype.initialize.apply( this, arguments );
+		},
+
+		activate: function() {
+			var selection = this.get('selection'),
+				id = media.view.settings.featuredImage.id;
+
+			if ( '' !== id && -1 !== id ) {
+				attachment = Attachment.get( id );
+				attachment.fetch();
+			}
+
+			selection.reset( attachment ? [ attachment ] : [] );
+			media.controller.Library.prototype.activate.apply( this, arguments );
+		}
+	});
+
+
 	// wp.media.controller.Embed
 	// -------------------------
 	media.controller.Embed = media.controller.State.extend({
@@ -603,7 +638,9 @@
 			}, this );
 
 			this.set( 'url', '' );
-			this.frame.toolbar.view().refresh();
+
+			if ( this.id === this.frame.state().id )
+				this.frame.toolbar.view().refresh();
 		}
 	});
 
@@ -1075,9 +1112,10 @@
 			if ( this.options.modal ) {
 				this.modal = new media.view.Modal({
 					controller: this,
-					$content:   this.$el,
 					title:      this.options.title
 				});
+
+				this.modal.content( this );
 			}
 
 			// Force the uploader off if the upload limit has been exceeded or
@@ -1100,14 +1138,6 @@
 			this.on( 'attach', _.bind( this.views.ready, this.views ), this );
 		},
 
-		render: function() {
-			if ( this.modal )
-				this.modal.render();
-
-			media.view.Frame.prototype.render.apply( this, arguments );
-			return this;
-		},
-
 		createIframeStates: function( options ) {
 			var settings = media.view.settings,
 				tabs = settings.tabs,
@@ -1184,7 +1214,7 @@
 	});
 
 	// Map some of the modal's methods to the frame.
-	_.each(['open','close','attach','detach'], function( method ) {
+	_.each(['open','close','attach','detach','escape'], function( method ) {
 		media.view.MediaFrame.prototype[ method ] = function( view ) {
 			if ( this.modal )
 				this.modal[ method ].apply( this.modal, arguments );
@@ -1200,7 +1230,6 @@
 			media.view.MediaFrame.prototype.initialize.apply( this, arguments );
 
 			_.defaults( this.options, {
-				state:     'upload',
 				selection: [],
 				library:   {},
 				multiple:  false
@@ -1347,7 +1376,6 @@
 	media.view.MediaFrame.Post = media.view.MediaFrame.Select.extend({
 		initialize: function() {
 			_.defaults( this.options, {
-				state:     'upload',
 				multiple:  true,
 				editing:   false
 			});
@@ -1407,6 +1435,14 @@
 					libraryState: 'gallery-edit'
 				})
 			]);
+
+
+			if ( media.view.settings.featuredImage ) {
+				this.states.add( new media.controller.FeaturedImage({
+					controller: this,
+					menu:       'main'
+				}) );
+			}
 		},
 
 		bindHandlers: function() {
@@ -1425,6 +1461,7 @@
 					toolbar: {
 						'main-attachments': 'mainAttachmentsToolbar',
 						'main-embed':       'mainEmbedToolbar',
+						'featured-image':   'featuredImageToolbar',
 						'gallery-edit':     'galleryEditToolbar',
 						'gallery-add':      'galleryAddToolbar'
 					}
@@ -1442,15 +1479,22 @@
 			media.view.MediaFrame.Select.prototype.mainMenu.call( this, { silent: true });
 
 			this.menu.view().set({
-				separateLibrary: new media.View({
+				'library-separator': new media.View({
 					className: 'separator',
 					priority: 60
 				}),
-				embed: {
+				'embed': {
 					text: l10n.fromUrlTitle,
 					priority: 80
 				}
 			});
+
+			if ( media.view.settings.featuredImage ) {
+				this.menu.view().set( 'featured-image', {
+					text: l10n.featuredImageTitle,
+					priority: 100
+				});
+			}
 		},
 
 		galleryMenu: function() {
@@ -1557,6 +1601,14 @@
 			}) );
 		},
 
+		featuredImageToolbar: function() {
+			this.toolbar.view( new media.view.Toolbar.Select({
+				controller: this,
+				text:       l10n.setFeaturedImage,
+				state:      this.options.state || 'upload'
+			}) );
+		},
+
 		mainEmbedToolbar: function() {
 			this.toolbar.view( new media.view.Toolbar.Embed({
 				controller: this
@@ -1627,7 +1679,7 @@
 		},
 
 		events: {
-			'click .media-modal-backdrop, .media-modal-close': 'closeHandler',
+			'click .media-modal-backdrop, .media-modal-close': 'escapeHandler',
 			'keydown': 'keydown'
 		},
 
@@ -1641,56 +1693,73 @@
 			});
 		},
 
-		render: function() {
-			// Ensure content div exists.
-			this.options.$content = this.options.$content || $('<div />');
-
-			// Detach the content element from the DOM to prevent
-			// `this.$el.html()` from garbage collecting its events.
-			this.options.$content.detach();
-
-			this.$el.html( this.template({
+		prepare: function() {
+			return {
 				title: this.options.title
-			}) );
-
-			this.options.$content.addClass('media-modal-content');
-			this.$('.media-modal').append( this.options.$content );
-			return this;
+			};
 		},
 
 		attach: function() {
+			if ( this.views.attached )
+				return this;
+
+			if ( ! this.views.rendered )
+				this.render();
+
 			this.$el.appendTo( this.options.container );
+
+			// Manually mark the view as attached and trigger ready.
+			this.views.attached = true;
+			this.views.ready();
+
 			return this.propagate('attach');
 		},
 
 		detach: function() {
+			if ( this.$el.is(':visible') )
+				this.close();
+
 			this.$el.detach();
+			this.views.attached = false;
 			return this.propagate('detach');
 		},
 
 		open: function() {
+			if ( this.$el.is(':visible') )
+				return this;
+
+			if ( ! this.views.attached )
+				this.attach();
+
 			this.$el.show().focus();
 			return this.propagate('open');
 		},
 
-		close: function() {
+		close: function( options ) {
+			if ( ! this.views.attached || ! this.$el.is(':visible') )
+				return this;
+
 			this.$el.hide();
-			return this.propagate('close');
+			this.propagate('close');
+
+			if ( options && options.escape )
+				this.propagate('escape');
+
+			return this;
 		},
 
-		closeHandler: function( event ) {
+		escape: function() {
+			return this.close({ escape: true });
+		},
+
+		escapeHandler: function( event ) {
 			event.preventDefault();
-			this.close();
+			this.escape();
 		},
 
-		content: function( $content ) {
-			// Detach any existing content to prevent events from being lost.
-			if ( this.options.$content )
-				this.options.$content.detach();
-
-			// Set and render the content.
-			this.options.$content = ( $content instanceof Backbone.View ) ? $content.$el : $content;
-			return this.render();
+		content: function( content ) {
+			this.views.set( '.media-modal-content', content );
+			return this;
 		},
 
 		// Triggers a modal event and if the `propagate` option is set,
@@ -1708,7 +1777,7 @@
 			// Close the modal when escape is pressed.
 			if ( 27 === event.which ) {
 				event.preventDefault();
-				this.close();
+				this.escape();
 				return;
 			}
 		}
Index: wp-includes/media.php
===================================================================
--- wp-includes/media.php	(revision 22966)
+++ wp-includes/media.php	(working copy)
@@ -1433,6 +1433,16 @@
 	if ( isset( $args['post'] ) ) {
 		$post = get_post( $args['post'] );
 		$settings['postId'] = $post->ID;
+
+		if ( current_theme_supports( 'post-thumbnails', $post->post_type ) && post_type_supports( $post->post_type, 'thumbnail' ) ) {
+
+			$featuredImageId = get_post_meta( $post->ID, '_thumbnail_id', true );
+
+			$settings['featuredImage'] = array(
+				'id'    => $featuredImageId ? $featuredImageId : -1,
+				'nonce' => wp_create_nonce( 'set_post_thumbnail-' . $post->ID ),
+			);
+		}
 	}
 
 	$hier = $post && is_post_type_hierarchical( $post->post_type );
@@ -1466,6 +1476,10 @@
 		// From URL
 		'fromUrlTitle'       => __( 'From URL' ),
 
+		// Featured Images
+		'featuredImageTitle'  => __( 'Featured Image' ),
+		'setFeaturedImage'    => __( 'Set featured image' ),
+
 		// Gallery
 		'createGalleryTitle' => __( 'Create Gallery' ),
 		'editGalleryTitle'   => __( 'Edit Gallery' ),
@@ -1508,6 +1522,7 @@
 		<div class="media-modal wp-core-ui">
 			<h3 class="media-modal-title">{{ data.title }}</h3>
 			<a class="media-modal-close media-modal-icon" href="#" title="<?php esc_attr_e('Close'); ?>"></a>
+			<div class="media-modal-content"></div>
 		</div>
 		<div class="media-modal-backdrop">
 			<div></div>
