diff --git a/src/wp-admin/customize.php b/src/wp-admin/customize.php
index b5524b0..87a0ec5 100644
--- a/src/wp-admin/customize.php
+++ b/src/wp-admin/customize.php
@@ -189,87 +189,7 @@ do_action( 'customize_controls_print_scripts' );
 	 */
 	do_action( 'customize_controls_print_footer_scripts' );
 
-	/*
-	 * If the frontend and the admin are served from the same domain, load the
-	 * preview over ssl if the customizer is being loaded over ssl. This avoids
-	 * insecure content warnings. This is not attempted if the admin and frontend
-	 * are on different domains to avoid the case where the frontend doesn't have
-	 * ssl certs. Domain mapping plugins can allow other urls in these conditions
-	 * using the customize_allowed_urls filter.
-	 */
-
-	$allowed_urls = array( home_url('/') );
-	$admin_origin = parse_url( admin_url() );
-	$home_origin  = parse_url( home_url() );
-	$cross_domain = ( strtolower( $admin_origin[ 'host' ] ) != strtolower( $home_origin[ 'host' ] ) );
-
-	if ( is_ssl() && ! $cross_domain )
-		$allowed_urls[] = home_url( '/', 'https' );
-
-	/**
-	 * Filter the list of URLs allowed to be clicked and followed in the Customizer preview.
-	 *
-	 * @since 3.4.0
-	 *
-	 * @param array $allowed_urls An array of allowed URLs.
-	 */
-	$allowed_urls = array_unique( apply_filters( 'customize_allowed_urls', $allowed_urls ) );
-
-	$fallback_url = add_query_arg( array(
-		'preview'        => 1,
-		'template'       => $wp_customize->get_template(),
-		'stylesheet'     => $wp_customize->get_stylesheet(),
-		'preview_iframe' => true,
-		'TB_iframe'      => 'true'
-	), home_url( '/' ) );
-
-	$login_url = add_query_arg( array(
-		'interim-login' => 1,
-		'customize-login' => 1
-	), wp_login_url() );
-
-	// Prepare customizer settings to pass to Javascript.
-	$settings = array(
-		'theme'    => array(
-			'stylesheet' => $wp_customize->get_stylesheet(),
-			'active'     => $wp_customize->is_theme_active(),
-		),
-		'url'      => array(
-			'preview'       => esc_url_raw( $url ? $url : home_url( '/' ) ),
-			'parent'        => esc_url_raw( admin_url() ),
-			'activated'     => esc_url_raw( admin_url( 'themes.php?activated=true&previewed' ) ),
-			'ajax'          => esc_url_raw( admin_url( 'admin-ajax.php', 'relative' ) ),
-			'allowed'       => array_map( 'esc_url_raw', $allowed_urls ),
-			'isCrossDomain' => $cross_domain,
-			'fallback'      => esc_url_raw( $fallback_url ),
-			'home'          => esc_url_raw( home_url( '/' ) ),
-			'login'         => esc_url_raw( $login_url ),
-		),
-		'browser'  => array(
-			'mobile' => wp_is_mobile(),
-			'ios'    => $is_ios,
-		),
-		'settings' => array(),
-		'controls' => array(),
-		'nonce'    => array(
-			'save'    => wp_create_nonce( 'save-customize_' . $wp_customize->get_stylesheet() ),
-			'preview' => wp_create_nonce( 'preview-customize_' . $wp_customize->get_stylesheet() )
-		),
-	);
-
-	// Prepare Customize Setting objects to pass to Javascript.
-	foreach ( $wp_customize->settings() as $id => $setting ) {
-		$settings['settings'][ $id ] = array(
-			'value'     => $setting->js_value(),
-			'transport' => $setting->transport,
-		);
-	}
-
-	// Prepare Customize Control objects to pass to Javascript.
-	foreach ( $wp_customize->controls() as $id => $control ) {
-		$control->to_json();
-		$settings['controls'][ $id ] = $control->json;
-	}
+	$settings = $wp_customize->get_settings_for_js();
 
 	?>
 	<script type="text/javascript">
