]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - vendor/wikimedia/remex-html/RemexHtml/TreeBuilder/InForeignContent.php
MediaWiki 1.30.2
[autoinstallsdev/mediawiki.git] / vendor / wikimedia / remex-html / RemexHtml / TreeBuilder / InForeignContent.php
1 <?php
2
3 namespace RemexHtml\TreeBuilder;
4 use RemexHtml\HTMLData;
5 use RemexHtml\Tokenizer\Attributes;
6
7 /**
8  * The rules for parsing tokens in foreign content.
9  *
10  * This is not referred to as an insertion mode in the spec, but is
11  * sufficiently similar to one that we can inherit from InsertionMode here.
12  */
13 class InForeignContent extends InsertionMode {
14         /**
15          * The list of tag names which unconditionally generate a parse error when
16          * seen in foreign content.
17          * @var bool[string]
18          */
19         private static $notAllowed = [
20                 'b' => true,
21                 'big' => true,
22                 'blockquote' => true,
23                 'body' => true,
24                 'br' => true,
25                 'center' => true,
26                 'code' => true,
27                 'dd' => true,
28                 'div' => true,
29                 'dl' => true,
30                 'dt' => true,
31                 'em' => true,
32                 'embed' => true,
33                 'h1' => true,
34                 'h2' => true,
35                 'h3' => true,
36                 'h4' => true,
37                 'h5' => true,
38                 'h6' => true,
39                 'head' => true,
40                 'hr' => true,
41                 'i' => true,
42                 'img' => true,
43                 'li' => true,
44                 'listing' => true,
45                 'menu' => true,
46                 'meta' => true,
47                 'nobr' => true,
48                 'ol' => true,
49                 'p' => true,
50                 'pre' => true,
51                 'ruby' => true,
52                 's' => true,
53                 'small' => true,
54                 'span' => true,
55                 'strong' => true,
56                 'strike' => true,
57                 'sub' => true,
58                 'sup' => true,
59                 'table' => true,
60                 'tt' => true,
61                 'u' => true,
62                 'ul' => true,
63                 'var' => true,
64         ];
65
66         /**
67          * The table for correcting the tag names of SVG elements, given in the
68          * "Any other start tag" section of the spec.
69          */
70         private static $svgElementCase = [
71                 'altglyph' => 'altGlyph',
72                 'altglyphdef' => 'altGlyphDef',
73                 'altglyphitem' => 'altGlyphItem',
74                 'animatecolor' => 'animateColor',
75                 'animatemotion' => 'animateMotion',
76                 'animatetransform' => 'animateTransform',
77                 'clippath' => 'clipPath',
78                 'feblend' => 'feBlend',
79                 'fecolormatrix' => 'feColorMatrix',
80                 'fecomponenttransfer' => 'feComponentTransfer',
81                 'fecomposite' => 'feComposite',
82                 'feconvolvematrix' => 'feConvolveMatrix',
83                 'fediffuselighting' => 'feDiffuseLighting',
84                 'fedisplacementmap' => 'feDisplacementMap',
85                 'fedistantlight' => 'feDistantLight',
86                 'fedropshadow' => 'feDropShadow',
87                 'feflood' => 'feFlood',
88                 'fefunca' => 'feFuncA',
89                 'fefuncb' => 'feFuncB',
90                 'fefuncg' => 'feFuncG',
91                 'fefuncr' => 'feFuncR',
92                 'fegaussianblur' => 'feGaussianBlur',
93                 'feimage' => 'feImage',
94                 'femerge' => 'feMerge',
95                 'femergenode' => 'feMergeNode',
96                 'femorphology' => 'feMorphology',
97                 'feoffset' => 'feOffset',
98                 'fepointlight' => 'fePointLight',
99                 'fespecularlighting' => 'feSpecularLighting',
100                 'fespotlight' => 'feSpotLight',
101                 'fetile' => 'feTile',
102                 'feturbulence' => 'feTurbulence',
103                 'foreignobject' => 'foreignObject',
104                 'glyphref' => 'glyphRef',
105                 'lineargradient' => 'linearGradient',
106                 'radialgradient' => 'radialGradient',
107                 'textpath' => 'textPath',
108         ];
109
110         public function characters( $text, $start, $length, $sourceStart, $sourceLength ) {
111                 $builder = $this->builder;
112
113                 while ( $length ) {
114                         $normalLength = strcspn( $text, "\0\t\n\f\r ", $start, $length );
115                         if ( $normalLength ) {
116                                 $builder->framesetOK = false;
117                                 $builder->insertCharacters( $text, $start, $normalLength,
118                                         $sourceStart, $sourceLength );
119                         }
120                         $start += $normalLength;
121                         $length -= $normalLength;
122                         $sourceStart += $normalLength;
123                         $sourceLength -= $normalLength;
124                         if ( !$length ) {
125                                 break;
126                         }
127
128                         $char = $text[$start];
129                         if ( $char === "\0" ) {
130                                 $builder->error( "replaced null character", $sourceStart );
131                                 $builder->insertCharacters( "\xef\xbf\xbd", 0, 3, $sourceStart, $sourceLength );
132                                 $start++;
133                                 $length--;
134                                 $sourceStart++;
135                                 $sourceLength--;
136                         } else {
137                                 // Whitespace
138                                 $wsLength = strspn( $text, "\t\n\f\r ", $start, $length );
139                                 $builder->insertCharacters( $text, $start, $wsLength, $sourceStart, $wsLength );
140                                 $start += $wsLength;
141                                 $length -= $wsLength;
142                                 $sourceStart += $wsLength;
143                                 $sourceLength -= $wsLength;
144                         }
145                 }
146         }
147
148         private function isIntegrationPoint( Element $element ) {
149                 return $element->namespace === HTMLData::NS_HTML
150                         || $element->isMathmlTextIntegration()
151                         || $element->isHtmlIntegration();
152         }
153
154         public function startTag( $name, Attributes $attrs, $selfClose, $sourceStart, $sourceLength ) {
155                 $builder = $this->builder;
156                 $stack = $builder->stack;
157                 $dispatcher = $this->dispatcher;
158
159                 if ( isset( self::$notAllowed[$name] ) ) {
160                         $allowed = false;
161                 } elseif ( $name === 'font' && (
162                         isset( $attrs['color'] ) || isset( $attrs['face'] ) || isset( $attrs['size'] ) )
163                 ) {
164                         $allowed = false;
165                 } else {
166                         $allowed = true;
167                 }
168
169                 if ( !$allowed ) {
170                         $builder->error( "unexpected <$name> tag in foreign content", $sourceStart );
171                         if ( !$builder->isFragment ) {
172                                 do {
173                                         $builder->pop( $sourceStart, 0 );
174                                 } while ( $stack->current && !$this->isIntegrationPoint( $stack->current ) );
175                                 $dispatcher->startTag( $name, $attrs, $selfClose, $sourceStart, $sourceLength );
176                                 return;
177                         }
178                 }
179
180                 $acnNs = $builder->adjustedCurrentNode()->namespace;
181                 if ( $acnNs === HTMLData::NS_MATHML ) {
182                         $attrs = new ForeignAttributes( $attrs, 'math' );
183                 } elseif ( $acnNs === HTMLData::NS_SVG ) {
184                         $attrs = new ForeignAttributes( $attrs, 'svg' );
185                         if ( isset( self::$svgElementCase[$name] ) ) {
186                                 $name = self::$svgElementCase[$name];
187                         }
188                 } else {
189                         $attrs = new ForeignAttributes( $attrs, 'other' );
190                 }
191                 $dispatcher->ack = true;
192                 $builder->insertForeign( $acnNs, $name, $attrs, $selfClose, $sourceStart, $sourceLength );
193         }
194
195         public function endTag( $name, $sourceStart, $sourceLength ) {
196                 $builder = $this->builder;
197                 $stack = $builder->stack;
198                 $dispatcher = $this->dispatcher;
199
200                 $node = $stack->current;
201                 if ( strcasecmp( $node->name, $name ) !== 0 ) {
202                         $builder->error( "mismatched end tag in foreign content", $sourceStart );
203                 }
204                 for ( $idx = $stack->length() - 1; $idx > 0; $idx-- ) {
205                         if ( strcasecmp( $node->name, $name ) === 0 ) {
206                                 $builder->popAllUpToElement( $node, $sourceStart, $sourceLength );
207                                 break;
208                         }
209                         $node = $stack->item( $idx - 1 );
210                         if ( $node->namespace === HTMLData::NS_HTML ) {
211                                 $dispatcher->getHandler()->endTag( $name, $sourceStart, $sourceLength );
212                                 break;
213                         }
214                 }
215         }
216
217         public function endDocument( $pos ) {
218                 throw new TreeBuilderError( "unspecified, presumed unreachable" );
219         }
220 }