Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 40
DOMFragmentBuilder
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 4
420
0.00% covered (danger)
0.00%
0 / 40
 __construct
0.00% covered (danger)
0.00%
0 / 1
2
0.00% covered (danger)
0.00%
0 / 2
 subpipelineUnnecessary
0.00% covered (danger)
0.00%
0 / 1
132
0.00% covered (danger)
0.00%
0 / 10
 buildDOMFragment
0.00% covered (danger)
0.00%
0 / 1
30
0.00% covered (danger)
0.00%
0 / 26
 onTag
0.00% covered (danger)
0.00%
0 / 1
12
0.00% covered (danger)
0.00%
0 / 2
<?php
declare( strict_types = 1 );
namespace Wikimedia\Parsoid\Wt2Html\TT;
use Wikimedia\Assert\Assert;
use Wikimedia\Parsoid\Tokens\EndTagTk;
use Wikimedia\Parsoid\Tokens\EOFTk;
use Wikimedia\Parsoid\Tokens\SelfclosingTagTk;
use Wikimedia\Parsoid\Tokens\TagTk;
use Wikimedia\Parsoid\Tokens\Token;
use Wikimedia\Parsoid\Utils\PipelineUtils;
use Wikimedia\Parsoid\Utils\TokenUtils;
use Wikimedia\Parsoid\Wt2Html\TokenTransformManager;
class DOMFragmentBuilder extends TokenHandler {
    /**
     * @param TokenTransformManager $manager manager environment
     * @param array $options options
     */
    public function __construct( TokenTransformManager $manager, array $options ) {
        parent::__construct( $manager, $options );
    }
    /**
     * Can/should content represented in 'toks' be processed in its own DOM scope?
     * 1. No reason to spin up a new pipeline for plain text
     * 2. In some cases, if templates need not be nested entirely within the
     *    boundary of the token, we cannot process the contents in a new scope.
     * @param array $toks
     * @param Token $contextTok
     * @return bool
     */
    private function subpipelineUnnecessary( array $toks, Token $contextTok ): bool {
        for ( $i = 0,  $n = count( $toks );  $i < $n;  $i++ ) {
            $t = $toks[$i];
            // For wikilinks and extlinks, templates should be properly nested
            // in the content section. So, we can process them in sub-pipelines.
            // But, for other context-toks, we back out. FIXME: Can be smarter and
            // detect proper template nesting, but, that can be a later enhancement
            // when dom-scope-tokens are used in other contexts.
            if ( $contextTok && $contextTok->getName() !== 'wikilink' &&
                $contextTok->getName() !== 'extlink' &&
                $t instanceof SelfclosingTagTk &&
                 $t->getName() === 'meta' && TokenUtils::hasTypeOf( $t, 'mw:Transclusion' )
            ) {
                return true;
            } elseif ( $t instanceof TagTk || $t instanceof EndTagTk || $t instanceof SelfclosingTagTk ) {
                // Since we encountered a complex token, we'll process this
                // in a subpipeline.
                return false;
            }
        }
        // No complex tokens at all -- no need to spin up a new pipeline
        return true;
    }
    /**
     * @param Token $scopeToken
     * @return TokenHandlerResult
     */
    private function buildDOMFragment( Token $scopeToken ): TokenHandlerResult {
        $contentKV = $scopeToken->getAttributeKV( 'content' );
        $content = $contentKV->v;
        if ( is_string( $content ) ||
            $this->subpipelineUnnecessary( $content, $scopeToken->getAttribute( 'contextTok' ) )
        ) {
            // New pipeline not needed. Pass them through
            return new TokenHandlerResult( is_string( $content ) ? [ $content ] : $content );
        } else {
            // Source offsets of content
            $srcOffsets = $contentKV->srcOffsets;
            // Without source offsets for the content, it isn't possible to
            // compute DSR and template wrapping in content. So, users of
            // mw:dom-fragment-token should always set offsets on content
            // that comes from the top-level document.
            Assert::invariant(
                $this->options['inTemplate'] || (bool)$srcOffsets,
                'Processing top-level content without source offsets'
            );
            $pipelineOpts = [
                'inlineContext' => $scopeToken->getAttribute( 'inlineContext' ) === "1",
                'expandTemplates' => $this->options['expandTemplates'],
                'inTemplate' => $this->options['inTemplate']
            ];
            // Append EOF
            $content[] = new EOFTk();
            // Process tokens
            $domFragment = PipelineUtils::processContentInPipeline(
                $this->env,
                $this->manager->getFrame(),
                // Append EOF
                $content,
                [
                    'pipelineType' => 'tokens/x-mediawiki/expanded',
                    'pipelineOpts' => $pipelineOpts,
                    'srcOffsets' => $srcOffsets->value,
                    'sol' => true
                ]
            );
            $toks = PipelineUtils::tunnelDOMThroughTokens(
                $this->env,
                $scopeToken,
                $domFragment,
                [ "pipelineOpts" => $pipelineOpts ]
            );
            return new TokenHandlerResult( $toks );
        }
    }
    /**
     * @inheritDoc
     */
    public function onTag( Token $token ): ?TokenHandlerResult {
        return $token->getName() === 'mw:dom-fragment-token' ?
            $this->buildDOMFragment( $token ) : null;
    }
}