Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 42
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
DOMFragmentBuilder
0.00% covered (danger)
0.00%
0 / 42
0.00% covered (danger)
0.00%
0 / 4
156
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 processContentInOwnPipeline
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 buildDOMFragment
0.00% covered (danger)
0.00%
0 / 34
0.00% covered (danger)
0.00%
0 / 1
42
 onTag
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2declare( strict_types = 1 );
3
4namespace Wikimedia\Parsoid\Wt2Html\TT;
5
6use Wikimedia\Assert\Assert;
7use Wikimedia\Parsoid\Tokens\EOFTk;
8use Wikimedia\Parsoid\Tokens\Token;
9use Wikimedia\Parsoid\Tokens\XMLTagTk;
10use Wikimedia\Parsoid\Utils\PipelineUtils;
11use Wikimedia\Parsoid\Wt2Html\TokenHandlerPipeline;
12
13class DOMFragmentBuilder extends XMLTagBasedHandler {
14    /**
15     * @param TokenHandlerPipeline $manager manager environment
16     * @param array $options options
17     */
18    public function __construct( TokenHandlerPipeline $manager, array $options ) {
19        parent::__construct( $manager, $options );
20    }
21
22    /**
23     * Can/should content represented in 'toks' be processed in its own DOM scope?
24     * 1. No reason to spin up a new pipeline for plain text
25     * 2. In some cases, if templates need not be nested entirely within the
26     *    boundary of the token, we cannot process the contents in a new scope.
27     * @param array $toks
28     * @param bool $isLinkContext
29     * @return bool
30     */
31    private function processContentInOwnPipeline( array $toks, bool $isLinkContext ): bool {
32        // For wikilinks and extlinks, templates should be properly nested
33        // in the content section. So, we can process them in sub-pipelines.
34        // But, for other context-toks, we should back out. FIXME: Can be smarter and
35        // detect proper template nesting, but, that can be a later enhancement
36        // when dom-scope-tokens are used in other contexts.
37        Assert::invariant( $isLinkContext, 'A link context is assumed.' );
38
39        foreach ( $toks as $t ) {
40            if ( $t instanceof XMLTagTk ) {
41                // Since we encountered a complex token, we'll process this
42                // in a subpipeline.
43                return true;
44            }
45        }
46
47        // No complex tokens at all -- no need to spin up a new pipeline
48        return false;
49    }
50
51    /**
52     * @return array<string|Token>
53     */
54    private function buildDOMFragment( Token $scopeToken ): array {
55        $contextTokName = $scopeToken->getAttributeV( 'contextTokName' );
56        $linkContext = $contextTokName === 'wikilink' || $contextTokName === 'extlink';
57
58        $contentKV = $scopeToken->getAttributeKV( 'content' );
59        $content = $contentKV->v;
60        if (
61            is_string( $content ) || !$this->processContentInOwnPipeline( $content, $linkContext )
62        ) {
63            // New pipeline not needed. Pass them through
64            return is_string( $content ) ? [ $content ] : $content;
65        } else {
66            // Source offsets of content
67            $srcOffsets = $contentKV->srcOffsets;
68
69            // Without source offsets for the content, it isn't possible to
70            // compute DSR and template wrapping in content. So, users of
71            // mw:dom-fragment-token should always set offsets on content
72            // that comes from the top-level document.
73            Assert::invariant(
74                $this->options['inTemplate'] || (bool)$srcOffsets,
75                'Processing top-level content without source offsets'
76            );
77
78            $pipelineOpts = [
79                'inlineContext' => $scopeToken->getAttributeV( 'inlineContext' ) === "1",
80                'expandTemplates' => $this->options['expandTemplates'],
81                'inTemplate' => $this->options['inTemplate']
82            ];
83
84            // Append EOF
85            $content[] = new EOFTk();
86
87            // Process tokens
88            $domFragment = PipelineUtils::processContentInPipeline(
89                $this->env,
90                $this->manager->getFrame(),
91                // Append EOF
92                $content,
93                [
94                    'pipelineType' => 'expanded-tokens-to-fragment',
95                    'pipelineOpts' => $pipelineOpts,
96                    'srcOffsets' => $srcOffsets->value,
97                    'sol' => true
98                ]
99            );
100
101            return PipelineUtils::tunnelDOMThroughTokens(
102                $this->env,
103                $scopeToken,
104                $domFragment,
105                [ "pipelineOpts" => $pipelineOpts ]
106            );
107        }
108    }
109
110    /**
111     * @inheritDoc
112     */
113    public function onTag( XMLTagTk $token ): ?array {
114        return $token->getName() === 'mw:dom-fragment-token' ?
115            $this->buildDOMFragment( $token ) : null;
116    }
117}