Index: src/wp-includes/formatting.php
===================================================================
--- src/wp-includes/formatting.php	(revision 27515)
+++ src/wp-includes/formatting.php	(working copy)
@@ -73,27 +73,44 @@
 		$static_characters = array_merge( array( '---', ' -- ', '--', ' - ', 'xn&#8211;', '...', '``', '\'\'', ' (tm)' ), $cockney );
 		$static_replacements = array_merge( array( $em_dash, ' ' . $em_dash . ' ', $en_dash, ' ' . $en_dash . ' ', 'xn--', '&#8230;', $opening_quote, $closing_quote, ' &#8482;' ), $cockneyreplace );
 
+		// Pattern-based replacements of characters.
 		$dynamic = array();
-		if ( "'" != $apos ) {
-			$dynamic[ '/\'(\d\d(?:&#8217;|\')?s)/' ] = $apos . '$1'; // '99's
-			$dynamic[ '/\'(\d)/'                   ] = $apos . '$1'; // '99
-		}
+		$spaces = wp_spaces_regexp();
+
+		// '99s (apostrophe)
+		if ( "'" != $apos )
+			$dynamic[ '/\'(?=\d)/' ] = $apos;
+
+		// Single quote at start, or preceded by (, {, <, [, ", or spaces.
 		if ( "'" != $opening_single_quote )
-			$dynamic[ '/(\s|\A|[([{<]|")\'/'       ] = '$1' . $opening_single_quote; // opening single quote, even after (, {, <, [
+			$dynamic[ '/(?<=\A|[([{<"]|' . $spaces . ')\'/' ] = $opening_single_quote;
+
+		// 9" (double prime)
 		if ( '"' != $double_prime )
-			$dynamic[ '/(\d)"/'                    ] = '$1' . $double_prime; // 9" (double prime)
+			$dynamic[ '/(?<=\d)"/' ] = $double_prime;
+
+		// 9' (prime)
 		if ( "'" != $prime )
-			$dynamic[ '/(\d)\'/'                   ] = '$1' . $prime; // 9' (prime)
+			$dynamic[ '/(?<=\d)\'/' ] = $prime;
+
+		// Apostrophe in a word.  No spaces or double primes.
 		if ( "'" != $apos )
-			$dynamic[ '/(\S)\'([^\'\s])/'          ] = '$1' . $apos . '$2'; // apostrophe in a word
+			$dynamic[ '/(?<!' . $spaces . ')\'(?!\'|' . $spaces . ')/' ] = $apos;
+
+		// Double quote at start, or preceded by (, {, <, [, or spaces, and not followed by spaces.
 		if ( '"' != $opening_quote )
-			$dynamic[ '/(\s|\A|[([{<])"(?!\s)/'    ] = '$1' . $opening_quote . '$2'; // opening double quote, even after (, {, <, [
+			$dynamic[ '/(?<=\A|[([{<]|' . $spaces . ')"(?!' . $spaces . ')/' ] = $opening_quote;
+
+		// Any remaining double quotes.
 		if ( '"' != $closing_quote )
-			$dynamic[ '/"(\s|\S|\Z)/'              ] = $closing_quote . '$1'; // closing double quote
+			$dynamic[ '/"/' ] = $closing_quote;
+
+		// Single quotes followed by spaces or a period.
 		if ( "'" != $closing_single_quote )
-			$dynamic[ '/\'([\s.]|\Z)/'             ] = $closing_single_quote . '$1'; // closing single quote
+			$dynamic[ '/\'(?=\Z|\.|' . $spaces . ')/' ] = $closing_single_quote; 
 
-		$dynamic[ '/\b(\d+)x(\d+)\b/'              ] = '$1&#215;$2'; // 9x9 (times)
+		// 9x9 (times)
+		$dynamic[ '/\b(\d+)x(\d+)\b/' ] = '$1&#215;$2';
 
 		$dynamic_characters = array_keys( $dynamic );
 		$dynamic_replacements = array_values( $dynamic );
@@ -318,11 +335,12 @@
 	}
 
 	$tagregexp = join( '|', array_map( 'preg_quote', array_keys( $shortcode_tags ) ) );
+	$spaces = wp_spaces_regexp();
 
 	$pattern =
 		  '/'
 		. '<p>'                              // Opening paragraph
-		. '\\s*+'                            // Optional leading whitespace
+		. '(?:' . $spaces . ')*+'            // Optional leading whitespace
 		. '('                                // 1: The shortcode
 		.     '\\['                          // Opening bracket
 		.     "($tagregexp)"                 // 2: Shortcode name
@@ -347,7 +365,7 @@
 		.         ')?'
 		.     ')'
 		. ')'
