Skip to content

Handle documents without a root element without panicking - #655

Open
nicoburns wants to merge 1 commit into
mainfrom
devin/1786204706-empty-doc-root-element
Open

Handle documents without a root element without panicking#655
nicoburns wants to merge 1 commit into
mainfrom
devin/1786204706-empty-doc-root-element

Conversation

@nicoburns

@nicoburns nicoburns commented Aug 8, 2026

Copy link
Copy Markdown
Member

Summary

BaseDocument::root_element() unwraps first_element_child(), which panics for documents with no root element. Such documents arise whenever an <iframe> sub-document is parsed by the default DummyHtmlParserProvider (whose parse_document returns an empty PlainDocument — this is what happens in the WPT runner, which doesn't pass an html_parser_provider), and can also arise for genuinely childless documents (e.g. an empty XML document).

The panic fired in resolve(): the parent document sets the sub-document's viewport via viewport_mut(), and ViewportMut::drop calls scroll_viewport_by(0.0, 0.0) to clamp the scroll offset, which called root_element() unconditionally.

Three call sites are made to explicitly handle the no-root-element case, where the correct behaviour is "there is nothing to do":

  • BaseDocument::scroll_viewport_by_has_changed: a document with no root element has zero scrollable content, so the scroll offset clamps to (0, 0):
    let (content_width, content_height) = match self.try_root_element() {
        Some(root) => (...root scrollable overflow...),
        None => (0.0, 0.0),
    };
  • BlitzDomPainter::paint_scene: nothing to paint — return early via try_root_element()
  • EventDriver::handle_ui_event: no fallback event target — return early instead of target.unwrap_or_else(|| ... root_element().id)

Fixes 113 of the 244 CRASHing tests in the WPT css suite (244 → 131 CRASH; PASS 11021 → 11065; no test or subtest that previously passed regresses).

Note: a follow-up improvement would be for the WPT runner (and/or HtmlDocument::from_html) to default html_parser_provider to blitz-html's HtmlProvider, so iframe sub-documents actually parse their HTML instead of being empty.

Link to Devin session: https://dioxus.staging.devinenterprise.com/sessions/5cb0957e4dae408aaf1d7c1860935027
Requested by: @nicoburns

WPT results

44 newly passing, 0 newly failing (net +44), 69 other status changes.

Full diff (113 changed tests)
+ Crash => Pass css/CSS2/linebox/iframe-in-block-in-inline.html
+ Crash => Pass css/CSS2/linebox/iframe-in-wrapped-span.html
! Crash => Fail css/CSS2/normal-flow/resizable-iframe-paint-order.html
! Crash => Fail css/compositing/mix-blend-mode/mix-blend-mode-iframe-parent.html
! Crash => Fail css/compositing/mix-blend-mode/mix-blend-mode-iframe-sibling.html
+ Crash => Pass css/css-backgrounds/background-margin-iframe-root.html
! Crash => Fail css/css-borders/border-shape/border-shape-overflow-replaced-iframe.html
! Crash => Fail css/css-borders/corner-shape/corner-shape-iframe-border.html
! Crash => Fail css/css-cascade/presentational-hints-rollback.html
+ Crash => Pass css/css-color-adjust/rendering/dark-color-scheme/color-scheme-iframe-background-about-blank.tentative.html
! Crash => Fail css/css-color-adjust/rendering/dark-color-scheme/color-scheme-iframe-background-mismatch-alpha.html
! Crash => Fail css/css-color-adjust/rendering/dark-color-scheme/color-scheme-iframe-background-mismatch-dynamic.html
! Crash => Fail css/css-color-adjust/rendering/dark-color-scheme/color-scheme-iframe-background-mismatch-opaque.html
! Crash => Fail css/css-color-adjust/rendering/dark-color-scheme/color-scheme-iframe-background-mismatch-used-preferred.html
+ Crash => Pass css/css-color-adjust/rendering/dark-color-scheme/color-scheme-iframe-background.html
+ Crash => Pass css/css-color-adjust/rendering/dark-color-scheme/color-scheme-iframe-preferred-change-cross-origin.sub.html
! Crash => Fail css/css-color-adjust/rendering/dark-color-scheme/color-scheme-iframe-preferred-page-dark.html
! Crash => Fail css/css-color-adjust/rendering/dark-color-scheme/color-scheme-iframe-preferred-page-light.html
! Crash => Fail css/css-color-adjust/rendering/dark-color-scheme/color-scheme-iframe-preferred.html
! Crash => Fail css/css-conditional/at-media-dynamic-001.html
! Crash => Fail css/css-contain/contain-inline-size-replaced.html
! Crash => Fail css/css-contain/contain-size-replaced-003a.html
! Crash => Fail css/css-contain/contain-size-replaced-003b.html
! Crash => Fail css/css-contain/contain-size-replaced-003c.html
+ Crash => Pass css/css-contain/content-visibility/content-visibility-004.html
+ Crash => Pass css/css-contain/content-visibility/content-visibility-019.sub.https.html
+ Crash => Pass css/css-contain/content-visibility/content-visibility-020.html
+ Crash => Pass css/css-contain/content-visibility/content-visibility-023.html
! Crash => Fail css/css-contain/content-visibility/content-visibility-032.html
+ Crash => Pass css/css-contain/content-visibility/content-visibility-auto-in-iframe.html
! Crash => Fail css/css-display/display-change-iframe.html
+ Crash => Pass css/css-fonts/downloadable-font-in-iframe-print.html
+ Crash => Pass css/css-fonts/downloadable-font-scoped-to-document.html
! Crash => Fail css/css-highlight-api/painting/custom-highlight-dynamic-container-metrics-001.html
! Crash => Fail css/css-highlight-api/painting/custom-highlight-dynamic-viewport-metrics-001.html
! Crash => Fail css/css-highlight-api/painting/custom-highlight-dynamic-viewport-metrics-first-line-001.html
+ Crash => Pass css/css-highlight-api/painting/custom-highlight-painting-iframe-001.html
+ Crash => Pass css/css-highlight-api/painting/custom-highlight-painting-iframe-002.html
! Crash => Fail css/css-highlight-api/painting/custom-highlight-painting-iframe-003.html
! Crash => Fail css/css-highlight-api/painting/custom-highlight-painting-iframe-004.html
+ Crash => Pass css/css-highlight-api/painting/custom-highlight-painting-iframe-005.html
+ Crash => Pass css/css-highlight-api/painting/custom-highlight-painting-iframe-006.html
+ Crash => Pass css/css-images/image-orientation/image-orientation-from-image-embedded-content.html
+ Crash => Pass css/css-images/image-orientation/image-orientation-iframe.html
+ Crash => Pass css/css-images/image-orientation/image-orientation-none-image-document.html
+ Crash => Pass css/css-images/object-view-box-iframe.html
+ Crash => Pass css/css-overflow/overflow-replaced-element-002.html
+ Crash => Pass css/css-overflow/scrollbar-large-scale-in-iframe.html
! Crash => Fail css/css-page/fixedpos-with-iframe-print.html
! Crash => Fail css/css-page/media-queries-002-print.html
+ Crash => Pass css/css-position/sticky/position-sticky-fixed-ancestor-iframe.html
! Crash => Fail css/css-scroll-anchoring/vertical-rl-viewport-size-change-000.html
! Crash => Fail css/css-scroll-anchoring/vertical-rl-viewport-size-change-001.html
! Crash => Fail css/css-scroll-snap/scroll-target-align-001.html
! Crash => Fail css/css-scroll-snap/scroll-target-align-004.html
! Crash => Fail css/css-scroll-snap/scroll-target-margin-001.html
! Crash => Fail css/css-scroll-snap/scroll-target-padding-001.html
! Crash => Fail css/css-scroll-snap/scroll-target-snap-001.html
+ Crash => Pass css/css-scrollbars/viewport-scrollbar-body.html
+ Crash => Pass css/css-scrollbars/viewport-scrollbar.html
! Crash => Fail css/css-sizing/dynamic-available-size-iframe.html
! Crash => Fail css/css-sizing/responsive-iframe/responsive-iframe-meta-after-head.html
+ Crash => Pass css/css-sizing/responsive-iframe/responsive-iframe-meta-dynamic-append-after-body.html
! Crash => Fail css/css-sizing/responsive-iframe/responsive-iframe-meta-dynamic-append-to-doc.html
! Crash => Fail css/css-sizing/responsive-iframe/responsive-iframe-meta-dynamic-append.html
+ Crash => Pass css/css-sizing/responsive-iframe/responsive-iframe-meta-dynamic-name-change-after-body.html
! Crash => Fail css/css-sizing/responsive-iframe/responsive-iframe-meta-dynamic-name-change.html
! Crash => Fail css/css-sizing/responsive-iframe/responsive-iframe-meta-dynamic-remove.html
! Crash => Fail css/css-sizing/responsive-iframe/responsive-iframe-meta-dynamic-write.html
+ Crash => Pass css/css-sizing/responsive-iframe/responsive-iframe-meta-in-body.html
+ Crash => Pass css/css-sizing/responsive-iframe/responsive-iframe-no-match-element.html
+ Crash => Pass css/css-sizing/responsive-iframe/responsive-iframe-not-embedded-sized.html
! Crash => Fail css/css-sizing/responsive-iframe/responsive-iframe-request-resize.html
! Crash => Fail css/css-sizing/responsive-iframe/responsive-iframe.html
+ Crash => Pass css/css-transforms/transform-iframe-001.html
! Crash => Fail css/css-transforms/transform-iframe-002.html
! Crash => Fail css/css-transforms/transform-iframe-scroll-position.html
! Crash => Fail css/css-values/inline-cache-base-uri.html
+ Crash => Pass css/css-values/vh-support-transform-origin.html
+ Crash => Pass css/css-values/vh-support-transform-translate.html
+ Crash => Pass css/css-values/vh-update-and-transition-in-subframe.html
+ Crash => Pass css/css-view-transitions/backdrop-filter-while-promise-pending.html
+ Crash => Pass css/css-view-transitions/dialog-in-rtl-iframe.html
! Crash => Fail css/css-view-transitions/iframe-and-main-frame-transition-new-main-new-iframe.html
! Crash => Fail css/css-view-transitions/iframe-and-main-frame-transition-new-main-old-iframe.html
! Crash => Fail css/css-view-transitions/iframe-and-main-frame-transition-old-main-new-iframe.html
! Crash => Fail css/css-view-transitions/iframe-and-main-frame-transition-old-main-old-iframe.html
! Crash => Fail css/css-view-transitions/iframe-and-main-frame-transition-old-main.html
! Crash => Fail css/css-view-transitions/iframe-and-main-frame-transition-with-name-on-iframe.html
+ Crash => Pass css/css-view-transitions/iframe-new-has-scrollbar.html
+ Crash => Pass css/css-view-transitions/iframe-old-has-scrollbar.html
+ Crash => Pass css/css-view-transitions/iframe-transition.sub.html
+ Crash => Pass css/css-view-transitions/navigation/root-element-transition-iframe-cross-origin.sub.html
! Crash => Fail css/css-view-transitions/navigation/root-element-transition-iframe-with-startVT-on-main.html
+ Crash => Pass css/css-view-transitions/navigation/root-element-transition-iframe.html
! Crash => Fail css/css-view-transitions/paint-holding-in-iframe.html
! Crash => Fail css/css-view-transitions/sibling-frames-transition.html
+ Crash => Pass css/css-view-transitions/snapshot-containing-block-static-iframe.html
+ Crash => Pass css/css-view-transitions/transition-in-empty-iframe.html
! Crash => Fail css/css-writing-modes/background-size-document-root-vrl-002.html
! Crash => Fail css/css-writing-modes/background-size-document-root-vrl-004.html
! Crash => Fail css/css-writing-modes/background-size-document-root-vrl-006.html
! Crash => Fail css/css-writing-modes/background-size-document-root-vrl-008.html
! Crash => Fail css/css-writing-modes/bidi-dynamic-iframe-001.html
! Crash => Fail css/css-writing-modes/orthogonal-root-resize-icb-001.html
! Crash => Fail css/css-writing-modes/orthogonal-root-resize-icb-002.html
! Crash => Fail css/css-writing-modes/orthogonal-root-resize-icb-003.html
! Crash => Fail css/css-writing-modes/orthogonal-root-resize-icb-004.html
! Crash => Fail css/css-writing-modes/orthogonal-root-resize-icb-005.html
! Crash => Fail css/css-writing-modes/orthogonal-root-resize-icb-006.html
! Crash => Fail css/css-writing-modes/orthogonal-root-resize-icb-007.html
! Crash => Fail css/filter-effects/svg-relative-urls-001.html
! Crash => Fail css/mediaqueries/min-width-tables-001.html

Generated by the WPT workflow.

Iframe sub-documents parsed by the default DummyHtmlParserProvider (and
other childless documents such as about:blank) have no root element.
Touching such a document's viewport panicked in scroll_viewport_by via
BaseDocument::root_element()'s unwrap, and painting or dispatching
events to one panicked the same way.

Treat a document without a root element as having zero scrollable
content, nothing to paint, and no event target.
@nicoburns nicoburns self-assigned this Aug 8, 2026
@staging-devin-ai-integration

Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

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.

1 participant