Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 122
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
ParserHook
0.00% covered (danger)
0.00%
0 / 122
0.00% covered (danger)
0.00%
0 / 5
812
0.00% covered (danger)
0.00%
0 / 1
 getParserTestConfigFileName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 sourceToDom
0.00% covered (danger)
0.00%
0 / 49
0.00% covered (danger)
0.00%
0 / 1
240
 processAttributeEmbeddedDom
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 domToWikitext
0.00% covered (danger)
0.00%
0 / 17
0.00% covered (danger)
0.00%
0 / 1
72
 getConfig
0.00% covered (danger)
0.00%
0 / 49
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2declare( strict_types = 1 );
3
4namespace Wikimedia\Parsoid\ParserTests;
5
6use Error;
7use Wikimedia\Parsoid\DOM\DocumentFragment;
8use Wikimedia\Parsoid\DOM\Element;
9use Wikimedia\Parsoid\Ext\ExtensionModule;
10use Wikimedia\Parsoid\Ext\ExtensionTagHandler;
11use Wikimedia\Parsoid\Ext\ParsoidExtensionAPI;
12use Wikimedia\Parsoid\Utils\DOMCompat;
13use Wikimedia\Parsoid\Utils\DOMDataUtils;
14use Wikimedia\Parsoid\Utils\WTUtils;
15
16/**
17 * See tests/parser/ParserTestParserHook.php in core.
18 */
19class ParserHook extends ExtensionTagHandler implements ExtensionModule {
20
21    /**
22     * Ensure that both integrated and standalone test runners have the
23     * magic word definitions used by these test hooks.
24     * @see SiteConfig::getCustomSiteConfigFileName()
25     * @see ParserTestRunner::staticSetup() (in core)
26     */
27    public static function getParserTestConfigFileName(): string {
28        return ParserTestPFragmentHandlers::getParserTestConfigFileName();
29    }
30
31    /** @inheritDoc */
32    public function sourceToDom(
33        ParsoidExtensionAPI $extApi, string $content, array $args
34    ): DocumentFragment {
35        $extName = $extApi->extTag->getName();
36        if ( $extApi->extTag->isSelfClosed() ) {
37            $content = null;
38        }
39        switch ( $extName ) {
40            case 'tag':
41            case 'tåg':
42                return $extApi->htmlToDom(
43                    "<pre>\n" .
44                        var_export( $content, true ) . "\n" .
45                        var_export( $extApi->extArgsToArray( $args ), true ) . "\n" .
46                    "</pre>"
47                );
48
49            case 'statictag':
50                // FIXME: Choose a better DOM representation that doesn't mess with
51                // newline constraints.
52                return $extApi->htmlToDom( '<span />' );
53
54            case 'asidetag':
55                // T278565
56                return $extApi->htmlToDom( '<aside>Some aside content</aside>' );
57
58            case 'pwraptest':
59                return $extApi->htmlToDom( '<!--CMT--><style>p{}</style>' );
60
61            case 'divtag':
62            case 'spantag':
63                // "Transparent" tag which wraps wikitext in a <span> or <div>;
64                // useful in testing various parsoid wrapping scenarios
65                // (we used to use <ref> for this)
66                //
67                // NOTE: When using <spantag>, p-wrapping and indent-pre
68                // transforms are disabled.
69                $argArray = $extApi->extArgsToArray( $args );
70                $isDiv = ( $extName === 'divtag' );
71                $isRaw = $argArray['raw'] ?? false;
72                $tag = $isDiv ? 'div' : 'span';
73                if ( $isRaw ) {
74                    return $extApi->htmlToDom( "<$tag>$content</$tag>" );
75                }
76                return $extApi->extTagToDOM( $args, $content, [
77                    'wrapperTag' => $tag,
78                    'parseOpts' => [
79                        'extTag' => $extName,
80                        'context' => $isDiv ? 'block' : 'inline',
81                    ],
82                ] );
83
84            case 'embedtag':
85                $dataMw = $extApi->extTag->getDefaultDataMw();
86                $domFragment = $extApi->extTagToDOM( $args, $content, [
87                    'parseOpts' => [
88                        'extTag' => $extName,
89                        'context' => 'inline',
90                    ],
91                ] );
92                $dataMw->body->extsrc = null; // clear wt representation
93                $dataMw->body->setHtml( $extApi, $domFragment );
94                $span = $domFragment->ownerDocument->createElement( 'span' );
95                DOMDataUtils::setDataMw( $span, $dataMw );
96                DOMCompat::replaceChildren( $domFragment, $span );
97                return $domFragment;
98
99            case 'sealtag':
100                return $extApi->htmlToDom( '<span />' );
101
102            default:
103                throw new Error( "Unexpected tag name: $extName in ParserHook" );
104        }
105    }
106
107    /** @inheritDoc */
108    public function processAttributeEmbeddedDom(
109        ParsoidExtensionAPI $extApi, Element $elt, callable $proc
110    ): void {
111        $dataMw = DOMDataUtils::getDataMw( $elt );
112        if ( isset( $dataMw->body->html ) ) {
113            $dom = $dataMw->body->getHtml( $extApi );
114            $ret = $proc( $dom );
115            if ( $ret ) {
116                $dataMw->body->setHtml( $extApi, $dom );
117            }
118        }
119    }
120
121    /** @inheritDoc */
122    public function domToWikitext(
123        ParsoidExtensionAPI $extApi, Element $node, bool $wrapperUnmodified
124    ) {
125        $dataMw = DOMDataUtils::getDataMw( $node );
126        $extName = WTUtils::getExtTagName( $node ) ?? $dataMw->name;
127        if ( !in_array( $extName, [ 'spantag', 'divtag', 'embedtag' ], true ) ) {
128            return false; // use default serialization
129        }
130        if ( in_array( $extName, [ 'spantag', 'divtag' ], true ) ) {
131            if ( $dataMw->getExtAttrib( 'raw' ) ?? false ) {
132                return false; // use default serialization in 'raw' mode
133            }
134        }
135        $html2wtOpts = [
136            'extName' => $extName,
137            // FIXME: One-off PHP parser state leak. This needs a better solution.
138            'inPHPBlock' => true
139        ];
140        if ( $wrapperUnmodified && isset( $dataMw->body->extsrc ) ) {
141            $src = $dataMw->body->extsrc;
142        } elseif ( $extName === 'embedtag' && isset( $dataMw->body->html ) ) {
143            // First look for the extension's content in data-mw.body.html
144            $src = $extApi->htmlToWikitext( $html2wtOpts, $dataMw->body->html );
145        } else {
146            $src = $extApi->htmlToWikitext( $html2wtOpts, DOMCompat::getInnerHTML( $node ) );
147        }
148        return "<$extName>" . $src . "</$extName>";
149    }
150
151    /** @inheritDoc */
152    public function getConfig(): array {
153        return [
154            'name' => 'ParserHook',
155            'tags' => [
156                [ 'name' => 'tag', 'handler' => self::class ],
157                [ 'name' => 'tåg', 'handler' => self::class ],
158                [ 'name' => 'statictag', 'handler' => self::class ],
159                [ 'name' => 'asidetag', 'handler' => self::class ],
160                [ 'name' => 'pwraptest', 'handler' => self::class ],
161                [
162                    'name' => 'divtag',
163                    'handler' => self::class,
164                    'options' => [
165                        'outputHasCoreMwDomSpecMarkup' => true,
166                    ],
167                ],
168                [
169                    'name' => 'spantag',
170                    'handler' => self::class,
171                    'options' => [
172                        'outputHasCoreMwDomSpecMarkup' => true,
173                    ],
174                ],
175                [
176                    'name' => 'embedtag',
177                    'handler' => self::class,
178                    'options' => [
179                        'wt2html' => [
180                            'embedsDomInAttributes' => true,
181                            'customizesDataMw' => true,
182                        ],
183                        'outputHasCoreMwDomSpecMarkup' => true,
184                    ],
185                ],
186                [
187                    'name' => 'sealtag',
188                    'handler' => self::class,
189                    'options' => [
190                        'wt2html' => [
191                            'unpackOutput' => false,
192                        ],
193                    ],
194                ],
195            ],
196            'domProcessors' => [
197                ParserHookProcessor::class
198            ],
199            'fragmentHandlers' =>
200                ParserTestPFragmentHandlers::getPFragmentHandlersConfig(),
201        ];
202    }
203
204}