Index: trunk/src/wp-includes/class-wp-customize-manager.php
===================================================================
--- trunk/src/wp-includes/class-wp-customize-manager.php	(revision 38933)
+++ trunk/src/wp-includes/class-wp-customize-manager.php	(revision 38948)
@@ -280,4 +280,5 @@
 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-image-control.php' );
 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-background-image-control.php' );
+		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-background-position-control.php' );
 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-cropped-image-control.php' );
 		require_once( ABSPATH . WPINC . '/customize/class-wp-customize-site-icon-control.php' );
@@ -3027,4 +3028,5 @@
 		$this->register_control_type( 'WP_Customize_Image_Control' );
 		$this->register_control_type( 'WP_Customize_Background_Image_Control' );
+		$this->register_control_type( 'WP_Customize_Background_Position_Control' );
 		$this->register_control_type( 'WP_Customize_Cropped_Image_Control' );
 		$this->register_control_type( 'WP_Customize_Site_Icon_Control' );
@@ -3277,26 +3279,30 @@
 			'default'        => get_theme_support( 'custom-background', 'default-image' ),
 			'theme_supports' => 'custom-background',
+			'sanitize_callback' => array( $this, '_sanitize_background_setting' ),
 		) );
 
 		$this->add_setting( new WP_Customize_Background_Image_Setting( $this, 'background_image_thumb', array(
 			'theme_supports' => 'custom-background',
+			'sanitize_callback' => array( $this, '_sanitize_background_setting' ),
 		) ) );
 
 		$this->add_control( new WP_Customize_Background_Image_Control( $this ) );
 
-		$this->add_setting( 'background_repeat', array(
-			'default'        => get_theme_support( 'custom-background', 'default-repeat' ),
+		$this->add_setting( 'background_preset', array(
+			'default'        => get_theme_support( 'custom-background', 'default-preset' ),
 			'theme_supports' => 'custom-background',
-		) );
-
-		$this->add_control( 'background_repeat', array(
-			'label'      => __( 'Background Repeat' ),
+			'sanitize_callback' => array( $this, '_sanitize_background_setting' ),
+		) );
+
+		$this->add_control( 'background_preset', array(
+			'label'      => _x( 'Preset', 'Background Preset' ),
 			'section'    => 'background_image',
-			'type'       => 'radio',
+			'type'       => 'select',
 			'choices'    => array(
-				'no-repeat'  => __('No Repeat'),
-				'repeat'     => __('Tile'),
-				'repeat-x'   => __('Tile Horizontally'),
-				'repeat-y'   => __('Tile Vertically'),
+				'default' => _x( 'Default', 'Default Preset' ),
+				'fill'    => __( 'Fill Screen' ),
+				'fit'     => __( 'Fit to Screen' ),
+				'repeat'  => _x( 'Repeat', 'Repeat Image' ),
+				'custom'  => _x( 'Custom', 'Custom Preset' ),
 			),
 		) );
@@ -3305,36 +3311,68 @@
 			'default'        => get_theme_support( 'custom-background', 'default-position-x' ),
 			'theme_supports' => 'custom-background',
-		) );
-
-		$this->add_control( 'background_position_x', array(
-			'label'      => __( 'Background Position' ),
+			'sanitize_callback' => array( $this, '_sanitize_background_setting' ),
+		) );
+
+		$this->add_setting( 'background_position_y', array(
+			'default'        => get_theme_support( 'custom-background', 'default-position-y' ),
+			'theme_supports' => 'custom-background',
+			'sanitize_callback' => array( $this, '_sanitize_background_setting' ),
+		) );
+
+		$this->add_control( new WP_Customize_Background_Position_Control( $this, 'background_position', array(
+			'label'    => __( 'Image Position' ),
+			'section'  => 'background_image',
+			'settings' => array(
+				'x' => 'background_position_x',
+				'y' => 'background_position_y',
+			),
+		) ) );
+
+		$this->add_setting( 'background_size', array(
+			'default'        => get_theme_support( 'custom-background', 'default-size' ),
+			'theme_supports' => 'custom-background',
+			'sanitize_callback' => array( $this, '_sanitize_background_setting' ),
+		) );
+
+		$this->add_control( 'background_size', array(
+			'label'      => __( 'Image Size' ),
 			'section'    => 'background_image',
-			'type'       => 'radio',
+			'type'       => 'select',
 			'choices'    => array(
-				'left'       => __('Left'),
-				'center'     => __('Center'),
-				'right'      => __('Right'),
+				'auto'    => __( 'Original' ),
+				'contain' => __( 'Fit to Screen' ),
+				'cover'   => __( 'Fill Screen' ),
 			),
 		) );
 