-		. '\\s*+'                            // optional trailing whitespace
+		. '(?:' . $spaces . ')*+'            // optional trailing whitespace
 		. '<\\/p>'                           // closing paragraph
 		. '/s';
 
@@ -1865,7 +1883,7 @@
 	 */
 	$src_url = apply_filters( 'smilies_src', includes_url( "images/smilies/$img" ), $img, site_url() );
 
-	return sprintf( ' <img src="%s" alt="%s" class="wp-smiley" /> ', esc_url( $src_url ), esc_attr( $smiley ) );
+	return sprintf( '<img src="%s" alt="%s" class="wp-smiley" />', esc_url( $src_url ), esc_attr( $smiley ) );
 }
 
 /**
@@ -3781,3 +3799,24 @@
 
 	return false;
 }
+
+/**
+ * Returns the regexp for common whitespace characters.
+ *
+ * By default, spaces include new lines, tabs, nbsp entities, and the UTF-8 nbsp.
+ * This is designed to replace the PCRE \s sequence.  In ticket #22692, that
+ * sequence was found to be unreliable due to random inclusion of the A0 byte.
+ *
+ * @since 3.9.0
+ *
+ * @return string The spaces regexp.
+ */
+function wp_spaces_regexp() {
+	static $spaces;
+
+	if (empty($spaces)) {
+		$spaces = apply_filters( 'wp_spaces_regexp', '[\r\n\t ]|\xC2\xA0|&nbsp;' );
+	}
+
+	return $spaces;
+}
Index: src/wp-includes/functions.php
===================================================================
--- src/wp-includes/functions.php	(revision 27515)
+++ src/wp-includes/functions.php	(working copy)
@@ -2560,8 +2560,11 @@
 	 */
 	krsort($wpsmiliestrans);
 
-	$wp_smiliessearch = '/((?:\s|^)';
+	$spaces = wp_spaces_regexp();
 
+	// Begin first "subpattern"
+	$wp_smiliessearch = '/(?<=' . $spaces . '|^)';
+
 	$subchar = '';
 	foreach ( (array) $wpsmiliestrans as $smiley => $img ) {
 		$firstchar = substr($smiley, 0, 1);
@@ -2570,7 +2573,8 @@
 		// new subpattern?
 		if ($firstchar != $subchar) {
 			if ($subchar != '') {
-				$wp_smiliessearch .= ')(?=\s|$))|((?:\s|^)'; ;
+				$wp_smiliessearch .= ')(?=' . $spaces . '|$)';  // End previous "subpattern"
+				$wp_smiliessearch .= '|(?<=' . $spaces . '|^)'; // Begin another "subpattern"
 			}
 			$subchar = $firstchar;
 			$wp_smiliessearch .= preg_quote($firstchar, '/') . '(?:';
@@ -2580,7 +2584,7 @@
 		$wp_smiliessearch .= preg_quote($rest, '/');
 	}
 
-	$wp_smiliessearch .= ')(?=\s|$))/m';
+	$wp_smiliessearch .= ')(?=' . $spaces . '|$)/m';
 
 }
 
Index: tests/phpunit/tests/formatting/Smilies.php
===================================================================
--- tests/phpunit/tests/formatting/Smilies.php	(revision 27515)
+++ tests/phpunit/tests/formatting/Smilies.php	(working copy)
@@ -275,4 +275,48 @@
 
 		$wpsmiliestrans = $orig_trans; // reset original translations array
 	}
-}
\ No newline at end of file
+
+	/**
+	 * Check that $wp_smiliessearch pattern will match smilies
+	 * between spaces, but never capture those spaces.
+	 *
+	 * Further check that spaces aren't randomly deleted
+	 * or added when replacing the text with an image.
+	 *
+	 * @ticket 22692
+	 */
+	function test_spaces_around_smilies() {
+		$nbsp = "\xC2\xA0";
+
+		$input  = array();
+		$output = array();
+
+		$input[]  = 'My test :) smile';
+		$output[] = array('test <img ', 'alt=":)"', ' /> smile');
+
+		$input[]  = 'My test ;) smile';
+		$output[] = array('test <img ', 'alt=";)"', ' /> smile');
+
+		$input[]  = 'My test &nbsp;:)&nbsp;smile';
+		$output[] = array('test &nbsp;<img ', 'alt=":)"', ' />&nbsp;smile');
+
+		$input[]  = 'My test &nbsp;;)&nbsp;smile';
+		$output[] = array('test &nbsp;<img ', 'alt=";)"', ' />&nbsp;smile');
+
+		$input[]  = "My test {$nbsp}:){$nbsp}smile";
+		$output[] = array("test {$nbsp}<img ", 'alt=":)"', " />{$nbsp}smile");
+
+		$input[]  = "My test {$nbsp};){$nbsp}smile";
+		$output[] = array("test {$nbsp}<img ", 'alt=";)"', " />{$nbsp}smile");
+
+		foreach($input as $key => $in) {
+			$result = convert_smilies( $in );
+			foreach($output[$key] as $k => $out) {
+
+				// Each output element must appear in the results.
+				$this->assertContains( $out[$k], $result );
+
+			}
+		}
+	}
+}
Index: tests/phpunit/tests/formatting/WPTexturize.php
===================================================================
--- tests/phpunit/tests/formatting/WPTexturize.php	(revision 27515)
+++ tests/phpunit/tests/formatting/WPTexturize.php	(working copy)
@@ -194,4 +194,55 @@
 		$this->assertEquals( ' &#8212;&nbsp;', wptexturize( ' --&nbsp;' ) );
 		$this->assertEquals( '&nbsp;&#8212; ', wptexturize( '&nbsp;-- ') );
 	}
