[OutputTransform] Add data-mw-parsoid-version to wrapper div

Adding a data-mw-parsoid-version attribute to the wrapper div helps to
unambiguously mark parsoid-generated output in a way which is compatible
with CSS rules and client-side JavaScript.

By embedding the current version of parsoid in the data attribute,
sophisticated CSS rules can match against a specific version of
Parsoid in order to facilitate proper behavior; for example:

    div[data-mw-parsoid-version^="0.20.0"]

This could be useful in deployment scenarios where the parser cache
might contain content generated by older or newer versions of Parsoid,
for roll-forward or roll-back deployment scenarios, respectively.

Bug: T363378
Change-Id: I941d31479eebb12ea1f4dcdb0a1737033ddc8ac1
This commit is contained in:
C. Scott Ananian 2024-04-24 16:35:11 -04:00
parent 22b42ae351
commit 7738554eee
3 changed files with 14 additions and 7 deletions

View file

@ -30,11 +30,15 @@ class AddWrapperDivClass extends ContentTextTransformStage {
protected function transformText( string $text, ParserOutput $po, ?ParserOptions $popts, array &$options ): string {
$wrapperDivClass = $options['wrapperDivClass'];
$pageLang = $this->getLanguageWithFallbackGuess( $po );
$parsoidVersion = $po->getExtensionData( 'core:parsoid-version' );
$extraAttrs = $parsoidVersion === null ? [] : [
'data-mw-parsoid-version' => $parsoidVersion,
];
return Html::rawElement( 'div', [
'class' => 'mw-content-' . $pageLang->getDir() . ' ' . $wrapperDivClass,
'lang' => $pageLang->toBcp47Code(),
'dir' => $pageLang->getDir(),
], $text );
] + $extraAttrs, $text );
}
private function getLanguageWithFallbackGuess( ParserOutput $po ): Language {

View file

@ -107,9 +107,9 @@ class ApiParseTest extends ApiTestCase {
$html = substr( $html, strlen( $expectedStart ) );
# Parsoid-based transformations may add an ID attribute to the
# wrapper div
$possibleIdAttr = '/^( id="[^"]+")?>/';
# Parsoid-based transformations may add ID and data-mw-parsoid-version
# attributes to the wrapper div
$possibleIdAttr = '/^( (id|data-mw[^=]*)="[^"]+")*>/';
$html = preg_replace( $possibleIdAttr, '', $html );
$possibleParserCache = '/\n<!-- Saved in (?>parser cache|RevisionOutputCache) (?>.*?\n -->)\n/';

View file

@ -6,6 +6,7 @@ use MediaWiki\Linker\LinkTarget;
use MediaWiki\MainConfigNames;
use MediaWiki\Title\Title;
use MediaWiki\Title\TitleValue;
use Wikimedia\Parsoid\Parsoid;
/**
* @group ContentHandler
@ -61,6 +62,8 @@ class WikitextContentHandlerIntegrationTest extends TextContentHandlerIntegratio
'suppressTOC',
'targetLanguage',
] );
$parsoidVersion = 'data-mw-parsoid-version="' . Parsoid::version() . '"';
yield 'Basic render' => [
'title' => 'WikitextContentTest_testGetParserOutput',
'model' => CONTENT_MODEL_WIKITEXT,
@ -78,7 +81,7 @@ class WikitextContentHandlerIntegrationTest extends TextContentHandlerIntegratio
'title' => 'WikitextContentTest_testGetParserOutput',
'model' => CONTENT_MODEL_WIKITEXT,
'text' => "hello ''world''\n",
'expectedHtml' => '<div class="mw-content-ltr mw-parser-output" lang="en" dir="ltr">' . "<section data-mw-section-id=\"0\" id=\"mwAQ\"><p id=\"mwAg\">hello <i id=\"mwAw\">world</i></p>\n</section></div>",
'expectedHtml' => "<div class=\"mw-content-ltr mw-parser-output\" lang=\"en\" dir=\"ltr\" $parsoidVersion><section data-mw-section-id=\"0\" id=\"mwAQ\"><p id=\"mwAg\">hello <i id=\"mwAw\">world</i></p>\n</section></div>",
'expectedFields' => [
'Links' => [
],
@ -92,7 +95,7 @@ class WikitextContentHandlerIntegrationTest extends TextContentHandlerIntegratio
'title' => 'WikitextContentTest_testGetParserOutput',
'model' => CONTENT_MODEL_WIKITEXT,
'text' => "#REDIRECT [[Main Page]]",
'expectedHtml' => '<div class="mw-content-ltr mw-parser-output" lang="en" dir="ltr">' . "<div class=\"redirectMsg\"><p>Redirect to:</p><ul class=\"redirectText\"><li><a href=\"/w/index.php?title=Main_Page&amp;action=edit&amp;redlink=1\" class=\"new\" title=\"Main Page (page does not exist)\">Main Page</a></li></ul></div><section data-mw-section-id=\"0\" id=\"mwAQ\"><link rel=\"mw:PageProp/redirect\" href=\"./Main_Page\" id=\"mwAg\"></section></div>",
'expectedHtml' => "<div class=\"mw-content-ltr mw-parser-output\" lang=\"en\" dir=\"ltr\" $parsoidVersion><div class=\"redirectMsg\"><p>Redirect to:</p><ul class=\"redirectText\"><li><a href=\"/w/index.php?title=Main_Page&amp;action=edit&amp;redlink=1\" class=\"new\" title=\"Main Page (page does not exist)\">Main Page</a></li></ul></div><section data-mw-section-id=\"0\" id=\"mwAQ\"><link rel=\"mw:PageProp/redirect\" href=\"./Main_Page\" id=\"mwAg\"></section></div>",
'expectedFields' => [
'Links' => [
[ 'Main_Page' => 0 ],
@ -107,7 +110,7 @@ class WikitextContentHandlerIntegrationTest extends TextContentHandlerIntegratio
'title' => 'WikitextContentTest_testGetParserOutput',
'model' => CONTENT_MODEL_WIKITEXT,
'text' => "== Hello ==",
'expectedHtml' => '<div class="mw-content-ltr mw-parser-output" lang="en" dir="ltr" id="mwAw"><section data-mw-section-id="0" id="mwAQ"></section><section data-mw-section-id="1" id="mwAg"><div class="mw-heading mw-heading2" id="mwBA"><h2 id="Hello">Hello</h2><span class="mw-editsection" id="mwBQ"><span class="mw-editsection-bracket" id="mwBg">[</span><a href="/w/index.php?title=WikitextContentTest_testGetParserOutput&amp;action=edit&amp;section=1" title="Edit section: Hello" id="mwBw"><span id="mwCA">edit</span></a><span class="mw-editsection-bracket" id="mwCQ">]</span></span></div></section></div>',
'expectedHtml' => "<div class=\"mw-content-ltr mw-parser-output\" lang=\"en\" dir=\"ltr\" $parsoidVersion id=\"mwAw\">" . '<section data-mw-section-id="0" id="mwAQ"></section><section data-mw-section-id="1" id="mwAg"><div class="mw-heading mw-heading2" id="mwBA"><h2 id="Hello">Hello</h2><span class="mw-editsection" id="mwBQ"><span class="mw-editsection-bracket" id="mwBg">[</span><a href="/w/index.php?title=WikitextContentTest_testGetParserOutput&amp;action=edit&amp;section=1" title="Edit section: Hello" id="mwBw"><span id="mwCA">edit</span></a><span class="mw-editsection-bracket" id="mwCQ">]</span></span></div></section></div>',
'expectedFields' => [
'Links' => [
],