Skip to content

fix(layout): guard the default tab-stop grid against a non-positive interval - #3947

Open
leonidkuznetsov18 wants to merge 1 commit into
superdoc:mainfrom
leonidkuznetsov18:fix-3944-default-tab-stop-zero-hang
Open

fix(layout): guard the default tab-stop grid against a non-positive interval#3947
leonidkuznetsov18 wants to merge 1 commit into
superdoc:mainfrom
leonidkuznetsov18:fix-3944-default-tab-stop-zero-hang

Conversation

@leonidkuznetsov18

@leonidkuznetsov18 leonidkuznetsov18 commented Aug 31, 2026

Copy link
Copy Markdown

What

A DOCX whose word/settings.xml carries <w:defaultTabStop w:val="0"/> froze the main thread forever: onReady/onException never fired and the tab pegged the CPU until killed (#3944).

computeTabStops (packages/layout-engine/contracts/src/engines/tabs.ts) generates the automatic tab-stop grid with pos += defaultTabInterval toward a fixed limit, so an interval of 0 (or a negative one) never advances and the loop never terminates. Both buildTabStopsPx callers (measuring/dom and layout-bridge/remeasure) pass the document value through tabIntervalTwips ?? DEFAULT_TAB_INTERVAL_TWIPS, and ?? doesn't catch 0. The grid is pre-computed for every measured paragraph, which is why any document with text content triggers it — no tab characters required (an empty <w:p/> escapes via the simple-empty-paragraph fast path, matching the repro matrix in the issue).

Fix

Skip the default grid for a non-positive (or non-finite) interval, keeping explicit stops and the implicit indent-driven stops intact. Word writes defaultTabStop 0 when a user sets the default tab interval to 0, opens such documents fine, and simply generates no automatic tab stops — so "no grid" is the Word-faithful behavior, and it mirrors the existing guard in word-layout's own computeTabStops (packages/word-layout/src/tab-layout.ts).

Guarding this single choke point covers both callers; it is the only += interval loop over the document value in the layout engine.

Testing

  • New regression tests in tabs.test.ts for interval 0 (no default grid, terminates) and a negative interval (explicit stops preserved, no default grid). Without the guard the first test hangs the runner — verified by stashing the fix: the suite ran into a 3-minute wall timeout exactly as described in the issue.
  • packages/layout-engine/contracts: 492/492 pass.
  • Downstream consumers unchanged for positive intervals: measuring/dom 529/529, layout-bridge 1653/1653 pass.

Fixes #3944

Review in cubic

…nterval

A DOCX whose word/settings.xml carries <w:defaultTabStop w:val="0"/> froze
the main thread forever: computeTabStops generates the automatic tab-stop
grid with `pos += defaultTabInterval` toward a fixed limit, so an interval
of 0 (or a negative one) never advances and the loop never terminates.
onReady/onException never fire and the page pegs the CPU until the tab is
killed. The grid is pre-computed for every measured paragraph, so any
document with text content triggers it — no tab characters required.

Word writes defaultTabStop 0 when a user sets the default tab interval to
0, opens such documents fine, and simply generates no automatic tab stops.
Do the same: skip the default grid for a non-positive (or non-finite)
interval, keeping explicit stops and the implicit indent-driven stops
intact. Mirrors the existing guard in word-layout's computeTabStops.

Fixes superdoc#3944

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@github-actions

github-actions Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

No issues found across 2 files

Re-trigger cubic

@leonidkuznetsov18

Copy link
Copy Markdown
Author

I have read the CLA Document and I hereby sign the CLA

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

defaultTabStop of 0 freezes the main thread forever (no onReady, no onException)

1 participant