Index: src/wp-admin/admin-ajax.php
===================================================================
--- src/wp-admin/admin-ajax.php	(revision 33142)
+++ src/wp-admin/admin-ajax.php	(working copy)
@@ -62,7 +62,7 @@
 	'send-attachment-to-editor', 'save-attachment-order', 'heartbeat', 'get-revision-diffs',
 	'save-user-color-scheme', 'update-widget', 'query-themes', 'parse-embed', 'set-attachment-thumbnail',
 	'parse-media-shortcode', 'destroy-sessions', 'install-plugin', 'update-plugin', 'press-this-save-post',
-	'press-this-add-category',
+	'press-this-add-category', 'crop-image',
 );
 
 // Deprecated
Index: src/wp-admin/css/customize-controls.css
===================================================================
--- src/wp-admin/css/customize-controls.css	(revision 33142)
+++ src/wp-admin/css/customize-controls.css	(working copy)
@@ -771,6 +771,8 @@
 .customize-control-upload .current,
 .customize-control-image .current,
 .customize-control-background .current,
+.customize-control-cropped_image .current,
+.customize-control-site_icon .current,
 .customize-control-header .current {
 	margin-bottom: 8px;
 }
@@ -806,6 +808,12 @@
 .customize-control-background .remove-button,
 .customize-control-background .default-button,
 .customize-control-background .upload-button,
