Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 47
0.00% covered (danger)
0.00%
0 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
SpanHandler
0.00% covered (danger)
0.00%
0 / 47
0.00% covered (danger)
0.00%
0 / 3
380
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
 handle
0.00% covered (danger)
0.00%
0 / 39
0.00% covered (danger)
0.00%
0 / 1
306
 isRecognizedSpanWrapper
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2declare( strict_types = 1 );
3
4namespace Wikimedia\Parsoid\Html2Wt\DOMHandlers;
5
6use Wikimedia\Parsoid\Core\MediaStructure;
7use Wikimedia\Parsoid\DOM\Element;
8use Wikimedia\Parsoid\DOM\Node;
9use Wikimedia\Parsoid\DOM\Text;
10use Wikimedia\Parsoid\Html2Wt\LinkHandlerUtils;
11use Wikimedia\Parsoid\Html2Wt\SerializerState;
12use Wikimedia\Parsoid\Html2Wt\WTSUtils;
13use Wikimedia\Parsoid\Utils\DiffDOMUtils;
14use Wikimedia\Parsoid\Utils\DOMCompat;
15use Wikimedia\Parsoid\Utils\DOMDataUtils;
16use Wikimedia\Parsoid\Utils\DOMUtils;
17use Wikimedia\Parsoid\Utils\Utils;
18use Wikimedia\Parsoid\Utils\WTUtils;
19
20class SpanHandler extends DOMHandler {
21
22    public function __construct() {
23        parent::__construct( false );
24    }
25
26    /** @inheritDoc */
27    public function handle(
28        Element $node, SerializerState $state, bool $wrapperUnmodified = false
29    ): ?Node {
30        $env = $state->getEnv();
31        $dp = DOMDataUtils::getDataParsoid( $node );
32        if ( self::isRecognizedSpanWrapper( $node ) ) {
33            if ( DOMUtils::hasTypeOf( $node, 'mw:Nowiki' ) ) {
34                $ext = $env->getSiteConfig()->getExtTagImpl( 'nowiki' );
35                $src = $ext->domToWikitext( $state->extApi, $node, $wrapperUnmodified );
36                $state->singleLineContext->disable();
37                $state->serializer->emitWikitext( $src, $node );
38                $state->singleLineContext->pop();
39            } elseif ( WTUtils::isInlineMedia( $node ) ) {
40                LinkHandlerUtils::figureHandler(
41                    $state, $node, MediaStructure::parse( $node )
42                );
43            } elseif (
44                DOMUtils::hasTypeOf( $node, 'mw:Entity' ) &&
45                DiffDOMUtils::hasNChildren( $node, 1 )
46            ) {
47                $contentSrc = ( $node->textContent != '' ) ? $node->textContent
48                    : DOMCompat::getInnerHTML( $node );
49                // handle a new mw:Entity (not handled by selser) by
50                // serializing its children
51                if ( isset( $dp->src ) && $contentSrc === ( $dp->srcContent ?? null ) ) {
52                    $state->serializer->emitWikitext( $dp->src, $node );
53                } elseif ( $node->firstChild instanceof Text ) {
54                    $state->emitChunk(
55                        Utils::entityEncodeAll( $node->firstChild->nodeValue ),
56                        $node->firstChild );
57                } else {
58                    $state->serializeChildren( $node );
59                }
60            } elseif ( DOMUtils::hasTypeOf( $node, 'mw:DisplaySpace' ) ) {
61                // FIXME(T254501): Throw an UnreachableException here instead
62                $state->emitChunk( ' ', $node );
63            } elseif ( DOMUtils::matchTypeOf( $node, '#^mw:Placeholder(/|$)#' ) ) {
64                if ( isset( $dp->src ) ) {
65                    $this->emitPlaceholderSrc( $node, $state );
66                    return $node->nextSibling;
67                } else {
68                    ( new FallbackHTMLHandler )->handle( $node, $state );
69                }
70            }
71        } elseif ( $node->hasAttribute( 'data-mw-selser-wrapper' ) ) {
72            $state->serializeChildren( $node );
73        } else {
74            if ( !empty( $dp->misnested ) && ( $dp->stx ?? null ) !== 'html'
75                && !WTSUtils::hasNonIgnorableAttributes( $node )
76            ) {
77                // Discard span wrappers added to flag misnested content.
78                // Warn since selser should have reused source.
79                $env->log( 'warn', 'Serializing misnested content: ' . DOMCompat::getOuterHTML( $node ) );
80                $state->serializeChildren( $node );
81            } else {
82                // Fall back to plain HTML serialization for spans created
83                // by the editor.
84                ( new FallbackHTMLHandler )->handle( $node, $state );
85            }
86        }
87        return $node->nextSibling;
88    }
89
90    private static function isRecognizedSpanWrapper( Element $node ): ?string {
91        return DOMUtils::matchTypeOf(
92            $node,
93            // FIXME(T254501): Remove mw:DisplaySpace
94            '#^mw:('
95                . 'Nowiki|Entity|DisplaySpace|Placeholder(/\w+)?'
96                . '|File(/(Frameless|Frame|Thumb))?'
97                . ')$#'
98        );
99    }
100}