diff --git a/src/wp-includes/class-wp-customize-manager.php b/src/wp-includes/class-wp-customize-manager.php
index 5067126..69a2aca 100644
--- a/src/wp-includes/class-wp-customize-manager.php
+++ b/src/wp-includes/class-wp-customize-manager.php
@@ -1182,11 +1182,113 @@ final class WP_Customize_Manager {
 		if ( 'blank' === $color )
 			return 'blank';
 
-		$color = sanitize_hex_color_no_hash( $color );
-		if ( empty( $color ) )
-			$color = get_theme_support( 'custom-header', 'default-text-color' );
+			$color = sanitize_hex_color_no_hash( $color );
+			if ( empty( $color ) )
+				$color = get_theme_support( 'custom-header', 'default-text-color' );
 
-		return $color;
+			return $color;
+		}
+
+	/**
+	 * Get settings array for Javascript.
+	 *
+	 * @since 4.0.0
+	 *
+	 * @return array
+	 */
+	public function get_settings_for_js() {
+		global $is_ios, $url;
+		if ( empty( $is_ios ) ) {
+			$is_ios = wp_is_mobile() && preg_match( '/iPad|iPod|iPhone/', $_SERVER['HTTP_USER_AGENT'] );
+		}
+		if ( empty( $url ) ) {
+			wp_reset_vars( array( 'url', 'return' ) );
+			$url = wp_unslash( $url );
+			$url = wp_validate_redirect( $url, home_url( '/' ) );
+		}
+
+		/*
+		 * If the frontend and the admin are served from the same domain, load the
+		 * preview over ssl if the customizer is being loaded over ssl. This avoids
+		 * insecure content warnings. This is not attempted if the admin and frontend
+		 * are on different domains to avoid the case where the frontend doesn't have
+		 * ssl certs. Domain mapping plugins can allow other urls in these conditions
+		 * using the customize_allowed_urls filter.
+		 */
+
+		$allowed_urls = array( home_url('/') );
+		$admin_origin = parse_url( admin_url() );
+		$home_origin  = parse_url( home_url() );
+		$cross_domain = ( strtolower( $admin_origin[ 'host' ] ) != strtolower( $home_origin[ 'host' ] ) );
+
+		if ( is_ssl() && ! $cross_domain )
+			$allowed_urls[] = home_url( '/', 'https' );
+
+		/**
+		 * Filter the list of URLs allowed to be clicked and followed in the Customizer preview.
+		 *
+		 * @since 3.4.0
+		 *
+		 * @param array $allowed_urls An array of allowed URLs.
+		 */
+		$allowed_urls = array_unique( apply_filters( 'customize_allowed_urls', $allowed_urls ) );
+
+		$fallback_url = add_query_arg( array(
+			'preview'        => 1,
+			'template'       => $this->get_template(),
+			'stylesheet'     => $this->get_stylesheet(),
+			'preview_iframe' => true,
+			'TB_iframe'      => 'true'
+		), home_url( '/' ) );
+
+		$login_url = add_query_arg( array(
+			'interim-login' => 1,
+			'customize-login' => 1
+		), wp_login_url() );
+
+		// Prepare customizer settings to pass to Javascript.
+		$settings = array(
+			'theme'    => array(
+				'stylesheet' => $this->get_stylesheet(),
+				'active'     => $this->is_theme_active(),
+			),
+			'url'      => array(
+				'preview'       => esc_url_raw( $url ? $url : home_url( '/' ) ),
+				'parent'        => esc_url_raw( admin_url() ),
+				'activated'     => esc_url_raw( admin_url( 'themes.php?activated=true&previewed' ) ),
+				'ajax'          => esc_url_raw( admin_url( 'admin-ajax.php', 'relative' ) ),
+				'allowed'       => array_map( 'esc_url_raw', $allowed_urls ),
+				'isCrossDomain' => $cross_domain,
+				'fallback'      => esc_url_raw( $fallback_url ),
+				'home'          => esc_url_raw( home_url( '/' ) ),
+				'login'         => esc_url_raw( $login_url ),
+			),
+			'browser'  => array(
+				'mobile' => wp_is_mobile(),
+				'ios'    => $is_ios,
+			),
+			'settings' => array(),
+			'controls' => array(),
+			'nonce'    => array(
+				'save'    => wp_create_nonce( 'save-customize_' . $this->get_stylesheet() ),
+				'preview' => wp_create_nonce( 'preview-customize_' . $this->get_stylesheet() )
+			),
+		);
+
+		// Prepare Customize Setting objects to pass to Javascript.
+		foreach ( $this->settings() as $id => $setting ) {
+			$settings['settings'][ $id ] = array(
+				'value'     => $setting->js_value(),
+				'transport' => $setting->transport,
+			);
+		}
+
+		// Prepare Customize Control objects to pass to Javascript.
+		foreach ( $this->controls() as $id => $control ) {
+			$control->to_json();
+			$settings['controls'][ $id ] = $control->json;
+		}
+		return $settings;
 	}
 }
 
