Closed Bug 1840932 Opened 3 years ago Closed 1 year ago

document.open while a stylesheet is pending can indefinitely block onload

Categories

(Web Compatibility :: Site Reports, defect, P1)

Tracking

(firefox136 fixed)

RESOLVED FIXED
Tracking Status
firefox136 --- fixed

People

(Reporter: twisniewski, Assigned: vhilla)

References

(Depends on 1 open bug, )

Details

(Keywords: webcompat:needs-contact, webcompat:platform-bug)

User Story

platform:windows,mac,linux,android
impact:content-missing
configuration:general
affects:all

Attachments

(2 files, 1 obsolete file)

There is a bunch of content "missing" on https://global.gmarket.co.kr/item?goodscode=2651997834 in Firefox, because the iframe which contains said content never fires its load event, and the page never sizes the frame to fit that content.

This is done using some React JS:

        return Ot(
          'iframe',
          {
            title: 'Description',
            src: 'about:blank',
            scrolling: 'no',
            frameBorder: '0',
            ref: function (t) {
              if (t) {
                var n = t.contentDocument;
                n.open(),
                n.write( 
                  '<title>Description</title>',
                  '<style>iframe,img,embed,object,video{max-width:100%;}</style>',
                  '<link rel="stylesheet" href="//script.gmarket.com/ebayeditor/css/ee-contents.css"></link>',
                  e.html
                ),
                n.close()
              } 
            },
            width: '860',
            height: n,
            onLoad: function (e) {
              var t = e.currentTarget.contentDocument.body.scrollHeight;
              a(t) 
            }
          }

Just after the newly-created iframe element is attached to the document, the ref handler runs, re-writes the iframe with its intended content, and then the code waits for the onLoad handler to fire. In Chrome it fires as expected sometime after the document is closed, once readyState is completed. In Firefox it never fires.

An error event is also never fired, and if I alter the source code of the iframe to include a script logging its readyState (<script>setInterval(() => { console.log(document.readyState) }, 200)</script>) it never completes, it remains interactive.

This is of course causing its content to remain hidden, so it's a webcompat issue affecting live sites, as reported on webcompat.com.

Summary: No iframe load event fired after opening/writing/closing to the document, unlike in Chrome → No iframe load event fired after opening/writing/closing to the document on a website, unlike in Chrome

Bug 220055 seems to have a similar issue.

Olli, Is this something that we should fix?

Given this is a web-compat issue, I am labelling this as S2 for now.

Severity: -- → S2
Flags: needinfo?(smaug)

What does the spec say?

Flags: needinfo?(smaug)

It's been a forever behavior on our end and we don't recall we've seen many reports regarding this. Moving severity to S3 for now.

Severity: S2 → S3
Webcompat Priority: --- → ?
Priority: -- → P3
Webcompat Priority: ? → P2

Since this seems like a near-dupe of 220055 I'm going to move this bug to be a site report, and create a kb entry for the general issue.

Severity: S3 → --
Component: DOM: Core & HTML → Site Reports
Priority: P3 → --
Product: Core → Web Compatibility
Depends on: 1885937
Webcompat Priority: P2 → ---
Severity: -- → S2
User Story: (updated)
Priority: -- → P1
Attached file testcase.html (obsolete) —

(In reply to Thomas Wisniewski [:twisniewski] from comment #0)

Just after the newly-created iframe element is attached to the document, the ref handler runs, re-writes the iframe with its intended content, and then the code waits for the onLoad handler to fire. In Chrome it fires as expected sometime after the document is closed, once readyState is completed. In Firefox it never fires.

I created a standalone testcase based off of this description. From what I see, Firefox does fire a load event after open/write/close. Maybe this is an about:blank or timing issue?

Firefox 135.0a1 behavior
When clicking test, the console shows

start writing
finished writing
onload file:...testcase.html

Chrome 133.0.6901.0 behavior
When clicking test, the console shows

onload about:blank
start writing
onload file:...testcase.html
finished writing

The problem with the webpage appears to be with the link element being written to the iframe.

If the ref method is changed such that the written html doesn't contain a stylesheet link, the page works. On the contrary, the page breaks if the written html contains a stylesheet link with non-empty href. The page breaks with other stylesheets, even invalid ones like href=//example.

I cannot reproduce this in a new React 16.14.0 (currently used by Gmarket) project.

Attachment #9444099 - Attachment is obsolete: true
Attached file test.html

I found the issue, the problem occurs when one synchronously open/write link/closes twice.

What's going on here is that one script loader instance is blocking onload twice, but only unblocks once.

The sequence of events is as follows:

  1. We start a document load via document.open
  2. This leads to ScriptLoader::BeginDeferringScripts, which calls Document::BlockOnload increasing the mOnloadBlockCount.
  3. document.write happens and we start processing the stylesheet
    • This leads to a race condition. If the sheet completes, nsContentSink::StyleSheetLoaded leads to ScriptLoader::DeferCheckpointReached. If the second document.open happens synchronously, this race is not triggered. But if one attaches the debugger, or adds a timeout, it sometimes is.
  4. The second document.open occurs and calls ScriptLoader::BeginDeferringScripts for the same ScriptLoader object. It's already blocking load, but if the defer checkpoint wasn't reached, it will call Document::BlockOnload again (see code).
    • Bug 1568171 is similar to this one and made sure that if the defer checkpoint is reached, we don't block twice.
  5. Another link is created, the stylesheet processed, defer checkpoint reached. This triggers ScriptLoader::ProcessPendingRequests, which will unblock load once.
    • This can happen only once. We only unblock when mDeferCheckpointReached is toggled false. We only set mDeferCheckpointReached when mDeferEnabled is toggled false. We only set mDeferEnabled during BeginDeferringScripts.

If a document is still processing a stylesheet while document.open occurs,
the defer checkpoint might not have been reached. This causes the
ScriptLoader to block load twice, although it will only unblock once.

Related: Bug 1568171

Assignee: nobody → vincenthilla5
Status: NEW → ASSIGNED
Pushed by vhilla@mozilla.com: https://hg.mozilla.org/integration/autoland/rev/0af2407bba0d Have ScriptLoader not block load twice on document.open. r=smaug
Summary: No iframe load event fired after opening/writing/closing to the document on a website, unlike in Chrome → document.open while a stylesheet is pending can indefinitely block onload
Status: ASSIGNED → RESOLVED
Closed: 1 year ago
Resolution: --- → FIXED
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: