source: trip-planner-front/node_modules/parse5/lib/parser/index.js@ 6a3a178

Last change on this file since 6a3a178 was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 91.1 KB
Line 
1'use strict';
2
3const Tokenizer = require('../tokenizer');
4const OpenElementStack = require('./open-element-stack');
5const FormattingElementList = require('./formatting-element-list');
6const LocationInfoParserMixin = require('../extensions/location-info/parser-mixin');
7const ErrorReportingParserMixin = require('../extensions/error-reporting/parser-mixin');
8const Mixin = require('../utils/mixin');
9const defaultTreeAdapter = require('../tree-adapters/default');
10const mergeOptions = require('../utils/merge-options');
11const doctype = require('../common/doctype');
12const foreignContent = require('../common/foreign-content');
13const ERR = require('../common/error-codes');
14const unicode = require('../common/unicode');
15const HTML = require('../common/html');
16
17//Aliases
18const $ = HTML.TAG_NAMES;
19const NS = HTML.NAMESPACES;
20const ATTRS = HTML.ATTRS;
21
22const DEFAULT_OPTIONS = {
23 scriptingEnabled: true,
24 sourceCodeLocationInfo: false,
25 onParseError: null,
26 treeAdapter: defaultTreeAdapter
27};
28
29//Misc constants
30const HIDDEN_INPUT_TYPE = 'hidden';
31
32//Adoption agency loops iteration count
33const AA_OUTER_LOOP_ITER = 8;
34const AA_INNER_LOOP_ITER = 3;
35
36//Insertion modes
37const INITIAL_MODE = 'INITIAL_MODE';
38const BEFORE_HTML_MODE = 'BEFORE_HTML_MODE';
39const BEFORE_HEAD_MODE = 'BEFORE_HEAD_MODE';
40const IN_HEAD_MODE = 'IN_HEAD_MODE';
41const IN_HEAD_NO_SCRIPT_MODE = 'IN_HEAD_NO_SCRIPT_MODE';
42const AFTER_HEAD_MODE = 'AFTER_HEAD_MODE';
43const IN_BODY_MODE = 'IN_BODY_MODE';
44const TEXT_MODE = 'TEXT_MODE';
45const IN_TABLE_MODE = 'IN_TABLE_MODE';
46const IN_TABLE_TEXT_MODE = 'IN_TABLE_TEXT_MODE';
47const IN_CAPTION_MODE = 'IN_CAPTION_MODE';
48const IN_COLUMN_GROUP_MODE = 'IN_COLUMN_GROUP_MODE';
49const IN_TABLE_BODY_MODE = 'IN_TABLE_BODY_MODE';
50const IN_ROW_MODE = 'IN_ROW_MODE';
51const IN_CELL_MODE = 'IN_CELL_MODE';
52const IN_SELECT_MODE = 'IN_SELECT_MODE';
53const IN_SELECT_IN_TABLE_MODE = 'IN_SELECT_IN_TABLE_MODE';
54const IN_TEMPLATE_MODE = 'IN_TEMPLATE_MODE';
55const AFTER_BODY_MODE = 'AFTER_BODY_MODE';
56const IN_FRAMESET_MODE = 'IN_FRAMESET_MODE';
57const AFTER_FRAMESET_MODE = 'AFTER_FRAMESET_MODE';
58const AFTER_AFTER_BODY_MODE = 'AFTER_AFTER_BODY_MODE';
59const AFTER_AFTER_FRAMESET_MODE = 'AFTER_AFTER_FRAMESET_MODE';
60
61//Insertion mode reset map
62const INSERTION_MODE_RESET_MAP = {
63 [$.TR]: IN_ROW_MODE,
64 [$.TBODY]: IN_TABLE_BODY_MODE,
65 [$.THEAD]: IN_TABLE_BODY_MODE,
66 [$.TFOOT]: IN_TABLE_BODY_MODE,
67 [$.CAPTION]: IN_CAPTION_MODE,
68 [$.COLGROUP]: IN_COLUMN_GROUP_MODE,
69 [$.TABLE]: IN_TABLE_MODE,
70 [$.BODY]: IN_BODY_MODE,
71 [$.FRAMESET]: IN_FRAMESET_MODE
72};
73
74//Template insertion mode switch map
75const TEMPLATE_INSERTION_MODE_SWITCH_MAP = {
76 [$.CAPTION]: IN_TABLE_MODE,
77 [$.COLGROUP]: IN_TABLE_MODE,
78 [$.TBODY]: IN_TABLE_MODE,
79 [$.TFOOT]: IN_TABLE_MODE,
80 [$.THEAD]: IN_TABLE_MODE,
81 [$.COL]: IN_COLUMN_GROUP_MODE,
82 [$.TR]: IN_TABLE_BODY_MODE,
83 [$.TD]: IN_ROW_MODE,
84 [$.TH]: IN_ROW_MODE
85};
86
87//Token handlers map for insertion modes
88const TOKEN_HANDLERS = {
89 [INITIAL_MODE]: {
90 [Tokenizer.CHARACTER_TOKEN]: tokenInInitialMode,
91 [Tokenizer.NULL_CHARACTER_TOKEN]: tokenInInitialMode,
92 [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: ignoreToken,
93 [Tokenizer.COMMENT_TOKEN]: appendComment,
94 [Tokenizer.DOCTYPE_TOKEN]: doctypeInInitialMode,
95 [Tokenizer.START_TAG_TOKEN]: tokenInInitialMode,
96 [Tokenizer.END_TAG_TOKEN]: tokenInInitialMode,
97 [Tokenizer.EOF_TOKEN]: tokenInInitialMode
98 },
99 [BEFORE_HTML_MODE]: {
100 [Tokenizer.CHARACTER_TOKEN]: tokenBeforeHtml,
101 [Tokenizer.NULL_CHARACTER_TOKEN]: tokenBeforeHtml,
102 [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: ignoreToken,
103 [Tokenizer.COMMENT_TOKEN]: appendComment,
104 [Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
105 [Tokenizer.START_TAG_TOKEN]: startTagBeforeHtml,
106 [Tokenizer.END_TAG_TOKEN]: endTagBeforeHtml,
107 [Tokenizer.EOF_TOKEN]: tokenBeforeHtml
108 },
109 [BEFORE_HEAD_MODE]: {
110 [Tokenizer.CHARACTER_TOKEN]: tokenBeforeHead,
111 [Tokenizer.NULL_CHARACTER_TOKEN]: tokenBeforeHead,
112 [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: ignoreToken,
113 [Tokenizer.COMMENT_TOKEN]: appendComment,
114 [Tokenizer.DOCTYPE_TOKEN]: misplacedDoctype,
115 [Tokenizer.START_TAG_TOKEN]: startTagBeforeHead,
116 [Tokenizer.END_TAG_TOKEN]: endTagBeforeHead,
117 [Tokenizer.EOF_TOKEN]: tokenBeforeHead
118 },
119 [IN_HEAD_MODE]: {
120 [Tokenizer.CHARACTER_TOKEN]: tokenInHead,
121 [Tokenizer.NULL_CHARACTER_TOKEN]: tokenInHead,
122 [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: insertCharacters,
123 [Tokenizer.COMMENT_TOKEN]: appendComment,
124 [Tokenizer.DOCTYPE_TOKEN]: misplacedDoctype,
125 [Tokenizer.START_TAG_TOKEN]: startTagInHead,
126 [Tokenizer.END_TAG_TOKEN]: endTagInHead,
127 [Tokenizer.EOF_TOKEN]: tokenInHead
128 },
129 [IN_HEAD_NO_SCRIPT_MODE]: {
130 [Tokenizer.CHARACTER_TOKEN]: tokenInHeadNoScript,
131 [Tokenizer.NULL_CHARACTER_TOKEN]: tokenInHeadNoScript,
132 [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: insertCharacters,
133 [Tokenizer.COMMENT_TOKEN]: appendComment,
134 [Tokenizer.DOCTYPE_TOKEN]: misplacedDoctype,
135 [Tokenizer.START_TAG_TOKEN]: startTagInHeadNoScript,
136 [Tokenizer.END_TAG_TOKEN]: endTagInHeadNoScript,
137 [Tokenizer.EOF_TOKEN]: tokenInHeadNoScript
138 },
139 [AFTER_HEAD_MODE]: {
140 [Tokenizer.CHARACTER_TOKEN]: tokenAfterHead,
141 [Tokenizer.NULL_CHARACTER_TOKEN]: tokenAfterHead,
142 [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: insertCharacters,
143 [Tokenizer.COMMENT_TOKEN]: appendComment,
144 [Tokenizer.DOCTYPE_TOKEN]: misplacedDoctype,
145 [Tokenizer.START_TAG_TOKEN]: startTagAfterHead,
146 [Tokenizer.END_TAG_TOKEN]: endTagAfterHead,
147 [Tokenizer.EOF_TOKEN]: tokenAfterHead
148 },
149 [IN_BODY_MODE]: {
150 [Tokenizer.CHARACTER_TOKEN]: characterInBody,
151 [Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken,
152 [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: whitespaceCharacterInBody,
153 [Tokenizer.COMMENT_TOKEN]: appendComment,
154 [Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
155 [Tokenizer.START_TAG_TOKEN]: startTagInBody,
156 [Tokenizer.END_TAG_TOKEN]: endTagInBody,
157 [Tokenizer.EOF_TOKEN]: eofInBody
158 },
159 [TEXT_MODE]: {
160 [Tokenizer.CHARACTER_TOKEN]: insertCharacters,
161 [Tokenizer.NULL_CHARACTER_TOKEN]: insertCharacters,
162 [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: insertCharacters,
163 [Tokenizer.COMMENT_TOKEN]: ignoreToken,
164 [Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
165 [Tokenizer.START_TAG_TOKEN]: ignoreToken,
166 [Tokenizer.END_TAG_TOKEN]: endTagInText,
167 [Tokenizer.EOF_TOKEN]: eofInText
168 },
169 [IN_TABLE_MODE]: {
170 [Tokenizer.CHARACTER_TOKEN]: characterInTable,
171 [Tokenizer.NULL_CHARACTER_TOKEN]: characterInTable,
172 [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: characterInTable,
173 [Tokenizer.COMMENT_TOKEN]: appendComment,
174 [Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
175 [Tokenizer.START_TAG_TOKEN]: startTagInTable,
176 [Tokenizer.END_TAG_TOKEN]: endTagInTable,
177 [Tokenizer.EOF_TOKEN]: eofInBody
178 },
179 [IN_TABLE_TEXT_MODE]: {
180 [Tokenizer.CHARACTER_TOKEN]: characterInTableText,
181 [Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken,
182 [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: whitespaceCharacterInTableText,
183 [Tokenizer.COMMENT_TOKEN]: tokenInTableText,
184 [Tokenizer.DOCTYPE_TOKEN]: tokenInTableText,
185 [Tokenizer.START_TAG_TOKEN]: tokenInTableText,
186 [Tokenizer.END_TAG_TOKEN]: tokenInTableText,
187 [Tokenizer.EOF_TOKEN]: tokenInTableText
188 },
189 [IN_CAPTION_MODE]: {
190 [Tokenizer.CHARACTER_TOKEN]: characterInBody,
191 [Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken,
192 [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: whitespaceCharacterInBody,
193 [Tokenizer.COMMENT_TOKEN]: appendComment,
194 [Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
195 [Tokenizer.START_TAG_TOKEN]: startTagInCaption,
196 [Tokenizer.END_TAG_TOKEN]: endTagInCaption,
197 [Tokenizer.EOF_TOKEN]: eofInBody
198 },
199 [IN_COLUMN_GROUP_MODE]: {
200 [Tokenizer.CHARACTER_TOKEN]: tokenInColumnGroup,
201 [Tokenizer.NULL_CHARACTER_TOKEN]: tokenInColumnGroup,
202 [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: insertCharacters,
203 [Tokenizer.COMMENT_TOKEN]: appendComment,
204 [Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
205 [Tokenizer.START_TAG_TOKEN]: startTagInColumnGroup,
206 [Tokenizer.END_TAG_TOKEN]: endTagInColumnGroup,
207 [Tokenizer.EOF_TOKEN]: eofInBody
208 },
209 [IN_TABLE_BODY_MODE]: {
210 [Tokenizer.CHARACTER_TOKEN]: characterInTable,
211 [Tokenizer.NULL_CHARACTER_TOKEN]: characterInTable,
212 [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: characterInTable,
213 [Tokenizer.COMMENT_TOKEN]: appendComment,
214 [Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
215 [Tokenizer.START_TAG_TOKEN]: startTagInTableBody,
216 [Tokenizer.END_TAG_TOKEN]: endTagInTableBody,
217 [Tokenizer.EOF_TOKEN]: eofInBody
218 },
219 [IN_ROW_MODE]: {
220 [Tokenizer.CHARACTER_TOKEN]: characterInTable,
221 [Tokenizer.NULL_CHARACTER_TOKEN]: characterInTable,
222 [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: characterInTable,
223 [Tokenizer.COMMENT_TOKEN]: appendComment,
224 [Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
225 [Tokenizer.START_TAG_TOKEN]: startTagInRow,
226 [Tokenizer.END_TAG_TOKEN]: endTagInRow,
227 [Tokenizer.EOF_TOKEN]: eofInBody
228 },
229 [IN_CELL_MODE]: {
230 [Tokenizer.CHARACTER_TOKEN]: characterInBody,
231 [Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken,
232 [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: whitespaceCharacterInBody,
233 [Tokenizer.COMMENT_TOKEN]: appendComment,
234 [Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
235 [Tokenizer.START_TAG_TOKEN]: startTagInCell,
236 [Tokenizer.END_TAG_TOKEN]: endTagInCell,
237 [Tokenizer.EOF_TOKEN]: eofInBody
238 },
239 [IN_SELECT_MODE]: {
240 [Tokenizer.CHARACTER_TOKEN]: insertCharacters,
241 [Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken,
242 [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: insertCharacters,
243 [Tokenizer.COMMENT_TOKEN]: appendComment,
244 [Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
245 [Tokenizer.START_TAG_TOKEN]: startTagInSelect,
246 [Tokenizer.END_TAG_TOKEN]: endTagInSelect,
247 [Tokenizer.EOF_TOKEN]: eofInBody
248 },
249 [IN_SELECT_IN_TABLE_MODE]: {
250 [Tokenizer.CHARACTER_TOKEN]: insertCharacters,
251 [Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken,
252 [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: insertCharacters,
253 [Tokenizer.COMMENT_TOKEN]: appendComment,
254 [Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
255 [Tokenizer.START_TAG_TOKEN]: startTagInSelectInTable,
256 [Tokenizer.END_TAG_TOKEN]: endTagInSelectInTable,
257 [Tokenizer.EOF_TOKEN]: eofInBody
258 },
259 [IN_TEMPLATE_MODE]: {
260 [Tokenizer.CHARACTER_TOKEN]: characterInBody,
261 [Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken,
262 [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: whitespaceCharacterInBody,
263 [Tokenizer.COMMENT_TOKEN]: appendComment,
264 [Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
265 [Tokenizer.START_TAG_TOKEN]: startTagInTemplate,
266 [Tokenizer.END_TAG_TOKEN]: endTagInTemplate,
267 [Tokenizer.EOF_TOKEN]: eofInTemplate
268 },
269 [AFTER_BODY_MODE]: {
270 [Tokenizer.CHARACTER_TOKEN]: tokenAfterBody,
271 [Tokenizer.NULL_CHARACTER_TOKEN]: tokenAfterBody,
272 [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: whitespaceCharacterInBody,
273 [Tokenizer.COMMENT_TOKEN]: appendCommentToRootHtmlElement,
274 [Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
275 [Tokenizer.START_TAG_TOKEN]: startTagAfterBody,
276 [Tokenizer.END_TAG_TOKEN]: endTagAfterBody,
277 [Tokenizer.EOF_TOKEN]: stopParsing
278 },
279 [IN_FRAMESET_MODE]: {
280 [Tokenizer.CHARACTER_TOKEN]: ignoreToken,
281 [Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken,
282 [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: insertCharacters,
283 [Tokenizer.COMMENT_TOKEN]: appendComment,
284 [Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
285 [Tokenizer.START_TAG_TOKEN]: startTagInFrameset,
286 [Tokenizer.END_TAG_TOKEN]: endTagInFrameset,
287 [Tokenizer.EOF_TOKEN]: stopParsing
288 },
289 [AFTER_FRAMESET_MODE]: {
290 [Tokenizer.CHARACTER_TOKEN]: ignoreToken,
291 [Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken,
292 [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: insertCharacters,
293 [Tokenizer.COMMENT_TOKEN]: appendComment,
294 [Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
295 [Tokenizer.START_TAG_TOKEN]: startTagAfterFrameset,
296 [Tokenizer.END_TAG_TOKEN]: endTagAfterFrameset,
297 [Tokenizer.EOF_TOKEN]: stopParsing
298 },
299 [AFTER_AFTER_BODY_MODE]: {
300 [Tokenizer.CHARACTER_TOKEN]: tokenAfterAfterBody,
301 [Tokenizer.NULL_CHARACTER_TOKEN]: tokenAfterAfterBody,
302 [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: whitespaceCharacterInBody,
303 [Tokenizer.COMMENT_TOKEN]: appendCommentToDocument,
304 [Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
305 [Tokenizer.START_TAG_TOKEN]: startTagAfterAfterBody,
306 [Tokenizer.END_TAG_TOKEN]: tokenAfterAfterBody,
307 [Tokenizer.EOF_TOKEN]: stopParsing
308 },
309 [AFTER_AFTER_FRAMESET_MODE]: {
310 [Tokenizer.CHARACTER_TOKEN]: ignoreToken,
311 [Tokenizer.NULL_CHARACTER_TOKEN]: ignoreToken,
312 [Tokenizer.WHITESPACE_CHARACTER_TOKEN]: whitespaceCharacterInBody,
313 [Tokenizer.COMMENT_TOKEN]: appendCommentToDocument,
314 [Tokenizer.DOCTYPE_TOKEN]: ignoreToken,
315 [Tokenizer.START_TAG_TOKEN]: startTagAfterAfterFrameset,
316 [Tokenizer.END_TAG_TOKEN]: ignoreToken,
317 [Tokenizer.EOF_TOKEN]: stopParsing
318 }
319};
320
321//Parser
322class Parser {
323 constructor(options) {
324 this.options = mergeOptions(DEFAULT_OPTIONS, options);
325
326 this.treeAdapter = this.options.treeAdapter;
327 this.pendingScript = null;
328
329 if (this.options.sourceCodeLocationInfo) {
330 Mixin.install(this, LocationInfoParserMixin);
331 }
332
333 if (this.options.onParseError) {
334 Mixin.install(this, ErrorReportingParserMixin, { onParseError: this.options.onParseError });
335 }
336 }
337
338 // API
339 parse(html) {
340 const document = this.treeAdapter.createDocument();
341
342 this._bootstrap(document, null);
343 this.tokenizer.write(html, true);
344 this._runParsingLoop(null);
345
346 return document;
347 }
348
349 parseFragment(html, fragmentContext) {
350 //NOTE: use <template> element as a fragment context if context element was not provided,
351 //so we will parse in "forgiving" manner
352 if (!fragmentContext) {
353 fragmentContext = this.treeAdapter.createElement($.TEMPLATE, NS.HTML, []);
354 }
355
356 //NOTE: create fake element which will be used as 'document' for fragment parsing.
357 //This is important for jsdom there 'document' can't be recreated, therefore
358 //fragment parsing causes messing of the main `document`.
359 const documentMock = this.treeAdapter.createElement('documentmock', NS.HTML, []);
360
361 this._bootstrap(documentMock, fragmentContext);
362
363 if (this.treeAdapter.getTagName(fragmentContext) === $.TEMPLATE) {
364 this._pushTmplInsertionMode(IN_TEMPLATE_MODE);
365 }
366
367 this._initTokenizerForFragmentParsing();
368 this._insertFakeRootElement();
369 this._resetInsertionMode();
370 this._findFormInFragmentContext();
371 this.tokenizer.write(html, true);
372 this._runParsingLoop(null);
373
374 const rootElement = this.treeAdapter.getFirstChild(documentMock);
375 const fragment = this.treeAdapter.createDocumentFragment();
376
377 this._adoptNodes(rootElement, fragment);
378
379 return fragment;
380 }
381
382 //Bootstrap parser
383 _bootstrap(document, fragmentContext) {
384 this.tokenizer = new Tokenizer(this.options);
385
386 this.stopped = false;
387
388 this.insertionMode = INITIAL_MODE;
389 this.originalInsertionMode = '';
390
391 this.document = document;
392 this.fragmentContext = fragmentContext;
393
394 this.headElement = null;
395 this.formElement = null;
396
397 this.openElements = new OpenElementStack(this.document, this.treeAdapter);
398 this.activeFormattingElements = new FormattingElementList(this.treeAdapter);
399
400 this.tmplInsertionModeStack = [];
401 this.tmplInsertionModeStackTop = -1;
402 this.currentTmplInsertionMode = null;
403
404 this.pendingCharacterTokens = [];
405 this.hasNonWhitespacePendingCharacterToken = false;
406
407 this.framesetOk = true;
408 this.skipNextNewLine = false;
409 this.fosterParentingEnabled = false;
410 }
411
412 //Errors
413 _err() {
414 // NOTE: err reporting is noop by default. Enabled by mixin.
415 }
416
417 //Parsing loop
418 _runParsingLoop(scriptHandler) {
419 while (!this.stopped) {
420 this._setupTokenizerCDATAMode();
421
422 const token = this.tokenizer.getNextToken();
423
424 if (token.type === Tokenizer.HIBERNATION_TOKEN) {
425 break;
426 }
427
428 if (this.skipNextNewLine) {
429 this.skipNextNewLine = false;
430
431 if (token.type === Tokenizer.WHITESPACE_CHARACTER_TOKEN && token.chars[0] === '\n') {
432 if (token.chars.length === 1) {
433 continue;
434 }
435
436 token.chars = token.chars.substr(1);
437 }
438 }
439
440 this._processInputToken(token);
441
442 if (scriptHandler && this.pendingScript) {
443 break;
444 }
445 }
446 }
447
448 runParsingLoopForCurrentChunk(writeCallback, scriptHandler) {
449 this._runParsingLoop(scriptHandler);
450
451 if (scriptHandler && this.pendingScript) {
452 const script = this.pendingScript;
453
454 this.pendingScript = null;
455
456 scriptHandler(script);
457
458 return;
459 }
460
461 if (writeCallback) {
462 writeCallback();
463 }
464 }
465
466 //Text parsing
467 _setupTokenizerCDATAMode() {
468 const current = this._getAdjustedCurrentElement();
469
470 this.tokenizer.allowCDATA =
471 current &&
472 current !== this.document &&
473 this.treeAdapter.getNamespaceURI(current) !== NS.HTML &&
474 !this._isIntegrationPoint(current);
475 }
476
477 _switchToTextParsing(currentToken, nextTokenizerState) {
478 this._insertElement(currentToken, NS.HTML);
479 this.tokenizer.state = nextTokenizerState;
480 this.originalInsertionMode = this.insertionMode;
481 this.insertionMode = TEXT_MODE;
482 }
483
484 switchToPlaintextParsing() {
485 this.insertionMode = TEXT_MODE;
486 this.originalInsertionMode = IN_BODY_MODE;
487 this.tokenizer.state = Tokenizer.MODE.PLAINTEXT;
488 }
489
490 //Fragment parsing
491 _getAdjustedCurrentElement() {
492 return this.openElements.stackTop === 0 && this.fragmentContext
493 ? this.fragmentContext
494 : this.openElements.current;
495 }
496
497 _findFormInFragmentContext() {
498 let node = this.fragmentContext;
499
500 do {
501 if (this.treeAdapter.getTagName(node) === $.FORM) {
502 this.formElement = node;
503 break;
504 }
505
506 node = this.treeAdapter.getParentNode(node);
507 } while (node);
508 }
509
510 _initTokenizerForFragmentParsing() {
511 if (this.treeAdapter.getNamespaceURI(this.fragmentContext) === NS.HTML) {
512 const tn = this.treeAdapter.getTagName(this.fragmentContext);
513
514 if (tn === $.TITLE || tn === $.TEXTAREA) {
515 this.tokenizer.state = Tokenizer.MODE.RCDATA;
516 } else if (
517 tn === $.STYLE ||
518 tn === $.XMP ||
519 tn === $.IFRAME ||
520 tn === $.NOEMBED ||
521 tn === $.NOFRAMES ||
522 tn === $.NOSCRIPT
523 ) {
524 this.tokenizer.state = Tokenizer.MODE.RAWTEXT;
525 } else if (tn === $.SCRIPT) {
526 this.tokenizer.state = Tokenizer.MODE.SCRIPT_DATA;
527 } else if (tn === $.PLAINTEXT) {
528 this.tokenizer.state = Tokenizer.MODE.PLAINTEXT;
529 }
530 }
531 }
532
533 //Tree mutation
534 _setDocumentType(token) {
535 const name = token.name || '';
536 const publicId = token.publicId || '';
537 const systemId = token.systemId || '';
538
539 this.treeAdapter.setDocumentType(this.document, name, publicId, systemId);
540 }
541
542 _attachElementToTree(element) {
543 if (this._shouldFosterParentOnInsertion()) {
544 this._fosterParentElement(element);
545 } else {
546 const parent = this.openElements.currentTmplContent || this.openElements.current;
547
548 this.treeAdapter.appendChild(parent, element);
549 }
550 }
551
552 _appendElement(token, namespaceURI) {
553 const element = this.treeAdapter.createElement(token.tagName, namespaceURI, token.attrs);
554
555 this._attachElementToTree(element);
556 }
557
558 _insertElement(token, namespaceURI) {
559 const element = this.treeAdapter.createElement(token.tagName, namespaceURI, token.attrs);
560
561 this._attachElementToTree(element);
562 this.openElements.push(element);
563 }
564
565 _insertFakeElement(tagName) {
566 const element = this.treeAdapter.createElement(tagName, NS.HTML, []);
567
568 this._attachElementToTree(element);
569 this.openElements.push(element);
570 }
571
572 _insertTemplate(token) {
573 const tmpl = this.treeAdapter.createElement(token.tagName, NS.HTML, token.attrs);
574 const content = this.treeAdapter.createDocumentFragment();
575
576 this.treeAdapter.setTemplateContent(tmpl, content);
577 this._attachElementToTree(tmpl);
578 this.openElements.push(tmpl);
579 }
580
581 _insertFakeRootElement() {
582 const element = this.treeAdapter.createElement($.HTML, NS.HTML, []);
583
584 this.treeAdapter.appendChild(this.openElements.current, element);
585 this.openElements.push(element);
586 }
587
588 _appendCommentNode(token, parent) {
589 const commentNode = this.treeAdapter.createCommentNode(token.data);
590
591 this.treeAdapter.appendChild(parent, commentNode);
592 }
593
594 _insertCharacters(token) {
595 if (this._shouldFosterParentOnInsertion()) {
596 this._fosterParentText(token.chars);
597 } else {
598 const parent = this.openElements.currentTmplContent || this.openElements.current;
599
600 this.treeAdapter.insertText(parent, token.chars);
601 }
602 }
603
604 _adoptNodes(donor, recipient) {
605 for (let child = this.treeAdapter.getFirstChild(donor); child; child = this.treeAdapter.getFirstChild(donor)) {
606 this.treeAdapter.detachNode(child);
607 this.treeAdapter.appendChild(recipient, child);
608 }
609 }
610
611 //Token processing
612 _shouldProcessTokenInForeignContent(token) {
613 const current = this._getAdjustedCurrentElement();
614
615 if (!current || current === this.document) {
616 return false;
617 }
618
619 const ns = this.treeAdapter.getNamespaceURI(current);
620
621 if (ns === NS.HTML) {
622 return false;
623 }
624
625 if (
626 this.treeAdapter.getTagName(current) === $.ANNOTATION_XML &&
627 ns === NS.MATHML &&
628 token.type === Tokenizer.START_TAG_TOKEN &&
629 token.tagName === $.SVG
630 ) {
631 return false;
632 }
633
634 const isCharacterToken =
635 token.type === Tokenizer.CHARACTER_TOKEN ||
636 token.type === Tokenizer.NULL_CHARACTER_TOKEN ||
637 token.type === Tokenizer.WHITESPACE_CHARACTER_TOKEN;
638
639 const isMathMLTextStartTag =
640 token.type === Tokenizer.START_TAG_TOKEN && token.tagName !== $.MGLYPH && token.tagName !== $.MALIGNMARK;
641
642 if ((isMathMLTextStartTag || isCharacterToken) && this._isIntegrationPoint(current, NS.MATHML)) {
643 return false;
644 }
645
646 if (
647 (token.type === Tokenizer.START_TAG_TOKEN || isCharacterToken) &&
648 this._isIntegrationPoint(current, NS.HTML)
649 ) {
650 return false;
651 }
652
653 return token.type !== Tokenizer.EOF_TOKEN;
654 }
655
656 _processToken(token) {
657 TOKEN_HANDLERS[this.insertionMode][token.type](this, token);
658 }
659
660 _processTokenInBodyMode(token) {
661 TOKEN_HANDLERS[IN_BODY_MODE][token.type](this, token);
662 }
663
664 _processTokenInForeignContent(token) {
665 if (token.type === Tokenizer.CHARACTER_TOKEN) {
666 characterInForeignContent(this, token);
667 } else if (token.type === Tokenizer.NULL_CHARACTER_TOKEN) {
668 nullCharacterInForeignContent(this, token);
669 } else if (token.type === Tokenizer.WHITESPACE_CHARACTER_TOKEN) {
670 insertCharacters(this, token);
671 } else if (token.type === Tokenizer.COMMENT_TOKEN) {
672 appendComment(this, token);
673 } else if (token.type === Tokenizer.START_TAG_TOKEN) {
674 startTagInForeignContent(this, token);
675 } else if (token.type === Tokenizer.END_TAG_TOKEN) {
676 endTagInForeignContent(this, token);
677 }
678 }
679
680 _processInputToken(token) {
681 if (this._shouldProcessTokenInForeignContent(token)) {
682 this._processTokenInForeignContent(token);
683 } else {
684 this._processToken(token);
685 }
686
687 if (token.type === Tokenizer.START_TAG_TOKEN && token.selfClosing && !token.ackSelfClosing) {
688 this._err(ERR.nonVoidHtmlElementStartTagWithTrailingSolidus);
689 }
690 }
691
692 //Integration points
693 _isIntegrationPoint(element, foreignNS) {
694 const tn = this.treeAdapter.getTagName(element);
695 const ns = this.treeAdapter.getNamespaceURI(element);
696 const attrs = this.treeAdapter.getAttrList(element);
697
698 return foreignContent.isIntegrationPoint(tn, ns, attrs, foreignNS);
699 }
700
701 //Active formatting elements reconstruction
702 _reconstructActiveFormattingElements() {
703 const listLength = this.activeFormattingElements.length;
704
705 if (listLength) {
706 let unopenIdx = listLength;
707 let entry = null;
708
709 do {
710 unopenIdx--;
711 entry = this.activeFormattingElements.entries[unopenIdx];
712
713 if (entry.type === FormattingElementList.MARKER_ENTRY || this.openElements.contains(entry.element)) {
714 unopenIdx++;
715 break;
716 }
717 } while (unopenIdx > 0);
718
719 for (let i = unopenIdx; i < listLength; i++) {
720 entry = this.activeFormattingElements.entries[i];
721 this._insertElement(entry.token, this.treeAdapter.getNamespaceURI(entry.element));
722 entry.element = this.openElements.current;
723 }
724 }
725 }
726
727 //Close elements
728 _closeTableCell() {
729 this.openElements.generateImpliedEndTags();
730 this.openElements.popUntilTableCellPopped();
731 this.activeFormattingElements.clearToLastMarker();
732 this.insertionMode = IN_ROW_MODE;
733 }
734
735 _closePElement() {
736 this.openElements.generateImpliedEndTagsWithExclusion($.P);
737 this.openElements.popUntilTagNamePopped($.P);
738 }
739
740 //Insertion modes
741 _resetInsertionMode() {
742 for (let i = this.openElements.stackTop, last = false; i >= 0; i--) {
743 let element = this.openElements.items[i];
744
745 if (i === 0) {
746 last = true;
747
748 if (this.fragmentContext) {
749 element = this.fragmentContext;
750 }
751 }
752
753 const tn = this.treeAdapter.getTagName(element);
754 const newInsertionMode = INSERTION_MODE_RESET_MAP[tn];
755
756 if (newInsertionMode) {
757 this.insertionMode = newInsertionMode;
758 break;
759 } else if (!last && (tn === $.TD || tn === $.TH)) {
760 this.insertionMode = IN_CELL_MODE;
761 break;
762 } else if (!last && tn === $.HEAD) {
763 this.insertionMode = IN_HEAD_MODE;
764 break;
765 } else if (tn === $.SELECT) {
766 this._resetInsertionModeForSelect(i);
767 break;
768 } else if (tn === $.TEMPLATE) {
769 this.insertionMode = this.currentTmplInsertionMode;
770 break;
771 } else if (tn === $.HTML) {
772 this.insertionMode = this.headElement ? AFTER_HEAD_MODE : BEFORE_HEAD_MODE;
773 break;
774 } else if (last) {
775 this.insertionMode = IN_BODY_MODE;
776 break;
777 }
778 }
779 }
780
781 _resetInsertionModeForSelect(selectIdx) {
782 if (selectIdx > 0) {
783 for (let i = selectIdx - 1; i > 0; i--) {
784 const ancestor = this.openElements.items[i];
785 const tn = this.treeAdapter.getTagName(ancestor);
786
787 if (tn === $.TEMPLATE) {
788 break;
789 } else if (tn === $.TABLE) {
790 this.insertionMode = IN_SELECT_IN_TABLE_MODE;
791 return;
792 }
793 }
794 }
795
796 this.insertionMode = IN_SELECT_MODE;
797 }
798
799 _pushTmplInsertionMode(mode) {
800 this.tmplInsertionModeStack.push(mode);
801 this.tmplInsertionModeStackTop++;
802 this.currentTmplInsertionMode = mode;
803 }
804
805 _popTmplInsertionMode() {
806 this.tmplInsertionModeStack.pop();
807 this.tmplInsertionModeStackTop--;
808 this.currentTmplInsertionMode = this.tmplInsertionModeStack[this.tmplInsertionModeStackTop];
809 }
810
811 //Foster parenting
812 _isElementCausesFosterParenting(element) {
813 const tn = this.treeAdapter.getTagName(element);
814
815 return tn === $.TABLE || tn === $.TBODY || tn === $.TFOOT || tn === $.THEAD || tn === $.TR;
816 }
817
818 _shouldFosterParentOnInsertion() {
819 return this.fosterParentingEnabled && this._isElementCausesFosterParenting(this.openElements.current);
820 }
821
822 _findFosterParentingLocation() {
823 const location = {
824 parent: null,
825 beforeElement: null
826 };
827
828 for (let i = this.openElements.stackTop; i >= 0; i--) {
829 const openElement = this.openElements.items[i];
830 const tn = this.treeAdapter.getTagName(openElement);
831 const ns = this.treeAdapter.getNamespaceURI(openElement);
832
833 if (tn === $.TEMPLATE && ns === NS.HTML) {
834 location.parent = this.treeAdapter.getTemplateContent(openElement);
835 break;
836 } else if (tn === $.TABLE) {
837 location.parent = this.treeAdapter.getParentNode(openElement);
838
839 if (location.parent) {
840 location.beforeElement = openElement;
841 } else {
842 location.parent = this.openElements.items[i - 1];
843 }
844
845 break;
846 }
847 }
848
849 if (!location.parent) {
850 location.parent = this.openElements.items[0];
851 }
852
853 return location;
854 }
855
856 _fosterParentElement(element) {
857 const location = this._findFosterParentingLocation();
858
859 if (location.beforeElement) {
860 this.treeAdapter.insertBefore(location.parent, element, location.beforeElement);
861 } else {
862 this.treeAdapter.appendChild(location.parent, element);
863 }
864 }
865
866 _fosterParentText(chars) {
867 const location = this._findFosterParentingLocation();
868
869 if (location.beforeElement) {
870 this.treeAdapter.insertTextBefore(location.parent, chars, location.beforeElement);
871 } else {
872 this.treeAdapter.insertText(location.parent, chars);
873 }
874 }
875
876 //Special elements
877 _isSpecialElement(element) {
878 const tn = this.treeAdapter.getTagName(element);
879 const ns = this.treeAdapter.getNamespaceURI(element);
880
881 return HTML.SPECIAL_ELEMENTS[ns][tn];
882 }
883}
884
885module.exports = Parser;
886
887//Adoption agency algorithm
888//(see: http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html#adoptionAgency)
889//------------------------------------------------------------------
890
891//Steps 5-8 of the algorithm
892function aaObtainFormattingElementEntry(p, token) {
893 let formattingElementEntry = p.activeFormattingElements.getElementEntryInScopeWithTagName(token.tagName);
894
895 if (formattingElementEntry) {
896 if (!p.openElements.contains(formattingElementEntry.element)) {
897 p.activeFormattingElements.removeEntry(formattingElementEntry);
898 formattingElementEntry = null;
899 } else if (!p.openElements.hasInScope(token.tagName)) {
900 formattingElementEntry = null;
901 }
902 } else {
903 genericEndTagInBody(p, token);
904 }
905
906 return formattingElementEntry;
907}
908
909//Steps 9 and 10 of the algorithm
910function aaObtainFurthestBlock(p, formattingElementEntry) {
911 let furthestBlock = null;
912
913 for (let i = p.openElements.stackTop; i >= 0; i--) {
914 const element = p.openElements.items[i];
915
916 if (element === formattingElementEntry.element) {
917 break;
918 }
919
920 if (p._isSpecialElement(element)) {
921 furthestBlock = element;
922 }
923 }
924
925 if (!furthestBlock) {
926 p.openElements.popUntilElementPopped(formattingElementEntry.element);
927 p.activeFormattingElements.removeEntry(formattingElementEntry);
928 }
929
930 return furthestBlock;
931}
932
933//Step 13 of the algorithm
934function aaInnerLoop(p, furthestBlock, formattingElement) {
935 let lastElement = furthestBlock;
936 let nextElement = p.openElements.getCommonAncestor(furthestBlock);
937
938 for (let i = 0, element = nextElement; element !== formattingElement; i++, element = nextElement) {
939 //NOTE: store next element for the next loop iteration (it may be deleted from the stack by step 9.5)
940 nextElement = p.openElements.getCommonAncestor(element);
941
942 const elementEntry = p.activeFormattingElements.getElementEntry(element);
943 const counterOverflow = elementEntry && i >= AA_INNER_LOOP_ITER;
944 const shouldRemoveFromOpenElements = !elementEntry || counterOverflow;
945
946 if (shouldRemoveFromOpenElements) {
947 if (counterOverflow) {
948 p.activeFormattingElements.removeEntry(elementEntry);
949 }
950
951 p.openElements.remove(element);
952 } else {
953 element = aaRecreateElementFromEntry(p, elementEntry);
954
955 if (lastElement === furthestBlock) {
956 p.activeFormattingElements.bookmark = elementEntry;
957 }
958
959 p.treeAdapter.detachNode(lastElement);
960 p.treeAdapter.appendChild(element, lastElement);
961 lastElement = element;
962 }
963 }
964
965 return lastElement;
966}
967
968//Step 13.7 of the algorithm
969function aaRecreateElementFromEntry(p, elementEntry) {
970 const ns = p.treeAdapter.getNamespaceURI(elementEntry.element);
971 const newElement = p.treeAdapter.createElement(elementEntry.token.tagName, ns, elementEntry.token.attrs);
972
973 p.openElements.replace(elementEntry.element, newElement);
974 elementEntry.element = newElement;
975
976 return newElement;
977}
978
979//Step 14 of the algorithm
980function aaInsertLastNodeInCommonAncestor(p, commonAncestor, lastElement) {
981 if (p._isElementCausesFosterParenting(commonAncestor)) {
982 p._fosterParentElement(lastElement);
983 } else {
984 const tn = p.treeAdapter.getTagName(commonAncestor);
985 const ns = p.treeAdapter.getNamespaceURI(commonAncestor);
986
987 if (tn === $.TEMPLATE && ns === NS.HTML) {
988 commonAncestor = p.treeAdapter.getTemplateContent(commonAncestor);
989 }
990
991 p.treeAdapter.appendChild(commonAncestor, lastElement);
992 }
993}
994
995//Steps 15-19 of the algorithm
996function aaReplaceFormattingElement(p, furthestBlock, formattingElementEntry) {
997 const ns = p.treeAdapter.getNamespaceURI(formattingElementEntry.element);
998 const token = formattingElementEntry.token;
999 const newElement = p.treeAdapter.createElement(token.tagName, ns, token.attrs);
1000
1001 p._adoptNodes(furthestBlock, newElement);
1002 p.treeAdapter.appendChild(furthestBlock, newElement);
1003
1004 p.activeFormattingElements.insertElementAfterBookmark(newElement, formattingElementEntry.token);
1005 p.activeFormattingElements.removeEntry(formattingElementEntry);
1006
1007 p.openElements.remove(formattingElementEntry.element);
1008 p.openElements.insertAfter(furthestBlock, newElement);
1009}
1010
1011//Algorithm entry point
1012function callAdoptionAgency(p, token) {
1013 let formattingElementEntry;
1014
1015 for (let i = 0; i < AA_OUTER_LOOP_ITER; i++) {
1016 formattingElementEntry = aaObtainFormattingElementEntry(p, token, formattingElementEntry);
1017
1018 if (!formattingElementEntry) {
1019 break;
1020 }
1021
1022 const furthestBlock = aaObtainFurthestBlock(p, formattingElementEntry);
1023
1024 if (!furthestBlock) {
1025 break;
1026 }
1027
1028 p.activeFormattingElements.bookmark = formattingElementEntry;
1029
1030 const lastElement = aaInnerLoop(p, furthestBlock, formattingElementEntry.element);
1031 const commonAncestor = p.openElements.getCommonAncestor(formattingElementEntry.element);
1032
1033 p.treeAdapter.detachNode(lastElement);
1034 aaInsertLastNodeInCommonAncestor(p, commonAncestor, lastElement);
1035 aaReplaceFormattingElement(p, furthestBlock, formattingElementEntry);
1036 }
1037}
1038
1039//Generic token handlers
1040//------------------------------------------------------------------
1041function ignoreToken() {
1042 //NOTE: do nothing =)
1043}
1044
1045function misplacedDoctype(p) {
1046 p._err(ERR.misplacedDoctype);
1047}
1048
1049function appendComment(p, token) {
1050 p._appendCommentNode(token, p.openElements.currentTmplContent || p.openElements.current);
1051}
1052
1053function appendCommentToRootHtmlElement(p, token) {
1054 p._appendCommentNode(token, p.openElements.items[0]);
1055}
1056
1057function appendCommentToDocument(p, token) {
1058 p._appendCommentNode(token, p.document);
1059}
1060
1061function insertCharacters(p, token) {
1062 p._insertCharacters(token);
1063}
1064
1065function stopParsing(p) {
1066 p.stopped = true;
1067}
1068
1069// The "initial" insertion mode
1070//------------------------------------------------------------------
1071function doctypeInInitialMode(p, token) {
1072 p._setDocumentType(token);
1073
1074 const mode = token.forceQuirks ? HTML.DOCUMENT_MODE.QUIRKS : doctype.getDocumentMode(token);
1075
1076 if (!doctype.isConforming(token)) {
1077 p._err(ERR.nonConformingDoctype);
1078 }
1079
1080 p.treeAdapter.setDocumentMode(p.document, mode);
1081
1082 p.insertionMode = BEFORE_HTML_MODE;
1083}
1084
1085function tokenInInitialMode(p, token) {
1086 p._err(ERR.missingDoctype, { beforeToken: true });
1087 p.treeAdapter.setDocumentMode(p.document, HTML.DOCUMENT_MODE.QUIRKS);
1088 p.insertionMode = BEFORE_HTML_MODE;
1089 p._processToken(token);
1090}
1091
1092// The "before html" insertion mode
1093//------------------------------------------------------------------
1094function startTagBeforeHtml(p, token) {
1095 if (token.tagName === $.HTML) {
1096 p._insertElement(token, NS.HTML);
1097 p.insertionMode = BEFORE_HEAD_MODE;
1098 } else {
1099 tokenBeforeHtml(p, token);
1100 }
1101}
1102
1103function endTagBeforeHtml(p, token) {
1104 const tn = token.tagName;
1105
1106 if (tn === $.HTML || tn === $.HEAD || tn === $.BODY || tn === $.BR) {
1107 tokenBeforeHtml(p, token);
1108 }
1109}
1110
1111function tokenBeforeHtml(p, token) {
1112 p._insertFakeRootElement();
1113 p.insertionMode = BEFORE_HEAD_MODE;
1114 p._processToken(token);
1115}
1116
1117// The "before head" insertion mode
1118//------------------------------------------------------------------
1119function startTagBeforeHead(p, token) {
1120 const tn = token.tagName;
1121
1122 if (tn === $.HTML) {
1123 startTagInBody(p, token);
1124 } else if (tn === $.HEAD) {
1125 p._insertElement(token, NS.HTML);
1126 p.headElement = p.openElements.current;
1127 p.insertionMode = IN_HEAD_MODE;
1128 } else {
1129 tokenBeforeHead(p, token);
1130 }
1131}
1132
1133function endTagBeforeHead(p, token) {
1134 const tn = token.tagName;
1135
1136 if (tn === $.HEAD || tn === $.BODY || tn === $.HTML || tn === $.BR) {
1137 tokenBeforeHead(p, token);
1138 } else {
1139 p._err(ERR.endTagWithoutMatchingOpenElement);
1140 }
1141}
1142
1143function tokenBeforeHead(p, token) {
1144 p._insertFakeElement($.HEAD);
1145 p.headElement = p.openElements.current;
1146 p.insertionMode = IN_HEAD_MODE;
1147 p._processToken(token);
1148}
1149
1150// The "in head" insertion mode
1151//------------------------------------------------------------------
1152function startTagInHead(p, token) {
1153 const tn = token.tagName;
1154
1155 if (tn === $.HTML) {
1156 startTagInBody(p, token);
1157 } else if (tn === $.BASE || tn === $.BASEFONT || tn === $.BGSOUND || tn === $.LINK || tn === $.META) {
1158 p._appendElement(token, NS.HTML);
1159 token.ackSelfClosing = true;
1160 } else if (tn === $.TITLE) {
1161 p._switchToTextParsing(token, Tokenizer.MODE.RCDATA);
1162 } else if (tn === $.NOSCRIPT) {
1163 if (p.options.scriptingEnabled) {
1164 p._switchToTextParsing(token, Tokenizer.MODE.RAWTEXT);
1165 } else {
1166 p._insertElement(token, NS.HTML);
1167 p.insertionMode = IN_HEAD_NO_SCRIPT_MODE;
1168 }
1169 } else if (tn === $.NOFRAMES || tn === $.STYLE) {
1170 p._switchToTextParsing(token, Tokenizer.MODE.RAWTEXT);
1171 } else if (tn === $.SCRIPT) {
1172 p._switchToTextParsing(token, Tokenizer.MODE.SCRIPT_DATA);
1173 } else if (tn === $.TEMPLATE) {
1174 p._insertTemplate(token, NS.HTML);
1175 p.activeFormattingElements.insertMarker();
1176 p.framesetOk = false;
1177 p.insertionMode = IN_TEMPLATE_MODE;
1178 p._pushTmplInsertionMode(IN_TEMPLATE_MODE);
1179 } else if (tn === $.HEAD) {
1180 p._err(ERR.misplacedStartTagForHeadElement);
1181 } else {
1182 tokenInHead(p, token);
1183 }
1184}
1185
1186function endTagInHead(p, token) {
1187 const tn = token.tagName;
1188
1189 if (tn === $.HEAD) {
1190 p.openElements.pop();
1191 p.insertionMode = AFTER_HEAD_MODE;
1192 } else if (tn === $.BODY || tn === $.BR || tn === $.HTML) {
1193 tokenInHead(p, token);
1194 } else if (tn === $.TEMPLATE) {
1195 if (p.openElements.tmplCount > 0) {
1196 p.openElements.generateImpliedEndTagsThoroughly();
1197
1198 if (p.openElements.currentTagName !== $.TEMPLATE) {
1199 p._err(ERR.closingOfElementWithOpenChildElements);
1200 }
1201
1202 p.openElements.popUntilTagNamePopped($.TEMPLATE);
1203 p.activeFormattingElements.clearToLastMarker();
1204 p._popTmplInsertionMode();
1205 p._resetInsertionMode();
1206 } else {
1207 p._err(ERR.endTagWithoutMatchingOpenElement);
1208 }
1209 } else {
1210 p._err(ERR.endTagWithoutMatchingOpenElement);
1211 }
1212}
1213
1214function tokenInHead(p, token) {
1215 p.openElements.pop();
1216 p.insertionMode = AFTER_HEAD_MODE;
1217 p._processToken(token);
1218}
1219
1220// The "in head no script" insertion mode
1221//------------------------------------------------------------------
1222function startTagInHeadNoScript(p, token) {
1223 const tn = token.tagName;
1224
1225 if (tn === $.HTML) {
1226 startTagInBody(p, token);
1227 } else if (
1228 tn === $.BASEFONT ||
1229 tn === $.BGSOUND ||
1230 tn === $.HEAD ||
1231 tn === $.LINK ||
1232 tn === $.META ||
1233 tn === $.NOFRAMES ||
1234 tn === $.STYLE
1235 ) {
1236 startTagInHead(p, token);
1237 } else if (tn === $.NOSCRIPT) {
1238 p._err(ERR.nestedNoscriptInHead);
1239 } else {
1240 tokenInHeadNoScript(p, token);
1241 }
1242}
1243
1244function endTagInHeadNoScript(p, token) {
1245 const tn = token.tagName;
1246
1247 if (tn === $.NOSCRIPT) {
1248 p.openElements.pop();
1249 p.insertionMode = IN_HEAD_MODE;
1250 } else if (tn === $.BR) {
1251 tokenInHeadNoScript(p, token);
1252 } else {
1253 p._err(ERR.endTagWithoutMatchingOpenElement);
1254 }
1255}
1256
1257function tokenInHeadNoScript(p, token) {
1258 const errCode =
1259 token.type === Tokenizer.EOF_TOKEN ? ERR.openElementsLeftAfterEof : ERR.disallowedContentInNoscriptInHead;
1260
1261 p._err(errCode);
1262 p.openElements.pop();
1263 p.insertionMode = IN_HEAD_MODE;
1264 p._processToken(token);
1265}
1266
1267// The "after head" insertion mode
1268//------------------------------------------------------------------
1269function startTagAfterHead(p, token) {
1270 const tn = token.tagName;
1271
1272 if (tn === $.HTML) {
1273 startTagInBody(p, token);
1274 } else if (tn === $.BODY) {
1275 p._insertElement(token, NS.HTML);
1276 p.framesetOk = false;
1277 p.insertionMode = IN_BODY_MODE;
1278 } else if (tn === $.FRAMESET) {
1279 p._insertElement(token, NS.HTML);
1280 p.insertionMode = IN_FRAMESET_MODE;
1281 } else if (
1282 tn === $.BASE ||
1283 tn === $.BASEFONT ||
1284 tn === $.BGSOUND ||
1285 tn === $.LINK ||
1286 tn === $.META ||
1287 tn === $.NOFRAMES ||
1288 tn === $.SCRIPT ||
1289 tn === $.STYLE ||
1290 tn === $.TEMPLATE ||
1291 tn === $.TITLE
1292 ) {
1293 p._err(ERR.abandonedHeadElementChild);
1294 p.openElements.push(p.headElement);
1295 startTagInHead(p, token);
1296 p.openElements.remove(p.headElement);
1297 } else if (tn === $.HEAD) {
1298 p._err(ERR.misplacedStartTagForHeadElement);
1299 } else {
1300 tokenAfterHead(p, token);
1301 }
1302}
1303
1304function endTagAfterHead(p, token) {
1305 const tn = token.tagName;
1306
1307 if (tn === $.BODY || tn === $.HTML || tn === $.BR) {
1308 tokenAfterHead(p, token);
1309 } else if (tn === $.TEMPLATE) {
1310 endTagInHead(p, token);
1311 } else {
1312 p._err(ERR.endTagWithoutMatchingOpenElement);
1313 }
1314}
1315
1316function tokenAfterHead(p, token) {
1317 p._insertFakeElement($.BODY);
1318 p.insertionMode = IN_BODY_MODE;
1319 p._processToken(token);
1320}
1321
1322// The "in body" insertion mode
1323//------------------------------------------------------------------
1324function whitespaceCharacterInBody(p, token) {
1325 p._reconstructActiveFormattingElements();
1326 p._insertCharacters(token);
1327}
1328
1329function characterInBody(p, token) {
1330 p._reconstructActiveFormattingElements();
1331 p._insertCharacters(token);
1332 p.framesetOk = false;
1333}
1334
1335function htmlStartTagInBody(p, token) {
1336 if (p.openElements.tmplCount === 0) {
1337 p.treeAdapter.adoptAttributes(p.openElements.items[0], token.attrs);
1338 }
1339}
1340
1341function bodyStartTagInBody(p, token) {
1342 const bodyElement = p.openElements.tryPeekProperlyNestedBodyElement();
1343
1344 if (bodyElement && p.openElements.tmplCount === 0) {
1345 p.framesetOk = false;
1346 p.treeAdapter.adoptAttributes(bodyElement, token.attrs);
1347 }
1348}
1349
1350function framesetStartTagInBody(p, token) {
1351 const bodyElement = p.openElements.tryPeekProperlyNestedBodyElement();
1352
1353 if (p.framesetOk && bodyElement) {
1354 p.treeAdapter.detachNode(bodyElement);
1355 p.openElements.popAllUpToHtmlElement();
1356 p._insertElement(token, NS.HTML);
1357 p.insertionMode = IN_FRAMESET_MODE;
1358 }
1359}
1360
1361function addressStartTagInBody(p, token) {
1362 if (p.openElements.hasInButtonScope($.P)) {
1363 p._closePElement();
1364 }
1365
1366 p._insertElement(token, NS.HTML);
1367}
1368
1369function numberedHeaderStartTagInBody(p, token) {
1370 if (p.openElements.hasInButtonScope($.P)) {
1371 p._closePElement();
1372 }
1373
1374 const tn = p.openElements.currentTagName;
1375
1376 if (tn === $.H1 || tn === $.H2 || tn === $.H3 || tn === $.H4 || tn === $.H5 || tn === $.H6) {
1377 p.openElements.pop();
1378 }
1379
1380 p._insertElement(token, NS.HTML);
1381}
1382
1383function preStartTagInBody(p, token) {
1384 if (p.openElements.hasInButtonScope($.P)) {
1385 p._closePElement();
1386 }
1387
1388 p._insertElement(token, NS.HTML);
1389 //NOTE: If the next token is a U+000A LINE FEED (LF) character token, then ignore that token and move
1390 //on to the next one. (Newlines at the start of pre blocks are ignored as an authoring convenience.)
1391 p.skipNextNewLine = true;
1392 p.framesetOk = false;
1393}
1394
1395function formStartTagInBody(p, token) {
1396 const inTemplate = p.openElements.tmplCount > 0;
1397
1398 if (!p.formElement || inTemplate) {
1399 if (p.openElements.hasInButtonScope($.P)) {
1400 p._closePElement();
1401 }
1402
1403 p._insertElement(token, NS.HTML);
1404
1405 if (!inTemplate) {
1406 p.formElement = p.openElements.current;
1407 }
1408 }
1409}
1410
1411function listItemStartTagInBody(p, token) {
1412 p.framesetOk = false;
1413
1414 const tn = token.tagName;
1415
1416 for (let i = p.openElements.stackTop; i >= 0; i--) {
1417 const element = p.openElements.items[i];
1418 const elementTn = p.treeAdapter.getTagName(element);
1419 let closeTn = null;
1420
1421 if (tn === $.LI && elementTn === $.LI) {
1422 closeTn = $.LI;
1423 } else if ((tn === $.DD || tn === $.DT) && (elementTn === $.DD || elementTn === $.DT)) {
1424 closeTn = elementTn;
1425 }
1426
1427 if (closeTn) {
1428 p.openElements.generateImpliedEndTagsWithExclusion(closeTn);
1429 p.openElements.popUntilTagNamePopped(closeTn);
1430 break;
1431 }
1432
1433 if (elementTn !== $.ADDRESS && elementTn !== $.DIV && elementTn !== $.P && p._isSpecialElement(element)) {
1434 break;
1435 }
1436 }
1437
1438 if (p.openElements.hasInButtonScope($.P)) {
1439 p._closePElement();
1440 }
1441
1442 p._insertElement(token, NS.HTML);
1443}
1444
1445function plaintextStartTagInBody(p, token) {
1446 if (p.openElements.hasInButtonScope($.P)) {
1447 p._closePElement();
1448 }
1449
1450 p._insertElement(token, NS.HTML);
1451 p.tokenizer.state = Tokenizer.MODE.PLAINTEXT;
1452}
1453
1454function buttonStartTagInBody(p, token) {
1455 if (p.openElements.hasInScope($.BUTTON)) {
1456 p.openElements.generateImpliedEndTags();
1457 p.openElements.popUntilTagNamePopped($.BUTTON);
1458 }
1459
1460 p._reconstructActiveFormattingElements();
1461 p._insertElement(token, NS.HTML);
1462 p.framesetOk = false;
1463}
1464
1465function aStartTagInBody(p, token) {
1466 const activeElementEntry = p.activeFormattingElements.getElementEntryInScopeWithTagName($.A);
1467
1468 if (activeElementEntry) {
1469 callAdoptionAgency(p, token);
1470 p.openElements.remove(activeElementEntry.element);
1471 p.activeFormattingElements.removeEntry(activeElementEntry);
1472 }
1473
1474 p._reconstructActiveFormattingElements();
1475 p._insertElement(token, NS.HTML);
1476 p.activeFormattingElements.pushElement(p.openElements.current, token);
1477}
1478
1479function bStartTagInBody(p, token) {
1480 p._reconstructActiveFormattingElements();
1481 p._insertElement(token, NS.HTML);
1482 p.activeFormattingElements.pushElement(p.openElements.current, token);
1483}
1484
1485function nobrStartTagInBody(p, token) {
1486 p._reconstructActiveFormattingElements();
1487
1488 if (p.openElements.hasInScope($.NOBR)) {
1489 callAdoptionAgency(p, token);
1490 p._reconstructActiveFormattingElements();
1491 }
1492
1493 p._insertElement(token, NS.HTML);
1494 p.activeFormattingElements.pushElement(p.openElements.current, token);
1495}
1496
1497function appletStartTagInBody(p, token) {
1498 p._reconstructActiveFormattingElements();
1499 p._insertElement(token, NS.HTML);
1500 p.activeFormattingElements.insertMarker();
1501 p.framesetOk = false;
1502}
1503
1504function tableStartTagInBody(p, token) {
1505 if (
1506 p.treeAdapter.getDocumentMode(p.document) !== HTML.DOCUMENT_MODE.QUIRKS &&
1507 p.openElements.hasInButtonScope($.P)
1508 ) {
1509 p._closePElement();
1510 }
1511
1512 p._insertElement(token, NS.HTML);
1513 p.framesetOk = false;
1514 p.insertionMode = IN_TABLE_MODE;
1515}
1516
1517function areaStartTagInBody(p, token) {
1518 p._reconstructActiveFormattingElements();
1519 p._appendElement(token, NS.HTML);
1520 p.framesetOk = false;
1521 token.ackSelfClosing = true;
1522}
1523
1524function inputStartTagInBody(p, token) {
1525 p._reconstructActiveFormattingElements();
1526 p._appendElement(token, NS.HTML);
1527
1528 const inputType = Tokenizer.getTokenAttr(token, ATTRS.TYPE);
1529
1530 if (!inputType || inputType.toLowerCase() !== HIDDEN_INPUT_TYPE) {
1531 p.framesetOk = false;
1532 }
1533
1534 token.ackSelfClosing = true;
1535}
1536
1537function paramStartTagInBody(p, token) {
1538 p._appendElement(token, NS.HTML);
1539 token.ackSelfClosing = true;
1540}
1541
1542function hrStartTagInBody(p, token) {
1543 if (p.openElements.hasInButtonScope($.P)) {
1544 p._closePElement();
1545 }
1546
1547 p._appendElement(token, NS.HTML);
1548 p.framesetOk = false;
1549 token.ackSelfClosing = true;
1550}
1551
1552function imageStartTagInBody(p, token) {
1553 token.tagName = $.IMG;
1554 areaStartTagInBody(p, token);
1555}
1556
1557function textareaStartTagInBody(p, token) {
1558 p._insertElement(token, NS.HTML);
1559 //NOTE: If the next token is a U+000A LINE FEED (LF) character token, then ignore that token and move
1560 //on to the next one. (Newlines at the start of textarea elements are ignored as an authoring convenience.)
1561 p.skipNextNewLine = true;
1562 p.tokenizer.state = Tokenizer.MODE.RCDATA;
1563 p.originalInsertionMode = p.insertionMode;
1564 p.framesetOk = false;
1565 p.insertionMode = TEXT_MODE;
1566}
1567
1568function xmpStartTagInBody(p, token) {
1569 if (p.openElements.hasInButtonScope($.P)) {
1570 p._closePElement();
1571 }
1572
1573 p._reconstructActiveFormattingElements();
1574 p.framesetOk = false;
1575 p._switchToTextParsing(token, Tokenizer.MODE.RAWTEXT);
1576}
1577
1578function iframeStartTagInBody(p, token) {
1579 p.framesetOk = false;
1580 p._switchToTextParsing(token, Tokenizer.MODE.RAWTEXT);
1581}
1582
1583//NOTE: here we assume that we always act as an user agent with enabled plugins, so we parse
1584//<noembed> as a rawtext.
1585function noembedStartTagInBody(p, token) {
1586 p._switchToTextParsing(token, Tokenizer.MODE.RAWTEXT);
1587}
1588
1589function selectStartTagInBody(p, token) {
1590 p._reconstructActiveFormattingElements();
1591 p._insertElement(token, NS.HTML);
1592 p.framesetOk = false;
1593
1594 if (
1595 p.insertionMode === IN_TABLE_MODE ||
1596 p.insertionMode === IN_CAPTION_MODE ||
1597 p.insertionMode === IN_TABLE_BODY_MODE ||
1598 p.insertionMode === IN_ROW_MODE ||
1599 p.insertionMode === IN_CELL_MODE
1600 ) {
1601 p.insertionMode = IN_SELECT_IN_TABLE_MODE;
1602 } else {
1603 p.insertionMode = IN_SELECT_MODE;
1604 }
1605}
1606
1607function optgroupStartTagInBody(p, token) {
1608 if (p.openElements.currentTagName === $.OPTION) {
1609 p.openElements.pop();
1610 }
1611
1612 p._reconstructActiveFormattingElements();
1613 p._insertElement(token, NS.HTML);
1614}
1615
1616function rbStartTagInBody(p, token) {
1617 if (p.openElements.hasInScope($.RUBY)) {
1618 p.openElements.generateImpliedEndTags();
1619 }
1620
1621 p._insertElement(token, NS.HTML);
1622}
1623
1624function rtStartTagInBody(p, token) {
1625 if (p.openElements.hasInScope($.RUBY)) {
1626 p.openElements.generateImpliedEndTagsWithExclusion($.RTC);
1627 }
1628
1629 p._insertElement(token, NS.HTML);
1630}
1631
1632function menuStartTagInBody(p, token) {
1633 if (p.openElements.hasInButtonScope($.P)) {
1634 p._closePElement();
1635 }
1636
1637 p._insertElement(token, NS.HTML);
1638}
1639
1640function mathStartTagInBody(p, token) {
1641 p._reconstructActiveFormattingElements();
1642
1643 foreignContent.adjustTokenMathMLAttrs(token);
1644 foreignContent.adjustTokenXMLAttrs(token);
1645
1646 if (token.selfClosing) {
1647 p._appendElement(token, NS.MATHML);
1648 } else {
1649 p._insertElement(token, NS.MATHML);
1650 }
1651
1652 token.ackSelfClosing = true;
1653}
1654
1655function svgStartTagInBody(p, token) {
1656 p._reconstructActiveFormattingElements();
1657
1658 foreignContent.adjustTokenSVGAttrs(token);
1659 foreignContent.adjustTokenXMLAttrs(token);
1660
1661 if (token.selfClosing) {
1662 p._appendElement(token, NS.SVG);
1663 } else {
1664 p._insertElement(token, NS.SVG);
1665 }
1666
1667 token.ackSelfClosing = true;
1668}
1669
1670function genericStartTagInBody(p, token) {
1671 p._reconstructActiveFormattingElements();
1672 p._insertElement(token, NS.HTML);
1673}
1674
1675//OPTIMIZATION: Integer comparisons are low-cost, so we can use very fast tag name length filters here.
1676//It's faster than using dictionary.
1677function startTagInBody(p, token) {
1678 const tn = token.tagName;
1679
1680 switch (tn.length) {
1681 case 1:
1682 if (tn === $.I || tn === $.S || tn === $.B || tn === $.U) {
1683 bStartTagInBody(p, token);
1684 } else if (tn === $.P) {
1685 addressStartTagInBody(p, token);
1686 } else if (tn === $.A) {
1687 aStartTagInBody(p, token);
1688 } else {
1689 genericStartTagInBody(p, token);
1690 }
1691
1692 break;
1693
1694 case 2:
1695 if (tn === $.DL || tn === $.OL || tn === $.UL) {
1696 addressStartTagInBody(p, token);
1697 } else if (tn === $.H1 || tn === $.H2 || tn === $.H3 || tn === $.H4 || tn === $.H5 || tn === $.H6) {
1698 numberedHeaderStartTagInBody(p, token);
1699 } else if (tn === $.LI || tn === $.DD || tn === $.DT) {
1700 listItemStartTagInBody(p, token);
1701 } else if (tn === $.EM || tn === $.TT) {
1702 bStartTagInBody(p, token);
1703 } else if (tn === $.BR) {
1704 areaStartTagInBody(p, token);
1705 } else if (tn === $.HR) {
1706 hrStartTagInBody(p, token);
1707 } else if (tn === $.RB) {
1708 rbStartTagInBody(p, token);
1709 } else if (tn === $.RT || tn === $.RP) {
1710 rtStartTagInBody(p, token);
1711 } else if (tn !== $.TH && tn !== $.TD && tn !== $.TR) {
1712 genericStartTagInBody(p, token);
1713 }
1714
1715 break;
1716
1717 case 3:
1718 if (tn === $.DIV || tn === $.DIR || tn === $.NAV) {
1719 addressStartTagInBody(p, token);
1720 } else if (tn === $.PRE) {
1721 preStartTagInBody(p, token);
1722 } else if (tn === $.BIG) {
1723 bStartTagInBody(p, token);
1724 } else if (tn === $.IMG || tn === $.WBR) {
1725 areaStartTagInBody(p, token);
1726 } else if (tn === $.XMP) {
1727 xmpStartTagInBody(p, token);
1728 } else if (tn === $.SVG) {
1729 svgStartTagInBody(p, token);
1730 } else if (tn === $.RTC) {
1731 rbStartTagInBody(p, token);
1732 } else if (tn !== $.COL) {
1733 genericStartTagInBody(p, token);
1734 }
1735
1736 break;
1737
1738 case 4:
1739 if (tn === $.HTML) {
1740 htmlStartTagInBody(p, token);
1741 } else if (tn === $.BASE || tn === $.LINK || tn === $.META) {
1742 startTagInHead(p, token);
1743 } else if (tn === $.BODY) {
1744 bodyStartTagInBody(p, token);
1745 } else if (tn === $.MAIN || tn === $.MENU) {
1746 addressStartTagInBody(p, token);
1747 } else if (tn === $.FORM) {
1748 formStartTagInBody(p, token);
1749 } else if (tn === $.CODE || tn === $.FONT) {
1750 bStartTagInBody(p, token);
1751 } else if (tn === $.NOBR) {
1752 nobrStartTagInBody(p, token);
1753 } else if (tn === $.AREA) {
1754 areaStartTagInBody(p, token);
1755 } else if (tn === $.MATH) {
1756 mathStartTagInBody(p, token);
1757 } else if (tn === $.MENU) {
1758 menuStartTagInBody(p, token);
1759 } else if (tn !== $.HEAD) {
1760 genericStartTagInBody(p, token);
1761 }
1762
1763 break;
1764
1765 case 5:
1766 if (tn === $.STYLE || tn === $.TITLE) {
1767 startTagInHead(p, token);
1768 } else if (tn === $.ASIDE) {
1769 addressStartTagInBody(p, token);
1770 } else if (tn === $.SMALL) {
1771 bStartTagInBody(p, token);
1772 } else if (tn === $.TABLE) {
1773 tableStartTagInBody(p, token);
1774 } else if (tn === $.EMBED) {
1775 areaStartTagInBody(p, token);
1776 } else if (tn === $.INPUT) {
1777 inputStartTagInBody(p, token);
1778 } else if (tn === $.PARAM || tn === $.TRACK) {
1779 paramStartTagInBody(p, token);
1780 } else if (tn === $.IMAGE) {
1781 imageStartTagInBody(p, token);
1782 } else if (tn !== $.FRAME && tn !== $.TBODY && tn !== $.TFOOT && tn !== $.THEAD) {
1783 genericStartTagInBody(p, token);
1784 }
1785
1786 break;
1787
1788 case 6:
1789 if (tn === $.SCRIPT) {
1790 startTagInHead(p, token);
1791 } else if (
1792 tn === $.CENTER ||
1793 tn === $.FIGURE ||
1794 tn === $.FOOTER ||
1795 tn === $.HEADER ||
1796 tn === $.HGROUP ||
1797 tn === $.DIALOG
1798 ) {
1799 addressStartTagInBody(p, token);
1800 } else if (tn === $.BUTTON) {
1801 buttonStartTagInBody(p, token);
1802 } else if (tn === $.STRIKE || tn === $.STRONG) {
1803 bStartTagInBody(p, token);
1804 } else if (tn === $.APPLET || tn === $.OBJECT) {
1805 appletStartTagInBody(p, token);
1806 } else if (tn === $.KEYGEN) {
1807 areaStartTagInBody(p, token);
1808 } else if (tn === $.SOURCE) {
1809 paramStartTagInBody(p, token);
1810 } else if (tn === $.IFRAME) {
1811 iframeStartTagInBody(p, token);
1812 } else if (tn === $.SELECT) {
1813 selectStartTagInBody(p, token);
1814 } else if (tn === $.OPTION) {
1815 optgroupStartTagInBody(p, token);
1816 } else {
1817 genericStartTagInBody(p, token);
1818 }
1819
1820 break;
1821
1822 case 7:
1823 if (tn === $.BGSOUND) {
1824 startTagInHead(p, token);
1825 } else if (
1826 tn === $.DETAILS ||
1827 tn === $.ADDRESS ||
1828 tn === $.ARTICLE ||
1829 tn === $.SECTION ||
1830 tn === $.SUMMARY
1831 ) {
1832 addressStartTagInBody(p, token);
1833 } else if (tn === $.LISTING) {
1834 preStartTagInBody(p, token);
1835 } else if (tn === $.MARQUEE) {
1836 appletStartTagInBody(p, token);
1837 } else if (tn === $.NOEMBED) {
1838 noembedStartTagInBody(p, token);
1839 } else if (tn !== $.CAPTION) {
1840 genericStartTagInBody(p, token);
1841 }
1842
1843 break;
1844
1845 case 8:
1846 if (tn === $.BASEFONT) {
1847 startTagInHead(p, token);
1848 } else if (tn === $.FRAMESET) {
1849 framesetStartTagInBody(p, token);
1850 } else if (tn === $.FIELDSET) {
1851 addressStartTagInBody(p, token);
1852 } else if (tn === $.TEXTAREA) {
1853 textareaStartTagInBody(p, token);
1854 } else if (tn === $.TEMPLATE) {
1855 startTagInHead(p, token);
1856 } else if (tn === $.NOSCRIPT) {
1857 if (p.options.scriptingEnabled) {
1858 noembedStartTagInBody(p, token);
1859 } else {
1860 genericStartTagInBody(p, token);
1861 }
1862 } else if (tn === $.OPTGROUP) {
1863 optgroupStartTagInBody(p, token);
1864 } else if (tn !== $.COLGROUP) {
1865 genericStartTagInBody(p, token);
1866 }
1867
1868 break;
1869
1870 case 9:
1871 if (tn === $.PLAINTEXT) {
1872 plaintextStartTagInBody(p, token);
1873 } else {
1874 genericStartTagInBody(p, token);
1875 }
1876
1877 break;
1878
1879 case 10:
1880 if (tn === $.BLOCKQUOTE || tn === $.FIGCAPTION) {
1881 addressStartTagInBody(p, token);
1882 } else {
1883 genericStartTagInBody(p, token);
1884 }
1885
1886 break;
1887
1888 default:
1889 genericStartTagInBody(p, token);
1890 }
1891}
1892
1893function bodyEndTagInBody(p) {
1894 if (p.openElements.hasInScope($.BODY)) {
1895 p.insertionMode = AFTER_BODY_MODE;
1896 }
1897}
1898
1899function htmlEndTagInBody(p, token) {
1900 if (p.openElements.hasInScope($.BODY)) {
1901 p.insertionMode = AFTER_BODY_MODE;
1902 p._processToken(token);
1903 }
1904}
1905
1906function addressEndTagInBody(p, token) {
1907 const tn = token.tagName;
1908
1909 if (p.openElements.hasInScope(tn)) {
1910 p.openElements.generateImpliedEndTags();
1911 p.openElements.popUntilTagNamePopped(tn);
1912 }
1913}
1914
1915function formEndTagInBody(p) {
1916 const inTemplate = p.openElements.tmplCount > 0;
1917 const formElement = p.formElement;
1918
1919 if (!inTemplate) {
1920 p.formElement = null;
1921 }
1922
1923 if ((formElement || inTemplate) && p.openElements.hasInScope($.FORM)) {
1924 p.openElements.generateImpliedEndTags();
1925
1926 if (inTemplate) {
1927 p.openElements.popUntilTagNamePopped($.FORM);
1928 } else {
1929 p.openElements.remove(formElement);
1930 }
1931 }
1932}
1933
1934function pEndTagInBody(p) {
1935 if (!p.openElements.hasInButtonScope($.P)) {
1936 p._insertFakeElement($.P);
1937 }
1938
1939 p._closePElement();
1940}
1941
1942function liEndTagInBody(p) {
1943 if (p.openElements.hasInListItemScope($.LI)) {
1944 p.openElements.generateImpliedEndTagsWithExclusion($.LI);
1945 p.openElements.popUntilTagNamePopped($.LI);
1946 }
1947}
1948
1949function ddEndTagInBody(p, token) {
1950 const tn = token.tagName;
1951
1952 if (p.openElements.hasInScope(tn)) {
1953 p.openElements.generateImpliedEndTagsWithExclusion(tn);
1954 p.openElements.popUntilTagNamePopped(tn);
1955 }
1956}
1957
1958function numberedHeaderEndTagInBody(p) {
1959 if (p.openElements.hasNumberedHeaderInScope()) {
1960 p.openElements.generateImpliedEndTags();
1961 p.openElements.popUntilNumberedHeaderPopped();
1962 }
1963}
1964
1965function appletEndTagInBody(p, token) {
1966 const tn = token.tagName;
1967
1968 if (p.openElements.hasInScope(tn)) {
1969 p.openElements.generateImpliedEndTags();
1970 p.openElements.popUntilTagNamePopped(tn);
1971 p.activeFormattingElements.clearToLastMarker();
1972 }
1973}
1974
1975function brEndTagInBody(p) {
1976 p._reconstructActiveFormattingElements();
1977 p._insertFakeElement($.BR);
1978 p.openElements.pop();
1979 p.framesetOk = false;
1980}
1981
1982function genericEndTagInBody(p, token) {
1983 const tn = token.tagName;
1984
1985 for (let i = p.openElements.stackTop; i > 0; i--) {
1986 const element = p.openElements.items[i];
1987
1988 if (p.treeAdapter.getTagName(element) === tn) {
1989 p.openElements.generateImpliedEndTagsWithExclusion(tn);
1990 p.openElements.popUntilElementPopped(element);
1991 break;
1992 }
1993
1994 if (p._isSpecialElement(element)) {
1995 break;
1996 }
1997 }
1998}
1999
2000//OPTIMIZATION: Integer comparisons are low-cost, so we can use very fast tag name length filters here.
2001//It's faster than using dictionary.
2002function endTagInBody(p, token) {
2003 const tn = token.tagName;
2004
2005 switch (tn.length) {
2006 case 1:
2007 if (tn === $.A || tn === $.B || tn === $.I || tn === $.S || tn === $.U) {
2008 callAdoptionAgency(p, token);
2009 } else if (tn === $.P) {
2010 pEndTagInBody(p, token);
2011 } else {
2012 genericEndTagInBody(p, token);
2013 }
2014
2015 break;
2016
2017 case 2:
2018 if (tn === $.DL || tn === $.UL || tn === $.OL) {
2019 addressEndTagInBody(p, token);
2020 } else if (tn === $.LI) {
2021 liEndTagInBody(p, token);
2022 } else if (tn === $.DD || tn === $.DT) {
2023 ddEndTagInBody(p, token);
2024 } else if (tn === $.H1 || tn === $.H2 || tn === $.H3 || tn === $.H4 || tn === $.H5 || tn === $.H6) {
2025 numberedHeaderEndTagInBody(p, token);
2026 } else if (tn === $.BR) {
2027 brEndTagInBody(p, token);
2028 } else if (tn === $.EM || tn === $.TT) {
2029 callAdoptionAgency(p, token);
2030 } else {
2031 genericEndTagInBody(p, token);
2032 }
2033
2034 break;
2035
2036 case 3:
2037 if (tn === $.BIG) {
2038 callAdoptionAgency(p, token);
2039 } else if (tn === $.DIR || tn === $.DIV || tn === $.NAV || tn === $.PRE) {
2040 addressEndTagInBody(p, token);
2041 } else {
2042 genericEndTagInBody(p, token);
2043 }
2044
2045 break;
2046
2047 case 4:
2048 if (tn === $.BODY) {
2049 bodyEndTagInBody(p, token);
2050 } else if (tn === $.HTML) {
2051 htmlEndTagInBody(p, token);
2052 } else if (tn === $.FORM) {
2053 formEndTagInBody(p, token);
2054 } else if (tn === $.CODE || tn === $.FONT || tn === $.NOBR) {
2055 callAdoptionAgency(p, token);
2056 } else if (tn === $.MAIN || tn === $.MENU) {
2057 addressEndTagInBody(p, token);
2058 } else {
2059 genericEndTagInBody(p, token);
2060 }
2061
2062 break;
2063
2064 case 5:
2065 if (tn === $.ASIDE) {
2066 addressEndTagInBody(p, token);
2067 } else if (tn === $.SMALL) {
2068 callAdoptionAgency(p, token);
2069 } else {
2070 genericEndTagInBody(p, token);
2071 }
2072
2073 break;
2074
2075 case 6:
2076 if (
2077 tn === $.CENTER ||
2078 tn === $.FIGURE ||
2079 tn === $.FOOTER ||
2080 tn === $.HEADER ||
2081 tn === $.HGROUP ||
2082 tn === $.DIALOG
2083 ) {
2084 addressEndTagInBody(p, token);
2085 } else if (tn === $.APPLET || tn === $.OBJECT) {
2086 appletEndTagInBody(p, token);
2087 } else if (tn === $.STRIKE || tn === $.STRONG) {
2088 callAdoptionAgency(p, token);
2089 } else {
2090 genericEndTagInBody(p, token);
2091 }
2092
2093 break;
2094
2095 case 7:
2096 if (
2097 tn === $.ADDRESS ||
2098 tn === $.ARTICLE ||
2099 tn === $.DETAILS ||
2100 tn === $.SECTION ||
2101 tn === $.SUMMARY ||
2102 tn === $.LISTING
2103 ) {
2104 addressEndTagInBody(p, token);
2105 } else if (tn === $.MARQUEE) {
2106 appletEndTagInBody(p, token);
2107 } else {
2108 genericEndTagInBody(p, token);
2109 }
2110
2111 break;
2112
2113 case 8:
2114 if (tn === $.FIELDSET) {
2115 addressEndTagInBody(p, token);
2116 } else if (tn === $.TEMPLATE) {
2117 endTagInHead(p, token);
2118 } else {
2119 genericEndTagInBody(p, token);
2120 }
2121
2122 break;
2123
2124 case 10:
2125 if (tn === $.BLOCKQUOTE || tn === $.FIGCAPTION) {
2126 addressEndTagInBody(p, token);
2127 } else {
2128 genericEndTagInBody(p, token);
2129 }
2130
2131 break;
2132
2133 default:
2134 genericEndTagInBody(p, token);
2135 }
2136}
2137
2138function eofInBody(p, token) {
2139 if (p.tmplInsertionModeStackTop > -1) {
2140 eofInTemplate(p, token);
2141 } else {
2142 p.stopped = true;
2143 }
2144}
2145
2146// The "text" insertion mode
2147//------------------------------------------------------------------
2148function endTagInText(p, token) {
2149 if (token.tagName === $.SCRIPT) {
2150 p.pendingScript = p.openElements.current;
2151 }
2152
2153 p.openElements.pop();
2154 p.insertionMode = p.originalInsertionMode;
2155}
2156
2157function eofInText(p, token) {
2158 p._err(ERR.eofInElementThatCanContainOnlyText);
2159 p.openElements.pop();
2160 p.insertionMode = p.originalInsertionMode;
2161 p._processToken(token);
2162}
2163
2164// The "in table" insertion mode
2165//------------------------------------------------------------------
2166function characterInTable(p, token) {
2167 const curTn = p.openElements.currentTagName;
2168
2169 if (curTn === $.TABLE || curTn === $.TBODY || curTn === $.TFOOT || curTn === $.THEAD || curTn === $.TR) {
2170 p.pendingCharacterTokens = [];
2171 p.hasNonWhitespacePendingCharacterToken = false;
2172 p.originalInsertionMode = p.insertionMode;
2173 p.insertionMode = IN_TABLE_TEXT_MODE;
2174 p._processToken(token);
2175 } else {
2176 tokenInTable(p, token);
2177 }
2178}
2179
2180function captionStartTagInTable(p, token) {
2181 p.openElements.clearBackToTableContext();
2182 p.activeFormattingElements.insertMarker();
2183 p._insertElement(token, NS.HTML);
2184 p.insertionMode = IN_CAPTION_MODE;
2185}
2186
2187function colgroupStartTagInTable(p, token) {
2188 p.openElements.clearBackToTableContext();
2189 p._insertElement(token, NS.HTML);
2190 p.insertionMode = IN_COLUMN_GROUP_MODE;
2191}
2192
2193function colStartTagInTable(p, token) {
2194 p.openElements.clearBackToTableContext();
2195 p._insertFakeElement($.COLGROUP);
2196 p.insertionMode = IN_COLUMN_GROUP_MODE;
2197 p._processToken(token);
2198}
2199
2200function tbodyStartTagInTable(p, token) {
2201 p.openElements.clearBackToTableContext();
2202 p._insertElement(token, NS.HTML);
2203 p.insertionMode = IN_TABLE_BODY_MODE;
2204}
2205
2206function tdStartTagInTable(p, token) {
2207 p.openElements.clearBackToTableContext();
2208 p._insertFakeElement($.TBODY);
2209 p.insertionMode = IN_TABLE_BODY_MODE;
2210 p._processToken(token);
2211}
2212
2213function tableStartTagInTable(p, token) {
2214 if (p.openElements.hasInTableScope($.TABLE)) {
2215 p.openElements.popUntilTagNamePopped($.TABLE);
2216 p._resetInsertionMode();
2217 p._processToken(token);
2218 }
2219}
2220
2221function inputStartTagInTable(p, token) {
2222 const inputType = Tokenizer.getTokenAttr(token, ATTRS.TYPE);
2223
2224 if (inputType && inputType.toLowerCase() === HIDDEN_INPUT_TYPE) {
2225 p._appendElement(token, NS.HTML);
2226 } else {
2227 tokenInTable(p, token);
2228 }
2229
2230 token.ackSelfClosing = true;
2231}
2232
2233function formStartTagInTable(p, token) {
2234 if (!p.formElement && p.openElements.tmplCount === 0) {
2235 p._insertElement(token, NS.HTML);
2236 p.formElement = p.openElements.current;
2237 p.openElements.pop();
2238 }
2239}
2240
2241function startTagInTable(p, token) {
2242 const tn = token.tagName;
2243
2244 switch (tn.length) {
2245 case 2:
2246 if (tn === $.TD || tn === $.TH || tn === $.TR) {
2247 tdStartTagInTable(p, token);
2248 } else {
2249 tokenInTable(p, token);
2250 }
2251
2252 break;
2253
2254 case 3:
2255 if (tn === $.COL) {
2256 colStartTagInTable(p, token);
2257 } else {
2258 tokenInTable(p, token);
2259 }
2260
2261 break;
2262
2263 case 4:
2264 if (tn === $.FORM) {
2265 formStartTagInTable(p, token);
2266 } else {
2267 tokenInTable(p, token);
2268 }
2269
2270 break;
2271
2272 case 5:
2273 if (tn === $.TABLE) {
2274 tableStartTagInTable(p, token);
2275 } else if (tn === $.STYLE) {
2276 startTagInHead(p, token);
2277 } else if (tn === $.TBODY || tn === $.TFOOT || tn === $.THEAD) {
2278 tbodyStartTagInTable(p, token);
2279 } else if (tn === $.INPUT) {
2280 inputStartTagInTable(p, token);
2281 } else {
2282 tokenInTable(p, token);
2283 }
2284
2285 break;
2286
2287 case 6:
2288 if (tn === $.SCRIPT) {
2289 startTagInHead(p, token);
2290 } else {
2291 tokenInTable(p, token);
2292 }
2293
2294 break;
2295
2296 case 7:
2297 if (tn === $.CAPTION) {
2298 captionStartTagInTable(p, token);
2299 } else {
2300 tokenInTable(p, token);
2301 }
2302
2303 break;
2304
2305 case 8:
2306 if (tn === $.COLGROUP) {
2307 colgroupStartTagInTable(p, token);
2308 } else if (tn === $.TEMPLATE) {
2309 startTagInHead(p, token);
2310 } else {
2311 tokenInTable(p, token);
2312 }
2313
2314 break;
2315
2316 default:
2317 tokenInTable(p, token);
2318 }
2319}
2320
2321function endTagInTable(p, token) {
2322 const tn = token.tagName;
2323
2324 if (tn === $.TABLE) {
2325 if (p.openElements.hasInTableScope($.TABLE)) {
2326 p.openElements.popUntilTagNamePopped($.TABLE);
2327 p._resetInsertionMode();
2328 }
2329 } else if (tn === $.TEMPLATE) {
2330 endTagInHead(p, token);
2331 } else if (
2332 tn !== $.BODY &&
2333 tn !== $.CAPTION &&
2334 tn !== $.COL &&
2335 tn !== $.COLGROUP &&
2336 tn !== $.HTML &&
2337 tn !== $.TBODY &&
2338 tn !== $.TD &&
2339 tn !== $.TFOOT &&
2340 tn !== $.TH &&
2341 tn !== $.THEAD &&
2342 tn !== $.TR
2343 ) {
2344 tokenInTable(p, token);
2345 }
2346}
2347
2348function tokenInTable(p, token) {
2349 const savedFosterParentingState = p.fosterParentingEnabled;
2350
2351 p.fosterParentingEnabled = true;
2352 p._processTokenInBodyMode(token);
2353 p.fosterParentingEnabled = savedFosterParentingState;
2354}
2355
2356// The "in table text" insertion mode
2357//------------------------------------------------------------------
2358function whitespaceCharacterInTableText(p, token) {
2359 p.pendingCharacterTokens.push(token);
2360}
2361
2362function characterInTableText(p, token) {
2363 p.pendingCharacterTokens.push(token);
2364 p.hasNonWhitespacePendingCharacterToken = true;
2365}
2366
2367function tokenInTableText(p, token) {
2368 let i = 0;
2369
2370 if (p.hasNonWhitespacePendingCharacterToken) {
2371 for (; i < p.pendingCharacterTokens.length; i++) {
2372 tokenInTable(p, p.pendingCharacterTokens[i]);
2373 }
2374 } else {
2375 for (; i < p.pendingCharacterTokens.length; i++) {
2376 p._insertCharacters(p.pendingCharacterTokens[i]);
2377 }
2378 }
2379
2380 p.insertionMode = p.originalInsertionMode;
2381 p._processToken(token);
2382}
2383
2384// The "in caption" insertion mode
2385//------------------------------------------------------------------
2386function startTagInCaption(p, token) {
2387 const tn = token.tagName;
2388
2389 if (
2390 tn === $.CAPTION ||
2391 tn === $.COL ||
2392 tn === $.COLGROUP ||
2393 tn === $.TBODY ||
2394 tn === $.TD ||
2395 tn === $.TFOOT ||
2396 tn === $.TH ||
2397 tn === $.THEAD ||
2398 tn === $.TR
2399 ) {
2400 if (p.openElements.hasInTableScope($.CAPTION)) {
2401 p.openElements.generateImpliedEndTags();
2402 p.openElements.popUntilTagNamePopped($.CAPTION);
2403 p.activeFormattingElements.clearToLastMarker();
2404 p.insertionMode = IN_TABLE_MODE;
2405 p._processToken(token);
2406 }
2407 } else {
2408 startTagInBody(p, token);
2409 }
2410}
2411
2412function endTagInCaption(p, token) {
2413 const tn = token.tagName;
2414
2415 if (tn === $.CAPTION || tn === $.TABLE) {
2416 if (p.openElements.hasInTableScope($.CAPTION)) {
2417 p.openElements.generateImpliedEndTags();
2418 p.openElements.popUntilTagNamePopped($.CAPTION);
2419 p.activeFormattingElements.clearToLastMarker();
2420 p.insertionMode = IN_TABLE_MODE;
2421
2422 if (tn === $.TABLE) {
2423 p._processToken(token);
2424 }
2425 }
2426 } else if (
2427 tn !== $.BODY &&
2428 tn !== $.COL &&
2429 tn !== $.COLGROUP &&
2430 tn !== $.HTML &&
2431 tn !== $.TBODY &&
2432 tn !== $.TD &&
2433 tn !== $.TFOOT &&
2434 tn !== $.TH &&
2435 tn !== $.THEAD &&
2436 tn !== $.TR
2437 ) {
2438 endTagInBody(p, token);
2439 }
2440}
2441
2442// The "in column group" insertion mode
2443//------------------------------------------------------------------
2444function startTagInColumnGroup(p, token) {
2445 const tn = token.tagName;
2446
2447 if (tn === $.HTML) {
2448 startTagInBody(p, token);
2449 } else if (tn === $.COL) {
2450 p._appendElement(token, NS.HTML);
2451 token.ackSelfClosing = true;
2452 } else if (tn === $.TEMPLATE) {
2453 startTagInHead(p, token);
2454 } else {
2455 tokenInColumnGroup(p, token);
2456 }
2457}
2458
2459function endTagInColumnGroup(p, token) {
2460 const tn = token.tagName;
2461
2462 if (tn === $.COLGROUP) {
2463 if (p.openElements.currentTagName === $.COLGROUP) {
2464 p.openElements.pop();
2465 p.insertionMode = IN_TABLE_MODE;
2466 }
2467 } else if (tn === $.TEMPLATE) {
2468 endTagInHead(p, token);
2469 } else if (tn !== $.COL) {
2470 tokenInColumnGroup(p, token);
2471 }
2472}
2473
2474function tokenInColumnGroup(p, token) {
2475 if (p.openElements.currentTagName === $.COLGROUP) {
2476 p.openElements.pop();
2477 p.insertionMode = IN_TABLE_MODE;
2478 p._processToken(token);
2479 }
2480}
2481
2482// The "in table body" insertion mode
2483//------------------------------------------------------------------
2484function startTagInTableBody(p, token) {
2485 const tn = token.tagName;
2486
2487 if (tn === $.TR) {
2488 p.openElements.clearBackToTableBodyContext();
2489 p._insertElement(token, NS.HTML);
2490 p.insertionMode = IN_ROW_MODE;
2491 } else if (tn === $.TH || tn === $.TD) {
2492 p.openElements.clearBackToTableBodyContext();
2493 p._insertFakeElement($.TR);
2494 p.insertionMode = IN_ROW_MODE;
2495 p._processToken(token);
2496 } else if (
2497 tn === $.CAPTION ||
2498 tn === $.COL ||
2499 tn === $.COLGROUP ||
2500 tn === $.TBODY ||
2501 tn === $.TFOOT ||
2502 tn === $.THEAD
2503 ) {
2504 if (p.openElements.hasTableBodyContextInTableScope()) {
2505 p.openElements.clearBackToTableBodyContext();
2506 p.openElements.pop();
2507 p.insertionMode = IN_TABLE_MODE;
2508 p._processToken(token);
2509 }
2510 } else {
2511 startTagInTable(p, token);
2512 }
2513}
2514
2515function endTagInTableBody(p, token) {
2516 const tn = token.tagName;
2517
2518 if (tn === $.TBODY || tn === $.TFOOT || tn === $.THEAD) {
2519 if (p.openElements.hasInTableScope(tn)) {
2520 p.openElements.clearBackToTableBodyContext();
2521 p.openElements.pop();
2522 p.insertionMode = IN_TABLE_MODE;
2523 }
2524 } else if (tn === $.TABLE) {
2525 if (p.openElements.hasTableBodyContextInTableScope()) {
2526 p.openElements.clearBackToTableBodyContext();
2527 p.openElements.pop();
2528 p.insertionMode = IN_TABLE_MODE;
2529 p._processToken(token);
2530 }
2531 } else if (
2532 (tn !== $.BODY && tn !== $.CAPTION && tn !== $.COL && tn !== $.COLGROUP) ||
2533 (tn !== $.HTML && tn !== $.TD && tn !== $.TH && tn !== $.TR)
2534 ) {
2535 endTagInTable(p, token);
2536 }
2537}
2538
2539// The "in row" insertion mode
2540//------------------------------------------------------------------
2541function startTagInRow(p, token) {
2542 const tn = token.tagName;
2543
2544 if (tn === $.TH || tn === $.TD) {
2545 p.openElements.clearBackToTableRowContext();
2546 p._insertElement(token, NS.HTML);
2547 p.insertionMode = IN_CELL_MODE;
2548 p.activeFormattingElements.insertMarker();
2549 } else if (
2550 tn === $.CAPTION ||
2551 tn === $.COL ||
2552 tn === $.COLGROUP ||
2553 tn === $.TBODY ||
2554 tn === $.TFOOT ||
2555 tn === $.THEAD ||
2556 tn === $.TR
2557 ) {
2558 if (p.openElements.hasInTableScope($.TR)) {
2559 p.openElements.clearBackToTableRowContext();
2560 p.openElements.pop();
2561 p.insertionMode = IN_TABLE_BODY_MODE;
2562 p._processToken(token);
2563 }
2564 } else {
2565 startTagInTable(p, token);
2566 }
2567}
2568
2569function endTagInRow(p, token) {
2570 const tn = token.tagName;
2571
2572 if (tn === $.TR) {
2573 if (p.openElements.hasInTableScope($.TR)) {
2574 p.openElements.clearBackToTableRowContext();
2575 p.openElements.pop();
2576 p.insertionMode = IN_TABLE_BODY_MODE;
2577 }
2578 } else if (tn === $.TABLE) {
2579 if (p.openElements.hasInTableScope($.TR)) {
2580 p.openElements.clearBackToTableRowContext();
2581 p.openElements.pop();
2582 p.insertionMode = IN_TABLE_BODY_MODE;
2583 p._processToken(token);
2584 }
2585 } else if (tn === $.TBODY || tn === $.TFOOT || tn === $.THEAD) {
2586 if (p.openElements.hasInTableScope(tn) || p.openElements.hasInTableScope($.TR)) {
2587 p.openElements.clearBackToTableRowContext();
2588 p.openElements.pop();
2589 p.insertionMode = IN_TABLE_BODY_MODE;
2590 p._processToken(token);
2591 }
2592 } else if (
2593 (tn !== $.BODY && tn !== $.CAPTION && tn !== $.COL && tn !== $.COLGROUP) ||
2594 (tn !== $.HTML && tn !== $.TD && tn !== $.TH)
2595 ) {
2596 endTagInTable(p, token);
2597 }
2598}
2599
2600// The "in cell" insertion mode
2601//------------------------------------------------------------------
2602function startTagInCell(p, token) {
2603 const tn = token.tagName;
2604
2605 if (
2606 tn === $.CAPTION ||
2607 tn === $.COL ||
2608 tn === $.COLGROUP ||
2609 tn === $.TBODY ||
2610 tn === $.TD ||
2611 tn === $.TFOOT ||
2612 tn === $.TH ||
2613 tn === $.THEAD ||
2614 tn === $.TR
2615 ) {
2616 if (p.openElements.hasInTableScope($.TD) || p.openElements.hasInTableScope($.TH)) {
2617 p._closeTableCell();
2618 p._processToken(token);
2619 }
2620 } else {
2621 startTagInBody(p, token);
2622 }
2623}
2624
2625function endTagInCell(p, token) {
2626 const tn = token.tagName;
2627
2628 if (tn === $.TD || tn === $.TH) {
2629 if (p.openElements.hasInTableScope(tn)) {
2630 p.openElements.generateImpliedEndTags();
2631 p.openElements.popUntilTagNamePopped(tn);
2632 p.activeFormattingElements.clearToLastMarker();
2633 p.insertionMode = IN_ROW_MODE;
2634 }
2635 } else if (tn === $.TABLE || tn === $.TBODY || tn === $.TFOOT || tn === $.THEAD || tn === $.TR) {
2636 if (p.openElements.hasInTableScope(tn)) {
2637 p._closeTableCell();
2638 p._processToken(token);
2639 }
2640 } else if (tn !== $.BODY && tn !== $.CAPTION && tn !== $.COL && tn !== $.COLGROUP && tn !== $.HTML) {
2641 endTagInBody(p, token);
2642 }
2643}
2644
2645// The "in select" insertion mode
2646//------------------------------------------------------------------
2647function startTagInSelect(p, token) {
2648 const tn = token.tagName;
2649
2650 if (tn === $.HTML) {
2651 startTagInBody(p, token);
2652 } else if (tn === $.OPTION) {
2653 if (p.openElements.currentTagName === $.OPTION) {
2654 p.openElements.pop();
2655 }
2656
2657 p._insertElement(token, NS.HTML);
2658 } else if (tn === $.OPTGROUP) {
2659 if (p.openElements.currentTagName === $.OPTION) {
2660 p.openElements.pop();
2661 }
2662
2663 if (p.openElements.currentTagName === $.OPTGROUP) {
2664 p.openElements.pop();
2665 }
2666
2667 p._insertElement(token, NS.HTML);
2668 } else if (tn === $.INPUT || tn === $.KEYGEN || tn === $.TEXTAREA || tn === $.SELECT) {
2669 if (p.openElements.hasInSelectScope($.SELECT)) {
2670 p.openElements.popUntilTagNamePopped($.SELECT);
2671 p._resetInsertionMode();
2672
2673 if (tn !== $.SELECT) {
2674 p._processToken(token);
2675 }
2676 }
2677 } else if (tn === $.SCRIPT || tn === $.TEMPLATE) {
2678 startTagInHead(p, token);
2679 }
2680}
2681
2682function endTagInSelect(p, token) {
2683 const tn = token.tagName;
2684
2685 if (tn === $.OPTGROUP) {
2686 const prevOpenElement = p.openElements.items[p.openElements.stackTop - 1];
2687 const prevOpenElementTn = prevOpenElement && p.treeAdapter.getTagName(prevOpenElement);
2688
2689 if (p.openElements.currentTagName === $.OPTION && prevOpenElementTn === $.OPTGROUP) {
2690 p.openElements.pop();
2691 }
2692
2693 if (p.openElements.currentTagName === $.OPTGROUP) {
2694 p.openElements.pop();
2695 }
2696 } else if (tn === $.OPTION) {
2697 if (p.openElements.currentTagName === $.OPTION) {
2698 p.openElements.pop();
2699 }
2700 } else if (tn === $.SELECT && p.openElements.hasInSelectScope($.SELECT)) {
2701 p.openElements.popUntilTagNamePopped($.SELECT);
2702 p._resetInsertionMode();
2703 } else if (tn === $.TEMPLATE) {
2704 endTagInHead(p, token);
2705 }
2706}
2707
2708//12.2.5.4.17 The "in select in table" insertion mode
2709//------------------------------------------------------------------
2710function startTagInSelectInTable(p, token) {
2711 const tn = token.tagName;
2712
2713 if (
2714 tn === $.CAPTION ||
2715 tn === $.TABLE ||
2716 tn === $.TBODY ||
2717 tn === $.TFOOT ||
2718 tn === $.THEAD ||
2719 tn === $.TR ||
2720 tn === $.TD ||
2721 tn === $.TH
2722 ) {
2723 p.openElements.popUntilTagNamePopped($.SELECT);
2724 p._resetInsertionMode();
2725 p._processToken(token);
2726 } else {
2727 startTagInSelect(p, token);
2728 }
2729}
2730
2731function endTagInSelectInTable(p, token) {
2732 const tn = token.tagName;
2733
2734 if (
2735 tn === $.CAPTION ||
2736 tn === $.TABLE ||
2737 tn === $.TBODY ||
2738 tn === $.TFOOT ||
2739 tn === $.THEAD ||
2740 tn === $.TR ||
2741 tn === $.TD ||
2742 tn === $.TH
2743 ) {
2744 if (p.openElements.hasInTableScope(tn)) {
2745 p.openElements.popUntilTagNamePopped($.SELECT);
2746 p._resetInsertionMode();
2747 p._processToken(token);
2748 }
2749 } else {
2750 endTagInSelect(p, token);
2751 }
2752}
2753
2754// The "in template" insertion mode
2755//------------------------------------------------------------------
2756function startTagInTemplate(p, token) {
2757 const tn = token.tagName;
2758
2759 if (
2760 tn === $.BASE ||
2761 tn === $.BASEFONT ||
2762 tn === $.BGSOUND ||
2763 tn === $.LINK ||
2764 tn === $.META ||
2765 tn === $.NOFRAMES ||
2766 tn === $.SCRIPT ||
2767 tn === $.STYLE ||
2768 tn === $.TEMPLATE ||
2769 tn === $.TITLE
2770 ) {
2771 startTagInHead(p, token);
2772 } else {
2773 const newInsertionMode = TEMPLATE_INSERTION_MODE_SWITCH_MAP[tn] || IN_BODY_MODE;
2774
2775 p._popTmplInsertionMode();
2776 p._pushTmplInsertionMode(newInsertionMode);
2777 p.insertionMode = newInsertionMode;
2778 p._processToken(token);
2779 }
2780}
2781
2782function endTagInTemplate(p, token) {
2783 if (token.tagName === $.TEMPLATE) {
2784 endTagInHead(p, token);
2785 }
2786}
2787
2788function eofInTemplate(p, token) {
2789 if (p.openElements.tmplCount > 0) {
2790 p.openElements.popUntilTagNamePopped($.TEMPLATE);
2791 p.activeFormattingElements.clearToLastMarker();
2792 p._popTmplInsertionMode();
2793 p._resetInsertionMode();
2794 p._processToken(token);
2795 } else {
2796 p.stopped = true;
2797 }
2798}
2799
2800// The "after body" insertion mode
2801//------------------------------------------------------------------
2802function startTagAfterBody(p, token) {
2803 if (token.tagName === $.HTML) {
2804 startTagInBody(p, token);
2805 } else {
2806 tokenAfterBody(p, token);
2807 }
2808}
2809
2810function endTagAfterBody(p, token) {
2811 if (token.tagName === $.HTML) {
2812 if (!p.fragmentContext) {
2813 p.insertionMode = AFTER_AFTER_BODY_MODE;
2814 }
2815 } else {
2816 tokenAfterBody(p, token);
2817 }
2818}
2819
2820function tokenAfterBody(p, token) {
2821 p.insertionMode = IN_BODY_MODE;
2822 p._processToken(token);
2823}
2824
2825// The "in frameset" insertion mode
2826//------------------------------------------------------------------
2827function startTagInFrameset(p, token) {
2828 const tn = token.tagName;
2829
2830 if (tn === $.HTML) {
2831 startTagInBody(p, token);
2832 } else if (tn === $.FRAMESET) {
2833 p._insertElement(token, NS.HTML);
2834 } else if (tn === $.FRAME) {
2835 p._appendElement(token, NS.HTML);
2836 token.ackSelfClosing = true;
2837 } else if (tn === $.NOFRAMES) {
2838 startTagInHead(p, token);
2839 }
2840}
2841
2842function endTagInFrameset(p, token) {
2843 if (token.tagName === $.FRAMESET && !p.openElements.isRootHtmlElementCurrent()) {
2844 p.openElements.pop();
2845
2846 if (!p.fragmentContext && p.openElements.currentTagName !== $.FRAMESET) {
2847 p.insertionMode = AFTER_FRAMESET_MODE;
2848 }
2849 }
2850}
2851
2852// The "after frameset" insertion mode
2853//------------------------------------------------------------------
2854function startTagAfterFrameset(p, token) {
2855 const tn = token.tagName;
2856
2857 if (tn === $.HTML) {
2858 startTagInBody(p, token);
2859 } else if (tn === $.NOFRAMES) {
2860 startTagInHead(p, token);
2861 }
2862}
2863
2864function endTagAfterFrameset(p, token) {
2865 if (token.tagName === $.HTML) {
2866 p.insertionMode = AFTER_AFTER_FRAMESET_MODE;
2867 }
2868}
2869
2870// The "after after body" insertion mode
2871//------------------------------------------------------------------
2872function startTagAfterAfterBody(p, token) {
2873 if (token.tagName === $.HTML) {
2874 startTagInBody(p, token);
2875 } else {
2876 tokenAfterAfterBody(p, token);
2877 }
2878}
2879
2880function tokenAfterAfterBody(p, token) {
2881 p.insertionMode = IN_BODY_MODE;
2882 p._processToken(token);
2883}
2884
2885// The "after after frameset" insertion mode
2886//------------------------------------------------------------------
2887function startTagAfterAfterFrameset(p, token) {
2888 const tn = token.tagName;
2889
2890 if (tn === $.HTML) {
2891 startTagInBody(p, token);
2892 } else if (tn === $.NOFRAMES) {
2893 startTagInHead(p, token);
2894 }
2895}
2896
2897// The rules for parsing tokens in foreign content
2898//------------------------------------------------------------------
2899function nullCharacterInForeignContent(p, token) {
2900 token.chars = unicode.REPLACEMENT_CHARACTER;
2901 p._insertCharacters(token);
2902}
2903
2904function characterInForeignContent(p, token) {
2905 p._insertCharacters(token);
2906 p.framesetOk = false;
2907}
2908
2909function startTagInForeignContent(p, token) {
2910 if (foreignContent.causesExit(token) && !p.fragmentContext) {
2911 while (
2912 p.treeAdapter.getNamespaceURI(p.openElements.current) !== NS.HTML &&
2913 !p._isIntegrationPoint(p.openElements.current)
2914 ) {
2915 p.openElements.pop();
2916 }
2917
2918 p._processToken(token);
2919 } else {
2920 const current = p._getAdjustedCurrentElement();
2921 const currentNs = p.treeAdapter.getNamespaceURI(current);
2922
2923 if (currentNs === NS.MATHML) {
2924 foreignContent.adjustTokenMathMLAttrs(token);
2925 } else if (currentNs === NS.SVG) {
2926 foreignContent.adjustTokenSVGTagName(token);
2927 foreignContent.adjustTokenSVGAttrs(token);
2928 }
2929
2930 foreignContent.adjustTokenXMLAttrs(token);
2931
2932 if (token.selfClosing) {
2933 p._appendElement(token, currentNs);
2934 } else {
2935 p._insertElement(token, currentNs);
2936 }
2937
2938 token.ackSelfClosing = true;
2939 }
2940}
2941
2942function endTagInForeignContent(p, token) {
2943 for (let i = p.openElements.stackTop; i > 0; i--) {
2944 const element = p.openElements.items[i];
2945
2946 if (p.treeAdapter.getNamespaceURI(element) === NS.HTML) {
2947 p._processToken(token);
2948 break;
2949 }
2950
2951 if (p.treeAdapter.getTagName(element).toLowerCase() === token.tagName) {
2952 p.openElements.popUntilElementPopped(element);
2953 break;
2954 }
2955 }
2956}
Note: See TracBrowser for help on using the repository browser.