From: C. Scott Ananian Date: Thu, 14 Jul 2016 22:00:20 +0000 (-0400) Subject: Support
tags in Balancer. X-Git-Tag: 1.31.0-rc.0~6318 X-Git-Url: http://git.cyclocoop.org/?a=commitdiff_plain;h=2f705013642287f021eed275bd4faba8ff227e21;p=lhc%2Fweb%2Fwiklou.git Support tags in Balancer. Change-Id: I893fc231fea71f58449ed426d64ac99fdcb31d9e --- diff --git a/includes/tidy/Balancer.php b/includes/tidy/Balancer.php index 1ad8a4a533..0fa96bd37a 100644 --- a/includes/tidy/Balancer.php +++ b/includes/tidy/Balancer.php @@ -69,7 +69,7 @@ class BalanceSets { public static $unsupportedSet = [ self::HTML_NAMESPACE => [ 'html' => true, 'head' => true, 'body' => true, 'frameset' => true, - 'form' => true, 'frame' => true, + 'frame' => true, 'plaintext' => true, 'isindex' => true, 'textarea' => true, 'xmp' => true, 'iframe' => true, 'noembed' => true, 'noscript' => true, 'script' => true, @@ -185,7 +185,14 @@ class BalanceSets { ] ]; - # OMITTED: formAssociatedSet, since we don't allow + // See https://html.spec.whatwg.org/multipage/forms.html#form-associated-element + public static $formAssociatedSet = [ + self::HTML_NAMESPACE => [ + 'button' => true, 'fieldset' => true, 'input' => true, + 'keygen' => true, 'object' => true, 'output' => true, + 'select' => true, 'textarea' => true, 'img' => true + ] + ]; public static $inScopeSet = [ self::HTML_NAMESPACE => [ @@ -1723,11 +1730,10 @@ class BalanceActiveFormattingElements { * - All comments and null characters are assumed to have been removed. * - We don't alter linefeeds after
/.
  * - The following elements are disallowed: , , , ,
- *   , , , <isindex>, <textarea>, <xmp>, <iframe>,
+ *   <frame>, <plaintext>, <isindex>, <textarea>, <xmp>, <iframe>,
  *   <noembed>, <noscript>, <script>, <title>.  As a result,
  *   further simplifications can be made:
  *   - `frameset-ok` is not tracked.
- *   - `form element pointer` is not tracked.
  *   - `head element pointer` is not tracked (but presumed non-null)
  *   - Tokenizer has only a single mode.
  *
@@ -1756,6 +1762,7 @@ class Balancer {
 	private $pendingTableText;
 	private $originalInsertionMode;
 	private $fragmentContext;
+	private $formElementPointer;
 
 	/**
 	 * Create a new Balancer.
@@ -1833,6 +1840,13 @@ class Balancer {
 		$this->fragmentContext =
 			new BalanceElement( BalanceSets::HTML_NAMESPACE, 'body', [] );
 		$this->resetInsertionMode();
+		$this->formElementPointer = null;
+		for ( $e = $this->fragmentContext; $e != null; $e = $e->parent ) {
+			if ( $e->isHtmlNamed( 'form' ) ) {
+				$this->formElementPointer = $e;
+				break;
+			}
+		}
 
 		// First element is text not tag
 		$x = $this->bitsIterator->current();
@@ -1849,6 +1863,7 @@ class Balancer {
 		$this->afe = null;
 		$this->stack = null;
 		$this->fragmentContext = null;
+		$this->formElementPointer = null;
 		return $result;
 	}
 
@@ -2349,7 +2364,21 @@ class Balancer {
 				# 2. OMITTED: frameset_ok
 				return true;
 
-			# OMITTED: <form>
+			case 'form':
+				if (
+					$this->formElementPointer &&
+					$this->stack->indexOf( 'template' ) < 0
+				) {
+					return true; // in a form, not in a template.
+				}
+				if ( $this->stack->inButtonScope( "p" ) ) {
+					$this->inBodyMode( 'endtag', 'p' );
+				}
+				$elt = $this->stack->insertHTMLElement( $value, $attribs );
+				if ( $this->stack->indexOf( 'template' ) < 0 ) {
+					$this->formElementPointer = $elt;
+				}
+				return true;
 
 			case 'li':
 				# OMITTED: frameset_ok
@@ -2643,7 +2672,26 @@ class Balancer {
 				$this->stack->popTag( $value );
 				return true;
 
-			# OMITTED: <form>
+			case 'form':
+				if ( $this->stack->indexOf( 'template' ) < 0 ) {
+					$openform = $this->formElementPointer;
+					$this->formElementPointer = null;
+					if ( !$openform || !$this->stack->inScope( $openform ) ) {
+						return true;
+					}
+					$this->stack->generateImpliedEndTags();
+					// Don't flatten yet if we're removing a <form> element
+					// out-of-order. (eg. `<form><div></form>`)
+					$flatten = ( $this->stack->currentNode === $openform );
+					$this->stack->removeElement( $openform, $flatten );
+				} else {
+					if ( !$this->stack->inScope( 'form' ) ) {
+						return true;
+					}
+					$this->stack->generateImpliedEndTags();
+					$this->stack->popTag( 'form' );
+				}
+				return true;
 
 			case 'p':
 				if ( !$this->stack->inButtonScope( 'p' ) ) {
@@ -2799,7 +2847,17 @@ class Balancer {
 				$this->stack->pop();
 				return true;
 
-			# OMITTED: <form>
+			case 'form':
+				if (
+					$this->formElementPointer ||
+					$this->stack->indexOf( 'template' ) >= 0
+				) {
+					return true; // ignore this token
+				}
+				$this->formElementPointer =
+					$this->stack->insertHTMLElement( $value, $attribs );
+				$this->stack->popTag( $this->formElementPointer );
+				return true;
 			}
 			// Fall through for "anything else" clause.
 		} elseif ( $token === 'endtag' ) {
diff --git a/tests/phpunit/includes/tidy/BalancerTest.php b/tests/phpunit/includes/tidy/BalancerTest.php
index 50931c7913..213982ad90 100644
--- a/tests/phpunit/includes/tidy/BalancerTest.php
+++ b/tests/phpunit/includes/tidy/BalancerTest.php
@@ -83,13 +83,12 @@ class BalancerTest extends MediaWikiTestCase {
 					// Skip tests involving doctypes.
 					continue;
 				}
-				if ( preg_match( ',</?(html|head|body|frame|plaintext)>|<rdar:,i', $case['data'] ) ) {
+				if ( preg_match( ',</?(html|head|body|frame|plaintext)>|<rdar:|<isindex,i', $case['data'] ) ) {
 					// Skip tests involving some literal tags, which are
 					// unsupported but don't show up in the expected output.
 					continue;
 				}
 				if (
-					isset( $case['document']['props']['tags']['form'] ) ||
 					isset( $case['document']['props']['tags']['iframe'] ) ||
 					isset( $case['document']['props']['tags']['noembed'] ) ||
 					isset( $case['document']['props']['tags']['noscript'] ) ||