parse5 8.1.0 → 8.1.1
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.
- package/README.md +12 -0
- package/dist/common/foreign-content.d.ts +1 -1
- package/dist/parser/formatting-element-list.d.ts +38 -10
- package/dist/parser/formatting-element-list.js +153 -70
- package/dist/parser/index.d.ts +6 -2
- package/dist/parser/index.js +169 -135
- package/dist/parser/open-element-stack.d.ts +85 -8
- package/dist/parser/open-element-stack.js +500 -150
- package/dist/serializer/index.js +48 -34
- package/dist/tokenizer/index.d.ts +3 -0
- package/dist/tokenizer/index.js +59 -5
- package/dist/tokenizer/preprocessor.d.ts +5 -1
- package/dist/tokenizer/preprocessor.js +8 -4
- package/dist/tree-adapters/default.js +50 -6
- package/package.json +1 -1
package/dist/parser/index.js
CHANGED
|
@@ -9,7 +9,27 @@ import * as unicode from '../common/unicode.js';
|
|
|
9
9
|
import { TAG_ID as $, TAG_NAMES as TN, NS, ATTRS, SPECIAL_ELEMENTS, DOCUMENT_MODE, NUMBERED_HEADERS, getTagID, } from '../common/html.js';
|
|
10
10
|
import { TokenType, getTokenAttr, } from '../common/token.js';
|
|
11
11
|
//Misc constants
|
|
12
|
+
// Elements that can select an insertion mode when it is reset.
|
|
13
|
+
const INSERTION_MODE_TAGS = [
|
|
14
|
+
$.TR,
|
|
15
|
+
$.TBODY,
|
|
16
|
+
$.THEAD,
|
|
17
|
+
$.TFOOT,
|
|
18
|
+
$.CAPTION,
|
|
19
|
+
$.COLGROUP,
|
|
20
|
+
$.TABLE,
|
|
21
|
+
$.FRAMESET,
|
|
22
|
+
$.SELECT,
|
|
23
|
+
$.TEMPLATE,
|
|
24
|
+
$.TD,
|
|
25
|
+
$.TH,
|
|
26
|
+
$.HEAD,
|
|
27
|
+
$.BODY,
|
|
28
|
+
$.HTML,
|
|
29
|
+
];
|
|
12
30
|
const HIDDEN_INPUT_TYPE = 'hidden';
|
|
31
|
+
// For the integration-point check of elements whose attributes do not matter; never attached to a node.
|
|
32
|
+
const EMPTY_ATTRS = [];
|
|
13
33
|
//Adoption agency loops iteration count
|
|
14
34
|
const AA_OUTER_LOOP_ITER = 8;
|
|
15
35
|
const AA_INNER_LOOP_ITER = 3;
|
|
@@ -77,8 +97,7 @@ export class Parser {
|
|
|
77
97
|
/** Indicates that the current node is not an element in the HTML namespace */
|
|
78
98
|
this.currentNotInHTML = false;
|
|
79
99
|
/**
|
|
80
|
-
* The template insertion mode
|
|
81
|
-
* Ie. the topmost element will always have index 0.
|
|
100
|
+
* The topmost template insertion mode is the last element.
|
|
82
101
|
*
|
|
83
102
|
* @internal
|
|
84
103
|
*/
|
|
@@ -105,7 +124,7 @@ export class Parser {
|
|
|
105
124
|
}
|
|
106
125
|
this.document = document !== null && document !== void 0 ? document : this.treeAdapter.createDocument();
|
|
107
126
|
this.tokenizer = new Tokenizer(this.options, this);
|
|
108
|
-
this.activeFormattingElements = new FormattingElementList(
|
|
127
|
+
this.activeFormattingElements = new FormattingElementList();
|
|
109
128
|
this.fragmentContextID = fragmentContext ? getTagID(this.treeAdapter.getTagName(fragmentContext)) : $.UNKNOWN;
|
|
110
129
|
this._setContextModes(fragmentContext !== null && fragmentContext !== void 0 ? fragmentContext : this.document, this.fragmentContextID);
|
|
111
130
|
this.openElements = new OpenElementStack(this.document, this.treeAdapter, this);
|
|
@@ -130,7 +149,7 @@ export class Parser {
|
|
|
130
149
|
const documentMock = opts.treeAdapter.createElement('documentmock', NS.HTML, []);
|
|
131
150
|
const parser = new this(opts, documentMock, fragmentContext);
|
|
132
151
|
if (parser.fragmentContextID === $.TEMPLATE) {
|
|
133
|
-
parser.tmplInsertionModeStack.
|
|
152
|
+
parser.tmplInsertionModeStack.push(InsertionMode.IN_TEMPLATE);
|
|
134
153
|
}
|
|
135
154
|
parser._initTokenizerForFragmentParsing();
|
|
136
155
|
parser._insertFakeRootElement();
|
|
@@ -172,11 +191,12 @@ export class Parser {
|
|
|
172
191
|
}
|
|
173
192
|
/** @internal */
|
|
174
193
|
onItemPop(node, isTop) {
|
|
175
|
-
var _a, _b;
|
|
194
|
+
var _a, _b, _c;
|
|
176
195
|
if (this.options.sourceCodeLocationInfo) {
|
|
177
196
|
this._setEndLocation(node, this.currentToken);
|
|
178
197
|
}
|
|
179
198
|
(_b = (_a = this.treeAdapter).onItemPop) === null || _b === void 0 ? void 0 : _b.call(_a, node, this.openElements.current);
|
|
199
|
+
(_c = this.annotationXmlIntegrationPoints) === null || _c === void 0 ? void 0 : _c.delete(node);
|
|
180
200
|
if (isTop) {
|
|
181
201
|
let current;
|
|
182
202
|
let currentTagId;
|
|
@@ -219,7 +239,7 @@ export class Parser {
|
|
|
219
239
|
_findFormInFragmentContext() {
|
|
220
240
|
let node = this.fragmentContext;
|
|
221
241
|
while (node) {
|
|
222
|
-
if (this.treeAdapter.getTagName(node) === TN.FORM) {
|
|
242
|
+
if (this.treeAdapter.getTagName(node) === TN.FORM && this.treeAdapter.getNamespaceURI(node) === NS.HTML) {
|
|
223
243
|
this.formElement = node;
|
|
224
244
|
break;
|
|
225
245
|
}
|
|
@@ -395,8 +415,12 @@ export class Parser {
|
|
|
395
415
|
}
|
|
396
416
|
/** @protected */
|
|
397
417
|
_adoptNodes(donor, recipient) {
|
|
398
|
-
|
|
399
|
-
|
|
418
|
+
// Detaching from the end keeps an array-backed child list from shifting once per child.
|
|
419
|
+
const children = [...this.treeAdapter.getChildNodes(donor)];
|
|
420
|
+
for (let i = children.length - 1; i >= 0; i--) {
|
|
421
|
+
this.treeAdapter.detachNode(children[i]);
|
|
422
|
+
}
|
|
423
|
+
for (const child of children) {
|
|
400
424
|
this.treeAdapter.appendChild(recipient, child);
|
|
401
425
|
}
|
|
402
426
|
}
|
|
@@ -488,25 +512,31 @@ export class Parser {
|
|
|
488
512
|
}
|
|
489
513
|
}
|
|
490
514
|
}
|
|
491
|
-
//Integration points
|
|
492
515
|
/** @protected */
|
|
493
516
|
_isIntegrationPoint(tid, element, foreignNS) {
|
|
517
|
+
var _a;
|
|
494
518
|
const ns = this.treeAdapter.getNamespaceURI(element);
|
|
495
|
-
|
|
496
|
-
|
|
519
|
+
if (tid === $.ANNOTATION_XML && ns === NS.MATHML && foreignNS !== NS.MATHML) {
|
|
520
|
+
const verdicts = ((_a = this.annotationXmlIntegrationPoints) !== null && _a !== void 0 ? _a : (this.annotationXmlIntegrationPoints = new Map()));
|
|
521
|
+
let isIntegrationPoint = verdicts.get(element);
|
|
522
|
+
if (isIntegrationPoint === undefined) {
|
|
523
|
+
isIntegrationPoint = foreignContent.isIntegrationPoint(tid, ns, this.treeAdapter.getAttrList(element));
|
|
524
|
+
verdicts.set(element, isIntegrationPoint);
|
|
525
|
+
}
|
|
526
|
+
return isIntegrationPoint;
|
|
527
|
+
}
|
|
528
|
+
return foreignContent.isIntegrationPoint(tid, ns, EMPTY_ATTRS, foreignNS);
|
|
497
529
|
}
|
|
498
530
|
//Active formatting elements reconstruction
|
|
499
531
|
/** @protected */
|
|
500
532
|
_reconstructActiveFormattingElements() {
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
entry.element = this.openElements.current;
|
|
509
|
-
}
|
|
533
|
+
let oldest = null;
|
|
534
|
+
for (let entry = this.activeFormattingElements.newest; entry !== null && entry.type === EntryType.Element && !this.openElements.contains(entry.element); entry = entry.older) {
|
|
535
|
+
oldest = entry;
|
|
536
|
+
}
|
|
537
|
+
for (let entry = oldest; entry !== null && entry.type === EntryType.Element; entry = entry.newer) {
|
|
538
|
+
this._insertElement(entry.token, this.treeAdapter.getNamespaceURI(entry.element));
|
|
539
|
+
this.activeFormattingElements.replaceElement(entry, this.openElements.current);
|
|
510
540
|
}
|
|
511
541
|
}
|
|
512
542
|
//Close elements
|
|
@@ -525,9 +555,14 @@ export class Parser {
|
|
|
525
555
|
//Insertion modes
|
|
526
556
|
/** @protected */
|
|
527
557
|
_resetInsertionMode() {
|
|
528
|
-
|
|
558
|
+
// The topmost open HTML element that selects a mode; the fragment context stands in for the root.
|
|
559
|
+
const i = this.openElements.indexOfTopmostOf(INSERTION_MODE_TAGS);
|
|
560
|
+
const tn = i === 0 && this.fragmentContext ? this.fragmentContextID : this.openElements.tagIDs[i];
|
|
561
|
+
const node = i === 0 && this.fragmentContext ? this.fragmentContext : this.openElements.items[i];
|
|
562
|
+
// Only HTML elements select an insertion mode.
|
|
563
|
+
if (node !== undefined && this.treeAdapter.getNamespaceURI(node) === NS.HTML) {
|
|
529
564
|
//Insertion mode reset map
|
|
530
|
-
switch (
|
|
565
|
+
switch (tn) {
|
|
531
566
|
case $.TR: {
|
|
532
567
|
this.insertionMode = InsertionMode.IN_ROW;
|
|
533
568
|
return;
|
|
@@ -563,7 +598,7 @@ export class Parser {
|
|
|
563
598
|
return;
|
|
564
599
|
}
|
|
565
600
|
case $.TEMPLATE: {
|
|
566
|
-
this.insertionMode = this.tmplInsertionModeStack[
|
|
601
|
+
this.insertionMode = this.tmplInsertionModeStack[this.tmplInsertionModeStack.length - 1];
|
|
567
602
|
return;
|
|
568
603
|
}
|
|
569
604
|
case $.HTML: {
|
|
@@ -591,16 +626,12 @@ export class Parser {
|
|
|
591
626
|
}
|
|
592
627
|
/** @protected */
|
|
593
628
|
_resetInsertionModeForSelect(selectIdx) {
|
|
629
|
+
// The select is the topmost element that selects a mode, so open tables and templates are below it.
|
|
594
630
|
if (selectIdx > 0) {
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
}
|
|
600
|
-
else if (tn === $.TABLE) {
|
|
601
|
-
this.insertionMode = InsertionMode.IN_SELECT_IN_TABLE;
|
|
602
|
-
return;
|
|
603
|
-
}
|
|
631
|
+
const tableIdx = this.openElements.indexOfTopmost($.TABLE);
|
|
632
|
+
if (tableIdx > 0 && tableIdx > this.openElements.indexOfTopmost($.TEMPLATE)) {
|
|
633
|
+
this.insertionMode = InsertionMode.IN_SELECT_IN_TABLE;
|
|
634
|
+
return;
|
|
604
635
|
}
|
|
605
636
|
}
|
|
606
637
|
this.insertionMode = InsertionMode.IN_SELECT;
|
|
@@ -618,27 +649,24 @@ export class Parser {
|
|
|
618
649
|
}
|
|
619
650
|
/** @protected */
|
|
620
651
|
_findFosterParentingLocation() {
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
return { parent: this.openElements.items[i - 1], beforeElement: null };
|
|
636
|
-
}
|
|
637
|
-
default:
|
|
638
|
-
// Do nothing
|
|
652
|
+
const { openElements } = this;
|
|
653
|
+
const tableIdx = openElements.indexOfTopmost($.TABLE);
|
|
654
|
+
const templateIdx = openElements.indexOfTopmost($.TEMPLATE);
|
|
655
|
+
if (templateIdx > tableIdx) {
|
|
656
|
+
return {
|
|
657
|
+
parent: this.treeAdapter.getTemplateContent(openElements.items[templateIdx]),
|
|
658
|
+
beforeElement: null,
|
|
659
|
+
};
|
|
660
|
+
}
|
|
661
|
+
if (tableIdx !== -1) {
|
|
662
|
+
const table = openElements.items[tableIdx];
|
|
663
|
+
const parent = this.treeAdapter.getParentNode(table);
|
|
664
|
+
if (parent) {
|
|
665
|
+
return { parent, beforeElement: table };
|
|
639
666
|
}
|
|
667
|
+
return { parent: openElements.items[openElements.slotBelow(tableIdx)], beforeElement: null };
|
|
640
668
|
}
|
|
641
|
-
return { parent:
|
|
669
|
+
return { parent: openElements.items[0], beforeElement: null };
|
|
642
670
|
}
|
|
643
671
|
/** @protected */
|
|
644
672
|
_fosterParentElement(element) {
|
|
@@ -1210,7 +1238,7 @@ function aaObtainFormattingElementEntry(p, token) {
|
|
|
1210
1238
|
p.activeFormattingElements.removeEntry(formattingElementEntry);
|
|
1211
1239
|
formattingElementEntry = null;
|
|
1212
1240
|
}
|
|
1213
|
-
else if (!p.openElements.hasInScope(token.tagID)) {
|
|
1241
|
+
else if (!p.openElements.hasInScope(token.tagID, formattingElementEntry.element)) {
|
|
1214
1242
|
formattingElementEntry = null;
|
|
1215
1243
|
}
|
|
1216
1244
|
}
|
|
@@ -1221,31 +1249,28 @@ function aaObtainFormattingElementEntry(p, token) {
|
|
|
1221
1249
|
}
|
|
1222
1250
|
//Steps 9 and 10 of the algorithm
|
|
1223
1251
|
function aaObtainFurthestBlock(p, formattingElementEntry) {
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
if (
|
|
1229
|
-
|
|
1230
|
-
}
|
|
1231
|
-
if (p._isSpecialElement(element, p.openElements.tagIDs[idx])) {
|
|
1232
|
-
furthestBlock = element;
|
|
1252
|
+
const { openElements } = p;
|
|
1253
|
+
const formattingIdx = openElements._indexOf(formattingElementEntry.element);
|
|
1254
|
+
// The furthest block is the lowest special element above the formatting element.
|
|
1255
|
+
for (let idx = openElements.slotAbove(formattingIdx); idx !== -1; idx = openElements.slotAbove(idx)) {
|
|
1256
|
+
if (p._isSpecialElement(openElements.items[idx], openElements.tagIDs[idx])) {
|
|
1257
|
+
return idx;
|
|
1233
1258
|
}
|
|
1234
1259
|
}
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
}
|
|
1239
|
-
return furthestBlock;
|
|
1260
|
+
openElements.shortenToLength(formattingIdx);
|
|
1261
|
+
p.activeFormattingElements.removeEntry(formattingElementEntry);
|
|
1262
|
+
return -1;
|
|
1240
1263
|
}
|
|
1241
1264
|
//Step 13 of the algorithm
|
|
1242
|
-
function aaInnerLoop(p,
|
|
1265
|
+
function aaInnerLoop(p, furthestBlockIdx, formattingElement) {
|
|
1266
|
+
const furthestBlock = p.openElements.items[furthestBlockIdx];
|
|
1243
1267
|
let lastElement = furthestBlock;
|
|
1244
|
-
let
|
|
1268
|
+
let nextIdx = p.openElements.slotBelow(furthestBlockIdx);
|
|
1245
1269
|
const intendedParent = p.openElements.getCommonAncestor(formattingElement);
|
|
1246
|
-
for (let i = 0,
|
|
1247
|
-
|
|
1248
|
-
|
|
1270
|
+
for (let i = 0, idx = nextIdx; p.openElements.items[idx] !== formattingElement; i++, idx = nextIdx) {
|
|
1271
|
+
let element = p.openElements.items[idx];
|
|
1272
|
+
// Store the next element for the next loop iteration (this one may be removed from the stack by step 9.5).
|
|
1273
|
+
nextIdx = p.openElements.slotBelow(idx);
|
|
1249
1274
|
const elementEntry = p.activeFormattingElements.getElementEntry(element);
|
|
1250
1275
|
const counterOverflow = elementEntry && i >= AA_INNER_LOOP_ITER;
|
|
1251
1276
|
const shouldRemoveFromOpenElements = !elementEntry || counterOverflow;
|
|
@@ -1253,7 +1278,7 @@ function aaInnerLoop(p, furthestBlock, formattingElement) {
|
|
|
1253
1278
|
if (counterOverflow) {
|
|
1254
1279
|
p.activeFormattingElements.removeEntry(elementEntry);
|
|
1255
1280
|
}
|
|
1256
|
-
p.openElements.
|
|
1281
|
+
p.openElements.removeAt(idx);
|
|
1257
1282
|
}
|
|
1258
1283
|
else {
|
|
1259
1284
|
element = aaRecreateElementFromEntry(p, elementEntry, intendedParent);
|
|
@@ -1272,7 +1297,7 @@ function aaRecreateElementFromEntry(p, elementEntry, intendedParent) {
|
|
|
1272
1297
|
const ns = p.treeAdapter.getNamespaceURI(elementEntry.element);
|
|
1273
1298
|
const newElement = p._createElement(elementEntry.token.tagName, ns, elementEntry.token.attrs, intendedParent);
|
|
1274
1299
|
p.openElements.replace(elementEntry.element, newElement);
|
|
1275
|
-
elementEntry
|
|
1300
|
+
p.activeFormattingElements.replaceElement(elementEntry, newElement);
|
|
1276
1301
|
return newElement;
|
|
1277
1302
|
}
|
|
1278
1303
|
//Step 14 of the algorithm
|
|
@@ -1299,22 +1324,36 @@ function aaReplaceFormattingElement(p, furthestBlock, formattingElementEntry) {
|
|
|
1299
1324
|
p.treeAdapter.appendChild(furthestBlock, newElement);
|
|
1300
1325
|
p.activeFormattingElements.insertElementAfterBookmark(newElement, token);
|
|
1301
1326
|
p.activeFormattingElements.removeEntry(formattingElementEntry);
|
|
1302
|
-
|
|
1303
|
-
p.openElements.
|
|
1327
|
+
// Only the elements the inner loop kept are open between the formatting element and the furthest block.
|
|
1328
|
+
const formattingIdx = p.openElements._indexOf(formattingElementEntry.element);
|
|
1329
|
+
let furthestBlockIdx = p.openElements.slotAbove(formattingIdx);
|
|
1330
|
+
while (p.openElements.items[furthestBlockIdx] !== furthestBlock) {
|
|
1331
|
+
furthestBlockIdx = p.openElements.slotAbove(furthestBlockIdx);
|
|
1332
|
+
}
|
|
1333
|
+
p.openElements.replaceFormattingElement(formattingIdx, furthestBlockIdx, newElement, token.tagID);
|
|
1304
1334
|
}
|
|
1305
1335
|
//Algorithm entry point
|
|
1306
1336
|
function callAdoptionAgency(p, token) {
|
|
1337
|
+
const { current, currentTagId } = p.openElements;
|
|
1338
|
+
//Step 2 of the algorithm
|
|
1339
|
+
if (currentTagId === token.tagID &&
|
|
1340
|
+
p.treeAdapter.getNamespaceURI(current) === NS.HTML &&
|
|
1341
|
+
!p.activeFormattingElements.getElementEntry(current)) {
|
|
1342
|
+
p.openElements.pop();
|
|
1343
|
+
return;
|
|
1344
|
+
}
|
|
1307
1345
|
for (let i = 0; i < AA_OUTER_LOOP_ITER; i++) {
|
|
1308
1346
|
const formattingElementEntry = aaObtainFormattingElementEntry(p, token);
|
|
1309
1347
|
if (!formattingElementEntry) {
|
|
1310
1348
|
break;
|
|
1311
1349
|
}
|
|
1312
|
-
const
|
|
1313
|
-
if (
|
|
1350
|
+
const furthestBlockIdx = aaObtainFurthestBlock(p, formattingElementEntry);
|
|
1351
|
+
if (furthestBlockIdx === -1) {
|
|
1314
1352
|
break;
|
|
1315
1353
|
}
|
|
1354
|
+
const furthestBlock = p.openElements.items[furthestBlockIdx];
|
|
1316
1355
|
p.activeFormattingElements.bookmark = formattingElementEntry;
|
|
1317
|
-
const lastElement = aaInnerLoop(p,
|
|
1356
|
+
const lastElement = aaInnerLoop(p, furthestBlockIdx, formattingElementEntry.element);
|
|
1318
1357
|
const commonAncestor = p.openElements.getCommonAncestor(formattingElementEntry.element);
|
|
1319
1358
|
p.treeAdapter.detachNode(lastElement);
|
|
1320
1359
|
if (commonAncestor)
|
|
@@ -1340,7 +1379,7 @@ function stopParsing(p, token) {
|
|
|
1340
1379
|
// NOTE: If we are not in a fragment, `html` and `body` will stay on the stack.
|
|
1341
1380
|
// This is a problem, as we might overwrite their end position here.
|
|
1342
1381
|
const target = p.fragmentContext ? 0 : 2;
|
|
1343
|
-
for (let i = p.openElements.stackTop; i >= target; i
|
|
1382
|
+
for (let i = p.openElements.stackTop; i >= target; i = p.openElements.slotBelow(i)) {
|
|
1344
1383
|
p._setEndLocation(p.openElements.items[i], token);
|
|
1345
1384
|
}
|
|
1346
1385
|
// Handle `html` and `body`
|
|
@@ -1478,7 +1517,7 @@ function startTagInHead(p, token) {
|
|
|
1478
1517
|
p.activeFormattingElements.insertMarker();
|
|
1479
1518
|
p.framesetOk = false;
|
|
1480
1519
|
p.insertionMode = InsertionMode.IN_TEMPLATE;
|
|
1481
|
-
p.tmplInsertionModeStack.
|
|
1520
|
+
p.tmplInsertionModeStack.push(InsertionMode.IN_TEMPLATE);
|
|
1482
1521
|
break;
|
|
1483
1522
|
}
|
|
1484
1523
|
case $.HEAD: {
|
|
@@ -1520,7 +1559,7 @@ function templateEndTagInHead(p, token) {
|
|
|
1520
1559
|
}
|
|
1521
1560
|
p.openElements.popUntilTagNamePopped($.TEMPLATE);
|
|
1522
1561
|
p.activeFormattingElements.clearToLastMarker();
|
|
1523
|
-
p.tmplInsertionModeStack.
|
|
1562
|
+
p.tmplInsertionModeStack.pop();
|
|
1524
1563
|
p._resetInsertionMode();
|
|
1525
1564
|
}
|
|
1526
1565
|
else {
|
|
@@ -1749,21 +1788,15 @@ function formStartTagInBody(p, token) {
|
|
|
1749
1788
|
}
|
|
1750
1789
|
function listItemStartTagInBody(p, token) {
|
|
1751
1790
|
p.framesetOk = false;
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
if (elementId !== $.ADDRESS &&
|
|
1762
|
-
elementId !== $.DIV &&
|
|
1763
|
-
elementId !== $.P &&
|
|
1764
|
-
p._isSpecialElement(p.openElements.items[i], elementId)) {
|
|
1765
|
-
break;
|
|
1766
|
-
}
|
|
1791
|
+
// The topmost open item of the token's kind closes, unless a special element other than address, div and p is
|
|
1792
|
+
// above it.
|
|
1793
|
+
const idx = token.tagID === $.LI
|
|
1794
|
+
? p.openElements.indexOfTopmost($.LI)
|
|
1795
|
+
: Math.max(p.openElements.indexOfTopmost($.DD), p.openElements.indexOfTopmost($.DT));
|
|
1796
|
+
if (idx !== -1 && !p.openElements.hasListItemBoundaryAbove(idx)) {
|
|
1797
|
+
const elementId = p.openElements.tagIDs[idx];
|
|
1798
|
+
p.openElements.generateImpliedEndTagsWithExclusion(elementId);
|
|
1799
|
+
p.openElements.popUntilTagNamePopped(elementId);
|
|
1767
1800
|
}
|
|
1768
1801
|
if (p.openElements.hasInButtonScope($.P)) {
|
|
1769
1802
|
p._closePElement();
|
|
@@ -2247,24 +2280,16 @@ function brEndTagInBody(p) {
|
|
|
2247
2280
|
p.framesetOk = false;
|
|
2248
2281
|
}
|
|
2249
2282
|
function genericEndTagInBody(p, token) {
|
|
2250
|
-
const tn = token.tagName;
|
|
2251
2283
|
const tid = token.tagID;
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
p.openElements.
|
|
2261
|
-
if (p.openElements.stackTop >= i)
|
|
2262
|
-
p.openElements.shortenToLength(i);
|
|
2263
|
-
break;
|
|
2264
|
-
}
|
|
2265
|
-
if (p._isSpecialElement(element, elementId)) {
|
|
2266
|
-
break;
|
|
2267
|
-
}
|
|
2284
|
+
// The topmost open HTML element with the token's name closes, unless a special element is above it. A tag without
|
|
2285
|
+
// an ID is matched by name.
|
|
2286
|
+
const i = tid === $.UNKNOWN
|
|
2287
|
+
? p.openElements.indexOfTopmostByName(NS.HTML, token.tagName)
|
|
2288
|
+
: p.openElements.indexOfTopmost(tid);
|
|
2289
|
+
if (i > 0 && !p.openElements.hasSpecialElementAbove(i)) {
|
|
2290
|
+
p.openElements.generateImpliedEndTagsWithExclusion(tid);
|
|
2291
|
+
if (p.openElements.stackTop >= i)
|
|
2292
|
+
p.openElements.shortenToLength(i);
|
|
2268
2293
|
}
|
|
2269
2294
|
}
|
|
2270
2295
|
function endTagInBody(p, token) {
|
|
@@ -2949,7 +2974,7 @@ function endTagInSelect(p, token) {
|
|
|
2949
2974
|
case $.OPTGROUP: {
|
|
2950
2975
|
if (p.openElements.stackTop > 0 &&
|
|
2951
2976
|
p.openElements.currentTagId === $.OPTION &&
|
|
2952
|
-
p.openElements.tagIDs[p.openElements.stackTop
|
|
2977
|
+
p.openElements.tagIDs[p.openElements.slotBelow(p.openElements.stackTop)] === $.OPTGROUP) {
|
|
2953
2978
|
p.openElements.pop();
|
|
2954
2979
|
}
|
|
2955
2980
|
if (p.openElements.currentTagId === $.OPTGROUP) {
|
|
@@ -3042,32 +3067,32 @@ function startTagInTemplate(p, token) {
|
|
|
3042
3067
|
case $.TBODY:
|
|
3043
3068
|
case $.TFOOT:
|
|
3044
3069
|
case $.THEAD: {
|
|
3045
|
-
p.tmplInsertionModeStack[
|
|
3070
|
+
p.tmplInsertionModeStack[p.tmplInsertionModeStack.length - 1] = InsertionMode.IN_TABLE;
|
|
3046
3071
|
p.insertionMode = InsertionMode.IN_TABLE;
|
|
3047
3072
|
startTagInTable(p, token);
|
|
3048
3073
|
break;
|
|
3049
3074
|
}
|
|
3050
3075
|
case $.COL: {
|
|
3051
|
-
p.tmplInsertionModeStack[
|
|
3076
|
+
p.tmplInsertionModeStack[p.tmplInsertionModeStack.length - 1] = InsertionMode.IN_COLUMN_GROUP;
|
|
3052
3077
|
p.insertionMode = InsertionMode.IN_COLUMN_GROUP;
|
|
3053
3078
|
startTagInColumnGroup(p, token);
|
|
3054
3079
|
break;
|
|
3055
3080
|
}
|
|
3056
3081
|
case $.TR: {
|
|
3057
|
-
p.tmplInsertionModeStack[
|
|
3082
|
+
p.tmplInsertionModeStack[p.tmplInsertionModeStack.length - 1] = InsertionMode.IN_TABLE_BODY;
|
|
3058
3083
|
p.insertionMode = InsertionMode.IN_TABLE_BODY;
|
|
3059
3084
|
startTagInTableBody(p, token);
|
|
3060
3085
|
break;
|
|
3061
3086
|
}
|
|
3062
3087
|
case $.TD:
|
|
3063
3088
|
case $.TH: {
|
|
3064
|
-
p.tmplInsertionModeStack[
|
|
3089
|
+
p.tmplInsertionModeStack[p.tmplInsertionModeStack.length - 1] = InsertionMode.IN_ROW;
|
|
3065
3090
|
p.insertionMode = InsertionMode.IN_ROW;
|
|
3066
3091
|
startTagInRow(p, token);
|
|
3067
3092
|
break;
|
|
3068
3093
|
}
|
|
3069
3094
|
default: {
|
|
3070
|
-
p.tmplInsertionModeStack[
|
|
3095
|
+
p.tmplInsertionModeStack[p.tmplInsertionModeStack.length - 1] = InsertionMode.IN_BODY;
|
|
3071
3096
|
p.insertionMode = InsertionMode.IN_BODY;
|
|
3072
3097
|
startTagInBody(p, token);
|
|
3073
3098
|
}
|
|
@@ -3080,9 +3105,12 @@ function endTagInTemplate(p, token) {
|
|
|
3080
3105
|
}
|
|
3081
3106
|
function eofInTemplate(p, token) {
|
|
3082
3107
|
if (p.openElements.tmplCount > 0) {
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3108
|
+
// EOF closes every open template.
|
|
3109
|
+
do {
|
|
3110
|
+
p.openElements.popUntilTagNamePopped($.TEMPLATE);
|
|
3111
|
+
p.activeFormattingElements.clearToLastMarker();
|
|
3112
|
+
p.tmplInsertionModeStack.pop();
|
|
3113
|
+
} while (p.openElements.tmplCount > 0);
|
|
3086
3114
|
p._resetInsertionMode();
|
|
3087
3115
|
p.onEof(token);
|
|
3088
3116
|
}
|
|
@@ -3257,18 +3285,24 @@ function endTagInForeignContent(p, token) {
|
|
|
3257
3285
|
p._endTagOutsideForeignContent(token);
|
|
3258
3286
|
return;
|
|
3259
3287
|
}
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3288
|
+
// The current node is the root element (fragment case): ignore the token.
|
|
3289
|
+
if (p.openElements.stackTop === 0)
|
|
3290
|
+
return;
|
|
3291
|
+
// The topmost foreign element with the token's name closes, unless an HTML element is above it. Foreign names are
|
|
3292
|
+
// the lowercased start-tag name or its SVG adjustment, so these two are the ASCII case-insensitive match.
|
|
3293
|
+
const { tagName } = token;
|
|
3294
|
+
const adjustedTagName = foreignContent.SVG_TAG_NAMES_ADJUSTMENT_MAP.get(tagName);
|
|
3295
|
+
// Elements outside HTML, SVG and MathML only come from a fragment context in their namespace.
|
|
3296
|
+
const contextNs = p.fragmentContext ? p.treeAdapter.getNamespaceURI(p.fragmentContext) : NS.HTML;
|
|
3297
|
+
const i = Math.max(p.openElements.indexOfTopmostByName(NS.SVG, tagName), p.openElements.indexOfTopmostByName(NS.MATHML, tagName), adjustedTagName === undefined ? -1 : p.openElements.indexOfTopmostByName(NS.SVG, adjustedTagName), contextNs === NS.HTML || contextNs === NS.SVG || contextNs === NS.MATHML
|
|
3298
|
+
? -1
|
|
3299
|
+
: p.openElements.indexOfTopmostByName(contextNs, tagName));
|
|
3300
|
+
if (i > p.openElements.indexOfTopmostHtmlElement()) {
|
|
3301
|
+
//NOTE: update the token tag name for `_setEndLocation`.
|
|
3302
|
+
token.tagName = p.treeAdapter.getTagName(p.openElements.items[i]);
|
|
3303
|
+
p.openElements.shortenToLength(i);
|
|
3304
|
+
}
|
|
3305
|
+
else {
|
|
3306
|
+
p._endTagOutsideForeignContent(token);
|
|
3273
3307
|
}
|
|
3274
3308
|
}
|