Index: src/wp-includes/class-wp-customize-widgets.php
===================================================================
--- src/wp-includes/class-wp-customize-widgets.php	(revision 27568)
+++ src/wp-includes/class-wp-customize-widgets.php	(working copy)
@@ -932,108 +932,107 @@
 	 * Find and invoke the widget update and control callbacks. Requires that
 	 * $_POST be populated with the instance data.
 	 *
-	 * @throws Widget_Customizer_Exception
-	 * @throws Exception
 	 *
-	 * @param string $widget_id
-	 * @return array
+	 * @param string      $widget_id
+	 * @return array|bool False on failure, array on success.
 	 */
 	static function call_widget_update( $widget_id ) {
 		global $wp_registered_widget_updates, $wp_registered_widget_controls;
 
 		$options_transaction = new Options_Transaction();
 
-		try {
-			$options_transaction->start();
-			$parsed_id   = self::parse_widget_id( $widget_id );
-			$option_name = 'widget_' . $parsed_id['id_base'];
+		$options_transaction->start();
+		$parsed_id   = self::parse_widget_id( $widget_id );
+		$option_name = 'widget_' . $parsed_id['id_base'];
 
-			/**
-			 * If a previously-sanitized instance is provided, populate the input vars
-			 * with its values so that the widget update callback will read this instance
-			 */
-			$added_input_vars = array();
-			if ( ! empty( $_POST['sanitized_widget_setting'] ) ) {
-				$sanitized_widget_setting = json_decode( self::get_post_value( 'sanitized_widget_setting' ), true );
-				if ( empty( $sanitized_widget_setting ) ) {
-					throw new Widget_Customizer_Exception( 'Malformed sanitized_widget_setting' );
-				}
-				$instance = self::sanitize_widget_instance( $sanitized_widget_setting );
-				if ( is_null( $instance ) ) {
-					throw new Widget_Customizer_Exception( 'Unsanitary sanitized_widget_setting' );
-				}
-				if ( ! is_null( $parsed_id['number'] ) ) {
-					$value = array();
-					$value[$parsed_id['number']] = $instance;
-					$key = 'widget-' . $parsed_id['id_base'];
+		/**
+		 * If a previously-sanitized instance is provided, populate the input vars
+		 * with its values so that the widget update callback will read this instance
+		 */
+		$added_input_vars = array();
+		if ( ! empty( $_POST['sanitized_widget_setting'] ) ) {
+			$sanitized_widget_setting = json_decode( self::get_post_value( 'sanitized_widget_setting' ), true );
+			if ( empty( $sanitized_widget_setting ) ) {
+				$options_transaction->rollback();
+				return false;
+			}
+
+			$instance = self::sanitize_widget_instance( $sanitized_widget_setting );
+			if ( is_null( $instance ) ) {
+				$options_transaction->rollback();
+				return false;
+			}
+
+			if ( ! is_null( $parsed_id['number'] ) ) {
+				$value = array();
+				$value[$parsed_id['number']] = $instance;
+				$key = 'widget-' . $parsed_id['id_base'];
+				$_REQUEST[$key] = $_POST[$key] = wp_slash( $value );
+				$added_input_vars[] = $key;
+			} else {
+				foreach ( $instance as $key => $value ) {
 					$_REQUEST[$key] = $_POST[$key] = wp_slash( $value );
 					$added_input_vars[] = $key;
-				} else {
-					foreach ( $instance as $key => $value ) {
-						$_REQUEST[$key] = $_POST[$key] = wp_slash( $value );
-						$added_input_vars[] = $key;
-					}
 				}
 			}
+		}
 
-			/**
-			 * Invoke the widget update callback
-			 */
-			foreach ( (array) $wp_registered_widget_updates as $name => $control ) {
-				if ( $name === $parsed_id['id_base'] && is_callable( $control['callback'] ) ) {
-					ob_start();
-					call_user_func_array( $control['callback'], $control['params'] );
-					ob_end_clean();
-					break;
-				}
+		/**
+		 * Invoke the widget update callback
+		 */
+		foreach ( (array) $wp_registered_widget_updates as $name => $control ) {
+			if ( $name === $parsed_id['id_base'] && is_callable( $control['callback'] ) ) {
+				ob_start();
+				call_user_func_array( $control['callback'], $control['params'] );
+				ob_end_clean();
+				break;
 			}
+		}
 
-			// Clean up any input vars that were manually added
-			foreach ( $added_input_vars as $key ) {
-				unset( $_POST[$key] );
-				unset( $_REQUEST[$key] );
-			}
+		// Clean up any input vars that were manually added
+		foreach ( $added_input_vars as $key ) {
+			unset( $_POST[$key] );
+			unset( $_REQUEST[$key] );
+		}
 
-			/**
-			 * Make sure the expected option was updated
-			 */
-			if ( 0 !== $options_transaction->count() ) {
-				if ( count( $options_transaction->options ) > 1 ) {
-					throw new Widget_Customizer_Exception( sprintf( 'Widget %1$s unexpectedly updated more than one option.', $widget_id ) );
-				}
-				$updated_option_name = key( $options_transaction->options );
-				if ( $updated_option_name !== $option_name ) {
-					throw new Widget_Customizer_Exception( sprintf( 'Widget %1$s updated option "%2$s", but expected "%3$s".', $widget_id, $updated_option_name, $option_name ) );
-				}
+		/**
+		 * Make sure the expected option was updated
+		 */
+		if ( 0 !== $options_transaction->count() ) {
+			if ( count( $options_transaction->options ) > 1 ) {
+				$options_transaction->rollback();
+				return false;
 			}
 
-			/**
-			 * Obtain the widget control with the updated instance in place
-			 */
-			ob_start();
-			$form = $wp_registered_widget_controls[$widget_id];
-			if ( $form ) {
-				call_user_func_array( $form['callback'], $form['params'] );
+			$updated_option_name = key( $options_transaction->options );
+			if ( $updated_option_name !== $option_name ) {
+				$options_transaction->rollback();
+				return false;
 			}
-			$form = ob_get_clean();
+		}
 
-			/**
-			 * Obtain the widget instance
-			 */
-			$option = get_option( $option_name );
-			if ( null !== $parsed_id['number'] ) {
-				$instance = $option[$parsed_id['number']];
-			} else {
-				$instance = $option;
-			}
+		/**
+		 * Obtain the widget control with the updated instance in place
+		 */
+		ob_start();
+		$form = $wp_registered_widget_controls[$widget_id];
+		if ( $form ) {
+			call_user_func_array( $form['callback'], $form['params'] );
+		}
+		$form = ob_get_clean();
 
-			$options_transaction->rollback();
-			return compact( 'instance', 'form' );
+		/**
+		 * Obtain the widget instance
+		 */
+		$option = get_option( $option_name );
+		if ( null !== $parsed_id['number'] ) {
+			$instance = $option[$parsed_id['number']];
+		} else {
+			$instance = $option;
 		}
-		catch ( Exception $e ) {
-			$options_transaction->rollback();
-			throw $e;
-		}
+
+		$options_transaction->rollback();
+		return compact( 'instance', 'form' );
 	}
 
 	/**
@@ -1043,56 +1042,42 @@
 	 *
 	 * @see wp_ajax_save_widget
 	 * @todo Reuse wp_ajax_save_widget now that we have option transactions?
-	 * @action wp_ajax_update_widget
 	 */
 	static function wp_ajax_update_widget() {
-		$generic_error = __( 'An error has occurred. Please reload the page and try again.' );
+		if ( ! check_ajax_referer( self::UPDATE_WIDGET_AJAX_ACTION, self::UPDATE_WIDGET_NONCE_POST_KEY, false ) ) {
+			wp_send_json_error();
+		}
 
-		try {
-			if ( ! check_ajax_referer( self::UPDATE_WIDGET_AJAX_ACTION, self::UPDATE_WIDGET_NONCE_POST_KEY, false ) ) {
-				throw new Widget_Customizer_Exception( __( 'Nonce check failed. Reload and try again?' ) );
-			}
-			if ( ! current_user_can( 'edit_theme_options' ) ) {
-				throw new Widget_Customizer_Exception( __( 'Current user cannot!' ) );
-			}
-			if ( ! isset( $_POST['widget-id'] ) ) {
-				throw new Widget_Customizer_Exception( __( 'Incomplete request' ) );
-			}
+		if ( ! current_user_can( 'edit_theme_options' ) ) {
+			wp_send_json_error();
+		}
 
-			unset( $_POST[self::UPDATE_WIDGET_NONCE_POST_KEY], $_POST['action'] );
+		if ( ! isset( $_POST['widget-id'] ) ) {
+			wp_send_json_error();
+		}
 
-			do_action( 'load-widgets.php' );
-			do_action( 'widgets.php' );
-			do_action( 'sidebar_admin_setup' );
+		unset( $_POST[self::UPDATE_WIDGET_NONCE_POST_KEY], $_POST['action'] );
 
-			$widget_id = self::get_post_value( 'widget-id' );
-			$parsed_id = self::parse_widget_id( $widget_id );
-			$id_base   = $parsed_id['id_base'];
+		do_action( 'load-widgets.php' );
+		do_action( 'widgets.php' );
+		do_action( 'sidebar_admin_setup' );
 
-			if ( isset( $_POST['widget-' . $id_base] ) && is_array( $_POST['widget-' . $id_base] ) && preg_match( '/__i__|%i%/', key( $_POST['widget-' . $id_base] ) ) ) {
-				throw new Widget_Customizer_Exception( 'Cannot pass widget templates to create new instances; apply template vars in JS' );
-			}
+		$widget_id = self::get_post_value( 'widget-id' );
+		$parsed_id = self::parse_widget_id( $widget_id );
+		$id_base   = $parsed_id['id_base'];
 
-			$updated_widget = self::call_widget_update( $widget_id ); // => {instance,form}
-			$form = $updated_widget['form'];
-			$instance = self::sanitize_widget_js_instance( $updated_widget['instance'] );
+		if ( isset( $_POST['widget-' . $id_base] ) && is_array( $_POST['widget-' . $id_base] ) && preg_match( '/__i__|%i%/', key( $_POST['widget-' . $id_base] ) ) ) {
+			wp_send_json_error();
+		}
 
-			wp_send_json_success( compact( 'form', 'instance' ) );
-		}
-		catch( Exception $e ) {
-			if ( $e instanceof Widget_Customizer_Exception ) {
-				$message = $e->getMessage();
-			} else {
-				error_log( sprintf( '%s in %s: %s', get_class( $e ), __FUNCTION__, $e->getMessage() ) );
-				$message = $generic_error;
-			}
-			wp_send_json_error( compact( 'message' ) );
-		}
+		$updated_widget = self::call_widget_update( $widget_id ); // => {instance,form}
+		$form = $updated_widget['form'];
+		$instance = self::sanitize_widget_js_instance( $updated_widget['instance'] );
+
+		wp_send_json_success( compact( 'form', 'instance' ) );
 	}
 }
 
-class Widget_Customizer_Exception extends Exception {}
-
 class Options_Transaction {
 
 	/**
@@ -1224,9 +1209,6 @@
 			else if ( 'update' === $option_operation['operation'] ) {
 				update_option( $option_operation['option_name'], $option_operation['old_value'] );
 			}
-			else {
-				throw new Exception( 'Unexpected operation' );
-			}
 		}
 		$this->_is_current = false;
 	}
