Index: wp-admin/includes/class-wp-theme-install-list-table.php
===================================================================
--- wp-admin/includes/class-wp-theme-install-list-table.php	(revision 20069)
+++ wp-admin/includes/class-wp-theme-install-list-table.php	(working copy)
@@ -9,6 +9,8 @@
  */
 class WP_Theme_Install_List_Table extends WP_List_Table {
 
+	var $features = array();
+
 	function __construct() {
 		parent::__construct( array(
 			'ajax' => true,
@@ -16,16 +18,25 @@
 	}
 
 	function ajax_user_can() {
-		return current_user_can('install_themes');
+		return current_user_can( 'install_themes' );
 	}
 
 	function prepare_items() {
 		include( ABSPATH . 'wp-admin/includes/theme-install.php' );
 
-		global $tabs, $tab, $paged, $type, $term, $theme_field_defaults;
-
+		global $tabs, $tab, $paged, $type, $theme_field_defaults;
 		wp_reset_vars( array( 'tab' ) );
 
+		$search_terms = array();
+		$search_string = '';
+		if ( ! empty( $_REQUEST['s'] ) ){
+			$search_string = strtolower( stripslashes( $_REQUEST['s'] ) );
+			$search_terms = array_unique( array_filter( array_map( 'trim', explode( ',', $search_string ) ) ) );
+		}
+
+		if ( ! empty( $_REQUEST['features'] ) )
+			$this->features = $_REQUEST['features'];
+
 		$paged = $this->get_pagenum();
 
 		$per_page = 36;
@@ -55,29 +66,21 @@
 		switch ( $tab ) {
 			case 'search':
 				$type = isset( $_REQUEST['type'] ) ? stripslashes( $_REQUEST['type'] ) : '';
-				$term = isset( $_REQUEST['s'] ) ? stripslashes( $_REQUEST['s'] ) : '';
-
 				switch ( $type ) {
 					case 'tag':
-						$terms = explode( ',', $term );
-						$terms = array_map( 'trim', $terms );
-						$terms = array_map( 'sanitize_title_with_dashes', $terms );
-						$args['tag'] = $terms;
+						$args['tag'] = array_map( 'sanitize_title_with_dashes', $search_terms );
 						break;
 					case 'term':
-						$args['search'] = $term;
+						$args['search'] = $search_string;
 						break;
 					case 'author':
-						$args['author'] = $term;
+						$args['author'] = $search_string;
 						break;
 				}
 
-				if ( !empty( $_REQUEST['features'] ) ) {
-					$terms = $_REQUEST['features'];
-					$terms = array_map( 'trim', $terms );
-					$terms = array_map( 'sanitize_title_with_dashes', $terms );
-					$args['tag'] = $terms;
-					$_REQUEST['s'] = implode( ',', $terms );
+				if ( ! empty( $this->features ) ) {
+					$args['tag'] = $this->features;
+					$_REQUEST['s'] = implode( ',', $this->features );
 					$_REQUEST['type'] = 'tag';
 				}
 
@@ -95,7 +98,7 @@
 				$args = false;
 		}
 
-		if ( !$args )
+		if ( ! $args )
 			return;
 
 		$api = themes_api( 'query_themes', $args );
@@ -170,4 +173,37 @@
 				?></div>
 		<?php } // end foreach $theme_names
 	}
+
+	/**
+	 * Send required variables to JavaScript land
+	 *
+	 * @since 3.4
+	 * @access private
+	 *
+	 * @uses $tab Global; current tab within Themes->Install screen
+	 * @uses $type Global; type of search.
+	 * @uses $this->features Array of all feature search terms.
+	 * @uses get_pagenum()
+	 * @uses _pagination_args['total_pages']
+	 */
+	function _js_vars() {
+		global $tab, $type;
+		$search_string = isset( $_REQUEST['s'] ) ? esc_attr( stripslashes( $_REQUEST['s'] ) ) : '';
+
+		$total_pages = 0;
+		if ( ! empty( $this->_pagination_args['total_pages'] ) )
+			$total_pages = $this->_pagination_args['total_pages'];
+
+		$args = array(
+			'search' => $search_string,
+			'features' => $this->features,
+			'paged' => $this->get_pagenum(),
+			'total_pages' => $total_pages,
+			'tab' => $tab,
+			'type' => $type,
+		);
+
+		printf( "<script type='text/javascript'>theme_list_args = %s;</script>\n", json_encode( $args ) );
+		parent::_js_vars(); 
+	}
 }
Index: wp-admin/includes/class-wp-themes-list-table.php
===================================================================
--- wp-admin/includes/class-wp-themes-list-table.php	(revision 20069)
+++ wp-admin/includes/class-wp-themes-list-table.php	(working copy)
@@ -20,7 +20,7 @@
 
 	function ajax_user_can() {
 		// Do not check edit_theme_options here. AJAX calls for available themes require switch_themes.
-		return current_user_can('switch_themes');
+		return current_user_can( 'switch_themes' );
 	}
 
 	function prepare_items() {
@@ -202,4 +202,32 @@
 
 		return true;
 	}
+
+	/**
+	 * Send required variables to JavaScript land
+	 *
+	 * @since 3.4
+	 * @access private
+	 *
+	 * @uses $this->features Array of all feature search terms.
+	 * @uses get_pagenum()
+	 * @uses _pagination_args['total_pages']
+	 */
+	 function _js_vars() {
+		$search_string = isset( $_REQUEST['s'] ) ? esc_attr( stripslashes( $_REQUEST['s'] ) ) : '';
+
+		$total_pages = 1;
+		if ( ! empty( $this->_pagination_args['total_pages'] ) )
+			$total_pages = $this->_pagination_args['total_pages'];
+
+		$args = array(
+			'search' => $search_string,
+			'features' => $this->features,
+			'paged' => $this->get_pagenum(),
+			'total_pages' => $total_pages,
+		);
+
+		printf( "<script type='text/javascript'>theme_list_args = %s;</script>\n", json_encode( $args ) );
+		parent::_js_vars();
+	}
 }
Index: wp-admin/js/theme.dev.js
===================================================================
--- wp-admin/js/theme.dev.js	(revision 20069)
+++ wp-admin/js/theme.dev.js	(working copy)
@@ -54,48 +54,51 @@
 	theme_viewer.init();
 });
 
