Open Bug 667227 Opened 9 years ago Updated 2 years ago

Issue with script loading in an iframe

Categories

(Core :: DOM: Core & HTML, defect, P5)

defect

Tracking

()

People

(Reporter: bruant.d, Unassigned)

References

(Depends on 1 open bug)

Details

Attachments

(2 files)

I was working on improving test262 and I have discovered a bug. Reducing the test case would take billions of hours, so I think I should rather send the website (I have removed most irrelevant parts).

Test262 is the official ECMAScript test suite effort. Their website is at http://test262.ecmascript.org/
Sources are at http://hg.ecmascript.org/tests/test262/
Attached is a reduced version of it.
The website main page is the default.html at the root of the directory.

_High level description_
In order to run tests, test262 opens an iframe, injects a couple of scripts in the iframe as <script> tags (including the test itself) and then get the result back. The file doing the injection is sth.js, function "this.run" in BrowserRunner.
I have decided to work on the script resources/scripts/global/sth.js. I have renamed the original sth.js file to sthDocstream.js. My version is sthDocfrag.js (Hence the two commented scripts in default.html, to switch easily).
In the original version, they created the iframe, opened its document with "document.open()" and were doing a bunch of "document.writeln()" to insert the scripts (hence "Docstream"). I wanted to take the following approach: create a document fragment and append all the scripts (as DOM nodes) to it (hence "Docfrag").
The change was not suppose to change tests results and it did not with the exception of 2 tests which "fail to load" on Firefox. The 2 tests are S15.7.3.2_A1 and S15.9.3.2_A1_T1 (code can be found under resources/scripts/testcases, but is base64 encoded. It can also be found at http://hg.ecmascript.org/tests/test262/file/89c60d2478f1/test/suite/sputnik_converted/15_Native/15.7_Number_Objects/15.7.3_Properties_of_Number_Constructor/15.7.3.2_Number.MAX_VALUE/S15.7.3.2_A1.js
and
http://hg.ecmascript.org/tests/test262/file/89c60d2478f1/test/suite/sputnik_converted/15_Native/15.9_Date_Objects/15.9.3_The_Date_Constructor/S15.9.3.2_A1_T1.js )

_More details on the 2 tests failing to load_
In rare cases, some tests require to include additional "include" scripts. This inclusion is done in sth.js starting at the line saying "if(includes !== null)" (l.106 in sthDocfrag.js). Scripts are retrieved with a synchronous (!) XMLHttpRequest then added to a cache (the "scriptCache" variable). If the script is already in this cache, the cached version is retrieved.
Here is the thing: the two tests failing at loading happen to be the very files which first trigger the "include" code and doing the synchronous request. All tests using the cached version of the include load perfectly well.

_Where does the bug occur?_
The exact same "include" code works well in the sthDocstream.js version.
There is no loading failure on Chrome 12 nor on Opera 11.11 in both sthDocfrag and sthDocstream versions, confirming this is a Firefox specific bug.
FF5, FF6.0a2 (2011-06-25), FF7.0a1 (2011-06-25) (on Ubuntu) are affected.
I was originally running the site with a "file://" url. When I switched to using an "http://localhost" URL with an Apache server, it didn't change anything to the bug.

I do not really know how to further simplify the use case. My guess is that the bug may be related to iframes/script nodes/textcontent/synchronous xhr but I don't know how.

This change in my code happens to be 40% faster (https://bugs.ecmascript.org/show_bug.cgi?id=113). It's unfortunate there is this Firefox-specific bug. I'd be happy to help if I'm a little bit guided on how to reduce the test case.
OS: Linux → All
Hardware: x86 → All
Version: 5 Branch → Trunk
> create a document fragment and append all the scripts (as DOM nodes) to it

Note that this doesn't guarantee that the scripts will execute in DOM order (unlike the document.write situation, which _does_ make such a guarantee).  When things fail, are scripts executing out of order?

> Scripts are retrieved with a synchronous (!) XMLHttpRequest

Note that sync XHR can spin the event loop and reenter other scripts.  When things fail, is that happening?  That is, does one script run while another is doing the script XHR?

> There is no loading failure on Chrome 12 nor on Opera 11.11 in both
> sthDocfrag and sthDocstream versions, confirming this is a Firefox specific
> bug.

Or at least a Firefox-specific behavior in your configuration and with your timing parameteres...
(In reply to comment #1)
> > create a document fragment and append all the scripts (as DOM nodes) to it
> 
> Note that this doesn't guarantee that the scripts will execute in DOM order
> (unlike the document.write situation, which _does_ make such a guarantee). 
I assume that we're refering to the lack of specification of how children of a documentFragment are appended in DOM core 3. This underspecification seems to be fixed in current DOM Core drafts http://www.w3.org/TR/domcore/#dom-node-appendchild and http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#dom-node-appendchild asking for insertion "in tree order".
Does Firefox already implement that or should I file a bug?


> > Scripts are retrieved with a synchronous (!) XMLHttpRequest
> Note that sync XHR can spin the event loop and reenter other scripts.  When
> things fail, is that happening?  That is, does one script run while another
> is doing the script XHR?
I thought of that and I have added log messages. They appear in the correct order, so assuming the console respects execution order, it's not a race condition of this kind.


> When things fail, are scripts executing out of order?
Apparently, they're not executed at all. None of them.
Apparently, appending the document fragment to the iframe doesn't work (at least, script do not execute which doesn't update test results which is interpreted as a test load failure by the test harness)
I've also tried to not use a document fragment but rather bundle all scripts within the same <script> element and append this to the iframe. The exact same thing happens, scripts are not executed for the two very same test cases.
Could it be some bad interaction between (src-less) iframes, script (or just nodes?) appending and (synchronous) xhr?

I'm going to poke around these features on their own and try to come up with a fresh small test case (or describe all failed attempt to understand where the bug come from).

Thanks for your comments!
> I assume that we're refering to the lack of specification of how children of a
> documentFragment are appended in DOM core 3.

No, that's well-specified.  But non-inline scripts inserted via the DOM are loaded async and execute once loaded; execution order for DOM-inserted scripts (unlike parser-inserted ones) is NOT forced to follow DOM order in HTML5.  So execution order depends on the order in which racing network requests complete and can be arbitrary.

> They appear in the correct order

That wasn't quite the question I asked.  I asked whether they were reentering.

> Apparently, they're not executed at all. None of them.

This is based on your log statements, right?

> I've also tried to not use a document fragment but rather bundle all scripts
> within the same <script> element and append this to the iframe. The exact
> same thing happens, scripts are not executed for the two very same test
> cases.

That's really weird.

> I'm going to poke around these features on their own and try to come up with
> a fresh small test case

That would be much appreciated!  If you can't manage that, you may have to talk me through reproducing using your nonminimal testcase while running as few tests as possible.
> But non-inline scripts inserted via the DOM

Except your scripts are inline, right?  So this should not be an issue...
Attached file reduced test case
Smaller test case
(In reply to comment #4)
> > But non-inline scripts inserted via the DOM
> 
> Except your scripts are inline, right?  So this should not be an issue...

So yeah, all scripts are inline, no download (that's the point of the synchrnous xhr with cache actually).

I worked on building a smaller test case and I have result!!
In my latest attachement, if "s" is the value of the synchrnous xhr (l.30), the script is not executed. However, if it is an hardcoded string (l.32), it works. I have no idea where this comes from or what can cause that. Maybe some wrapper issues? :-s
Both cases work the same in Chrome.

I haven't worked on making the thing even smaller. Maybe it's unrelated to iframes.
(In reply to comment #6)
> Both cases work the same in Chrome.
And Opera 11.11 too.

On the big test case, after the xhr, the script string is put in a cache. Using it the first time doesn't work, but all test cases requiring the same include pull it from the cache and it works for them.

I'm currently out of idea on what could cause such a bug.
Aha, that testcase works great!  Thank you!

So the key part here, is that you're creating an iframe, then immediately grabbing the document and creating a script, then doing a sync XHR.  That spins the event loop, allowing network loads to proceed, including the about:blank network load happening in the iframe.   By the time your XHR is done, the document you grabbed up front has unloaded already.  Then you insert a script into this no-longer-loaded document, but that does nothing of course.

You can verify this by comparing the values of iframeWin.contentDocument before and after the XHR.

Henri's changes to make no about:blank load happen in this situation might change the behavior, but in the meantime, just move your "grab the document and create the script" code after the XHR?
Depends on: sync-about-blank
(In reply to comment #8)
> So the key part here, is that you're creating an iframe, then immediately
> grabbing the document and creating a script, then doing a sync XHR.  That
> spins the event loop, allowing network loads to proceed, including the
> about:blank network load happening in the iframe.   By the time your XHR is
> done, the document you grabbed up front has unloaded already.  Then you
> insert a script into this no-longer-loaded document, but that does nothing
> of course.
I don't understand. Why would the document unload? Shouldn't it happen only when I remove the iframe from the DOM tree?

> Henri's changes to make no about:blank load happen in this situation might
> change the behavior, but in the meantime, just move your "grab the document
> and create the script" code after the XHR?
Works like a charm. I'll follow the work on sync-about-blank to see if it fixes this bug too.

Thanks a lot for your help :-)
> I don't understand. Why would the document unload?

Because a new document is being loaded.

Again, when you insert an iframe in the DOM it starts loading a new URL: about:blank.  This is an asynchronous operation.  If you try to get the document inside the iframe before the async load completes it will synchronously synthesize a fake document and return it, but the async load is still there, and when it completes it will replace the synthetic document with a document parsed from the data retrieved from about:blank.

Try the same exercise with the iframe src set to http://www.example.com and log the url loaded in the iframe at various points to see how it goes.
https://bugzilla.mozilla.org/show_bug.cgi?id=1472046

Move all DOM bugs that haven’t been updated in more than 3 years and has no one currently assigned to P5.

If you have questions, please contact :mdaly.
Priority: -- → P5
You need to log in before you can comment on or make changes to this bug.