+.customize-control-cropped_image .remove-button,
+.customize-control-cropped_image .default-button,
+.customize-control-cropped_image .upload-button,
+.customize-control-site_icon .remove-button,
+.customize-control-site_icon .default-button,
+.customize-control-site_icon .upload-button,
 .customize-control-header button.new,
 .customize-control-header button.remove {
 	white-space: normal;
@@ -817,6 +825,8 @@
 .customize-control-upload .current .container,
 .customize-control-image .current .container,
 .customize-control-background .current .container,
+.customize-control-cropped_image .current .container,
+.customize-control-site_icon .current .container,
 .customize-control-header .current .container {
 	overflow: hidden;
 	-webkit-border-radius: 2px;
@@ -828,6 +838,8 @@
 .customize-control-media .current .container,
 .customize-control-upload .current .container,
 .customize-control-background .current .container,
+.customize-control-cropped_image .current .container,
+.customize-control-site_icon .current .container,
 .customize-control-image .current .container {
 	min-height: 40px;
 }
@@ -836,6 +848,8 @@
 .customize-control-upload .placeholder,
 .customize-control-image .placeholder,
 .customize-control-background .placeholder,
+.customize-control-cropped_image .placeholder,
+.customize-control-site_icon .placeholder,
 .customize-control-header .placeholder {
 	width: 100%;
 	position: relative;
@@ -847,6 +861,8 @@
 .customize-control-upload .inner,
 .customize-control-image .inner,
 .customize-control-background .inner,
+.customize-control-cropped_image .inner,
+.customize-control-site_icon .inner,
 .customize-control-header .inner {
 	display: none;
 	position: absolute;
@@ -860,6 +876,8 @@
 .customize-control-media .inner,
 .customize-control-upload .inner,
 .customize-control-background .inner,
+.customize-control-cropped_image .inner,
+.customize-control-site_icon .inner,
 .customize-control-image .inner {
 	display: block;
 	min-height: 40px;
@@ -869,6 +887,8 @@
 .customize-control-upload .inner,
 .customize-control-image .inner,
 .customize-control-background .inner,
+.customize-control-cropped_image .inner,
+.customize-control-site_icon .inner,
 .customize-control-header .inner,
 .customize-control-header .inner .dashicons {
 	line-height: 20px;
@@ -972,6 +992,8 @@
 .customize-control-upload .actions,
 .customize-control-image .actions,
 .customize-control-background .actions,
+.customize-control-cropped_image .actions,
+.customize-control-site_icon .actions,
 .customize-control-header .actions {
 	margin-bottom: 32px;
 }
@@ -990,6 +1012,8 @@
 .customize-control-upload img,
 .customize-control-image img,
 .customize-control-background img,
+.customize-control-cropped_image img,
+.customize-control-site_icon img,
 .customize-control-header img {
 	width: 100%;
 	-webkit-border-radius: 2px;
@@ -1004,6 +1028,10 @@
 .customize-control-image .default-button,
 .customize-control-background .remove-button,
 .customize-control-background .default-button,
+.customize-control-cropped_image .remove-button,
+.customize-control-cropped_image .default-button,
+.customize-control-site_icon .remove-button,
+.customize-control-site_icon .default-button,
 .customize-control-header .remove {
 	float: left;
 	margin-right: 3px;
@@ -1013,6 +1041,8 @@
 .customize-control-upload .upload-button,
 .customize-control-image .upload-button,
 .customize-control-background .upload-button,
+.customize-control-cropped_image .upload-button,
+.customize-control-site_icon .upload-button,
 .customize-control-header .new {
 	float: right;
 }
Index: src/wp-admin/includes/ajax-actions.php
===================================================================
--- src/wp-admin/includes/ajax-actions.php	(revision 33142)
+++ src/wp-admin/includes/ajax-actions.php	(working copy)
@@ -3052,3 +3052,62 @@
 
 	$GLOBALS['wp_press_this']->add_category();
 }
+
+/**
+ * AJAX handler for cropping an image.
+ *
+ * @since 4.3.0
+ *
+ * @global WP_Site_Icon $wp_site_icon
+ */
+function wp_ajax_crop_image() {
+	$attachment_id = absint( $_POST['id'] );
+
+	check_ajax_referer( 'image_editor-' . $attachment_id, 'nonce' );
+	if ( ! current_user_can( 'customize' ) ) {
+		wp_send_json_error();
+	}
+
+	$context = $_POST['context'];
+	$data    = array_map( 'absint', $_POST['cropDetails'] );
+	$cropped = wp_crop_image( $attachment_id, $data['x1'], $data['y1'], $data['width'], $data['height'], $data['dst_width'], $data['dst_height'] );
+
+	if ( ! $cropped || is_wp_error( $cropped ) ) {
+		wp_send_json_error( array( 'message' => __( 'Image could not be processed. Please go back and try again.' ) ) );
+	}
+
+	switch ( $context ) {
+		case 'site_icon':
+			require_once ABSPATH . '/wp-admin/includes/class-wp-site-icon.php';
+			global $wp_site_icon;
+
+			/** This filter is documented in wp-admin/custom-header.php */
+			$cropped = apply_filters( 'wp_create_file_in_uploads', $cropped, $attachment_id ); // For replication.
+			$object  = $wp_site_icon->create_attachment_object( $cropped, $attachment_id );
+			unset( $object['ID'] );
+
+			// Update the attachment.
+			add_filter( 'intermediate_image_sizes_advanced', array( $wp_site_icon, 'additional_sizes' ) );
+			$attachment_id = $wp_site_icon->insert_attachment( $object, $cropped );
+			remove_filter( 'intermediate_image_sizes_advanced', array( $wp_site_icon, 'additional_sizes' ) );
+			break;
+
+		default:
+
+			/**
+			 * Filters the attachment id for a cropped image.
+			 *
+			 * @since 4.3.0
+			 *
+			 * @param int    $attachment_id The ID of the cropped image.
+			 * @param string $context       The feature requesting the cropped image.
+			 */
+			$attachment_id = apply_filters( 'wp_ajax_cropped_attachment_id', 0, $context );
+	}
+
+	if ( ! $attachment_id ) {
+		wp_send_json_error();
+	}
+
+	wp_send_json_success( wp_prepare_attachment_for_js( $attachment_id ) );
+}
\ No newline at end of file
Index: src/wp-admin/js/customize-controls.js
===================================================================
--- src/wp-admin/js/customize-controls.js	(revision 33142)
+++ src/wp-admin/js/customize-controls.js	(working copy)
@@ -1836,6 +1836,229 @@
 	});
 
 	/**
+	 * A control for selecting and cropping an image.
+	 *
+	 * @class
+	 * @augments wp.customize.MediaControl
+	 * @augments wp.customize.Control
+	 * @augments wp.customize.Class
+	 */
+	api.CroppedImageControl = api.MediaControl.extend({
+		/**
+		 * Open the media modal to the library state.
+		 */
+		openFrame: function( event ) {
+			if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
+				return;
+			}
+
+			if ( this.frame ) {
+				this.frame.setState( 'library' );
+			}
+
+			api.MediaControl.prototype.openFrame.call( this, event );
+		},
+
+		/**
+		 * Create a media modal select frame, and store it so the instance can be reused when needed.
+		 */
+		initFrame: function() {
+			var l10n = _wpMediaViewsL10n;
+
+			this.frame = wp.media({
+				button: {
+					text: l10n.selectAndCrop,
+					close: false
+				},
+				states: [
+					new wp.media.controller.Library({
+						title: this.params.button_labels.frame_title,
+						library: wp.media.query({ type: 'image' }),
+						multiple: false,
+						date: false,
+						priority: 20,
+						suggestedWidth: this.params.width,
+						suggestedHeight: this.params.height
+					}),
+					new wp.media.controller.customizeImageCropper({
+						imgSelectOptions: this.calculateImageSelectOptions,
+						control: this
+					})
+				]
+			});
+
+			this.frame.on( 'select', this.onSelect, this );
+			this.frame.on( 'cropped', this.onCropped, this );
+			this.frame.on( 'skippedcrop', this.onSkippedCrop, this );
+		},
+
+		/**
+		 * After an image is selected in the media modal,
+		 * switch to the cropper state.
+		 */
+		onSelect: function() {
+			this.frame.setState( 'cropper' );
+		},
+
+		/**
+		 * After the image has been cropped, apply the cropped image data to the setting.
+		 *
+		 * @param {object} croppedImage Cropped attachment data.
+		 */
+		onCropped: function( croppedImage ) {
+			this.setImageFromAttachment( croppedImage );
+		},
+
+		/**
+		 * Returns a set of options, computed from the attached image data and
+		 * control-specific data, to be fed to the imgAreaSelect plugin in
+		 * wp.media.view.Cropper.
+		 *
+		 * @param {wp.media.model.Attachment} attachment
+		 * @param {wp.media.controller.Cropper} controller
+		 * @returns {Object} Options
+		 */
+		calculateImageSelectOptions: function( attachment, controller ) {
+			var control = controller.get( 'control' ),
+				xInit = parseInt( control.params.width, 10 ),
+				yInit = parseInt( control.params.height, 10 ),
+				flexWidth  = !! parseInt( control.params.flex_width, 10 ),
+				flexHeight = !! parseInt( control.params.flex_height, 10 ),
+				realWidth  = attachment.get( 'width' ),
+				realHeight = attachment.get( 'height' ),
+				ratio = xInit / yInit,
+				xImg  = realWidth,
+				yImg  = realHeight,
+				imgSelectOptions;
+
+			controller.set( 'canSkipCrop', ! control.mustBeCropped( flexWidth, flexHeight, xInit, yInit, realWidth, realHeight ) );
+
+			if ( xImg / yImg > ratio ) {
+				yInit = yImg;
+				xInit = yInit * ratio;
+			} else {
+				xInit = xImg;
+				yInit = xInit / ratio;
+			}
+
+			imgSelectOptions = {
+				handles: true,
+				keys: true,
+				instance: true,
+				persistent: true,
+				imageWidth: realWidth,
+				imageHeight: realHeight,
+				x1: 0,
+				y1: 0,
+				x2: xInit,
+				y2: yInit
+			};
+
+			if ( flexHeight === false && flexWidth === false ) {
+				imgSelectOptions.aspectRatio = xInit + ':' + yInit;
+			}
+			if ( flexHeight === false ) {
+				imgSelectOptions.maxHeight = yInit;
+			}
+			if ( flexWidth === false ) {
+				imgSelectOptions.maxWidth = xInit;
+			}
+
+			return imgSelectOptions;
+		},
+
+		/**
+		 * Return whether the image must be cropped, based on required dimensions.
+		 */
+		mustBeCropped: function( flexW, flexH, dstW, dstH, imgW, imgH ) {
+			if ( flexW === true && flexH === true ) {
+				return false;
+			}
+
+			if ( flexW === true && dstH === imgH ) {
+				return false;
+			}
+
+			if ( flexH === true && dstW === imgW ) {
+				return false;
+			}
+
+			if ( dstW === imgW && dstH === imgH ) {
+				return false;
+			}
+
+			if ( imgW <= dstW ) {
+				return false;
+			}
+
+			return true;
+		},
+
+		/**
+		 * If cropping was skipped, apply the image data directly to the setting.
+		 */
+		onSkippedCrop: function() {
+			var attachment = this.frame.state().get( 'selection' ).first().toJSON();
+			this.setImageFromAttachment( attachment );
+		},
+
+		/**
+		 * Updates the setting and re-renders the control UI.
+		 *
+		 * @param {object} attachment
+		 */
+		setImageFromAttachment: function( attachment ) {
+			this.params.attachment = attachment;
+
+			// Set the Customizer setting; the callback takes care of rendering.
+			this.setting( attachment.id );
+		}
+	});
+
+	/**
+	 * A control for selecting and cropping Site Icons.
+	 *
+	 * @class
+	 * @augments wp.customize.CroppedImageControl
+	 * @augments wp.customize.MediaControl
+	 * @augments wp.customize.Control
+	 * @augments wp.customize.Class
+	 */
+	api.SiteIconControl = api.CroppedImageControl.extend({
+		/**
+		 * Updates the setting and re-renders the control UI.
+		 *
+		 * @param {object} attachment
+		 */
+		setImageFromAttachment: function( attachment ) {
+			this.params.attachment = attachment;
+
+			// Set the Customizer setting; the callback takes care of rendering.
+			this.setting( attachment.id );
+
+			// Update the icon in-browser.
+			$( 'link[rel="icon"]' ).attr( 'href', attachment.sizes.thumbnail.url );
+		},
+
+		/**
+		 * Called when the "Remove" link is clicked. Empties the setting.
+		 *
+		 * @param {object} event jQuery Event object
+		 */
+		removeFile: function( event ) {
+			if ( api.utils.isKeydownButNotEnterEvent( event ) ) {
+				return;
+			}
+			event.preventDefault();
+
+			this.params.attachment = {};
+			this.setting( '' );
+			this.renderContent(); // Not bound to setting change when emptying.
+			$( 'link[rel="icon"]' ).attr( 'href', '' );
+		}
+	});
+
+	/**
 	 * @class
 	 * @augments wp.customize.Control
 	 * @augments wp.customize.Class
@@ -2695,13 +2918,15 @@
 	});
 
 	api.controlConstructor = {
-		color:      api.ColorControl,
-		media:      api.MediaControl,
-		upload:     api.UploadControl,
-		image:      api.ImageControl,
-		header:     api.HeaderControl,
-		background: api.BackgroundControl,
-		theme:      api.ThemeControl
+		color:         api.ColorControl,
+		media:         api.MediaControl,
+		upload:        api.UploadControl,
+		image:         api.ImageControl,
+		cropped_image: api.CroppedImageControl,
+		site_icon:     api.SiteIconControl,
+		header:        api.HeaderControl,
+		background:    api.BackgroundControl,
+		theme:         api.ThemeControl
 	};
 	api.panelConstructor = {};
 	api.sectionConstructor = {
Index: src/wp-includes/class-wp-customize-control.php
===================================================================
--- src/wp-includes/class-wp-customize-control.php	(revision 33142)
+++ src/wp-includes/class-wp-customize-control.php	(working copy)
@@ -1001,6 +1001,108 @@
 }
 
 /**
+ * Customize Cropped Image Control class.
+ *
+ * @since 4.3.0
+ *
+ * @see WP_Customize_Image_Control
+ */
+class WP_Customize_Cropped_Image_Control extends WP_Customize_Image_Control {
+
+	/**
+	 * @access public
+	 * @var string
+	 */
+	public $type = 'cropped_image';
+
+	/**
+	 * @access public
+	 * @var int
+	 */
+	public $width = 150;
+
+	/**
+	 * @access public
+	 * @var int
+	 */
+	public $height = 150;
+
+	/**
+	 * @access public
+	 * @var bool
+	 */
+	public $flex_width = false;
+
+	/**
+	 * @access public
+	 * @var bool
+	 */
+	public $flex_height = false;
+
+	/**
+	 * Enqueue control related scripts/styles.
+	 *
+	 * @access public
+	 */
+	public function enqueue() {
+		wp_enqueue_script( 'customize-views' );
+
+		parent::enqueue();
+	}
+
+	/**
+	 * Refresh the parameters passed to the JavaScript via JSON.
+	 *
+	 * @since 4.3.0
+	 * @access public
+	 * @uses WP_Customize_Image_Control::to_json()
+	 *
+	 * @see WP_Customize_Control::to_json()
+	 */
+	public function to_json() {
+		parent::to_json();
+
+		$this->json['width']       = absint( $this->width );
+		$this->json['height']      = absint( $this->height );
+		$this->json['flex_width']  = absint( $this->flex_width );
+		$this->json['flex_height'] = absint( $this->flex_height );
+	}
+
+}
+
+/**
+ * Customize Site Icon control class.
+ *
+ * Used only for custom functionality in JavaScript.
+ *
+ * @since 4.3.0
+ *
+ * @see WP_Customize_Cropped_Image_Control
+ */
+class WP_Customize_Site_Icon_Control extends WP_Customize_Cropped_Image_Control {
+
+	/**
+	 * @access public
+	 * @var string
+	 */
+	public $type = 'site_icon';
+
+	/**
+	 * Constructor.
+	 *
+	 * @since 4.3.0
+	 *
+	 * @param WP_Customize_Manager $manager
+	 * @param string               $id
+	 * @param array                $args
+	 */
+	public function __construct( $manager, $id, $args = array() ) {
+		parent::__construct( $manager, $id, $args );
+		add_action( 'customize_controls_print_styles', 'wp_site_icon', 99 );
+	}
+}
+
+/**
  * Customize Header Image Control class.
  *
  * @since 3.4.0
Index: src/wp-includes/class-wp-customize-manager.php
===================================================================
--- src/wp-includes/class-wp-customize-manager.php	(revision 33142)
+++ src/wp-includes/class-wp-customize-manager.php	(working copy)
@@ -1278,6 +1278,8 @@
 		$this->register_control_type( 'WP_Customize_Upload_Control' );
 		$this->register_control_type( 'WP_Customize_Image_Control' );
 		$this->register_control_type( 'WP_Customize_Background_Image_Control' );
+		$this->register_control_type( 'WP_Customize_Cropped_Image_Control' );
+		$this->register_control_type( 'WP_Customize_Site_Icon_Control' );
 		$this->register_control_type( 'WP_Customize_Theme_Control' );
 
 		/* Themes */
@@ -1324,10 +1326,10 @@
 			) ) );
 		}
 