+
+	/**
+	 * @ticket 22692
+	 */
+	function test_spaces_around_quotes() {
+		$nbsp = "\xC2\xA0";
+		$pi   = "\xCE\xA0";
+
+		// This should never happen, even if the desired output changes some day.
+
+		$problem_input  = "$nbsp\"A";
+		$problem_output = "$nbsp&#8221;A";
+
+		$this->assertNotEquals( $problem_output, wptexturize( $problem_input ) );
+
+		// These are desirable outputs for the current design.
+
+		$input  = array();
+		$output = array();
+
+		$input[]  = "stop. $nbsp\"A quote after 2 spaces.\"";
+		$output[] = "stop. $nbsp&#8220;A quote after 2 spaces.&#8221;";
+
+		$input[]  = "stop.$nbsp$nbsp\"A quote after 2 spaces.\"";
+		$output[] = "stop.$nbsp$nbsp&#8220;A quote after 2 spaces.&#8221;";
+
+		$input[]  = "stop. $nbsp'A quote after 2 spaces.'";
+		$output[] = "stop. $nbsp&#8216;A quote after 2 spaces.&#8217;";
+
+		$input[]  = "stop.$nbsp$nbsp'A quote after 2 spaces.'";
+		$output[] = "stop.$nbsp$nbsp&#8216;A quote after 2 spaces.&#8217;";
+
+		$input[]  = "stop. &nbsp;\"A quote after 2 spaces.\"";
+		$output[] = "stop. &nbsp;&#8220;A quote after 2 spaces.&#8221;";
+
+		$input[]  = "stop.&nbsp;&nbsp;\"A quote after 2 spaces.\"";
+		$output[] = "stop.&nbsp;&nbsp;&#8220;A quote after 2 spaces.&#8221;";
+
+		$input[]  = "stop. &nbsp;'A quote after 2 spaces.'";
+		$output[] = "stop. &nbsp;&#8216;A quote after 2 spaces.&#8217;";
+
+		$input[]  = "stop.&nbsp;&nbsp;'A quote after 2 spaces.'";
+		$output[] = "stop.&nbsp;&nbsp;&#8216;A quote after 2 spaces.&#8217;";
+
+		$input[]  = "Contraction: $pi's";
+		$output[] = "Contraction: $pi&#8217;s";
+
+		foreach($input as $key => $in) {
+			$this->assertEquals( $output[$key], wptexturize( $in ) );
+		}
+	}
 }
Index: tests/phpunit/tests/shortcode.php
===================================================================
--- tests/phpunit/tests/shortcode.php	(revision 27515)
+++ tests/phpunit/tests/shortcode.php	(working copy)
@@ -373,4 +373,25 @@
 		remove_filter( 'shortcode_atts_bartag', array( $this, '_filter_atts2' ), 10, 3 );
 	}
 
+	/**
+	 * Check that shortcode_unautop() will always recognize spaces around shortcodes.
+	 *
+	 * @ticket 22692
+	 */
+	function test_spaces_around_shortcodes() {
+		$nbsp = "\xC2\xA0";
+
+		$input  = array();
+
+		$input[] = "<p>[gallery ids=\"37,15,11\"]</p>";
+		$input[] = "<p> [gallery ids=\"37,15,11\"] </p>";
+		$input[] = "<p> $nbsp[gallery ids=\"37,15,11\"] $nbsp</p>";
+		$input[] = "<p> &nbsp;[gallery ids=\"37,15,11\"] &nbsp;</p>";
+
+		$output = "[gallery ids=\"37,15,11\"]";
+
+		foreach($input as $in) {
+			$this->assertEquals( $output, shortcode_unautop( $in ) );
+		}
+	}
 }
