tutuca 0.9.38 → 0.9.39

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.
@@ -2762,7 +2762,7 @@ var init_html_tokenizer = __esm(() => {
2762
2762
  });
2763
2763
 
2764
2764
  // tools/core/htmllinter-tables.js
2765
- var VOID_ELEMENTS, RAW_TEXT_ELEMENTS, RCDATA_ELEMENTS, SPECIAL_ELEMENTS, FORMATTING_ELEMENTS, DEFAULT_SCOPE_BOUNDARIES, SCOPE_LIST_ITEM, SCOPE_BUTTON, SCOPE_DEFAULT, SCOPE_TABLE, SCOPE_SELECT, STANDARD_SVG_CAMEL_ELEMENTS, STANDARD_SVG_CAMEL_ATTRS, MATHML_CAMEL_ATTRS, FOREIGN_BREAKOUT_TAGS, MATHML_TEXT_INTEGRATION_POINTS, BLOCK_LEVEL_AUTO_CLOSE_P, SELECT_VALID_CHILDREN, SELECT_BREAKOUT_TAGS, MODES, NS, FRAGMENT_CONTEXT_MODES;
2765
+ var VOID_ELEMENTS, RAW_TEXT_ELEMENTS, RCDATA_ELEMENTS, SPECIAL_ELEMENTS, FORMATTING_ELEMENTS, DEFAULT_SCOPE_BOUNDARIES, MATHML_TEXT_INTEGRATION_POINT_NAMES, SVG_HTML_INTEGRATION_POINT_NAMES, SCOPE_LIST_ITEM, SCOPE_BUTTON, SCOPE_DEFAULT, SCOPE_TABLE, SCOPE_SELECT, STANDARD_SVG_CAMEL_ELEMENTS, STANDARD_SVG_CAMEL_ATTRS, MATHML_CAMEL_ATTRS, SVG_ATTR_LOWERCASE_TO_CAMEL, MATHML_ATTR_LOWERCASE_TO_CAMEL, FOREIGN_BREAKOUT_TAGS, MATHML_TEXT_INTEGRATION_POINTS, BLOCK_LEVEL_AUTO_CLOSE_P, SELECT_VALID_CHILDREN, SELECT_BREAKOUT_TAGS, MODES, NS, FRAGMENT_CONTEXT_MODES;
2766
2766
  var init_htmllinter_tables = __esm(() => {
2767
2767
  VOID_ELEMENTS = new Set([
2768
2768
  "area",
@@ -2895,8 +2895,11 @@ var init_htmllinter_tables = __esm(() => {
2895
2895
  "th",
2896
2896
  "marquee",
2897
2897
  "object",
2898
+ "select",
2898
2899
  "template"
2899
2900
  ]);
2901
+ MATHML_TEXT_INTEGRATION_POINT_NAMES = new Set(["mi", "mo", "mn", "ms", "mtext"]);
2902
+ SVG_HTML_INTEGRATION_POINT_NAMES = new Set(["foreignobject", "desc", "title"]);
2900
2903
  SCOPE_LIST_ITEM = new Set([...DEFAULT_SCOPE_BOUNDARIES, "ol", "ul"]);
2901
2904
  SCOPE_BUTTON = new Set([...DEFAULT_SCOPE_BOUNDARIES, "button"]);
2902
2905
  SCOPE_DEFAULT = DEFAULT_SCOPE_BOUNDARIES;
@@ -3001,6 +3004,12 @@ var init_htmllinter_tables = __esm(() => {
3001
3004
  "zoomAndPan"
3002
3005
  ]);
3003
3006
  MATHML_CAMEL_ATTRS = new Set(["definitionURL"]);
3007
+ SVG_ATTR_LOWERCASE_TO_CAMEL = new Map;
3008
+ for (const camel of STANDARD_SVG_CAMEL_ATTRS)
3009
+ SVG_ATTR_LOWERCASE_TO_CAMEL.set(camel.toLowerCase(), camel);
3010
+ MATHML_ATTR_LOWERCASE_TO_CAMEL = new Map;
3011
+ for (const camel of MATHML_CAMEL_ATTRS)
3012
+ MATHML_ATTR_LOWERCASE_TO_CAMEL.set(camel.toLowerCase(), camel);
3004
3013
  FOREIGN_BREAKOUT_TAGS = new Set([
3005
3014
  "b",
3006
3015
  "big",
@@ -3095,7 +3104,6 @@ var init_htmllinter_tables = __esm(() => {
3095
3104
  MODES = Object.freeze({
3096
3105
  inBody: "inBody",
3097
3106
  inTable: "inTable",
3098
- inTableText: "inTableText",
3099
3107
  inCaption: "inCaption",
3100
3108
  inColumnGroup: "inColumnGroup",
3101
3109
  inTableBody: "inTableBody",
@@ -3103,8 +3111,7 @@ var init_htmllinter_tables = __esm(() => {
3103
3111
  inCell: "inCell",
3104
3112
  inSelect: "inSelect",
3105
3113
  inSelectInTable: "inSelectInTable",
3106
- inTemplate: "inTemplate",
3107
- text: "text"
3114
+ inTemplate: "inTemplate"
3108
3115
  });
3109
3116
  NS = Object.freeze({
3110
3117
  html: "html",
@@ -3171,30 +3178,75 @@ class LinterCtx {
3171
3178
  this.openElements.push({ name: "select", ns: NS.html, start: -1 });
3172
3179
  }
3173
3180
  this.insertionMode = ctxInfo.mode;
3174
- this.originalInsertionMode = MODES.inBody;
3175
3181
  this.templateInsertionModes = ctxName === "template" ? [MODES.inTemplate] : [];
3176
3182
  this.activeFormatting = [];
3177
- this.formPointer = null;
3178
3183
  this.framesetOk = true;
3179
3184
  this.svgCamelElements = opts.svgCamelElements ?? STANDARD_SVG_CAMEL_ELEMENTS;
3180
- this.transparentTagPrefixes = opts.transparentTagPrefixes ?? [];
3185
+ this.transparentTagPrefixes = (opts.transparentTagPrefixes ?? []).map((p) => p.toLowerCase());
3181
3186
  this.currentTagName = "";
3182
3187
  this.currentTagRawName = "";
3183
3188
  this.currentTagStart = 0;
3189
+ this.currentAttrs = [];
3190
+ this.currentAttr = null;
3184
3191
  this.tokenizer = null;
3185
- this.textRestoreMode = null;
3186
3192
  }
3187
3193
  onopentagname(start, end) {
3188
3194
  const raw = this.html.slice(start, end);
3189
3195
  this.currentTagRawName = raw;
3190
3196
  this.currentTagName = raw.toLowerCase();
3191
3197
  this.currentTagStart = start;
3198
+ this.currentAttrs = [];
3199
+ this.currentAttr = null;
3200
+ }
3201
+ onattribname(start, end) {
3202
+ const rawName = this.html.slice(start, end);
3203
+ this.currentAttr = {
3204
+ name: rawName.toLowerCase(),
3205
+ rawName,
3206
+ nameStart: start,
3207
+ value: null,
3208
+ valueStart: -1,
3209
+ valueEnd: -1,
3210
+ quote: QuoteType.NoValue
3211
+ };
3212
+ }
3213
+ onattribdata(start, end) {
3214
+ if (!this.currentAttr)
3215
+ return;
3216
+ this.currentAttr.valueStart = start;
3217
+ this.currentAttr.valueEnd = end;
3218
+ this.currentAttr.value = this.html.slice(start, end);
3219
+ }
3220
+ onattribend(quote, _end) {
3221
+ const a = this.currentAttr;
3222
+ if (!a)
3223
+ return;
3224
+ a.quote = quote;
3225
+ const dup = this.currentAttrs.find((x) => x.name === a.name);
3226
+ if (dup) {
3227
+ this.report(HTML_DUPLICATE_ATTRIBUTE, LEVEL_WARN, a.nameStart, {
3228
+ name: a.name,
3229
+ firstAt: dup.nameStart
3230
+ });
3231
+ } else {
3232
+ this.currentAttrs.push(a);
3233
+ }
3234
+ if (a.quote === QuoteType.Unquoted && a.value === "") {
3235
+ this.report(HTML_MISSING_ATTRIBUTE_VALUE, LEVEL_WARN, a.nameStart, {
3236
+ name: a.name
3237
+ });
3238
+ }
3239
+ this.currentAttr = null;
3192
3240
  }
3193
- onattribname(_start, _end) {}
3194
- onattribdata(_start, _end) {}
3195
3241
  onattribentity(_cp) {}
3196
- onattribend(_quote, _end) {}
3197
3242
  ontextentity(_cp, _end) {}
3243
+ getAttr(name) {
3244
+ const a = this.currentAttrs.find((x) => x.name === name);
3245
+ return a ? a.value : null;
3246
+ }
3247
+ hasAttr(name) {
3248
+ return this.currentAttrs.some((x) => x.name === name);
3249
+ }
3198
3250
  onopentagend(endIndex) {
3199
3251
  this.handleStartTag(false, endIndex);
3200
3252
  }
@@ -3204,20 +3256,56 @@ class LinterCtx {
3204
3256
  onclosetag(start, end) {
3205
3257
  const raw = this.html.slice(start, end);
3206
3258
  const name = raw.toLowerCase();
3259
+ let i = end;
3260
+ let lastNonWs = -1;
3261
+ while (i < this.html.length) {
3262
+ const c = this.html.charCodeAt(i);
3263
+ if (c === 62)
3264
+ break;
3265
+ if (c !== 32 && c !== 9 && c !== 10 && c !== 13 && c !== 12)
3266
+ lastNonWs = i;
3267
+ i++;
3268
+ }
3269
+ if (lastNonWs >= 0) {
3270
+ if (this.html.charCodeAt(lastNonWs) === 47 && lastNonWs === i - 1) {
3271
+ let firstNonWs = -1;
3272
+ for (let j = end;j < lastNonWs; j++) {
3273
+ const c2 = this.html.charCodeAt(j);
3274
+ if (c2 !== 32 && c2 !== 9 && c2 !== 10 && c2 !== 13 && c2 !== 12) {
3275
+ firstNonWs = j;
3276
+ break;
3277
+ }
3278
+ }
3279
+ if (firstNonWs < 0) {
3280
+ this.report(HTML_SELF_CLOSING_END_TAG, LEVEL_WARN, start, { tag: name });
3281
+ } else {
3282
+ this.report(HTML_ATTRIBUTES_ON_END_TAG, LEVEL_WARN, start, { tag: name });
3283
+ }
3284
+ } else {
3285
+ this.report(HTML_ATTRIBUTES_ON_END_TAG, LEVEL_WARN, start, { tag: name });
3286
+ }
3287
+ }
3207
3288
  this.handleEndTag(name, start);
3208
3289
  }
3209
3290
  ontext(start, end) {
3210
3291
  if (start >= end)
3211
3292
  return;
3293
+ const top = this.currentNode();
3294
+ if (top && top.ns !== NS.html && !this.isIntegrationPoint(top))
3295
+ return;
3212
3296
  this.handleText(start, end);
3213
3297
  }
3214
- oncomment(_start, _end, _endOffset) {}
3298
+ oncomment(start, _end, endOffset) {
3299
+ if (endOffset === 0) {
3300
+ this.report(HTML_BOGUS_COMMENT, LEVEL_WARN, start, {
3301
+ mode: this.insertionMode
3302
+ });
3303
+ }
3304
+ }
3215
3305
  oncdata(start, _end, _endOffset) {
3216
3306
  if (this.currentNamespace() === NS.html) {
3217
- this.report(HTML_TAG_NOT_ALLOWED_IN_PARENT, LEVEL_WARN, start, {
3218
- tag: "[CDATA[",
3219
- mode: this.insertionMode,
3220
- action: "ignored"
3307
+ this.report(HTML_CDATA_IN_HTML_NAMESPACE, LEVEL_WARN, start, {
3308
+ mode: this.insertionMode
3221
3309
  });
3222
3310
  }
3223
3311
  }
@@ -3255,14 +3343,37 @@ class LinterCtx {
3255
3343
  const f = this.openElements[i];
3256
3344
  if (f.name === target && f.ns === NS.html)
3257
3345
  return true;
3258
- if (f.ns === NS.html && scopeSet.has(f.name))
3259
- return false;
3260
- if (f.ns !== NS.html) {
3346
+ if (f.ns === NS.html) {
3347
+ if (scopeSet.has(f.name))
3348
+ return false;
3349
+ } else if (this.isScopeBoundary(f)) {
3261
3350
  return false;
3262
3351
  }
3263
3352
  }
3264
3353
  return false;
3265
3354
  }
3355
+ isScopeBoundary(frame) {
3356
+ if (frame.ns === NS.math) {
3357
+ return MATHML_TEXT_INTEGRATION_POINT_NAMES.has(frame.name) || frame.name === "annotation-xml";
3358
+ }
3359
+ if (frame.ns === NS.svg) {
3360
+ return SVG_HTML_INTEGRATION_POINT_NAMES.has(frame.name);
3361
+ }
3362
+ return false;
3363
+ }
3364
+ isIntegrationPoint(frame) {
3365
+ if (frame.ns === NS.math) {
3366
+ if (MATHML_TEXT_INTEGRATION_POINT_NAMES.has(frame.name))
3367
+ return true;
3368
+ if (frame.name === "annotation-xml" && frame.htmlIntegration)
3369
+ return true;
3370
+ return false;
3371
+ }
3372
+ if (frame.ns === NS.svg) {
3373
+ return SVG_HTML_INTEGRATION_POINT_NAMES.has(frame.name);
3374
+ }
3375
+ return false;
3376
+ }
3266
3377
  hasInDefaultScope(target) {
3267
3378
  return this.hasInScope(target, SCOPE_DEFAULT);
3268
3379
  }
@@ -3333,13 +3444,43 @@ class LinterCtx {
3333
3444
  });
3334
3445
  }
3335
3446
  }
3447
+ const targetNs = ns !== NS.html ? ns : name === "svg" ? NS.svg : name === "math" ? NS.math : NS.html;
3448
+ if (targetNs === NS.svg) {
3449
+ for (const a of this.currentAttrs) {
3450
+ const canonical = SVG_ATTR_LOWERCASE_TO_CAMEL.get(a.name);
3451
+ if (canonical && a.rawName !== canonical) {
3452
+ this.report(HTML_SVG_ATTR_WILL_LOWERCASE, LEVEL_ERROR, a.nameStart, {
3453
+ raw: a.rawName,
3454
+ canonical
3455
+ });
3456
+ }
3457
+ }
3458
+ } else if (targetNs === NS.math) {
3459
+ for (const a of this.currentAttrs) {
3460
+ const canonical = MATHML_ATTR_LOWERCASE_TO_CAMEL.get(a.name);
3461
+ if (canonical && a.rawName !== canonical) {
3462
+ this.report(HTML_MATHML_ATTR_WILL_LOWERCASE, LEVEL_ERROR, a.nameStart, {
3463
+ raw: a.rawName,
3464
+ canonical
3465
+ });
3466
+ }
3467
+ }
3468
+ }
3336
3469
  if (this.isTransparentTag(name))
3337
3470
  return;
3338
- if (ns !== NS.html && !this.shouldBreakoutFromForeign(name)) {
3471
+ const top = this.currentNode();
3472
+ const inForeign = ns !== NS.html && !(top && this.isIntegrationPoint(top));
3473
+ if (inForeign && !this.shouldBreakoutFromForeign(name)) {
3339
3474
  this.startTagInForeign(name, raw, selfClosing, start);
3340
3475
  return;
3341
3476
  }
3342
- if (ns !== NS.html && this.shouldBreakoutFromForeign(name)) {
3477
+ if (inForeign && this.shouldBreakoutFromForeign(name)) {
3478
+ this.report(HTML_TAG_NOT_ALLOWED_IN_PARENT, LEVEL_WARN, start, {
3479
+ tag: raw,
3480
+ parent: this.currentNode()?.name ?? "(root)",
3481
+ mode: this.insertionMode,
3482
+ action: "foreign-breakout"
3483
+ });
3343
3484
  while (this.openElements.length && this.currentNode()?.ns !== NS.html) {
3344
3485
  this.openElements.pop();
3345
3486
  }
@@ -3349,20 +3490,28 @@ class LinterCtx {
3349
3490
  shouldBreakoutFromForeign(name) {
3350
3491
  if (FOREIGN_BREAKOUT_TAGS.has(name))
3351
3492
  return true;
3352
- if (name === "font")
3353
- return true;
3493
+ if (name === "font") {
3494
+ return this.hasAttr("color") || this.hasAttr("face") || this.hasAttr("size");
3495
+ }
3354
3496
  return false;
3355
3497
  }
3356
3498
  startTagInForeign(name, raw, selfClosing, start) {
3357
3499
  const ns = name === "svg" ? NS.svg : name === "math" ? NS.math : this.currentNamespace();
3358
3500
  const top = this.currentNode();
3359
- if (top && top.ns === NS.math && MATHML_TEXT_INTEGRATION_POINTS.has(top.name) && name !== "mglyph" && name !== "malignmark") {
3501
+ if (top && this.isIntegrationPoint(top) && name !== "mglyph" && name !== "malignmark") {
3360
3502
  this.dispatchStartTag(name, raw, selfClosing, start, start + raw.length);
3361
3503
  return;
3362
3504
  }
3363
3505
  if (selfClosing)
3364
3506
  return;
3365
- this.push(raw, ns, start);
3507
+ const frame = { name, ns, start };
3508
+ if (ns === NS.math && name === "annotation-xml") {
3509
+ const enc = (this.getAttr("encoding") ?? "").toLowerCase();
3510
+ if (enc === "text/html" || enc === "application/xhtml+xml") {
3511
+ frame.htmlIntegration = true;
3512
+ }
3513
+ }
3514
+ this.openElements.push(frame);
3366
3515
  }
3367
3516
  dispatchStartTag(name, raw, selfClosing, start, endIndex) {
3368
3517
  switch (this.insertionMode) {
@@ -3372,10 +3521,6 @@ class LinterCtx {
3372
3521
  return this.startInBody(name, raw, selfClosing, start, endIndex);
3373
3522
  case MODES.inTable:
3374
3523
  return this.startInTable(name, raw, selfClosing, start, endIndex);
3375
- case MODES.inTableText:
3376
- this.flushTableText();
3377
- this.insertionMode = this.originalInsertionMode;
3378
- return this.dispatchStartTag(name, raw, selfClosing, start, endIndex);
3379
3524
  case MODES.inCaption:
3380
3525
  return this.startInCaption(name, raw, selfClosing, start, endIndex);
3381
3526
  case MODES.inColumnGroup:
@@ -3390,8 +3535,6 @@ class LinterCtx {
3390
3535
  return this.startInSelect(name, raw, selfClosing, start, endIndex);
3391
3536
  case MODES.inSelectInTable:
3392
3537
  return this.startInSelectInTable(name, raw, selfClosing, start, endIndex);
3393
- case MODES.text:
3394
- return;
3395
3538
  }
3396
3539
  }
3397
3540
  startInBody(name, raw, selfClosing, start, _endIndex) {
@@ -3433,7 +3576,7 @@ class LinterCtx {
3433
3576
  return;
3434
3577
  }
3435
3578
  if (name === "form") {
3436
- if (this.formPointer !== null && !this.openElementsHas("template")) {
3579
+ if (this.openElementsHas("form") && !this.openElementsHas("template")) {
3437
3580
  this.report(HTML_DUPLICATE_FORM, LEVEL_ERROR, start, {
3438
3581
  tag: raw,
3439
3582
  mode: this.insertionMode
@@ -3443,7 +3586,6 @@ class LinterCtx {
3443
3586
  if (this.hasInButtonScope("p"))
3444
3587
  this.implicitlyClose("p", start, raw);
3445
3588
  this.push(raw, NS.html, start);
3446
- this.formPointer = this.currentNode();
3447
3589
  return;
3448
3590
  }
3449
3591
  if (name === "li") {
@@ -3516,7 +3658,7 @@ class LinterCtx {
3516
3658
  return;
3517
3659
  }
3518
3660
  if (name === "a") {
3519
- if (this.openElementsHas("a") || this.activeFormattingHas("a")) {
3661
+ if (this.activeFormattingHas("a")) {
3520
3662
  this.report(HTML_NESTED_INTERACTIVE, LEVEL_WARN, start, {
3521
3663
  tag: raw,
3522
3664
  mode: this.insertionMode
@@ -3533,7 +3675,7 @@ class LinterCtx {
3533
3675
  return;
3534
3676
  }
3535
3677
  if (name === "nobr") {
3536
- if (this.hasInDefaultScope("nobr")) {
3678
+ if (this.activeFormattingHas("nobr")) {
3537
3679
  this.report(HTML_NESTED_INTERACTIVE, LEVEL_WARN, start, {
3538
3680
  tag: raw,
3539
3681
  mode: this.insertionMode
@@ -3617,7 +3759,6 @@ class LinterCtx {
3617
3759
  if (e === null)
3618
3760
  break;
3619
3761
  if (e.name === name) {
3620
- this.activeFormatting.splice(i, 1);
3621
3762
  const idx = this.openElements.indexOf(e);
3622
3763
  if (idx >= 0)
3623
3764
  this.openElements.splice(idx, 1);
@@ -3710,6 +3851,9 @@ class LinterCtx {
3710
3851
  return this.startInBody(name, raw, selfClosing, start, endIndex);
3711
3852
  }
3712
3853
  if (name === "input") {
3854
+ const type = (this.getAttr("type") ?? "").toLowerCase();
3855
+ if (type === "hidden")
3856
+ return;
3713
3857
  this.report(HTML_TAG_NOT_ALLOWED_IN_PARENT, LEVEL_WARN, start, {
3714
3858
  tag: raw,
3715
3859
  parent: "table",
@@ -3745,18 +3889,6 @@ class LinterCtx {
3745
3889
  this.openElements.pop();
3746
3890
  }
3747
3891
  }
3748
- flushTableText() {
3749
- if (!this.pendingTableText)
3750
- return;
3751
- const { hasNonWhitespace, start, snippet } = this.pendingTableText;
3752
- if (hasNonWhitespace) {
3753
- this.report(HTML_TEXT_NOT_ALLOWED_IN_PARENT, LEVEL_ERROR, start, {
3754
- mode: this.originalInsertionMode,
3755
- snippet
3756
- });
3757
- }
3758
- this.pendingTableText = null;
3759
- }
3760
3892
  startInCaption(name, raw, selfClosing, start, endIndex) {
3761
3893
  if (name === "caption" || name === "col" || name === "colgroup" || name === "tbody" || name === "td" || name === "tfoot" || name === "th" || name === "thead" || name === "tr") {
3762
3894
  if (this.hasInTableScope("caption")) {
@@ -3989,14 +4121,24 @@ class LinterCtx {
3989
4121
  return;
3990
4122
  const ns = this.currentNamespace();
3991
4123
  if (ns !== NS.html) {
3992
- for (let i = this.openElements.length - 1;i >= 0; i--) {
3993
- const f = this.openElements[i];
3994
- if (f.ns === NS.html)
4124
+ let stackIdx = this.openElements.length - 1;
4125
+ let first = true;
4126
+ while (stackIdx > 0) {
4127
+ const f = this.openElements[stackIdx];
4128
+ if (!first && f.ns === NS.html)
3995
4129
  break;
3996
- if (f.name.toLowerCase() === name) {
3997
- this.openElements.length = i;
4130
+ if (f.ns !== NS.html && f.name.toLowerCase() === name) {
4131
+ this.openElements.length = stackIdx;
3998
4132
  return;
3999
4133
  }
4134
+ if (first) {
4135
+ this.report(HTML_UNEXPECTED_END_TAG, LEVEL_WARN, start, {
4136
+ tag: name,
4137
+ mode: this.insertionMode
4138
+ });
4139
+ first = false;
4140
+ }
4141
+ stackIdx--;
4000
4142
  }
4001
4143
  }
4002
4144
  if (VOID_ELEMENTS.has(name)) {
@@ -4027,19 +4169,33 @@ class LinterCtx {
4027
4169
  });
4028
4170
  return;
4029
4171
  }
4172
+ const endIsTableStructural = TABLE_SCOPE_TAGS.has(name);
4030
4173
  for (let i = this.openElements.length - 1;i >= 0; i--) {
4031
4174
  const f = this.openElements[i];
4032
4175
  if (f.ns === NS.html && f.name === name) {
4176
+ for (let j = this.openElements.length - 1;j > i; j--) {
4177
+ const popped = this.openElements[j];
4178
+ if (popped.ns === NS.html && popped.name !== name && !IMPLIED_END_TAGS.has(popped.name) && !(endIsTableStructural && TABLE_SCOPE_TAGS.has(popped.name))) {
4179
+ this.report(HTML_UNCLOSED_BEFORE_END, LEVEL_WARN, start, {
4180
+ tag: name,
4181
+ unclosed: popped.name,
4182
+ mode: this.insertionMode
4183
+ });
4184
+ break;
4185
+ }
4186
+ }
4033
4187
  this.openElements.length = i;
4034
- if (name === "form")
4035
- this.formPointer = null;
4036
4188
  if (TABLE_SCOPE_TAGS.has(name) || name === "select" || name === "template") {
4037
4189
  this.resetInsertionModeAppropriately();
4038
4190
  }
4039
4191
  return;
4040
4192
  }
4041
- if (f.ns === NS.html && SPECIAL_ELEMENTS.has(f.name)) {
4042
- break;
4193
+ if (f.ns === NS.html && SPECIAL_ELEMENTS.has(f.name) && !IMPLIED_END_TAGS.has(f.name) && !(endIsTableStructural && TABLE_SCOPE_TAGS.has(f.name))) {
4194
+ this.report(HTML_UNEXPECTED_END_TAG, LEVEL_WARN, start, {
4195
+ tag: name,
4196
+ mode: this.insertionMode
4197
+ });
4198
+ return;
4043
4199
  }
4044
4200
  }
4045
4201
  }
@@ -4096,7 +4252,7 @@ function offsetToLineCol(lineStarts, offset) {
4096
4252
  }
4097
4253
  return { line: lo + 1, column: offset - lineStarts[lo] + 1 };
4098
4254
  }
4099
- var HTML_TAG_NAME_HAS_UPPERCASE = "HTML_TAG_NAME_HAS_UPPERCASE", HTML_SVG_TAG_WILL_LOWERCASE = "HTML_SVG_TAG_WILL_LOWERCASE", HTML_TAG_NOT_ALLOWED_IN_PARENT = "HTML_TAG_NOT_ALLOWED_IN_PARENT", HTML_TEXT_NOT_ALLOWED_IN_PARENT = "HTML_TEXT_NOT_ALLOWED_IN_PARENT", HTML_VOID_ELEMENT_HAS_CLOSE_TAG = "HTML_VOID_ELEMENT_HAS_CLOSE_TAG", HTML_DUPLICATE_FORM = "HTML_DUPLICATE_FORM", HTML_NESTED_INTERACTIVE = "HTML_NESTED_INTERACTIVE", HTML_MISNESTED_FORMATTING = "HTML_MISNESTED_FORMATTING", HTML_UNEXPECTED_END_TAG = "HTML_UNEXPECTED_END_TAG", LEVEL_ERROR = "error", LEVEL_WARN = "warn", TABLE_SCOPE_TAGS, TABLE_BODY_CELL_TAGS;
4255
+ var HTML_TAG_NAME_HAS_UPPERCASE = "HTML_TAG_NAME_HAS_UPPERCASE", HTML_SVG_TAG_WILL_LOWERCASE = "HTML_SVG_TAG_WILL_LOWERCASE", HTML_SVG_ATTR_WILL_LOWERCASE = "HTML_SVG_ATTR_WILL_LOWERCASE", HTML_MATHML_ATTR_WILL_LOWERCASE = "HTML_MATHML_ATTR_WILL_LOWERCASE", HTML_TAG_NOT_ALLOWED_IN_PARENT = "HTML_TAG_NOT_ALLOWED_IN_PARENT", HTML_TEXT_NOT_ALLOWED_IN_PARENT = "HTML_TEXT_NOT_ALLOWED_IN_PARENT", HTML_VOID_ELEMENT_HAS_CLOSE_TAG = "HTML_VOID_ELEMENT_HAS_CLOSE_TAG", HTML_DUPLICATE_FORM = "HTML_DUPLICATE_FORM", HTML_NESTED_INTERACTIVE = "HTML_NESTED_INTERACTIVE", HTML_MISNESTED_FORMATTING = "HTML_MISNESTED_FORMATTING", HTML_UNEXPECTED_END_TAG = "HTML_UNEXPECTED_END_TAG", HTML_DUPLICATE_ATTRIBUTE = "HTML_DUPLICATE_ATTRIBUTE", HTML_ATTRIBUTES_ON_END_TAG = "HTML_ATTRIBUTES_ON_END_TAG", HTML_SELF_CLOSING_END_TAG = "HTML_SELF_CLOSING_END_TAG", HTML_MISSING_ATTRIBUTE_VALUE = "HTML_MISSING_ATTRIBUTE_VALUE", HTML_CDATA_IN_HTML_NAMESPACE = "HTML_CDATA_IN_HTML_NAMESPACE", HTML_BOGUS_COMMENT = "HTML_BOGUS_COMMENT", HTML_UNCLOSED_BEFORE_END = "HTML_UNCLOSED_BEFORE_END", LEVEL_ERROR = "error", LEVEL_WARN = "warn", TABLE_SCOPE_TAGS, TABLE_BODY_CELL_TAGS, IMPLIED_END_TAGS;
4100
4256
  var init_htmllinter = __esm(() => {
4101
4257
  init_html_tokenizer();
4102
4258
  init_htmllinter_tables();
@@ -4112,6 +4268,18 @@ var init_htmllinter = __esm(() => {
4112
4268
  "table"
4113
4269
  ]);
4114
4270
  TABLE_BODY_CELL_TAGS = new Set(["td", "th"]);
4271
+ IMPLIED_END_TAGS = new Set([
4272
+ "dd",
4273
+ "dt",
4274
+ "li",
4275
+ "optgroup",
4276
+ "option",
4277
+ "p",
4278
+ "rb",
4279
+ "rp",
4280
+ "rt",
4281
+ "rtc"
4282
+ ]);
4115
4283
  });
4116
4284
 
4117
4285
  // tools/core/lint-check.js