@@ -2,12 +2,11 @@ import { Tokenizer, TokenizerMode } from '../tokenizer/index.js';
22import { OpenElementStack } from './open-element-stack.js' ;
33import { FormattingElementList , ElementEntry , EntryType } from './formatting-element-list.js' ;
44import { LocationInfoParserMixin } from '../extensions/location-info/parser-mixin.js' ;
5- import { ErrorReportingParserMixin } from '../extensions/error-reporting/parser-mixin.js' ;
65import { Mixin } from '../utils/mixin.js' ;
76import * as defaultTreeAdapter from '../tree-adapters/default.js' ;
87import * as doctype from '../common/doctype.js' ;
98import * as foreignContent from '../common/foreign-content.js' ;
10- import { ERR } from '../common/error-codes.js' ;
9+ import { ERR , ParserErrorHandler } from '../common/error-codes.js' ;
1110import * as unicode from '../common/unicode.js' ;
1211import {
1312 TAG_NAMES as $ ,
@@ -18,7 +17,6 @@ import {
1817 isNumberedHeader ,
1918} from '../common/html.js' ;
2019import type { TreeAdapter , TreeAdapterTypeMap } from '../tree-adapters/interface.js' ;
21- import type { ParserError } from '../common/error-codes.js' ;
2220import {
2321 TokenType ,
2422 getTokenAttr ,
@@ -89,6 +87,15 @@ const TEMPLATE_INSERTION_MODE_SWITCH_MAP = new Map<string, InsertionMode>([
8987 [ $ . TH , InsertionMode . IN_ROW ] ,
9088] ) ;
9189
90+ const BASE_LOC = {
91+ startLine : - 1 ,
92+ startCol : - 1 ,
93+ startOffset : - 1 ,
94+ endLine : - 1 ,
95+ endCol : - 1 ,
96+ endOffset : - 1 ,
97+ } ;
98+
9299const TABLE_STRUCTURE_TAGS = new Set < string > ( [ $ . TABLE , $ . TBODY , $ . TFOOT , $ . THEAD , $ . TR ] ) ;
93100
94101export interface ParserOptions < T extends TreeAdapterTypeMap > {
@@ -124,14 +131,15 @@ export interface ParserOptions<T extends TreeAdapterTypeMap> {
124131 *
125132 * @default `null`
126133 */
127- onParseError ?: ( ( err : ParserError ) => void ) | null ;
134+ onParseError ?: ParserErrorHandler | null ;
128135}
129136
130137//Parser
131138export class Parser < T extends TreeAdapterTypeMap > {
132139 options : ParserOptions < T > ;
133140 treeAdapter : TreeAdapter < T > ;
134141 pendingScript : null | T [ 'element' ] ;
142+ private onParseError : ParserErrorHandler | null ;
135143
136144 constructor ( options ?: ParserOptions < T > ) {
137145 this . options = {
@@ -145,8 +153,9 @@ export class Parser<T extends TreeAdapterTypeMap> {
145153 this . treeAdapter = this . options . treeAdapter ! ;
146154 this . pendingScript = null ;
147155
156+ this . onParseError = this . options . onParseError ?? null ;
157+
148158 if ( this . options . onParseError ) {
149- Mixin . install ( this , ErrorReportingParserMixin as any , { onParseError : this . options . onParseError } ) ;
150159 this . options . sourceCodeLocationInfo = true ;
151160 }
152161
@@ -255,8 +264,21 @@ export class Parser<T extends TreeAdapterTypeMap> {
255264 }
256265
257266 //Errors
258- _err ( _err : ERR , _opts ?: { beforeToken : boolean } ) {
259- // NOTE: err reporting is noop by default. Enabled by mixin.
267+ _err ( token : Token , code : ERR , beforeToken ?: boolean ) {
268+ if ( ! this . onParseError ) return ;
269+
270+ const loc = token . location ?? BASE_LOC ;
271+ const err = {
272+ code,
273+ startLine : loc . startLine ,
274+ startCol : loc . startCol ,
275+ startOffset : loc . startOffset ,
276+ endLine : beforeToken ? loc . startLine : loc . endLine ,
277+ endCol : beforeToken ? loc . startCol : loc . endCol ,
278+ endOffset : beforeToken ? loc . startOffset : loc . endOffset ,
279+ } ;
280+
281+ this . onParseError ( err ) ;
260282 }
261283
262284 //Parsing loop
@@ -572,7 +594,7 @@ export class Parser<T extends TreeAdapterTypeMap> {
572594 }
573595
574596 if ( token . type === TokenType . START_TAG && token . selfClosing && ! token . ackSelfClosing ) {
575- this . _err ( ERR . nonVoidHtmlElementStartTagWithTrailingSolidus ) ;
597+ this . _err ( token , ERR . nonVoidHtmlElementStartTagWithTrailingSolidus ) ;
576598 }
577599 }
578600
@@ -911,8 +933,8 @@ function callAdoptionAgency<T extends TreeAdapterTypeMap>(p: Parser<T>, token: T
911933//Generic token handlers
912934//------------------------------------------------------------------
913935
914- function misplacedDoctype < T extends TreeAdapterTypeMap > ( p : Parser < T > ) {
915- p . _err ( ERR . misplacedDoctype ) ;
936+ function misplacedDoctype < T extends TreeAdapterTypeMap > ( p : Parser < T > , token : DoctypeToken ) {
937+ p . _err ( token , ERR . misplacedDoctype ) ;
916938}
917939
918940function appendComment < T extends TreeAdapterTypeMap > ( p : Parser < T > , token : CommentToken ) {
@@ -949,7 +971,7 @@ function doctypeInInitialMode<T extends TreeAdapterTypeMap>(p: Parser<T>, token:
949971 const mode = token . forceQuirks ? DOCUMENT_MODE . QUIRKS : doctype . getDocumentMode ( token ) ;
950972
951973 if ( ! doctype . isConforming ( token ) ) {
952- p . _err ( ERR . nonConformingDoctype ) ;
974+ p . _err ( token , ERR . nonConformingDoctype ) ;
953975 }
954976
955977 p . treeAdapter . setDocumentMode ( p . document , mode ) ;
@@ -958,7 +980,7 @@ function doctypeInInitialMode<T extends TreeAdapterTypeMap>(p: Parser<T>, token:
958980}
959981
960982function tokenInInitialMode < T extends TreeAdapterTypeMap > ( p : Parser < T > , token : Token ) {
961- p . _err ( ERR . missingDoctype , { beforeToken : true } ) ;
983+ p . _err ( token , ERR . missingDoctype , true ) ;
962984 p . treeAdapter . setDocumentMode ( p . document , DOCUMENT_MODE . QUIRKS ) ;
963985 p . insertionMode = InsertionMode . BEFORE_HTML ;
964986 p . _processToken ( token ) ;
@@ -1009,7 +1031,7 @@ function modeBeforeHead<T extends TreeAdapterTypeMap>(p: Parser<T>, token: Token
10091031 } else if ( token . type === TokenType . COMMENT ) {
10101032 appendComment ( p , token ) ;
10111033 } else if ( token . type === TokenType . DOCTYPE ) {
1012- misplacedDoctype ( p ) ;
1034+ misplacedDoctype ( p , token ) ;
10131035 } else if ( token . type === TokenType . START_TAG ) {
10141036 startTagBeforeHead ( p , token ) ;
10151037 } else if ( token . type === TokenType . END_TAG ) {
@@ -1037,7 +1059,7 @@ function endTagBeforeHead<T extends TreeAdapterTypeMap>(p: Parser<T>, token: Tag
10371059 if ( tn === $ . HEAD || tn === $ . BODY || tn === $ . HTML || tn === $ . BR ) {
10381060 tokenBeforeHead ( p , token ) ;
10391061 } else {
1040- p . _err ( ERR . endTagWithoutMatchingOpenElement ) ;
1062+ p . _err ( token , ERR . endTagWithoutMatchingOpenElement ) ;
10411063 }
10421064}
10431065
@@ -1058,7 +1080,7 @@ function modeInHead<T extends TreeAdapterTypeMap>(p: Parser<T>, token: Token) {
10581080 } else if ( token . type === TokenType . COMMENT ) {
10591081 appendComment ( p , token ) ;
10601082 } else if ( token . type === TokenType . DOCTYPE ) {
1061- misplacedDoctype ( p ) ;
1083+ misplacedDoctype ( p , token ) ;
10621084 } else if ( token . type === TokenType . START_TAG ) {
10631085 startTagInHead ( p , token ) ;
10641086 } else if ( token . type === TokenType . END_TAG ) {
@@ -1094,7 +1116,7 @@ function startTagInHead<T extends TreeAdapterTypeMap>(p: Parser<T>, token: TagTo
10941116 p . insertionMode = InsertionMode . IN_TEMPLATE ;
10951117 p . tmplInsertionModeStack . unshift ( InsertionMode . IN_TEMPLATE ) ;
10961118 } else if ( tn === $ . HEAD ) {
1097- p . _err ( ERR . misplacedStartTagForHeadElement ) ;
1119+ p . _err ( token , ERR . misplacedStartTagForHeadElement ) ;
10981120 } else {
10991121 tokenInHead ( p , token ) ;
11001122 }
@@ -1113,18 +1135,18 @@ function endTagInHead<T extends TreeAdapterTypeMap>(p: Parser<T>, token: TagToke
11131135 p . openElements . generateImpliedEndTagsThoroughly ( ) ;
11141136
11151137 if ( p . openElements . currentTagName !== $ . TEMPLATE ) {
1116- p . _err ( ERR . closingOfElementWithOpenChildElements ) ;
1138+ p . _err ( token , ERR . closingOfElementWithOpenChildElements ) ;
11171139 }
11181140
11191141 p . openElements . popUntilTagNamePopped ( $ . TEMPLATE ) ;
11201142 p . activeFormattingElements . clearToLastMarker ( ) ;
11211143 p . tmplInsertionModeStack . shift ( ) ;
11221144 p . _resetInsertionMode ( ) ;
11231145 } else {
1124- p . _err ( ERR . endTagWithoutMatchingOpenElement ) ;
1146+ p . _err ( token , ERR . endTagWithoutMatchingOpenElement ) ;
11251147 }
11261148 } else {
1127- p . _err ( ERR . endTagWithoutMatchingOpenElement ) ;
1149+ p . _err ( token , ERR . endTagWithoutMatchingOpenElement ) ;
11281150 }
11291151}
11301152
@@ -1144,7 +1166,7 @@ function modeInHeadNoScript<T extends TreeAdapterTypeMap>(p: Parser<T>, token: T
11441166 } else if ( token . type === TokenType . COMMENT ) {
11451167 appendComment ( p , token ) ;
11461168 } else if ( token . type === TokenType . DOCTYPE ) {
1147- misplacedDoctype ( p ) ;
1169+ misplacedDoctype ( p , token ) ;
11481170 } else if ( token . type === TokenType . START_TAG ) {
11491171 startTagInHeadNoScript ( p , token ) ;
11501172 } else if ( token . type === TokenType . END_TAG ) {
@@ -1168,7 +1190,7 @@ function startTagInHeadNoScript<T extends TreeAdapterTypeMap>(p: Parser<T>, toke
11681190 ) {
11691191 startTagInHead ( p , token ) ;
11701192 } else if ( tn === $ . NOSCRIPT ) {
1171- p . _err ( ERR . nestedNoscriptInHead ) ;
1193+ p . _err ( token , ERR . nestedNoscriptInHead ) ;
11721194 } else {
11731195 tokenInHeadNoScript ( p , token ) ;
11741196 }
@@ -1183,14 +1205,14 @@ function endTagInHeadNoScript<T extends TreeAdapterTypeMap>(p: Parser<T>, token:
11831205 } else if ( tn === $ . BR ) {
11841206 tokenInHeadNoScript ( p , token ) ;
11851207 } else {
1186- p . _err ( ERR . endTagWithoutMatchingOpenElement ) ;
1208+ p . _err ( token , ERR . endTagWithoutMatchingOpenElement ) ;
11871209 }
11881210}
11891211
11901212function tokenInHeadNoScript < T extends TreeAdapterTypeMap > ( p : Parser < T > , token : Token ) {
11911213 const errCode = token . type === TokenType . EOF ? ERR . openElementsLeftAfterEof : ERR . disallowedContentInNoscriptInHead ;
11921214
1193- p . _err ( errCode ) ;
1215+ p . _err ( token , errCode ) ;
11941216 p . openElements . pop ( ) ;
11951217 p . insertionMode = InsertionMode . IN_HEAD ;
11961218 p . _processToken ( token ) ;
@@ -1206,7 +1228,7 @@ function modeAfterHead<T extends TreeAdapterTypeMap>(p: Parser<T>, token: Token)
12061228 } else if ( token . type === TokenType . COMMENT ) {
12071229 appendComment ( p , token ) ;
12081230 } else if ( token . type === TokenType . DOCTYPE ) {
1209- misplacedDoctype ( p ) ;
1231+ misplacedDoctype ( p , token ) ;
12101232 } else if ( token . type === TokenType . START_TAG ) {
12111233 startTagAfterHead ( p , token ) ;
12121234 } else if ( token . type === TokenType . END_TAG ) {
@@ -1240,12 +1262,12 @@ function startTagAfterHead<T extends TreeAdapterTypeMap>(p: Parser<T>, token: Ta
12401262 p . _insertElement ( token , NS . HTML ) ;
12411263 p . insertionMode = InsertionMode . IN_FRAMESET ;
12421264 } else if ( ABANDONED_HEAD_ELEMENT_CHILDS . has ( tn ) ) {
1243- p . _err ( ERR . abandonedHeadElementChild ) ;
1265+ p . _err ( token , ERR . abandonedHeadElementChild ) ;
12441266 p . openElements . push ( p . headElement ! ) ;
12451267 startTagInHead ( p , token ) ;
12461268 p . openElements . remove ( p . headElement ! ) ;
12471269 } else if ( tn === $ . HEAD ) {
1248- p . _err ( ERR . misplacedStartTagForHeadElement ) ;
1270+ p . _err ( token , ERR . misplacedStartTagForHeadElement ) ;
12491271 } else {
12501272 tokenAfterHead ( p , token ) ;
12511273 }
@@ -1259,7 +1281,7 @@ function endTagAfterHead<T extends TreeAdapterTypeMap>(p: Parser<T>, token: TagT
12591281 } else if ( tn === $ . TEMPLATE ) {
12601282 endTagInHead ( p , token ) ;
12611283 } else {
1262- p . _err ( ERR . endTagWithoutMatchingOpenElement ) ;
1284+ p . _err ( token , ERR . endTagWithoutMatchingOpenElement ) ;
12631285 }
12641286}
12651287
@@ -2126,7 +2148,7 @@ function endTagInText<T extends TreeAdapterTypeMap>(p: Parser<T>, token: TagToke
21262148}
21272149
21282150function eofInText < T extends TreeAdapterTypeMap > ( p : Parser < T > , token : Token ) {
2129- p . _err ( ERR . eofInElementThatCanContainOnlyText ) ;
2151+ p . _err ( token , ERR . eofInElementThatCanContainOnlyText ) ;
21302152 p . openElements . pop ( ) ;
21312153 p . insertionMode = p . originalInsertionMode ;
21322154 p . _processToken ( token ) ;
0 commit comments