Skip to content

fix: [#2052] Correct caption element content model to allow flow content#2058

Merged
capricorn86 merged 1 commit intocapricorn86:masterfrom
atzzCokeK:fix/issue-2052-caption-content-model
Feb 9, 2026
Merged

fix: [#2052] Correct caption element content model to allow flow content#2058
capricorn86 merged 1 commit intocapricorn86:masterfrom
atzzCokeK:fix/issue-2052-caption-content-model

Conversation

@atzzCokeK
Copy link
Copy Markdown
Contributor

@atzzCokeK atzzCokeK commented Feb 6, 2026

Fixes #2052

Problem

The <caption> element was incorrectly configured with contentModel: textOrComments, which only allowed text nodes and comments as children. According to the HTML specification, caption elements should contain flow content (including inline and block elements), except table elements.

This caused inline elements like <b>, <em>, <span>, etc. to be incorrectly moved outside the caption during HTML parsing, resulting in broken DOM structures.

Reproduction

import { Window } from 'happy-dom';

const window = new Window();
window.document.write(`
  <table>
    <caption>
      This <b>is</b> a caption.
    </caption>
  </table>
`);

console.log(window.document.documentElement.outerHTML);
// Before fix: <b></b><table><caption>This is a caption.</caption>...
// After fix:  <table><caption>This <b>is</b> a caption.</caption>...

Changes

packages/happy-dom/src/config/HTMLElementConfig.ts

Updated the caption element configuration to follow the HTML spec and match the pattern used by other table elements (td, th):

caption: {
    className: 'HTMLTableCaptionElement',
    contentModel: HTMLElementConfigContentModelEnum.noForbiddenFirstLevelDescendants,
    forbiddenDescendants: ['table', 'tbody', 'thead', 'tfoot', 'tr', 'td', 'th', 'col', 'colgroup'],
    permittedParents: ['table']
}

Changes explained:

  • contentModel: Changed from textOrComments to noForbiddenFirstLevelDescendants to allow flow content
  • forbiddenDescendants: Added table structure elements (per HTML spec: "Flow content, but with no descendant table elements")
  • permittedParents: Added ['table'] to ensure caption only appears as a child of table elements

packages/happy-dom/test/html-parser/HTMLParser.malformedHTML.test.ts

Added comprehensive test coverage (9 new tests) for the caption element content model:

  1. Inline elements preservation - Tests that <b>, <strong>, <em>, <span>, <a> are correctly preserved inside caption
  2. Nested inline elements - Tests that nested structures like <small><b>text</b></small> work correctly
  3. Block-level elements - Tests that block elements like <p> and <div> are allowed (flow content)
  4. Table element prohibition - Tests that <table> elements inside caption are correctly moved outside
  5. Content serialization - Tests that caption content is preserved when serializing to HTML
  6. permittedParents validation - Tests that caption tags are removed when parent is not <table> or when used standalone

Implementation Details

This implementation follows the same pattern as PR #2007 (paragraph elements), using the existing noForbiddenFirstLevelDescendants content model. While this approach doesn't recursively check for deeply nested table elements (e.g., <caption><div><table>...</table></div></caption>), it:

  • Matches the established codebase patterns
  • Handles the vast majority of real-world use cases
  • Provides dual protection through both forbiddenDescendants and permittedParents
  • Maintains consistency with other table-related elements

Test Coverage

All tests pass:

✓ test/html-parser/HTMLParser.malformedHTML.test.ts (16 tests) 17ms
  Test Files  1 passed (1)
  Tests  16 passed (16)

Full test suite:

Test Files  294 passed (294)
Tests  7164 passed (7164)

@atzzCokeK atzzCokeK force-pushed the fix/issue-2052-caption-content-model branch from 22e636a to 614b105 Compare February 6, 2026 13:41
@atzzCokeK atzzCokeK marked this pull request as ready for review February 6, 2026 13:50
…w flow content

Fixes capricorn86#2052

## Problem

The caption element was incorrectly configured with contentModel: textOrComments,
which only allowed text nodes and comments. Per the HTML spec, caption elements
should contain flow content (inline and block elements), except table elements.

This caused elements like <b>, <em>, <span>, etc. to be incorrectly moved
outside the caption during parsing.

## Changes

### packages/happy-dom/src/config/HTMLElementConfig.ts

Updated caption element configuration to match the HTML spec and follow the
same pattern as td/th elements:

- Changed contentModel from textOrComments to noForbiddenFirstLevelDescendants
- Added forbiddenDescendants: table structure elements (table, tbody, thead,
  tfoot, tr, td, th, col, colgroup)
- Added permittedParents: ['table'] to ensure caption only appears in tables

### packages/happy-dom/test/html-parser/HTMLParser.malformedHTML.test.ts

Added comprehensive test coverage for caption element content model:
- Inline elements preservation (b, strong, em, span, a)
- Nested inline elements
- Block-level elements (p, div)
- Table element prohibition
- Content serialization
- permittedParents validation (wrong parent, standalone, correct parent)

## Implementation Details

This fix follows the same pattern as PR capricorn86#2007 for paragraph elements, using
the existing noForbiddenFirstLevelDescendants content model. While this
doesn't recursively check deeply nested table elements, it matches the
existing codebase patterns and handles the vast majority of real-world cases.
@atzzCokeK atzzCokeK force-pushed the fix/issue-2052-caption-content-model branch from e9ef366 to 639c2d8 Compare February 6, 2026 13:54
@atzzCokeK
Copy link
Copy Markdown
Contributor Author

atzzCokeK commented Feb 6, 2026

CI Test Failure Note

The failing Fetch test is unrelated to this PR's changes (caption element only). The same failure occurs in #2048 and is linked to #2022/#2023.

All 16 caption-related tests pass successfully in my environment.

@Spixmaster
Copy link
Copy Markdown

Was this done an LLM? Who on earth writes such pull request descriptions?

@atzzCokeK
Copy link
Copy Markdown
Contributor Author

I did use an LLM as a writing aid and reviewed the content myself. Please let me know if you have any feedback on the actual changes👋

Copy link
Copy Markdown
Owner

@capricorn86 capricorn86 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for your contribution @atzzCokeK! :star

@capricorn86 capricorn86 merged commit fbef5d9 into capricorn86:master Feb 9, 2026
3 checks passed
RAprogramm pushed a commit to RAprogramm/fork-happy-dom that referenced this pull request Feb 20, 2026
…w flow content (capricorn86#2058)

Fixes capricorn86#2052

## Problem

The caption element was incorrectly configured with contentModel: textOrComments,
which only allowed text nodes and comments. Per the HTML spec, caption elements
should contain flow content (inline and block elements), except table elements.

This caused elements like <b>, <em>, <span>, etc. to be incorrectly moved
outside the caption during parsing.

## Changes

### packages/happy-dom/src/config/HTMLElementConfig.ts

Updated caption element configuration to match the HTML spec and follow the
same pattern as td/th elements:

- Changed contentModel from textOrComments to noForbiddenFirstLevelDescendants
- Added forbiddenDescendants: table structure elements (table, tbody, thead,
  tfoot, tr, td, th, col, colgroup)
- Added permittedParents: ['table'] to ensure caption only appears in tables

### packages/happy-dom/test/html-parser/HTMLParser.malformedHTML.test.ts

Added comprehensive test coverage for caption element content model:
- Inline elements preservation (b, strong, em, span, a)
- Nested inline elements
- Block-level elements (p, div)
- Table element prohibition
- Content serialization
- permittedParents validation (wrong parent, standalone, correct parent)

## Implementation Details

This fix follows the same pattern as PR capricorn86#2007 for paragraph elements, using
the existing noForbiddenFirstLevelDescendants content model. While this
doesn't recursively check deeply nested table elements, it matches the
existing codebase patterns and handles the vast majority of real-world cases.
RAprogramm pushed a commit to RAprogramm/fork-happy-dom that referenced this pull request Feb 20, 2026
…w flow content (capricorn86#2058)

Fixes capricorn86#2052

## Problem

The caption element was incorrectly configured with contentModel: textOrComments,
which only allowed text nodes and comments. Per the HTML spec, caption elements
should contain flow content (inline and block elements), except table elements.

This caused elements like <b>, <em>, <span>, etc. to be incorrectly moved
outside the caption during parsing.

## Changes

### packages/happy-dom/src/config/HTMLElementConfig.ts

Updated caption element configuration to match the HTML spec and follow the
same pattern as td/th elements:

- Changed contentModel from textOrComments to noForbiddenFirstLevelDescendants
- Added forbiddenDescendants: table structure elements (table, tbody, thead,
  tfoot, tr, td, th, col, colgroup)
- Added permittedParents: ['table'] to ensure caption only appears in tables

### packages/happy-dom/test/html-parser/HTMLParser.malformedHTML.test.ts

Added comprehensive test coverage for caption element content model:
- Inline elements preservation (b, strong, em, span, a)
- Nested inline elements
- Block-level elements (p, div)
- Table element prohibition
- Content serialization
- permittedParents validation (wrong parent, standalone, correct parent)

## Implementation Details

This fix follows the same pattern as PR capricorn86#2007 for paragraph elements, using
the existing noForbiddenFirstLevelDescendants content model. While this
doesn't recursively check deeply nested table elements, it matches the
existing codebase patterns and handles the vast majority of real-world cases.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Incorrect DOM structure with <caption> elements

3 participants