-		/* Site Title & Tagline */
+		/* Site Identity */
 
 		$this->add_section( 'title_tagline', array(
-			'title'    => __( 'Site Title & Tagline' ),
+			'title'    => __( 'Site Identity' ),
 			'priority' => 20,
 		) );
 
@@ -1353,6 +1355,23 @@
 			'section'    => 'title_tagline',
 		) );
 
+		$icon = wp_get_attachment_image_src( absint( get_option( 'site_icon' ) ), 'medium' );
+		$this->add_setting( 'site_icon', array(
+			'default'    => $icon[0] ? $icon[0] : '',
+			'type'       => 'option',
+			'capability' => 'manage_options',
+			'transport'  => 'postMessage', // Previewed with JS in the Customizer controls window.
+		) );
+
+		$this->add_control( new WP_Customize_Site_Icon_Control( $this, 'site_icon', array(
+			'label'       => __( 'Site Icon' ),
+			'description' => __( 'The site icon is used as the browser and device icon for your site. Your theme may also display the site icon. Icons must be square, and at least 512px wide and tall.' ),
+			'section'     => 'title_tagline',
+			'priority'    => 60,
+			'height'      => 512,
+			'width'       => 512,
+		) ) );
+
 		/* Colors */
 
 		$this->add_section( 'colors', array(
@@ -1375,6 +1394,7 @@
 			'label'    => __( 'Display Header Text' ),
 			'section'  => 'title_tagline',
 			'type'     => 'checkbox',
+			'priority' => 40,
 		) );
 
 		$this->add_control( new WP_Customize_Color_Control( $this, 'header_textcolor', array(
Index: src/wp-includes/js/customize-views.js
===================================================================
--- src/wp-includes/js/customize-views.js	(revision 33142)
+++ src/wp-includes/js/customize-views.js	(working copy)
@@ -3,6 +3,26 @@
 	if ( ! wp || ! wp.customize ) { return; }
 	var api = wp.customize;
 
+	/**
+	 * Use a custom ajax action for cropped image controls.
+	 */
+	wp.media.controller.customizeImageCropper = wp.media.controller.Cropper.extend( {
+		doCrop: function( attachment ) {
+			var cropDetails = attachment.get( 'cropDetails' ),
+				control = this.get( 'control' );
+
+			cropDetails.dst_width  = control.params.width;
+			cropDetails.dst_height = control.params.height;
+
+			return wp.ajax.post( 'crop-image', {
+				wp_customize: 'on',
+				nonce: attachment.get( 'nonces' ).edit,
+				id: attachment.get( 'id' ),
+				context: control.params.type,
+				cropDetails: cropDetails
+			} );
+		}
+	} );
 
 	/**
 	 * wp.customize.HeaderTool.CurrentView
