protected function handleAttribsAndClose( $state, $tagName, $isEndTag, $startPos ) {
		$attribStartPos = $this->pos;
		$attribs = $this->consumeAttribs();
		$pos = $this->pos;

		// Literal characters are emitted on EOF or "anything else" from the
		// end tag substates of the text states.
		// (spec ref 8.2.4 sections 11-19, 25-27)
		$isDataState = $state === self::STATE_DATA;
		$isLiteral = $attribStartPos === $pos && !$isDataState;

		if ( $pos >= $this->length ) {
			$this->error( 'unexpected end of file inside tag' );
			if ( $isLiteral ) {
				$this->listener->characters( $this->text,
					$startPos, $this->length - $startPos,
					$startPos, $this->length - $startPos );
			}
			return self::STATE_EOF;
		}
		if ( $isEndTag && !$this->ignoreErrors && $attribs->count() ) {
			$this->error( 'end tag has an attribute' );
		}

		if ( $this->text[$pos] === '/' && $this->text[$pos + 1] === '>' ) {
			$pos += 2;
			$selfClose = true;
		} elseif ( $this->text[$pos] === '>' ) {
			$pos++;
			$selfClose = false;
		} elseif ( $isLiteral ) {
			$this->listener->characters( $this->text,
				$startPos, $attribStartPos - $startPos,
				$startPos, $attribStartPos - $startPos );
			return $state;
		} else {
			$this->fatal( 'failed to find an already-matched ">"' );
		}
		$this->pos = $pos;
		if ( $isEndTag ) {
			if ( $selfClose ) {
				$this->error( 'self-closing end tag' );
			}
			$this->listener->endTag( $tagName, $startPos, $pos - $startPos );
		} else {
			$this->listener->startTag( $tagName, $attribs, $selfClose,
				$startPos, $pos - $startPos );
		}
		return self::STATE_DATA;
	}