Skip to content

Conversation

@TimvdLippe
Copy link
Contributor

There was a discrepancy that for iframes with a sandbox, the script_thread wouldn't handle the about: URLs correctly. Now, we reuse the same logic as used when navigating in an already present child frame.

Fixes #36529
Fixes #37499

Copy link
Contributor Author

@TimvdLippe TimvdLippe left a comment

Choose a reason for hiding this comment

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

See the comments in order to read through the code. While it avoids the CSP error, it doesn't actually load the iframe and I don't understand why. @jdm any thoughts on where the sandbox could have an effect? I feel like we are missing some sort of "hey, go start doing things now" in the iframe. Since it seems like it correctly navigates to the URL and then simply shows a blank screen and does nothing.

self.initiate_load_based_on_url(new_load);
}

fn initiate_load_based_on_url(&self, load: InProgressLoad) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is taken from #36529 (comment)

While this avoids the CSP error as reported in #37499, it doesn't correctly do what the user would expect. On the MDN page, or any sandboxed iframe in WPT tests, the iframe remains empty. E.g. it is as is it never starts.


let (event_loop, host) = match sandbox {
IFrameSandboxState::IFrameSandboxed => (None, None),
IFrameSandboxState::IFrameSandboxed => {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Then I thought: if it doesn't start, maybe the special case here is the culprit. Since this is the only place where I can see the sandbox attribute have an effect. Here, my assumption was: we don't create an event loop in sandbox iframes, so it never starts.

Unfortunately, I still see a blank screen.


let scheme = url.scheme();
match scheme {
"about" if url.path() == "srcdoc" => unreachable!(),
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I can confirm that we load the correct URL, as with a print in htmliframeelement, the correct URL is processed. And we don't hit this panic.

@jdm
Copy link
Member

jdm commented Jun 28, 2025

Ok, I think the problem from #37499 on main is that when a non-sandboxed iframe has a src URL, we handle the initial about:blank entirely within the script thread without going to the network thread (so no CSP check happens against about:blank), and then the proper src URL goes to the network thread and passes the CSP check. A sandboxed iframe starts an about:blank load in a new script thread and that goes to the network thread and fails the CSP check.

I'm going to try to investigate the changes in this PR later today.

@jdm
Copy link
Member

jdm commented Jun 28, 2025

Weirdly, when

fn process_the_iframe_attributes(&self, mode: ProcessingMode, can_gc: CanGc) {
runs from the iframe's post-connection steps, the URL we try to load is about:blank. This suggests to me that the iframe's URL is changed later, but maybe this JS exception prevents the code from running that changes the URL:

(new Error("Minified React error #421; visit https://reactjs.org/docs/error-decoder.html?invariant=421 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.", "https://developer.mozilla.org/static/js/main.a918a4e7.js", 2))

@TimvdLippe
Copy link
Contributor Author

I created a dummy HTML page to test this with a static src. However, I don't think it worked there either. Maybe you can use that to test it out? See the second details contents in #37499 (comment)

@TimvdLippe TimvdLippe added the T-linux-wpt Do a try run of the WPT label Jun 28, 2025
@github-actions github-actions bot removed the T-linux-wpt Do a try run of the WPT label Jun 28, 2025
@github-actions
Copy link

🔨 Triggering try run (#15948085161) for Linux (WPT)

@github-actions
Copy link

Test results for linux-wpt from try job (#15948085161):

Flaky unexpected result (14)
  • TIMEOUT [expected OK] /_mozilla/mozilla/window_resizeTo.html (#36741)
    • TIMEOUT [expected PASS] subtest: Popup onresize event fires after resizeTo

      Test timed out
      

  • CRASH [expected OK] /css/compositing/canvas-composite-modes.html (#37283)
  • OK /css/css-align/blocks/align-content-block-002.html
    • PASS [expected FAIL] subtest: .test 1: start
    • PASS [expected FAIL] subtest: .test 4: baseline
    • PASS [expected FAIL] subtest: .test 6: flex-start
    • PASS [expected FAIL] subtest: .test 8: unsafe start
    • PASS [expected FAIL] subtest: .test 11: safe start
    • PASS [expected FAIL] subtest: .test 15: space-between
    • PASS [expected FAIL] subtest: .test 17: normal
  • OK /fetch/metadata/generated/css-font-face.https.sub.tentative.html (#32732)
    • PASS [expected FAIL] subtest: sec-fetch-dest
    • PASS [expected FAIL] subtest: sec-fetch-storage-access - Same site
  • OK /html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/iframe-src-aboutblank-navigate-immediately.html (#29048)
    • PASS [expected FAIL] subtest: Navigating to a different document with link click
  • OK /html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-cross-origin.sub.window.html (#29056)
    • PASS [expected FAIL] subtest: Cross-origin navigation started from unload handler must be ignored
  • OK /html/browsers/history/the-history-interface/traverse_the_history_5.html (#21383)
    • PASS [expected FAIL] subtest: Multiple history traversals, last would be aborted
  • PASS [expected FAIL] /html/canvas/element/manual/drawing-text-to-the-canvas/canvas.2d.disconnected-font-size-math.html (#30063)
  • TIMEOUT [expected OK] /html/interaction/focus/the-autofocus-attribute/skip-another-top-level-browsing-context.html (#24161)
    • TIMEOUT [expected PASS] subtest: Autofocus elements queued in another top-level browsing context's documents should be skipped.

      Test timed out
      

  • OK /html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/allow-scripts-flag-changing-1.html
    • PASS [expected FAIL] subtest: Meta refresh is blocked by the allow-scripts sandbox flag at its creation time, not when refresh comes due
  • OK /html/semantics/document-metadata/the-meta-element/pragma-directives/attr-meta-http-equiv-refresh/allow-scripts-flag-changing-2.html
    • PASS [expected FAIL] subtest: Meta refresh of the original iframe is not blocked if moved into a sandboxed iframe
  • CRASH [expected OK] /html/semantics/forms/form-submission-0/form-submission-algorithm.html (#23003)
  • TIMEOUT [expected OK] /html/semantics/forms/form-submission-0/reparent-form-during-planned-navigation-task.html (#29724)
    • TIMEOUT [expected PASS] subtest: reparent-form-during-planned-navigation-task

      Test timed out
      

  • OK /html/semantics/scripting-1/the-script-element/module/dynamic-import/blob-url.any.html (#33948)
    • FAIL [expected PASS] subtest: Revoking a blob URL immediately after calling import will not fail

      promise_test: Unhandled rejection with value: object "TypeError: Dynamic import failed"
      

Stable unexpected results that are known to be intermittent (28)
  • FAIL [expected PASS] /_mozilla/mozilla/sslfail.html (#10760)
  • OK /_webgl/conformance/textures/misc/texture-upload-size.html (#21770)
    • FAIL [expected PASS] subtest: WebGL test #61

      assert_true: Texture was smaller than the expected size 2x2 expected true got false
      

    • FAIL [expected PASS] subtest: WebGL test #63

      assert_true: getError expected: INVALID_VALUE. Was NO_ERROR : when calling texSubImage2D with the same texture upload with offset 1, 1 expected true got false
      

    • FAIL [expected PASS] subtest: WebGL test #65

      assert_true: Texture was smaller than the expected size 2x2 expected true got false
      

    • FAIL [expected PASS] subtest: WebGL test #67

      assert_true: getError expected: INVALID_VALUE. Was NO_ERROR : when calling texSubImage2D with the same texture upload with offset 1, 1 expected true got false
      

    • FAIL [expected PASS] subtest: WebGL test #77

      assert_true: Texture was smaller than the expected size 2x2 expected true got false
      

    • FAIL [expected PASS] subtest: WebGL test #79

      assert_true: getError expected: INVALID_VALUE. Was NO_ERROR : when calling texSubImage2D with the same texture upload with offset 1, 1 expected true got false
      

    • FAIL [expected PASS] subtest: WebGL test #81

      assert_true: Texture was smaller than the expected size 2x2 expected true got false
      

    • FAIL [expected PASS] subtest: WebGL test #83

      assert_true: getError expected: INVALID_VALUE. Was NO_ERROR : when calling texSubImage2D with the same texture upload with offset 1, 1 expected true got false
      

    • PASS [expected FAIL] subtest: WebGL test #85
    • PASS [expected FAIL] subtest: WebGL test #87
    • And 2 more unexpected results...
  • PASS [expected FAIL] /css/compositing/mix-blend-mode/mix-blend-mode-video-sibling.html (#32849)
  • PASS [expected FAIL] /css/compositing/mix-blend-mode/mix-blend-mode-video.html (#32763)
  • FAIL [expected PASS] /css/css-grid/grid-items/grid-auto-margin-and-replaced-item-001.html (#37162)
  • FAIL [expected PASS] /css/css-overflow/overflow-video.html (#34720)
  • OK /css/cssom-view/scroll-behavior-smooth-navigation.html (#29564)
    • FAIL [expected PASS] subtest: Smooth scrolling while doing history navigation.

      assert_equals: Should be scrolled to top. expected 0 but got 14419
      

  • ERROR [expected TIMEOUT] /fetch/fetch-later/quota/same-origin-iframe/max-payload.tentative.https.window.html (#35210)
  • OK /html/browsers/browsing-the-web/navigating-across-documents/009.html (#24456)
    • FAIL [expected PASS] subtest: Link with onclick form submit to javascript url with document.write and href navigation

      assert_array_equals: expected property 1 to be "href" but got "click" (expected array ["write", "href"] got ["write", "click"])
      

  • CRASH [expected OK] /html/browsers/browsing-the-web/navigating-across-documents/about-srcdoc-navigation-blocked.window.html (#31025)
  • OK /html/browsers/browsing-the-web/navigating-across-documents/initial-empty-document/load-pageshow-events-iframe-contentWindow.html (#28681)
    • PASS [expected FAIL] subtest: load & pageshow events do not fire on contentWindow of <iframe> element created with src=''
  • OK /html/browsers/browsing-the-web/navigating-across-documents/navigation-unload-same-origin-fragment.html (#20768)
    • FAIL [expected PASS] subtest: Tests that a fragment navigation in the unload handler will not block the initial navigation

      assert_equals: expected "" but got "#fragment"
      

  • OK /html/browsers/history/the-history-interface/traverse_the_history_4.html (#21383)
    • FAIL [expected PASS] subtest: Multiple history traversals, last would be aborted

      assert_array_equals: Pages opened during history navigation expected property 1 to be 5 but got 3 (expected array [6, 5] got [6, 3])
      

  • OK /html/browsers/windows/browsing-context-names/duplicate-name-order.html (#34623)
    • FAIL [expected PASS] subtest: Duplicate name lookup order

      assert_equals: subtree first expected "ChildA" but got "SiblingA"
      

  • FAIL [expected PASS] /html/canvas/element/manual/text/canvas.2d.disconnected.html (#30063)
  • TIMEOUT /html/interaction/focus/the-autofocus-attribute/supported-elements.html (#24145)
    • TIMEOUT [expected NOTRUN] subtest: Host element with delegatesFocus should support autofocus

      Test timed out
      

  • OK [expected TIMEOUT] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-1.html (#22647)
    • FAIL [expected TIMEOUT] subtest: Check that popups from a sandboxed iframe escape the sandbox if allow-popups-to-escape-sandbox is used

      assert_equals: It came from a sandboxed iframe expected "null" but got "http://web-platform.test:8000"
      

  • OK [expected TIMEOUT] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-2.html (#22667)
    • FAIL [expected TIMEOUT] subtest: Check that popups from a sandboxed iframe escape the sandbox if allow-popups-to-escape-sandbox is used

      assert_equals: It came from a sandboxed iframe expected "null" but got "http://web-platform.test:8000"
      

  • OK [expected TIMEOUT] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_escaping-3.html (#24057)
    • FAIL [expected TIMEOUT] subtest: Check that popups from a sandboxed iframe escape the sandbox if allow-popups-to-escape-sandbox is used

      assert_equals: It came from a sandboxed iframe expected "null" but got "http://web-platform.test:8000"
      

  • OK [expected TIMEOUT] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-1.html (#24066)
    • FAIL [expected NOTRUN] subtest: Check that popups from a sandboxed iframe do not escape the sandbox

      assert_equals: It came from a sandboxed iframe expected "null" but got "http://web-platform.test:8000"
      

  • OK [expected TIMEOUT] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_popups_nonescaping-2.html (#22154)
    • FAIL [expected NOTRUN] subtest: Check that popups from a sandboxed iframe do not escape the sandbox

      assert_equals: It came from a sandboxed iframe expected "null" but got "http://web-platform.test:8000"
      

  • OK /navigation-timing/test-navigation-type-reload.html (#33334)
    • FAIL [expected PASS] subtest: Reload domComplete > Original domComplete

      assert_true: Reload domComplete > Original domComplete expected true got false
      

    • FAIL [expected PASS] subtest: Reload loadEventEnd > Original loadEventEnd

      assert_true: Reload loadEventEnd > Original loadEventEnd expected true got false
      

    • FAIL [expected PASS] subtest: Reload loadEventStart > Original loadEventStart

      assert_true: Reload loadEventStart > Original loadEventStart expected true got false
      

  • OK [expected TIMEOUT] /performance-timeline/navigation-id-detached-frame.tentative.html (#34773)
    • PASS [expected TIMEOUT] subtest: The navigation_id getter does not crash a window of detached frame
  • OK /preload/preload-error.sub.html (#37177)
    • PASS [expected FAIL] subtest: success (style): main
    • FAIL [expected PASS] subtest: 404 (style): main

      assert_greater_than: http://web-platform.test:8000/preload/resources/dummy.css?pipe=status%28404%29&label=style should be loaded expected a number greater than 0 but got 0
      

    • PASS [expected FAIL] subtest: CORS (style): main
    • PASS [expected FAIL] subtest: 404 (script): main
    • FAIL [expected PASS] subtest: success (xhr): main

      assert_greater_than: http://web-platform.test:8000/preload/resources/dummy.xml?label=xhr should be loaded expected a number greater than 0 but got 0
      

    • PASS [expected FAIL] subtest: CORS (xhr): main
    • PASS [expected FAIL] subtest: Decode-error (script): main
  • OK /webaudio/the-audio-api/the-audiobuffersourcenode-interface/sub-sample-buffer-stitching.html (#22849)
    • FAIL [expected PASS] subtest: X Stitched sine-wave buffers at sample rate 43800 does not equal [0,0.06264832615852356,0.12505052983760834,0.18696144223213196,0.24813786149024963,0.308339387178421,0.36732959747314453,0.4248766601085663,0.480754554271698,0.5347436666488647,0.5866320133209229,0.6362156271934509,0.6832997798919678,0.7276994585990906,0.7692402601242065,0.8077589869499207...] with an element-wise tolerance of {"absoluteThreshold":0.0038986,"relativeThreshold":0}. Index Actual Expected AbsError RelError Test threshold [14650] 1.0629878488543909e-7 8.6956524848937988e-1 8.6956514219059500e-1 9.9999987775640176e-1 3.8985999999999999e-3 [14651] 3.0547976493835449e-1 8.9879405498504639e-1 5.9331429004669189e-1 6.6012262403823208e-1 3.8985999999999999e-3 Max AbsError of 8.6956514219059500e-1 at index of 14650. Max RelError of 9.9999987775640176e-1 at index of 14650.

      assert_true: expected true got false
      

    • FAIL [expected PASS] subtest: X SNR (42.96525360075581 dB) is not greater than or equal to 65.737. Got 42.96525360075581.

      assert_true: expected true got false
      

  • TIMEOUT [expected OK] /webmessaging/with-ports/017.html (#24486)
    • TIMEOUT [expected PASS] subtest: origin of the script that invoked the method, about:blank

      Test timed out
      

  • TIMEOUT [expected OK] /webmessaging/with-ports/018.html (#24485)
    • TIMEOUT [expected PASS] subtest: origin of the script that invoked the method, javascript:

      Test timed out
      

  • TIMEOUT [expected OK] /webmessaging/without-ports/018.html (#24485)
    • TIMEOUT [expected PASS] subtest: origin of the script that invoked the method, javascript:

      Test timed out
      

Stable unexpected results (34)
  • OK [expected TIMEOUT] /content-security-policy/base-uri/base-uri_iframe_sandbox.sub.html
    • PASS [expected TIMEOUT] subtest: base-uri 'self' works with same-origin sandboxed iframes.
    • FAIL [expected TIMEOUT] subtest: base-uri 'self' blocks foreign-origin sandboxed iframes.

      assert_equals: expected "http://web-platform.test:8000/content-security-policy/base-uri/base-uri_iframe_sandbox.sub.html" but got "http://www2.web-platform.test:8000/base/"
      

  • OK /content-security-policy/frame-ancestors/frame-ancestors-nested-cross-in-sandboxed-cross-url-block.html
    • FAIL [expected PASS] subtest: A 'frame-ancestors' CSP directive with a URL value should compare against each frame's origin rather than URL, so a nested frame with a sandboxed parent frame should be blocked due to the parent having a unique origin.

      assert_unreached: Inner IFrame msg: The IFrame should have been blocked (or cross-origin). It wasn't. Reached unreachable code
      

  • OK /content-security-policy/frame-ancestors/frame-ancestors-sandbox-same-origin-self.html
    • FAIL [expected PASS] subtest: A 'frame-ancestors' CSP directive with a 'self' value should compare the child URL (self) against each parent's origin's URL rather then URL. When the ancestors are sandboxed, they never match.

      assert_unreached: Inner IFrame msg: The IFrame should have been blocked (or cross-origin). It wasn't. Reached unreachable code
      

  • TIMEOUT /content-security-policy/inheritance/blob-url-inherits-from-initiator.sub.html
    • FAIL [expected TIMEOUT] subtest: Initiator is same-origin with target frame.

      assert_equals: Eval should be blocked by CSP in blob URL. expected "eval blocked" but got "eval allowed"
      

  • OK [expected TIMEOUT] /content-security-policy/meta/sandbox-iframe.html
    • FAIL [expected TIMEOUT] subtest: img-src 'self' works when specified in a meta tag.

      assert_equals: expected "PASS" but got "FAIL"
      

  • OK [expected TIMEOUT] /cookies/cookie-enabled-noncookie-frame.html
    • PASS [expected TIMEOUT] subtest: navigator.cookieEnabled behavior on frames without cookie access
  • OK [expected ERROR] /html/browsers/sandboxing/sandbox-allow-scripts.html
    • PASS [expected NOTRUN] subtest: Running script from sandbox='allow-scripts' iframe is allowed
  • OK [expected TIMEOUT] /html/browsers/sandboxing/sandbox-disallow-popups.html
    • FAIL [expected NOTRUN] subtest: window.open in sandbox iframe

      uncaught exception: Error: assert_equals: return value of window.open (stringified) expected "null" but got "SyntaxError: The string did not match the expected pattern."
      

  • OK /html/browsers/sandboxing/sandbox-disallow-same-origin.html
    • FAIL [expected PASS] subtest: Access to sandbox iframe is disallowed

      assert_throws_dom: function "() => {
                  document.getElementById('sandboxedframe').contentWindow.document;
                }" did not throw
      

  • ERROR [expected OK] /html/browsers/sandboxing/sandbox-disallow-scripts.html
    • NOTRUN [expected PASS] subtest: Running script from sandbox iframe is disallowed
  • OK /html/browsers/sandboxing/sandbox-new-execution-context.html
    • FAIL [expected PASS] subtest: iframe with sandbox should load with new execution context

      assert_equals: New document in sandboxed iframe should have opaque origin expected null but got Document node with 1 child
      

  • OK [expected TIMEOUT] /html/infrastructure/urls/terminology-0/document-base-url-about-srcdoc.https.window.html
    • FAIL [expected TIMEOUT] subtest: allow-same-origin

      assert_equals: expected "https://web-platform.test:8443/html/infrastructure/urls/terminology-0/document-base-url-about-srcdoc.https.window.html" but got "about:srcdoc"
      

    • FAIL [expected NOTRUN] subtest: disallow-same-origin

      assert_equals: expected "https://web-platform.test:8443/html/infrastructure/urls/terminology-0/document-base-url-about-srcdoc.https.window.html" but got "about:srcdoc"
      

  • OK [expected TIMEOUT] /html/infrastructure/urls/terminology-0/document-base-url-changes-about-srcdoc.https.window.html
    • FAIL [expected TIMEOUT] subtest: sandboxed srcdoc - parent changes baseURI

      assert_not_equals: parent baseURI failed to change. got disallowed value "https://foo.com/"
      

  • OK /html/semantics/embedded-content/the-iframe-element/iframe-loading-lazy-in-script-disabled-iframe.html
    • PASS [expected FAIL] subtest: Iframes with loading='lazy' in script disabled iframe are not handled as 'lazy'
  • OK [expected ERROR] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_allow_script.html
    • FAIL [expected NOTRUN] subtest: iframe_sandbox_allow_scripts

      assert_false: [allow-scripts] is not set. expected false got true
      

  • OK /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_allow_top_navigation-1.html
    • PASS [expected FAIL] subtest: Frames with allow-top-navigation should be able to navigate the top frame.
  • OK /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_allow_top_navigation-2.html
    • FAIL [expected PASS] subtest: Frames without allow-top-navigation should not be able to navigate the top frame.

      assert_equals: expected "cannot navigate" but got "can navigate"
      

  • OK /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_allow_top_navigation-3.html
    • PASS [expected FAIL] subtest: Frames with allow-top-navigationshould be able to navigate the top frame even whenallow-top-navigation-by-user-activation is set.
  • OK /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_allow_top_navigation_by_user_activation_without_user_gesture.html
    • FAIL [expected PASS] subtest: The sandboxed iframe should post a message saying the test was in the state of 'PASS'.

      assert_equals: The message should say 'PASS' instead of 'FAIL' expected "PASS" but got "FAIL"
      

  • OK /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_ancestor-1.html
    • FAIL [expected PASS] subtest: Check that sandboxed iframe can not navigate their ancestors

      assert_equals: Should have the right message expected "can not navigate" but got "can navigate"
      

  • TIMEOUT [expected OK] /html/semantics/embedded-content/the-iframe-element/iframe_sandbox_navigate_other_frame_popup.sub.html
    • TIMEOUT [expected PASS] subtest: Sandboxed iframe can not navigate other frame's popup

      Test timed out
      

  • OK /html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-child-cross-origin-frame.tentative.sub.window.html
    • PASS [expected FAIL] subtest: A cross-origin frame with frame sandbox flags can navigate top
  • OK /html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-grandchild-frame-allow-top.tentative.sub.window.html
    • PASS [expected FAIL] subtest: A same-origin grandchild with frame allow-top can navigate top
  • OK /html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-grandchild-sandboxed-cross-origin-parent.tentative.sub.window.html
    • PASS [expected FAIL] subtest: A same-origin sandboxed grandchild in a cross-origin parent can navigate top
  • OK /html/semantics/embedded-content/the-iframe-element/sandbox-top-navigation-grandchild-sandboxed.tentative.sub.window.html
    • FAIL [expected PASS] subtest: A fully sandboxed same-origin grandchild can't navigate top

      assert_equals: The navigation should fail. expected false but got true
      

  • OK /html/semantics/embedded-content/the-iframe-element/sandbox_004.htm
    • PASS [expected FAIL] subtest: Fallback content is always displayed for sandboxed PDFs
  • OK /html/semantics/embedded-content/the-iframe-element/sandbox_023.htm
    • PASS [expected FAIL] subtest: Allow sandbox iframe to access other content from the same origin if sandbox='allow-same-origin'
  • OK /html/semantics/embedded-content/the-iframe-element/sandbox_025.htm
    • PASS [expected FAIL] subtest: Allow parent content to access sandbox child iframe content when sandbox='allow-same-origin'
  • OK /html/semantics/embedded-content/the-iframe-element/sandbox_028.htm
    • FAIL [expected PASS] subtest: Block sandbox iframe from accessing other content from the same origin.

      assert_equals: expected "!window.parent.document" but got "window.parent.document"
      

  • OK /html/semantics/embedded-content/the-img-element/image-loading-lazy-in-script-disabled-iframe.html
    • PASS [expected FAIL] subtest: Images with loading='lazy' in script disabled iframe are not handled as 'lazy'
  • OK [expected TIMEOUT] /secure-contexts/basic-popup-and-iframe-tests.html
    • PASS [expected TIMEOUT] subtest: Test Window.isSecureContext in a sandboxed iframe loading a srcdoc
    • FAIL [expected PASS] subtest: Test Window.isSecureContext in a popup loading a blob: URI

      assert_false: a blob: URI in a popup should not create a Secure Context when its creator is not a Secure Context. expected false got true
      

    • FAIL [expected PASS] subtest: Test Window.isSecureContext in a popup loading a javascript: URI

      assert_false: a javascript: URI in a popup should not create a Secure Context when its creator is not a Secure Context. expected false got true
      

    • FAIL [expected PASS] subtest: Test Window.isSecureContext in a popup loading about:blank

      assert_false: about:blank in a popup should not create a Secure Context when its creator is not a Secure Context. expected false got true
      

    • FAIL [expected PASS] subtest: Test Window.isSecureContext in a popup loading initial about:blank

      assert_false: initial about:blank in a popup should not create a Secure Context when its creator is not a Secure Context. expected false got true
      

  • OK [expected TIMEOUT] /secure-contexts/basic-popup-and-iframe-tests.https.html
    • PASS [expected TIMEOUT] subtest: Test Window.isSecureContext in a sandboxed iframe loading a srcdoc
  • OK [expected TIMEOUT] /shadow-dom/declarative/declarative-shadow-dom-opt-in.html
    • PASS [expected TIMEOUT] subtest: sandboxed iframe allows declarative Shadow DOM
  • OK /upgrade-insecure-requests/link-upgrade.sub.https.html
    • PASS [expected TIMEOUT] subtest: ./link-upgrade/iframe-top-navigation-no-upgrade-1.sub.html
    • PASS [expected TIMEOUT] subtest: ./link-upgrade/iframe-top-navigation-no-upgrade-2.sub.html

@github-actions
Copy link

⚠️ Try run (#15948085161) failed.

@TimvdLippe
Copy link
Contributor Author

The results of /html/infrastructure/urls/terminology-0/document-base-url-about-srcdoc.https.window.html look similar to the issue with MDN. It incorrectly leads to loading about:srcdoc instead of the WPT url. Need to figure out why that's the case.

@jdm
Copy link
Member

jdm commented Jun 29, 2025

The results of /html/infrastructure/urls/terminology-0/document-base-url-about-srcdoc.https.window.html look similar to the issue with MDN. It incorrectly leads to loading about:srcdoc instead of the WPT url. Need to figure out why that's the case.

If you're referring to these failures:

  FAIL allow-same-origin - assert_equals: expected "https://web-platform.test:8443/html/infrastructure/urls/terminology-0/document-base-url-about-srcdoc.https.window.html" but got "about:srcdoc"
runTest/<@https://web-platform.test:8443/html/infrastructure/urls/terminology-0/document-base-url-about-srcdoc.https.window.js:20:18
async*Test.prototype.step@https://web-platform.test:8443/resources/testharness.js:2684:25
promise_test/tests.promise_tests</<@https://web-platform.test:8443/resources/testharness.js:738:36
promise_test/tests.promise_tests<@https://web-platform.test:8443/resources/testharness.js:737:20
  FAIL disallow-same-origin - assert_equals: expected "https://web-platform.test:8443/html/infrastructure/urls/terminology-0/document-base-url-about-srcdoc.https.window.html" but got "about:srcdoc"
runTest/<@https://web-platform.test:8443/html/infrastructure/urls/terminology-0/document-base-url-about-srcdoc.https.window.js:20:18
async*Test.prototype.step@https://web-platform.test:8443/resources/testharness.js:2684:25
promise_test/tests.promise_tests</<@https://web-platform.test:8443/resources/testharness.js:738:36
promise_test/tests.promise_tests<@https://web-platform.test:8443/resources/testharness.js:737:20

That comes from the document being initialized with

let url = ServoUrl::parse("about:srcdoc").unwrap();
let mut meta = Metadata::default(url.clone());
via
let final_url = metadata.final_url.clone();
.

@jdm
Copy link
Member

jdm commented Jun 29, 2025

Ooh, and

if let Some(browsing_context) = self.browsing_context() {
// Step 1: If document is an iframe srcdoc document, then return the
// document base URL of document's browsing context's container document.
let container_base_url = browsing_context
.parent()
.and_then(|parent| parent.document())
.map(|document| document.base_url());
if document_url.as_str() == "about:srcdoc" {
if let Some(base_url) = container_base_url {
return base_url;
}
}
// Step 2: If document's URL is about:blank, and document's browsing
// context's creator base URL is non-null, then return that creator base URL.
if document_url.as_str() == "about:blank" && browsing_context.has_creator_base_url() {
return browsing_context.creator_base_url().unwrap();
is broken when we load a sandboxed about:blank/about:srcdoc in another script thread, because we can't access the parent browsing context.

There was a discrepancy that for iframes with a sandbox,
the script_thread wouldn't handle the `about:` URLs correctly.
Now, we reuse the same logic as used when navigating in
an already present child frame.

Fixes servo#36529
Fixes servo#37499

Signed-off-by: Tim van der Lippe <[email protected]>
This is used in the fallback base URL for about:blank
and about:srcdoc documents.

Signed-off-by: Tim van der Lippe <[email protected]>
@TimvdLippe TimvdLippe force-pushed the pr-timvdlippe-handle-iframe-sandbox branch from b0c3581 to bf3ca58 Compare June 29, 2025 09:30
@TimvdLippe
Copy link
Contributor Author

Pushed my attempt on realigning the implementation to the spec. Unfortunately it doesn't work yet, as the URL is None in the case of about:srcdoc in a sandbox. In #37775 (comment) I wrote my attempts on figuring out why that's the case, but unfortunately have to give up as I can't find any references to the spec about this.

@jdm
Copy link
Member

jdm commented Jun 29, 2025

unfortunately have to give up as I can't find any references to the spec about this.

This isn't very surprising to me—this part of the specification (including the lines you quoted in the issue) is much newer than most of the navigation-related code in Servo.

@TimvdLippe
Copy link
Contributor Author

Okay, it makes sense to me that the code predates the spec. I am surprised as to (seemingly to me at least) the degree they diverge. This might be my lack of experience working with network code, as I don't see a lot of similarities with the code as it is and what the spec expects.

Whenever I venture into the constellation or iframe network code, I feel like I hit a brick wall. Then I wonder what's causing that to happen: is it my lack of experience with network code, lack of experience with building browsers, or is this too complicated for me to pick up at this stage? With scripting code, I feel like I can manage, with the odd Rust bit that I need learn (looking at you Arc). However, that's not the case here.

Do let me know if I am missing something fundamental and how I can more knowledge about that. I am eager to learn, but at the same time the brick wall is still there and I don't know how to get around it.

@jdm
Copy link
Member

jdm commented Jun 29, 2025

A lot of the network code was written alongside the original Fetch specification (and evolved from a non-Fetch implementation), and there have been a lot of changes to the specification since that implementation. Likewise, the constellation is one of the earliest pieces of Servo (I remember describing it in technical talks in 2013), and encompasses some complex parts of the specification like session history that have also undergone extensive revisions in the past five years, while the constellation implementation has largely remained unchanged.

My experience is that navigation, session history, and async fetching are already very complex parts of the specification to reason about, and what exists in the specification has long evolved in a different direction than Servo's implementation (despite our influence like whatwg/html#1454 (comment)). This makes it particularly unapproachable if you're used to using the specification text to help guide your understanding of Servo's code, which is a much easier experience in must the script crate (as you have found!).

#33087 is an example of my experiments to bring more of our iframe loading implementation in line with the specification. It's a lot of trial and error to figure out where are the right places to make changes, unfortunately. I don't have good suggestions right now, but I may try writing down some high level documentation about what Servo does today so it's easier to see the big picture.

@jdm
Copy link
Member

jdm commented Jun 29, 2025

In particular, whatwg/html#6315 was a huge change to the specification around navigation and session history and occurred while Servo was mostly dormant. As a result we haven't dedicated much effort to adjusting our implementation to catch up with it, leading to the current difficulties of determining how to make correctness changes to Servo's implementation.

@TimvdLippe
Copy link
Contributor Author

Closing this in favor of #39610 CC @mrobinson

@TimvdLippe TimvdLippe closed this Oct 2, 2025
@mrobinson
Copy link
Member

@TimvdLippe Oh, I didn't realize these two changes were conflicting. Apologies for stepping on your toes here.

@TimvdLippe
Copy link
Contributor Author

You weren't. I had sort of given up on this PR already as I realized the iframe machinery needs some love first. So I am actually glad you took over!

@TimvdLippe TimvdLippe deleted the pr-timvdlippe-handle-iframe-sandbox branch November 7, 2025 09:47
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.

MDN inline examples are blocked by Content Security Policy Sandboxed iframes fetch about:srcdoc instead of loading the srcdoc contents

3 participants