@cjser/parse5 0.0.0-stage → 8.1.0-cjser.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2908 @@
1
+ import { Preprocessor } from './preprocessor.js';
2
+ import { CODE_POINTS as $, SEQUENCES as $$, REPLACEMENT_CHARACTER, isSurrogate, isUndefinedCodePoint, isControlCodePoint, } from '../common/unicode.js';
3
+ import { TokenType, getTokenAttr, } from '../common/token.js';
4
+ import { HtmlEntityDecoder, DecodingMode } from '@cjser/entities/decode';
5
+ import { ERR } from '../common/error-codes.js';
6
+ import { TAG_ID, getTagID } from '../common/html.js';
7
+ //States
8
+ var State;
9
+ (function (State) {
10
+ State[State["DATA"] = 0] = "DATA";
11
+ State[State["RCDATA"] = 1] = "RCDATA";
12
+ State[State["RAWTEXT"] = 2] = "RAWTEXT";
13
+ State[State["SCRIPT_DATA"] = 3] = "SCRIPT_DATA";
14
+ State[State["PLAINTEXT"] = 4] = "PLAINTEXT";
15
+ State[State["TAG_OPEN"] = 5] = "TAG_OPEN";
16
+ State[State["END_TAG_OPEN"] = 6] = "END_TAG_OPEN";
17
+ State[State["TAG_NAME"] = 7] = "TAG_NAME";
18
+ State[State["RCDATA_LESS_THAN_SIGN"] = 8] = "RCDATA_LESS_THAN_SIGN";
19
+ State[State["RCDATA_END_TAG_OPEN"] = 9] = "RCDATA_END_TAG_OPEN";
20
+ State[State["RCDATA_END_TAG_NAME"] = 10] = "RCDATA_END_TAG_NAME";
21
+ State[State["RAWTEXT_LESS_THAN_SIGN"] = 11] = "RAWTEXT_LESS_THAN_SIGN";
22
+ State[State["RAWTEXT_END_TAG_OPEN"] = 12] = "RAWTEXT_END_TAG_OPEN";
23
+ State[State["RAWTEXT_END_TAG_NAME"] = 13] = "RAWTEXT_END_TAG_NAME";
24
+ State[State["SCRIPT_DATA_LESS_THAN_SIGN"] = 14] = "SCRIPT_DATA_LESS_THAN_SIGN";
25
+ State[State["SCRIPT_DATA_END_TAG_OPEN"] = 15] = "SCRIPT_DATA_END_TAG_OPEN";
26
+ State[State["SCRIPT_DATA_END_TAG_NAME"] = 16] = "SCRIPT_DATA_END_TAG_NAME";
27
+ State[State["SCRIPT_DATA_ESCAPE_START"] = 17] = "SCRIPT_DATA_ESCAPE_START";
28
+ State[State["SCRIPT_DATA_ESCAPE_START_DASH"] = 18] = "SCRIPT_DATA_ESCAPE_START_DASH";
29
+ State[State["SCRIPT_DATA_ESCAPED"] = 19] = "SCRIPT_DATA_ESCAPED";
30
+ State[State["SCRIPT_DATA_ESCAPED_DASH"] = 20] = "SCRIPT_DATA_ESCAPED_DASH";
31
+ State[State["SCRIPT_DATA_ESCAPED_DASH_DASH"] = 21] = "SCRIPT_DATA_ESCAPED_DASH_DASH";
32
+ State[State["SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN"] = 22] = "SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN";
33
+ State[State["SCRIPT_DATA_ESCAPED_END_TAG_OPEN"] = 23] = "SCRIPT_DATA_ESCAPED_END_TAG_OPEN";
34
+ State[State["SCRIPT_DATA_ESCAPED_END_TAG_NAME"] = 24] = "SCRIPT_DATA_ESCAPED_END_TAG_NAME";
35
+ State[State["SCRIPT_DATA_DOUBLE_ESCAPE_START"] = 25] = "SCRIPT_DATA_DOUBLE_ESCAPE_START";
36
+ State[State["SCRIPT_DATA_DOUBLE_ESCAPED"] = 26] = "SCRIPT_DATA_DOUBLE_ESCAPED";
37
+ State[State["SCRIPT_DATA_DOUBLE_ESCAPED_DASH"] = 27] = "SCRIPT_DATA_DOUBLE_ESCAPED_DASH";
38
+ State[State["SCRIPT_DATA_DOUBLE_ESCAPED_DASH_DASH"] = 28] = "SCRIPT_DATA_DOUBLE_ESCAPED_DASH_DASH";
39
+ State[State["SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN"] = 29] = "SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN";
40
+ State[State["SCRIPT_DATA_DOUBLE_ESCAPE_END"] = 30] = "SCRIPT_DATA_DOUBLE_ESCAPE_END";
41
+ State[State["BEFORE_ATTRIBUTE_NAME"] = 31] = "BEFORE_ATTRIBUTE_NAME";
42
+ State[State["ATTRIBUTE_NAME"] = 32] = "ATTRIBUTE_NAME";
43
+ State[State["AFTER_ATTRIBUTE_NAME"] = 33] = "AFTER_ATTRIBUTE_NAME";
44
+ State[State["BEFORE_ATTRIBUTE_VALUE"] = 34] = "BEFORE_ATTRIBUTE_VALUE";
45
+ State[State["ATTRIBUTE_VALUE_DOUBLE_QUOTED"] = 35] = "ATTRIBUTE_VALUE_DOUBLE_QUOTED";
46
+ State[State["ATTRIBUTE_VALUE_SINGLE_QUOTED"] = 36] = "ATTRIBUTE_VALUE_SINGLE_QUOTED";
47
+ State[State["ATTRIBUTE_VALUE_UNQUOTED"] = 37] = "ATTRIBUTE_VALUE_UNQUOTED";
48
+ State[State["AFTER_ATTRIBUTE_VALUE_QUOTED"] = 38] = "AFTER_ATTRIBUTE_VALUE_QUOTED";
49
+ State[State["SELF_CLOSING_START_TAG"] = 39] = "SELF_CLOSING_START_TAG";
50
+ State[State["BOGUS_COMMENT"] = 40] = "BOGUS_COMMENT";
51
+ State[State["MARKUP_DECLARATION_OPEN"] = 41] = "MARKUP_DECLARATION_OPEN";
52
+ State[State["COMMENT_START"] = 42] = "COMMENT_START";
53
+ State[State["COMMENT_START_DASH"] = 43] = "COMMENT_START_DASH";
54
+ State[State["COMMENT"] = 44] = "COMMENT";
55
+ State[State["COMMENT_LESS_THAN_SIGN"] = 45] = "COMMENT_LESS_THAN_SIGN";
56
+ State[State["COMMENT_LESS_THAN_SIGN_BANG"] = 46] = "COMMENT_LESS_THAN_SIGN_BANG";
57
+ State[State["COMMENT_LESS_THAN_SIGN_BANG_DASH"] = 47] = "COMMENT_LESS_THAN_SIGN_BANG_DASH";
58
+ State[State["COMMENT_LESS_THAN_SIGN_BANG_DASH_DASH"] = 48] = "COMMENT_LESS_THAN_SIGN_BANG_DASH_DASH";
59
+ State[State["COMMENT_END_DASH"] = 49] = "COMMENT_END_DASH";
60
+ State[State["COMMENT_END"] = 50] = "COMMENT_END";
61
+ State[State["COMMENT_END_BANG"] = 51] = "COMMENT_END_BANG";
62
+ State[State["DOCTYPE"] = 52] = "DOCTYPE";
63
+ State[State["BEFORE_DOCTYPE_NAME"] = 53] = "BEFORE_DOCTYPE_NAME";
64
+ State[State["DOCTYPE_NAME"] = 54] = "DOCTYPE_NAME";
65
+ State[State["AFTER_DOCTYPE_NAME"] = 55] = "AFTER_DOCTYPE_NAME";
66
+ State[State["AFTER_DOCTYPE_PUBLIC_KEYWORD"] = 56] = "AFTER_DOCTYPE_PUBLIC_KEYWORD";
67
+ State[State["BEFORE_DOCTYPE_PUBLIC_IDENTIFIER"] = 57] = "BEFORE_DOCTYPE_PUBLIC_IDENTIFIER";
68
+ State[State["DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED"] = 58] = "DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED";
69
+ State[State["DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED"] = 59] = "DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED";
70
+ State[State["AFTER_DOCTYPE_PUBLIC_IDENTIFIER"] = 60] = "AFTER_DOCTYPE_PUBLIC_IDENTIFIER";
71
+ State[State["BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS"] = 61] = "BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS";
72
+ State[State["AFTER_DOCTYPE_SYSTEM_KEYWORD"] = 62] = "AFTER_DOCTYPE_SYSTEM_KEYWORD";
73
+ State[State["BEFORE_DOCTYPE_SYSTEM_IDENTIFIER"] = 63] = "BEFORE_DOCTYPE_SYSTEM_IDENTIFIER";
74
+ State[State["DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED"] = 64] = "DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED";
75
+ State[State["DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED"] = 65] = "DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED";
76
+ State[State["AFTER_DOCTYPE_SYSTEM_IDENTIFIER"] = 66] = "AFTER_DOCTYPE_SYSTEM_IDENTIFIER";
77
+ State[State["BOGUS_DOCTYPE"] = 67] = "BOGUS_DOCTYPE";
78
+ State[State["CDATA_SECTION"] = 68] = "CDATA_SECTION";
79
+ State[State["CDATA_SECTION_BRACKET"] = 69] = "CDATA_SECTION_BRACKET";
80
+ State[State["CDATA_SECTION_END"] = 70] = "CDATA_SECTION_END";
81
+ State[State["CHARACTER_REFERENCE"] = 71] = "CHARACTER_REFERENCE";
82
+ State[State["AMBIGUOUS_AMPERSAND"] = 72] = "AMBIGUOUS_AMPERSAND";
83
+ })(State || (State = {}));
84
+ //Tokenizer initial states for different modes
85
+ export const TokenizerMode = {
86
+ DATA: State.DATA,
87
+ RCDATA: State.RCDATA,
88
+ RAWTEXT: State.RAWTEXT,
89
+ SCRIPT_DATA: State.SCRIPT_DATA,
90
+ PLAINTEXT: State.PLAINTEXT,
91
+ CDATA_SECTION: State.CDATA_SECTION,
92
+ };
93
+ //Utils
94
+ //OPTIMIZATION: these utility functions should not be moved out of this module. V8 Crankshaft will not inline
95
+ //this functions if they will be situated in another module due to context switch.
96
+ //Always perform inlining check before modifying this functions ('node --trace-inlining').
97
+ function isAsciiDigit(cp) {
98
+ return cp >= $.DIGIT_0 && cp <= $.DIGIT_9;
99
+ }
100
+ function isAsciiUpper(cp) {
101
+ return cp >= $.LATIN_CAPITAL_A && cp <= $.LATIN_CAPITAL_Z;
102
+ }
103
+ function isAsciiLower(cp) {
104
+ return cp >= $.LATIN_SMALL_A && cp <= $.LATIN_SMALL_Z;
105
+ }
106
+ function isAsciiLetter(cp) {
107
+ return isAsciiLower(cp) || isAsciiUpper(cp);
108
+ }
109
+ function isAsciiAlphaNumeric(cp) {
110
+ return isAsciiLetter(cp) || isAsciiDigit(cp);
111
+ }
112
+ function toAsciiLower(cp) {
113
+ return cp + 32;
114
+ }
115
+ function isWhitespace(cp) {
116
+ return (cp === $.SPACE || cp === $.LINE_FEED || cp === $.TABULATION || cp === $.FORM_FEED || cp === $.CARRIAGE_RETURN);
117
+ }
118
+ function isScriptDataDoubleEscapeSequenceEnd(cp) {
119
+ return isWhitespace(cp) || cp === $.SOLIDUS || cp === $.GREATER_THAN_SIGN;
120
+ }
121
+ function getErrorForNumericCharacterReference(code) {
122
+ if (code === $.NULL) {
123
+ return ERR.nullCharacterReference;
124
+ }
125
+ else if (code > 1114111) {
126
+ return ERR.characterReferenceOutsideUnicodeRange;
127
+ }
128
+ else if (isSurrogate(code)) {
129
+ return ERR.surrogateCharacterReference;
130
+ }
131
+ else if (isUndefinedCodePoint(code)) {
132
+ return ERR.noncharacterCharacterReference;
133
+ }
134
+ else if (isControlCodePoint(code) || code === $.CARRIAGE_RETURN) {
135
+ return ERR.controlCharacterReference;
136
+ }
137
+ return null;
138
+ }
139
+ //Tokenizer
140
+ export class Tokenizer {
141
+ constructor(options, handler) {
142
+ this.options = options;
143
+ this.handler = handler;
144
+ this.paused = false;
145
+ /** Ensures that the parsing loop isn't run multiple times at once. */
146
+ this.inLoop = false;
147
+ /**
148
+ * Indicates that the current adjusted node exists, is not an element in the HTML namespace,
149
+ * and that it is not an integration point for either MathML or HTML.
150
+ *
151
+ * @see {@link https://html.spec.whatwg.org/multipage/parsing.html#tree-construction}
152
+ */
153
+ this.inForeignNode = false;
154
+ this.lastStartTagName = '';
155
+ this.active = false;
156
+ this.state = State.DATA;
157
+ this.returnState = State.DATA;
158
+ this.entityStartPos = 0;
159
+ this.consumedAfterSnapshot = -1;
160
+ this.currentCharacterToken = null;
161
+ this.currentToken = null;
162
+ this.currentAttr = { name: '', value: '' };
163
+ this.currentAttrLocation = null;
164
+ /**
165
+ * `[start, end)` of `preprocessor.html` still to be appended to the attribute value, comment data or character
166
+ * token being built (`_flushRun`): input that is used as is is appended as one slice, not a code point at a time.
167
+ */
168
+ this.runStart = 0;
169
+ this.runEnd = 0;
170
+ this.preprocessor = new Preprocessor(handler);
171
+ this.currentLocation = this.getCurrentLocation(-1);
172
+ this.entityDecoder = new HtmlEntityDecoder((cp, consumed) => {
173
+ // Note: Set `pos` _before_ flushing, as flushing might drop
174
+ // the current chunk and invalidate `entityStartPos`.
175
+ this.preprocessor.pos = this.entityStartPos + consumed - 1;
176
+ if (!this._isCharacterReferenceInAttribute()) {
177
+ this._ensureCharacterToken(isWhitespace(cp)
178
+ ? TokenType.WHITESPACE_CHARACTER
179
+ : cp === $.NULL
180
+ ? TokenType.NULL_CHARACTER
181
+ : TokenType.CHARACTER);
182
+ }
183
+ this._extendRunWith(String.fromCodePoint(cp), 0);
184
+ }, handler.onParseError
185
+ ? {
186
+ missingSemicolonAfterCharacterReference: () => {
187
+ this._err(ERR.missingSemicolonAfterCharacterReference, 1);
188
+ },
189
+ absenceOfDigitsInNumericCharacterReference: (consumed) => {
190
+ this._err(ERR.absenceOfDigitsInNumericCharacterReference, this.entityStartPos - this.preprocessor.pos + consumed);
191
+ },
192
+ validateNumericCharacterReference: (code) => {
193
+ const error = getErrorForNumericCharacterReference(code);
194
+ if (error)
195
+ this._err(error, 1);
196
+ },
197
+ }
198
+ : undefined);
199
+ }
200
+ //Errors
201
+ _err(code, cpOffset = 0) {
202
+ var _a, _b;
203
+ (_b = (_a = this.handler).onParseError) === null || _b === void 0 ? void 0 : _b.call(_a, this.preprocessor.getError(code, cpOffset));
204
+ }
205
+ // NOTE: `offset` may never run across line boundaries.
206
+ getCurrentLocation(offset) {
207
+ if (!this.options.sourceCodeLocationInfo) {
208
+ return null;
209
+ }
210
+ return {
211
+ startLine: this.preprocessor.line,
212
+ startCol: this.preprocessor.col - offset,
213
+ startOffset: this.preprocessor.offset - offset,
214
+ endLine: -1,
215
+ endCol: -1,
216
+ endOffset: -1,
217
+ };
218
+ }
219
+ _runParsingLoop() {
220
+ if (this.inLoop)
221
+ return;
222
+ this.inLoop = true;
223
+ while (this.active && !this.paused) {
224
+ this.consumedAfterSnapshot = 0;
225
+ const cp = this._consume();
226
+ if (!this._ensureHibernation()) {
227
+ this._callState(cp);
228
+ }
229
+ }
230
+ this.inLoop = false;
231
+ }
232
+ //API
233
+ /** Emit buffered character tokens without completing the current token or emitting EOF. */
234
+ flushCharacters() {
235
+ var _a;
236
+ if (this.inLoop) {
237
+ throw new Error('Cannot flush characters from a tokenizer callback');
238
+ }
239
+ const location = (_a = this.currentCharacterToken) === null || _a === void 0 ? void 0 : _a.location;
240
+ let nextLocation = null;
241
+ if (location) {
242
+ nextLocation = this.getCharacterTokenEndLocation(location);
243
+ this.currentLocation = nextLocation;
244
+ }
245
+ this.inLoop = true;
246
+ try {
247
+ this._emitCurrentCharacterToken(nextLocation);
248
+ }
249
+ finally {
250
+ this.inLoop = false;
251
+ }
252
+ this._runParsingLoop();
253
+ }
254
+ getCharacterTokenEndLocation(location) {
255
+ const endOffset = this.getCharacterTokenEndOffset();
256
+ const html = this.preprocessor.html.slice(location.startOffset - this.preprocessor.droppedBufferSize, endOffset - this.preprocessor.droppedBufferSize);
257
+ let line = location.startLine;
258
+ let lastLineBreak = -1;
259
+ for (let i = html.indexOf('\n'); i !== -1; i = html.indexOf('\n', i + 1)) {
260
+ line++;
261
+ lastLineBreak = i;
262
+ }
263
+ for (let i = html.indexOf('\r'); i !== -1; i = html.indexOf('\r', i + 1)) {
264
+ // Count CRLF as one line break by counting its LF only.
265
+ if (html.charCodeAt(i + 1) !== $.LINE_FEED) {
266
+ line++;
267
+ lastLineBreak = Math.max(lastLineBreak, i);
268
+ }
269
+ }
270
+ return {
271
+ startLine: line,
272
+ startCol: lastLineBreak === -1 ? location.startCol + html.length : html.length - lastLineBreak,
273
+ startOffset: endOffset,
274
+ endLine: -1,
275
+ endCol: -1,
276
+ endOffset: -1,
277
+ };
278
+ }
279
+ // Return the input boundary before any unfinished token or buffered delimiter.
280
+ getCharacterTokenEndOffset() {
281
+ var _a;
282
+ const end = this.preprocessor.droppedBufferSize + this.preprocessor.pos + 1;
283
+ const tokenLocation = (_a = this.currentToken) === null || _a === void 0 ? void 0 : _a.location;
284
+ switch (this.state) {
285
+ case State.TAG_OPEN:
286
+ case State.RCDATA_LESS_THAN_SIGN:
287
+ case State.RAWTEXT_LESS_THAN_SIGN:
288
+ case State.SCRIPT_DATA_LESS_THAN_SIGN:
289
+ case State.SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN:
290
+ case State.CDATA_SECTION_BRACKET: {
291
+ return end - 1;
292
+ }
293
+ case State.END_TAG_OPEN:
294
+ case State.MARKUP_DECLARATION_OPEN:
295
+ case State.RCDATA_END_TAG_OPEN:
296
+ case State.RCDATA_END_TAG_NAME:
297
+ case State.RAWTEXT_END_TAG_OPEN:
298
+ case State.RAWTEXT_END_TAG_NAME:
299
+ case State.SCRIPT_DATA_END_TAG_OPEN:
300
+ case State.SCRIPT_DATA_END_TAG_NAME:
301
+ case State.SCRIPT_DATA_ESCAPED_END_TAG_OPEN:
302
+ case State.SCRIPT_DATA_ESCAPED_END_TAG_NAME:
303
+ case State.CDATA_SECTION_END: {
304
+ return end - 2;
305
+ }
306
+ case State.CHARACTER_REFERENCE: {
307
+ return this._isCharacterReferenceInAttribute() && tokenLocation
308
+ ? tokenLocation.startOffset
309
+ : this.preprocessor.droppedBufferSize + this.entityStartPos;
310
+ }
311
+ case State.DOCTYPE:
312
+ case State.BEFORE_DOCTYPE_NAME: {
313
+ return this.currentLocation.startOffset;
314
+ }
315
+ default: {
316
+ if (tokenLocation &&
317
+ (this.state === State.TAG_NAME ||
318
+ (this.state >= State.BEFORE_ATTRIBUTE_NAME && this.state <= State.BOGUS_COMMENT) ||
319
+ (this.state >= State.COMMENT_START && this.state <= State.BOGUS_DOCTYPE))) {
320
+ return tokenLocation.startOffset;
321
+ }
322
+ return end;
323
+ }
324
+ }
325
+ }
326
+ pause() {
327
+ this.paused = true;
328
+ }
329
+ resume(writeCallback) {
330
+ if (!this.paused) {
331
+ throw new Error('Parser was already resumed');
332
+ }
333
+ this.paused = false;
334
+ // Necessary for synchronous resume.
335
+ if (this.inLoop)
336
+ return;
337
+ this._runParsingLoop();
338
+ if (!this.paused) {
339
+ writeCallback === null || writeCallback === void 0 ? void 0 : writeCallback();
340
+ }
341
+ }
342
+ write(chunk, isLastChunk, writeCallback) {
343
+ this.active = true;
344
+ this.preprocessor.write(chunk, isLastChunk);
345
+ this._runParsingLoop();
346
+ if (!this.paused) {
347
+ writeCallback === null || writeCallback === void 0 ? void 0 : writeCallback();
348
+ }
349
+ }
350
+ insertHtmlAtCurrentPos(chunk) {
351
+ this.active = true;
352
+ this.preprocessor.insertHtmlAtCurrentPos(chunk);
353
+ this._runParsingLoop();
354
+ }
355
+ //Hibernation
356
+ _ensureHibernation() {
357
+ if (this.preprocessor.endOfChunkHit) {
358
+ this.preprocessor.retreat(this.consumedAfterSnapshot);
359
+ this.consumedAfterSnapshot = 0;
360
+ this.active = false;
361
+ return true;
362
+ }
363
+ return false;
364
+ }
365
+ //Consumption
366
+ _consume() {
367
+ this.consumedAfterSnapshot++;
368
+ return this.preprocessor.advance();
369
+ }
370
+ _advanceBy(count) {
371
+ this.consumedAfterSnapshot += count;
372
+ for (let i = 0; i < count; i++) {
373
+ this.preprocessor.advance();
374
+ }
375
+ }
376
+ _consumeSequenceIfMatch(pattern, caseSensitive) {
377
+ if (this.preprocessor.startsWith(pattern, caseSensitive)) {
378
+ // We will already have consumed one character before calling this method.
379
+ this._advanceBy(pattern.length - 1);
380
+ return true;
381
+ }
382
+ return false;
383
+ }
384
+ //Token creation
385
+ _createStartTagToken() {
386
+ this._flushRun('');
387
+ this.currentToken = {
388
+ type: TokenType.START_TAG,
389
+ tagName: '',
390
+ tagID: TAG_ID.UNKNOWN,
391
+ selfClosing: false,
392
+ ackSelfClosing: false,
393
+ attrs: [],
394
+ location: this.getCurrentLocation(1),
395
+ };
396
+ }
397
+ _createEndTagToken() {
398
+ this._flushRun('');
399
+ this.currentToken = {
400
+ type: TokenType.END_TAG,
401
+ tagName: '',
402
+ tagID: TAG_ID.UNKNOWN,
403
+ selfClosing: false,
404
+ ackSelfClosing: false,
405
+ attrs: [],
406
+ location: this.getCurrentLocation(2),
407
+ };
408
+ }
409
+ _createCommentToken(offset) {
410
+ this._flushRun('');
411
+ this.currentToken = {
412
+ type: TokenType.COMMENT,
413
+ data: '',
414
+ location: this.getCurrentLocation(offset),
415
+ };
416
+ }
417
+ _createDoctypeToken(initialName) {
418
+ this._flushRun('');
419
+ this.currentToken = {
420
+ type: TokenType.DOCTYPE,
421
+ name: initialName,
422
+ forceQuirks: false,
423
+ publicId: null,
424
+ systemId: null,
425
+ location: this.currentLocation,
426
+ };
427
+ }
428
+ _createCharacterToken(type) {
429
+ if (this.currentLocation) {
430
+ const start = this.currentLocation.startOffset - this.preprocessor.droppedBufferSize;
431
+ // A flushed CR already advanced the line; exclude its ignored LF from the next token's range.
432
+ if (this.preprocessor.html[start] === '\n' && this.preprocessor.html[start - 1] === '\r') {
433
+ this.currentLocation.startOffset++;
434
+ }
435
+ }
436
+ this.currentCharacterToken = {
437
+ type,
438
+ chars: '',
439
+ location: this.currentLocation,
440
+ };
441
+ }
442
+ //Tag attributes
443
+ _createAttr(attrNameFirstCh) {
444
+ this._flushRun('');
445
+ this.currentAttr = {
446
+ name: attrNameFirstCh,
447
+ value: '',
448
+ };
449
+ this.currentAttrLocation = this.getCurrentLocation(0);
450
+ }
451
+ _leaveAttrName() {
452
+ var _a;
453
+ var _b;
454
+ const token = this.currentToken;
455
+ if (getTokenAttr(token, this.currentAttr.name) === null) {
456
+ token.attrs.push(this.currentAttr);
457
+ if (token.location && this.currentAttrLocation) {
458
+ const attrLocations = ((_a = (_b = token.location).attrs) !== null && _a !== void 0 ? _a : (_b.attrs = Object.create(null)));
459
+ attrLocations[this.currentAttr.name] = this.currentAttrLocation;
460
+ // Set end location
461
+ this._leaveAttrValue();
462
+ }
463
+ }
464
+ else {
465
+ this._err(ERR.duplicateAttribute);
466
+ }
467
+ }
468
+ _leaveAttrValue() {
469
+ if (this.currentAttrLocation) {
470
+ this.currentAttrLocation.endLine = this.preprocessor.line;
471
+ this.currentAttrLocation.endCol = this.preprocessor.col;
472
+ this.currentAttrLocation.endOffset = this.preprocessor.offset;
473
+ }
474
+ }
475
+ /**
476
+ * Append the pending run and `text` to the attribute value, comment data or character token being built.
477
+ * The run is a string of its own: V8 copies a slice of fewer than 13 characters but lets a longer one share
478
+ * the input's buffer, which would keep that buffer alive for as long as any attribute value or text cut from
479
+ * it is; a concatenation is flattened into a buffer of its own when it is sliced.
480
+ */
481
+ _flushRun(text) {
482
+ if (this.runEnd === this.runStart && text.length === 0) {
483
+ return;
484
+ }
485
+ const slice = this.preprocessor.html.slice(this.runStart, this.runEnd);
486
+ const run = (slice.length < 13 ? slice : ` ${slice}`.slice(1)) + text;
487
+ const token = this.currentToken;
488
+ this.runStart = this.runEnd;
489
+ if (token === null) {
490
+ this.currentCharacterToken.chars += run;
491
+ }
492
+ else if (token.type === TokenType.COMMENT) {
493
+ token.data += run;
494
+ }
495
+ else {
496
+ this.currentAttr.value += run;
497
+ }
498
+ }
499
+ /** Extend the pending run by the current input code point, read as `cp`, or flush it and start a new one. */
500
+ _extendRun(cp) {
501
+ const { preprocessor } = this;
502
+ //NOTE: a CR is read as LF, which is then not in the input.
503
+ if (cp === $.LINE_FEED && preprocessor.html.charCodeAt(preprocessor.pos) !== $.LINE_FEED) {
504
+ this._flushRun('\n');
505
+ this.runStart = preprocessor.pos + 1;
506
+ }
507
+ else {
508
+ const start = preprocessor.pos - Number(cp > 65535);
509
+ if (start !== this.runEnd) {
510
+ this._flushRun('');
511
+ this.runStart = start;
512
+ }
513
+ }
514
+ this.runEnd = preprocessor.pos + 1;
515
+ }
516
+ /**
517
+ * Extend the pending run by `text`, the input that ends `cpOffset` code points after the current one, or flush
518
+ * the run and append `text` where the input differs (a NUL replacement, a decoded reference, a dropped chunk).
519
+ */
520
+ _extendRunWith(text, cpOffset) {
521
+ const { preprocessor } = this;
522
+ const start = preprocessor.codePointStart + 1 + cpOffset - text.length;
523
+ if (start >= 0 && preprocessor.html.startsWith(text, start)) {
524
+ if (start !== this.runEnd) {
525
+ this._flushRun('');
526
+ this.runStart = start;
527
+ }
528
+ }
529
+ else {
530
+ this._flushRun(text);
531
+ this.runStart = start + text.length;
532
+ }
533
+ this.runEnd = start + text.length;
534
+ }
535
+ //Token emission
536
+ prepareToken(ct) {
537
+ this._emitCurrentCharacterToken(ct.location);
538
+ this.currentToken = null;
539
+ if (ct.location) {
540
+ ct.location.endLine = this.preprocessor.line;
541
+ ct.location.endCol = this.preprocessor.col + 1;
542
+ ct.location.endOffset = this.preprocessor.offset + 1;
543
+ }
544
+ this.currentLocation = this.getCurrentLocation(-1);
545
+ }
546
+ emitCurrentTagToken() {
547
+ const ct = this.currentToken;
548
+ this.prepareToken(ct);
549
+ ct.tagID = getTagID(ct.tagName);
550
+ if (ct.type === TokenType.START_TAG) {
551
+ this.lastStartTagName = ct.tagName;
552
+ this.handler.onStartTag(ct);
553
+ }
554
+ else {
555
+ if (ct.attrs.length > 0) {
556
+ this._err(ERR.endTagWithAttributes);
557
+ }
558
+ if (ct.selfClosing) {
559
+ this._err(ERR.endTagWithTrailingSolidus);
560
+ }
561
+ this.handler.onEndTag(ct);
562
+ }
563
+ this.preprocessor.dropParsedChunk();
564
+ }
565
+ emitCurrentComment(ct) {
566
+ this.prepareToken(ct);
567
+ this.handler.onComment(ct);
568
+ this.preprocessor.dropParsedChunk();
569
+ }
570
+ emitCurrentDoctype(ct) {
571
+ this.prepareToken(ct);
572
+ this.handler.onDoctype(ct);
573
+ this.preprocessor.dropParsedChunk();
574
+ }
575
+ _emitCurrentCharacterToken(nextLocation) {
576
+ this._flushRun('');
577
+ if (this.currentCharacterToken) {
578
+ //NOTE: if we have a pending character token, make it's end location equal to the
579
+ //current token's start location.
580
+ if (nextLocation && this.currentCharacterToken.location) {
581
+ this.currentCharacterToken.location.endLine = nextLocation.startLine;
582
+ this.currentCharacterToken.location.endCol = nextLocation.startCol;
583
+ this.currentCharacterToken.location.endOffset = nextLocation.startOffset;
584
+ }
585
+ switch (this.currentCharacterToken.type) {
586
+ case TokenType.CHARACTER: {
587
+ this.handler.onCharacter(this.currentCharacterToken);
588
+ break;
589
+ }
590
+ case TokenType.NULL_CHARACTER: {
591
+ this.handler.onNullCharacter(this.currentCharacterToken);
592
+ break;
593
+ }
594
+ case TokenType.WHITESPACE_CHARACTER: {
595
+ this.handler.onWhitespaceCharacter(this.currentCharacterToken);
596
+ break;
597
+ }
598
+ }
599
+ this.currentCharacterToken = null;
600
+ }
601
+ }
602
+ _emitEOFToken() {
603
+ const location = this.getCurrentLocation(0);
604
+ if (location) {
605
+ location.endLine = location.startLine;
606
+ location.endCol = location.startCol;
607
+ location.endOffset = location.startOffset;
608
+ }
609
+ this._emitCurrentCharacterToken(location);
610
+ this.handler.onEof({ type: TokenType.EOF, location });
611
+ this.active = false;
612
+ }
613
+ //Characters emission
614
+ //OPTIMIZATION: The specification uses only one type of character token (one token per character).
615
+ //This causes a huge memory overhead and a lot of unnecessary parser loops. parse5 uses 3 groups of characters.
616
+ //If we have a sequence of characters that belong to the same group, the parser can process it
617
+ //as a single solid character token.
618
+ //So, there are 3 types of character tokens in parse5:
619
+ //1)TokenType.NULL_CHARACTER - \u0000-character sequences (e.g. '\u0000\u0000\u0000')
620
+ //2)TokenType.WHITESPACE_CHARACTER - any whitespace/new-line character sequences (e.g. '\n \r\t \f')
621
+ //3)TokenType.CHARACTER - any character sequence which don't belong to groups 1 and 2 (e.g. 'abcdef1234@@#$%^')
622
+ /** Make the current character token one of `type`, emitting the current one if it has another type. */
623
+ _ensureCharacterToken(type) {
624
+ if (this.currentCharacterToken) {
625
+ if (this.currentCharacterToken.type === type) {
626
+ return;
627
+ }
628
+ this.currentLocation = this.getCurrentLocation(0);
629
+ this._emitCurrentCharacterToken(this.currentLocation);
630
+ this.preprocessor.dropParsedChunk();
631
+ }
632
+ this._createCharacterToken(type);
633
+ }
634
+ /** Emit the current input code point, read as `cp`. */
635
+ _emitCodePoint(cp) {
636
+ const type = isWhitespace(cp)
637
+ ? TokenType.WHITESPACE_CHARACTER
638
+ : cp === $.NULL
639
+ ? TokenType.NULL_CHARACTER
640
+ : TokenType.CHARACTER;
641
+ const token = this.currentCharacterToken;
642
+ if (token === null || token.type !== type) {
643
+ this._ensureCharacterToken(type);
644
+ }
645
+ this._extendRun(cp);
646
+ }
647
+ //NOTE: used when we emit characters explicitly.
648
+ //This is always for non-whitespace and non-null characters, which allows us to avoid additional checks.
649
+ // Negative `cpOffset` values locate text emitted before the current input character.
650
+ _emitChars(ch, cpOffset = 0) {
651
+ if (this.options.sourceCodeLocationInfo &&
652
+ cpOffset !== 0 &&
653
+ this.currentCharacterToken &&
654
+ this.currentCharacterToken.type !== TokenType.CHARACTER) {
655
+ this.currentLocation = this.getCurrentLocation(ch.length - 1 - cpOffset);
656
+ this._emitCurrentCharacterToken(this.currentLocation);
657
+ // Keep source characters that precede the lookahead available for a subsequent flush.
658
+ this._createCharacterToken(TokenType.CHARACTER);
659
+ }
660
+ else {
661
+ this._ensureCharacterToken(TokenType.CHARACTER);
662
+ }
663
+ this._extendRunWith(ch, cpOffset);
664
+ }
665
+ // Character reference helpers
666
+ _startCharacterReference() {
667
+ this.returnState = this.state;
668
+ this.state = State.CHARACTER_REFERENCE;
669
+ this.entityStartPos = this.preprocessor.pos;
670
+ this.entityDecoder.startEntity(this._isCharacterReferenceInAttribute() ? DecodingMode.Attribute : DecodingMode.Legacy);
671
+ }
672
+ _isCharacterReferenceInAttribute() {
673
+ return (this.returnState === State.ATTRIBUTE_VALUE_DOUBLE_QUOTED ||
674
+ this.returnState === State.ATTRIBUTE_VALUE_SINGLE_QUOTED ||
675
+ this.returnState === State.ATTRIBUTE_VALUE_UNQUOTED);
676
+ }
677
+ _flushCodePointConsumedAsCharacterReference(cp) {
678
+ if (this._isCharacterReferenceInAttribute()) {
679
+ this._extendRun(cp);
680
+ }
681
+ else {
682
+ this._emitCodePoint(cp);
683
+ }
684
+ }
685
+ // Calling states this way turns out to be much faster than any other approach.
686
+ _callState(cp) {
687
+ switch (this.state) {
688
+ case State.DATA: {
689
+ this._stateData(cp);
690
+ break;
691
+ }
692
+ case State.RCDATA: {
693
+ this._stateRcdata(cp);
694
+ break;
695
+ }
696
+ case State.RAWTEXT: {
697
+ this._stateRawtext(cp);
698
+ break;
699
+ }
700
+ case State.SCRIPT_DATA: {
701
+ this._stateScriptData(cp);
702
+ break;
703
+ }
704
+ case State.PLAINTEXT: {
705
+ this._statePlaintext(cp);
706
+ break;
707
+ }
708
+ case State.TAG_OPEN: {
709
+ this._stateTagOpen(cp);
710
+ break;
711
+ }
712
+ case State.END_TAG_OPEN: {
713
+ this._stateEndTagOpen(cp);
714
+ break;
715
+ }
716
+ case State.TAG_NAME: {
717
+ this._stateTagName(cp);
718
+ break;
719
+ }
720
+ case State.RCDATA_LESS_THAN_SIGN: {
721
+ this._stateRcdataLessThanSign(cp);
722
+ break;
723
+ }
724
+ case State.RCDATA_END_TAG_OPEN: {
725
+ this._stateRcdataEndTagOpen(cp);
726
+ break;
727
+ }
728
+ case State.RCDATA_END_TAG_NAME: {
729
+ this._stateRcdataEndTagName(cp);
730
+ break;
731
+ }
732
+ case State.RAWTEXT_LESS_THAN_SIGN: {
733
+ this._stateRawtextLessThanSign(cp);
734
+ break;
735
+ }
736
+ case State.RAWTEXT_END_TAG_OPEN: {
737
+ this._stateRawtextEndTagOpen(cp);
738
+ break;
739
+ }
740
+ case State.RAWTEXT_END_TAG_NAME: {
741
+ this._stateRawtextEndTagName(cp);
742
+ break;
743
+ }
744
+ case State.SCRIPT_DATA_LESS_THAN_SIGN: {
745
+ this._stateScriptDataLessThanSign(cp);
746
+ break;
747
+ }
748
+ case State.SCRIPT_DATA_END_TAG_OPEN: {
749
+ this._stateScriptDataEndTagOpen(cp);
750
+ break;
751
+ }
752
+ case State.SCRIPT_DATA_END_TAG_NAME: {
753
+ this._stateScriptDataEndTagName(cp);
754
+ break;
755
+ }
756
+ case State.SCRIPT_DATA_ESCAPE_START: {
757
+ this._stateScriptDataEscapeStart(cp);
758
+ break;
759
+ }
760
+ case State.SCRIPT_DATA_ESCAPE_START_DASH: {
761
+ this._stateScriptDataEscapeStartDash(cp);
762
+ break;
763
+ }
764
+ case State.SCRIPT_DATA_ESCAPED: {
765
+ this._stateScriptDataEscaped(cp);
766
+ break;
767
+ }
768
+ case State.SCRIPT_DATA_ESCAPED_DASH: {
769
+ this._stateScriptDataEscapedDash(cp);
770
+ break;
771
+ }
772
+ case State.SCRIPT_DATA_ESCAPED_DASH_DASH: {
773
+ this._stateScriptDataEscapedDashDash(cp);
774
+ break;
775
+ }
776
+ case State.SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN: {
777
+ this._stateScriptDataEscapedLessThanSign(cp);
778
+ break;
779
+ }
780
+ case State.SCRIPT_DATA_ESCAPED_END_TAG_OPEN: {
781
+ this._stateScriptDataEscapedEndTagOpen(cp);
782
+ break;
783
+ }
784
+ case State.SCRIPT_DATA_ESCAPED_END_TAG_NAME: {
785
+ this._stateScriptDataEscapedEndTagName(cp);
786
+ break;
787
+ }
788
+ case State.SCRIPT_DATA_DOUBLE_ESCAPE_START: {
789
+ this._stateScriptDataDoubleEscapeStart(cp);
790
+ break;
791
+ }
792
+ case State.SCRIPT_DATA_DOUBLE_ESCAPED: {
793
+ this._stateScriptDataDoubleEscaped(cp);
794
+ break;
795
+ }
796
+ case State.SCRIPT_DATA_DOUBLE_ESCAPED_DASH: {
797
+ this._stateScriptDataDoubleEscapedDash(cp);
798
+ break;
799
+ }
800
+ case State.SCRIPT_DATA_DOUBLE_ESCAPED_DASH_DASH: {
801
+ this._stateScriptDataDoubleEscapedDashDash(cp);
802
+ break;
803
+ }
804
+ case State.SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN: {
805
+ this._stateScriptDataDoubleEscapedLessThanSign(cp);
806
+ break;
807
+ }
808
+ case State.SCRIPT_DATA_DOUBLE_ESCAPE_END: {
809
+ this._stateScriptDataDoubleEscapeEnd(cp);
810
+ break;
811
+ }
812
+ case State.BEFORE_ATTRIBUTE_NAME: {
813
+ this._stateBeforeAttributeName(cp);
814
+ break;
815
+ }
816
+ case State.ATTRIBUTE_NAME: {
817
+ this._stateAttributeName(cp);
818
+ break;
819
+ }
820
+ case State.AFTER_ATTRIBUTE_NAME: {
821
+ this._stateAfterAttributeName(cp);
822
+ break;
823
+ }
824
+ case State.BEFORE_ATTRIBUTE_VALUE: {
825
+ this._stateBeforeAttributeValue(cp);
826
+ break;
827
+ }
828
+ case State.ATTRIBUTE_VALUE_DOUBLE_QUOTED: {
829
+ this._stateAttributeValueDoubleQuoted(cp);
830
+ break;
831
+ }
832
+ case State.ATTRIBUTE_VALUE_SINGLE_QUOTED: {
833
+ this._stateAttributeValueSingleQuoted(cp);
834
+ break;
835
+ }
836
+ case State.ATTRIBUTE_VALUE_UNQUOTED: {
837
+ this._stateAttributeValueUnquoted(cp);
838
+ break;
839
+ }
840
+ case State.AFTER_ATTRIBUTE_VALUE_QUOTED: {
841
+ this._stateAfterAttributeValueQuoted(cp);
842
+ break;
843
+ }
844
+ case State.SELF_CLOSING_START_TAG: {
845
+ this._stateSelfClosingStartTag(cp);
846
+ break;
847
+ }
848
+ case State.BOGUS_COMMENT: {
849
+ this._stateBogusComment(cp);
850
+ break;
851
+ }
852
+ case State.MARKUP_DECLARATION_OPEN: {
853
+ this._stateMarkupDeclarationOpen(cp);
854
+ break;
855
+ }
856
+ case State.COMMENT_START: {
857
+ this._stateCommentStart(cp);
858
+ break;
859
+ }
860
+ case State.COMMENT_START_DASH: {
861
+ this._stateCommentStartDash(cp);
862
+ break;
863
+ }
864
+ case State.COMMENT: {
865
+ this._stateComment(cp);
866
+ break;
867
+ }
868
+ case State.COMMENT_LESS_THAN_SIGN: {
869
+ this._stateCommentLessThanSign(cp);
870
+ break;
871
+ }
872
+ case State.COMMENT_LESS_THAN_SIGN_BANG: {
873
+ this._stateCommentLessThanSignBang(cp);
874
+ break;
875
+ }
876
+ case State.COMMENT_LESS_THAN_SIGN_BANG_DASH: {
877
+ this._stateCommentLessThanSignBangDash(cp);
878
+ break;
879
+ }
880
+ case State.COMMENT_LESS_THAN_SIGN_BANG_DASH_DASH: {
881
+ this._stateCommentLessThanSignBangDashDash(cp);
882
+ break;
883
+ }
884
+ case State.COMMENT_END_DASH: {
885
+ this._stateCommentEndDash(cp);
886
+ break;
887
+ }
888
+ case State.COMMENT_END: {
889
+ this._stateCommentEnd(cp);
890
+ break;
891
+ }
892
+ case State.COMMENT_END_BANG: {
893
+ this._stateCommentEndBang(cp);
894
+ break;
895
+ }
896
+ case State.DOCTYPE: {
897
+ this._stateDoctype(cp);
898
+ break;
899
+ }
900
+ case State.BEFORE_DOCTYPE_NAME: {
901
+ this._stateBeforeDoctypeName(cp);
902
+ break;
903
+ }
904
+ case State.DOCTYPE_NAME: {
905
+ this._stateDoctypeName(cp);
906
+ break;
907
+ }
908
+ case State.AFTER_DOCTYPE_NAME: {
909
+ this._stateAfterDoctypeName(cp);
910
+ break;
911
+ }
912
+ case State.AFTER_DOCTYPE_PUBLIC_KEYWORD: {
913
+ this._stateAfterDoctypePublicKeyword(cp);
914
+ break;
915
+ }
916
+ case State.BEFORE_DOCTYPE_PUBLIC_IDENTIFIER: {
917
+ this._stateBeforeDoctypePublicIdentifier(cp);
918
+ break;
919
+ }
920
+ case State.DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED: {
921
+ this._stateDoctypePublicIdentifierDoubleQuoted(cp);
922
+ break;
923
+ }
924
+ case State.DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED: {
925
+ this._stateDoctypePublicIdentifierSingleQuoted(cp);
926
+ break;
927
+ }
928
+ case State.AFTER_DOCTYPE_PUBLIC_IDENTIFIER: {
929
+ this._stateAfterDoctypePublicIdentifier(cp);
930
+ break;
931
+ }
932
+ case State.BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS: {
933
+ this._stateBetweenDoctypePublicAndSystemIdentifiers(cp);
934
+ break;
935
+ }
936
+ case State.AFTER_DOCTYPE_SYSTEM_KEYWORD: {
937
+ this._stateAfterDoctypeSystemKeyword(cp);
938
+ break;
939
+ }
940
+ case State.BEFORE_DOCTYPE_SYSTEM_IDENTIFIER: {
941
+ this._stateBeforeDoctypeSystemIdentifier(cp);
942
+ break;
943
+ }
944
+ case State.DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED: {
945
+ this._stateDoctypeSystemIdentifierDoubleQuoted(cp);
946
+ break;
947
+ }
948
+ case State.DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED: {
949
+ this._stateDoctypeSystemIdentifierSingleQuoted(cp);
950
+ break;
951
+ }
952
+ case State.AFTER_DOCTYPE_SYSTEM_IDENTIFIER: {
953
+ this._stateAfterDoctypeSystemIdentifier(cp);
954
+ break;
955
+ }
956
+ case State.BOGUS_DOCTYPE: {
957
+ this._stateBogusDoctype(cp);
958
+ break;
959
+ }
960
+ case State.CDATA_SECTION: {
961
+ this._stateCdataSection(cp);
962
+ break;
963
+ }
964
+ case State.CDATA_SECTION_BRACKET: {
965
+ this._stateCdataSectionBracket(cp);
966
+ break;
967
+ }
968
+ case State.CDATA_SECTION_END: {
969
+ this._stateCdataSectionEnd(cp);
970
+ break;
971
+ }
972
+ case State.CHARACTER_REFERENCE: {
973
+ this._stateCharacterReference();
974
+ break;
975
+ }
976
+ case State.AMBIGUOUS_AMPERSAND: {
977
+ this._stateAmbiguousAmpersand(cp);
978
+ break;
979
+ }
980
+ default: {
981
+ throw new Error('Unknown state');
982
+ }
983
+ }
984
+ }
985
+ // State machine
986
+ // Data state
987
+ //------------------------------------------------------------------
988
+ _stateData(cp) {
989
+ switch (cp) {
990
+ case $.LESS_THAN_SIGN: {
991
+ this.state = State.TAG_OPEN;
992
+ break;
993
+ }
994
+ case $.AMPERSAND: {
995
+ this._startCharacterReference();
996
+ break;
997
+ }
998
+ case $.NULL: {
999
+ this._err(ERR.unexpectedNullCharacter);
1000
+ this._emitCodePoint(cp);
1001
+ break;
1002
+ }
1003
+ case $.EOF: {
1004
+ this._emitEOFToken();
1005
+ break;
1006
+ }
1007
+ default: {
1008
+ this._emitCodePoint(cp);
1009
+ }
1010
+ }
1011
+ }
1012
+ // RCDATA state
1013
+ //------------------------------------------------------------------
1014
+ _stateRcdata(cp) {
1015
+ switch (cp) {
1016
+ case $.AMPERSAND: {
1017
+ this._startCharacterReference();
1018
+ break;
1019
+ }
1020
+ case $.LESS_THAN_SIGN: {
1021
+ this.state = State.RCDATA_LESS_THAN_SIGN;
1022
+ break;
1023
+ }
1024
+ case $.NULL: {
1025
+ this._err(ERR.unexpectedNullCharacter);
1026
+ this._emitChars(REPLACEMENT_CHARACTER);
1027
+ break;
1028
+ }
1029
+ case $.EOF: {
1030
+ this._emitEOFToken();
1031
+ break;
1032
+ }
1033
+ default: {
1034
+ this._emitCodePoint(cp);
1035
+ }
1036
+ }
1037
+ }
1038
+ // RAWTEXT state
1039
+ //------------------------------------------------------------------
1040
+ _stateRawtext(cp) {
1041
+ switch (cp) {
1042
+ case $.LESS_THAN_SIGN: {
1043
+ this.state = State.RAWTEXT_LESS_THAN_SIGN;
1044
+ break;
1045
+ }
1046
+ case $.NULL: {
1047
+ this._err(ERR.unexpectedNullCharacter);
1048
+ this._emitChars(REPLACEMENT_CHARACTER);
1049
+ break;
1050
+ }
1051
+ case $.EOF: {
1052
+ this._emitEOFToken();
1053
+ break;
1054
+ }
1055
+ default: {
1056
+ this._emitCodePoint(cp);
1057
+ }
1058
+ }
1059
+ }
1060
+ // Script data state
1061
+ //------------------------------------------------------------------
1062
+ _stateScriptData(cp) {
1063
+ switch (cp) {
1064
+ case $.LESS_THAN_SIGN: {
1065
+ this.state = State.SCRIPT_DATA_LESS_THAN_SIGN;
1066
+ break;
1067
+ }
1068
+ case $.NULL: {
1069
+ this._err(ERR.unexpectedNullCharacter);
1070
+ this._emitChars(REPLACEMENT_CHARACTER);
1071
+ break;
1072
+ }
1073
+ case $.EOF: {
1074
+ this._emitEOFToken();
1075
+ break;
1076
+ }
1077
+ default: {
1078
+ this._emitCodePoint(cp);
1079
+ }
1080
+ }
1081
+ }
1082
+ // PLAINTEXT state
1083
+ //------------------------------------------------------------------
1084
+ _statePlaintext(cp) {
1085
+ switch (cp) {
1086
+ case $.NULL: {
1087
+ this._err(ERR.unexpectedNullCharacter);
1088
+ this._emitChars(REPLACEMENT_CHARACTER);
1089
+ break;
1090
+ }
1091
+ case $.EOF: {
1092
+ this._emitEOFToken();
1093
+ break;
1094
+ }
1095
+ default: {
1096
+ this._emitCodePoint(cp);
1097
+ }
1098
+ }
1099
+ }
1100
+ // Tag open state
1101
+ //------------------------------------------------------------------
1102
+ _stateTagOpen(cp) {
1103
+ if (isAsciiLetter(cp)) {
1104
+ this._createStartTagToken();
1105
+ this.state = State.TAG_NAME;
1106
+ this._stateTagName(cp);
1107
+ }
1108
+ else
1109
+ switch (cp) {
1110
+ case $.EXCLAMATION_MARK: {
1111
+ this.state = State.MARKUP_DECLARATION_OPEN;
1112
+ break;
1113
+ }
1114
+ case $.SOLIDUS: {
1115
+ this.state = State.END_TAG_OPEN;
1116
+ break;
1117
+ }
1118
+ case $.QUESTION_MARK: {
1119
+ this._err(ERR.unexpectedQuestionMarkInsteadOfTagName);
1120
+ this._createCommentToken(1);
1121
+ this.state = State.BOGUS_COMMENT;
1122
+ this._stateBogusComment(cp);
1123
+ break;
1124
+ }
1125
+ case $.EOF: {
1126
+ this._err(ERR.eofBeforeTagName);
1127
+ this._emitChars('<', -1);
1128
+ this._emitEOFToken();
1129
+ break;
1130
+ }
1131
+ default: {
1132
+ this._err(ERR.invalidFirstCharacterOfTagName);
1133
+ this._emitChars('<', -1);
1134
+ this.state = State.DATA;
1135
+ this._stateData(cp);
1136
+ }
1137
+ }
1138
+ }
1139
+ // End tag open state
1140
+ //------------------------------------------------------------------
1141
+ _stateEndTagOpen(cp) {
1142
+ if (isAsciiLetter(cp)) {
1143
+ this._createEndTagToken();
1144
+ this.state = State.TAG_NAME;
1145
+ this._stateTagName(cp);
1146
+ }
1147
+ else
1148
+ switch (cp) {
1149
+ case $.GREATER_THAN_SIGN: {
1150
+ this._err(ERR.missingEndTagName);
1151
+ this.state = State.DATA;
1152
+ break;
1153
+ }
1154
+ case $.EOF: {
1155
+ this._err(ERR.eofBeforeTagName);
1156
+ this._emitChars('</', -1);
1157
+ this._emitEOFToken();
1158
+ break;
1159
+ }
1160
+ default: {
1161
+ this._err(ERR.invalidFirstCharacterOfTagName);
1162
+ this._createCommentToken(2);
1163
+ this.state = State.BOGUS_COMMENT;
1164
+ this._stateBogusComment(cp);
1165
+ }
1166
+ }
1167
+ }
1168
+ // Tag name state
1169
+ //------------------------------------------------------------------
1170
+ _stateTagName(cp) {
1171
+ const token = this.currentToken;
1172
+ switch (cp) {
1173
+ case $.SPACE:
1174
+ case $.LINE_FEED:
1175
+ case $.TABULATION:
1176
+ case $.FORM_FEED: {
1177
+ this.state = State.BEFORE_ATTRIBUTE_NAME;
1178
+ break;
1179
+ }
1180
+ case $.SOLIDUS: {
1181
+ this.state = State.SELF_CLOSING_START_TAG;
1182
+ break;
1183
+ }
1184
+ case $.GREATER_THAN_SIGN: {
1185
+ this.state = State.DATA;
1186
+ this.emitCurrentTagToken();
1187
+ break;
1188
+ }
1189
+ case $.NULL: {
1190
+ this._err(ERR.unexpectedNullCharacter);
1191
+ token.tagName += REPLACEMENT_CHARACTER;
1192
+ break;
1193
+ }
1194
+ case $.EOF: {
1195
+ this._err(ERR.eofInTag);
1196
+ this._emitEOFToken();
1197
+ break;
1198
+ }
1199
+ default: {
1200
+ token.tagName += String.fromCodePoint(isAsciiUpper(cp) ? toAsciiLower(cp) : cp);
1201
+ }
1202
+ }
1203
+ }
1204
+ // RCDATA less-than sign state
1205
+ //------------------------------------------------------------------
1206
+ _stateRcdataLessThanSign(cp) {
1207
+ if (cp === $.SOLIDUS) {
1208
+ this.state = State.RCDATA_END_TAG_OPEN;
1209
+ }
1210
+ else {
1211
+ this._emitChars('<', -1);
1212
+ this.state = State.RCDATA;
1213
+ this._stateRcdata(cp);
1214
+ }
1215
+ }
1216
+ // RCDATA end tag open state
1217
+ //------------------------------------------------------------------
1218
+ _stateRcdataEndTagOpen(cp) {
1219
+ if (isAsciiLetter(cp)) {
1220
+ this.state = State.RCDATA_END_TAG_NAME;
1221
+ this._stateRcdataEndTagName(cp);
1222
+ }
1223
+ else {
1224
+ this._emitChars('</', -1);
1225
+ this.state = State.RCDATA;
1226
+ this._stateRcdata(cp);
1227
+ }
1228
+ }
1229
+ handleSpecialEndTag(_cp) {
1230
+ if (!this.preprocessor.startsWith(this.lastStartTagName, false)) {
1231
+ return !this._ensureHibernation();
1232
+ }
1233
+ this._createEndTagToken();
1234
+ const token = this.currentToken;
1235
+ token.tagName = this.lastStartTagName;
1236
+ const cp = this.preprocessor.peek(this.lastStartTagName.length);
1237
+ switch (cp) {
1238
+ case $.SPACE:
1239
+ case $.LINE_FEED:
1240
+ case $.TABULATION:
1241
+ case $.FORM_FEED: {
1242
+ this._advanceBy(this.lastStartTagName.length);
1243
+ this.state = State.BEFORE_ATTRIBUTE_NAME;
1244
+ return false;
1245
+ }
1246
+ case $.SOLIDUS: {
1247
+ this._advanceBy(this.lastStartTagName.length);
1248
+ this.state = State.SELF_CLOSING_START_TAG;
1249
+ return false;
1250
+ }
1251
+ case $.GREATER_THAN_SIGN: {
1252
+ this._advanceBy(this.lastStartTagName.length);
1253
+ this.emitCurrentTagToken();
1254
+ this.state = State.DATA;
1255
+ return false;
1256
+ }
1257
+ default: {
1258
+ //NOTE: the end tag is text after all, so no token is being built.
1259
+ this.currentToken = null;
1260
+ return !this._ensureHibernation();
1261
+ }
1262
+ }
1263
+ }
1264
+ // RCDATA end tag name state
1265
+ //------------------------------------------------------------------
1266
+ _stateRcdataEndTagName(cp) {
1267
+ if (this.handleSpecialEndTag(cp)) {
1268
+ this._emitChars('</', -1);
1269
+ this.state = State.RCDATA;
1270
+ this._stateRcdata(cp);
1271
+ }
1272
+ }
1273
+ // RAWTEXT less-than sign state
1274
+ //------------------------------------------------------------------
1275
+ _stateRawtextLessThanSign(cp) {
1276
+ if (cp === $.SOLIDUS) {
1277
+ this.state = State.RAWTEXT_END_TAG_OPEN;
1278
+ }
1279
+ else {
1280
+ this._emitChars('<', -1);
1281
+ this.state = State.RAWTEXT;
1282
+ this._stateRawtext(cp);
1283
+ }
1284
+ }
1285
+ // RAWTEXT end tag open state
1286
+ //------------------------------------------------------------------
1287
+ _stateRawtextEndTagOpen(cp) {
1288
+ if (isAsciiLetter(cp)) {
1289
+ this.state = State.RAWTEXT_END_TAG_NAME;
1290
+ this._stateRawtextEndTagName(cp);
1291
+ }
1292
+ else {
1293
+ this._emitChars('</', -1);
1294
+ this.state = State.RAWTEXT;
1295
+ this._stateRawtext(cp);
1296
+ }
1297
+ }
1298
+ // RAWTEXT end tag name state
1299
+ //------------------------------------------------------------------
1300
+ _stateRawtextEndTagName(cp) {
1301
+ if (this.handleSpecialEndTag(cp)) {
1302
+ this._emitChars('</', -1);
1303
+ this.state = State.RAWTEXT;
1304
+ this._stateRawtext(cp);
1305
+ }
1306
+ }
1307
+ // Script data less-than sign state
1308
+ //------------------------------------------------------------------
1309
+ _stateScriptDataLessThanSign(cp) {
1310
+ switch (cp) {
1311
+ case $.SOLIDUS: {
1312
+ this.state = State.SCRIPT_DATA_END_TAG_OPEN;
1313
+ break;
1314
+ }
1315
+ case $.EXCLAMATION_MARK: {
1316
+ this.state = State.SCRIPT_DATA_ESCAPE_START;
1317
+ this._emitChars('<!');
1318
+ break;
1319
+ }
1320
+ default: {
1321
+ this._emitChars('<', -1);
1322
+ this.state = State.SCRIPT_DATA;
1323
+ this._stateScriptData(cp);
1324
+ }
1325
+ }
1326
+ }
1327
+ // Script data end tag open state
1328
+ //------------------------------------------------------------------
1329
+ _stateScriptDataEndTagOpen(cp) {
1330
+ if (isAsciiLetter(cp)) {
1331
+ this.state = State.SCRIPT_DATA_END_TAG_NAME;
1332
+ this._stateScriptDataEndTagName(cp);
1333
+ }
1334
+ else {
1335
+ this._emitChars('</', -1);
1336
+ this.state = State.SCRIPT_DATA;
1337
+ this._stateScriptData(cp);
1338
+ }
1339
+ }
1340
+ // Script data end tag name state
1341
+ //------------------------------------------------------------------
1342
+ _stateScriptDataEndTagName(cp) {
1343
+ if (this.handleSpecialEndTag(cp)) {
1344
+ this._emitChars('</', -1);
1345
+ this.state = State.SCRIPT_DATA;
1346
+ this._stateScriptData(cp);
1347
+ }
1348
+ }
1349
+ // Script data escape start state
1350
+ //------------------------------------------------------------------
1351
+ _stateScriptDataEscapeStart(cp) {
1352
+ if (cp === $.HYPHEN_MINUS) {
1353
+ this.state = State.SCRIPT_DATA_ESCAPE_START_DASH;
1354
+ this._emitChars('-');
1355
+ }
1356
+ else {
1357
+ this.state = State.SCRIPT_DATA;
1358
+ this._stateScriptData(cp);
1359
+ }
1360
+ }
1361
+ // Script data escape start dash state
1362
+ //------------------------------------------------------------------
1363
+ _stateScriptDataEscapeStartDash(cp) {
1364
+ if (cp === $.HYPHEN_MINUS) {
1365
+ this.state = State.SCRIPT_DATA_ESCAPED_DASH_DASH;
1366
+ this._emitChars('-');
1367
+ }
1368
+ else {
1369
+ this.state = State.SCRIPT_DATA;
1370
+ this._stateScriptData(cp);
1371
+ }
1372
+ }
1373
+ // Script data escaped state
1374
+ //------------------------------------------------------------------
1375
+ _stateScriptDataEscaped(cp) {
1376
+ switch (cp) {
1377
+ case $.HYPHEN_MINUS: {
1378
+ this.state = State.SCRIPT_DATA_ESCAPED_DASH;
1379
+ this._emitChars('-');
1380
+ break;
1381
+ }
1382
+ case $.LESS_THAN_SIGN: {
1383
+ this.state = State.SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN;
1384
+ break;
1385
+ }
1386
+ case $.NULL: {
1387
+ this._err(ERR.unexpectedNullCharacter);
1388
+ this._emitChars(REPLACEMENT_CHARACTER);
1389
+ break;
1390
+ }
1391
+ case $.EOF: {
1392
+ this._err(ERR.eofInScriptHtmlCommentLikeText);
1393
+ this._emitEOFToken();
1394
+ break;
1395
+ }
1396
+ default: {
1397
+ this._emitCodePoint(cp);
1398
+ }
1399
+ }
1400
+ }
1401
+ // Script data escaped dash state
1402
+ //------------------------------------------------------------------
1403
+ _stateScriptDataEscapedDash(cp) {
1404
+ switch (cp) {
1405
+ case $.HYPHEN_MINUS: {
1406
+ this.state = State.SCRIPT_DATA_ESCAPED_DASH_DASH;
1407
+ this._emitChars('-');
1408
+ break;
1409
+ }
1410
+ case $.LESS_THAN_SIGN: {
1411
+ this.state = State.SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN;
1412
+ break;
1413
+ }
1414
+ case $.NULL: {
1415
+ this._err(ERR.unexpectedNullCharacter);
1416
+ this.state = State.SCRIPT_DATA_ESCAPED;
1417
+ this._emitChars(REPLACEMENT_CHARACTER);
1418
+ break;
1419
+ }
1420
+ case $.EOF: {
1421
+ this._err(ERR.eofInScriptHtmlCommentLikeText);
1422
+ this._emitEOFToken();
1423
+ break;
1424
+ }
1425
+ default: {
1426
+ this.state = State.SCRIPT_DATA_ESCAPED;
1427
+ this._emitCodePoint(cp);
1428
+ }
1429
+ }
1430
+ }
1431
+ // Script data escaped dash dash state
1432
+ //------------------------------------------------------------------
1433
+ _stateScriptDataEscapedDashDash(cp) {
1434
+ switch (cp) {
1435
+ case $.HYPHEN_MINUS: {
1436
+ this._emitChars('-');
1437
+ break;
1438
+ }
1439
+ case $.LESS_THAN_SIGN: {
1440
+ this.state = State.SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN;
1441
+ break;
1442
+ }
1443
+ case $.GREATER_THAN_SIGN: {
1444
+ this.state = State.SCRIPT_DATA;
1445
+ this._emitChars('>');
1446
+ break;
1447
+ }
1448
+ case $.NULL: {
1449
+ this._err(ERR.unexpectedNullCharacter);
1450
+ this.state = State.SCRIPT_DATA_ESCAPED;
1451
+ this._emitChars(REPLACEMENT_CHARACTER);
1452
+ break;
1453
+ }
1454
+ case $.EOF: {
1455
+ this._err(ERR.eofInScriptHtmlCommentLikeText);
1456
+ this._emitEOFToken();
1457
+ break;
1458
+ }
1459
+ default: {
1460
+ this.state = State.SCRIPT_DATA_ESCAPED;
1461
+ this._emitCodePoint(cp);
1462
+ }
1463
+ }
1464
+ }
1465
+ // Script data escaped less-than sign state
1466
+ //------------------------------------------------------------------
1467
+ _stateScriptDataEscapedLessThanSign(cp) {
1468
+ if (cp === $.SOLIDUS) {
1469
+ this.state = State.SCRIPT_DATA_ESCAPED_END_TAG_OPEN;
1470
+ }
1471
+ else if (isAsciiLetter(cp)) {
1472
+ this._emitChars('<', -1);
1473
+ this.state = State.SCRIPT_DATA_DOUBLE_ESCAPE_START;
1474
+ this._stateScriptDataDoubleEscapeStart(cp);
1475
+ }
1476
+ else {
1477
+ this._emitChars('<', -1);
1478
+ this.state = State.SCRIPT_DATA_ESCAPED;
1479
+ this._stateScriptDataEscaped(cp);
1480
+ }
1481
+ }
1482
+ // Script data escaped end tag open state
1483
+ //------------------------------------------------------------------
1484
+ _stateScriptDataEscapedEndTagOpen(cp) {
1485
+ if (isAsciiLetter(cp)) {
1486
+ this.state = State.SCRIPT_DATA_ESCAPED_END_TAG_NAME;
1487
+ this._stateScriptDataEscapedEndTagName(cp);
1488
+ }
1489
+ else {
1490
+ this._emitChars('</', -1);
1491
+ this.state = State.SCRIPT_DATA_ESCAPED;
1492
+ this._stateScriptDataEscaped(cp);
1493
+ }
1494
+ }
1495
+ // Script data escaped end tag name state
1496
+ //------------------------------------------------------------------
1497
+ _stateScriptDataEscapedEndTagName(cp) {
1498
+ if (this.handleSpecialEndTag(cp)) {
1499
+ this._emitChars('</', -1);
1500
+ this.state = State.SCRIPT_DATA_ESCAPED;
1501
+ this._stateScriptDataEscaped(cp);
1502
+ }
1503
+ }
1504
+ // Script data double escape start state
1505
+ //------------------------------------------------------------------
1506
+ _stateScriptDataDoubleEscapeStart(cp) {
1507
+ if (this.preprocessor.startsWith($$.SCRIPT, false) &&
1508
+ isScriptDataDoubleEscapeSequenceEnd(this.preprocessor.peek($$.SCRIPT.length))) {
1509
+ this._emitCodePoint(cp);
1510
+ for (let i = 0; i < $$.SCRIPT.length; i++) {
1511
+ this._emitCodePoint(this._consume());
1512
+ }
1513
+ this.state = State.SCRIPT_DATA_DOUBLE_ESCAPED;
1514
+ }
1515
+ else if (!this._ensureHibernation()) {
1516
+ this.state = State.SCRIPT_DATA_ESCAPED;
1517
+ this._stateScriptDataEscaped(cp);
1518
+ }
1519
+ }
1520
+ // Script data double escaped state
1521
+ //------------------------------------------------------------------
1522
+ _stateScriptDataDoubleEscaped(cp) {
1523
+ switch (cp) {
1524
+ case $.HYPHEN_MINUS: {
1525
+ this.state = State.SCRIPT_DATA_DOUBLE_ESCAPED_DASH;
1526
+ this._emitChars('-');
1527
+ break;
1528
+ }
1529
+ case $.LESS_THAN_SIGN: {
1530
+ this.state = State.SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN;
1531
+ this._emitChars('<');
1532
+ break;
1533
+ }
1534
+ case $.NULL: {
1535
+ this._err(ERR.unexpectedNullCharacter);
1536
+ this._emitChars(REPLACEMENT_CHARACTER);
1537
+ break;
1538
+ }
1539
+ case $.EOF: {
1540
+ this._err(ERR.eofInScriptHtmlCommentLikeText);
1541
+ this._emitEOFToken();
1542
+ break;
1543
+ }
1544
+ default: {
1545
+ this._emitCodePoint(cp);
1546
+ }
1547
+ }
1548
+ }
1549
+ // Script data double escaped dash state
1550
+ //------------------------------------------------------------------
1551
+ _stateScriptDataDoubleEscapedDash(cp) {
1552
+ switch (cp) {
1553
+ case $.HYPHEN_MINUS: {
1554
+ this.state = State.SCRIPT_DATA_DOUBLE_ESCAPED_DASH_DASH;
1555
+ this._emitChars('-');
1556
+ break;
1557
+ }
1558
+ case $.LESS_THAN_SIGN: {
1559
+ this.state = State.SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN;
1560
+ this._emitChars('<');
1561
+ break;
1562
+ }
1563
+ case $.NULL: {
1564
+ this._err(ERR.unexpectedNullCharacter);
1565
+ this.state = State.SCRIPT_DATA_DOUBLE_ESCAPED;
1566
+ this._emitChars(REPLACEMENT_CHARACTER);
1567
+ break;
1568
+ }
1569
+ case $.EOF: {
1570
+ this._err(ERR.eofInScriptHtmlCommentLikeText);
1571
+ this._emitEOFToken();
1572
+ break;
1573
+ }
1574
+ default: {
1575
+ this.state = State.SCRIPT_DATA_DOUBLE_ESCAPED;
1576
+ this._emitCodePoint(cp);
1577
+ }
1578
+ }
1579
+ }
1580
+ // Script data double escaped dash dash state
1581
+ //------------------------------------------------------------------
1582
+ _stateScriptDataDoubleEscapedDashDash(cp) {
1583
+ switch (cp) {
1584
+ case $.HYPHEN_MINUS: {
1585
+ this._emitChars('-');
1586
+ break;
1587
+ }
1588
+ case $.LESS_THAN_SIGN: {
1589
+ this.state = State.SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN;
1590
+ this._emitChars('<');
1591
+ break;
1592
+ }
1593
+ case $.GREATER_THAN_SIGN: {
1594
+ this.state = State.SCRIPT_DATA;
1595
+ this._emitChars('>');
1596
+ break;
1597
+ }
1598
+ case $.NULL: {
1599
+ this._err(ERR.unexpectedNullCharacter);
1600
+ this.state = State.SCRIPT_DATA_DOUBLE_ESCAPED;
1601
+ this._emitChars(REPLACEMENT_CHARACTER);
1602
+ break;
1603
+ }
1604
+ case $.EOF: {
1605
+ this._err(ERR.eofInScriptHtmlCommentLikeText);
1606
+ this._emitEOFToken();
1607
+ break;
1608
+ }
1609
+ default: {
1610
+ this.state = State.SCRIPT_DATA_DOUBLE_ESCAPED;
1611
+ this._emitCodePoint(cp);
1612
+ }
1613
+ }
1614
+ }
1615
+ // Script data double escaped less-than sign state
1616
+ //------------------------------------------------------------------
1617
+ _stateScriptDataDoubleEscapedLessThanSign(cp) {
1618
+ if (cp === $.SOLIDUS) {
1619
+ this.state = State.SCRIPT_DATA_DOUBLE_ESCAPE_END;
1620
+ this._emitChars('/');
1621
+ }
1622
+ else {
1623
+ this.state = State.SCRIPT_DATA_DOUBLE_ESCAPED;
1624
+ this._stateScriptDataDoubleEscaped(cp);
1625
+ }
1626
+ }
1627
+ // Script data double escape end state
1628
+ //------------------------------------------------------------------
1629
+ _stateScriptDataDoubleEscapeEnd(cp) {
1630
+ if (this.preprocessor.startsWith($$.SCRIPT, false) &&
1631
+ isScriptDataDoubleEscapeSequenceEnd(this.preprocessor.peek($$.SCRIPT.length))) {
1632
+ this._emitCodePoint(cp);
1633
+ for (let i = 0; i < $$.SCRIPT.length; i++) {
1634
+ this._emitCodePoint(this._consume());
1635
+ }
1636
+ this.state = State.SCRIPT_DATA_ESCAPED;
1637
+ }
1638
+ else if (!this._ensureHibernation()) {
1639
+ this.state = State.SCRIPT_DATA_DOUBLE_ESCAPED;
1640
+ this._stateScriptDataDoubleEscaped(cp);
1641
+ }
1642
+ }
1643
+ // Before attribute name state
1644
+ //------------------------------------------------------------------
1645
+ _stateBeforeAttributeName(cp) {
1646
+ switch (cp) {
1647
+ case $.SPACE:
1648
+ case $.LINE_FEED:
1649
+ case $.TABULATION:
1650
+ case $.FORM_FEED: {
1651
+ // Ignore whitespace
1652
+ break;
1653
+ }
1654
+ case $.SOLIDUS:
1655
+ case $.GREATER_THAN_SIGN:
1656
+ case $.EOF: {
1657
+ this.state = State.AFTER_ATTRIBUTE_NAME;
1658
+ this._stateAfterAttributeName(cp);
1659
+ break;
1660
+ }
1661
+ case $.EQUALS_SIGN: {
1662
+ this._err(ERR.unexpectedEqualsSignBeforeAttributeName);
1663
+ this._createAttr('=');
1664
+ this.state = State.ATTRIBUTE_NAME;
1665
+ break;
1666
+ }
1667
+ default: {
1668
+ this._createAttr('');
1669
+ this.state = State.ATTRIBUTE_NAME;
1670
+ this._stateAttributeName(cp);
1671
+ }
1672
+ }
1673
+ }
1674
+ // Attribute name state
1675
+ //------------------------------------------------------------------
1676
+ _stateAttributeName(cp) {
1677
+ switch (cp) {
1678
+ case $.SPACE:
1679
+ case $.LINE_FEED:
1680
+ case $.TABULATION:
1681
+ case $.FORM_FEED:
1682
+ case $.SOLIDUS:
1683
+ case $.GREATER_THAN_SIGN:
1684
+ case $.EOF: {
1685
+ this._leaveAttrName();
1686
+ this.state = State.AFTER_ATTRIBUTE_NAME;
1687
+ this._stateAfterAttributeName(cp);
1688
+ break;
1689
+ }
1690
+ case $.EQUALS_SIGN: {
1691
+ this._leaveAttrName();
1692
+ this.state = State.BEFORE_ATTRIBUTE_VALUE;
1693
+ break;
1694
+ }
1695
+ case $.QUOTATION_MARK:
1696
+ case $.APOSTROPHE:
1697
+ case $.LESS_THAN_SIGN: {
1698
+ this._err(ERR.unexpectedCharacterInAttributeName);
1699
+ this.currentAttr.name += String.fromCodePoint(cp);
1700
+ break;
1701
+ }
1702
+ case $.NULL: {
1703
+ this._err(ERR.unexpectedNullCharacter);
1704
+ this.currentAttr.name += REPLACEMENT_CHARACTER;
1705
+ break;
1706
+ }
1707
+ default: {
1708
+ this.currentAttr.name += String.fromCodePoint(isAsciiUpper(cp) ? toAsciiLower(cp) : cp);
1709
+ }
1710
+ }
1711
+ }
1712
+ // After attribute name state
1713
+ //------------------------------------------------------------------
1714
+ _stateAfterAttributeName(cp) {
1715
+ switch (cp) {
1716
+ case $.SPACE:
1717
+ case $.LINE_FEED:
1718
+ case $.TABULATION:
1719
+ case $.FORM_FEED: {
1720
+ // Ignore whitespace
1721
+ break;
1722
+ }
1723
+ case $.SOLIDUS: {
1724
+ this.state = State.SELF_CLOSING_START_TAG;
1725
+ break;
1726
+ }
1727
+ case $.EQUALS_SIGN: {
1728
+ this.state = State.BEFORE_ATTRIBUTE_VALUE;
1729
+ break;
1730
+ }
1731
+ case $.GREATER_THAN_SIGN: {
1732
+ this.state = State.DATA;
1733
+ this.emitCurrentTagToken();
1734
+ break;
1735
+ }
1736
+ case $.EOF: {
1737
+ this._err(ERR.eofInTag);
1738
+ this._emitEOFToken();
1739
+ break;
1740
+ }
1741
+ default: {
1742
+ this._createAttr('');
1743
+ this.state = State.ATTRIBUTE_NAME;
1744
+ this._stateAttributeName(cp);
1745
+ }
1746
+ }
1747
+ }
1748
+ // Before attribute value state
1749
+ //------------------------------------------------------------------
1750
+ _stateBeforeAttributeValue(cp) {
1751
+ switch (cp) {
1752
+ case $.SPACE:
1753
+ case $.LINE_FEED:
1754
+ case $.TABULATION:
1755
+ case $.FORM_FEED: {
1756
+ // Ignore whitespace
1757
+ break;
1758
+ }
1759
+ case $.QUOTATION_MARK: {
1760
+ this.state = State.ATTRIBUTE_VALUE_DOUBLE_QUOTED;
1761
+ break;
1762
+ }
1763
+ case $.APOSTROPHE: {
1764
+ this.state = State.ATTRIBUTE_VALUE_SINGLE_QUOTED;
1765
+ break;
1766
+ }
1767
+ case $.GREATER_THAN_SIGN: {
1768
+ this._err(ERR.missingAttributeValue);
1769
+ this.state = State.DATA;
1770
+ this.emitCurrentTagToken();
1771
+ break;
1772
+ }
1773
+ default: {
1774
+ this.state = State.ATTRIBUTE_VALUE_UNQUOTED;
1775
+ this._stateAttributeValueUnquoted(cp);
1776
+ }
1777
+ }
1778
+ }
1779
+ // Attribute value (double-quoted) state
1780
+ //------------------------------------------------------------------
1781
+ _stateAttributeValueDoubleQuoted(cp) {
1782
+ switch (cp) {
1783
+ case $.QUOTATION_MARK: {
1784
+ this.state = State.AFTER_ATTRIBUTE_VALUE_QUOTED;
1785
+ break;
1786
+ }
1787
+ case $.AMPERSAND: {
1788
+ this._startCharacterReference();
1789
+ break;
1790
+ }
1791
+ case $.NULL: {
1792
+ this._err(ERR.unexpectedNullCharacter);
1793
+ this._extendRunWith(REPLACEMENT_CHARACTER, 0);
1794
+ break;
1795
+ }
1796
+ case $.EOF: {
1797
+ this._err(ERR.eofInTag);
1798
+ this._emitEOFToken();
1799
+ break;
1800
+ }
1801
+ default: {
1802
+ this._extendRun(cp);
1803
+ }
1804
+ }
1805
+ }
1806
+ // Attribute value (single-quoted) state
1807
+ //------------------------------------------------------------------
1808
+ _stateAttributeValueSingleQuoted(cp) {
1809
+ switch (cp) {
1810
+ case $.APOSTROPHE: {
1811
+ this.state = State.AFTER_ATTRIBUTE_VALUE_QUOTED;
1812
+ break;
1813
+ }
1814
+ case $.AMPERSAND: {
1815
+ this._startCharacterReference();
1816
+ break;
1817
+ }
1818
+ case $.NULL: {
1819
+ this._err(ERR.unexpectedNullCharacter);
1820
+ this._extendRunWith(REPLACEMENT_CHARACTER, 0);
1821
+ break;
1822
+ }
1823
+ case $.EOF: {
1824
+ this._err(ERR.eofInTag);
1825
+ this._emitEOFToken();
1826
+ break;
1827
+ }
1828
+ default: {
1829
+ this._extendRun(cp);
1830
+ }
1831
+ }
1832
+ }
1833
+ // Attribute value (unquoted) state
1834
+ //------------------------------------------------------------------
1835
+ _stateAttributeValueUnquoted(cp) {
1836
+ switch (cp) {
1837
+ case $.SPACE:
1838
+ case $.LINE_FEED:
1839
+ case $.TABULATION:
1840
+ case $.FORM_FEED: {
1841
+ this._leaveAttrValue();
1842
+ this.state = State.BEFORE_ATTRIBUTE_NAME;
1843
+ break;
1844
+ }
1845
+ case $.AMPERSAND: {
1846
+ this._startCharacterReference();
1847
+ break;
1848
+ }
1849
+ case $.GREATER_THAN_SIGN: {
1850
+ this._leaveAttrValue();
1851
+ this.state = State.DATA;
1852
+ this.emitCurrentTagToken();
1853
+ break;
1854
+ }
1855
+ case $.NULL: {
1856
+ this._err(ERR.unexpectedNullCharacter);
1857
+ this._extendRunWith(REPLACEMENT_CHARACTER, 0);
1858
+ break;
1859
+ }
1860
+ case $.QUOTATION_MARK:
1861
+ case $.APOSTROPHE:
1862
+ case $.LESS_THAN_SIGN:
1863
+ case $.EQUALS_SIGN:
1864
+ case $.GRAVE_ACCENT: {
1865
+ this._err(ERR.unexpectedCharacterInUnquotedAttributeValue);
1866
+ this._extendRun(cp);
1867
+ break;
1868
+ }
1869
+ case $.EOF: {
1870
+ this._err(ERR.eofInTag);
1871
+ this._emitEOFToken();
1872
+ break;
1873
+ }
1874
+ default: {
1875
+ this._extendRun(cp);
1876
+ }
1877
+ }
1878
+ }
1879
+ // After attribute value (quoted) state
1880
+ //------------------------------------------------------------------
1881
+ _stateAfterAttributeValueQuoted(cp) {
1882
+ switch (cp) {
1883
+ case $.SPACE:
1884
+ case $.LINE_FEED:
1885
+ case $.TABULATION:
1886
+ case $.FORM_FEED: {
1887
+ this._leaveAttrValue();
1888
+ this.state = State.BEFORE_ATTRIBUTE_NAME;
1889
+ break;
1890
+ }
1891
+ case $.SOLIDUS: {
1892
+ this._leaveAttrValue();
1893
+ this.state = State.SELF_CLOSING_START_TAG;
1894
+ break;
1895
+ }
1896
+ case $.GREATER_THAN_SIGN: {
1897
+ this._leaveAttrValue();
1898
+ this.state = State.DATA;
1899
+ this.emitCurrentTagToken();
1900
+ break;
1901
+ }
1902
+ case $.EOF: {
1903
+ this._err(ERR.eofInTag);
1904
+ this._emitEOFToken();
1905
+ break;
1906
+ }
1907
+ default: {
1908
+ this._err(ERR.missingWhitespaceBetweenAttributes);
1909
+ this._leaveAttrValue();
1910
+ this.state = State.BEFORE_ATTRIBUTE_NAME;
1911
+ this._stateBeforeAttributeName(cp);
1912
+ }
1913
+ }
1914
+ }
1915
+ // Self-closing start tag state
1916
+ //------------------------------------------------------------------
1917
+ _stateSelfClosingStartTag(cp) {
1918
+ switch (cp) {
1919
+ case $.GREATER_THAN_SIGN: {
1920
+ const token = this.currentToken;
1921
+ token.selfClosing = true;
1922
+ this.state = State.DATA;
1923
+ this.emitCurrentTagToken();
1924
+ break;
1925
+ }
1926
+ case $.EOF: {
1927
+ this._err(ERR.eofInTag);
1928
+ this._emitEOFToken();
1929
+ break;
1930
+ }
1931
+ default: {
1932
+ this._err(ERR.unexpectedSolidusInTag);
1933
+ this.state = State.BEFORE_ATTRIBUTE_NAME;
1934
+ this._stateBeforeAttributeName(cp);
1935
+ }
1936
+ }
1937
+ }
1938
+ // Bogus comment state
1939
+ //------------------------------------------------------------------
1940
+ _stateBogusComment(cp) {
1941
+ const token = this.currentToken;
1942
+ switch (cp) {
1943
+ case $.GREATER_THAN_SIGN: {
1944
+ this.state = State.DATA;
1945
+ this.emitCurrentComment(token);
1946
+ break;
1947
+ }
1948
+ case $.EOF: {
1949
+ this.emitCurrentComment(token);
1950
+ this._emitEOFToken();
1951
+ break;
1952
+ }
1953
+ case $.NULL: {
1954
+ this._err(ERR.unexpectedNullCharacter);
1955
+ this._extendRunWith(REPLACEMENT_CHARACTER, 0);
1956
+ break;
1957
+ }
1958
+ default: {
1959
+ this._extendRun(cp);
1960
+ }
1961
+ }
1962
+ }
1963
+ // Markup declaration open state
1964
+ //------------------------------------------------------------------
1965
+ _stateMarkupDeclarationOpen(cp) {
1966
+ if (this._consumeSequenceIfMatch($$.DASH_DASH, true)) {
1967
+ this._createCommentToken($$.DASH_DASH.length + 1);
1968
+ this.state = State.COMMENT_START;
1969
+ }
1970
+ else if (this._consumeSequenceIfMatch($$.DOCTYPE, false)) {
1971
+ // NOTE: Doctypes tokens are created without fixed offsets. We keep track of the moment a doctype *might* start here.
1972
+ this.currentLocation = this.getCurrentLocation($$.DOCTYPE.length + 1);
1973
+ this.state = State.DOCTYPE;
1974
+ }
1975
+ else if (this._consumeSequenceIfMatch($$.CDATA_START, true)) {
1976
+ if (this.inForeignNode) {
1977
+ this.state = State.CDATA_SECTION;
1978
+ }
1979
+ else {
1980
+ this._err(ERR.cdataInHtmlContent);
1981
+ this._createCommentToken($$.CDATA_START.length + 1);
1982
+ this.currentToken.data = '[CDATA[';
1983
+ this.state = State.BOGUS_COMMENT;
1984
+ }
1985
+ }
1986
+ //NOTE: Sequence lookups can be abrupted by hibernation. In that case, lookup
1987
+ //results are no longer valid and we will need to start over.
1988
+ else if (!this._ensureHibernation()) {
1989
+ this._err(ERR.incorrectlyOpenedComment);
1990
+ this._createCommentToken(2);
1991
+ this.state = State.BOGUS_COMMENT;
1992
+ this._stateBogusComment(cp);
1993
+ }
1994
+ }
1995
+ // Comment start state
1996
+ //------------------------------------------------------------------
1997
+ _stateCommentStart(cp) {
1998
+ switch (cp) {
1999
+ case $.HYPHEN_MINUS: {
2000
+ this.state = State.COMMENT_START_DASH;
2001
+ break;
2002
+ }
2003
+ case $.GREATER_THAN_SIGN: {
2004
+ this._err(ERR.abruptClosingOfEmptyComment);
2005
+ this.state = State.DATA;
2006
+ const token = this.currentToken;
2007
+ this.emitCurrentComment(token);
2008
+ break;
2009
+ }
2010
+ default: {
2011
+ this.state = State.COMMENT;
2012
+ this._stateComment(cp);
2013
+ }
2014
+ }
2015
+ }
2016
+ // Comment start dash state
2017
+ //------------------------------------------------------------------
2018
+ _stateCommentStartDash(cp) {
2019
+ const token = this.currentToken;
2020
+ switch (cp) {
2021
+ case $.HYPHEN_MINUS: {
2022
+ this.state = State.COMMENT_END;
2023
+ break;
2024
+ }
2025
+ case $.GREATER_THAN_SIGN: {
2026
+ this._err(ERR.abruptClosingOfEmptyComment);
2027
+ this.state = State.DATA;
2028
+ this.emitCurrentComment(token);
2029
+ break;
2030
+ }
2031
+ case $.EOF: {
2032
+ this._err(ERR.eofInComment);
2033
+ this.emitCurrentComment(token);
2034
+ this._emitEOFToken();
2035
+ break;
2036
+ }
2037
+ default: {
2038
+ this._extendRunWith('-', -1);
2039
+ this.state = State.COMMENT;
2040
+ this._stateComment(cp);
2041
+ }
2042
+ }
2043
+ }
2044
+ // Comment state
2045
+ //------------------------------------------------------------------
2046
+ _stateComment(cp) {
2047
+ const token = this.currentToken;
2048
+ switch (cp) {
2049
+ case $.HYPHEN_MINUS: {
2050
+ this.state = State.COMMENT_END_DASH;
2051
+ break;
2052
+ }
2053
+ case $.LESS_THAN_SIGN: {
2054
+ this._extendRunWith('<', 0);
2055
+ this.state = State.COMMENT_LESS_THAN_SIGN;
2056
+ break;
2057
+ }
2058
+ case $.NULL: {
2059
+ this._err(ERR.unexpectedNullCharacter);
2060
+ this._extendRunWith(REPLACEMENT_CHARACTER, 0);
2061
+ break;
2062
+ }
2063
+ case $.EOF: {
2064
+ this._err(ERR.eofInComment);
2065
+ this.emitCurrentComment(token);
2066
+ this._emitEOFToken();
2067
+ break;
2068
+ }
2069
+ default: {
2070
+ this._extendRun(cp);
2071
+ }
2072
+ }
2073
+ }
2074
+ // Comment less-than sign state
2075
+ //------------------------------------------------------------------
2076
+ _stateCommentLessThanSign(cp) {
2077
+ switch (cp) {
2078
+ case $.EXCLAMATION_MARK: {
2079
+ this._extendRunWith('!', 0);
2080
+ this.state = State.COMMENT_LESS_THAN_SIGN_BANG;
2081
+ break;
2082
+ }
2083
+ case $.LESS_THAN_SIGN: {
2084
+ this._extendRunWith('<', 0);
2085
+ break;
2086
+ }
2087
+ default: {
2088
+ this.state = State.COMMENT;
2089
+ this._stateComment(cp);
2090
+ }
2091
+ }
2092
+ }
2093
+ // Comment less-than sign bang state
2094
+ //------------------------------------------------------------------
2095
+ _stateCommentLessThanSignBang(cp) {
2096
+ if (cp === $.HYPHEN_MINUS) {
2097
+ this.state = State.COMMENT_LESS_THAN_SIGN_BANG_DASH;
2098
+ }
2099
+ else {
2100
+ this.state = State.COMMENT;
2101
+ this._stateComment(cp);
2102
+ }
2103
+ }
2104
+ // Comment less-than sign bang dash state
2105
+ //------------------------------------------------------------------
2106
+ _stateCommentLessThanSignBangDash(cp) {
2107
+ if (cp === $.HYPHEN_MINUS) {
2108
+ this.state = State.COMMENT_LESS_THAN_SIGN_BANG_DASH_DASH;
2109
+ }
2110
+ else {
2111
+ this.state = State.COMMENT_END_DASH;
2112
+ this._stateCommentEndDash(cp);
2113
+ }
2114
+ }
2115
+ // Comment less-than sign bang dash dash state
2116
+ //------------------------------------------------------------------
2117
+ _stateCommentLessThanSignBangDashDash(cp) {
2118
+ if (cp !== $.GREATER_THAN_SIGN && cp !== $.EOF) {
2119
+ this._err(ERR.nestedComment);
2120
+ }
2121
+ this.state = State.COMMENT_END;
2122
+ this._stateCommentEnd(cp);
2123
+ }
2124
+ // Comment end dash state
2125
+ //------------------------------------------------------------------
2126
+ _stateCommentEndDash(cp) {
2127
+ const token = this.currentToken;
2128
+ switch (cp) {
2129
+ case $.HYPHEN_MINUS: {
2130
+ this.state = State.COMMENT_END;
2131
+ break;
2132
+ }
2133
+ case $.EOF: {
2134
+ this._err(ERR.eofInComment);
2135
+ this.emitCurrentComment(token);
2136
+ this._emitEOFToken();
2137
+ break;
2138
+ }
2139
+ default: {
2140
+ this._extendRunWith('-', -1);
2141
+ this.state = State.COMMENT;
2142
+ this._stateComment(cp);
2143
+ }
2144
+ }
2145
+ }
2146
+ // Comment end state
2147
+ //------------------------------------------------------------------
2148
+ _stateCommentEnd(cp) {
2149
+ const token = this.currentToken;
2150
+ switch (cp) {
2151
+ case $.GREATER_THAN_SIGN: {
2152
+ this.state = State.DATA;
2153
+ this.emitCurrentComment(token);
2154
+ break;
2155
+ }
2156
+ case $.EXCLAMATION_MARK: {
2157
+ this.state = State.COMMENT_END_BANG;
2158
+ break;
2159
+ }
2160
+ case $.HYPHEN_MINUS: {
2161
+ this._extendRunWith('-', -2);
2162
+ break;
2163
+ }
2164
+ case $.EOF: {
2165
+ this._err(ERR.eofInComment);
2166
+ this.emitCurrentComment(token);
2167
+ this._emitEOFToken();
2168
+ break;
2169
+ }
2170
+ default: {
2171
+ this._extendRunWith('--', -1);
2172
+ this.state = State.COMMENT;
2173
+ this._stateComment(cp);
2174
+ }
2175
+ }
2176
+ }
2177
+ // Comment end bang state
2178
+ //------------------------------------------------------------------
2179
+ _stateCommentEndBang(cp) {
2180
+ const token = this.currentToken;
2181
+ switch (cp) {
2182
+ case $.HYPHEN_MINUS: {
2183
+ this._extendRunWith('--!', -1);
2184
+ this.state = State.COMMENT_END_DASH;
2185
+ break;
2186
+ }
2187
+ case $.GREATER_THAN_SIGN: {
2188
+ this._err(ERR.incorrectlyClosedComment);
2189
+ this.state = State.DATA;
2190
+ this.emitCurrentComment(token);
2191
+ break;
2192
+ }
2193
+ case $.EOF: {
2194
+ this._err(ERR.eofInComment);
2195
+ this.emitCurrentComment(token);
2196
+ this._emitEOFToken();
2197
+ break;
2198
+ }
2199
+ default: {
2200
+ this._extendRunWith('--!', -1);
2201
+ this.state = State.COMMENT;
2202
+ this._stateComment(cp);
2203
+ }
2204
+ }
2205
+ }
2206
+ // DOCTYPE state
2207
+ //------------------------------------------------------------------
2208
+ _stateDoctype(cp) {
2209
+ switch (cp) {
2210
+ case $.SPACE:
2211
+ case $.LINE_FEED:
2212
+ case $.TABULATION:
2213
+ case $.FORM_FEED: {
2214
+ this.state = State.BEFORE_DOCTYPE_NAME;
2215
+ break;
2216
+ }
2217
+ case $.GREATER_THAN_SIGN: {
2218
+ this.state = State.BEFORE_DOCTYPE_NAME;
2219
+ this._stateBeforeDoctypeName(cp);
2220
+ break;
2221
+ }
2222
+ case $.EOF: {
2223
+ this._err(ERR.eofInDoctype);
2224
+ this._createDoctypeToken(null);
2225
+ const token = this.currentToken;
2226
+ token.forceQuirks = true;
2227
+ this.emitCurrentDoctype(token);
2228
+ this._emitEOFToken();
2229
+ break;
2230
+ }
2231
+ default: {
2232
+ this._err(ERR.missingWhitespaceBeforeDoctypeName);
2233
+ this.state = State.BEFORE_DOCTYPE_NAME;
2234
+ this._stateBeforeDoctypeName(cp);
2235
+ }
2236
+ }
2237
+ }
2238
+ // Before DOCTYPE name state
2239
+ //------------------------------------------------------------------
2240
+ _stateBeforeDoctypeName(cp) {
2241
+ if (isAsciiUpper(cp)) {
2242
+ this._createDoctypeToken(String.fromCharCode(toAsciiLower(cp)));
2243
+ this.state = State.DOCTYPE_NAME;
2244
+ }
2245
+ else
2246
+ switch (cp) {
2247
+ case $.SPACE:
2248
+ case $.LINE_FEED:
2249
+ case $.TABULATION:
2250
+ case $.FORM_FEED: {
2251
+ // Ignore whitespace
2252
+ break;
2253
+ }
2254
+ case $.NULL: {
2255
+ this._err(ERR.unexpectedNullCharacter);
2256
+ this._createDoctypeToken(REPLACEMENT_CHARACTER);
2257
+ this.state = State.DOCTYPE_NAME;
2258
+ break;
2259
+ }
2260
+ case $.GREATER_THAN_SIGN: {
2261
+ this._err(ERR.missingDoctypeName);
2262
+ this._createDoctypeToken(null);
2263
+ const token = this.currentToken;
2264
+ token.forceQuirks = true;
2265
+ this.emitCurrentDoctype(token);
2266
+ this.state = State.DATA;
2267
+ break;
2268
+ }
2269
+ case $.EOF: {
2270
+ this._err(ERR.eofInDoctype);
2271
+ this._createDoctypeToken(null);
2272
+ const token = this.currentToken;
2273
+ token.forceQuirks = true;
2274
+ this.emitCurrentDoctype(token);
2275
+ this._emitEOFToken();
2276
+ break;
2277
+ }
2278
+ default: {
2279
+ this._createDoctypeToken(String.fromCodePoint(cp));
2280
+ this.state = State.DOCTYPE_NAME;
2281
+ }
2282
+ }
2283
+ }
2284
+ // DOCTYPE name state
2285
+ //------------------------------------------------------------------
2286
+ _stateDoctypeName(cp) {
2287
+ const token = this.currentToken;
2288
+ switch (cp) {
2289
+ case $.SPACE:
2290
+ case $.LINE_FEED:
2291
+ case $.TABULATION:
2292
+ case $.FORM_FEED: {
2293
+ this.state = State.AFTER_DOCTYPE_NAME;
2294
+ break;
2295
+ }
2296
+ case $.GREATER_THAN_SIGN: {
2297
+ this.state = State.DATA;
2298
+ this.emitCurrentDoctype(token);
2299
+ break;
2300
+ }
2301
+ case $.NULL: {
2302
+ this._err(ERR.unexpectedNullCharacter);
2303
+ token.name += REPLACEMENT_CHARACTER;
2304
+ break;
2305
+ }
2306
+ case $.EOF: {
2307
+ this._err(ERR.eofInDoctype);
2308
+ token.forceQuirks = true;
2309
+ this.emitCurrentDoctype(token);
2310
+ this._emitEOFToken();
2311
+ break;
2312
+ }
2313
+ default: {
2314
+ token.name += String.fromCodePoint(isAsciiUpper(cp) ? toAsciiLower(cp) : cp);
2315
+ }
2316
+ }
2317
+ }
2318
+ // After DOCTYPE name state
2319
+ //------------------------------------------------------------------
2320
+ _stateAfterDoctypeName(cp) {
2321
+ const token = this.currentToken;
2322
+ switch (cp) {
2323
+ case $.SPACE:
2324
+ case $.LINE_FEED:
2325
+ case $.TABULATION:
2326
+ case $.FORM_FEED: {
2327
+ // Ignore whitespace
2328
+ break;
2329
+ }
2330
+ case $.GREATER_THAN_SIGN: {
2331
+ this.state = State.DATA;
2332
+ this.emitCurrentDoctype(token);
2333
+ break;
2334
+ }
2335
+ case $.EOF: {
2336
+ this._err(ERR.eofInDoctype);
2337
+ token.forceQuirks = true;
2338
+ this.emitCurrentDoctype(token);
2339
+ this._emitEOFToken();
2340
+ break;
2341
+ }
2342
+ default: {
2343
+ if (this._consumeSequenceIfMatch($$.PUBLIC, false)) {
2344
+ this.state = State.AFTER_DOCTYPE_PUBLIC_KEYWORD;
2345
+ }
2346
+ else if (this._consumeSequenceIfMatch($$.SYSTEM, false)) {
2347
+ this.state = State.AFTER_DOCTYPE_SYSTEM_KEYWORD;
2348
+ }
2349
+ //NOTE: sequence lookup can be abrupted by hibernation. In that case lookup
2350
+ //results are no longer valid and we will need to start over.
2351
+ else if (!this._ensureHibernation()) {
2352
+ this._err(ERR.invalidCharacterSequenceAfterDoctypeName);
2353
+ token.forceQuirks = true;
2354
+ this.state = State.BOGUS_DOCTYPE;
2355
+ this._stateBogusDoctype(cp);
2356
+ }
2357
+ }
2358
+ }
2359
+ }
2360
+ // After DOCTYPE public keyword state
2361
+ //------------------------------------------------------------------
2362
+ _stateAfterDoctypePublicKeyword(cp) {
2363
+ const token = this.currentToken;
2364
+ switch (cp) {
2365
+ case $.SPACE:
2366
+ case $.LINE_FEED:
2367
+ case $.TABULATION:
2368
+ case $.FORM_FEED: {
2369
+ this.state = State.BEFORE_DOCTYPE_PUBLIC_IDENTIFIER;
2370
+ break;
2371
+ }
2372
+ case $.QUOTATION_MARK: {
2373
+ this._err(ERR.missingWhitespaceAfterDoctypePublicKeyword);
2374
+ token.publicId = '';
2375
+ this.state = State.DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED;
2376
+ break;
2377
+ }
2378
+ case $.APOSTROPHE: {
2379
+ this._err(ERR.missingWhitespaceAfterDoctypePublicKeyword);
2380
+ token.publicId = '';
2381
+ this.state = State.DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED;
2382
+ break;
2383
+ }
2384
+ case $.GREATER_THAN_SIGN: {
2385
+ this._err(ERR.missingDoctypePublicIdentifier);
2386
+ token.forceQuirks = true;
2387
+ this.state = State.DATA;
2388
+ this.emitCurrentDoctype(token);
2389
+ break;
2390
+ }
2391
+ case $.EOF: {
2392
+ this._err(ERR.eofInDoctype);
2393
+ token.forceQuirks = true;
2394
+ this.emitCurrentDoctype(token);
2395
+ this._emitEOFToken();
2396
+ break;
2397
+ }
2398
+ default: {
2399
+ this._err(ERR.missingQuoteBeforeDoctypePublicIdentifier);
2400
+ token.forceQuirks = true;
2401
+ this.state = State.BOGUS_DOCTYPE;
2402
+ this._stateBogusDoctype(cp);
2403
+ }
2404
+ }
2405
+ }
2406
+ // Before DOCTYPE public identifier state
2407
+ //------------------------------------------------------------------
2408
+ _stateBeforeDoctypePublicIdentifier(cp) {
2409
+ const token = this.currentToken;
2410
+ switch (cp) {
2411
+ case $.SPACE:
2412
+ case $.LINE_FEED:
2413
+ case $.TABULATION:
2414
+ case $.FORM_FEED: {
2415
+ // Ignore whitespace
2416
+ break;
2417
+ }
2418
+ case $.QUOTATION_MARK: {
2419
+ token.publicId = '';
2420
+ this.state = State.DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED;
2421
+ break;
2422
+ }
2423
+ case $.APOSTROPHE: {
2424
+ token.publicId = '';
2425
+ this.state = State.DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED;
2426
+ break;
2427
+ }
2428
+ case $.GREATER_THAN_SIGN: {
2429
+ this._err(ERR.missingDoctypePublicIdentifier);
2430
+ token.forceQuirks = true;
2431
+ this.state = State.DATA;
2432
+ this.emitCurrentDoctype(token);
2433
+ break;
2434
+ }
2435
+ case $.EOF: {
2436
+ this._err(ERR.eofInDoctype);
2437
+ token.forceQuirks = true;
2438
+ this.emitCurrentDoctype(token);
2439
+ this._emitEOFToken();
2440
+ break;
2441
+ }
2442
+ default: {
2443
+ this._err(ERR.missingQuoteBeforeDoctypePublicIdentifier);
2444
+ token.forceQuirks = true;
2445
+ this.state = State.BOGUS_DOCTYPE;
2446
+ this._stateBogusDoctype(cp);
2447
+ }
2448
+ }
2449
+ }
2450
+ // DOCTYPE public identifier (double-quoted) state
2451
+ //------------------------------------------------------------------
2452
+ _stateDoctypePublicIdentifierDoubleQuoted(cp) {
2453
+ const token = this.currentToken;
2454
+ switch (cp) {
2455
+ case $.QUOTATION_MARK: {
2456
+ this.state = State.AFTER_DOCTYPE_PUBLIC_IDENTIFIER;
2457
+ break;
2458
+ }
2459
+ case $.NULL: {
2460
+ this._err(ERR.unexpectedNullCharacter);
2461
+ token.publicId += REPLACEMENT_CHARACTER;
2462
+ break;
2463
+ }
2464
+ case $.GREATER_THAN_SIGN: {
2465
+ this._err(ERR.abruptDoctypePublicIdentifier);
2466
+ token.forceQuirks = true;
2467
+ this.emitCurrentDoctype(token);
2468
+ this.state = State.DATA;
2469
+ break;
2470
+ }
2471
+ case $.EOF: {
2472
+ this._err(ERR.eofInDoctype);
2473
+ token.forceQuirks = true;
2474
+ this.emitCurrentDoctype(token);
2475
+ this._emitEOFToken();
2476
+ break;
2477
+ }
2478
+ default: {
2479
+ token.publicId += String.fromCodePoint(cp);
2480
+ }
2481
+ }
2482
+ }
2483
+ // DOCTYPE public identifier (single-quoted) state
2484
+ //------------------------------------------------------------------
2485
+ _stateDoctypePublicIdentifierSingleQuoted(cp) {
2486
+ const token = this.currentToken;
2487
+ switch (cp) {
2488
+ case $.APOSTROPHE: {
2489
+ this.state = State.AFTER_DOCTYPE_PUBLIC_IDENTIFIER;
2490
+ break;
2491
+ }
2492
+ case $.NULL: {
2493
+ this._err(ERR.unexpectedNullCharacter);
2494
+ token.publicId += REPLACEMENT_CHARACTER;
2495
+ break;
2496
+ }
2497
+ case $.GREATER_THAN_SIGN: {
2498
+ this._err(ERR.abruptDoctypePublicIdentifier);
2499
+ token.forceQuirks = true;
2500
+ this.emitCurrentDoctype(token);
2501
+ this.state = State.DATA;
2502
+ break;
2503
+ }
2504
+ case $.EOF: {
2505
+ this._err(ERR.eofInDoctype);
2506
+ token.forceQuirks = true;
2507
+ this.emitCurrentDoctype(token);
2508
+ this._emitEOFToken();
2509
+ break;
2510
+ }
2511
+ default: {
2512
+ token.publicId += String.fromCodePoint(cp);
2513
+ }
2514
+ }
2515
+ }
2516
+ // After DOCTYPE public identifier state
2517
+ //------------------------------------------------------------------
2518
+ _stateAfterDoctypePublicIdentifier(cp) {
2519
+ const token = this.currentToken;
2520
+ switch (cp) {
2521
+ case $.SPACE:
2522
+ case $.LINE_FEED:
2523
+ case $.TABULATION:
2524
+ case $.FORM_FEED: {
2525
+ this.state = State.BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS;
2526
+ break;
2527
+ }
2528
+ case $.GREATER_THAN_SIGN: {
2529
+ this.state = State.DATA;
2530
+ this.emitCurrentDoctype(token);
2531
+ break;
2532
+ }
2533
+ case $.QUOTATION_MARK: {
2534
+ this._err(ERR.missingWhitespaceBetweenDoctypePublicAndSystemIdentifiers);
2535
+ token.systemId = '';
2536
+ this.state = State.DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED;
2537
+ break;
2538
+ }
2539
+ case $.APOSTROPHE: {
2540
+ this._err(ERR.missingWhitespaceBetweenDoctypePublicAndSystemIdentifiers);
2541
+ token.systemId = '';
2542
+ this.state = State.DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED;
2543
+ break;
2544
+ }
2545
+ case $.EOF: {
2546
+ this._err(ERR.eofInDoctype);
2547
+ token.forceQuirks = true;
2548
+ this.emitCurrentDoctype(token);
2549
+ this._emitEOFToken();
2550
+ break;
2551
+ }
2552
+ default: {
2553
+ this._err(ERR.missingQuoteBeforeDoctypeSystemIdentifier);
2554
+ token.forceQuirks = true;
2555
+ this.state = State.BOGUS_DOCTYPE;
2556
+ this._stateBogusDoctype(cp);
2557
+ }
2558
+ }
2559
+ }
2560
+ // Between DOCTYPE public and system identifiers state
2561
+ //------------------------------------------------------------------
2562
+ _stateBetweenDoctypePublicAndSystemIdentifiers(cp) {
2563
+ const token = this.currentToken;
2564
+ switch (cp) {
2565
+ case $.SPACE:
2566
+ case $.LINE_FEED:
2567
+ case $.TABULATION:
2568
+ case $.FORM_FEED: {
2569
+ // Ignore whitespace
2570
+ break;
2571
+ }
2572
+ case $.GREATER_THAN_SIGN: {
2573
+ this.emitCurrentDoctype(token);
2574
+ this.state = State.DATA;
2575
+ break;
2576
+ }
2577
+ case $.QUOTATION_MARK: {
2578
+ token.systemId = '';
2579
+ this.state = State.DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED;
2580
+ break;
2581
+ }
2582
+ case $.APOSTROPHE: {
2583
+ token.systemId = '';
2584
+ this.state = State.DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED;
2585
+ break;
2586
+ }
2587
+ case $.EOF: {
2588
+ this._err(ERR.eofInDoctype);
2589
+ token.forceQuirks = true;
2590
+ this.emitCurrentDoctype(token);
2591
+ this._emitEOFToken();
2592
+ break;
2593
+ }
2594
+ default: {
2595
+ this._err(ERR.missingQuoteBeforeDoctypeSystemIdentifier);
2596
+ token.forceQuirks = true;
2597
+ this.state = State.BOGUS_DOCTYPE;
2598
+ this._stateBogusDoctype(cp);
2599
+ }
2600
+ }
2601
+ }
2602
+ // After DOCTYPE system keyword state
2603
+ //------------------------------------------------------------------
2604
+ _stateAfterDoctypeSystemKeyword(cp) {
2605
+ const token = this.currentToken;
2606
+ switch (cp) {
2607
+ case $.SPACE:
2608
+ case $.LINE_FEED:
2609
+ case $.TABULATION:
2610
+ case $.FORM_FEED: {
2611
+ this.state = State.BEFORE_DOCTYPE_SYSTEM_IDENTIFIER;
2612
+ break;
2613
+ }
2614
+ case $.QUOTATION_MARK: {
2615
+ this._err(ERR.missingWhitespaceAfterDoctypeSystemKeyword);
2616
+ token.systemId = '';
2617
+ this.state = State.DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED;
2618
+ break;
2619
+ }
2620
+ case $.APOSTROPHE: {
2621
+ this._err(ERR.missingWhitespaceAfterDoctypeSystemKeyword);
2622
+ token.systemId = '';
2623
+ this.state = State.DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED;
2624
+ break;
2625
+ }
2626
+ case $.GREATER_THAN_SIGN: {
2627
+ this._err(ERR.missingDoctypeSystemIdentifier);
2628
+ token.forceQuirks = true;
2629
+ this.state = State.DATA;
2630
+ this.emitCurrentDoctype(token);
2631
+ break;
2632
+ }
2633
+ case $.EOF: {
2634
+ this._err(ERR.eofInDoctype);
2635
+ token.forceQuirks = true;
2636
+ this.emitCurrentDoctype(token);
2637
+ this._emitEOFToken();
2638
+ break;
2639
+ }
2640
+ default: {
2641
+ this._err(ERR.missingQuoteBeforeDoctypeSystemIdentifier);
2642
+ token.forceQuirks = true;
2643
+ this.state = State.BOGUS_DOCTYPE;
2644
+ this._stateBogusDoctype(cp);
2645
+ }
2646
+ }
2647
+ }
2648
+ // Before DOCTYPE system identifier state
2649
+ //------------------------------------------------------------------
2650
+ _stateBeforeDoctypeSystemIdentifier(cp) {
2651
+ const token = this.currentToken;
2652
+ switch (cp) {
2653
+ case $.SPACE:
2654
+ case $.LINE_FEED:
2655
+ case $.TABULATION:
2656
+ case $.FORM_FEED: {
2657
+ // Ignore whitespace
2658
+ break;
2659
+ }
2660
+ case $.QUOTATION_MARK: {
2661
+ token.systemId = '';
2662
+ this.state = State.DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED;
2663
+ break;
2664
+ }
2665
+ case $.APOSTROPHE: {
2666
+ token.systemId = '';
2667
+ this.state = State.DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED;
2668
+ break;
2669
+ }
2670
+ case $.GREATER_THAN_SIGN: {
2671
+ this._err(ERR.missingDoctypeSystemIdentifier);
2672
+ token.forceQuirks = true;
2673
+ this.state = State.DATA;
2674
+ this.emitCurrentDoctype(token);
2675
+ break;
2676
+ }
2677
+ case $.EOF: {
2678
+ this._err(ERR.eofInDoctype);
2679
+ token.forceQuirks = true;
2680
+ this.emitCurrentDoctype(token);
2681
+ this._emitEOFToken();
2682
+ break;
2683
+ }
2684
+ default: {
2685
+ this._err(ERR.missingQuoteBeforeDoctypeSystemIdentifier);
2686
+ token.forceQuirks = true;
2687
+ this.state = State.BOGUS_DOCTYPE;
2688
+ this._stateBogusDoctype(cp);
2689
+ }
2690
+ }
2691
+ }
2692
+ // DOCTYPE system identifier (double-quoted) state
2693
+ //------------------------------------------------------------------
2694
+ _stateDoctypeSystemIdentifierDoubleQuoted(cp) {
2695
+ const token = this.currentToken;
2696
+ switch (cp) {
2697
+ case $.QUOTATION_MARK: {
2698
+ this.state = State.AFTER_DOCTYPE_SYSTEM_IDENTIFIER;
2699
+ break;
2700
+ }
2701
+ case $.NULL: {
2702
+ this._err(ERR.unexpectedNullCharacter);
2703
+ token.systemId += REPLACEMENT_CHARACTER;
2704
+ break;
2705
+ }
2706
+ case $.GREATER_THAN_SIGN: {
2707
+ this._err(ERR.abruptDoctypeSystemIdentifier);
2708
+ token.forceQuirks = true;
2709
+ this.emitCurrentDoctype(token);
2710
+ this.state = State.DATA;
2711
+ break;
2712
+ }
2713
+ case $.EOF: {
2714
+ this._err(ERR.eofInDoctype);
2715
+ token.forceQuirks = true;
2716
+ this.emitCurrentDoctype(token);
2717
+ this._emitEOFToken();
2718
+ break;
2719
+ }
2720
+ default: {
2721
+ token.systemId += String.fromCodePoint(cp);
2722
+ }
2723
+ }
2724
+ }
2725
+ // DOCTYPE system identifier (single-quoted) state
2726
+ //------------------------------------------------------------------
2727
+ _stateDoctypeSystemIdentifierSingleQuoted(cp) {
2728
+ const token = this.currentToken;
2729
+ switch (cp) {
2730
+ case $.APOSTROPHE: {
2731
+ this.state = State.AFTER_DOCTYPE_SYSTEM_IDENTIFIER;
2732
+ break;
2733
+ }
2734
+ case $.NULL: {
2735
+ this._err(ERR.unexpectedNullCharacter);
2736
+ token.systemId += REPLACEMENT_CHARACTER;
2737
+ break;
2738
+ }
2739
+ case $.GREATER_THAN_SIGN: {
2740
+ this._err(ERR.abruptDoctypeSystemIdentifier);
2741
+ token.forceQuirks = true;
2742
+ this.emitCurrentDoctype(token);
2743
+ this.state = State.DATA;
2744
+ break;
2745
+ }
2746
+ case $.EOF: {
2747
+ this._err(ERR.eofInDoctype);
2748
+ token.forceQuirks = true;
2749
+ this.emitCurrentDoctype(token);
2750
+ this._emitEOFToken();
2751
+ break;
2752
+ }
2753
+ default: {
2754
+ token.systemId += String.fromCodePoint(cp);
2755
+ }
2756
+ }
2757
+ }
2758
+ // After DOCTYPE system identifier state
2759
+ //------------------------------------------------------------------
2760
+ _stateAfterDoctypeSystemIdentifier(cp) {
2761
+ const token = this.currentToken;
2762
+ switch (cp) {
2763
+ case $.SPACE:
2764
+ case $.LINE_FEED:
2765
+ case $.TABULATION:
2766
+ case $.FORM_FEED: {
2767
+ // Ignore whitespace
2768
+ break;
2769
+ }
2770
+ case $.GREATER_THAN_SIGN: {
2771
+ this.emitCurrentDoctype(token);
2772
+ this.state = State.DATA;
2773
+ break;
2774
+ }
2775
+ case $.EOF: {
2776
+ this._err(ERR.eofInDoctype);
2777
+ token.forceQuirks = true;
2778
+ this.emitCurrentDoctype(token);
2779
+ this._emitEOFToken();
2780
+ break;
2781
+ }
2782
+ default: {
2783
+ this._err(ERR.unexpectedCharacterAfterDoctypeSystemIdentifier);
2784
+ this.state = State.BOGUS_DOCTYPE;
2785
+ this._stateBogusDoctype(cp);
2786
+ }
2787
+ }
2788
+ }
2789
+ // Bogus DOCTYPE state
2790
+ //------------------------------------------------------------------
2791
+ _stateBogusDoctype(cp) {
2792
+ const token = this.currentToken;
2793
+ switch (cp) {
2794
+ case $.GREATER_THAN_SIGN: {
2795
+ this.emitCurrentDoctype(token);
2796
+ this.state = State.DATA;
2797
+ break;
2798
+ }
2799
+ case $.NULL: {
2800
+ this._err(ERR.unexpectedNullCharacter);
2801
+ break;
2802
+ }
2803
+ case $.EOF: {
2804
+ this.emitCurrentDoctype(token);
2805
+ this._emitEOFToken();
2806
+ break;
2807
+ }
2808
+ default:
2809
+ // Do nothing
2810
+ }
2811
+ }
2812
+ // CDATA section state
2813
+ //------------------------------------------------------------------
2814
+ _stateCdataSection(cp) {
2815
+ switch (cp) {
2816
+ case $.RIGHT_SQUARE_BRACKET: {
2817
+ this.state = State.CDATA_SECTION_BRACKET;
2818
+ break;
2819
+ }
2820
+ case $.EOF: {
2821
+ this._err(ERR.eofInCdata);
2822
+ this._emitEOFToken();
2823
+ break;
2824
+ }
2825
+ default: {
2826
+ this._emitCodePoint(cp);
2827
+ }
2828
+ }
2829
+ }
2830
+ // CDATA section bracket state
2831
+ //------------------------------------------------------------------
2832
+ _stateCdataSectionBracket(cp) {
2833
+ if (cp === $.RIGHT_SQUARE_BRACKET) {
2834
+ this.state = State.CDATA_SECTION_END;
2835
+ }
2836
+ else {
2837
+ this._emitChars(']', -1);
2838
+ this.state = State.CDATA_SECTION;
2839
+ this._stateCdataSection(cp);
2840
+ }
2841
+ }
2842
+ // CDATA section end state
2843
+ //------------------------------------------------------------------
2844
+ _stateCdataSectionEnd(cp) {
2845
+ switch (cp) {
2846
+ case $.GREATER_THAN_SIGN: {
2847
+ this.state = State.DATA;
2848
+ break;
2849
+ }
2850
+ case $.RIGHT_SQUARE_BRACKET: {
2851
+ this._emitChars(']', -2);
2852
+ break;
2853
+ }
2854
+ default: {
2855
+ this._emitChars(']]', -1);
2856
+ this.state = State.CDATA_SECTION;
2857
+ this._stateCdataSection(cp);
2858
+ }
2859
+ }
2860
+ }
2861
+ // Character reference state
2862
+ //------------------------------------------------------------------
2863
+ _stateCharacterReference() {
2864
+ let length = this.entityDecoder.write(this.preprocessor.html, this.preprocessor.pos);
2865
+ if (length < 0) {
2866
+ if (this.preprocessor.lastChunkWritten) {
2867
+ length = this.entityDecoder.end();
2868
+ }
2869
+ else {
2870
+ // Wait for the rest of the entity.
2871
+ this.active = false;
2872
+ // Mark the entire buffer as read.
2873
+ this.preprocessor.pos = this.preprocessor.html.length - 1;
2874
+ this.consumedAfterSnapshot = 0;
2875
+ this.preprocessor.endOfChunkHit = true;
2876
+ return;
2877
+ }
2878
+ }
2879
+ if (length === 0) {
2880
+ // This was not a valid entity. Go back to the beginning, and
2881
+ // figure out what to do.
2882
+ this.preprocessor.pos = this.entityStartPos;
2883
+ this._flushCodePointConsumedAsCharacterReference($.AMPERSAND);
2884
+ this.state =
2885
+ !this._isCharacterReferenceInAttribute() && isAsciiAlphaNumeric(this.preprocessor.peek(1))
2886
+ ? State.AMBIGUOUS_AMPERSAND
2887
+ : this.returnState;
2888
+ }
2889
+ else {
2890
+ // We successfully parsed an entity. Switch to the return state.
2891
+ this.state = this.returnState;
2892
+ }
2893
+ }
2894
+ // Ambiguos ampersand state
2895
+ //------------------------------------------------------------------
2896
+ _stateAmbiguousAmpersand(cp) {
2897
+ if (isAsciiAlphaNumeric(cp)) {
2898
+ this._flushCodePointConsumedAsCharacterReference(cp);
2899
+ }
2900
+ else {
2901
+ if (cp === $.SEMICOLON) {
2902
+ this._err(ERR.unknownNamedCharacterReference);
2903
+ }
2904
+ this.state = this.returnState;
2905
+ this._callState(cp);
2906
+ }
2907
+ }
2908
+ }