Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
93.90% covered (success)
93.90%
77 / 82
50.00% covered (danger)
50.00%
2 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
InSelect
93.90% covered (success)
93.90%
77 / 82
50.00% covered (danger)
50.00%
2 / 4
30.20
0.00% covered (danger)
0.00%
0 / 1
 characters
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
1
 startTag
93.02% covered (success)
93.02%
40 / 43
0.00% covered (danger)
0.00%
0 / 1
16.09
 endTag
93.75% covered (success)
93.75%
30 / 32
0.00% covered (danger)
0.00%
0 / 1
12.04
 endDocument
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace Wikimedia\RemexHtml\TreeBuilder;
4
5use Wikimedia\RemexHtml\Tokenizer\Attributes;
6
7/**
8 * The "in select" insertion mode
9 */
10class InSelect extends InsertionMode {
11    public function characters( $text, $start, $length, $sourceStart, $sourceLength ) {
12        $this->stripNulls(
13            function ( $text, $start, $length, $sourceStart, $sourceLength ) {
14                $this->builder->insertCharacters( $text, $start, $length,
15                    $sourceStart, $sourceLength );
16            },
17            $text, $start, $length, $sourceStart, $sourceLength );
18    }
19
20    public function startTag( $name, Attributes $attrs, $selfClose, $sourceStart, $sourceLength ) {
21        $builder = $this->builder;
22        $stack = $builder->stack;
23        $dispatcher = $this->dispatcher;
24
25        switch ( $name ) {
26            case 'html':
27                $dispatcher->inBody->startTag( $name, $attrs, $selfClose, $sourceStart, $sourceLength );
28                break;
29
30            case 'option':
31                if ( $stack->current->htmlName === 'option' ) {
32                    $builder->pop( $sourceStart, 0 );
33                }
34                $builder->insertElement( 'option', $attrs, false, $sourceStart, $sourceLength );
35                break;
36
37            case 'optgroup':
38                if ( $stack->current->htmlName === 'option' ) {
39                    $builder->pop( $sourceStart, 0 );
40                }
41                if ( $stack->current->htmlName === 'optgroup' ) {
42                    $builder->pop( $sourceStart, 0 );
43                }
44                $builder->insertElement( 'optgroup', $attrs, false, $sourceStart, $sourceLength );
45                break;
46
47            case 'select':
48                if ( !$stack->isInSelectScope( 'select' ) ) {
49                    $builder->error( "<select> found in select mode but no select element is in " .
50                        "scope, ignoring", $sourceStart );
51                    return;
52                }
53
54                $builder->error( "<select> found inside a select element", $sourceStart );
55                $builder->popAllUpToName( 'select', $sourceStart, $sourceLength );
56                $dispatcher->reset();
57                break;
58
59            case 'input':
60            case 'keygen':
61            case 'textarea':
62                $builder->error( "<$name> found inside a select element", $sourceStart );
63                if ( !$stack->isInSelectScope( 'select' ) ) {
64                    // Ignore
65                    return;
66                }
67                $builder->popAllUpToName( 'select', $sourceStart, 0 );
68                $dispatcher->reset()
69                    ->startTag( $name, $attrs, $selfClose, $sourceStart, $sourceLength );
70                break;
71
72            case 'script':
73            case 'template':
74                $dispatcher->inHead->startTag( $name, $attrs, $selfClose,
75                    $sourceStart, $sourceLength );
76                break;
77
78            default:
79                $builder->error( "<$name> found inside a select element, ignoring", $sourceStart );
80        }
81    }
82
83    public function endTag( $name, $sourceStart, $sourceLength ) {
84        $builder = $this->builder;
85        $stack = $builder->stack;
86        $dispatcher = $this->dispatcher;
87
88        switch ( $name ) {
89            case 'optgroup':
90                if ( $stack->current->htmlName === 'option' ) {
91                    $penultimate = $stack->item( $stack->length() - 2 );
92                    if ( $penultimate && $penultimate->htmlName === 'optgroup' ) {
93                        $builder->pop( $sourceStart, 0 );
94                    }
95                }
96                if ( $stack->current->htmlName !== 'optgroup' ) {
97                    $builder->error( "unexpected </optgroup>, ignoring", $sourceStart );
98                    return;
99                }
100                $builder->pop( $sourceStart, $sourceLength );
101                break;
102
103            case 'option':
104                if ( $stack->current->htmlName !== 'option' ) {
105                    $builder->error( "unexpected </option>, ignoring", $sourceStart );
106                    return;
107                }
108                $builder->pop( $sourceStart, $sourceLength );
109                break;
110
111            case 'select':
112                if ( !$stack->isInSelectScope( 'select' ) ) {
113                    $builder->error( "</select> found but the select element is " .
114                        "not in scope", $sourceStart );
115                    return;
116                }
117                $builder->popAllUpToName( 'select', $sourceStart, $sourceLength );
118                $dispatcher->reset();
119                break;
120
121            case 'template':
122                $dispatcher->inHead->endTag( $name, $sourceStart, $sourceLength );
123                break;
124
125            default:
126                $builder->error( "unexpected </$name> in select, ignoring", $sourceStart );
127                break;
128        }
129    }
130
131    public function endDocument( $pos ) {
132        $this->dispatcher->inBody->endDocument( $pos );
133    }
134}