+
+/**
+ * Class that provides infinite scroll for Themes admin screens
+ *
+ * @since 3.4
+ *
+ * @uses ajaxurl
+ * @uses list_args
+ * @uses theme_list_args
+ * @uses $('#_ajax_fetch_list_nonce').val()
+* */
 var ThemeScroller;
-
 (function($){
 	ThemeScroller = {
-		// Inputs
 		nonce: '',
-		search: '',
-		tab: '',
-		type: '',
-		nextPage: 2,
-		features: {},
-
-		// Preferences
+		nextPage: 2, // By default, assume we're on the first page.
+		querying: false,
 		scrollPollingDelay: 500,
 		failedRetryDelay: 4000,
 		outListBottomThreshold: 300,
 
-		// Flags
-		scrolling: false,
-		querying: false,
-
+		/**
+		 * Initializer
+		 *
+		 * @since 3.4
+		 * @access private
+		 */
 		init: function() {
 			var self = this,
-				startPage,
-				queryArray = {},
-				queryString = window.location.search;
+				startPage;
 
 			// We're using infinite scrolling, so hide all pagination.
 			$('.pagination-links').hide();
 
-			// Parse GET query string
-			queryArray = this.parseQuery( queryString.substring( 1 ) );
+			// Get out early if we don't have the required arguments.
+			if ( ajaxurl === undefined ||
+				 list_args === undefined ||
+				 theme_list_args === undefined )
+				return;
 
 			// Handle inputs
 			this.nonce = $('#_ajax_fetch_list_nonce').val();
-			this.search = queryArray['s'];
-			this.features = queryArray['features'];
-			this.tab = queryArray['tab'];
-			this.type = queryArray['type'];
 
-			startPage = parseInt( queryArray['paged'], 10 );
-			if ( ! isNaN( startPage ) )
+			startPage = theme_list_args.paged;
+			if ( startPage !== undefined )
 				this.nextPage = ( startPage + 1 );
 
 			// Cache jQuery selectors
@@ -104,12 +107,25 @@
 			this.$window = $(window);
 			this.$document = $(document);
 
-			if ( $('.tablenav-pages').length )
+			/**
+			 * If there are more pages to query, then start polling to track
+			 * when user hits the bottom of the current page
+			 */
+			if ( theme_list_args.total_pages !== undefined &&
+				 theme_list_args.total_pages >= this.nextPage )
 				this.pollInterval =
 					setInterval( function() {
 						return self.poll();
 					}, this.scrollPollingDelay );
 		},
+
+		/**
+		 * Checks to see if user has scrolled to bottom of page.
+		 * If so, requests another page of content from self.ajax().
+		 *
+		 * @since 3.4
+		 * @access private
+		 */
 		poll: function() {
 			var bottom = this.$document.scrollTop() + this.$window.innerHeight();
 
@@ -119,32 +135,49 @@
 
 			this.ajax();
 		},
+
+		/**
+		 * Applies results passed from this.ajax() to $outList
+		 *
+		 * @since 3.4
+		 * @access private
+		 *
+		 * @param results Array with results from this.ajax() query.
+		 */
 		process: function( results ) {
 			if ( ( results === undefined ) ||
-				( results.rows.indexOf( 'no-items' ) != -1 ) ) {
+				 ( results.rows === undefined ) ||
+				 ( results.rows.indexOf( 'no-items' ) != -1 ) ) {
 				clearInterval( this.pollInterval );
 				return;
 			}
 
-			var totalPages = parseInt( results.total_pages, 10 );
-			if ( this.nextPage > totalPages )
+			if ( this.nextPage > theme_list_args.total_pages )
 				clearInterval( this.pollInterval );
 
-			if ( this.nextPage <= ( totalPages + 1 ) )
+			if ( this.nextPage <= ( theme_list_args.total_pages + 1 ) )
 				this.$outList.append( results.rows );
 		},
+
+		/**
+		 * Queries next page of themes
+		 *
+		 * @since 3.4
+		 * @access private
+		 */
 		ajax: function() {
 			var self = this;
+
 			this.querying = true;
 
 			var query = {
 				action: 'fetch-list',
-				tab: this.tab,
 				paged: this.nextPage,
-				s: this.search,
-				type: this.type,
+				s: theme_list_args.search,
+				tab: theme_list_args.tab,
+				type: theme_list_args.type,
 				_ajax_fetch_list_nonce: this.nonce,
-				'features[]': this.features,
+				'features[]': theme_list_args.features,
 				'list_args': list_args
 			};
 
@@ -159,40 +192,13 @@
 				.fail( function() {
 					self.$spinner.css( 'visibility', 'hidden' );
 					self.querying = false;
-					setTimeout( function() { self.ajax(); }, self.failedRetryDelay )
+					setTimeout( function() { self.ajax(); }, self.failedRetryDelay );
 				});
-		},
-		parseQuery: function( query ) {
-			var params = {};
-			if ( ! query )
-				return params;
-
-			var pairs = query.split( /[;&]/ );
-			for ( var i = 0; i < pairs.length; i++ ) {
-				var keyVal = pairs[i].split( '=' );
-
-				if ( ! keyVal || keyVal.length != 2 )
-					continue;
-
-				var key = unescape( keyVal[0] );
-				var val = unescape( keyVal[1] );
-				val = val.replace( /\+/g, ' ' );
-				key = key.replace( /\[.*\]$/g, '' );
-
-				if ( params[key] === undefined ) {
-					params[key] = val;
-				} else {
-					var oldVal = params[key];
-					if ( ! $.isArray( params[key] ) )
-						params[key] = new Array( oldVal, val );
-					else
-						params[key].push( val );
-				}
-			}
-			return params;
 		}
 	}
 
-	$(document).ready( function( $ ) { ThemeScroller.init(); });
+	$(document).ready( function($) {
+		ThemeScroller.init();
+	});
 
 })(jQuery);
