*/ private static $notAllowed = [ 'b' => true, 'big' => true, 'blockquote' => true, 'body' => true, 'br' => true, 'center' => true, 'code' => true, 'dd' => true, 'div' => true, 'dl' => true, 'dt' => true, 'em' => true, 'embed' => true, 'h1' => true, 'h2' => true, 'h3' => true, 'h4' => true, 'h5' => true, 'h6' => true, 'head' => true, 'hr' => true, 'i' => true, 'img' => true, 'li' => true, 'listing' => true, 'menu' => true, 'meta' => true, 'nobr' => true, 'ol' => true, 'p' => true, 'pre' => true, 'ruby' => true, 's' => true, 'small' => true, 'span' => true, 'strong' => true, 'strike' => true, 'sub' => true, 'sup' => true, 'table' => true, 'tt' => true, 'u' => true, 'ul' => true, 'var' => true, ]; /** * The table for correcting the tag names of SVG elements, given in the * "Any other start tag" section of the spec. */ private static $svgElementCase = [ 'altglyph' => 'altGlyph', 'altglyphdef' => 'altGlyphDef', 'altglyphitem' => 'altGlyphItem', 'animatecolor' => 'animateColor', 'animatemotion' => 'animateMotion', 'animatetransform' => 'animateTransform', 'clippath' => 'clipPath', 'feblend' => 'feBlend', 'fecolormatrix' => 'feColorMatrix', 'fecomponenttransfer' => 'feComponentTransfer', 'fecomposite' => 'feComposite', 'feconvolvematrix' => 'feConvolveMatrix', 'fediffuselighting' => 'feDiffuseLighting', 'fedisplacementmap' => 'feDisplacementMap', 'fedistantlight' => 'feDistantLight', 'fedropshadow' => 'feDropShadow', 'feflood' => 'feFlood', 'fefunca' => 'feFuncA', 'fefuncb' => 'feFuncB', 'fefuncg' => 'feFuncG', 'fefuncr' => 'feFuncR', 'fegaussianblur' => 'feGaussianBlur', 'feimage' => 'feImage', 'femerge' => 'feMerge', 'femergenode' => 'feMergeNode', 'femorphology' => 'feMorphology', 'feoffset' => 'feOffset', 'fepointlight' => 'fePointLight', 'fespecularlighting' => 'feSpecularLighting', 'fespotlight' => 'feSpotLight', 'fetile' => 'feTile', 'feturbulence' => 'feTurbulence', 'foreignobject' => 'foreignObject', 'glyphref' => 'glyphRef', 'lineargradient' => 'linearGradient', 'radialgradient' => 'radialGradient', 'textpath' => 'textPath', ]; public function characters( $text, $start, $length, $sourceStart, $sourceLength ) { $builder = $this->builder; while ( $length ) { $normalLength = strcspn( $text, "\0\t\n\f\r ", $start, $length ); if ( $normalLength ) { $builder->framesetOK = false; $builder->insertCharacters( $text, $start, $normalLength, $sourceStart, $sourceLength ); } $start += $normalLength; $length -= $normalLength; $sourceStart += $normalLength; $sourceLength -= $normalLength; if ( !$length ) { break; } $char = $text[$start]; if ( $char === "\0" ) { $builder->error( "replaced null character", $sourceStart ); $builder->insertCharacters( "\xef\xbf\xbd", 0, 3, $sourceStart, $sourceLength ); $start++; $length--; $sourceStart++; $sourceLength--; } else { // Whitespace $wsLength = strspn( $text, "\t\n\f\r ", $start, $length ); $builder->insertCharacters( $text, $start, $wsLength, $sourceStart, $wsLength ); $start += $wsLength; $length -= $wsLength; $sourceStart += $wsLength; $sourceLength -= $wsLength; } } } private function isIntegrationPoint( Element $element ) { return $element->namespace === HTMLData::NS_HTML || $element->isMathmlTextIntegration() || $element->isHtmlIntegration(); } public function startTag( $name, Attributes $attrs, $selfClose, $sourceStart, $sourceLength ) { $builder = $this->builder; $stack = $builder->stack; $dispatcher = $this->dispatcher; if ( isset( self::$notAllowed[$name] ) ) { $allowed = false; } elseif ( $name === 'font' && ( isset( $attrs['color'] ) || isset( $attrs['face'] ) || isset( $attrs['size'] ) ) ) { $allowed = false; } else { $allowed = true; } if ( !$allowed ) { $builder->error( "unexpected <$name> tag in foreign content", $sourceStart ); if ( !$builder->isFragment ) { do { $builder->pop( $sourceStart, 0 ); } while ( $stack->current && !$this->isIntegrationPoint( $stack->current ) ); $dispatcher->startTag( $name, $attrs, $selfClose, $sourceStart, $sourceLength ); return; } } $acnNs = $builder->adjustedCurrentNode()->namespace; if ( $acnNs === HTMLData::NS_MATHML ) { $attrs = new ForeignAttributes( $attrs, 'math' ); } elseif ( $acnNs === HTMLData::NS_SVG ) { $attrs = new ForeignAttributes( $attrs, 'svg' ); $name = self::$svgElementCase[$name] ?? $name; } else { $attrs = new ForeignAttributes( $attrs, 'other' ); } $dispatcher->ack = true; $builder->insertForeign( $acnNs, $name, $attrs, $selfClose, $sourceStart, $sourceLength ); } public function endTag( $name, $sourceStart, $sourceLength ) { $builder = $this->builder; $stack = $builder->stack; $dispatcher = $this->dispatcher; $node = $stack->current; if ( strcasecmp( $node->name, $name ) !== 0 ) { $builder->error( "mismatched end tag in foreign content", $sourceStart ); } for ( $idx = $stack->length() - 1; $idx > 0; $idx-- ) { if ( strcasecmp( $node->name, $name ) === 0 ) { $builder->popAllUpToElement( $node, $sourceStart, $sourceLength ); break; } $node = $stack->item( $idx - 1 ); if ( $node->namespace === HTMLData::NS_HTML ) { $dispatcher->getHandler()->endTag( $name, $sourceStart, $sourceLength ); break; } } } public function endDocument( $pos ) { throw new TreeBuilderError( "unspecified, presumed unreachable" ); } }