+		$this->add_setting( 'background_repeat', array(
+			'default'           => get_theme_support( 'custom-background', 'default-repeat' ),
+			'sanitize_callback' => array( $this, '_sanitize_background_setting' ),
+			'theme_supports'    => 'custom-background',
+		) );
+
+		$this->add_control( 'background_repeat', array(
+			'label'    => __( 'Repeat Background Image' ),
+			'section'  => 'background_image',
+			'type'     => 'checkbox',
+		) );
+
 		$this->add_setting( 'background_attachment', array(
-			'default'        => get_theme_support( 'custom-background', 'default-attachment' ),
-			'theme_supports' => 'custom-background',
+			'default'           => get_theme_support( 'custom-background', 'default-attachment' ),
+			'sanitize_callback' => array( $this, '_sanitize_background_setting' ),
+			'theme_supports'    => 'custom-background',
 		) );
 
 		$this->add_control( 'background_attachment', array(
-			'label'      => __( 'Background Attachment' ),
-			'section'    => 'background_image',
-			'type'       => 'radio',
-			'choices'    => array(
-				'scroll'     => __('Scroll'),
-				'fixed'      => __('Fixed'),
-			),
-		) );
+			'label'    => __( 'Scroll with Page' ),
+			'section'  => 'background_image',
+			'type'     => 'checkbox',
+		) );
+
 
 		// If the theme is using the default background callback, we can update
 		// the background CSS using postMessage.
 		if ( get_theme_support( 'custom-background', 'wp-head-callback' ) === '_custom_background_cb' ) {
-			foreach ( array( 'color', 'image', 'position_x', 'repeat', 'attachment' ) as $prop ) {
+			foreach ( array( 'color', 'image', 'preset', 'position_x', 'position_y', 'size', 'repeat', 'attachment' ) as $prop ) {
 				$this->get_setting( 'background_' . $prop )->transport = 'postMessage';
 			}
@@ -3625,4 +3663,47 @@
 
 	/**
+	 * Callback for validating a background setting value.
+	 *
+	 * @since 4.7.0
+	 * @access private
+	 *
+	 * @param string $value Repeat value.
+	 * @param WP_Customize_Setting $setting Setting.
+	 * @return string|WP_Error Background value or validation error.
+	 */
+	public function _sanitize_background_setting( $value, $setting ) {
+		if ( 'background_repeat' === $setting->id ) {
+			if ( ! in_array( $value, array( 'repeat-x', 'repeat-y', 'repeat', 'no-repeat' ) ) ) {
+				return new WP_Error( 'invalid_value', __( 'Invalid value for background repeat.' ) );
+			}
+		} else if ( 'background_attachment' === $setting->id ) {
+			if ( ! in_array( $value, array( 'fixed', 'scroll' ) ) ) {
+				return new WP_Error( 'invalid_value', __( 'Invalid value for background attachment.' ) );
+			}
+		} else if ( 'background_position_x' === $setting->id ) {
+			if ( ! in_array( $value, array( 'left', 'center', 'right' ), true ) ) {
+				return new WP_Error( 'invalid_value', __( 'Invalid value for background position X.' ) );
+			}
+		} else if ( 'background_position_y' === $setting->id ) {
+			if ( ! in_array( $value, array( 'top', 'center', 'bottom' ), true ) ) {
+				return new WP_Error( 'invalid_value', __( 'Invalid value for background position Y.' ) );
+			}
+		} else if ( 'background_size' === $setting->id ) {
+			if ( ! in_array( $value, array( 'auto', 'contain', 'cover' ), true ) ) {
+				return new WP_Error( 'invalid_value', __( 'Invalid value for background size.' ) );
+			}
+		} else if ( 'background_preset' === $setting->id ) {
+			if ( ! in_array( $value, array( 'default', 'fill', 'fit', 'repeat', 'custom' ), true ) ) {
+				return new WP_Error( 'invalid_value', __( 'Invalid value for background size.' ) );
+			}
+		} else if ( 'background_image' === $setting->id || 'background_image_thumb' === $setting->id ) {
+			$value = empty( $value ) ? '' : esc_url_raw( $value );
+		} else {
+			return new WP_Error( 'unrecognized_setting', __( 'Unrecognized background setting.' ) );
+		}
+		return $value;
+	}
+
+	/**
 	 * Callback for rendering the custom logo, used in the custom_logo partial.
 	 *
