Parser: Don't wrap <style> or <link> tags in paragraphs
authorBrad Jorsch <bjorsch@wikimedia.org>
Mon, 26 Feb 2018 21:49:08 +0000 (16:49 -0500)
committerBrad Jorsch <bjorsch@wikimedia.org>
Wed, 28 Feb 2018 19:12:49 +0000 (14:12 -0500)
If <style> or <link> tags are by themselves on a line, don't wrap them
in <p> tags. But, at the same time, don't end an existing paragraph if
we find <style> or <link> in the middle (like we would if we just
treated them as block tags).

If <style> or <link> is on a line with other text, though, let it be
wrapped in a paragraph along with that other text.

Bug: T186965
Change-Id: Ide4005842cdab537226aa538cb5f7d8e363ba95d

includes/parser/BlockLevelPass.php
tests/parser/ParserTestRunner.php
tests/parser/parserTests.txt

index 8cf8f85..7f78912 100644 (file)
@@ -329,6 +329,14 @@ class BlockLevelPass {
                                                        $this->lastSection = 'pre';
                                                }
                                                $t = substr( $t, 1 );
                                                        $this->lastSection = 'pre';
                                                }
                                                $t = substr( $t, 1 );
+                                       } elseif ( preg_match( '/^(?:<style\\b[^>]*>.*?<\\/style>\s*|<link\\b[^>]*>\s*)+$/iS', $t ) ) {
+                                               # T186965: <style> or <link> by itself on a line shouldn't open or close paragraphs.
+                                               # But it should clear $pendingPTag.
+                                               if ( $pendingPTag ) {
+                                                       $output .= $this->closeParagraph();
+                                                       $pendingPTag = false;
+                                                       $this->lastSection = '';
+                                               }
                                        } else {
                                                # paragraph
                                                if ( trim( $t ) === '' ) {
                                        } else {
                                                # paragraph
                                                if ( trim( $t ) === '' ) {
index d4b1f91..a03f969 100644 (file)
@@ -831,6 +831,19 @@ class ParserTestRunner {
                $parser = $this->getParser( $preprocessor );
                $title = Title::newFromText( $titleText );
 
                $parser = $this->getParser( $preprocessor );
                $title = Title::newFromText( $titleText );
 
+               if ( isset( $opts['styletag'] ) ) {
+                       // For testing the behavior of <style> (including those deduplicated
+                       // into <link> tags), add tag hooks to allow them to be generated.
+                       $parser->setHook( 'style', function ( $content, $attributes, $parser ) {
+                               $marker = Parser::MARKER_PREFIX . '-style-' . md5( $content ) . Parser::MARKER_SUFFIX;
+                               $parser->mStripState->addNoWiki( $marker, $content );
+                               return Html::inlineStyle( $marker, 'all', $attributes );
+                       } );
+                       $parser->setHook( 'link', function ( $content, $attributes, $parser ) {
+                               return Html::element( 'link', $attributes );
+                       } );
+               }
+
                if ( isset( $opts['pst'] ) ) {
                        $out = $parser->preSaveTransform( $test['input'], $title, $user, $options );
                        $output = $parser->getOutput();
                if ( isset( $opts['pst'] ) ) {
                        $out = $parser->preSaveTransform( $test['input'], $title, $user, $options );
                        $output = $parser->getOutput();
index e6fa203..4d6efff 100644 (file)
@@ -29899,16 +29899,14 @@ unclosed internal link XSS (T137264)
 <p>[[#%3Cscript%3Ealert(1)%3C/script%3E|</p>
 !! end
 
 <p>[[#%3Cscript%3Ealert(1)%3C/script%3E|</p>
 !! end
 
-# Use $wgRawHtml to inject a <style> tag, since you normally can't in wikitext
-# (Parsoid doesn't support $wgRawHtml==true)
 !! test
 Validating that <style> isn't eaten by tidy (T167349)
 !! options
 !! test
 Validating that <style> isn't eaten by tidy (T167349)
 !! options
-wgRawHtml=1
+styletag=1
 !! wikitext
 <div class="foo">
 !! wikitext
 <div class="foo">
-<html><style>.foo::before { content: "<foo>"; }</style></html>
-<html><style data-mw-foobar="baz">.foo::after { content: "<bar>"; }</style></html>
+<style>.foo::before { content: "<foo>"; }</style>
+<style data-mw-foobar="baz">.foo::after { content: "<bar>"; }</style>
 </div>
 !! html/php+tidy
 <div class="foo">
 </div>
 !! html/php+tidy
 <div class="foo">
@@ -29917,6 +29915,86 @@ wgRawHtml=1
 </div>
 !! end
 
 </div>
 !! end
 
+!! test
+Validating that <style> isn't wrapped in a paragraph (T186965)
+!! options
+styletag=1
+!! wikitext
+A style tag, by itself or with other style/link tags, shouldn't be wrapped in a paragraph
+
+<style>.foo::before { content: "<foo>"; }</style>
+
+<style>.foo::before { content: "<foo>"; }</style> <link rel="foo" href="bar"/><style>.foo::before { content: "<foo>"; }</style>
+
+But if it's on a line with other content, let it be wrapped.
+
+<style>.foo::before { content: "<foo>"; }</style> bar
+
+foo <style>.foo::before { content: "<foo>"; }</style>
+
+foo <style>.foo::before { content: "<foo>"; }</style> bar
+
+And the same if we have non-paragraph-breaking whitespace
+
+foo
+<style>.foo::before { content: "<foo>"; }</style>
+bar
+!! html/php
+<p>A style tag, by itself or with other style/link tags, shouldn't be wrapped in a paragraph
+</p>
+<style>.foo::before { content: "<foo>"; }</style>
+<style>.foo::before { content: "<foo>"; }</style> <link rel="foo" href="bar"/><style>.foo::before { content: "<foo>"; }</style>
+<p>But if it's on a line with other content, let it be wrapped.
+</p><p><style>.foo::before { content: "<foo>"; }</style> bar
+</p><p>foo <style>.foo::before { content: "<foo>"; }</style>
+</p><p>foo <style>.foo::before { content: "<foo>"; }</style> bar
+</p><p>And the same if we have non-paragraph-breaking whitespace
+</p><p>foo
+<style>.foo::before { content: "<foo>"; }</style>
+bar
+</p>
+!! end
+
+!! test
+Validating that <link> isn't wrapped in a paragraph (T186965)
+!! options
+styletag=1
+!! wikitext
+A link tag, by itself or with other style/link tags, shouldn't be wrapped in a paragraph
+
+<link rel="foo" href="bar"/>
+
+<link rel="foo" href="bar"/> <style>.foo::before { content: "<foo>"; }</style><link rel="foo" href="bar"/>
+
+But if it's on a line with other content, let it be wrapped.
+
+<link rel="foo" href="bar"/> bar
+
+foo <link rel="foo" href="bar"/>
+
+foo <link rel="foo" href="bar"/> bar
+
+And the same if we have non-paragraph-breaking whitespace
+
+foo
+<link rel="foo" href="bar"/>
+bar
+!! html/php
+<p>A link tag, by itself or with other style/link tags, shouldn't be wrapped in a paragraph
+</p>
+<link rel="foo" href="bar"/>
+<link rel="foo" href="bar"/> <style>.foo::before { content: "<foo>"; }</style><link rel="foo" href="bar"/>
+<p>But if it's on a line with other content, let it be wrapped.
+</p><p><link rel="foo" href="bar"/> bar
+</p><p>foo <link rel="foo" href="bar"/>
+</p><p>foo <link rel="foo" href="bar"/> bar
+</p><p>And the same if we have non-paragraph-breaking whitespace
+</p><p>foo
+<link rel="foo" href="bar"/>
+bar
+</p>
+!! end
+
 !! test
 Decoding of HTML entities in headings and links for IDs and link fragments (T103714)
 !! config
 !! test
 Decoding of HTML entities in headings and links for IDs and link fragments (T103714)
 !! config