1<?php 2declare( strict_types = 1 ); 3 4namespace Wikimedia\Parsoid\Wt2Html\PP\Processors; 5 6use DOMElement; 7use DOMNode; 8use Wikimedia\Parsoid\Config\Env; 9use Wikimedia\Parsoid\Config\WikitextConstants; 10use Wikimedia\Parsoid\Utils\DOMDataUtils; 11use Wikimedia\Parsoid\Utils\DOMUtils; 12use Wikimedia\Parsoid\Utils\WTUtils; 13use Wikimedia\Parsoid\Wt2Html\Wt2HtmlDOMProcessor; 14 15class MigrateTemplateMarkerMetas implements Wt2HtmlDOMProcessor { 16 /** 17 * This will move the start/end-meta closest to the content 18 * that the template/extension produced and improve accuracy 19 * of finding dom ranges and wrapping templates. 20 * 21 * If the last child of a node is a start-meta, 22 * move it up and make it the parent's right sibling. 23 * 24 * If the first child of a node is an end-meta, 25 * move it up and make it the parent's left sibling. 26 * 27 * @param DOMNode $node 28 * @param Env $env 29 */ 30 private function doMigrate( DOMNode $node, Env $env ): void { 31 $c = $node->firstChild; 32 while ( $c ) { 33 $sibling = $c->nextSibling; 34 if ( $c->hasChildNodes() ) { 35 $this->doMigrate( $c, $env ); 36 } 37 $c = $sibling; 38 } 39 40 // No migration out of BODY 41 if ( DOMUtils::isBody( $node ) ) { 42 return; 43 } 44 45 $firstChild = DOMUtils::firstNonSepChild( $node ); 46 if ( $firstChild && WTUtils::isTplEndMarkerMeta( $firstChild ) ) { 47 // We can migrate the meta-tag across this node's start-tag barrier only 48 // if that start-tag is zero-width, or auto-inserted. 49 $tagWidth = WikitextConstants::$WtTagWidths[$node->nodeName] ?? null; 50 DOMUtils::assertElt( $node ); 51 if ( ( $tagWidth && $tagWidth[0] === 0 && !WTUtils::isLiteralHTMLNode( $node ) ) || 52 !empty( DOMDataUtils::getDataParsoid( $node )->autoInsertedStart ) 53 ) { 54 $sentinel = $firstChild; 55 do { 56 $firstChild = $node->firstChild; 57 $node->parentNode->insertBefore( $firstChild, $node ); 58 } while ( $sentinel !== $firstChild ); 59 } 60 } 61 62 $lastChild = DOMUtils::lastNonSepChild( $node ); 63 if ( $lastChild && WTUtils::isTplStartMarkerMeta( $lastChild ) ) { 64 // We can migrate the meta-tag across this node's end-tag barrier only 65 // if that end-tag is zero-width, or auto-inserted. 66 $tagWidth = WikitextConstants::$WtTagWidths[$node->nodeName] ?? null; 67 DOMUtils::assertElt( $node ); 68 if ( ( $tagWidth && $tagWidth[1] === 0 && 69 // Except, don't migrate out of a table since the end meta 70 !WTUtils::isLiteralHTMLNode( $node ) ) || 71 // marker may have been fostered and this is more likely to 72 // result in a flipped range that isn't enclosed. 73 ( !empty( DOMDataUtils::getDataParsoid( $node )->autoInsertedEnd ) && 74 $node->nodeName !== 'table' ) 75 ) { 76 $sentinel = $lastChild; 77 do { 78 $lastChild = $node->lastChild; 79 $node->parentNode->insertBefore( $lastChild, $node->nextSibling ); 80 } while ( $sentinel !== $lastChild ); 81 } 82 } 83 } 84 85 /** 86 * @inheritDoc 87 */ 88 public function run( 89 Env $env, DOMElement $root, array $options = [], bool $atTopLevel = false 90 ): void { 91 $this->doMigrate( $root, $env ); 92 } 93} 94